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
