[clang] 36fea45 - [CIR] Create cir.cleanup.scope operation (#178085)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Feb 2 08:53:19 PST 2026
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> ®ions) {
+ 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: }
+
+}
More information about the cfe-commits
mailing list