https://github.com/kumarak updated https://github.com/llvm/llvm-project/pull/210384
>From d3b22dc1bfc19ed01c616bf56a85b4843df6300d Mon Sep 17 00:00:00 2001 From: AkshayK <[email protected]> Date: Fri, 17 Jul 2026 07:46:34 -0400 Subject: [PATCH 1/2] [CIR] Clean up conditional emitters to terminate regions after creation --- clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 65 ++---- clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp | 6 +- clang/test/CIR/CodeGen/agg-cond-throw.cpp | 207 ++++++++++++++++++ clang/test/CIR/CodeGen/ternary-throw.cpp | 12 +- 4 files changed, 237 insertions(+), 53 deletions(-) create mode 100644 clang/test/CIR/CodeGen/agg-cond-throw.cpp diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index 9b9811f7c4cb1..cbe42708b2fa9 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -3094,8 +3094,6 @@ CIRGenFunction::emitConditionalBlocks(const AbstractConditionalOperator *e, CIRGenBuilderTy &builder = getBuilder(); mlir::Value condV = emitOpOnBoolExpr(loc, e->getCond()); - SmallVector<mlir::OpBuilder::InsertPoint, 2> insertPoints{}; - mlir::Type yieldTy{}; auto emitBranch = [&](mlir::OpBuilder &b, mlir::Location loc, const Expr *expr, std::optional<LValue> &resultLV) { @@ -3115,53 +3113,30 @@ CIRGenFunction::emitConditionalBlocks(const AbstractConditionalOperator *e, branchCleanups.forceCleanup({&resultPtr}); } - if (resultPtr) { - yieldTy = resultPtr.getType(); + // A branch that produced no result is a throw-expression; its region is + // already terminated by cir.unreachable and needs no yield. + if (resultPtr) 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<cir::UnreachableOp>(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); - } - } + cir::TernaryOp ternary = 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); + }); + + // Close any region left unterminated (which can only happen on error + // paths, since a glvalue arm either yields its pointer or throws) with an + // empty cir.yield. + terminateStructuredRegionBody(ternary.getTrueRegion(), loc); + terminateStructuredRegionBody(ternary.getFalseRegion(), loc); + info.result = ternary.getResult(); return info; } diff --git a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp index 484f26bbc35ed..f7f0440766c34 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp @@ -479,8 +479,11 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> { cgf.curLexScope->setAsTernary(); dest.setExternallyDestructed(isExternallyDestructed); assert(!cir::MissingFeatures::incrementProfileCounter()); + // emitIfOnBoolExpr terminates the region with a yield if needed; + // creating one here unconditionally would land in the dead block + // left behind by a noreturn arm (e.g. a throw-expression) and + // keep that block alive. Visit(e->getTrueExpr()); - cir::YieldOp::create(b, loc); } eval.endEvaluation(); }, @@ -500,7 +503,6 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> { dest.setExternallyDestructed(isExternallyDestructed); assert(!cir::MissingFeatures::incrementProfileCounter()); Visit(e->getFalseExpr()); - cir::YieldOp::create(b, loc); } eval.endEvaluation(); }, diff --git a/clang/test/CIR/CodeGen/agg-cond-throw.cpp b/clang/test/CIR/CodeGen/agg-cond-throw.cpp new file mode 100644 index 0000000000000..0446e6c0c1745 --- /dev/null +++ b/clang/test/CIR/CodeGen/agg-cond-throw.cpp @@ -0,0 +1,207 @@ +// 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 --check-prefix=OGCG --input-file=%t.ll %s + +// Aggregate conditional operators are emitted as cir.if; the branch regions +// are terminated after creation, so a throw arm must end at cir.unreachable +// with no trailing dead block (checked with CIR-NEXT below). + +struct Agg { int x; int y; }; +void take(Agg a); + +// Baseline: both arms emit into the destination slot and the regions are +// closed with implicit terminators. +void init_normal(bool c) { + Agg a = c ? Agg{1, 2} : Agg{3, 4}; +} + +// CIR-LABEL: cir.func{{.*}} @_Z11init_normalb( +// CIR: %[[C:.*]] = cir.alloca "c" {{.*}} init : !cir.ptr<!cir.bool> +// CIR: %[[A:.*]] = cir.alloca "a" {{.*}} init : !cir.ptr<!rec_Agg> +// CIR: %[[C_VAL:.*]] = cir.load{{.*}} %[[C]] : !cir.ptr<!cir.bool>, !cir.bool +// CIR: cir.if %[[C_VAL]] { +// CIR: %[[X:.*]] = cir.get_member %[[A]][0] {name = "x"} +// CIR: %[[ONE:.*]] = cir.const #cir.int<1> : !s32i +// CIR: cir.store{{.*}} %[[ONE]], %[[X]] +// CIR: %[[Y:.*]] = cir.get_member %[[A]][1] {name = "y"} +// CIR: %[[TWO:.*]] = cir.const #cir.int<2> : !s32i +// CIR: cir.store{{.*}} %[[TWO]], %[[Y]] +// CIR-NEXT: } else { +// CIR: %[[X2:.*]] = cir.get_member %[[A]][0] {name = "x"} +// CIR: %[[THREE:.*]] = cir.const #cir.int<3> : !s32i +// CIR: cir.store{{.*}} %[[THREE]], %[[X2]] +// CIR: %[[Y2:.*]] = cir.get_member %[[A]][1] {name = "y"} +// CIR: %[[FOUR:.*]] = cir.const #cir.int<4> : !s32i +// CIR: cir.store{{.*}} %[[FOUR]], %[[Y2]] +// CIR-NEXT: } +// CIR: cir.return + +// LLVM-LABEL: define{{.*}} void @_Z11init_normalb( +// LLVM: br i1 %{{.*}}, label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]] +// LLVM: [[TRUE_BB]]: +// LLVM: store i32 1, ptr %{{.*}} +// LLVM: store i32 2, ptr %{{.*}} +// LLVM: br label %[[END:.*]] +// LLVM: [[FALSE_BB]]: +// LLVM: store i32 3, ptr %{{.*}} +// LLVM: store i32 4, ptr %{{.*}} +// LLVM: br label %[[END]] +// LLVM: [[END]]: +// LLVM: ret void + +// OGCG-LABEL: define{{.*}} void @_Z11init_normalb( +// OGCG: br i1 %{{.*}}, label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]] +// OGCG: [[TRUE_BB]]: +// OGCG: store i32 1, ptr %{{.*}} +// OGCG: store i32 2, ptr %{{.*}} +// OGCG: br label %[[END:.*]] +// OGCG: [[FALSE_BB]]: +// OGCG: store i32 3, ptr %{{.*}} +// OGCG: store i32 4, ptr %{{.*}} +// OGCG: br label %[[END]] +// OGCG: [[END]]: +// OGCG: ret void + +// Assignment context: the conditional materializes into a temporary that is +// then assigned to the target. +void assign_throw(bool c, Agg &a) { + a = c ? throw 0 : Agg{1, 2}; +} + +// CIR-LABEL: cir.func{{.*}} @_Z12assign_throwbR3Agg( +// CIR: %[[C:.*]] = cir.alloca "c" {{.*}} init : !cir.ptr<!cir.bool> +// CIR: %[[A_REF:.*]] = cir.alloca "a" {{.*}} init const : !cir.ptr<!cir.ptr<!rec_Agg>> +// CIR: %[[TMP:.*]] = cir.alloca "ref.tmp0" {{.*}} : !cir.ptr<!rec_Agg> +// CIR: %[[C_VAL:.*]] = cir.load{{.*}} %[[C]] : !cir.ptr<!cir.bool>, !cir.bool +// CIR: cir.if %[[C_VAL]] { +// CIR: %[[EXC:.*]] = cir.alloc.exception{{.*}} -> !cir.ptr<!s32i> +// CIR: cir.throw %[[EXC]] : !cir.ptr<!s32i>, @_ZTIi +// CIR: cir.unreachable +// CIR-NEXT: } else { +// CIR: cir.get_member %[[TMP]][0] {name = "x"} +// CIR: cir.get_member %[[TMP]][1] {name = "y"} +// CIR: } +// CIR: %[[A_VAL:.*]] = cir.load %[[A_REF]] +// CIR: cir.call @_ZN3AggaSEOS_(%[[A_VAL]], %[[TMP]]) + +// LLVM-LABEL: define{{.*}} void @_Z12assign_throwbR3Agg( +// LLVM: br i1 %{{.*}}, label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]] +// LLVM: [[TRUE_BB]]: +// LLVM: call{{.*}} ptr @__cxa_allocate_exception +// LLVM: call void @__cxa_throw(ptr %{{.*}}, ptr @_ZTIi +// LLVM: unreachable +// LLVM: [[FALSE_BB]]: +// LLVM: store i32 1, ptr %{{.*}} +// LLVM: store i32 2, ptr %{{.*}} +// LLVM: br label %[[END:.*]] +// LLVM: [[END]]: +// LLVM: call{{.*}} ptr @_ZN3AggaSEOS_( + +// OGCG-LABEL: define{{.*}} void @_Z12assign_throwbR3Agg( +// OGCG: br i1 %{{.*}}, label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]] +// OGCG: [[TRUE_BB]]: +// OGCG: call{{.*}} ptr @__cxa_allocate_exception +// OGCG: call void @__cxa_throw(ptr %{{.*}}, ptr @_ZTIi +// OGCG: unreachable +// OGCG: [[FALSE_BB]]: +// OGCG: store i32 1, ptr %{{.*}} +// OGCG: store i32 2, ptr %{{.*}} +// OGCG: br label %[[END:.*]] +// OGCG: [[END]]: +// OGCG: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 %{{.*}}, i64 8 + +// Nested conditional: the inner throw arm terminates the inner cir.if region +// directly. +void nested_throw(bool c1, bool c2) { + Agg a = c1 ? (c2 ? throw 0 : Agg{1, 2}) : Agg{3, 4}; +} + +// CIR-LABEL: cir.func{{.*}} @_Z12nested_throwbb( +// CIR: %[[A:.*]] = cir.alloca "a" {{.*}} init : !cir.ptr<!rec_Agg> +// CIR: cir.if %{{.*}} { +// CIR: %[[C2_VAL:.*]] = cir.load{{.*}} : !cir.ptr<!cir.bool>, !cir.bool +// CIR: cir.if %[[C2_VAL]] { +// CIR: %[[EXC:.*]] = cir.alloc.exception{{.*}} -> !cir.ptr<!s32i> +// CIR: cir.throw %[[EXC]] : !cir.ptr<!s32i>, @_ZTIi +// CIR: cir.unreachable +// CIR-NEXT: } else { +// CIR: cir.get_member %[[A]][0] {name = "x"} +// CIR: cir.get_member %[[A]][1] {name = "y"} +// CIR: } +// CIR: } else { +// CIR: cir.get_member %[[A]][0] {name = "x"} +// CIR: cir.get_member %[[A]][1] {name = "y"} +// CIR: } +// CIR: cir.return + +// LLVM-LABEL: define{{.*}} void @_Z12nested_throwbb( +// LLVM: br i1 %{{.*}}, label %[[OUTER_TRUE:.*]], label %[[OUTER_FALSE:.*]] +// LLVM: [[OUTER_TRUE]]: +// LLVM: br i1 %{{.*}}, label %[[INNER_TRUE:.*]], label %[[INNER_FALSE:.*]] +// LLVM: [[INNER_TRUE]]: +// LLVM: call void @__cxa_throw(ptr %{{.*}}, ptr @_ZTIi +// LLVM: unreachable +// LLVM: [[INNER_FALSE]]: +// LLVM: store i32 1, ptr %{{.*}} +// LLVM: store i32 2, ptr %{{.*}} +// LLVM: [[OUTER_FALSE]]: +// LLVM: store i32 3, ptr %{{.*}} +// LLVM: store i32 4, ptr %{{.*}} + +// OGCG-LABEL: define{{.*}} void @_Z12nested_throwbb( +// OGCG: br i1 %{{.*}}, label %[[OUTER_TRUE:.*]], label %[[OUTER_FALSE:.*]] +// OGCG: [[OUTER_TRUE]]: +// OGCG: br i1 %{{.*}}, label %[[INNER_TRUE:.*]], label %[[INNER_FALSE:.*]] +// OGCG: [[INNER_TRUE]]: +// OGCG: call void @__cxa_throw(ptr %{{.*}}, ptr @_ZTIi +// OGCG: unreachable +// OGCG: [[INNER_FALSE]]: +// OGCG: store i32 1, ptr %{{.*}} +// OGCG: store i32 2, ptr %{{.*}} +// OGCG: [[OUTER_FALSE]]: +// OGCG: store i32 3, ptr %{{.*}} +// OGCG: store i32 4, ptr %{{.*}} + +// Call-argument context: the conditional materializes the argument temporary. +void arg_throw(bool c) { + take(c ? throw 0 : Agg{1, 2}); +} + +// CIR-LABEL: cir.func{{.*}} @_Z9arg_throwb( +// CIR: %[[TMP:.*]] = cir.alloca "agg.tmp0" {{.*}} : !cir.ptr<!rec_Agg> +// CIR: cir.if %{{.*}} { +// CIR: %[[EXC:.*]] = cir.alloc.exception{{.*}} -> !cir.ptr<!s32i> +// CIR: cir.throw %[[EXC]] : !cir.ptr<!s32i>, @_ZTIi +// CIR: cir.unreachable +// CIR-NEXT: } else { +// CIR: cir.get_member %[[TMP]][0] {name = "x"} +// CIR: cir.get_member %[[TMP]][1] {name = "y"} +// CIR: } +// CIR: %[[ARG:.*]] = cir.load{{.*}} %[[TMP]] : !cir.ptr<!rec_Agg>, !rec_Agg +// CIR: cir.call @_Z4take3Agg(%[[ARG]]) + +// LLVM-LABEL: define{{.*}} void @_Z9arg_throwb( +// LLVM: br i1 %{{.*}}, label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]] +// LLVM: [[TRUE_BB]]: +// LLVM: call void @__cxa_throw(ptr %{{.*}}, ptr @_ZTIi +// LLVM: unreachable +// LLVM: [[FALSE_BB]]: +// LLVM: store i32 1, ptr %{{.*}} +// LLVM: store i32 2, ptr %{{.*}} +// LLVM: br label %[[END:.*]] +// LLVM: [[END]]: +// LLVM: call void @_Z4take3Agg( + +// OGCG-LABEL: define{{.*}} void @_Z9arg_throwb( +// OGCG: br i1 %{{.*}}, label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]] +// OGCG: [[TRUE_BB]]: +// OGCG: call void @__cxa_throw(ptr %{{.*}}, ptr @_ZTIi +// OGCG: unreachable +// OGCG: [[FALSE_BB]]: +// OGCG: store i32 1, ptr %{{.*}} +// OGCG: store i32 2, ptr %{{.*}} +// OGCG: [[END:.*]]: +// OGCG: call void @_Z4take3Agg( diff --git a/clang/test/CIR/CodeGen/ternary-throw.cpp b/clang/test/CIR/CodeGen/ternary-throw.cpp index 8a342ee51308d..1d05f965a0e01 100644 --- a/clang/test/CIR/CodeGen/ternary-throw.cpp +++ b/clang/test/CIR/CodeGen/ternary-throw.cpp @@ -18,13 +18,13 @@ const int& test_cond_throw_false(bool flag) { // CIR: %[[FLAG_VAL:.*]] = cir.load{{.*}} %[[FLAG]] : !cir.ptr<!cir.bool>, !cir.bool // CIR: %[[RESULT:.*]] = cir.ternary(%[[FLAG_VAL]], true { // CIR: cir.yield %[[A]] : !cir.ptr<!s32i> -// CIR: }, false { +// CIR-NEXT: }, false { // CIR: %[[EXCEPTION:.*]] = cir.alloc.exception{{.*}} -> !cir.ptr<!s32i> // CIR: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i // CIR: cir.store{{.*}} %[[ZERO]], %[[EXCEPTION]] : !s32i, !cir.ptr<!s32i> // CIR: cir.throw %[[EXCEPTION]] : !cir.ptr<!s32i>, @_ZTIi // CIR: cir.unreachable -// CIR: }) : (!cir.bool) -> !cir.ptr<!s32i> +// CIR-NEXT: }) : (!cir.bool) -> !cir.ptr<!s32i> // LLVM-LABEL: define{{.*}} ptr @_Z21test_cond_throw_falseb( // LLVM: %[[FLAG_ALLOCA:.*]] = alloca i8 @@ -85,9 +85,9 @@ const int& test_cond_throw_true(bool flag) { // CIR: cir.store{{.*}} %[[ZERO]], %[[EXCEPTION]] : !s32i, !cir.ptr<!s32i> // CIR: cir.throw %[[EXCEPTION]] : !cir.ptr<!s32i>, @_ZTIi // CIR: cir.unreachable -// CIR: }, false { +// CIR-NEXT: }, false { // CIR: cir.yield %[[A]] : !cir.ptr<!s32i> -// CIR: }) : (!cir.bool) -> !cir.ptr<!s32i> +// CIR-NEXT: }) : (!cir.bool) -> !cir.ptr<!s32i> // LLVM-LABEL: define{{.*}} ptr @_Z20test_cond_throw_trueb( // LLVM: %[[FLAG_ALLOCA:.*]] = alloca i8 @@ -541,7 +541,7 @@ void test_agg_throw_true(bool flag) { // CIR: cir.store{{.*}} %[[ZERO]], %[[EXC]] : !s32i, !cir.ptr<!s32i> // CIR: cir.throw %[[EXC]] : !cir.ptr<!s32i>, @_ZTIi // CIR: cir.unreachable -// CIR: } else { +// CIR-NEXT: } else { // CIR: %[[X:.*]] = cir.get_member %[[A]][0] {name = "x"} : !cir.ptr<!rec_Agg> -> !cir.ptr<!s32i> // CIR: %[[ONE:.*]] = cir.const #cir.int<1> : !s32i // CIR: cir.store{{.*}} %[[ONE]], %[[X]] : !s32i, !cir.ptr<!s32i> @@ -610,7 +610,7 @@ void test_agg_throw_false(bool flag) { // CIR: cir.store{{.*}} %[[ZERO]], %[[EXC]] : !s32i, !cir.ptr<!s32i> // CIR: cir.throw %[[EXC]] : !cir.ptr<!s32i>, @_ZTIi // CIR: cir.unreachable -// CIR: } +// CIR-NEXT: } // CIR: cir.return // LLVM-LABEL: define{{.*}} void @_Z20test_agg_throw_falseb( >From 75dfd34e752f030949df02817022b2c19b4d109e Mon Sep 17 00:00:00 2001 From: AkshayK <[email protected]> Date: Fri, 17 Jul 2026 11:58:41 -0400 Subject: [PATCH 2/2] [CIR] Use the ternary result for glvalue conditionals with a throw arm --- clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 12 +- clang/test/CIR/CodeGen/agg-cond-throw.cpp | 207 ------------- clang/test/CIR/CodeGen/ternary-throw.cpp | 341 +++++++++++++++++++++- 3 files changed, 346 insertions(+), 214 deletions(-) delete mode 100644 clang/test/CIR/CodeGen/agg-cond-throw.cpp diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index cbe42708b2fa9..cef978dca3ba4 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -3180,7 +3180,17 @@ LValue CIRGenFunction::emitConditionalOperatorLValue( assert((info.lhs || info.rhs) && "both operands of glvalue conditional are throw-expressions?"); - return info.lhs ? *info.lhs : *info.rhs; + + // Only one arm produced an lvalue; the other was a throw-expression. The + // surviving arm's pointer was materialized inside the ternary's region and + // is not visible past the op, so address the result through the ternary's + // result value, which the region's cir.yield carries out. + LValue &survivingLV = info.lhs ? *info.lhs : *info.rhs; + Address survivingAddr = survivingLV.getAddress(); + Address result(info.result, survivingAddr.getElementType(), + survivingAddr.getAlignment()); + assert(!cir::MissingFeatures::opTBAA()); + return makeAddrLValue(result, expr->getType(), survivingLV.getBaseInfo()); } /// An LValue is a candidate for having its loads and stores be made atomic if diff --git a/clang/test/CIR/CodeGen/agg-cond-throw.cpp b/clang/test/CIR/CodeGen/agg-cond-throw.cpp deleted file mode 100644 index 0446e6c0c1745..0000000000000 --- a/clang/test/CIR/CodeGen/agg-cond-throw.cpp +++ /dev/null @@ -1,207 +0,0 @@ -// 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 --check-prefix=OGCG --input-file=%t.ll %s - -// Aggregate conditional operators are emitted as cir.if; the branch regions -// are terminated after creation, so a throw arm must end at cir.unreachable -// with no trailing dead block (checked with CIR-NEXT below). - -struct Agg { int x; int y; }; -void take(Agg a); - -// Baseline: both arms emit into the destination slot and the regions are -// closed with implicit terminators. -void init_normal(bool c) { - Agg a = c ? Agg{1, 2} : Agg{3, 4}; -} - -// CIR-LABEL: cir.func{{.*}} @_Z11init_normalb( -// CIR: %[[C:.*]] = cir.alloca "c" {{.*}} init : !cir.ptr<!cir.bool> -// CIR: %[[A:.*]] = cir.alloca "a" {{.*}} init : !cir.ptr<!rec_Agg> -// CIR: %[[C_VAL:.*]] = cir.load{{.*}} %[[C]] : !cir.ptr<!cir.bool>, !cir.bool -// CIR: cir.if %[[C_VAL]] { -// CIR: %[[X:.*]] = cir.get_member %[[A]][0] {name = "x"} -// CIR: %[[ONE:.*]] = cir.const #cir.int<1> : !s32i -// CIR: cir.store{{.*}} %[[ONE]], %[[X]] -// CIR: %[[Y:.*]] = cir.get_member %[[A]][1] {name = "y"} -// CIR: %[[TWO:.*]] = cir.const #cir.int<2> : !s32i -// CIR: cir.store{{.*}} %[[TWO]], %[[Y]] -// CIR-NEXT: } else { -// CIR: %[[X2:.*]] = cir.get_member %[[A]][0] {name = "x"} -// CIR: %[[THREE:.*]] = cir.const #cir.int<3> : !s32i -// CIR: cir.store{{.*}} %[[THREE]], %[[X2]] -// CIR: %[[Y2:.*]] = cir.get_member %[[A]][1] {name = "y"} -// CIR: %[[FOUR:.*]] = cir.const #cir.int<4> : !s32i -// CIR: cir.store{{.*}} %[[FOUR]], %[[Y2]] -// CIR-NEXT: } -// CIR: cir.return - -// LLVM-LABEL: define{{.*}} void @_Z11init_normalb( -// LLVM: br i1 %{{.*}}, label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]] -// LLVM: [[TRUE_BB]]: -// LLVM: store i32 1, ptr %{{.*}} -// LLVM: store i32 2, ptr %{{.*}} -// LLVM: br label %[[END:.*]] -// LLVM: [[FALSE_BB]]: -// LLVM: store i32 3, ptr %{{.*}} -// LLVM: store i32 4, ptr %{{.*}} -// LLVM: br label %[[END]] -// LLVM: [[END]]: -// LLVM: ret void - -// OGCG-LABEL: define{{.*}} void @_Z11init_normalb( -// OGCG: br i1 %{{.*}}, label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]] -// OGCG: [[TRUE_BB]]: -// OGCG: store i32 1, ptr %{{.*}} -// OGCG: store i32 2, ptr %{{.*}} -// OGCG: br label %[[END:.*]] -// OGCG: [[FALSE_BB]]: -// OGCG: store i32 3, ptr %{{.*}} -// OGCG: store i32 4, ptr %{{.*}} -// OGCG: br label %[[END]] -// OGCG: [[END]]: -// OGCG: ret void - -// Assignment context: the conditional materializes into a temporary that is -// then assigned to the target. -void assign_throw(bool c, Agg &a) { - a = c ? throw 0 : Agg{1, 2}; -} - -// CIR-LABEL: cir.func{{.*}} @_Z12assign_throwbR3Agg( -// CIR: %[[C:.*]] = cir.alloca "c" {{.*}} init : !cir.ptr<!cir.bool> -// CIR: %[[A_REF:.*]] = cir.alloca "a" {{.*}} init const : !cir.ptr<!cir.ptr<!rec_Agg>> -// CIR: %[[TMP:.*]] = cir.alloca "ref.tmp0" {{.*}} : !cir.ptr<!rec_Agg> -// CIR: %[[C_VAL:.*]] = cir.load{{.*}} %[[C]] : !cir.ptr<!cir.bool>, !cir.bool -// CIR: cir.if %[[C_VAL]] { -// CIR: %[[EXC:.*]] = cir.alloc.exception{{.*}} -> !cir.ptr<!s32i> -// CIR: cir.throw %[[EXC]] : !cir.ptr<!s32i>, @_ZTIi -// CIR: cir.unreachable -// CIR-NEXT: } else { -// CIR: cir.get_member %[[TMP]][0] {name = "x"} -// CIR: cir.get_member %[[TMP]][1] {name = "y"} -// CIR: } -// CIR: %[[A_VAL:.*]] = cir.load %[[A_REF]] -// CIR: cir.call @_ZN3AggaSEOS_(%[[A_VAL]], %[[TMP]]) - -// LLVM-LABEL: define{{.*}} void @_Z12assign_throwbR3Agg( -// LLVM: br i1 %{{.*}}, label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]] -// LLVM: [[TRUE_BB]]: -// LLVM: call{{.*}} ptr @__cxa_allocate_exception -// LLVM: call void @__cxa_throw(ptr %{{.*}}, ptr @_ZTIi -// LLVM: unreachable -// LLVM: [[FALSE_BB]]: -// LLVM: store i32 1, ptr %{{.*}} -// LLVM: store i32 2, ptr %{{.*}} -// LLVM: br label %[[END:.*]] -// LLVM: [[END]]: -// LLVM: call{{.*}} ptr @_ZN3AggaSEOS_( - -// OGCG-LABEL: define{{.*}} void @_Z12assign_throwbR3Agg( -// OGCG: br i1 %{{.*}}, label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]] -// OGCG: [[TRUE_BB]]: -// OGCG: call{{.*}} ptr @__cxa_allocate_exception -// OGCG: call void @__cxa_throw(ptr %{{.*}}, ptr @_ZTIi -// OGCG: unreachable -// OGCG: [[FALSE_BB]]: -// OGCG: store i32 1, ptr %{{.*}} -// OGCG: store i32 2, ptr %{{.*}} -// OGCG: br label %[[END:.*]] -// OGCG: [[END]]: -// OGCG: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 %{{.*}}, i64 8 - -// Nested conditional: the inner throw arm terminates the inner cir.if region -// directly. -void nested_throw(bool c1, bool c2) { - Agg a = c1 ? (c2 ? throw 0 : Agg{1, 2}) : Agg{3, 4}; -} - -// CIR-LABEL: cir.func{{.*}} @_Z12nested_throwbb( -// CIR: %[[A:.*]] = cir.alloca "a" {{.*}} init : !cir.ptr<!rec_Agg> -// CIR: cir.if %{{.*}} { -// CIR: %[[C2_VAL:.*]] = cir.load{{.*}} : !cir.ptr<!cir.bool>, !cir.bool -// CIR: cir.if %[[C2_VAL]] { -// CIR: %[[EXC:.*]] = cir.alloc.exception{{.*}} -> !cir.ptr<!s32i> -// CIR: cir.throw %[[EXC]] : !cir.ptr<!s32i>, @_ZTIi -// CIR: cir.unreachable -// CIR-NEXT: } else { -// CIR: cir.get_member %[[A]][0] {name = "x"} -// CIR: cir.get_member %[[A]][1] {name = "y"} -// CIR: } -// CIR: } else { -// CIR: cir.get_member %[[A]][0] {name = "x"} -// CIR: cir.get_member %[[A]][1] {name = "y"} -// CIR: } -// CIR: cir.return - -// LLVM-LABEL: define{{.*}} void @_Z12nested_throwbb( -// LLVM: br i1 %{{.*}}, label %[[OUTER_TRUE:.*]], label %[[OUTER_FALSE:.*]] -// LLVM: [[OUTER_TRUE]]: -// LLVM: br i1 %{{.*}}, label %[[INNER_TRUE:.*]], label %[[INNER_FALSE:.*]] -// LLVM: [[INNER_TRUE]]: -// LLVM: call void @__cxa_throw(ptr %{{.*}}, ptr @_ZTIi -// LLVM: unreachable -// LLVM: [[INNER_FALSE]]: -// LLVM: store i32 1, ptr %{{.*}} -// LLVM: store i32 2, ptr %{{.*}} -// LLVM: [[OUTER_FALSE]]: -// LLVM: store i32 3, ptr %{{.*}} -// LLVM: store i32 4, ptr %{{.*}} - -// OGCG-LABEL: define{{.*}} void @_Z12nested_throwbb( -// OGCG: br i1 %{{.*}}, label %[[OUTER_TRUE:.*]], label %[[OUTER_FALSE:.*]] -// OGCG: [[OUTER_TRUE]]: -// OGCG: br i1 %{{.*}}, label %[[INNER_TRUE:.*]], label %[[INNER_FALSE:.*]] -// OGCG: [[INNER_TRUE]]: -// OGCG: call void @__cxa_throw(ptr %{{.*}}, ptr @_ZTIi -// OGCG: unreachable -// OGCG: [[INNER_FALSE]]: -// OGCG: store i32 1, ptr %{{.*}} -// OGCG: store i32 2, ptr %{{.*}} -// OGCG: [[OUTER_FALSE]]: -// OGCG: store i32 3, ptr %{{.*}} -// OGCG: store i32 4, ptr %{{.*}} - -// Call-argument context: the conditional materializes the argument temporary. -void arg_throw(bool c) { - take(c ? throw 0 : Agg{1, 2}); -} - -// CIR-LABEL: cir.func{{.*}} @_Z9arg_throwb( -// CIR: %[[TMP:.*]] = cir.alloca "agg.tmp0" {{.*}} : !cir.ptr<!rec_Agg> -// CIR: cir.if %{{.*}} { -// CIR: %[[EXC:.*]] = cir.alloc.exception{{.*}} -> !cir.ptr<!s32i> -// CIR: cir.throw %[[EXC]] : !cir.ptr<!s32i>, @_ZTIi -// CIR: cir.unreachable -// CIR-NEXT: } else { -// CIR: cir.get_member %[[TMP]][0] {name = "x"} -// CIR: cir.get_member %[[TMP]][1] {name = "y"} -// CIR: } -// CIR: %[[ARG:.*]] = cir.load{{.*}} %[[TMP]] : !cir.ptr<!rec_Agg>, !rec_Agg -// CIR: cir.call @_Z4take3Agg(%[[ARG]]) - -// LLVM-LABEL: define{{.*}} void @_Z9arg_throwb( -// LLVM: br i1 %{{.*}}, label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]] -// LLVM: [[TRUE_BB]]: -// LLVM: call void @__cxa_throw(ptr %{{.*}}, ptr @_ZTIi -// LLVM: unreachable -// LLVM: [[FALSE_BB]]: -// LLVM: store i32 1, ptr %{{.*}} -// LLVM: store i32 2, ptr %{{.*}} -// LLVM: br label %[[END:.*]] -// LLVM: [[END]]: -// LLVM: call void @_Z4take3Agg( - -// OGCG-LABEL: define{{.*}} void @_Z9arg_throwb( -// OGCG: br i1 %{{.*}}, label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]] -// OGCG: [[TRUE_BB]]: -// OGCG: call void @__cxa_throw(ptr %{{.*}}, ptr @_ZTIi -// OGCG: unreachable -// OGCG: [[FALSE_BB]]: -// OGCG: store i32 1, ptr %{{.*}} -// OGCG: store i32 2, ptr %{{.*}} -// OGCG: [[END:.*]]: -// OGCG: call void @_Z4take3Agg( diff --git a/clang/test/CIR/CodeGen/ternary-throw.cpp b/clang/test/CIR/CodeGen/ternary-throw.cpp index 1d05f965a0e01..2f09d1d7a4ae7 100644 --- a/clang/test/CIR/CodeGen/ternary-throw.cpp +++ b/clang/test/CIR/CodeGen/ternary-throw.cpp @@ -47,7 +47,7 @@ const int& test_cond_throw_false(bool flag) { // 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: store ptr %[[PHI]], ptr %[[RET_ALLOCA]] // LLVM: %[[RET:.*]] = load ptr, ptr %[[RET_ALLOCA]] // LLVM: ret ptr %[[RET]] @@ -110,7 +110,7 @@ const int& test_cond_throw_true(bool flag) { // 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: store ptr %[[PHI]], ptr %[[RET_ALLOCA]] // LLVM: %[[RET:.*]] = load ptr, ptr %[[RET_ALLOCA]] // LLVM: ret ptr %[[RET]] @@ -258,7 +258,7 @@ int test_agg_cond_throw_false(bool flag, struct s6 a1, struct s6 a2) { // CIR: cir.throw %[[EXC]] : !cir.ptr<!s32i>, @_ZTIi // CIR: cir.unreachable // CIR: }) : (!cir.bool) -> !cir.ptr<!rec_s6> -// CIR: %[[F0:.*]] = cir.get_member %[[A1]][0] {name = "f0"} : !cir.ptr<!rec_s6> -> !cir.ptr<!s32i> +// CIR: %[[F0:.*]] = cir.get_member %[[COND_RES]][0] {name = "f0"} : !cir.ptr<!rec_s6> -> !cir.ptr<!s32i> // CIR: %[[LOAD:.*]] = cir.load{{.*}} %[[F0]] : !cir.ptr<!s32i>, !s32i // CIR: cir.return %{{.*}} : !s32i @@ -282,7 +282,7 @@ int test_agg_cond_throw_false(bool flag, struct s6 a1, struct s6 a2) { // LLVM: %[[PHI:.*]] = phi ptr [ %[[A1_ALLOCA]], %[[TRUE_BB]] ] // LLVM: br label %[[CONT_BB:.*]] // LLVM: [[CONT_BB]]: -// LLVM: %[[F0_PTR:.*]] = getelementptr inbounds nuw %struct.s6, ptr %[[A1_ALLOCA]], i32 0, i32 0 +// LLVM: %[[F0_PTR:.*]] = getelementptr inbounds nuw %struct.s6, ptr %[[PHI]], i32 0, i32 0 // LLVM: %[[F0_VAL:.*]] = load i32, ptr %[[F0_PTR]] // LLVM: ret i32 %{{.*}} @@ -323,7 +323,7 @@ int test_agg_cond_throw_true(bool flag, struct s6 a1, struct s6 a2) { // CIR: }, false { // CIR: cir.yield %[[A1]] : !cir.ptr<!rec_s6> // CIR: }) : (!cir.bool) -> !cir.ptr<!rec_s6> -// CIR: %[[F0:.*]] = cir.get_member %[[A1]][0] {name = "f0"} : !cir.ptr<!rec_s6> -> !cir.ptr<!s32i> +// CIR: %[[F0:.*]] = cir.get_member %[[COND_RES]][0] {name = "f0"} : !cir.ptr<!rec_s6> -> !cir.ptr<!s32i> // CIR: %[[LOAD:.*]] = cir.load{{.*}} %[[F0]] : !cir.ptr<!s32i>, !s32i // CIR: cir.return %{{.*}} : !s32i @@ -347,7 +347,7 @@ int test_agg_cond_throw_true(bool flag, struct s6 a1, struct s6 a2) { // LLVM: %[[PHI:.*]] = phi ptr [ %[[A1_ALLOCA]], %[[FALSE_BB]] ] // LLVM: br label %[[CONT_BB:.*]] // LLVM: [[CONT_BB]]: -// LLVM: %[[F0_PTR:.*]] = getelementptr inbounds nuw %struct.s6, ptr %[[A1_ALLOCA]], i32 0, i32 0 +// LLVM: %[[F0_PTR:.*]] = getelementptr inbounds nuw %struct.s6, ptr %[[PHI]], i32 0, i32 0 // LLVM: %[[F0_VAL:.*]] = load i32, ptr %[[F0_PTR]] // LLVM: ret i32 %{{.*}} @@ -739,3 +739,332 @@ void test_both_throw(bool flag) { // OGCG: [[FALSE_BB]]: // OGCG: call void @__cxa_throw(ptr %{{.*}}, ptr @_ZTIi // OGCG: unreachable + +// The surviving arm of a glvalue conditional may compute its pointer inside +// the ternary region (e.g. loading a reference parameter); the result must be +// addressed through the cir.ternary result, which the region's yield carries +// out, not through the region-local pointer. +int &test_ref_cond_throw(bool c, int &x) { + return c ? x : throw 0; +} + +// CIR-LABEL: cir.func{{.*}} @_Z19test_ref_cond_throwbRi( +// CIR: %[[C:.*]] = cir.alloca "c" {{.*}} init : !cir.ptr<!cir.bool> +// CIR: %[[X_REF:.*]] = cir.alloca "x" {{.*}} init const : !cir.ptr<!cir.ptr<!s32i>> +// CIR: %[[RET_ADDR:.*]] = cir.alloca "__retval" {{.*}} : !cir.ptr<!cir.ptr<!s32i>> +// CIR: %[[C_VAL:.*]] = cir.load{{.*}} %[[C]] : !cir.ptr<!cir.bool>, !cir.bool +// CIR: %[[RES:.*]] = cir.ternary(%[[C_VAL]], true { +// CIR: %[[X_PTR:.*]] = cir.load %[[X_REF]] : !cir.ptr<!cir.ptr<!s32i>>, !cir.ptr<!s32i> +// CIR: cir.yield %[[X_PTR]] : !cir.ptr<!s32i> +// CIR-NEXT: }, false { +// CIR: cir.throw {{.*}} @_ZTIi +// CIR: cir.unreachable +// CIR-NEXT: }) : (!cir.bool) -> !cir.ptr<!s32i> +// CIR: cir.store %[[RES]], %[[RET_ADDR]] : !cir.ptr<!s32i>, !cir.ptr<!cir.ptr<!s32i>> + +// LLVM-LABEL: define{{.*}} ptr @_Z19test_ref_cond_throwbRi( +// LLVM: br i1 %{{.*}}, label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]] +// LLVM: [[TRUE_BB]]: +// LLVM: %[[X_PTR:.*]] = load ptr, ptr %{{.*}} +// LLVM: br label %[[PHI_BB:.*]] +// LLVM: [[FALSE_BB]]: +// LLVM: call void @__cxa_throw(ptr %{{.*}}, ptr @_ZTIi +// LLVM: unreachable +// LLVM: [[PHI_BB]]: +// LLVM: %[[PHI:.*]] = phi ptr [ %[[X_PTR]], %[[TRUE_BB]] ] +// LLVM: store ptr %[[PHI]], ptr %[[RET_ALLOCA:.*]], align 8 +// LLVM: %[[RET:.*]] = load ptr, ptr %[[RET_ALLOCA]] +// LLVM: ret ptr %[[RET]] + +// OGCG-LABEL: define{{.*}} ptr @_Z19test_ref_cond_throwbRi( +// OGCG: br i1 %{{.*}}, label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]] +// OGCG: [[TRUE_BB]]: +// OGCG: %[[X_PTR:.*]] = load ptr, ptr %{{.*}} +// OGCG: br label %[[END:.*]] +// OGCG: [[FALSE_BB]]: +// OGCG: call void @__cxa_throw(ptr %{{.*}}, ptr @_ZTIi +// OGCG: unreachable +// OGCG: [[END]]: +// OGCG: ret ptr %[[X_PTR]] + +// Same shape with the conditional as the target of an assignment. +void test_assign_through_cond(bool c, int &x) { + (c ? x : throw 0) = 5; +} + +// CIR-LABEL: cir.func{{.*}} @_Z24test_assign_through_condbRi( +// CIR: %[[X_REF:.*]] = cir.alloca "x" {{.*}} init const : !cir.ptr<!cir.ptr<!s32i>> +// CIR: %[[FIVE:.*]] = cir.const #cir.int<5> : !s32i +// CIR: %[[RES:.*]] = cir.ternary(%{{.*}}, true { +// CIR: %[[X_PTR:.*]] = cir.load %[[X_REF]] : !cir.ptr<!cir.ptr<!s32i>>, !cir.ptr<!s32i> +// CIR: cir.yield %[[X_PTR]] : !cir.ptr<!s32i> +// CIR-NEXT: }, false { +// CIR: cir.throw {{.*}} @_ZTIi +// CIR: cir.unreachable +// CIR-NEXT: }) : (!cir.bool) -> !cir.ptr<!s32i> +// CIR: cir.store{{.*}} %[[FIVE]], %[[RES]] : !s32i, !cir.ptr<!s32i> + +// LLVM-LABEL: define{{.*}} void @_Z24test_assign_through_condbRi( +// LLVM: br i1 %{{.*}}, label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]] +// LLVM: [[TRUE_BB]]: +// LLVM: %[[X_PTR:.*]] = load ptr, ptr %{{.*}} +// LLVM: br label %[[PHI_BB:.*]] +// LLVM: [[FALSE_BB]]: +// LLVM: call void @__cxa_throw(ptr %{{.*}}, ptr @_ZTIi +// LLVM: unreachable +// LLVM: [[PHI_BB]]: +// LLVM: %[[PHI:.*]] = phi ptr [ %[[X_PTR]], %[[TRUE_BB]] ] +// LLVM: store i32 5, ptr %[[PHI]], align 4 +// LLVM: ret void + +// OGCG-LABEL: define{{.*}} void @_Z24test_assign_through_condbRi( +// OGCG: br i1 %{{.*}}, label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]] +// OGCG: [[TRUE_BB]]: +// OGCG: %[[X_PTR:.*]] = load ptr, ptr %{{.*}} +// OGCG: br label %[[END:.*]] +// OGCG: [[FALSE_BB]]: +// OGCG: call void @__cxa_throw(ptr %{{.*}}, ptr @_ZTIi +// OGCG: unreachable +// OGCG: [[END]]: +// OGCG: store i32 5, ptr %[[X_PTR]], align 4 +// OGCG: ret void + +// Same shape where the surviving arm addresses a member through a pointer. +int &test_member_cond_throw(bool c, struct s6 *p) { + return c ? p->f0 : throw 0; +} + +// CIR-LABEL: cir.func{{.*}} @_Z22test_member_cond_throwbP2s6( +// CIR: %[[P_ADDR:.*]] = cir.alloca "p" {{.*}} : !cir.ptr<!cir.ptr<!rec_s6>> +// CIR: %[[RES:.*]] = cir.ternary(%{{.*}}, true { +// CIR: %[[P:.*]] = cir.load{{.*}} %[[P_ADDR]] : !cir.ptr<!cir.ptr<!rec_s6>>, !cir.ptr<!rec_s6> +// CIR: %[[F0:.*]] = cir.get_member %[[P]][0] {name = "f0"} : !cir.ptr<!rec_s6> -> !cir.ptr<!s32i> +// CIR: cir.yield %[[F0]] : !cir.ptr<!s32i> +// CIR-NEXT: }, false { +// CIR: cir.throw {{.*}} @_ZTIi +// CIR: cir.unreachable +// CIR-NEXT: }) : (!cir.bool) -> !cir.ptr<!s32i> +// CIR: cir.store %[[RES]], %{{.*}} : !cir.ptr<!s32i>, !cir.ptr<!cir.ptr<!s32i>> + +// LLVM-LABEL: define{{.*}} ptr @_Z22test_member_cond_throwbP2s6( +// LLVM: br i1 %{{.*}}, label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]] +// LLVM: [[TRUE_BB]]: +// LLVM: %[[F0_PTR:.*]] = getelementptr{{.*}}%struct.s6, ptr %{{.*}} +// LLVM: br label %[[PHI_BB:.*]] +// LLVM: [[FALSE_BB]]: +// LLVM: call void @__cxa_throw(ptr %{{.*}}, ptr @_ZTIi +// LLVM: unreachable +// LLVM: [[PHI_BB]]: +// LLVM: %[[PHI:.*]] = phi ptr [ %[[F0_PTR]], %[[TRUE_BB]] ] +// LLVM: store ptr %[[PHI]], ptr %{{.*}}, align 8 + +// OGCG-LABEL: define{{.*}} ptr @_Z22test_member_cond_throwbP2s6( +// OGCG: br i1 %{{.*}}, label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]] +// OGCG: [[TRUE_BB]]: +// OGCG: %[[F0_PTR:.*]] = getelementptr{{.*}}%struct.s6, ptr %{{.*}} +// OGCG: br label %[[END:.*]] +// OGCG: [[FALSE_BB]]: +// OGCG: call void @__cxa_throw(ptr %{{.*}}, ptr @_ZTIi +// OGCG: unreachable +// OGCG: [[END]]: +// OGCG: ret ptr %[[F0_PTR]] + +// Aggregate conditional operators are emitted as cir.if; the branch regions +// are terminated after creation, so a throw arm must end at cir.unreachable +// with no trailing dead block (checked with CIR-NEXT below). + +void take(Agg a); + +// Baseline: both arms emit into the destination slot and the regions are +// closed with implicit terminators. +void test_agg_init_normal(bool c) { + Agg a = c ? Agg{1, 2} : Agg{3, 4}; +} + +// CIR-LABEL: cir.func{{.*}} @_Z20test_agg_init_normalb( +// CIR: %[[C:.*]] = cir.alloca "c" {{.*}} init : !cir.ptr<!cir.bool> +// CIR: %[[A:.*]] = cir.alloca "a" {{.*}} init : !cir.ptr<!rec_Agg> +// CIR: %[[C_VAL:.*]] = cir.load{{.*}} %[[C]] : !cir.ptr<!cir.bool>, !cir.bool +// CIR: cir.if %[[C_VAL]] { +// CIR: %[[X:.*]] = cir.get_member %[[A]][0] {name = "x"} +// CIR: %[[ONE:.*]] = cir.const #cir.int<1> : !s32i +// CIR: cir.store{{.*}} %[[ONE]], %[[X]] +// CIR: %[[Y:.*]] = cir.get_member %[[A]][1] {name = "y"} +// CIR: %[[TWO:.*]] = cir.const #cir.int<2> : !s32i +// CIR: cir.store{{.*}} %[[TWO]], %[[Y]] +// CIR-NEXT: } else { +// CIR: %[[X2:.*]] = cir.get_member %[[A]][0] {name = "x"} +// CIR: %[[THREE:.*]] = cir.const #cir.int<3> : !s32i +// CIR: cir.store{{.*}} %[[THREE]], %[[X2]] +// CIR: %[[Y2:.*]] = cir.get_member %[[A]][1] {name = "y"} +// CIR: %[[FOUR:.*]] = cir.const #cir.int<4> : !s32i +// CIR: cir.store{{.*}} %[[FOUR]], %[[Y2]] +// CIR-NEXT: } +// CIR: cir.return + +// LLVM-LABEL: define{{.*}} void @_Z20test_agg_init_normalb( +// LLVM: br i1 %{{.*}}, label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]] +// LLVM: [[TRUE_BB]]: +// LLVM: store i32 1, ptr %{{.*}} +// LLVM: store i32 2, ptr %{{.*}} +// LLVM: br label %[[END:.*]] +// LLVM: [[FALSE_BB]]: +// LLVM: store i32 3, ptr %{{.*}} +// LLVM: store i32 4, ptr %{{.*}} +// LLVM: br label %[[END]] +// LLVM: [[END]]: +// LLVM: ret void + +// OGCG-LABEL: define{{.*}} void @_Z20test_agg_init_normalb( +// OGCG: br i1 %{{.*}}, label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]] +// OGCG: [[TRUE_BB]]: +// OGCG: store i32 1, ptr %{{.*}} +// OGCG: store i32 2, ptr %{{.*}} +// OGCG: br label %[[END:.*]] +// OGCG: [[FALSE_BB]]: +// OGCG: store i32 3, ptr %{{.*}} +// OGCG: store i32 4, ptr %{{.*}} +// OGCG: br label %[[END]] +// OGCG: [[END]]: +// OGCG: ret void + +// Assignment context: the conditional materializes into a temporary that is +// then assigned to the target. +void test_agg_assign_throw(bool c, Agg &a) { + a = c ? throw 0 : Agg{1, 2}; +} + +// CIR-LABEL: cir.func{{.*}} @_Z21test_agg_assign_throwbR3Agg( +// CIR: %[[C:.*]] = cir.alloca "c" {{.*}} init : !cir.ptr<!cir.bool> +// CIR: %[[A_REF:.*]] = cir.alloca "a" {{.*}} init const : !cir.ptr<!cir.ptr<!rec_Agg>> +// CIR: %[[TMP:.*]] = cir.alloca "ref.tmp0" {{.*}} : !cir.ptr<!rec_Agg> +// CIR: %[[C_VAL:.*]] = cir.load{{.*}} %[[C]] : !cir.ptr<!cir.bool>, !cir.bool +// CIR: cir.if %[[C_VAL]] { +// CIR: %[[EXC:.*]] = cir.alloc.exception{{.*}} -> !cir.ptr<!s32i> +// CIR: cir.throw %[[EXC]] : !cir.ptr<!s32i>, @_ZTIi +// CIR: cir.unreachable +// CIR-NEXT: } else { +// CIR: cir.get_member %[[TMP]][0] {name = "x"} +// CIR: cir.get_member %[[TMP]][1] {name = "y"} +// CIR: } +// CIR: %[[A_VAL:.*]] = cir.load %[[A_REF]] +// CIR: cir.call @_ZN3AggaSEOS_(%[[A_VAL]], %[[TMP]]) + +// LLVM-LABEL: define{{.*}} void @_Z21test_agg_assign_throwbR3Agg( +// LLVM: br i1 %{{.*}}, label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]] +// LLVM: [[TRUE_BB]]: +// LLVM: call{{.*}} ptr @__cxa_allocate_exception +// LLVM: call void @__cxa_throw(ptr %{{.*}}, ptr @_ZTIi +// LLVM: unreachable +// LLVM: [[FALSE_BB]]: +// LLVM: store i32 1, ptr %{{.*}} +// LLVM: store i32 2, ptr %{{.*}} +// LLVM: br label %[[END:.*]] +// LLVM: [[END]]: +// LLVM: call{{.*}} ptr @_ZN3AggaSEOS_( + +// OGCG-LABEL: define{{.*}} void @_Z21test_agg_assign_throwbR3Agg( +// OGCG: br i1 %{{.*}}, label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]] +// OGCG: [[TRUE_BB]]: +// OGCG: call{{.*}} ptr @__cxa_allocate_exception +// OGCG: call void @__cxa_throw(ptr %{{.*}}, ptr @_ZTIi +// OGCG: unreachable +// OGCG: [[FALSE_BB]]: +// OGCG: store i32 1, ptr %{{.*}} +// OGCG: store i32 2, ptr %{{.*}} +// OGCG: br label %[[END:.*]] +// OGCG: [[END]]: +// OGCG: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %{{.*}}, ptr align 4 %{{.*}}, i64 8 + +// Nested conditional: the inner throw arm terminates the inner cir.if region +// directly. +void test_agg_nested_throw(bool c1, bool c2) { + Agg a = c1 ? (c2 ? throw 0 : Agg{1, 2}) : Agg{3, 4}; +} + +// CIR-LABEL: cir.func{{.*}} @_Z21test_agg_nested_throwbb( +// CIR: %[[A:.*]] = cir.alloca "a" {{.*}} init : !cir.ptr<!rec_Agg> +// CIR: cir.if %{{.*}} { +// CIR: %[[C2_VAL:.*]] = cir.load{{.*}} : !cir.ptr<!cir.bool>, !cir.bool +// CIR: cir.if %[[C2_VAL]] { +// CIR: %[[EXC:.*]] = cir.alloc.exception{{.*}} -> !cir.ptr<!s32i> +// CIR: cir.throw %[[EXC]] : !cir.ptr<!s32i>, @_ZTIi +// CIR: cir.unreachable +// CIR-NEXT: } else { +// CIR: cir.get_member %[[A]][0] {name = "x"} +// CIR: cir.get_member %[[A]][1] {name = "y"} +// CIR: } +// CIR: } else { +// CIR: cir.get_member %[[A]][0] {name = "x"} +// CIR: cir.get_member %[[A]][1] {name = "y"} +// CIR: } +// CIR: cir.return + +// LLVM-LABEL: define{{.*}} void @_Z21test_agg_nested_throwbb( +// LLVM: br i1 %{{.*}}, label %[[OUTER_TRUE:.*]], label %[[OUTER_FALSE:.*]] +// LLVM: [[OUTER_TRUE]]: +// LLVM: br i1 %{{.*}}, label %[[INNER_TRUE:.*]], label %[[INNER_FALSE:.*]] +// LLVM: [[INNER_TRUE]]: +// LLVM: call void @__cxa_throw(ptr %{{.*}}, ptr @_ZTIi +// LLVM: unreachable +// LLVM: [[INNER_FALSE]]: +// LLVM: store i32 1, ptr %{{.*}} +// LLVM: store i32 2, ptr %{{.*}} +// LLVM: [[OUTER_FALSE]]: +// LLVM: store i32 3, ptr %{{.*}} +// LLVM: store i32 4, ptr %{{.*}} + +// OGCG-LABEL: define{{.*}} void @_Z21test_agg_nested_throwbb( +// OGCG: br i1 %{{.*}}, label %[[OUTER_TRUE:.*]], label %[[OUTER_FALSE:.*]] +// OGCG: [[OUTER_TRUE]]: +// OGCG: br i1 %{{.*}}, label %[[INNER_TRUE:.*]], label %[[INNER_FALSE:.*]] +// OGCG: [[INNER_TRUE]]: +// OGCG: call void @__cxa_throw(ptr %{{.*}}, ptr @_ZTIi +// OGCG: unreachable +// OGCG: [[INNER_FALSE]]: +// OGCG: store i32 1, ptr %{{.*}} +// OGCG: store i32 2, ptr %{{.*}} +// OGCG: [[OUTER_FALSE]]: +// OGCG: store i32 3, ptr %{{.*}} +// OGCG: store i32 4, ptr %{{.*}} + +// Call-argument context: the conditional materializes the argument temporary. +void test_agg_arg_throw(bool c) { + take(c ? throw 0 : Agg{1, 2}); +} + +// CIR-LABEL: cir.func{{.*}} @_Z18test_agg_arg_throwb( +// CIR: %[[TMP:.*]] = cir.alloca "agg.tmp0" {{.*}} : !cir.ptr<!rec_Agg> +// CIR: cir.if %{{.*}} { +// CIR: %[[EXC:.*]] = cir.alloc.exception{{.*}} -> !cir.ptr<!s32i> +// CIR: cir.throw %[[EXC]] : !cir.ptr<!s32i>, @_ZTIi +// CIR: cir.unreachable +// CIR-NEXT: } else { +// CIR: cir.get_member %[[TMP]][0] {name = "x"} +// CIR: cir.get_member %[[TMP]][1] {name = "y"} +// CIR: } +// CIR: %[[ARG:.*]] = cir.load{{.*}} %[[TMP]] : !cir.ptr<!rec_Agg>, !rec_Agg +// CIR: cir.call @_Z4take3Agg(%[[ARG]]) + +// LLVM-LABEL: define{{.*}} void @_Z18test_agg_arg_throwb( +// LLVM: br i1 %{{.*}}, label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]] +// LLVM: [[TRUE_BB]]: +// LLVM: call void @__cxa_throw(ptr %{{.*}}, ptr @_ZTIi +// LLVM: unreachable +// LLVM: [[FALSE_BB]]: +// LLVM: store i32 1, ptr %{{.*}} +// LLVM: store i32 2, ptr %{{.*}} +// LLVM: br label %[[END:.*]] +// LLVM: [[END]]: +// LLVM: call void @_Z4take3Agg( + +// OGCG-LABEL: define{{.*}} void @_Z18test_agg_arg_throwb( +// OGCG: br i1 %{{.*}}, label %[[TRUE_BB:.*]], label %[[FALSE_BB:.*]] +// OGCG: [[TRUE_BB]]: +// OGCG: call void @__cxa_throw(ptr %{{.*}}, ptr @_ZTIi +// OGCG: unreachable +// OGCG: [[FALSE_BB]]: +// OGCG: store i32 1, ptr %{{.*}} +// OGCG: store i32 2, ptr %{{.*}} +// OGCG: [[END:.*]]: +// OGCG: call void @_Z4take3Agg( _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
