https://github.com/andykaylor created https://github.com/llvm/llvm-project/pull/152574
This adds support for initializing the vptr member of a dynamic class in the constructor of that class. >From 952c8634b6adc6d4f86cdcc145fbfe0405356509 Mon Sep 17 00:00:00 2001 From: Andy Kaylor <akay...@nvidia.com> Date: Thu, 31 Jul 2025 11:14:30 -0700 Subject: [PATCH] [CIR] Initialize vptr in dynamic classes This adds support for initializing the vptr member of a dynamic class in the constructor of that class. --- clang/include/clang/CIR/MissingFeatures.h | 2 + clang/lib/CIR/CodeGen/CIRGenBuilder.h | 11 ++ clang/lib/CIR/CodeGen/CIRGenCXXABI.h | 25 ++++ clang/lib/CIR/CodeGen/CIRGenClass.cpp | 102 ++++++++++++++-- clang/lib/CIR/CodeGen/CIRGenFunction.h | 24 ++++ clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp | 111 ++++++++++++++++++ clang/lib/CIR/CodeGen/CIRGenModule.cpp | 45 ++++++- clang/lib/CIR/CodeGen/CIRGenModule.h | 24 +++- clang/lib/CIR/CodeGen/CIRGenTypeCache.h | 1 + clang/lib/CIR/CodeGen/CIRGenVTables.cpp | 45 +++++++ clang/lib/CIR/CodeGen/CIRGenVTables.h | 53 +++++++++ clang/lib/CIR/CodeGen/CMakeLists.txt | 1 + .../CIR/CodeGen/virtual-function-calls.cpp | 19 ++- 13 files changed, 447 insertions(+), 16 deletions(-) create mode 100644 clang/lib/CIR/CodeGen/CIRGenVTables.cpp create mode 100644 clang/lib/CIR/CodeGen/CIRGenVTables.h diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index adf3f08269268..c3e87f5b187ac 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -192,6 +192,7 @@ struct MissingFeatures { static bool constantFoldSwitchStatement() { return false; } static bool constructABIArgDirectExtend() { return false; } static bool coverageMapping() { return false; } + static bool createInvariantGroup() { return false; } static bool createProfileWeightsForLoop() { return false; } static bool ctorMemcpyizer() { return false; } static bool cudaSupport() { return false; } @@ -259,6 +260,7 @@ struct MissingFeatures { static bool appleKext() { return false; } static bool dtorCleanups() { return false; } static bool vtableInitialization() { return false; } + static bool vtableRelativeLayout() { return false; } static bool msvcBuiltins() { return false; } static bool vlas() { return false; } diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h index ff8e12190c972..8b2538c941f47 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h +++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h @@ -244,6 +244,17 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy { } bool isInt(mlir::Type i) { return mlir::isa<cir::IntType>(i); } + // Fetch the type representing a pointer to unsigned int8 values. + cir::PointerType getUInt8PtrTy() { return typeCache.UInt8PtrTy; } + + /// Get a CIR anonymous record type. + cir::RecordType getAnonRecordTy(llvm::ArrayRef<mlir::Type> members, + bool packed = false, bool padded = false) { + assert(!cir::MissingFeatures::astRecordDeclAttr()); + auto kind = cir::RecordType::RecordKind::Struct; + return getType<cir::RecordType>(members, packed, padded, kind); + } + // // Constant creation helpers // ------------------------- diff --git a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h index 5929568505ef2..abde1a7687a90 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h +++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h @@ -80,6 +80,11 @@ class CIRGenCXXABI { bool forVirtualBase, bool delegating, Address thisAddr, QualType thisTy) = 0; + /// Checks if ABI requires extra virtual offset for vtable field. + virtual bool + isVirtualOffsetNeededForVTableField(CIRGenFunction &cgf, + CIRGenFunction::VPtr vptr) = 0; + /// Returns true if the given destructor type should be emitted as a linkonce /// delegating thunk, regardless of whether the dtor is defined in this TU or /// not. @@ -90,6 +95,26 @@ class CIRGenCXXABI { getCXXDestructorLinkage(GVALinkage linkage, const CXXDestructorDecl *dtor, CXXDtorType dt) const; + /// Get the address of the vtable for the given record decl which should be + /// used for the vptr at the given offset in RD. + virtual cir::GlobalOp getAddrOfVTable(const CXXRecordDecl *rd, + CharUnits vptrOffset) = 0; + + /// Get the address point of the vtable for the given base subobject. + virtual mlir::Value + getVTableAddressPoint(BaseSubobject base, + const CXXRecordDecl *vtableClass) = 0; + + /// Get the address point of the vtable for the given base subobject while + /// building a constructor or a destructor. + virtual mlir::Value getVTableAddressPointInStructor( + CIRGenFunction &cgf, const CXXRecordDecl *vtableClass, BaseSubobject base, + const CXXRecordDecl *nearestVBase) = 0; + + /// Checks if ABI requires to initialize vptrs for given dynamic class. + virtual bool + doStructorsInitializeVPtrs(const clang::CXXRecordDecl *vtableClass) = 0; + /// Returns true if the given constructor or destructor is one of the kinds /// that the ABI says returns 'this' (only applies when called non-virtually /// for destructors). diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp b/clang/lib/CIR/CodeGen/CIRGenClass.cpp index 72b9d177e4c63..f0fcfd2cff8d1 100644 --- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp @@ -197,10 +197,6 @@ void CIRGenFunction::emitCtorPrologue(const CXXConstructorDecl *cd, return; } - // If there are no member initializers, we can just return. - if (cd->getNumCtorInitializers() == 0) - return; - const CXXRecordDecl *classDecl = cd->getParent(); // This code doesn't use range-based iteration because we may need to emit @@ -225,7 +221,8 @@ void CIRGenFunction::emitCtorPrologue(const CXXConstructorDecl *cd, } const mlir::Value oldThisValue = cxxThisValue; - if (!constructVBases && (*b)->isBaseInitializer() && (*b)->isBaseVirtual()) { + if (!constructVBases && b != e && (*b)->isBaseInitializer() && + (*b)->isBaseVirtual()) { cgm.errorNYI(cd->getSourceRange(), "emitCtorPrologue: virtual base initializer"); return; @@ -247,11 +244,7 @@ void CIRGenFunction::emitCtorPrologue(const CXXConstructorDecl *cd, cxxThisValue = oldThisValue; - if (classDecl->isDynamicClass()) { - cgm.errorNYI(cd->getSourceRange(), - "emitCtorPrologue: initialize vtable pointers"); - return; - } + initializeVTablePointers(getLoc(cd->getBeginLoc()), classDecl); // Finally, initialize class members. FieldConstructionScope fcs(*this, loadCXXThisAddress()); @@ -269,6 +262,95 @@ void CIRGenFunction::emitCtorPrologue(const CXXConstructorDecl *cd, } } +void CIRGenFunction::initializeVTablePointer(mlir::Location loc, + const VPtr &vptr) { + // Compute the address point. + mlir::Value vtableAddressPoint = + cgm.getCXXABI().getVTableAddressPointInStructor( + *this, vptr.vtableClass, vptr.base, vptr.nearestVBase); + + if (!vtableAddressPoint) + return; + + // Compute where to store the address point. + mlir::Value virtualOffset{}; + CharUnits nonVirtualOffset = CharUnits::Zero(); + + mlir::Type baseValueTy; + if (cgm.getCXXABI().isVirtualOffsetNeededForVTableField(*this, vptr)) { + cgm.errorNYI(loc, "initializeVTablePointer: virtual offset for vtable"); + } else { + // We can just use the base offset in the complete class. + nonVirtualOffset = vptr.base.getBaseOffset(); + baseValueTy = convertType(getContext().getTagDeclType(vptr.base.getBase())); + } + + // Apply the offsets. + Address vtableField = loadCXXThisAddress(); + if (!nonVirtualOffset.isZero() || virtualOffset) { + cgm.errorNYI(loc, + "initializeVTablePointer: non-virtual and virtual offset"); + } + + // Finally, store the address point. Use the same CIR types as the field. + // + // vtable field is derived from `this` pointer, therefore they should be in + // the same addr space. + assert(!cir::MissingFeatures::addressSpace()); + // TODO(cir): This should be cir.vtable.get_vptr. + vtableField = builder.createElementBitCast(loc, vtableField, + vtableAddressPoint.getType()); + builder.createStore(loc, vtableAddressPoint, vtableField); + assert(!cir::MissingFeatures::opTBAA()); + assert(!cir::MissingFeatures::createInvariantGroup()); +} + +void CIRGenFunction::initializeVTablePointers(mlir::Location loc, + const CXXRecordDecl *rd) { + // Ignore classes without a vtable. + if (!rd->isDynamicClass()) + return; + + // Initialize the vtable pointers for this class and all of its bases. + if (cgm.getCXXABI().doStructorsInitializeVPtrs(rd)) + for (const auto &vptr : getVTablePointers(rd)) + initializeVTablePointer(loc, vptr); + + if (rd->getNumVBases()) + cgm.errorNYI(loc, "initializeVTablePointers: virtual base"); +} + +CIRGenFunction::VPtrsVector +CIRGenFunction::getVTablePointers(const CXXRecordDecl *vtableClass) { + CIRGenFunction::VPtrsVector vptrsResult; + getVTablePointers(BaseSubobject(vtableClass, CharUnits::Zero()), + /*NearestVBase=*/nullptr, + /*OffsetFromNearestVBase=*/CharUnits::Zero(), + /*BaseIsNonVirtualPrimaryBase=*/false, vtableClass, + vptrsResult); + return vptrsResult; +} + +void CIRGenFunction::getVTablePointers(BaseSubobject base, + const CXXRecordDecl *nearestVBase, + CharUnits offsetFromNearestVBase, + bool baseIsNonVirtualPrimaryBase, + const CXXRecordDecl *vtableClass, + VPtrsVector &vptrs) { + // If this base is a non-virtual primary base the address point has already + // been set. + if (!baseIsNonVirtualPrimaryBase) { + // Initialize the vtable pointer for this base. + VPtr vptr = {base, nearestVBase, offsetFromNearestVBase, vtableClass}; + vptrs.push_back(vptr); + } + + const CXXRecordDecl *rd = base.getBase(); + + if (rd->getNumBases()) + cgm.errorNYI(rd->getSourceRange(), "getVTablePointers: traverse bases"); +} + Address CIRGenFunction::loadCXXThisAddress() { assert(curFuncDecl && "loading 'this' without a func declaration?"); assert(isa<CXXMethodDecl>(curFuncDecl)); diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index 2e60cfc8211e6..d1f399d5bda4a 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -23,6 +23,7 @@ #include "Address.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/BaseSubobject.h" #include "clang/AST/CharUnits.h" #include "clang/AST/Decl.h" #include "clang/AST/Stmt.h" @@ -500,6 +501,25 @@ class CIRGenFunction : public CIRGenTypeCache { static bool isConstructorDelegationValid(const clang::CXXConstructorDecl *ctor); + struct VPtr { + clang::BaseSubobject base; + const clang::CXXRecordDecl *nearestVBase; + clang::CharUnits offsetFromNearestVBase; + const clang::CXXRecordDecl *vtableClass; + }; + + using VPtrsVector = llvm::SmallVector<VPtr, 4>; + VPtrsVector getVTablePointers(const clang::CXXRecordDecl *vtableClass); + void getVTablePointers(clang::BaseSubobject base, + const clang::CXXRecordDecl *nearestVBase, + clang::CharUnits offsetFromNearestVBase, + bool baseIsNonVirtualPrimaryBase, + const clang::CXXRecordDecl *vtableClass, + VPtrsVector &vptrs); + /// Return the Value of the vtable pointer member pointed to by thisAddr. + mlir::Value getVTablePtr(mlir::Location loc, Address thisAddr, + const clang::CXXRecordDecl *vtableClass); + /// A scope within which we are constructing the fields of an object which /// might use a CXXDefaultInitExpr. This stashes away a 'this' value to use if /// we need to evaluate the CXXDefaultInitExpr within the evaluation. @@ -548,6 +568,10 @@ class CIRGenFunction : public CIRGenTypeCache { return LValue::makeAddr(addr, ty, baseInfo); } + void initializeVTablePointers(mlir::Location loc, + const clang::CXXRecordDecl *rd); + void initializeVTablePointer(mlir::Location loc, const VPtr &vptr); + /// Return the address of a local variable. Address getAddrOfLocalVar(const clang::VarDecl *vd) { auto it = localDeclMap.find(vd); diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp index e5e4c68a1bfa7..43dfd6468046d 100644 --- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp @@ -22,6 +22,7 @@ #include "clang/AST/ExprCXX.h" #include "clang/AST/GlobalDecl.h" +#include "clang/AST/VTableBuilder.h" #include "clang/CIR/MissingFeatures.h" #include "llvm/Support/ErrorHandling.h" @@ -31,6 +32,10 @@ using namespace clang::CIRGen; namespace { class CIRGenItaniumCXXABI : public CIRGenCXXABI { +protected: + /// All the vtables which have been defined. + llvm::DenseMap<const CXXRecordDecl *, cir::GlobalOp> vtables; + public: CIRGenItaniumCXXABI(CIRGenModule &cgm) : CIRGenCXXABI(cgm) { assert(!cir::MissingFeatures::cxxabiUseARMMethodPtrABI()); @@ -58,6 +63,24 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI { // emitted with external linkage or as linkonce if they are inline and used. return false; } + + bool isVirtualOffsetNeededForVTableField(CIRGenFunction &cgf, + CIRGenFunction::VPtr vptr) override; + + cir::GlobalOp getAddrOfVTable(const CXXRecordDecl *rd, + CharUnits vptrOffset) override; + + mlir::Value getVTableAddressPoint(BaseSubobject base, + const CXXRecordDecl *vtableClass) override; + + mlir::Value getVTableAddressPointInStructor( + CIRGenFunction &cgf, const clang::CXXRecordDecl *vtableClass, + clang::BaseSubobject base, + const clang::CXXRecordDecl *nearestVBase) override; + + bool doStructorsInitializeVPtrs(const CXXRecordDecl *vtableClass) override { + return true; + } }; } // namespace @@ -278,3 +301,91 @@ CIRGenCXXABI *clang::CIRGen::CreateCIRGenItaniumCXXABI(CIRGenModule &cgm) { llvm_unreachable("bad or NYI ABI kind"); } } + +cir::GlobalOp CIRGenItaniumCXXABI::getAddrOfVTable(const CXXRecordDecl *rd, + CharUnits vptrOffset) { + assert(vptrOffset.isZero() && "Itanium ABI only supports zero vptr offsets"); + cir::GlobalOp &vtable = vtables[rd]; + if (vtable) + return vtable; + + // Queue up this vtable for possible deferred emission. + assert(!cir::MissingFeatures::deferredVtables()); + + SmallString<256> name; + llvm::raw_svector_ostream out(name); + getMangleContext().mangleCXXVTable(rd, out); + + const VTableLayout &vtLayout = + cgm.getItaniumVTableContext().getVTableLayout(rd); + mlir::Type vtableType = cgm.getVTables().getVTableType(vtLayout); + + // Use pointer alignment for the vtable. Otherwise we would align them based + // on the size of the initializer which doesn't make sense as only single + // values are read. + unsigned ptrAlign = cgm.getItaniumVTableContext().isRelativeLayout() + ? 32 + : cgm.getTarget().getPointerAlign(LangAS::Default); + + vtable = cgm.createOrReplaceCXXRuntimeVariable( + cgm.getLoc(rd->getSourceRange()), name, vtableType, + cir::GlobalLinkageKind::ExternalLinkage, + cgm.getASTContext().toCharUnitsFromBits(ptrAlign)); + // LLVM codegen handles unnamedAddr + assert(!cir::MissingFeatures::opGlobalUnnamedAddr()); + + // In MS C++ if you have a class with virtual functions in which you are using + // selective member import/export, then all virtual functions must be exported + // unless they are inline, otherwise a link error will result. To match this + // behavior, for such classes, we dllimport the vtable if it is defined + // externally and all the non-inline virtual methods are marked dllimport, and + // we dllexport the vtable if it is defined in this TU and all the non-inline + // virtual methods are marked dllexport. + if (cgm.getTarget().hasPS4DLLImportExport()) + cgm.errorNYI(rd->getSourceRange(), + "getAddrOfVTable: PS4 DLL import/export"); + + cgm.setGVProperties(vtable, rd); + return vtable; +} + +mlir::Value +CIRGenItaniumCXXABI::getVTableAddressPoint(BaseSubobject base, + const CXXRecordDecl *vtableClass) { + cir::GlobalOp vtable = getAddrOfVTable(vtableClass, CharUnits()); + + // Find the appropriate vtable within the vtable group, and the address point + // within that vtable. + VTableLayout::AddressPointLocation addressPoint = + cgm.getItaniumVTableContext() + .getVTableLayout(vtableClass) + .getAddressPoint(base); + + mlir::OpBuilder &builder = cgm.getBuilder(); + auto vtablePtrTy = cir::VPtrType::get(builder.getContext()); + + return builder.create<cir::VTableAddrPointOp>( + cgm.getLoc(vtableClass->getSourceRange()), vtablePtrTy, + mlir::FlatSymbolRefAttr::get(vtable.getSymNameAttr()), + cir::AddressPointAttr::get(cgm.getBuilder().getContext(), + addressPoint.VTableIndex, + addressPoint.AddressPointIndex)); +} + +mlir::Value CIRGenItaniumCXXABI::getVTableAddressPointInStructor( + CIRGenFunction &cgf, const clang::CXXRecordDecl *vtableClass, + clang::BaseSubobject base, const clang::CXXRecordDecl *nearestVBase) { + + if (base.getBase()->getNumVBases()) { + cgm.errorNYI(cgf.curFuncDecl->getLocation(), + "getVTableAddressPointInStructor: virtual base"); + } + return getVTableAddressPoint(base, vtableClass); +} + +bool CIRGenItaniumCXXABI::isVirtualOffsetNeededForVTableField( + CIRGenFunction &cgf, CIRGenFunction::VPtr vptr) { + if (vptr.nearestVBase == nullptr) + return false; + return needsVTTParameter(cgf.curGD); +} diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index ff6d293aae229..aeac360843087 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -64,7 +64,7 @@ CIRGenModule::CIRGenModule(mlir::MLIRContext &mlirContext, langOpts(astContext.getLangOpts()), codeGenOpts(cgo), theModule{mlir::ModuleOp::create(mlir::UnknownLoc::get(&mlirContext))}, diags(diags), target(astContext.getTargetInfo()), - abi(createCXXABI(*this)), genTypes(*this) { + abi(createCXXABI(*this)), genTypes(*this), vtables(*this) { // Initialize cached types VoidTy = cir::VoidType::get(&getMLIRContext()); @@ -75,6 +75,7 @@ CIRGenModule::CIRGenModule(mlir::MLIRContext &mlirContext, SInt64Ty = cir::IntType::get(&getMLIRContext(), 64, /*isSigned=*/true); SInt128Ty = cir::IntType::get(&getMLIRContext(), 128, /*isSigned=*/true); UInt8Ty = cir::IntType::get(&getMLIRContext(), 8, /*isSigned=*/false); + UInt8PtrTy = cir::PointerType::get(UInt8Ty); UInt16Ty = cir::IntType::get(&getMLIRContext(), 16, /*isSigned=*/false); UInt32Ty = cir::IntType::get(&getMLIRContext(), 32, /*isSigned=*/false); UInt64Ty = cir::IntType::get(&getMLIRContext(), 64, /*isSigned=*/false); @@ -946,6 +947,39 @@ void CIRGenModule::applyReplacements() { } } +cir::GlobalOp CIRGenModule::createOrReplaceCXXRuntimeVariable( + mlir::Location loc, StringRef name, mlir::Type ty, + cir::GlobalLinkageKind linkage, clang::CharUnits alignment) { + auto gv = mlir::dyn_cast_or_null<cir::GlobalOp>( + mlir::SymbolTable::lookupSymbolIn(theModule, name)); + + if (gv) { + // There should be handling added here to check the type as assert that + // gv was a declaration if the type doesn't match and handling below + // to replace the variable if it was a declaration. + errorNYI(loc, "createOrReplaceCXXRuntimeVariable: already exists"); + return gv; + } + + // Create a new variable. + gv = createGlobalOp(*this, loc, name, ty); + + // Set up extra information and add to the module + gv.setLinkageAttr( + cir::GlobalLinkageKindAttr::get(&getMLIRContext(), linkage)); + mlir::SymbolTable::setSymbolVisibility(gv, + CIRGenModule::getMLIRVisibility(gv)); + + if (supportsCOMDAT() && cir::isWeakForLinker(linkage) && + !gv.hasAvailableExternallyLinkage()) { + gv.setComdat(true); + } + + gv.setAlignmentAttr(getSize(alignment)); + setDSOLocal(static_cast<mlir::Operation *>(gv)); + return gv; +} + // TODO(CIR): this could be a common method between LLVM codegen. static bool isVarDeclStrongDefinition(const ASTContext &astContext, CIRGenModule &cgm, const VarDecl *vd, @@ -1940,6 +1974,15 @@ CIRGenModule::createCIRFunction(mlir::Location loc, StringRef name, return func; } +mlir::SymbolTable::Visibility +CIRGenModule::getMLIRVisibility(cir::GlobalOp op) { + // MLIR doesn't accept public symbols declarations (only + // definitions). + if (op.isDeclaration()) + return mlir::SymbolTable::Visibility::Private; + return getMLIRVisibilityFromCIRLinkage(op.getLinkage()); +} + mlir::SymbolTable::Visibility CIRGenModule::getMLIRVisibilityFromCIRLinkage(cir::GlobalLinkageKind glk) { switch (glk) { diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h index 163a0fc925e7e..a59981a3de0cd 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -17,6 +17,7 @@ #include "CIRGenCall.h" #include "CIRGenTypeCache.h" #include "CIRGenTypes.h" +#include "CIRGenVTables.h" #include "CIRGenValue.h" #include "clang/AST/CharUnits.h" @@ -86,6 +87,9 @@ class CIRGenModule : public CIRGenTypeCache { CIRGenTypes genTypes; + /// Holds information about C++ vtables. + CIRGenVTables vtables; + /// Per-function codegen information. Updated everytime emitCIR is called /// for FunctionDecls's. CIRGenFunction *curCGF = nullptr; @@ -170,6 +174,14 @@ class CIRGenModule : public CIRGenTypeCache { void constructAttributeList(CIRGenCalleeInfo calleeInfo, mlir::NamedAttrList &attrs); + /// Will return a global variable of the given type. If a variable with a + /// different type already exists then a new variable with the right type + /// will be created and all uses of the old variable will be replaced with a + /// bitcast to the new variable. + cir::GlobalOp createOrReplaceCXXRuntimeVariable( + mlir::Location loc, llvm::StringRef name, mlir::Type ty, + cir::GlobalLinkageKind linkage, clang::CharUnits alignment); + /// Return a constant array for the given string. mlir::Attribute getConstantArrayFromStringLiteral(const StringLiteral *e); @@ -215,6 +227,16 @@ class CIRGenModule : public CIRGenTypeCache { cir::FuncType fnType = nullptr, bool dontDefer = false, ForDefinition_t isForDefinition = NotForDefinition); + mlir::Type getVTableComponentType(); + CIRGenVTables &getVTables() { return vtables; } + + ItaniumVTableContext &getItaniumVTableContext() { + return vtables.getItaniumVTableContext(); + } + const ItaniumVTableContext &getItaniumVTableContext() const { + return vtables.getItaniumVTableContext(); + } + /// This contains all the decls which have definitions but which are deferred /// for emission and therefore should only be output if they are actually /// used. If a decl is in this, then it is known to have not been referenced @@ -357,8 +379,8 @@ class CIRGenModule : public CIRGenTypeCache { static cir::VisibilityKind getGlobalVisibilityKindFromClangVisibility( clang::VisibilityAttr::VisibilityType visibility); cir::VisibilityAttr getGlobalVisibilityAttrFromDecl(const Decl *decl); - static mlir::SymbolTable::Visibility getMLIRVisibility(cir::GlobalOp op); cir::GlobalLinkageKind getFunctionLinkage(GlobalDecl gd); + static mlir::SymbolTable::Visibility getMLIRVisibility(cir::GlobalOp op); cir::GlobalLinkageKind getCIRLinkageForDeclarator(const DeclaratorDecl *dd, GVALinkage linkage, bool isConstantVariable); diff --git a/clang/lib/CIR/CodeGen/CIRGenTypeCache.h b/clang/lib/CIR/CodeGen/CIRGenTypeCache.h index 1d081d53ad15c..eb8dcd67a4a81 100644 --- a/clang/lib/CIR/CodeGen/CIRGenTypeCache.h +++ b/clang/lib/CIR/CodeGen/CIRGenTypeCache.h @@ -59,6 +59,7 @@ struct CIRGenTypeCache { /// void* in address space 0 cir::PointerType VoidPtrTy; + cir::PointerType UInt8PtrTy; /// The size and alignment of a pointer into the generic address space. union { diff --git a/clang/lib/CIR/CodeGen/CIRGenVTables.cpp b/clang/lib/CIR/CodeGen/CIRGenVTables.cpp new file mode 100644 index 0000000000000..fdd1a6e3f57ef --- /dev/null +++ b/clang/lib/CIR/CodeGen/CIRGenVTables.cpp @@ -0,0 +1,45 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This contains code dealing with C++ code generation of virtual tables. +// +//===----------------------------------------------------------------------===// + +#include "CIRGenVTables.h" +#include "CIRGenModule.h" +#include "mlir/IR/Types.h" +#include "clang/AST/VTableBuilder.h" +#include "llvm/ADT/SmallVector.h" + +using namespace llvm; +using namespace clang; +using namespace clang::CIRGen; + +CIRGenVTables::CIRGenVTables(CIRGenModule &cgm) + : cgm(cgm), vtContext(cgm.getASTContext().getVTableContext()) {} + +mlir::Type CIRGenModule::getVTableComponentType() { + mlir::Type ptrTy = builder.getUInt8PtrTy(); + assert(!cir::MissingFeatures::vtableRelativeLayout()); + return ptrTy; +} + +mlir::Type CIRGenVTables::getVTableComponentType() { + return cgm.getVTableComponentType(); +} + +mlir::Type CIRGenVTables::getVTableType(const VTableLayout &layout) { + SmallVector<mlir::Type, 4> tys; + auto componentType = getVTableComponentType(); + for (unsigned i = 0, e = layout.getNumVTables(); i != e; ++i) + tys.push_back(cir::ArrayType::get(componentType, layout.getVTableSize(i))); + + // FIXME(cir): should VTableLayout be encoded like we do for some + // AST nodes? + return cgm.getBuilder().getAnonRecordTy(tys, /*incomplete=*/false); +} diff --git a/clang/lib/CIR/CodeGen/CIRGenVTables.h b/clang/lib/CIR/CodeGen/CIRGenVTables.h new file mode 100644 index 0000000000000..66318c5f2393a --- /dev/null +++ b/clang/lib/CIR/CodeGen/CIRGenVTables.h @@ -0,0 +1,53 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This contains code dealing with C++ code generation of virtual tables. +// +//===----------------------------------------------------------------------===// + +#ifndef CLANG_LIB_CIR_CODEGEN_CIRGENVTABLES_H +#define CLANG_LIB_CIR_CODEGEN_CIRGENVTABLES_H + +#include "mlir/IR/Types.h" +#include "clang/AST/GlobalDecl.h" +#include "clang/AST/VTableBuilder.h" + +namespace clang { +class CXXRecordDecl; +} + +namespace clang::CIRGen { +class CIRGenModule; + +class CIRGenVTables { + CIRGenModule &cgm; + + clang::VTableContextBase *vtContext; + + mlir::Type getVTableComponentType(); + +public: + CIRGenVTables(CIRGenModule &cgm); + + clang::ItaniumVTableContext &getItaniumVTableContext() { + return *llvm::cast<clang::ItaniumVTableContext>(vtContext); + } + + const clang::ItaniumVTableContext &getItaniumVTableContext() const { + return *llvm::cast<clang::ItaniumVTableContext>(vtContext); + } + + /// Returns the type of a vtable with the given layout. Normally a struct of + /// arrays of pointers, with one struct element for each vtable in the vtable + /// group. + mlir::Type getVTableType(const clang::VTableLayout &layout); +}; + +} // namespace clang::CIRGen + +#endif // CLANG_LIB_CIR_CODEGEN_CIRGENVTABLES_H diff --git a/clang/lib/CIR/CodeGen/CMakeLists.txt b/clang/lib/CIR/CodeGen/CMakeLists.txt index ca3a329d0c56d..7639ab24dbbd2 100644 --- a/clang/lib/CIR/CodeGen/CMakeLists.txt +++ b/clang/lib/CIR/CodeGen/CMakeLists.txt @@ -33,6 +33,7 @@ add_clang_library(clangCIR CIRGenStmtOpenACC.cpp CIRGenStmtOpenACCLoop.cpp CIRGenTypes.cpp + CIRGenVTables.cpp TargetInfo.cpp DEPENDS diff --git a/clang/test/CIR/CodeGen/virtual-function-calls.cpp b/clang/test/CIR/CodeGen/virtual-function-calls.cpp index 3e03b32ce1fd2..f3b134b135c85 100644 --- a/clang/test/CIR/CodeGen/virtual-function-calls.cpp +++ b/clang/test/CIR/CodeGen/virtual-function-calls.cpp @@ -2,12 +2,23 @@ // RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s struct A { + A(); virtual void f(char); }; -// This is just here to force the class definition to be emitted without -// requiring any other support. It will be removed when more complete -// vtable support is implemented. -A *a; +// This should initialize the vtable pointer. +A::A() {} // CIR: !rec_A = !cir.record<struct "A" {!cir.vptr}> +// CIR: !rec_anon_struct = !cir.record<struct {!cir.array<!cir.ptr<!u8i> x 3>}> + +// CIR: cir.global "private" external @_ZTV1A : !rec_anon_struct + +// CIR: cir.func dso_local @_ZN1AC2Ev(%arg0: !cir.ptr<!rec_A> {{.*}}) +// CIR: %[[THIS_ADDR:.*]] = cir.alloca !cir.ptr<!rec_A>, !cir.ptr<!cir.ptr<!rec_A>>, ["this", init] +// CIR: cir.store %arg0, %[[THIS_ADDR]] : !cir.ptr<!rec_A>, !cir.ptr<!cir.ptr<!rec_A>> +// CIR: %[[THIS:.*]] = cir.load %[[THIS_ADDR]] : !cir.ptr<!cir.ptr<!rec_A>>, !cir.ptr<!rec_A> +// CIR: %[[VPTR:.*]] = cir.vtable.address_point(@_ZTV1A, address_point = <index = 0, offset = 2>) : !cir.vptr +// CIR: %[[THIS_VPTR_PTR:.*]] = cir.cast(bitcast, %[[THIS]] : !cir.ptr<!rec_A>), !cir.ptr<!cir.vptr> +// CIR: cir.store align(8) %[[VPTR]], %[[THIS_VPTR_PTR]] : !cir.vptr, !cir.ptr<!cir.vptr> +// CIR: cir.return _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits