diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index 4897c29b58a1f..0d364e1bb1595 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -2394,6 +2394,180 @@ LValue CIRGenFunction::emitPredefinedLValue(const PredefinedExpr *e) { return emitStringLiteralLValue(sl, gvName); } +LValue CIRGenFunction::emitOpaqueValueLValue(const OpaqueValueExpr *e) { + assert(OpaqueValueMappingData::shouldBindAsLValue(e)); + return getOrCreateOpaqueLValueMapping(e); +} + +namespace { +// 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. +std::optional handleConditionalOperatorLValueSimpleCase( + CIRGenFunction &cgf, const AbstractConditionalOperator *e) { + const Expr *condExpr = e->getCond(); + llvm::APSInt condExprVal; + if (!cgf.constantFoldsToSimpleInteger(condExpr, condExprVal)) + return std::nullopt; + + const Expr *live = e->getTrueExpr(), *dead = e->getFalseExpr(); + if (!condExprVal.getBoolValue()) + std::swap(live, dead); + + if (cgf.containsLabel(dead)) + return std::nullopt; + + // If the true case is live, we need to track its region. + assert(!cir::MissingFeatures::incrementProfileCounter()); + assert(!cir::MissingFeatures::pgoUse()); + // If a throw expression we emit it and return an undefined lvalue + // because it can't be used. + if (auto *throwExpr = dyn_cast(live->IgnoreParens())) { + cgf.emitCXXThrowExpr(throwExpr); + // Return an undefined lvalue - the throw terminates execution + // so this value will never actually be used + mlir::Type elemTy = cgf.convertType(dead->getType()); + mlir::Value undefPtr = + cgf.getBuilder().getNullPtr(cgf.getBuilder().getPointerTo(elemTy), + cgf.getLoc(throwExpr->getSourceRange())); + return cgf.makeAddrLValue(Address(undefPtr, elemTy, CharUnits::One()), + dead->getType()); + } + return cgf.emitLValue(live); +} + +/// 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 emitLValueOrThrowExpression(CIRGenFunction &cgf, + const Expr *operand) { + if (auto *throwExpr = dyn_cast(operand->IgnoreParens())) { + cgf.emitCXXThrowExpr(throwExpr); + return std::nullopt; + } + + return cgf.emitLValue(operand); +} +} // namespace + +// Create and generate the 3 blocks for a conditional operator. +// Leaves the 'current block' in the continuation basic block. +template +CIRGenFunction::ConditionalInfo +CIRGenFunction::emitConditionalBlocks(const AbstractConditionalOperator *e, + const FuncTy &branchGenFunc) { + ConditionalInfo info; + ConditionalEvaluation eval(*this); + mlir::Location loc = getLoc(e->getSourceRange()); + CIRGenBuilderTy &builder = getBuilder(); + + mlir::Value condV = emitOpOnBoolExpr(loc, e->getCond()); + SmallVector insertPoints{}; + mlir::Type yieldTy{}; + + auto emitBranch = [&](mlir::OpBuilder &b, mlir::Location loc, + const Expr *expr, std::optional &resultLV) { + CIRGenFunction::LexicalScope lexScope{*this, loc, b.getInsertionBlock()}; + curLexScope->setAsTernary(); + + assert(!cir::MissingFeatures::incrementProfileCounter()); + eval.beginEvaluation(); + resultLV = branchGenFunc(*this, expr); + mlir::Value resultPtr = resultLV ? resultLV->getPointer() : mlir::Value(); + eval.endEvaluation(); + + if (resultPtr) { + yieldTy = resultPtr.getType(); + cir::YieldOp::create(b, loc, resultPtr); + } else { + // If LHS or RHS is a void expression we need + // to patch arms as to properly match yield types. + // If the current block's terminator is an UnreachableOp (from a throw), + // we don't need a yield + if (builder.getInsertionBlock()->mightHaveTerminator()) { + mlir::Operation *terminator = + builder.getInsertionBlock()->getTerminator(); + if (isa_and_nonnull(terminator)) + insertPoints.push_back(b.saveInsertionPoint()); + } + } + }; + + info.result = cir::TernaryOp::create( + builder, loc, condV, + /*trueBuilder=*/ + [&](mlir::OpBuilder &b, mlir::Location loc) { + emitBranch(b, loc, e->getTrueExpr(), info.lhs); + }, + /*falseBuilder=*/ + [&](mlir::OpBuilder &b, mlir::Location loc) { + emitBranch(b, loc, e->getFalseExpr(), info.rhs); + }) + .getResult(); + + // If both arms are void, so be it. + if (!yieldTy) + yieldTy = 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 (!yieldTy) { + cir::YieldOp::create(builder, loc); + } else { // Block returns: set null yield value. + mlir::Value op0 = builder.getNullValue(yieldTy, loc); + cir::YieldOp::create(builder, 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 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 with non-simple lvalue"); + return LValue(); + } + + 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; +} + /// An LValue is a candidate for having its loads and stores be made atomic if /// we are operating under /volatile:ms *and* the LValue itself is volatile and /// performing such an operation can be performed without a libcall. diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index ba36cbeeb620e..25a46df406df4 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -822,6 +822,10 @@ LValue CIRGenFunction::emitLValue(const Expr *e) { std::string("l-value not implemented for '") + e->getStmtClassName() + "'"); return LValue(); + case Expr::ConditionalOperatorClass: + return emitConditionalOperatorLValue(cast(e)); + case Expr::BinaryConditionalOperatorClass: + return emitConditionalOperatorLValue(cast(e)); case Expr::ArraySubscriptExprClass: return emitArraySubscriptExpr(cast(e)); case Expr::UnaryOperatorClass: @@ -866,6 +870,8 @@ LValue CIRGenFunction::emitLValue(const Expr *e) { return emitCastLValue(cast(e)); case Expr::MaterializeTemporaryExprClass: return emitMaterializeTemporaryExpr(cast(e)); + case Expr::OpaqueValueExprClass: + return emitOpaqueValueLValue(cast(e)); case Expr::ChooseExprClass: return emitLValue(cast(e)->getChosenSubExpr()); } diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index 3c36f5c697118..e265811e94fea 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -1518,6 +1518,10 @@ class CIRGenFunction : public CIRGenTypeCache { LValue emitMemberExpr(const MemberExpr *e); + LValue emitOpaqueValueLValue(const OpaqueValueExpr *e); + + LValue emitConditionalOperatorLValue(const AbstractConditionalOperator *expr); + /// Given an expression with a pointer type, emit the value and compute our /// best estimate of the alignment of the pointee. /// diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index f03b891079f63..0c0d91501219b 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -1978,13 +1978,19 @@ void cir::TernaryOp::build( result.addOperands(cond); OpBuilder::InsertionGuard guard(builder); Region *trueRegion = result.addRegion(); - Block *block = builder.createBlock(trueRegion); + builder.createBlock(trueRegion); trueBuilder(builder, result.location); Region *falseRegion = result.addRegion(); builder.createBlock(falseRegion); falseBuilder(builder, result.location); - auto yield = dyn_cast(block->getTerminator()); + // Get result type from whichever branch has a yield (the other may have + // unreachable from a throw expression) + auto yield = + dyn_cast_or_null(trueRegion->back().getTerminator()); + if (!yield) + yield = dyn_cast_or_null(falseRegion->back().getTerminator()); + assert((yield && yield.getNumOperands() <= 1) && "expected zero or one result type"); if (yield.getNumOperands() == 1) diff --git a/clang/lib/CIR/Dialect/Transforms/FlattenCFG.cpp b/clang/lib/CIR/Dialect/Transforms/FlattenCFG.cpp index 26e5c0572f12e..8589a2eefa92f 100644 --- a/clang/lib/CIR/Dialect/Transforms/FlattenCFG.cpp +++ b/clang/lib/CIR/Dialect/Transforms/FlattenCFG.cpp @@ -505,10 +505,19 @@ class CIRTernaryOpFlattening : public mlir::OpRewritePattern { Block *trueBlock = &trueRegion.front(); mlir::Operation *trueTerminator = trueRegion.back().getTerminator(); rewriter.setInsertionPointToEnd(&trueRegion.back()); - auto trueYieldOp = dyn_cast(trueTerminator); - rewriter.replaceOpWithNewOp(trueYieldOp, trueYieldOp.getArgs(), - continueBlock); + // Handle both yield and unreachable terminators (throw expressions) + if (auto trueYieldOp = dyn_cast(trueTerminator)) { + rewriter.replaceOpWithNewOp(trueYieldOp, trueYieldOp.getArgs(), + continueBlock); + } else if (isa(trueTerminator)) { + // Terminator is unreachable (e.g., from throw), just keep it + } else { + trueTerminator->emitError("unexpected terminator in ternary true region, " + "expected yield or unreachable, got: ") + << trueTerminator->getName(); + return mlir::failure(); + } rewriter.inlineRegionBefore(trueRegion, continueBlock); Block *falseBlock = continueBlock; @@ -517,9 +526,19 @@ class CIRTernaryOpFlattening : public mlir::OpRewritePattern { falseBlock = &falseRegion.front(); mlir::Operation *falseTerminator = falseRegion.back().getTerminator(); rewriter.setInsertionPointToEnd(&falseRegion.back()); - auto falseYieldOp = dyn_cast(falseTerminator); - rewriter.replaceOpWithNewOp(falseYieldOp, falseYieldOp.getArgs(), - continueBlock); + + // Handle both yield and unreachable terminators (throw expressions) + if (auto falseYieldOp = dyn_cast(falseTerminator)) { + rewriter.replaceOpWithNewOp( + falseYieldOp, falseYieldOp.getArgs(), continueBlock); + } else if (isa(falseTerminator)) { + // Terminator is unreachable (e.g., from throw), just keep it + } else { + falseTerminator->emitError("unexpected terminator in ternary false " + "region, expected yield or unreachable, got: ") + << falseTerminator->getName(); + return mlir::failure(); + } rewriter.inlineRegionBefore(falseRegion, continueBlock); rewriter.setInsertionPointToEnd(condBlock); diff --git a/clang/test/CIR/CodeGen/opaque.cpp b/clang/test/CIR/CodeGen/opaque.cpp index 028bfd9ef4cd0..eac0dfa3755ab 100644 --- a/clang/test/CIR/CodeGen/opaque.cpp +++ b/clang/test/CIR/CodeGen/opaque.cpp @@ -154,3 +154,166 @@ void foo3() { // OGCG: [[COND_END]]: // OGCG: %[[RESULT:.*]] = phi i32 [ %[[TMP_A]], %[[COND_TRUE]] ], [ %[[TMP_B]], %[[COND_FALSE]] ] // OGCG: store i32 %[[RESULT]], ptr %[[C_ADDR]], align 4 + +void test_gnu_binary_lvalue_assign() { + int a = 5; + int b = 10; + (a ?: b) = 42; +} + +// CIR-LABEL: cir.func{{.*}} @_Z29test_gnu_binary_lvalue_assignv( +// CIR: %[[A:.*]] = cir.alloca !s32i, !cir.ptr, ["a", init] +// CIR: %[[B:.*]] = cir.alloca !s32i, !cir.ptr, ["b", init] +// CIR: %[[A_VAL:.*]] = cir.load{{.*}} %[[A]] : !cir.ptr, !s32i +// CIR: %[[A_BOOL:.*]] = cir.cast int_to_bool %[[A_VAL]] : !s32i -> !cir.bool +// CIR: %[[TERNARY_PTR:.*]] = cir.ternary(%[[A_BOOL]], true { +// CIR: cir.yield %[[A]] : !cir.ptr +// CIR: }, false { +// CIR: cir.yield %[[B]] : !cir.ptr +// CIR: }) : (!cir.bool) -> !cir.ptr +// CIR: cir.store{{.*}} %{{.*}}, %[[TERNARY_PTR]] : !s32i, !cir.ptr + +// LLVM-LABEL: define{{.*}} void @_Z29test_gnu_binary_lvalue_assignv( +// LLVM: %[[A:.*]] = alloca i32 +// LLVM: %[[B:.*]] = alloca i32 +// LLVM: %[[A_VAL:.*]] = load i32, ptr %[[A]] +// LLVM: %[[COND:.*]] = icmp ne i32 %[[A_VAL]], 0 +// LLVM: br i1 %[[COND]], 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_PTR:.*]] = phi ptr [ %[[B]], %[[FALSE_BB]] ], [ %[[A]], %[[TRUE_BB]] ] +// LLVM: br label %[[CONT_BB:.*]] +// LLVM: [[CONT_BB]]: +// LLVM: store i32 42, ptr %[[PHI_PTR]] + +// OGCG-LABEL: define{{.*}} void @_Z29test_gnu_binary_lvalue_assignv( +// OGCG: %[[A:.*]] = alloca i32 +// OGCG: %[[B:.*]] = alloca i32 +// OGCG: %[[A_VAL:.*]] = load i32, ptr %[[A]] +// OGCG: %[[COND:.*]] = icmp ne i32 %[[A_VAL]], 0 +// OGCG: br i1 %[[COND]], label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]] +// OGCG: [[TRUE_BB]]: +// OGCG: br label %[[MERGE_BB:.*]] +// OGCG: [[FALSE_BB]]: +// OGCG: br label %[[MERGE_BB]] +// OGCG: [[MERGE_BB]]: +// OGCG: %[[PHI_PTR:.*]] = phi ptr [ %[[A]], %[[TRUE_BB]] ], [ %[[B]], %[[FALSE_BB]] ] +// OGCG: store i32 42, ptr %[[PHI_PTR]] + +void test_gnu_binary_lvalue_compound() { + int a = 7; + int b = 14; + (a ?: b) += 5; +} + +// CIR-LABEL: cir.func{{.*}} @_Z31test_gnu_binary_lvalue_compoundv( +// CIR: %[[A:.*]] = cir.alloca !s32i, !cir.ptr, ["a", init] +// CIR: %[[B:.*]] = cir.alloca !s32i, !cir.ptr, ["b", init] +// CIR: %[[A_VAL:.*]] = cir.load{{.*}} %[[A]] : !cir.ptr, !s32i +// CIR: %[[A_BOOL:.*]] = cir.cast int_to_bool %[[A_VAL]] : !s32i -> !cir.bool +// CIR: %[[LVAL_PTR:.*]] = cir.ternary(%[[A_BOOL]], true { +// CIR: cir.yield %[[A]] : !cir.ptr +// CIR: }, false { +// CIR: cir.yield %[[B]] : !cir.ptr +// CIR: }) : (!cir.bool) -> !cir.ptr +// CIR: %[[OLD_VAL:.*]] = cir.load{{.*}} %[[LVAL_PTR]] +// CIR: %[[NEW_VAL:.*]] = cir.binop(add, %[[OLD_VAL]], %{{.*}}) +// CIR: cir.store{{.*}} %[[NEW_VAL]], %[[LVAL_PTR]] + +// LLVM-LABEL: define{{.*}} void @_Z31test_gnu_binary_lvalue_compoundv( +// LLVM: %[[A:.*]] = alloca i32 +// LLVM: %[[B:.*]] = alloca i32 +// LLVM: %[[A_VAL:.*]] = load i32, ptr %[[A]] +// LLVM: %[[COND:.*]] = icmp ne i32 %[[A_VAL]], 0 +// LLVM: br i1 %[[COND]], 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: %[[PTR:.*]] = phi ptr [ %[[B]], %[[FALSE_BB]] ], [ %[[A]], %[[TRUE_BB]] ] +// LLVM: br label %[[CONT:.*]] +// LLVM: [[CONT]]: +// LLVM: %[[OLD:.*]] = load i32, ptr %[[PTR]] +// LLVM: %[[NEW:.*]] = add{{.*}} i32 %[[OLD]], 5 +// LLVM: store i32 %[[NEW]], ptr %[[PTR]] + +// OGCG-LABEL: define{{.*}} void @_Z31test_gnu_binary_lvalue_compoundv( +// OGCG: %[[A:.*]] = alloca i32 +// OGCG: %[[B:.*]] = alloca i32 +// OGCG: %[[A_VAL:.*]] = load i32, ptr %[[A]] +// OGCG: %[[COND:.*]] = icmp ne i32 %[[A_VAL]], 0 +// OGCG: br i1 %[[COND]], label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]] +// OGCG: [[TRUE_BB]]: +// OGCG: br label %[[MERGE_BB:.*]] +// OGCG: [[FALSE_BB]]: +// OGCG: br label %[[MERGE_BB]] +// OGCG: [[MERGE_BB]]: +// OGCG: %[[PTR:.*]] = phi ptr [ %[[A]], %[[TRUE_BB]] ], [ %[[B]], %[[FALSE_BB]] ] +// OGCG: %[[OLD:.*]] = load i32, ptr %[[PTR]] +// OGCG: %[[NEW:.*]] = add{{.*}} i32 %[[OLD]], 5 +// OGCG: store i32 %[[NEW]], ptr %[[PTR]] + +void test_gnu_binary_lvalue_ptr() { + int x = 1, y = 2; + int *p = &x; + int *q = nullptr; + *(p ?: q) = 99; +} + +// CIR-LABEL: cir.func{{.*}} @_Z26test_gnu_binary_lvalue_ptrv( +// CIR: %[[X:.*]] = cir.alloca !s32i, !cir.ptr, ["x", init] +// CIR: %[[Y:.*]] = cir.alloca !s32i, !cir.ptr, ["y", init] +// CIR: %[[P:.*]] = cir.alloca !cir.ptr, !cir.ptr>, ["p", init] +// CIR: %[[Q:.*]] = cir.alloca !cir.ptr, !cir.ptr>, ["q", init] +// CIR: %[[P_VAL:.*]] = cir.load{{.*}} %[[P]] +// CIR: %[[P_BOOL:.*]] = cir.cast ptr_to_bool %[[P_VAL]] +// CIR: %[[PTR_RESULT:.*]] = cir.ternary(%[[P_BOOL]], true { +// CIR: %[[P_LOAD:.*]] = cir.load{{.*}} %[[P]] +// CIR: cir.yield %[[P_LOAD]] : !cir.ptr +// CIR: }, false { +// CIR: %[[Q_LOAD:.*]] = cir.load{{.*}} %[[Q]] +// CIR: cir.yield %[[Q_LOAD]] : !cir.ptr +// CIR: }) : (!cir.bool) -> !cir.ptr +// CIR: cir.store{{.*}} %{{.*}}, %[[PTR_RESULT]] + +// LLVM-LABEL: define{{.*}} void @_Z26test_gnu_binary_lvalue_ptrv( +// LLVM: %[[X:.*]] = alloca i32 +// LLVM: %[[Y:.*]] = alloca i32 +// LLVM: %[[P:.*]] = alloca ptr +// LLVM: %[[Q:.*]] = alloca ptr +// LLVM: %[[P_VAL:.*]] = load ptr, ptr %[[P]] +// LLVM: %[[COND:.*]] = icmp ne ptr %[[P_VAL]], null +// LLVM: br i1 %[[COND]], label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]] +// LLVM: [[TRUE_BB]]: +// LLVM: %[[P_LOAD:.*]] = load ptr, ptr %[[P]] +// LLVM: br label %[[MERGE_BB:.*]] +// LLVM: [[FALSE_BB]]: +// LLVM: %[[Q_LOAD:.*]] = load ptr, ptr %[[Q]] +// LLVM: br label %[[MERGE_BB]] +// LLVM: [[MERGE_BB]]: +// LLVM: %[[PHI:.*]] = phi ptr [ %[[Q_LOAD]], %[[FALSE_BB]] ], [ %[[P_LOAD]], %[[TRUE_BB]] ] +// LLVM: br label %[[CONT:.*]] +// LLVM: [[CONT]]: +// LLVM: store i32 99, ptr %[[PHI]] + +// OGCG-LABEL: define{{.*}} void @_Z26test_gnu_binary_lvalue_ptrv( +// OGCG: %[[X:.*]] = alloca i32 +// OGCG: %[[Y:.*]] = alloca i32 +// OGCG: %[[P:.*]] = alloca ptr +// OGCG: %[[Q:.*]] = alloca ptr +// OGCG: %[[P_VAL:.*]] = load ptr, ptr %[[P]] +// OGCG: %[[COND:.*]] = icmp ne ptr %[[P_VAL]], null +// OGCG: br i1 %[[COND]], label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]] +// OGCG: [[TRUE_BB]]: +// OGCG: %[[P_LOAD:.*]] = load ptr, ptr %[[P]] +// OGCG: br label %[[MERGE_BB:.*]] +// OGCG: [[FALSE_BB]]: +// OGCG: %[[Q_LOAD:.*]] = load ptr, ptr %[[Q]] +// OGCG: br label %[[MERGE_BB]] +// OGCG: [[MERGE_BB]]: +// OGCG: %[[PHI:.*]] = phi ptr [ %[[P_LOAD]], %[[TRUE_BB]] ], [ %[[Q_LOAD]], %[[FALSE_BB]] ] +// OGCG: store i32 99, ptr %[[PHI]] diff --git a/clang/test/CIR/CodeGen/ternary-throw.cpp b/clang/test/CIR/CodeGen/ternary-throw.cpp new file mode 100644 index 0000000000000..fb8897fa18a74 --- /dev/null +++ b/clang/test/CIR/CodeGen/ternary-throw.cpp @@ -0,0 +1,197 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -fexceptions -fcxx-exceptions -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 -fexceptions -fcxx-exceptions -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fexceptions -fcxx-exceptions -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG + +const int& test_cond_throw_false(bool flag) { + const int a = 10; + return flag ? a : throw 0; +} + +// CIR-LABEL: cir.func{{.*}} @_Z21test_cond_throw_falseb( +// CIR: %[[FLAG:.*]] = cir.alloca !cir.bool, !cir.ptr, ["flag", init] +// CIR: %[[A:.*]] = cir.alloca !s32i, !cir.ptr, ["a", init, const] +// CIR: %[[TEN:.*]] = cir.const #cir.int<10> : !s32i +// CIR: cir.store{{.*}} %[[TEN]], %[[A]] : !s32i, !cir.ptr +// CIR: %[[FLAG_VAL:.*]] = cir.load{{.*}} %[[FLAG]] : !cir.ptr, !cir.bool +// CIR: %[[RESULT:.*]] = cir.ternary(%[[FLAG_VAL]], true { +// CIR: cir.yield %[[A]] : !cir.ptr +// CIR: }, false { +// CIR: %[[EXCEPTION:.*]] = cir.alloc.exception{{.*}} -> !cir.ptr +// CIR: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i +// CIR: cir.store{{.*}} %[[ZERO]], %[[EXCEPTION]] : !s32i, !cir.ptr +// CIR: cir.throw %[[EXCEPTION]] : !cir.ptr, @_ZTIi +// CIR: cir.unreachable +// CIR: }) : (!cir.bool) -> !cir.ptr + +// LLVM-LABEL: define{{.*}} ptr @_Z21test_cond_throw_falseb( +// LLVM: %[[FLAG_ALLOCA:.*]] = alloca i8 +// LLVM: %[[RET_ALLOCA:.*]] = alloca ptr +// LLVM: %[[A_ALLOCA:.*]] = alloca i32 +// LLVM: %[[ZEXT:.*]] = zext i1 %{{.*}} to i8 +// LLVM: store i8 %[[ZEXT]], ptr %[[FLAG_ALLOCA]] +// LLVM: store i32 10, ptr %[[A_ALLOCA]] +// LLVM: %[[LOAD:.*]] = load i8, ptr %[[FLAG_ALLOCA]] +// LLVM: %[[BOOL:.*]] = trunc i8 %[[LOAD]] to i1 +// LLVM: br i1 %[[BOOL]], label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]] +// LLVM: [[TRUE_BB]]: +// LLVM: br label %[[PHI_BB:.*]] +// LLVM: [[FALSE_BB]]: +// LLVM: %[[EXC:.*]] = call{{.*}} ptr @__cxa_allocate_exception +// LLVM: store i32 0, ptr %[[EXC]] +// LLVM: call void @__cxa_throw(ptr %[[EXC]], ptr @_ZTIi +// LLVM: unreachable +// LLVM: [[PHI_BB]]: +// LLVM: %[[PHI:.*]] = phi ptr [ %[[A_ALLOCA]], %[[TRUE_BB]] ] +// LLVM: br label %[[CONT_BB:.*]] +// LLVM: [[CONT_BB]]: +// LLVM: store ptr %[[A_ALLOCA]], ptr %[[RET_ALLOCA]] +// LLVM: %[[RET:.*]] = load ptr, ptr %[[RET_ALLOCA]] +// LLVM: ret ptr %[[RET]] + +// OGCG-LABEL: define{{.*}} ptr @_Z21test_cond_throw_falseb( +// OGCG: %{{.*}} = alloca i8 +// OGCG: %[[A:.*]] = alloca i32 +// OGCG: store i32 10, ptr %[[A]] +// OGCG: %{{.*}} = load i8, ptr %{{.*}} +// OGCG: %[[BOOL:.*]] = trunc i8 %{{.*}} to i1 +// OGCG: br i1 %[[BOOL]], label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]] +// OGCG: [[TRUE_BB]]: +// OGCG: br label %[[END:.*]] +// OGCG: [[FALSE_BB]]: +// OGCG: %{{.*}} = call{{.*}} ptr @__cxa_allocate_exception +// OGCG: store i32 0, ptr %{{.*}} +// OGCG: call void @__cxa_throw(ptr %{{.*}}, ptr @_ZTIi +// OGCG: unreachable +// OGCG: [[END]]: +// OGCG: ret ptr %[[A]] + +const int& test_cond_throw_true(bool flag) { + const int a = 10; + return flag ? throw 0 : a; +} + +// CIR-LABEL: cir.func{{.*}} @_Z20test_cond_throw_trueb( +// CIR: %[[FLAG:.*]] = cir.alloca !cir.bool, !cir.ptr, ["flag", init] +// CIR: %[[A:.*]] = cir.alloca !s32i, !cir.ptr, ["a", init, const] +// CIR: %[[TEN:.*]] = cir.const #cir.int<10> : !s32i +// CIR: cir.store{{.*}} %[[TEN]], %[[A]] : !s32i, !cir.ptr +// CIR: %[[FLAG_VAL:.*]] = cir.load{{.*}} %[[FLAG]] : !cir.ptr, !cir.bool +// CIR: %[[RESULT:.*]] = cir.ternary(%[[FLAG_VAL]], true { +// CIR: %[[EXCEPTION:.*]] = cir.alloc.exception{{.*}} -> !cir.ptr +// CIR: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i +// CIR: cir.store{{.*}} %[[ZERO]], %[[EXCEPTION]] : !s32i, !cir.ptr +// CIR: cir.throw %[[EXCEPTION]] : !cir.ptr, @_ZTIi +// CIR: cir.unreachable +// CIR: }, false { +// CIR: cir.yield %[[A]] : !cir.ptr +// CIR: }) : (!cir.bool) -> !cir.ptr + +// LLVM-LABEL: define{{.*}} ptr @_Z20test_cond_throw_trueb( +// LLVM: %[[FLAG_ALLOCA:.*]] = alloca i8 +// LLVM: %[[RET_ALLOCA:.*]] = alloca ptr +// LLVM: %[[A_ALLOCA:.*]] = alloca i32 +// LLVM: %[[ZEXT:.*]] = zext i1 %{{.*}} to i8 +// LLVM: store i8 %[[ZEXT]], ptr %[[FLAG_ALLOCA]] +// LLVM: store i32 10, ptr %[[A_ALLOCA]] +// LLVM: %[[LOAD:.*]] = load i8, ptr %[[FLAG_ALLOCA]] +// LLVM: %[[BOOL:.*]] = trunc i8 %[[LOAD]] to i1 +// LLVM: br i1 %[[BOOL]], label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]] +// LLVM: [[TRUE_BB]]: +// LLVM: %[[EXC:.*]] = call{{.*}} ptr @__cxa_allocate_exception +// LLVM: store i32 0, ptr %[[EXC]] +// LLVM: call void @__cxa_throw(ptr %[[EXC]], ptr @_ZTIi +// LLVM: unreachable +// LLVM: [[FALSE_BB]]: +// LLVM: br label %[[PHI_BB:.*]] +// LLVM: [[PHI_BB]]: +// LLVM: %[[PHI:.*]] = phi ptr [ %[[A_ALLOCA]], %[[FALSE_BB]] ] +// LLVM: br label %[[CONT_BB:.*]] +// LLVM: [[CONT_BB]]: +// LLVM: store ptr %[[A_ALLOCA]], ptr %[[RET_ALLOCA]] +// LLVM: %[[RET:.*]] = load ptr, ptr %[[RET_ALLOCA]] +// LLVM: ret ptr %[[RET]] + +// OGCG-LABEL: define{{.*}} ptr @_Z20test_cond_throw_trueb( +// OGCG: %{{.*}} = alloca i8 +// OGCG: %[[A:.*]] = alloca i32 +// OGCG: store i32 10, ptr %[[A]] +// OGCG: %{{.*}} = load i8, ptr %{{.*}} +// OGCG: %[[BOOL:.*]] = trunc i8 %{{.*}} to i1 +// OGCG: br i1 %[[BOOL]], label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]] +// OGCG: [[TRUE_BB]]: +// OGCG: %{{.*}} = call{{.*}} ptr @__cxa_allocate_exception +// OGCG: store i32 0, ptr %{{.*}} +// OGCG: call void @__cxa_throw(ptr %{{.*}}, ptr @_ZTIi +// OGCG: unreachable +// OGCG: [[FALSE_BB]]: +// OGCG: br label %[[END:.*]] +// OGCG: [[END]]: +// OGCG: ret ptr %[[A]] + +// Test constant folding with throw - compile-time true condition, dead throw in false branch +const int& test_cond_const_true_throw_false() { + const int a = 20; + return true ? a : throw 0; +} + +// CIR-LABEL: cir.func{{.*}} @_Z32test_cond_const_true_throw_falsev( +// CIR: %[[A:.*]] = cir.alloca !s32i, !cir.ptr, ["a", init, const] +// CIR: %[[TWENTY:.*]] = cir.const #cir.int<20> : !s32i +// CIR: cir.store{{.*}} %[[TWENTY]], %[[A]] : !s32i, !cir.ptr +// CIR-NOT: cir.ternary +// CIR-NOT: cir.throw +// CIR: cir.store %[[A]] +// CIR: %[[RET:.*]] = cir.load +// CIR: cir.return %[[RET]] : !cir.ptr + +// LLVM-LABEL: define{{.*}} ptr @_Z32test_cond_const_true_throw_falsev( +// LLVM: %[[A:.*]] = alloca i32 +// LLVM: store i32 20, ptr %[[A]] +// LLVM-NOT: br i1 +// LLVM-NOT: __cxa_throw +// LLVM: store ptr %[[A]] +// LLVM: %[[RET:.*]] = load ptr +// LLVM: ret ptr %[[RET]] + +// OGCG-LABEL: define{{.*}} ptr @_Z32test_cond_const_true_throw_falsev( +// OGCG: %[[A:.*]] = alloca i32 +// OGCG: store i32 20, ptr %[[A]] +// OGCG-NOT: br i1 +// OGCG-NOT: __cxa_throw +// OGCG: ret ptr %[[A]] + +// Test constant folding with throw - compile-time false condition, dead throw in true branch +const int& test_cond_const_false_throw_true() { + const int a = 30; + return false ? throw 0 : a; +} + +// CIR-LABEL: cir.func{{.*}} @_Z32test_cond_const_false_throw_truev( +// CIR: %[[A:.*]] = cir.alloca !s32i, !cir.ptr, ["a", init, const] +// CIR: %[[THIRTY:.*]] = cir.const #cir.int<30> : !s32i +// CIR: cir.store{{.*}} %[[THIRTY]], %[[A]] : !s32i, !cir.ptr +// CIR-NOT: cir.ternary +// CIR-NOT: cir.throw +// CIR: cir.store %[[A]] +// CIR: %[[RET:.*]] = cir.load +// CIR: cir.return %[[RET]] : !cir.ptr + +// LLVM-LABEL: define{{.*}} ptr @_Z32test_cond_const_false_throw_truev( +// LLVM: %[[A:.*]] = alloca i32 +// LLVM: store i32 30, ptr %[[A]] +// LLVM-NOT: br i1 +// LLVM-NOT: __cxa_throw +// LLVM: store ptr %[[A]] +// LLVM: %[[RET:.*]] = load ptr +// LLVM: ret ptr %[[RET]] + +// OGCG-LABEL: define{{.*}} ptr @_Z32test_cond_const_false_throw_truev( +// OGCG: %[[A:.*]] = alloca i32 +// OGCG: store i32 30, ptr %[[A]] +// OGCG-NOT: br i1 +// OGCG-NOT: __cxa_throw +// OGCG: ret ptr %[[A]] + diff --git a/clang/test/CIR/CodeGen/ternary.cpp b/clang/test/CIR/CodeGen/ternary.cpp index e7b72708e26a4..847c0b4a04009 100644 --- a/clang/test/CIR/CodeGen/ternary.cpp +++ b/clang/test/CIR/CodeGen/ternary.cpp @@ -10,11 +10,11 @@ int x(int y) { } // CIR-LABEL: cir.func{{.*}} @_Z1xi( -// CIR-SAME: %[[ARG0:.*]]: !s32i {{.*}}) -> !s32i -// CIR: [[Y:%.+]] = cir.alloca !s32i, !cir.ptr, ["y", init] {alignment = 4 : i64} -// CIR: [[RETVAL:%.+]] = cir.alloca !s32i, !cir.ptr, ["__retval"] {alignment = 4 : i64} +// CIR-SAME: %[[ARG0:.*]]: !s32i {{.*}}) -> !s32i{{.*}}{ +// CIR: [[Y:%.+]] = cir.alloca !s32i, !cir.ptr, ["y", init] +// CIR: [[RETVAL:%.+]] = cir.alloca !s32i, !cir.ptr, ["__retval"] // CIR: cir.store %[[ARG0]], [[Y]] : !s32i, !cir.ptr -// CIR: [[YVAL:%.+]] = cir.load align(4) [[Y]] : !cir.ptr, !s32i +// CIR: [[YVAL:%.+]] = cir.load{{.*}} [[Y]] : !cir.ptr, !s32i // CIR: [[ZERO:%.+]] = cir.const #cir.int<0> : !s32i // CIR: [[CMP:%.+]] = cir.cmp(gt, [[YVAL]], [[ZERO]]) : !s32i, !cir.bool // CIR: [[THREE:%.+]] = cir.const #cir.int<3> : !s32i @@ -52,21 +52,21 @@ int foo(int a, int b) { } // CIR-LABEL: cir.func{{.*}} @_Z3fooii( -// CIR-SAME: %[[ARG0:.*]]: !s32i {{.*}}, %[[ARG1:.*]]: !s32i {{.*}}) -> !s32i -// CIR: [[A:%.+]] = cir.alloca !s32i, !cir.ptr, ["a", init] {alignment = 4 : i64} -// CIR: [[B:%.+]] = cir.alloca !s32i, !cir.ptr, ["b", init] {alignment = 4 : i64} -// CIR: [[RETVAL:%.+]] = cir.alloca !s32i, !cir.ptr, ["__retval"] {alignment = 4 : i64} +// CIR-SAME: %[[ARG0:.*]]: !s32i {{.*}}, %[[ARG1:.*]]: !s32i {{.*}}) -> !s32i{{.*}}{ +// CIR: [[A:%.+]] = cir.alloca !s32i, !cir.ptr, ["a", init] +// CIR: [[B:%.+]] = cir.alloca !s32i, !cir.ptr, ["b", init] +// CIR: [[RETVAL:%.+]] = cir.alloca !s32i, !cir.ptr, ["__retval"] // CIR: cir.store %[[ARG0]], [[A]] : !s32i, !cir.ptr // CIR: cir.store %[[ARG1]], [[B]] : !s32i, !cir.ptr // CIR: cir.scope { -// CIR: [[ALOAD:%.+]] = cir.load align(4) [[A]] : !cir.ptr, !s32i -// CIR: [[BLOAD:%.+]] = cir.load align(4) [[B]] : !cir.ptr, !s32i +// CIR: [[ALOAD:%.+]] = cir.load{{.*}} [[A]] : !cir.ptr, !s32i +// CIR: [[BLOAD:%.+]] = cir.load{{.*}} [[B]] : !cir.ptr, !s32i // CIR: [[CMP:%.+]] = cir.cmp(lt, [[ALOAD]], [[BLOAD]]) : !s32i, !cir.bool // CIR: [[TERNARY_RES:%.+]] = cir.ternary([[CMP]], true { // CIR: [[ZERO:%.+]] = cir.const #cir.int<0> : !s32i // CIR: cir.yield [[ZERO]] : !s32i // CIR: }, false { -// CIR: [[ALOAD2:%.+]] = cir.load align(4) [[A]] : !cir.ptr, !s32i +// CIR: [[ALOAD2:%.+]] = cir.load{{.*}} [[A]] : !cir.ptr, !s32i // CIR: cir.yield [[ALOAD2]] : !s32i // CIR: }) : (!cir.bool) -> !s32i // CIR: [[CAST:%.+]] = cir.cast int_to_bool [[TERNARY_RES]] : !s32i -> !cir.bool @@ -145,3 +145,214 @@ int foo(int a, int b) { // OGCG: [[RETURN]]: // OGCG: %[[RET2:.*]] = load i32, ptr %[[RETVAL]] // OGCG: ret i32 %[[RET2]] + +void test_cond_lvalue_assign(bool flag) { + int a = 1; + int b = 2; + (flag ? a : b) = 42; +} + +// CIR-LABEL: cir.func{{.*}} @_Z23test_cond_lvalue_assignb( +// CIR: %[[FLAG:.*]] = cir.alloca !cir.bool, !cir.ptr, ["flag", init] +// CIR: %[[A:.*]] = cir.alloca !s32i, !cir.ptr, ["a", init] +// CIR: %[[B:.*]] = cir.alloca !s32i, !cir.ptr, ["b", init] +// CIR: %[[FLAG_VAL:.*]] = cir.load{{.*}} %[[FLAG]] +// CIR: %[[TERNARY_PTR:.*]] = cir.ternary(%[[FLAG_VAL]], true { +// CIR: cir.yield %[[A]] : !cir.ptr +// CIR: }, false { +// CIR: cir.yield %[[B]] : !cir.ptr +// CIR: }) : (!cir.bool) -> !cir.ptr +// CIR: cir.store{{.*}} %{{.*}}, %[[TERNARY_PTR]] : !s32i, !cir.ptr + +// LLVM-LABEL: define{{.*}} void @_Z23test_cond_lvalue_assignb( +// LLVM: %[[FLAG:.*]] = alloca i8 +// LLVM: %[[A:.*]] = alloca i32 +// LLVM: %[[B:.*]] = alloca i32 +// LLVM: %[[FLAG_VAL:.*]] = load i8, ptr %[[FLAG]] +// LLVM: %[[COND:.*]] = trunc i8 %[[FLAG_VAL]] to i1 +// LLVM: br i1 %[[COND]], 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_PTR:.*]] = phi ptr [ %[[B]], %[[FALSE_BB]] ], [ %[[A]], %[[TRUE_BB]] ] +// LLVM: br label %[[CONT_BB:.*]] +// LLVM: [[CONT_BB]]: +// LLVM: store i32 42, ptr %[[PHI_PTR]] + +// OGCG-LABEL: define{{.*}} void @_Z23test_cond_lvalue_assignb( +// OGCG: %[[FLAG:.*]] = alloca i8 +// OGCG: %[[A:.*]] = alloca i32 +// OGCG: %[[B:.*]] = alloca i32 +// OGCG: %[[FLAG_VAL:.*]] = load i8, ptr %[[FLAG]] +// OGCG: %[[COND:.*]] = trunc i8 %[[FLAG_VAL]] to i1 +// OGCG: br i1 %[[COND]], label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]] +// OGCG: [[TRUE_BB]]: +// OGCG: br label %[[MERGE_BB:.*]] +// OGCG: [[FALSE_BB]]: +// OGCG: br label %[[MERGE_BB]] +// OGCG: [[MERGE_BB]]: +// OGCG: %[[PHI_PTR:.*]] = phi ptr [ %[[A]], %[[TRUE_BB]] ], [ %[[B]], %[[FALSE_BB]] ] +// OGCG: store i32 42, ptr %[[PHI_PTR]] + +int& test_cond_lvalue_ref(bool cond, int x, int y) { + return cond ? x : y; +} + +// CIR-LABEL: cir.func{{.*}} @_Z20test_cond_lvalue_refbii( +// CIR: %[[COND:.*]] = cir.alloca !cir.bool, !cir.ptr, ["cond", init] +// CIR: %[[X:.*]] = cir.alloca !s32i, !cir.ptr, ["x", init] +// CIR: %[[Y:.*]] = cir.alloca !s32i, !cir.ptr, ["y", init] +// CIR: %[[COND_VAL:.*]] = cir.load{{.*}} %[[COND]] +// CIR: %[[REF_PTR:.*]] = cir.ternary(%[[COND_VAL]], true { +// CIR: cir.yield %[[X]] : !cir.ptr +// CIR: }, false { +// CIR: cir.yield %[[Y]] : !cir.ptr +// CIR: }) : (!cir.bool) -> !cir.ptr +// CIR: cir.store %[[REF_PTR]] +// CIR: %[[RET_VAL:.*]] = cir.load +// CIR: cir.return %[[RET_VAL]] : !cir.ptr + +// LLVM-LABEL: define{{.*}} ptr @_Z20test_cond_lvalue_refbii( +// LLVM: %[[COND:.*]] = alloca i8 +// LLVM: %[[X:.*]] = alloca i32 +// LLVM: %[[Y:.*]] = alloca i32 +// LLVM: %[[COND_VAL:.*]] = load i8, ptr %[[COND]] +// LLVM: %[[BOOL:.*]] = trunc i8 %[[COND_VAL]] to i1 +// LLVM: br i1 %[[BOOL]], 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 ptr [ %[[Y]], %[[FALSE_BB]] ], [ %[[X]], %[[TRUE_BB]] ] +// LLVM: br label %[[CONT:.*]] +// LLVM: [[CONT]]: +// LLVM: store ptr %[[PHI]] +// LLVM: %[[RET:.*]] = load ptr +// LLVM: ret ptr %[[RET]] + +// OGCG-LABEL: define{{.*}} ptr @_Z20test_cond_lvalue_refbii( +// OGCG: %[[COND:.*]] = alloca i8 +// OGCG: %[[X:.*]] = alloca i32 +// OGCG: %[[Y:.*]] = alloca i32 +// OGCG: %[[COND_VAL:.*]] = load i8, ptr %[[COND]] +// OGCG: %[[BOOL:.*]] = trunc i8 %[[COND_VAL]] to i1 +// OGCG: br i1 %[[BOOL]], label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]] +// OGCG: [[TRUE_BB]]: +// OGCG: br label %[[MERGE_BB:.*]] +// OGCG: [[FALSE_BB]]: +// OGCG: br label %[[MERGE_BB]] +// OGCG: [[MERGE_BB]]: +// OGCG: %[[PHI:.*]] = phi ptr [ %[[X]], %[[TRUE_BB]] ], [ %[[Y]], %[[FALSE_BB]] ] +// OGCG: ret ptr %[[PHI]] + +// Test ConditionalOperator as lvalue - compound assignment +void test_cond_lvalue_compound(bool flag) { + int a = 5; + int b = 10; + (flag ? a : b) += 3; +} + +// CIR-LABEL: cir.func{{.*}} @_Z25test_cond_lvalue_compoundb( +// CIR: %[[FLAG:.*]] = cir.alloca !cir.bool, !cir.ptr, ["flag", init] +// CIR: %[[A:.*]] = cir.alloca !s32i, !cir.ptr, ["a", init] +// CIR: %[[B:.*]] = cir.alloca !s32i, !cir.ptr, ["b", init] +// CIR: %[[FLAG_VAL:.*]] = cir.load{{.*}} %[[FLAG]] +// CIR: %[[LVAL_PTR:.*]] = cir.ternary(%[[FLAG_VAL]], true { +// CIR: cir.yield %[[A]] : !cir.ptr +// CIR: }, false { +// CIR: cir.yield %[[B]] : !cir.ptr +// CIR: }) : (!cir.bool) -> !cir.ptr +// CIR: %[[OLD_VAL:.*]] = cir.load{{.*}} %[[LVAL_PTR]] +// CIR: %[[NEW_VAL:.*]] = cir.binop(add, %[[OLD_VAL]], %{{.*}}) +// CIR: cir.store{{.*}} %[[NEW_VAL]], %[[LVAL_PTR]] + +// LLVM-LABEL: define{{.*}} void @_Z25test_cond_lvalue_compoundb( +// LLVM: %[[FLAG:.*]] = alloca i8 +// LLVM: %[[A:.*]] = alloca i32 +// LLVM: %[[B:.*]] = alloca i32 +// LLVM: %[[FLAG_VAL:.*]] = load i8, ptr %[[FLAG]] +// LLVM: %[[BOOL:.*]] = trunc i8 %[[FLAG_VAL]] to i1 +// LLVM: br i1 %[[BOOL]], 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: %[[PTR:.*]] = phi ptr [ %[[B]], %[[FALSE_BB]] ], [ %[[A]], %[[TRUE_BB]] ] +// LLVM: br label %[[CONT:.*]] +// LLVM: [[CONT]]: +// LLVM: %[[OLD:.*]] = load i32, ptr %[[PTR]] +// LLVM: %[[NEW:.*]] = add{{.*}} i32 %[[OLD]], 3 +// LLVM: store i32 %[[NEW]], ptr %[[PTR]] + +// OGCG-LABEL: define{{.*}} void @_Z25test_cond_lvalue_compoundb( +// OGCG: %[[FLAG:.*]] = alloca i8 +// OGCG: %[[A:.*]] = alloca i32 +// OGCG: %[[B:.*]] = alloca i32 +// OGCG: %[[FLAG_VAL:.*]] = load i8, ptr %[[FLAG]] +// OGCG: %[[BOOL:.*]] = trunc i8 %[[FLAG_VAL]] to i1 +// OGCG: br i1 %[[BOOL]], label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]] +// OGCG: [[TRUE_BB]]: +// OGCG: br label %[[MERGE_BB:.*]] +// OGCG: [[FALSE_BB]]: +// OGCG: br label %[[MERGE_BB]] +// OGCG: [[MERGE_BB]]: +// OGCG: %[[PTR:.*]] = phi ptr [ %[[A]], %[[TRUE_BB]] ], [ %[[B]], %[[FALSE_BB]] ] +// OGCG: %[[OLD:.*]] = load i32, ptr %[[PTR]] +// OGCG: %[[NEW:.*]] = add{{.*}} i32 %[[OLD]], 3 +// OGCG: store i32 %[[NEW]], ptr %[[PTR]] + +// Test constant folding - compile-time true condition with lvalue assignment +void test_cond_const_true_lvalue() { + int a = 1; + int b = 2; + (true ? a : b) = 99; +} + +// CIR-LABEL: cir.func{{.*}} @_Z27test_cond_const_true_lvaluev( +// CIR: %[[A:.*]] = cir.alloca !s32i, !cir.ptr, ["a", init] +// CIR: %[[B:.*]] = cir.alloca !s32i, !cir.ptr, ["b", init] +// CIR-NOT: cir.ternary +// CIR: %[[NINETYNINE:.*]] = cir.const #cir.int<99> : !s32i +// CIR: cir.store{{.*}} %[[NINETYNINE]], %[[A]] : !s32i, !cir.ptr + +// LLVM-LABEL: define{{.*}} void @_Z27test_cond_const_true_lvaluev( +// LLVM: %[[A:.*]] = alloca i32 +// LLVM: %[[B:.*]] = alloca i32 +// LLVM-NOT: br i1 +// LLVM: store i32 99, ptr %[[A]] + +// OGCG-LABEL: define{{.*}} void @_Z27test_cond_const_true_lvaluev( +// OGCG: %[[A:.*]] = alloca i32 +// OGCG: %[[B:.*]] = alloca i32 +// OGCG-NOT: br i1 +// OGCG: store i32 99, ptr %[[A]] + +// Test constant folding - compile-time false condition with lvalue assignment +void test_cond_const_false_lvalue() { + int a = 1; + int b = 2; + (false ? a : b) = 88; +} + +// CIR-LABEL: cir.func{{.*}} @_Z28test_cond_const_false_lvaluev( +// CIR: %[[A:.*]] = cir.alloca !s32i, !cir.ptr, ["a", init] +// CIR: %[[B:.*]] = cir.alloca !s32i, !cir.ptr, ["b", init] +// CIR-NOT: cir.ternary +// CIR: %[[EIGHTYEIGHT:.*]] = cir.const #cir.int<88> : !s32i +// CIR: cir.store{{.*}} %[[EIGHTYEIGHT]], %[[B]] : !s32i, !cir.ptr + +// LLVM-LABEL: define{{.*}} void @_Z28test_cond_const_false_lvaluev( +// LLVM: %[[A:.*]] = alloca i32 +// LLVM: %[[B:.*]] = alloca i32 +// LLVM-NOT: br i1 +// LLVM: store i32 88, ptr %[[B]] + +// OGCG-LABEL: define{{.*}} void @_Z28test_cond_const_false_lvaluev( +// OGCG: %[[A:.*]] = alloca i32 +// OGCG: %[[B:.*]] = alloca i32 +// OGCG-NOT: br i1 +// OGCG: store i32 88, ptr %[[B]]