Author: Andy Kaylor
Date: 2026-09-17T10:52:29-07:00
New Revision: 4569873831550f6d66088ce036336323527397f6

URL: 
https://github.com/llvm/llvm-project/commit/4569873831550f6d66088ce036336323527397f6
DIFF: 
https://github.com/llvm/llvm-project/commit/4569873831550f6d66088ce036336323527397f6.diff

LOG: [CIR][EH] Implement EH ABI lowering for dynamic exception specification 
(#223862)

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

Added: 
    clang/test/CIR/Transforms/eh-abi-lowering-eh-spec.cir

Modified: 
    clang/include/clang/CIR/Dialect/IR/CIROps.td
    clang/lib/CIR/Dialect/Transforms/EHABILowering.cpp
    clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
    clang/test/CIR/Lowering/eh-inflight.cir

Removed: 
    clang/test/CIR/Transforms/eh-abi-lowering-eh-spec-nyi.cir


################################################################################
diff  --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td 
b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 56991ea236a07..f23475842ed3a 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -8422,7 +8422,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..77ce7434c8ed3 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 596c0d72264a0..f29fa93ee4609 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -4660,7 +4660,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();
@@ -4679,18 +4680,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
@@ -4698,7 +4725,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>)
+
+}


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to