[clang] [CIR] Upstream missing support for vtables and virtual bases (PR #192617)

via cfe-commits cfe-commits at lists.llvm.org
Sat Apr 18 02:42:25 PDT 2026


https://github.com/xiongzile updated https://github.com/llvm/llvm-project/pull/192617

>From d680c4c14b0598f4b776778d2aa4352ef48c5a2e Mon Sep 17 00:00:00 2001
From: Zile Xiong <xiongzile at bytedance.com>
Date: Fri, 17 Apr 2026 17:18:23 +0800
Subject: [PATCH 1/4] [CIR] relative vtable layout & apply virtual and
 non-virtual offset

---
 clang/lib/CIR/CodeGen/CIRGenClass.cpp         | 12 ++--
 clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp |  8 ++-
 clang/test/CIR/CodeGen/virtual-base-cast.cpp  | 60 +++++++++++++++++++
 3 files changed, 74 insertions(+), 6 deletions(-)
 create mode 100644 clang/test/CIR/CodeGen/virtual-base-cast.cpp

diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
index 5f9fdfba9d31b..9755acb883675 100644
--- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
@@ -399,10 +399,14 @@ static Address applyNonVirtualAndVirtualOffset(
   mlir::Value baseOffset;
   if (!nonVirtualOffset.isZero()) {
     if (virtualOffset) {
-      cgf.cgm.errorNYI(
-          loc,
-          "applyNonVirtualAndVirtualOffset: virtual and non-virtual offset");
-      return Address::invalid();
+      mlir::Type offsetType =
+          (cgf.cgm.getTarget().getCXXABI().isItaniumFamily() &&
+           cgf.cgm.getLangOpts().RelativeCXXABIVTables)
+              ? cgf.sInt32Ty
+              : cgf.ptrDiffTy;
+      baseOffset = cgf.getBuilder().getConstInt(loc, offsetType,
+                                                nonVirtualOffset.getQuantity());
+      baseOffset = cgf.getBuilder().createAdd(loc, virtualOffset, baseOffset);
     } else {
       assert(baseValueTy && "expected base type");
       // If no virtualOffset is present this is the final stop.
diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
index 75658b23790bf..982349abb48c4 100644
--- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
@@ -2056,6 +2056,7 @@ bool CIRGenItaniumCXXABI::isVirtualOffsetNeededForVTableField(
 mlir::Value CIRGenItaniumCXXABI::getVirtualBaseClassOffset(
     mlir::Location loc, CIRGenFunction &cgf, Address thisAddr,
     const CXXRecordDecl *classDecl, const CXXRecordDecl *baseClassDecl) {
+
   CIRGenBuilderTy &builder = cgf.getBuilder();
   mlir::Value vtablePtr = cgf.getVTablePtr(loc, thisAddr, classDecl);
   mlir::Value vtableBytePtr = builder.createBitcast(vtablePtr, cgm.uInt8PtrTy);
@@ -2069,8 +2070,11 @@ 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));
diff --git a/clang/test/CIR/CodeGen/virtual-base-cast.cpp b/clang/test/CIR/CodeGen/virtual-base-cast.cpp
new file mode 100644
index 0000000000000..04fad6d4143c7
--- /dev/null
+++ b/clang/test/CIR/CodeGen/virtual-base-cast.cpp
@@ -0,0 +1,60 @@
+// RUN: %clang_cc1 -triple aarch64-none-linux-android21 -std=c++20 -mconstructor-aliases -O0 -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -triple aarch64-none-linux-android21 -std=c++20 -mconstructor-aliases -O0 -fclangir -emit-llvm -fno-clangir-call-conv-lowering %s -o %t.ll
+// RUN: FileCheck --check-prefix=LLVM --input-file=%t.ll %s
+
+struct A { int a; virtual int aa(); };
+struct B { int b; virtual int bb(); };
+struct C : virtual A, virtual B { int c; virtual int aa(); virtual int bb(); };
+struct AA { int a; virtual int aa(); };
+struct BB { int b; virtual int bb(); };
+struct CC : AA, BB { virtual int aa(); virtual int bb(); virtual int cc(); };
+struct D : virtual C, virtual CC { int e; };
+
+D* x;
+
+A* a() { return x; }
+// CIR-LABEL: @_Z1av()
+
+// This uses the vtable to get the offset to the base object. The offset from
+// the vptr to the base object offset in the vtable is a compile-time constant.
+// CIR: %[[X_ADDR:.*]] = cir.get_global @x : !cir.ptr<!cir.ptr<!rec_D>>
+// CIR: %[[X:.*]] = cir.load{{.*}} %[[X_ADDR]]
+// CIR: %[[X_VPTR_ADDR:.*]] = cir.vtable.get_vptr %[[X]] : !cir.ptr<!rec_D> -> !cir.ptr<!cir.vptr>
+// CIR: %[[X_VPTR_BASE:.*]] = cir.load{{.*}} %[[X_VPTR_ADDR]] : !cir.ptr<!cir.vptr>, !cir.vptr
+// CIR: %[[X_BASE_I8PTR:.*]] = cir.cast bitcast %[[X_VPTR_BASE]] : !cir.vptr -> !cir.ptr<!u8i>
+// CIR:  %[[OFFSET_OFFSET:.*]] = cir.const #cir.int<-32> : !s64i
+// CIR:  %[[OFFSET_PTR:.*]] = cir.ptr_stride %[[X_BASE_I8PTR]], %[[OFFSET_OFFSET]] : (!cir.ptr<!u8i>, !s64i) -> !cir.ptr<!u8i>
+// CIR:  %[[OFFSET_PTR_CAST:.*]] = cir.cast bitcast %[[OFFSET_PTR]] : !cir.ptr<!u8i> -> !cir.ptr<!s64i>
+// CIR:  %[[OFFSET:.*]] = cir.load{{.*}} %[[OFFSET_PTR_CAST]] : !cir.ptr<!s64i>, !s64i
+// CIR:  %[[VBASE_ADDR:.*]] = cir.ptr_stride {{.*}}, %[[OFFSET]] : (!cir.ptr<!u8i>, !s64i) -> !cir.ptr<!u8i>
+// CIR:  cir.cast bitcast %[[VBASE_ADDR]] : !cir.ptr<!u8i> -> !cir.ptr<!rec_D>
+
+// FIXME: this version should include null check.
+// LLVM-LABEL: @_Z1av()
+// LLVM:  %[[OFFSET_OFFSET:.*]] = getelementptr i8, ptr {{.*}}, i64 -32
+// LLVM:  %[[OFFSET_PTR:.*]] = load i64, ptr %[[OFFSET_OFFSET]], align 8
+// LLVM:  %[[VBASE_ADDR:.*]] = getelementptr i8, ptr {{.*}}, i64 %[[OFFSET_PTR]]
+// LLVM:  store ptr %[[VBASE_ADDR]], ptr {{.*}}, align 8
+
+B* b() { return x; }
+BB* c() { return x; }
+
+// Put the vbptr at a non-zero offset inside a non-virtual base.
+struct E { int e; };
+struct F : E, D { int f; };
+
+F* y;
+
+BB* d() { return y; }
+// CIR-LABEL: @_Z1dv
+// CIR: %[[OFFSET:.*]] = cir.load{{.*}} {{.*}} : !cir.ptr<!s64i>, !s64i
+// CIR: %[[ADJUST:.*]] = cir.const #cir.int<16> : !s64i
+// CIR: cir.binop(add, %[[OFFSET]], %[[ADJUST]]) : !s64i
+
+// LLVM-LABEL: @_Z1dv
+// LLVM: %[[OFFSET_OFFSET:.*]] = getelementptr i8, ptr {{.*}}, i64 -48
+// LLVM: %[[OFFSET_PTR:.*]] = load i64, ptr %[[OFFSET_OFFSET]], align 8
+// LLVM: %[[ADJUST:.*]] = add i64 %[[OFFSET_PTR]], 16
+// LLVM: %[[VBASE_ADDR:.*]] = getelementptr i8, ptr {{.*}}, i64 %[[ADJUST]]
+// LLVM: store ptr %[[VBASE_ADDR]],

>From ef4f102cfeb9d8da9150308f4f7b9c5971d5f9b8 Mon Sep 17 00:00:00 2001
From: Zile Xiong <xiongzile at bytedance.com>
Date: Fri, 17 Apr 2026 20:12:19 +0800
Subject: [PATCH 2/4] [CIR] virtual-base-cast test diff

---
 clang/test/CIR/CodeGen/virtual-base-cast.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/test/CIR/CodeGen/virtual-base-cast.cpp b/clang/test/CIR/CodeGen/virtual-base-cast.cpp
index 04fad6d4143c7..a37edd622dd86 100644
--- a/clang/test/CIR/CodeGen/virtual-base-cast.cpp
+++ b/clang/test/CIR/CodeGen/virtual-base-cast.cpp
@@ -1,6 +1,6 @@
 // RUN: %clang_cc1 -triple aarch64-none-linux-android21 -std=c++20 -mconstructor-aliases -O0 -fclangir -emit-cir %s -o %t.cir
 // RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
-// RUN: %clang_cc1 -triple aarch64-none-linux-android21 -std=c++20 -mconstructor-aliases -O0 -fclangir -emit-llvm -fno-clangir-call-conv-lowering %s -o %t.ll
+// RUN: %clang_cc1 -triple aarch64-none-linux-android21 -std=c++20 -mconstructor-aliases -O0 -fclangir -emit-llvm %s -o %t.ll
 // RUN: FileCheck --check-prefix=LLVM --input-file=%t.ll %s
 
 struct A { int a; virtual int aa(); };
@@ -50,7 +50,7 @@ BB* d() { return y; }
 // CIR-LABEL: @_Z1dv
 // CIR: %[[OFFSET:.*]] = cir.load{{.*}} {{.*}} : !cir.ptr<!s64i>, !s64i
 // CIR: %[[ADJUST:.*]] = cir.const #cir.int<16> : !s64i
-// CIR: cir.binop(add, %[[OFFSET]], %[[ADJUST]]) : !s64i
+// CIR: cir.add %[[OFFSET]], %[[ADJUST]] : !s64i
 
 // LLVM-LABEL: @_Z1dv
 // LLVM: %[[OFFSET_OFFSET:.*]] = getelementptr i8, ptr {{.*}}, i64 -48

>From 092d120fc088264e91483c3e23a5253da71573c8 Mon Sep 17 00:00:00 2001
From: Zile Xiong <xiongzile at bytedance.com>
Date: Sat, 18 Apr 2026 15:44:06 +0800
Subject: [PATCH 3/4] [CIR] vtable type metadata portion

---
 clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp | 13 ++++++++++---
 clang/lib/CIR/CodeGen/CIRGenModule.cpp        |  8 ++++++++
 clang/lib/CIR/CodeGen/CIRGenModule.h          |  4 ++++
 3 files changed, 22 insertions(+), 3 deletions(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
index 982349abb48c4..74e50c3f9cab4 100644
--- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
@@ -521,6 +521,7 @@ void CIRGenItaniumCXXABI::emitVTableDefinitions(CIRGenVTables &cgvt,
   [[maybe_unused]] auto vtableAsGlobalValue =
       dyn_cast<cir::CIRGlobalValueInterface>(*vtable);
   assert(vtableAsGlobalValue && "VTable must support CIRGlobalValueInterface");
+  bool isDeclarationForLinker = vtableAsGlobalValue.isDeclarationForLinker();
   // Always emit type metadata on non-available_externally definitions, and on
   // available_externally definitions if we are performing whole program
   // devirtualization. For WPD we need the type metadata on all vtable
@@ -528,9 +529,15 @@ void CIRGenItaniumCXXABI::emitVTableDefinitions(CIRGenVTables &cgvt,
   // defined in headers but with a strong definition only in a shared
   // library.
   assert(!cir::MissingFeatures::vtableEmitMetadata());
-  if (cgm.getCodeGenOpts().WholeProgramVTables) {
-    cgm.errorNYI(rd->getSourceRange(),
-                 "emitVTableDefinitions: WholeProgramVTables");
+  if (!isDeclarationForLinker || cgm.getCodeGenOpts().WholeProgramVTables) {
+    cgm.emitVTableTypeMetadata(rd, vtable, vtLayout);
+    // For available_externally definitions, add the vtable to
+    // @llvm.compiler.used so that it isn't deleted before whole program
+    // analysis.
+    if (isDeclarationForLinker) {
+      cgm.errorNYI(rd->getSourceRange(), "isDeclarationForLinker");
+      assert(cgm.getCodeGenOpts().WholeProgramVTables);
+    }
   }
 
   assert(!cir::MissingFeatures::vtableRelativeLayout());
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 2f64fe45a694d..6155ab3ec39b2 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -3509,3 +3509,11 @@ CIRGenModule::getAddrOfGlobalTemporary(const MaterializeTemporaryExpr *mte,
 
   return cv;
 }
+
+void CIRGenModule::emitVTableTypeMetadata(const CXXRecordDecl *rd,
+                                          cir::GlobalOp vTable,
+                                          const VTableLayout &vtLayout) {
+  if (!getCodeGenOpts().LTOUnit)
+    return;
+  errorNYI(rd->getSourceRange(), "emitVTableTypeMetadata");
+}
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h
index ba3f936106d31..8ae414ff43e3e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -467,6 +467,10 @@ class CIRGenModule : public CIRGenTypeCache {
   std::vector<cir::CIRGlobalValueInterface> llvmUsed;
   std::vector<cir::CIRGlobalValueInterface> llvmCompilerUsed;
 
+  /// Emit type metadata for the given vtable using the given layout.
+  void emitVTableTypeMetadata(const CXXRecordDecl *rd, cir::GlobalOp vTable,
+                              const VTableLayout &vtLayout);
+
   mlir::Type getVTableComponentType();
   CIRGenVTables &getVTables() { return vtables; }
 

>From 8e79f8f99e548b05a767601e777c4d9d969c728c Mon Sep 17 00:00:00 2001
From: Zile Xiong <xiongzile at bytedance.com>
Date: Sat, 18 Apr 2026 17:42:08 +0800
Subject: [PATCH 4/4] [CIR] relative layout test

---
 clang/test/CIR/CodeGen/relative-layout.c | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 clang/test/CIR/CodeGen/relative-layout.c

diff --git a/clang/test/CIR/CodeGen/relative-layout.c b/clang/test/CIR/CodeGen/relative-layout.c
new file mode 100644
index 0000000000000..489d9d2e740fd
--- /dev/null
+++ b/clang/test/CIR/CodeGen/relative-layout.c
@@ -0,0 +1 @@
+// TODO: by @Elio
\ No newline at end of file



More information about the cfe-commits mailing list