blob: 6577f5fb0f2ef1f1873dd85ab237b7e2b76b69c9 [file] [log] [blame]
//===----------------------------------------------------------------------===//
//
// 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 provides C++ code generation targeting the Itanium C++ ABI. The class
// in this file generates structures that follow the Itanium C++ ABI, which is
// documented at:
// https://itanium-cxx-abi.github.io/cxx-abi/abi.html
// https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html
//
// It also supports the closely-related ARM ABI, documented at:
// https://developer.arm.com/documentation/ihi0041/g/
//
//===----------------------------------------------------------------------===//
#include "CIRGenCXXABI.h"
#include "CIRGenFunction.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/GlobalDecl.h"
#include "clang/CIR/MissingFeatures.h"
#include "llvm/Support/ErrorHandling.h"
using namespace clang;
using namespace clang::CIRGen;
namespace {
class CIRGenItaniumCXXABI : public CIRGenCXXABI {
public:
CIRGenItaniumCXXABI(CIRGenModule &cgm) : CIRGenCXXABI(cgm) {
assert(!cir::MissingFeatures::cxxabiUseARMMethodPtrABI());
assert(!cir::MissingFeatures::cxxabiUseARMGuardVarABI());
}
bool needsVTTParameter(clang::GlobalDecl gd) override;
void emitInstanceFunctionProlog(SourceLocation loc,
CIRGenFunction &cgf) override;
void emitCXXConstructors(const clang::CXXConstructorDecl *d) override;
void emitCXXDestructors(const clang::CXXDestructorDecl *d) override;
void emitCXXStructor(clang::GlobalDecl gd) override;
void emitDestructorCall(CIRGenFunction &cgf, const CXXDestructorDecl *dd,
CXXDtorType type, bool forVirtualBase,
bool delegating, Address thisAddr,
QualType thisTy) override;
bool useThunkForDtorVariant(const CXXDestructorDecl *dtor,
CXXDtorType dt) const override {
// Itanium does not emit any destructor variant as an inline thunk.
// Delegating may occur as an optimization, but all variants are either
// emitted with external linkage or as linkonce if they are inline and used.
return false;
}
};
} // namespace
void CIRGenItaniumCXXABI::emitInstanceFunctionProlog(SourceLocation loc,
CIRGenFunction &cgf) {
// Naked functions have no prolog.
if (cgf.curFuncDecl && cgf.curFuncDecl->hasAttr<NakedAttr>()) {
cgf.cgm.errorNYI(cgf.curFuncDecl->getLocation(),
"emitInstanceFunctionProlog: Naked");
}
/// Initialize the 'this' slot. In the Itanium C++ ABI, no prologue
/// adjustments are required, because they are all handled by thunks.
setCXXABIThisValue(cgf, loadIncomingCXXThis(cgf));
/// Classic codegen has code here to initialize the 'vtt' slot if
// getStructorImplicitParamDecl(cgf) returns a non-null value, but in the
// current implementation (of classic codegen) it never does.
assert(!cir::MissingFeatures::cxxabiStructorImplicitParam());
/// If this is a function that the ABI specifies returns 'this', initialize
/// the return slot to this' at the start of the function.
///
/// Unlike the setting of return types, this is done within the ABI
/// implementation instead of by clients of CIRGenCXXBI because:
/// 1) getThisValue is currently protected
/// 2) in theory, an ABI could implement 'this' returns some other way;
/// HasThisReturn only specifies a contract, not the implementation
if (hasThisReturn(cgf.curGD)) {
cgf.cgm.errorNYI(cgf.curFuncDecl->getLocation(),
"emitInstanceFunctionProlog: hasThisReturn");
}
}
// Find out how to cirgen the complete destructor and constructor
namespace {
enum class StructorCIRGen { Emit, RAUW, Alias, COMDAT };
}
static StructorCIRGen getCIRGenToUse(CIRGenModule &cgm,
const CXXMethodDecl *md) {
if (!cgm.getCodeGenOpts().CXXCtorDtorAliases)
return StructorCIRGen::Emit;
// The complete and base structors are not equivalent if there are any virtual
// bases, so emit separate functions.
if (md->getParent()->getNumVBases()) {
// The return value is correct here, but other support for this is NYI.
cgm.errorNYI(md->getSourceRange(), "getCIRGenToUse: virtual bases");
return StructorCIRGen::Emit;
}
GlobalDecl aliasDecl;
if (const auto *dd = dyn_cast<CXXDestructorDecl>(md)) {
// The assignment is correct here, but other support for this is NYI.
cgm.errorNYI(md->getSourceRange(), "getCIRGenToUse: dtor");
aliasDecl = GlobalDecl(dd, Dtor_Complete);
} else {
const auto *cd = cast<CXXConstructorDecl>(md);
aliasDecl = GlobalDecl(cd, Ctor_Complete);
}
cir::GlobalLinkageKind linkage = cgm.getFunctionLinkage(aliasDecl);
if (cir::isDiscardableIfUnused(linkage))
return StructorCIRGen::RAUW;
// FIXME: Should we allow available_externally aliases?
if (!cir::isValidLinkage(linkage))
return StructorCIRGen::RAUW;
if (cir::isWeakForLinker(linkage)) {
// Only ELF and wasm support COMDATs with arbitrary names (C5/D5).
if (cgm.getTarget().getTriple().isOSBinFormatELF() ||
cgm.getTarget().getTriple().isOSBinFormatWasm())
return StructorCIRGen::COMDAT;
return StructorCIRGen::Emit;
}
return StructorCIRGen::Alias;
}
static void emitConstructorDestructorAlias(CIRGenModule &cgm,
GlobalDecl aliasDecl,
GlobalDecl targetDecl) {
cir::GlobalLinkageKind linkage = cgm.getFunctionLinkage(aliasDecl);
// Does this function alias already exists?
StringRef mangledName = cgm.getMangledName(aliasDecl);
auto globalValue = dyn_cast_or_null<cir::CIRGlobalValueInterface>(
cgm.getGlobalValue(mangledName));
if (globalValue && !globalValue.isDeclaration())
return;
auto entry = cast_or_null<cir::FuncOp>(cgm.getGlobalValue(mangledName));
// Retrieve aliasee info.
auto aliasee = cast<cir::FuncOp>(cgm.getAddrOfGlobal(targetDecl));
// Populate actual alias.
cgm.emitAliasForGlobal(mangledName, entry, aliasDecl, aliasee, linkage);
}
void CIRGenItaniumCXXABI::emitCXXStructor(GlobalDecl gd) {
auto *md = cast<CXXMethodDecl>(gd.getDecl());
StructorCIRGen cirGenType = getCIRGenToUse(cgm, md);
const auto *cd = dyn_cast<CXXConstructorDecl>(md);
if (cd ? gd.getCtorType() == Ctor_Complete
: gd.getDtorType() == Dtor_Complete) {
GlobalDecl baseDecl =
cd ? gd.getWithCtorType(Ctor_Base) : gd.getWithDtorType(Dtor_Base);
;
if (cirGenType == StructorCIRGen::Alias ||
cirGenType == StructorCIRGen::COMDAT) {
emitConstructorDestructorAlias(cgm, gd, baseDecl);
return;
}
if (cirGenType == StructorCIRGen::RAUW) {
StringRef mangledName = cgm.getMangledName(gd);
mlir::Operation *aliasee = cgm.getAddrOfGlobal(baseDecl);
cgm.addReplacement(mangledName, aliasee);
return;
}
}
auto fn = cgm.codegenCXXStructor(gd);
cgm.maybeSetTrivialComdat(*md, fn);
}
void CIRGenItaniumCXXABI::emitCXXConstructors(const CXXConstructorDecl *d) {
// Just make sure we're in sync with TargetCXXABI.
assert(cgm.getTarget().getCXXABI().hasConstructorVariants());
// The constructor used for constructing this as a base class;
// ignores virtual bases.
cgm.emitGlobal(GlobalDecl(d, Ctor_Base));
// The constructor used for constructing this as a complete class;
// constructs the virtual bases, then calls the base constructor.
if (!d->getParent()->isAbstract()) {
// We don't need to emit the complete ctro if the class is abstract.
cgm.emitGlobal(GlobalDecl(d, Ctor_Complete));
}
}
void CIRGenItaniumCXXABI::emitCXXDestructors(const CXXDestructorDecl *d) {
// The destructor used for destructing this as a base class; ignores
// virtual bases.
cgm.emitGlobal(GlobalDecl(d, Dtor_Base));
// The destructor used for destructing this as a most-derived class;
// call the base destructor and then destructs any virtual bases.
cgm.emitGlobal(GlobalDecl(d, Dtor_Complete));
// The destructor in a virtual table is always a 'deleting'
// destructor, which calls the complete destructor and then uses the
// appropriate operator delete.
if (d->isVirtual())
cgm.emitGlobal(GlobalDecl(d, Dtor_Deleting));
}
/// Return whether the given global decl needs a VTT (virtual table table)
/// parameter, which it does if it's a base constructor or destructor with
/// virtual bases.
bool CIRGenItaniumCXXABI::needsVTTParameter(GlobalDecl gd) {
auto *md = cast<CXXMethodDecl>(gd.getDecl());
// We don't have any virtual bases, just return early.
if (!md->getParent()->getNumVBases())
return false;
// Check if we have a base constructor.
if (isa<CXXConstructorDecl>(md) && gd.getCtorType() == Ctor_Base)
return true;
// Check if we have a base destructor.
if (isa<CXXDestructorDecl>(md) && gd.getDtorType() == Dtor_Base)
return true;
return false;
}
void CIRGenItaniumCXXABI::emitDestructorCall(
CIRGenFunction &cgf, const CXXDestructorDecl *dd, CXXDtorType type,
bool forVirtualBase, bool delegating, Address thisAddr, QualType thisTy) {
GlobalDecl gd(dd, type);
if (needsVTTParameter(gd)) {
cgm.errorNYI(dd->getSourceRange(), "emitDestructorCall: VTT");
}
mlir::Value vtt = nullptr;
ASTContext &astContext = cgm.getASTContext();
QualType vttTy = astContext.getPointerType(astContext.VoidPtrTy);
assert(!cir::MissingFeatures::appleKext());
CIRGenCallee callee =
CIRGenCallee::forDirect(cgm.getAddrOfCXXStructor(gd), gd);
cgf.emitCXXDestructorCall(gd, callee, thisAddr.getPointer(), thisTy, vtt,
vttTy, nullptr);
}
CIRGenCXXABI *clang::CIRGen::CreateCIRGenItaniumCXXABI(CIRGenModule &cgm) {
switch (cgm.getASTContext().getCXXABIKind()) {
case TargetCXXABI::GenericItanium:
case TargetCXXABI::GenericAArch64:
return new CIRGenItaniumCXXABI(cgm);
case TargetCXXABI::AppleARM64:
// The general Itanium ABI will do until we implement something that
// requires special handling.
assert(!cir::MissingFeatures::cxxabiAppleARM64CXXABI());
return new CIRGenItaniumCXXABI(cgm);
default:
llvm_unreachable("bad or NYI ABI kind");
}
}