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: } + +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
