https://github.com/E00N777 created https://github.com/llvm/llvm-project/pull/224829
### summary fix https://github.com/llvm/llvm-project/issues/224821 A lifetime marker is not a real cleanup, so it must never be the reason an unwind edge exists. Assisted by : Claude Opus5 >From 2283c5ef83af2062a4e82d89ed02ab21d09658fc Mon Sep 17 00:00:00 2001 From: E00N777 <[email protected]> Date: Sat, 19 Sep 2026 17:31:12 +0800 Subject: [PATCH] [CIR] Don't create EH paths for lifetime-marker-only cleanups --- clang/lib/CIR/CodeGen/CIRGenCleanup.cpp | 3 ++ clang/lib/CIR/CodeGen/CIRGenStmt.cpp | 34 +++++++++------ clang/test/CIR/CodeGen/lifetime-marker.cpp | 49 +++++++++++----------- 3 files changed, 49 insertions(+), 37 deletions(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp b/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp index 8103ef37f9225..53a0b78c6ec7c 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp @@ -349,6 +349,9 @@ void *EHScopeStack::pushCleanup(CleanupKind kind, size_t size) { bool isLifetimeMarker = kind & LifetimeMarker; bool skipCleanupScope = false; + if (isLifetimeMarker) + isEHCleanup = false; + cir::CleanupKind cleanupKind = cir::CleanupKind::All; if (isEHCleanup && cgf->getLangOpts().Exceptions) { cleanupKind = diff --git a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp index e9f5e466c63d2..044947b9ef1bc 100644 --- a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp @@ -1026,10 +1026,10 @@ mlir::LogicalResult CIRGenFunction::emitForStmt(const ForStmt &s) { // per-iteration cleanup region. This scope is constructed after the // init-statement so the init-statement's cleanups are not captured. const VarDecl *condVar = s.getConditionVariable(); + bool condVarNeedsDtor = + condVar && condVar->needsDestruction(getContext()) != QualType::DK_none; bool needsCondCleanup = - condVar && - (condVar->needsDestruction(getContext()) != QualType::DK_none || - shouldEmitLifetimeMarkersForAutoVar()); + condVar && (condVarNeedsDtor || shouldEmitLifetimeMarkersForAutoVar()); DeferredLoopConditionCleanup loopCondScope(*this, needsCondCleanup); auto condBuilder = [&](mlir::OpBuilder &b, mlir::Location loc) { @@ -1065,9 +1065,14 @@ mlir::LogicalResult CIRGenFunction::emitForStmt(const ForStmt &s) { }; if (needsCondCleanup) { - cir::CleanupKind cleanupKind = getLangOpts().Exceptions - ? cir::CleanupKind::All - : cir::CleanupKind::Normal; + // Without a destructor the cleanup region holds nothing but + // cir.lifetime.end. A lifetime marker must not be the reason an unwind + // edge exists, and an EH kind here would make every call in the + // condition, body and step unwind to it. + cir::CleanupKind cleanupKind = + getLangOpts().Exceptions && condVarNeedsDtor + ? cir::CleanupKind::All + : cir::CleanupKind::Normal; forOp = builder.createFor( getLoc(s.getSourceRange()), condBuilder, bodyBuilder, stepBuilder, /*cleanupBuilder=*/ @@ -1158,10 +1163,10 @@ mlir::LogicalResult CIRGenFunction::emitWhileStmt(const WhileStmt &s) { // destructor and lifetime-end cleanups and emit them into the loop's // per-iteration cleanup region. const VarDecl *condVar = s.getConditionVariable(); + bool condVarNeedsDtor = + condVar && condVar->needsDestruction(getContext()) != QualType::DK_none; bool needsCondCleanup = - condVar && - (condVar->needsDestruction(getContext()) != QualType::DK_none || - shouldEmitLifetimeMarkersForAutoVar()); + condVar && (condVarNeedsDtor || shouldEmitLifetimeMarkersForAutoVar()); DeferredLoopConditionCleanup loopCondScope(*this, needsCondCleanup); auto condBuilder = [&](mlir::OpBuilder &b, mlir::Location loc) { @@ -1185,9 +1190,14 @@ mlir::LogicalResult CIRGenFunction::emitWhileStmt(const WhileStmt &s) { }; if (needsCondCleanup) { - cir::CleanupKind cleanupKind = getLangOpts().Exceptions - ? cir::CleanupKind::All - : cir::CleanupKind::Normal; + // Without a destructor the cleanup region holds nothing but + // cir.lifetime.end. A lifetime marker must not be the reason an unwind + // edge exists, and an EH kind here would make every call in the + // condition and body unwind to it. + cir::CleanupKind cleanupKind = + getLangOpts().Exceptions && condVarNeedsDtor + ? cir::CleanupKind::All + : cir::CleanupKind::Normal; whileOp = builder.createWhile( getLoc(s.getSourceRange()), condBuilder, bodyBuilder, /*cleanupBuilder=*/ diff --git a/clang/test/CIR/CodeGen/lifetime-marker.cpp b/clang/test/CIR/CodeGen/lifetime-marker.cpp index dde66fac13d96..d1de8ef9185c9 100644 --- a/clang/test/CIR/CodeGen/lifetime-marker.cpp +++ b/clang/test/CIR/CodeGen/lifetime-marker.cpp @@ -134,10 +134,10 @@ void if_body(int n) { // LLVM: [[IF_END]]: // LLVM: call void @_Z3usei -// With exceptions enabled the scope cleanup runs on both the normal and the -// exceptional edge, so the cleanup kind is "all" and lifetime.end is emitted in -// the EH cleanup handler (the landing pad) as well as on the normal path. The -// may_throw() call is what forces an unwind edge. +// A lifetime marker is not a real cleanup, so even with exceptions enabled it +// must never be the reason an unwind path exists. The cleanup kind stays +// "normal" and may_throw() is a plain call, mirroring classic CodeGen, where +// EHScopeStack::requiresLandingPad skips lifetime-marker cleanups. void may_throw(); void eh_cleanup() { @@ -151,19 +151,16 @@ void eh_cleanup() { // CIR-EH: cir.lifetime.start %[[X]] : !cir.ptr<!s32i> // CIR-EH: cir.cleanup.scope { // CIR-EH: cir.call @_Z9may_throwv() -// CIR-EH: } cleanup all { +// CIR-EH: } cleanup normal { // CIR-EH: cir.lifetime.end %[[X]] : !cir.ptr<!s32i> // CIR-EH: } -// LLVM-EH-LABEL: define{{.*}} void @_Z10eh_cleanupv() +// The '{' right after the attribute group pins the absence of a personality +// clause: the function needs no exception handling at all. +// LLVM-EH-LABEL: define{{.*}} void @_Z10eh_cleanupv() #{{[0-9]+}} { // LLVM-EH: %[[X:.*]] = alloca i32 // LLVM-EH: call void @llvm.lifetime.start.p0(ptr %[[X]]) -// LLVM-EH: invoke void @_Z9may_throwv() -// The normal-path end marker. -// LLVM-EH: call void @llvm.lifetime.end.p0(ptr %[[X]]) -// The EH cleanup handler runs the same end marker on the unwind path. -// LLVM-EH: landingpad { ptr, i32 } -// LLVM-EH-NEXT: cleanup +// LLVM-EH: call void @_Z9may_throwv() // LLVM-EH: call void @llvm.lifetime.end.p0(ptr %[[X]]) // A loop condition variable is destroyed and re-created on every iteration @@ -188,19 +185,21 @@ void while_condvar() { // LLVM: call void @llvm.lifetime.start.p0(ptr %[[C:.*]]) // LLVM: call void @llvm.lifetime.end.p0(ptr %[[C]]) +// A loop's cleanup region spans the condition, the body and the step, so an EH +// cleanup kind here would make every call in the loop unwind to it. A +// marker-only region must therefore stay "normal". // CIR-EH-LABEL: cir.func{{.*}} @_Z13while_condvarv // CIR-EH: %[[C:.*]] = cir.alloca "c" {{.*}} : !cir.ptr<!s32i> // CIR-EH: cir.while { // CIR-EH: cir.lifetime.start %[[C]] : !cir.ptr<!s32i> // CIR-EH: } do { -// CIR-EH: } cleanup all { +// CIR-EH: } cleanup normal { // CIR-EH: cir.lifetime.end %[[C]] : !cir.ptr<!s32i> -// LLVM-EH-LABEL: define{{.*}} void @_Z13while_condvarv +// LLVM-EH-LABEL: define{{.*}} void @_Z13while_condvarv() #{{[0-9]+}} { // LLVM-EH: call void @llvm.lifetime.start.p0(ptr %[[C:.*]]) -// LLVM-EH: call void @llvm.lifetime.end.p0(ptr %[[C]]) -// LLVM-EH: landingpad { ptr, i32 } -// LLVM-EH-NEXT: cleanup +// LLVM-EH: call noundef i32 @_Z6sourcev() +// LLVM-EH: call void @_Z3usei // LLVM-EH: call void @llvm.lifetime.end.p0(ptr %[[C]]) void for_condvar() { @@ -227,14 +226,13 @@ void for_condvar() { // CIR-EH: cir.lifetime.start %[[C]] : !cir.ptr<!s32i> // CIR-EH: } body { // CIR-EH: } step { -// CIR-EH: } cleanup all { +// CIR-EH: } cleanup normal { // CIR-EH: cir.lifetime.end %[[C]] : !cir.ptr<!s32i> -// LLVM-EH-LABEL: define{{.*}} void @_Z11for_condvarv +// LLVM-EH-LABEL: define{{.*}} void @_Z11for_condvarv() #{{[0-9]+}} { // LLVM-EH: call void @llvm.lifetime.start.p0(ptr %[[C:.*]]) -// LLVM-EH: call void @llvm.lifetime.end.p0(ptr %[[C]]) -// LLVM-EH: landingpad { ptr, i32 } -// LLVM-EH-NEXT: cleanup +// LLVM-EH: call noundef i32 @_Z6sourcev() +// LLVM-EH: call void @_Z3usei // LLVM-EH: call void @llvm.lifetime.end.p0(ptr %[[C]]) struct LoopCond { @@ -243,7 +241,8 @@ struct LoopCond { }; // A non-trivial condition variable runs its destructor before lifetime.end in -// the loop cleanup region. +// the loop cleanup region. That region is a real cleanup, so it keeps the "all" +// kind and the marker rides along on the unwind path the destructor requires. void while_record_condvar() { while (LoopCond c{}) {} } @@ -300,7 +299,7 @@ void catch_by_ref() { // CIR-EH: } cleanup all { // CIR-EH: cir.end_catch %[[CATCH_TOK]] // CIR-EH: } -// CIR-EH: } cleanup all { +// CIR-EH: } cleanup normal { // CIR-EH-NEXT: cir.lifetime.end %[[E]] : !cir.ptr<!cir.ptr<!rec_Ex>> // LLVM-EH-LABEL: define{{.*}} void @_Z12catch_by_refv() @@ -330,7 +329,7 @@ void catch_by_value() { // CIR-EH-NEXT: %[[CATCH_TOK:.*]], %{{.*}} = cir.begin_catch %[[TOK]] // CIR-EH: cir.call @_ZN4CopyD1Ev(%[[C]]) // CIR-EH: cir.end_catch %[[CATCH_TOK]] -// CIR-EH: } cleanup all { +// CIR-EH: } cleanup normal { // CIR-EH-NEXT: cir.lifetime.end %[[C]] : !cir.ptr<!rec_Copy> // LLVM-EH-LABEL: define{{.*}} void @_Z14catch_by_valuev() _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
