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

Andy Kaylor via cfe-commits cfe-commits at lists.llvm.org
Wed Apr 15 11:31:15 PDT 2026


https://github.com/andykaylor created https://github.com/llvm/llvm-project/pull/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.

>From ceb38fbe3acc78bd97688db4ce939d5cb8e89b5e Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Wed, 15 Apr 2026 10:44:58 -0700
Subject: [PATCH] [CIR] Add EH handling for lifetime extended cleanups

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.
---
 clang/lib/CIR/CodeGen/CIRGenDecl.cpp          | 13 ++--
 clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp |  1 +
 clang/lib/CIR/CodeGen/CIRGenFunction.h        |  4 +-
 .../test/CIR/CodeGen/cleanup-automatic-eh.cpp | 62 +++++++++++++++++++
 4 files changed, 70 insertions(+), 10 deletions(-)
 create mode 100644 clang/test/CIR/CodeGen/cleanup-automatic-eh.cpp

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



More information about the cfe-commits mailing list