Author: Akash Manna
Date: 2026-10-31T04:46:33Z
New Revision: b5f241e103af0decc24956273c7aa36a300909f8

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

LOG: [clang][CodeGen] Fix missing terminator when a new-expression initializer 
starts with a loop (#230902)

Fixes #175427

`EmitCXXNewExpr` marks the point where the operator delete cleanup
becomes active with a placeholder `unreachable` and erases it once the
initializer is emitted. If the initializer starts with a loop or a
label, `EmitBlock` takes that terminator as the end of the block and
emits no fall-through branch, so erasing the placeholder leaves the
block unterminated and valid code fails with "Broken module found".

The marker only has to be an instruction that the deactivation can
insert a store before, while the rest of CodeGen assumes the current
block has no terminator until a real one is written. The placeholder is
now the `i1` load from null that the other cleanup markers already use,
so the block stays open and `EmitBlock` branches normally. The same
marker in `EmitDelegateCallArg` is switched for consistency.

LLM tools were used for this contribution.

Added: 
    clang/test/CodeGenCXX/new-init-stmt-expr.cpp

Modified: 
    clang/docs/ReleaseNotes.md
    clang/lib/CodeGen/CGCall.cpp
    clang/lib/CodeGen/CGExprCXX.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 54a4f6fde79d27..39aa1bf0a5f433 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -793,6 +793,7 @@ documentation](CIR/index.md) for more information regarding 
ClangIR's status and
 - Clang now defines the GCC-compatible predefined macro `__SIG_ATOMIC_TYPE__`. 
