[clang] [CIR] Upstream CIR Dialect TryOp with Catch Attrs (PR #162897)
Amr Hesham via cfe-commits
cfe-commits at lists.llvm.org
Sat Oct 11 06:53:49 PDT 2025
https://github.com/AmrDeveloper updated https://github.com/llvm/llvm-project/pull/162897
>From 3af059e4bbc1ccd09170264670a38d3c36a932cd Mon Sep 17 00:00:00 2001
From: Amr Hesham <amr96 at programmer.net>
Date: Fri, 10 Oct 2025 19:09:16 +0200
Subject: [PATCH 1/4] [CIR] Upstream CIR Dialect TryOp with Catch Attrs
---
.../include/clang/CIR/Dialect/IR/CIRAttrs.td | 15 ++
clang/include/clang/CIR/Dialect/IR/CIROps.td | 63 ++++++++-
clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 132 ++++++++++++++++++
clang/test/CIR/IR/try-catch.cir | 84 +++++++++++
4 files changed, 292 insertions(+), 2 deletions(-)
create mode 100644 clang/test/CIR/IR/try-catch.cir
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
index bb62223d9e152..38b53396bad31 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
@@ -960,4 +960,19 @@ def CIR_TypeInfoAttr : CIR_Attr<"TypeInfo", "typeinfo", [TypedAttrInterface]> {
}];
}
+//===----------------------------------------------------------------------===//
+// CatchAllAttr & CatchUnwindAttr
+//===----------------------------------------------------------------------===//
+
+// Represents the unwind region where unwind continues or
+// the program std::terminate's.
+def CIR_CatchUnwindAttr : CIR_UnitAttr<"CatchUnwind", "unwind"> {
+ let storageType = [{ CatchUnwind }];
+}
+
+// Represents the catch_all region.
+def CIR_CatchAllAttr : CIR_UnitAttr<"CatchAll", "all"> {
+ let storageType = [{ CatchAllAttr }];
+}
+
#endif // CLANG_CIR_DIALECT_IR_CIRATTRS_TD
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 27fe0cc46d7cf..1171b6f820341 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -631,7 +631,7 @@ def CIR_StoreOp : CIR_Op<"store", [
defvar CIR_ReturnableScopes = [
"FuncOp", "ScopeOp", "IfOp", "SwitchOp", "CaseOp",
- "DoWhileOp", "WhileOp", "ForOp"
+ "DoWhileOp", "WhileOp", "ForOp", "TryOp"
];
def CIR_ReturnOp : CIR_Op<"return", [
@@ -778,7 +778,7 @@ def CIR_ConditionOp : CIR_Op<"condition", [
defvar CIR_YieldableScopes = [
"ArrayCtor", "ArrayDtor", "CaseOp", "DoWhileOp", "ForOp", "GlobalOp", "IfOp",
- "ScopeOp", "SwitchOp", "TernaryOp", "WhileOp"
+ "ScopeOp", "SwitchOp", "TernaryOp", "WhileOp", "TryOp"
];
def CIR_YieldOp : CIR_Op<"yield", [
@@ -4296,6 +4296,65 @@ def CIR_AllocExceptionOp : CIR_Op<"alloc.exception"> {
}];
}
+//===----------------------------------------------------------------------===//
+// TryOp
+//===----------------------------------------------------------------------===//
+
+def CIR_TryOp : CIR_Op<"try",[
+ DeclareOpInterfaceMethods<RegionBranchOpInterface>,
+ RecursivelySpeculatable, AutomaticAllocationScope, NoRegionArguments
+]> {
+ let summary = "C++ try block";
+ let description = [{
+ Holds the lexical scope of `try {}`. Note that resources used on catch
+ clauses are usually allocated in the same parent as `cir.try`.
+
+ `synthetic`: use `cir.try` to represent try/catches not originally
+ present in the source code (e.g. `g = new Class` under `-fexceptions`).
+
+ `cleanup`: signal to targets (LLVM for now) that this try/catch, needs
+ to specially tag their landing pads as needing "cleanup".
+
+ Example:
+
+ ```mlir
+ %0 = cir.alloc.exception 16 -> !cir.ptr<!some_record>
+ %1 = cir.get_global @d2 : !cir.ptr<!some_record>
+ cir.try synthetic cleanup {
+ cir.call exception @_ZN7test2_DC1ERKS_(%0, %1)
+ : (!cir.ptr<!some_record>, !cir.ptr<!some_record>) -> () cleanup {
+ %2 = cir.cast bitcast %0 : !cir.ptr<!some_record> -> !cir.ptr<!void>
+ cir.free.exception %2
+ cir.yield
+ }
+ ...
+ }
+ ```
+ }];
+
+ let arguments = (ins UnitAttr:$synthetic, UnitAttr:$cleanup,
+ OptionalAttr<ArrayAttr>:$catch_types);
+ let regions = (region AnyRegion:$try_region,
+ VariadicRegion<AnyRegion>:$catch_regions);
+
+ let assemblyFormat = [{
+ (`synthetic` $synthetic^)?
+ (`cleanup` $cleanup^)?
+ $try_region
+ custom<CatchRegions>($catch_regions, $catch_types)
+ attr-dict
+ }];
+
+ // Everything already covered elsewhere.
+ let builders = [OpBuilder<(ins
+ "llvm::function_ref<void(mlir::OpBuilder &, "
+ "mlir::Location)>":$tryBuilder,
+ "llvm::function_ref<void(mlir::OpBuilder &, mlir::Location, "
+ "mlir::OperationState &)>":$catchBuilder)>];
+
+ let hasLLVMLowering = false;
+}
+
//===----------------------------------------------------------------------===//
// Atomic operations
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 5f88590c48d30..6e6eb4e5c9093 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -2878,6 +2878,138 @@ LogicalResult cir::TypeInfoAttr::verify(
return success();
}
+//===----------------------------------------------------------------------===//
+// TryOp
+//===----------------------------------------------------------------------===//
+
+void cir::TryOp::build(
+ OpBuilder &builder, OperationState &result,
+ function_ref<void(OpBuilder &, Location)> tryBuilder,
+ function_ref<void(OpBuilder &, Location, OperationState &)> catchBuilder) {
+ assert(tryBuilder && "expected builder callback for 'cir.try' body");
+ assert(catchBuilder && "expected builder callback for 'catch' body");
+
+ OpBuilder::InsertionGuard guard(builder);
+
+ // Try body region
+ Region *tryBodyRegion = result.addRegion();
+
+ // Create try body region and set insertion point
+ builder.createBlock(tryBodyRegion);
+ tryBuilder(builder, result.location);
+ catchBuilder(builder, result.location, result);
+}
+
+void cir::TryOp::getSuccessorRegions(
+ mlir::RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> ®ions) {
+ // If any index all the underlying regions branch back to the parent
+ // operation.
+ if (!point.isParent()) {
+ regions.push_back(RegionSuccessor());
+ return;
+ }
+
+ // If the condition isn't constant, both regions may be executed.
+ regions.push_back(RegionSuccessor(&getTryRegion()));
+
+ // FIXME: optimize, ideas include:
+ // - If we know a target function never throws a specific type, we can
+ // remove the catch handler.
+ for (mlir::Region &r : this->getCatchRegions())
+ regions.push_back(RegionSuccessor(&r));
+}
+
+static void printCatchRegions(OpAsmPrinter &printer, cir::TryOp op,
+ mlir::MutableArrayRef<::mlir::Region> regions,
+ mlir::ArrayAttr catchersAttr) {
+ if (!catchersAttr)
+ return;
+
+ int currCatchIdx = 0;
+ printer << "catch [";
+ llvm::interleaveComma(catchersAttr, printer, [&](const Attribute &a) {
+ if (mlir::isa<cir::CatchUnwindAttr>(a)) {
+ printer.printAttribute(a);
+ printer << " ";
+ } else if (!a) {
+ printer << "all";
+ } else {
+ printer << "type ";
+ printer.printAttribute(a);
+ printer << " ";
+ }
+ printer.printRegion(regions[currCatchIdx], /*printEntryBLockArgs=*/false,
+ /*printBlockTerminators=*/true);
+ currCatchIdx++;
+ });
+
+ printer << "]";
+}
+
+static ParseResult parseCatchRegions(
+ OpAsmParser &parser,
+ llvm::SmallVectorImpl<std::unique_ptr<::mlir::Region>> ®ions,
+ ::mlir::ArrayAttr &catchersAttr) {
+ if (parser.parseKeyword("catch").failed())
+ return parser.emitError(parser.getCurrentLocation(),
+ "expected 'catch' keyword here");
+
+ auto parseAndCheckRegion = [&]() -> ParseResult {
+ // Parse region attached to catch
+ regions.emplace_back(new Region);
+ Region &currRegion = *regions.back();
+ SMLoc parserLoc = parser.getCurrentLocation();
+ if (parser.parseRegion(currRegion)) {
+ regions.clear();
+ return failure();
+ }
+
+ if (currRegion.empty()) {
+ return parser.emitError(parser.getCurrentLocation(),
+ "catch region shall not be empty");
+ }
+
+ if (!(currRegion.back().mightHaveTerminator() &&
+ currRegion.back().getTerminator()))
+ return parser.emitError(
+ parserLoc, "blocks are expected to be explicitly terminated");
+
+ return success();
+ };
+
+ llvm::SmallVector<mlir::Attribute, 4> catchList;
+ auto parseCatchEntry = [&]() -> ParseResult {
+ mlir::Attribute exceptionTypeInfo;
+
+ if (parser.parseOptionalAttribute(exceptionTypeInfo).has_value()) {
+ catchList.push_back(exceptionTypeInfo);
+ } else {
+ ::llvm::StringRef attrStr;
+ if (parser.parseOptionalKeyword(&attrStr, {"all"}).succeeded()) {
+ // "all" keyword found, exceptionTypeInfo remains null
+ } else if (parser.parseOptionalKeyword("type").succeeded()) {
+ if (parser.parseAttribute(exceptionTypeInfo).failed())
+ return parser.emitError(parser.getCurrentLocation(),
+ "expected valid RTTI info attribute");
+ } else {
+ return parser.emitError(parser.getCurrentLocation(),
+ "expected attribute, 'all', or 'type' keyword");
+ }
+ catchList.push_back(exceptionTypeInfo);
+ }
+ return parseAndCheckRegion();
+ };
+
+ if (parser
+ .parseCommaSeparatedList(OpAsmParser::Delimiter::Square,
+ parseCatchEntry, " in catch list")
+ .failed())
+ return failure();
+
+ catchersAttr = parser.getBuilder().getArrayAttr(catchList);
+ return ::mlir::success();
+}
+
//===----------------------------------------------------------------------===//
// TableGen'd op method definitions
//===----------------------------------------------------------------------===//
diff --git a/clang/test/CIR/IR/try-catch.cir b/clang/test/CIR/IR/try-catch.cir
new file mode 100644
index 0000000000000..7bc71ff84d4ae
--- /dev/null
+++ b/clang/test/CIR/IR/try-catch.cir
@@ -0,0 +1,84 @@
+// RUN: cir-opt %s --verify-roundtrip | FileCheck %s
+
+!u8i = !cir.int<u, 8>
+
+module {
+
+cir.global "private" constant external @_ZTIi : !cir.ptr<!u8i>
+cir.global "private" constant external @_ZTIPKc : !cir.ptr<!u8i>
+
+cir.func dso_local @empty_try_block_with_catch_all() {
+ cir.scope {
+ cir.try {
+ cir.yield
+ } catch [type #cir.all {
+ cir.yield
+ }]
+ }
+ cir.return
+}
+
+// CHECK: cir.func dso_local @empty_try_block_with_catch_all() {
+// CHECK: cir.scope {
+// CHECK: cir.try {
+// CHECK: cir.yield
+// CHECK: } catch [type #cir.all {
+// CHECK: cir.yield
+// CHECK: }]
+// CHECK: }
+// CHECK: cir.return
+// CHECK: }
+
+cir.func dso_local @empty_try_block_with_catch_unwind() {
+ cir.scope {
+ cir.try {
+ cir.yield
+ } catch [#cir.unwind {
+ cir.yield
+ }]
+ }
+ cir.return
+}
+
+// CHECK: cir.func dso_local @empty_try_block_with_catch_unwind() {
+// CHECK: cir.scope {
+// CHECK: cir.try {
+// CHECK: cir.yield
+// CHECK: } catch [#cir.unwind {
+// CHECK: cir.yield
+// CHECK: }]
+// CHECK: }
+// CHECK: cir.return
+// CHECK: }
+
+cir.func dso_local @empty_try_block_with_catch_ist() {
+ cir.scope {
+ cir.try {
+ cir.yield
+ } catch [type #cir.global_view<@_ZTIi> : !cir.ptr<!u8i> {
+ cir.yield
+ }, type #cir.global_view<@_ZTIPKc> : !cir.ptr<!u8i> {
+ cir.yield
+ }, #cir.unwind {
+ cir.yield
+ }]
+ }
+ cir.return
+}
+
+// CHECK: cir.func dso_local @empty_try_block_with_catch_ist() {
+// CHECK: cir.scope {
+// CHECK: cir.try {
+// CHECK: cir.yield
+// CHECK: } catch [type #cir.global_view<@_ZTIi> : !cir.ptr<!u8i> {
+// CHECK: cir.yield
+// CHECK: }, type #cir.global_view<@_ZTIPKc> : !cir.ptr<!u8i> {
+// CHECK: cir.yield
+// CHECK: }, #cir.unwind {
+// CHECK: cir.yield
+// CHECK: }]
+// CHECK: }
+// CHECK: cir.return
+// CHECK: }
+
+}
>From 720984fcd4e6936c82bc1cb98c599f66abc6ee65 Mon Sep 17 00:00:00 2001
From: Amr Hesham <amr96 at programmer.net>
Date: Fri, 10 Oct 2025 21:14:15 +0200
Subject: [PATCH 2/4] Address code review comments
---
clang/include/clang/CIR/Dialect/IR/CIROps.td | 20 +++++-
clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 18 -----
clang/test/CIR/IR/invalid-try-catch.cir | 75 ++++++++++++++++++++
3 files changed, 93 insertions(+), 20 deletions(-)
create mode 100644 clang/test/CIR/IR/invalid-try-catch.cir
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 1171b6f820341..db832330aaae2 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -4346,11 +4346,27 @@ def CIR_TryOp : CIR_Op<"try",[
}];
// Everything already covered elsewhere.
- let builders = [OpBuilder<(ins
+ let builders = [
+ OpBuilder<(ins
"llvm::function_ref<void(mlir::OpBuilder &, "
"mlir::Location)>":$tryBuilder,
"llvm::function_ref<void(mlir::OpBuilder &, mlir::Location, "
- "mlir::OperationState &)>":$catchBuilder)>];
+ "mlir::OperationState &)>":$catchBuilder),
+ [{
+ assert(tryBuilder && "expected builder callback for 'cir.try' body");
+ assert(catchBuilder && "expected builder callback for 'catch' body");
+
+ OpBuilder::InsertionGuard guard($_builder);
+
+ // Try body region
+ Region *tryBodyRegion = $_state.addRegion();
+
+ // Create try body region and set insertion point
+ $_builder.createBlock(tryBodyRegion);
+ tryBuilder($_builder, $_state.location);
+ catchBuilder($_builder, $_state.location, $_state);
+ }]>
+ ];
let hasLLVMLowering = false;
}
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 6e6eb4e5c9093..9f06d799a04ce 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -2882,24 +2882,6 @@ LogicalResult cir::TypeInfoAttr::verify(
// TryOp
//===----------------------------------------------------------------------===//
-void cir::TryOp::build(
- OpBuilder &builder, OperationState &result,
- function_ref<void(OpBuilder &, Location)> tryBuilder,
- function_ref<void(OpBuilder &, Location, OperationState &)> catchBuilder) {
- assert(tryBuilder && "expected builder callback for 'cir.try' body");
- assert(catchBuilder && "expected builder callback for 'catch' body");
-
- OpBuilder::InsertionGuard guard(builder);
-
- // Try body region
- Region *tryBodyRegion = result.addRegion();
-
- // Create try body region and set insertion point
- builder.createBlock(tryBodyRegion);
- tryBuilder(builder, result.location);
- catchBuilder(builder, result.location, result);
-}
-
void cir::TryOp::getSuccessorRegions(
mlir::RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> ®ions) {
// If any index all the underlying regions branch back to the parent
diff --git a/clang/test/CIR/IR/invalid-try-catch.cir b/clang/test/CIR/IR/invalid-try-catch.cir
new file mode 100644
index 0000000000000..57863af13d20b
--- /dev/null
+++ b/clang/test/CIR/IR/invalid-try-catch.cir
@@ -0,0 +1,75 @@
+// RUN: cir-opt %s -verify-diagnostics -split-input-file
+
+module {
+
+cir.func dso_local @invalid_catch_without_all_or_type() {
+ cir.scope {
+ cir.try {
+ cir.yield
+ // expected-error @below {{'cir.try' expected attribute, 'all', or 'type' keyword}}
+ } catch [invalid_keyword {
+ cir.yield
+ }]
+ }
+ cir.return
+}
+
+}
+
+// -----
+
+module {
+
+cir.func dso_local @invalid_catch_rtti_type() {
+ cir.scope {
+ cir.try {
+ cir.yield
+ // expected-error @below {{expected attribute value}}
+ // expected-error @below {{'cir.try' expected valid RTTI info attribute}}
+ } catch [type invalid_type {
+ cir.yield
+ }]
+ }
+ cir.return
+}
+
+}
+
+// -----
+
+module {
+
+cir.func dso_local @invalid_catch_empty_block() {
+ cir.scope {
+ cir.try {
+ cir.yield
+ } catch [type #cir.all {
+ // expected-error @below {{'cir.try' catch region shall not be empty}}
+ }]
+ }
+ cir.return
+}
+
+}
+
+// -----
+
+!s32i = !cir.int<s, 32>
+
+module {
+
+cir.func dso_local @invalid_catch_not_terminated() {
+ %a = cir.alloca !s32i, !cir.ptr<!s32i>, ["a", init]
+ cir.scope {
+ cir.try {
+ cir.yield
+ }
+ // expected-error @below {{'cir.try' blocks are expected to be explicitly terminated}}
+ catch [type #cir.all {
+ %tmp_a = cir.load %a : !cir.ptr<!s32i>, !s32i
+ }]
+ }
+ cir.return
+}
+
+}
>From 43306db5c16b97575ba8b1261a7b51dee24d65e2 Mon Sep 17 00:00:00 2001
From: Amr Hesham <amr96 at programmer.net>
Date: Sat, 11 Oct 2025 00:05:22 +0200
Subject: [PATCH 3/4] Address code review comments
---
clang/include/clang/CIR/Dialect/IR/CIROps.td | 26 +++++++++++---------
1 file changed, 14 insertions(+), 12 deletions(-)
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index db832330aaae2..08bc34d419638 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -4310,24 +4310,26 @@ def CIR_TryOp : CIR_Op<"try",[
clauses are usually allocated in the same parent as `cir.try`.
`synthetic`: use `cir.try` to represent try/catches not originally
- present in the source code (e.g. `g = new Class` under `-fexceptions`).
+ present in the source code. For example, a synthetic `cir.try` region
+ is created around the constructor call when `operator new` is used
+ so that the memory allocated will be freed if the constructor throws
+ an exception.
- `cleanup`: signal to targets (LLVM for now) that this try/catch, needs
- to specially tag their landing pads as needing "cleanup".
+ `cleanup`: indicates that there are cleanups that must be performed
+ when exiting the try region via exception, even if the exception is not
+ caught.
Example:
```mlir
- %0 = cir.alloc.exception 16 -> !cir.ptr<!some_record>
- %1 = cir.get_global @d2 : !cir.ptr<!some_record>
- cir.try synthetic cleanup {
- cir.call exception @_ZN7test2_DC1ERKS_(%0, %1)
- : (!cir.ptr<!some_record>, !cir.ptr<!some_record>) -> () cleanup {
- %2 = cir.cast bitcast %0 : !cir.ptr<!some_record> -> !cir.ptr<!void>
- cir.free.exception %2
- cir.yield
- }
+ cir.try {
+ cir.call exception @function() : () -> ()
+ cir.yield
+ } catch [type #cir.global_view<@_ZTIPf> : !cir.ptr<!u8i>] {
...
+ cir.yield
+ } unwind {
+ cir.resume
}
```
}];
>From 523b1eeb171f996958107cfdc35b9c103f018961 Mon Sep 17 00:00:00 2001
From: Amr Hesham <amr96 at programmer.net>
Date: Sat, 11 Oct 2025 13:25:55 +0200
Subject: [PATCH 4/4] TryOp & Catchers Parsers and printers
---
.../CIR/Dialect/IR/CIRAttrConstraints.td | 30 +++-
.../include/clang/CIR/Dialect/IR/CIRAttrs.td | 14 +-
clang/include/clang/CIR/Dialect/IR/CIROps.td | 17 +-
clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 152 +++++++++---------
clang/test/CIR/IR/invalid-try-catch.cir | 82 ++++++++--
clang/test/CIR/IR/try-catch.cir | 60 +++----
6 files changed, 223 insertions(+), 132 deletions(-)
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrConstraints.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrConstraints.td
index 8f72ff4d754ad..bab847ef621bf 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRAttrConstraints.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrConstraints.td
@@ -38,14 +38,32 @@ def CIR_AnyIntOrFloatAttr : AnyAttrOf<[CIR_AnyIntAttr, CIR_AnyFPAttr],
string cppType = "::mlir::TypedAttr";
}
+//===----------------------------------------------------------------------===//
+// Exceptions constraints
+//===----------------------------------------------------------------------===//
+
+def CIR_AnyCatchAllAttr
+ : CIR_AttrConstraint<"::cir::CatchAllAttr", "catch all attribute">;
+
+def CIR_AnyUnwindAttr
+ : CIR_AttrConstraint<"::cir::UnwindAttr", "unwind attribute">;
+
//===----------------------------------------------------------------------===//
// GlobalViewAttr constraints
//===----------------------------------------------------------------------===//
-def CIR_AnyGlobalViewAttr : CIR_AttrConstraint<"::cir::GlobalViewAttr", "GlobalView attribute">;
+def CIR_AnyGlobalViewAttr
+ : CIR_AttrConstraint<"::cir::GlobalViewAttr", "GlobalView attribute">;
-def CIR_AnyIntOrGlobalViewAttr : AnyAttrOf<[CIR_AnyIntAttr, CIR_AnyGlobalViewAttr],
- "integer or global view attribute"> {
+def CIR_AnyIntOrGlobalViewAttr
+ : AnyAttrOf<[CIR_AnyIntAttr, CIR_AnyGlobalViewAttr],
+ "integer or global view attribute"> {
+ string cppType = "::mlir::TypedAttr";
+}
+
+def CIR_AnyGlobalViewOrCatchAllOrUnwindAttr
+ : AnyAttrOf<[CIR_AnyGlobalViewAttr, CIR_AnyCatchAllAttr, CIR_AnyUnwindAttr],
+ "catch all or unwind or global view attribute"> {
string cppType = "::mlir::TypedAttr";
}
@@ -61,4 +79,8 @@ def CIR_IntOrGlobalViewArrayAttr : TypedArrayAttrBase<CIR_AnyIntOrGlobalViewAttr
string cppType = "::mlir::ArrayAttr";
}
-#endif // CLANG_CIR_DIALECT_IR_CIRATTRCONSTRAINTS_TD
\ No newline at end of file
+def CIR_GlobalViewOrCatchAllOrUnwindArrayAttr
+ : TypedArrayAttrBase<CIR_AnyGlobalViewOrCatchAllOrUnwindAttr,
+ "catch all or unwind or global view array attribute">;
+
+#endif // CLANG_CIR_DIALECT_IR_CIRATTRCONSTRAINTS_TD
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
index 38b53396bad31..f8916760c37a2 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
@@ -961,18 +961,18 @@ def CIR_TypeInfoAttr : CIR_Attr<"TypeInfo", "typeinfo", [TypedAttrInterface]> {
}
//===----------------------------------------------------------------------===//
-// CatchAllAttr & CatchUnwindAttr
+// CatchAllAttr & UnwindAttr
//===----------------------------------------------------------------------===//
-// Represents the unwind region where unwind continues or
-// the program std::terminate's.
-def CIR_CatchUnwindAttr : CIR_UnitAttr<"CatchUnwind", "unwind"> {
- let storageType = [{ CatchUnwind }];
-}
-
// Represents the catch_all region.
def CIR_CatchAllAttr : CIR_UnitAttr<"CatchAll", "all"> {
let storageType = [{ CatchAllAttr }];
}
+// Represents the unwind region where unwind continues or
+// the program std::terminate's.
+def CIR_UnwindAttr : CIR_UnitAttr<"Unwind", "unwind"> {
+ let storageType = [{ CatchUnwind }];
+}
+
#endif // CLANG_CIR_DIALECT_IR_CIRATTRS_TD
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 08bc34d419638..2c637c9cf4214 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -4334,10 +4334,16 @@ def CIR_TryOp : CIR_Op<"try",[
```
}];
- let arguments = (ins UnitAttr:$synthetic, UnitAttr:$cleanup,
- OptionalAttr<ArrayAttr>:$catch_types);
- let regions = (region AnyRegion:$try_region,
- VariadicRegion<AnyRegion>:$catch_regions);
+ let arguments = (ins
+ UnitAttr:$synthetic,
+ UnitAttr:$cleanup,
+ CIR_GlobalViewOrCatchAllOrUnwindArrayAttr:$catch_types
+ );
+
+ let regions = (region
+ AnyRegion:$try_region,
+ VariadicRegion<MinSizedRegion<1>>:$catch_regions
+ );
let assemblyFormat = [{
(`synthetic` $synthetic^)?
@@ -4347,7 +4353,6 @@ def CIR_TryOp : CIR_Op<"try",[
attr-dict
}];
- // Everything already covered elsewhere.
let builders = [
OpBuilder<(ins
"llvm::function_ref<void(mlir::OpBuilder &, "
@@ -4361,7 +4366,7 @@ def CIR_TryOp : CIR_Op<"try",[
OpBuilder::InsertionGuard guard($_builder);
// Try body region
- Region *tryBodyRegion = $_state.addRegion();
+ mlir::Region *tryBodyRegion = $_state.addRegion();
// Create try body region and set insertion point
$_builder.createBlock(tryBodyRegion);
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 9f06d799a04ce..72b590e286333 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -2883,113 +2883,117 @@ LogicalResult cir::TypeInfoAttr::verify(
//===----------------------------------------------------------------------===//
void cir::TryOp::getSuccessorRegions(
- mlir::RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> ®ions) {
- // If any index all the underlying regions branch back to the parent
- // operation.
+ mlir::RegionBranchPoint point,
+ llvm::SmallVectorImpl<mlir::RegionSuccessor> ®ions) {
+ // The `try` and the `catchers` region branch back to the parent operation.
if (!point.isParent()) {
- regions.push_back(RegionSuccessor());
+ regions.push_back(mlir::RegionSuccessor());
return;
}
- // If the condition isn't constant, both regions may be executed.
- regions.push_back(RegionSuccessor(&getTryRegion()));
+ regions.push_back(mlir::RegionSuccessor(&getTryRegion()));
- // FIXME: optimize, ideas include:
- // - If we know a target function never throws a specific type, we can
- // remove the catch handler.
- for (mlir::Region &r : this->getCatchRegions())
- regions.push_back(RegionSuccessor(&r));
+ // TODO(CIR): If we know a target function never throws a specific type, we
+ // can remove the catch handler.
+ for (mlir::Region ®ion : this->getCatchRegions())
+ regions.push_back(mlir::RegionSuccessor(®ion));
}
-static void printCatchRegions(OpAsmPrinter &printer, cir::TryOp op,
- mlir::MutableArrayRef<::mlir::Region> regions,
+static void printCatchRegions(mlir::OpAsmPrinter &printer, cir::TryOp op,
+ mlir::MutableArrayRef<mlir::Region> regions,
mlir::ArrayAttr catchersAttr) {
if (!catchersAttr)
return;
- int currCatchIdx = 0;
- printer << "catch [";
- llvm::interleaveComma(catchersAttr, printer, [&](const Attribute &a) {
- if (mlir::isa<cir::CatchUnwindAttr>(a)) {
- printer.printAttribute(a);
+ for (const auto [catcherIdx, catcherAttr] : llvm::enumerate(catchersAttr)) {
+ if (catcherIdx)
printer << " ";
- } else if (!a) {
- printer << "all";
+
+ if (mlir::isa<cir::CatchAllAttr>(catcherAttr)) {
+ printer << "catch all ";
+ } else if (mlir::isa<cir::UnwindAttr>(catcherAttr)) {
+ printer << "unwind ";
} else {
- printer << "type ";
- printer.printAttribute(a);
- printer << " ";
+ printer << "catch [type ";
+ printer.printAttribute(catcherAttr);
+ printer << "] ";
}
- printer.printRegion(regions[currCatchIdx], /*printEntryBLockArgs=*/false,
- /*printBlockTerminators=*/true);
- currCatchIdx++;
- });
- printer << "]";
+ printer.printRegion(regions[catcherIdx], /*printEntryBLockArgs=*/false,
+ /*printBlockTerminators=*/true);
+ }
}
-static ParseResult parseCatchRegions(
- OpAsmParser &parser,
- llvm::SmallVectorImpl<std::unique_ptr<::mlir::Region>> ®ions,
- ::mlir::ArrayAttr &catchersAttr) {
- if (parser.parseKeyword("catch").failed())
- return parser.emitError(parser.getCurrentLocation(),
- "expected 'catch' keyword here");
+static mlir::ParseResult
+parseCatchRegions(mlir::OpAsmParser &parser,
+ llvm::SmallVectorImpl<std::unique_ptr<mlir::Region>> ®ions,
+ mlir::ArrayAttr &catchersAttr) {
- auto parseAndCheckRegion = [&]() -> ParseResult {
- // Parse region attached to catch
- regions.emplace_back(new Region);
- Region &currRegion = *regions.back();
- SMLoc parserLoc = parser.getCurrentLocation();
+ auto parseCheckedCatcherRegion = [&]() -> mlir::ParseResult {
+ regions.emplace_back(new mlir::Region);
+
+ mlir::Region &currRegion = *regions.back();
+ mlir::SMLoc regionLoc = parser.getCurrentLocation();
if (parser.parseRegion(currRegion)) {
regions.clear();
return failure();
}
- if (currRegion.empty()) {
- return parser.emitError(parser.getCurrentLocation(),
- "catch region shall not be empty");
- }
-
- if (!(currRegion.back().mightHaveTerminator() &&
- currRegion.back().getTerminator()))
+ if (!currRegion.empty() && !(currRegion.back().mightHaveTerminator() &&
+ currRegion.back().getTerminator()))
return parser.emitError(
- parserLoc, "blocks are expected to be explicitly terminated");
+ regionLoc, "blocks are expected to be explicitly terminated");
return success();
};
- llvm::SmallVector<mlir::Attribute, 4> catchList;
- auto parseCatchEntry = [&]() -> ParseResult {
- mlir::Attribute exceptionTypeInfo;
+ bool hasCatchAll = false;
+ llvm::SmallVector<mlir::Attribute, 4> catcherAttrs;
+ while (parser.parseOptionalKeyword("catch").succeeded()) {
+ bool hasLSquare = parser.parseOptionalLSquare().succeeded();
- if (parser.parseOptionalAttribute(exceptionTypeInfo).has_value()) {
- catchList.push_back(exceptionTypeInfo);
- } else {
- ::llvm::StringRef attrStr;
- if (parser.parseOptionalKeyword(&attrStr, {"all"}).succeeded()) {
- // "all" keyword found, exceptionTypeInfo remains null
- } else if (parser.parseOptionalKeyword("type").succeeded()) {
- if (parser.parseAttribute(exceptionTypeInfo).failed())
- return parser.emitError(parser.getCurrentLocation(),
- "expected valid RTTI info attribute");
- } else {
+ llvm::StringRef attrStr;
+ if (parser.parseOptionalKeyword(&attrStr, {"all", "type"}).failed())
+ return parser.emitError(parser.getCurrentLocation(),
+ "expected 'all' or 'type' keyword");
+
+ bool isCatchAll = attrStr == "all";
+ if (isCatchAll) {
+ if (hasCatchAll)
return parser.emitError(parser.getCurrentLocation(),
- "expected attribute, 'all', or 'type' keyword");
- }
- catchList.push_back(exceptionTypeInfo);
+ "can't have more than one catch all");
+ hasCatchAll = true;
}
- return parseAndCheckRegion();
- };
- if (parser
- .parseCommaSeparatedList(OpAsmParser::Delimiter::Square,
- parseCatchEntry, " in catch list")
- .failed())
- return failure();
+ mlir::Attribute exceptionRTTIAttr;
+ if (!isCatchAll && parser.parseAttribute(exceptionRTTIAttr).failed())
+ return parser.emitError(parser.getCurrentLocation(),
+ "expected valid RTTI info attribute");
+
+ catcherAttrs.push_back(isCatchAll
+ ? cir::CatchAllAttr::get(parser.getContext())
+ : exceptionRTTIAttr);
+
+ if (hasLSquare && isCatchAll)
+ return parser.emitError(parser.getCurrentLocation(),
+ "catch all dosen't need RTTI info attribute");
+
+ if (hasLSquare && parser.parseRSquare().failed())
+ return parser.emitError(parser.getCurrentLocation(),
+ "expected `]` after RTTI info attribute");
+
+ if (parseCheckedCatcherRegion().failed())
+ return mlir::failure();
+ }
- catchersAttr = parser.getBuilder().getArrayAttr(catchList);
- return ::mlir::success();
+ if (parser.parseOptionalKeyword("unwind").succeeded()) {
+ catcherAttrs.push_back(cir::UnwindAttr::get(parser.getContext()));
+ if (parseCheckedCatcherRegion().failed())
+ return mlir::failure();
+ }
+
+ catchersAttr = parser.getBuilder().getArrayAttr(catcherAttrs);
+ return mlir::success();
}
//===----------------------------------------------------------------------===//
diff --git a/clang/test/CIR/IR/invalid-try-catch.cir b/clang/test/CIR/IR/invalid-try-catch.cir
index 57863af13d20b..40eaa7b06e3c3 100644
--- a/clang/test/CIR/IR/invalid-try-catch.cir
+++ b/clang/test/CIR/IR/invalid-try-catch.cir
@@ -6,10 +6,10 @@ cir.func dso_local @invalid_catch_without_all_or_type() {
cir.scope {
cir.try {
cir.yield
- // expected-error @below {{'cir.try' expected attribute, 'all', or 'type' keyword}}
+ // expected-error @below {{'cir.try' expected 'all' or 'type' keyword}}
} catch [invalid_keyword {
cir.yield
- }]
+ }
}
cir.return
}
@@ -22,13 +22,12 @@ module {
cir.func dso_local @invalid_catch_rtti_type() {
cir.scope {
+ // expected-error @below {{'cir.try' op attribute 'catch_types' failed to satisfy constraint: catch all or unwind or global view array attribute}}
cir.try {
cir.yield
- // expected-error @below {{expected attribute value}}
- // expected-error @below {{'cir.try' expected valid RTTI info attribute}}
- } catch [type invalid_type {
+ } catch [type #cir.undef] {
cir.yield
- }]
+ }
}
cir.return
}
@@ -41,11 +40,11 @@ module {
cir.func dso_local @invalid_catch_empty_block() {
cir.scope {
+ // expected-error @below {{'cir.try' op region #1 ('catch_regions') failed to verify constraint: region with at least 1 blocks}}
cir.try {
cir.yield
- } catch [type #cir.all {
- // expected-error @below {{'cir.try' catch region shall not be empty}}
- }]
+ } catch all {
+ }
}
cir.return
}
@@ -65,11 +64,72 @@ cir.func dso_local @invalid_catch_not_terminated() {
cir.yield
}
// expected-error @below {{'cir.try' blocks are expected to be explicitly terminated}}
- catch [type #cir.all {
+ catch all {
%tmp_a = cir.load %a : !cir.ptr<!s32i>, !s32i
- }]
+ }
}
cir.return
}
}
+
+// -----
+
+module {
+
+cir.func dso_local @invalid_catch_multiple_catch_all() {
+ cir.scope {
+ cir.try {
+ cir.yield
+ } catch all {
+ cir.yield
+ }
+ // expected-error @below {{op 'cir.try' can't have more than one catch all}}
+ catch all {
+ cir.yield
+ }
+ }
+ cir.return
+}
+
+}
+
+// -----
+
+module {
+
+cir.func dso_local @invalid_catch_without_type_info() {
+ cir.scope {
+ cir.try {
+ cir.yield
+ }
+ // expected-error @below {{expected attribute value}}
+ // expected-error @below {{op 'cir.try' expected valid RTTI info attribute}}
+ catch [type] {
+ cir.yield
+ }
+ }
+ cir.return
+}
+
+}
+
+// -----
+
+module {
+
+cir.func dso_local @invalid_catch_all_with_type_info() {
+ cir.scope {
+ cir.try {
+ cir.yield
+ }
+ // expected-error @below {{op 'cir.try' catch all dosen't need RTTI info attribute}}
+ catch [all] {
+ cir.yield
+ }
+ }
+ cir.return
+}
+
+}
+
diff --git a/clang/test/CIR/IR/try-catch.cir b/clang/test/CIR/IR/try-catch.cir
index 7bc71ff84d4ae..7becd0b559f5e 100644
--- a/clang/test/CIR/IR/try-catch.cir
+++ b/clang/test/CIR/IR/try-catch.cir
@@ -11,57 +11,57 @@ cir.func dso_local @empty_try_block_with_catch_all() {
cir.scope {
cir.try {
cir.yield
- } catch [type #cir.all {
+ } catch all {
cir.yield
- }]
+ }
}
cir.return
}
-// CHECK: cir.func dso_local @empty_try_block_with_catch_all() {
-// CHECK: cir.scope {
-// CHECK: cir.try {
-// CHECK: cir.yield
-// CHECK: } catch [type #cir.all {
-// CHECK: cir.yield
-// CHECK: }]
-// CHECK: }
-// CHECK: cir.return
-// CHECK: }
+// CHECK: cir.func dso_local @empty_try_block_with_catch_all() {
+// CHECK: cir.scope {
+// CHECK: cir.try {
+// CHECK: cir.yield
+// CHECK: } catch all {
+// CHECK: cir.yield
+// CHECK: }
+// CHECK: }
+// CHECK: cir.return
+// CHECK: }
cir.func dso_local @empty_try_block_with_catch_unwind() {
cir.scope {
cir.try {
cir.yield
- } catch [#cir.unwind {
+ } unwind {
cir.yield
- }]
+ }
}
cir.return
}
// CHECK: cir.func dso_local @empty_try_block_with_catch_unwind() {
-// CHECK: cir.scope {
-// CHECK: cir.try {
-// CHECK: cir.yield
-// CHECK: } catch [#cir.unwind {
-// CHECK: cir.yield
-// CHECK: }]
-// CHECK: }
-// CHECK: cir.return
+// CHECK: cir.scope {
+// CHECK: cir.try {
+// CHECK: cir.yield
+// CHECK: } unwind {
+// CHECK: cir.yield
+// CHECK: }
+// CHECK: }
+// CHECK: cir.return
// CHECK: }
cir.func dso_local @empty_try_block_with_catch_ist() {
cir.scope {
cir.try {
cir.yield
- } catch [type #cir.global_view<@_ZTIi> : !cir.ptr<!u8i> {
+ } catch [type #cir.global_view<@_ZTIi> : !cir.ptr<!u8i>] {
cir.yield
- }, type #cir.global_view<@_ZTIPKc> : !cir.ptr<!u8i> {
+ } catch [type #cir.global_view<@_ZTIPKc> : !cir.ptr<!u8i>] {
cir.yield
- }, #cir.unwind {
+ } unwind {
cir.yield
- }]
+ }
}
cir.return
}
@@ -70,13 +70,13 @@ cir.func dso_local @empty_try_block_with_catch_ist() {
// CHECK: cir.scope {
// CHECK: cir.try {
// CHECK: cir.yield
-// CHECK: } catch [type #cir.global_view<@_ZTIi> : !cir.ptr<!u8i> {
+// CHECK: } catch [type #cir.global_view<@_ZTIi> : !cir.ptr<!u8i>] {
// CHECK: cir.yield
-// CHECK: }, type #cir.global_view<@_ZTIPKc> : !cir.ptr<!u8i> {
+// CHECK: } catch [type #cir.global_view<@_ZTIPKc> : !cir.ptr<!u8i>] {
// CHECK: cir.yield
-// CHECK: }, #cir.unwind {
+// CHECK: } unwind {
// CHECK: cir.yield
-// CHECK: }]
+// CHECK: }
// CHECK: }
// CHECK: cir.return
// CHECK: }
More information about the cfe-commits
mailing list