llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Andy Kaylor (andykaylor) <details> <summary>Changes</summary> This adds support for initializing the vptr member of a dynamic class in the constructor of that class. --- Patch is 29.01 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/152574.diff 13 Files Affected: - (modified) clang/include/clang/CIR/MissingFeatures.h (+2) - (modified) clang/lib/CIR/CodeGen/CIRGenBuilder.h (+11) - (modified) clang/lib/CIR/CodeGen/CIRGenCXXABI.h (+25) - (modified) clang/lib/CIR/CodeGen/CIRGenClass.cpp (+92-10) - (modified) clang/lib/CIR/CodeGen/CIRGenFunction.h (+24) - (modified) clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp (+111) - (modified) clang/lib/CIR/CodeGen/CIRGenModule.cpp (+44-1) - (modified) clang/lib/CIR/CodeGen/CIRGenModule.h (+23-1) - (modified) clang/lib/CIR/CodeGen/CIRGenTypeCache.h (+1) - (added) clang/lib/CIR/CodeGen/CIRGenVTables.cpp (+45) - (added) clang/lib/CIR/CodeGen/CIRGenVTables.h (+53) - (modified) clang/lib/CIR/CodeGen/CMakeLists.txt (+1) - (modified) clang/test/CIR/CodeGen/virtual-function-calls.cpp (+15-4) ``````````diff 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 getMLIRVisibilityFromCI... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/152574 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits