https://github.com/kumarak created 
https://github.com/llvm/llvm-project/pull/210384

Follow-up to #208850: @andykaylor's comments about 
`CIRGenFunction::emitConditionalBlocks` also have a similar insertion-point 
problem. 

The PR addresses the two related problems found while looking into the 
conditional branch emission path:

**1. Terminate conditional regions after creation instead of patching yields**
`CIRGenFunction::emitConditionalBlocks` still had the save-and-patch 
insertion-point machinery removed from the scalar path in #208850. The path is 
unreachable, and its guard is saved only after `cir.unreachable`, where a 
patched yield would generate the dead code. The machinery is removed; regions 
are closed with `terminateStructuredRegionBody` after creation.

The aggregate emitter had the reverse problem: an unconditional cir.yield after 
each arm lands in the dead block a throw leaves behind and keeps it in the 
final CIR:

```
struct test_s { int x; int y; };
void test_throw(bool flag) {
  test_s a = flag ? throw 0 : test_s{1, 2};
}
mlir
cir.if %2 {
  ...
  cir.throw %3 : !cir.ptr<!s32i>, @_ZTIi
  cir.unreachable
^bb1:  // no predecessors      <-- dead block kept alive by the yield
  cir.yield
} else {
  ...
}
```
The explicit yields are removed; `emitIfOnBoolExpr` already terminates each 
region, adding a yield only where one is missing. Tests pin the dead block's 
absence with CIR-NEXT after `cir.unreachable`.

**2. Fix glvalue conditionals with a throw arm using a region-local pointer**

```
int &ref_cond(bool c, int &x) { return c ? x : throw 0; }
// error: operand #0 does not dominate this use
// fatal error: CIR module verification error
```
With one arm throwing, `emitConditionalOperatorLValue` returned the surviving 
arm's LValue directly, valid in OG CodeGen's flat CFG, invalid in CIR where the 
pointer is materialized inside the ternary's region:

```
%4 = cir.ternary(%3, true {
  %8 = cir.load %1 ...          // pointer loaded inside the region
  cir.yield %8 : !cir.ptr<!s32i>
}, false { ... cir.unreachable })
cir.store %8, %2 ...            // region-local %8 used outside the region
```

Existing tests missed it because a local's pointer is a function-entry 
`alloca`, so dominance held by accident. The fix addresses the result through 
the ternary's result value, which the yield carries out. New tests cover a 
reference parameter, assignment via the conditional, and member access, all of 
which previously failed verification.

Assisted-by: Codex


>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/3] [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 c173665411e2ce2d5e518b89aa11ad4ae295fcc3 Mon Sep 17 00:00:00 2001
From: AkshayK <[email protected]>
Date: Fri, 17 Jul 2026 11:58:41 -0400
Subject: [PATCH 2/3] [CIR] Use the ternary result for glvalue conditionals
 with a throw arm

---
 clang/lib/CIR/CodeGen/CIRGenExpr.cpp     |  12 +-
 clang/test/CIR/CodeGen/ternary-throw.cpp | 141 ++++++++++++++++++++++-
 2 files changed, 146 insertions(+), 7 deletions(-)

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/ternary-throw.cpp 
b/clang/test/CIR/CodeGen/ternary-throw.cpp
index 1d05f965a0e01..7af189c447601 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,132 @@ 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]]

>From 4c02baa5e6a7e9bd9706b36c1debf82d2afa21c0 Mon Sep 17 00:00:00 2001
From: AkshayK <[email protected]>
Date: Fri, 17 Jul 2026 12:25:15 -0400
Subject: [PATCH 3/3] [CIR] Merge agg conditional tests into ternary-throw.cpp

---
 clang/test/CIR/CodeGen/agg-cond-throw.cpp | 207 ----------------------
 clang/test/CIR/CodeGen/ternary-throw.cpp  | 200 +++++++++++++++++++++
 2 files changed, 200 insertions(+), 207 deletions(-)
 delete mode 100644 clang/test/CIR/CodeGen/agg-cond-throw.cpp

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 7af189c447601..2f09d1d7a4ae7 100644
--- a/clang/test/CIR/CodeGen/ternary-throw.cpp
+++ b/clang/test/CIR/CodeGen/ternary-throw.cpp
@@ -868,3 +868,203 @@ int &test_member_cond_throw(bool c, struct s6 *p) {
 // 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

Reply via email to