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

via cfe-commits cfe-commits at lists.llvm.org
Tue Apr 21 02:26:50 PDT 2026


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

>From f2c17b51114934a873d5e8487db4bcab11f79499 Mon Sep 17 00:00:00 2001
From: Zile Xiong <xiongzile at bytedance.com>
Date: Tue, 21 Apr 2026 17:26:31 +0800
Subject: [PATCH] [CIR] Combined virtual + non-virtual base offset

---
 clang/lib/CIR/CodeGen/CIRGenClass.cpp | 12 ++++--
 clang/test/CIR/virtual-base-cast.cpp  | 60 +++++++++++++++++++++++++++
 2 files changed, 68 insertions(+), 4 deletions(-)
 create mode 100644 clang/test/CIR/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/test/CIR/virtual-base-cast.cpp b/clang/test/CIR/virtual-base-cast.cpp
new file mode 100644
index 0000000000000..1c036690f30a4
--- /dev/null
+++ b/clang/test/CIR/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 %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.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]],
\ No newline at end of file



More information about the cfe-commits mailing list