Author: Andy Kaylor Date: 2026-04-15T12:53:40-07:00 New Revision: 35dcb5c1303b251d8728bd4f8ed93ff4316eea3a
URL: https://github.com/llvm/llvm-project/commit/35dcb5c1303b251d8728bd4f8ed93ff4316eea3a DIFF: https://github.com/llvm/llvm-project/commit/35dcb5c1303b251d8728bd4f8ed93ff4316eea3a.diff LOG: [CIR] Add EH handling for lifetime extended cleanups (#192305) This adds code to call pushDestroyAndDeferDeactivation from the pushLifetimeExtendedDestroy function. This was needed to generate the correct code for lifetime extended cleanups when exceptions are enabled. An extended version of the cleanup with automatic storage duration is used as a test case. To make this work correctly, I had to add a CleanupDeactivationScope to RunCleanupsScope and force deactivation when forceCleanup is called. This matches the corresponding code in classic codegen. I surveyed other places where classic codegen is using CleanupDeactivationScope and added a MissingFeatures marker in one location where it was not previously marked. Other places where it was missing were already marked in this way. Added: clang/test/CIR/CodeGen/cleanup-automatic-eh.cpp Modified: clang/lib/CIR/CodeGen/CIRGenDecl.cpp clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp clang/lib/CIR/CodeGen/CIRGenFunction.h Removed: ################################################################################ diff --git a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp index fcc8564acf341..7e9f6a8a230f3 100644 --- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp @@ -1112,15 +1112,10 @@ void CIRGenFunction::pushLifetimeExtendedDestroy(CleanupKind cleanupKind, return; } - // Classic codegen also uses pushDestroyAndDeferDeactivation here to push an - // EH cleanup that protects the temporary during the rest of the full - // expression, then deactivates it when the full expression ends. Deferred - // deactivation is being implemented now, but it wasn't when this code was - // implemented. This will be updated in a separate change. - if (getLangOpts().Exceptions) { - cgm.errorNYI("lifetime-extended cleanup with exceptions enabled"); - return; - } + // Add the cleanup to the EHStack. After the full-expr, this would be + // deactivated before being popped from the stack. + pushDestroyAndDeferDeactivation(cleanupKind, addr, type, destroyer, + useEHCleanupForArray); assert(!cir::MissingFeatures::useEHCleanupForArray()); diff --git a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp index a87cc91037d08..37157e8c2d065 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp @@ -726,6 +726,7 @@ void AggExprEmitter::emitArrayInit(Address destPtr, cir::ArrayType arrayTy, // members if an initializer throws. For that, we'll need an EH cleanup. QualType::DestructionKind dtorKind = elementType.isDestructedType(); Address endOfInit = Address::invalid(); + assert(!cir::MissingFeatures::cleanupDeactivationScope()); if (dtorKind && cgf.getLangOpts().Exceptions) { endOfInit = cgf.createTempAlloca(cirElementPtrType, cgf.getPointerAlign(), diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index efa5527dfa720..7f91cba115f04 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -1145,6 +1145,7 @@ class CIRGenFunction : public CIRGenTypeCache { class RunCleanupsScope { EHScopeStack::stable_iterator cleanupStackDepth, oldCleanupStackDepth; size_t lifetimeExtendedCleanupStackSize; + CleanupDeactivationScope deactivateCleanups; protected: bool performCleanup; @@ -1160,7 +1161,7 @@ class CIRGenFunction : public CIRGenTypeCache { public: /// Enter a new cleanup scope. explicit RunCleanupsScope(CIRGenFunction &cgf) - : performCleanup(true), cgf(cgf) { + : deactivateCleanups(cgf), performCleanup(true), cgf(cgf) { cleanupStackDepth = cgf.ehStack.stable_begin(); lifetimeExtendedCleanupStackSize = cgf.lifetimeExtendedCleanupStack.size(); @@ -1181,6 +1182,7 @@ class CIRGenFunction : public CIRGenTypeCache { void forceCleanup(ArrayRef<mlir::Value *> valuesToReload = {}) { assert(performCleanup && "Already forced cleanup"); cgf.didCallStackSave = oldDidCallStackSave; + deactivateCleanups.forceDeactivate(); cgf.popCleanupBlocks(cleanupStackDepth, lifetimeExtendedCleanupStackSize, valuesToReload); performCleanup = false; diff --git a/clang/test/CIR/CodeGen/cleanup-automatic-eh.cpp b/clang/test/CIR/CodeGen/cleanup-automatic-eh.cpp new file mode 100644 index 0000000000000..c6bd774971ef7 --- /dev/null +++ b/clang/test/CIR/CodeGen/cleanup-automatic-eh.cpp @@ -0,0 +1,62 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fexceptions -fcxx-exceptions -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fexceptions -fcxx-exceptions -fclangir -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fexceptions -fcxx-exceptions -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG + +struct Struk { + Struk(); + ~Struk(); +}; + +void mayThrow(); + +void test_cleanup_with_automatic_storage_duration() { + const Struk &ref = Struk{}; + mayThrow(); +} + +// CIR: cir.func{{.*}} @_Z44test_cleanup_with_automatic_storage_durationv() +// CIR: %[[REF_TMP:.*]] = cir.alloca !rec_Struk, !cir.ptr<!rec_Struk>, ["ref.tmp0"] +// CIR: %[[REF:.*]] = cir.alloca !cir.ptr<!rec_Struk>, !cir.ptr<!cir.ptr<!rec_Struk>>, ["ref", init, const] +// CIR: cir.call @_ZN5StrukC1Ev(%[[REF_TMP]]) +// CIR: cir.cleanup.scope { +// CIR: cir.store{{.*}} %[[REF_TMP]], %[[REF]] +// CIR: cir.call @_Z8mayThrowv() +// CIR: cir.yield +// CIR: } cleanup all { +// CIR: cir.call @_ZN5StrukD1Ev(%[[REF_TMP]]) nothrow +// CIR: cir.yield +// CIR: } +// CIR: cir.return + +// LLVM: define {{.*}} void @_Z44test_cleanup_with_automatic_storage_durationv() +// LLVM: call void @_ZN5StrukC1Ev( +// LLVM: invoke void @_Z8mayThrowv() +// LLVM: to label %[[CONT:.*]] unwind label %[[LPAD:.*]] +// LLVM: [[CONT]]: +// LLVM: call void @_ZN5StrukD1Ev( +// LLVM: br label %[[EXIT_NORMAL_CLEANUP:.*]] +// LLVM: [[EXIT_NORMAL_CLEANUP]]: +// LLVM: br label %[[EXIT:.*]] +// LLVM: [[LPAD]]: +// LLVM: landingpad { ptr, i32 } +// LLVM: cleanup +// LLVM: call void @_ZN5StrukD1Ev( +// LLVM: resume +// LLVM: [[EXIT]]: +// LLVM: ret void + +// OGCG: define {{.*}} void @_Z44test_cleanup_with_automatic_storage_durationv() +// OGCG: call void @_ZN5StrukC1Ev( +// OGCG: invoke void @_Z8mayThrowv() +// OGCG: to label %[[CONT:.*]] unwind label %[[LPAD:.*]] +// OGCG: [[CONT]]: +// OGCG: call void @_ZN5StrukD1Ev( +// OGCG: ret void +// OGCG: [[LPAD]]: +// OGCG: landingpad { ptr, i32 } +// OGCG: cleanup +// OGCG: call void @_ZN5StrukD1Ev( +// OGCG: resume _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