(#GH213895)
 - Fixed IEEE f128 complex mul/div using the IBM f128 libcalls on powerpc. 
(#GH216820)
 - Fixed an ICE that occurred when a structured binding pack is expanded 
outside the lambda where it was declared. (#GH214160)
+- Fixed a crash when a statement expression in the initializer of a 
new-expression starts with a loop or a label. (#GH175427)
 - Fixed a bug where a stray closing curley brace in an OpenMP/OpenACC pragma 
could cause pragma parsing issues when inside of a member function. (#GH214195)
 - Fixed a bug where preprocessor directives following comments were not 
correctly recognized when using -C. (#GH48361)
 - Fixed a crash when declaring a member template within a local class inside 
an OpenMP region. (#GH216052)

diff  --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index 9b8b0b52b66cb5..0b63ea4a97b365 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -4729,8 +4729,9 @@ void CodeGenFunction::EmitDelegateCallArg(CallArgList 
&args,
         CalleeDestructedParamCleanups.lookup(cast<ParmVarDecl>(param));
     assert(cleanup.isValid() &&
            "cleanup for callee-destructed param not recorded");
-    // This unreachable is a temporary marker which will be removed later.
-    llvm::Instruction *isActive = Builder.CreateUnreachable();
+    // This load is a temporary marker which will be removed later.
+    llvm::Instruction *isActive =
+        Builder.CreateFlagLoad(llvm::Constant::getNullValue(Int8PtrTy));
     args.addArgCleanupDeactivation(cleanup, isActive);
   }
 }

diff  --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp
index 60bb27a6fab69d..853289e71101b0 100644
--- a/clang/lib/CodeGen/CGExprCXX.cpp
+++ b/clang/lib/CodeGen/CGExprCXX.cpp
@@ -1798,7 +1798,8 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const 
CXXNewExpr *E) {
     EnterNewDeleteCleanup(*this, E, TypeIdentityArg, allocation, allocSize,
                           allocAlign, allocatorArgs);
     operatorDeleteCleanup = EHStack.stable_begin();
-    cleanupDominator = Builder.CreateUnreachable();
+    cleanupDominator =
+        Builder.CreateFlagLoad(llvm::Constant::getNullValue(Int8PtrTy));
   }
 
   // Without a cookie the two sizes must match. With a cookie they usually

diff  --git a/clang/test/CodeGenCXX/new-init-stmt-expr.cpp 
b/clang/test/CodeGenCXX/new-init-stmt-expr.cpp
new file mode 100644
index 00000000000000..08f58f090308c2
--- /dev/null
+++ b/clang/test/CodeGenCXX/new-init-stmt-expr.cpp
@@ -0,0 +1,216 @@
+// RUN: %clang_cc1 -std=c++20 -fexceptions -triple x86_64-linux-gnu -emit-llvm 
%s -o - | FileCheck -check-prefixes=CHECK,EH %s
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-linux-gnu -emit-llvm %s -o - | 
FileCheck -check-prefixes=CHECK,NOEH %s
+
+struct NoThrow {};
+void *operator new(decltype(sizeof 0), NoThrow) noexcept;
+void operator delete(void *, NoThrow) noexcept;
+
+namespace GH175427 {
+struct S {
+  S(int);
+};
+[[noreturn]] void nr();
+
+void WhileTrue() {
+  // CHECK-LABEL: define dso_local void @_ZN8GH1754279WhileTrueEv()
+  // CHECK:         call {{.*}}@_Znwm(i64 noundef 4)
+  // EH-NEXT:       br label %while.cond
+  // EH:          while.cond:
+  // EH-NEXT:       br label %while.body
+  // NOEH-NEXT:     br label %while.body
+  // CHECK:       while.body:
+  // EH-NEXT:       br label %while.cond
+  // NOEH-NEXT:     br label %while.body
+  new int(({
+    while (true)
+      ;
+    1;
+  }));
+}
+
+void ForFirst() {
+  // CHECK-LABEL: define dso_local void @_ZN8GH1754278ForFirstEv()
+  // CHECK:         call {{.*}}@_Znwm(i64 noundef 4)
+  // CHECK-NEXT:    br label %for.cond
+  // CHECK:       for.cond:
+  // CHECK-NEXT:    br label %for.cond
+  new int(({
+    for (;;)
+      ;
+    1;
+  }));
+}
+
+void DoFirst() {
+  // CHECK-LABEL: define dso_local void @_ZN8GH1754277DoFirstEv()
+  // CHECK:         [[CALL:%.+]] = call {{.*}}@_Znwm(i64 noundef 4)
+  // CHECK-NEXT:    br label %do.body
+  // CHECK:       do.body:
+  // CHECK-NEXT:    br label %do.cond
+  // CHECK:       do.cond:
+  // CHECK-NEXT:    br i1 true, label %do.body, label %do.end
+  // CHECK:       do.end:
+  // CHECK:         store i32 {{.*}}, ptr [[CALL]]
+  // CHECK-NEXT:    ret void
+  new int(({
+    do
+      ;
+    while (true);
+    1;
+  }));
+}
+
+void LabelFirst() {
+  // CHECK-LABEL: define dso_local void @_ZN8GH17542710LabelFirstEv()
+  // CHECK:         [[CALL:%.+]] = call {{.*}}@_Znwm(i64 noundef 4)
+  // CHECK-NEXT:    br label %L
+  // CHECK:       L: ; preds = %entry
+  // CHECK-NEXT:    store i32 1, ptr [[TMP:%[^,]+]]
+  // CHECK-NEXT:    [[V:%.+]] = load i32, ptr [[TMP]]
+  // CHECK-NEXT:    store i32 [[V]], ptr [[CALL]]
+  // CHECK-NEXT:    ret void
+  new int(({
+  L:;
+    1;
+  }));
+}
+
+void WhileX(bool x) {
+  // CHECK-LABEL: define dso_local void @_ZN8GH1754276WhileXEb(
+  // CHECK:         [[CALL:%.+]] = call {{.*}}@_Znwm(i64 noundef 4)
+  // CHECK-NEXT:    br label %while.cond
+  // CHECK:       while.cond:
+  // CHECK:         br i1 {{.*}}, label %while.body, label %while.end
+  // CHECK:       while.body:
+  // CHECK-NEXT:    br label %while.cond
+  // CHECK:       while.end:
+  // CHECK:         store i32 {{.*}}, ptr [[CALL]]
+  // CHECK-NEXT:    ret void
+  new int(({
+    while (x)
+      ;
+    1;
+  }));
+}
+
+void ArrayNew() {
+  // CHECK-LABEL: define dso_local void @_ZN8GH1754278ArrayNewEv()
+  // CHECK:         call {{.*}}@_Znam(i64 noundef 8)
+  // EH-NEXT:       br label %while.cond
+  // EH:          while.cond:
+  // EH-NEXT:       br label %while.body
+  // NOEH-NEXT:     br label %while.body
+  // CHECK:       while.body:
+  // EH-NEXT:       br label %while.cond
+  // NOEH-NEXT:     br label %while.body
+  new int[2]{({
+    while (true)
+      ;
+    1;
+  })};
+}
+
+void ClassType() {
+  // CHECK-LABEL: define dso_local void @_ZN8GH1754279ClassTypeEv()
+  // CHECK:         [[CALL:%.+]] = call {{.*}}@_Znwm(i64 noundef 1)
+  // EH-NEXT:       br label %while.cond
+  // EH:          while.cond:
+  // EH-NEXT:       br label %while.body
+  // NOEH-NEXT:     br label %while.body
+  // CHECK:       while.body:
+  // EH-NEXT:       br label %while.cond
+  // NOEH-NEXT:     br label %while.body
+  // EH:            invoke void @_ZN8GH1754271SC1Ei(ptr {{.*}}[[CALL]], i32 
{{.*}})
+  // EH-NEXT:       to label %invoke.cont unwind label %lpad
+  // EH:          lpad:
+  // EH:            call void @_ZdlPvm(ptr noundef [[CALL]], i64 noundef 1)
+  new S(({
+    while (true)
+      ;
+    1;
+  }));
+}
+
+void NothrowNew() {
+  // CHECK-LABEL: define dso_local void @_ZN8GH17542710NothrowNewEv()
+  // CHECK:         [[CALL:%.+]] = call {{.*}}@_Znwm7NoThrow(i64 noundef 4
+  // CHECK-NEXT:    [[ISNULL:%.+]] = icmp eq ptr [[CALL]], null
+  // CHECK:         br i1 [[ISNULL]], label %new.cont, label %new.notnull
+  // CHECK:       new.notnull:
+  // EH:            br label %while.cond
+  // EH:          while.cond:
+  // EH-NEXT:       br label %while.body
+  // NOEH-NEXT:     br label %while.body
+  // CHECK:       while.body:
+  // EH-NEXT:       br label %while.cond
+  // NOEH-NEXT:     br label %while.body
+  // CHECK:       new.cont:
+  // CHECK-NEXT:    phi ptr [ [[CALL]], %{{.*}} ], [ null, %entry ]
+  // CHECK-NEXT:    ret void
+  new (NoThrow{}) int(({
+    while (true)
+      ;
+    1;
+  }));
+}
+
+void ReturnFirst() {
+  // CHECK-LABEL: define dso_local void @_ZN8GH17542711ReturnFirstEv()
+  // CHECK:         call {{.*}}@_Znwm(i64 noundef 4)
+  // CHECK-NEXT:    ret void
+  new int(({
+    return;
+    1;
+  }));
+}
+
+void GotoFirst() {
+  // CHECK-LABEL: define dso_local void @_ZN8GH1754279GotoFirstEv()
+  // CHECK:         call {{.*}}@_Znwm(i64 noundef 4)
+  // CHECK-NEXT:    br label %L
+  // CHECK:       L: ; preds = %entry
+  // CHECK-NEXT:    ret void
+  new int(({
+    goto L;
+    1;
+  }));
+L:;
+}
+
+void NoreturnFirst() {
+  // CHECK-LABEL: define dso_local void @_ZN8GH17542713NoreturnFirstEv()
+  // CHECK:         [[CALL:%.+]] = call {{.*}}@_Znwm(i64 noundef 4)
+  // EH-NEXT:       invoke void @_ZN8GH1754272nrEv()
+  // EH-NEXT:       to label %invoke.cont unwind label %lpad
+  // EH:          invoke.cont:
+  // EH-NEXT:       unreachable
+  // EH:          lpad:
+  // EH:            call void @_ZdlPvm(ptr noundef [[CALL]], i64 noundef 4)
+  // NOEH-NEXT:     call void @_ZN8GH1754272nrEv()
+  // NOEH-NEXT:     unreachable
+  new int(({
+    nr();
+    1;
+  }));
+}
+
+void UnreachableFirst() {
+  // CHECK-LABEL: define dso_local void @_ZN8GH17542716UnreachableFirstEv()
+  // CHECK:         call {{.*}}@_Znwm(i64 noundef 4)
+  // CHECK-NEXT:    unreachable
+  new int(({
+    __builtin_unreachable();
+    1;
+  }));
+}
+
+void Plain() {
+  // CHECK-LABEL: define dso_local void @_ZN8GH1754275PlainEv()
+  // CHECK:         [[CALL:%.+]] = call {{.*}}@_Znwm(i64 noundef 4)
+  // CHECK-NEXT:    store i32 1, ptr [[TMP:%[^,]+]]
+  // CHECK-NEXT:    [[V:%.+]] = load i32, ptr [[TMP]]
+  // CHECK-NEXT:    store i32 [[V]], ptr [[CALL]]
+  // CHECK-NEXT:    ret void
+  new int(({ 1; }));
+}
+}  // namespace GH175427


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

Reply via email to