Author: Morris Hafner
Date: 2026-10-07T18:54:13Z
New Revision: ee29c890f04750561f5ca5ce2e6a63a9941ff41d

URL: 
https://github.com/llvm/llvm-project/commit/ee29c890f04750561f5ca5ce2e6a63a9941ff41d
DIFF: 
https://github.com/llvm/llvm-project/commit/ee29c890f04750561f5ca5ce2e6a63a9941ff41d.diff

LOG: [CIR][NFC] Generalize loop condition cleanup capturing (#229773)

The mechanism for deferring cleanups for loop conditions is also useful
for coroutines. For those we need to emit cleanups for the coroutine
parameters and the promise in a separate destroy region.

Rename identifiers such as LoopConditionCleanups to CapturedCleanups (to
disambiguate between this and deferred conditional cleanups, which are
something else) so future coroutine related patches can reuse this
mechanism.

This only updates identifier names and comments and brings no functional
change.

Co-authored-by: Claude Opus 5.5 <[email protected]>

Added: 
    

Modified: 
    clang/lib/CIR/CodeGen/CIRGenCleanup.cpp
    clang/lib/CIR/CodeGen/CIRGenDecl.cpp
    clang/lib/CIR/CodeGen/CIRGenFunction.h
    clang/lib/CIR/CodeGen/CIRGenStmt.cpp
    clang/lib/CIR/CodeGen/EHScopeStack.h

Removed: 
    


################################################################################
diff  --git a/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp 
b/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp
index a84531fe908d8..2173b27a20d78 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp
@@ -428,10 +428,10 @@ void *EHScopeStack::pushCleanup(CleanupKind kind, size_t 
size) {
       skipCleanupScope = true;
   }
 
-  // While emitting a loop's condition variable, suppress cir.cleanup.scope
-  // creation. The variable's cleanups are captured on the EH stack and later
-  // emitted into the loop op's per-iteration cleanup region.
-  if (capturingLoopConditionCleanups)
+  // While emitting a variable with captured cleanups, suppress
+  // cir.cleanup.scope creation. The variable's cleanups are captured on the EH
+  // stack and later emitted into the cleanup region of the op that owns them.
+  if (capturingCleanups)
     skipCleanupScope = true;
 
   cir::CleanupScopeOp cleanupScope = nullptr;
@@ -790,17 +790,17 @@ void CIRGenFunction::popCleanupBlock(bool 
forDeactivation) {
   emitCleanup(*this, cleanupScope, cleanup, cleanupFlags, cleanupActiveFlag);
 }
 
-void CIRGenFunction::emitLoopConditionCleanups(
-    EHScopeStack::stable_iterator depth, mlir::Location loc) {
-  // The captured cleanups were pushed while emitting the loop's condition
-  // variable with EHScopeStack capturing condition cleanups, so they own no
-  // cir.cleanup.scope. Emit them directly into the loop's cleanup region (the
-  // current insertion point), popping each off the EH stack.
+void CIRGenFunction::emitCapturedCleanups(EHScopeStack::stable_iterator depth,
+                                          mlir::Location loc) {
+  // The captured cleanups were pushed while EHScopeStack was capturing
+  // cleanups, so they own no cir.cleanup.scope. Emit them directly into the
+  // owning op's cleanup region (the current insertion point), popping each off
+  // the EH stack.
   while (ehStack.stable_begin() != depth) {
     assert(isa<EHCleanupScope>(*ehStack.begin()) && "top not a cleanup!");
     EHCleanupScope &scope = cast<EHCleanupScope>(*ehStack.begin());
     assert(!scope.getCleanupScopeOp() &&
-           "captured loop-condition cleanup should not own a cleanup scope");
+           "captured cleanup should not own a cleanup scope");
 
     EHScopeStack::Cleanup::Flags cleanupFlags;
     if (scope.isNormalCleanup())
@@ -808,10 +808,11 @@ void CIRGenFunction::emitLoopConditionCleanups(
     if (scope.isEHCleanup())
       cleanupFlags.setIsEHCleanupKind();
 
-    // A condition variable's destructor cleanup is guarded by an active flag
-    // that is false while its initializer runs, so a throwing initializer does
-    // not destroy the not-yet-constructed variable. The lifetime-end cleanup
-    // has no flag because its lifetime starts before initialization. Each
+    // A captured cleanup may be guarded by an active flag. A loop condition
+    // variable's destructor cleanup has one that is false while its
+    // initializer runs, so a throwing initializer does not destroy the
+    // not-yet-constructed variable. The lifetime-end cleanup has no flag
+    // because its lifetime starts before initialization. For a loop, each
     // emission serves both the normal per-iteration exit and the EH unwind
     // path.
     Address activeFlag = scope.getActiveFlag();

diff  --git a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp 
b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
index 1c15c2fd6ec65..01b9f810fbed4 100644
--- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
@@ -387,8 +387,8 @@ void CIRGenFunction::emitAutoVarDecl(const VarDecl &d) {
   emitAutoVarCleanups(emission);
 }
 
-void CIRGenFunction::emitLoopConditionVariable(
-    const VarDecl &d, DeferredLoopConditionCleanup &condCleanup) {
+void CIRGenFunction::emitLoopConditionVariable(const VarDecl &d,
+                                               CapturedCleanups &condCleanup) {
   // A condition variable always has automatic storage duration, so this
   // mirrors the auto-var path of emitVarDecl/emitAutoVarDecl. Capture the
   // lifetime-end cleanup pushed while emitting the alloca, but emit the
@@ -406,7 +406,7 @@ void CIRGenFunction::emitLoopConditionVariable(
 
   CIRGenFunction::VarDeclContext varDeclCtx{*this, &d};
   CIRGenFunction::AutoVarEmission emission = [&] {
-    DeferredLoopConditionCleanup::CaptureScope capture(condCleanup);
+    CapturedCleanups::CaptureScope capture(condCleanup);
     return emitAutoVarAlloca(d);
   }();
 
@@ -439,7 +439,7 @@ void CIRGenFunction::emitLoopConditionVariable(
   }
 
   {
-    DeferredLoopConditionCleanup::CaptureScope capture(condCleanup);
+    CapturedCleanups::CaptureScope capture(condCleanup);
     emitAutoVarCleanups(emission);
   }
 

diff  --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h 
b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index c4963806c815b..fc225d17d4471 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -1253,12 +1253,12 @@ class CIRGenFunction : public CIRGenTypeCache {
                         ArrayRef<mlir::Value *> valuesToReload = {});
   void popCleanupBlock(bool forDeactivation = false);
 
-  /// Emit the cleanups captured for a loop's condition variable (those pushed
-  /// above \p depth while EHScopeStack was capturing condition cleanups) at
-  /// the current insertion point, which must be inside the loop op's cleanup
-  /// region, and pop them off the EH stack.
-  void emitLoopConditionCleanups(EHScopeStack::stable_iterator depth,
-                                 mlir::Location loc);
+  /// Emit the captured cleanups (those pushed above \p depth while 
EHScopeStack
+  /// was capturing cleanups) at the current insertion point, which must be
+  /// inside the cleanup region of the op that owns them, and pop them off the
+  /// EH stack. Every cleanup above \p depth must have been captured.
+  void emitCapturedCleanups(EHScopeStack::stable_iterator depth,
+                            mlir::Location loc);
 
   void terminateStructuredRegionBody(mlir::Region &r, mlir::Location loc);
 
@@ -1492,50 +1492,55 @@ class CIRGenFunction : public CIRGenTypeCache {
     void operator=(const FullExprCleanupScope &) = delete;
   };
 
-  /// Captures cleanups for a loop's condition variable so that they can be
-  /// emitted into the loop op's per-iteration cleanup region.
-  class DeferredLoopConditionCleanup {
+  /// Captures cleanups for variables whose lifetime ends in a region owned by
+  /// an enclosing op rather than at the end of a cir.cleanup.scope, so that
+  /// they can be emitted into that region later. Examples for uses are:
+  ///   * A loop condition variable's cleanups in the loop op's per-iteration
+  ///     cleanup region.
+  ///   * Coroutine parameters and promises, which are cleaned up in a separate
+  ///     destroy region
+  class CapturedCleanups {
     CIRGenFunction &cgf;
     EHScopeStack::stable_iterator depth;
     bool active;
 
   public:
-    DeferredLoopConditionCleanup(CIRGenFunction &cgf, bool active)
+    CapturedCleanups(CIRGenFunction &cgf, bool active)
         : cgf(cgf), depth(cgf.ehStack.stable_begin()), active(active) {}
 
     /// An RAII class that suppresses cir.cleanup.scope creation for cleanups
-    /// pushed onto the EH stack while a loop condition variable is being
-    /// emitted and instead captures these cleanups so that they can be emitted
-    /// into the loop op's cleanup region after the condition region is built.
+    /// pushed onto the EH stack while such a variable is being emitted and
+    /// instead captures these cleanups so that they can be emitted into the
+    /// owning op's cleanup region later.
     class CaptureScope {
       EHScopeStack &ehStack;
 
     public:
-      explicit CaptureScope(DeferredLoopConditionCleanup &scope)
+      explicit CaptureScope(CapturedCleanups &scope)
           : ehStack(scope.cgf.ehStack) {
         // Capture scopes deliberately wrap individual cleanup-producing
         // operations, so they must never nest.
-        assert(!ehStack.isCapturingLoopConditionCleanups() &&
-               "loop condition cleanup capturing should not nest");
+        assert(!ehStack.isCapturingCleanups() &&
+               "cleanup capturing should not nest");
         if (scope.active)
-          ehStack.setCapturingLoopConditionCleanups(true);
+          ehStack.setCapturingCleanups(true);
       }
-      ~CaptureScope() { ehStack.setCapturingLoopConditionCleanups(false); }
+      ~CaptureScope() { ehStack.setCapturingCleanups(false); }
 
       CaptureScope(const CaptureScope &) = delete;
       void operator=(const CaptureScope &) = delete;
     };
 
-    /// Emit the captured condition-variable cleanups into the current 
insertion
-    /// point (the loop's cleanup region).
-    void emitIntoLoopCleanupRegion(mlir::Location loc) {
+    /// Emit the captured cleanups into the current insertion point, which must
+    /// be inside the owning op's cleanup region.
+    void emitIntoCleanupRegion(mlir::Location loc) {
       if (active)
-        cgf.emitLoopConditionCleanups(depth, loc);
+        cgf.emitCapturedCleanups(depth, loc);
     }
 
   private:
-    DeferredLoopConditionCleanup(const DeferredLoopConditionCleanup &) = 
delete;
-    void operator=(const DeferredLoopConditionCleanup &) = delete;
+    CapturedCleanups(const CapturedCleanups &) = delete;
+    void operator=(const CapturedCleanups &) = delete;
   };
 
 public:
@@ -1901,7 +1906,7 @@ class CIRGenFunction : public CIRGenTypeCache {
   /// Emit a loop's condition-variable declaration. This needs special handling
   /// so that we can manage per-iteration cleanups for the loop condition.
   void emitLoopConditionVariable(const clang::VarDecl &d,
-                                 DeferredLoopConditionCleanup &condCleanup);
+                                 CapturedCleanups &condCleanup);
 
   /// Emit the initializer for an allocated variable.  If this call is not
   /// associated with the call to emitAutoVarAlloca (as the address of the

diff  --git a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp 
b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
index 0bf3dfa94a030..d06d32f5b33c0 100644
--- a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
@@ -1038,7 +1038,7 @@ mlir::LogicalResult CIRGenFunction::emitForStmt(const 
ForStmt &s) {
         condVar &&
         (condVar->needsDestruction(getContext()) != QualType::DK_none ||
          shouldEmitLifetimeMarkersForAutoVar());
-    DeferredLoopConditionCleanup loopCondScope(*this, needsCondCleanup);
+    CapturedCleanups loopCondScope(*this, needsCondCleanup);
 
     auto condBuilder = [&](mlir::OpBuilder &b, mlir::Location loc) {
       assert(!cir::MissingFeatures::createProfileWeightsForLoop());
@@ -1080,7 +1080,7 @@ mlir::LogicalResult CIRGenFunction::emitForStmt(const 
ForStmt &s) {
           getLoc(s.getSourceRange()), condBuilder, bodyBuilder, stepBuilder,
           /*cleanupBuilder=*/
           [&](mlir::OpBuilder &b, mlir::Location loc) {
-            loopCondScope.emitIntoLoopCleanupRegion(loc);
+            loopCondScope.emitIntoCleanupRegion(loc);
             builder.createYield(loc);
           },
           cleanupKind);
@@ -1172,7 +1172,7 @@ mlir::LogicalResult CIRGenFunction::emitWhileStmt(const 
WhileStmt &s) {
         condVar &&
         (condVar->needsDestruction(getContext()) != QualType::DK_none ||
          shouldEmitLifetimeMarkersForAutoVar());
-    DeferredLoopConditionCleanup loopCondScope(*this, needsCondCleanup);
+    CapturedCleanups loopCondScope(*this, needsCondCleanup);
 
     auto condBuilder = [&](mlir::OpBuilder &b, mlir::Location loc) {
       assert(!cir::MissingFeatures::createProfileWeightsForLoop());
@@ -1202,7 +1202,7 @@ mlir::LogicalResult CIRGenFunction::emitWhileStmt(const 
WhileStmt &s) {
           getLoc(s.getSourceRange()), condBuilder, bodyBuilder,
           /*cleanupBuilder=*/
           [&](mlir::OpBuilder &b, mlir::Location loc) {
-            loopCondScope.emitIntoLoopCleanupRegion(loc);
+            loopCondScope.emitIntoCleanupRegion(loc);
             builder.createYield(loc);
           },
           cleanupKind);

diff  --git a/clang/lib/CIR/CodeGen/EHScopeStack.h 
b/clang/lib/CIR/CodeGen/EHScopeStack.h
index 80536b96739a3..aeeeb873ca6c9 100644
--- a/clang/lib/CIR/CodeGen/EHScopeStack.h
+++ b/clang/lib/CIR/CodeGen/EHScopeStack.h
@@ -169,10 +169,10 @@ class EHScopeStack {
   CIRGenFunction *cgf = nullptr;
 
   /// When true, pushCleanup() does not create a cir.cleanup.scope op for the
-  /// pushed cleanup. This is set while emitting a loop's condition variable so
-  /// its destructor can be captured on the stack and later emitted into the
-  /// loop op's per-iteration cleanup region.
-  bool capturingLoopConditionCleanups = false;
+  /// pushed cleanup. This is set while emitting a variable whose cleanups are
+  /// captured on the stack and later emitted into a cleanup region owned by an
+  /// enclosing op, such as a loop's per-iteration cleanup region.
+  bool capturingCleanups = false;
 
   // This class uses a custom allocator for maximum efficiency because cleanups
   // are allocated and freed very frequently. It's basically a bump pointer
@@ -231,12 +231,8 @@ class EHScopeStack {
 
   void setCGF(CIRGenFunction *inCGF) { cgf = inCGF; }
 
-  bool isCapturingLoopConditionCleanups() const {
-    return capturingLoopConditionCleanups;
-  }
-  void setCapturingLoopConditionCleanups(bool value) {
-    capturingLoopConditionCleanups = value;
-  }
+  bool isCapturingCleanups() const { return capturingCleanups; }
+  void setCapturingCleanups(bool value) { capturingCleanups = value; }
 
   /// Pops a cleanup scope off the stack.  This is private to 
CIRGenCleanup.cpp.
   void popCleanup();


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to