Author: Andy Kaylor
Date: 2026-02-02T08:53:14-08:00
New Revision: 36fea452c32a1e1c717e96609d7e634e66cd8e8b

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

LOG: [CIR] Create cir.cleanup.scope operation (#178085)

This adds the CIR dialect definitions for the cir.cleanup.scope
operation. Nothing is being added yet to generate this operation. That
will be done in a later change.

Added: 
    clang/test/CIR/IR/cleanup-scope.cir

Modified: 
    clang/include/clang/CIR/Dialect/IR/CIROps.td
    clang/lib/CIR/Dialect/IR/CIRDialect.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td 
b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 6cebf6e62af6f..36823e63fea66 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -869,8 +869,9 @@ def CIR_ConditionOp : CIR_Op<"condition", [
 
//===----------------------------------------------------------------------===//
 
 defvar CIR_YieldableScopes = [
-  "ArrayCtor", "ArrayDtor", "AwaitOp", "CaseOp", "DoWhileOp", "ForOp",
-  "GlobalOp", "IfOp", "ScopeOp", "SwitchOp", "TernaryOp", "WhileOp", "TryOp"
+  "ArrayCtor", "ArrayDtor", "AwaitOp", "CaseOp", "CleanupScopeOp", "DoWhileOp",
+  "ForOp", "GlobalOp", "IfOp", "ScopeOp", "SwitchOp", "TernaryOp", "TryOp",
+  "WhileOp"
 ];
 
 def CIR_YieldOp : CIR_Op<"yield", [
@@ -1087,6 +1088,112 @@ def CIR_ScopeOp : CIR_Op<"scope", [
   let hasLLVMLowering = false;
 }
 
+//===----------------------------------------------------------------------===//
+// CleanupScopeOp
+//===----------------------------------------------------------------------===//
+
+def CIR_CleanupKind : CIR_I32EnumAttr<"CleanupKind", "cleanup kind", [
+  I32EnumAttrCase<"Normal", 1, "normal">,
+  I32EnumAttrCase<"EH", 2, "eh">,
+  I32EnumAttrCase<"All", 3, "all">
+]> {
+  let genSpecializedAttr = 0;
+}
+
+def CIR_CleanupKindAttr : CIR_EnumAttr<CIR_CleanupKind, "cleanup_kind"> {
+  let summary = "Cleanup kind attribute";
+  let description = [{
+    Cleanup kind attributes.
+  }];
+
+  let cppClassName = "CleanupKindAttr";
+
+  let skipDefaultBuilders = 1;
+  let builders = [
+    AttrBuilder<(ins CArg<"CleanupKind",
+                          "cir::CleanupKind::All">:$value), [{
+      return $_get($_ctxt, value);
+    }]>
+  ];
+
+  let assemblyFormat = [{
+    $value
+  }];
+
+  let extraClassDeclaration = [{
+    bool isNormal() const {
+      return getValue() == CleanupKind::Normal ||
+             getValue() == CleanupKind::All;
+    };
+    bool isEH() const {
+      return getValue() == CleanupKind::EH || getValue() == CleanupKind::All;
+    };
+    bool isNormalAndEH() const { return getValue() == CleanupKind::All; };
+  }];
+}
+
+def CIR_CleanupScopeOp : CIR_Op<"cleanup.scope", [
+  DeclareOpInterfaceMethods<RegionBranchOpInterface, ["getSuccessorInputs"]>,
+  RecursivelySpeculatable, AutomaticAllocationScope, NoRegionArguments,
+  RecursiveMemoryEffects
+]> {
+  let summary = "Represents a scope with associated cleanup code";
+  let description = [{
+    `cir.cleanup.scope` contains a body region and a cleanup region. The body
+    region is executed first, and the cleanup region is executed when the body
+    region is exited, either normally or due to an exception.
+
+    The cleanup kind attribute specifies when the cleanup region should be
+    executed:
+    - `none`: No cleanup (cleanup region is empty/unused)
+    - `normal`: Cleanup is executed only on normal exit
+    - `eh`: Cleanup is executed only on exception unwinding
+    - `all`: Cleanup is executed on both normal exit and exception unwinding
+
+    Examples:
+
+    ```mlir
+    // Cleanup that runs on both normal and exception paths
+    cir.cleanup.scope {
+      cir.call @mayThrow() : () -> ()
+      cir.yield
+    } cleanup all {
+      cir.call @destructor() : () -> ()
+      cir.yield
+    }
+
+    // EH-only cleanup (destructor only called on exception)
+    cir.cleanup.scope {
+      cir.call @mayThrow() : () -> ()
+      cir.yield
+    } cleanup eh {
+      cir.call @destructor() : () -> ()
+      cir.yield
+    }
+    ```
+
+    Both regions must be terminated. If a region has only one block, the
+    terminator can be left out, and `cir.yield` will be inserted implicitly.
+  }];
+
+  let arguments = (ins CIR_CleanupKindAttr:$cleanupKind);
+  let regions = (region AnyRegion:$bodyRegion, AnyRegion:$cleanupRegion);
+
+  let skipDefaultBuilders = 1;
+
+  let assemblyFormat = [{
+    $bodyRegion `cleanup` $cleanupKind $cleanupRegion attr-dict
+  }];
+
+  let builders = [
+    OpBuilder<(ins "CleanupKind":$cleanupKind,
+      "llvm::function_ref<void(mlir::OpBuilder &, 
mlir::Location)>":$bodyBuilder,
+      "llvm::function_ref<void(mlir::OpBuilder &, 
mlir::Location)>":$cleanupBuilder)>
+  ];
+
+  let hasLLVMLowering = false;
+}
+
 
//===----------------------------------------------------------------------===//
 // SwitchOp
 
//===----------------------------------------------------------------------===//

diff  --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp 
b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index a5b3dbe3ecb91..abdb6ad1ec6af 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -1306,6 +1306,49 @@ LogicalResult cir::ScopeOp::fold(FoldAdaptor /*adaptor*/,
   return success();
 }
 
+//===----------------------------------------------------------------------===//
+// CleanupScopeOp
+//===----------------------------------------------------------------------===//
+
+void cir::CleanupScopeOp::getSuccessorRegions(
+    mlir::RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> &regions) {
+  if (!point.isParent()) {
+    regions.push_back(RegionSuccessor::parent());
+    return;
+  }
+
+  // Execution always proceeds from the body region to the cleanup region.
+  regions.push_back(RegionSuccessor(&getBodyRegion()));
+  regions.push_back(RegionSuccessor(&getCleanupRegion()));
+}
+
+mlir::ValueRange
+cir::CleanupScopeOp::getSuccessorInputs(RegionSuccessor successor) {
+  return ValueRange();
+}
+
+void cir::CleanupScopeOp::build(
+    OpBuilder &builder, OperationState &result, CleanupKind cleanupKind,
+    function_ref<void(OpBuilder &, Location)> bodyBuilder,
+    function_ref<void(OpBuilder &, Location)> cleanupBuilder) {
+  result.addAttribute(getCleanupKindAttrName(result.name),
+                      CleanupKindAttr::get(builder.getContext(), cleanupKind));
+
+  OpBuilder::InsertionGuard guard(builder);
+
+  // Build body region.
+  Region *bodyRegion = result.addRegion();
+  builder.createBlock(bodyRegion);
+  if (bodyBuilder)
+    bodyBuilder(builder, result.location);
+
+  // Build cleanup region.
+  Region *cleanupRegion = result.addRegion();
+  builder.createBlock(cleanupRegion);
+  if (cleanupBuilder)
+    cleanupBuilder(builder, result.location);
+}
+
 
//===----------------------------------------------------------------------===//
 // BrOp
 
//===----------------------------------------------------------------------===//

diff  --git a/clang/test/CIR/IR/cleanup-scope.cir 
b/clang/test/CIR/IR/cleanup-scope.cir
new file mode 100644
index 0000000000000..d03b999088b86
--- /dev/null
+++ b/clang/test/CIR/IR/cleanup-scope.cir
@@ -0,0 +1,116 @@
+// RUN: cir-opt %s --verify-roundtrip | FileCheck %s
+
+!s32i = !cir.int<s, 32>
+
+module {
+
+// Test basic cleanup.scope with all cleanup kind (normal + eh)
+cir.func @cleanup_scope_all() {
+  cir.cleanup.scope {
+    cir.yield
+  } cleanup all {
+    cir.yield
+  }
+  cir.return
+}
+
+// CHECK: cir.func @cleanup_scope_all() {
+// CHECK:   cir.cleanup.scope {
+// CHECK:     cir.yield
+// CHECK:   } cleanup all {
+// CHECK:     cir.yield
+// CHECK:   }
+// CHECK:   cir.return
+// CHECK: }
+
+// Test cleanup.scope with normal-only cleanup
+cir.func @cleanup_scope_normal() {
+  cir.cleanup.scope {
+    cir.yield
+  } cleanup normal {
+    cir.yield
+  }
+  cir.return
+}
+
+// CHECK: cir.func @cleanup_scope_normal() {
+// CHECK:   cir.cleanup.scope {
+// CHECK:     cir.yield
+// CHECK:   } cleanup normal {
+// CHECK:     cir.yield
+// CHECK:   }
+// CHECK:   cir.return
+// CHECK: }
+
+// Test cleanup.scope with EH-only cleanup
+cir.func @cleanup_scope_eh() {
+  cir.cleanup.scope {
+    cir.yield
+  } cleanup eh {
+    cir.yield
+  }
+  cir.return
+}
+
+// CHECK: cir.func @cleanup_scope_eh() {
+// CHECK:   cir.cleanup.scope {
+// CHECK:     cir.yield
+// CHECK:   } cleanup eh {
+// CHECK:     cir.yield
+// CHECK:   }
+// CHECK:   cir.return
+// CHECK: }
+
+// Test nested cleanup.scope operations
+cir.func @nested_cleanup_scopes() {
+  cir.cleanup.scope {
+    cir.cleanup.scope {
+      cir.yield
+    } cleanup all {
+      cir.yield
+    }
+    cir.yield
+  } cleanup all {
+    cir.yield
+  }
+  cir.return
+}
+
+// CHECK: cir.func @nested_cleanup_scopes() {
+// CHECK:   cir.cleanup.scope {
+// CHECK:     cir.cleanup.scope {
+// CHECK:       cir.yield
+// CHECK:     } cleanup all {
+// CHECK:       cir.yield
+// CHECK:     }
+// CHECK:     cir.yield
+// CHECK:   } cleanup all {
+// CHECK:     cir.yield
+// CHECK:   }
+// CHECK:   cir.return
+// CHECK: }
+
+// Test cleanup.scope inside cir.scope
+cir.func @cleanup_in_scope() {
+  cir.scope {
+    cir.cleanup.scope {
+      cir.yield
+    } cleanup all {
+      cir.yield
+    }
+  }
+  cir.return
+}
+
+// CHECK: cir.func @cleanup_in_scope() {
+// CHECK:   cir.scope {
+// CHECK:     cir.cleanup.scope {
+// CHECK:       cir.yield
+// CHECK:     } cleanup all {
+// CHECK:       cir.yield
+// CHECK:     }
+// CHECK:   }
+// CHECK:   cir.return
+// CHECK: }
+
+}


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

Reply via email to