mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-04-27 21:52:15 +03:00
791 lines
24 KiB
C++
791 lines
24 KiB
C++
//===-- irclass.cpp -------------------------------------------------------===//
|
||
//
|
||
// LDC – the LLVM D compiler
|
||
//
|
||
// This file is distributed under the BSD-style LDC license. See the LICENSE
|
||
// file for details.
|
||
//
|
||
//===----------------------------------------------------------------------===//
|
||
|
||
#include "dmd/aggregate.h"
|
||
#include "dmd/declaration.h"
|
||
#include "dmd/errors.h"
|
||
#include "dmd/hdrgen.h" // for parametersTypeToChars()
|
||
#include "dmd/identifier.h"
|
||
#include "dmd/mangle.h"
|
||
#include "dmd/mtype.h"
|
||
#include "dmd/target.h"
|
||
#include "gen/abi/abi.h"
|
||
#include "gen/arrays.h"
|
||
#include "gen/classes.h"
|
||
#include "gen/funcgenstate.h"
|
||
#include "gen/functions.h"
|
||
#include "gen/irstate.h"
|
||
#include "gen/logger.h"
|
||
#include "gen/llvmhelpers.h"
|
||
#include "gen/mangling.h"
|
||
#include "gen/passes/metadata.h"
|
||
#include "gen/rttibuilder.h"
|
||
#include "gen/runtime.h"
|
||
#include "gen/tollvm.h"
|
||
#include "gen/typinf.h"
|
||
#include "ir/iraggr.h"
|
||
#include "ir/irdsymbol.h"
|
||
#include "ir/irfunction.h"
|
||
#include "ir/irtypeclass.h"
|
||
#include "llvm/ADT/SmallString.h"
|
||
#include "llvm/IR/Constants.h"
|
||
#include "llvm/IR/DerivedTypes.h"
|
||
#include "llvm/Support/Endian.h"
|
||
#include "llvm/Support/MD5.h"
|
||
|
||
#ifndef NDEBUG
|
||
#include "llvm/Support/raw_ostream.h"
|
||
#endif
|
||
|
||
using namespace dmd;
|
||
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
|
||
IrClass::IrClass(ClassDeclaration *cd) : IrAggr(cd) {
|
||
addInterfaceVtbls(cd);
|
||
|
||
assert(interfacesWithVtbls.size() ==
|
||
getIrType(type)->isClass()->getNumInterfaceVtbls() &&
|
||
"inconsistent number of interface vtables in this class");
|
||
}
|
||
|
||
void IrClass::addInterfaceVtbls(ClassDeclaration *cd) {
|
||
|
||
// No interface vtables in Objective-C
|
||
if (cd->classKind == ClassKind::objc)
|
||
return;
|
||
|
||
if (cd->baseClass && !cd->isInterfaceDeclaration()) {
|
||
addInterfaceVtbls(cd->baseClass);
|
||
}
|
||
|
||
if (cd->vtblInterfaces) {
|
||
for (auto bc : *cd->vtblInterfaces) {
|
||
interfacesWithVtbls.push_back(bc);
|
||
}
|
||
}
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
|
||
LLGlobalVariable *IrClass::getVtblSymbol(bool define) {
|
||
if (!vtbl) {
|
||
const auto irMangle = getIRMangledVTableSymbolName(aggrdecl);
|
||
|
||
LLType *vtblTy = getIrType(type)->isClass()->getVtblType();
|
||
|
||
vtbl = declareGlobal(aggrdecl->loc, gIR->module, vtblTy, irMangle,
|
||
/*isConstant=*/true, false, useDLLImport());
|
||
|
||
if (!define)
|
||
define = defineOnDeclare(aggrdecl, /*isFunction=*/false);
|
||
}
|
||
|
||
if (define) {
|
||
auto init = getVtblInit(); // might define vtbl
|
||
if (!vtbl->hasInitializer())
|
||
defineGlobal(vtbl, init, aggrdecl);
|
||
}
|
||
|
||
return vtbl;
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
|
||
LLGlobalVariable *IrClass::getClassInfoSymbol(bool define) {
|
||
if (!typeInfo) {
|
||
const auto irMangle = getIRMangledClassInfoSymbolName(aggrdecl);
|
||
|
||
// The type is also ClassInfo for interfaces – the actual TypeInfo for them
|
||
// is a TypeInfo_Interface instance that references __ClassZ in its "base"
|
||
// member.
|
||
IrTypeClass *tc = getIrType(getClassInfoType(), true)->isClass();
|
||
assert(tc && "invalid ClassInfo type");
|
||
|
||
// We need to keep the symbol mutable as the type is not declared as
|
||
// immutable on the D side, and e.g. synchronized() can be used on the
|
||
// implicit monitor.
|
||
typeInfo = declareGlobal(aggrdecl->loc, gIR->module, tc->getMemoryLLType(),
|
||
irMangle, /*isConstant=*/false, false, useDLLImport());
|
||
|
||
// Generate some metadata on this ClassInfo if it's for a class.
|
||
if (!aggrdecl->isInterfaceDeclaration()) {
|
||
// regular TypeInfo metadata
|
||
emitTypeInfoMetadata(typeInfo, aggrdecl->type);
|
||
|
||
// Gather information
|
||
LLType *bodyType = getLLStructType();
|
||
bool hasDestructor = (aggrdecl->dtor != nullptr);
|
||
// Construct the fields
|
||
llvm::Metadata *mdVals[CD_NumFields];
|
||
mdVals[CD_BodyType] =
|
||
llvm::ConstantAsMetadata::get(llvm::UndefValue::get(bodyType));
|
||
mdVals[CD_Finalize] = llvm::ConstantAsMetadata::get(
|
||
LLConstantInt::get(LLType::getInt1Ty(gIR->context()), hasDestructor));
|
||
// Construct the metadata and insert it into the module.
|
||
const auto metaname = getMetadataName(CD_PREFIX, typeInfo);
|
||
llvm::NamedMDNode *node = gIR->module.getOrInsertNamedMetadata(metaname);
|
||
node->addOperand(llvm::MDNode::get(gIR->context(), mdVals));
|
||
}
|
||
|
||
if (!define)
|
||
define = defineOnDeclare(aggrdecl, /*isFunction=*/false);
|
||
}
|
||
|
||
if (define) {
|
||
auto init = getClassInfoInit();
|
||
if (!typeInfo->hasInitializer())
|
||
defineGlobal(typeInfo, init, aggrdecl);
|
||
}
|
||
|
||
return typeInfo;
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
|
||
static Type *getInterfacesArrayType() {
|
||
getClassInfoType(); // check declaration in object.d
|
||
return Type::typeinfoclass->fields[3]->type;
|
||
}
|
||
|
||
LLGlobalVariable *IrClass::getInterfaceArraySymbol() {
|
||
if (classInterfacesArray) {
|
||
return classInterfacesArray;
|
||
}
|
||
|
||
ClassDeclaration *cd = aggrdecl->isClassDeclaration();
|
||
|
||
size_t n = getIrType(type)->isClass()->getNumInterfaceVtbls();
|
||
assert(n > 0 && "getting ClassInfo.interfaces storage symbol, but we "
|
||
"don't implement any interfaces");
|
||
|
||
LLType *InterfaceTy = DtoType(getInterfacesArrayType()->nextOf());
|
||
|
||
// create Interface[N]
|
||
const auto irMangle = getIRMangledInterfaceInfosSymbolName(cd);
|
||
|
||
LLArrayType *array_type = llvm::ArrayType::get(InterfaceTy, n);
|
||
|
||
classInterfacesArray =
|
||
declareGlobal(cd->loc, gIR->module, array_type, irMangle,
|
||
/*isConstant=*/true, false, false);
|
||
|
||
return classInterfacesArray;
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
|
||
LLConstant *IrClass::getVtblInit() {
|
||
if (constVtbl) {
|
||
return constVtbl;
|
||
}
|
||
|
||
IF_LOG Logger::println("Building vtbl initializer");
|
||
LOG_SCOPE;
|
||
|
||
ClassDeclaration *cd = aggrdecl->isClassDeclaration();
|
||
assert(cd && "not class");
|
||
|
||
std::vector<llvm::Constant *> constants;
|
||
constants.reserve(cd->vtbl.length);
|
||
|
||
// start with the classinfo
|
||
llvm::Constant *c;
|
||
if (!cd->isCPPclass()) {
|
||
if (!suppressTypeInfo()) {
|
||
c = getClassInfoSymbol();
|
||
} else {
|
||
// use null if there are no TypeInfos
|
||
c = getNullPtr();
|
||
}
|
||
constants.push_back(c);
|
||
}
|
||
|
||
// add virtual function pointers
|
||
size_t n = cd->vtbl.length;
|
||
for (size_t i = cd->vtblOffset(); i < n; i++) {
|
||
Dsymbol *dsym = cd->vtbl[i];
|
||
assert(dsym && "null vtbl member");
|
||
|
||
FuncDeclaration *fd = dsym->isFuncDeclaration();
|
||
assert(fd && "vtbl entry not a function");
|
||
|
||
if (cd->isAbstract() || (fd->isAbstract() && !fd->fbody)) {
|
||
c = getNullPtr();
|
||
} else {
|
||
// If inferring return type and semantic3 has not been run, do it now.
|
||
// This pops up in some other places in the frontend as well, however
|
||
// it is probably a bug that it still occurs that late.
|
||
if (fd->inferRetType() && !fd->type->nextOf()) {
|
||
Logger::println("Running late functionSemantic to infer return type.");
|
||
if (!functionSemantic(fd)) {
|
||
if (fd->hasSemantic3Errors()) {
|
||
Logger::println(
|
||
"functionSemantic failed; using null for vtbl entry.");
|
||
constants.push_back(getNullPtr());
|
||
continue;
|
||
}
|
||
error(fd->loc,
|
||
"failed to infer return type of `%s` for vtbl initializer",
|
||
fd->toPrettyChars());
|
||
fatal();
|
||
}
|
||
}
|
||
|
||
c = DtoCallee(fd);
|
||
|
||
if (cd->isFuncHidden(fd) && !fd->isFuture()) {
|
||
// fd is hidden from the view of this class. If fd overlaps with any
|
||
// function in the vtbl[], issue error.
|
||
for (size_t j = cd->vtblOffset(); j < n; j++) {
|
||
if (j == i) {
|
||
continue;
|
||
}
|
||
auto fd2 = cd->vtbl[j]->isFuncDeclaration();
|
||
if (!fd2->ident->equals(fd->ident)) {
|
||
continue;
|
||
}
|
||
if (fd2->isFuture()) {
|
||
continue;
|
||
}
|
||
if (leastAsSpecialized(fd, fd2, nullptr) != MATCH::nomatch ||
|
||
leastAsSpecialized(fd2, fd, nullptr) != MATCH::nomatch) {
|
||
TypeFunction *tf = static_cast<TypeFunction *>(fd->type);
|
||
if (tf->ty == TY::Tfunction) {
|
||
error(cd->loc,
|
||
"%s `%s` use of `%s%s` is hidden by `%s`; use `alias %s = "
|
||
"%s.%s;` to introduce base class overload set",
|
||
cd->kind(), cd->toPrettyChars(), fd->toPrettyChars(),
|
||
parametersTypeToChars(tf->parameterList), cd->toChars(),
|
||
fd->toChars(), fd->parent->toChars(), fd->toChars());
|
||
} else {
|
||
error(cd->loc, "%s `%s` use of `%s` is hidden by `%s`",
|
||
cd->kind(), cd->toPrettyChars(), fd->toPrettyChars(),
|
||
cd->toChars());
|
||
}
|
||
fatal();
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
constants.push_back(c);
|
||
}
|
||
|
||
// build the constant array
|
||
LLArrayType *vtblTy = LLArrayType::get(getOpaquePtrType(), constants.size());
|
||
constVtbl = LLConstantArray::get(vtblTy, constants);
|
||
|
||
return constVtbl;
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
|
||
/**
|
||
* The following code should be kept in sync with upstream, dmd/toobj.d:
|
||
* - genClassInfoForClass()
|
||
* - genClassInfoForInterface()
|
||
*/
|
||
|
||
namespace {
|
||
unsigned buildClassinfoFlags(ClassDeclaration *cd) {
|
||
auto flags =
|
||
ClassFlags::hasOffTi | ClassFlags::hasTypeInfo | ClassFlags::hasNameSig;
|
||
if (cd->isInterfaceDeclaration()) {
|
||
if (cd->isCOMinterface()) {
|
||
flags |= ClassFlags::isCOMclass;
|
||
}
|
||
return flags;
|
||
}
|
||
|
||
if (cd->isCOMclass()) {
|
||
flags |= ClassFlags::isCOMclass;
|
||
}
|
||
if (cd->isCPPclass()) {
|
||
flags |= ClassFlags::isCPPclass;
|
||
}
|
||
flags |= ClassFlags::hasGetMembers;
|
||
if (cd->ctor) {
|
||
flags |= ClassFlags::hasCtor;
|
||
}
|
||
for (ClassDeclaration *pc = cd; pc; pc = pc->baseClass) {
|
||
if (pc->dtor) {
|
||
flags |= ClassFlags::hasDtor;
|
||
break;
|
||
}
|
||
}
|
||
if (cd->isAbstract()) {
|
||
flags |= ClassFlags::isAbstract;
|
||
}
|
||
for (ClassDeclaration *pc = cd; pc; pc = pc->baseClass) {
|
||
if (pc->members) {
|
||
for (Dsymbol *sm : *pc->members) {
|
||
// printf("sm = %s %s\n", sm->kind(), sm->toChars());
|
||
if (sm->hasPointers()) {
|
||
return flags;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
flags |= ClassFlags::noPointers;
|
||
|
||
return flags;
|
||
}
|
||
}
|
||
|
||
LLConstant *IrClass::getClassInfoInit() {
|
||
if (constTypeInfo) {
|
||
return constTypeInfo;
|
||
}
|
||
|
||
auto cd = aggrdecl->isClassDeclaration();
|
||
IF_LOG Logger::println("Defining ClassInfo for: %s", cd->toChars());
|
||
LOG_SCOPE;
|
||
|
||
Type *const cinfoType = getClassInfoType(); // check declaration in object.d
|
||
ClassDeclaration *const cinfo = Type::typeinfoclass;
|
||
|
||
if (cinfo->fields.length != 14) {
|
||
error(Loc(), "Unexpected number of fields in `object.ClassInfo`; "
|
||
"druntime version does not match compiler (see -v)");
|
||
fatal();
|
||
}
|
||
|
||
const bool isInterface = cd->isInterfaceDeclaration();
|
||
|
||
RTTIBuilder b(cinfoType);
|
||
|
||
// adapted from original dmd code
|
||
// byte[] m_init
|
||
if (isInterface) {
|
||
b.push_null_void_array();
|
||
} else {
|
||
b.push_void_array(cd->size(Loc()), getInitSymbol());
|
||
}
|
||
|
||
// string name
|
||
const char *name = cd->ident->toChars();
|
||
if (strncmp(name, "TypeInfo_", 9) != 0) {
|
||
name = cd->toPrettyChars(/*QualifyTypes=*/true);
|
||
}
|
||
b.push_string(name);
|
||
|
||
// void*[] vtbl
|
||
if (isInterface) {
|
||
b.push_array(0, getNullPtr());
|
||
} else {
|
||
b.push_array(cd->vtbl.length, getVtblSymbol());
|
||
}
|
||
|
||
// Interface[] interfaces
|
||
b.push(getClassInfoInterfaces());
|
||
|
||
// TypeInfo_Class base
|
||
assert(!isInterface || !cd->baseClass);
|
||
if (cd->baseClass) {
|
||
DtoResolveClass(cd->baseClass);
|
||
b.push(getIrAggr(cd->baseClass)->getClassInfoSymbol());
|
||
} else {
|
||
b.push_null(cinfoType);
|
||
}
|
||
|
||
// void* destructor
|
||
assert(!isInterface || !cd->tidtor);
|
||
b.push_funcptr(cd->tidtor);
|
||
|
||
// void function(Object) classInvariant
|
||
assert(!isInterface || !cd->inv);
|
||
b.push_funcptr(cd->inv);
|
||
|
||
// ClassFlags m_flags
|
||
const auto flags = buildClassinfoFlags(cd);
|
||
b.push(DtoConstUshort(flags));
|
||
|
||
// ushort depth
|
||
{
|
||
uint16_t depth = 0;
|
||
for (auto pc = cd; pc; pc = pc->baseClass)
|
||
++depth; // distance to Object
|
||
b.push(DtoConstUshort(depth));
|
||
}
|
||
|
||
// void* deallocator
|
||
b.push_null_vp();
|
||
|
||
// OffsetTypeInfo[] m_offTi
|
||
VarDeclaration *offTiVar = cinfo->fields[10];
|
||
b.push_null(offTiVar->type);
|
||
|
||
// void function(Object) defaultConstructor
|
||
CtorDeclaration *defConstructor = cd->defaultCtor;
|
||
if (defConstructor && (defConstructor->storage_class & STCdisable)) {
|
||
defConstructor = nullptr;
|
||
}
|
||
assert(!isInterface || !defConstructor);
|
||
b.push_funcptr(defConstructor);
|
||
|
||
// immutable(void)* m_RTInfo
|
||
if (cd->getRTInfo) {
|
||
b.push(toConstElem(cd->getRTInfo, gIR));
|
||
} else if (isInterface || (flags & ClassFlags::noPointers)) {
|
||
b.push_size_as_vp(0); // no pointers
|
||
} else {
|
||
b.push_size_as_vp(1); // has pointers
|
||
}
|
||
|
||
// uint[4] nameSig
|
||
{
|
||
llvm::MD5 hasher;
|
||
hasher.update(name);
|
||
llvm::MD5::MD5Result result;
|
||
hasher.final(result);
|
||
|
||
LLConstant *dwords[4];
|
||
for (int i = 0; i < 4; ++i) {
|
||
// make sure the 4 dwords don't depend on the endianness of the *host*
|
||
dwords[i] = DtoConstUint(llvm::support::endian::read32le(&result[4 * i]));
|
||
}
|
||
|
||
auto t = llvm::ArrayType::get(dwords[0]->getType(), 4);
|
||
b.push(LLConstantArray::get(t, dwords));
|
||
}
|
||
|
||
// build the initializer
|
||
LLType *initType = getClassInfoSymbol()->getValueType();
|
||
constTypeInfo = b.get_constant(isaStruct(initType));
|
||
|
||
return constTypeInfo;
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
|
||
llvm::GlobalVariable *IrClass::getInterfaceVtblSymbol(BaseClass *b,
|
||
size_t interfaces_index,
|
||
bool define) {
|
||
LLGlobalVariable *gvar = nullptr;
|
||
|
||
const auto it = interfaceVtblMap.find({b->sym, interfaces_index});
|
||
if (it != interfaceVtblMap.end()) {
|
||
gvar = it->second;
|
||
} else {
|
||
llvm::Type *vtblType =
|
||
LLArrayType::get(getOpaquePtrType(), b->sym->vtbl.length);
|
||
|
||
// Thunk prefix
|
||
char thunkPrefix[16];
|
||
int thunkLen = snprintf(thunkPrefix, 16, "Thn%d_", b->offset);
|
||
char thunkPrefixLen[16];
|
||
snprintf(thunkPrefixLen, 16, "%d", thunkLen);
|
||
|
||
OutBuffer mangledName;
|
||
mangledName.writestring("_D");
|
||
mangleToBuffer(aggrdecl, mangledName);
|
||
mangledName.writestring("11__interface");
|
||
mangleToBuffer(b->sym, mangledName);
|
||
mangledName.writestring(thunkPrefixLen);
|
||
mangledName.writestring(thunkPrefix);
|
||
mangledName.writestring("6__vtblZ");
|
||
|
||
const auto irMangle = getIRMangledVarName(mangledName.peekChars(), LINK::d);
|
||
|
||
gvar = declareGlobal(aggrdecl->loc, gIR->module, vtblType, irMangle,
|
||
/*isConstant=*/true, false, false);
|
||
|
||
// insert into the vtbl map
|
||
interfaceVtblMap.insert({{b->sym, interfaces_index}, gvar});
|
||
|
||
if (!define)
|
||
define = defineOnDeclare(aggrdecl, /*isFunction=*/false);
|
||
}
|
||
|
||
if (define && !gvar->hasInitializer()) {
|
||
auto init = getInterfaceVtblInit(b, interfaces_index);
|
||
defineGlobal(gvar, init, aggrdecl);
|
||
}
|
||
|
||
return gvar;
|
||
}
|
||
|
||
LLConstant *IrClass::getInterfaceVtblInit(BaseClass *b,
|
||
size_t interfaces_index) {
|
||
IF_LOG Logger::println(
|
||
"Defining vtbl for implementation of interface %s in class %s",
|
||
b->sym->toPrettyChars(), aggrdecl->toPrettyChars());
|
||
LOG_SCOPE;
|
||
|
||
ClassDeclaration *cd = aggrdecl->isClassDeclaration();
|
||
assert(cd && "not a class aggregate");
|
||
|
||
FuncDeclarations vtbl_array;
|
||
const bool new_instance = b->sym == cd;
|
||
b->fillVtbl(cd, &vtbl_array, new_instance);
|
||
|
||
std::vector<llvm::Constant *> constants;
|
||
constants.reserve(vtbl_array.length);
|
||
|
||
char thunkPrefix[16];
|
||
snprintf(thunkPrefix, 16, "Thn%d_", b->offset);
|
||
|
||
if (!b->sym->isCPPinterface()) { // skip interface info for CPP interfaces
|
||
if (!suppressTypeInfo()) {
|
||
// index into the interfaces array
|
||
llvm::Constant *idxs[2] = {DtoConstSize_t(0),
|
||
DtoConstSize_t(interfaces_index)};
|
||
|
||
llvm::GlobalVariable *interfaceInfosZ = getInterfaceArraySymbol();
|
||
llvm::Constant *c = llvm::ConstantExpr::getGetElementPtr(
|
||
interfaceInfosZ->getValueType(), interfaceInfosZ, idxs, true);
|
||
|
||
constants.push_back(c);
|
||
} else {
|
||
// use null if there are no TypeInfos
|
||
constants.push_back(getNullPtr());
|
||
}
|
||
}
|
||
|
||
// add virtual function pointers
|
||
size_t n = vtbl_array.length;
|
||
for (size_t i = b->sym->vtblOffset(); i < n; i++) {
|
||
FuncDeclaration *fd = vtbl_array[i];
|
||
if (!fd) {
|
||
// FIXME
|
||
// why is this null?
|
||
// happens for mini/s.d
|
||
constants.push_back(getNullPtr());
|
||
continue;
|
||
}
|
||
|
||
assert((!fd->isAbstract() || fd->fbody) &&
|
||
"null symbol in interface implementation vtable");
|
||
|
||
DtoResolveFunction(fd);
|
||
assert(isIrFuncCreated(fd) && "invalid vtbl function");
|
||
|
||
IrFunction *irFunc = getIrFunc(fd);
|
||
|
||
assert(irFunc->irFty.arg_this);
|
||
|
||
int thunkOffset = b->offset;
|
||
if (fd->interfaceVirtual)
|
||
thunkOffset -= fd->interfaceVirtual->offset;
|
||
if (thunkOffset == 0) {
|
||
constants.push_back(irFunc->getLLVMCallee());
|
||
continue;
|
||
}
|
||
|
||
// Create the thunk function if it does not already exist in this
|
||
// module.
|
||
OutBuffer nameBuf;
|
||
const auto mangledTargetName = mangleExact(fd);
|
||
nameBuf.write(mangledTargetName, 2);
|
||
nameBuf.writestring(thunkPrefix);
|
||
nameBuf.writestring(mangledTargetName + 2);
|
||
|
||
const auto thunkIRMangle =
|
||
getIRMangledFuncName(nameBuf.peekChars(), fd->resolvedLinkage());
|
||
|
||
llvm::Function *thunk = gIR->module.getFunction(thunkIRMangle);
|
||
if (!thunk) {
|
||
const LinkageWithCOMDAT lwc(LLGlobalValue::LinkOnceODRLinkage,
|
||
needsCOMDAT());
|
||
const auto callee = irFunc->getLLVMCallee();
|
||
thunk = LLFunction::Create(
|
||
callee->getFunctionType(), lwc.first,
|
||
thunkIRMangle, &gIR->module);
|
||
setLinkage(lwc, thunk);
|
||
thunk->copyAttributesFrom(callee);
|
||
|
||
// Thunks themselves don't have an identity, only the target function has.
|
||
thunk->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
|
||
|
||
// thunks don't need exception handling themselves
|
||
thunk->setPersonalityFn(nullptr);
|
||
|
||
// It is necessary to add debug information to the thunk in case it is
|
||
// subject to inlining. See https://llvm.org/bugs/show_bug.cgi?id=26833
|
||
IF_LOG Logger::println("Doing function body for thunk to: %s",
|
||
fd->toChars());
|
||
|
||
// Create a dummy FuncDeclaration with enough information to satisfy the
|
||
// DIBuilder
|
||
FuncDeclaration *thunkFd = reinterpret_cast<FuncDeclaration *>(
|
||
memcpy(new char[sizeof(FuncDeclaration)], (void *)fd,
|
||
sizeof(FuncDeclaration)));
|
||
thunkFd->ir = new IrDsymbol();
|
||
auto thunkFunc = getIrFunc(thunkFd, true); // create the IrFunction
|
||
thunkFunc->setLLVMFunc(thunk);
|
||
thunkFunc->type = irFunc->type;
|
||
gIR->funcGenStates.emplace_back(new FuncGenState(*thunkFunc, *gIR));
|
||
|
||
// debug info
|
||
thunkFunc->diSubprogram = nullptr;
|
||
thunkFunc->diSubprogram = gIR->DBuilder.EmitThunk(thunk, thunkFd);
|
||
|
||
// create entry and end blocks
|
||
llvm::BasicBlock *beginbb =
|
||
llvm::BasicBlock::Create(gIR->context(), "", thunk);
|
||
|
||
// set up the IRBuilder scope for the thunk
|
||
const auto savedIRBuilderScope = gIR->setInsertPoint(beginbb);
|
||
gIR->DBuilder.EmitFuncStart(thunkFd);
|
||
|
||
// Copy the function parameters, so later we can pass them to the
|
||
// real function and set their names from the original function (the
|
||
// latter being just for IR readablilty).
|
||
std::vector<LLValue *> args;
|
||
llvm::Function::arg_iterator thunkArg = thunk->arg_begin();
|
||
llvm::Function::arg_iterator origArg = callee->arg_begin();
|
||
for (; thunkArg != thunk->arg_end(); ++thunkArg, ++origArg) {
|
||
thunkArg->setName(origArg->getName());
|
||
args.push_back(&(*thunkArg));
|
||
}
|
||
|
||
// cast 'this' to Object
|
||
const int thisArgIndex =
|
||
(!irFunc->irFty.arg_sret || gABI->passThisBeforeSret(irFunc->type))
|
||
? 0
|
||
: 1;
|
||
LLValue *&thisArg = args[thisArgIndex];
|
||
thisArg = DtoGEP1(getI8Type(), thisArg, DtoConstInt(-thunkOffset));
|
||
|
||
// all calls that might be subject to inlining into a caller with debug
|
||
// info should have debug info, too
|
||
gIR->DBuilder.EmitStopPoint(fd->loc);
|
||
|
||
// call the real vtbl function.
|
||
llvm::CallInst *call = gIR->ir->CreateCall(callee, args);
|
||
call->setCallingConv(callee->getCallingConv());
|
||
call->setAttributes(callee->getAttributes());
|
||
call->setTailCallKind(callee->isVarArg() ? llvm::CallInst::TCK_MustTail
|
||
: llvm::CallInst::TCK_Tail);
|
||
|
||
// return from the thunk
|
||
if (thunk->getReturnType() == LLType::getVoidTy(gIR->context())) {
|
||
llvm::ReturnInst::Create(gIR->context(), beginbb);
|
||
} else {
|
||
llvm::ReturnInst::Create(gIR->context(), call, beginbb);
|
||
}
|
||
|
||
gIR->funcGenStates.pop_back();
|
||
}
|
||
|
||
constants.push_back(thunk);
|
||
}
|
||
|
||
// build the vtbl constant
|
||
llvm::Constant *vtbl_constant = LLConstantArray::get(
|
||
LLArrayType::get(getOpaquePtrType(), constants.size()), constants);
|
||
|
||
return vtbl_constant;
|
||
}
|
||
|
||
void IrClass::defineInterfaceVtbls() {
|
||
const size_t n = interfacesWithVtbls.size();
|
||
assert(n == getIrType(type)->isClass()->getNumInterfaceVtbls() &&
|
||
"inconsistent number of interface vtables in this class");
|
||
|
||
for (size_t i = 0; i < n; ++i) {
|
||
auto baseClass = interfacesWithVtbls[i];
|
||
getInterfaceVtblSymbol(baseClass, i, /*define=*/true);
|
||
}
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
|
||
LLConstant *IrClass::getClassInfoInterfaces() {
|
||
IF_LOG Logger::println("Building ClassInfo.interfaces");
|
||
LOG_SCOPE;
|
||
|
||
ClassDeclaration *cd = aggrdecl->isClassDeclaration();
|
||
assert(cd);
|
||
|
||
size_t n = interfacesWithVtbls.size();
|
||
assert(getIrType(type)->isClass()->getNumInterfaceVtbls() == n &&
|
||
"inconsistent number of interface vtables in this class");
|
||
|
||
Type *interfacesArrayType = getInterfacesArrayType();
|
||
|
||
if (n == 0) {
|
||
return getNullValue(DtoType(interfacesArrayType));
|
||
}
|
||
|
||
// Build array of:
|
||
//
|
||
// struct Interface
|
||
// {
|
||
// ClassInfo classinfo;
|
||
// void*[] vtbl;
|
||
// ptrdiff_t offset;
|
||
// }
|
||
|
||
LLSmallVector<LLConstant *, 6> constants;
|
||
constants.reserve(cd->vtblInterfaces->length);
|
||
|
||
LLStructType *interface_type =
|
||
isaStruct(DtoType(interfacesArrayType->nextOf()));
|
||
assert(interface_type);
|
||
|
||
for (size_t i = 0; i < n; ++i) {
|
||
BaseClass *it = interfacesWithVtbls[i];
|
||
|
||
IF_LOG Logger::println("Adding interface %s", it->sym->toPrettyChars());
|
||
|
||
IrClass *irinter = getIrAggr(it->sym);
|
||
assert(irinter && "interface has null IrStruct");
|
||
IrTypeClass *itc = getIrType(irinter->type)->isClass();
|
||
assert(itc && "null interface IrTypeClass");
|
||
|
||
// classinfo
|
||
LLConstant *ci = irinter->getClassInfoSymbol();
|
||
|
||
// vtbl
|
||
LLConstant *vtb;
|
||
// interface get a null
|
||
if (cd->isInterfaceDeclaration()) {
|
||
vtb = DtoConstSlice(DtoConstSize_t(0), getNullPtr());
|
||
} else {
|
||
vtb = getInterfaceVtblSymbol(it, i);
|
||
auto vtblSize = itc->getVtblType()->getNumContainedTypes();
|
||
vtb = DtoConstSlice(DtoConstSize_t(vtblSize), vtb);
|
||
}
|
||
|
||
// offset
|
||
LLConstant *off = DtoConstSize_t(it->offset);
|
||
|
||
// create Interface struct
|
||
LLConstant *inits[3] = {ci, vtb, off};
|
||
LLConstant *entry = LLConstantStruct::get(interface_type, inits);
|
||
constants.push_back(entry);
|
||
}
|
||
|
||
// create Interface[N]
|
||
LLArrayType *array_type = llvm::ArrayType::get(interface_type, n);
|
||
|
||
// create and apply initializer
|
||
LLConstant *arr = LLConstantArray::get(array_type, constants);
|
||
auto ciarr = getInterfaceArraySymbol();
|
||
defineGlobal(ciarr, arr, cd);
|
||
|
||
// return null, only baseclass provide interfaces
|
||
if (cd->vtblInterfaces->length == 0) {
|
||
return getNullValue(DtoType(interfacesArrayType));
|
||
}
|
||
|
||
// only the interface explicitly implemented by this class
|
||
// (not super classes) should show in ClassInfo
|
||
LLConstant *idxs[2] = {DtoConstSize_t(0),
|
||
DtoConstSize_t(n - cd->vtblInterfaces->length)};
|
||
|
||
LLConstant *ptr = llvm::ConstantExpr::getGetElementPtr(ciarr->getValueType(),
|
||
ciarr, idxs, true);
|
||
|
||
// return as a slice
|
||
return DtoConstSlice(DtoConstSize_t(cd->vtblInterfaces->length), ptr);
|
||
}
|