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 <[email protected]>
Date: Thu, 30 Apr 2026 15:14:45 +0800
Subject: [PATCH 1/4] [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 <[email protected]>
Date: Thu, 30 Apr 2026 17:24:00 +0800
Subject: [PATCH 2/4] [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 f06a6afb9f4eb8219179409b2829dc26dff7821d Mon Sep 17 00:00:00 2001
From: Zile Xiong <[email protected]>
Date: Wed, 6 May 2026 23:33:48 +0800
Subject: [PATCH 3/4] [CIR] add emit relative vtable support

---
 clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp | 40 ++++++----
 clang/lib/CIR/CodeGen/CIRGenVTables.cpp       | 77 ++++++++++++++-----
 clang/lib/CIR/CodeGen/CIRGenVTables.h         |  3 +
 clang/lib/CIR/Dialect/IR/CIRAttrs.cpp         | 12 ++-
 4 files changed, 91 insertions(+), 41 deletions(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp 
b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
index b6e6bd4857c9b..7e8d47f5e9a2e 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 756ce62458290..0d93227f962a5 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() {
@@ -134,6 +137,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 builder.getI32IntegerAttr(offset.getQuantity());
+    }
+    return builder.getConstPtrAttr(builder.getUInt8PtrTy(),
+                                   offset.getQuantity());
+  };
+
+  switch (component.getKind()) {
+  case VTableComponent::CK_UnusedFunctionPointer:
+    if (isRelative)
+      return builder.getI32IntegerAttr(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,
@@ -142,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:
@@ -176,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(),
@@ -217,7 +250,7 @@ mlir::Attribute CIRGenVTables::getVTableComponent(
     }
 
     return cir::GlobalViewAttr::get(
-        builder.getUInt8PtrTy(),
+        getVTableComponentType(),
         mlir::FlatSymbolRefAttr::get(fnPtr.getSymNameAttr()));
   }
   }
@@ -244,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()),
@@ -263,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 270c55dfc4541..eda8bb1328e1c 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";
 
@@ -708,16 +708,20 @@ 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,
+                        mlir::IntegerAttr>(attr))
             return;
 
-          eltTypeCheck = emitError()
-                         << "expected GlobalViewAttr or ConstPtrAttr";
+          eltTypeCheck =
+              emitError()
+              << "expected GlobalViewAttr, ConstPtrAttr, or IntegerAttr";
         },
         [&](mlir::Type type) {});
+
     if (eltTypeCheck.failed())
       return eltTypeCheck;
   }
+
   return success();
 }
 

>From 5982ec3f68ff37b4016580c277b5f9f4305658bf Mon Sep 17 00:00:00 2001
From: Zile Xiong <[email protected]>
Date: Thu, 7 May 2026 20:06:26 +0800
Subject: [PATCH 4/4] [CIR] add support for getVirtualFunctionPointer

---
 clang/include/clang/CIR/Dialect/IR/CIROps.td  | 54 +++++++++++++++++++
 clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp | 16 ++++--
 .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 22 ++++++++
 3 files changed, 89 insertions(+), 3 deletions(-)

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 7e8d47f5e9a2e..baa1fa6705075 100644
--- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
@@ -1957,10 +1957,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);
@@ -1985,6 +1994,7 @@ CIRGenCallee 
CIRGenItaniumCXXABI::getVirtualFunctionPointer(
   return callee;
 }
 
+
 mlir::Value CIRGenItaniumCXXABI::getVTableAddressPointInStructorWithVTT(
     CIRGenFunction &cgf, const CXXRecordDecl *vtableClass, BaseSubobject base,
     const CXXRecordDecl *nearestVBase) {
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 {

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to