https://github.com/AmrDeveloper updated https://github.com/llvm/llvm-project/pull/165621
>From 57151797aab47fa203f1867c41c2ff6280e34e3c Mon Sep 17 00:00:00 2001 From: Amr Hesham <[email protected]> Date: Wed, 29 Oct 2025 21:01:11 +0100 Subject: [PATCH 1/2] [CIR] Upstream Exception EhInflight op --- clang/include/clang/CIR/Dialect/IR/CIROps.td | 29 +++++++ .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 82 +++++++++++++++++++ clang/test/CIR/IR/eh-inflight.cir | 15 ++++ 3 files changed, 126 insertions(+) create mode 100644 clang/test/CIR/IR/eh-inflight.cir diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 2124b1dc62a81..4bf897c37a12f 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -4606,6 +4606,35 @@ 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`: pointer to the exception object + This operation is expected to be the first one basic blocks on the + exception path out of `cir.try_call` operations. + + The `cleanup` attribute indicates that clean up code might run before the + values produced by this operation are used to gather exception information. + This helps CIR to pass down more accurate information for LLVM lowering + to landingpads. + }]; + + let arguments = (ins UnitAttr:$cleanup, + OptionalAttr<FlatSymbolRefArrayAttr>:$sym_type_list); + let results = (outs CIR_VoidPtrType:$exception_ptr, CIR_UInt32:$type_id); + let assemblyFormat = [{ + (`cleanup` $cleanup^)? + ($sym_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 d88a4ad76f27b..d8947f0874aaa 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -3065,6 +3065,88 @@ 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 symListAttr = op.getSymTypeListAttr(); + mlir::SmallVector<mlir::Value, 4> symAddrs; + + 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 (symListAttr) { + // catch ptr @_ZTIi + // catch ptr @_ZTIPKc + for (mlir::Attribute attr : symListAttr) { + auto symAttr = cast<mlir::FlatSymbolRefAttr>(attr); + // 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()); + symAddrs.push_back(addrOp); + } + } else if (!op.getCleanup()) { + // catch ptr null + mlir::OpBuilder::InsertionGuard guard(rewriter); + rewriter.setInsertionPointToStart(entryBlock); + mlir::Value nullOp = mlir::LLVM::ZeroOp::create(rewriter, loc, llvmPtrTy); + symAddrs.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, symAddrs); + + 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); + // Get or create `__gxx_personality_v0` + 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..90a5f3f67b6fe --- /dev/null +++ b/clang/test/CIR/IR/eh-inflight.cir @@ -0,0 +1,15 @@ +// RUN: cir-opt %s --verify-roundtrip | FileCheck %s + +module { + +cir.func dso_local @function_with_inflight_exception() { + %exception_ptr, %type_id = cir.eh.inflight_exception + cir.return +} + +// CHECK: cir.func dso_local @function_with_inflight_exception() { +// CHECK: %exception_ptr, %type_id = cir.eh.inflight_exception +// CHECK: cir.return +// CHECK: } + +} >From 94e8d43fd60f1b9f3ed172d6e22b8c3655f09774 Mon Sep 17 00:00:00 2001 From: Amr Hesham <[email protected]> Date: Sun, 16 Nov 2025 14:55:46 +0100 Subject: [PATCH 2/2] Address code review comments --- clang/include/clang/CIR/Dialect/IR/CIROps.td | 23 +++++--- .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 22 ++++---- clang/test/CIR/IR/eh-inflight.cir | 29 +++++++++- clang/test/CIR/Lowering/eh-inflight.cir | 53 +++++++++++++++++++ 4 files changed, 108 insertions(+), 19 deletions(-) create mode 100644 clang/test/CIR/Lowering/eh-inflight.cir diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 4bf897c37a12f..815202399cf18 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -4615,22 +4615,31 @@ def CIR_EhInflightOp : CIR_Op<"eh.inflight_exception"> { let description = [{ `cir.eh.inflight_exception` returns two values: - `exception_ptr`: The exception pointer for the inflight exception - - `type_id`: pointer to the exception object - This operation is expected to be the first one basic blocks on the - exception path out of `cir.try_call` operations. + - `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 might run before the - values produced by this operation are used to gather exception information. + 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>:$sym_type_list); + OptionalAttr<FlatSymbolRefArrayAttr>:$catch_type_list); let results = (outs CIR_VoidPtrType:$exception_ptr, CIR_UInt32:$type_id); let assemblyFormat = [{ (`cleanup` $cleanup^)? - ($sym_type_list^)? + ($catch_type_list^)? attr-dict }]; } diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index d8947f0874aaa..d66cf97c35dbd 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -3082,8 +3082,8 @@ mlir::LogicalResult CIRToLLVMEhInflightOpLowering::matchAndRewrite( mlir::Block *entryBlock = &llvmFn.getRegion().front(); assert(entryBlock->isEntryBlock()); - mlir::ArrayAttr symListAttr = op.getSymTypeListAttr(); - mlir::SmallVector<mlir::Value, 4> symAddrs; + mlir::ArrayAttr catchListAttr = op.getCatchTypeListAttr(); + mlir::SmallVector<mlir::Value> catchSymAddrs; auto llvmPtrTy = mlir::LLVM::LLVMPointerType::get(rewriter.getContext()); mlir::Location loc = op.getLoc(); @@ -3091,25 +3091,27 @@ mlir::LogicalResult CIRToLLVMEhInflightOpLowering::matchAndRewrite( // %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 (symListAttr) { + if (catchListAttr) { // catch ptr @_ZTIi // catch ptr @_ZTIPKc - for (mlir::Attribute attr : symListAttr) { - auto symAttr = cast<mlir::FlatSymbolRefAttr>(attr); + 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()); - symAddrs.push_back(addrOp); + catchSymAddrs.push_back(addrOp); } } else if (!op.getCleanup()) { - // catch ptr null + // 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); - symAddrs.push_back(nullOp); + catchSymAddrs.push_back(nullOp); } // %slot = extractvalue { ptr, i32 } %x, 0 @@ -3117,7 +3119,7 @@ mlir::LogicalResult CIRToLLVMEhInflightOpLowering::matchAndRewrite( mlir::LLVM::LLVMStructType llvmLandingPadStructTy = getLLVMLandingPadStructTy(rewriter); auto landingPadOp = mlir::LLVM::LandingpadOp::create( - rewriter, loc, llvmLandingPadStructTy, symAddrs); + rewriter, loc, llvmLandingPadStructTy, catchSymAddrs); if (op.getCleanup()) landingPadOp.setCleanup(true); @@ -3139,7 +3141,7 @@ mlir::LogicalResult CIRToLLVMEhInflightOpLowering::matchAndRewrite( auto personalityFnTy = mlir::LLVM::LLVMFunctionType::get(rewriter.getI32Type(), {}, /*isVarArg=*/true); - // Get or create `__gxx_personality_v0` + const StringRef fnName = "__gxx_personality_v0"; createLLVMFuncOpIfNotExist(rewriter, op, fnName, personalityFnTy); llvmFn.setPersonality(fnName); diff --git a/clang/test/CIR/IR/eh-inflight.cir b/clang/test/CIR/IR/eh-inflight.cir index 90a5f3f67b6fe..d4d8d5cefb0af 100644 --- a/clang/test/CIR/IR/eh-inflight.cir +++ b/clang/test/CIR/IR/eh-inflight.cir @@ -1,15 +1,40 @@ // RUN: cir-opt %s --verify-roundtrip | FileCheck %s +!u8i = !cir.int<u, 8> + module { -cir.func dso_local @function_with_inflight_exception() { +cir.func dso_local @inflight_exception() { %exception_ptr, %type_id = cir.eh.inflight_exception cir.return } -// CHECK: cir.func dso_local @function_with_inflight_exception() { +// 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: } + + +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
