[Mlir-commits] [clang] [llvm] [mlir] [CIR][WIP] support relative vtable (PR #195025)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Tue May 12 07:56:35 PDT 2026
https://github.com/xiongzile updated https://github.com/llvm/llvm-project/pull/195025
>From bc30eea7f976d182909bea07e4d7a1de7079e7ef Mon Sep 17 00:00:00 2001
From: Zile Xiong <xiongzile at bytedance.com>
Date: Thu, 30 Apr 2026 15:14:45 +0800
Subject: [PATCH 1/5] [CIR] Relative vtable layout for virtual base offset
---
clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
index 946739d4e1702..f56f291c70772 100644
--- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
@@ -2057,8 +2057,10 @@ mlir::Value CIRGenItaniumCXXABI::getVirtualBaseClassOffset(
mlir::Value vbaseOffset;
if (cgm.getLangOpts().RelativeCXXABIVTables) {
- assert(!cir::MissingFeatures::vtableRelativeLayout());
- cgm.errorNYI(loc, "getVirtualBaseClassOffset: relative layout");
+ mlir::Value offsetPtr = builder.createBitcast(vbaseOffsetPtr, builder.getPointerTo(cgm.sInt32Ty));
+ vbaseOffset = cgf.getBuilder().createLoad(
+ loc, Address(offsetPtr, cgm.sInt32Ty,
+ CharUnits::fromQuantity(4))); // vbase.offset
} else {
mlir::Value offsetPtr = builder.createBitcast(
vbaseOffsetPtr, builder.getPointerTo(cgm.ptrDiffTy));
>From c22a4d7db8f00d90c4900c85402a5f24f602a611 Mon Sep 17 00:00:00 2001
From: Zile Xiong <xiongzile99 at gmail.com>
Date: Thu, 30 Apr 2026 17:24:00 +0800
Subject: [PATCH 2/5] [CIR] add test for getVirtualBaseOffsetOffset:
RelativeCXXABIVTables
---
clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp | 3 +-
.../CodeGenCXX/vtable-relative-baseoffset.cpp | 65 +++++++++++++++++++
2 files changed, 67 insertions(+), 1 deletion(-)
create mode 100644 clang/test/CIR/CodeGenCXX/vtable-relative-baseoffset.cpp
diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
index f56f291c70772..b6e6bd4857c9b 100644
--- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
@@ -2057,7 +2057,8 @@ mlir::Value CIRGenItaniumCXXABI::getVirtualBaseClassOffset(
mlir::Value vbaseOffset;
if (cgm.getLangOpts().RelativeCXXABIVTables) {
- mlir::Value offsetPtr = builder.createBitcast(vbaseOffsetPtr, builder.getPointerTo(cgm.sInt32Ty));
+ mlir::Value offsetPtr = builder.createBitcast(
+ vbaseOffsetPtr, builder.getPointerTo(cgm.sInt32Ty));
vbaseOffset = cgf.getBuilder().createLoad(
loc, Address(offsetPtr, cgm.sInt32Ty,
CharUnits::fromQuantity(4))); // vbase.offset
diff --git a/clang/test/CIR/CodeGenCXX/vtable-relative-baseoffset.cpp b/clang/test/CIR/CodeGenCXX/vtable-relative-baseoffset.cpp
new file mode 100644
index 0000000000000..b58cf0622781c
--- /dev/null
+++ b/clang/test/CIR/CodeGenCXX/vtable-relative-baseoffset.cpp
@@ -0,0 +1,65 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fexperimental-relative-c++-abi-vtables -fclangir -emit-cir %s -o - | FileCheck --check-prefix=CIR %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fexperimental-relative-c++-abi-vtables -fclangir -emit-llvm %s -o - | FileCheck --check-prefix=LLVM %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fexperimental-relative-c++-abi-vtables -emit-llvm %s -o - | FileCheck --check-prefix=OGCG %s
+
+// vbase-offset.cpp
+
+struct V {
+ int x;
+};
+
+struct A : virtual V {
+};
+
+struct B : A {
+};
+// CIR-LABEL: @_Z1fP1B(
+// CIR: [[P:%.*]] = cir.load align(8) {{%.*}} : !cir.ptr<!cir.ptr<!rec_B>>, !cir.ptr<!rec_B>
+// CIR-NEXT: [[VPTR_PTR:%.*]] = cir.vtable.get_vptr [[P]] : !cir.ptr<!rec_B> -> !cir.ptr<!cir.vptr>
+// CIR-NEXT: [[VTABLE:%.*]] = cir.load align(8) [[VPTR_PTR]] : !cir.ptr<!cir.vptr>, !cir.vptr
+// CIR-NEXT: [[VTABLE_BYTES:%.*]] = cir.cast bitcast [[VTABLE]] : !cir.vptr -> !cir.ptr<!u8i>
+// CIR-NEXT: [[VBASE_OFFSET_SLOT_OFFSET:%.*]] = cir.const #cir.int<-12> : !s64i
+// CIR-NEXT: [[VBASE_OFFSET_SLOT:%.*]] = cir.ptr_stride [[VTABLE_BYTES]], [[VBASE_OFFSET_SLOT_OFFSET]] : (!cir.ptr<!u8i>, !s64i) -> !cir.ptr<!u8i>
+// CIR-NEXT: [[VBASE_OFFSET_PTR:%.*]] = cir.cast bitcast [[VBASE_OFFSET_SLOT]] : !cir.ptr<!u8i> -> !cir.ptr<!s32i>
+// CIR-NEXT: [[VBASE_OFFSET:%.*]] = cir.load align(4) [[VBASE_OFFSET_PTR]] : !cir.ptr<!s32i>, !s32i
+// CIR-NEXT: [[P_BYTES:%.*]] = cir.cast bitcast [[P]] : !cir.ptr<!rec_B> -> !cir.ptr<!u8i>
+// CIR-NEXT: [[VBASE_PTR_BYTES:%.*]] = cir.ptr_stride [[P_BYTES]], [[VBASE_OFFSET]] : (!cir.ptr<!u8i>, !s32i) -> !cir.ptr<!u8i>
+// CIR-NEXT: [[VBASE_PTR_B:%.*]] = cir.cast bitcast [[VBASE_PTR_BYTES]] : !cir.ptr<!u8i> -> !cir.ptr<!rec_B>
+// CIR-NEXT: [[VBASE_PTR:%.*]] = cir.cast bitcast [[VBASE_PTR_B]] : !cir.ptr<!rec_B> -> !cir.ptr<!rec_V>
+// CIR-NEXT: [[X_PTR:%.*]] = cir.get_member [[VBASE_PTR]][0] {name = "x"} : !cir.ptr<!rec_V> -> !cir.ptr<!s32i>
+// CIR-NEXT: [[X:%.*]] = cir.load align(4) [[X_PTR]] : !cir.ptr<!s32i>, !s32i
+//
+// LLVM-LABEL: @_Z1fP1B(
+// LLVM: [[P:%.*]] = load ptr, ptr {{.*}}, align 8
+// LLVM-NEXT: [[VTABLE:%.*]] = load ptr, ptr [[P]], align 8
+// LLVM-NEXT: [[VBASE_OFFSET_PTR:%.*]] = getelementptr i8, ptr [[VTABLE]], i64 -12
+// LLVM-NEXT: [[VBASE_OFFSET_I32:%.*]] = load i32, ptr [[VBASE_OFFSET_PTR]], align 4
+// LLVM-NEXT: [[VBASE_OFFSET:%.*]] = sext i32 [[VBASE_OFFSET_I32]] to i64
+// LLVM-NEXT: [[VBASE_PTR:%.*]] = getelementptr i8, ptr [[P]], i64 [[VBASE_OFFSET]]
+// LLVM-NEXT: [[X_PTR:%.*]] = getelementptr inbounds nuw [[STRUCT_V:%.*]], ptr [[VBASE_PTR]], i32 0, i32 0
+// LLVM-NEXT: [[X:%.*]] = load i32, ptr [[X_PTR]], align 4
+// LLVM: ret i32
+//
+// OGCG-LABEL: define dso_local noundef i32 @_Z1fP1B(
+// OGCG-SAME: ptr noundef [[P:%.*]]) #[[ATTR0:[0-9]+]] {
+// OGCG-NEXT: [[ENTRY:.*]]:
+// OGCG-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8
+// OGCG-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8
+// OGCG-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8
+// OGCG-NEXT: [[TMP1:%.*]] = icmp eq ptr [[TMP0]], null
+// OGCG-NEXT: br i1 [[TMP1]], label %[[CAST_END:.*]], label %[[CAST_NOTNULL:.*]]
+// OGCG: [[CAST_NOTNULL]]:
+// OGCG-NEXT: [[VTABLE:%.*]] = load ptr, ptr [[TMP0]], align 8
+// OGCG-NEXT: [[VBASE_OFFSET_PTR:%.*]] = getelementptr i8, ptr [[VTABLE]], i64 -12
+// OGCG-NEXT: [[VBASE_OFFSET:%.*]] = load i32, ptr [[VBASE_OFFSET_PTR]], align 4
+// OGCG-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i32 [[VBASE_OFFSET]]
+// OGCG-NEXT: br label %[[CAST_END]]
+// OGCG: [[CAST_END]]:
+// OGCG-NEXT: [[CAST_RESULT:%.*]] = phi ptr [ [[ADD_PTR]], %[[CAST_NOTNULL]] ], [ null, %[[ENTRY]] ]
+// OGCG-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_V:%.*]], ptr [[CAST_RESULT]], i32 0, i32 0
+// OGCG-NEXT: [[TMP2:%.*]] = load i32, ptr [[X]], align 4
+// OGCG-NEXT: ret i32 [[TMP2]]
+//
+int f(B *p) {
+ return static_cast<V *>(p)->x;
+}
>From d36b220dfda282d7669f395ca127d8fade6e66f3 Mon Sep 17 00:00:00 2001
From: Zile Xiong <xiongzile99 at gmail.com>
Date: Thu, 7 May 2026 20:06:26 +0800
Subject: [PATCH 3/5] [CIR] add support for getVirtualFunctionPointer
---
1.cpp | 73 +++++
cir-ll.ll | 212 ++++++++++++++
cir.ll | 215 +++++++++++++++
clang/include/clang/CIR/Dialect/IR/CIROps.td | 54 ++++
clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp | 18 +-
clang/lib/CIR/CodeGen/CIRGenVTables.cpp | 38 ++-
clang/lib/CIR/Dialect/IR/CIRAttrs.cpp | 8 +-
.../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 22 ++
ll.ll | 250 +++++++++++++++++
rel-ll.ll | 259 ++++++++++++++++++
10 files changed, 1140 insertions(+), 9 deletions(-)
create mode 100644 1.cpp
create mode 100644 cir-ll.ll
create mode 100644 cir.ll
create mode 100644 ll.ll
create mode 100644 rel-ll.ll
diff --git a/1.cpp b/1.cpp
new file mode 100644
index 0000000000000..d736d6f48ab03
--- /dev/null
+++ b/1.cpp
@@ -0,0 +1,73 @@
+struct A {
+ virtual void f();
+ int a;
+};
+
+struct B {
+ virtual void g();
+ int b;
+};
+
+struct C : A, B {
+ void f() override;
+ void g() override;
+ int c;
+};
+
+void A::f() {}
+void B::g() {}
+void C::f() {}
+void C::g() {}
+
+// 1. 通过 A* 做虚调用。
+// 这里应该会查 A-subobject 的 vptr,然后从 vtable slot 里取 f。
+void call_through_A(A *pa) {
+ pa->f();
+}
+
+// 2. 通过 B* 做虚调用。
+// 这里应该会查 B-subobject 的 vptr,然后从 secondary vtable slot 里取 g。
+// 如果实际对象是 C,slot 里可能是 thunk。
+void call_through_B(B *pb) {
+ pb->g();
+}
+
+// 3. 已知静态类型是 C*,但调用 virtual 函数。
+// C++ 语义上仍然是 virtual call,除非编译器能 devirtualize。
+// 在 -O0 / CIR 阶段通常更容易看到 vptr load。
+void call_through_C(C *pc) {
+ pc->f();
+ pc->g();
+}
+
+// 4. 非虚的限定调用。
+// 这里不会查 vptr,应该直接 call C::f / C::g。
+void direct_qualified_call(C *pc) {
+ pc->C::f();
+ pc->C::g();
+}
+
+// 5. 从 C* 转成 A* / B*。
+// A 是 primary base,一般 offset 是 0。
+// B 是 secondary base,一般需要 this + 16 之类的调整。
+void base_casts(C *pc) {
+ A *pa = pc;
+ B *pb = pc;
+
+ pa->f();
+ pb->g();
+}
+
+// 6. 栈上构造 C。
+// 这里适合观察 constructor / vptr 初始化。
+// 如果 CIR 暂时没有完整 ctor lowering,也至少能暴露相关路径。
+void construct_and_call() {
+ C obj;
+ obj.f();
+ obj.g();
+
+ A *pa = &obj;
+ B *pb = &obj;
+ pa->f();
+ pb->g();
+}
\ No newline at end of file
diff --git a/cir-ll.ll b/cir-ll.ll
new file mode 100644
index 0000000000000..67b701207dcb9
--- /dev/null
+++ b/cir-ll.ll
@@ -0,0 +1,212 @@
+; ModuleID = '/data00/home/xiongzile/workspace/llvm-worktree/llvm-project/1.cpp'
+source_filename = "/data00/home/xiongzile/workspace/llvm-worktree/llvm-project/1.cpp"
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+%struct.C = type { %struct.A.base, [4 x i8], %struct.B.base, i32 }
+%struct.A.base = type <{ ptr, i32 }>
+%struct.B.base = type <{ ptr, i32 }>
+
+ at _ZTV1A = global { [3 x i32] } { [3 x i32] [i32 0, i32 ptrtoint (ptr @_ZTI1A to i32), i32 ptrtoint (ptr @_ZN1A1fEv to i32)] }, align 4
+ at _ZTVN10__cxxabiv117__class_type_infoE = external global ptr, align 8
+ at _ZTS1A = global [3 x i8] c"1A\00", align 1
+ at _ZTI1A = constant { i32, ptr } { i32 ptrtoint (ptr getelementptr (i8, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 16) to i32), ptr @_ZTS1A }, align 8
+ at _ZTV1B = global { [3 x i32] } { [3 x i32] [i32 0, i32 ptrtoint (ptr @_ZTI1B to i32), i32 ptrtoint (ptr @_ZN1B1gEv to i32)] }, align 4
+ at _ZTS1B = global [3 x i8] c"1B\00", align 1
+ at _ZTI1B = constant { i32, ptr } { i32 ptrtoint (ptr getelementptr (i8, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 16) to i32), ptr @_ZTS1B }, align 8
+ at _ZTV1C = global { [4 x i32], [3 x i32] } { [4 x i32] [i32 0, i32 ptrtoint (ptr @_ZTI1C to i32), i32 ptrtoint (ptr @_ZN1C1fEv to i32), i32 ptrtoint (ptr @_ZN1C1gEv to i32)], [3 x i32] [i32 -16, i32 ptrtoint (ptr @_ZTI1C to i32), i32 ptrtoint (ptr @_ZThn16_N1C1gEv to i32)] }, align 4
+ at _ZTVN10__cxxabiv121__vmi_class_type_infoE = external global ptr, align 8
+ at _ZTS1C = global [3 x i8] c"1C\00", align 1
+ at _ZTI1C = constant { i32, ptr, i32, i32, ptr, i64, ptr, i64 } { i32 ptrtoint (ptr getelementptr (i8, ptr @_ZTVN10__cxxabiv121__vmi_class_type_infoE, i64 16) to i32), ptr @_ZTS1C, i32 0, i32 2, ptr @_ZTI1A, i64 2, ptr @_ZTI1B, i64 4098 }, align 8
+
+; Function Attrs: noinline
+define dso_local void @_ZN1A1fEv(ptr noundef nonnull align 8 dereferenceable(12) %0) #0 {
+ %2 = alloca ptr, i64 1, align 8
+ store ptr %0, ptr %2, align 8
+ %3 = load ptr, ptr %2, align 8
+ ret void
+}
+
+; Function Attrs: noinline
+define dso_local void @_ZN1B1gEv(ptr noundef nonnull align 8 dereferenceable(12) %0) #0 {
+ %2 = alloca ptr, i64 1, align 8
+ store ptr %0, ptr %2, align 8
+ %3 = load ptr, ptr %2, align 8
+ ret void
+}
+
+; Function Attrs: noinline
+define dso_local void @_ZN1C1fEv(ptr noundef nonnull align 8 dereferenceable(32) %0) #0 {
+ %2 = alloca ptr, i64 1, align 8
+ store ptr %0, ptr %2, align 8
+ %3 = load ptr, ptr %2, align 8
+ ret void
+}
+
+; Function Attrs: noinline
+define dso_local void @_ZN1C1gEv(ptr noundef nonnull align 8 dereferenceable(32) %0) #0 {
+ %2 = alloca ptr, i64 1, align 8
+ store ptr %0, ptr %2, align 8
+ %3 = load ptr, ptr %2, align 8
+ ret void
+}
+
+; Function Attrs: noinline
+define dso_local void @_ZThn16_N1C1gEv(ptr noundef %0) #1 {
+ %2 = alloca ptr, i64 1, align 8
+ store ptr %0, ptr %2, align 8
+ %3 = load ptr, ptr %2, align 8
+ %4 = getelementptr i8, ptr %3, i64 -16
+ call void @_ZN1C1gEv(ptr noundef nonnull align 8 dereferenceable(32) %4)
+ ret void
+}
+
+; Function Attrs: noinline
+define dso_local void @_Z14call_through_AP1A(ptr noundef %0) #0 {
+ %2 = alloca ptr, i64 1, align 8
+ store ptr %0, ptr %2, align 8
+ %3 = load ptr, ptr %2, align 8
+ %4 = load ptr, ptr %3, align 8
+ %5 = call ptr @llvm.load.relative.i32(ptr %4, i32 0)
+ call void %5(ptr noundef nonnull align 8 dereferenceable(12) %3)
+ ret void
+}
+
+; Function Attrs: noinline
+define dso_local void @_Z14call_through_BP1B(ptr noundef %0) #0 {
+ %2 = alloca ptr, i64 1, align 8
+ store ptr %0, ptr %2, align 8
+ %3 = load ptr, ptr %2, align 8
+ %4 = load ptr, ptr %3, align 8
+ %5 = call ptr @llvm.load.relative.i32(ptr %4, i32 0)
+ call void %5(ptr noundef nonnull align 8 dereferenceable(12) %3)
+ ret void
+}
+
+; Function Attrs: noinline
+define dso_local void @_Z14call_through_CP1C(ptr noundef %0) #0 {
+ %2 = alloca ptr, i64 1, align 8
+ store ptr %0, ptr %2, align 8
+ %3 = load ptr, ptr %2, align 8
+ %4 = load ptr, ptr %3, align 8
+ %5 = call ptr @llvm.load.relative.i32(ptr %4, i32 0)
+ call void %5(ptr noundef nonnull align 8 dereferenceable(32) %3)
+ %6 = load ptr, ptr %2, align 8
+ %7 = load ptr, ptr %6, align 8
+ %8 = call ptr @llvm.load.relative.i32(ptr %7, i32 4)
+ call void %8(ptr noundef nonnull align 8 dereferenceable(32) %6)
+ ret void
+}
+
+; Function Attrs: noinline
+define dso_local void @_Z21direct_qualified_callP1C(ptr noundef %0) #0 {
+ %2 = alloca ptr, i64 1, align 8
+ store ptr %0, ptr %2, align 8
+ %3 = load ptr, ptr %2, align 8
+ call void @_ZN1C1fEv(ptr noundef nonnull align 8 dereferenceable(32) %3)
+ %4 = load ptr, ptr %2, align 8
+ call void @_ZN1C1gEv(ptr noundef nonnull align 8 dereferenceable(32) %4)
+ ret void
+}
+
+; Function Attrs: noinline
+define dso_local void @_Z10base_castsP1C(ptr noundef %0) #0 {
+ %2 = alloca ptr, i64 1, align 8
+ %3 = alloca ptr, i64 1, align 8
+ %4 = alloca ptr, i64 1, align 8
+ store ptr %0, ptr %2, align 8
+ %5 = load ptr, ptr %2, align 8
+ store ptr %5, ptr %3, align 8
+ %6 = load ptr, ptr %2, align 8
+ %7 = icmp eq ptr %6, null
+ %8 = getelementptr i8, ptr %6, i32 16
+ %9 = select i1 %7, ptr %6, ptr %8
+ store ptr %9, ptr %4, align 8
+ %10 = load ptr, ptr %3, align 8
+ %11 = load ptr, ptr %10, align 8
+ %12 = call ptr @llvm.load.relative.i32(ptr %11, i32 0)
+ call void %12(ptr noundef nonnull align 8 dereferenceable(12) %10)
+ %13 = load ptr, ptr %4, align 8
+ %14 = load ptr, ptr %13, align 8
+ %15 = call ptr @llvm.load.relative.i32(ptr %14, i32 0)
+ call void %15(ptr noundef nonnull align 8 dereferenceable(12) %13)
+ ret void
+}
+
+; Function Attrs: noinline
+define dso_local void @_Z18construct_and_callv() #0 {
+ %1 = alloca %struct.C, i64 1, align 8
+ %2 = alloca ptr, i64 1, align 8
+ %3 = alloca ptr, i64 1, align 8
+ call void @_ZN1CC1Ev(ptr noundef nonnull align 8 dereferenceable(32) %1) #3
+ call void @_ZN1C1fEv(ptr noundef nonnull align 8 dereferenceable(32) %1)
+ call void @_ZN1C1gEv(ptr noundef nonnull align 8 dereferenceable(32) %1)
+ store ptr %1, ptr %2, align 8
+ %4 = icmp eq ptr %1, null
+ %5 = getelementptr i8, ptr %1, i32 16
+ %6 = select i1 %4, ptr %1, ptr %5
+ store ptr %6, ptr %3, align 8
+ %7 = load ptr, ptr %2, align 8
+ %8 = load ptr, ptr %7, align 8
+ %9 = call ptr @llvm.load.relative.i32(ptr %8, i32 0)
+ call void %9(ptr noundef nonnull align 8 dereferenceable(12) %7)
+ %10 = load ptr, ptr %3, align 8
+ %11 = load ptr, ptr %10, align 8
+ %12 = call ptr @llvm.load.relative.i32(ptr %11, i32 0)
+ call void %12(ptr noundef nonnull align 8 dereferenceable(12) %10)
+ ret void
+}
+
+; Function Attrs: noinline
+define linkonce_odr void @_ZN1AC2Ev(ptr noundef nonnull align 8 dereferenceable(12) %0) #0 {
+ %2 = alloca ptr, i64 1, align 8
+ store ptr %0, ptr %2, align 8
+ %3 = load ptr, ptr %2, align 8
+ store ptr getelementptr inbounds nuw (i8, ptr @_ZTV1A, i64 8), ptr %3, align 8
+ ret void
+}
+
+; Function Attrs: noinline
+define linkonce_odr void @_ZN1BC2Ev(ptr noundef nonnull align 8 dereferenceable(12) %0) #0 {
+ %2 = alloca ptr, i64 1, align 8
+ store ptr %0, ptr %2, align 8
+ %3 = load ptr, ptr %2, align 8
+ store ptr getelementptr inbounds nuw (i8, ptr @_ZTV1B, i64 8), ptr %3, align 8
+ ret void
+}
+
+; Function Attrs: noinline
+define linkonce_odr void @_ZN1CC2Ev(ptr noundef nonnull align 8 dereferenceable(32) %0) #0 {
+ %2 = alloca ptr, i64 1, align 8
+ store ptr %0, ptr %2, align 8
+ %3 = load ptr, ptr %2, align 8
+ call void @_ZN1AC2Ev(ptr noundef nonnull align 8 dereferenceable(12) %3) #3
+ %4 = getelementptr i8, ptr %3, i32 16
+ call void @_ZN1BC2Ev(ptr noundef nonnull align 8 dereferenceable(12) %4) #3
+ store ptr getelementptr inbounds nuw (i8, ptr @_ZTV1C, i64 8), ptr %3, align 8
+ %5 = getelementptr i8, ptr %3, i32 16
+ store ptr getelementptr inbounds nuw (i8, ptr @_ZTV1C, i64 24), ptr %5, align 8
+ ret void
+}
+
+; Function Attrs: noinline
+define linkonce_odr void @_ZN1CC1Ev(ptr noundef nonnull align 8 dereferenceable(32) %0) #0 {
+ %2 = alloca ptr, i64 1, align 8
+ store ptr %0, ptr %2, align 8
+ %3 = load ptr, ptr %2, align 8
+ call void @_ZN1CC2Ev(ptr noundef nonnull align 8 dereferenceable(32) %3) #3
+ ret void
+}
+
+
+; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: read)
+declare ptr @llvm.load.relative.i32(ptr, i32) #2
+
+attributes #0 = { noinline "target-features"="+cx8,+mmx,+sse,+sse2,+x87" }
+attributes #1 = { noinline }
+attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: read) }
+attributes #3 = { nounwind }
+
+!llvm.module.flags = !{!0}
+
+!0 = !{i32 2, !"Debug Info Version", i32 3}
diff --git a/cir.ll b/cir.ll
new file mode 100644
index 0000000000000..48f99824f98a0
--- /dev/null
+++ b/cir.ll
@@ -0,0 +1,215 @@
+; ModuleID = '/data00/home/xiongzile/workspace/llvm-worktree/llvm-project/1.cpp'
+source_filename = "/data00/home/xiongzile/workspace/llvm-worktree/llvm-project/1.cpp"
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+%struct.C = type { %struct.A.base, [4 x i8], %struct.B.base, i32 }
+%struct.A.base = type <{ ptr, i32 }>
+%struct.B.base = type <{ ptr, i32 }>
+
+ at _ZTV1A = global { [3 x ptr] } { [3 x ptr] [ptr null, ptr @_ZTI1A, ptr @_ZN1A1fEv] }, align 8
+ at _ZTVN10__cxxabiv117__class_type_infoE = external global ptr, align 8
+ at _ZTS1A = global [3 x i8] c"1A\00", align 1
+ at _ZTI1A = constant { ptr, ptr } { ptr getelementptr (i8, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 16), ptr @_ZTS1A }, align 8
+ at _ZTV1B = global { [3 x ptr] } { [3 x ptr] [ptr null, ptr @_ZTI1B, ptr @_ZN1B1gEv] }, align 8
+ at _ZTS1B = global [3 x i8] c"1B\00", align 1
+ at _ZTI1B = constant { ptr, ptr } { ptr getelementptr (i8, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 16), ptr @_ZTS1B }, align 8
+ at _ZTV1C = global { [4 x ptr], [3 x ptr] } { [4 x ptr] [ptr null, ptr @_ZTI1C, ptr @_ZN1C1fEv, ptr @_ZN1C1gEv], [3 x ptr] [ptr inttoptr (i64 -16 to ptr), ptr @_ZTI1C, ptr @_ZThn16_N1C1gEv] }, align 8
+ at _ZTVN10__cxxabiv121__vmi_class_type_infoE = external global ptr, align 8
+ at _ZTS1C = global [3 x i8] c"1C\00", align 1
+ at _ZTI1C = constant { ptr, ptr, i32, i32, ptr, i64, ptr, i64 } { ptr getelementptr (i8, ptr @_ZTVN10__cxxabiv121__vmi_class_type_infoE, i64 16), ptr @_ZTS1C, i32 0, i32 2, ptr @_ZTI1A, i64 2, ptr @_ZTI1B, i64 4098 }, align 8
+
+; Function Attrs: noinline
+define dso_local void @_ZN1A1fEv(ptr noundef nonnull align 8 dereferenceable(12) %0) #0 {
+ %2 = alloca ptr, i64 1, align 8
+ store ptr %0, ptr %2, align 8
+ %3 = load ptr, ptr %2, align 8
+ ret void
+}
+
+; Function Attrs: noinline
+define dso_local void @_ZN1B1gEv(ptr noundef nonnull align 8 dereferenceable(12) %0) #0 {
+ %2 = alloca ptr, i64 1, align 8
+ store ptr %0, ptr %2, align 8
+ %3 = load ptr, ptr %2, align 8
+ ret void
+}
+
+; Function Attrs: noinline
+define dso_local void @_ZN1C1fEv(ptr noundef nonnull align 8 dereferenceable(32) %0) #0 {
+ %2 = alloca ptr, i64 1, align 8
+ store ptr %0, ptr %2, align 8
+ %3 = load ptr, ptr %2, align 8
+ ret void
+}
+
+; Function Attrs: noinline
+define dso_local void @_ZN1C1gEv(ptr noundef nonnull align 8 dereferenceable(32) %0) #0 {
+ %2 = alloca ptr, i64 1, align 8
+ store ptr %0, ptr %2, align 8
+ %3 = load ptr, ptr %2, align 8
+ ret void
+}
+
+; Function Attrs: noinline
+define dso_local void @_ZThn16_N1C1gEv(ptr noundef %0) #1 {
+ %2 = alloca ptr, i64 1, align 8
+ store ptr %0, ptr %2, align 8
+ %3 = load ptr, ptr %2, align 8
+ %4 = getelementptr i8, ptr %3, i64 -16
+ call void @_ZN1C1gEv(ptr noundef nonnull align 8 dereferenceable(32) %4)
+ ret void
+}
+
+; Function Attrs: noinline
+define dso_local void @_Z14call_through_AP1A(ptr noundef %0) #0 {
+ %2 = alloca ptr, i64 1, align 8
+ store ptr %0, ptr %2, align 8
+ %3 = load ptr, ptr %2, align 8
+ %4 = load ptr, ptr %3, align 8
+ %5 = getelementptr inbounds ptr, ptr %4, i32 0
+ %6 = load ptr, ptr %5, align 8
+ call void %6(ptr noundef nonnull align 8 dereferenceable(12) %3)
+ ret void
+}
+
+; Function Attrs: noinline
+define dso_local void @_Z14call_through_BP1B(ptr noundef %0) #0 {
+ %2 = alloca ptr, i64 1, align 8
+ store ptr %0, ptr %2, align 8
+ %3 = load ptr, ptr %2, align 8
+ %4 = load ptr, ptr %3, align 8
+ %5 = getelementptr inbounds ptr, ptr %4, i32 0
+ %6 = load ptr, ptr %5, align 8
+ call void %6(ptr noundef nonnull align 8 dereferenceable(12) %3)
+ ret void
+}
+
+; Function Attrs: noinline
+define dso_local void @_Z14call_through_CP1C(ptr noundef %0) #0 {
+ %2 = alloca ptr, i64 1, align 8
+ store ptr %0, ptr %2, align 8
+ %3 = load ptr, ptr %2, align 8
+ %4 = load ptr, ptr %3, align 8
+ %5 = getelementptr inbounds ptr, ptr %4, i32 0
+ %6 = load ptr, ptr %5, align 8
+ call void %6(ptr noundef nonnull align 8 dereferenceable(32) %3)
+ %7 = load ptr, ptr %2, align 8
+ %8 = load ptr, ptr %7, align 8
+ %9 = getelementptr inbounds ptr, ptr %8, i32 1
+ %10 = load ptr, ptr %9, align 8
+ call void %10(ptr noundef nonnull align 8 dereferenceable(32) %7)
+ ret void
+}
+
+; Function Attrs: noinline
+define dso_local void @_Z21direct_qualified_callP1C(ptr noundef %0) #0 {
+ %2 = alloca ptr, i64 1, align 8
+ store ptr %0, ptr %2, align 8
+ %3 = load ptr, ptr %2, align 8
+ call void @_ZN1C1fEv(ptr noundef nonnull align 8 dereferenceable(32) %3)
+ %4 = load ptr, ptr %2, align 8
+ call void @_ZN1C1gEv(ptr noundef nonnull align 8 dereferenceable(32) %4)
+ ret void
+}
+
+; Function Attrs: noinline
+define dso_local void @_Z10base_castsP1C(ptr noundef %0) #0 {
+ %2 = alloca ptr, i64 1, align 8
+ %3 = alloca ptr, i64 1, align 8
+ %4 = alloca ptr, i64 1, align 8
+ store ptr %0, ptr %2, align 8
+ %5 = load ptr, ptr %2, align 8
+ store ptr %5, ptr %3, align 8
+ %6 = load ptr, ptr %2, align 8
+ %7 = icmp eq ptr %6, null
+ %8 = getelementptr i8, ptr %6, i32 16
+ %9 = select i1 %7, ptr %6, ptr %8
+ store ptr %9, ptr %4, align 8
+ %10 = load ptr, ptr %3, align 8
+ %11 = load ptr, ptr %10, align 8
+ %12 = getelementptr inbounds ptr, ptr %11, i32 0
+ %13 = load ptr, ptr %12, align 8
+ call void %13(ptr noundef nonnull align 8 dereferenceable(12) %10)
+ %14 = load ptr, ptr %4, align 8
+ %15 = load ptr, ptr %14, align 8
+ %16 = getelementptr inbounds ptr, ptr %15, i32 0
+ %17 = load ptr, ptr %16, align 8
+ call void %17(ptr noundef nonnull align 8 dereferenceable(12) %14)
+ ret void
+}
+
+; Function Attrs: noinline
+define linkonce_odr void @_ZN1AC2Ev(ptr noundef nonnull align 8 dereferenceable(12) %0) #0 {
+ %2 = alloca ptr, i64 1, align 8
+ store ptr %0, ptr %2, align 8
+ %3 = load ptr, ptr %2, align 8
+ store ptr getelementptr inbounds nuw (i8, ptr @_ZTV1A, i64 16), ptr %3, align 8
+ ret void
+}
+
+; Function Attrs: noinline
+define linkonce_odr void @_ZN1BC2Ev(ptr noundef nonnull align 8 dereferenceable(12) %0) #0 {
+ %2 = alloca ptr, i64 1, align 8
+ store ptr %0, ptr %2, align 8
+ %3 = load ptr, ptr %2, align 8
+ store ptr getelementptr inbounds nuw (i8, ptr @_ZTV1B, i64 16), ptr %3, align 8
+ ret void
+}
+
+; Function Attrs: noinline
+define linkonce_odr void @_ZN1CC2Ev(ptr noundef nonnull align 8 dereferenceable(32) %0) #0 {
+ %2 = alloca ptr, i64 1, align 8
+ store ptr %0, ptr %2, align 8
+ %3 = load ptr, ptr %2, align 8
+ call void @_ZN1AC2Ev(ptr noundef nonnull align 8 dereferenceable(12) %3) #2
+ %4 = getelementptr i8, ptr %3, i32 16
+ call void @_ZN1BC2Ev(ptr noundef nonnull align 8 dereferenceable(12) %4) #2
+ store ptr getelementptr inbounds nuw (i8, ptr @_ZTV1C, i64 16), ptr %3, align 8
+ %5 = getelementptr i8, ptr %3, i32 16
+ store ptr getelementptr inbounds nuw (i8, ptr @_ZTV1C, i64 48), ptr %5, align 8
+ ret void
+}
+
+; Function Attrs: noinline
+define linkonce_odr void @_ZN1CC1Ev(ptr noundef nonnull align 8 dereferenceable(32) %0) #0 {
+ %2 = alloca ptr, i64 1, align 8
+ store ptr %0, ptr %2, align 8
+ %3 = load ptr, ptr %2, align 8
+ call void @_ZN1CC2Ev(ptr noundef nonnull align 8 dereferenceable(32) %3) #2
+ ret void
+}
+
+; Function Attrs: noinline
+define dso_local void @_Z18construct_and_callv() #0 {
+ %1 = alloca %struct.C, i64 1, align 8
+ %2 = alloca ptr, i64 1, align 8
+ %3 = alloca ptr, i64 1, align 8
+ call void @_ZN1CC1Ev(ptr noundef nonnull align 8 dereferenceable(32) %1) #2
+ call void @_ZN1C1fEv(ptr noundef nonnull align 8 dereferenceable(32) %1)
+ call void @_ZN1C1gEv(ptr noundef nonnull align 8 dereferenceable(32) %1)
+ store ptr %1, ptr %2, align 8
+ %4 = icmp eq ptr %1, null
+ %5 = getelementptr i8, ptr %1, i32 16
+ %6 = select i1 %4, ptr %1, ptr %5
+ store ptr %6, ptr %3, align 8
+ %7 = load ptr, ptr %2, align 8
+ %8 = load ptr, ptr %7, align 8
+ %9 = getelementptr inbounds ptr, ptr %8, i32 0
+ %10 = load ptr, ptr %9, align 8
+ call void %10(ptr noundef nonnull align 8 dereferenceable(12) %7)
+ %11 = load ptr, ptr %3, align 8
+ %12 = load ptr, ptr %11, align 8
+ %13 = getelementptr inbounds ptr, ptr %12, i32 0
+ %14 = load ptr, ptr %13, align 8
+ call void %14(ptr noundef nonnull align 8 dereferenceable(12) %11)
+ ret void
+}
+
+attributes #0 = { noinline "target-features"="+cx8,+mmx,+sse,+sse2,+x87" }
+attributes #1 = { noinline }
+attributes #2 = { nounwind }
+
+!llvm.module.flags = !{!0}
+
+!0 = !{i32 2, !"Debug Info Version", i32 3}
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index db3ac6340eccb..96625a36a0153 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -3109,6 +3109,60 @@ def CIR_VTableGetVirtualFnAddrOp : CIR_Op<"vtable.get_virtual_fn_addr", [
}];
}
+//===----------------------------------------------------------------------===//
+// VTableGetRelativeVirtualFnAddrOp
+//===----------------------------------------------------------------------===//
+
+def CIR_VTableGetRelativeVirtualFnAddrOp
+ : CIR_Op<"vtable.get_relative_virtual_fn_addr", [Pure]> {
+ let summary = "Resolve a virtual function pointer from a relative vtable";
+
+ let description = [{
+ The `vtable.get_relative_virtual_fn_addr` operation resolves a virtual
+ function pointer from a relative C++ ABI vtable.
+
+ Relative vtables store 32-bit signed offsets in their entries instead of
+ storing function pointers directly. The `vptr` operand is the vtable address
+ point. The `index` attribute is the virtual function index, counted in
+ vtable entries.
+
+ This operation returns the resolved function pointer. It does not return
+ the address of the vtable slot.
+
+ During CIR-to-LLVM lowering, this operation should lower to:
+
+ ```
+ call ptr @llvm.load.relative.i32(ptr %vptr, i32 index * 4)
+ ```
+
+ Example:
+ ```
+ %vptr.addr = cir.vtable.get_vptr %obj : !cir.ptr<!rec_C>
+ -> !cir.ptr<!cir.vptr>
+ %vptr = cir.load %vptr.addr : !cir.ptr<!cir.vptr>, !cir.vptr
+ %fn = cir.vtable.get_relative_virtual_fn_addr %vptr[1]
+ : !cir.vptr -> !cir.ptr<!cir.func<(!cir.ptr<!rec_C>) -> !s32i>>
+ %ret = cir.call %fn(%obj)
+ : (!cir.ptr<!cir.func<(!cir.ptr<!rec_C>) -> !s32i>>,
+ !cir.ptr<!rec_C>) -> !s32i
+ ```
+ }];
+
+ let arguments = (ins
+ CIR_VPtrType:$vptr,
+ I64Attr:$index
+ );
+
+ let results = (outs
+ CIR_PointerType:$result
+ );
+
+ let assemblyFormat = [{
+ $vptr `[` $index `]` attr-dict
+ `:` qualified(type($vptr)) `->` qualified(type($result))
+ }];
+}
+
//===----------------------------------------------------------------------===//
// VTableGetTypeInfoOp
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
index b6e6bd4857c9b..b79fb507f9db8 100644
--- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
@@ -1949,10 +1949,19 @@ CIRGenCallee CIRGenItaniumCXXABI::getVirtualFunctionPointer(
} else {
assert(!cir::MissingFeatures::emitTypeMetadataCodeForVCall());
- mlir::Value vfuncLoad;
+ mlir::Value vfuncLoad{};
if (cgm.getLangOpts().RelativeCXXABIVTables) {
- assert(!cir::MissingFeatures::vtableRelativeLayout());
- cgm.errorNYI(loc, "getVirtualFunctionPointer: isRelativeLayout");
+ // Relative vtables store 32-bit offsets in the vtable entries.
+ //
+ // Keep this as a CIR-level relative virtual call operation and let
+ // the CIR-to-LLVM lowering translate it to:
+ //
+ // call ptr @llvm.load.relative.i32(ptr %vtable,
+ // i32 (vtableIndex * 4))
+ //
+ // The result is the resolved virtual function pointer.
+ vfuncLoad = cir::VTableGetRelativeVirtualFnAddrOp::create(
+ builder, loc, tyPtr, vtable, vtableIndex);
} else {
auto vtableSlotPtr = cir::VTableGetVirtualFnAddrOp::create(
builder, loc, builder.getPointerTo(tyPtr), vtable, vtableIndex);
@@ -1977,6 +1986,7 @@ CIRGenCallee CIRGenItaniumCXXABI::getVirtualFunctionPointer(
return callee;
}
+
mlir::Value CIRGenItaniumCXXABI::getVTableAddressPointInStructorWithVTT(
CIRGenFunction &cgf, const CXXRecordDecl *vtableClass, BaseSubobject base,
const CXXRecordDecl *nearestVBase) {
@@ -2636,7 +2646,7 @@ static mlir::Value performTypeAdjustment(CIRGenFunction &cgf,
cir::PtrStrideOp::create(builder, loc, i8PtrTy, vtablePtr,
builder.getSInt64(virtualAdjustment, loc));
if (cgf.cgm.getLangOpts().RelativeCXXABIVTables) {
- assert(!cir::MissingFeatures::vtableRelativeLayout());
+ assert(!cir::MissingFeatures::vtableRelativeLayout()); // performTypeAdjustment
cgf.cgm.errorNYI("virtual adjustment for relative layout vtables");
} else {
offset = builder.createAlignedLoad(loc, cgf.ptrDiffTy, offsetPtr,
diff --git a/clang/lib/CIR/CodeGen/CIRGenVTables.cpp b/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
index 756ce62458290..8d9cf3e0a1ab7 100644
--- a/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
@@ -134,6 +134,40 @@ void CIRGenVTables::generateClassData(const CXXRecordDecl *rd) {
cgm.getCXXABI().emitVTableDefinitions(*this, rd);
}
+mlir::Attribute CIRGenVTables::getVTableIntegerOrNullComponent(
+ const VTableComponent &component) {
+ CIRGenBuilderTy &builder = cgm.getBuilder();
+ bool isRelative = cgm.getLangOpts().RelativeCXXABIVTables;
+
+ auto getOffsetAttr = [&](CharUnits offset) -> mlir::Attribute {
+ if (isRelative) {
+ return cir::IntAttr::get(builder.getSInt32Ty(), offset.getQuantity());
+ }
+ return builder.getConstPtrAttr(builder.getUInt8PtrTy(),
+ offset.getQuantity());
+ };
+
+ switch (component.getKind()) {
+ case VTableComponent::CK_UnusedFunctionPointer:
+ if (isRelative)
+ return cir::IntAttr::get(builder.getSInt32Ty(), 0);
+
+ return builder.getConstNullPtrAttr(builder.getUInt8PtrTy());
+
+ case VTableComponent::CK_VCallOffset:
+ return getOffsetAttr(component.getVCallOffset());
+
+ case VTableComponent::CK_VBaseOffset:
+ return getOffsetAttr(component.getVBaseOffset());
+
+ case VTableComponent::CK_OffsetToTop:
+ return getOffsetAttr(component.getOffsetToTop());
+
+ default:
+ llvm_unreachable("expected integer or null vtable component");
+ }
+}
+
mlir::Attribute CIRGenVTables::getVTableComponent(
const VTableLayout &layout, unsigned componentIndex, mlir::Attribute rtti,
unsigned &nextVTableThunkIndex, unsigned vtableAddressPoint,
@@ -286,7 +320,7 @@ cir::GlobalOp CIRGenVTables::generateConstructionVTable(
base.getBase(), out);
SmallString<256> name(outName);
- assert(!cir::MissingFeatures::vtableRelativeLayout());
+ assert(!cir::MissingFeatures::vtableRelativeLayout()); // generateConstructionVTable
cir::RecordType vtType = getVTableType(*vtLayout);
@@ -321,7 +355,7 @@ cir::GlobalOp CIRGenVTables::generateConstructionVTable(
cgm.setGVProperties(vtable, rd);
assert(!cir::MissingFeatures::vtableEmitMetadata());
- assert(!cir::MissingFeatures::vtableRelativeLayout());
+ assert(!cir::MissingFeatures::vtableRelativeLayout()); // generateConstructionVTable
return vtable;
}
diff --git a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp
index 270c55dfc4541..6cd2374673e69 100644
--- a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp
@@ -708,11 +708,13 @@ LogicalResult cir::VTableAttr::verify(
auto arrayElts = mlir::cast<ArrayAttr>(constArrayAttr.getElts());
arrayElts.walkImmediateSubElements(
[&](mlir::Attribute attr) {
- if (mlir::isa<ConstPtrAttr, GlobalViewAttr>(attr))
+ if (mlir::isa<cir::ConstPtrAttr, cir::GlobalViewAttr,
+ cir::IntAttr>(attr))
return;
- eltTypeCheck = emitError()
- << "expected GlobalViewAttr or ConstPtrAttr";
+ eltTypeCheck =
+ emitError()
+ << "expected GlobalViewAttr, ConstPtrAttr, or IntAttr";
},
[&](mlir::Type type) {});
if (eltTypeCheck.failed())
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index e17c7a209db6b..2aaf2b9f946a3 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -4201,6 +4201,28 @@ mlir::LogicalResult CIRToLLVMVTableGetVirtualFnAddrOpLowering::matchAndRewrite(
return mlir::success();
}
+mlir::LogicalResult
+CIRToLLVMVTableGetRelativeVirtualFnAddrOpLowering::matchAndRewrite(
+ cir::VTableGetRelativeVirtualFnAddrOp op, OpAdaptor adaptor,
+ mlir::ConversionPatternRewriter &rewriter) const {
+ mlir::Location loc = op.getLoc();
+
+ mlir::Type targetType = getTypeConverter()->convertType(op.getType());
+
+ // llvm.load.relative.i32 takes a byte offset, not an entry index.
+ uint64_t byteOffset = op.getIndex() * 4;
+
+ mlir::Value offset = mlir::LLVM::ConstantOp::create(
+ rewriter, loc, rewriter.getI32Type(),
+ rewriter.getI32IntegerAttr(static_cast<int32_t>(byteOffset)));
+
+ replaceOpWithCallLLVMIntrinsicOp(rewriter, op.getOperation(),
+ "llvm.load.relative.i32", targetType,
+ mlir::ValueRange{adaptor.getVptr(), offset});
+
+ return mlir::success();
+}
+
mlir::LogicalResult CIRToLLVMVTTAddrPointOpLowering::matchAndRewrite(
cir::VTTAddrPointOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
diff --git a/ll.ll b/ll.ll
new file mode 100644
index 0000000000000..7cae76a7071d9
--- /dev/null
+++ b/ll.ll
@@ -0,0 +1,250 @@
+; ModuleID = '1.cpp'
+source_filename = "1.cpp"
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+%struct.C = type { %struct.A.base, [4 x i8], %struct.B.base, i32 }
+%struct.A.base = type <{ ptr, i32 }>
+%struct.B.base = type <{ ptr, i32 }>
+
+$_ZN1CC1Ev = comdat any
+
+$_ZN1CC2Ev = comdat any
+
+$_ZN1AC2Ev = comdat any
+
+$_ZN1BC2Ev = comdat any
+
+ at _ZTV1A = unnamed_addr constant { [3 x ptr] } { [3 x ptr] [ptr null, ptr @_ZTI1A, ptr @_ZN1A1fEv] }, align 8
+ at _ZTI1A = constant { ptr, ptr } { ptr getelementptr inbounds (ptr, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 2), ptr @_ZTS1A }, align 8
+ at _ZTVN10__cxxabiv117__class_type_infoE = external global [0 x ptr]
+ at _ZTS1A = constant [3 x i8] c"1A\00", align 1
+ at _ZTV1B = unnamed_addr constant { [3 x ptr] } { [3 x ptr] [ptr null, ptr @_ZTI1B, ptr @_ZN1B1gEv] }, align 8
+ at _ZTI1B = constant { ptr, ptr } { ptr getelementptr inbounds (ptr, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 2), ptr @_ZTS1B }, align 8
+ at _ZTS1B = constant [3 x i8] c"1B\00", align 1
+ at _ZTV1C = unnamed_addr constant { [4 x ptr], [3 x ptr] } { [4 x ptr] [ptr null, ptr @_ZTI1C, ptr @_ZN1C1fEv, ptr @_ZN1C1gEv], [3 x ptr] [ptr inttoptr (i64 -16 to ptr), ptr @_ZTI1C, ptr @_ZThn16_N1C1gEv] }, align 8
+ at _ZTI1C = constant { ptr, ptr, i32, i32, ptr, i64, ptr, i64 } { ptr getelementptr inbounds (ptr, ptr @_ZTVN10__cxxabiv121__vmi_class_type_infoE, i64 2), ptr @_ZTS1C, i32 0, i32 2, ptr @_ZTI1A, i64 2, ptr @_ZTI1B, i64 4098 }, align 8
+ at _ZTVN10__cxxabiv121__vmi_class_type_infoE = external global [0 x ptr]
+ at _ZTS1C = constant [3 x i8] c"1C\00", align 1
+
+; Function Attrs: mustprogress noinline nounwind optnone
+define dso_local void @_ZN1A1fEv(ptr noundef nonnull align 8 dereferenceable(12) %this) unnamed_addr #0 align 2 {
+entry:
+ %this.addr = alloca ptr, align 8
+ store ptr %this, ptr %this.addr, align 8
+ %this1 = load ptr, ptr %this.addr, align 8
+ ret void
+}
+
+; Function Attrs: mustprogress noinline nounwind optnone
+define dso_local void @_ZN1B1gEv(ptr noundef nonnull align 8 dereferenceable(12) %this) unnamed_addr #0 align 2 {
+entry:
+ %this.addr = alloca ptr, align 8
+ store ptr %this, ptr %this.addr, align 8
+ %this1 = load ptr, ptr %this.addr, align 8
+ ret void
+}
+
+; Function Attrs: mustprogress noinline nounwind optnone
+define dso_local void @_ZN1C1fEv(ptr noundef nonnull align 8 dereferenceable(32) %this) unnamed_addr #0 align 2 {
+entry:
+ %this.addr = alloca ptr, align 8
+ store ptr %this, ptr %this.addr, align 8
+ %this1 = load ptr, ptr %this.addr, align 8
+ ret void
+}
+
+; Function Attrs: mustprogress noinline nounwind optnone
+define dso_local void @_ZN1C1gEv(ptr noundef nonnull align 8 dereferenceable(32) %this) unnamed_addr #0 align 2 {
+entry:
+ %this.addr = alloca ptr, align 8
+ store ptr %this, ptr %this.addr, align 8
+ %this1 = load ptr, ptr %this.addr, align 8
+ ret void
+}
+
+; Function Attrs: noinline nounwind optnone
+define dso_local void @_ZThn16_N1C1gEv(ptr noundef %this) unnamed_addr #1 align 2 {
+entry:
+ %this.addr = alloca ptr, align 8
+ store ptr %this, ptr %this.addr, align 8
+ %this1 = load ptr, ptr %this.addr, align 8
+ %0 = getelementptr inbounds i8, ptr %this1, i64 -16
+ tail call void @_ZN1C1gEv(ptr noundef nonnull align 8 dereferenceable(32) %0)
+ ret void
+}
+
+; Function Attrs: mustprogress noinline nounwind optnone
+define dso_local void @_Z14call_through_AP1A(ptr noundef %pa) #0 {
+entry:
+ %pa.addr = alloca ptr, align 8
+ store ptr %pa, ptr %pa.addr, align 8
+ %0 = load ptr, ptr %pa.addr, align 8
+ %vtable = load ptr, ptr %0, align 8
+ %vfn = getelementptr inbounds ptr, ptr %vtable, i64 0
+ %1 = load ptr, ptr %vfn, align 8
+ call void %1(ptr noundef nonnull align 8 dereferenceable(12) %0)
+ ret void
+}
+
+; Function Attrs: mustprogress noinline nounwind optnone
+define dso_local void @_Z14call_through_BP1B(ptr noundef %pb) #0 {
+entry:
+ %pb.addr = alloca ptr, align 8
+ store ptr %pb, ptr %pb.addr, align 8
+ %0 = load ptr, ptr %pb.addr, align 8
+ %vtable = load ptr, ptr %0, align 8
+ %vfn = getelementptr inbounds ptr, ptr %vtable, i64 0
+ %1 = load ptr, ptr %vfn, align 8
+ call void %1(ptr noundef nonnull align 8 dereferenceable(12) %0)
+ ret void
+}
+
+; Function Attrs: mustprogress noinline nounwind optnone
+define dso_local void @_Z14call_through_CP1C(ptr noundef %pc) #0 {
+entry:
+ %pc.addr = alloca ptr, align 8
+ store ptr %pc, ptr %pc.addr, align 8
+ %0 = load ptr, ptr %pc.addr, align 8
+ %vtable = load ptr, ptr %0, align 8
+ %vfn = getelementptr inbounds ptr, ptr %vtable, i64 0
+ %1 = load ptr, ptr %vfn, align 8
+ call void %1(ptr noundef nonnull align 8 dereferenceable(32) %0)
+ %2 = load ptr, ptr %pc.addr, align 8
+ %vtable1 = load ptr, ptr %2, align 8
+ %vfn2 = getelementptr inbounds ptr, ptr %vtable1, i64 1
+ %3 = load ptr, ptr %vfn2, align 8
+ call void %3(ptr noundef nonnull align 8 dereferenceable(32) %2)
+ ret void
+}
+
+; Function Attrs: mustprogress noinline nounwind optnone
+define dso_local void @_Z21direct_qualified_callP1C(ptr noundef %pc) #0 {
+entry:
+ %pc.addr = alloca ptr, align 8
+ store ptr %pc, ptr %pc.addr, align 8
+ %0 = load ptr, ptr %pc.addr, align 8
+ call void @_ZN1C1fEv(ptr noundef nonnull align 8 dereferenceable(32) %0)
+ %1 = load ptr, ptr %pc.addr, align 8
+ call void @_ZN1C1gEv(ptr noundef nonnull align 8 dereferenceable(32) %1)
+ ret void
+}
+
+; Function Attrs: mustprogress noinline nounwind optnone
+define dso_local void @_Z10base_castsP1C(ptr noundef %pc) #0 {
+entry:
+ %pc.addr = alloca ptr, align 8
+ %pa = alloca ptr, align 8
+ %pb = alloca ptr, align 8
+ store ptr %pc, ptr %pc.addr, align 8
+ %0 = load ptr, ptr %pc.addr, align 8
+ store ptr %0, ptr %pa, align 8
+ %1 = load ptr, ptr %pc.addr, align 8
+ %2 = icmp eq ptr %1, null
+ br i1 %2, label %cast.end, label %cast.notnull
+
+cast.notnull: ; preds = %entry
+ %add.ptr = getelementptr inbounds i8, ptr %1, i64 16
+ br label %cast.end
+
+cast.end: ; preds = %cast.notnull, %entry
+ %cast.result = phi ptr [ %add.ptr, %cast.notnull ], [ null, %entry ]
+ store ptr %cast.result, ptr %pb, align 8
+ %3 = load ptr, ptr %pa, align 8
+ %vtable = load ptr, ptr %3, align 8
+ %vfn = getelementptr inbounds ptr, ptr %vtable, i64 0
+ %4 = load ptr, ptr %vfn, align 8
+ call void %4(ptr noundef nonnull align 8 dereferenceable(12) %3)
+ %5 = load ptr, ptr %pb, align 8
+ %vtable1 = load ptr, ptr %5, align 8
+ %vfn2 = getelementptr inbounds ptr, ptr %vtable1, i64 0
+ %6 = load ptr, ptr %vfn2, align 8
+ call void %6(ptr noundef nonnull align 8 dereferenceable(12) %5)
+ ret void
+}
+
+; Function Attrs: mustprogress noinline nounwind optnone
+define dso_local void @_Z18construct_and_callv() #0 {
+entry:
+ %obj = alloca %struct.C, align 8
+ %pa = alloca ptr, align 8
+ %pb = alloca ptr, align 8
+ call void @_ZN1CC1Ev(ptr noundef nonnull align 8 dereferenceable(32) %obj) #2
+ call void @_ZN1C1fEv(ptr noundef nonnull align 8 dereferenceable(32) %obj)
+ call void @_ZN1C1gEv(ptr noundef nonnull align 8 dereferenceable(32) %obj)
+ store ptr %obj, ptr %pa, align 8
+ %0 = icmp eq ptr %obj, null
+ br i1 %0, label %cast.end, label %cast.notnull
+
+cast.notnull: ; preds = %entry
+ %add.ptr = getelementptr inbounds i8, ptr %obj, i64 16
+ br label %cast.end
+
+cast.end: ; preds = %cast.notnull, %entry
+ %cast.result = phi ptr [ %add.ptr, %cast.notnull ], [ null, %entry ]
+ store ptr %cast.result, ptr %pb, align 8
+ %1 = load ptr, ptr %pa, align 8
+ %vtable = load ptr, ptr %1, align 8
+ %vfn = getelementptr inbounds ptr, ptr %vtable, i64 0
+ %2 = load ptr, ptr %vfn, align 8
+ call void %2(ptr noundef nonnull align 8 dereferenceable(12) %1)
+ %3 = load ptr, ptr %pb, align 8
+ %vtable1 = load ptr, ptr %3, align 8
+ %vfn2 = getelementptr inbounds ptr, ptr %vtable1, i64 0
+ %4 = load ptr, ptr %vfn2, align 8
+ call void %4(ptr noundef nonnull align 8 dereferenceable(12) %3)
+ ret void
+}
+
+; Function Attrs: mustprogress noinline nounwind optnone
+define linkonce_odr void @_ZN1CC1Ev(ptr noundef nonnull align 8 dereferenceable(32) %this) unnamed_addr #0 comdat align 2 {
+entry:
+ %this.addr = alloca ptr, align 8
+ store ptr %this, ptr %this.addr, align 8
+ %this1 = load ptr, ptr %this.addr, align 8
+ call void @_ZN1CC2Ev(ptr noundef nonnull align 8 dereferenceable(32) %this1) #2
+ ret void
+}
+
+; Function Attrs: mustprogress noinline nounwind optnone
+define linkonce_odr void @_ZN1CC2Ev(ptr noundef nonnull align 8 dereferenceable(32) %this) unnamed_addr #0 comdat align 2 {
+entry:
+ %this.addr = alloca ptr, align 8
+ store ptr %this, ptr %this.addr, align 8
+ %this1 = load ptr, ptr %this.addr, align 8
+ call void @_ZN1AC2Ev(ptr noundef nonnull align 8 dereferenceable(12) %this1) #2
+ %0 = getelementptr inbounds i8, ptr %this1, i64 16
+ call void @_ZN1BC2Ev(ptr noundef nonnull align 8 dereferenceable(12) %0) #2
+ store ptr getelementptr inbounds inrange(-16, 16) ({ [4 x ptr], [3 x ptr] }, ptr @_ZTV1C, i32 0, i32 0, i32 2), ptr %this1, align 8
+ %add.ptr = getelementptr inbounds i8, ptr %this1, i64 16
+ store ptr getelementptr inbounds inrange(-16, 8) ({ [4 x ptr], [3 x ptr] }, ptr @_ZTV1C, i32 0, i32 1, i32 2), ptr %add.ptr, align 8
+ ret void
+}
+
+; Function Attrs: mustprogress noinline nounwind optnone
+define linkonce_odr void @_ZN1AC2Ev(ptr noundef nonnull align 8 dereferenceable(12) %this) unnamed_addr #0 comdat align 2 {
+entry:
+ %this.addr = alloca ptr, align 8
+ store ptr %this, ptr %this.addr, align 8
+ %this1 = load ptr, ptr %this.addr, align 8
+ store ptr getelementptr inbounds inrange(-16, 8) ({ [3 x ptr] }, ptr @_ZTV1A, i32 0, i32 0, i32 2), ptr %this1, align 8
+ ret void
+}
+
+; Function Attrs: mustprogress noinline nounwind optnone
+define linkonce_odr void @_ZN1BC2Ev(ptr noundef nonnull align 8 dereferenceable(12) %this) unnamed_addr #0 comdat align 2 {
+entry:
+ %this.addr = alloca ptr, align 8
+ store ptr %this, ptr %this.addr, align 8
+ %this1 = load ptr, ptr %this.addr, align 8
+ store ptr getelementptr inbounds inrange(-16, 8) ({ [3 x ptr] }, ptr @_ZTV1B, i32 0, i32 0, i32 2), ptr %this1, align 8
+ ret void
+}
+
+attributes #0 = { mustprogress noinline nounwind optnone "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" }
+attributes #1 = { noinline nounwind optnone "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" }
+attributes #2 = { nounwind }
+
+!llvm.ident = !{!0}
+
+!0 = !{!"clang version 23.0.0git (git at github.com:xiongzile/llvm-project.git c1ef54f3f3c46982dd7dc03c41f66f8e274ac2e6)"}
diff --git a/rel-ll.ll b/rel-ll.ll
new file mode 100644
index 0000000000000..46c7a87b63872
--- /dev/null
+++ b/rel-ll.ll
@@ -0,0 +1,259 @@
+; ModuleID = '1.cpp'
+source_filename = "1.cpp"
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+%struct.C = type { %struct.A.base, [4 x i8], %struct.B.base, i32 }
+%struct.A.base = type <{ ptr, i32 }>
+%struct.B.base = type <{ ptr, i32 }>
+
+$_ZN1CC1Ev = comdat any
+
+$_ZN1CC2Ev = comdat any
+
+$_ZN1AC2Ev = comdat any
+
+$_ZN1BC2Ev = comdat any
+
+$_ZTI1A.rtti_proxy = comdat any
+
+$_ZTI1B.rtti_proxy = comdat any
+
+$_ZTI1C.rtti_proxy = comdat any
+
+ at _ZTV1A.local = internal unnamed_addr constant { [3 x i32] } { [3 x i32] [i32 0, i32 trunc (i64 sub (i64 ptrtoint (ptr @_ZTI1A.rtti_proxy to i64), i64 ptrtoint (ptr getelementptr inbounds ({ [3 x i32] }, ptr @_ZTV1A.local, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (ptr dso_local_equivalent @_ZN1A1fEv to i64), i64 ptrtoint (ptr getelementptr inbounds ({ [3 x i32] }, ptr @_ZTV1A.local, i32 0, i32 0, i32 2) to i64)) to i32)] }, align 4
+ at _ZTI1A = constant { ptr, ptr } { ptr getelementptr inbounds (i8, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i32 8), ptr @_ZTS1A }, align 8
+ at _ZTVN10__cxxabiv117__class_type_infoE = external global [0 x ptr]
+ at _ZTS1A = constant [3 x i8] c"1A\00", align 1
+ at _ZTI1A.rtti_proxy = linkonce_odr hidden unnamed_addr constant ptr @_ZTI1A, comdat
+ at _ZTV1B.local = internal unnamed_addr constant { [3 x i32] } { [3 x i32] [i32 0, i32 trunc (i64 sub (i64 ptrtoint (ptr @_ZTI1B.rtti_proxy to i64), i64 ptrtoint (ptr getelementptr inbounds ({ [3 x i32] }, ptr @_ZTV1B.local, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (ptr dso_local_equivalent @_ZN1B1gEv to i64), i64 ptrtoint (ptr getelementptr inbounds ({ [3 x i32] }, ptr @_ZTV1B.local, i32 0, i32 0, i32 2) to i64)) to i32)] }, align 4
+ at _ZTI1B = constant { ptr, ptr } { ptr getelementptr inbounds (i8, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i32 8), ptr @_ZTS1B }, align 8
+ at _ZTS1B = constant [3 x i8] c"1B\00", align 1
+ at _ZTI1B.rtti_proxy = linkonce_odr hidden unnamed_addr constant ptr @_ZTI1B, comdat
+ at _ZTV1C.local = internal unnamed_addr constant { [4 x i32], [3 x i32] } { [4 x i32] [i32 0, i32 trunc (i64 sub (i64 ptrtoint (ptr @_ZTI1C.rtti_proxy to i64), i64 ptrtoint (ptr getelementptr inbounds ({ [4 x i32], [3 x i32] }, ptr @_ZTV1C.local, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (ptr dso_local_equivalent @_ZN1C1fEv to i64), i64 ptrtoint (ptr getelementptr inbounds ({ [4 x i32], [3 x i32] }, ptr @_ZTV1C.local, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (ptr dso_local_equivalent @_ZN1C1gEv to i64), i64 ptrtoint (ptr getelementptr inbounds ({ [4 x i32], [3 x i32] }, ptr @_ZTV1C.local, i32 0, i32 0, i32 2) to i64)) to i32)], [3 x i32] [i32 -16, i32 trunc (i64 sub (i64 ptrtoint (ptr @_ZTI1C.rtti_proxy to i64), i64 ptrtoint (ptr getelementptr inbounds ({ [4 x i32], [3 x i32] }, ptr @_ZTV1C.local, i32 0, i32 1, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (ptr dso_local_equivalent @_ZThn16_N1C1gEv to i64), i64 ptrtoint (ptr getelementptr inbounds ({ [4 x i32], [3 x i32] }, ptr @_ZTV1C.local, i32 0, i32 1, i32 2) to i64)) to i32)] }, align 4
+ at _ZTI1C = constant { ptr, ptr, i32, i32, ptr, i64, ptr, i64 } { ptr getelementptr inbounds (i8, ptr @_ZTVN10__cxxabiv121__vmi_class_type_infoE, i32 8), ptr @_ZTS1C, i32 0, i32 2, ptr @_ZTI1A, i64 2, ptr @_ZTI1B, i64 4098 }, align 8
+ at _ZTVN10__cxxabiv121__vmi_class_type_infoE = external global [0 x ptr]
+ at _ZTS1C = constant [3 x i8] c"1C\00", align 1
+ at _ZTI1C.rtti_proxy = linkonce_odr hidden unnamed_addr constant ptr @_ZTI1C, comdat
+
+ at _ZTV1A = unnamed_addr alias { [3 x i32] }, ptr @_ZTV1A.local
+ at _ZTV1B = unnamed_addr alias { [3 x i32] }, ptr @_ZTV1B.local
+ at _ZTV1C = unnamed_addr alias { [4 x i32], [3 x i32] }, ptr @_ZTV1C.local
+
+; Function Attrs: mustprogress noinline nounwind optnone
+define dso_local void @_ZN1A1fEv(ptr noundef nonnull align 8 dereferenceable(12) %this) unnamed_addr #0 align 2 {
+entry:
+ %this.addr = alloca ptr, align 8
+ store ptr %this, ptr %this.addr, align 8
+ %this1 = load ptr, ptr %this.addr, align 8
+ ret void
+}
+
+; Function Attrs: mustprogress noinline nounwind optnone
+define dso_local void @_ZN1B1gEv(ptr noundef nonnull align 8 dereferenceable(12) %this) unnamed_addr #0 align 2 {
+entry:
+ %this.addr = alloca ptr, align 8
+ store ptr %this, ptr %this.addr, align 8
+ %this1 = load ptr, ptr %this.addr, align 8
+ ret void
+}
+
+; Function Attrs: mustprogress noinline nounwind optnone
+define dso_local void @_ZN1C1fEv(ptr noundef nonnull align 8 dereferenceable(32) %this) unnamed_addr #0 align 2 {
+entry:
+ %this.addr = alloca ptr, align 8
+ store ptr %this, ptr %this.addr, align 8
+ %this1 = load ptr, ptr %this.addr, align 8
+ ret void
+}
+
+; Function Attrs: mustprogress noinline nounwind optnone
+define dso_local void @_ZN1C1gEv(ptr noundef nonnull align 8 dereferenceable(32) %this) unnamed_addr #0 align 2 {
+entry:
+ %this.addr = alloca ptr, align 8
+ store ptr %this, ptr %this.addr, align 8
+ %this1 = load ptr, ptr %this.addr, align 8
+ ret void
+}
+
+; Function Attrs: noinline nounwind optnone
+define dso_local void @_ZThn16_N1C1gEv(ptr noundef %this) unnamed_addr #1 align 2 {
+entry:
+ %this.addr = alloca ptr, align 8
+ store ptr %this, ptr %this.addr, align 8
+ %this1 = load ptr, ptr %this.addr, align 8
+ %0 = getelementptr inbounds i8, ptr %this1, i64 -16
+ tail call void @_ZN1C1gEv(ptr noundef nonnull align 8 dereferenceable(32) %0)
+ ret void
+}
+
+; Function Attrs: mustprogress noinline nounwind optnone
+define dso_local void @_Z14call_through_AP1A(ptr noundef %pa) #0 {
+entry:
+ %pa.addr = alloca ptr, align 8
+ store ptr %pa, ptr %pa.addr, align 8
+ %0 = load ptr, ptr %pa.addr, align 8
+ %vtable = load ptr, ptr %0, align 8
+ %1 = call ptr @llvm.load.relative.i32(ptr %vtable, i32 0)
+ call void %1(ptr noundef nonnull align 8 dereferenceable(12) %0)
+ ret void
+}
+
+; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: read)
+declare ptr @llvm.load.relative.i32(ptr, i32) #2
+
+; Function Attrs: mustprogress noinline nounwind optnone
+define dso_local void @_Z14call_through_BP1B(ptr noundef %pb) #0 {
+entry:
+ %pb.addr = alloca ptr, align 8
+ store ptr %pb, ptr %pb.addr, align 8
+ %0 = load ptr, ptr %pb.addr, align 8
+ %vtable = load ptr, ptr %0, align 8
+ %1 = call ptr @llvm.load.relative.i32(ptr %vtable, i32 0)
+ call void %1(ptr noundef nonnull align 8 dereferenceable(12) %0)
+ ret void
+}
+
+; Function Attrs: mustprogress noinline nounwind optnone
+define dso_local void @_Z14call_through_CP1C(ptr noundef %pc) #0 {
+entry:
+ %pc.addr = alloca ptr, align 8
+ store ptr %pc, ptr %pc.addr, align 8
+ %0 = load ptr, ptr %pc.addr, align 8
+ %vtable = load ptr, ptr %0, align 8
+ %1 = call ptr @llvm.load.relative.i32(ptr %vtable, i32 0)
+ call void %1(ptr noundef nonnull align 8 dereferenceable(32) %0)
+ %2 = load ptr, ptr %pc.addr, align 8
+ %vtable1 = load ptr, ptr %2, align 8
+ %3 = call ptr @llvm.load.relative.i32(ptr %vtable1, i32 4)
+ call void %3(ptr noundef nonnull align 8 dereferenceable(32) %2)
+ ret void
+}
+
+; Function Attrs: mustprogress noinline nounwind optnone
+define dso_local void @_Z21direct_qualified_callP1C(ptr noundef %pc) #0 {
+entry:
+ %pc.addr = alloca ptr, align 8
+ store ptr %pc, ptr %pc.addr, align 8
+ %0 = load ptr, ptr %pc.addr, align 8
+ call void @_ZN1C1fEv(ptr noundef nonnull align 8 dereferenceable(32) %0)
+ %1 = load ptr, ptr %pc.addr, align 8
+ call void @_ZN1C1gEv(ptr noundef nonnull align 8 dereferenceable(32) %1)
+ ret void
+}
+
+; Function Attrs: mustprogress noinline nounwind optnone
+define dso_local void @_Z10base_castsP1C(ptr noundef %pc) #0 {
+entry:
+ %pc.addr = alloca ptr, align 8
+ %pa = alloca ptr, align 8
+ %pb = alloca ptr, align 8
+ store ptr %pc, ptr %pc.addr, align 8
+ %0 = load ptr, ptr %pc.addr, align 8
+ store ptr %0, ptr %pa, align 8
+ %1 = load ptr, ptr %pc.addr, align 8
+ %2 = icmp eq ptr %1, null
+ br i1 %2, label %cast.end, label %cast.notnull
+
+cast.notnull: ; preds = %entry
+ %add.ptr = getelementptr inbounds i8, ptr %1, i32 16
+ br label %cast.end
+
+cast.end: ; preds = %cast.notnull, %entry
+ %cast.result = phi ptr [ %add.ptr, %cast.notnull ], [ null, %entry ]
+ store ptr %cast.result, ptr %pb, align 8
+ %3 = load ptr, ptr %pa, align 8
+ %vtable = load ptr, ptr %3, align 8
+ %4 = call ptr @llvm.load.relative.i32(ptr %vtable, i32 0)
+ call void %4(ptr noundef nonnull align 8 dereferenceable(12) %3)
+ %5 = load ptr, ptr %pb, align 8
+ %vtable1 = load ptr, ptr %5, align 8
+ %6 = call ptr @llvm.load.relative.i32(ptr %vtable1, i32 0)
+ call void %6(ptr noundef nonnull align 8 dereferenceable(12) %5)
+ ret void
+}
+
+; Function Attrs: mustprogress noinline nounwind optnone
+define dso_local void @_Z18construct_and_callv() #0 {
+entry:
+ %obj = alloca %struct.C, align 8
+ %pa = alloca ptr, align 8
+ %pb = alloca ptr, align 8
+ call void @_ZN1CC1Ev(ptr noundef nonnull align 8 dereferenceable(32) %obj) #3
+ call void @_ZN1C1fEv(ptr noundef nonnull align 8 dereferenceable(32) %obj)
+ call void @_ZN1C1gEv(ptr noundef nonnull align 8 dereferenceable(32) %obj)
+ store ptr %obj, ptr %pa, align 8
+ %0 = icmp eq ptr %obj, null
+ br i1 %0, label %cast.end, label %cast.notnull
+
+cast.notnull: ; preds = %entry
+ %add.ptr = getelementptr inbounds i8, ptr %obj, i32 16
+ br label %cast.end
+
+cast.end: ; preds = %cast.notnull, %entry
+ %cast.result = phi ptr [ %add.ptr, %cast.notnull ], [ null, %entry ]
+ store ptr %cast.result, ptr %pb, align 8
+ %1 = load ptr, ptr %pa, align 8
+ %vtable = load ptr, ptr %1, align 8
+ %2 = call ptr @llvm.load.relative.i32(ptr %vtable, i32 0)
+ call void %2(ptr noundef nonnull align 8 dereferenceable(12) %1)
+ %3 = load ptr, ptr %pb, align 8
+ %vtable1 = load ptr, ptr %3, align 8
+ %4 = call ptr @llvm.load.relative.i32(ptr %vtable1, i32 0)
+ call void %4(ptr noundef nonnull align 8 dereferenceable(12) %3)
+ ret void
+}
+
+; Function Attrs: mustprogress noinline nounwind optnone
+define linkonce_odr void @_ZN1CC1Ev(ptr noundef nonnull align 8 dereferenceable(32) %this) unnamed_addr #0 comdat align 2 {
+entry:
+ %this.addr = alloca ptr, align 8
+ store ptr %this, ptr %this.addr, align 8
+ %this1 = load ptr, ptr %this.addr, align 8
+ call void @_ZN1CC2Ev(ptr noundef nonnull align 8 dereferenceable(32) %this1) #3
+ ret void
+}
+
+; Function Attrs: mustprogress noinline nounwind optnone
+define linkonce_odr void @_ZN1CC2Ev(ptr noundef nonnull align 8 dereferenceable(32) %this) unnamed_addr #0 comdat align 2 {
+entry:
+ %this.addr = alloca ptr, align 8
+ store ptr %this, ptr %this.addr, align 8
+ %this1 = load ptr, ptr %this.addr, align 8
+ call void @_ZN1AC2Ev(ptr noundef nonnull align 8 dereferenceable(12) %this1) #3
+ %0 = getelementptr inbounds i8, ptr %this1, i64 16
+ call void @_ZN1BC2Ev(ptr noundef nonnull align 8 dereferenceable(12) %0) #3
+ store ptr getelementptr inbounds inrange(-8, 8) ({ [4 x i32], [3 x i32] }, ptr @_ZTV1C.local, i32 0, i32 0, i32 2), ptr %this1, align 8
+ %add.ptr = getelementptr inbounds i8, ptr %this1, i32 16
+ store ptr getelementptr inbounds inrange(-8, 4) ({ [4 x i32], [3 x i32] }, ptr @_ZTV1C.local, i32 0, i32 1, i32 2), ptr %add.ptr, align 8
+ ret void
+}
+
+; Function Attrs: mustprogress noinline nounwind optnone
+define linkonce_odr void @_ZN1AC2Ev(ptr noundef nonnull align 8 dereferenceable(12) %this) unnamed_addr #0 comdat align 2 {
+entry:
+ %this.addr = alloca ptr, align 8
+ store ptr %this, ptr %this.addr, align 8
+ %this1 = load ptr, ptr %this.addr, align 8
+ store ptr getelementptr inbounds inrange(-8, 4) ({ [3 x i32] }, ptr @_ZTV1A.local, i32 0, i32 0, i32 2), ptr %this1, align 8
+ ret void
+}
+
+; Function Attrs: mustprogress noinline nounwind optnone
+define linkonce_odr void @_ZN1BC2Ev(ptr noundef nonnull align 8 dereferenceable(12) %this) unnamed_addr #0 comdat align 2 {
+entry:
+ %this.addr = alloca ptr, align 8
+ store ptr %this, ptr %this.addr, align 8
+ %this1 = load ptr, ptr %this.addr, align 8
+ store ptr getelementptr inbounds inrange(-8, 4) ({ [3 x i32] }, ptr @_ZTV1B.local, i32 0, i32 0, i32 2), ptr %this1, align 8
+ ret void
+}
+
+attributes #0 = { mustprogress noinline nounwind optnone "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" }
+attributes #1 = { noinline nounwind optnone "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" }
+attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: read) }
+attributes #3 = { nounwind }
+
+!llvm.ident = !{!0}
+
+!0 = !{!"clang version 23.0.0git (git at github.com:xiongzile/llvm-project.git c1ef54f3f3c46982dd7dc03c41f66f8e274ac2e6)"}
>From 9f64e4cf0fb16d1b378af243172364b10e261995 Mon Sep 17 00:00:00 2001
From: Zile Xiong <xiongzile99 at gmail.com>
Date: Wed, 6 May 2026 23:33:48 +0800
Subject: [PATCH 4/5] [CIR] add emit relative vtable support
---
clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp | 40 ++++++++++-------
clang/lib/CIR/CodeGen/CIRGenVTables.cpp | 43 ++++++++++---------
clang/lib/CIR/CodeGen/CIRGenVTables.h | 3 ++
clang/lib/CIR/Dialect/IR/CIRAttrs.cpp | 4 +-
4 files changed, 52 insertions(+), 38 deletions(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
index b79fb507f9db8..70c9ff9492cf3 100644
--- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
@@ -26,6 +26,7 @@
#include "clang/AST/VTableBuilder.h"
#include "clang/CIR/MissingFeatures.h"
#include "llvm/Support/ErrorHandling.h"
+#include <cassert>
using namespace clang;
using namespace clang::CIRGen;
@@ -479,20 +480,27 @@ void CIRGenItaniumCXXABI::emitVTableDefinitions(CIRGenVTables &cgvt,
cir::GlobalOp vtable = getAddrOfVTable(rd, CharUnits());
if (vtable.hasInitializer())
return;
-
+ llvm::errs() << "vtable = ";
+ vtable->dump();
+ llvm::errs() << "";
ItaniumVTableContext &vtContext = cgm.getItaniumVTableContext();
const VTableLayout &vtLayout = vtContext.getVTableLayout(rd);
cir::GlobalLinkageKind linkage = cgm.getVTableLinkage(rd);
mlir::Attribute rtti =
cgm.getAddrOfRTTIDescriptor(cgm.getLoc(rd->getBeginLoc()),
cgm.getASTContext().getCanonicalTagType(rd));
-
+ llvm::errs() << "rtti = ";
+ rtti.dump();
+ llvm::errs() << "\n";
// Classic codegen uses ConstantInitBuilder here, which is a very general
// and feature-rich class to generate initializers for global values.
// For now, this is using a simpler approach to create the initializer in CIR.
cgvt.createVTableInitializer(vtable, vtLayout, rtti,
cir::isLocalLinkage(linkage));
-
+ llvm::errs() << "new vtable = ";
+ vtable->dump();
+ llvm::errs() << "\n";
+ llvm::errs() << "\n";
// Set the correct linkage.
vtable.setLinkage(linkage);
@@ -530,10 +538,11 @@ void CIRGenItaniumCXXABI::emitVTableDefinitions(CIRGenVTables &cgvt,
"emitVTableDefinitions: WholeProgramVTables");
}
- assert(!cir::MissingFeatures::vtableRelativeLayout());
- if (cgm.getLangOpts().RelativeCXXABIVTables) {
- cgm.errorNYI(rd->getSourceRange(), "vtableRelativeLayout");
- }
+ // TODO: by @Elio
+ // assert(!cir::MissingFeatures::vtableRelativeLayout());
+ // if (cgm.getLangOpts().RelativeCXXABIVTables) {
+ // cgm.errorNYI(rd->getSourceRange(), "vtableRelativeLayout");
+ // }
}
mlir::Value CIRGenItaniumCXXABI::emitVirtualDestructorCall(
@@ -1223,11 +1232,6 @@ void CIRGenItaniumRTTIBuilder::buildVTablePointer(mlir::Location loc,
const char *vTableName = vTableClassNameForType(cgm, ty);
// Check if the alias exists. If it doesn't, then get or create the global.
- if (cgm.getLangOpts().RelativeCXXABIVTables) {
- cgm.errorNYI("buildVTablePointer: isRelativeLayout");
- return;
- }
-
mlir::Type vtableGlobalTy = builder.getPointerTo(builder.getUInt8PtrTy());
llvm::Align align = cgm.getDataLayout().getABITypeAlign(vtableGlobalTy);
cir::GlobalOp vTable = cgm.createOrReplaceCXXRuntimeVariable(
@@ -1235,13 +1239,17 @@ void CIRGenItaniumRTTIBuilder::buildVTablePointer(mlir::Location loc,
CharUnits::fromQuantity(align));
// The vtable address point is 2.
+ SmallVector<mlir::Attribute, 4> offsets{
+ cgm.getBuilder().getI32IntegerAttr(2)};
+ auto indices = mlir::ArrayAttr::get(builder.getContext(), offsets);
mlir::Attribute field{};
if (cgm.getLangOpts().RelativeCXXABIVTables) {
- cgm.errorNYI("buildVTablePointer: isRelativeLayout");
+ // TODO: by @Elio.
+ // For relative vtables, this needs special handling during lowering: the
+ // GlobalViewAttr target should be emitted as target - current slot.
+ auto symbol = mlir::FlatSymbolRefAttr::get(vTable.getSymNameAttr());
+ field = cir::GlobalViewAttr::get(builder.getSInt32Ty(), symbol, indices);
} else {
- SmallVector<mlir::Attribute, 4> offsets{
- cgm.getBuilder().getI32IntegerAttr(2)};
- auto indices = mlir::ArrayAttr::get(builder.getContext(), offsets);
field = cgm.getBuilder().getGlobalViewAttr(cgm.getBuilder().getUInt8PtrTy(),
vTable, indices);
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenVTables.cpp b/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
index 8d9cf3e0a1ab7..a93cd23313c0e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
@@ -52,9 +52,12 @@ static void setThunkProperties(CIRGenModule &cgm, const ThunkInfo &thunk,
}
mlir::Type CIRGenModule::getVTableComponentType() {
- mlir::Type ptrTy = builder.getUInt8PtrTy();
- assert(!cir::MissingFeatures::vtableRelativeLayout());
- return ptrTy;
+ mlir::Type ty = builder.getUInt8PtrTy();
+ // assert(!cir::MissingFeatures::vtableRelativeLayout());
+ if (getLangOpts().RelativeCXXABIVTables) {
+ ty = builder.getSInt32Ty();
+ }
+ return ty;
}
mlir::Type CIRGenVTables::getVTableComponentType() {
@@ -176,28 +179,26 @@ mlir::Attribute CIRGenVTables::getVTableComponent(
CIRGenBuilderTy builder = cgm.getBuilder();
- assert(!cir::MissingFeatures::vtableRelativeLayout());
+ // assert(!cir::MissingFeatures::vtableRelativeLayout());
switch (component.getKind()) {
case VTableComponent::CK_UnusedFunctionPointer:
- return builder.getConstNullPtrAttr(builder.getUInt8PtrTy());
-
case VTableComponent::CK_VCallOffset:
- return builder.getConstPtrAttr(builder.getUInt8PtrTy(),
- component.getVCallOffset().getQuantity());
-
case VTableComponent::CK_VBaseOffset:
- return builder.getConstPtrAttr(builder.getUInt8PtrTy(),
- component.getVBaseOffset().getQuantity());
-
case VTableComponent::CK_OffsetToTop:
- return builder.getConstPtrAttr(builder.getUInt8PtrTy(),
- component.getOffsetToTop().getQuantity());
+ return getVTableIntegerOrNullComponent(component);
case VTableComponent::CK_RTTI:
- assert((mlir::isa<cir::GlobalViewAttr>(rtti) ||
- mlir::isa<cir::ConstPtrAttr>(rtti)) &&
- "expected GlobalViewAttr or ConstPtrAttr");
+ if (cgm.getLangOpts().RelativeCXXABIVTables) {
+ if (auto gv = mlir::dyn_cast<cir::GlobalViewAttr>(rtti)) {
+ rtti = cir::GlobalViewAttr::get(builder.getSInt32Ty(), gv.getSymbol(),
+ gv.getIndices());
+ } else {
+ // For null RTTI / special cases. Adjust if ConstPtrAttr has meaningful
+ // non-zero cases in your path.
+ rtti = builder.getI32IntegerAttr(0);
+ }
+ }
return rtti;
case VTableComponent::CK_FunctionPointer:
@@ -210,8 +211,6 @@ mlir::Attribute CIRGenVTables::getVTableComponent(
assert(!cir::MissingFeatures::cudaSupport());
auto getSpecialVirtFn = [&](StringRef name) -> cir::FuncOp {
- assert(!cir::MissingFeatures::vtableRelativeLayout());
-
if (cgm.getLangOpts().OpenMP && cgm.getLangOpts().OpenMPIsTargetDevice &&
cgm.getTriple().isNVPTX())
cgm.errorNYI(gd.getDecl()->getSourceRange(),
@@ -251,7 +250,7 @@ mlir::Attribute CIRGenVTables::getVTableComponent(
}
return cir::GlobalViewAttr::get(
- builder.getUInt8PtrTy(),
+ getVTableComponentType(),
mlir::FlatSymbolRefAttr::get(fnPtr.getSymNameAttr()));
}
}
@@ -278,10 +277,11 @@ void CIRGenVTables::createVTableInitializer(cir::GlobalOp &vtableOp,
size_t vtableEnd = vtableStart + layout.getVTableSize(vtableIndex);
llvm::SmallVector<mlir::Attribute> components;
components.reserve(vtableEnd - vtableStart);
- for (size_t componentIndex : llvm::seq(vtableStart, vtableEnd))
+ for (size_t componentIndex : llvm::seq(vtableStart, vtableEnd)) {
components.push_back(
getVTableComponent(layout, componentIndex, rtti, nextVTableThunkIndex,
addressPoint, vtableHasLocalLinkage));
+ }
// Create a ConstArrayAttr to hold the components.
auto arr = cir::ConstArrayAttr::get(
cir::ArrayType::get(componentType, components.size()),
@@ -297,6 +297,7 @@ void CIRGenVTables::createVTableInitializer(cir::GlobalOp &vtableOp,
auto vtableAttr = cir::VTableAttr::get(record.getType(), record.getMembers());
// Add the vtable initializer to the vtable global op.
+ // CHECKME: by @Elio
cgm.setInitializer(vtableOp, vtableAttr);
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenVTables.h b/clang/lib/CIR/CodeGen/CIRGenVTables.h
index 1e4da7d8f945f..6011c1479fdb7 100644
--- a/clang/lib/CIR/CodeGen/CIRGenVTables.h
+++ b/clang/lib/CIR/CodeGen/CIRGenVTables.h
@@ -56,6 +56,9 @@ class CIRGenVTables {
mlir::Attribute rtti, unsigned &nextVTableThunkIndex,
unsigned vtableAddressPoint, bool vtableHasLocalLinkage);
+ mlir::Attribute
+ getVTableIntegerOrNullComponent(const VTableComponent &component);
+
mlir::Type getVTableComponentType();
public:
diff --git a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp
index 6cd2374673e69..0f38c76c6bbc4 100644
--- a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp
@@ -700,7 +700,7 @@ LogicalResult cir::VTableAttr::verify(
return failure();
for (const auto &element : data.getAsRange<mlir::Attribute>()) {
- const auto &constArrayAttr = mlir::dyn_cast<cir::ConstArrayAttr>(element);
+ auto constArrayAttr = mlir::dyn_cast<cir::ConstArrayAttr>(element);
if (!constArrayAttr)
return emitError() << "expected constant array subtype";
@@ -717,9 +717,11 @@ LogicalResult cir::VTableAttr::verify(
<< "expected GlobalViewAttr, ConstPtrAttr, or IntAttr";
},
[&](mlir::Type type) {});
+
if (eltTypeCheck.failed())
return eltTypeCheck;
}
+
return success();
}
>From 41220f310b388c744e3c3f5522f3dc7ead65ffef Mon Sep 17 00:00:00 2001
From: Zile Xiong <xiongzile99 at gmail.com>
Date: Sat, 9 May 2026 17:32:47 +0800
Subject: [PATCH 5/5] [CIR][WIP] full support
---
1.cpp | 73 -----
cir-ll.ll | 212 --------------
cir.ll | 215 ---------------
.../include/clang/CIR/Dialect/IR/CIRAttrs.td | 149 ++++++++++
clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp | 40 +--
clang/lib/CIR/CodeGen/CIRGenVTables.cpp | 75 +++--
clang/lib/CIR/CodeGen/CIRGenVTables.h | 5 +-
clang/lib/CIR/Dialect/IR/CIRAttrs.cpp | 61 ++++-
clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 14 +-
.../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 57 +++-
clang/test/CIR/IR/invalid-vtable.cir | 2 +-
ll.ll | 250 -----------------
mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp | 3 +-
rel-ll.ll | 259 ------------------
14 files changed, 344 insertions(+), 1071 deletions(-)
delete mode 100644 1.cpp
delete mode 100644 cir-ll.ll
delete mode 100644 cir.ll
delete mode 100644 ll.ll
delete mode 100644 rel-ll.ll
diff --git a/1.cpp b/1.cpp
deleted file mode 100644
index d736d6f48ab03..0000000000000
--- a/1.cpp
+++ /dev/null
@@ -1,73 +0,0 @@
-struct A {
- virtual void f();
- int a;
-};
-
-struct B {
- virtual void g();
- int b;
-};
-
-struct C : A, B {
- void f() override;
- void g() override;
- int c;
-};
-
-void A::f() {}
-void B::g() {}
-void C::f() {}
-void C::g() {}
-
-// 1. 通过 A* 做虚调用。
-// 这里应该会查 A-subobject 的 vptr,然后从 vtable slot 里取 f。
-void call_through_A(A *pa) {
- pa->f();
-}
-
-// 2. 通过 B* 做虚调用。
-// 这里应该会查 B-subobject 的 vptr,然后从 secondary vtable slot 里取 g。
-// 如果实际对象是 C,slot 里可能是 thunk。
-void call_through_B(B *pb) {
- pb->g();
-}
-
-// 3. 已知静态类型是 C*,但调用 virtual 函数。
-// C++ 语义上仍然是 virtual call,除非编译器能 devirtualize。
-// 在 -O0 / CIR 阶段通常更容易看到 vptr load。
-void call_through_C(C *pc) {
- pc->f();
- pc->g();
-}
-
-// 4. 非虚的限定调用。
-// 这里不会查 vptr,应该直接 call C::f / C::g。
-void direct_qualified_call(C *pc) {
- pc->C::f();
- pc->C::g();
-}
-
-// 5. 从 C* 转成 A* / B*。
-// A 是 primary base,一般 offset 是 0。
-// B 是 secondary base,一般需要 this + 16 之类的调整。
-void base_casts(C *pc) {
- A *pa = pc;
- B *pb = pc;
-
- pa->f();
- pb->g();
-}
-
-// 6. 栈上构造 C。
-// 这里适合观察 constructor / vptr 初始化。
-// 如果 CIR 暂时没有完整 ctor lowering,也至少能暴露相关路径。
-void construct_and_call() {
- C obj;
- obj.f();
- obj.g();
-
- A *pa = &obj;
- B *pb = &obj;
- pa->f();
- pb->g();
-}
\ No newline at end of file
diff --git a/cir-ll.ll b/cir-ll.ll
deleted file mode 100644
index 67b701207dcb9..0000000000000
--- a/cir-ll.ll
+++ /dev/null
@@ -1,212 +0,0 @@
-; ModuleID = '/data00/home/xiongzile/workspace/llvm-worktree/llvm-project/1.cpp'
-source_filename = "/data00/home/xiongzile/workspace/llvm-worktree/llvm-project/1.cpp"
-target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
-target triple = "x86_64-unknown-linux-gnu"
-
-%struct.C = type { %struct.A.base, [4 x i8], %struct.B.base, i32 }
-%struct.A.base = type <{ ptr, i32 }>
-%struct.B.base = type <{ ptr, i32 }>
-
- at _ZTV1A = global { [3 x i32] } { [3 x i32] [i32 0, i32 ptrtoint (ptr @_ZTI1A to i32), i32 ptrtoint (ptr @_ZN1A1fEv to i32)] }, align 4
- at _ZTVN10__cxxabiv117__class_type_infoE = external global ptr, align 8
- at _ZTS1A = global [3 x i8] c"1A\00", align 1
- at _ZTI1A = constant { i32, ptr } { i32 ptrtoint (ptr getelementptr (i8, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 16) to i32), ptr @_ZTS1A }, align 8
- at _ZTV1B = global { [3 x i32] } { [3 x i32] [i32 0, i32 ptrtoint (ptr @_ZTI1B to i32), i32 ptrtoint (ptr @_ZN1B1gEv to i32)] }, align 4
- at _ZTS1B = global [3 x i8] c"1B\00", align 1
- at _ZTI1B = constant { i32, ptr } { i32 ptrtoint (ptr getelementptr (i8, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 16) to i32), ptr @_ZTS1B }, align 8
- at _ZTV1C = global { [4 x i32], [3 x i32] } { [4 x i32] [i32 0, i32 ptrtoint (ptr @_ZTI1C to i32), i32 ptrtoint (ptr @_ZN1C1fEv to i32), i32 ptrtoint (ptr @_ZN1C1gEv to i32)], [3 x i32] [i32 -16, i32 ptrtoint (ptr @_ZTI1C to i32), i32 ptrtoint (ptr @_ZThn16_N1C1gEv to i32)] }, align 4
- at _ZTVN10__cxxabiv121__vmi_class_type_infoE = external global ptr, align 8
- at _ZTS1C = global [3 x i8] c"1C\00", align 1
- at _ZTI1C = constant { i32, ptr, i32, i32, ptr, i64, ptr, i64 } { i32 ptrtoint (ptr getelementptr (i8, ptr @_ZTVN10__cxxabiv121__vmi_class_type_infoE, i64 16) to i32), ptr @_ZTS1C, i32 0, i32 2, ptr @_ZTI1A, i64 2, ptr @_ZTI1B, i64 4098 }, align 8
-
-; Function Attrs: noinline
-define dso_local void @_ZN1A1fEv(ptr noundef nonnull align 8 dereferenceable(12) %0) #0 {
- %2 = alloca ptr, i64 1, align 8
- store ptr %0, ptr %2, align 8
- %3 = load ptr, ptr %2, align 8
- ret void
-}
-
-; Function Attrs: noinline
-define dso_local void @_ZN1B1gEv(ptr noundef nonnull align 8 dereferenceable(12) %0) #0 {
- %2 = alloca ptr, i64 1, align 8
- store ptr %0, ptr %2, align 8
- %3 = load ptr, ptr %2, align 8
- ret void
-}
-
-; Function Attrs: noinline
-define dso_local void @_ZN1C1fEv(ptr noundef nonnull align 8 dereferenceable(32) %0) #0 {
- %2 = alloca ptr, i64 1, align 8
- store ptr %0, ptr %2, align 8
- %3 = load ptr, ptr %2, align 8
- ret void
-}
-
-; Function Attrs: noinline
-define dso_local void @_ZN1C1gEv(ptr noundef nonnull align 8 dereferenceable(32) %0) #0 {
- %2 = alloca ptr, i64 1, align 8
- store ptr %0, ptr %2, align 8
- %3 = load ptr, ptr %2, align 8
- ret void
-}
-
-; Function Attrs: noinline
-define dso_local void @_ZThn16_N1C1gEv(ptr noundef %0) #1 {
- %2 = alloca ptr, i64 1, align 8
- store ptr %0, ptr %2, align 8
- %3 = load ptr, ptr %2, align 8
- %4 = getelementptr i8, ptr %3, i64 -16
- call void @_ZN1C1gEv(ptr noundef nonnull align 8 dereferenceable(32) %4)
- ret void
-}
-
-; Function Attrs: noinline
-define dso_local void @_Z14call_through_AP1A(ptr noundef %0) #0 {
- %2 = alloca ptr, i64 1, align 8
- store ptr %0, ptr %2, align 8
- %3 = load ptr, ptr %2, align 8
- %4 = load ptr, ptr %3, align 8
- %5 = call ptr @llvm.load.relative.i32(ptr %4, i32 0)
- call void %5(ptr noundef nonnull align 8 dereferenceable(12) %3)
- ret void
-}
-
-; Function Attrs: noinline
-define dso_local void @_Z14call_through_BP1B(ptr noundef %0) #0 {
- %2 = alloca ptr, i64 1, align 8
- store ptr %0, ptr %2, align 8
- %3 = load ptr, ptr %2, align 8
- %4 = load ptr, ptr %3, align 8
- %5 = call ptr @llvm.load.relative.i32(ptr %4, i32 0)
- call void %5(ptr noundef nonnull align 8 dereferenceable(12) %3)
- ret void
-}
-
-; Function Attrs: noinline
-define dso_local void @_Z14call_through_CP1C(ptr noundef %0) #0 {
- %2 = alloca ptr, i64 1, align 8
- store ptr %0, ptr %2, align 8
- %3 = load ptr, ptr %2, align 8
- %4 = load ptr, ptr %3, align 8
- %5 = call ptr @llvm.load.relative.i32(ptr %4, i32 0)
- call void %5(ptr noundef nonnull align 8 dereferenceable(32) %3)
- %6 = load ptr, ptr %2, align 8
- %7 = load ptr, ptr %6, align 8
- %8 = call ptr @llvm.load.relative.i32(ptr %7, i32 4)
- call void %8(ptr noundef nonnull align 8 dereferenceable(32) %6)
- ret void
-}
-
-; Function Attrs: noinline
-define dso_local void @_Z21direct_qualified_callP1C(ptr noundef %0) #0 {
- %2 = alloca ptr, i64 1, align 8
- store ptr %0, ptr %2, align 8
- %3 = load ptr, ptr %2, align 8
- call void @_ZN1C1fEv(ptr noundef nonnull align 8 dereferenceable(32) %3)
- %4 = load ptr, ptr %2, align 8
- call void @_ZN1C1gEv(ptr noundef nonnull align 8 dereferenceable(32) %4)
- ret void
-}
-
-; Function Attrs: noinline
-define dso_local void @_Z10base_castsP1C(ptr noundef %0) #0 {
- %2 = alloca ptr, i64 1, align 8
- %3 = alloca ptr, i64 1, align 8
- %4 = alloca ptr, i64 1, align 8
- store ptr %0, ptr %2, align 8
- %5 = load ptr, ptr %2, align 8
- store ptr %5, ptr %3, align 8
- %6 = load ptr, ptr %2, align 8
- %7 = icmp eq ptr %6, null
- %8 = getelementptr i8, ptr %6, i32 16
- %9 = select i1 %7, ptr %6, ptr %8
- store ptr %9, ptr %4, align 8
- %10 = load ptr, ptr %3, align 8
- %11 = load ptr, ptr %10, align 8
- %12 = call ptr @llvm.load.relative.i32(ptr %11, i32 0)
- call void %12(ptr noundef nonnull align 8 dereferenceable(12) %10)
- %13 = load ptr, ptr %4, align 8
- %14 = load ptr, ptr %13, align 8
- %15 = call ptr @llvm.load.relative.i32(ptr %14, i32 0)
- call void %15(ptr noundef nonnull align 8 dereferenceable(12) %13)
- ret void
-}
-
-; Function Attrs: noinline
-define dso_local void @_Z18construct_and_callv() #0 {
- %1 = alloca %struct.C, i64 1, align 8
- %2 = alloca ptr, i64 1, align 8
- %3 = alloca ptr, i64 1, align 8
- call void @_ZN1CC1Ev(ptr noundef nonnull align 8 dereferenceable(32) %1) #3
- call void @_ZN1C1fEv(ptr noundef nonnull align 8 dereferenceable(32) %1)
- call void @_ZN1C1gEv(ptr noundef nonnull align 8 dereferenceable(32) %1)
- store ptr %1, ptr %2, align 8
- %4 = icmp eq ptr %1, null
- %5 = getelementptr i8, ptr %1, i32 16
- %6 = select i1 %4, ptr %1, ptr %5
- store ptr %6, ptr %3, align 8
- %7 = load ptr, ptr %2, align 8
- %8 = load ptr, ptr %7, align 8
- %9 = call ptr @llvm.load.relative.i32(ptr %8, i32 0)
- call void %9(ptr noundef nonnull align 8 dereferenceable(12) %7)
- %10 = load ptr, ptr %3, align 8
- %11 = load ptr, ptr %10, align 8
- %12 = call ptr @llvm.load.relative.i32(ptr %11, i32 0)
- call void %12(ptr noundef nonnull align 8 dereferenceable(12) %10)
- ret void
-}
-
-; Function Attrs: noinline
-define linkonce_odr void @_ZN1AC2Ev(ptr noundef nonnull align 8 dereferenceable(12) %0) #0 {
- %2 = alloca ptr, i64 1, align 8
- store ptr %0, ptr %2, align 8
- %3 = load ptr, ptr %2, align 8
- store ptr getelementptr inbounds nuw (i8, ptr @_ZTV1A, i64 8), ptr %3, align 8
- ret void
-}
-
-; Function Attrs: noinline
-define linkonce_odr void @_ZN1BC2Ev(ptr noundef nonnull align 8 dereferenceable(12) %0) #0 {
- %2 = alloca ptr, i64 1, align 8
- store ptr %0, ptr %2, align 8
- %3 = load ptr, ptr %2, align 8
- store ptr getelementptr inbounds nuw (i8, ptr @_ZTV1B, i64 8), ptr %3, align 8
- ret void
-}
-
-; Function Attrs: noinline
-define linkonce_odr void @_ZN1CC2Ev(ptr noundef nonnull align 8 dereferenceable(32) %0) #0 {
- %2 = alloca ptr, i64 1, align 8
- store ptr %0, ptr %2, align 8
- %3 = load ptr, ptr %2, align 8
- call void @_ZN1AC2Ev(ptr noundef nonnull align 8 dereferenceable(12) %3) #3
- %4 = getelementptr i8, ptr %3, i32 16
- call void @_ZN1BC2Ev(ptr noundef nonnull align 8 dereferenceable(12) %4) #3
- store ptr getelementptr inbounds nuw (i8, ptr @_ZTV1C, i64 8), ptr %3, align 8
- %5 = getelementptr i8, ptr %3, i32 16
- store ptr getelementptr inbounds nuw (i8, ptr @_ZTV1C, i64 24), ptr %5, align 8
- ret void
-}
-
-; Function Attrs: noinline
-define linkonce_odr void @_ZN1CC1Ev(ptr noundef nonnull align 8 dereferenceable(32) %0) #0 {
- %2 = alloca ptr, i64 1, align 8
- store ptr %0, ptr %2, align 8
- %3 = load ptr, ptr %2, align 8
- call void @_ZN1CC2Ev(ptr noundef nonnull align 8 dereferenceable(32) %3) #3
- ret void
-}
-
-
-; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: read)
-declare ptr @llvm.load.relative.i32(ptr, i32) #2
-
-attributes #0 = { noinline "target-features"="+cx8,+mmx,+sse,+sse2,+x87" }
-attributes #1 = { noinline }
-attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: read) }
-attributes #3 = { nounwind }
-
-!llvm.module.flags = !{!0}
-
-!0 = !{i32 2, !"Debug Info Version", i32 3}
diff --git a/cir.ll b/cir.ll
deleted file mode 100644
index 48f99824f98a0..0000000000000
--- a/cir.ll
+++ /dev/null
@@ -1,215 +0,0 @@
-; ModuleID = '/data00/home/xiongzile/workspace/llvm-worktree/llvm-project/1.cpp'
-source_filename = "/data00/home/xiongzile/workspace/llvm-worktree/llvm-project/1.cpp"
-target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
-target triple = "x86_64-unknown-linux-gnu"
-
-%struct.C = type { %struct.A.base, [4 x i8], %struct.B.base, i32 }
-%struct.A.base = type <{ ptr, i32 }>
-%struct.B.base = type <{ ptr, i32 }>
-
- at _ZTV1A = global { [3 x ptr] } { [3 x ptr] [ptr null, ptr @_ZTI1A, ptr @_ZN1A1fEv] }, align 8
- at _ZTVN10__cxxabiv117__class_type_infoE = external global ptr, align 8
- at _ZTS1A = global [3 x i8] c"1A\00", align 1
- at _ZTI1A = constant { ptr, ptr } { ptr getelementptr (i8, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 16), ptr @_ZTS1A }, align 8
- at _ZTV1B = global { [3 x ptr] } { [3 x ptr] [ptr null, ptr @_ZTI1B, ptr @_ZN1B1gEv] }, align 8
- at _ZTS1B = global [3 x i8] c"1B\00", align 1
- at _ZTI1B = constant { ptr, ptr } { ptr getelementptr (i8, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 16), ptr @_ZTS1B }, align 8
- at _ZTV1C = global { [4 x ptr], [3 x ptr] } { [4 x ptr] [ptr null, ptr @_ZTI1C, ptr @_ZN1C1fEv, ptr @_ZN1C1gEv], [3 x ptr] [ptr inttoptr (i64 -16 to ptr), ptr @_ZTI1C, ptr @_ZThn16_N1C1gEv] }, align 8
- at _ZTVN10__cxxabiv121__vmi_class_type_infoE = external global ptr, align 8
- at _ZTS1C = global [3 x i8] c"1C\00", align 1
- at _ZTI1C = constant { ptr, ptr, i32, i32, ptr, i64, ptr, i64 } { ptr getelementptr (i8, ptr @_ZTVN10__cxxabiv121__vmi_class_type_infoE, i64 16), ptr @_ZTS1C, i32 0, i32 2, ptr @_ZTI1A, i64 2, ptr @_ZTI1B, i64 4098 }, align 8
-
-; Function Attrs: noinline
-define dso_local void @_ZN1A1fEv(ptr noundef nonnull align 8 dereferenceable(12) %0) #0 {
- %2 = alloca ptr, i64 1, align 8
- store ptr %0, ptr %2, align 8
- %3 = load ptr, ptr %2, align 8
- ret void
-}
-
-; Function Attrs: noinline
-define dso_local void @_ZN1B1gEv(ptr noundef nonnull align 8 dereferenceable(12) %0) #0 {
- %2 = alloca ptr, i64 1, align 8
- store ptr %0, ptr %2, align 8
- %3 = load ptr, ptr %2, align 8
- ret void
-}
-
-; Function Attrs: noinline
-define dso_local void @_ZN1C1fEv(ptr noundef nonnull align 8 dereferenceable(32) %0) #0 {
- %2 = alloca ptr, i64 1, align 8
- store ptr %0, ptr %2, align 8
- %3 = load ptr, ptr %2, align 8
- ret void
-}
-
-; Function Attrs: noinline
-define dso_local void @_ZN1C1gEv(ptr noundef nonnull align 8 dereferenceable(32) %0) #0 {
- %2 = alloca ptr, i64 1, align 8
- store ptr %0, ptr %2, align 8
- %3 = load ptr, ptr %2, align 8
- ret void
-}
-
-; Function Attrs: noinline
-define dso_local void @_ZThn16_N1C1gEv(ptr noundef %0) #1 {
- %2 = alloca ptr, i64 1, align 8
- store ptr %0, ptr %2, align 8
- %3 = load ptr, ptr %2, align 8
- %4 = getelementptr i8, ptr %3, i64 -16
- call void @_ZN1C1gEv(ptr noundef nonnull align 8 dereferenceable(32) %4)
- ret void
-}
-
-; Function Attrs: noinline
-define dso_local void @_Z14call_through_AP1A(ptr noundef %0) #0 {
- %2 = alloca ptr, i64 1, align 8
- store ptr %0, ptr %2, align 8
- %3 = load ptr, ptr %2, align 8
- %4 = load ptr, ptr %3, align 8
- %5 = getelementptr inbounds ptr, ptr %4, i32 0
- %6 = load ptr, ptr %5, align 8
- call void %6(ptr noundef nonnull align 8 dereferenceable(12) %3)
- ret void
-}
-
-; Function Attrs: noinline
-define dso_local void @_Z14call_through_BP1B(ptr noundef %0) #0 {
- %2 = alloca ptr, i64 1, align 8
- store ptr %0, ptr %2, align 8
- %3 = load ptr, ptr %2, align 8
- %4 = load ptr, ptr %3, align 8
- %5 = getelementptr inbounds ptr, ptr %4, i32 0
- %6 = load ptr, ptr %5, align 8
- call void %6(ptr noundef nonnull align 8 dereferenceable(12) %3)
- ret void
-}
-
-; Function Attrs: noinline
-define dso_local void @_Z14call_through_CP1C(ptr noundef %0) #0 {
- %2 = alloca ptr, i64 1, align 8
- store ptr %0, ptr %2, align 8
- %3 = load ptr, ptr %2, align 8
- %4 = load ptr, ptr %3, align 8
- %5 = getelementptr inbounds ptr, ptr %4, i32 0
- %6 = load ptr, ptr %5, align 8
- call void %6(ptr noundef nonnull align 8 dereferenceable(32) %3)
- %7 = load ptr, ptr %2, align 8
- %8 = load ptr, ptr %7, align 8
- %9 = getelementptr inbounds ptr, ptr %8, i32 1
- %10 = load ptr, ptr %9, align 8
- call void %10(ptr noundef nonnull align 8 dereferenceable(32) %7)
- ret void
-}
-
-; Function Attrs: noinline
-define dso_local void @_Z21direct_qualified_callP1C(ptr noundef %0) #0 {
- %2 = alloca ptr, i64 1, align 8
- store ptr %0, ptr %2, align 8
- %3 = load ptr, ptr %2, align 8
- call void @_ZN1C1fEv(ptr noundef nonnull align 8 dereferenceable(32) %3)
- %4 = load ptr, ptr %2, align 8
- call void @_ZN1C1gEv(ptr noundef nonnull align 8 dereferenceable(32) %4)
- ret void
-}
-
-; Function Attrs: noinline
-define dso_local void @_Z10base_castsP1C(ptr noundef %0) #0 {
- %2 = alloca ptr, i64 1, align 8
- %3 = alloca ptr, i64 1, align 8
- %4 = alloca ptr, i64 1, align 8
- store ptr %0, ptr %2, align 8
- %5 = load ptr, ptr %2, align 8
- store ptr %5, ptr %3, align 8
- %6 = load ptr, ptr %2, align 8
- %7 = icmp eq ptr %6, null
- %8 = getelementptr i8, ptr %6, i32 16
- %9 = select i1 %7, ptr %6, ptr %8
- store ptr %9, ptr %4, align 8
- %10 = load ptr, ptr %3, align 8
- %11 = load ptr, ptr %10, align 8
- %12 = getelementptr inbounds ptr, ptr %11, i32 0
- %13 = load ptr, ptr %12, align 8
- call void %13(ptr noundef nonnull align 8 dereferenceable(12) %10)
- %14 = load ptr, ptr %4, align 8
- %15 = load ptr, ptr %14, align 8
- %16 = getelementptr inbounds ptr, ptr %15, i32 0
- %17 = load ptr, ptr %16, align 8
- call void %17(ptr noundef nonnull align 8 dereferenceable(12) %14)
- ret void
-}
-
-; Function Attrs: noinline
-define linkonce_odr void @_ZN1AC2Ev(ptr noundef nonnull align 8 dereferenceable(12) %0) #0 {
- %2 = alloca ptr, i64 1, align 8
- store ptr %0, ptr %2, align 8
- %3 = load ptr, ptr %2, align 8
- store ptr getelementptr inbounds nuw (i8, ptr @_ZTV1A, i64 16), ptr %3, align 8
- ret void
-}
-
-; Function Attrs: noinline
-define linkonce_odr void @_ZN1BC2Ev(ptr noundef nonnull align 8 dereferenceable(12) %0) #0 {
- %2 = alloca ptr, i64 1, align 8
- store ptr %0, ptr %2, align 8
- %3 = load ptr, ptr %2, align 8
- store ptr getelementptr inbounds nuw (i8, ptr @_ZTV1B, i64 16), ptr %3, align 8
- ret void
-}
-
-; Function Attrs: noinline
-define linkonce_odr void @_ZN1CC2Ev(ptr noundef nonnull align 8 dereferenceable(32) %0) #0 {
- %2 = alloca ptr, i64 1, align 8
- store ptr %0, ptr %2, align 8
- %3 = load ptr, ptr %2, align 8
- call void @_ZN1AC2Ev(ptr noundef nonnull align 8 dereferenceable(12) %3) #2
- %4 = getelementptr i8, ptr %3, i32 16
- call void @_ZN1BC2Ev(ptr noundef nonnull align 8 dereferenceable(12) %4) #2
- store ptr getelementptr inbounds nuw (i8, ptr @_ZTV1C, i64 16), ptr %3, align 8
- %5 = getelementptr i8, ptr %3, i32 16
- store ptr getelementptr inbounds nuw (i8, ptr @_ZTV1C, i64 48), ptr %5, align 8
- ret void
-}
-
-; Function Attrs: noinline
-define linkonce_odr void @_ZN1CC1Ev(ptr noundef nonnull align 8 dereferenceable(32) %0) #0 {
- %2 = alloca ptr, i64 1, align 8
- store ptr %0, ptr %2, align 8
- %3 = load ptr, ptr %2, align 8
- call void @_ZN1CC2Ev(ptr noundef nonnull align 8 dereferenceable(32) %3) #2
- ret void
-}
-
-; Function Attrs: noinline
-define dso_local void @_Z18construct_and_callv() #0 {
- %1 = alloca %struct.C, i64 1, align 8
- %2 = alloca ptr, i64 1, align 8
- %3 = alloca ptr, i64 1, align 8
- call void @_ZN1CC1Ev(ptr noundef nonnull align 8 dereferenceable(32) %1) #2
- call void @_ZN1C1fEv(ptr noundef nonnull align 8 dereferenceable(32) %1)
- call void @_ZN1C1gEv(ptr noundef nonnull align 8 dereferenceable(32) %1)
- store ptr %1, ptr %2, align 8
- %4 = icmp eq ptr %1, null
- %5 = getelementptr i8, ptr %1, i32 16
- %6 = select i1 %4, ptr %1, ptr %5
- store ptr %6, ptr %3, align 8
- %7 = load ptr, ptr %2, align 8
- %8 = load ptr, ptr %7, align 8
- %9 = getelementptr inbounds ptr, ptr %8, i32 0
- %10 = load ptr, ptr %9, align 8
- call void %10(ptr noundef nonnull align 8 dereferenceable(12) %7)
- %11 = load ptr, ptr %3, align 8
- %12 = load ptr, ptr %11, align 8
- %13 = getelementptr inbounds ptr, ptr %12, i32 0
- %14 = load ptr, ptr %13, align 8
- call void %14(ptr noundef nonnull align 8 dereferenceable(12) %11)
- ret void
-}
-
-attributes #0 = { noinline "target-features"="+cx8,+mmx,+sse,+sse2,+x87" }
-attributes #1 = { noinline }
-attributes #2 = { nounwind }
-
-!llvm.module.flags = !{!0}
-
-!0 = !{i32 2, !"Debug Info Version", i32 3}
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
index 1520999e3f85f..7c8de3d4a379e 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
@@ -893,6 +893,155 @@ def CIR_VTableAttr : CIR_ValueLikeAttr<"VTable", "vtable"> {
}];
}
+//===----------------------------------------------------------------------===//
+// RelativeVTableComponentAttr
+//===----------------------------------------------------------------------===//
+
+def CIR_RelativeVTableComponentAttr
+ : CIR_ValueLikeAttr<"RelativeVTableComponent", "relative_vtable_component"> {
+ let summary = "Represents one C++ relative vtable component";
+
+ let description = [{
+ Represents one entry in a C++ relative vtable.
+
+ A relative vtable entry does not materialize the absolute address of the
+ referenced entity. Instead, it represents the 32-bit offset from the vtable
+ address point to the referenced symbol, optionally adjusted according to the
+ kind of vtable component being emitted.
+
+ The symbol identifies the referenced global, such as an RTTI proxy or a
+ function. The vtable address point identifies the address point used as the
+ base for computing the relative offset. The component kind records how the
+ entry should be interpreted during lowering, for example as an RTTI entry or
+ a function entry.
+
+ This attribute is intended for relative vtable initializers. Unlike
+ #cir.global_view, it does not produce a pointer-valued constant view of a
+ global. Its lowered value is a 32-bit integer relative-offset entry.
+
+ Example:
+ ```
+ cir.global linkonce_odr @_ZTV1A =
+ #cir.relative_vtable<{
+ #cir.const_array<[
+ #cir.int<0> : !s32i,
+ #cir.relative_vtable_component<@_ZTI1A.rtti_proxy, 0, 2, 0> : !s32i,
+ #cir.relative_vtable_component<@_ZN1A1fEv, 0, 2, 1> : !s32i
+ ]> : !cir.array<!s32i x 3>
+ }> : !rec_anon_struct
+ ```
+ }];
+
+ let parameters = (ins
+ AttributeSelfTypeParameter<"">:$type,
+ "mlir::FlatSymbolRefAttr":$symbol,
+ "unsigned":$vtableComponentIndex,
+ "unsigned":$vtableAddressPoint,
+ "uint32_t":$componentKind // 0 -> RTTI 1 -> Virtual function pointer(TODO: magic number)
+ );
+
+ let builders = [
+ AttrBuilderWithInferredContext<(ins
+ "mlir::Type":$type,
+ "mlir::FlatSymbolRefAttr":$symbol,
+ "unsigned":$vtableComponentIndex,
+ "unsigned":$vtableAddressPoint,
+ "uint32_t":$componentKind
+ ), [{
+ return $_get(type.getContext(), type, symbol, vtableComponentIndex, vtableAddressPoint,
+ componentKind);
+ }]>
+ ];
+
+ let genVerifyDecl = 1;
+
+ let assemblyFormat = [{
+ `<`
+ $symbol `,`
+ $vtableComponentIndex `,`
+ $vtableAddressPoint `,`
+ $componentKind
+ `>`
+ }];
+}
+
+//===----------------------------------------------------------------------===//
+// RelativeVTableAttr
+//===----------------------------------------------------------------------===//
+
+def CIR_RelativeVTableAttr : CIR_ValueLikeAttr<"RelativeVTable", "relative_vtable"> {
+ let summary = "Represents a C++ relative vtable";
+ let description = [{
+ Represents the initializer for a C++ relative vtable global.
+
+ The attribute stores the constant elements of the relative vtable object.
+ The associated attribute type is the anonymous record type of the global.
+ The data parameter contains the record members, where each member is a
+ constant array corresponding to one vtable component.
+
+ Unlike a regular vtable, a relative vtable does not store absolute pointers
+ in its entries. Each vtable entry is represented as a 32-bit relative offset.
+ The offset is interpreted relative to the address point of vtable.
+
+ In the common single-inheritance case, the anonymous record type contains a
+ single array corresponding to the primary vtable for one class. In multiple
+ inheritance cases, the anonymous record may contain multiple arrays, each
+ representing a distinct vtable component within the same vtable object.
+
+ Example 1 (single vtable):
+ ```
+ cir.global linkonce_odr @_ZTV6Mother =
+ #cir.relative_vtable<{
+ #cir.const_array<[
+ #cir.int<0> : !s32i,
+ #cir.int<...> : !s32i,
+ #cir.int<...> : !s32i,
+ #cir.int<...> : !s32i
+ ]> : !cir.array<!s32i x 4>
+ }> : !rec_anon_struct1
+ ```
+
+ Example 2 (multiple vtables):
+ ```
+ cir.global linkonce_odr @_ZTV5Child =
+ #cir.relative_vtable<{
+ #cir.const_array<[
+ #cir.int<0> : !s32i,
+ #cir.int<...> : !s32i,
+ #cir.int<...> : !s32i,
+ #cir.int<...> : !s32i
+ ]> : !cir.array<!s32i x 4>,
+ #cir.const_array<[
+ #cir.int<-8> : !s32i,
+ #cir.int<...> : !s32i,
+ #cir.int<...> : !s32i
+ ]> : !cir.array<!s32i x 3>
+ }> : !rec_anon_struct2
+ ```
+ }];
+
+ // `type` is the anonymous record type of the relative vtable global.
+ // `data` contains the record members. Each member is expected to be a
+ // constant array of 32-bit relative-offset entries representing one vtable
+ // component.
+ let parameters = (ins
+ AttributeSelfTypeParameter<"">:$type,
+ "mlir::ArrayAttr":$data
+ );
+
+ let builders = [
+ AttrBuilderWithInferredContext<(ins "mlir::Type":$type,
+ "mlir::ArrayAttr":$data), [{
+ return $_get(type.getContext(), type, data);
+ }]>
+ ];
+
+ let genVerifyDecl = 1;
+ let assemblyFormat = [{
+ `<` custom<RecordMembers>($data) `>`
+ }];
+}
+
//===----------------------------------------------------------------------===//
// DynamicCastInfoAttr
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
index 70c9ff9492cf3..e4f14f44c1f0d 100644
--- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
@@ -480,27 +480,18 @@ void CIRGenItaniumCXXABI::emitVTableDefinitions(CIRGenVTables &cgvt,
cir::GlobalOp vtable = getAddrOfVTable(rd, CharUnits());
if (vtable.hasInitializer())
return;
- llvm::errs() << "vtable = ";
- vtable->dump();
- llvm::errs() << "";
+
ItaniumVTableContext &vtContext = cgm.getItaniumVTableContext();
const VTableLayout &vtLayout = vtContext.getVTableLayout(rd);
cir::GlobalLinkageKind linkage = cgm.getVTableLinkage(rd);
mlir::Attribute rtti =
cgm.getAddrOfRTTIDescriptor(cgm.getLoc(rd->getBeginLoc()),
cgm.getASTContext().getCanonicalTagType(rd));
- llvm::errs() << "rtti = ";
- rtti.dump();
- llvm::errs() << "\n";
// Classic codegen uses ConstantInitBuilder here, which is a very general
// and feature-rich class to generate initializers for global values.
// For now, this is using a simpler approach to create the initializer in CIR.
cgvt.createVTableInitializer(vtable, vtLayout, rtti,
cir::isLocalLinkage(linkage));
- llvm::errs() << "new vtable = ";
- vtable->dump();
- llvm::errs() << "\n";
- llvm::errs() << "\n";
// Set the correct linkage.
vtable.setLinkage(linkage);
@@ -538,9 +529,10 @@ void CIRGenItaniumCXXABI::emitVTableDefinitions(CIRGenVTables &cgvt,
"emitVTableDefinitions: WholeProgramVTables");
}
- // TODO: by @Elio
- // assert(!cir::MissingFeatures::vtableRelativeLayout());
- // if (cgm.getLangOpts().RelativeCXXABIVTables) {
+ // TODO: we don't try to generate alias here like:
+ // clang/lib/CodeGen/ItaniumCXXABI.cpp Instead, we create alias variable on
+ // LowerToLLVM assert(!cir::MissingFeatures::vtableRelativeLayout()); if
+ // (cgm.getLangOpts().RelativeCXXABIVTables) {
// cgm.errorNYI(rd->getSourceRange(), "vtableRelativeLayout");
// }
}
@@ -1243,16 +1235,14 @@ void CIRGenItaniumRTTIBuilder::buildVTablePointer(mlir::Location loc,
cgm.getBuilder().getI32IntegerAttr(2)};
auto indices = mlir::ArrayAttr::get(builder.getContext(), offsets);
mlir::Attribute field{};
- if (cgm.getLangOpts().RelativeCXXABIVTables) {
- // TODO: by @Elio.
- // For relative vtables, this needs special handling during lowering: the
- // GlobalViewAttr target should be emitted as target - current slot.
- auto symbol = mlir::FlatSymbolRefAttr::get(vTable.getSymNameAttr());
- field = cir::GlobalViewAttr::get(builder.getSInt32Ty(), symbol, indices);
- } else {
- field = cgm.getBuilder().getGlobalViewAttr(cgm.getBuilder().getUInt8PtrTy(),
- vTable, indices);
- }
+
+ // We don't have to change this when support relative vtable.
+ // This is something like: @_ZTI1A = constant { ptr, ptr } { ptr getelementptr
+ // inbounds (ptr, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 2), ptr
+ // @_ZTS1A }, align 8
+ // don't use relative offset here.
+ field = cgm.getBuilder().getGlobalViewAttr(cgm.getBuilder().getUInt8PtrTy(),
+ vTable, indices);
assert(field && "expected attribute");
fields.push_back(field);
@@ -1994,7 +1984,6 @@ CIRGenCallee CIRGenItaniumCXXABI::getVirtualFunctionPointer(
return callee;
}
-
mlir::Value CIRGenItaniumCXXABI::getVTableAddressPointInStructorWithVTT(
CIRGenFunction &cgf, const CXXRecordDecl *vtableClass, BaseSubobject base,
const CXXRecordDecl *nearestVBase) {
@@ -2654,7 +2643,8 @@ static mlir::Value performTypeAdjustment(CIRGenFunction &cgf,
cir::PtrStrideOp::create(builder, loc, i8PtrTy, vtablePtr,
builder.getSInt64(virtualAdjustment, loc));
if (cgf.cgm.getLangOpts().RelativeCXXABIVTables) {
- assert(!cir::MissingFeatures::vtableRelativeLayout()); // performTypeAdjustment
+ assert(!cir::MissingFeatures::
+ vtableRelativeLayout()); // performTypeAdjustment
cgf.cgm.errorNYI("virtual adjustment for relative layout vtables");
} else {
offset = builder.createAlignedLoad(loc, cgf.ptrDiffTy, offsetPtr,
diff --git a/clang/lib/CIR/CodeGen/CIRGenVTables.cpp b/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
index a93cd23313c0e..aa6493ba83592 100644
--- a/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
@@ -53,7 +53,6 @@ static void setThunkProperties(CIRGenModule &cgm, const ThunkInfo &thunk,
mlir::Type CIRGenModule::getVTableComponentType() {
mlir::Type ty = builder.getUInt8PtrTy();
- // assert(!cir::MissingFeatures::vtableRelativeLayout());
if (getLangOpts().RelativeCXXABIVTables) {
ty = builder.getSInt32Ty();
}
@@ -144,7 +143,7 @@ mlir::Attribute CIRGenVTables::getVTableIntegerOrNullComponent(
auto getOffsetAttr = [&](CharUnits offset) -> mlir::Attribute {
if (isRelative) {
- return cir::IntAttr::get(builder.getSInt32Ty(), offset.getQuantity());
+ return cir::IntAttr::get(getVTableComponentType(), offset.getQuantity());
}
return builder.getConstPtrAttr(builder.getUInt8PtrTy(),
offset.getQuantity());
@@ -153,7 +152,7 @@ mlir::Attribute CIRGenVTables::getVTableIntegerOrNullComponent(
switch (component.getKind()) {
case VTableComponent::CK_UnusedFunctionPointer:
if (isRelative)
- return cir::IntAttr::get(builder.getSInt32Ty(), 0);
+ return cir::IntAttr::get(getVTableComponentType(), 0);
return builder.getConstNullPtrAttr(builder.getUInt8PtrTy());
@@ -171,16 +170,33 @@ mlir::Attribute CIRGenVTables::getVTableIntegerOrNullComponent(
}
}
+cir::GlobalOp
+CIRGenVTables::getRTTIProxy(cir::GlobalOp &vtable, /* to calculate loc*/
+ mlir::Attribute rtti) {
+ assert(dyn_cast<cir::GlobalViewAttr>(rtti));
+ cir::GlobalOp proxy;
+ if (auto r = dyn_cast<cir::GlobalViewAttr>(rtti)) {
+ llvm::SmallString<16> rttiProxyName(r.getSymbol().getValue());
+ rttiProxyName.append(".rtti_proxy");
+ proxy = cgm.createOrReplaceCXXRuntimeVariable(
+ vtable->getLoc(), rttiProxyName, r.getType(),
+ cir::GlobalLinkageKind::PrivateLinkage,
+ CharUnits::fromQuantity(
+ cgm.getDataLayout().getABITypeAlign(r.getType())));
+ }
+ cgm.setInitializer(proxy, rtti);
+ return proxy;
+}
+
mlir::Attribute CIRGenVTables::getVTableComponent(
- const VTableLayout &layout, unsigned componentIndex, mlir::Attribute rtti,
+ const VTableLayout &layout, cir::GlobalOp &vtableOp,
+ unsigned componentIndex, unsigned vtableIndex, mlir::Attribute rtti,
unsigned &nextVTableThunkIndex, unsigned vtableAddressPoint,
bool vtableHasLocalLinkage) {
const VTableComponent &component = layout.vtable_components()[componentIndex];
CIRGenBuilderTy builder = cgm.getBuilder();
- // assert(!cir::MissingFeatures::vtableRelativeLayout());
-
switch (component.getKind()) {
case VTableComponent::CK_UnusedFunctionPointer:
case VTableComponent::CK_VCallOffset:
@@ -190,14 +206,11 @@ mlir::Attribute CIRGenVTables::getVTableComponent(
case VTableComponent::CK_RTTI:
if (cgm.getLangOpts().RelativeCXXABIVTables) {
- if (auto gv = mlir::dyn_cast<cir::GlobalViewAttr>(rtti)) {
- rtti = cir::GlobalViewAttr::get(builder.getSInt32Ty(), gv.getSymbol(),
- gv.getIndices());
- } else {
- // For null RTTI / special cases. Adjust if ConstPtrAttr has meaningful
- // non-zero cases in your path.
- rtti = builder.getI32IntegerAttr(0);
- }
+ cir::GlobalOp rttiProxy = getRTTIProxy(vtableOp, rtti);
+ return cir::RelativeVTableComponentAttr::get(
+ getVTableComponentType(),
+ mlir::FlatSymbolRefAttr::get(rttiProxy.getSymNameAttr()), vtableIndex,
+ vtableAddressPoint, /* RTTI */ 0);
}
return rtti;
@@ -249,6 +262,12 @@ mlir::Attribute CIRGenVTables::getVTableComponent(
fnPtr = cgm.getAddrOfFunction(gd, fnTy, /*ForVTable=*/true);
}
+ if (cgm.getLangOpts().RelativeCXXABIVTables) {
+ return cir::RelativeVTableComponentAttr::get(
+ cgm.getBuilder().getSInt32Ty(),
+ mlir::FlatSymbolRefAttr::get(fnPtr.getSymNameAttr()), vtableIndex,
+ vtableAddressPoint, /* function pointer */ 1);
+ }
return cir::GlobalViewAttr::get(
getVTableComponentType(),
mlir::FlatSymbolRefAttr::get(fnPtr.getSymNameAttr()));
@@ -278,9 +297,9 @@ void CIRGenVTables::createVTableInitializer(cir::GlobalOp &vtableOp,
llvm::SmallVector<mlir::Attribute> components;
components.reserve(vtableEnd - vtableStart);
for (size_t componentIndex : llvm::seq(vtableStart, vtableEnd)) {
- components.push_back(
- getVTableComponent(layout, componentIndex, rtti, nextVTableThunkIndex,
- addressPoint, vtableHasLocalLinkage));
+ components.push_back(getVTableComponent(
+ layout, vtableOp, componentIndex, vtableIndex, rtti,
+ nextVTableThunkIndex, addressPoint, vtableHasLocalLinkage));
}
// Create a ConstArrayAttr to hold the components.
auto arr = cir::ConstArrayAttr::get(
@@ -293,12 +312,18 @@ void CIRGenVTables::createVTableInitializer(cir::GlobalOp &vtableOp,
const auto members = mlir::ArrayAttr::get(mlirContext, vtables);
cir::ConstRecordAttr record = cgm.getBuilder().getAnonConstRecord(members);
- // Create a VTableAttr
- auto vtableAttr = cir::VTableAttr::get(record.getType(), record.getMembers());
+ if (cgm.getLangOpts().RelativeCXXABIVTables) {
+ auto relativeVtableAttr =
+ cir::RelativeVTableAttr::get(record.getType(), record.getMembers());
+ cgm.setInitializer(vtableOp, relativeVtableAttr);
+ } else {
+ // Create a VTableAttr
+ auto vtableAttr =
+ cir::VTableAttr::get(record.getType(), record.getMembers());
- // Add the vtable initializer to the vtable global op.
- // CHECKME: by @Elio
- cgm.setInitializer(vtableOp, vtableAttr);
+ // Add the vtable initializer to the vtable global op.
+ cgm.setInitializer(vtableOp, vtableAttr);
+ }
}
cir::GlobalOp CIRGenVTables::generateConstructionVTable(
@@ -321,7 +346,8 @@ cir::GlobalOp CIRGenVTables::generateConstructionVTable(
base.getBase(), out);
SmallString<256> name(outName);
- assert(!cir::MissingFeatures::vtableRelativeLayout()); // generateConstructionVTable
+ assert(!cir::MissingFeatures::
+ vtableRelativeLayout()); // generateConstructionVTable
cir::RecordType vtType = getVTableType(*vtLayout);
@@ -356,7 +382,8 @@ cir::GlobalOp CIRGenVTables::generateConstructionVTable(
cgm.setGVProperties(vtable, rd);
assert(!cir::MissingFeatures::vtableEmitMetadata());
- assert(!cir::MissingFeatures::vtableRelativeLayout()); // generateConstructionVTable
+ assert(!cir::MissingFeatures::
+ vtableRelativeLayout()); // generateConstructionVTable
return vtable;
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenVTables.h b/clang/lib/CIR/CodeGen/CIRGenVTables.h
index 6011c1479fdb7..4eb4004c3a104 100644
--- a/clang/lib/CIR/CodeGen/CIRGenVTables.h
+++ b/clang/lib/CIR/CodeGen/CIRGenVTables.h
@@ -52,7 +52,8 @@ class CIRGenVTables {
cir::FuncOp deletedVirtualFn = nullptr;
mlir::Attribute
- getVTableComponent(const VTableLayout &layout, unsigned componentIndex,
+ getVTableComponent(const VTableLayout &layout, cir::GlobalOp &vtableOp,
+ unsigned componentIndex, unsigned vtableIndex,
mlir::Attribute rtti, unsigned &nextVTableThunkIndex,
unsigned vtableAddressPoint, bool vtableHasLocalLinkage);
@@ -119,6 +120,8 @@ class CIRGenVTables {
/// arrays of pointers, with one struct element for each vtable in the vtable
/// group.
cir::RecordType getVTableType(const clang::VTableLayout &layout);
+
+ cir::GlobalOp getRTTIProxy(cir::GlobalOp &vtable, mlir::Attribute rtti);
};
} // namespace clang::CIRGen
diff --git a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp
index 0f38c76c6bbc4..91b37004c8ff2 100644
--- a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp
@@ -700,7 +700,7 @@ LogicalResult cir::VTableAttr::verify(
return failure();
for (const auto &element : data.getAsRange<mlir::Attribute>()) {
- auto constArrayAttr = mlir::dyn_cast<cir::ConstArrayAttr>(element);
+ const auto &constArrayAttr = mlir::dyn_cast<cir::ConstArrayAttr>(element);
if (!constArrayAttr)
return emitError() << "expected constant array subtype";
@@ -708,13 +708,51 @@ LogicalResult cir::VTableAttr::verify(
auto arrayElts = mlir::cast<ArrayAttr>(constArrayAttr.getElts());
arrayElts.walkImmediateSubElements(
[&](mlir::Attribute attr) {
- if (mlir::isa<cir::ConstPtrAttr, cir::GlobalViewAttr,
- cir::IntAttr>(attr))
+ if (mlir::isa<ConstPtrAttr, GlobalViewAttr>(attr))
return;
- eltTypeCheck =
- emitError()
- << "expected GlobalViewAttr, ConstPtrAttr, or IntAttr";
+ eltTypeCheck = emitError()
+ << "expected GlobalViewAttr or ConstPtrAttr";
+ },
+ [&](mlir::Type type) {});
+ if (eltTypeCheck.failed())
+ return eltTypeCheck;
+ }
+ return success();
+}
+
+//===----------------------------------------------------------------------===//
+// CIR RelativeVTableAttr
+//===----------------------------------------------------------------------===//
+
+LogicalResult cir::RelativeVTableAttr::verify(
+ llvm::function_ref<mlir::InFlightDiagnostic()> emitError, mlir::Type type,
+ mlir::ArrayAttr data) {
+ auto sTy = mlir::dyn_cast_if_present<cir::RecordType>(type);
+ if (!sTy)
+ return emitError() << "expected !cir.record type result";
+
+ if (sTy.getMembers().empty() || data.empty())
+ return emitError() << "expected record type with one or more subtype";
+
+ if (cir::ConstRecordAttr::verify(emitError, type, data).failed())
+ return failure();
+
+ for (const auto &element : data.getAsRange<mlir::Attribute>()) {
+ const auto &constArrayAttr = mlir::dyn_cast<cir::ConstArrayAttr>(element);
+ if (!constArrayAttr)
+ return emitError() << "expected constant array subtype";
+
+ LogicalResult eltTypeCheck = success();
+ auto arrayElts = mlir::cast<mlir::ArrayAttr>(constArrayAttr.getElts());
+
+ arrayElts.walkImmediateSubElements(
+ [&](mlir::Attribute attr) {
+ if (mlir::isa<cir::IntAttr, cir::RelativeVTableComponentAttr>(attr))
+ return;
+
+ eltTypeCheck = emitError()
+ << "expected IntAttr or RelativeVTableComponentAttr";
},
[&](mlir::Type type) {});
@@ -725,6 +763,17 @@ LogicalResult cir::VTableAttr::verify(
return success();
}
+//===----------------------------------------------------------------------===//
+// CIR RelativeVTableComponentAttr
+//===----------------------------------------------------------------------===//
+
+LogicalResult cir::RelativeVTableComponentAttr::verify(
+ llvm::function_ref<mlir::InFlightDiagnostic()>, mlir::Type,
+ mlir::FlatSymbolRefAttr, unsigned int, unsigned, unsigned int) {
+ // TODO by @Elio
+ return success();
+}
+
//===----------------------------------------------------------------------===//
// DynamicCastInfoAtttr definitions
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 47ccb291ea745..5401fefd50c98 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -497,6 +497,15 @@ static LogicalResult checkConstantTypes(mlir::Operation *op, mlir::Type opType,
cir::VTableAttr>(attrType))
return success();
+ if (mlir::isa<cir::RelativeVTableAttr>(attrType)) {
+ auto at = mlir::cast<mlir::TypedAttr>(attrType);
+ if (at.getType() != opType)
+ return op->emitOpError("result type (")
+ << opType << ") does not match value type (" << at.getType()
+ << ")";
+ return success();
+ }
+
assert(isa<TypedAttr>(attrType) && "What else could we be looking at here?");
return op->emitOpError("global with type ")
<< cast<TypedAttr>(attrType).getType() << " not yet supported";
@@ -2129,8 +2138,9 @@ cir::VTableAddrPointOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
std::optional<mlir::Attribute> init = op.getInitialValue();
if (!init)
return success();
- if (!isa<cir::VTableAttr>(*init))
- return emitOpError("Expected #cir.vtable in initializer for global '")
+ if (!(isa<cir::VTableAttr>(*init) || isa<cir::RelativeVTableAttr>(*init)))
+ return emitOpError("Expected #cir.vtable or #cir.relative_vtable in "
+ "initializer for global '")
<< name << "'";
return success();
}
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 2aaf2b9f946a3..75f44eaf185a8 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -691,6 +691,58 @@ mlir::Value CIRAttrToValue::visitCirAttr(cir::VTableAttr vtableArr) {
return result;
}
+// RelativeVTableAttr visitor.
+mlir::Value CIRAttrToValue::visitCirAttr(cir::RelativeVTableAttr rVtableArr) {
+ mlir::Type llvmTy = converter->convertType(rVtableArr.getType());
+ mlir::Location loc = parentOp->getLoc();
+ mlir::Value result = mlir::LLVM::UndefOp::create(rewriter, loc, llvmTy);
+
+ for (auto [idx, elt] : llvm::enumerate(rVtableArr.getData())) {
+ mlir::Value init = visit(elt);
+ result =
+ mlir::LLVM::InsertValueOp::create(rewriter, loc, result, init, idx);
+ }
+
+ return result;
+}
+
+// RelativeVTableAttr visitor.
+mlir::Value
+CIRAttrToValue::visitCirAttr(cir::RelativeVTableComponentAttr attr) {
+ mlir::Location loc = parentOp->getLoc();
+
+ auto i32Ty = mlir::IntegerType::get(rewriter.getContext(), 32);
+ auto i64Ty = mlir::IntegerType::get(rewriter.getContext(), 64);
+ auto ptrTy = mlir::LLVM::LLVMPointerType::get(rewriter.getContext());
+
+ auto globalOp = mlir::cast<cir::GlobalOp>(parentOp);
+
+ auto target = mlir::LLVM::AddressOfOp::create(rewriter, loc, ptrTy,
+ attr.getSymbol().getValue());
+
+ auto vtable = mlir::LLVM::AddressOfOp::create(rewriter, loc, ptrTy,
+ globalOp.getSymName());
+
+ auto vtableObjectTy = converter->convertType(globalOp.getSymType());
+
+ auto addressPoint = mlir::LLVM::GEPOp::create(
+ rewriter, loc, ptrTy, vtableObjectTy, vtable,
+ llvm::ArrayRef<mlir::LLVM::GEPArg>{0, attr.getVtableComponentIndex(),
+ attr.getVtableAddressPoint()},
+ mlir::LLVM::GEPNoWrapFlags::inbounds);
+ addressPoint->dump();
+
+ auto targetInt = mlir::LLVM::PtrToIntOp::create(rewriter, loc, i64Ty, target);
+
+ auto addressPointInt =
+ mlir::LLVM::PtrToIntOp::create(rewriter, loc, i64Ty, addressPoint);
+
+ auto diff = mlir::LLVM::SubOp::create(rewriter, loc, i64Ty, targetInt,
+ addressPointInt);
+
+ return mlir::LLVM::TruncOp::create(rewriter, loc, i32Ty, diff);
+}
+
/// ZeroAttr visitor.
mlir::Value CIRAttrToValue::visitCirAttr(cir::ZeroAttr attr) {
mlir::Location loc = parentOp->getLoc();
@@ -2427,7 +2479,7 @@ CIRToLLVMGlobalOpLowering::matchAndRewriteRegionInitializedGlobal(
assert((isa<cir::ConstArrayAttr, cir::ConstRecordAttr, cir::ConstVectorAttr,
cir::ConstPtrAttr, cir::ConstComplexAttr, cir::GlobalViewAttr,
cir::TypeInfoAttr, cir::UndefAttr, cir::PoisonAttr,
- cir::VTableAttr, cir::ZeroAttr>(init)));
+ cir::VTableAttr, cir::ZeroAttr, cir::RelativeVTableAttr>(init)));
// TODO(cir): once LLVM's dialect has proper equivalent attributes this
// should be updated. For now, we use a custom op to initialize globals
@@ -2489,7 +2541,8 @@ mlir::LogicalResult CIRToLLVMGlobalOpLowering::matchAndRewrite(
cir::ConstRecordAttr, cir::ConstPtrAttr,
cir::ConstComplexAttr, cir::GlobalViewAttr,
cir::TypeInfoAttr, cir::UndefAttr, cir::PoisonAttr,
- cir::VTableAttr, cir::ZeroAttr>(init.value())) {
+ cir::VTableAttr, cir::ZeroAttr,
+ cir::RelativeVTableAttr>(init.value())) {
// TODO(cir): once LLVM's dialect has proper equivalent attributes this
// should be updated. For now, we use a custom op to initialize globals
// to the appropriate value.
diff --git a/clang/test/CIR/IR/invalid-vtable.cir b/clang/test/CIR/IR/invalid-vtable.cir
index 2e880169abbfa..9fda20e8bbbb4 100644
--- a/clang/test/CIR/IR/invalid-vtable.cir
+++ b/clang/test/CIR/IR/invalid-vtable.cir
@@ -13,7 +13,7 @@ cir.func @reference_unknown_vtable() {
!u32i = !cir.int<u, 32>
cir.global linkonce_odr @_ZTT1D = #cir.const_array<[#cir.global_view<@_ZTV1D, [0 : i32, 3 : i32]> : !cir.ptr<!u8i>, #cir.global_view<@_ZTC1D0_1B, [0 : i32, 3 : i32]> : !cir.ptr<!u8i>]> : !cir.array<!cir.ptr<!u8i> x 2>
cir.func @reference_non_vtable() {
- // expected-error @below {{Expected #cir.vtable in initializer for global '_ZTT1D'}}
+ // expected-error @below {{Expected #cir.vtable or #cir.relative_vtable in initializer for global '_ZTT1D'}}
%0 = cir.vtable.address_point(@_ZTT1D, address_point = <index = 0, offset = 2>) : !cir.vptr
cir.return
}
diff --git a/ll.ll b/ll.ll
deleted file mode 100644
index 7cae76a7071d9..0000000000000
--- a/ll.ll
+++ /dev/null
@@ -1,250 +0,0 @@
-; ModuleID = '1.cpp'
-source_filename = "1.cpp"
-target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
-target triple = "x86_64-unknown-linux-gnu"
-
-%struct.C = type { %struct.A.base, [4 x i8], %struct.B.base, i32 }
-%struct.A.base = type <{ ptr, i32 }>
-%struct.B.base = type <{ ptr, i32 }>
-
-$_ZN1CC1Ev = comdat any
-
-$_ZN1CC2Ev = comdat any
-
-$_ZN1AC2Ev = comdat any
-
-$_ZN1BC2Ev = comdat any
-
- at _ZTV1A = unnamed_addr constant { [3 x ptr] } { [3 x ptr] [ptr null, ptr @_ZTI1A, ptr @_ZN1A1fEv] }, align 8
- at _ZTI1A = constant { ptr, ptr } { ptr getelementptr inbounds (ptr, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 2), ptr @_ZTS1A }, align 8
- at _ZTVN10__cxxabiv117__class_type_infoE = external global [0 x ptr]
- at _ZTS1A = constant [3 x i8] c"1A\00", align 1
- at _ZTV1B = unnamed_addr constant { [3 x ptr] } { [3 x ptr] [ptr null, ptr @_ZTI1B, ptr @_ZN1B1gEv] }, align 8
- at _ZTI1B = constant { ptr, ptr } { ptr getelementptr inbounds (ptr, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 2), ptr @_ZTS1B }, align 8
- at _ZTS1B = constant [3 x i8] c"1B\00", align 1
- at _ZTV1C = unnamed_addr constant { [4 x ptr], [3 x ptr] } { [4 x ptr] [ptr null, ptr @_ZTI1C, ptr @_ZN1C1fEv, ptr @_ZN1C1gEv], [3 x ptr] [ptr inttoptr (i64 -16 to ptr), ptr @_ZTI1C, ptr @_ZThn16_N1C1gEv] }, align 8
- at _ZTI1C = constant { ptr, ptr, i32, i32, ptr, i64, ptr, i64 } { ptr getelementptr inbounds (ptr, ptr @_ZTVN10__cxxabiv121__vmi_class_type_infoE, i64 2), ptr @_ZTS1C, i32 0, i32 2, ptr @_ZTI1A, i64 2, ptr @_ZTI1B, i64 4098 }, align 8
- at _ZTVN10__cxxabiv121__vmi_class_type_infoE = external global [0 x ptr]
- at _ZTS1C = constant [3 x i8] c"1C\00", align 1
-
-; Function Attrs: mustprogress noinline nounwind optnone
-define dso_local void @_ZN1A1fEv(ptr noundef nonnull align 8 dereferenceable(12) %this) unnamed_addr #0 align 2 {
-entry:
- %this.addr = alloca ptr, align 8
- store ptr %this, ptr %this.addr, align 8
- %this1 = load ptr, ptr %this.addr, align 8
- ret void
-}
-
-; Function Attrs: mustprogress noinline nounwind optnone
-define dso_local void @_ZN1B1gEv(ptr noundef nonnull align 8 dereferenceable(12) %this) unnamed_addr #0 align 2 {
-entry:
- %this.addr = alloca ptr, align 8
- store ptr %this, ptr %this.addr, align 8
- %this1 = load ptr, ptr %this.addr, align 8
- ret void
-}
-
-; Function Attrs: mustprogress noinline nounwind optnone
-define dso_local void @_ZN1C1fEv(ptr noundef nonnull align 8 dereferenceable(32) %this) unnamed_addr #0 align 2 {
-entry:
- %this.addr = alloca ptr, align 8
- store ptr %this, ptr %this.addr, align 8
- %this1 = load ptr, ptr %this.addr, align 8
- ret void
-}
-
-; Function Attrs: mustprogress noinline nounwind optnone
-define dso_local void @_ZN1C1gEv(ptr noundef nonnull align 8 dereferenceable(32) %this) unnamed_addr #0 align 2 {
-entry:
- %this.addr = alloca ptr, align 8
- store ptr %this, ptr %this.addr, align 8
- %this1 = load ptr, ptr %this.addr, align 8
- ret void
-}
-
-; Function Attrs: noinline nounwind optnone
-define dso_local void @_ZThn16_N1C1gEv(ptr noundef %this) unnamed_addr #1 align 2 {
-entry:
- %this.addr = alloca ptr, align 8
- store ptr %this, ptr %this.addr, align 8
- %this1 = load ptr, ptr %this.addr, align 8
- %0 = getelementptr inbounds i8, ptr %this1, i64 -16
- tail call void @_ZN1C1gEv(ptr noundef nonnull align 8 dereferenceable(32) %0)
- ret void
-}
-
-; Function Attrs: mustprogress noinline nounwind optnone
-define dso_local void @_Z14call_through_AP1A(ptr noundef %pa) #0 {
-entry:
- %pa.addr = alloca ptr, align 8
- store ptr %pa, ptr %pa.addr, align 8
- %0 = load ptr, ptr %pa.addr, align 8
- %vtable = load ptr, ptr %0, align 8
- %vfn = getelementptr inbounds ptr, ptr %vtable, i64 0
- %1 = load ptr, ptr %vfn, align 8
- call void %1(ptr noundef nonnull align 8 dereferenceable(12) %0)
- ret void
-}
-
-; Function Attrs: mustprogress noinline nounwind optnone
-define dso_local void @_Z14call_through_BP1B(ptr noundef %pb) #0 {
-entry:
- %pb.addr = alloca ptr, align 8
- store ptr %pb, ptr %pb.addr, align 8
- %0 = load ptr, ptr %pb.addr, align 8
- %vtable = load ptr, ptr %0, align 8
- %vfn = getelementptr inbounds ptr, ptr %vtable, i64 0
- %1 = load ptr, ptr %vfn, align 8
- call void %1(ptr noundef nonnull align 8 dereferenceable(12) %0)
- ret void
-}
-
-; Function Attrs: mustprogress noinline nounwind optnone
-define dso_local void @_Z14call_through_CP1C(ptr noundef %pc) #0 {
-entry:
- %pc.addr = alloca ptr, align 8
- store ptr %pc, ptr %pc.addr, align 8
- %0 = load ptr, ptr %pc.addr, align 8
- %vtable = load ptr, ptr %0, align 8
- %vfn = getelementptr inbounds ptr, ptr %vtable, i64 0
- %1 = load ptr, ptr %vfn, align 8
- call void %1(ptr noundef nonnull align 8 dereferenceable(32) %0)
- %2 = load ptr, ptr %pc.addr, align 8
- %vtable1 = load ptr, ptr %2, align 8
- %vfn2 = getelementptr inbounds ptr, ptr %vtable1, i64 1
- %3 = load ptr, ptr %vfn2, align 8
- call void %3(ptr noundef nonnull align 8 dereferenceable(32) %2)
- ret void
-}
-
-; Function Attrs: mustprogress noinline nounwind optnone
-define dso_local void @_Z21direct_qualified_callP1C(ptr noundef %pc) #0 {
-entry:
- %pc.addr = alloca ptr, align 8
- store ptr %pc, ptr %pc.addr, align 8
- %0 = load ptr, ptr %pc.addr, align 8
- call void @_ZN1C1fEv(ptr noundef nonnull align 8 dereferenceable(32) %0)
- %1 = load ptr, ptr %pc.addr, align 8
- call void @_ZN1C1gEv(ptr noundef nonnull align 8 dereferenceable(32) %1)
- ret void
-}
-
-; Function Attrs: mustprogress noinline nounwind optnone
-define dso_local void @_Z10base_castsP1C(ptr noundef %pc) #0 {
-entry:
- %pc.addr = alloca ptr, align 8
- %pa = alloca ptr, align 8
- %pb = alloca ptr, align 8
- store ptr %pc, ptr %pc.addr, align 8
- %0 = load ptr, ptr %pc.addr, align 8
- store ptr %0, ptr %pa, align 8
- %1 = load ptr, ptr %pc.addr, align 8
- %2 = icmp eq ptr %1, null
- br i1 %2, label %cast.end, label %cast.notnull
-
-cast.notnull: ; preds = %entry
- %add.ptr = getelementptr inbounds i8, ptr %1, i64 16
- br label %cast.end
-
-cast.end: ; preds = %cast.notnull, %entry
- %cast.result = phi ptr [ %add.ptr, %cast.notnull ], [ null, %entry ]
- store ptr %cast.result, ptr %pb, align 8
- %3 = load ptr, ptr %pa, align 8
- %vtable = load ptr, ptr %3, align 8
- %vfn = getelementptr inbounds ptr, ptr %vtable, i64 0
- %4 = load ptr, ptr %vfn, align 8
- call void %4(ptr noundef nonnull align 8 dereferenceable(12) %3)
- %5 = load ptr, ptr %pb, align 8
- %vtable1 = load ptr, ptr %5, align 8
- %vfn2 = getelementptr inbounds ptr, ptr %vtable1, i64 0
- %6 = load ptr, ptr %vfn2, align 8
- call void %6(ptr noundef nonnull align 8 dereferenceable(12) %5)
- ret void
-}
-
-; Function Attrs: mustprogress noinline nounwind optnone
-define dso_local void @_Z18construct_and_callv() #0 {
-entry:
- %obj = alloca %struct.C, align 8
- %pa = alloca ptr, align 8
- %pb = alloca ptr, align 8
- call void @_ZN1CC1Ev(ptr noundef nonnull align 8 dereferenceable(32) %obj) #2
- call void @_ZN1C1fEv(ptr noundef nonnull align 8 dereferenceable(32) %obj)
- call void @_ZN1C1gEv(ptr noundef nonnull align 8 dereferenceable(32) %obj)
- store ptr %obj, ptr %pa, align 8
- %0 = icmp eq ptr %obj, null
- br i1 %0, label %cast.end, label %cast.notnull
-
-cast.notnull: ; preds = %entry
- %add.ptr = getelementptr inbounds i8, ptr %obj, i64 16
- br label %cast.end
-
-cast.end: ; preds = %cast.notnull, %entry
- %cast.result = phi ptr [ %add.ptr, %cast.notnull ], [ null, %entry ]
- store ptr %cast.result, ptr %pb, align 8
- %1 = load ptr, ptr %pa, align 8
- %vtable = load ptr, ptr %1, align 8
- %vfn = getelementptr inbounds ptr, ptr %vtable, i64 0
- %2 = load ptr, ptr %vfn, align 8
- call void %2(ptr noundef nonnull align 8 dereferenceable(12) %1)
- %3 = load ptr, ptr %pb, align 8
- %vtable1 = load ptr, ptr %3, align 8
- %vfn2 = getelementptr inbounds ptr, ptr %vtable1, i64 0
- %4 = load ptr, ptr %vfn2, align 8
- call void %4(ptr noundef nonnull align 8 dereferenceable(12) %3)
- ret void
-}
-
-; Function Attrs: mustprogress noinline nounwind optnone
-define linkonce_odr void @_ZN1CC1Ev(ptr noundef nonnull align 8 dereferenceable(32) %this) unnamed_addr #0 comdat align 2 {
-entry:
- %this.addr = alloca ptr, align 8
- store ptr %this, ptr %this.addr, align 8
- %this1 = load ptr, ptr %this.addr, align 8
- call void @_ZN1CC2Ev(ptr noundef nonnull align 8 dereferenceable(32) %this1) #2
- ret void
-}
-
-; Function Attrs: mustprogress noinline nounwind optnone
-define linkonce_odr void @_ZN1CC2Ev(ptr noundef nonnull align 8 dereferenceable(32) %this) unnamed_addr #0 comdat align 2 {
-entry:
- %this.addr = alloca ptr, align 8
- store ptr %this, ptr %this.addr, align 8
- %this1 = load ptr, ptr %this.addr, align 8
- call void @_ZN1AC2Ev(ptr noundef nonnull align 8 dereferenceable(12) %this1) #2
- %0 = getelementptr inbounds i8, ptr %this1, i64 16
- call void @_ZN1BC2Ev(ptr noundef nonnull align 8 dereferenceable(12) %0) #2
- store ptr getelementptr inbounds inrange(-16, 16) ({ [4 x ptr], [3 x ptr] }, ptr @_ZTV1C, i32 0, i32 0, i32 2), ptr %this1, align 8
- %add.ptr = getelementptr inbounds i8, ptr %this1, i64 16
- store ptr getelementptr inbounds inrange(-16, 8) ({ [4 x ptr], [3 x ptr] }, ptr @_ZTV1C, i32 0, i32 1, i32 2), ptr %add.ptr, align 8
- ret void
-}
-
-; Function Attrs: mustprogress noinline nounwind optnone
-define linkonce_odr void @_ZN1AC2Ev(ptr noundef nonnull align 8 dereferenceable(12) %this) unnamed_addr #0 comdat align 2 {
-entry:
- %this.addr = alloca ptr, align 8
- store ptr %this, ptr %this.addr, align 8
- %this1 = load ptr, ptr %this.addr, align 8
- store ptr getelementptr inbounds inrange(-16, 8) ({ [3 x ptr] }, ptr @_ZTV1A, i32 0, i32 0, i32 2), ptr %this1, align 8
- ret void
-}
-
-; Function Attrs: mustprogress noinline nounwind optnone
-define linkonce_odr void @_ZN1BC2Ev(ptr noundef nonnull align 8 dereferenceable(12) %this) unnamed_addr #0 comdat align 2 {
-entry:
- %this.addr = alloca ptr, align 8
- store ptr %this, ptr %this.addr, align 8
- %this1 = load ptr, ptr %this.addr, align 8
- store ptr getelementptr inbounds inrange(-16, 8) ({ [3 x ptr] }, ptr @_ZTV1B, i32 0, i32 0, i32 2), ptr %this1, align 8
- ret void
-}
-
-attributes #0 = { mustprogress noinline nounwind optnone "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" }
-attributes #1 = { noinline nounwind optnone "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" }
-attributes #2 = { nounwind }
-
-!llvm.ident = !{!0}
-
-!0 = !{!"clang version 23.0.0git (git at github.com:xiongzile/llvm-project.git c1ef54f3f3c46982dd7dc03c41f66f8e274ac2e6)"}
diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
index 2b603efadb476..8117d083aba71 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
@@ -2267,9 +2267,10 @@ AddressOfOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
auto alias = dyn_cast_or_null<AliasOp>(symbol);
auto ifunc = dyn_cast_or_null<IFuncOp>(symbol);
- if (!global && !function && !alias && !ifunc)
+ if (!global && !function && !alias && !ifunc) {
return emitOpError("must reference a global defined by 'llvm.mlir.global', "
"'llvm.mlir.alias' or 'llvm.func' or 'llvm.mlir.ifunc'");
+ }
LLVMPointerType type = getType();
if ((global && global.getAddrSpace() != type.getAddressSpace()) ||
diff --git a/rel-ll.ll b/rel-ll.ll
deleted file mode 100644
index 46c7a87b63872..0000000000000
--- a/rel-ll.ll
+++ /dev/null
@@ -1,259 +0,0 @@
-; ModuleID = '1.cpp'
-source_filename = "1.cpp"
-target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
-target triple = "x86_64-unknown-linux-gnu"
-
-%struct.C = type { %struct.A.base, [4 x i8], %struct.B.base, i32 }
-%struct.A.base = type <{ ptr, i32 }>
-%struct.B.base = type <{ ptr, i32 }>
-
-$_ZN1CC1Ev = comdat any
-
-$_ZN1CC2Ev = comdat any
-
-$_ZN1AC2Ev = comdat any
-
-$_ZN1BC2Ev = comdat any
-
-$_ZTI1A.rtti_proxy = comdat any
-
-$_ZTI1B.rtti_proxy = comdat any
-
-$_ZTI1C.rtti_proxy = comdat any
-
- at _ZTV1A.local = internal unnamed_addr constant { [3 x i32] } { [3 x i32] [i32 0, i32 trunc (i64 sub (i64 ptrtoint (ptr @_ZTI1A.rtti_proxy to i64), i64 ptrtoint (ptr getelementptr inbounds ({ [3 x i32] }, ptr @_ZTV1A.local, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (ptr dso_local_equivalent @_ZN1A1fEv to i64), i64 ptrtoint (ptr getelementptr inbounds ({ [3 x i32] }, ptr @_ZTV1A.local, i32 0, i32 0, i32 2) to i64)) to i32)] }, align 4
- at _ZTI1A = constant { ptr, ptr } { ptr getelementptr inbounds (i8, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i32 8), ptr @_ZTS1A }, align 8
- at _ZTVN10__cxxabiv117__class_type_infoE = external global [0 x ptr]
- at _ZTS1A = constant [3 x i8] c"1A\00", align 1
- at _ZTI1A.rtti_proxy = linkonce_odr hidden unnamed_addr constant ptr @_ZTI1A, comdat
- at _ZTV1B.local = internal unnamed_addr constant { [3 x i32] } { [3 x i32] [i32 0, i32 trunc (i64 sub (i64 ptrtoint (ptr @_ZTI1B.rtti_proxy to i64), i64 ptrtoint (ptr getelementptr inbounds ({ [3 x i32] }, ptr @_ZTV1B.local, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (ptr dso_local_equivalent @_ZN1B1gEv to i64), i64 ptrtoint (ptr getelementptr inbounds ({ [3 x i32] }, ptr @_ZTV1B.local, i32 0, i32 0, i32 2) to i64)) to i32)] }, align 4
- at _ZTI1B = constant { ptr, ptr } { ptr getelementptr inbounds (i8, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i32 8), ptr @_ZTS1B }, align 8
- at _ZTS1B = constant [3 x i8] c"1B\00", align 1
- at _ZTI1B.rtti_proxy = linkonce_odr hidden unnamed_addr constant ptr @_ZTI1B, comdat
- at _ZTV1C.local = internal unnamed_addr constant { [4 x i32], [3 x i32] } { [4 x i32] [i32 0, i32 trunc (i64 sub (i64 ptrtoint (ptr @_ZTI1C.rtti_proxy to i64), i64 ptrtoint (ptr getelementptr inbounds ({ [4 x i32], [3 x i32] }, ptr @_ZTV1C.local, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (ptr dso_local_equivalent @_ZN1C1fEv to i64), i64 ptrtoint (ptr getelementptr inbounds ({ [4 x i32], [3 x i32] }, ptr @_ZTV1C.local, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (ptr dso_local_equivalent @_ZN1C1gEv to i64), i64 ptrtoint (ptr getelementptr inbounds ({ [4 x i32], [3 x i32] }, ptr @_ZTV1C.local, i32 0, i32 0, i32 2) to i64)) to i32)], [3 x i32] [i32 -16, i32 trunc (i64 sub (i64 ptrtoint (ptr @_ZTI1C.rtti_proxy to i64), i64 ptrtoint (ptr getelementptr inbounds ({ [4 x i32], [3 x i32] }, ptr @_ZTV1C.local, i32 0, i32 1, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (ptr dso_local_equivalent @_ZThn16_N1C1gEv to i64), i64 ptrtoint (ptr getelementptr inbounds ({ [4 x i32], [3 x i32] }, ptr @_ZTV1C.local, i32 0, i32 1, i32 2) to i64)) to i32)] }, align 4
- at _ZTI1C = constant { ptr, ptr, i32, i32, ptr, i64, ptr, i64 } { ptr getelementptr inbounds (i8, ptr @_ZTVN10__cxxabiv121__vmi_class_type_infoE, i32 8), ptr @_ZTS1C, i32 0, i32 2, ptr @_ZTI1A, i64 2, ptr @_ZTI1B, i64 4098 }, align 8
- at _ZTVN10__cxxabiv121__vmi_class_type_infoE = external global [0 x ptr]
- at _ZTS1C = constant [3 x i8] c"1C\00", align 1
- at _ZTI1C.rtti_proxy = linkonce_odr hidden unnamed_addr constant ptr @_ZTI1C, comdat
-
- at _ZTV1A = unnamed_addr alias { [3 x i32] }, ptr @_ZTV1A.local
- at _ZTV1B = unnamed_addr alias { [3 x i32] }, ptr @_ZTV1B.local
- at _ZTV1C = unnamed_addr alias { [4 x i32], [3 x i32] }, ptr @_ZTV1C.local
-
-; Function Attrs: mustprogress noinline nounwind optnone
-define dso_local void @_ZN1A1fEv(ptr noundef nonnull align 8 dereferenceable(12) %this) unnamed_addr #0 align 2 {
-entry:
- %this.addr = alloca ptr, align 8
- store ptr %this, ptr %this.addr, align 8
- %this1 = load ptr, ptr %this.addr, align 8
- ret void
-}
-
-; Function Attrs: mustprogress noinline nounwind optnone
-define dso_local void @_ZN1B1gEv(ptr noundef nonnull align 8 dereferenceable(12) %this) unnamed_addr #0 align 2 {
-entry:
- %this.addr = alloca ptr, align 8
- store ptr %this, ptr %this.addr, align 8
- %this1 = load ptr, ptr %this.addr, align 8
- ret void
-}
-
-; Function Attrs: mustprogress noinline nounwind optnone
-define dso_local void @_ZN1C1fEv(ptr noundef nonnull align 8 dereferenceable(32) %this) unnamed_addr #0 align 2 {
-entry:
- %this.addr = alloca ptr, align 8
- store ptr %this, ptr %this.addr, align 8
- %this1 = load ptr, ptr %this.addr, align 8
- ret void
-}
-
-; Function Attrs: mustprogress noinline nounwind optnone
-define dso_local void @_ZN1C1gEv(ptr noundef nonnull align 8 dereferenceable(32) %this) unnamed_addr #0 align 2 {
-entry:
- %this.addr = alloca ptr, align 8
- store ptr %this, ptr %this.addr, align 8
- %this1 = load ptr, ptr %this.addr, align 8
- ret void
-}
-
-; Function Attrs: noinline nounwind optnone
-define dso_local void @_ZThn16_N1C1gEv(ptr noundef %this) unnamed_addr #1 align 2 {
-entry:
- %this.addr = alloca ptr, align 8
- store ptr %this, ptr %this.addr, align 8
- %this1 = load ptr, ptr %this.addr, align 8
- %0 = getelementptr inbounds i8, ptr %this1, i64 -16
- tail call void @_ZN1C1gEv(ptr noundef nonnull align 8 dereferenceable(32) %0)
- ret void
-}
-
-; Function Attrs: mustprogress noinline nounwind optnone
-define dso_local void @_Z14call_through_AP1A(ptr noundef %pa) #0 {
-entry:
- %pa.addr = alloca ptr, align 8
- store ptr %pa, ptr %pa.addr, align 8
- %0 = load ptr, ptr %pa.addr, align 8
- %vtable = load ptr, ptr %0, align 8
- %1 = call ptr @llvm.load.relative.i32(ptr %vtable, i32 0)
- call void %1(ptr noundef nonnull align 8 dereferenceable(12) %0)
- ret void
-}
-
-; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: read)
-declare ptr @llvm.load.relative.i32(ptr, i32) #2
-
-; Function Attrs: mustprogress noinline nounwind optnone
-define dso_local void @_Z14call_through_BP1B(ptr noundef %pb) #0 {
-entry:
- %pb.addr = alloca ptr, align 8
- store ptr %pb, ptr %pb.addr, align 8
- %0 = load ptr, ptr %pb.addr, align 8
- %vtable = load ptr, ptr %0, align 8
- %1 = call ptr @llvm.load.relative.i32(ptr %vtable, i32 0)
- call void %1(ptr noundef nonnull align 8 dereferenceable(12) %0)
- ret void
-}
-
-; Function Attrs: mustprogress noinline nounwind optnone
-define dso_local void @_Z14call_through_CP1C(ptr noundef %pc) #0 {
-entry:
- %pc.addr = alloca ptr, align 8
- store ptr %pc, ptr %pc.addr, align 8
- %0 = load ptr, ptr %pc.addr, align 8
- %vtable = load ptr, ptr %0, align 8
- %1 = call ptr @llvm.load.relative.i32(ptr %vtable, i32 0)
- call void %1(ptr noundef nonnull align 8 dereferenceable(32) %0)
- %2 = load ptr, ptr %pc.addr, align 8
- %vtable1 = load ptr, ptr %2, align 8
- %3 = call ptr @llvm.load.relative.i32(ptr %vtable1, i32 4)
- call void %3(ptr noundef nonnull align 8 dereferenceable(32) %2)
- ret void
-}
-
-; Function Attrs: mustprogress noinline nounwind optnone
-define dso_local void @_Z21direct_qualified_callP1C(ptr noundef %pc) #0 {
-entry:
- %pc.addr = alloca ptr, align 8
- store ptr %pc, ptr %pc.addr, align 8
- %0 = load ptr, ptr %pc.addr, align 8
- call void @_ZN1C1fEv(ptr noundef nonnull align 8 dereferenceable(32) %0)
- %1 = load ptr, ptr %pc.addr, align 8
- call void @_ZN1C1gEv(ptr noundef nonnull align 8 dereferenceable(32) %1)
- ret void
-}
-
-; Function Attrs: mustprogress noinline nounwind optnone
-define dso_local void @_Z10base_castsP1C(ptr noundef %pc) #0 {
-entry:
- %pc.addr = alloca ptr, align 8
- %pa = alloca ptr, align 8
- %pb = alloca ptr, align 8
- store ptr %pc, ptr %pc.addr, align 8
- %0 = load ptr, ptr %pc.addr, align 8
- store ptr %0, ptr %pa, align 8
- %1 = load ptr, ptr %pc.addr, align 8
- %2 = icmp eq ptr %1, null
- br i1 %2, label %cast.end, label %cast.notnull
-
-cast.notnull: ; preds = %entry
- %add.ptr = getelementptr inbounds i8, ptr %1, i32 16
- br label %cast.end
-
-cast.end: ; preds = %cast.notnull, %entry
- %cast.result = phi ptr [ %add.ptr, %cast.notnull ], [ null, %entry ]
- store ptr %cast.result, ptr %pb, align 8
- %3 = load ptr, ptr %pa, align 8
- %vtable = load ptr, ptr %3, align 8
- %4 = call ptr @llvm.load.relative.i32(ptr %vtable, i32 0)
- call void %4(ptr noundef nonnull align 8 dereferenceable(12) %3)
- %5 = load ptr, ptr %pb, align 8
- %vtable1 = load ptr, ptr %5, align 8
- %6 = call ptr @llvm.load.relative.i32(ptr %vtable1, i32 0)
- call void %6(ptr noundef nonnull align 8 dereferenceable(12) %5)
- ret void
-}
-
-; Function Attrs: mustprogress noinline nounwind optnone
-define dso_local void @_Z18construct_and_callv() #0 {
-entry:
- %obj = alloca %struct.C, align 8
- %pa = alloca ptr, align 8
- %pb = alloca ptr, align 8
- call void @_ZN1CC1Ev(ptr noundef nonnull align 8 dereferenceable(32) %obj) #3
- call void @_ZN1C1fEv(ptr noundef nonnull align 8 dereferenceable(32) %obj)
- call void @_ZN1C1gEv(ptr noundef nonnull align 8 dereferenceable(32) %obj)
- store ptr %obj, ptr %pa, align 8
- %0 = icmp eq ptr %obj, null
- br i1 %0, label %cast.end, label %cast.notnull
-
-cast.notnull: ; preds = %entry
- %add.ptr = getelementptr inbounds i8, ptr %obj, i32 16
- br label %cast.end
-
-cast.end: ; preds = %cast.notnull, %entry
- %cast.result = phi ptr [ %add.ptr, %cast.notnull ], [ null, %entry ]
- store ptr %cast.result, ptr %pb, align 8
- %1 = load ptr, ptr %pa, align 8
- %vtable = load ptr, ptr %1, align 8
- %2 = call ptr @llvm.load.relative.i32(ptr %vtable, i32 0)
- call void %2(ptr noundef nonnull align 8 dereferenceable(12) %1)
- %3 = load ptr, ptr %pb, align 8
- %vtable1 = load ptr, ptr %3, align 8
- %4 = call ptr @llvm.load.relative.i32(ptr %vtable1, i32 0)
- call void %4(ptr noundef nonnull align 8 dereferenceable(12) %3)
- ret void
-}
-
-; Function Attrs: mustprogress noinline nounwind optnone
-define linkonce_odr void @_ZN1CC1Ev(ptr noundef nonnull align 8 dereferenceable(32) %this) unnamed_addr #0 comdat align 2 {
-entry:
- %this.addr = alloca ptr, align 8
- store ptr %this, ptr %this.addr, align 8
- %this1 = load ptr, ptr %this.addr, align 8
- call void @_ZN1CC2Ev(ptr noundef nonnull align 8 dereferenceable(32) %this1) #3
- ret void
-}
-
-; Function Attrs: mustprogress noinline nounwind optnone
-define linkonce_odr void @_ZN1CC2Ev(ptr noundef nonnull align 8 dereferenceable(32) %this) unnamed_addr #0 comdat align 2 {
-entry:
- %this.addr = alloca ptr, align 8
- store ptr %this, ptr %this.addr, align 8
- %this1 = load ptr, ptr %this.addr, align 8
- call void @_ZN1AC2Ev(ptr noundef nonnull align 8 dereferenceable(12) %this1) #3
- %0 = getelementptr inbounds i8, ptr %this1, i64 16
- call void @_ZN1BC2Ev(ptr noundef nonnull align 8 dereferenceable(12) %0) #3
- store ptr getelementptr inbounds inrange(-8, 8) ({ [4 x i32], [3 x i32] }, ptr @_ZTV1C.local, i32 0, i32 0, i32 2), ptr %this1, align 8
- %add.ptr = getelementptr inbounds i8, ptr %this1, i32 16
- store ptr getelementptr inbounds inrange(-8, 4) ({ [4 x i32], [3 x i32] }, ptr @_ZTV1C.local, i32 0, i32 1, i32 2), ptr %add.ptr, align 8
- ret void
-}
-
-; Function Attrs: mustprogress noinline nounwind optnone
-define linkonce_odr void @_ZN1AC2Ev(ptr noundef nonnull align 8 dereferenceable(12) %this) unnamed_addr #0 comdat align 2 {
-entry:
- %this.addr = alloca ptr, align 8
- store ptr %this, ptr %this.addr, align 8
- %this1 = load ptr, ptr %this.addr, align 8
- store ptr getelementptr inbounds inrange(-8, 4) ({ [3 x i32] }, ptr @_ZTV1A.local, i32 0, i32 0, i32 2), ptr %this1, align 8
- ret void
-}
-
-; Function Attrs: mustprogress noinline nounwind optnone
-define linkonce_odr void @_ZN1BC2Ev(ptr noundef nonnull align 8 dereferenceable(12) %this) unnamed_addr #0 comdat align 2 {
-entry:
- %this.addr = alloca ptr, align 8
- store ptr %this, ptr %this.addr, align 8
- %this1 = load ptr, ptr %this.addr, align 8
- store ptr getelementptr inbounds inrange(-8, 4) ({ [3 x i32] }, ptr @_ZTV1B.local, i32 0, i32 0, i32 2), ptr %this1, align 8
- ret void
-}
-
-attributes #0 = { mustprogress noinline nounwind optnone "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" }
-attributes #1 = { noinline nounwind optnone "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" }
-attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: read) }
-attributes #3 = { nounwind }
-
-!llvm.ident = !{!0}
-
-!0 = !{!"clang version 23.0.0git (git at github.com:xiongzile/llvm-project.git c1ef54f3f3c46982dd7dc03c41f66f8e274ac2e6)"}
More information about the Mlir-commits
mailing list