[clang] [CIR] Add EH handling for lifetime extended cleanups (PR #192305)

via cfe-commits cfe-commits at lists.llvm.org
Wed Apr 15 11:32:01 PDT 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Andy Kaylor (andykaylor)

<details>
<summary>Changes</summary>

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.

---
Full diff: https://github.com/llvm/llvm-project/pull/192305.diff


4 Files Affected:

- (modified) clang/lib/CIR/CodeGen/CIRGenDecl.cpp (+4-9) 
- (modified) clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp (+1) 
- (modified) clang/lib/CIR/CodeGen/CIRGenFunction.h (+3-1) 
- (added) clang/test/CIR/CodeGen/cleanup-automatic-eh.cpp (+62) 


``````````diff
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 7f0edd2138836..2ba5b7730b88c 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

``````````

</details>


https://github.com/llvm/llvm-project/pull/192305


More information about the cfe-commits mailing list