blob: 3c2af8fbbfdf9a1ff5c9dc1be18f70245a70c1e7 [file] [log] [blame]
//===--- CIRGenVTables.cpp - Emit CIR Code for C++ vtables ----------------===//
//
// 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 "CIRGenCXXABI.h"
#include "CIRGenFunction.h"
#include "CIRGenModule.h"
#include "mlir/IR/Attributes.h"
#include "clang/AST/Attr.h"
#include "clang/AST/CXXInheritance.h"
#include "clang/AST/RecordLayout.h"
#include "clang/AST/VTTBuilder.h"
#include "clang/Basic/CodeGenOptions.h"
#include "clang/CIR/Dialect/IR/CIRAttrs.h"
#include "clang/CIR/Dialect/IR/CIRTypes.h"
#include "clang/CodeGen/CGFunctionInfo.h"
#include "clang/CodeGen/ConstantInitBuilder.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Format.h"
#include "llvm/Transforms/Utils/Cloning.h"
#include <algorithm>
#include <cstdio>
using namespace clang;
using namespace cir;
CIRGenVTables::CIRGenVTables(CIRGenModule &CGM)
: CGM(CGM), VTContext(CGM.getASTContext().getVTableContext()) {}
static bool UseRelativeLayout(const CIRGenModule &CGM) {
return CGM.getTarget().getCXXABI().isItaniumFamily() &&
CGM.getItaniumVTableContext().isRelativeLayout();
}
bool CIRGenVTables::useRelativeLayout() const { return UseRelativeLayout(CGM); }
mlir::Type CIRGenModule::getVTableComponentType() {
mlir::Type ptrTy = builder.getUInt8PtrTy();
if (UseRelativeLayout(*this))
ptrTy = builder.getUInt32PtrTy();
return ptrTy;
}
mlir::Type CIRGenVTables::getVTableComponentType() {
return CGM.getVTableComponentType();
}
mlir::Type CIRGenVTables::getVTableType(const VTableLayout &layout) {
SmallVector<mlir::Type, 4> tys;
auto ctx = CGM.getBuilder().getContext();
auto componentType = getVTableComponentType();
for (unsigned i = 0, e = layout.getNumVTables(); i != e; ++i)
tys.push_back(
mlir::cir::ArrayType::get(ctx, componentType, layout.getVTableSize(i)));
// FIXME(cir): should VTableLayout be encoded like we do for some
// AST nodes?
return CGM.getBuilder().getAnonStructTy(tys, /*incomplete=*/false);
}
/// At this point in the translation unit, does it appear that can we
/// rely on the vtable being defined elsewhere in the program?
///
/// The response is really only definitive when called at the end of
/// the translation unit.
///
/// The only semantic restriction here is that the object file should
/// not contain a vtable definition when that vtable is defined
/// strongly elsewhere. Otherwise, we'd just like to avoid emitting
/// vtables when unnecessary.
/// TODO(cir): this should be merged into common AST helper for codegen.
bool CIRGenVTables::isVTableExternal(const CXXRecordDecl *RD) {
assert(RD->isDynamicClass() && "Non-dynamic classes have no VTable.");
// We always synthesize vtables if they are needed in the MS ABI. MSVC doesn't
// emit them even if there is an explicit template instantiation.
if (CGM.getTarget().getCXXABI().isMicrosoft())
return false;
// If we have an explicit instantiation declaration (and not a
// definition), the vtable is defined elsewhere.
TemplateSpecializationKind TSK = RD->getTemplateSpecializationKind();
if (TSK == TSK_ExplicitInstantiationDeclaration)
return true;
// Otherwise, if the class is an instantiated template, the
// vtable must be defined here.
if (TSK == TSK_ImplicitInstantiation ||
TSK == TSK_ExplicitInstantiationDefinition)
return false;
// Otherwise, if the class doesn't have a key function (possibly
// anymore), the vtable must be defined here.
const CXXMethodDecl *keyFunction =
CGM.getASTContext().getCurrentKeyFunction(RD);
if (!keyFunction)
return false;
// Otherwise, if we don't have a definition of the key function, the
// vtable must be defined somewhere else.
return !keyFunction->hasBody();
}
static bool shouldEmitAvailableExternallyVTable(const CIRGenModule &CGM,
const CXXRecordDecl *RD) {
return CGM.getCodeGenOpts().OptimizationLevel > 0 &&
CGM.getCXXABI().canSpeculativelyEmitVTable(RD);
}
/// Given that we're currently at the end of the translation unit, and
/// we've emitted a reference to the vtable for this class, should
/// we define that vtable?
static bool shouldEmitVTableAtEndOfTranslationUnit(CIRGenModule &CGM,
const CXXRecordDecl *RD) {
// If vtable is internal then it has to be done.
if (!CGM.getVTables().isVTableExternal(RD))
return true;
// If it's external then maybe we will need it as available_externally.
return shouldEmitAvailableExternallyVTable(CGM, RD);
}
/// Given that at some point we emitted a reference to one or more
/// vtables, and that we are now at the end of the translation unit,
/// decide whether we should emit them.
void CIRGenModule::buildDeferredVTables() {
#ifndef NDEBUG
// Remember the size of DeferredVTables, because we're going to assume
// that this entire operation doesn't modify it.
size_t savedSize = DeferredVTables.size();
#endif
for (const CXXRecordDecl *RD : DeferredVTables)
if (shouldEmitVTableAtEndOfTranslationUnit(*this, RD)) {
VTables.GenerateClassData(RD);
} else if (shouldOpportunisticallyEmitVTables()) {
llvm_unreachable("NYI");
}
assert(savedSize == DeferredVTables.size() &&
"deferred extra vtables during vtable emission?");
DeferredVTables.clear();
}
void CIRGenVTables::GenerateClassData(const CXXRecordDecl *RD) {
assert(!MissingFeatures::generateDebugInfo());
if (RD->getNumVBases())
CGM.getCXXABI().emitVirtualInheritanceTables(RD);
CGM.getCXXABI().emitVTableDefinitions(*this, RD);
}
static void AddPointerLayoutOffset(CIRGenModule &CGM,
ConstantArrayBuilder &builder,
CharUnits offset) {
builder.add(CGM.getBuilder().getConstPtrAttr(CGM.getBuilder().getUInt8PtrTy(),
offset.getQuantity()));
}
static void AddRelativeLayoutOffset(CIRGenModule &CGM,
ConstantArrayBuilder &builder,
CharUnits offset) {
llvm_unreachable("NYI");
// builder.add(llvm::ConstantInt::get(CGM.Int32Ty, offset.getQuantity()));
}
void CIRGenVTables::addVTableComponent(ConstantArrayBuilder &builder,
const VTableLayout &layout,
unsigned componentIndex,
mlir::Attribute rtti,
unsigned &nextVTableThunkIndex,
unsigned vtableAddressPoint,
bool vtableHasLocalLinkage) {
auto &component = layout.vtable_components()[componentIndex];
auto addOffsetConstant =
useRelativeLayout() ? AddRelativeLayoutOffset : AddPointerLayoutOffset;
switch (component.getKind()) {
case VTableComponent::CK_VCallOffset:
return addOffsetConstant(CGM, builder, component.getVCallOffset());
case VTableComponent::CK_VBaseOffset:
return addOffsetConstant(CGM, builder, component.getVBaseOffset());
case VTableComponent::CK_OffsetToTop:
return addOffsetConstant(CGM, builder, component.getOffsetToTop());
case VTableComponent::CK_RTTI:
if (useRelativeLayout()) {
llvm_unreachable("NYI");
// return addRelativeComponent(builder, rtti, vtableAddressPoint,
// vtableHasLocalLinkage,
// /*isCompleteDtor=*/false);
} else {
assert((mlir::isa<mlir::cir::GlobalViewAttr>(rtti) ||
mlir::isa<mlir::cir::ConstPtrAttr>(rtti)) &&
"expected GlobalViewAttr or ConstPtrAttr");
return builder.add(rtti);
}
case VTableComponent::CK_FunctionPointer:
case VTableComponent::CK_CompleteDtorPointer:
case VTableComponent::CK_DeletingDtorPointer: {
GlobalDecl GD = component.getGlobalDecl();
if (CGM.getLangOpts().CUDA) {
llvm_unreachable("NYI");
}
auto getSpecialVirtualFn = [&](StringRef name) -> mlir::cir::FuncOp {
// FIXME(PR43094): When merging comdat groups, lld can select a local
// symbol as the signature symbol even though it cannot be accessed
// outside that symbol's TU. The relative vtables ABI would make
// __cxa_pure_virtual and __cxa_deleted_virtual local symbols, and
// depending on link order, the comdat groups could resolve to the one
// with the local symbol. As a temporary solution, fill these components
// with zero. We shouldn't be calling these in the first place anyway.
if (useRelativeLayout())
llvm_unreachable("NYI");
// For NVPTX devices in OpenMP emit special functon as null pointers,
// otherwise linking ends up with unresolved references.
if (CGM.getLangOpts().OpenMP && CGM.getLangOpts().OpenMPIsTargetDevice &&
CGM.getTriple().isNVPTX())
llvm_unreachable("NYI");
mlir::cir::FuncType fnTy =
CGM.getBuilder().getFuncType({}, CGM.getBuilder().getVoidTy());
mlir::cir::FuncOp fnPtr = CGM.createRuntimeFunction(fnTy, name);
// LLVM codegen handles unnamedAddr
assert(!MissingFeatures::unnamedAddr());
return fnPtr;
};
mlir::cir::FuncOp fnPtr;
if (cast<CXXMethodDecl>(GD.getDecl())->isPureVirtual()) {
// Pure virtual member functions.
if (!PureVirtualFn)
PureVirtualFn =
getSpecialVirtualFn(CGM.getCXXABI().getPureVirtualCallName());
fnPtr = PureVirtualFn;
} else if (cast<CXXMethodDecl>(GD.getDecl())->isDeleted()) {
// Deleted virtual member functions.
if (!DeletedVirtualFn)
DeletedVirtualFn =
getSpecialVirtualFn(CGM.getCXXABI().getDeletedVirtualCallName());
fnPtr = DeletedVirtualFn;
} else if (nextVTableThunkIndex < layout.vtable_thunks().size() &&
layout.vtable_thunks()[nextVTableThunkIndex].first ==
componentIndex) {
// Thunks.
llvm_unreachable("NYI");
// auto &thunkInfo = layout.vtable_thunks()[nextVTableThunkIndex].second;
// nextVTableThunkIndex++;
// fnPtr = maybeEmitThunk(GD, thunkInfo, /*ForVTable=*/true);
} else {
// Otherwise we can use the method definition directly.
auto fnTy = CGM.getTypes().GetFunctionTypeForVTable(GD);
fnPtr = CGM.GetAddrOfFunction(GD, fnTy, /*ForVTable=*/true);
}
if (useRelativeLayout()) {
llvm_unreachable("NYI");
} else {
return builder.add(mlir::cir::GlobalViewAttr::get(
CGM.getBuilder().getUInt8PtrTy(),
mlir::FlatSymbolRefAttr::get(fnPtr.getSymNameAttr())));
}
}
case VTableComponent::CK_UnusedFunctionPointer:
if (useRelativeLayout())
llvm_unreachable("NYI");
else {
llvm_unreachable("NYI");
// return builder.addNullPointer(CGM.Int8PtrTy);
}
}
llvm_unreachable("Unexpected vtable component kind");
}
void CIRGenVTables::createVTableInitializer(ConstantStructBuilder &builder,
const VTableLayout &layout,
mlir::Attribute rtti,
bool vtableHasLocalLinkage) {
auto componentType = getVTableComponentType();
const auto &addressPoints = layout.getAddressPointIndices();
unsigned nextVTableThunkIndex = 0;
for (unsigned vtableIndex = 0, endIndex = layout.getNumVTables();
vtableIndex != endIndex; ++vtableIndex) {
auto vtableElem = builder.beginArray(componentType);
size_t vtableStart = layout.getVTableOffset(vtableIndex);
size_t vtableEnd = vtableStart + layout.getVTableSize(vtableIndex);
for (size_t componentIndex = vtableStart; componentIndex < vtableEnd;
++componentIndex) {
addVTableComponent(vtableElem, layout, componentIndex, rtti,
nextVTableThunkIndex, addressPoints[vtableIndex],
vtableHasLocalLinkage);
}
vtableElem.finishAndAddTo(rtti.getContext(), builder);
}
}
/// Compute the required linkage of the vtable for the given class.
///
/// Note that we only call this at the end of the translation unit.
mlir::cir::GlobalLinkageKind
CIRGenModule::getVTableLinkage(const CXXRecordDecl *RD) {
if (!RD->isExternallyVisible())
return mlir::cir::GlobalLinkageKind::InternalLinkage;
// We're at the end of the translation unit, so the current key
// function is fully correct.
const CXXMethodDecl *keyFunction = astCtx.getCurrentKeyFunction(RD);
if (keyFunction && !RD->hasAttr<DLLImportAttr>()) {
// If this class has a key function, use that to determine the
// linkage of the vtable.
const FunctionDecl *def = nullptr;
if (keyFunction->hasBody(def))
keyFunction = cast<CXXMethodDecl>(def);
switch (keyFunction->getTemplateSpecializationKind()) {
case TSK_Undeclared:
case TSK_ExplicitSpecialization:
assert(
(def || codeGenOpts.OptimizationLevel > 0 ||
codeGenOpts.getDebugInfo() != llvm::codegenoptions::NoDebugInfo) &&
"Shouldn't query vtable linkage without key function, "
"optimizations, or debug info");
if (!def && codeGenOpts.OptimizationLevel > 0)
return mlir::cir::GlobalLinkageKind::AvailableExternallyLinkage;
if (keyFunction->isInlined())
return !astCtx.getLangOpts().AppleKext
? mlir::cir::GlobalLinkageKind::LinkOnceODRLinkage
: mlir::cir::GlobalLinkageKind::InternalLinkage;
return mlir::cir::GlobalLinkageKind::ExternalLinkage;
case TSK_ImplicitInstantiation:
return !astCtx.getLangOpts().AppleKext
? mlir::cir::GlobalLinkageKind::LinkOnceODRLinkage
: mlir::cir::GlobalLinkageKind::InternalLinkage;
case TSK_ExplicitInstantiationDefinition:
return !astCtx.getLangOpts().AppleKext
? mlir::cir::GlobalLinkageKind::WeakODRLinkage
: mlir::cir::GlobalLinkageKind::InternalLinkage;
case TSK_ExplicitInstantiationDeclaration:
llvm_unreachable("Should not have been asked to emit this");
}
}
// -fapple-kext mode does not support weak linkage, so we must use
// internal linkage.
if (astCtx.getLangOpts().AppleKext)
return mlir::cir::GlobalLinkageKind::InternalLinkage;
auto DiscardableODRLinkage = mlir::cir::GlobalLinkageKind::LinkOnceODRLinkage;
auto NonDiscardableODRLinkage = mlir::cir::GlobalLinkageKind::WeakODRLinkage;
if (RD->hasAttr<DLLExportAttr>()) {
// Cannot discard exported vtables.
DiscardableODRLinkage = NonDiscardableODRLinkage;
} else if (RD->hasAttr<DLLImportAttr>()) {
// Imported vtables are available externally.
DiscardableODRLinkage =
mlir::cir::GlobalLinkageKind::AvailableExternallyLinkage;
NonDiscardableODRLinkage =
mlir::cir::GlobalLinkageKind::AvailableExternallyLinkage;
}
switch (RD->getTemplateSpecializationKind()) {
case TSK_Undeclared:
case TSK_ExplicitSpecialization:
case TSK_ImplicitInstantiation:
return DiscardableODRLinkage;
case TSK_ExplicitInstantiationDeclaration: {
// Explicit instantiations in MSVC do not provide vtables, so we must emit
// our own.
if (getTarget().getCXXABI().isMicrosoft())
return DiscardableODRLinkage;
auto r = shouldEmitAvailableExternallyVTable(*this, RD)
? mlir::cir::GlobalLinkageKind::AvailableExternallyLinkage
: mlir::cir::GlobalLinkageKind::ExternalLinkage;
assert(r == mlir::cir::GlobalLinkageKind::ExternalLinkage &&
"available external NYI");
return r;
}
case TSK_ExplicitInstantiationDefinition:
return NonDiscardableODRLinkage;
}
llvm_unreachable("Invalid TemplateSpecializationKind!");
}
mlir::cir::GlobalOp
getAddrOfVTTVTable(CIRGenVTables &CGVT, CIRGenModule &CGM,
const CXXRecordDecl *MostDerivedClass,
const VTTVTable &vtable,
mlir::cir::GlobalLinkageKind linkage,
VTableLayout::AddressPointsMapTy &addressPoints) {
if (vtable.getBase() == MostDerivedClass) {
assert(vtable.getBaseOffset().isZero() &&
"Most derived class vtable must have a zero offset!");
// This is a regular vtable.
return CGM.getCXXABI().getAddrOfVTable(MostDerivedClass, CharUnits());
}
llvm_unreachable("generateConstructionVTable NYI");
}
mlir::cir::GlobalOp CIRGenVTables::getAddrOfVTT(const CXXRecordDecl *RD) {
assert(RD->getNumVBases() && "Only classes with virtual bases need a VTT");
SmallString<256> OutName;
llvm::raw_svector_ostream Out(OutName);
cast<ItaniumMangleContext>(CGM.getCXXABI().getMangleContext())
.mangleCXXVTT(RD, Out);
StringRef Name = OutName.str();
// This will also defer the definition of the VTT.
(void)CGM.getCXXABI().getAddrOfVTable(RD, CharUnits());
VTTBuilder Builder(CGM.getASTContext(), RD, /*GenerateDefinition=*/false);
auto ArrayType = mlir::cir::ArrayType::get(CGM.getBuilder().getContext(),
CGM.getBuilder().getUInt8PtrTy(),
Builder.getVTTComponents().size());
auto Align =
CGM.getDataLayout().getABITypeAlign(CGM.getBuilder().getUInt8PtrTy());
auto VTT = CGM.createOrReplaceCXXRuntimeVariable(
CGM.getLoc(RD->getSourceRange()), Name, ArrayType,
mlir::cir::GlobalLinkageKind::ExternalLinkage,
CharUnits::fromQuantity(Align));
CGM.setGVProperties(VTT, RD);
return VTT;
}
/// Emit the definition of the given vtable.
void CIRGenVTables::buildVTTDefinition(mlir::cir::GlobalOp VTT,
mlir::cir::GlobalLinkageKind Linkage,
const CXXRecordDecl *RD) {
VTTBuilder Builder(CGM.getASTContext(), RD, /*GenerateDefinition=*/true);
auto ArrayType = mlir::cir::ArrayType::get(CGM.getBuilder().getContext(),
CGM.getBuilder().getUInt8PtrTy(),
Builder.getVTTComponents().size());
SmallVector<mlir::cir::GlobalOp, 8> VTables;
SmallVector<VTableAddressPointsMapTy, 8> VTableAddressPoints;
for (const VTTVTable *i = Builder.getVTTVTables().begin(),
*e = Builder.getVTTVTables().end();
i != e; ++i) {
VTableAddressPoints.push_back(VTableAddressPointsMapTy());
VTables.push_back(getAddrOfVTTVTable(*this, CGM, RD, *i, Linkage,
VTableAddressPoints.back()));
}
SmallVector<mlir::Attribute, 8> VTTComponents;
for (const VTTComponent *i = Builder.getVTTComponents().begin(),
*e = Builder.getVTTComponents().end();
i != e; ++i) {
const VTTVTable &VTTVT = Builder.getVTTVTables()[i->VTableIndex];
mlir::cir::GlobalOp VTable = VTables[i->VTableIndex];
VTableLayout::AddressPointLocation AddressPoint;
if (VTTVT.getBase() == RD) {
// Just get the address point for the regular vtable.
AddressPoint =
getItaniumVTableContext().getVTableLayout(RD).getAddressPoint(
i->VTableBase);
} else {
AddressPoint = VTableAddressPoints[i->VTableIndex].lookup(i->VTableBase);
assert(AddressPoint.AddressPointIndex != 0 &&
"Did not find ctor vtable address point!");
}
mlir::Attribute Idxs[3] = {
CGM.getBuilder().getI32IntegerAttr(0),
CGM.getBuilder().getI32IntegerAttr(AddressPoint.VTableIndex),
CGM.getBuilder().getI32IntegerAttr(AddressPoint.AddressPointIndex),
};
auto Indices = mlir::ArrayAttr::get(CGM.getBuilder().getContext(), Idxs);
auto Init = CGM.getBuilder().getGlobalViewAttr(
CGM.getBuilder().getUInt8PtrTy(), VTable, Indices);
VTTComponents.push_back(Init);
}
auto Init = CGM.getBuilder().getConstArray(
mlir::ArrayAttr::get(CGM.getBuilder().getContext(), VTTComponents),
ArrayType);
VTT.setInitialValueAttr(Init);
// Set the correct linkage.
VTT.setLinkage(Linkage);
mlir::SymbolTable::setSymbolVisibility(VTT,
CIRGenModule::getMLIRVisibility(VTT));
if (CGM.supportsCOMDAT() && VTT.isWeakForLinker()) {
assert(!MissingFeatures::setComdat());
}
}
void CIRGenVTables::buildThunks(GlobalDecl GD) {
const CXXMethodDecl *MD =
cast<CXXMethodDecl>(GD.getDecl())->getCanonicalDecl();
// We don't need to generate thunks for the base destructor.
if (isa<CXXDestructorDecl>(MD) && GD.getDtorType() == Dtor_Base)
return;
const VTableContextBase::ThunkInfoVectorTy *ThunkInfoVector =
VTContext->getThunkInfo(GD);
if (!ThunkInfoVector)
return;
for ([[maybe_unused]] const ThunkInfo &Thunk : *ThunkInfoVector)
llvm_unreachable("NYI");
}
bool CIRGenModule::AlwaysHasLTOVisibilityPublic(const CXXRecordDecl *RD) {
if (RD->hasAttr<LTOVisibilityPublicAttr>() || RD->hasAttr<UuidAttr>() ||
RD->hasAttr<DLLExportAttr>() || RD->hasAttr<DLLImportAttr>())
return true;
if (!getCodeGenOpts().LTOVisibilityPublicStd)
return false;
const DeclContext *DC = RD;
while (true) {
auto *D = cast<Decl>(DC);
DC = DC->getParent();
if (isa<TranslationUnitDecl>(DC->getRedeclContext())) {
if (auto *ND = dyn_cast<NamespaceDecl>(D))
if (const IdentifierInfo *II = ND->getIdentifier())
if (II->isStr("std") || II->isStr("stdext"))
return true;
break;
}
}
return false;
}
bool CIRGenModule::HasHiddenLTOVisibility(const CXXRecordDecl *RD) {
LinkageInfo LV = RD->getLinkageAndVisibility();
if (!isExternallyVisible(LV.getLinkage()))
return true;
if (!getTriple().isOSBinFormatCOFF() &&
LV.getVisibility() != HiddenVisibility)
return false;
return !AlwaysHasLTOVisibilityPublic(RD);
}