https://github.com/andykaylor updated https://github.com/llvm/llvm-project/pull/223862
>From a105a00821ab3b2bb4c88ab00c45c484928a6744 Mon Sep 17 00:00:00 2001 From: Andy Kaylor <[email protected]> Date: Tue, 15 Sep 2026 13:11:08 -0700 Subject: [PATCH 1/2] [CIR][EH] Implement EH ABI lowering for dynamic exception specification This change implements EH ABI lowering for Itanium targets for the CIR constructs used to represent dynamic exception specifications. This also includes minor changes to lower new forms of the cir.eh.inflight_exception operation to the LLVM dialect. Code generation support for dynamic exception specification and tests for complete lowering of generated CIR to LLVM IR will be added in a follow-up change. Assisted-by: Cursor / various models --- clang/include/clang/CIR/Dialect/IR/CIROps.td | 2 +- .../CIR/Dialect/Transforms/EHABILowering.cpp | 163 ++++++++++++------ .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 39 ++++- clang/test/CIR/Lowering/eh-inflight.cir | 34 +++- .../eh-abi-lowering-eh-spec-nyi.cir | 81 --------- .../Transforms/eh-abi-lowering-eh-spec.cir | 156 +++++++++++++++++ 6 files changed, 330 insertions(+), 145 deletions(-) delete mode 100644 clang/test/CIR/Transforms/eh-abi-lowering-eh-spec-nyi.cir create mode 100644 clang/test/CIR/Transforms/eh-abi-lowering-eh-spec.cir diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index c5a836a0c2f03..244eba4c18801 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -8421,7 +8421,7 @@ def CIR_EhInflightOp : CIR_Op<"eh.inflight_exception"> { %exception_ptr, %type_id = cir.eh.inflight_exception catch_all [@_ZTIi] %exception_ptr, %type_id = cir.eh.inflight_exception filter [@_ZTIi] %exception_ptr, %type_id = cir.eh.inflight_exception [@_ZTIi] filter [] - `` + ``` }]; let arguments = (ins UnitAttr:$cleanup, diff --git a/clang/lib/CIR/Dialect/Transforms/EHABILowering.cpp b/clang/lib/CIR/Dialect/Transforms/EHABILowering.cpp index e545b8021daa1..f3971403a2ef3 100644 --- a/clang/lib/CIR/Dialect/Transforms/EHABILowering.cpp +++ b/clang/lib/CIR/Dialect/Transforms/EHABILowering.cpp @@ -18,6 +18,7 @@ // - cir.begin_catch → call to __cxa_begin_catch // - cir.end_catch → call to __cxa_end_catch // - cir.eh.terminate → call to __clang_call_terminate + unreachable +// - cir.eh.unexpected → call to __cxa_call_unexpected + unreachable // - cir.resume → cir.resume.flat // - !cir.eh_token values → (!cir.ptr<!void>, !u32i) value pairs // - cir.construct_catch_param → __cxa_get_exception_ptr + inlined @@ -118,6 +119,7 @@ class ItaniumEHLowering : public EHABILowering { cir::PointerType voidPtrType; cir::PointerType u8PtrType; cir::IntType u32Type; + cir::IntType s32Type; // Cached runtime function declarations, initialized when needed by // ensureRuntimeDecls(). @@ -128,6 +130,7 @@ class ItaniumEHLowering : public EHABILowering { cir::FuncOp clangCallTerminateFunc; cir::FuncOp cxaThrowFunc; cir::FuncOp cxaRethrowFunc; + cir::FuncOp cxaCallUnexpectedFunc; DenseMap<mlir::StringAttr, cir::FuncOp> catchCopyThunks; @@ -138,6 +141,7 @@ class ItaniumEHLowering : public EHABILowering { void ensureClangCallTerminate(mlir::Location loc); void ensureCxaThrowDecl(mlir::Location loc); void ensureCxaRethrowDecl(mlir::Location loc); + void ensureCxaCallUnexpectedDecl(mlir::Location loc); mlir::Block *buildTerminateBlock(cir::FuncOp funcOp, mlir::Location loc); mlir::FailureOr<cir::FuncOp> resolveCatchCopyThunk(cir::ConstructCatchParamOp op); @@ -163,6 +167,7 @@ mlir::LogicalResult ItaniumEHLowering::run() { auto u8Type = cir::IntType::get(ctx, 8, /*isSigned=*/false); u8PtrType = cir::PointerType::get(u8Type); u32Type = cir::IntType::get(ctx, 32, /*isSigned=*/false); + s32Type = cir::IntType::get(ctx, 32, /*isSigned=*/true); for (cir::FuncOp funcOp : mod.getOps<cir::FuncOp>()) { if (mlir::failed(lowerFunc(funcOp))) @@ -177,7 +182,6 @@ void ItaniumEHLowering::ensureRuntimeDecls(mlir::Location loc) { // TODO(cir): Handle other personality functions. This probably isn't needed // here if we fix codegen to always set the personality function. if (!personalityFunc) { - auto s32Type = cir::IntType::get(ctx, 32, /*isSigned=*/true); auto personalityFuncTy = cir::FuncType::get({}, s32Type, /*isVarArg=*/true); personalityFunc = getOrCreateRuntimeFuncDecl(mod, loc, kGxxPersonality, personalityFuncTy); @@ -282,6 +286,16 @@ void ItaniumEHLowering::ensureCxaRethrowDecl(mlir::Location loc) { getOrCreateRuntimeFuncDecl(mod, loc, "__cxa_rethrow", rethrowFuncTy); } +/// void __cxa_call_unexpected(void *exn); +void ItaniumEHLowering::ensureCxaCallUnexpectedDecl(mlir::Location loc) { + if (cxaCallUnexpectedFunc) + return; + auto unexpectedFuncTy = + cir::FuncType::get({voidPtrType}, voidType, /*isVarArg=*/false); + cxaCallUnexpectedFunc = getOrCreateRuntimeFuncDecl( + mod, loc, "__cxa_call_unexpected", unexpectedFuncTy); +} + /// Create a terminate landing pad block at the end of the specified function. mlir::Block *ItaniumEHLowering::buildTerminateBlock(cir::FuncOp funcOp, mlir::Location loc) { @@ -489,20 +503,33 @@ mlir::LogicalResult ItaniumEHLowering::lowerEhInitiate( // destructive token-graph traversal below -- keeps it correct regardless of // the order in which sibling/nested initiates are lowered. mlir::ArrayAttr catchTypeList; + mlir::ArrayAttr filterTypeList; bool catchAll = false; SmallVector<mlir::Attribute> typeSymbols; for (cir::EhDispatchOp dispatch : reachedDispatches) { - if (mlir::ArrayAttr catchTypes = dispatch.getCatchTypesAttr()) - for (mlir::Attribute attr : catchTypes) + if (mlir::ArrayAttr catchTypes = dispatch.getCatchTypesAttr()) { + for (mlir::Attribute attr : catchTypes) { + if (auto filter = mlir::dyn_cast<cir::EhFilterAttr>(attr)) { + SmallVector<mlir::Attribute> filterSymbols; + // A filter terminates the landing-pad clause list the same way a + // catch-all does. Nothing outside the specification is reachable. + for (mlir::Attribute typeAttr : filter.getPermittedTypes()) { + auto globalView = mlir::cast<cir::GlobalViewAttr>(typeAttr); + filterSymbols.push_back(globalView.getSymbol()); + } + filterTypeList = builder.getArrayAttr(filterSymbols); + continue; + } typeSymbols.push_back( mlir::cast<cir::GlobalViewAttr>(attr).getSymbol()); - if (dispatch.getDefaultIsCatchAll()) { - catchAll = true; - // A catch-all handles every exception, so it stops the unwind: no - // enclosing dispatch is reachable past it, and the collector therefore - // never records one after it. Drop the rest of the clauses here. + } + } + if (dispatch.getDefaultIsCatchAll() || filterTypeList) { + catchAll = dispatch.getDefaultIsCatchAll(); + // A catch-all or filter handles the remaining exceptions, so it + // stops the unwind. No enclosing dispatch is reachable past it. assert(dispatch == reachedDispatches.back() && - "catch-all must be the last reachable dispatch"); + "catch-all or filter must be the last reachable dispatch"); break; } } @@ -512,8 +539,7 @@ mlir::LogicalResult ItaniumEHLowering::lowerEhInitiate( builder.setInsertionPoint(initiateOp); auto inflightOp = cir::EhInflightOp::create( builder, initiateOp.getLoc(), initiateOp.getCleanup() || reachesCleanup, - catchAll, catchTypeList, - /*filter_type_list=*/mlir::ArrayAttr{}); + catchAll, catchTypeList, filterTypeList); ehTokenMap[rootToken] = {inflightOp.getExceptionPtr(), inflightOp.getTypeId()}; @@ -620,6 +646,18 @@ mlir::LogicalResult ItaniumEHLowering::lowerEhInitiate( builder.getUnitAttr()); cir::UnreachableOp::create(builder, op.getLoc()); op.erase(); + } else if (auto op = mlir::dyn_cast<cir::EhUnexpectedOp>(user)) { + auto [exnPtr, typeId] = ehTokenMap.lookup(op.getEhToken()); + ensureCxaCallUnexpectedDecl(op.getLoc()); + builder.setInsertionPoint(op); + auto call = cir::CallOp::create( + builder, op.getLoc(), + mlir::FlatSymbolRefAttr::get(cxaCallUnexpectedFunc), voidType, + mlir::ValueRange{exnPtr}); + call->setAttr(cir::CIRDialect::getNoReturnAttrName(), + builder.getUnitAttr()); + cir::UnreachableOp::create(builder, op.getLoc()); + op.erase(); } else if (auto op = mlir::dyn_cast<cir::ResumeOp>(user)) { auto [exnPtr, typeId] = ehTokenMap.lookup(op.getEhToken()); builder.setInsertionPoint(op); @@ -659,28 +697,62 @@ void ItaniumEHLowering::lowerDispatch(cir::EhDispatchOp dispatch, mlir::Value exnPtr, mlir::Value typeId) { mlir::Location dispLoc = dispatch.getLoc(); mlir::Block *defaultDest = dispatch.getDefaultDestination(); - mlir::ArrayAttr catchTypes = dispatch.getCatchTypesAttr(); - mlir::SuccessorRange catchDests = dispatch.getCatchDestinations(); mlir::Block *dispatchBlock = dispatch->getBlock(); + llvm::SmallVector<mlir::Attribute> catchAttrs; + llvm::SmallVector<mlir::Block *> catchDests; + cir::EhFilterAttr filterAttr; + mlir::Block *filterDest = nullptr; + if (mlir::ArrayAttr catchTypes = dispatch.getCatchTypesAttr()) { + for (auto [attr, dest] : + llvm::zip(catchTypes, dispatch.getCatchDestinations())) { + if (auto filter = mlir::dyn_cast<cir::EhFilterAttr>(attr)) { + assert(!filterAttr && "at most one filter handler"); + filterAttr = filter; + filterDest = dest; + continue; + } + catchAttrs.push_back(attr); + catchDests.push_back(dest); + } + } + // Build the comparison chain in new blocks inserted after the dispatch's // block. The dispatch itself is replaced with a branch to the first // comparison block and erased below. - if (!catchTypes || catchTypes.empty()) { - // No typed catches: replace dispatch with a direct branch. - builder.setInsertionPoint(dispatch); - cir::BrOp::create(builder, dispLoc, defaultDest, - mlir::ValueRange{exnPtr, typeId}); - } else { - unsigned numCatches = catchTypes.size(); - - // Create and populate comparison blocks in reverse order so that each - // block's false destination (the next comparison block, or defaultDest - // for the last one) is already available. Each createBlock inserts - // before the previous one, so the blocks end up in forward order. - mlir::Block *insertBefore = dispatchBlock->getNextNode(); - mlir::Block *falseDest = defaultDest; - mlir::Block *firstCmpBlock = nullptr; + mlir::Block *insertBefore = dispatchBlock->getNextNode(); + mlir::Block *falseDest = defaultDest; + if (filterAttr) { + assert(filterDest && "filter handler requires a destination"); + // An empty permitted-type list means that every exception violates the + // specification, so the filter destination is taken unconditionally. + if (filterAttr.getPermittedTypes().empty()) { + falseDest = filterDest; + } else { + // The personality reports a filter failure with a negative selector. + // Type ids are still !u32i on the eh_token replacement pair, so recast + // to !s32i for the signed comparison. TODO: produce !s32i throughout. + auto *cmpBlock = builder.createBlock( + insertBefore, {voidPtrType, u32Type}, {dispLoc, dispLoc}); + mlir::Value cmpExnPtr = cmpBlock->getArgument(0); + mlir::Value cmpTypeId = cmpBlock->getArgument(1); + mlir::Value signedTypeId = cir::CastOp::create( + builder, dispLoc, s32Type, cir::CastKind::integral, cmpTypeId); + mlir::Value zero = cir::ConstantOp::create( + builder, dispLoc, cir::IntAttr::get(s32Type, 0)); + auto cmpOp = cir::CmpOp::create(builder, dispLoc, cir::CmpOpKind::lt, + signedTypeId, zero); + cir::BrCondOp::create(builder, dispLoc, cmpOp, filterDest, defaultDest, + mlir::ValueRange{cmpExnPtr, cmpTypeId}, + mlir::ValueRange{cmpExnPtr, cmpTypeId}); + insertBefore = cmpBlock; + falseDest = cmpBlock; + } + } + + mlir::Block *firstCmpBlock = nullptr; + if (!catchAttrs.empty()) { + unsigned numCatches = catchAttrs.size(); for (int i = numCatches - 1; i >= 0; --i) { auto *cmpBlock = builder.createBlock(insertBefore, {voidPtrType, u32Type}, {dispLoc, dispLoc}); @@ -688,7 +760,7 @@ void ItaniumEHLowering::lowerDispatch(cir::EhDispatchOp dispatch, mlir::Value cmpExnPtr = cmpBlock->getArgument(0); mlir::Value cmpTypeId = cmpBlock->getArgument(1); - auto globalView = mlir::cast<cir::GlobalViewAttr>(catchTypes[i]); + auto globalView = mlir::cast<cir::GlobalViewAttr>(catchAttrs[i]); auto ehTypeIdOp = cir::EhTypeIdOp::create(builder, dispLoc, globalView.getSymbol()); auto cmpOp = cir::CmpOp::create(builder, dispLoc, cir::CmpOpKind::eq, @@ -702,13 +774,14 @@ void ItaniumEHLowering::lowerDispatch(cir::EhDispatchOp dispatch, falseDest = cmpBlock; firstCmpBlock = cmpBlock; } - - // Replace the dispatch with a branch to the first comparison block. - builder.setInsertionPoint(dispatch); - cir::BrOp::create(builder, dispLoc, firstCmpBlock, - mlir::ValueRange{exnPtr, typeId}); + } else { + firstCmpBlock = falseDest; } + builder.setInsertionPoint(dispatch); + cir::BrOp::create(builder, dispLoc, firstCmpBlock, + mlir::ValueRange{exnPtr, typeId}); + // The caller lowers each dispatch exactly once after every initiate has been // processed, so no sibling still needs it; erase it now. dispatch.erase(); @@ -1035,28 +1108,6 @@ void CIREHABILoweringPass::runOnOperation() { lowering = std::make_unique<ItaniumEHLowering>(mod); } - // Dynamic exception specifications are not lowered yet. Diagnose them before - // the lowering runs, which would otherwise treat a filter handler as a typed - // catch handler. - if (mod.walk([&](mlir::Operation *op) { - if (auto dispatch = mlir::dyn_cast<cir::EhDispatchOp>(op)) { - mlir::ArrayAttr handlerTypes = dispatch.getCatchTypesAttr(); - if (!handlerTypes || - llvm::none_of(handlerTypes, [](mlir::Attribute typeAttr) { - return mlir::isa<cir::EhFilterAttr>(typeAttr); - })) - return mlir::WalkResult::advance(); - dispatch.emitError("NYI: EH ABI lowering of a 'filter' handler"); - return mlir::WalkResult::interrupt(); - } else if (mlir::isa<cir::EhUnexpectedOp>(op)) { - op->emitError("NYI: EH ABI lowering of 'cir.eh.unexpected'"); - return mlir::WalkResult::interrupt(); - } - return mlir::WalkResult::advance(); - }) - .wasInterrupted()) - return signalPassFailure(); - if (mlir::failed(lowering->run())) return signalPassFailure(); diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index 24ee700e87ece..3fd22b5e42225 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -4651,7 +4651,8 @@ mlir::LogicalResult CIRToLLVMEhInflightOpLowering::matchAndRewrite( assert(entryBlock->isEntryBlock()); mlir::ArrayAttr catchListAttr = op.getCatchTypeListAttr(); - mlir::SmallVector<mlir::Value> catchSymAddrs; + mlir::ArrayAttr filterListAttr = op.getFilterTypeListAttr(); + mlir::SmallVector<mlir::Value> landingPadClauses; auto llvmPtrTy = mlir::LLVM::LLVMPointerType::get(rewriter.getContext()); mlir::Location loc = op.getLoc(); @@ -4670,18 +4671,44 @@ mlir::LogicalResult CIRToLLVMEhInflightOpLowering::matchAndRewrite( rewriter.setInsertionPointToStart(entryBlock); mlir::Value addrOp = mlir::LLVM::AddressOfOp::create( rewriter, loc, llvmPtrTy, symAttr.getValue()); - catchSymAddrs.push_back(addrOp); + landingPadClauses.push_back(addrOp); } } // Emit a catch-all clause (catch ptr null) when: // - The catch_all attribute is set (typed catches + catch-all), or - // - No typed catches and no cleanup (legacy pure catch-all form) - if (op.getCatchAll() || (!catchListAttr && !op.getCleanup())) { + // - No typed catches, no cleanup, and no filter (legacy pure catch-all) + if (op.getCatchAll() || + (!catchListAttr && !op.getCleanup() && !filterListAttr)) { mlir::OpBuilder::InsertionGuard guard(rewriter); rewriter.setInsertionPointToStart(entryBlock); mlir::Value nullOp = mlir::LLVM::ZeroOp::create(rewriter, loc, llvmPtrTy); - catchSymAddrs.push_back(nullOp); + landingPadClauses.push_back(nullOp); + } + + if (filterListAttr) { + // Filter clauses are array-typed landingpad operands: + // filter [N x ptr] [ptr @_ZTIi, ...] + // An empty list is throw() and lowers to [0 x ptr] zeroinitializer. + mlir::OpBuilder::InsertionGuard guard(rewriter); + rewriter.setInsertionPointToStart(entryBlock); + auto filterArrayTy = mlir::LLVM::LLVMArrayType::get( + llvmPtrTy, filterListAttr.size()); + mlir::Value filterVal; + if (filterListAttr.empty()) { + filterVal = mlir::LLVM::ZeroOp::create(rewriter, loc, filterArrayTy); + } else { + filterVal = mlir::LLVM::UndefOp::create(rewriter, loc, filterArrayTy); + for (auto [i, filterAttr] : llvm::enumerate(filterListAttr)) { + auto symAttr = mlir::cast<mlir::FlatSymbolRefAttr>(filterAttr); + mlir::Value addrOp = mlir::LLVM::AddressOfOp::create( + rewriter, loc, llvmPtrTy, symAttr.getValue()); + filterVal = mlir::LLVM::InsertValueOp::create( + rewriter, loc, filterVal, addrOp, + llvm::ArrayRef<int64_t>{static_cast<int64_t>(i)}); + } + } + landingPadClauses.push_back(filterVal); } // %slot = extractvalue { ptr, i32 } %x, 0 @@ -4689,7 +4716,7 @@ mlir::LogicalResult CIRToLLVMEhInflightOpLowering::matchAndRewrite( mlir::LLVM::LLVMStructType llvmLandingPadStructTy = getLLVMLandingPadStructTy(rewriter); auto landingPadOp = mlir::LLVM::LandingpadOp::create( - rewriter, loc, llvmLandingPadStructTy, catchSymAddrs); + rewriter, loc, llvmLandingPadStructTy, landingPadClauses); // The LLVM cleanup flag is only needed when there is no catch-all handler, // since catch-all (catch ptr null) already ensures the personality function diff --git a/clang/test/CIR/Lowering/eh-inflight.cir b/clang/test/CIR/Lowering/eh-inflight.cir index e65cc30e8cff3..eca931893c8cb 100644 --- a/clang/test/CIR/Lowering/eh-inflight.cir +++ b/clang/test/CIR/Lowering/eh-inflight.cir @@ -1,9 +1,10 @@ // RUN: cir-opt %s -cir-to-llvm -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s !s32i = !cir.int<s, 32> !u8i = !cir.int<u, 8> -module { +module attributes {cir.triple = "x86_64-unknown-linux-gnu"} { cir.func private @__gxx_personality_v0(...) -> !s32i @@ -37,6 +38,7 @@ cir.func @inflight_exception_with_cleanup() personality(@__gxx_personality_v0) { cir.global "private" constant external @_ZTIi : !cir.ptr<!u8i> cir.global "private" constant external @_ZTIPKc : !cir.ptr<!u8i> +cir.global "private" constant external @_ZTIl : !cir.ptr<!u8i> cir.func @inflight_exception_with_catch_type_list() personality(@__gxx_personality_v0) { %exception_ptr, %type_id = cir.eh.inflight_exception [@_ZTIi, @_ZTIPKc] @@ -79,5 +81,35 @@ cir.func @inflight_exception_catch_all_only() personality(@__gxx_personality_v0) // CHECK: llvm.return // CHECK: } +cir.func @inflight_exception_filter() personality(@__gxx_personality_v0) { + %exception_ptr, %type_id = cir.eh.inflight_exception filter [@_ZTIi] + cir.return +} + +// CHECK-LABEL: llvm.func @inflight_exception_filter() +// CHECK: %[[UNDEF:.*]] = llvm.mlir.undef : !llvm.array<1 x ptr> +// CHECK: %[[TI:.*]] = llvm.mlir.addressof @_ZTIi : !llvm.ptr +// CHECK: %[[FILTER:.*]] = llvm.insertvalue %[[TI]], %[[UNDEF]][0] : !llvm.array<1 x ptr> +// CHECK: %[[LP:.*]] = llvm.landingpad (filter %[[FILTER]] : !llvm.array<1 x ptr>) : !llvm.struct<(ptr, i32)> + +cir.func @inflight_exception_empty_filter() personality(@__gxx_personality_v0) { + %exception_ptr, %type_id = cir.eh.inflight_exception filter [] + cir.return +} + +// CHECK-LABEL: llvm.func @inflight_exception_empty_filter() +// CHECK: %[[ZERO:.*]] = llvm.mlir.zero : !llvm.array<0 x ptr> +// CHECK: %[[LP:.*]] = llvm.landingpad (filter %[[ZERO]] : !llvm.array<0 x ptr>) : !llvm.struct<(ptr, i32)> + +cir.func @inflight_exception_catch_and_filter() personality(@__gxx_personality_v0) { + %exception_ptr, %type_id = + cir.eh.inflight_exception cleanup [@_ZTIl] filter [] + cir.return +} + +// CHECK-LABEL: llvm.func @inflight_exception_catch_and_filter() +// CHECK: %[[ZERO:.*]] = llvm.mlir.zero : !llvm.array<0 x ptr> +// CHECK: %[[TI:.*]] = llvm.mlir.addressof @_ZTIl : !llvm.ptr +// CHECK: %[[LP:.*]] = llvm.landingpad cleanup (catch %[[TI]] : !llvm.ptr) (filter %[[ZERO]] : !llvm.array<0 x ptr>) : !llvm.struct<(ptr, i32)> } diff --git a/clang/test/CIR/Transforms/eh-abi-lowering-eh-spec-nyi.cir b/clang/test/CIR/Transforms/eh-abi-lowering-eh-spec-nyi.cir deleted file mode 100644 index 670559c419477..0000000000000 --- a/clang/test/CIR/Transforms/eh-abi-lowering-eh-spec-nyi.cir +++ /dev/null @@ -1,81 +0,0 @@ -// RUN: cir-opt %s -cir-eh-abi-lowering -split-input-file -verify-diagnostics - -!u8i = !cir.int<u, 8> - -module attributes {cir.triple = "x86_64-unknown-linux-gnu"} { - -cir.func private @_Z8externalv() - -cir.func @dispatch_with_filter() { - cir.try_call @_Z8externalv() ^bb1, ^bb2 : () -> () -^bb1: - cir.br ^bb5 -^bb2: - %0 = cir.eh.initiate : !cir.eh_token - cir.br ^bb3(%0 : !cir.eh_token) -^bb3(%eh_token: !cir.eh_token): - // expected-error@+1 {{NYI: EH ABI lowering of a 'filter' handler}} - cir.eh.dispatch %eh_token : !cir.eh_token [ - filter(#cir.global_view<@_ZTIi> : !cir.ptr<!u8i>) : ^bb4, - unwind : ^bb6 - ] -^bb4(%eh_token.1: !cir.eh_token): - cir.eh.unexpected %eh_token.1 : !cir.eh_token -^bb5: - cir.return -^bb6(%eh_token.2: !cir.eh_token): - cir.resume %eh_token.2 : !cir.eh_token -} - -} - -// ----- - -module attributes {cir.triple = "x86_64-unknown-linux-gnu"} { - -cir.func private @_Z8externalv() - -// An empty filter list still needs lowering support. -cir.func @dispatch_with_empty_filter() { - cir.try_call @_Z8externalv() ^bb1, ^bb2 : () -> () -^bb1: - cir.br ^bb5 -^bb2: - %0 = cir.eh.initiate : !cir.eh_token - cir.br ^bb3(%0 : !cir.eh_token) -^bb3(%eh_token: !cir.eh_token): - // expected-error@+1 {{NYI: EH ABI lowering of a 'filter' handler}} - cir.eh.dispatch %eh_token : !cir.eh_token [ - filter() : ^bb4, - unwind : ^bb6 - ] -^bb4(%eh_token.1: !cir.eh_token): - cir.eh.unexpected %eh_token.1 : !cir.eh_token -^bb5: - cir.return -^bb6(%eh_token.2: !cir.eh_token): - cir.unreachable -} - -} - -// ----- - -module attributes {cir.triple = "x86_64-unknown-linux-gnu"} { - -cir.func private @_Z8externalv() - -// The dispatch of a dynamic exception specification is diagnosed before its -// 'cir.eh.unexpected' is reached, so the diagnostic for the operation itself -// needs a function that has no 'filter' handler. -cir.func @unexpected_without_dispatch() { - cir.try_call @_Z8externalv() ^bb1, ^bb2 : () -> () -^bb1: - cir.return -^bb2: - %0 = cir.eh.initiate : !cir.eh_token - // expected-error@+1 {{NYI: EH ABI lowering of 'cir.eh.unexpected'}} - cir.eh.unexpected %0 : !cir.eh_token -} - -} diff --git a/clang/test/CIR/Transforms/eh-abi-lowering-eh-spec.cir b/clang/test/CIR/Transforms/eh-abi-lowering-eh-spec.cir new file mode 100644 index 0000000000000..9e6b5093f4b99 --- /dev/null +++ b/clang/test/CIR/Transforms/eh-abi-lowering-eh-spec.cir @@ -0,0 +1,156 @@ +// RUN: cir-opt %s -cir-eh-abi-lowering -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s + +!s32i = !cir.int<s, 32> +!u8i = !cir.int<u, 8> +!void = !cir.void + +module attributes {cir.triple = "x86_64-unknown-linux-gnu"} { + +cir.func private @_Z8externalv() +cir.func private @_Z5innerv() + +cir.global "private" constant external @_ZTIi : !cir.ptr<!u8i> + +// void target() throw(int) +cir.func @filter_with_types() { + cir.try_call @_Z8externalv() ^bb1, ^bb2 : () -> () +^bb1: + cir.br ^bb5 +^bb2: + %0 = cir.eh.initiate : !cir.eh_token + cir.br ^bb3(%0 : !cir.eh_token) +^bb3(%eh_token: !cir.eh_token): + cir.eh.dispatch %eh_token : !cir.eh_token [ + filter(#cir.global_view<@_ZTIi> : !cir.ptr<!u8i>) : ^bb4, + unwind : ^bb6 + ] +^bb4(%eh_token.1: !cir.eh_token): + cir.eh.unexpected %eh_token.1 : !cir.eh_token +^bb5: + cir.return +^bb6(%eh_token.2: !cir.eh_token): + cir.resume %eh_token.2 : !cir.eh_token +} + +// CHECK-LABEL: cir.func @filter_with_types() +// CHECK-SAME: personality(@__gxx_personality_v0) +// CHECK: cir.try_call @_Z8externalv() ^[[NORMAL:bb[0-9]+]], ^[[UNWIND:bb[0-9]+]] +// CHECK: ^[[NORMAL]]: +// CHECK: cir.br ^[[RETURN:bb[0-9]+]] +// CHECK: ^[[UNWIND]]: +// CHECK: %[[EXN:.*]], %[[TID:.*]] = cir.eh.inflight_exception filter [@_ZTIi] +// CHECK: cir.br ^[[DISPATCH:bb[0-9]+]](%[[EXN]], %[[TID]] : !cir.ptr<!void>, !u32i) +// CHECK: ^[[DISPATCH]](%[[D_EXN:.*]]: !cir.ptr<!void>, %[[D_TID:.*]]: !u32i): +// CHECK: cir.br ^[[FILTER:bb[0-9]+]](%[[D_EXN]], %[[D_TID]] : !cir.ptr<!void>, !u32i) +// CHECK: ^[[FILTER]](%[[F_EXN:.*]]: !cir.ptr<!void>, %[[F_TID:.*]]: !u32i): +// CHECK: %[[SID:.*]] = cir.cast integral %[[F_TID]] : !u32i -> !s32i +// CHECK: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i +// CHECK: %[[CMP:.*]] = cir.cmp lt %[[SID]], %[[ZERO]] : !s32i +// CHECK: cir.brcond %[[CMP]] ^[[VIOLATED:bb[0-9]+]](%[[F_EXN]], %[[F_TID]] : !cir.ptr<!void>, !u32i), ^[[PERMITTED:bb[0-9]+]](%[[F_EXN]], %[[F_TID]] : !cir.ptr<!void>, !u32i) +// CHECK: ^[[VIOLATED]](%[[V_EXN:.*]]: !cir.ptr<!void>, %{{.*}}: !u32i): +// CHECK: cir.call @__cxa_call_unexpected(%[[V_EXN]]){{.*}}noreturn +// CHECK: cir.unreachable +// CHECK: ^[[RETURN]]: +// CHECK: cir.return +// CHECK: ^[[PERMITTED]](%[[P_EXN:.*]]: !cir.ptr<!void>, %[[P_TID:.*]]: !u32i): +// CHECK: cir.resume.flat %[[P_EXN]], %[[P_TID]] + +// void target() throw() +cir.func @filter_without_types() { + cir.try_call @_Z8externalv() ^bb1, ^bb2 : () -> () +^bb1: + cir.br ^bb5 +^bb2: + %0 = cir.eh.initiate : !cir.eh_token + cir.br ^bb3(%0 : !cir.eh_token) +^bb3(%eh_token: !cir.eh_token): + cir.eh.dispatch %eh_token : !cir.eh_token [ + filter() : ^bb4, + unwind : ^bb6 + ] +^bb4(%eh_token.1: !cir.eh_token): + cir.eh.unexpected %eh_token.1 : !cir.eh_token +^bb5: + cir.return +^bb6(%eh_token.2: !cir.eh_token): + cir.unreachable +} + +// CHECK-LABEL: cir.func @filter_without_types() +// CHECK: %[[EXN:.*]], %[[TID:.*]] = cir.eh.inflight_exception filter [] +// CHECK: cir.br ^[[DISPATCH:bb[0-9]+]](%[[EXN]], %[[TID]] : !cir.ptr<!void>, !u32i) +// CHECK: ^[[DISPATCH]](%[[D_EXN:.*]]: !cir.ptr<!void>, %[[D_TID:.*]]: !u32i): +// CHECK: cir.br ^[[VIOLATED:bb[0-9]+]](%[[D_EXN]], %[[D_TID]] : !cir.ptr<!void>, !u32i) +// CHECK: ^[[VIOLATED]](%[[V_EXN:.*]]: !cir.ptr<!void>, %{{.*}}: !u32i): +// CHECK: cir.call @__cxa_call_unexpected(%[[V_EXN]]){{.*}}noreturn +// CHECK: cir.unreachable +// CHECK: cir.unreachable + +// Catch then filter: the landing pad lists the catch type and then the filter. +cir.func @catch_then_filter() { + cir.try_call @_Z5innerv() ^bb1, ^bb2 : () -> () +^bb1: + cir.br ^bb8 +^bb2: + %0 = cir.eh.initiate : !cir.eh_token + cir.br ^bb3(%0 : !cir.eh_token) +^bb3(%eh_token: !cir.eh_token): + cir.eh.dispatch %eh_token : !cir.eh_token [ + catch(#cir.global_view<@_ZTIi> : !cir.ptr<!u8i>) : ^bb4, + unwind : ^bb5 + ] +^bb4(%eh_token.1: !cir.eh_token): + %ct, %exn = cir.begin_catch %eh_token.1 + : !cir.eh_token -> (!cir.catch_token, !cir.ptr<!void>) + cir.end_catch %ct : !cir.catch_token + cir.br ^bb8 +^bb5(%eh_token.2: !cir.eh_token): + cir.br ^bb6(%eh_token.2 : !cir.eh_token) +^bb6(%eh_token.3: !cir.eh_token): + cir.eh.dispatch %eh_token.3 : !cir.eh_token [ + filter() : ^bb7, + unwind : ^bb9 + ] +^bb7(%eh_token.4: !cir.eh_token): + cir.eh.unexpected %eh_token.4 : !cir.eh_token +^bb8: + cir.return +^bb9(%eh_token.5: !cir.eh_token): + cir.unreachable +} + +// CHECK-LABEL: cir.func @catch_then_filter() +// CHECK-SAME: personality(@__gxx_personality_v0) +// CHECK: cir.try_call @_Z5innerv() ^[[NORMAL:bb[0-9]+]], ^[[UNWIND:bb[0-9]+]] +// CHECK: ^[[NORMAL]]: +// CHECK: cir.br ^[[RETURN:bb[0-9]+]] +// CHECK: ^[[UNWIND]]: +// CHECK: %[[EXN:.*]], %[[TID:.*]] = cir.eh.inflight_exception [@_ZTIi] filter [] +// CHECK: cir.br ^[[DISPATCH:bb[0-9]+]](%[[EXN]], %[[TID]] : !cir.ptr<!void>, !u32i) +// CHECK: ^[[DISPATCH]](%[[D_EXN:.*]]: !cir.ptr<!void>, %[[D_TID:.*]]: !u32i): +// CHECK: cir.br ^[[CATCH_CMP:bb[0-9]+]](%[[D_EXN]], %[[D_TID]] : !cir.ptr<!void>, !u32i) +// CHECK: ^[[CATCH_CMP]](%[[C_EXN:.*]]: !cir.ptr<!void>, %[[C_TID:.*]]: !u32i): +// CHECK: %[[TYPEID:.*]] = cir.eh.typeid @_ZTIi +// CHECK: %[[CMP:.*]] = cir.cmp eq %[[C_TID]], %[[TYPEID]] : !u32i +// CHECK: cir.brcond %[[CMP]] ^[[CATCH:bb[0-9]+]](%[[C_EXN]], %[[C_TID]] : !cir.ptr<!void>, !u32i), ^[[NOT_CAUGHT:bb[0-9]+]](%[[C_EXN]], %[[C_TID]] : !cir.ptr<!void>, !u32i) +// CHECK: ^[[CATCH]](%[[H_EXN:.*]]: !cir.ptr<!void>, %{{.*}}: !u32i): +// CHECK: %[[OBJ:.*]] = cir.call @__cxa_begin_catch(%[[H_EXN]]) : (!cir.ptr<!void>) -> !cir.ptr<!u8i> +// CHECK: cir.cast bitcast %[[OBJ]] : !cir.ptr<!u8i> -> !cir.ptr<!void> +// CHECK: cir.call @__cxa_end_catch() : () -> () +// CHECK: cir.br ^[[RETURN]] +// Not caught by the handler: continue unwinding into the filter dispatch, +// which is an empty filter and so always calls __cxa_call_unexpected. +// CHECK: ^[[NOT_CAUGHT]](%[[N_EXN:.*]]: !cir.ptr<!void>, %[[N_TID:.*]]: !u32i): +// CHECK: cir.br ^[[FILTER_ENTRY:bb[0-9]+]](%[[N_EXN]], %[[N_TID]] : !cir.ptr<!void>, !u32i) +// CHECK: ^[[FILTER_ENTRY]](%[[FE_EXN:.*]]: !cir.ptr<!void>, %[[FE_TID:.*]]: !u32i): +// CHECK: cir.br ^[[VIOLATED:bb[0-9]+]](%[[FE_EXN]], %[[FE_TID]] : !cir.ptr<!void>, !u32i) +// CHECK: ^[[VIOLATED]](%[[V_EXN:.*]]: !cir.ptr<!void>, %{{.*}}: !u32i): +// CHECK: cir.call @__cxa_call_unexpected(%[[V_EXN]]){{.*}}noreturn +// CHECK: cir.unreachable +// CHECK: ^[[RETURN]]: +// CHECK: cir.return + +// CHECK: cir.func {{.*}} @__cxa_call_unexpected(!cir.ptr<!void>) + +} >From 387b714b41a276c5254bfc051c1b298b8c0f12bd Mon Sep 17 00:00:00 2001 From: Andy Kaylor <[email protected]> Date: Tue, 15 Sep 2026 17:35:48 -0700 Subject: [PATCH 2/2] Fix formatting --- clang/lib/CIR/Dialect/Transforms/EHABILowering.cpp | 8 ++++---- clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/clang/lib/CIR/Dialect/Transforms/EHABILowering.cpp b/clang/lib/CIR/Dialect/Transforms/EHABILowering.cpp index f3971403a2ef3..77ce7434c8ed3 100644 --- a/clang/lib/CIR/Dialect/Transforms/EHABILowering.cpp +++ b/clang/lib/CIR/Dialect/Transforms/EHABILowering.cpp @@ -732,14 +732,14 @@ void ItaniumEHLowering::lowerDispatch(cir::EhDispatchOp dispatch, // The personality reports a filter failure with a negative selector. // Type ids are still !u32i on the eh_token replacement pair, so recast // to !s32i for the signed comparison. TODO: produce !s32i throughout. - auto *cmpBlock = builder.createBlock( - insertBefore, {voidPtrType, u32Type}, {dispLoc, dispLoc}); + auto *cmpBlock = builder.createBlock(insertBefore, {voidPtrType, u32Type}, + {dispLoc, dispLoc}); mlir::Value cmpExnPtr = cmpBlock->getArgument(0); mlir::Value cmpTypeId = cmpBlock->getArgument(1); mlir::Value signedTypeId = cir::CastOp::create( builder, dispLoc, s32Type, cir::CastKind::integral, cmpTypeId); - mlir::Value zero = cir::ConstantOp::create( - builder, dispLoc, cir::IntAttr::get(s32Type, 0)); + mlir::Value zero = cir::ConstantOp::create(builder, dispLoc, + cir::IntAttr::get(s32Type, 0)); auto cmpOp = cir::CmpOp::create(builder, dispLoc, cir::CmpOpKind::lt, signedTypeId, zero); cir::BrCondOp::create(builder, dispLoc, cmpOp, filterDest, defaultDest, diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index 3fd22b5e42225..c1e63f32b3ce6 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -4692,8 +4692,8 @@ mlir::LogicalResult CIRToLLVMEhInflightOpLowering::matchAndRewrite( // An empty list is throw() and lowers to [0 x ptr] zeroinitializer. mlir::OpBuilder::InsertionGuard guard(rewriter); rewriter.setInsertionPointToStart(entryBlock); - auto filterArrayTy = mlir::LLVM::LLVMArrayType::get( - llvmPtrTy, filterListAttr.size()); + auto filterArrayTy = + mlir::LLVM::LLVMArrayType::get(llvmPtrTy, filterListAttr.size()); mlir::Value filterVal; if (filterListAttr.empty()) { filterVal = mlir::LLVM::ZeroOp::create(rewriter, loc, filterArrayTy); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
