[clang] [CIR] Skip record rebuilds for unchanging types in CXXABI lowering (PR #232880)
Bruno Cardoso Lopes via cfe-commits
cfe-commits at lists.llvm.org
Fri Oct 16 19:09:55 PDT 2026
https://github.com/bcardosolopes updated https://github.com/llvm/llvm-project/pull/232880
>From c14553b1d0bf41482c5b568de327b83ef9652de4 Mon Sep 17 00:00:00 2001
From: Bruno Cardoso Lopes <bruno.cardoso at gmail.com>
Date: Thu, 15 Oct 2026 14:25:37 -0700
Subject: [PATCH 1/2] [CIR] Skip record rebuilds for unchanging types in CXXABI
lowering
CIRABITypeConverter only rewrites data-member and method types, but it
did not override typeMayChange, so every named record converted to a
renamed copy and every op touching one was illegal, forcing the generic
pattern to rebuild them all. Override it to report only those two types
as possibly changing, letting records that reach neither convert to
themselves and stay legal.
One behavior guard: no-op casts that always fold (same-type bitcast,
address_space, complex) must stay illegal so the driver's BeforePatterns
fold still removes them; without this carve-out the dead casts that
TargetLowering leaves behind would survive into later passes (caught by
cxx-this-lvalue.hlsl). Same-type casts on non-record types now fold in
this pass too, which shows in -emit-cir output; -emit-llvm is unchanged.
CXXABILowering on SemaExpr CIR: 2.42s->0.90s (-63%). 3-rep medians on the
five slowest -fclangir translation units: Registry 36.7->35.8,
ASTImporterTest 64.4->63.7, SLPVectorizer 93.6->91.9, OpenMPDecomp
39.5->38.1, SemaExpr 59.3->57.5. Total 67.9s->61.6s (-6.3s). .text
bit-identical on all five. check-clang-cir green, 1250 passed with
0 unexpected failures.
Assisted-by: Muse Code (Muse).
---
.../CIR/Dialect/Transforms/CXXABILowering.cpp | 28 ++++++++++++
.../cxx-abi-lowering-same-type-cast-fold.cir | 45 +++++++++++++++++++
2 files changed, 73 insertions(+)
create mode 100644 clang/test/CIR/Transforms/cxx-abi-lowering-same-type-cast-fold.cir
diff --git a/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp b/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp
index 86c74ce14842b9..2bf8bf1677912b 100644
--- a/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp
@@ -846,6 +846,13 @@ class CIRABITypeConverter : public cir::RecordRewritingTypeConverter {
return convertType(abiType);
});
}
+
+protected:
+ // Only member pointer types are rewritten. Records that reach none of them
+ // are left alone.
+ bool typeMayChange(mlir::Type type) const override {
+ return mlir::isa<cir::DataMemberType, cir::MethodType>(type);
+ }
};
} // namespace
@@ -885,6 +892,27 @@ populateCXXABIConversionTarget(mlir::ConversionTarget &target,
});
});
+ // TargetLowering leaves same-type casts behind. Keep them illegal so the
+ // conversion driver folds them away.
+ target.addDynamicallyLegalOp<cir::CastOp>([&typeConverter](cir::CastOp op) {
+ if (op.getSrc().getType() == op.getType()) {
+ switch (op.getKind()) {
+ case cir::CastKind::bitcast:
+ case cir::CastKind::address_space:
+ case cir::CastKind::float_complex:
+ case cir::CastKind::int_complex:
+ return false;
+ default:
+ break;
+ }
+ }
+ if (!typeConverter.isLegal(op.getOperation()))
+ return false;
+ // CastOp has no regions, so operand/result types and inherent
+ // attributes are the whole legality check.
+ return areCXXABIInherentAttrsLegal(op, typeConverter);
+ });
+
// Some CIR ops needs special checking for legality
target.addDynamicallyLegalOp<cir::FuncOp>([&typeConverter](cir::FuncOp op) {
bool attrs = areCXXABIInherentAttrsLegal(op, typeConverter);
diff --git a/clang/test/CIR/Transforms/cxx-abi-lowering-same-type-cast-fold.cir b/clang/test/CIR/Transforms/cxx-abi-lowering-same-type-cast-fold.cir
new file mode 100644
index 00000000000000..c0ee9a9b611a43
--- /dev/null
+++ b/clang/test/CIR/Transforms/cxx-abi-lowering-same-type-cast-fold.cir
@@ -0,0 +1,45 @@
+// RUN: cir-opt %s -cir-cxxabi-lowering -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s
+
+!u8i = !cir.int<u, 8>
+!s32i = !cir.int<s, 32>
+!rec_S = !cir.struct<"S" {data !s32i}>
+!rec_Other = !cir.struct<"Other" {pad !u8i}>
+!rec_Dmp = !cir.struct<"Dmp" {data !cir.data_member<!s32i in !rec_Other>}>
+// CHECK: !rec_Dmp = !cir.struct<"Dmp" {data !s64i}>
+
+module attributes {cir.triple = "x86_64-unknown-linux-gnu"} {
+ // Same-type no-op casts stay illegal so the driver folds them away,
+ // on plain ...
+ cir.func @cast_plain(%p: !cir.ptr<!s32i>) -> !cir.ptr<!s32i> {
+ %c = cir.cast bitcast %p : !cir.ptr<!s32i> -> !cir.ptr<!s32i>
+ cir.return %c : !cir.ptr<!s32i>
+ }
+ // CHECK-LABEL: cir.func @cast_plain
+ // CHECK-NEXT: cir.return %{{.*}} : !cir.ptr<!s32i>
+
+ // ... and record pointers.
+ cir.func @cast_record(%p: !cir.ptr<!rec_S>) -> !cir.ptr<!rec_S> {
+ %c = cir.cast bitcast %p : !cir.ptr<!rec_S> -> !cir.ptr<!rec_S>
+ cir.return %c : !cir.ptr<!rec_S>
+ }
+ // CHECK-LABEL: cir.func @cast_record
+ // CHECK-NEXT: cir.return %{{.*}} : !cir.ptr<!rec_S>
+
+ // Same for the address_space casts TargetLowering leaves behind.
+ cir.func @cast_addrspace(%p: !cir.ptr<!s32i>) -> !cir.ptr<!s32i> {
+ %c = cir.cast address_space %p : !cir.ptr<!s32i> -> !cir.ptr<!s32i>
+ cir.return %c : !cir.ptr<!s32i>
+ }
+ // CHECK-LABEL: cir.func @cast_addrspace
+ // CHECK-NEXT: cir.return %{{.*}} : !cir.ptr<!s32i>
+
+ // Records that reach member pointers are still rewritten.
+ cir.func @dmp_rewritten() {
+ %r = cir.const #cir.zero : !rec_Dmp
+ cir.return
+ }
+ // CHECK-LABEL: cir.func @dmp_rewritten
+ // CHECK-NEXT: %{{.*}} = cir.const #cir.zero : !rec_Dmp
+ // CHECK-NEXT: cir.return
+}
>From 6ef66f677eec156a08e74b439b35d237a96f03d2 Mon Sep 17 00:00:00 2001
From: Bruno Cardoso Lopes <bruno.cardoso at gmail.com>
Date: Fri, 16 Oct 2026 19:04:24 -0700
Subject: [PATCH 2/2] Add member-record lowering coverage for typeMayChange
---
.../cxx-abi-lowering-member-record.cir | 26 +++++++++++++++++++
1 file changed, 26 insertions(+)
create mode 100644 clang/test/CIR/Transforms/cxx-abi-lowering-member-record.cir
diff --git a/clang/test/CIR/Transforms/cxx-abi-lowering-member-record.cir b/clang/test/CIR/Transforms/cxx-abi-lowering-member-record.cir
new file mode 100644
index 00000000000000..ee5a0ede7b0bff
--- /dev/null
+++ b/clang/test/CIR/Transforms/cxx-abi-lowering-member-record.cir
@@ -0,0 +1,26 @@
+// RUN: cir-opt %s -cir-cxxabi-lowering -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s
+
+!s32i = !cir.int<s, 32>
+!rec_Foo = !cir.struct<"Foo" {data !s32i}>
+!method = !cir.method<!cir.func<(!cir.ptr<!rec_Foo>, !s32i)> in !rec_Foo>
+!rec_HasMethod = !cir.struct<"HasMethod" {data !method}>
+!rec_Plain = !cir.struct<"Plain" {data !s32i}>
+
+// A record reaching a method type is rebuilt with the Itanium lowered form,
+// a {ptrdiff_t, ptrdiff_t} pair. Dropping MethodType from typeMayChange
+// leaves the member unlowered and this check fails.
+// CHECK-DAG: !rec_HasMethod = !cir.struct<"HasMethod" {data !rec_anon_struct}>
+// CHECK-DAG: !rec_anon_struct = !cir.struct<{data !s64i, data !s64i}>
+
+// A record reaching no member pointer type converts to itself and passes
+// through with its name and members untouched.
+// CHECK-DAG: !rec_Plain = !cir.struct<"Plain" {data !s32i}>
+
+module attributes {cir.triple = "x86_64-unknown-linux-gnu"} {
+ cir.func @method_record() {
+ %m = cir.const #cir.zero : !rec_HasMethod
+ %p = cir.const #cir.zero : !rec_Plain
+ cir.return
+ }
+}
More information about the cfe-commits
mailing list