Author: Andy Kaylor
Date: 2026-08-03T18:55:58Z
New Revision: d5a6124259b55789bc49489632efa7c168a4f6cb

URL: 
https://github.com/llvm/llvm-project/commit/d5a6124259b55789bc49489632efa7c168a4f6cb
DIFF: 
https://github.com/llvm/llvm-project/commit/d5a6124259b55789bc49489632efa7c168a4f6cb.diff

LOG: [CIR] Fix cleanup of temporaries with null-checked new expr (#213376)

We had a problem where a new expression that required both a null check
of the allocated pointer and a cleanup of temporaries values created for
the initializer was causing us to generate an unterminated cleanup
scope. This was happening because we weren't pre-creating a cleanup
scope around the entire expression for the temporary and ended up
emitting it in an unexpected location.

This change adds a check for the null-checked new expression in
`ConditionalEvaluationFinder` and wraps the call to emit the initializer
with `ConditionEvalulation::begin/endEvaluation` calls.

Note that I have sunk the `begin/endEvaluation` into the `emitInit`
lambda so that it can surround just the `emitNewInitializer` call and
not the `enterNewDeleteCleanup` call, which creates a local cleanup
scope for the case where the allocation succeeded but the constructor
throws an exception. Classic codegen calls `begin/endEvaluation` at a
higher level and ends up using an active flag for the allocation delete
cleanup even though the flag is always true when that cleanup is
reached.

Assisted-by: Cursor / various models

Added: 
    

Modified: 
    clang/lib/CIR/CodeGen/CIRGenCleanup.cpp
    clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
    clang/test/CIR/CodeGen/new-null.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp 
b/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp
index 104a49c8bce26..07cbe34409ea4 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp
@@ -26,11 +26,8 @@ using namespace clang;
 using namespace clang::CIRGen;
 
 namespace {
-/// Return true if the expression tree contains an AbstractConditionalOperator
-/// (ternary ?:), which is the only construct whose CIR codegen calls
-/// ConditionalEvaluation::beginEvaluation() and thus causes cleanups to be
-/// deferred via pushFullExprCleanup.  Logical &&/|| do NOT call
-/// beginEvaluation(); their branch-local cleanups are handled by LexicalScope.
+/// Return true if the expression tree contains a construct that causes 
cleanups
+/// to be deferred via pushFullExprCleanup.
 class ConditionalEvaluationFinder
     : public RecursiveASTVisitor<ConditionalEvaluationFinder> {
   bool foundConditional = false;
@@ -43,6 +40,17 @@ class ConditionalEvaluationFinder
     return false;
   }
 
+  bool VisitCXXNewExpr(CXXNewExpr *e) {
+    // If the new expression has an initializer, the initializer may contain a
+    // a temporary expression that requires deferred cleanup. If we're emitting
+    // a null check, we need to make this cleanup conditional.
+    if (e->hasInitializer() && e->shouldNullCheckAllocation()) {
+      foundConditional = true;
+      return false;
+    }
+    return true;
+  }
+
   // Don't cross evaluation-context boundaries.
   bool TraverseLambdaExpr(LambdaExpr *) { return true; }
   bool TraverseBlockExpr(BlockExpr *) { return true; }

diff  --git a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
index b46fb8a93da42..43c210b44f011 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
@@ -1695,9 +1695,15 @@ mlir::Value CIRGenFunction::emitCXXNewExpr(const 
CXXNewExpr *e) {
 
   // Lambda that emits the init sequence: cleanup setup, cookie init,
   // bitcast + initializer, and cleanup deactivation.
+  //
+  // \p conditional is non-null when this new-expression is null-checked.
+  // ConditionalEvaluation is activated only around emitNewInitializer so that
+  // temporaries created there get conditional cleanups, while the operator
+  // delete cleanup (which is entered and left entirely inside the null-check
+  // branch) stays on the cheaper unconditional path.
   Address result = Address::invalid();
   Address resultPtr = Address::invalid();
-  auto emitInit = [&]() {
+  auto emitInit = [&](ConditionalEvaluation *conditional) {
     EHScopeStack::stable_iterator operatorDeleteCleanup;
     mlir::Operation *cleanupDominator = nullptr;
     if (useNewDeleteCleanup) {
@@ -1739,8 +1745,12 @@ mlir::Value CIRGenFunction::emitCXXNewExpr(const 
CXXNewExpr *e) {
 
     assert(!cir::MissingFeatures::sanitizers());
 
+    if (conditional)
+      conditional->beginEvaluation();
     emitNewInitializer(*this, e, allocType, elementTy, result, numElements,
                        allocSizeWithoutCookie);
+    if (conditional)
+      conditional->endEvaluation();
 
     // Deactivate the 'operator delete' cleanup if we finished
     // initialization.
@@ -1755,17 +1765,23 @@ mlir::Value CIRGenFunction::emitCXXNewExpr(const 
CXXNewExpr *e) {
 
   cir::IfOp nullCheckOp;
   if (nullCheck) {
+    // The initializer is only run if the allocation succeeds, so any
+    // temporaries created while emitting the initializer must be cleaned up
+    // conditionally (with an active flag) after the branch. The enclosing
+    // FullExprCleanupScope detects this via ConditionalEvaluationFinder and
+    // provides the cleanup region for the deferred destructors.
+    ConditionalEvaluation eval(*this);
     mlir::Value isNotNull = 
builder.createPtrIsNotNull(allocation.getPointer());
     nullCheckOp =
         cir::IfOp::create(builder, getLoc(e->getSourceRange()), isNotNull,
                           /*withElseRegion=*/false,
                           /*thenBuilder=*/
                           [&](mlir::OpBuilder &, mlir::Location loc) {
-                            emitInit();
+                            emitInit(&eval);
                             builder.createYield(loc);
                           });
   } else {
-    emitInit();
+    emitInit(/*conditional=*/nullptr);
   }
 
   mlir::Value resultValue = result.getPointer();

diff  --git a/clang/test/CIR/CodeGen/new-null.cpp 
b/clang/test/CIR/CodeGen/new-null.cpp
index 1aa085e6d95d0..298ab033c149e 100644
--- a/clang/test/CIR/CodeGen/new-null.cpp
+++ b/clang/test/CIR/CodeGen/new-null.cpp
@@ -114,3 +114,266 @@ int *test_nothrow_new_init() {
 // OGCG:   br label %[[CONT]]
 // OGCG: [[CONT]]:
 // OGCG:   phi ptr
+
+struct T {
+  T();
+  ~T();
+  operator int();
+};
+
+T makeT();
+
+struct U {
+  U(int);
+  ~U();
+};
+
+// nothrow new with a temporary in the initializer: the temporary's dtor 
cleanup
+// must be conditional because the initializer only runs when allocation 
succeeds.
+// The operator delete cleanup stays unconditional: it is entered entirely 
inside
+// the null-check branch.
+U *test_nothrow_new_temp() {
+  return new (nothrow) U(makeT());
+}
+
+// CHECK: cir.func {{.*}} @_Z21test_nothrow_new_tempv()
+// CHECK:   %[[TMP:.*]] = cir.alloca "ref.tmp0" {{.*}} : !cir.ptr<!rec_T>
+// CHECK:   %[[TMP_ACTIVE:.*]] = cir.alloca "cleanup.cond" {{.*}} : 
!cir.ptr<!cir.bool>
+// CHECK:   cir.cleanup.scope {
+// CHECK:     %[[ALLOC:.*]] = cir.call @_ZnwmRKSt9nothrow_t({{.*}}) nothrow
+// CHECK:     %[[NULL:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!void>
+// CHECK:     %[[IS_NOT_NULL:.*]] = cir.cmp ne %[[ALLOC]], %[[NULL]] : 
!cir.ptr<!void>
+// CHECK:     %[[FALSE:.*]] = cir.const #false
+// CHECK:     cir.store %[[FALSE]], %[[TMP_ACTIVE]] : !cir.bool, 
!cir.ptr<!cir.bool>
+// CHECK:     cir.if %[[IS_NOT_NULL]] {
+// CHECK:       cir.cleanup.scope {
+// CHECK:         %[[MAKE_T:.*]] = cir.call @_Z5makeTv() : () -> !rec_T
+// CHECK:         cir.store{{.*}} %[[MAKE_T]], %[[TMP]] : !rec_T, 
!cir.ptr<!rec_T>
+// CHECK:         %[[TRUE:.*]] = cir.const #true
+// CHECK:         cir.store %[[TRUE]], %[[TMP_ACTIVE]] : !cir.bool, 
!cir.ptr<!cir.bool>
+// CHECK:         %[[CONV:.*]] = cir.call @_ZN1TcviEv(%[[TMP]])
+// CHECK:         cir.call @_ZN1UC1Ei({{.*}}, %[[CONV]])
+// CHECK:       } cleanup eh {
+// CHECK:         cir.call @_ZdlPvRKSt9nothrow_t(%[[ALLOC]], {{.*}}) nothrow
+// CHECK:       } loc({{.*}})
+// CHECK:     } loc({{.*}})
+// CHECK:     %[[LOADED:.*]] = cir.load{{.*}} : !cir.ptr<!cir.ptr<!rec_U>>, 
!cir.ptr<!rec_U>
+// CHECK:     %[[NULL_U:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!rec_U>
+// CHECK:     cir.select if %[[IS_NOT_NULL]] then %[[LOADED]] else %[[NULL_U]]
+// CHECK:   } cleanup all {
+// CHECK:     %[[TMP_IS_ACTIVE:.*]] = cir.load{{.*}} %[[TMP_ACTIVE]] : 
!cir.ptr<!cir.bool>, !cir.bool
+// CHECK:     cir.if %[[TMP_IS_ACTIVE]] {
+// CHECK:       cir.call @_ZN1TD1Ev(%[[TMP]]) nothrow
+// CHECK:     }
+// CHECK:   }
+
+// LLVM: define {{.*}} ptr @_Z21test_nothrow_new_tempv() {{.*}}personality ptr 
@__gxx_personality_v0
+// LLVM:   %[[TMP:.*]] = alloca %struct.T
+// LLVM:   %[[ALLOC:.*]] = call {{.*}} ptr @_ZnwmRKSt9nothrow_t(i64 noundef 1, 
{{.*}})
+// LLVM:   %[[CMP:.*]] = icmp ne ptr %[[ALLOC]], null
+// LLVM:   store i8 0, ptr %[[TMP_ACTIVE:.*]]
+// LLVM:   br i1 %[[CMP]], label %[[NOT_NULL:.*]], label %[[CONT:.*]]
+// LLVM: [[NOT_NULL]]:
+// LLVM:   %[[MAKE_T:.*]] = invoke %struct.T @_Z5makeTv()
+// LLVM:           to label %[[INVOKE_CONT:.*]] unwind label %[[LPAD:.*]]
+// LLVM: [[INVOKE_CONT]]:
+// LLVM:   store {{.*}} %[[MAKE_T]], ptr %[[TMP]]
+// LLVM:   store i8 1, ptr %[[TMP_ACTIVE]]
+// LLVM:   invoke {{.*}} @_ZN1TcviEv(ptr {{.*}} %[[TMP]])
+// LLVM:   invoke void @_ZN1UC1Ei(ptr {{.*}} %[[ALLOC]], i32 {{.*}})
+// LLVM: [[LPAD]]:
+// LLVM:   landingpad { ptr, i32 }
+// LLVM:          cleanup
+// LLVM:   call void @_ZdlPvRKSt9nothrow_t({{.*}} %[[ALLOC]], {{.*}})
+// LLVM: [[CONT]]:
+// LLVM:   select i1 %[[CMP]], ptr {{.*}}, ptr null
+// LLVM:   %[[TMP_I8:.*]] = load i8, ptr %[[TMP_ACTIVE]]
+// LLVM:   %[[TMP_IS_ACTIVE:.*]] = trunc i8 %[[TMP_I8]] to i1
+// LLVM:   br i1 %[[TMP_IS_ACTIVE]], label %[[DO_TMP_DTOR:.*]], label 
%[[SKIP_TMP_DTOR:.*]]
+// LLVM: [[DO_TMP_DTOR]]:
+// LLVM:   call void @_ZN1TD1Ev(ptr {{.*}} %[[TMP]])
+
+// OGCG: define {{.*}} ptr @_Z21test_nothrow_new_tempv() {{.*}}personality ptr 
@__gxx_personality_v0
+// OGCG: entry:
+// OGCG:   %[[TMP:.*]] = alloca %struct.T
+// OGCG:   %[[ALLOC:.*]] = call {{.*}} ptr @_ZnwmRKSt9nothrow_t(i64 noundef 1, 
{{.*}})
+// OGCG:   %[[IS_NULL:.*]] = icmp eq ptr %[[ALLOC]], null
+// OGCG:   store i1 false, ptr %[[DELETE_ACTIVE:.*]]
+// OGCG:   store i1 false, ptr %[[TMP_ACTIVE:.*]]
+// OGCG:   br i1 %[[IS_NULL]], label %[[CONT:.*]], label %[[NOT_NULL:.*]]
+// OGCG: [[NOT_NULL]]:
+// OGCG:   store i1 true, ptr %[[DELETE_ACTIVE]]
+// OGCG:   invoke void @_Z5makeTv(ptr {{.*}} %[[TMP]])
+// OGCG:           to label %[[INVOKE_CONT:.*]] unwind label %[[LPAD:.*]]
+// OGCG: [[INVOKE_CONT]]:
+// OGCG:   store i1 true, ptr %[[TMP_ACTIVE]]
+// OGCG:   invoke {{.*}} @_ZN1TcviEv(ptr {{.*}} %[[TMP]])
+// OGCG:   invoke void @_ZN1UC1Ei(ptr {{.*}} %[[ALLOC]], i32 {{.*}})
+// OGCG:   store i1 false, ptr %[[DELETE_ACTIVE]]
+// OGCG:   br label %[[CONT]]
+// OGCG: [[CONT]]:
+// OGCG:   phi ptr
+// OGCG:   %[[TMP_IS_ACTIVE:.*]] = load i1, ptr %[[TMP_ACTIVE]]
+// OGCG:   br i1 %[[TMP_IS_ACTIVE]], label %[[DO_TMP_DTOR:.*]], label 
%[[SKIP_TMP_DTOR:.*]]
+// OGCG: [[DO_TMP_DTOR]]:
+// OGCG:   call void @_ZN1TD1Ev(ptr {{.*}} %[[TMP]])
+// OGCG: [[LPAD]]:
+// OGCG:   landingpad { ptr, i32 }
+// OGCG:          cleanup
+// OGCG:   %[[DEL_ACTIVE:.*]] = load i1, ptr %[[DELETE_ACTIVE]]
+// OGCG:   br i1 %[[DEL_ACTIVE]], label %[[DO_DELETE:.*]], label 
%[[SKIP_DELETE:.*]]
+// OGCG: [[DO_DELETE]]:
+// OGCG:   call void @_ZdlPvRKSt9nothrow_t({{.*}})
+
+struct InnerT {
+  InnerT();
+  ~InnerT();
+  operator int();
+};
+
+struct OuterT {
+  OuterT(int);
+  ~OuterT();
+  operator int();
+};
+
+InnerT makeInnerT();
+OuterT makeOuterT(int);
+
+// Nested temporaries in a nothrow-new initializer: each temporary gets its own
+// conditional cleanup flag, destroyed in reverse construction order.
+U *test_nothrow_new_nested_temps() {
+  return new (nothrow) U(makeOuterT(makeInnerT()));
+}
+
+// CHECK: cir.func {{.*}} @_Z29test_nothrow_new_nested_tempsv()
+// CHECK:   %[[OUTER_TMP:.*]] = cir.alloca "ref.tmp0" {{.*}} : 
!cir.ptr<!rec_OuterT>
+// CHECK:   %[[INNER_TMP:.*]] = cir.alloca "ref.tmp1" {{.*}} : 
!cir.ptr<!rec_InnerT>
+// CHECK:   %[[INNER_ACTIVE:.*]] = cir.alloca "cleanup.cond" {{.*}} : 
!cir.ptr<!cir.bool>
+// CHECK:   %[[OUTER_ACTIVE:.*]] = cir.alloca "cleanup.cond" {{.*}} : 
!cir.ptr<!cir.bool>
+// CHECK:   cir.cleanup.scope {
+// CHECK:     %[[ALLOC:.*]] = cir.call @_ZnwmRKSt9nothrow_t({{.*}}) nothrow
+// CHECK:     %[[NULL:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!void>
+// CHECK:     %[[IS_NOT_NULL:.*]] = cir.cmp ne %[[ALLOC]], %[[NULL]] : 
!cir.ptr<!void>
+// CHECK:     %[[FALSE:.*]] = cir.const #false
+// CHECK:     cir.store %[[FALSE]], %[[INNER_ACTIVE]] : !cir.bool, 
!cir.ptr<!cir.bool>
+// CHECK:     %[[FALSE2:.*]] = cir.const #false
+// CHECK:     cir.store %[[FALSE2]], %[[OUTER_ACTIVE]] : !cir.bool, 
!cir.ptr<!cir.bool>
+// CHECK:     cir.if %[[IS_NOT_NULL]] {
+// CHECK:       cir.cleanup.scope {
+// CHECK:         %[[MAKE_INNER:.*]] = cir.call @_Z10makeInnerTv() : () -> 
!rec_InnerT
+// CHECK:         cir.store{{.*}} %[[MAKE_INNER]], %[[INNER_TMP]] : 
!rec_InnerT, !cir.ptr<!rec_InnerT>
+// CHECK:         %[[TRUE:.*]] = cir.const #true
+// CHECK:         cir.store %[[TRUE]], %[[INNER_ACTIVE]] : !cir.bool, 
!cir.ptr<!cir.bool>
+// CHECK:         %[[INNER_CONV:.*]] = cir.call 
@_ZN6InnerTcviEv(%[[INNER_TMP]])
+// CHECK:         %[[MAKE_OUTER:.*]] = cir.call 
@_Z10makeOuterTi(%[[INNER_CONV]]) : (!s32i {{.*}}) -> !rec_OuterT
+// CHECK:         cir.store{{.*}} %[[MAKE_OUTER]], %[[OUTER_TMP]] : 
!rec_OuterT, !cir.ptr<!rec_OuterT>
+// CHECK:         %[[TRUE2:.*]] = cir.const #true
+// CHECK:         cir.store %[[TRUE2]], %[[OUTER_ACTIVE]] : !cir.bool, 
!cir.ptr<!cir.bool>
+// CHECK:         %[[OUTER_CONV:.*]] = cir.call 
@_ZN6OuterTcviEv(%[[OUTER_TMP]])
+// CHECK:         cir.call @_ZN1UC1Ei({{.*}}, %[[OUTER_CONV]])
+// CHECK:       } cleanup eh {
+// CHECK:         cir.call @_ZdlPvRKSt9nothrow_t(%[[ALLOC]], {{.*}}) nothrow
+// CHECK:       } loc({{.*}})
+// CHECK:     } loc({{.*}})
+// CHECK:     %[[LOADED:.*]] = cir.load{{.*}} : !cir.ptr<!cir.ptr<!rec_U>>, 
!cir.ptr<!rec_U>
+// CHECK:     %[[NULL_U:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!rec_U>
+// CHECK:     cir.select if %[[IS_NOT_NULL]] then %[[LOADED]] else %[[NULL_U]]
+// CHECK:   } cleanup all {
+// CHECK:     %[[OUTER_IS_ACTIVE:.*]] = cir.load{{.*}} %[[OUTER_ACTIVE]] : 
!cir.ptr<!cir.bool>, !cir.bool
+// CHECK:     cir.if %[[OUTER_IS_ACTIVE]] {
+// CHECK:       cir.call @_ZN6OuterTD1Ev(%[[OUTER_TMP]]) nothrow
+// CHECK:     }
+// CHECK:     %[[INNER_IS_ACTIVE:.*]] = cir.load{{.*}} %[[INNER_ACTIVE]] : 
!cir.ptr<!cir.bool>, !cir.bool
+// CHECK:     cir.if %[[INNER_IS_ACTIVE]] {
+// CHECK:       cir.call @_ZN6InnerTD1Ev(%[[INNER_TMP]]) nothrow
+// CHECK:     }
+// CHECK:   }
+
+// LLVM: define {{.*}} ptr @_Z29test_nothrow_new_nested_tempsv() 
{{.*}}personality ptr @__gxx_personality_v0
+// LLVM:   %[[OUTER_TMP:.*]] = alloca %struct.OuterT
+// LLVM:   %[[INNER_TMP:.*]] = alloca %struct.InnerT
+// LLVM:   %[[ALLOC:.*]] = call {{.*}} ptr @_ZnwmRKSt9nothrow_t(i64 noundef 1, 
{{.*}})
+// LLVM:   %[[CMP:.*]] = icmp ne ptr %[[ALLOC]], null
+// LLVM:   store i8 0, ptr %[[INNER_ACTIVE:.*]]
+// LLVM:   store i8 0, ptr %[[OUTER_ACTIVE:.*]]
+// LLVM:   br i1 %[[CMP]], label %[[NOT_NULL:.*]], label %[[CONT:.*]]
+// LLVM: [[NOT_NULL]]:
+// LLVM:   %[[MAKE_INNER:.*]] = invoke %struct.InnerT @_Z10makeInnerTv()
+// LLVM:           to label %[[INNER_CONT:.*]] unwind label %[[LPAD:.*]]
+// LLVM: [[INNER_CONT]]:
+// LLVM:   store {{.*}} %[[MAKE_INNER]], ptr %[[INNER_TMP]]
+// LLVM:   store i8 1, ptr %[[INNER_ACTIVE]]
+// LLVM:   %[[INNER_CONV:.*]] = invoke {{.*}} @_ZN6InnerTcviEv(ptr {{.*}} 
%[[INNER_TMP]])
+// LLVM:           to label %[[INNER_CONV_CONT:.*]] unwind label %[[LPAD]]
+// LLVM: [[INNER_CONV_CONT]]:
+// LLVM:   %[[MAKE_OUTER:.*]] = invoke %struct.OuterT @_Z10makeOuterTi(i32 
{{.*}} %[[INNER_CONV]])
+// LLVM:           to label %[[OUTER_CONT:.*]] unwind label %[[LPAD]]
+// LLVM: [[OUTER_CONT]]:
+// LLVM:   store {{.*}} %[[MAKE_OUTER]], ptr %[[OUTER_TMP]]
+// LLVM:   store i8 1, ptr %[[OUTER_ACTIVE]]
+// LLVM:   invoke {{.*}} @_ZN6OuterTcviEv(ptr {{.*}} %[[OUTER_TMP]])
+// LLVM:   invoke void @_ZN1UC1Ei(ptr {{.*}} %[[ALLOC]], i32 {{.*}})
+// LLVM: [[LPAD]]:
+// LLVM:   landingpad { ptr, i32 }
+// LLVM:          cleanup
+// LLVM:   call void @_ZdlPvRKSt9nothrow_t({{.*}} %[[ALLOC]], {{.*}})
+// LLVM: [[CONT]]:
+// LLVM:   select i1 %[[CMP]], ptr {{.*}}, ptr null
+// LLVM:   %[[OUTER_I8:.*]] = load i8, ptr %[[OUTER_ACTIVE]]
+// LLVM:   %[[OUTER_IS_ACTIVE:.*]] = trunc i8 %[[OUTER_I8]] to i1
+// LLVM:   br i1 %[[OUTER_IS_ACTIVE]], label %[[DO_OUTER_DTOR:.*]], label 
%[[SKIP_OUTER_DTOR:.*]]
+// LLVM: [[DO_OUTER_DTOR]]:
+// LLVM:   call void @_ZN6OuterTD1Ev(ptr {{.*}} %[[OUTER_TMP]])
+// LLVM: [[SKIP_OUTER_DTOR]]:
+// LLVM:   %[[INNER_I8:.*]] = load i8, ptr %[[INNER_ACTIVE]]
+// LLVM:   %[[INNER_IS_ACTIVE:.*]] = trunc i8 %[[INNER_I8]] to i1
+// LLVM:   br i1 %[[INNER_IS_ACTIVE]], label %[[DO_INNER_DTOR:.*]], label 
%[[SKIP_INNER_DTOR:.*]]
+// LLVM: [[DO_INNER_DTOR]]:
+// LLVM:   call void @_ZN6InnerTD1Ev(ptr {{.*}} %[[INNER_TMP]])
+
+// OGCG: define {{.*}} ptr @_Z29test_nothrow_new_nested_tempsv() 
{{.*}}personality ptr @__gxx_personality_v0
+// OGCG: entry:
+// OGCG:   %[[OUTER_TMP:.*]] = alloca %struct.OuterT
+// OGCG:   %[[INNER_TMP:.*]] = alloca %struct.InnerT
+// OGCG:   %[[ALLOC:.*]] = call {{.*}} ptr @_ZnwmRKSt9nothrow_t(i64 noundef 1, 
{{.*}})
+// OGCG:   %[[IS_NULL:.*]] = icmp eq ptr %[[ALLOC]], null
+// OGCG:   store i1 false, ptr %[[DELETE_ACTIVE:.*]]
+// OGCG:   store i1 false, ptr %[[INNER_ACTIVE:.*]]
+// OGCG:   store i1 false, ptr %[[OUTER_ACTIVE:.*]]
+// OGCG:   br i1 %[[IS_NULL]], label %[[CONT:.*]], label %[[NOT_NULL:.*]]
+// OGCG: [[NOT_NULL]]:
+// OGCG:   store i1 true, ptr %[[DELETE_ACTIVE]]
+// OGCG:   invoke void @_Z10makeInnerTv(ptr {{.*}} %[[INNER_TMP]])
+// OGCG:           to label %[[INNER_CONT:.*]] unwind label %[[LPAD:.*]]
+// OGCG: [[INNER_CONT]]:
+// OGCG:   store i1 true, ptr %[[INNER_ACTIVE]]
+// OGCG:   %[[INNER_CONV:.*]] = invoke {{.*}} @_ZN6InnerTcviEv(ptr {{.*}} 
%[[INNER_TMP]])
+// OGCG:           to label %[[INNER_CONV_CONT:.*]] unwind label 
%[[LPAD_INNER:.*]]
+// OGCG: [[INNER_CONV_CONT]]:
+// OGCG:   invoke void @_Z10makeOuterTi(ptr {{.*}} %[[OUTER_TMP]], i32 {{.*}} 
%[[INNER_CONV]])
+// OGCG:           to label %[[OUTER_CONT:.*]] unwind label %[[LPAD_INNER]]
+// OGCG: [[OUTER_CONT]]:
+// OGCG:   store i1 true, ptr %[[OUTER_ACTIVE]]
+// OGCG:   invoke {{.*}} @_ZN6OuterTcviEv(ptr {{.*}} %[[OUTER_TMP]])
+// OGCG:   invoke void @_ZN1UC1Ei(ptr {{.*}} %[[ALLOC]], i32 {{.*}})
+// OGCG:   store i1 false, ptr %[[DELETE_ACTIVE]]
+// OGCG:   br label %[[CONT]]
+// OGCG: [[CONT]]:
+// OGCG:   phi ptr
+// OGCG:   %[[OUTER_IS_ACTIVE:.*]] = load i1, ptr %[[OUTER_ACTIVE]]
+// OGCG:   br i1 %[[OUTER_IS_ACTIVE]], label %[[DO_OUTER_DTOR:.*]], label 
%[[SKIP_OUTER_DTOR:.*]]
+// OGCG: [[DO_OUTER_DTOR]]:
+// OGCG:   call void @_ZN6OuterTD1Ev(ptr {{.*}} %[[OUTER_TMP]])
+// OGCG: [[SKIP_OUTER_DTOR]]:
+// OGCG:   %[[INNER_IS_ACTIVE:.*]] = load i1, ptr %[[INNER_ACTIVE]]
+// OGCG:   br i1 %[[INNER_IS_ACTIVE]], label %[[DO_INNER_DTOR:.*]], label 
%[[SKIP_INNER_DTOR:.*]]
+// OGCG: [[DO_INNER_DTOR]]:
+// OGCG:   call void @_ZN6InnerTD1Ev(ptr {{.*}} %[[INNER_TMP]])
+// OGCG: [[LPAD]]:
+// OGCG:   landingpad { ptr, i32 }
+// OGCG:          cleanup
+// OGCG:   %[[DEL_ACTIVE:.*]] = load i1, ptr %[[DELETE_ACTIVE]]
+// OGCG:   br i1 %[[DEL_ACTIVE]], label %[[DO_DELETE:.*]], label 
%[[SKIP_DELETE:.*]]
+// OGCG: [[DO_DELETE]]:
+// OGCG:   call void @_ZdlPvRKSt9nothrow_t({{.*}})


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to