527 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This contains code dealing with C++ code generation of virtual tables.10//11//===----------------------------------------------------------------------===//12 13#include "CIRGenVTables.h"14 15#include "CIRGenCXXABI.h"16#include "CIRGenModule.h"17#include "mlir/IR/Types.h"18#include "clang/AST/VTTBuilder.h"19#include "clang/AST/VTableBuilder.h"20#include "llvm/ADT/SmallVector.h"21 22using namespace llvm;23using namespace clang;24using namespace clang::CIRGen;25 26CIRGenVTables::CIRGenVTables(CIRGenModule &cgm)27 : cgm(cgm), vtContext(cgm.getASTContext().getVTableContext()) {}28 29mlir::Type CIRGenModule::getVTableComponentType() {30 mlir::Type ptrTy = builder.getUInt8PtrTy();31 assert(!cir::MissingFeatures::vtableRelativeLayout());32 return ptrTy;33}34 35mlir::Type CIRGenVTables::getVTableComponentType() {36 return cgm.getVTableComponentType();37}38 39cir::RecordType CIRGenVTables::getVTableType(const VTableLayout &layout) {40 SmallVector<mlir::Type, 4> tys;41 mlir::Type componentType = getVTableComponentType();42 for (unsigned i = 0, e = layout.getNumVTables(); i != e; ++i)43 tys.push_back(cir::ArrayType::get(componentType, layout.getVTableSize(i)));44 45 // FIXME(cir): should VTableLayout be encoded like we do for some46 // AST nodes?47 return cgm.getBuilder().getAnonRecordTy(tys, /*incomplete=*/false);48}49 50/// At this point in the translation unit, does it appear that can we51/// rely on the vtable being defined elsewhere in the program?52///53/// The response is really only definitive when called at the end of54/// the translation unit.55///56/// The only semantic restriction here is that the object file should57/// not contain a vtable definition when that vtable is defined58/// strongly elsewhere. Otherwise, we'd just like to avoid emitting59/// vtables when unnecessary.60/// TODO(cir): this should be merged into common AST helper for codegen.61bool CIRGenVTables::isVTableExternal(const CXXRecordDecl *rd) {62 assert(rd->isDynamicClass() && "Non-dynamic classes have no VTable.");63 64 // We always synthesize vtables if they are needed in the MS ABI. MSVC doesn't65 // emit them even if there is an explicit template instantiation.66 if (cgm.getTarget().getCXXABI().isMicrosoft())67 return false;68 69 // If we have an explicit instantiation declaration (and not a70 // definition), the vtable is defined elsewhere.71 TemplateSpecializationKind tsk = rd->getTemplateSpecializationKind();72 if (tsk == TSK_ExplicitInstantiationDeclaration)73 return true;74 75 // Otherwise, if the class is an instantiated template, the76 // vtable must be defined here.77 if (tsk == TSK_ImplicitInstantiation ||78 tsk == TSK_ExplicitInstantiationDefinition)79 return false;80 81 // Otherwise, if the class doesn't have a key function (possibly82 // anymore), the vtable must be defined here.83 const CXXMethodDecl *keyFunction =84 cgm.getASTContext().getCurrentKeyFunction(rd);85 if (!keyFunction)86 return false;87 88 // Otherwise, if we don't have a definition of the key function, the89 // vtable must be defined somewhere else.90 return !keyFunction->hasBody();91}92 93/// This is a callback from Sema to tell us that a particular vtable is94/// required to be emitted in this translation unit.95///96/// This is only called for vtables that _must_ be emitted (mainly due to key97/// functions). For weak vtables, CodeGen tracks when they are needed and98/// emits them as-needed.99void CIRGenModule::emitVTable(const CXXRecordDecl *rd) {100 vtables.generateClassData(rd);101}102 103void CIRGenVTables::generateClassData(const CXXRecordDecl *rd) {104 assert(!cir::MissingFeatures::generateDebugInfo());105 106 if (rd->getNumVBases())107 cgm.getCXXABI().emitVirtualInheritanceTables(rd);108 109 cgm.getCXXABI().emitVTableDefinitions(*this, rd);110}111 112mlir::Attribute CIRGenVTables::getVTableComponent(113 const VTableLayout &layout, unsigned componentIndex, mlir::Attribute rtti,114 unsigned &nextVTableThunkIndex, unsigned vtableAddressPoint,115 bool vtableHasLocalLinkage) {116 const VTableComponent &component = layout.vtable_components()[componentIndex];117 118 CIRGenBuilderTy builder = cgm.getBuilder();119 120 assert(!cir::MissingFeatures::vtableRelativeLayout());121 122 switch (component.getKind()) {123 case VTableComponent::CK_UnusedFunctionPointer:124 cgm.errorNYI("getVTableComponent: UnusedFunctionPointer");125 return mlir::Attribute();126 127 case VTableComponent::CK_VCallOffset:128 return builder.getConstPtrAttr(builder.getUInt8PtrTy(),129 component.getVCallOffset().getQuantity());130 131 case VTableComponent::CK_VBaseOffset:132 return builder.getConstPtrAttr(builder.getUInt8PtrTy(),133 component.getVBaseOffset().getQuantity());134 135 case VTableComponent::CK_OffsetToTop:136 return builder.getConstPtrAttr(builder.getUInt8PtrTy(),137 component.getOffsetToTop().getQuantity());138 139 case VTableComponent::CK_RTTI:140 assert((mlir::isa<cir::GlobalViewAttr>(rtti) ||141 mlir::isa<cir::ConstPtrAttr>(rtti)) &&142 "expected GlobalViewAttr or ConstPtrAttr");143 return rtti;144 145 case VTableComponent::CK_FunctionPointer:146 case VTableComponent::CK_CompleteDtorPointer:147 case VTableComponent::CK_DeletingDtorPointer: {148 GlobalDecl gd = component.getGlobalDecl();149 150 assert(!cir::MissingFeatures::cudaSupport());151 152 cir::FuncOp fnPtr;153 if (cast<CXXMethodDecl>(gd.getDecl())->isPureVirtual()) {154 cgm.errorNYI("getVTableComponent: CK_FunctionPointer: pure virtual");155 return mlir::Attribute();156 } else if (cast<CXXMethodDecl>(gd.getDecl())->isDeleted()) {157 cgm.errorNYI("getVTableComponent: CK_FunctionPointer: deleted virtual");158 return mlir::Attribute();159 } else if (nextVTableThunkIndex < layout.vtable_thunks().size() &&160 layout.vtable_thunks()[nextVTableThunkIndex].first ==161 componentIndex) {162 cgm.errorNYI("getVTableComponent: CK_FunctionPointer: thunk");163 return mlir::Attribute();164 } else {165 // Otherwise we can use the method definition directly.166 cir::FuncType fnTy = cgm.getTypes().getFunctionTypeForVTable(gd);167 fnPtr = cgm.getAddrOfFunction(gd, fnTy, /*ForVTable=*/true);168 }169 170 return cir::GlobalViewAttr::get(171 builder.getUInt8PtrTy(),172 mlir::FlatSymbolRefAttr::get(fnPtr.getSymNameAttr()));173 }174 }175 176 llvm_unreachable("Unexpected vtable component kind");177}178 179void CIRGenVTables::createVTableInitializer(cir::GlobalOp &vtableOp,180 const clang::VTableLayout &layout,181 mlir::Attribute rtti,182 bool vtableHasLocalLinkage) {183 mlir::Type componentType = getVTableComponentType();184 185 const llvm::SmallVectorImpl<unsigned> &addressPoints =186 layout.getAddressPointIndices();187 unsigned nextVTableThunkIndex = 0;188 189 mlir::MLIRContext *mlirContext = &cgm.getMLIRContext();190 191 SmallVector<mlir::Attribute> vtables;192 for (auto [vtableIndex, addressPoint] : llvm::enumerate(addressPoints)) {193 // Build a ConstArrayAttr of the vtable components.194 size_t vtableStart = layout.getVTableOffset(vtableIndex);195 size_t vtableEnd = vtableStart + layout.getVTableSize(vtableIndex);196 llvm::SmallVector<mlir::Attribute> components;197 components.reserve(vtableEnd - vtableStart);198 for (size_t componentIndex : llvm::seq(vtableStart, vtableEnd))199 components.push_back(200 getVTableComponent(layout, componentIndex, rtti, nextVTableThunkIndex,201 addressPoint, vtableHasLocalLinkage));202 // Create a ConstArrayAttr to hold the components.203 auto arr = cir::ConstArrayAttr::get(204 cir::ArrayType::get(componentType, components.size()),205 mlir::ArrayAttr::get(mlirContext, components));206 vtables.push_back(arr);207 }208 209 // Create a ConstRecordAttr to hold the component array.210 const auto members = mlir::ArrayAttr::get(mlirContext, vtables);211 cir::ConstRecordAttr record = cgm.getBuilder().getAnonConstRecord(members);212 213 // Create a VTableAttr214 auto vtableAttr = cir::VTableAttr::get(record.getType(), record.getMembers());215 216 // Add the vtable initializer to the vtable global op.217 cgm.setInitializer(vtableOp, vtableAttr);218}219 220cir::GlobalOp CIRGenVTables::generateConstructionVTable(221 const CXXRecordDecl *rd, const BaseSubobject &base, bool baseIsVirtual,222 cir::GlobalLinkageKind linkage, VTableAddressPointsMapTy &addressPoints) {223 assert(!cir::MissingFeatures::generateDebugInfo());224 225 std::unique_ptr<VTableLayout> vtLayout(226 getItaniumVTableContext().createConstructionVTableLayout(227 base.getBase(), base.getBaseOffset(), baseIsVirtual, rd));228 229 // Add the address points.230 addressPoints = vtLayout->getAddressPoints();231 232 // Get the mangled construction vtable name.233 SmallString<256> outName;234 llvm::raw_svector_ostream out(outName);235 cast<ItaniumMangleContext>(cgm.getCXXABI().getMangleContext())236 .mangleCXXCtorVTable(rd, base.getBaseOffset().getQuantity(),237 base.getBase(), out);238 SmallString<256> name(outName);239 240 assert(!cir::MissingFeatures::vtableRelativeLayout());241 242 cir::RecordType vtType = getVTableType(*vtLayout);243 244 // Construction vtable symbols are not part of the Itanium ABI, so we cannot245 // guarantee that they actually will be available externally. Instead, when246 // emitting an available_externally VTT, we provide references to an internal247 // linkage construction vtable. The ABI only requires complete-object vtables248 // to be the same for all instances of a type, not construction vtables.249 if (linkage == cir::GlobalLinkageKind::AvailableExternallyLinkage)250 linkage = cir::GlobalLinkageKind::InternalLinkage;251 252 llvm::Align align = cgm.getDataLayout().getABITypeAlign(vtType);253 mlir::Location loc = cgm.getLoc(rd->getSourceRange());254 255 // Create the variable that will hold the construction vtable.256 cir::GlobalOp vtable = cgm.createOrReplaceCXXRuntimeVariable(257 loc, name, vtType, linkage, CharUnits::fromQuantity(align));258 259 // V-tables are always unnamed_addr.260 assert(!cir::MissingFeatures::opGlobalUnnamedAddr());261 262 mlir::Attribute rtti = cgm.getAddrOfRTTIDescriptor(263 loc, cgm.getASTContext().getCanonicalTagType(base.getBase()));264 265 // Create and set the initializer.266 createVTableInitializer(vtable, *vtLayout, rtti,267 cir::isLocalLinkage(vtable.getLinkage()));268 269 // Set properties only after the initializer has been set to ensure that the270 // GV is treated as definition and not declaration.271 assert(!vtable.isDeclaration() && "Shouldn't set properties on declaration");272 cgm.setGVProperties(vtable, rd);273 274 assert(!cir::MissingFeatures::vtableEmitMetadata());275 assert(!cir::MissingFeatures::vtableRelativeLayout());276 277 return vtable;278}279 280/// Compute the required linkage of the vtable for the given class.281///282/// Note that we only call this at the end of the translation unit.283cir::GlobalLinkageKind CIRGenModule::getVTableLinkage(const CXXRecordDecl *rd) {284 if (!rd->isExternallyVisible())285 return cir::GlobalLinkageKind::InternalLinkage;286 287 // We're at the end of the translation unit, so the current key288 // function is fully correct.289 const CXXMethodDecl *keyFunction = astContext.getCurrentKeyFunction(rd);290 if (keyFunction && !rd->hasAttr<DLLImportAttr>()) {291 // If this class has a key function, use that to determine the292 // linkage of the vtable.293 const FunctionDecl *def = nullptr;294 if (keyFunction->hasBody(def))295 keyFunction = cast<CXXMethodDecl>(def);296 297 // All of the cases below do something different with AppleKext enabled.298 assert(!cir::MissingFeatures::appleKext());299 switch (keyFunction->getTemplateSpecializationKind()) {300 case TSK_Undeclared:301 case TSK_ExplicitSpecialization:302 assert(303 (def || codeGenOpts.OptimizationLevel > 0 ||304 codeGenOpts.getDebugInfo() != llvm::codegenoptions::NoDebugInfo) &&305 "Shouldn't query vtable linkage without key function, "306 "optimizations, or debug info");307 if (!def && codeGenOpts.OptimizationLevel > 0)308 return cir::GlobalLinkageKind::AvailableExternallyLinkage;309 310 if (keyFunction->isInlined())311 return !astContext.getLangOpts().AppleKext312 ? cir::GlobalLinkageKind::LinkOnceODRLinkage313 : cir::GlobalLinkageKind::InternalLinkage;314 return cir::GlobalLinkageKind::ExternalLinkage;315 316 case TSK_ImplicitInstantiation:317 return cir::GlobalLinkageKind::LinkOnceODRLinkage;318 319 case TSK_ExplicitInstantiationDefinition:320 return cir::GlobalLinkageKind::WeakODRLinkage;321 322 case TSK_ExplicitInstantiationDeclaration:323 llvm_unreachable("Should not have been asked to emit this");324 }325 }326 // -fapple-kext mode does not support weak linkage, so we must use327 // internal linkage.328 if (astContext.getLangOpts().AppleKext)329 return cir::GlobalLinkageKind::InternalLinkage;330 331 auto discardableODRLinkage = cir::GlobalLinkageKind::LinkOnceODRLinkage;332 auto nonDiscardableODRLinkage = cir::GlobalLinkageKind::WeakODRLinkage;333 if (rd->hasAttr<DLLExportAttr>()) {334 // Cannot discard exported vtables.335 discardableODRLinkage = nonDiscardableODRLinkage;336 } else if (rd->hasAttr<DLLImportAttr>()) {337 // Imported vtables are available externally.338 discardableODRLinkage = cir::GlobalLinkageKind::AvailableExternallyLinkage;339 nonDiscardableODRLinkage =340 cir::GlobalLinkageKind::AvailableExternallyLinkage;341 }342 343 switch (rd->getTemplateSpecializationKind()) {344 case TSK_Undeclared:345 case TSK_ExplicitSpecialization:346 case TSK_ImplicitInstantiation:347 return discardableODRLinkage;348 349 case TSK_ExplicitInstantiationDeclaration: {350 errorNYI(rd->getSourceRange(),351 "getVTableLinkage: explicit instantiation declaration");352 return cir::GlobalLinkageKind::ExternalLinkage;353 }354 355 case TSK_ExplicitInstantiationDefinition:356 return nonDiscardableODRLinkage;357 }358 359 llvm_unreachable("Invalid TemplateSpecializationKind!");360}361 362cir::GlobalOp CIRGenVTables::getAddrOfVTT(const CXXRecordDecl *rd) {363 assert(rd->getNumVBases() && "Only classes with virtual bases need a VTT");364 365 SmallString<256> outName;366 llvm::raw_svector_ostream out(outName);367 cast<ItaniumMangleContext>(cgm.getCXXABI().getMangleContext())368 .mangleCXXVTT(rd, out);369 StringRef name = outName.str();370 371 // This will also defer the definition of the VTT.372 (void)cgm.getCXXABI().getAddrOfVTable(rd, CharUnits());373 374 VTTBuilder builder(cgm.getASTContext(), rd, /*GenerateDefinition=*/false);375 376 auto arrayType = cir::ArrayType::get(cgm.getBuilder().getUInt8PtrTy(),377 builder.getVTTComponents().size());378 llvm::Align align =379 cgm.getDataLayout().getABITypeAlign(cgm.getBuilder().getUInt8PtrTy());380 cir::GlobalOp vtt = cgm.createOrReplaceCXXRuntimeVariable(381 cgm.getLoc(rd->getSourceRange()), name, arrayType,382 cir::GlobalLinkageKind::ExternalLinkage, CharUnits::fromQuantity(align));383 cgm.setGVProperties(vtt, rd);384 return vtt;385}386 387static cir::GlobalOp388getAddrOfVTTVTable(CIRGenVTables &cgvt, CIRGenModule &cgm,389 const CXXRecordDecl *mostDerivedClass,390 const VTTVTable &vtable, cir::GlobalLinkageKind linkage,391 VTableLayout::AddressPointsMapTy &addressPoints) {392 if (vtable.getBase() == mostDerivedClass) {393 assert(vtable.getBaseOffset().isZero() &&394 "Most derived class vtable must have a zero offset!");395 // This is a regular vtable.396 return cgm.getCXXABI().getAddrOfVTable(mostDerivedClass, CharUnits());397 }398 return cgvt.generateConstructionVTable(399 mostDerivedClass, vtable.getBaseSubobject(), vtable.isVirtual(), linkage,400 addressPoints);401}402 403/// Emit the definition of the given vtable.404void CIRGenVTables::emitVTTDefinition(cir::GlobalOp vttOp,405 cir::GlobalLinkageKind linkage,406 const CXXRecordDecl *rd) {407 VTTBuilder builder(cgm.getASTContext(), rd, /*GenerateDefinition=*/true);408 409 mlir::MLIRContext *mlirContext = &cgm.getMLIRContext();410 411 auto arrayType = cir::ArrayType::get(cgm.getBuilder().getUInt8PtrTy(),412 builder.getVTTComponents().size());413 414 SmallVector<cir::GlobalOp> vtables;415 SmallVector<VTableAddressPointsMapTy> vtableAddressPoints;416 for (const VTTVTable &vtt : builder.getVTTVTables()) {417 vtableAddressPoints.push_back(VTableAddressPointsMapTy());418 vtables.push_back(getAddrOfVTTVTable(*this, cgm, rd, vtt, linkage,419 vtableAddressPoints.back()));420 }421 422 SmallVector<mlir::Attribute> vttComponents;423 for (const VTTComponent &vttComponent : builder.getVTTComponents()) {424 const VTTVTable &vttVT = builder.getVTTVTables()[vttComponent.VTableIndex];425 cir::GlobalOp vtable = vtables[vttComponent.VTableIndex];426 VTableLayout::AddressPointLocation addressPoint;427 if (vttVT.getBase() == rd) {428 // Just get the address point for the regular vtable.429 addressPoint =430 getItaniumVTableContext().getVTableLayout(rd).getAddressPoint(431 vttComponent.VTableBase);432 } else {433 addressPoint = vtableAddressPoints[vttComponent.VTableIndex].lookup(434 vttComponent.VTableBase);435 assert(addressPoint.AddressPointIndex != 0 &&436 "Did not find ctor vtable address point!");437 }438 439 mlir::Attribute indices[2] = {440 cgm.getBuilder().getI32IntegerAttr(addressPoint.VTableIndex),441 cgm.getBuilder().getI32IntegerAttr(addressPoint.AddressPointIndex),442 };443 444 auto indicesAttr = mlir::ArrayAttr::get(mlirContext, indices);445 cir::GlobalViewAttr init = cgm.getBuilder().getGlobalViewAttr(446 cgm.getBuilder().getUInt8PtrTy(), vtable, indicesAttr);447 448 vttComponents.push_back(init);449 }450 451 auto init = cir::ConstArrayAttr::get(452 arrayType, mlir::ArrayAttr::get(mlirContext, vttComponents));453 454 vttOp.setInitialValueAttr(init);455 456 // Set the correct linkage.457 vttOp.setLinkage(linkage);458 mlir::SymbolTable::setSymbolVisibility(459 vttOp, CIRGenModule::getMLIRVisibility(vttOp));460 461 if (cgm.supportsCOMDAT() && vttOp.isWeakForLinker())462 vttOp.setComdat(true);463}464 465uint64_t CIRGenVTables::getSubVTTIndex(const CXXRecordDecl *rd,466 BaseSubobject base) {467 BaseSubobjectPairTy classSubobjectPair(rd, base);468 469 SubVTTIndiciesMapTy::iterator it = subVTTIndicies.find(classSubobjectPair);470 if (it != subVTTIndicies.end())471 return it->second;472 473 VTTBuilder builder(cgm.getASTContext(), rd, /*GenerateDefinition=*/false);474 475 for (const auto &entry : builder.getSubVTTIndices()) {476 // Insert all indices.477 BaseSubobjectPairTy subclassSubobjectPair(rd, entry.first);478 479 subVTTIndicies.insert(std::make_pair(subclassSubobjectPair, entry.second));480 }481 482 it = subVTTIndicies.find(classSubobjectPair);483 assert(it != subVTTIndicies.end() && "Did not find index!");484 485 return it->second;486}487 488uint64_t CIRGenVTables::getSecondaryVirtualPointerIndex(const CXXRecordDecl *rd,489 BaseSubobject base) {490 auto it = secondaryVirtualPointerIndices.find(std::make_pair(rd, base));491 492 if (it != secondaryVirtualPointerIndices.end())493 return it->second;494 495 VTTBuilder builder(cgm.getASTContext(), rd, /*GenerateDefinition=*/false);496 497 // Insert all secondary vpointer indices.498 for (const auto &entry : builder.getSecondaryVirtualPointerIndices()) {499 std::pair<const CXXRecordDecl *, BaseSubobject> pair =500 std::make_pair(rd, entry.first);501 502 secondaryVirtualPointerIndices.insert(std::make_pair(pair, entry.second));503 }504 505 it = secondaryVirtualPointerIndices.find(std::make_pair(rd, base));506 assert(it != secondaryVirtualPointerIndices.end() && "Did not find index!");507 508 return it->second;509}510 511void CIRGenVTables::emitThunks(GlobalDecl gd) {512 const CXXMethodDecl *md =513 cast<CXXMethodDecl>(gd.getDecl())->getCanonicalDecl();514 515 // We don't need to generate thunks for the base destructor.516 if (isa<CXXDestructorDecl>(md) && gd.getDtorType() == Dtor_Base)517 return;518 519 const VTableContextBase::ThunkInfoVectorTy *thunkInfoVector =520 vtContext->getThunkInfo(gd);521 522 if (!thunkInfoVector)523 return;524 525 cgm.errorNYI(md->getSourceRange(), "emitThunks");526}527