mirror of
https://github.com/xomboverlord/ldc.git
synced 2026-01-12 02:43:14 +01:00
(b) don't override the delete operator (on top of the regular conditions for stack allocation that also apply to arrays, structs, etc.). The "no destructor" clause is not strictly necessary, but calling them at the right time would be tricky to say the least; it would involve, among other things, "manually" inserting a try-finally block around anything that might throw exceptions not caught in the current function. Note: objects with custom new operators are automatically ignored because they don't use the regular allocation runtime call, so there's no need to pay special attention to them.
526 lines
16 KiB
C++
526 lines
16 KiB
C++
#include "llvm/Constants.h"
|
|
#include "llvm/DerivedTypes.h"
|
|
|
|
#include "aggregate.h"
|
|
#include "declaration.h"
|
|
#include "mtype.h"
|
|
|
|
#include "gen/irstate.h"
|
|
#include "gen/logger.h"
|
|
#include "gen/tollvm.h"
|
|
#include "gen/llvmhelpers.h"
|
|
#include "gen/utils.h"
|
|
#include "gen/arrays.h"
|
|
#include "gen/metadata.h"
|
|
|
|
#include "ir/irstruct.h"
|
|
#include "ir/irtypeclass.h"
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
extern LLConstant* get_default_initializer(VarDeclaration* vd, Initializer* init);
|
|
extern size_t add_zeros(std::vector<llvm::Constant*>& constants, size_t diff);
|
|
|
|
extern LLConstant* DtoDefineClassInfo(ClassDeclaration* cd);
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
LLGlobalVariable * IrStruct::getVtblSymbol()
|
|
{
|
|
if (vtbl)
|
|
return vtbl;
|
|
|
|
// create the initZ symbol
|
|
std::string initname("_D");
|
|
initname.append(aggrdecl->mangle());
|
|
initname.append("6__vtblZ");
|
|
|
|
llvm::GlobalValue::LinkageTypes _linkage = DtoExternalLinkage(aggrdecl);
|
|
|
|
const LLType* vtblTy = type->irtype->isClass()->getVtbl();
|
|
|
|
vtbl = new llvm::GlobalVariable(
|
|
vtblTy, true, _linkage, NULL, initname, gIR->module);
|
|
|
|
return vtbl;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
LLGlobalVariable * IrStruct::getClassInfoSymbol()
|
|
{
|
|
if (classInfo)
|
|
return classInfo;
|
|
|
|
// create the initZ symbol
|
|
std::string initname("_D");
|
|
initname.append(aggrdecl->mangle());
|
|
if (aggrdecl->isInterfaceDeclaration())
|
|
initname.append("11__InterfaceZ");
|
|
else
|
|
initname.append("7__ClassZ");
|
|
|
|
llvm::GlobalValue::LinkageTypes _linkage = DtoExternalLinkage(aggrdecl);
|
|
|
|
ClassDeclaration* cinfo = ClassDeclaration::classinfo;
|
|
DtoType(cinfo->type);
|
|
IrTypeClass* tc = cinfo->type->irtype->isClass();
|
|
assert(tc && "invalid ClassInfo type");
|
|
|
|
// classinfos cannot be constants since they're used a locks for synchronized
|
|
classInfo = new llvm::GlobalVariable(
|
|
tc->getPA().get(), false, _linkage, NULL, initname, gIR->module);
|
|
|
|
#ifdef USE_METADATA
|
|
// Generate some metadata on this ClassInfo if it's for a class.
|
|
|
|
ClassDeclaration* classdecl = aggrdecl->isClassDeclaration();
|
|
if (classdecl && !aggrdecl->isInterfaceDeclaration()) {
|
|
// Gather information
|
|
const LLType* type = DtoType(aggrdecl->type);
|
|
const LLType* bodyType = llvm::cast<LLPointerType>(type)->getElementType();
|
|
bool hasDestructor = (classdecl->dtor != NULL);
|
|
bool hasCustomDelete = (classdecl->aggDelete != NULL);
|
|
// Construct the fields
|
|
LLConstant* mdVals[CD_NumFields];
|
|
mdVals[CD_BodyType] = llvm::UndefValue::get(bodyType);
|
|
mdVals[CD_Finalize] = LLConstantInt::get(LLType::Int1Ty, hasDestructor);
|
|
mdVals[CD_CustomDelete] = LLConstantInt::get(LLType::Int1Ty, hasCustomDelete);
|
|
// Construct the metadata
|
|
llvm::MDNode* metadata = llvm::MDNode::get(mdVals, CD_NumFields);
|
|
// Insert it into the module
|
|
new llvm::GlobalVariable(metadata->getType(), true,
|
|
METADATA_LINKAGE_TYPE, metadata, CD_PREFIX + initname, gIR->module);
|
|
}
|
|
#endif
|
|
|
|
return classInfo;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
LLGlobalVariable * IrStruct::getInterfaceArraySymbol()
|
|
{
|
|
if (classInterfacesArray)
|
|
return classInterfacesArray;
|
|
|
|
ClassDeclaration* cd = aggrdecl->isClassDeclaration();
|
|
|
|
size_t n = type->irtype->isClass()->getNumInterfaceVtbls();
|
|
assert(n > 0 && "getting ClassInfo.interfaces storage symbol, but we "
|
|
"don't implement any interfaces");
|
|
|
|
VarDeclarationIter idx(ClassDeclaration::classinfo->fields, 3);
|
|
const llvm::Type* InterfaceTy = DtoType(idx->type->next);
|
|
|
|
// create Interface[N]
|
|
const llvm::ArrayType* array_type = llvm::ArrayType::get(InterfaceTy,n);
|
|
|
|
// put it in a global
|
|
std::string name("_D");
|
|
name.append(cd->mangle());
|
|
name.append("16__interfaceInfosZ");
|
|
classInterfacesArray = new llvm::GlobalVariable(array_type, true, DtoLinkage(cd), NULL, name, classInfo);
|
|
|
|
return classInterfacesArray;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
LLConstant * IrStruct::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);
|
|
|
|
// start with the classinfo
|
|
llvm::Constant* c = getClassInfoSymbol();
|
|
c = DtoBitCast(c, DtoType(ClassDeclaration::classinfo->type));
|
|
constants.push_back(c);
|
|
|
|
// add virtual function pointers
|
|
size_t n = cd->vtbl.dim;
|
|
for (size_t i = 1; i < n; i++)
|
|
{
|
|
Dsymbol* dsym = (Dsymbol*)cd->vtbl.data[i];
|
|
assert(dsym && "null vtbl member");
|
|
|
|
FuncDeclaration* fd = dsym->isFuncDeclaration();
|
|
assert(fd && "vtbl entry not a function");
|
|
|
|
if (fd->isAbstract() && !fd->fbody)
|
|
{
|
|
c = getNullValue(DtoType(fd->type->pointerTo()));
|
|
}
|
|
else
|
|
{
|
|
fd->codegen(Type::sir);
|
|
assert(fd->ir.irFunc && "invalid vtbl function");
|
|
c = fd->ir.irFunc->func;
|
|
}
|
|
constants.push_back(c);
|
|
}
|
|
|
|
// build the constant struct
|
|
constVtbl = llvm::ConstantStruct::get(constants, false);
|
|
|
|
// sanity check
|
|
#if 0
|
|
IF_LOG Logger::cout() << "constVtbl type: " << *constVtbl->getType() << std::endl;
|
|
IF_LOG Logger::cout() << "vtbl type: " << *type->irtype->isClass()->getVtbl() << std::endl;
|
|
#endif
|
|
|
|
assert(constVtbl->getType() == type->irtype->isClass()->getVtbl() &&
|
|
"vtbl initializer type mismatch");
|
|
|
|
return constVtbl;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
LLConstant * IrStruct::getClassInfoInit()
|
|
{
|
|
if (constClassInfo)
|
|
return constClassInfo;
|
|
constClassInfo = DtoDefineClassInfo(aggrdecl->isClassDeclaration());
|
|
return constClassInfo;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
void IrStruct::addBaseClassInits(
|
|
std::vector<llvm::Constant*>& constants,
|
|
ClassDeclaration* base,
|
|
size_t& offset,
|
|
size_t& field_index)
|
|
{
|
|
if (base->baseClass)
|
|
{
|
|
addBaseClassInits(constants, base->baseClass, offset, field_index);
|
|
}
|
|
|
|
IrTypeClass* tc = base->type->irtype->isClass();
|
|
assert(tc);
|
|
|
|
// go through fields
|
|
IrTypeAggr::iterator it;
|
|
for (it = tc->def_begin(); it != tc->def_end(); ++it)
|
|
{
|
|
VarDeclaration* vd = *it;
|
|
|
|
IF_LOG Logger::println("Adding default field %s %s (+%u)", vd->type->toChars(), vd->toChars(), vd->offset);
|
|
LOG_SCOPE;
|
|
|
|
assert(vd->offset >= offset && "default fields not sorted by offset");
|
|
|
|
// get next aligned offset for this type
|
|
size_t alignsize = vd->type->alignsize();
|
|
size_t alignedoffset = (offset + alignsize - 1) & ~(alignsize - 1);
|
|
|
|
// insert explicit padding?
|
|
if (alignedoffset < vd->offset)
|
|
{
|
|
add_zeros(constants, vd->offset - alignedoffset);
|
|
}
|
|
|
|
// add default type
|
|
constants.push_back(get_default_initializer(vd, vd->init));
|
|
|
|
// advance offset to right past this field
|
|
offset = vd->offset + vd->type->size();
|
|
}
|
|
|
|
// has interface vtbls?
|
|
if (base->vtblInterfaces && base->vtblInterfaces->dim > 0)
|
|
{
|
|
// false when it's not okay to use functions from super classes
|
|
bool newinsts = (base == aggrdecl->isClassDeclaration());
|
|
|
|
size_t inter_idx = interfacesWithVtbls.size();
|
|
|
|
offset = (offset + PTRSIZE - 1) & ~(PTRSIZE - 1);
|
|
|
|
ArrayIter<BaseClass> it2(*base->vtblInterfaces);
|
|
for (; !it2.done(); it2.next())
|
|
{
|
|
BaseClass* b = it2.get();
|
|
constants.push_back(getInterfaceVtbl(b, newinsts, inter_idx));
|
|
offset += PTRSIZE;
|
|
|
|
// add to the interface list
|
|
interfacesWithVtbls.push_back(b);
|
|
inter_idx++;
|
|
}
|
|
}
|
|
|
|
// tail padding?
|
|
if (offset < base->structsize)
|
|
{
|
|
add_zeros(constants, base->structsize - offset);
|
|
offset = base->structsize;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
LLConstant * IrStruct::createClassDefaultInitializer()
|
|
{
|
|
ClassDeclaration* cd = aggrdecl->isClassDeclaration();
|
|
assert(cd && "invalid class aggregate");
|
|
|
|
IF_LOG Logger::println("Building class default initializer %s @ %s", cd->toPrettyChars(), cd->loc.toChars());
|
|
LOG_SCOPE;
|
|
IF_LOG Logger::println("Instance size: %u", cd->structsize);
|
|
|
|
// find the fields that contribute to the default initializer.
|
|
// these will define the default type.
|
|
|
|
std::vector<llvm::Constant*> constants;
|
|
constants.reserve(32);
|
|
|
|
// add vtbl
|
|
constants.push_back(getVtblSymbol());
|
|
// add monitor
|
|
constants.push_back(getNullValue(DtoType(Type::tvoid->pointerTo())));
|
|
|
|
// we start right after the vtbl and monitor
|
|
size_t offset = PTRSIZE * 2;
|
|
size_t field_index = 2;
|
|
|
|
// add data members recursively
|
|
addBaseClassInits(constants, cd, offset, field_index);
|
|
|
|
// build the constant
|
|
llvm::Constant* definit = llvm::ConstantStruct::get(constants, false);
|
|
|
|
return definit;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
llvm::GlobalVariable * IrStruct::getInterfaceVtbl(BaseClass * b, bool new_instance, size_t interfaces_index)
|
|
{
|
|
ClassGlobalMap::iterator it = interfaceVtblMap.find(b->base);
|
|
if (it != interfaceVtblMap.end())
|
|
return it->second;
|
|
|
|
IF_LOG Logger::println("Building vtbl for implementation of interface %s in class %s",
|
|
b->base->toPrettyChars(), aggrdecl->toPrettyChars());
|
|
LOG_SCOPE;
|
|
|
|
ClassDeclaration* cd = aggrdecl->isClassDeclaration();
|
|
assert(cd && "not a class aggregate");
|
|
|
|
Array vtbl_array;
|
|
b->fillVtbl(cd, &vtbl_array, new_instance);
|
|
|
|
std::vector<llvm::Constant*> constants;
|
|
constants.reserve(vtbl_array.dim);
|
|
|
|
// start with the interface info
|
|
VarDeclarationIter interfaces_idx(ClassDeclaration::classinfo->fields, 3);
|
|
Type* first = interfaces_idx->type->next->pointerTo();
|
|
|
|
// index into the interfaces array
|
|
llvm::Constant* idxs[2] = {
|
|
DtoConstSize_t(0),
|
|
DtoConstSize_t(interfaces_index)
|
|
};
|
|
|
|
llvm::Constant* c = llvm::ConstantExpr::getGetElementPtr(
|
|
getInterfaceArraySymbol(), idxs, 2);
|
|
|
|
constants.push_back(c);
|
|
|
|
// add virtual function pointers
|
|
size_t n = vtbl_array.dim;
|
|
for (size_t i = 1; i < n; i++)
|
|
{
|
|
Dsymbol* dsym = (Dsymbol*)vtbl_array.data[i];
|
|
if (dsym == NULL)
|
|
{
|
|
// FIXME
|
|
// why is this null?
|
|
// happens for mini/s.d
|
|
constants.push_back(getNullValue(getVoidPtrType()));
|
|
continue;
|
|
}
|
|
|
|
FuncDeclaration* fd = dsym->isFuncDeclaration();
|
|
assert(fd && "vtbl entry not a function");
|
|
|
|
assert((!fd->isAbstract() || fd->fbody) &&
|
|
"null symbol in interface implementation vtable");
|
|
|
|
fd->codegen(Type::sir);
|
|
assert(fd->ir.irFunc && "invalid vtbl function");
|
|
|
|
constants.push_back(fd->ir.irFunc->func);
|
|
}
|
|
|
|
// build the vtbl constant
|
|
llvm::Constant* vtbl_constant = llvm::ConstantStruct::get(constants, false);
|
|
|
|
// create the global variable to hold it
|
|
llvm::GlobalValue::LinkageTypes _linkage = DtoExternalLinkage(aggrdecl);
|
|
|
|
std::string mangle("_D");
|
|
mangle.append(cd->mangle());
|
|
mangle.append("11__interface");
|
|
mangle.append(b->base->mangle());
|
|
mangle.append("6__vtblZ");
|
|
|
|
llvm::GlobalVariable* GV = new llvm::GlobalVariable(
|
|
vtbl_constant->getType(),
|
|
true,
|
|
_linkage,
|
|
vtbl_constant,
|
|
mangle,
|
|
gIR->module
|
|
);
|
|
|
|
// insert into the vtbl map
|
|
interfaceVtblMap.insert(std::make_pair(b->base, GV));
|
|
|
|
return GV;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
LLConstant * IrStruct::getClassInfoInterfaces()
|
|
{
|
|
IF_LOG Logger::println("Building ClassInfo.interfaces");
|
|
LOG_SCOPE;
|
|
|
|
ClassDeclaration* cd = aggrdecl->isClassDeclaration();
|
|
assert(cd);
|
|
|
|
size_t n = interfacesWithVtbls.size();
|
|
assert(type->irtype->isClass()->getNumInterfaceVtbls() == n &&
|
|
"inconsistent number of interface vtables in this class");
|
|
|
|
VarDeclarationIter interfaces_idx(ClassDeclaration::classinfo->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);
|
|
|
|
const LLType* classinfo_type = DtoType(ClassDeclaration::classinfo->type);
|
|
const LLType* voidptrptr_type = DtoType(
|
|
Type::tvoid->pointerTo()->pointerTo());
|
|
|
|
const LLType* our_type = type->irtype->isClass()->getPA().get();
|
|
|
|
for (size_t i = 0; i < n; ++i)
|
|
{
|
|
BaseClass* it = interfacesWithVtbls[i];
|
|
|
|
IF_LOG Logger::println("Adding interface %s", it->base->toPrettyChars());
|
|
|
|
IrStruct* irinter = it->base->ir.irStruct;
|
|
assert(irinter && "interface has null IrStruct");
|
|
IrTypeClass* itc = irinter->type->irtype->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
|
|
{
|
|
ClassGlobalMap::iterator itv = interfaceVtblMap.find(it->base);
|
|
assert(itv != interfaceVtblMap.end() && "interface vtbl not found");
|
|
vtb = itv->second;
|
|
vtb = DtoBitCast(vtb, voidptrptr_type);
|
|
vtb = DtoConstSlice(DtoConstSize_t(itc->getVtblSize()), vtb);
|
|
}
|
|
|
|
// offset
|
|
LLConstant* off = DtoConstSize_t(it->offset);
|
|
|
|
// create Interface struct
|
|
LLConstant* inits[3] = { ci, vtb, off };
|
|
LLConstant* entry = llvm::ConstantStruct::get(inits, 3);
|
|
constants.push_back(entry);
|
|
}
|
|
|
|
// create Interface[N]
|
|
const llvm::ArrayType* array_type = llvm::ArrayType::get(
|
|
constants[0]->getType(),
|
|
n);
|
|
|
|
LLConstant* arr = llvm::ConstantArray::get(
|
|
array_type,
|
|
&constants[0],
|
|
n);
|
|
|
|
// apply the initializer
|
|
classInterfacesArray->setInitializer(arr);
|
|
|
|
// 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(
|
|
classInterfacesArray, idxs, 2);
|
|
|
|
// return as a slice
|
|
return DtoConstSlice( DtoConstSize_t(cd->vtblInterfaces->dim), ptr );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
void IrStruct::initializeInterface()
|
|
{
|
|
InterfaceDeclaration* base = aggrdecl->isInterfaceDeclaration();
|
|
assert(base && "not interface");
|
|
|
|
// has interface vtbls?
|
|
if (!base->vtblInterfaces)
|
|
return;
|
|
|
|
ArrayIter<BaseClass> it(*base->vtblInterfaces);
|
|
for (; !it.done(); it.next())
|
|
{
|
|
// add to the interface list
|
|
interfacesWithVtbls.push_back(it.get());
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|