[clang] [CIR] Fix bug where block after-unreachable wasn't CXXABILowered (PR #186869)
Erich Keane via cfe-commits
cfe-commits at lists.llvm.org
Mon Mar 16 12:57:41 PDT 2026
https://github.com/erichkeane updated https://github.com/llvm/llvm-project/pull/186869
>From f8059fc05fb8095a710954817298aa8fe99d9bcc Mon Sep 17 00:00:00 2001
From: erichkeane <ekeane at nvidia.com>
Date: Mon, 16 Mar 2026 11:46:52 -0700
Subject: [PATCH 1/2] [CIR] Fix bug where block after-unreachable wasn't
CXXABILowered
If a TU has an 'unreachable' block, it wouldn't be CXXABILower'ed, which
would cause a legalization failure. This patch adds the same solution
we do in LowerToLLVM, which is to make sure we transform those sections
separately.
---
.../CIR/Dialect/Transforms/CXXABILowering.cpp | 69 ++++++++++++++++++-
.../CodeGen/abi-lower-after-unreachable.cpp | 31 +++++++++
2 files changed, 99 insertions(+), 1 deletion(-)
create mode 100644 clang/test/CIR/CodeGen/abi-lower-after-unreachable.cpp
diff --git a/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp b/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp
index d2c7ac37e8a96..fcaa38683c396 100644
--- a/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp
@@ -538,6 +538,69 @@ populateCXXABIConversionTarget(mlir::ConversionTarget &target,
// The Pass
//===----------------------------------------------------------------------===//
+// The applyPartialConversion function traverses blocks in the dominance order,
+// so it does not lower and operations that are not reachachable from the
+// operations passed in as arguments. Since we do need to lower such code in
+// order to avoid verification errors occur, we cannot just pass the module op
+// to applyPartialConversion. We must build a set of unreachable ops and
+// explicitly add them, along with the module, to the vector we pass to
+// applyPartialConversion.
+//
+// For instance, this CIR code:
+//
+// cir.func @foo(%arg0: !s32i) -> !s32i {
+// %4 = cir.cast int_to_bool %arg0 : !s32i -> !cir.bool
+// cir.if %4 {
+// %5 = cir.const #cir.int<1> : !s32i
+// cir.return %5 : !s32i
+// } else {
+// %5 = cir.const #cir.int<0> : !s32i
+// cir.return %5 : !s32i
+// }
+// cir.return %arg0 : !s32i
+// }
+//
+// contains an unreachable return operation (the last one). After the CXXABI
+// pass it will be placed into the unreachable block. This will error because
+// it will have not converted the types in the block, making the legalizer fail.
+//
+// In the future we may want to get rid of this function and use a DCE pass or
+// something similar. But for now we need to guarantee the absence of the
+// dialect verification errors. Note: We do the same in LowerToLLVM as well,
+// this is a striaght copy/paste including most of the comment. We might wi sh
+// to combine these if we don't want to do a DCE pass/etc.
+static void collectUnreachable(mlir::Operation *parent,
+ llvm::SmallVector<mlir::Operation *> &ops) {
+
+ llvm::SmallVector<mlir::Block *> unreachableBlocks;
+ parent->walk([&](mlir::Block *blk) { // check
+ if (blk->hasNoPredecessors() && !blk->isEntryBlock())
+ unreachableBlocks.push_back(blk);
+ });
+
+ std::set<mlir::Block *> visited;
+ for (mlir::Block *root : unreachableBlocks) {
+ // We create a work list for each unreachable block.
+ // Thus we traverse operations in some order.
+ std::deque<mlir::Block *> workList;
+ workList.push_back(root);
+
+ while (!workList.empty()) {
+ mlir::Block *blk = workList.back();
+ workList.pop_back();
+ if (visited.count(blk))
+ continue;
+ visited.emplace(blk);
+
+ for (mlir::Operation &op : *blk)
+ ops.push_back(&op);
+
+ for (mlir::Block *succ : blk->getSuccessors())
+ workList.push_back(succ);
+ }
+ }
+}
+
void CXXABILoweringPass::runOnOperation() {
auto mod = mlir::cast<mlir::ModuleOp>(getOperation());
mlir::MLIRContext *ctx = mod.getContext();
@@ -566,7 +629,11 @@ void CXXABILoweringPass::runOnOperation() {
mlir::ConversionTarget target(*ctx);
populateCXXABIConversionTarget(target, typeConverter);
- if (failed(mlir::applyPartialConversion(mod, target, std::move(patterns))))
+ llvm::SmallVector<mlir::Operation *> ops;
+ ops.push_back(mod);
+ collectUnreachable(mod, ops);
+
+ if (failed(mlir::applyPartialConversion(ops, target, std::move(patterns))))
signalPassFailure();
}
diff --git a/clang/test/CIR/CodeGen/abi-lower-after-unreachable.cpp b/clang/test/CIR/CodeGen/abi-lower-after-unreachable.cpp
new file mode 100644
index 0000000000000..180bf365940ef
--- /dev/null
+++ b/clang/test/CIR/CodeGen/abi-lower-after-unreachable.cpp
@@ -0,0 +1,31 @@
+// RUN: %clang_cc1 -I%S/Inputs %s -triple x86_64-unknown-linux-gnu -fclangir -emit-cir -mmlir --mlir-print-ir-before=cir-cxxabi-lowering -o %t.cir 2> %t-before.cir
+// RUN: FileCheck %s --input-file=%t-before.cir --check-prefixes=CIR,CIR-BEFORE
+// RUN: FileCheck %s --input-file=%t.cir --check-prefixes=CIR,CIR-AFTER
+struct Base1 {
+ virtual ~Base1();
+};
+
+struct Base2 {
+ virtual ~Base2();
+};
+
+struct Derived final : Base1 {};
+
+using PMFTy = void(Derived::*)(void);
+
+void untransformed_after_unreachable(Base2 &ref, PMFTy pmf) {
+ auto badcast = dynamic_cast<Derived &>(ref);
+ (badcast.*pmf)();
+
+// CIR-LABEL: cir.func {{.*}}@_Z31untransformed_after_unreachableR5Base2M7DerivedFvvE
+// CIR: %[[PMF:.*]] = cir.alloca !{{.*}} ["pmf", init]
+// CIR: %[[DERIVED:.*]] = cir.alloca !rec_Derived
+// CIR: cir.load %{{.*}} : !cir.ptr<!cir.ptr<!rec_Base2>>, !cir.ptr<!rec_Base2>
+// CIR-NEXT: cir.call @__cxa_bad_cast() : () -> ()
+// CIR-NEXT: cir.unreachable
+// CIR-BEFORE: %[[PMF_LOAD:.*]] = cir.load{{.*}} %[[PMF]]
+// CIR-BEFORE: cir.get_method %[[PMF_LOAD]], %[[DERIVED]]
+// CIR-AFTER: %[[PMF_LOAD:.*]] = cir.load{{.*}} %[[PMF]]
+// CIR-AFTER: %[[PMF_EXTRACT:.*]] = cir.extract_member %[[PMF_LOAD]][1]
+// CIR-AFTER: %[[PMF_EXTRACT:.*]] = cir.extract_member %[[PMF_LOAD]][0]
+}
>From e216f502cb549b096ec080f4ed91bebaaa6a025e Mon Sep 17 00:00:00 2001
From: erichkeane <ekeane at nvidia.com>
Date: Mon, 16 Mar 2026 12:57:22 -0700
Subject: [PATCH 2/2] Add 'deque' include, since apparently it was transitively
included on my computer but not on one of the CI bots
---
clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp | 2 ++
1 file changed, 2 insertions(+)
diff --git a/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp b/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp
index fcaa38683c396..a68256ed76b6f 100644
--- a/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp
@@ -6,6 +6,8 @@
//
//===----------------------------------------------------------------------===//
+#include <deque>
+
#include "PassDetail.h"
#include "TargetLowering/LowerModule.h"
More information about the cfe-commits
mailing list