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/3] [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/3] [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 1376fdfdf2aae197bd8dff8f16b783d8fefdadb3 Mon Sep 17 00:00:00 2001 From: Zile Xiong <[email protected]> Date: Wed, 6 May 2026 23:33:48 +0800 Subject: [PATCH 3/3] CIR: try to do full support for vtable --- clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp | 40 ++++++---- clang/lib/CIR/CodeGen/CIRGenVTables.cpp | 75 ++++++++++++++----- clang/lib/CIR/CodeGen/CIRGenVTables.h | 3 + clang/lib/CIR/Dialect/IR/CIRAttrs.cpp | 12 ++- 4 files changed, 91 insertions(+), 39 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..a28c450b4e55a 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: @@ -217,7 +252,7 @@ mlir::Attribute CIRGenVTables::getVTableComponent( } return cir::GlobalViewAttr::get( - builder.getUInt8PtrTy(), + getVTableComponentType(), mlir::FlatSymbolRefAttr::get(fnPtr.getSymNameAttr())); } } @@ -244,10 +279,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 +299,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(); } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
