Permalink
Fetching contributors…
Cannot retrieve contributors at this time
578 lines (465 sloc) 19 KB
//===-- 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
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));
// 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
OutBuffer mangledName;
mangledName.writestring("_D");
mangleToBuffer(cd, &mangledName);
mangledName.writestring("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, mangledName.peekString());
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 = 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 = getNullValue(voidPtrType);
} 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 (!fd->functionSemantic()) {
fd->error("failed to infer return type for vtbl initializer");
fatal();
}
}
DtoResolveFunction(fd);
assert(isIrFuncCreated(fd) && "invalid vtbl function");
c = DtoBitCast(DtoCallee(fd), voidPtrType);
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 = cd->vtblOffset(); j < n; j++) {
if (j == i) {
continue;
}
auto fd2 = cd->vtbl[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(c);
}
// 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(
isaPointer(interfaceInfosZ)->getElementType(),
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->getLLVMCallee(), 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());
const auto callee = irFunc->getLLVMCallee();
thunk = LLFunction::Create(
isaFunction(callee->getType()->getContainedType(0)), lwc.first,
thunkName, &gIR->module);
setLinkage(lwc, thunk);
thunk->copyAttributesFrom(callee);
// 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
// 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 = 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 = 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];
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(callee, args);
call->setCallingConv(irFunc->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);
OutBuffer mangledName;
mangledName.writestring("_D");
mangleToBuffer(cd, &mangledName);
mangledName.writestring("11__interface");
mangleToBuffer(b->sym, &mangledName);
mangledName.writestring(thunkPrefixLen);
mangledName.writestring(thunkPrefix);
mangledName.writestring("6__vtblZ");
const auto lwc = DtoLinkage(cd);
LLGlobalVariable *GV =
getOrCreateGlobal(cd->loc, gIR->module, vtbl_constant->getType(), true,
lwc.first, vtbl_constant, mangledName.peekString());
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(
isaPointer(ciarr)->getElementType(), 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);
}
}
//////////////////////////////////////////////////////////////////////////////