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

via cfe-commits cfe-commits at lists.llvm.org
Fri Apr 17 07:09:36 PDT 2026


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

>From b58c488b3db34f0a5c9b0e6c86d150250d1f9ec7 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/3] [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 a98b85173ddb68a8afb778e5e2ba5343ff3c28a6 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/3] [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 92ca84a6f44238eceb86045eb3125545a242d5e8 Mon Sep 17 00:00:00 2001
From: Zile Xiong <xiongzile at bytedance.com>
Date: Fri, 17 Apr 2026 22:09:20 +0800
Subject: [PATCH 3/3] [CIR] add WholeProgramVTables metadata support(NYI)

---
 clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp | 20 ++++++++++++++++---
 1 file changed, 17 insertions(+), 3 deletions(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
index 982349abb48c4..94d48bd8d1c31 100644
--- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
@@ -198,6 +198,8 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI {
                                 QualType elementType) override;
 
   bool isZeroInitializable(const MemberPointerType *MPT) override;
+  void emitVTableTypeMetadata(const CXXRecordDecl *rd, cir::GlobalOp vTable,
+                              const VTableLayout &vtLayout);
 
 protected:
   CharUnits getArrayCookieSizeImpl(QualType elementType) override;
@@ -477,6 +479,12 @@ bool CIRGenItaniumCXXABI::needsVTTParameter(GlobalDecl gd) {
   return false;
 }
 
+void CIRGenItaniumCXXABI::emitVTableTypeMetadata(const CXXRecordDecl *rd,
+                                                 cir::GlobalOp vTable,
+                                                 const VTableLayout &vtLayout) {
+  cgm.errorNYI(rd->getSourceRange(), "emitVTableTypeMetadata");
+}
+
 void CIRGenItaniumCXXABI::emitVTableDefinitions(CIRGenVTables &cgvt,
                                                 const CXXRecordDecl *rd) {
   cir::GlobalOp vtable = getAddrOfVTable(rd, CharUnits());
@@ -521,6 +529,8 @@ 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 +538,13 @@ 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) {
+    emitVTableTypeMetadata(rd, vtable, vtLayout);
+
+    if (isDeclarationForLinker) {
+      // available_externally
+      cgm.errorNYI(rd->getSourceRange(), "available_externally");
+    }
   }
 
   assert(!cir::MissingFeatures::vtableRelativeLayout());



More information about the cfe-commits mailing list