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
