[clang] 009ec6f - [CIR] Upstream Exception EhInflight op (#165621)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Nov 19 09:28:11 PST 2025
Author: Amr Hesham
Date: 2025-11-19T18:28:07+01:00
New Revision: 009ec6fc64e23ee853485f2b97027a2dc1f040ab
URL: https://github.com/llvm/llvm-project/commit/009ec6fc64e23ee853485f2b97027a2dc1f040ab
DIFF: https://github.com/llvm/llvm-project/commit/009ec6fc64e23ee853485f2b97027a2dc1f040ab.diff
LOG: [CIR] Upstream Exception EhInflight op (#165621)
Upstream Exception EhInflight op as a prerequisite for full catch
handlers implementation
Issue https://github.com/llvm/llvm-project/issues/154992
Added:
clang/test/CIR/IR/eh-inflight.cir
clang/test/CIR/Lowering/eh-inflight.cir
Modified:
clang/include/clang/CIR/Dialect/IR/CIROps.td
clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index e612d6a0ba886..06e0796959c8f 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -4658,6 +4658,44 @@ def CIR_TryOp : CIR_Op<"try",[
let hasLLVMLowering = false;
}
+//===----------------------------------------------------------------------===//
+// Exception related: EhInflightOp
+//===----------------------------------------------------------------------===//
+
+def CIR_EhInflightOp : CIR_Op<"eh.inflight_exception"> {
+ let summary = "Materialize the catch clause formal parameter";
+ let description = [{
+ `cir.eh.inflight_exception` returns two values:
+ - `exception_ptr`: The exception pointer for the inflight exception
+ - `type_id`: the type info index for the exception type
+ This operation is expected to be the first operation in the unwind
+ destination basic blocks of a `cir.try_call` operation.
+
+ The `cleanup` attribute indicates that clean up code must be run before the
+ values produced by this operation are used to dispatch the exception. This
+ cleanup code must be executed even if the exception is not caught.
+ This helps CIR to pass down more accurate information for LLVM lowering
+ to landingpads.
+
+ Example:
+
+ ```mlir
+ %exception_ptr, %type_id = cir.eh.inflight_exception
+ %exception_ptr, %type_id = cir.eh.inflight_exception [@_ZTIi, @_ZTIPKc]
+ %exception_ptr, %type_id = cir.eh.inflight_exception cleanup
+ ``
+ }];
+
+ let arguments = (ins UnitAttr:$cleanup,
+ OptionalAttr<FlatSymbolRefArrayAttr>:$catch_type_list);
+ let results = (outs CIR_VoidPtrType:$exception_ptr, CIR_UInt32:$type_id);
+ let assemblyFormat = [{
+ (`cleanup` $cleanup^)?
+ ($catch_type_list^)?
+ attr-dict
+ }];
+}
+
//===----------------------------------------------------------------------===//
// Atomic operations
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 5a0fdeee5a21a..b35359609521e 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -3104,6 +3104,90 @@ mlir::LogicalResult CIRToLLVMAllocExceptionOpLowering::matchAndRewrite(
return mlir::success();
}
+static mlir::LLVM::LLVMStructType
+getLLVMLandingPadStructTy(mlir::ConversionPatternRewriter &rewriter) {
+ // Create the landing pad type: struct { ptr, i32 }
+ mlir::MLIRContext *ctx = rewriter.getContext();
+ auto llvmPtr = mlir::LLVM::LLVMPointerType::get(ctx);
+ llvm::SmallVector<mlir::Type> structFields = {llvmPtr, rewriter.getI32Type()};
+ return mlir::LLVM::LLVMStructType::getLiteral(ctx, structFields);
+}
+
+mlir::LogicalResult CIRToLLVMEhInflightOpLowering::matchAndRewrite(
+ cir::EhInflightOp op, OpAdaptor adaptor,
+ mlir::ConversionPatternRewriter &rewriter) const {
+ auto llvmFn = op->getParentOfType<mlir::LLVM::LLVMFuncOp>();
+ assert(llvmFn && "expected LLVM function parent");
+ mlir::Block *entryBlock = &llvmFn.getRegion().front();
+ assert(entryBlock->isEntryBlock());
+
+ mlir::ArrayAttr catchListAttr = op.getCatchTypeListAttr();
+ mlir::SmallVector<mlir::Value> catchSymAddrs;
+
+ auto llvmPtrTy = mlir::LLVM::LLVMPointerType::get(rewriter.getContext());
+ mlir::Location loc = op.getLoc();
+
+ // %landingpad = landingpad { ptr, i32 }
+ // Note that since llvm.landingpad has to be the first operation on the
+ // block, any needed value for its operands has to be added somewhere else.
+ if (catchListAttr) {
+ // catch ptr @_ZTIi
+ // catch ptr @_ZTIPKc
+ for (mlir::Attribute catchAttr : catchListAttr) {
+ auto symAttr = cast<mlir::FlatSymbolRefAttr>(catchAttr);
+ // Generate `llvm.mlir.addressof` for each symbol, and place those
+ // operations in the LLVM function entry basic block.
+ mlir::OpBuilder::InsertionGuard guard(rewriter);
+ rewriter.setInsertionPointToStart(entryBlock);
+ mlir::Value addrOp = mlir::LLVM::AddressOfOp::create(
+ rewriter, loc, llvmPtrTy, symAttr.getValue());
+ catchSymAddrs.push_back(addrOp);
+ }
+ } else if (!op.getCleanup()) {
+ // We need to emit catch-all only if cleanup is not set, because when we
+ // have catch-all handler, there is no case when we set would unwind past
+ // the handler
+ mlir::OpBuilder::InsertionGuard guard(rewriter);
+ rewriter.setInsertionPointToStart(entryBlock);
+ mlir::Value nullOp = mlir::LLVM::ZeroOp::create(rewriter, loc, llvmPtrTy);
+ catchSymAddrs.push_back(nullOp);
+ }
+
+ // %slot = extractvalue { ptr, i32 } %x, 0
+ // %selector = extractvalue { ptr, i32 } %x, 1
+ mlir::LLVM::LLVMStructType llvmLandingPadStructTy =
+ getLLVMLandingPadStructTy(rewriter);
+ auto landingPadOp = mlir::LLVM::LandingpadOp::create(
+ rewriter, loc, llvmLandingPadStructTy, catchSymAddrs);
+
+ if (op.getCleanup())
+ landingPadOp.setCleanup(true);
+
+ mlir::Value slot =
+ mlir::LLVM::ExtractValueOp::create(rewriter, loc, landingPadOp, 0);
+ mlir::Value selector =
+ mlir::LLVM::ExtractValueOp::create(rewriter, loc, landingPadOp, 1);
+ rewriter.replaceOp(op, mlir::ValueRange{slot, selector});
+
+ // Landing pads are required to be in LLVM functions with personality
+ // attribute.
+ // TODO(cir): for now hardcode personality creation in order to start
+ // adding exception tests, once we annotate CIR with such information,
+ // change it to be in FuncOp lowering instead.
+ mlir::OpBuilder::InsertionGuard guard(rewriter);
+ // Insert personality decl before the current function.
+ rewriter.setInsertionPoint(llvmFn);
+ auto personalityFnTy =
+ mlir::LLVM::LLVMFunctionType::get(rewriter.getI32Type(), {},
+ /*isVarArg=*/true);
+
+ const StringRef fnName = "__gxx_personality_v0";
+ createLLVMFuncOpIfNotExist(rewriter, op, fnName, personalityFnTy);
+ llvmFn.setPersonality(fnName);
+
+ return mlir::success();
+}
+
mlir::LogicalResult CIRToLLVMTrapOpLowering::matchAndRewrite(
cir::TrapOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
diff --git a/clang/test/CIR/IR/eh-inflight.cir b/clang/test/CIR/IR/eh-inflight.cir
new file mode 100644
index 0000000000000..d4d8d5cefb0af
--- /dev/null
+++ b/clang/test/CIR/IR/eh-inflight.cir
@@ -0,0 +1,40 @@
+// RUN: cir-opt %s --verify-roundtrip | FileCheck %s
+
+!u8i = !cir.int<u, 8>
+
+module {
+
+cir.func dso_local @inflight_exception() {
+ %exception_ptr, %type_id = cir.eh.inflight_exception
+ cir.return
+}
+
+// CHECK: cir.func dso_local @inflight_exception() {
+// CHECK: %exception_ptr, %type_id = cir.eh.inflight_exception
+// CHECK: cir.return
+// CHECK: }
+
+cir.func dso_local @inflight_exception_with_cleanup() {
+ %exception_ptr, %type_id = cir.eh.inflight_exception cleanup
+ cir.return
+}
+
+// CHECK: cir.func dso_local @inflight_exception_with_cleanup() {
+// CHECK: %exception_ptr, %type_id = cir.eh.inflight_exception cleanup
+// CHECK: cir.return
+// CHECK: }
+
+cir.global "private" constant external @_ZTIi : !cir.ptr<!u8i>
+cir.global "private" constant external @_ZTIPKc : !cir.ptr<!u8i>
+
+cir.func dso_local @inflight_exception_with_catch_type_list() {
+ %exception_ptr, %type_id = cir.eh.inflight_exception [@_ZTIi, @_ZTIPKc]
+ cir.return
+}
+
+// CHECK: cir.func dso_local @inflight_exception_with_catch_type_list() {
+// CHECK: %exception_ptr, %type_id = cir.eh.inflight_exception [@_ZTIi, @_ZTIPKc]
+// CHECK: cir.return
+// CHECK:}
+
+}
diff --git a/clang/test/CIR/Lowering/eh-inflight.cir b/clang/test/CIR/Lowering/eh-inflight.cir
new file mode 100644
index 0000000000000..31e1e474a046b
--- /dev/null
+++ b/clang/test/CIR/Lowering/eh-inflight.cir
@@ -0,0 +1,53 @@
+// RUN: cir-opt %s -cir-to-llvm -o %t.cir
+
+!u8i = !cir.int<u, 8>
+
+module {
+
+// CHECK: llvm.func @__gxx_personality_v0(...) -> i32
+
+cir.func @inflight_exception() {
+ %exception_ptr, %type_id = cir.eh.inflight_exception
+ cir.return
+}
+
+// CHECK: llvm.func @inflight_exception() attributes {personality = @__gxx_personality_v0} {
+// CHECK: %[[CONST_0:.*]] = llvm.mlir.zero : !llvm.ptr
+// CHECK: %[[LP:.*]] = llvm.landingpad (catch %[[CONST_0]] : !llvm.ptr) : !llvm.struct<(ptr, i32)>
+// CHECK: %[[EXCEPTION_PTR:.*]] = llvm.extractvalue %[[LP]][0] : !llvm.struct<(ptr, i32)>
+// CHECK: %[[TYPE_ID:.*]] = llvm.extractvalue %[[LP]][1] : !llvm.struct<(ptr, i32)>
+// CHECK: llvm.return
+// CHECK: }
+
+cir.func @inflight_exception_with_cleanup() {
+ %exception_ptr, %type_id = cir.eh.inflight_exception cleanup
+ cir.return
+}
+
+// CHECK: llvm.func @inflight_exception_with_cleanup() attributes {personality = @__gxx_personality_v0} {
+// CHECK: %[[LP:.*]] = llvm.landingpad cleanup : !llvm.struct<(ptr, i32)>
+// CHECK: %[[EXCEPTION_PTR:.*]] = llvm.extractvalue %[[LP]][0] : !llvm.struct<(ptr, i32)>
+// CHECK: %[[TYPE_ID:.*]] = llvm.extractvalue %[[LP]][1] : !llvm.struct<(ptr, i32)>
+// CHECK: llvm.return
+// CHECK: }
+
+
+cir.global "private" constant external @_ZTIi : !cir.ptr<!u8i>
+cir.global "private" constant external @_ZTIPKc : !cir.ptr<!u8i>
+
+cir.func @inflight_exception_with_catch_type_list() {
+ %exception_ptr, %type_id = cir.eh.inflight_exception [@_ZTIi, @_ZTIPKc]
+ cir.return
+}
+
+// CHECK: llvm.func @inflight_exception_with_catch_type_list() attributes {personality = @__gxx_personality_v0} {
+// CHECK: %[[TI_1_ADDR:.*]] = llvm.mlir.addressof @_ZTIPKc : !llvm.ptr
+// CHECK: %[[TI_2_ADDR:.*]] = llvm.mlir.addressof @_ZTIi : !llvm.ptr
+// CHECK: %[[LP:.*]] = llvm.landingpad (catch %[[TI_2_ADDR]] : !llvm.ptr) (catch %[[TI_1_ADDR]] : !llvm.ptr) : !llvm.struct<(ptr, i32)>
+// CHECK: %[[EXCEPTION_PTR:.*]] = llvm.extractvalue %[[LP]][0] : !llvm.struct<(ptr, i32)>
+// CHECK: %[[TYPE_ID:.*]] = llvm.extractvalue %[[LP]][1] : !llvm.struct<(ptr, i32)>
+// CHECK: llvm.return
+// CHECK: }
+
+
+}
More information about the cfe-commits
mailing list