[clang] [CIR] Add support for emitting multi-vtables (PR #155027)
Andy Kaylor via cfe-commits
cfe-commits at lists.llvm.org
Mon Aug 25 09:27:14 PDT 2025
https://github.com/andykaylor updated https://github.com/llvm/llvm-project/pull/155027
>From 6d89819044ba3f2500af4dfa07f8f93bba00aeb9 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Wed, 20 Aug 2025 14:44:32 -0700
Subject: [PATCH 1/3] [CIR] Add support for emitting multi-vtables
This change adds support for emitting multiple tables in a global
vtable object to handle the case of multiple-inheritence.
---
clang/lib/CIR/CodeGen/CIRGenVTables.cpp | 43 ++++----
clang/test/CIR/CodeGen/multi-vtable.cpp | 136 ++++++++++++++++++++++++
2 files changed, 156 insertions(+), 23 deletions(-)
create mode 100644 clang/test/CIR/CodeGen/multi-vtable.cpp
diff --git a/clang/lib/CIR/CodeGen/CIRGenVTables.cpp b/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
index 438b483ccb9c2..28a630becc3dc 100644
--- a/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
@@ -144,33 +144,30 @@ void CIRGenVTables::createVTableInitializer(cir::GlobalOp &vtableOp,
layout.getAddressPointIndices();
unsigned nextVTableThunkIndex = 0;
- if (layout.getNumVTables() > 1)
- cgm.errorNYI("emitVTableDefinitions: multiple vtables");
-
- // We'll need a loop here to handle multiple vtables, but for now we only
- // support one.
- unsigned vtableIndex = 0;
- size_t vtableStart = layout.getVTableOffset(vtableIndex);
- size_t vtableEnd = vtableStart + layout.getVTableSize(vtableIndex);
-
- // Build a ConstArrayAttr of the vtable components.
- llvm::SmallVector<mlir::Attribute> components;
- for (size_t componentIndex = vtableStart; componentIndex < vtableEnd;
- ++componentIndex) {
- components.push_back(
- getVTableComponent(layout, componentIndex, rtti, nextVTableThunkIndex,
- addressPoints[vtableIndex], vtableHasLocalLinkage));
- }
-
mlir::MLIRContext *mlirContext = &cgm.getMLIRContext();
- // Create a ConstArrayAttr to hold the components.
- auto arr = cir::ConstArrayAttr::get(
- cir::ArrayType::get(componentType, components.size()),
- mlir::ArrayAttr::get(mlirContext, components));
+ SmallVector<mlir::Attribute> vtables;
+ for (unsigned vtableIndex = 0, endIndex = layout.getNumVTables();
+ vtableIndex != endIndex; ++vtableIndex) {
+ // Build a ConstArrayAttr of the vtable components.
+ size_t vtableStart = layout.getVTableOffset(vtableIndex);
+ size_t vtableEnd = vtableStart + layout.getVTableSize(vtableIndex);
+ llvm::SmallVector<mlir::Attribute> components;
+ for (size_t componentIndex = vtableStart; componentIndex < vtableEnd;
+ ++componentIndex) {
+ components.push_back(getVTableComponent(
+ layout, componentIndex, rtti, nextVTableThunkIndex,
+ addressPoints[vtableIndex], vtableHasLocalLinkage));
+ }
+ // Create a ConstArrayAttr to hold the components.
+ auto arr = cir::ConstArrayAttr::get(
+ cir::ArrayType::get(componentType, components.size()),
+ mlir::ArrayAttr::get(mlirContext, components));
+ vtables.push_back(arr);
+ }
// Create a ConstRecordAttr to hold the component array.
- const auto members = mlir::ArrayAttr::get(mlirContext, {arr});
+ const auto members = mlir::ArrayAttr::get(mlirContext, vtables);
cir::ConstRecordAttr record = cgm.getBuilder().getAnonConstRecord(members);
// Create a VTableAttr
diff --git a/clang/test/CIR/CodeGen/multi-vtable.cpp b/clang/test/CIR/CodeGen/multi-vtable.cpp
new file mode 100644
index 0000000000000..c127285800844
--- /dev/null
+++ b/clang/test/CIR/CodeGen/multi-vtable.cpp
@@ -0,0 +1,136 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -mconstructor-aliases -fno-rtti -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -mconstructor-aliases -fno-rtti -fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -mconstructor-aliases -fno-rtti -emit-llvm %s -o %t.ll
+// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
+
+// Note: This test is using -fno-rtti so that we can delay implemntation of that handling.
+// When rtti handling for vtables is implemented, that option should be removed.
+
+class Mother {
+public:
+ virtual void MotherKey();
+ void simple() { }
+ virtual void MotherNonKey() {}
+};
+
+class Father {
+public:
+ virtual void FatherKey();
+};
+
+class Child : public Mother, public Father {
+public:
+ void MotherKey() override;
+};
+
+void Mother::MotherKey() {}
+void Father::FatherKey() {}
+void Child::MotherKey() {}
+
+// CIR-DAG: [[MOTHER_VTABLE_TYPE:.*]] = !cir.record<struct {!cir.array<!cir.ptr<!u8i> x 4>}>
+// CIR-DAG: [[FATHER_VTABLE_TYPE:.*]] = !cir.record<struct {!cir.array<!cir.ptr<!u8i> x 3>}>
+// CIR-DAG: [[CHILD_VTABLE_TYPE:.*]] = !cir.record<struct {!cir.array<!cir.ptr<!u8i> x 4>, !cir.array<!cir.ptr<!u8i> x 3>}>
+// CIR-DAG: !rec_Father = !cir.record<class "Father" {!cir.vptr}
+// CIR-DAG: !rec_Mother = !cir.record<class "Mother" {!cir.vptr}
+// CIR-DAG: !rec_Child = !cir.record<class "Child" {!rec_Mother, !rec_Father}
+
+// Mother vtable
+
+// CIR: cir.global "private" external @_ZTV6Mother = #cir.vtable<{
+// CIR-SAME: #cir.const_array<[
+// CIR-SAME: #cir.ptr<null> : !cir.ptr<!u8i>,
+// CIR-SAME: #cir.ptr<null> : !cir.ptr<!u8i>,
+// CIR-SAME: #cir.global_view<@_ZN6Mother9MotherKeyEv> : !cir.ptr<!u8i>,
+// CIR-SAME: #cir.global_view<@_ZN6Mother12MotherNonKeyEv> : !cir.ptr<!u8i>
+// CIR-SAME: ]> : !cir.array<!cir.ptr<!u8i> x 4>
+// CIR-SAME: }> : [[MOTHER_VTABLE_TYPE]]
+
+// LLVM: @_ZTV6Mother = global { [4 x ptr] } {
+// LLVM-SAME: [4 x ptr] [
+// LLVM-SAME: ptr null,
+// LLVM-SAME: ptr null,
+// LLVM-SAME: ptr @_ZN6Mother9MotherKeyEv,
+// LLVM-SAME: ptr @_ZN6Mother12MotherNonKeyEv
+// LLVM-SAME: ]
+// LLVM-SAME: }
+
+// OGCG: @_ZTV6Mother = unnamed_addr constant { [4 x ptr] } {
+// OGCG-SAME: [4 x ptr] [
+// OGCG-SAME: ptr null,
+// OGCG-SAME: ptr null,
+// OGCG-SAME: ptr @_ZN6Mother9MotherKeyEv,
+// OGCG-SAME: ptr @_ZN6Mother12MotherNonKeyEv
+// OGCG-SAME: ]
+// OGCG-SAME: }
+
+// Father vtable
+
+// CIR: cir.global "private" external @_ZTV6Father = #cir.vtable<{
+// CIR-SAME: #cir.const_array<[
+// CIR-SAME: #cir.ptr<null> : !cir.ptr<!u8i>,
+// CIR-SAME: #cir.ptr<null> : !cir.ptr<!u8i>,
+// CIR-SAME: #cir.global_view<@_ZN6Father9FatherKeyEv> : !cir.ptr<!u8i>
+// CIR-SAME: ]> : !cir.array<!cir.ptr<!u8i> x 3>
+// CIR-SAME: }> : [[FATHER_VTABLE_TYPE]]
+
+// LLVM: @_ZTV6Father = global { [3 x ptr] } {
+// LLVM-SAME: [3 x ptr] [
+// LLVM-SAME: ptr null,
+// LLVM-SAME: ptr null,
+// LLVM-SAME: ptr @_ZN6Father9FatherKeyEv
+// LLVM-SAME: ]
+// LLVM-SAME: }
+
+// OGCG: @_ZTV6Father = unnamed_addr constant { [3 x ptr] } {
+// OGCG-SAME: [3 x ptr] [
+// OGCG-SAME: ptr null,
+// OGCG-SAME: ptr null,
+// OGCG-SAME: ptr @_ZN6Father9FatherKeyEv
+// OGCG-SAME: ]
+// OGCG-SAME: }
+
+// Child vtable
+
+// CIR: cir.global "private" external @_ZTV5Child = #cir.vtable<{
+// CIR-SAME: #cir.const_array<[
+// CIR-SAME: #cir.ptr<null> : !cir.ptr<!u8i>,
+// CIR-SAME: #cir.ptr<null> : !cir.ptr<!u8i>,
+// CIR-SAME: #cir.global_view<@_ZN5Child9MotherKeyEv> : !cir.ptr<!u8i>,
+// CIR-SAME: #cir.global_view<@_ZN6Mother12MotherNonKeyEv> : !cir.ptr<!u8i>
+// CIR-SAME: ]> : !cir.array<!cir.ptr<!u8i> x 4>,
+// CIR-SAME: #cir.const_array<[
+// CIR-SAME: #cir.ptr<-8 : i64> : !cir.ptr<!u8i>,
+// CIR-SAME: #cir.ptr<null> : !cir.ptr<!u8i>,
+// CIR-SAME: #cir.global_view<@_ZN6Father9FatherKeyEv> : !cir.ptr<!u8i>
+// CIR-SAME: ]> : !cir.array<!cir.ptr<!u8i> x 3>
+// CIR-SAME: }> : [[CHILD_VTABLE_TYPE]]
+
+// LLVM: @_ZTV5Child = global { [4 x ptr], [3 x ptr] } {
+// LLVM-SAME: [4 x ptr] [
+// LLVM-SAME: ptr null,
+// LLVM-SAME: ptr null,
+// LLVM-SAME: ptr @_ZN5Child9MotherKeyEv,
+// LLVM-SAME: ptr @_ZN6Mother12MotherNonKeyEv
+// LLVM-SAME: ],
+// LLVM-SAME: [3 x ptr] [
+// LLVM-SAME: ptr inttoptr (i64 -8 to ptr),
+// LLVM-SAME: ptr null,
+// LLVM-SAME: ptr @_ZN6Father9FatherKeyEv
+// LLVM-SAME: ]
+// LLVM-SAME: }
+
+// OGCG: @_ZTV5Child = unnamed_addr constant { [4 x ptr], [3 x ptr] } {
+// OGCG-SAME: [4 x ptr] [
+// OGCG-SAME: ptr null,
+// OGCG-SAME: ptr null,
+// OGCG-SAME: ptr @_ZN5Child9MotherKeyEv,
+// OGCG-SAME: ptr @_ZN6Mother12MotherNonKeyEv
+// OGCG-SAME: ],
+// OGCG-SAME: [3 x ptr] [
+// OGCG-SAME: ptr inttoptr (i64 -8 to ptr),
+// OGCG-SAME: ptr null,
+// OGCG-SAME: ptr @_ZN6Father9FatherKeyEv
+// OGCG-SAME: ]
+// OGCG-SAME: }
>From 1bd07fd2ce82a3584c43af0ae9f58c04be2c92d4 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Fri, 22 Aug 2025 17:00:34 -0700
Subject: [PATCH 2/3] Use llvm:enumerate for vtable address points
---
clang/lib/CIR/CodeGen/CIRGenVTables.cpp | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenVTables.cpp b/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
index 28a630becc3dc..0c7b2aa7944a4 100644
--- a/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
@@ -147,17 +147,16 @@ void CIRGenVTables::createVTableInitializer(cir::GlobalOp &vtableOp,
mlir::MLIRContext *mlirContext = &cgm.getMLIRContext();
SmallVector<mlir::Attribute> vtables;
- for (unsigned vtableIndex = 0, endIndex = layout.getNumVTables();
- vtableIndex != endIndex; ++vtableIndex) {
+ for (auto [vtableIndex, addressPoint] : llvm::enumerate(addressPoints)) {
// Build a ConstArrayAttr of the vtable components.
size_t vtableStart = layout.getVTableOffset(vtableIndex);
size_t vtableEnd = vtableStart + layout.getVTableSize(vtableIndex);
llvm::SmallVector<mlir::Attribute> components;
for (size_t componentIndex = vtableStart; componentIndex < vtableEnd;
++componentIndex) {
- components.push_back(getVTableComponent(
- layout, componentIndex, rtti, nextVTableThunkIndex,
- addressPoints[vtableIndex], vtableHasLocalLinkage));
+ components.push_back(
+ getVTableComponent(layout, componentIndex, rtti, nextVTableThunkIndex,
+ addressPoint, vtableHasLocalLinkage));
}
// Create a ConstArrayAttr to hold the components.
auto arr = cir::ConstArrayAttr::get(
>From bd3d54bd226ee2733ab791d18e816e6d336417e3 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Mon, 25 Aug 2025 09:26:35 -0700
Subject: [PATCH 3/3] Implement additional review suggestions
---
clang/lib/CIR/CodeGen/CIRGenVTables.cpp | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenVTables.cpp b/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
index 0c7b2aa7944a4..aca12aa62f55b 100644
--- a/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
@@ -152,12 +152,11 @@ void CIRGenVTables::createVTableInitializer(cir::GlobalOp &vtableOp,
size_t vtableStart = layout.getVTableOffset(vtableIndex);
size_t vtableEnd = vtableStart + layout.getVTableSize(vtableIndex);
llvm::SmallVector<mlir::Attribute> components;
- for (size_t componentIndex = vtableStart; componentIndex < vtableEnd;
- ++componentIndex) {
+ components.reserve(vtableEnd - vtableStart);
+ 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()),
More information about the cfe-commits
mailing list