https://github.com/mmha updated https://github.com/llvm/llvm-project/pull/138156
>From 66cea1ef63965b27617a6b6995ac1c5f8d8b8ed6 Mon Sep 17 00:00:00 2001 From: Morris Hafner <mhaf...@nvidia.com> Date: Thu, 1 May 2025 17:27:23 +0200 Subject: [PATCH 1/4] [CIR] Upstream lowering of lvalue conditional operators to TernaryOp This patch adds visitors for BinLAnd, BinLOr and AbstractConditionalOperator. Note that this patch still lacks visitation of OpaqueValueExpr which are needed for the GNU ?: operator. --- .../CIR/Dialect/Builder/CIRBaseBuilder.h | 16 + clang/include/clang/CIR/MissingFeatures.h | 5 +- clang/lib/CIR/CodeGen/CIRGenDecl.cpp | 3 +- clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 276 ++++++++++++++- clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp | 11 +- clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 334 ++++++++++++++++++ clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 14 + clang/lib/CIR/CodeGen/CIRGenFunction.h | 241 ++++++++++++- clang/lib/CIR/CodeGen/CIRGenValue.h | 1 + clang/test/CIR/CodeGen/binop.c | 13 + clang/test/CIR/CodeGen/binop.cpp | 225 ++++++++++++ clang/test/CIR/CodeGen/ternary.cpp | 120 +++++++ 12 files changed, 1239 insertions(+), 20 deletions(-) create mode 100644 clang/test/CIR/CodeGen/binop.c create mode 100644 clang/test/CIR/CodeGen/ternary.cpp diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h index 9de3a66755778..e143c7958f0bc 100644 --- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h +++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h @@ -282,6 +282,22 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { return createCast(loc, cir::CastKind::bitcast, src, newTy); } + // TODO(cir): the following function was introduced to keep in sync with LLVM + // codegen. CIR does not have "zext" operations. It should eventually be + // renamed or removed. For now, we just add whatever cast is required here. + mlir::Value createZExtOrBitCast(mlir::Location loc, mlir::Value src, + mlir::Type newTy) { + mlir::Type srcTy = src.getType(); + + if (srcTy == newTy) + return src; + + if (mlir::isa<cir::BoolType>(srcTy) && mlir::isa<cir::IntType>(newTy)) + return createBoolToInt(src, newTy); + + llvm_unreachable("unhandled extension cast"); + } + //===--------------------------------------------------------------------===// // Binary Operators //===--------------------------------------------------------------------===// diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index fb205e9cd85d1..4689f1d7f4a63 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -201,6 +201,8 @@ struct MissingFeatures { static bool writebacks() { return false; } static bool cleanupsToDeactivate() { return false; } static bool stackBase() { return false; } + static bool peepholeProtection() { return false; } + static bool instrumenation() { return false; } // Missing types static bool dataMemberType() { return false; } @@ -225,8 +227,9 @@ struct MissingFeatures { static bool ptrDiffOp() { return false; } static bool ptrStrideOp() { return false; } static bool switchOp() { return false; } - static bool ternaryOp() { return false; } + static bool throwOp() { return false; } static bool tryOp() { return false; } + static bool vecTernaryOp() { return false; } static bool zextOp() { return false; } // Future CIR attributes diff --git a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp index 61af33053dc0a..80b0172090aa3 100644 --- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp @@ -50,8 +50,7 @@ CIRGenFunction::emitAutoVarAlloca(const VarDecl &d) { // A normal fixed sized variable becomes an alloca in the entry block, mlir::Type allocaTy = convertTypeForMem(ty); // Create the temp alloca and declare variable using it. - address = createTempAlloca(allocaTy, alignment, loc, d.getName(), - /*insertIntoFnEntryBlock=*/false); + address = createTempAlloca(allocaTy, alignment, loc, d.getName()); declare(address.getPointer(), &d, ty, getLoc(d.getSourceRange()), alignment); emission.Addr = address; diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index 5424c6a8d6f3c..556346fa495a3 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -15,6 +15,7 @@ #include "CIRGenModule.h" #include "CIRGenValue.h" #include "mlir/IR/BuiltinAttributes.h" +#include "mlir/IR/Value.h" #include "clang/AST/Attr.h" #include "clang/AST/CharUnits.h" #include "clang/AST/Decl.h" @@ -22,6 +23,7 @@ #include "clang/AST/ExprCXX.h" #include "clang/CIR/Dialect/IR/CIRDialect.h" #include "clang/CIR/MissingFeatures.h" +#include <optional> using namespace clang; using namespace clang::CIRGen; @@ -229,7 +231,7 @@ void CIRGenFunction::emitStoreThroughLValue(RValue src, LValue dst, static LValue emitGlobalVarDeclLValue(CIRGenFunction &cgf, const Expr *e, const VarDecl *vd) { - QualType T = e->getType(); + QualType t = e->getType(); // If it's thread_local, emit a call to its wrapper function instead. assert(!cir::MissingFeatures::opGlobalThreadLocal()); @@ -259,7 +261,7 @@ static LValue emitGlobalVarDeclLValue(CIRGenFunction &cgf, const Expr *e, cgf.cgm.errorNYI(e->getSourceRange(), "emitGlobalVarDeclLValue: reference type"); else - lv = cgf.makeAddrLValue(addr, T, AlignmentSource::Decl); + lv = cgf.makeAddrLValue(addr, t, AlignmentSource::Decl); assert(!cir::MissingFeatures::setObjCGCLValueClass()); return lv; } @@ -1195,6 +1197,165 @@ Address CIRGenFunction::emitArrayToPointerDecay(const Expr *e) { return Address(ptr, addr.getAlignment()); } +// Handle the case where the condition is a constant evaluatable simple integer, +// which means we don't have to separately handle the true/false blocks. +static std::optional<LValue> handleConditionalOperatorLValueSimpleCase( + CIRGenFunction &cgf, const AbstractConditionalOperator *e) { + const Expr *condExpr = e->getCond(); + bool condExprBool; + if (cgf.constantFoldsToSimpleInteger(condExpr, condExprBool)) { + const Expr *live = e->getTrueExpr(), *dead = e->getFalseExpr(); + if (!condExprBool) + std::swap(live, dead); + + if (!cgf.containsLabel(dead)) { + // If the true case is live, we need to track its region. + if (condExprBool) { + assert(!cir::MissingFeatures::incrementProfileCounter()); + } + // If a throw expression we emit it and return an undefined lvalue + // because it can't be used. + if (isa<CXXThrowExpr>(live->IgnoreParens())) { + assert(!cir::MissingFeatures::throwOp()); + cgf.cgm.errorNYI(live->getSourceRange(), + "throw expressions in conditional operator"); + return std::nullopt; + } + return cgf.emitLValue(live); + } + } + return std::nullopt; +} + +/// Emit the operand of a glvalue conditional operator. This is either a glvalue +/// or a (possibly-parenthesized) throw-expression. If this is a throw, no +/// LValue is returned and the current block has been terminated. +static std::optional<LValue> emitLValueOrThrowExpression(CIRGenFunction &cgf, + const Expr *operand) { + if (isa<CXXThrowExpr>(operand->IgnoreParens())) { + assert(!cir::MissingFeatures::throwOp()); + cgf.cgm.errorNYI(operand->getSourceRange(), + "throw expressions in conditional operator"); + return std::nullopt; + } + + return cgf.emitLValue(operand); +} + +// Create and generate the 3 blocks for a conditional operator. +// Leaves the 'current block' in the continuation basic block. +template <typename FuncTy> +CIRGenFunction::ConditionalInfo +CIRGenFunction::emitConditionalBlocks(const AbstractConditionalOperator *e, + const FuncTy &branchGenFunc) { + ConditionalInfo info; + CIRGenFunction &cgf = *this; + ConditionalEvaluation eval(cgf); + mlir::Location loc = cgf.getLoc(e->getSourceRange()); + CIRGenBuilderTy &builder = cgf.getBuilder(); + Expr *trueExpr = e->getTrueExpr(); + Expr *falseExpr = e->getFalseExpr(); + + mlir::Value condV = cgf.emitOpOnBoolExpr(loc, e->getCond()); + SmallVector<mlir::OpBuilder::InsertPoint, 2> insertPoints{}; + mlir::Type yieldTy{}; + + auto emitBranch = [&](mlir::OpBuilder &b, mlir::Location loc, Expr *expr, + std::optional<LValue> &branchInfo) { + CIRGenFunction::LexicalScope lexScope{cgf, loc, b.getInsertionBlock()}; + cgf.curLexScope->setAsTernary(); + + assert(!cir::MissingFeatures::incrementProfileCounter()); + eval.begin(cgf); + branchInfo = branchGenFunc(cgf, expr); + mlir::Value branch = branchInfo->getPointer(); + eval.end(cgf); + + if (branch) { + yieldTy = branch.getType(); + b.create<cir::YieldOp>(loc, branch); + } else { + // If LHS or RHS is a throw or void expression we need to patch + // arms as to properly match yield types. + insertPoints.push_back(b.saveInsertionPoint()); + } + }; + + info.result = builder + .create<cir::TernaryOp>( + loc, condV, /*trueBuilder=*/ + [&](mlir::OpBuilder &b, mlir::Location loc) { + emitBranch(b, loc, trueExpr, info.lhs); + }, + /*falseBuilder=*/ + [&](mlir::OpBuilder &b, mlir::Location loc) { + emitBranch(b, loc, falseExpr, info.rhs); + }) + .getResult(); + + if (!insertPoints.empty()) { + // If both arms are void, so be it. + if (!yieldTy) + yieldTy = cgf.VoidTy; + + // Insert required yields. + for (mlir::OpBuilder::InsertPoint &toInsert : insertPoints) { + mlir::OpBuilder::InsertionGuard guard(builder); + builder.restoreInsertionPoint(toInsert); + + // Block does not return: build empty yield. + if (mlir::isa<cir::VoidType>(yieldTy)) { + builder.create<cir::YieldOp>(loc); + } else { // Block returns: set null yield value. + mlir::Value op0 = builder.getNullValue(yieldTy, loc); + builder.create<cir::YieldOp>(loc, op0); + } + } + } + return info; +} + +LValue CIRGenFunction::emitConditionalOperatorLValue( + const AbstractConditionalOperator *expr) { + if (!expr->isGLValue()) { + // ?: here should be an aggregate. + assert(hasAggregateEvaluationKind(expr->getType()) && + "Unexpected conditional operator!"); + return emitAggExprToLValue(expr); + } + + OpaqueValueMapping binding(*this, expr); + if (std::optional<LValue> res = + handleConditionalOperatorLValueSimpleCase(*this, expr)) + return *res; + + ConditionalInfo info = + emitConditionalBlocks(expr, [](CIRGenFunction &cgf, const Expr *e) { + return emitLValueOrThrowExpression(cgf, e); + }); + + if ((info.lhs && !info.lhs->isSimple()) || + (info.rhs && !info.rhs->isSimple())) { + cgm.errorNYI(expr->getSourceRange(), "unsupported conditional operator"); + return {}; + } + + if (info.lhs && info.rhs) { + Address lhsAddr = info.lhs->getAddress(); + Address rhsAddr = info.rhs->getAddress(); + Address result(info.result, lhsAddr.getElementType(), + std::min(lhsAddr.getAlignment(), rhsAddr.getAlignment())); + AlignmentSource alignSource = + std::max(info.lhs->getBaseInfo().getAlignmentSource(), + info.rhs->getBaseInfo().getAlignmentSource()); + assert(!cir::MissingFeatures::opTBAA()); + return makeAddrLValue(result, expr->getType(), LValueBaseInfo(alignSource)); + } + assert((info.lhs || info.rhs) && + "both operands of glvalue conditional are throw-expressions?"); + return info.lhs ? *info.lhs : *info.rhs; +} + /// Emit an `if` on a boolean condition, filling `then` and `else` into /// appropriated regions. mlir::LogicalResult CIRGenFunction::emitIfOnBoolExpr(const Expr *cond, @@ -1259,10 +1420,28 @@ mlir::Value CIRGenFunction::emitOpOnBoolExpr(mlir::Location loc, // cir.ternary(!x, t, f) -> cir.ternary(x, f, t) assert(!cir::MissingFeatures::shouldReverseUnaryCondOnBoolExpr()); - if (isa<ConditionalOperator>(cond)) { - cgm.errorNYI(cond->getExprLoc(), "Ternary NYI"); - assert(!cir::MissingFeatures::ternaryOp()); - return createDummyValue(loc, cond->getType()); + if (const ConditionalOperator *condOp = dyn_cast<ConditionalOperator>(cond)) { + Expr *trueExpr = condOp->getTrueExpr(); + Expr *falseExpr = condOp->getFalseExpr(); + mlir::Value condV = emitOpOnBoolExpr(loc, condOp->getCond()); + + mlir::Value ternaryOpRes = + builder + .create<cir::TernaryOp>( + loc, condV, /*thenBuilder=*/ + [this, trueExpr](mlir::OpBuilder &b, mlir::Location loc) { + mlir::Value lhs = emitScalarExpr(trueExpr); + b.create<cir::YieldOp>(loc, lhs); + }, + /*elseBuilder=*/ + [this, falseExpr](mlir::OpBuilder &b, mlir::Location loc) { + mlir::Value rhs = emitScalarExpr(falseExpr); + b.create<cir::YieldOp>(loc, rhs); + }) + .getResult(); + + return emitScalarConversion(ternaryOpRes, condOp->getType(), + getContext().BoolTy, condOp->getExprLoc()); } if (isa<CXXThrowExpr>(cond)) { @@ -1394,13 +1573,84 @@ mlir::Value CIRGenFunction::createDummyValue(mlir::Location loc, return builder.createDummyValue(loc, t, alignment); } -/// This creates an alloca and inserts it into the entry block if -/// \p insertIntoFnEntryBlock is true, otherwise it inserts it at the current -/// insertion point of the builder. +//===----------------------------------------------------------------------===// +// CIR builder helpers +//===----------------------------------------------------------------------===// + +Address CIRGenFunction::createMemTemp(QualType ty, mlir::Location loc, + const Twine &name, Address *alloca, + mlir::OpBuilder::InsertPoint ip) { + // FIXME: Should we prefer the preferred type alignment here? + return createMemTemp(ty, getContext().getTypeAlignInChars(ty), loc, name, + alloca, ip); +} + +Address CIRGenFunction::createMemTemp(QualType ty, CharUnits align, + mlir::Location loc, const Twine &name, + Address *alloca, + mlir::OpBuilder::InsertPoint ip) { + Address result = createTempAlloca(convertTypeForMem(ty), align, loc, name, + /*ArraySize=*/nullptr, alloca, ip); + if (ty->isConstantMatrixType()) { + assert(!cir::MissingFeatures::matrixType()); + cgm.errorNYI(loc, "temporary matrix value"); + } + return result; +} + +/// This creates a alloca and inserts it into the entry block of the +/// current region. +Address CIRGenFunction::createTempAllocaWithoutCast( + mlir::Type ty, CharUnits align, mlir::Location loc, const Twine &name, + mlir::Value arraySize, mlir::OpBuilder::InsertPoint ip) { + cir::AllocaOp alloca = ip.isSet() + ? createTempAlloca(ty, loc, name, ip, arraySize) + : createTempAlloca(ty, loc, name, arraySize); + alloca.setAlignmentAttr(cgm.getSize(align)); + return Address(alloca, ty, align); +} + +/// This creates a alloca and inserts it into the entry block. The alloca is +/// casted to default address space if necessary. Address CIRGenFunction::createTempAlloca(mlir::Type ty, CharUnits align, mlir::Location loc, const Twine &name, - bool insertIntoFnEntryBlock) { - mlir::Value alloca = - emitAlloca(name.str(), ty, loc, align, insertIntoFnEntryBlock); - return Address(alloca, ty, align); + mlir::Value arraySize, + Address *allocaAddr, + mlir::OpBuilder::InsertPoint ip) { + Address alloca = + createTempAllocaWithoutCast(ty, align, loc, name, arraySize, ip); + if (allocaAddr) + *allocaAddr = alloca; + mlir::Value v = alloca.getPointer(); + // Alloca always returns a pointer in alloca address space, which may + // be different from the type defined by the language. For example, + // in C++ the auto variables are in the default address space. Therefore + // cast alloca to the default address space when necessary. + assert(!cir::MissingFeatures::addressSpace()); + return Address(v, ty, align); +} + +/// This creates an alloca and inserts it into the entry block if \p ArraySize +/// is nullptr, otherwise inserts it at the current insertion point of the +/// builder. +cir::AllocaOp CIRGenFunction::createTempAlloca(mlir::Type ty, + mlir::Location loc, + const Twine &name, + mlir::Value arraySize, + bool insertIntoFnEntryBlock) { + return cast<cir::AllocaOp>(emitAlloca(name.str(), ty, loc, CharUnits(), + insertIntoFnEntryBlock, arraySize) + .getDefiningOp()); +} + +/// This creates an alloca and inserts it into the provided insertion point +cir::AllocaOp CIRGenFunction::createTempAlloca(mlir::Type ty, + mlir::Location loc, + const Twine &name, + mlir::OpBuilder::InsertPoint ip, + mlir::Value arraySize) { + assert(ip.isSet() && "Insertion point is not set"); + return cast<cir::AllocaOp>( + emitAlloca(name.str(), ty, loc, CharUnits(), ip, arraySize) + .getDefiningOp()); } diff --git a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp index b375bcf2f483f..56d7ea3884ba7 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp @@ -155,8 +155,7 @@ void AggExprEmitter::emitArrayInit(Address destPtr, cir::ArrayType arrayTy, // Allocate the temporary variable // to store the pointer to first unitialized element const Address tmpAddr = cgf.createTempAlloca( - cirElementPtrType, cgf.getPointerAlign(), loc, "arrayinit.temp", - /*insertIntoFnEntryBlock=*/false); + cirElementPtrType, cgf.getPointerAlign(), loc, "arrayinit.temp"); LValue tmpLV = cgf.makeAddrLValue(tmpAddr, elementPtrType); cgf.emitStoreThroughLValue(RValue::get(element), tmpLV); @@ -274,3 +273,11 @@ void AggExprEmitter::visitCXXParenListOrInitListExpr( void CIRGenFunction::emitAggExpr(const Expr *e, AggValueSlot slot) { AggExprEmitter(*this, slot).Visit(const_cast<Expr *>(e)); } + +LValue CIRGenFunction::emitAggExprToLValue(const Expr *e) { + assert(hasAggregateEvaluationKind(e->getType()) && "Invalid argument!"); + Address temp = createMemTemp(e->getType(), getLoc(e->getSourceRange())); + LValue lv = makeAddrLValue(temp, e->getType()); + emitAggExpr(e, AggValueSlot::forLValue(lv)); + return lv; +} diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp index 058015ca55729..1038bc7e37683 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -307,6 +307,8 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { } mlir::Value VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *e); + mlir::Value + VisitAbstractConditionalOperator(const AbstractConditionalOperator *e); // Unary Operators. mlir::Value VisitUnaryPostDec(const UnaryOperator *e) { @@ -905,6 +907,174 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { // NOTE: We don't need to EnsureInsertPoint() like LLVM codegen. return Visit(e->getRHS()); } + + mlir::Value VisitBinLAnd(const clang::BinaryOperator *e) { + if (e->getType()->isVectorType()) { + assert(!cir::MissingFeatures::vectorType()); + return {}; + } + + bool instrumentRegions = cgf.cgm.getCodeGenOpts().hasProfileClangInstr(); + mlir::Type resTy = cgf.convertType(e->getType()); + mlir::Location loc = cgf.getLoc(e->getExprLoc()); + + // If we have 0 && RHS, see if we can elide RHS, if so, just return 0. + // If we have 1 && X, just emit X without inserting the control flow. + bool lhsCondVal; + if (cgf.constantFoldsToSimpleInteger(e->getLHS(), lhsCondVal)) { + if (lhsCondVal) { // If we have 1 && X, just emit X. + + mlir::Value rhsCond = cgf.evaluateExprAsBool(e->getRHS()); + + if (instrumentRegions) { + assert(!cir::MissingFeatures::instrumenation()); + cgf.cgm.errorNYI(e->getExprLoc(), "instrumenation"); + } + // ZExt result to int or bool. + return builder.createZExtOrBitCast(rhsCond.getLoc(), rhsCond, resTy); + } + // 0 && RHS: If it is safe, just elide the RHS, and return 0/false. + if (!cgf.containsLabel(e->getRHS())) + return builder.getNullValue(resTy, loc); + } + + CIRGenFunction::ConditionalEvaluation eval(cgf); + + mlir::Value lhsCondV = cgf.evaluateExprAsBool(e->getLHS()); + auto resOp = builder.create<cir::TernaryOp>( + loc, lhsCondV, /*trueBuilder=*/ + [&](mlir::OpBuilder &b, mlir::Location loc) { + CIRGenFunction::LexicalScope lexScope{cgf, loc, + b.getInsertionBlock()}; + cgf.curLexScope->setAsTernary(); + mlir::Value rhsCondV = cgf.evaluateExprAsBool(e->getRHS()); + auto res = b.create<cir::TernaryOp>( + loc, rhsCondV, /*trueBuilder*/ + [&](mlir::OpBuilder &b, mlir::Location loc) { + CIRGenFunction::LexicalScope lexScope{cgf, loc, + b.getInsertionBlock()}; + cgf.curLexScope->setAsTernary(); + auto res = + b.create<cir::ConstantOp>(loc, builder.getTrueAttr()); + b.create<cir::YieldOp>(loc, res.getRes()); + }, + /*falseBuilder*/ + [&](mlir::OpBuilder &b, mlir::Location loc) { + CIRGenFunction::LexicalScope lexScope{cgf, loc, + b.getInsertionBlock()}; + cgf.curLexScope->setAsTernary(); + auto res = + b.create<cir::ConstantOp>(loc, builder.getFalseAttr()); + b.create<cir::YieldOp>(loc, res.getRes()); + }); + b.create<cir::YieldOp>(loc, res.getResult()); + }, + /*falseBuilder*/ + [&](mlir::OpBuilder &b, mlir::Location loc) { + CIRGenFunction::LexicalScope lexScope{cgf, loc, + b.getInsertionBlock()}; + cgf.curLexScope->setAsTernary(); + auto res = b.create<cir::ConstantOp>(loc, builder.getFalseAttr()); + b.create<cir::YieldOp>(loc, res.getRes()); + }); + return builder.createZExtOrBitCast(resOp.getLoc(), resOp.getResult(), + resTy); + } + + mlir::Value VisitBinLOr(const clang::BinaryOperator *e) { + if (e->getType()->isVectorType()) { + assert(!cir::MissingFeatures::vectorType()); + return {}; + } + + bool instrumentRegions = cgf.cgm.getCodeGenOpts().hasProfileClangInstr(); + mlir::Type resTy = cgf.convertType(e->getType()); + mlir::Location loc = cgf.getLoc(e->getExprLoc()); + + // If we have 1 || RHS, see if we can elide RHS, if so, just return 1. + // If we have 0 || X, just emit X without inserting the control flow. + bool lhsCondVal; + if (cgf.constantFoldsToSimpleInteger(e->getLHS(), lhsCondVal)) { + if (!lhsCondVal) { // If we have 0 || X, just emit X. + + mlir::Value rhsCond = cgf.evaluateExprAsBool(e->getRHS()); + + if (instrumentRegions) { + assert(!cir::MissingFeatures::instrumenation()); + cgf.cgm.errorNYI(e->getExprLoc(), "instrumenation"); + } + // ZExt result to int or bool. + return builder.createZExtOrBitCast(rhsCond.getLoc(), rhsCond, resTy); + } + // 1 || RHS: If it is safe, just elide the RHS, and return 1/true. + if (!cgf.containsLabel(e->getRHS())) { + if (auto intTy = mlir::dyn_cast<cir::IntType>(resTy)) + return builder.getConstantInt(loc, intTy, 1); + return builder.getBool(true, loc); + } + } + + CIRGenFunction::ConditionalEvaluation eval(cgf); + + mlir::Value lhsCondV = cgf.evaluateExprAsBool(e->getLHS()); + auto resOp = builder.create<cir::TernaryOp>( + loc, lhsCondV, /*trueBuilder=*/ + [&](mlir::OpBuilder &b, mlir::Location loc) { + CIRGenFunction::LexicalScope lexScope{cgf, loc, + b.getInsertionBlock()}; + cgf.curLexScope->setAsTernary(); + auto res = b.create<cir::ConstantOp>(loc, builder.getTrueAttr()); + b.create<cir::YieldOp>(loc, res.getRes()); + }, + /*falseBuilder*/ + [&](mlir::OpBuilder &b, mlir::Location loc) { + CIRGenFunction::LexicalScope lexScope{cgf, loc, + b.getInsertionBlock()}; + cgf.curLexScope->setAsTernary(); + mlir::Value rhsCondV = cgf.evaluateExprAsBool(e->getRHS()); + auto res = b.create<cir::TernaryOp>( + loc, rhsCondV, /*trueBuilder*/ + [&](mlir::OpBuilder &b, mlir::Location loc) { + SmallVector<mlir::Location, 2> locs; + if (mlir::isa<mlir::FileLineColLoc>(loc)) { + locs.push_back(loc); + locs.push_back(loc); + } else if (mlir::isa<mlir::FusedLoc>(loc)) { + auto fusedLoc = mlir::cast<mlir::FusedLoc>(loc); + locs.push_back(fusedLoc.getLocations()[0]); + locs.push_back(fusedLoc.getLocations()[1]); + } + CIRGenFunction::LexicalScope lexScope{cgf, loc, + b.getInsertionBlock()}; + cgf.curLexScope->setAsTernary(); + auto res = + b.create<cir::ConstantOp>(loc, builder.getTrueAttr()); + b.create<cir::YieldOp>(loc, res.getRes()); + }, + /*falseBuilder*/ + [&](mlir::OpBuilder &b, mlir::Location loc) { + SmallVector<mlir::Location, 2> locs; + if (mlir::isa<mlir::FileLineColLoc>(loc)) { + locs.push_back(loc); + locs.push_back(loc); + } else if (mlir::isa<mlir::FusedLoc>(loc)) { + auto fusedLoc = mlir::cast<mlir::FusedLoc>(loc); + locs.push_back(fusedLoc.getLocations()[0]); + locs.push_back(fusedLoc.getLocations()[1]); + } + CIRGenFunction::LexicalScope lexScope{cgf, loc, + b.getInsertionBlock()}; + cgf.curLexScope->setAsTernary(); + auto res = + b.create<cir::ConstantOp>(loc, builder.getFalseAttr()); + b.create<cir::YieldOp>(loc, res.getRes()); + }); + b.create<cir::YieldOp>(loc, res.getResult()); + }); + + return builder.createZExtOrBitCast(resOp.getLoc(), resOp.getResult(), + resTy); + } }; LValue ScalarExprEmitter::emitCompoundAssignLValue( @@ -1799,6 +1969,170 @@ mlir::Value ScalarExprEmitter::VisitUnaryExprOrTypeTraitExpr( cgf.cgm.UInt64Ty, e->EvaluateKnownConstInt(cgf.getContext()))); } +/// Return true if the specified expression is cheap enough and side-effect-free +/// enough to evaluate unconditionally instead of conditionally. This is used +/// to convert control flow into selects in some cases. +/// TODO(cir): can be shared with LLVM codegen. +static bool isCheapEnoughToEvaluateUnconditionally(const Expr *e, + CIRGenFunction &cgf) { + // Anything that is an integer or floating point constant is fine. + return e->IgnoreParens()->isEvaluatable(cgf.getContext()); + + // Even non-volatile automatic variables can't be evaluated unconditionally. + // Referencing a thread_local may cause non-trivial initialization work to + // occur. If we're inside a lambda and one of the variables is from the scope + // outside the lambda, that function may have returned already. Reading its + // locals is a bad idea. Also, these reads may introduce races there didn't + // exist in the source-level program. +} + +mlir::Value ScalarExprEmitter::VisitAbstractConditionalOperator( + const AbstractConditionalOperator *e) { + CIRGenBuilderTy &builder = cgf.getBuilder(); + mlir::Location loc = cgf.getLoc(e->getSourceRange()); + ignoreResultAssign = false; + + // Bind the common expression if necessary. + CIRGenFunction::OpaqueValueMapping binding(cgf, e); + + Expr *condExpr = e->getCond(); + Expr *lhsExpr = e->getTrueExpr(); + Expr *rhsExpr = e->getFalseExpr(); + + // If the condition constant folds and can be elided, try to avoid emitting + // the condition and the dead arm. + bool condExprBool; + if (cgf.constantFoldsToSimpleInteger(condExpr, condExprBool)) { + Expr *live = lhsExpr, *dead = rhsExpr; + if (!condExprBool) + std::swap(live, dead); + + // If the dead side doesn't have labels we need, just emit the Live part. + if (!cgf.containsLabel(dead)) { + if (condExprBool) + assert(!cir::MissingFeatures::incrementProfileCounter()); + mlir::Value result = Visit(live); + + // If the live part is a throw expression, it acts like it has a void + // type, so evaluating it returns a null Value. However, a conditional + // with non-void type must return a non-null Value. + if (!result && !e->getType()->isVoidType()) { + cgf.cgm.errorNYI(e->getSourceRange(), + "throw expression in conditional operator"); + result = {}; + } + + return result; + } + } + + // OpenCL: If the condition is a vector, we can treat this condition like + // the select function. + if ((cgf.getLangOpts().OpenCL && condExpr->getType()->isVectorType()) || + condExpr->getType()->isExtVectorType()) { + assert(!cir::MissingFeatures::vectorType()); + cgf.cgm.errorNYI(e->getSourceRange(), "vector ternary op"); + } + + if (condExpr->getType()->isVectorType() || + condExpr->getType()->isSveVLSBuiltinType()) { + assert(!cir::MissingFeatures::vecTernaryOp()); + cgf.cgm.errorNYI(e->getSourceRange(), "vector ternary op"); + return {}; + } + + // If this is a really simple expression (like x ? 4 : 5), emit this as a + // select instead of as control flow. We can only do this if it is cheap and + // safe to evaluate the LHS and RHS unconditionally. + if (isCheapEnoughToEvaluateUnconditionally(lhsExpr, cgf) && + isCheapEnoughToEvaluateUnconditionally(rhsExpr, cgf)) { + bool lhsIsVoid = false; + mlir::Value condV = cgf.evaluateExprAsBool(condExpr); + assert(!cir::MissingFeatures::incrementProfileCounter()); + + return builder + .create<cir::TernaryOp>( + loc, condV, /*thenBuilder=*/ + [&](mlir::OpBuilder &b, mlir::Location loc) { + mlir::Value lhs = Visit(lhsExpr); + if (!lhs) { + lhs = builder.getNullValue(cgf.VoidTy, loc); + lhsIsVoid = true; + } + builder.create<cir::YieldOp>(loc, lhs); + }, + /*elseBuilder=*/ + [&](mlir::OpBuilder &b, mlir::Location loc) { + mlir::Value rhs = Visit(rhsExpr); + if (lhsIsVoid) { + assert(!rhs && "lhs and rhs types must match"); + rhs = builder.getNullValue(cgf.VoidTy, loc); + } + builder.create<cir::YieldOp>(loc, rhs); + }) + .getResult(); + } + + mlir::Value condV = cgf.emitOpOnBoolExpr(loc, condExpr); + CIRGenFunction::ConditionalEvaluation eval(cgf); + SmallVector<mlir::OpBuilder::InsertPoint, 2> insertPoints{}; + mlir::Type yieldTy{}; + + auto emitBranch = [&](mlir::OpBuilder &b, mlir::Location loc, Expr *expr) { + CIRGenFunction::LexicalScope lexScope{cgf, loc, b.getInsertionBlock()}; + cgf.curLexScope->setAsTernary(); + + assert(!cir::MissingFeatures::incrementProfileCounter()); + eval.begin(cgf); + mlir::Value branch = Visit(expr); + eval.end(cgf); + + if (branch) { + yieldTy = branch.getType(); + b.create<cir::YieldOp>(loc, branch); + } else { + // If LHS or RHS is a throw or void expression we need to patch + // arms as to properly match yield types. + insertPoints.push_back(b.saveInsertionPoint()); + } + }; + + mlir::Value result = builder + .create<cir::TernaryOp>( + loc, condV, + /*trueBuilder=*/ + [&](mlir::OpBuilder &b, mlir::Location loc) { + emitBranch(b, loc, lhsExpr); + }, + /*falseBuilder=*/ + [&](mlir::OpBuilder &b, mlir::Location loc) { + emitBranch(b, loc, rhsExpr); + }) + .getResult(); + + if (!insertPoints.empty()) { + // If both arms are void, so be it. + if (!yieldTy) + yieldTy = cgf.VoidTy; + + // Insert required yields. + for (mlir::OpBuilder::InsertPoint &toInsert : insertPoints) { + mlir::OpBuilder::InsertionGuard guard(builder); + builder.restoreInsertionPoint(toInsert); + + // Block does not return: build empty yield. + if (mlir::isa<cir::VoidType>(yieldTy)) { + builder.create<cir::YieldOp>(loc); + } else { // Block returns: set null yield value. + mlir::Value op0 = builder.getNullValue(yieldTy, loc); + builder.create<cir::YieldOp>(loc, op0); + } + } + } + + return result; +} + mlir::Value CIRGenFunction::emitScalarPrePostIncDec(const UnaryOperator *e, LValue lv, bool isInc, bool isPre) { diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index 4335214d77b7d..2f527303e621b 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -201,6 +201,20 @@ bool CIRGenFunction::constantFoldsToSimpleInteger(const Expr *cond, return true; } +/// If the specified expression does not fold +/// to a constant, or if it does but contains a label, return false. If it +/// constant folds return true and set the boolean result in `resultBool`. +bool CIRGenFunction::constantFoldsToSimpleInteger(const Expr *cond, + bool &resultBool, + bool allowLabels) { + llvm::APSInt resultInt; + if (!constantFoldsToSimpleInteger(cond, resultInt, allowLabels)) + return false; + + resultBool = resultInt.getBoolValue(); + return true; +} + void CIRGenFunction::emitAndUpdateRetAlloca(QualType type, mlir::Location loc, CharUnits alignment) { if (!type->isVoidType()) { diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index 74f2e4043933d..fc103f8d9f895 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -108,6 +108,152 @@ class CIRGenFunction : public CIRGenTypeCache { mlir::MLIRContext &getMLIRContext() { return cgm.getMLIRContext(); } + // --------------------- + // Opaque value handling + // --------------------- + + /// Keeps track of the current set of opaque value expressions. + llvm::DenseMap<const OpaqueValueExpr *, LValue> opaqueLValues; + llvm::DenseMap<const OpaqueValueExpr *, RValue> opaqueRValues; + + // This keeps track of the associated size for each VLA type. + // We track this by the size expression rather than the type itself because + // in certain situations, like a const qualifier applied to an VLA typedef, + // multiple VLA types can share the same size expression. + // FIXME: Maybe this could be a stack of maps that is pushed/popped as we + // enter/leave scopes. + llvm::DenseMap<const Expr *, mlir::Value> vlaSizeMap; + +public: + /// A non-RAII class containing all the information about a bound + /// opaque value. OpaqueValueMapping, below, is a RAII wrapper for + /// this which makes individual mappings very simple; using this + /// class directly is useful when you have a variable number of + /// opaque values or don't want the RAII functionality for some + /// reason. + class OpaqueValueMappingData { + const OpaqueValueExpr *opaqueValue; + bool boundLValue; + + OpaqueValueMappingData(const OpaqueValueExpr *ov, bool boundLValue) + : opaqueValue(ov), boundLValue(boundLValue) {} + + public: + OpaqueValueMappingData() : opaqueValue(nullptr) {} + + static bool shouldBindAsLValue(const Expr *expr) { + // gl-values should be bound as l-values for obvious reasons. + // Records should be bound as l-values because IR generation + // always keeps them in memory. Expressions of function type + // act exactly like l-values but are formally required to be + // r-values in C. + return expr->isGLValue() || expr->getType()->isFunctionType() || + hasAggregateEvaluationKind(expr->getType()); + } + + static OpaqueValueMappingData + bind(CIRGenFunction &cgf, const OpaqueValueExpr *ov, const Expr *e) { + if (shouldBindAsLValue(ov)) + return bind(cgf, ov, cgf.emitLValue(e)); + return bind(cgf, ov, cgf.emitAnyExpr(e)); + } + + static OpaqueValueMappingData + bind(CIRGenFunction &cgf, const OpaqueValueExpr *ov, const LValue &lv) { + assert(shouldBindAsLValue(ov)); + cgf.opaqueLValues.insert(std::make_pair(ov, lv)); + return OpaqueValueMappingData(ov, true); + } + + static OpaqueValueMappingData + bind(CIRGenFunction &cgf, const OpaqueValueExpr *ov, const RValue &rv) { + assert(!shouldBindAsLValue(ov)); + cgf.opaqueRValues.insert(std::make_pair(ov, rv)); + + OpaqueValueMappingData data(ov, false); + + // Work around an extremely aggressive peephole optimization in + // EmitScalarConversion which assumes that all other uses of a + // value are extant. + assert(!cir::MissingFeatures::peepholeProtection() && "NYI"); + return data; + } + + bool isValid() const { return opaqueValue != nullptr; } + void clear() { opaqueValue = nullptr; } + + void unbind(CIRGenFunction &cgf) { + assert(opaqueValue && "no data to unbind!"); + + if (boundLValue) { + cgf.opaqueLValues.erase(opaqueValue); + } else { + cgf.opaqueRValues.erase(opaqueValue); + assert(!cir::MissingFeatures::peepholeProtection() && "NYI"); + } + } + }; + + /// An RAII object to set (and then clear) a mapping for an OpaqueValueExpr. + class OpaqueValueMapping { + CIRGenFunction &cgf; + OpaqueValueMappingData data; + + public: + static bool shouldBindAsLValue(const Expr *expr) { + return OpaqueValueMappingData::shouldBindAsLValue(expr); + } + + /// Build the opaque value mapping for the given conditional + /// operator if it's the GNU ?: extension. This is a common + /// enough pattern that the convenience operator is really + /// helpful. + /// + OpaqueValueMapping(CIRGenFunction &cgf, + const AbstractConditionalOperator *op) + : cgf(cgf) { + if (mlir::isa<ConditionalOperator>(op)) + // Leave Data empty. + return; + + const BinaryConditionalOperator *e = + mlir::cast<BinaryConditionalOperator>(op); + data = OpaqueValueMappingData::bind(cgf, e->getOpaqueValue(), + e->getCommon()); + } + + /// Build the opaque value mapping for an OpaqueValueExpr whose source + /// expression is set to the expression the OVE represents. + OpaqueValueMapping(CIRGenFunction &cgf, const OpaqueValueExpr *ov) + : cgf(cgf) { + if (ov) { + assert(ov->getSourceExpr() && "wrong form of OpaqueValueMapping used " + "for OVE with no source expression"); + data = OpaqueValueMappingData::bind(cgf, ov, ov->getSourceExpr()); + } + } + + OpaqueValueMapping(CIRGenFunction &cgf, const OpaqueValueExpr *opaqueValue, + LValue lvalue) + : cgf(cgf), + data(OpaqueValueMappingData::bind(cgf, opaqueValue, lvalue)) {} + + OpaqueValueMapping(CIRGenFunction &cgf, const OpaqueValueExpr *opaqueValue, + RValue rvalue) + : cgf(cgf), + data(OpaqueValueMappingData::bind(cgf, opaqueValue, rvalue)) {} + + void pop() { + data.unbind(cgf); + data.clear(); + } + + ~OpaqueValueMapping() { + if (data.isValid()) + data.unbind(cgf); + } + }; + private: /// Declare a variable in the current scope, return success if the variable /// wasn't declared yet. @@ -217,6 +363,8 @@ class CIRGenFunction : public CIRGenTypeCache { /// the boolean result in Result. bool constantFoldsToBool(const clang::Expr *cond, bool &resultBool, bool allowLabels = false); + bool constantFoldsToSimpleInteger(const clang::Expr *cond, bool &resultBool, + bool allowLabels = false); bool constantFoldsToSimpleInteger(const clang::Expr *cond, llvm::APSInt &resultInt, bool allowLabels = false); @@ -262,7 +410,7 @@ class CIRGenFunction : public CIRGenTypeCache { /// Returns the address of the object within this declaration. /// Note that this does not chase the forwarding pointer for /// __block decls. - Address getObjectAddress(CIRGenFunction &CGF) const { + Address getObjectAddress(CIRGenFunction &cgf) const { if (!IsEscapingByRef) return Addr; @@ -475,6 +623,8 @@ class CIRGenFunction : public CIRGenTypeCache { void emitAggExpr(const clang::Expr *e, AggValueSlot slot); + LValue emitAggExprToLValue(const Expr *e); + /// Emit code to compute the specified expression which can have any type. The /// result is returned as an RValue struct. If this is an aggregate /// expression, the aggloc/agglocvolatile arguments indicate where the result @@ -601,6 +751,8 @@ class CIRGenFunction : public CIRGenTypeCache { void emitCompoundStmtWithoutScope(const clang::CompoundStmt &s); + LValue emitConditionalOperatorLValue(const AbstractConditionalOperator *expr); + void emitDecl(const clang::Decl &d); mlir::LogicalResult emitDeclStmt(const clang::DeclStmt &s); LValue emitDeclRefLValue(const clang::DeclRefExpr *e); @@ -721,12 +873,97 @@ class CIRGenFunction : public CIRGenTypeCache { void emitNullabilityCheck(LValue lhs, mlir::Value rhs, clang::SourceLocation loc); + /// An object to manage conditionally-evaluated expressions. + class ConditionalEvaluation { + mlir::OpBuilder::InsertPoint insertPt; + + public: + ConditionalEvaluation(CIRGenFunction &cgf) + : insertPt(cgf.builder.saveInsertionPoint()) {} + ConditionalEvaluation(mlir::OpBuilder::InsertPoint ip) : insertPt(ip) {} + + void begin(CIRGenFunction &cgf) { + assert(cgf.outermostConditional != this); + if (!cgf.outermostConditional) + cgf.outermostConditional = this; + } + + void end(CIRGenFunction &cgf) { + assert(cgf.outermostConditional != nullptr); + if (cgf.outermostConditional == this) + cgf.outermostConditional = nullptr; + } + + /// Returns the insertion point which will be executed prior to each + /// evaluation of the conditional code. In LLVM OG, this method + /// is called getStartingBlock. + mlir::OpBuilder::InsertPoint getInsertPoint() const { return insertPt; } + }; + + struct ConditionalInfo { + std::optional<LValue> lhs{}, rhs{}; + mlir::Value result{}; + }; + + // Return true if we're currently emitting one branch or the other of a + // conditional expression. + bool isInConditionalBranch() const { return outermostConditional != nullptr; } + + void setBeforeOutermostConditional(mlir::Value value, Address addr) { + assert(isInConditionalBranch()); + { + mlir::OpBuilder::InsertionGuard guard(builder); + builder.restoreInsertionPoint(outermostConditional->getInsertPoint()); + assert(!cir::MissingFeatures::opLoadStoreAlignment()); + // TODO(cir): This store needs to use the alignment of addr + builder.createStore(value.getLoc(), value, addr.getPointer()); + } + } + + // Points to the outermost active conditional control. This is used so that + // we know if a temporary should be destroyed conditionally. + ConditionalEvaluation *outermostConditional = nullptr; + + template <typename FuncTy> + ConditionalInfo emitConditionalBlocks(const AbstractConditionalOperator *e, + const FuncTy &branchGenFunc); + + mlir::Value emitTernaryOnBoolExpr(const clang::Expr *cond, mlir::Location loc, + const clang::Stmt *thenS, + const clang::Stmt *elseS); + /// ---------------------- /// CIR build helpers /// ----------------- public: + cir::AllocaOp createTempAlloca(mlir::Type ty, mlir::Location loc, + const Twine &name = "tmp", + mlir::Value arraySize = nullptr, + bool insertIntoFnEntryBlock = false); + cir::AllocaOp createTempAlloca(mlir::Type ty, mlir::Location loc, + const Twine &name = "tmp", + mlir::OpBuilder::InsertPoint ip = {}, + mlir::Value arraySize = nullptr); Address createTempAlloca(mlir::Type ty, CharUnits align, mlir::Location loc, - const Twine &name, bool insertIntoFnEntryBlock); + const Twine &name = "tmp", + mlir::Value arraySize = nullptr, + Address *alloca = nullptr, + mlir::OpBuilder::InsertPoint ip = {}); + Address createTempAllocaWithoutCast(mlir::Type ty, CharUnits align, + mlir::Location loc, + const Twine &name = "tmp", + mlir::Value arraySize = nullptr, + mlir::OpBuilder::InsertPoint ip = {}); + + /// Create a temporary memory object of the given type, with + /// appropriate alignmen and cast it to the default address space. Returns + /// the original alloca instruction by \p Alloca if it is not nullptr. + Address createMemTemp(QualType t, mlir::Location loc, + const Twine &name = "tmp", Address *alloca = nullptr, + mlir::OpBuilder::InsertPoint ip = {}); + Address createMemTemp(QualType t, CharUnits align, mlir::Location loc, + const Twine &name = "tmp", Address *alloca = nullptr, + mlir::OpBuilder::InsertPoint ip = {}); //===--------------------------------------------------------------------===// // OpenACC Emission diff --git a/clang/lib/CIR/CodeGen/CIRGenValue.h b/clang/lib/CIR/CodeGen/CIRGenValue.h index 3feadfaf56354..56177c948df94 100644 --- a/clang/lib/CIR/CodeGen/CIRGenValue.h +++ b/clang/lib/CIR/CodeGen/CIRGenValue.h @@ -164,6 +164,7 @@ class LValue { clang::Qualifiers &getQuals() { return quals; } LValueBaseInfo getBaseInfo() const { return baseInfo; } + void setBaseInfo(LValueBaseInfo info) { baseInfo = info; } static LValue makeAddr(Address address, clang::QualType t, LValueBaseInfo baseInfo) { diff --git a/clang/test/CIR/CodeGen/binop.c b/clang/test/CIR/CodeGen/binop.c new file mode 100644 index 0000000000000..280fd29b067f9 --- /dev/null +++ b/clang/test/CIR/CodeGen/binop.c @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s + +void conditionalResultIimplicitCast(int a, int b, float f) { + // Should implicit cast back to int. + int x = a && b; + // CHECK: %[[#INT:]] = cir.ternary + // CHECK: %{{.+}} = cir.cast(bool_to_int, %[[#INT]] : !cir.bool), !s32i + float y = f && f; + // CHECK: %[[#BOOL:]] = cir.ternary + // CHECK: %[[#INT:]] = cir.cast(bool_to_int, %[[#BOOL]] : !cir.bool), !s32i + // CHECK: %{{.+}} = cir.cast(int_to_float, %[[#INT]] : !s32i), !cir.float +} diff --git a/clang/test/CIR/CodeGen/binop.cpp b/clang/test/CIR/CodeGen/binop.cpp index dbd17fb7ba83d..cd53c6f0bbcf1 100644 --- a/clang/test/CIR/CodeGen/binop.cpp +++ b/clang/test/CIR/CodeGen/binop.cpp @@ -540,3 +540,228 @@ void long_shift_example(long long a, short b) { // OGCG: store i64 %[[SHL]], ptr %[[X]] // OGCG: ret void + +void b1(bool a, bool b) { + bool x = a && b; + x = x || b; +} + +// CIR-LABEL: cir.func @_Z2b1bb( +// CIR-SAME: %[[ARG0:.*]]: !cir.bool {{.*}}, %[[ARG1:.*]]: !cir.bool {{.*}}) +// CIR: [[A:%[0-9]+]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["a", init] +// CIR: [[B:%[0-9]+]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["b", init] +// CIR: [[X:%[0-9]+]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["x", init] +// CIR: cir.store %[[ARG0]], [[A]] : !cir.bool, !cir.ptr<!cir.bool> +// CIR: cir.store %[[ARG1]], [[B]] : !cir.bool, !cir.ptr<!cir.bool> +// CIR: [[AVAL:%[0-9]+]] = cir.load [[A]] : !cir.ptr<!cir.bool>, !cir.bool +// CIR: [[RES1:%[0-9]+]] = cir.ternary([[AVAL]], true { +// CIR: [[BVAL:%[0-9]+]] = cir.load [[B]] : !cir.ptr<!cir.bool>, !cir.bool +// CIR: [[INNER1:%[0-9]+]] = cir.ternary([[BVAL]], true { +// CIR: {{%[0-9]+}} = cir.const #true +// CIR: cir.yield {{%[0-9]+}} : !cir.bool +// CIR: }, false { +// CIR: {{%[0-9]+}} = cir.const #false +// CIR: cir.yield {{%[0-9]+}} : !cir.bool +// CIR: }) : (!cir.bool) -> !cir.bool +// CIR: cir.yield [[INNER1]] : !cir.bool +// CIR: }, false { +// CIR: {{%[0-9]+}} = cir.const #false +// CIR: cir.yield {{%[0-9]+}} : !cir.bool +// CIR: }) : (!cir.bool) -> !cir.bool +// CIR: cir.store [[RES1]], [[X]] : !cir.bool, !cir.ptr<!cir.bool> +// CIR: [[XVAL:%[0-9]+]] = cir.load [[X]] : !cir.ptr<!cir.bool>, !cir.bool +// CIR: [[RES2:%[0-9]+]] = cir.ternary([[XVAL]], true { +// CIR: {{%[0-9]+}} = cir.const #true +// CIR: cir.yield {{%[0-9]+}} : !cir.bool +// CIR: }, false { +// CIR: [[BVAL2:%[0-9]+]] = cir.load [[B]] : !cir.ptr<!cir.bool>, !cir.bool +// CIR: [[INNER2:%[0-9]+]] = cir.ternary([[BVAL2]], true { +// CIR: {{%[0-9]+}} = cir.const #true +// CIR: cir.yield {{%[0-9]+}} : !cir.bool +// CIR: }, false { +// CIR: {{%[0-9]+}} = cir.const #false +// CIR: cir.yield {{%[0-9]+}} : !cir.bool +// CIR: }) : (!cir.bool) -> !cir.bool +// CIR: cir.yield [[INNER2]] : !cir.bool +// CIR: }) : (!cir.bool) -> !cir.bool +// CIR: cir.store [[RES2]], [[X]] : !cir.bool, !cir.ptr<!cir.bool> +// CIR: cir.return + +// LLVM-LABEL: define void @_Z2b1bb( +// LLVM-SAME: i1 %[[ARG0:.*]], i1 %[[ARG1:.*]]) +// LLVM: %[[A_ADDR:.*]] = alloca i8 +// LLVM: %[[B_ADDR:.*]] = alloca i8 +// LLVM: %[[X:.*]] = alloca i8 +// LLVM: %[[ZEXT0:.*]] = zext i1 %[[ARG0]] to i8 +// LLVM: store i8 %[[ZEXT0]], ptr %[[A_ADDR]] +// LLVM: %[[ZEXT1:.*]] = zext i1 %[[ARG1]] to i8 +// LLVM: store i8 %[[ZEXT1]], ptr %[[B_ADDR]] +// LLVM: %[[A_VAL:.*]] = load i8, ptr %[[A_ADDR]] +// LLVM: %[[A_BOOL:.*]] = trunc i8 %[[A_VAL]] to i1 +// LLVM: br i1 %[[A_BOOL]], label %[[AND_LHS_TRUE:.*]], label %[[AND_END:.*]] +// LLVM: [[AND_LHS_TRUE]]: +// LLVM: %[[B_VAL:.*]] = load i8, ptr %[[B_ADDR]] +// LLVM: %[[B_BOOL:.*]] = trunc i8 %[[B_VAL]] to i1 +// LLVM: br i1 %[[B_BOOL]], label %[[B_TRUE:.*]], label %[[B_FALSE:.*]] +// LLVM: [[B_TRUE]]: +// LLVM: br label %[[AND_RHS_END:.*]] +// LLVM: [[B_FALSE]]: +// LLVM: br label %[[AND_RHS_END]] +// LLVM: [[AND_RHS_END]]: +// LLVM: %[[RHS_PHI:.*]] = phi i1 [ false, %[[B_FALSE]] ], [ true, %[[B_TRUE]] ] +// LLVM: br label %[[AND_DONE:.*]] +// LLVM: [[AND_DONE]]: +// LLVM: br label %[[MERGE_BB:.*]] +// LLVM: [[AND_END]]: +// LLVM: %[[AND_RESULT:.*]] = phi i1 [ false, %[[PRED1:.*]] ], [ %[[RHS_PHI]], %[[PRED2:.*]] ] +// LLVM: br label %[[STORE_X:.*]] +// LLVM: [[STORE_X]]: +// LLVM: %[[ZEXT_AND:.*]] = zext i1 %[[AND_RESULT]] to i8 +// LLVM: store i8 %[[ZEXT_AND]], ptr %[[X]] +// LLVM: %[[X_VAL:.*]] = load i8, ptr %[[X]] +// LLVM: %[[X_BOOL:.*]] = trunc i8 %[[X_VAL]] to i1 +// LLVM: br i1 %[[X_BOOL]], label %[[OR_TRUE:.*]], label %[[OR_RHS:.*]] +// LLVM: [[OR_TRUE]]: +// LLVM: br label %[[OR_END:.*]] +// LLVM: [[OR_RHS]]: +// LLVM: %[[B_VAL2:.*]] = load i8, ptr %[[B_ADDR]] +// LLVM: %[[B_BOOL2:.*]] = trunc i8 %[[B_VAL2]] to i1 +// LLVM: br i1 %[[B_BOOL2]], label %[[B_TRUE2:.*]], label %[[B_FALSE2:.*]] +// LLVM: [[B_TRUE2]]: +// LLVM: br label %[[OR_RHS_END:.*]] +// LLVM: [[B_FALSE2]]: +// LLVM: br label %[[OR_RHS_END]] +// LLVM: [[OR_RHS_END]]: +// LLVM: %[[RHS_PHI2:.*]] = phi i1 [ false, %[[B_FALSE2]] ], [ true, %[[B_TRUE2]] ] +// LLVM: br label %[[OR_RHS_DONE:.*]] +// LLVM: [[OR_RHS_DONE]]: +// LLVM: br label %[[OR_END]] +// LLVM: [[OR_END]]: +// LLVM: %[[OR_RESULT:.*]] = phi i1 [ %[[RHS_PHI2]], %[[OR_RHS_DONE]] ], [ true, %[[OR_TRUE]] ] +// LLVM: br label %[[FINAL_STORE:.*]] +// LLVM: [[FINAL_STORE]]: +// LLVM: %[[ZEXT_OR:.*]] = zext i1 %[[OR_RESULT]] to i8 +// LLVM: store i8 %[[ZEXT_OR]], ptr %[[X]] +// LLVM: ret void + + +void b3(int a, int b, int c, int d) { + bool x = (a == b) && (c == d); + x = (a == b) || (c == d); +} + +// CIR-LABEL: cir.func @_Z2b3iiii( +// CIR-SAME: %[[ARG0:.*]]: !s32i {{.*}}, %[[ARG1:.*]]: !s32i {{.*}}, %[[ARG2:.*]]: !s32i {{.*}}, %[[ARG3:.*]]: !s32i {{.*}}) +// CIR: [[A:%[0-9]+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["a", init] +// CIR: [[B:%[0-9]+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["b", init] +// CIR: [[C:%[0-9]+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["c", init] +// CIR: [[D:%[0-9]+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["d", init] +// CIR: [[X:%[0-9]+]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["x", init] +// CIR: cir.store %[[ARG0]], [[A]] : !s32i, !cir.ptr<!s32i> +// CIR: cir.store %[[ARG1]], [[B]] : !s32i, !cir.ptr<!s32i> +// CIR: cir.store %[[ARG2]], [[C]] : !s32i, !cir.ptr<!s32i> +// CIR: cir.store %[[ARG3]], [[D]] : !s32i, !cir.ptr<!s32i> +// CIR: [[AVAL1:%[0-9]+]] = cir.load [[A]] : !cir.ptr<!s32i>, !s32i +// CIR: [[BVAL1:%[0-9]+]] = cir.load [[B]] : !cir.ptr<!s32i>, !s32i +// CIR: [[CMP1:%[0-9]+]] = cir.cmp(eq, [[AVAL1]], [[BVAL1]]) : !s32i, !cir.bool +// CIR: [[AND_RESULT:%[0-9]+]] = cir.ternary([[CMP1]], true { +// CIR: [[CVAL1:%[0-9]+]] = cir.load [[C]] : !cir.ptr<!s32i>, !s32i +// CIR: [[DVAL1:%[0-9]+]] = cir.load [[D]] : !cir.ptr<!s32i>, !s32i +// CIR: [[CMP2:%[0-9]+]] = cir.cmp(eq, [[CVAL1]], [[DVAL1]]) : !s32i, !cir.bool +// CIR: [[INNER1:%[0-9]+]] = cir.ternary([[CMP2]], true { +// CIR: {{%[0-9]+}} = cir.const #true +// CIR: cir.yield {{%[0-9]+}} : !cir.bool +// CIR: }, false { +// CIR: {{%[0-9]+}} = cir.const #false +// CIR: cir.yield {{%[0-9]+}} : !cir.bool +// CIR: }) : (!cir.bool) -> !cir.bool +// CIR: cir.yield [[INNER1]] : !cir.bool +// CIR: }, false { +// CIR: {{%[0-9]+}} = cir.const #false +// CIR: cir.yield {{%[0-9]+}} : !cir.bool +// CIR: }) : (!cir.bool) -> !cir.bool +// CIR: cir.store [[AND_RESULT]], [[X]] : !cir.bool, !cir.ptr<!cir.bool> +// CIR: [[AVAL2:%[0-9]+]] = cir.load [[A]] : !cir.ptr<!s32i>, !s32i +// CIR: [[BVAL2:%[0-9]+]] = cir.load [[B]] : !cir.ptr<!s32i>, !s32i +// CIR: [[CMP3:%[0-9]+]] = cir.cmp(eq, [[AVAL2]], [[BVAL2]]) : !s32i, !cir.bool +// CIR: [[OR_RESULT:%[0-9]+]] = cir.ternary([[CMP3]], true { +// CIR: {{%[0-9]+}} = cir.const #true +// CIR: cir.yield {{%[0-9]+}} : !cir.bool +// CIR: }, false { +// CIR: [[CVAL2:%[0-9]+]] = cir.load [[C]] : !cir.ptr<!s32i>, !s32i +// CIR: [[DVAL2:%[0-9]+]] = cir.load [[D]] : !cir.ptr<!s32i>, !s32i +// CIR: [[CMP4:%[0-9]+]] = cir.cmp(eq, [[CVAL2]], [[DVAL2]]) : !s32i, !cir.bool +// CIR: [[INNER2:%[0-9]+]] = cir.ternary([[CMP4]], true { +// CIR: {{%[0-9]+}} = cir.const #true +// CIR: cir.yield {{%[0-9]+}} : !cir.bool +// CIR: }, false { +// CIR: {{%[0-9]+}} = cir.const #false +// CIR: cir.yield {{%[0-9]+}} : !cir.bool +// CIR: }) : (!cir.bool) -> !cir.bool +// CIR: cir.yield [[INNER2]] : !cir.bool +// CIR: }) : (!cir.bool) -> !cir.bool +// CIR: cir.store [[OR_RESULT]], [[X]] : !cir.bool, !cir.ptr<!cir.bool> +// CIR: cir.return + +// LLVM-LABEL: define void @_Z2b3iiii( +// LLVM-SAME: i32 %[[ARG0:.*]], i32 %[[ARG1:.*]], i32 %[[ARG2:.*]], i32 %[[ARG3:.*]]) +// LLVM: %[[A_ADDR:.*]] = alloca i32 +// LLVM: %[[B_ADDR:.*]] = alloca i32 +// LLVM: %[[C_ADDR:.*]] = alloca i32 +// LLVM: %[[D_ADDR:.*]] = alloca i32 +// LLVM: %[[X:.*]] = alloca i8 +// LLVM: store i32 %[[ARG0]], ptr %[[A_ADDR]] +// LLVM: store i32 %[[ARG1]], ptr %[[B_ADDR]] +// LLVM: store i32 %[[ARG2]], ptr %[[C_ADDR]] +// LLVM: store i32 %[[ARG3]], ptr %[[D_ADDR]] +// LLVM: %[[A_VAL:.*]] = load i32, ptr %[[A_ADDR]] +// LLVM: %[[B_VAL:.*]] = load i32, ptr %[[B_ADDR]] +// LLVM: %[[CMP1:.*]] = icmp eq i32 %[[A_VAL]], %[[B_VAL]] +// LLVM: br i1 %[[CMP1]], label %[[AND_LHS_TRUE:.*]], label %[[AND_END:.*]] +// LLVM: [[AND_LHS_TRUE]]: +// LLVM: %[[C_VAL:.*]] = load i32, ptr %[[C_ADDR]] +// LLVM: %[[D_VAL:.*]] = load i32, ptr %[[D_ADDR]] +// LLVM: %[[CMP2:.*]] = icmp eq i32 %[[C_VAL]], %[[D_VAL]] +// LLVM: br i1 %[[CMP2]], label %[[CD_TRUE:.*]], label %[[CD_FALSE:.*]] +// LLVM: [[CD_TRUE]]: +// LLVM: br label %[[AND_RHS_END:.*]] +// LLVM: [[CD_FALSE]]: +// LLVM: br label %[[AND_RHS_END]] +// LLVM: [[AND_RHS_END]]: +// LLVM: %[[RHS_PHI:.*]] = phi i1 [ false, %[[CD_FALSE]] ], [ true, %[[CD_TRUE]] ] +// LLVM: br label %[[AND_DONE:.*]] +// LLVM: [[AND_DONE]]: +// LLVM: br label %[[MERGE_BB2:.*]] +// LLVM: [[AND_END]]: +// LLVM: %[[AND_RESULT:.*]] = phi i1 [ false, %[[AND_END]] ], [ %[[RHS_PHI]], %[[AND_DONE]] ] +// LLVM: br label %[[STORE_X:.*]] +// LLVM: [[STORE_X]]: +// LLVM: %[[ZEXT_AND:.*]] = zext i1 %[[AND_RESULT]] to i8 +// LLVM: store i8 %[[ZEXT_AND]], ptr %[[X]] +// LLVM: %[[A_VAL2:.*]] = load i32, ptr %[[A_ADDR]] +// LLVM: %[[B_VAL2:.*]] = load i32, ptr %[[B_ADDR]] +// LLVM: %[[CMP3:.*]] = icmp eq i32 %[[A_VAL2]], %[[B_VAL2]] +// LLVM: br i1 %[[CMP3]], label %[[OR_TRUE:.*]], label %[[OR_RHS:.*]] +// LLVM: [[OR_TRUE]]: +// LLVM: br label %[[OR_END:.*]] +// LLVM: [[OR_RHS]]: +// LLVM: %[[C_VAL2:.*]] = load i32, ptr %[[C_ADDR]] +// LLVM: %[[D_VAL2:.*]] = load i32, ptr %[[D_ADDR]] +// LLVM: %[[CMP4:.*]] = icmp eq i32 %[[C_VAL2]], %[[D_VAL2]] +// LLVM: br i1 %[[CMP4]], label %[[CD_TRUE2:.*]], label %[[CD_FALSE2:.*]] +// LLVM: [[CD_TRUE2]]: +// LLVM: br label %[[OR_RHS_END:.*]] +// LLVM: [[CD_FALSE2]]: +// LLVM: br label %[[OR_RHS_END]] +// LLVM: [[OR_RHS_END]]: +// LLVM: %[[RHS_PHI2:.*]] = phi i1 [ false, %[[CD_FALSE2]] ], [ true, %[[CD_TRUE2]] ] +// LLVM: br label %[[OR_RHS_DONE:.*]] +// LLVM: [[OR_RHS_DONE]]: +// LLVM: br label %[[OR_END]] +// LLVM: [[OR_END]]: +// LLVM: %[[OR_RESULT:.*]] = phi i1 [ %[[RHS_PHI2]], %[[OR_RHS_DONE]] ], [ true, %[[OR_TRUE]] ] +// LLVM: br label %[[FINAL_STORE:.*]] +// LLVM: [[FINAL_STORE]]: +// LLVM: %[[ZEXT_OR:.*]] = zext i1 %[[OR_RESULT]] to i8 +// LLVM: store i8 %[[ZEXT_OR]], ptr %[[X]] +// LLVM: ret void diff --git a/clang/test/CIR/CodeGen/ternary.cpp b/clang/test/CIR/CodeGen/ternary.cpp new file mode 100644 index 0000000000000..28aaa2d4a85d1 --- /dev/null +++ b/clang/test/CIR/CodeGen/ternary.cpp @@ -0,0 +1,120 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s + +int x(int y) { + return y > 0 ? 3 : 5; +} + +// CIR-LABEL: cir.func @_Z1xi( +// CIR-SAME: %[[ARG0:.*]]: !s32i {{.*}}) -> !s32i { +// CIR: [[Y:%[0-9]+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["y", init] +// CIR: [[RETVAL:%[0-9]+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"] +// CIR: cir.store %[[ARG0]], [[Y]] : !s32i, !cir.ptr<!s32i> +// CIR: [[YVAL:%[0-9]+]] = cir.load [[Y]] : !cir.ptr<!s32i>, !s32i +// CIR: [[ZERO:%[0-9]+]] = cir.const #cir.int<0> : !s32i +// CIR: [[CMP:%[0-9]+]] = cir.cmp(gt, [[YVAL]], [[ZERO]]) : !s32i, !cir.bool +// CIR: [[TERNARY_RES:%[0-9]+]] = cir.ternary([[CMP]], true { +// CIR: [[THREE:%[0-9]+]] = cir.const #cir.int<3> : !s32i +// CIR: cir.yield [[THREE]] : !s32i +// CIR: }, false { +// CIR: [[FIVE:%[0-9]+]] = cir.const #cir.int<5> : !s32i +// CIR: cir.yield [[FIVE]] : !s32i +// CIR: }) : (!cir.bool) -> !s32i +// CIR: cir.store [[TERNARY_RES]], [[RETVAL]] : !s32i, !cir.ptr<!s32i> +// CIR: [[RETVAL_VAL:%[0-9]+]] = cir.load [[RETVAL]] : !cir.ptr<!s32i>, !s32i +// CIR: cir.return [[RETVAL_VAL]] : !s32i + +// LLVM-LABEL: define i32 @_Z1xi( +// LLVM-SAME: i32 %[[ARG0:.*]]) +// LLVM: %[[Y:.*]] = alloca i32 +// LLVM: %[[RETVAL:.*]] = alloca i32 +// LLVM: store i32 %[[ARG0]], ptr %[[Y]] +// LLVM: %[[YVAL:.*]] = load i32, ptr %[[Y]] +// LLVM: %[[CMP:.*]] = icmp sgt i32 %[[YVAL]], 0 +// LLVM: br i1 %[[CMP]], label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]] +// LLVM: [[TRUE_BB]]: +// LLVM: br label %[[MERGE_BB:.*]] +// LLVM: [[FALSE_BB]]: +// LLVM: br label %[[MERGE_BB]] +// LLVM: [[MERGE_BB]]: +// LLVM: %[[PHI:.*]] = phi i32 [ 5, %[[FALSE_BB]] ], [ 3, %[[TRUE_BB]] ] +// LLVM: br label %[[FINAL_BB:.*]] +// LLVM: [[FINAL_BB]]: +// LLVM: store i32 %[[PHI]], ptr %[[RETVAL]] +// LLVM: %[[RESULT:.*]] = load i32, ptr %[[RETVAL]] +// LLVM: ret i32 %[[RESULT]] + +int foo(int a, int b) { + if (a < b ? 0 : a) + return -1; + return 0; +} + +// CIR-LABEL: cir.func @_Z3fooii( +// CIR-SAME: %[[ARG0:.*]]: !s32i {{.*}}, %[[ARG1:.*]]: !s32i {{.*}}) -> !s32i { +// CIR: [[A:%[0-9]+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["a", init] +// CIR: [[B:%[0-9]+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["b", init] +// CIR: [[RETVAL:%[0-9]+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"] +// CIR: cir.store %[[ARG0]], [[A]] : !s32i, !cir.ptr<!s32i> +// CIR: cir.store %[[ARG1]], [[B]] : !s32i, !cir.ptr<!s32i> +// CIR: cir.scope { +// CIR: [[ALOAD:%[0-9]+]] = cir.load [[A]] : !cir.ptr<!s32i>, !s32i +// CIR: [[BLOAD:%[0-9]+]] = cir.load [[B]] : !cir.ptr<!s32i>, !s32i +// CIR: [[CMP:%[0-9]+]] = cir.cmp(lt, [[ALOAD]], [[BLOAD]]) : !s32i, !cir.bool +// CIR: [[TERNARY_RES:%[0-9]+]] = cir.ternary([[CMP]], true { +// CIR: [[ZERO:%[0-9]+]] = cir.const #cir.int<0> : !s32i +// CIR: cir.yield [[ZERO]] : !s32i +// CIR: }, false { +// CIR: [[ALOAD2:%[0-9]+]] = cir.load [[A]] : !cir.ptr<!s32i>, !s32i +// CIR: cir.yield [[ALOAD2]] : !s32i +// CIR: }) : (!cir.bool) -> !s32i +// CIR: [[CAST:%[0-9]+]] = cir.cast(int_to_bool, [[TERNARY_RES]] : !s32i), !cir.bool +// CIR: cir.if [[CAST]] { +// CIR: [[ONE:%[0-9]+]] = cir.const #cir.int<1> : !s32i +// CIR: [[MINUS_ONE:%[0-9]+]] = cir.unary(minus, [[ONE]]) nsw : !s32i, !s32i +// CIR: cir.store [[MINUS_ONE]], [[RETVAL]] : !s32i, !cir.ptr<!s32i> +// CIR: [[RETVAL_VAL:%[0-9]+]] = cir.load [[RETVAL]] : !cir.ptr<!s32i>, !s32i +// CIR: cir.return [[RETVAL_VAL]] : !s32i +// CIR: } +// CIR: } +// CIR: [[ZERO2:%[0-9]+]] = cir.const #cir.int<0> : !s32i +// CIR: cir.store [[ZERO2]], [[RETVAL]] : !s32i, !cir.ptr<!s32i> +// CIR: [[RETVAL_VAL2:%[0-9]+]] = cir.load [[RETVAL]] : !cir.ptr<!s32i>, !s32i +// CIR: cir.return [[RETVAL_VAL2]] : !s32i + +// LLVM-LABEL: define i32 @_Z3fooii( +// LLVM-SAME: i32 %[[ARG0:.*]], i32 %[[ARG1:.*]]) +// LLVM: %[[A:.*]] = alloca i32 +// LLVM: %[[B:.*]] = alloca i32 +// LLVM: %[[RETVAL:.*]] = alloca i32 +// LLVM: store i32 %[[ARG0]], ptr %[[A]] +// LLVM: store i32 %[[ARG1]], ptr %[[B]] +// LLVM: br label %[[ENTRY_BB:.*]] +// LLVM: [[ENTRY_BB]]: +// LLVM: %[[AVAL:.*]] = load i32, ptr %[[A]] +// LLVM: %[[BVAL:.*]] = load i32, ptr %[[B]] +// LLVM: %[[CMP:.*]] = icmp slt i32 %[[AVAL]], %[[BVAL]] +// LLVM: br i1 %[[CMP]], label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]] +// LLVM: [[TRUE_BB]]: +// LLVM: br label %[[MERGE_BB:.*]] +// LLVM: [[FALSE_BB]]: +// LLVM: %[[AVAL2:.*]] = load i32, ptr %[[A]] +// LLVM: br label %[[MERGE_BB]] +// LLVM: [[MERGE_BB]]: +// LLVM: %[[PHI:.*]] = phi i32 [ %[[AVAL2]], %[[FALSE_BB]] ], [ 0, %[[TRUE_BB]] ] +// LLVM: br label %[[CHECK_BB:.*]] +// LLVM: [[CHECK_BB]]: +// LLVM: %[[COND:.*]] = icmp ne i32 %[[PHI]], 0 +// LLVM: br i1 %[[COND]], label %[[RETURN_MINUS_ONE:.*]], label %[[CONT_BB:.*]] +// LLVM: [[RETURN_MINUS_ONE]]: +// LLVM: store i32 -1, ptr %[[RETVAL]] +// LLVM: %[[RET1:.*]] = load i32, ptr %[[RETVAL]] +// LLVM: ret i32 %[[RET1]] +// LLVM: [[CONT_BB]]: +// LLVM: br label %[[RETURN_ZERO:.*]] +// LLVM: [[RETURN_ZERO]]: +// LLVM: store i32 0, ptr %[[RETVAL]] +// LLVM: %[[RET2:.*]] = load i32, ptr %[[RETVAL]] +// LLVM: ret i32 %[[RET2]] >From 0c5474f91b62572c21231372437da2990d0a2122 Mon Sep 17 00:00:00 2001 From: Morris Hafner <m...@users.noreply.github.com> Date: Thu, 1 May 2025 18:05:47 +0200 Subject: [PATCH 2/4] Update clang/lib/CIR/CodeGen/CIRGenExpr.cpp Co-authored-by: Erich Keane <eke...@nvidia.com> --- clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index 556346fa495a3..1b6b739b982a4 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -1202,7 +1202,7 @@ Address CIRGenFunction::emitArrayToPointerDecay(const Expr *e) { static std::optional<LValue> handleConditionalOperatorLValueSimpleCase( CIRGenFunction &cgf, const AbstractConditionalOperator *e) { const Expr *condExpr = e->getCond(); - bool condExprBool; + bool condExprBool = false; if (cgf.constantFoldsToSimpleInteger(condExpr, condExprBool)) { const Expr *live = e->getTrueExpr(), *dead = e->getFalseExpr(); if (!condExprBool) >From c22fa96a69e44bfad8b3c36835dd3b3c3cdbd4d9 Mon Sep 17 00:00:00 2001 From: Morris Hafner <mhaf...@nvidia.com> Date: Mon, 19 May 2025 19:32:22 +0200 Subject: [PATCH 3/4] typo fix --- clang/include/clang/CIR/MissingFeatures.h | 2 +- clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 4689f1d7f4a63..263736928d767 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -202,7 +202,7 @@ struct MissingFeatures { static bool cleanupsToDeactivate() { return false; } static bool stackBase() { return false; } static bool peepholeProtection() { return false; } - static bool instrumenation() { return false; } + static bool instrumentation() { return false; } // Missing types static bool dataMemberType() { return false; } diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp index 1038bc7e37683..fbb20dcde01c4 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -927,8 +927,8 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { mlir::Value rhsCond = cgf.evaluateExprAsBool(e->getRHS()); if (instrumentRegions) { - assert(!cir::MissingFeatures::instrumenation()); - cgf.cgm.errorNYI(e->getExprLoc(), "instrumenation"); + assert(!cir::MissingFeatures::instrumentation()); + cgf.cgm.errorNYI(e->getExprLoc(), "instrumentation"); } // ZExt result to int or bool. return builder.createZExtOrBitCast(rhsCond.getLoc(), rhsCond, resTy); @@ -1000,8 +1000,8 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { mlir::Value rhsCond = cgf.evaluateExprAsBool(e->getRHS()); if (instrumentRegions) { - assert(!cir::MissingFeatures::instrumenation()); - cgf.cgm.errorNYI(e->getExprLoc(), "instrumenation"); + assert(!cir::MissingFeatures::instrumentation()); + cgf.cgm.errorNYI(e->getExprLoc(), "instrumentation"); } // ZExt result to int or bool. return builder.createZExtOrBitCast(rhsCond.getLoc(), rhsCond, resTy); >From 8d992efee2c02c4f291f63514f64c26e18065853 Mon Sep 17 00:00:00 2001 From: Morris Hafner <mhaf...@nvidia.com> Date: Tue, 27 May 2025 18:57:25 +0200 Subject: [PATCH 4/4] Address review feedback --- .../CIR/Dialect/Builder/CIRBaseBuilder.h | 34 ++-- clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 86 ++------- clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 167 +++------------- clang/lib/CIR/CodeGen/CIRGenFunction.h | 11 +- clang/test/CIR/CodeGen/binop.cpp | 181 ++++++------------ clang/test/CIR/CodeGen/ternary.cpp | 32 +--- 6 files changed, 144 insertions(+), 367 deletions(-) diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h index e143c7958f0bc..8efd3243d94d0 100644 --- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h +++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h @@ -282,22 +282,6 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { return createCast(loc, cir::CastKind::bitcast, src, newTy); } - // TODO(cir): the following function was introduced to keep in sync with LLVM - // codegen. CIR does not have "zext" operations. It should eventually be - // renamed or removed. For now, we just add whatever cast is required here. - mlir::Value createZExtOrBitCast(mlir::Location loc, mlir::Value src, - mlir::Type newTy) { - mlir::Type srcTy = src.getType(); - - if (srcTy == newTy) - return src; - - if (mlir::isa<cir::BoolType>(srcTy) && mlir::isa<cir::IntType>(newTy)) - return createBoolToInt(src, newTy); - - llvm_unreachable("unhandled extension cast"); - } - //===--------------------------------------------------------------------===// // Binary Operators //===--------------------------------------------------------------------===// @@ -322,6 +306,24 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { return createBinop(loc, lhs, cir::BinOpKind::Or, rhs); } + mlir::Value createSelect(mlir::Location loc, mlir::Value condition, + mlir::Value trueValue, mlir::Value falseValue) { + assert(trueValue.getType() == falseValue.getType() && + "trueValue and falseValue should have the same type"); + return create<cir::SelectOp>(loc, trueValue.getType(), condition, trueValue, + falseValue); + } + + mlir::Value createLogicalAnd(mlir::Location loc, mlir::Value lhs, + mlir::Value rhs) { + return createSelect(loc, lhs, rhs, getBool(false, loc)); + } + + mlir::Value createLogicalOr(mlir::Location loc, mlir::Value lhs, + mlir::Value rhs) { + return createSelect(loc, lhs, getBool(true, loc), rhs); + } + mlir::Value createMul(mlir::Location loc, mlir::Value lhs, mlir::Value rhs, OverflowBehavior ob = OverflowBehavior::None) { auto op = diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index 1b6b739b982a4..5758a77f2ecdc 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -1197,6 +1197,21 @@ Address CIRGenFunction::emitArrayToPointerDecay(const Expr *e) { return Address(ptr, addr.getAlignment()); } +/// Emit the operand of a glvalue conditional operator. This is either a glvalue +/// or a (possibly-parenthesized) throw-expression. If this is a throw, no +/// LValue is returned and the current block has been terminated. +static std::optional<LValue> emitLValueOrThrowExpression(CIRGenFunction &cgf, + const Expr *operand) { + if (isa<CXXThrowExpr>(operand->IgnoreParens())) { + assert(!cir::MissingFeatures::throwOp()); + cgf.cgm.errorNYI(operand->getSourceRange(), + "throw expressions in conditional operator"); + return std::nullopt; + } + + return cgf.emitLValue(operand); +} + // Handle the case where the condition is a constant evaluatable simple integer, // which means we don't have to separately handle the true/false blocks. static std::optional<LValue> handleConditionalOperatorLValueSimpleCase( @@ -1208,6 +1223,8 @@ static std::optional<LValue> handleConditionalOperatorLValueSimpleCase( if (!condExprBool) std::swap(live, dead); + // If there's a label in the "dead" branch we can't eliminate that as it + // could be a used jump target. if (!cgf.containsLabel(dead)) { // If the true case is live, we need to track its region. if (condExprBool) { @@ -1215,33 +1232,12 @@ static std::optional<LValue> handleConditionalOperatorLValueSimpleCase( } // If a throw expression we emit it and return an undefined lvalue // because it can't be used. - if (isa<CXXThrowExpr>(live->IgnoreParens())) { - assert(!cir::MissingFeatures::throwOp()); - cgf.cgm.errorNYI(live->getSourceRange(), - "throw expressions in conditional operator"); - return std::nullopt; - } - return cgf.emitLValue(live); + return emitLValueOrThrowExpression(cgf, live); } } return std::nullopt; } -/// Emit the operand of a glvalue conditional operator. This is either a glvalue -/// or a (possibly-parenthesized) throw-expression. If this is a throw, no -/// LValue is returned and the current block has been terminated. -static std::optional<LValue> emitLValueOrThrowExpression(CIRGenFunction &cgf, - const Expr *operand) { - if (isa<CXXThrowExpr>(operand->IgnoreParens())) { - assert(!cir::MissingFeatures::throwOp()); - cgf.cgm.errorNYI(operand->getSourceRange(), - "throw expressions in conditional operator"); - return std::nullopt; - } - - return cgf.emitLValue(operand); -} - // Create and generate the 3 blocks for a conditional operator. // Leaves the 'current block' in the continuation basic block. template <typename FuncTy> @@ -1252,7 +1248,6 @@ CIRGenFunction::emitConditionalBlocks(const AbstractConditionalOperator *e, CIRGenFunction &cgf = *this; ConditionalEvaluation eval(cgf); mlir::Location loc = cgf.getLoc(e->getSourceRange()); - CIRGenBuilderTy &builder = cgf.getBuilder(); Expr *trueExpr = e->getTrueExpr(); Expr *falseExpr = e->getFalseExpr(); @@ -1266,10 +1261,10 @@ CIRGenFunction::emitConditionalBlocks(const AbstractConditionalOperator *e, cgf.curLexScope->setAsTernary(); assert(!cir::MissingFeatures::incrementProfileCounter()); - eval.begin(cgf); + eval.beginEvaluation(); branchInfo = branchGenFunc(cgf, expr); mlir::Value branch = branchInfo->getPointer(); - eval.end(cgf); + eval.endEvaluation(); if (branch) { yieldTy = branch.getType(); @@ -1315,47 +1310,6 @@ CIRGenFunction::emitConditionalBlocks(const AbstractConditionalOperator *e, return info; } -LValue CIRGenFunction::emitConditionalOperatorLValue( - const AbstractConditionalOperator *expr) { - if (!expr->isGLValue()) { - // ?: here should be an aggregate. - assert(hasAggregateEvaluationKind(expr->getType()) && - "Unexpected conditional operator!"); - return emitAggExprToLValue(expr); - } - - OpaqueValueMapping binding(*this, expr); - if (std::optional<LValue> res = - handleConditionalOperatorLValueSimpleCase(*this, expr)) - return *res; - - ConditionalInfo info = - emitConditionalBlocks(expr, [](CIRGenFunction &cgf, const Expr *e) { - return emitLValueOrThrowExpression(cgf, e); - }); - - if ((info.lhs && !info.lhs->isSimple()) || - (info.rhs && !info.rhs->isSimple())) { - cgm.errorNYI(expr->getSourceRange(), "unsupported conditional operator"); - return {}; - } - - if (info.lhs && info.rhs) { - Address lhsAddr = info.lhs->getAddress(); - Address rhsAddr = info.rhs->getAddress(); - Address result(info.result, lhsAddr.getElementType(), - std::min(lhsAddr.getAlignment(), rhsAddr.getAlignment())); - AlignmentSource alignSource = - std::max(info.lhs->getBaseInfo().getAlignmentSource(), - info.rhs->getBaseInfo().getAlignmentSource()); - assert(!cir::MissingFeatures::opTBAA()); - return makeAddrLValue(result, expr->getType(), LValueBaseInfo(alignSource)); - } - assert((info.lhs || info.rhs) && - "both operands of glvalue conditional are throw-expressions?"); - return info.lhs ? *info.lhs : *info.rhs; -} - /// Emit an `if` on a boolean condition, filling `then` and `else` into /// appropriated regions. mlir::LogicalResult CIRGenFunction::emitIfOnBoolExpr(const Expr *cond, diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp index fbb20dcde01c4..7e1b189ac8d97 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -98,6 +98,14 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { mlir::Value emitPromoted(const Expr *e, QualType promotionType); + mlir::Value maybePromoteBoolResult(mlir::Value value, + mlir::Type dstTy) const { + if (mlir::isa<cir::IntType>(dstTy)) + return builder.createBoolToInt(value, dstTy); + if (mlir::isa<cir::BoolType>(dstTy)) + return value; + } + //===--------------------------------------------------------------------===// // Visitor Methods //===--------------------------------------------------------------------===// @@ -918,26 +926,6 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { mlir::Type resTy = cgf.convertType(e->getType()); mlir::Location loc = cgf.getLoc(e->getExprLoc()); - // If we have 0 && RHS, see if we can elide RHS, if so, just return 0. - // If we have 1 && X, just emit X without inserting the control flow. - bool lhsCondVal; - if (cgf.constantFoldsToSimpleInteger(e->getLHS(), lhsCondVal)) { - if (lhsCondVal) { // If we have 1 && X, just emit X. - - mlir::Value rhsCond = cgf.evaluateExprAsBool(e->getRHS()); - - if (instrumentRegions) { - assert(!cir::MissingFeatures::instrumentation()); - cgf.cgm.errorNYI(e->getExprLoc(), "instrumentation"); - } - // ZExt result to int or bool. - return builder.createZExtOrBitCast(rhsCond.getLoc(), rhsCond, resTy); - } - // 0 && RHS: If it is safe, just elide the RHS, and return 0/false. - if (!cgf.containsLabel(e->getRHS())) - return builder.getNullValue(resTy, loc); - } - CIRGenFunction::ConditionalEvaluation eval(cgf); mlir::Value lhsCondV = cgf.evaluateExprAsBool(e->getLHS()); @@ -947,27 +935,7 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { CIRGenFunction::LexicalScope lexScope{cgf, loc, b.getInsertionBlock()}; cgf.curLexScope->setAsTernary(); - mlir::Value rhsCondV = cgf.evaluateExprAsBool(e->getRHS()); - auto res = b.create<cir::TernaryOp>( - loc, rhsCondV, /*trueBuilder*/ - [&](mlir::OpBuilder &b, mlir::Location loc) { - CIRGenFunction::LexicalScope lexScope{cgf, loc, - b.getInsertionBlock()}; - cgf.curLexScope->setAsTernary(); - auto res = - b.create<cir::ConstantOp>(loc, builder.getTrueAttr()); - b.create<cir::YieldOp>(loc, res.getRes()); - }, - /*falseBuilder*/ - [&](mlir::OpBuilder &b, mlir::Location loc) { - CIRGenFunction::LexicalScope lexScope{cgf, loc, - b.getInsertionBlock()}; - cgf.curLexScope->setAsTernary(); - auto res = - b.create<cir::ConstantOp>(loc, builder.getFalseAttr()); - b.create<cir::YieldOp>(loc, res.getRes()); - }); - b.create<cir::YieldOp>(loc, res.getResult()); + b.create<cir::YieldOp>(loc, cgf.evaluateExprAsBool(e->getRHS())); }, /*falseBuilder*/ [&](mlir::OpBuilder &b, mlir::Location loc) { @@ -977,8 +945,7 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { auto res = b.create<cir::ConstantOp>(loc, builder.getFalseAttr()); b.create<cir::YieldOp>(loc, res.getRes()); }); - return builder.createZExtOrBitCast(resOp.getLoc(), resOp.getResult(), - resTy); + return maybePromoteBoolResult(resOp.getResult(), resTy); } mlir::Value VisitBinLOr(const clang::BinaryOperator *e) { @@ -991,29 +958,6 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { mlir::Type resTy = cgf.convertType(e->getType()); mlir::Location loc = cgf.getLoc(e->getExprLoc()); - // If we have 1 || RHS, see if we can elide RHS, if so, just return 1. - // If we have 0 || X, just emit X without inserting the control flow. - bool lhsCondVal; - if (cgf.constantFoldsToSimpleInteger(e->getLHS(), lhsCondVal)) { - if (!lhsCondVal) { // If we have 0 || X, just emit X. - - mlir::Value rhsCond = cgf.evaluateExprAsBool(e->getRHS()); - - if (instrumentRegions) { - assert(!cir::MissingFeatures::instrumentation()); - cgf.cgm.errorNYI(e->getExprLoc(), "instrumentation"); - } - // ZExt result to int or bool. - return builder.createZExtOrBitCast(rhsCond.getLoc(), rhsCond, resTy); - } - // 1 || RHS: If it is safe, just elide the RHS, and return 1/true. - if (!cgf.containsLabel(e->getRHS())) { - if (auto intTy = mlir::dyn_cast<cir::IntType>(resTy)) - return builder.getConstantInt(loc, intTy, 1); - return builder.getBool(true, loc); - } - } - CIRGenFunction::ConditionalEvaluation eval(cgf); mlir::Value lhsCondV = cgf.evaluateExprAsBool(e->getLHS()); @@ -1031,49 +975,10 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { CIRGenFunction::LexicalScope lexScope{cgf, loc, b.getInsertionBlock()}; cgf.curLexScope->setAsTernary(); - mlir::Value rhsCondV = cgf.evaluateExprAsBool(e->getRHS()); - auto res = b.create<cir::TernaryOp>( - loc, rhsCondV, /*trueBuilder*/ - [&](mlir::OpBuilder &b, mlir::Location loc) { - SmallVector<mlir::Location, 2> locs; - if (mlir::isa<mlir::FileLineColLoc>(loc)) { - locs.push_back(loc); - locs.push_back(loc); - } else if (mlir::isa<mlir::FusedLoc>(loc)) { - auto fusedLoc = mlir::cast<mlir::FusedLoc>(loc); - locs.push_back(fusedLoc.getLocations()[0]); - locs.push_back(fusedLoc.getLocations()[1]); - } - CIRGenFunction::LexicalScope lexScope{cgf, loc, - b.getInsertionBlock()}; - cgf.curLexScope->setAsTernary(); - auto res = - b.create<cir::ConstantOp>(loc, builder.getTrueAttr()); - b.create<cir::YieldOp>(loc, res.getRes()); - }, - /*falseBuilder*/ - [&](mlir::OpBuilder &b, mlir::Location loc) { - SmallVector<mlir::Location, 2> locs; - if (mlir::isa<mlir::FileLineColLoc>(loc)) { - locs.push_back(loc); - locs.push_back(loc); - } else if (mlir::isa<mlir::FusedLoc>(loc)) { - auto fusedLoc = mlir::cast<mlir::FusedLoc>(loc); - locs.push_back(fusedLoc.getLocations()[0]); - locs.push_back(fusedLoc.getLocations()[1]); - } - CIRGenFunction::LexicalScope lexScope{cgf, loc, - b.getInsertionBlock()}; - cgf.curLexScope->setAsTernary(); - auto res = - b.create<cir::ConstantOp>(loc, builder.getFalseAttr()); - b.create<cir::YieldOp>(loc, res.getRes()); - }); - b.create<cir::YieldOp>(loc, res.getResult()); + b.create<cir::YieldOp>(loc, cgf.evaluateExprAsBool(e->getRHS())); }); - return builder.createZExtOrBitCast(resOp.getLoc(), resOp.getResult(), - resTy); + return maybePromoteBoolResult(resOp.getResult(), resTy); } }; @@ -1922,11 +1827,7 @@ mlir::Value ScalarExprEmitter::VisitUnaryLNot(const UnaryOperator *e) { boolVal = builder.createNot(boolVal); // ZExt result to the expr type. - mlir::Type dstTy = cgf.convertType(e->getType()); - if (mlir::isa<cir::IntType>(dstTy)) - return builder.createBoolToInt(boolVal, dstTy); - if (mlir::isa<cir::BoolType>(dstTy)) - return boolVal; + return maybePromoteBoolResult(boolVal, cgf.convertType(e->getType())); cgf.cgm.errorNYI("destination type for logical-not unary operator is NYI"); return {}; @@ -2042,35 +1943,27 @@ mlir::Value ScalarExprEmitter::VisitAbstractConditionalOperator( } // If this is a really simple expression (like x ? 4 : 5), emit this as a - // select instead of as control flow. We can only do this if it is cheap and - // safe to evaluate the LHS and RHS unconditionally. + // select instead of as control flow. We can only do this if it is cheap + // and safe to evaluate the LHS and RHS unconditionally. if (isCheapEnoughToEvaluateUnconditionally(lhsExpr, cgf) && isCheapEnoughToEvaluateUnconditionally(rhsExpr, cgf)) { bool lhsIsVoid = false; mlir::Value condV = cgf.evaluateExprAsBool(condExpr); assert(!cir::MissingFeatures::incrementProfileCounter()); - return builder - .create<cir::TernaryOp>( - loc, condV, /*thenBuilder=*/ - [&](mlir::OpBuilder &b, mlir::Location loc) { - mlir::Value lhs = Visit(lhsExpr); - if (!lhs) { - lhs = builder.getNullValue(cgf.VoidTy, loc); - lhsIsVoid = true; - } - builder.create<cir::YieldOp>(loc, lhs); - }, - /*elseBuilder=*/ - [&](mlir::OpBuilder &b, mlir::Location loc) { - mlir::Value rhs = Visit(rhsExpr); - if (lhsIsVoid) { - assert(!rhs && "lhs and rhs types must match"); - rhs = builder.getNullValue(cgf.VoidTy, loc); - } - builder.create<cir::YieldOp>(loc, rhs); - }) - .getResult(); + mlir::Value lhs = Visit(lhsExpr); + if (!lhs) { + lhs = builder.getNullValue(cgf.VoidTy, loc); + lhsIsVoid = true; + } + + mlir::Value rhs = Visit(rhsExpr); + if (lhsIsVoid) { + assert(!rhs && "lhs and rhs types must match"); + rhs = builder.getNullValue(cgf.VoidTy, loc); + } + + return builder.createSelect(loc, condV, lhs, rhs); } mlir::Value condV = cgf.emitOpOnBoolExpr(loc, condExpr); @@ -2083,9 +1976,9 @@ mlir::Value ScalarExprEmitter::VisitAbstractConditionalOperator( cgf.curLexScope->setAsTernary(); assert(!cir::MissingFeatures::incrementProfileCounter()); - eval.begin(cgf); + eval.beginEvaluation(); mlir::Value branch = Visit(expr); - eval.end(cgf); + eval.endEvaluation(); if (branch) { yieldTy = branch.getType(); diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index fc103f8d9f895..0deb140131460 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -751,8 +751,6 @@ class CIRGenFunction : public CIRGenTypeCache { void emitCompoundStmtWithoutScope(const clang::CompoundStmt &s); - LValue emitConditionalOperatorLValue(const AbstractConditionalOperator *expr); - void emitDecl(const clang::Decl &d); mlir::LogicalResult emitDeclStmt(const clang::DeclStmt &s); LValue emitDeclRefLValue(const clang::DeclRefExpr *e); @@ -875,20 +873,21 @@ class CIRGenFunction : public CIRGenTypeCache { /// An object to manage conditionally-evaluated expressions. class ConditionalEvaluation { + CIRGenFunction &cgf; mlir::OpBuilder::InsertPoint insertPt; public: ConditionalEvaluation(CIRGenFunction &cgf) - : insertPt(cgf.builder.saveInsertionPoint()) {} - ConditionalEvaluation(mlir::OpBuilder::InsertPoint ip) : insertPt(ip) {} + : cgf(cgf), insertPt(cgf.builder.saveInsertionPoint()) {} + ConditionalEvaluation(CIRGenFunction &cgf, mlir::OpBuilder::InsertPoint ip) : cgf(cgf), insertPt(ip) {} - void begin(CIRGenFunction &cgf) { + void beginEvaluation() { assert(cgf.outermostConditional != this); if (!cgf.outermostConditional) cgf.outermostConditional = this; } - void end(CIRGenFunction &cgf) { + void endEvaluation() { assert(cgf.outermostConditional != nullptr); if (cgf.outermostConditional == this) cgf.outermostConditional = nullptr; diff --git a/clang/test/CIR/CodeGen/binop.cpp b/clang/test/CIR/CodeGen/binop.cpp index cd53c6f0bbcf1..f44cc5148f767 100644 --- a/clang/test/CIR/CodeGen/binop.cpp +++ b/clang/test/CIR/CodeGen/binop.cpp @@ -556,95 +556,66 @@ void b1(bool a, bool b) { // CIR: [[AVAL:%[0-9]+]] = cir.load [[A]] : !cir.ptr<!cir.bool>, !cir.bool // CIR: [[RES1:%[0-9]+]] = cir.ternary([[AVAL]], true { // CIR: [[BVAL:%[0-9]+]] = cir.load [[B]] : !cir.ptr<!cir.bool>, !cir.bool -// CIR: [[INNER1:%[0-9]+]] = cir.ternary([[BVAL]], true { -// CIR: {{%[0-9]+}} = cir.const #true -// CIR: cir.yield {{%[0-9]+}} : !cir.bool +// CIR: cir.yield [[BVAL]] : !cir.bool // CIR: }, false { -// CIR: {{%[0-9]+}} = cir.const #false -// CIR: cir.yield {{%[0-9]+}} : !cir.bool -// CIR: }) : (!cir.bool) -> !cir.bool -// CIR: cir.yield [[INNER1]] : !cir.bool -// CIR: }, false { -// CIR: {{%[0-9]+}} = cir.const #false -// CIR: cir.yield {{%[0-9]+}} : !cir.bool +// CIR: [[FALSE:%[0-9]+]] = cir.const #false +// CIR: cir.yield [[FALSE]] : !cir.bool // CIR: }) : (!cir.bool) -> !cir.bool // CIR: cir.store [[RES1]], [[X]] : !cir.bool, !cir.ptr<!cir.bool> // CIR: [[XVAL:%[0-9]+]] = cir.load [[X]] : !cir.ptr<!cir.bool>, !cir.bool // CIR: [[RES2:%[0-9]+]] = cir.ternary([[XVAL]], true { -// CIR: {{%[0-9]+}} = cir.const #true -// CIR: cir.yield {{%[0-9]+}} : !cir.bool +// CIR: [[TRUE:%[0-9]+]] = cir.const #true +// CIR: cir.yield [[TRUE]] : !cir.bool // CIR: }, false { // CIR: [[BVAL2:%[0-9]+]] = cir.load [[B]] : !cir.ptr<!cir.bool>, !cir.bool -// CIR: [[INNER2:%[0-9]+]] = cir.ternary([[BVAL2]], true { -// CIR: {{%[0-9]+}} = cir.const #true -// CIR: cir.yield {{%[0-9]+}} : !cir.bool -// CIR: }, false { -// CIR: {{%[0-9]+}} = cir.const #false -// CIR: cir.yield {{%[0-9]+}} : !cir.bool -// CIR: }) : (!cir.bool) -> !cir.bool -// CIR: cir.yield [[INNER2]] : !cir.bool +// CIR: cir.yield [[BVAL2]] : !cir.bool // CIR: }) : (!cir.bool) -> !cir.bool // CIR: cir.store [[RES2]], [[X]] : !cir.bool, !cir.ptr<!cir.bool> // CIR: cir.return // LLVM-LABEL: define void @_Z2b1bb( -// LLVM-SAME: i1 %[[ARG0:.*]], i1 %[[ARG1:.*]]) -// LLVM: %[[A_ADDR:.*]] = alloca i8 -// LLVM: %[[B_ADDR:.*]] = alloca i8 -// LLVM: %[[X:.*]] = alloca i8 +// LLVM-SAME: i1 %[[ARG0:[0-9]+]], i1 %[[ARG1:[0-9]+]]) +// LLVM: %[[A_ADDR:.*]] = alloca i8, i64 1 +// LLVM: %[[B_ADDR:.*]] = alloca i8, i64 1 +// LLVM: %[[X:.*]] = alloca i8, i64 1 // LLVM: %[[ZEXT0:.*]] = zext i1 %[[ARG0]] to i8 // LLVM: store i8 %[[ZEXT0]], ptr %[[A_ADDR]] // LLVM: %[[ZEXT1:.*]] = zext i1 %[[ARG1]] to i8 // LLVM: store i8 %[[ZEXT1]], ptr %[[B_ADDR]] // LLVM: %[[A_VAL:.*]] = load i8, ptr %[[A_ADDR]] // LLVM: %[[A_BOOL:.*]] = trunc i8 %[[A_VAL]] to i1 -// LLVM: br i1 %[[A_BOOL]], label %[[AND_LHS_TRUE:.*]], label %[[AND_END:.*]] -// LLVM: [[AND_LHS_TRUE]]: +// LLVM: br i1 %[[A_BOOL]], label %[[AND_TRUE:[0-9]+]], label %[[AND_FALSE:[0-9]+]] +// LLVM: [[AND_TRUE]]: // LLVM: %[[B_VAL:.*]] = load i8, ptr %[[B_ADDR]] // LLVM: %[[B_BOOL:.*]] = trunc i8 %[[B_VAL]] to i1 -// LLVM: br i1 %[[B_BOOL]], label %[[B_TRUE:.*]], label %[[B_FALSE:.*]] -// LLVM: [[B_TRUE]]: -// LLVM: br label %[[AND_RHS_END:.*]] -// LLVM: [[B_FALSE]]: -// LLVM: br label %[[AND_RHS_END]] -// LLVM: [[AND_RHS_END]]: -// LLVM: %[[RHS_PHI:.*]] = phi i1 [ false, %[[B_FALSE]] ], [ true, %[[B_TRUE]] ] -// LLVM: br label %[[AND_DONE:.*]] -// LLVM: [[AND_DONE]]: -// LLVM: br label %[[MERGE_BB:.*]] -// LLVM: [[AND_END]]: -// LLVM: %[[AND_RESULT:.*]] = phi i1 [ false, %[[PRED1:.*]] ], [ %[[RHS_PHI]], %[[PRED2:.*]] ] -// LLVM: br label %[[STORE_X:.*]] -// LLVM: [[STORE_X]]: -// LLVM: %[[ZEXT_AND:.*]] = zext i1 %[[AND_RESULT]] to i8 +// LLVM: br label %[[AND_MERGE:[0-9]+]] +// LLVM: [[AND_FALSE]]: +// LLVM: br label %[[AND_MERGE]] +// LLVM: [[AND_MERGE]]: +// LLVM: %[[AND_PHI:.*]] = phi i1 [ false, %[[AND_FALSE]] ], [ %[[B_BOOL]], %[[AND_TRUE]] ] +// LLVM: br label %[[AND_CONT:[0-9]+]] +// LLVM: [[AND_CONT]]: +// LLVM: %[[ZEXT_AND:.*]] = zext i1 %[[AND_PHI]] to i8 // LLVM: store i8 %[[ZEXT_AND]], ptr %[[X]] // LLVM: %[[X_VAL:.*]] = load i8, ptr %[[X]] // LLVM: %[[X_BOOL:.*]] = trunc i8 %[[X_VAL]] to i1 -// LLVM: br i1 %[[X_BOOL]], label %[[OR_TRUE:.*]], label %[[OR_RHS:.*]] +// LLVM: br i1 %[[X_BOOL]], label %[[OR_TRUE:[0-9]+]], label %[[OR_FALSE:[0-9]+]] // LLVM: [[OR_TRUE]]: -// LLVM: br label %[[OR_END:.*]] -// LLVM: [[OR_RHS]]: +// LLVM: br label %[[OR_MERGE:[0-9]+]] +// LLVM: [[OR_FALSE]]: // LLVM: %[[B_VAL2:.*]] = load i8, ptr %[[B_ADDR]] // LLVM: %[[B_BOOL2:.*]] = trunc i8 %[[B_VAL2]] to i1 -// LLVM: br i1 %[[B_BOOL2]], label %[[B_TRUE2:.*]], label %[[B_FALSE2:.*]] -// LLVM: [[B_TRUE2]]: -// LLVM: br label %[[OR_RHS_END:.*]] -// LLVM: [[B_FALSE2]]: -// LLVM: br label %[[OR_RHS_END]] -// LLVM: [[OR_RHS_END]]: -// LLVM: %[[RHS_PHI2:.*]] = phi i1 [ false, %[[B_FALSE2]] ], [ true, %[[B_TRUE2]] ] -// LLVM: br label %[[OR_RHS_DONE:.*]] -// LLVM: [[OR_RHS_DONE]]: -// LLVM: br label %[[OR_END]] -// LLVM: [[OR_END]]: -// LLVM: %[[OR_RESULT:.*]] = phi i1 [ %[[RHS_PHI2]], %[[OR_RHS_DONE]] ], [ true, %[[OR_TRUE]] ] -// LLVM: br label %[[FINAL_STORE:.*]] -// LLVM: [[FINAL_STORE]]: -// LLVM: %[[ZEXT_OR:.*]] = zext i1 %[[OR_RESULT]] to i8 +// LLVM: br label %[[OR_MERGE]] +// LLVM: [[OR_MERGE]]: +// LLVM: %[[OR_PHI:.*]] = phi i1 [ %[[B_BOOL2]], %[[OR_FALSE]] ], [ true, %[[OR_TRUE]] ] +// LLVM: br label %[[OR_CONT:[0-9]+]] +// LLVM: [[OR_CONT]]: +// LLVM: %[[ZEXT_OR:.*]] = zext i1 %[[OR_PHI]] to i8 // LLVM: store i8 %[[ZEXT_OR]], ptr %[[X]] // LLVM: ret void + void b3(int a, int b, int c, int d) { bool x = (a == b) && (c == d); x = (a == b) || (c == d); @@ -668,48 +639,34 @@ void b3(int a, int b, int c, int d) { // CIR: [[CVAL1:%[0-9]+]] = cir.load [[C]] : !cir.ptr<!s32i>, !s32i // CIR: [[DVAL1:%[0-9]+]] = cir.load [[D]] : !cir.ptr<!s32i>, !s32i // CIR: [[CMP2:%[0-9]+]] = cir.cmp(eq, [[CVAL1]], [[DVAL1]]) : !s32i, !cir.bool -// CIR: [[INNER1:%[0-9]+]] = cir.ternary([[CMP2]], true { -// CIR: {{%[0-9]+}} = cir.const #true -// CIR: cir.yield {{%[0-9]+}} : !cir.bool +// CIR: cir.yield [[CMP2]] : !cir.bool // CIR: }, false { -// CIR: {{%[0-9]+}} = cir.const #false -// CIR: cir.yield {{%[0-9]+}} : !cir.bool -// CIR: }) : (!cir.bool) -> !cir.bool -// CIR: cir.yield [[INNER1]] : !cir.bool -// CIR: }, false { -// CIR: {{%[0-9]+}} = cir.const #false -// CIR: cir.yield {{%[0-9]+}} : !cir.bool +// CIR: [[FALSE:%[0-9]+]] = cir.const #false +// CIR: cir.yield [[FALSE]] : !cir.bool // CIR: }) : (!cir.bool) -> !cir.bool // CIR: cir.store [[AND_RESULT]], [[X]] : !cir.bool, !cir.ptr<!cir.bool> // CIR: [[AVAL2:%[0-9]+]] = cir.load [[A]] : !cir.ptr<!s32i>, !s32i // CIR: [[BVAL2:%[0-9]+]] = cir.load [[B]] : !cir.ptr<!s32i>, !s32i // CIR: [[CMP3:%[0-9]+]] = cir.cmp(eq, [[AVAL2]], [[BVAL2]]) : !s32i, !cir.bool // CIR: [[OR_RESULT:%[0-9]+]] = cir.ternary([[CMP3]], true { -// CIR: {{%[0-9]+}} = cir.const #true -// CIR: cir.yield {{%[0-9]+}} : !cir.bool +// CIR: [[TRUE:%[0-9]+]] = cir.const #true +// CIR: cir.yield [[TRUE]] : !cir.bool // CIR: }, false { // CIR: [[CVAL2:%[0-9]+]] = cir.load [[C]] : !cir.ptr<!s32i>, !s32i // CIR: [[DVAL2:%[0-9]+]] = cir.load [[D]] : !cir.ptr<!s32i>, !s32i // CIR: [[CMP4:%[0-9]+]] = cir.cmp(eq, [[CVAL2]], [[DVAL2]]) : !s32i, !cir.bool -// CIR: [[INNER2:%[0-9]+]] = cir.ternary([[CMP4]], true { -// CIR: {{%[0-9]+}} = cir.const #true -// CIR: cir.yield {{%[0-9]+}} : !cir.bool -// CIR: }, false { -// CIR: {{%[0-9]+}} = cir.const #false -// CIR: cir.yield {{%[0-9]+}} : !cir.bool -// CIR: }) : (!cir.bool) -> !cir.bool -// CIR: cir.yield [[INNER2]] : !cir.bool +// CIR: cir.yield [[CMP4]] : !cir.bool // CIR: }) : (!cir.bool) -> !cir.bool // CIR: cir.store [[OR_RESULT]], [[X]] : !cir.bool, !cir.ptr<!cir.bool> // CIR: cir.return // LLVM-LABEL: define void @_Z2b3iiii( -// LLVM-SAME: i32 %[[ARG0:.*]], i32 %[[ARG1:.*]], i32 %[[ARG2:.*]], i32 %[[ARG3:.*]]) -// LLVM: %[[A_ADDR:.*]] = alloca i32 -// LLVM: %[[B_ADDR:.*]] = alloca i32 -// LLVM: %[[C_ADDR:.*]] = alloca i32 -// LLVM: %[[D_ADDR:.*]] = alloca i32 -// LLVM: %[[X:.*]] = alloca i8 +// LLVM-SAME: i32 %[[ARG0:[0-9]+]], i32 %[[ARG1:[0-9]+]], i32 %[[ARG2:[0-9]+]], i32 %[[ARG3:[0-9]+]]) +// LLVM: %[[A_ADDR:.*]] = alloca i32, i64 1 +// LLVM: %[[B_ADDR:.*]] = alloca i32, i64 1 +// LLVM: %[[C_ADDR:.*]] = alloca i32, i64 1 +// LLVM: %[[D_ADDR:.*]] = alloca i32, i64 1 +// LLVM: %[[X:.*]] = alloca i8, i64 1 // LLVM: store i32 %[[ARG0]], ptr %[[A_ADDR]] // LLVM: store i32 %[[ARG1]], ptr %[[B_ADDR]] // LLVM: store i32 %[[ARG2]], ptr %[[C_ADDR]] @@ -717,51 +674,35 @@ void b3(int a, int b, int c, int d) { // LLVM: %[[A_VAL:.*]] = load i32, ptr %[[A_ADDR]] // LLVM: %[[B_VAL:.*]] = load i32, ptr %[[B_ADDR]] // LLVM: %[[CMP1:.*]] = icmp eq i32 %[[A_VAL]], %[[B_VAL]] -// LLVM: br i1 %[[CMP1]], label %[[AND_LHS_TRUE:.*]], label %[[AND_END:.*]] -// LLVM: [[AND_LHS_TRUE]]: +// LLVM: br i1 %[[CMP1]], label %[[AND_TRUE:[0-9]+]], label %[[AND_FALSE:[0-9]+]] +// LLVM: [[AND_TRUE]]: // LLVM: %[[C_VAL:.*]] = load i32, ptr %[[C_ADDR]] // LLVM: %[[D_VAL:.*]] = load i32, ptr %[[D_ADDR]] // LLVM: %[[CMP2:.*]] = icmp eq i32 %[[C_VAL]], %[[D_VAL]] -// LLVM: br i1 %[[CMP2]], label %[[CD_TRUE:.*]], label %[[CD_FALSE:.*]] -// LLVM: [[CD_TRUE]]: -// LLVM: br label %[[AND_RHS_END:.*]] -// LLVM: [[CD_FALSE]]: -// LLVM: br label %[[AND_RHS_END]] -// LLVM: [[AND_RHS_END]]: -// LLVM: %[[RHS_PHI:.*]] = phi i1 [ false, %[[CD_FALSE]] ], [ true, %[[CD_TRUE]] ] -// LLVM: br label %[[AND_DONE:.*]] -// LLVM: [[AND_DONE]]: -// LLVM: br label %[[MERGE_BB2:.*]] -// LLVM: [[AND_END]]: -// LLVM: %[[AND_RESULT:.*]] = phi i1 [ false, %[[AND_END]] ], [ %[[RHS_PHI]], %[[AND_DONE]] ] -// LLVM: br label %[[STORE_X:.*]] -// LLVM: [[STORE_X]]: -// LLVM: %[[ZEXT_AND:.*]] = zext i1 %[[AND_RESULT]] to i8 +// LLVM: br label %[[AND_MERGE:[0-9]+]] +// LLVM: [[AND_FALSE]]: +// LLVM: br label %[[AND_MERGE]] +// LLVM: [[AND_MERGE]]: +// LLVM: %[[AND_PHI:.*]] = phi i1 [ false, %[[AND_FALSE]] ], [ %[[CMP2]], %[[AND_TRUE]] ] +// LLVM: br label %[[AND_CONT:[0-9]+]] +// LLVM: [[AND_CONT]]: +// LLVM: %[[ZEXT_AND:.*]] = zext i1 %[[AND_PHI]] to i8 // LLVM: store i8 %[[ZEXT_AND]], ptr %[[X]] // LLVM: %[[A_VAL2:.*]] = load i32, ptr %[[A_ADDR]] // LLVM: %[[B_VAL2:.*]] = load i32, ptr %[[B_ADDR]] // LLVM: %[[CMP3:.*]] = icmp eq i32 %[[A_VAL2]], %[[B_VAL2]] -// LLVM: br i1 %[[CMP3]], label %[[OR_TRUE:.*]], label %[[OR_RHS:.*]] +// LLVM: br i1 %[[CMP3]], label %[[OR_TRUE:[0-9]+]], label %[[OR_FALSE:[0-9]+]] // LLVM: [[OR_TRUE]]: -// LLVM: br label %[[OR_END:.*]] -// LLVM: [[OR_RHS]]: +// LLVM: br label %[[OR_MERGE:[0-9]+]] +// LLVM: [[OR_FALSE]]: // LLVM: %[[C_VAL2:.*]] = load i32, ptr %[[C_ADDR]] // LLVM: %[[D_VAL2:.*]] = load i32, ptr %[[D_ADDR]] // LLVM: %[[CMP4:.*]] = icmp eq i32 %[[C_VAL2]], %[[D_VAL2]] -// LLVM: br i1 %[[CMP4]], label %[[CD_TRUE2:.*]], label %[[CD_FALSE2:.*]] -// LLVM: [[CD_TRUE2]]: -// LLVM: br label %[[OR_RHS_END:.*]] -// LLVM: [[CD_FALSE2]]: -// LLVM: br label %[[OR_RHS_END]] -// LLVM: [[OR_RHS_END]]: -// LLVM: %[[RHS_PHI2:.*]] = phi i1 [ false, %[[CD_FALSE2]] ], [ true, %[[CD_TRUE2]] ] -// LLVM: br label %[[OR_RHS_DONE:.*]] -// LLVM: [[OR_RHS_DONE]]: -// LLVM: br label %[[OR_END]] -// LLVM: [[OR_END]]: -// LLVM: %[[OR_RESULT:.*]] = phi i1 [ %[[RHS_PHI2]], %[[OR_RHS_DONE]] ], [ true, %[[OR_TRUE]] ] -// LLVM: br label %[[FINAL_STORE:.*]] -// LLVM: [[FINAL_STORE]]: -// LLVM: %[[ZEXT_OR:.*]] = zext i1 %[[OR_RESULT]] to i8 +// LLVM: br label %[[OR_MERGE]] +// LLVM: [[OR_MERGE]]: +// LLVM: %[[OR_PHI:.*]] = phi i1 [ %[[CMP4]], %[[OR_FALSE]] ], [ true, %[[OR_TRUE]] ] +// LLVM: br label %[[OR_CONT:[0-9]+]] +// LLVM: [[OR_CONT]]: +// LLVM: %[[ZEXT_OR:.*]] = zext i1 %[[OR_PHI]] to i8 // LLVM: store i8 %[[ZEXT_OR]], ptr %[[X]] // LLVM: ret void diff --git a/clang/test/CIR/CodeGen/ternary.cpp b/clang/test/CIR/CodeGen/ternary.cpp index 28aaa2d4a85d1..92dbefa7a9596 100644 --- a/clang/test/CIR/CodeGen/ternary.cpp +++ b/clang/test/CIR/CodeGen/ternary.cpp @@ -15,35 +15,23 @@ int x(int y) { // CIR: [[YVAL:%[0-9]+]] = cir.load [[Y]] : !cir.ptr<!s32i>, !s32i // CIR: [[ZERO:%[0-9]+]] = cir.const #cir.int<0> : !s32i // CIR: [[CMP:%[0-9]+]] = cir.cmp(gt, [[YVAL]], [[ZERO]]) : !s32i, !cir.bool -// CIR: [[TERNARY_RES:%[0-9]+]] = cir.ternary([[CMP]], true { // CIR: [[THREE:%[0-9]+]] = cir.const #cir.int<3> : !s32i -// CIR: cir.yield [[THREE]] : !s32i -// CIR: }, false { // CIR: [[FIVE:%[0-9]+]] = cir.const #cir.int<5> : !s32i -// CIR: cir.yield [[FIVE]] : !s32i -// CIR: }) : (!cir.bool) -> !s32i -// CIR: cir.store [[TERNARY_RES]], [[RETVAL]] : !s32i, !cir.ptr<!s32i> +// CIR: [[SELECT_RES:%[0-9]+]] = cir.select if [[CMP]] then [[THREE]] else [[FIVE]] : (!cir.bool, !s32i, !s32i) -> !s32i +// CIR: cir.store [[SELECT_RES]], [[RETVAL]] : !s32i, !cir.ptr<!s32i> // CIR: [[RETVAL_VAL:%[0-9]+]] = cir.load [[RETVAL]] : !cir.ptr<!s32i>, !s32i // CIR: cir.return [[RETVAL_VAL]] : !s32i // LLVM-LABEL: define i32 @_Z1xi( -// LLVM-SAME: i32 %[[ARG0:.*]]) -// LLVM: %[[Y:.*]] = alloca i32 -// LLVM: %[[RETVAL:.*]] = alloca i32 -// LLVM: store i32 %[[ARG0]], ptr %[[Y]] -// LLVM: %[[YVAL:.*]] = load i32, ptr %[[Y]] +// LLVM-SAME: i32 %[[ARG0:[0-9]+]]) +// LLVM: %[[Y:.*]] = alloca i32, i64 1, align 4 +// LLVM: %[[RETVAL:.*]] = alloca i32, i64 1, align 4 +// LLVM: store i32 %[[ARG0]], ptr %[[Y]], align 4 +// LLVM: %[[YVAL:.*]] = load i32, ptr %[[Y]], align 4 // LLVM: %[[CMP:.*]] = icmp sgt i32 %[[YVAL]], 0 -// LLVM: br i1 %[[CMP]], label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]] -// LLVM: [[TRUE_BB]]: -// LLVM: br label %[[MERGE_BB:.*]] -// LLVM: [[FALSE_BB]]: -// LLVM: br label %[[MERGE_BB]] -// LLVM: [[MERGE_BB]]: -// LLVM: %[[PHI:.*]] = phi i32 [ 5, %[[FALSE_BB]] ], [ 3, %[[TRUE_BB]] ] -// LLVM: br label %[[FINAL_BB:.*]] -// LLVM: [[FINAL_BB]]: -// LLVM: store i32 %[[PHI]], ptr %[[RETVAL]] -// LLVM: %[[RESULT:.*]] = load i32, ptr %[[RETVAL]] +// LLVM: %[[SELECT:.*]] = select i1 %[[CMP]], i32 3, i32 5 +// LLVM: store i32 %[[SELECT]], ptr %[[RETVAL]], align 4 +// LLVM: %[[RESULT:.*]] = load i32, ptr %[[RETVAL]], align 4 // LLVM: ret i32 %[[RESULT]] int foo(int a, int b) { _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits