Skip to content

Commit bd21800

Browse files
authored
[CIR] Upstream CIR Dialect TryOp with Catch Attrs (#162897)
Upstream CIR TryOp with catch attributes as a prerequisite for implementing try-catch in #162528 Issue #154992
1 parent 8b06ef3 commit bd21800

File tree

6 files changed

+489
-6
lines changed

6 files changed

+489
-6
lines changed

clang/include/clang/CIR/Dialect/IR/CIRAttrConstraints.td

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,32 @@ def CIR_AnyIntOrFloatAttr : AnyAttrOf<[CIR_AnyIntAttr, CIR_AnyFPAttr],
3838
string cppType = "::mlir::TypedAttr";
3939
}
4040

41+
//===----------------------------------------------------------------------===//
42+
// Exceptions constraints
43+
//===----------------------------------------------------------------------===//
44+
45+
def CIR_AnyCatchAllAttr
46+
: CIR_AttrConstraint<"::cir::CatchAllAttr", "catch all attribute">;
47+
48+
def CIR_AnyUnwindAttr
49+
: CIR_AttrConstraint<"::cir::UnwindAttr", "unwind attribute">;
50+
4151
//===----------------------------------------------------------------------===//
4252
// GlobalViewAttr constraints
4353
//===----------------------------------------------------------------------===//
4454

45-
def CIR_AnyGlobalViewAttr : CIR_AttrConstraint<"::cir::GlobalViewAttr", "GlobalView attribute">;
55+
def CIR_AnyGlobalViewAttr
56+
: CIR_AttrConstraint<"::cir::GlobalViewAttr", "GlobalView attribute">;
4657

47-
def CIR_AnyIntOrGlobalViewAttr : AnyAttrOf<[CIR_AnyIntAttr, CIR_AnyGlobalViewAttr],
48-
"integer or global view attribute"> {
58+
def CIR_AnyIntOrGlobalViewAttr
59+
: AnyAttrOf<[CIR_AnyIntAttr, CIR_AnyGlobalViewAttr],
60+
"integer or global view attribute"> {
61+
string cppType = "::mlir::TypedAttr";
62+
}
63+
64+
def CIR_TryHandlerAttr
65+
: AnyAttrOf<[CIR_AnyGlobalViewAttr, CIR_AnyCatchAllAttr, CIR_AnyUnwindAttr],
66+
"catch all or unwind or global view attribute"> {
4967
string cppType = "::mlir::TypedAttr";
5068
}
5169

@@ -61,4 +79,7 @@ def CIR_IntOrGlobalViewArrayAttr : TypedArrayAttrBase<CIR_AnyIntOrGlobalViewAttr
6179
string cppType = "::mlir::ArrayAttr";
6280
}
6381

64-
#endif // CLANG_CIR_DIALECT_IR_CIRATTRCONSTRAINTS_TD
82+
def CIR_TryHandlerArrayAttr : TypedArrayAttrBase<CIR_TryHandlerAttr,
83+
"catch all or unwind or global view array attribute">;
84+
85+
#endif // CLANG_CIR_DIALECT_IR_CIRATTRCONSTRAINTS_TD

clang/include/clang/CIR/Dialect/IR/CIRAttrs.td

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -968,4 +968,19 @@ def CIR_TypeInfoAttr : CIR_Attr<"TypeInfo", "typeinfo", [TypedAttrInterface]> {
968968
}];
969969
}
970970

971+
//===----------------------------------------------------------------------===//
972+
// CatchAllAttr & UnwindAttr
973+
//===----------------------------------------------------------------------===//
974+
975+
// Represents the catch_all region.
976+
def CIR_CatchAllAttr : CIR_UnitAttr<"CatchAll", "all"> {
977+
let storageType = [{ CatchAllAttr }];
978+
}
979+
980+
// Represents the unwind region where unwind continues or
981+
// the program std::terminate's.
982+
def CIR_UnwindAttr : CIR_UnitAttr<"Unwind", "unwind"> {
983+
let storageType = [{ CatchUnwind }];
984+
}
985+
971986
#endif // CLANG_CIR_DIALECT_IR_CIRATTRS_TD

clang/include/clang/CIR/Dialect/IR/CIROps.td

Lines changed: 85 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -644,7 +644,7 @@ def CIR_StoreOp : CIR_Op<"store", [
644644

645645
defvar CIR_ReturnableScopes = [
646646
"FuncOp", "ScopeOp", "IfOp", "SwitchOp", "CaseOp",
647-
"DoWhileOp", "WhileOp", "ForOp"
647+
"DoWhileOp", "WhileOp", "ForOp", "TryOp"
648648
];
649649

650650
def CIR_ReturnOp : CIR_Op<"return", [
@@ -791,7 +791,7 @@ def CIR_ConditionOp : CIR_Op<"condition", [
791791

792792
defvar CIR_YieldableScopes = [
793793
"ArrayCtor", "ArrayDtor", "CaseOp", "DoWhileOp", "ForOp", "GlobalOp", "IfOp",
794-
"ScopeOp", "SwitchOp", "TernaryOp", "WhileOp"
794+
"ScopeOp", "SwitchOp", "TernaryOp", "WhileOp", "TryOp"
795795
];
796796

797797
def CIR_YieldOp : CIR_Op<"yield", [
@@ -4325,6 +4325,89 @@ def CIR_AllocExceptionOp : CIR_Op<"alloc.exception"> {
43254325
}];
43264326
}
43274327

4328+
//===----------------------------------------------------------------------===//
4329+
// TryOp
4330+
//===----------------------------------------------------------------------===//
4331+
4332+
def CIR_TryOp : CIR_Op<"try",[
4333+
DeclareOpInterfaceMethods<RegionBranchOpInterface>,
4334+
RecursivelySpeculatable, AutomaticAllocationScope, NoRegionArguments
4335+
]> {
4336+
let summary = "C++ try block";
4337+
let description = [{
4338+
Holds the lexical scope of `try {}`. Note that resources used on catch
4339+
clauses are usually allocated in the same parent as `cir.try`.
4340+
4341+
`synthetic`: use `cir.try` to represent try/catches not originally
4342+
present in the source code. For example, a synthetic `cir.try` region
4343+
is created around the constructor call when `operator new` is used
4344+
so that the memory allocated will be freed if the constructor throws
4345+
an exception.
4346+
4347+
`cleanup`: indicates that there are cleanups that must be performed
4348+
when exiting the try region via exception, even if the exception is not
4349+
caught.
4350+
4351+
Example:
4352+
4353+
```mlir
4354+
cir.try {
4355+
cir.call exception @function() : () -> ()
4356+
cir.yield
4357+
} catch [type #cir.global_view<@_ZTIPf> : !cir.ptr<!u8i>] {
4358+
...
4359+
cir.yield
4360+
} unwind {
4361+
cir.resume
4362+
}
4363+
```
4364+
}];
4365+
4366+
let arguments = (ins
4367+
UnitAttr:$synthetic,
4368+
UnitAttr:$cleanup,
4369+
CIR_TryHandlerArrayAttr:$handler_types
4370+
);
4371+
4372+
let regions = (region
4373+
AnyRegion:$try_region,
4374+
VariadicRegion<MinSizedRegion<1>>:$handler_regions
4375+
);
4376+
4377+
let assemblyFormat = [{
4378+
(`synthetic` $synthetic^)?
4379+
(`cleanup` $cleanup^)?
4380+
$try_region
4381+
custom<TryHandlerRegions>($handler_regions, $handler_types)
4382+
attr-dict
4383+
}];
4384+
4385+
let builders = [
4386+
OpBuilder<(ins
4387+
"llvm::function_ref<void(mlir::OpBuilder &, "
4388+
"mlir::Location)>":$tryBuilder,
4389+
"llvm::function_ref<void(mlir::OpBuilder &, mlir::Location, "
4390+
"mlir::OperationState &)>":$handlersBuilder),
4391+
[{
4392+
assert(tryBuilder && "expected builder callback for 'cir.try' body");
4393+
assert(handlersBuilder
4394+
&& "expected builder callback for 'handlers' body");
4395+
4396+
OpBuilder::InsertionGuard guard($_builder);
4397+
4398+
// Try body region
4399+
mlir::Region *tryBodyRegion = $_state.addRegion();
4400+
4401+
// Create try body region and set insertion point
4402+
$_builder.createBlock(tryBodyRegion);
4403+
tryBuilder($_builder, $_state.location);
4404+
handlersBuilder($_builder, $_state.location, $_state);
4405+
}]>
4406+
];
4407+
4408+
let hasLLVMLowering = false;
4409+
}
4410+
43284411
//===----------------------------------------------------------------------===//
43294412
// Atomic operations
43304413
//===----------------------------------------------------------------------===//

clang/lib/CIR/Dialect/IR/CIRDialect.cpp

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2914,6 +2914,130 @@ LogicalResult cir::TypeInfoAttr::verify(
29142914
return success();
29152915
}
29162916

2917+
//===----------------------------------------------------------------------===//
2918+
// TryOp
2919+
//===----------------------------------------------------------------------===//
2920+
2921+
void cir::TryOp::getSuccessorRegions(
2922+
mlir::RegionBranchPoint point,
2923+
llvm::SmallVectorImpl<mlir::RegionSuccessor> &regions) {
2924+
// The `try` and the `catchers` region branch back to the parent operation.
2925+
if (!point.isParent()) {
2926+
regions.push_back(mlir::RegionSuccessor());
2927+
return;
2928+
}
2929+
2930+
regions.push_back(mlir::RegionSuccessor(&getTryRegion()));
2931+
2932+
// TODO(CIR): If we know a target function never throws a specific type, we
2933+
// can remove the catch handler.
2934+
for (mlir::Region &handlerRegion : this->getHandlerRegions())
2935+
regions.push_back(mlir::RegionSuccessor(&handlerRegion));
2936+
}
2937+
2938+
static void
2939+
printTryHandlerRegions(mlir::OpAsmPrinter &printer, cir::TryOp op,
2940+
mlir::MutableArrayRef<mlir::Region> handlerRegions,
2941+
mlir::ArrayAttr handlerTypes) {
2942+
if (!handlerTypes)
2943+
return;
2944+
2945+
for (const auto [typeIdx, typeAttr] : llvm::enumerate(handlerTypes)) {
2946+
if (typeIdx)
2947+
printer << " ";
2948+
2949+
if (mlir::isa<cir::CatchAllAttr>(typeAttr)) {
2950+
printer << "catch all ";
2951+
} else if (mlir::isa<cir::UnwindAttr>(typeAttr)) {
2952+
printer << "unwind ";
2953+
} else {
2954+
printer << "catch [type ";
2955+
printer.printAttribute(typeAttr);
2956+
printer << "] ";
2957+
}
2958+
2959+
printer.printRegion(handlerRegions[typeIdx],
2960+
/*printEntryBLockArgs=*/false,
2961+
/*printBlockTerminators=*/true);
2962+
}
2963+
}
2964+
2965+
static mlir::ParseResult parseTryHandlerRegions(
2966+
mlir::OpAsmParser &parser,
2967+
llvm::SmallVectorImpl<std::unique_ptr<mlir::Region>> &handlerRegions,
2968+
mlir::ArrayAttr &handlerTypes) {
2969+
2970+
auto parseCheckedCatcherRegion = [&]() -> mlir::ParseResult {
2971+
handlerRegions.emplace_back(new mlir::Region);
2972+
2973+
mlir::Region &currRegion = *handlerRegions.back();
2974+
mlir::SMLoc regionLoc = parser.getCurrentLocation();
2975+
if (parser.parseRegion(currRegion)) {
2976+
handlerRegions.clear();
2977+
return failure();
2978+
}
2979+
2980+
if (!currRegion.empty() && !(currRegion.back().mightHaveTerminator() &&
2981+
currRegion.back().getTerminator()))
2982+
return parser.emitError(
2983+
regionLoc, "blocks are expected to be explicitly terminated");
2984+
2985+
return success();
2986+
};
2987+
2988+
bool hasCatchAll = false;
2989+
llvm::SmallVector<mlir::Attribute, 4> catcherAttrs;
2990+
while (parser.parseOptionalKeyword("catch").succeeded()) {
2991+
bool hasLSquare = parser.parseOptionalLSquare().succeeded();
2992+
2993+
llvm::StringRef attrStr;
2994+
if (parser.parseOptionalKeyword(&attrStr, {"all", "type"}).failed())
2995+
return parser.emitError(parser.getCurrentLocation(),
2996+
"expected 'all' or 'type' keyword");
2997+
2998+
bool isCatchAll = attrStr == "all";
2999+
if (isCatchAll) {
3000+
if (hasCatchAll)
3001+
return parser.emitError(parser.getCurrentLocation(),
3002+
"can't have more than one catch all");
3003+
hasCatchAll = true;
3004+
}
3005+
3006+
mlir::Attribute exceptionRTTIAttr;
3007+
if (!isCatchAll && parser.parseAttribute(exceptionRTTIAttr).failed())
3008+
return parser.emitError(parser.getCurrentLocation(),
3009+
"expected valid RTTI info attribute");
3010+
3011+
catcherAttrs.push_back(isCatchAll
3012+
? cir::CatchAllAttr::get(parser.getContext())
3013+
: exceptionRTTIAttr);
3014+
3015+
if (hasLSquare && isCatchAll)
3016+
return parser.emitError(parser.getCurrentLocation(),
3017+
"catch all dosen't need RTTI info attribute");
3018+
3019+
if (hasLSquare && parser.parseRSquare().failed())
3020+
return parser.emitError(parser.getCurrentLocation(),
3021+
"expected `]` after RTTI info attribute");
3022+
3023+
if (parseCheckedCatcherRegion().failed())
3024+
return mlir::failure();
3025+
}
3026+
3027+
if (parser.parseOptionalKeyword("unwind").succeeded()) {
3028+
if (hasCatchAll)
3029+
return parser.emitError(parser.getCurrentLocation(),
3030+
"unwind can't be used with catch all");
3031+
3032+
catcherAttrs.push_back(cir::UnwindAttr::get(parser.getContext()));
3033+
if (parseCheckedCatcherRegion().failed())
3034+
return mlir::failure();
3035+
}
3036+
3037+
handlerTypes = parser.getBuilder().getArrayAttr(catcherAttrs);
3038+
return mlir::success();
3039+
}
3040+
29173041
//===----------------------------------------------------------------------===//
29183042
// TableGen'd op method definitions
29193043
//===----------------------------------------------------------------------===//

0 commit comments

Comments
 (0)