ldc/ir/irclass.cpp
Martin 16d15e01cb Use opaque [N x i8*] for vtables
This fixes the forward-referencing issue #1741.
2016-11-21 21:54:53 +01:00

579 lines
19 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//===-- irclass.cpp -------------------------------------------------------===//
//
// LDC the LLVM D compiler
//
// This file is distributed under the BSD-style LDC license. See the LICENSE
// file for details.
//
//===----------------------------------------------------------------------===//
#include "llvm/IR/Constants.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/ADT/SmallString.h"
#ifndef NDEBUG
#include "llvm/Support/raw_ostream.h"
#endif
#include "aggregate.h"
#include "declaration.h"
#include "hdrgen.h" // for parametersTypeToChars()
#include "mtype.h"
#include "target.h"
#include "gen/funcgenstate.h"
#include "gen/irstate.h"
#include "gen/logger.h"
#include "gen/tollvm.h"
#include "gen/llvmhelpers.h"
#include "gen/arrays.h"
#include "gen/metadata.h"
#include "gen/runtime.h"
#include "gen/functions.h"
#include "gen/abi.h"
#include "gen/mangling.h"
#include "ir/iraggr.h"
#include "ir/irfunction.h"
#include "ir/irtypeclass.h"
//////////////////////////////////////////////////////////////////////////////
extern LLConstant *DtoDefineClassInfo(ClassDeclaration *cd);
//////////////////////////////////////////////////////////////////////////////
LLGlobalVariable *IrAggr::getVtblSymbol() {
if (vtbl) {
return vtbl;
}
// create the vtblZ symbol
auto initname = getMangledVTableSymbolName(aggrdecl);
LLType *vtblTy = stripModifiers(type)->ctype->isClass()->getVtblType();
vtbl =
getOrCreateGlobal(aggrdecl->loc, gIR->module, vtblTy, true,
llvm::GlobalValue::ExternalLinkage, nullptr, initname);
return vtbl;
}
//////////////////////////////////////////////////////////////////////////////
LLGlobalVariable *IrAggr::getClassInfoSymbol() {
if (classInfo) {
return classInfo;
}
// create the ClassZ / InterfaceZ symbol
std::string initname = getMangledClassInfoSymbolName(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.
ClassDeclaration *cinfo = Type::typeinfoclass;
DtoType(cinfo->type);
IrTypeClass *tc = stripModifiers(cinfo->type)->ctype->isClass();
assert(tc && "invalid ClassInfo type");
// classinfos cannot be constants since they're used as locks for synchronized
classInfo = getOrCreateGlobal(
aggrdecl->loc, gIR->module, tc->getMemoryLLType(), false,
llvm::GlobalValue::ExternalLinkage, nullptr, initname);
// Generate some metadata on this ClassInfo if it's for a class.
ClassDeclaration *classdecl = aggrdecl->isClassDeclaration();
if (classdecl && !aggrdecl->isInterfaceDeclaration()) {
// Gather information
LLType *type = DtoType(aggrdecl->type);
LLType *bodyType = llvm::cast<LLPointerType>(type)->getElementType();
bool hasDestructor = (classdecl->dtor != nullptr);
bool hasCustomDelete = (classdecl->aggDelete != nullptr);
// Construct the fields
#if LDC_LLVM_VER >= 306
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));
mdVals[CD_CustomDelete] = llvm::ConstantAsMetadata::get(
LLConstantInt::get(LLType::getInt1Ty(gIR->context()), hasCustomDelete));
#else
MDNodeField *mdVals[CD_NumFields];
mdVals[CD_BodyType] = llvm::UndefValue::get(bodyType);
mdVals[CD_Finalize] =
LLConstantInt::get(LLType::getInt1Ty(gIR->context()), hasDestructor);
mdVals[CD_CustomDelete] =
LLConstantInt::get(LLType::getInt1Ty(gIR->context()), hasCustomDelete);
#endif
// Construct the metadata and insert it into the module.
llvm::SmallString<64> name;
llvm::NamedMDNode *node = gIR->module.getOrInsertNamedMetadata(
llvm::Twine(CD_PREFIX, initname).toStringRef(name));
node->addOperand(llvm::MDNode::get(
gIR->context(), llvm::makeArrayRef(mdVals, CD_NumFields)));
}
return classInfo;
}
//////////////////////////////////////////////////////////////////////////////
LLGlobalVariable *IrAggr::getInterfaceArraySymbol() {
if (classInterfacesArray) {
return classInterfacesArray;
}
ClassDeclaration *cd = aggrdecl->isClassDeclaration();
size_t n = stripModifiers(type)->ctype->isClass()->getNumInterfaceVtbls();
assert(n > 0 && "getting ClassInfo.interfaces storage symbol, but we "
"don't implement any interfaces");
LLType *InterfaceTy = DtoType(Type::typeinfoclass->fields[3]->type->nextOf());
// create Interface[N]
LLArrayType *array_type = llvm::ArrayType::get(InterfaceTy, n);
// put it in a global
std::string name("_D");
name.append(mangle(cd));
name.append("16__interfaceInfosZ");
// We keep this as external for now and only consider template linkage if
// we emit the initializer later.
classInterfacesArray =
getOrCreateGlobal(cd->loc, gIR->module, array_type, true,
llvm::GlobalValue::ExternalLinkage, nullptr, name);
return classInterfacesArray;
}
//////////////////////////////////////////////////////////////////////////////
LLConstant *IrAggr::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.dim);
const auto voidPtrType = getVoidPtrType();
// start with the classinfo
llvm::Constant *c;
if (!cd->isCPPclass()) {
c = getClassInfoSymbol();
c = DtoBitCast(c, voidPtrType);
constants.push_back(c);
}
// add virtual function pointers
size_t n = cd->vtbl.dim;
for (size_t i = cd->vtblOffset(); i < n; i++) {
Dsymbol *dsym = static_cast<Dsymbol *>(cd->vtbl.data[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 = getNullValue(getPtrToType(DtoFunctionType(fd)));
} else {
DtoResolveFunction(fd);
assert(isIrFuncCreated(fd) && "invalid vtbl function");
c = getIrFunc(fd)->func;
if (cd->isFuncHidden(fd)) {
// fd is hidden from the view of this class. If fd overlaps with any
// function in the vtbl[], issue error.
for (size_t j = 1; j < n; j++) {
if (j == i) {
continue;
}
auto fd2 =
static_cast<Dsymbol *>(cd->vtbl.data[j])->isFuncDeclaration();
if (!fd2->ident->equals(fd->ident)) {
continue;
}
if (fd->leastAsSpecialized(fd2) || fd2->leastAsSpecialized(fd)) {
TypeFunction *tf = static_cast<TypeFunction *>(fd->type);
if (tf->ty == Tfunction) {
cd->error("use of %s%s is hidden by %s; use 'alias %s = %s.%s;' "
"to introduce base class overload set",
fd->toPrettyChars(),
parametersTypeToChars(tf->parameters, tf->varargs),
cd->toChars(),
fd->toChars(), fd->parent->toChars(), fd->toChars());
} else {
cd->error("use of %s is hidden by %s", fd->toPrettyChars(),
cd->toChars());
}
fatal();
break;
}
}
}
}
constants.push_back(DtoBitCast(c, voidPtrType));
}
// build the constant array
LLArrayType *vtblTy = LLArrayType::get(voidPtrType, constants.size());
constVtbl = LLConstantArray::get(vtblTy, constants);
return constVtbl;
}
//////////////////////////////////////////////////////////////////////////////
LLConstant *IrAggr::getClassInfoInit() {
if (constClassInfo) {
return constClassInfo;
}
constClassInfo = DtoDefineClassInfo(aggrdecl->isClassDeclaration());
return constClassInfo;
}
//////////////////////////////////////////////////////////////////////////////
llvm::GlobalVariable *IrAggr::getInterfaceVtbl(BaseClass *b, bool new_instance,
size_t interfaces_index) {
auto it = interfaceVtblMap.find({b->sym, interfaces_index});
if (it != interfaceVtblMap.end()) {
return it->second;
}
IF_LOG Logger::println(
"Building 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;
b->fillVtbl(cd, &vtbl_array, new_instance);
std::vector<llvm::Constant *> constants;
constants.reserve(vtbl_array.dim);
const auto voidPtrTy = getVoidPtrType();
if (!b->sym->isCPPinterface()) { // skip interface info for CPP interfaces
// 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(
#if LDC_LLVM_VER >= 307
isaPointer(interfaceInfosZ)->getElementType(),
#endif
interfaceInfosZ, idxs, true);
constants.push_back(DtoBitCast(c, voidPtrTy));
}
// Thunk prefix
char thunkPrefix[16];
int thunkLen = sprintf(thunkPrefix, "Thn%d_", b->offset);
char thunkPrefixLen[16];
sprintf(thunkPrefixLen, "%d", thunkLen);
// add virtual function pointers
size_t n = vtbl_array.dim;
for (size_t i = b->sym->vtblOffset(); i < n; i++) {
Dsymbol *dsym = static_cast<Dsymbol *>(vtbl_array.data[i]);
if (dsym == nullptr) {
// FIXME
// why is this null?
// happens for mini/s.d
constants.push_back(getNullValue(voidPtrTy));
continue;
}
FuncDeclaration *fd = dsym->isFuncDeclaration();
assert(fd && "vtbl entry not a function");
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(DtoBitCast(irFunc->func, voidPtrTy));
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 char *thunkName = nameBuf.extractString();
llvm::Function *thunk = gIR->module.getFunction(thunkName);
if (!thunk) {
const LinkageWithCOMDAT lwc(LLGlobalValue::LinkOnceODRLinkage,
supportsCOMDAT());
thunk = LLFunction::Create(
isaFunction(irFunc->func->getType()->getContainedType(0)), lwc.first,
thunkName, &gIR->module);
setLinkage(lwc, thunk);
thunk->copyAttributesFrom(irFunc->func);
// Thunks themselves don't have an identity, only the target
// function has.
#if LDC_LLVM_VER >= 309
thunk->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
#else
thunk->setUnnamedAddr(true);
#endif
#if LDC_LLVM_VER >= 307
// thunks don't need exception handling themselves
thunk->setPersonalityFn(nullptr);
#endif
// 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->func = thunk;
thunkFunc->type = irFunc->type;
gIR->funcGenStates.emplace_back(new FuncGenState(*thunkFunc, *gIR));
// debug info
thunkFunc->diSubprogram = gIR->DBuilder.EmitThunk(thunk, thunkFd);
// create entry and end blocks
llvm::BasicBlock *beginbb =
llvm::BasicBlock::Create(gIR->context(), "", thunk);
gIR->scopes.push_back(IRScope(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 = irFunc->func->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];
LLType *targetThisType = thisArg->getType();
thisArg = DtoBitCast(thisArg, getVoidPtrType());
thisArg = DtoGEP1(thisArg, DtoConstInt(-thunkOffset), true);
thisArg = DtoBitCast(thisArg, targetThisType);
// 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(irFunc->func, args);
call->setCallingConv(irFunc->func->getCallingConv());
call->setTailCallKind(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->DBuilder.EmitFuncEnd(thunkFd);
// clean up
gIR->scopes.pop_back();
gIR->funcGenStates.pop_back();
}
constants.push_back(DtoBitCast(thunk, voidPtrTy));
}
// build the vtbl constant
llvm::Constant *vtbl_constant = LLConstantArray::get(
LLArrayType::get(voidPtrTy, constants.size()), constants);
std::string mangledName("_D");
mangledName.append(mangle(cd));
mangledName.append("11__interface");
mangledName.append(mangle(b->sym));
mangledName.append(thunkPrefixLen);
mangledName.append(thunkPrefix);
mangledName.append("6__vtblZ");
const auto lwc = DtoLinkage(cd);
LLGlobalVariable *GV =
getOrCreateGlobal(cd->loc, gIR->module, vtbl_constant->getType(), true,
lwc.first, vtbl_constant, mangledName);
setLinkage(lwc, GV);
// insert into the vtbl map
interfaceVtblMap.insert({{b->sym, interfaces_index}, GV});
return GV;
}
bool IrAggr::isPacked() const {
return static_cast<IrTypeAggr *>(type->ctype)->packed;
}
//////////////////////////////////////////////////////////////////////////////
LLConstant *IrAggr::getClassInfoInterfaces() {
IF_LOG Logger::println("Building ClassInfo.interfaces");
LOG_SCOPE;
ClassDeclaration *cd = aggrdecl->isClassDeclaration();
assert(cd);
size_t n = interfacesWithVtbls.size();
assert(stripModifiers(type)->ctype->isClass()->getNumInterfaceVtbls() == n &&
"inconsistent number of interface vtables in this class");
VarDeclaration *interfaces_idx = Type::typeinfoclass->fields[3];
if (n == 0) {
return getNullValue(DtoType(interfaces_idx->type));
}
// Build array of:
//
// struct Interface
// {
// ClassInfo classinfo;
// void*[] vtbl;
// ptrdiff_t offset;
// }
LLSmallVector<LLConstant *, 6> constants;
constants.reserve(cd->vtblInterfaces->dim);
LLType *classinfo_type = DtoType(Type::typeinfoclass->type);
LLType *voidptrptr_type = DtoType(Type::tvoid->pointerTo()->pointerTo());
VarDeclaration *idx = Type::typeinfoclass->fields[3];
LLStructType *interface_type = isaStruct(DtoType(idx->type->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());
IrAggr *irinter = getIrAggr(it->sym);
assert(irinter && "interface has null IrStruct");
IrTypeClass *itc = stripModifiers(irinter->type)->ctype->isClass();
assert(itc && "null interface IrTypeClass");
// classinfo
LLConstant *ci = irinter->getClassInfoSymbol();
ci = DtoBitCast(ci, classinfo_type);
// vtbl
LLConstant *vtb;
// interface get a null
if (cd->isInterfaceDeclaration()) {
vtb = DtoConstSlice(DtoConstSize_t(0), getNullValue(voidptrptr_type));
} else {
auto itv = interfaceVtblMap.find({it->sym, i});
assert(itv != interfaceVtblMap.end() && "interface vtbl not found");
vtb = itv->second;
vtb = DtoBitCast(vtb, voidptrptr_type);
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, llvm::makeArrayRef(inits, 3));
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();
ciarr->setInitializer(arr);
setLinkage(cd, ciarr);
// return null, only baseclass provide interfaces
if (cd->vtblInterfaces->dim == 0) {
return getNullValue(DtoType(interfaces_idx->type));
}
// 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->dim)};
LLConstant *ptr = llvm::ConstantExpr::getGetElementPtr(
#if LDC_LLVM_VER >= 307
isaPointer(ciarr)->getElementType(),
#endif
ciarr, idxs, true);
// return as a slice
return DtoConstSlice(DtoConstSize_t(cd->vtblInterfaces->dim), ptr);
}
//////////////////////////////////////////////////////////////////////////////
void IrAggr::initializeInterface() {
InterfaceDeclaration *base = aggrdecl->isInterfaceDeclaration();
assert(base && "not interface");
// has interface vtbls?
if (!base->vtblInterfaces) {
return;
}
for (auto bc : *base->vtblInterfaces) {
// add to the interface list
interfacesWithVtbls.push_back(bc);
}
}
//////////////////////////////////////////////////////////////////////////////