mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-01 15:40:55 +03:00

Static ctors/dtors now work according to spec. Changed class vtable types slightly in some cases. Overridden functions now always take the the type of the first class declaring the method as this parameter. This helps when using headers (w. implementation somewhere else)
1067 lines
34 KiB
C++
1067 lines
34 KiB
C++
#include <sstream>
|
|
#include "gen/llvm.h"
|
|
|
|
#include "mtype.h"
|
|
#include "aggregate.h"
|
|
#include "init.h"
|
|
#include "declaration.h"
|
|
|
|
#include "gen/irstate.h"
|
|
#include "gen/tollvm.h"
|
|
#include "gen/arrays.h"
|
|
#include "gen/logger.h"
|
|
#include "gen/classes.h"
|
|
#include "gen/functions.h"
|
|
#include "gen/runtime.h"
|
|
#include "gen/dvalue.h"
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void LLVM_AddBaseClassData(BaseClasses* bcs)
|
|
{
|
|
// add base class data members first
|
|
for (int j=0; j<bcs->dim; j++)
|
|
{
|
|
BaseClass* bc = (BaseClass*)(bcs->data[j]);
|
|
assert(bc);
|
|
if (bc->base->isInterfaceDeclaration())
|
|
continue; // interfaces only has methods
|
|
|
|
LLVM_AddBaseClassData(&bc->base->baseclasses);
|
|
|
|
Logger::println("Adding base class members of %s", bc->base->toChars());
|
|
LOG_SCOPE;
|
|
|
|
for (int k=0; k < bc->base->members->dim; k++) {
|
|
Dsymbol* dsym = (Dsymbol*)(bc->base->members->data[k]);
|
|
if (dsym->isVarDeclaration())
|
|
{
|
|
dsym->toObjFile();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void DtoResolveClass(ClassDeclaration* cd)
|
|
{
|
|
if (cd->llvmResolved) return;
|
|
cd->llvmResolved = true;
|
|
|
|
// first resolve the base class
|
|
if (cd->baseClass) {
|
|
DtoResolveClass(cd->baseClass);
|
|
}
|
|
|
|
// resolve interfaces
|
|
if (cd->vtblInterfaces) {
|
|
for (int i=0; i < cd->vtblInterfaces->dim; i++) {
|
|
BaseClass *b = (BaseClass *)cd->vtblInterfaces->data[i];
|
|
ClassDeclaration *id = b->base;
|
|
DtoResolveClass(id);
|
|
// Fill in vtbl[]
|
|
b->fillVtbl(cd, &b->vtbl, 1);
|
|
}
|
|
}
|
|
|
|
Logger::println("DtoResolveClass(%s)", cd->toPrettyChars());
|
|
LOG_SCOPE;
|
|
|
|
assert(cd->type->ty == Tclass);
|
|
TypeClass* ts = (TypeClass*)cd->type;
|
|
|
|
assert(!cd->llvmIRStruct);
|
|
IRStruct* irstruct = new IRStruct(ts);
|
|
cd->llvmIRStruct = irstruct;
|
|
|
|
gIR->structs.push_back(irstruct);
|
|
gIR->classes.push_back(cd);
|
|
|
|
// add vtable
|
|
ts->llvmVtblType = new llvm::PATypeHolder(llvm::OpaqueType::get());
|
|
const llvm::Type* vtabty = llvm::PointerType::get(ts->llvmVtblType->get());
|
|
|
|
std::vector<const llvm::Type*> fieldtypes;
|
|
fieldtypes.push_back(vtabty);
|
|
|
|
// add monitor
|
|
fieldtypes.push_back(llvm::PointerType::get(llvm::Type::Int8Ty));
|
|
|
|
// add interface vtables
|
|
if (cd->vtblInterfaces)
|
|
for (size_t i = 0; i < cd->vtblInterfaces->dim; i++)
|
|
{
|
|
BaseClass *b = (BaseClass *)cd->vtblInterfaces->data[i];
|
|
ClassDeclaration *id = b->base;
|
|
assert(id->type->ty == Tclass);
|
|
TypeClass* itc = (TypeClass*)id->type;
|
|
const llvm::Type* ivtblTy = llvm::PointerType::get(itc->llvmVtblType->get());
|
|
fieldtypes.push_back(ivtblTy);
|
|
|
|
// add this interface to the map
|
|
IRInterface* iri = new IRInterface(b, isaStruct(itc->llvmVtblType->get()));
|
|
irstruct->interfaces.insert(std::make_pair(id, iri));
|
|
}
|
|
|
|
// base classes first
|
|
LLVM_AddBaseClassData(&cd->baseclasses);
|
|
|
|
// then add own members
|
|
for (int k=0; k < cd->members->dim; k++) {
|
|
Dsymbol* dsym = (Dsymbol*)(cd->members->data[k]);
|
|
dsym->toObjFile();
|
|
}
|
|
|
|
// add field types
|
|
for (IRStruct::OffsetMap::iterator i=irstruct->offsets.begin(); i!=irstruct->offsets.end(); ++i) {
|
|
fieldtypes.push_back(i->second.type);
|
|
}
|
|
|
|
const llvm::StructType* structtype = llvm::StructType::get(fieldtypes);
|
|
// refine abstract types for stuff like: class C {C next;}
|
|
assert(irstruct->recty != 0);
|
|
|
|
llvm::PATypeHolder& spa = irstruct->recty;
|
|
llvm::cast<llvm::OpaqueType>(spa.get())->refineAbstractTypeTo(structtype);
|
|
structtype = isaStruct(spa.get());
|
|
|
|
if (!ts->llvmType)
|
|
ts->llvmType = new llvm::PATypeHolder(structtype);
|
|
else
|
|
*ts->llvmType = structtype;
|
|
|
|
if (cd->isNested()) {
|
|
assert(0 && "nested classes not implemented");
|
|
}
|
|
else {
|
|
gIR->module->addTypeName(cd->mangle(), ts->llvmType->get());
|
|
}
|
|
|
|
// build interface info type
|
|
std::vector<const llvm::Type*> infoTypes;
|
|
// ClassInfo classinfo
|
|
ClassDeclaration* cinfod = ClassDeclaration::classinfo;
|
|
DtoResolveClass(cinfod);
|
|
infoTypes.push_back(llvm::PointerType::get(cinfod->type->llvmType->get()));
|
|
// void*[] vtbl
|
|
std::vector<const llvm::Type*> infoVtbltypes;
|
|
infoVtbltypes.push_back(DtoSize_t());
|
|
const llvm::Type* byteptrptrty = llvm::PointerType::get(llvm::PointerType::get(llvm::Type::Int8Ty));
|
|
infoVtbltypes.push_back(byteptrptrty);
|
|
infoTypes.push_back(llvm::StructType::get(infoVtbltypes));
|
|
// int offset
|
|
infoTypes.push_back(llvm::Type::Int32Ty);
|
|
// create type
|
|
const llvm::StructType* infoTy = llvm::StructType::get(infoTypes);
|
|
|
|
// create vtable type
|
|
llvm::GlobalVariable* svtblVar = 0;
|
|
std::vector<const llvm::Type*> sinits_ty;
|
|
|
|
for (int k=0; k < cd->vtbl.dim; k++)
|
|
{
|
|
Dsymbol* dsym = (Dsymbol*)cd->vtbl.data[k];
|
|
assert(dsym);
|
|
//Logger::cout() << "vtblsym: " << dsym->toChars() << '\n';
|
|
|
|
if (FuncDeclaration* fd = dsym->isFuncDeclaration()) {
|
|
DtoResolveFunction(fd);
|
|
//assert(fd->type->ty == Tfunction);
|
|
//TypeFunction* tf = (TypeFunction*)fd->type;
|
|
//const llvm::Type* fpty = llvm::PointerType::get(tf->llvmType->get());
|
|
const llvm::FunctionType* vfty = DtoBaseFunctionType(fd);
|
|
const llvm::Type* vfpty = llvm::PointerType::get(vfty);
|
|
sinits_ty.push_back(vfpty);
|
|
}
|
|
else if (ClassDeclaration* cd2 = dsym->isClassDeclaration()) {
|
|
Logger::println("*** ClassDeclaration in vtable: %s", cd2->toChars());
|
|
const llvm::Type* cinfoty;
|
|
if (cd->isInterfaceDeclaration()) {
|
|
cinfoty = infoTy;
|
|
}
|
|
else if (cd != cinfod) {
|
|
DtoResolveClass(cinfod);
|
|
cinfoty = cinfod->type->llvmType->get();
|
|
}
|
|
else {
|
|
// this is the ClassInfo class, the type is this type
|
|
cinfoty = ts->llvmType->get();
|
|
}
|
|
const llvm::Type* cty = llvm::PointerType::get(cinfoty);
|
|
sinits_ty.push_back(cty);
|
|
}
|
|
else
|
|
assert(0);
|
|
}
|
|
|
|
assert(!sinits_ty.empty());
|
|
const llvm::StructType* svtbl_ty = llvm::StructType::get(sinits_ty);
|
|
|
|
std::string styname(cd->mangle());
|
|
styname.append("__vtblType");
|
|
gIR->module->addTypeName(styname, svtbl_ty);
|
|
|
|
// refine for final vtable type
|
|
llvm::cast<llvm::OpaqueType>(ts->llvmVtblType->get())->refineAbstractTypeTo(svtbl_ty);
|
|
|
|
gIR->classes.pop_back();
|
|
gIR->structs.pop_back();
|
|
|
|
gIR->declareList.push_back(cd);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void DtoDeclareClass(ClassDeclaration* cd)
|
|
{
|
|
if (cd->llvmDeclared) return;
|
|
cd->llvmDeclared = true;
|
|
|
|
Logger::println("DtoDeclareClass(%s)", cd->toPrettyChars());
|
|
LOG_SCOPE;
|
|
|
|
assert(cd->type->ty == Tclass);
|
|
TypeClass* ts = (TypeClass*)cd->type;
|
|
|
|
assert(cd->llvmIRStruct);
|
|
IRStruct* irstruct = cd->llvmIRStruct;
|
|
|
|
gIR->structs.push_back(irstruct);
|
|
gIR->classes.push_back(cd);
|
|
|
|
bool needs_definition = false;
|
|
if (cd->parent->isModule()) {
|
|
needs_definition = (cd->getModule() == gIR->dmodule);
|
|
}
|
|
|
|
// interface vtables are emitted by the class implementing them
|
|
// also interfaces have no static initializer
|
|
if (!cd->isInterfaceDeclaration()) {
|
|
// vtable
|
|
std::string varname("_D");
|
|
varname.append(cd->mangle());
|
|
varname.append("6__vtblZ");
|
|
|
|
llvm::GlobalValue::LinkageTypes _linkage = llvm::GlobalValue::ExternalLinkage;
|
|
|
|
const llvm::StructType* svtbl_ty = isaStruct(ts->llvmVtblType->get());
|
|
cd->llvmVtbl = new llvm::GlobalVariable(svtbl_ty, true, _linkage, 0, varname, gIR->module);
|
|
|
|
// build interface info type
|
|
std::vector<const llvm::Type*> types;
|
|
// ClassInfo classinfo
|
|
ClassDeclaration* cd2 = ClassDeclaration::classinfo;
|
|
DtoResolveClass(cd2);
|
|
types.push_back(llvm::PointerType::get(cd2->type->llvmType->get()));
|
|
// void*[] vtbl
|
|
std::vector<const llvm::Type*> vtbltypes;
|
|
vtbltypes.push_back(DtoSize_t());
|
|
const llvm::Type* byteptrptrty = llvm::PointerType::get(llvm::PointerType::get(llvm::Type::Int8Ty));
|
|
vtbltypes.push_back(byteptrptrty);
|
|
types.push_back(llvm::StructType::get(vtbltypes));
|
|
// int offset
|
|
types.push_back(llvm::Type::Int32Ty);
|
|
// create type
|
|
const llvm::StructType* infoTy = llvm::StructType::get(types);
|
|
|
|
// interface info array
|
|
if (needs_definition && cd->vtblInterfaces->dim > 0) {
|
|
// symbol name
|
|
std::string nam = "_D";
|
|
nam.append(cd->mangle());
|
|
nam.append("16__interfaceInfosZ");
|
|
// resolve array type
|
|
const llvm::ArrayType* arrTy = llvm::ArrayType::get(infoTy, cd->vtblInterfaces->dim);
|
|
// declare global
|
|
irstruct->interfaceInfosTy = arrTy;
|
|
irstruct->interfaceInfos = new llvm::GlobalVariable(arrTy, true, llvm::GlobalValue::InternalLinkage, 0, nam, gIR->module);
|
|
}
|
|
|
|
// interface vtables
|
|
unsigned idx = 0;
|
|
for (IRStruct::InterfaceIter i=irstruct->interfaces.begin(); i!=irstruct->interfaces.end(); ++i)
|
|
{
|
|
ClassDeclaration* id = i->first;
|
|
IRInterface* iri = i->second;
|
|
|
|
std::string nam("_D");
|
|
nam.append(cd->mangle());
|
|
nam.append("11__interface");
|
|
nam.append(id->mangle());
|
|
nam.append("6__vtblZ");
|
|
|
|
assert(iri->vtblTy);
|
|
iri->vtbl = new llvm::GlobalVariable(iri->vtblTy, true, _linkage, 0, nam, gIR->module);
|
|
iri->infoTy = infoTy;
|
|
llvm::Constant* idxs[2] = {DtoConstUint(0), DtoConstUint(idx)};
|
|
iri->info = llvm::ConstantExpr::getGetElementPtr(irstruct->interfaceInfos, idxs, 2);
|
|
idx++;
|
|
}
|
|
|
|
// init
|
|
std::string initname("_D");
|
|
initname.append(cd->mangle());
|
|
initname.append("6__initZ");
|
|
|
|
llvm::GlobalVariable* initvar = new llvm::GlobalVariable(ts->llvmType->get(), true, _linkage, NULL, initname, gIR->module);
|
|
ts->llvmInit = initvar;
|
|
}
|
|
|
|
gIR->classes.pop_back();
|
|
gIR->structs.pop_back();
|
|
|
|
gIR->constInitList.push_back(cd);
|
|
if (needs_definition)
|
|
gIR->defineList.push_back(cd);
|
|
|
|
// classinfo
|
|
DtoDeclareClassInfo(cd);
|
|
|
|
// typeinfo
|
|
if (cd->parent->isModule() && cd->getModule() == gIR->dmodule)
|
|
cd->type->getTypeInfo(NULL);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void DtoConstInitClass(ClassDeclaration* cd)
|
|
{
|
|
if (cd->llvmInitialized) return;
|
|
cd->llvmInitialized = true;
|
|
|
|
if (cd->isInterfaceDeclaration())
|
|
return; // nothing to do
|
|
|
|
Logger::println("DtoConstInitClass(%s)", cd->toPrettyChars());
|
|
LOG_SCOPE;
|
|
|
|
IRStruct* irstruct = cd->llvmIRStruct;
|
|
gIR->structs.push_back(irstruct);
|
|
gIR->classes.push_back(cd);
|
|
|
|
// make sure each offset knows its default initializer
|
|
for (IRStruct::OffsetMap::iterator i=irstruct->offsets.begin(); i!=irstruct->offsets.end(); ++i)
|
|
{
|
|
IRStruct::Offset* so = &i->second;
|
|
llvm::Constant* finit = DtoConstFieldInitializer(so->var->type, so->var->init);
|
|
so->init = finit;
|
|
so->var->llvmConstInit = finit;
|
|
}
|
|
|
|
// fill out fieldtypes/inits
|
|
std::vector<llvm::Constant*> fieldinits;
|
|
|
|
// first field is always the vtable
|
|
assert(cd->llvmVtbl != 0);
|
|
fieldinits.push_back(cd->llvmVtbl);
|
|
|
|
// then comes monitor
|
|
fieldinits.push_back(llvm::ConstantPointerNull::get(llvm::PointerType::get(llvm::Type::Int8Ty)));
|
|
|
|
// next comes interface vtables
|
|
for (IRStruct::InterfaceIter i=irstruct->interfaces.begin(); i!=irstruct->interfaces.end(); ++i)
|
|
{
|
|
IRInterface* iri = i->second;
|
|
assert(iri->vtbl);
|
|
fieldinits.push_back(iri->vtbl);
|
|
}
|
|
|
|
// rest
|
|
for (IRStruct::OffsetMap::iterator i=irstruct->offsets.begin(); i!=irstruct->offsets.end(); ++i) {
|
|
Logger::println("adding fieldinit for: %s", i->second.var->toChars());
|
|
fieldinits.push_back(i->second.init);
|
|
}
|
|
|
|
// get the struct (class) type
|
|
assert(cd->type->ty == Tclass);
|
|
TypeClass* ts = (TypeClass*)cd->type;
|
|
const llvm::StructType* structtype = isaStruct(ts->llvmType->get());
|
|
const llvm::StructType* vtbltype = isaStruct(ts->llvmVtblType->get());
|
|
|
|
// generate initializer
|
|
#if 0
|
|
Logger::cout() << cd->toPrettyChars() << " | " << *structtype << '\n';
|
|
|
|
for(size_t i=0; i<structtype->getNumElements(); ++i) {
|
|
Logger::cout() << "s#" << i << " = " << *structtype->getElementType(i) << '\n';
|
|
}
|
|
|
|
for(size_t i=0; i<fieldinits.size(); ++i) {
|
|
Logger::cout() << "i#" << i << " = " << *fieldinits[i]->getType() << '\n';
|
|
}
|
|
#endif
|
|
|
|
llvm::Constant* _init = llvm::ConstantStruct::get(structtype, fieldinits);
|
|
assert(_init);
|
|
cd->llvmInitZ = _init;
|
|
|
|
// generate vtable initializer
|
|
std::vector<llvm::Constant*> sinits;
|
|
|
|
for (int k=0; k < cd->vtbl.dim; k++)
|
|
{
|
|
Dsymbol* dsym = (Dsymbol*)cd->vtbl.data[k];
|
|
assert(dsym);
|
|
//Logger::cout() << "vtblsym: " << dsym->toChars() << '\n';
|
|
|
|
if (FuncDeclaration* fd = dsym->isFuncDeclaration()) {
|
|
DtoForceDeclareDsymbol(fd);
|
|
assert(fd->llvmValue);
|
|
llvm::Constant* c = llvm::cast<llvm::Constant>(fd->llvmValue);
|
|
// cast if necessary (overridden method)
|
|
if (c->getType() != vtbltype->getElementType(k))
|
|
c = llvm::ConstantExpr::getBitCast(c, vtbltype->getElementType(k));
|
|
sinits.push_back(c);
|
|
}
|
|
else if (ClassDeclaration* cd2 = dsym->isClassDeclaration()) {
|
|
assert(cd->llvmClass);
|
|
llvm::Constant* c = cd->llvmClass;
|
|
sinits.push_back(c);
|
|
}
|
|
else
|
|
assert(0);
|
|
}
|
|
|
|
const llvm::StructType* svtbl_ty = isaStruct(ts->llvmVtblType->get());
|
|
|
|
#if 0
|
|
for (size_t i=0; i< sinits.size(); ++i)
|
|
{
|
|
Logger::cout() << "field[" << i << "] = " << *svtbl_ty->getElementType(i) << '\n';
|
|
Logger::cout() << "init [" << i << "] = " << *sinits[i]->getType() << '\n';
|
|
assert(svtbl_ty->getElementType(i) == sinits[i]->getType());
|
|
}
|
|
#endif
|
|
|
|
llvm::Constant* cvtblInit = llvm::ConstantStruct::get(svtbl_ty, sinits);
|
|
cd->llvmConstVtbl = llvm::cast<llvm::ConstantStruct>(cvtblInit);
|
|
|
|
// create interface vtable const initalizers
|
|
int idx = 2;
|
|
int idxScale = PTRSIZE;
|
|
for (IRStruct::InterfaceIter i=irstruct->interfaces.begin(); i!=irstruct->interfaces.end(); ++i)
|
|
{
|
|
ClassDeclaration* id = i->first;
|
|
assert(id->type->ty == Tclass);
|
|
TypeClass* its = (TypeClass*)id->type;
|
|
|
|
IRInterface* iri = i->second;
|
|
BaseClass* b = iri->base;
|
|
|
|
const llvm::StructType* ivtbl_ty = isaStruct(its->llvmVtblType->get());
|
|
|
|
// generate interface info initializer
|
|
std::vector<llvm::Constant*> infoInits;
|
|
// classinfo
|
|
assert(id->llvmClass);
|
|
llvm::Constant* c = id->llvmClass;
|
|
infoInits.push_back(c);
|
|
// vtbl
|
|
const llvm::Type* byteptrptrty = llvm::PointerType::get(llvm::PointerType::get(llvm::Type::Int8Ty));
|
|
c = llvm::ConstantExpr::getBitCast(iri->vtbl, byteptrptrty);
|
|
c = DtoConstSlice(DtoConstSize_t(b->vtbl.dim), c);
|
|
infoInits.push_back(c);
|
|
// offset
|
|
infoInits.push_back(DtoConstInt(idx*idxScale));
|
|
// create interface info initializer constant
|
|
iri->infoInit = llvm::cast<llvm::ConstantStruct>(llvm::ConstantStruct::get(iri->infoTy, infoInits));
|
|
|
|
// generate vtable initializer
|
|
std::vector<llvm::Constant*> iinits;
|
|
|
|
// add interface info
|
|
iinits.push_back(iri->info);
|
|
|
|
for (int k=1; k < b->vtbl.dim; k++)
|
|
{
|
|
Logger::println("interface vtbl const init nr. %d", k);
|
|
Dsymbol* dsym = (Dsymbol*)b->vtbl.data[k];
|
|
FuncDeclaration* fd = dsym->isFuncDeclaration();
|
|
assert(fd);
|
|
DtoForceDeclareDsymbol(fd);
|
|
assert(fd->llvmValue);
|
|
llvm::Constant* c = llvm::cast<llvm::Constant>(fd->llvmValue);
|
|
// we have to bitcast, as the type created in ResolveClass expects a different this type
|
|
c = llvm::ConstantExpr::getBitCast(c, iri->vtblTy->getContainedType(k));
|
|
iinits.push_back(c);
|
|
}
|
|
|
|
#if 1
|
|
for (size_t x=0; x< iinits.size(); ++x)
|
|
{
|
|
Logger::cout() << "field[" << x << "] = " << *ivtbl_ty->getElementType(x) << "\n\n";
|
|
Logger::cout() << "init [" << x << "] = " << *iinits[x] << "\n\n";
|
|
assert(ivtbl_ty->getElementType(x) == iinits[x]->getType());
|
|
}
|
|
#endif
|
|
|
|
llvm::Constant* civtblInit = llvm::ConstantStruct::get(ivtbl_ty, iinits);
|
|
iri->vtblInit = llvm::cast<llvm::ConstantStruct>(civtblInit);
|
|
|
|
idx++;
|
|
}
|
|
|
|
gIR->classes.pop_back();
|
|
gIR->structs.pop_back();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void DtoDefineClass(ClassDeclaration* cd)
|
|
{
|
|
if (cd->llvmDefined) return;
|
|
cd->llvmDefined = true;
|
|
|
|
Logger::println("DtoDefineClass(%s)", cd->toPrettyChars());
|
|
LOG_SCOPE;
|
|
|
|
// get the struct (class) type
|
|
assert(cd->type->ty == Tclass);
|
|
TypeClass* ts = (TypeClass*)cd->type;
|
|
|
|
bool def = false;
|
|
if (cd->parent->isModule() && cd->getModule() == gIR->dmodule) {
|
|
// interfaces don't have initializers
|
|
if (!cd->isInterfaceDeclaration()) {
|
|
ts->llvmInit->setInitializer(cd->llvmInitZ);
|
|
cd->llvmVtbl->setInitializer(cd->llvmConstVtbl);
|
|
|
|
// initialize interface vtables
|
|
IRStruct* irstruct = cd->llvmIRStruct;
|
|
std::vector<llvm::Constant*> infoInits;
|
|
for (IRStruct::InterfaceIter i=irstruct->interfaces.begin(); i!=irstruct->interfaces.end(); ++i)
|
|
{
|
|
IRInterface* iri = i->second;
|
|
iri->vtbl->setInitializer(iri->vtblInit);
|
|
infoInits.push_back(iri->infoInit);
|
|
}
|
|
// initialize interface info array
|
|
if (!infoInits.empty()) {
|
|
llvm::Constant* arrInit = llvm::ConstantArray::get(irstruct->interfaceInfosTy, infoInits);
|
|
irstruct->interfaceInfos->setInitializer(arrInit);
|
|
}
|
|
}
|
|
def = true;
|
|
}
|
|
|
|
// generate classinfo
|
|
if (def) DtoDefineClassInfo(cd);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void DtoCallClassDtors(TypeClass* tc, llvm::Value* instance)
|
|
{
|
|
Array* arr = &tc->sym->dtors;
|
|
for (size_t i=0; i<arr->dim; i++)
|
|
{
|
|
FuncDeclaration* fd = (FuncDeclaration*)arr->data[i];
|
|
assert(fd->llvmValue);
|
|
new llvm::CallInst(fd->llvmValue, instance, "", gIR->scopebb());
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void DtoInitClass(TypeClass* tc, llvm::Value* dst)
|
|
{
|
|
assert(gIR);
|
|
|
|
assert(tc->llvmType);
|
|
uint64_t size_t_size = gTargetData->getTypeSize(DtoSize_t());
|
|
uint64_t n = gTargetData->getTypeSize(tc->llvmType->get()) - size_t_size;
|
|
|
|
// set vtable field
|
|
llvm::Value* vtblvar = DtoGEPi(dst,0,0,"tmp",gIR->scopebb());
|
|
assert(tc->sym->llvmVtbl);
|
|
new llvm::StoreInst(tc->sym->llvmVtbl, vtblvar, gIR->scopebb());
|
|
|
|
// copy the static initializer
|
|
if (n > 0) {
|
|
assert(tc->llvmInit);
|
|
assert(dst->getType() == tc->llvmInit->getType());
|
|
|
|
llvm::Type* arrty = llvm::PointerType::get(llvm::Type::Int8Ty);
|
|
|
|
llvm::Value* dstarr = new llvm::BitCastInst(dst,arrty,"tmp",gIR->scopebb());
|
|
dstarr = DtoGEPi(dstarr,size_t_size,"tmp",gIR->scopebb());
|
|
|
|
llvm::Value* srcarr = new llvm::BitCastInst(tc->llvmInit,arrty,"tmp",gIR->scopebb());
|
|
srcarr = DtoGEPi(srcarr,size_t_size,"tmp",gIR->scopebb());
|
|
|
|
llvm::Function* fn = LLVM_DeclareMemCpy32();
|
|
std::vector<llvm::Value*> llargs;
|
|
llargs.resize(4);
|
|
llargs[0] = dstarr;
|
|
llargs[1] = srcarr;
|
|
llargs[2] = llvm::ConstantInt::get(llvm::Type::Int32Ty, n, false);
|
|
llargs[3] = llvm::ConstantInt::get(llvm::Type::Int32Ty, 0, false);
|
|
|
|
new llvm::CallInst(fn, llargs.begin(), llargs.end(), "", gIR->scopebb());
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* DtoCastClass(DValue* val, Type* _to)
|
|
{
|
|
Type* to = DtoDType(_to);
|
|
if (to->ty == Tpointer) {
|
|
const llvm::Type* tolltype = DtoType(_to);
|
|
llvm::Value* rval = DtoBitCast(val->getRVal(), tolltype);
|
|
return new DImValue(_to, rval);
|
|
}
|
|
|
|
assert(to->ty == Tclass);
|
|
TypeClass* tc = (TypeClass*)to;
|
|
|
|
Type* from = DtoDType(val->getType());
|
|
TypeClass* fc = (TypeClass*)from;
|
|
|
|
if (tc->sym->isInterfaceDeclaration()) {
|
|
assert(!fc->sym->isInterfaceDeclaration());
|
|
return DtoDynamicCastObject(val, _to);
|
|
}
|
|
else {
|
|
int poffset;
|
|
if (fc->sym->isInterfaceDeclaration()) {
|
|
return DtoCastInterfaceToObject(val, _to);
|
|
}
|
|
else if (tc->sym->isBaseOf(fc->sym,NULL)) {
|
|
const llvm::Type* tolltype = DtoType(_to);
|
|
llvm::Value* rval = DtoBitCast(val->getRVal(), tolltype);
|
|
return new DImValue(_to, rval);
|
|
}
|
|
else {
|
|
return DtoDynamicCastObject(val, _to);
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* DtoDynamicCastObject(DValue* val, Type* _to)
|
|
{
|
|
// call:
|
|
// Object _d_dynamic_cast(Object o, ClassInfo c)
|
|
|
|
DtoForceDeclareDsymbol(ClassDeclaration::object);
|
|
DtoForceDeclareDsymbol(ClassDeclaration::classinfo);
|
|
|
|
llvm::Function* func = LLVM_D_GetRuntimeFunction(gIR->module, "_d_dynamic_cast");
|
|
const llvm::FunctionType* funcTy = func->getFunctionType();
|
|
|
|
std::vector<llvm::Value*> args;
|
|
|
|
// Object o
|
|
llvm::Value* tmp = val->getRVal();
|
|
tmp = DtoBitCast(tmp, funcTy->getParamType(0));
|
|
args.push_back(tmp);
|
|
assert(funcTy->getParamType(0) == tmp->getType());
|
|
|
|
// ClassInfo c
|
|
TypeClass* to = (TypeClass*)DtoDType(_to);
|
|
DtoForceDeclareDsymbol(to->sym);
|
|
assert(to->sym->llvmClass);
|
|
tmp = to->sym->llvmClass;
|
|
// unfortunately this is needed as the implementation of object differs somehow from the declaration
|
|
// this could happen in user code as well :/
|
|
tmp = DtoBitCast(tmp, funcTy->getParamType(1));
|
|
args.push_back(tmp);
|
|
assert(funcTy->getParamType(1) == tmp->getType());
|
|
|
|
// call it
|
|
llvm::Value* ret = gIR->ir->CreateCall(func, args.begin(), args.end(), "tmp");
|
|
|
|
// cast return value
|
|
ret = DtoBitCast(ret, DtoType(_to));
|
|
|
|
return new DImValue(_to, ret);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* DtoCastInterfaceToObject(DValue* val, Type* to)
|
|
{
|
|
// call:
|
|
// Object _d_toObject(void* p)
|
|
|
|
llvm::Function* func = LLVM_D_GetRuntimeFunction(gIR->module, "_d_toObject");
|
|
const llvm::FunctionType* funcTy = func->getFunctionType();
|
|
|
|
// void* p
|
|
llvm::Value* tmp = val->getRVal();
|
|
tmp = DtoBitCast(tmp, funcTy->getParamType(0));
|
|
|
|
// call it
|
|
llvm::Value* ret = gIR->ir->CreateCall(func, tmp, "tmp");
|
|
|
|
// cast return value
|
|
if (to != NULL)
|
|
ret = DtoBitCast(ret, DtoType(to));
|
|
else
|
|
to = ClassDeclaration::object->type;
|
|
|
|
return new DImValue(to, ret);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void DtoDeclareClassInfo(ClassDeclaration* cd)
|
|
{
|
|
if (cd->llvmClassDeclared) return;
|
|
cd->llvmClassDeclared = true;
|
|
|
|
Logger::println("DtoDeclareClassInfo(%s)", cd->toChars());
|
|
LOG_SCOPE;
|
|
|
|
ClassDeclaration* cinfo = ClassDeclaration::classinfo;
|
|
DtoResolveClass(cinfo);
|
|
|
|
std::string gname("_D");
|
|
gname.append(cd->mangle());
|
|
if (!cd->isInterfaceDeclaration())
|
|
gname.append("7__ClassZ");
|
|
else
|
|
gname.append("11__InterfaceZ");
|
|
|
|
const llvm::Type* st = cinfo->type->llvmType->get();
|
|
|
|
cd->llvmClass = new llvm::GlobalVariable(st, true, llvm::GlobalValue::ExternalLinkage, NULL, gname, gIR->module);
|
|
}
|
|
|
|
static llvm::Constant* build_offti_entry(VarDeclaration* vd)
|
|
{
|
|
std::vector<const llvm::Type*> types;
|
|
std::vector<llvm::Constant*> inits;
|
|
|
|
types.push_back(DtoSize_t());
|
|
|
|
size_t offset = vd->offset; // TODO might not be the true offset
|
|
// dmd only accounts for the vtable, not classinfo or monitor
|
|
if (global.params.is64bit)
|
|
offset += 8;
|
|
else
|
|
offset += 4;
|
|
inits.push_back(DtoConstSize_t(offset));
|
|
|
|
vd->type->getTypeInfo(NULL);
|
|
assert(vd->type->vtinfo);
|
|
DtoForceDeclareDsymbol(vd->type->vtinfo);
|
|
llvm::Constant* c = isaConstant(vd->type->vtinfo->llvmValue);
|
|
|
|
const llvm::Type* tiTy = llvm::PointerType::get(Type::typeinfo->type->llvmType->get());
|
|
Logger::cout() << "tiTy = " << *tiTy << '\n';
|
|
|
|
types.push_back(tiTy);
|
|
inits.push_back(llvm::ConstantExpr::getBitCast(c, tiTy));
|
|
|
|
const llvm::StructType* sTy = llvm::StructType::get(types);
|
|
return llvm::ConstantStruct::get(sTy, inits);
|
|
}
|
|
|
|
static llvm::Constant* build_offti_array(ClassDeclaration* cd, llvm::Constant* init)
|
|
{
|
|
const llvm::StructType* initTy = isaStruct(init->getType());
|
|
assert(initTy);
|
|
|
|
std::vector<llvm::Constant*> arrayInits;
|
|
for (ClassDeclaration *cd2 = cd; cd2; cd2 = cd2->baseClass)
|
|
{
|
|
if (cd2->members)
|
|
{
|
|
for (size_t i = 0; i < cd2->members->dim; i++)
|
|
{
|
|
Dsymbol *sm = (Dsymbol *)cd2->members->data[i];
|
|
if (VarDeclaration* vd = sm->isVarDeclaration()) // is this enough?
|
|
{
|
|
llvm::Constant* c = build_offti_entry(vd);
|
|
assert(c);
|
|
arrayInits.push_back(c);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
size_t ninits = arrayInits.size();
|
|
llvm::Constant* size = DtoConstSize_t(ninits);
|
|
llvm::Constant* ptr;
|
|
|
|
if (ninits > 0) {
|
|
// OffsetTypeInfo type
|
|
std::vector<const llvm::Type*> elemtypes;
|
|
elemtypes.push_back(DtoSize_t());
|
|
const llvm::Type* tiTy = llvm::PointerType::get(Type::typeinfo->type->llvmType->get());
|
|
elemtypes.push_back(tiTy);
|
|
const llvm::StructType* sTy = llvm::StructType::get(elemtypes);
|
|
|
|
// array type
|
|
const llvm::ArrayType* arrTy = llvm::ArrayType::get(sTy, ninits);
|
|
llvm::Constant* arrInit = llvm::ConstantArray::get(arrTy, arrayInits);
|
|
|
|
std::string name(cd->type->vtinfo->toChars());
|
|
name.append("__OffsetTypeInfos");
|
|
llvm::GlobalVariable* gvar = new llvm::GlobalVariable(arrTy,true,llvm::GlobalValue::InternalLinkage,arrInit,name,gIR->module);
|
|
ptr = llvm::ConstantExpr::getBitCast(gvar, llvm::PointerType::get(sTy));
|
|
}
|
|
else {
|
|
ptr = llvm::ConstantPointerNull::get(isaPointer(initTy->getElementType(1)));
|
|
}
|
|
|
|
return DtoConstSlice(size, ptr);
|
|
}
|
|
|
|
static llvm::Constant* build_class_dtor(ClassDeclaration* cd)
|
|
{
|
|
// construct the function
|
|
std::vector<const llvm::Type*> paramTypes;
|
|
paramTypes.push_back(llvm::PointerType::get(cd->type->llvmType->get()));
|
|
|
|
const llvm::FunctionType* fnTy = llvm::FunctionType::get(llvm::Type::VoidTy, paramTypes, false);
|
|
|
|
if (cd->dtors.dim == 0) {
|
|
return llvm::ConstantPointerNull::get(llvm::PointerType::get(llvm::Type::Int8Ty));
|
|
}
|
|
else if (cd->dtors.dim == 1) {
|
|
DtorDeclaration *d = (DtorDeclaration *)cd->dtors.data[0];
|
|
DtoForceDeclareDsymbol(d);
|
|
assert(d->llvmValue);
|
|
return llvm::ConstantExpr::getBitCast(isaConstant(d->llvmValue), llvm::PointerType::get(llvm::Type::Int8Ty));
|
|
}
|
|
|
|
std::string gname("_D");
|
|
gname.append(cd->mangle());
|
|
gname.append("12__destructorMFZv");
|
|
|
|
llvm::Function* func = new llvm::Function(fnTy, llvm::GlobalValue::InternalLinkage, gname, gIR->module);
|
|
llvm::Value* thisptr = func->arg_begin();
|
|
thisptr->setName("this");
|
|
|
|
llvm::BasicBlock* bb = new llvm::BasicBlock("entry", func);
|
|
LLVMBuilder builder(bb);
|
|
|
|
for (size_t i = 0; i < cd->dtors.dim; i++)
|
|
{
|
|
DtorDeclaration *d = (DtorDeclaration *)cd->dtors.data[i];
|
|
DtoForceDeclareDsymbol(d);
|
|
assert(d->llvmValue);
|
|
builder.CreateCall(d->llvmValue, thisptr);
|
|
}
|
|
builder.CreateRetVoid();
|
|
|
|
return llvm::ConstantExpr::getBitCast(func, llvm::PointerType::get(llvm::Type::Int8Ty));
|
|
}
|
|
|
|
static uint build_classinfo_flags(ClassDeclaration* cd)
|
|
{
|
|
// adapted from original dmd code
|
|
uint flags = 0;
|
|
//flags |= isCOMclass(); // IUnknown
|
|
bool hasOffTi = false;
|
|
if (cd->ctor) flags |= 8;
|
|
for (ClassDeclaration *cd2 = cd; cd2; cd2 = cd2->baseClass)
|
|
{
|
|
if (cd2->members)
|
|
{
|
|
for (size_t i = 0; i < cd2->members->dim; i++)
|
|
{
|
|
Dsymbol *sm = (Dsymbol *)cd2->members->data[i];
|
|
if (sm->isVarDeclaration()) // is this enough?
|
|
hasOffTi = true;
|
|
//printf("sm = %s %s\n", sm->kind(), sm->toChars());
|
|
if (sm->hasPointers())
|
|
goto L2;
|
|
}
|
|
}
|
|
}
|
|
flags |= 2; // no pointers
|
|
L2:
|
|
if (hasOffTi)
|
|
flags |= 4;
|
|
return flags;
|
|
}
|
|
|
|
void DtoDefineClassInfo(ClassDeclaration* cd)
|
|
{
|
|
// The layout is:
|
|
// {
|
|
// void **vptr;
|
|
// monitor_t monitor;
|
|
// byte[] initializer; // static initialization data
|
|
// char[] name; // class name
|
|
// void *[] vtbl;
|
|
// Interface[] interfaces;
|
|
// ClassInfo *base; // base class
|
|
// void *destructor;
|
|
// void *invariant; // class invariant
|
|
// uint flags;
|
|
// void *deallocator;
|
|
// OffsetTypeInfo[] offTi;
|
|
// void *defaultConstructor;
|
|
// }
|
|
|
|
if (cd->llvmClassDefined) return;
|
|
cd->llvmClassDefined = true;
|
|
|
|
Logger::println("DtoDefineClassInfo(%s)", cd->toChars());
|
|
LOG_SCOPE;
|
|
|
|
assert(cd->type->ty == Tclass);
|
|
assert(cd->llvmClass);
|
|
|
|
TypeClass* cdty = (TypeClass*)cd->type;
|
|
if (!cd->isInterfaceDeclaration()) {
|
|
assert(cd->llvmInitZ);
|
|
assert(cd->llvmVtbl);
|
|
assert(cd->llvmConstVtbl);
|
|
assert(cdty->llvmInit);
|
|
}
|
|
|
|
// holds the list of initializers for llvm
|
|
std::vector<llvm::Constant*> inits;
|
|
|
|
ClassDeclaration* cinfo = ClassDeclaration::classinfo;
|
|
DtoForceConstInitDsymbol(cinfo);
|
|
assert(cinfo->llvmInitZ);
|
|
|
|
llvm::Constant* c;
|
|
|
|
// own vtable
|
|
c = cinfo->llvmInitZ->getOperand(0);
|
|
assert(c);
|
|
inits.push_back(c);
|
|
|
|
// monitor
|
|
c = cinfo->llvmInitZ->getOperand(1);
|
|
inits.push_back(c);
|
|
|
|
// byte[] init
|
|
const llvm::Type* byteptrty = llvm::PointerType::get(llvm::Type::Int8Ty);
|
|
if (cd->isInterfaceDeclaration()) {
|
|
c = cinfo->llvmInitZ->getOperand(2);
|
|
}
|
|
else {
|
|
c = llvm::ConstantExpr::getBitCast(cdty->llvmInit, byteptrty);
|
|
assert(!cd->llvmInitZ->getType()->isAbstract());
|
|
size_t initsz = gTargetData->getTypeSize(cd->llvmInitZ->getType());
|
|
c = DtoConstSlice(DtoConstSize_t(initsz), c);
|
|
}
|
|
inits.push_back(c);
|
|
|
|
// class name
|
|
// from dmd
|
|
char *name = cd->ident->toChars();
|
|
size_t namelen = strlen(name);
|
|
if (!(namelen > 9 && memcmp(name, "TypeInfo_", 9) == 0))
|
|
{
|
|
name = cd->toPrettyChars();
|
|
namelen = strlen(name);
|
|
}
|
|
c = DtoConstString(name);
|
|
inits.push_back(c);
|
|
|
|
// vtbl array
|
|
if (cd->isInterfaceDeclaration()) {
|
|
c = cinfo->llvmInitZ->getOperand(4);
|
|
}
|
|
else {
|
|
const llvm::Type* byteptrptrty = llvm::PointerType::get(byteptrty);
|
|
assert(!cd->llvmVtbl->getType()->isAbstract());
|
|
c = llvm::ConstantExpr::getBitCast(cd->llvmVtbl, byteptrptrty);
|
|
assert(!cd->llvmConstVtbl->getType()->isAbstract());
|
|
size_t vtblsz = cd->llvmConstVtbl->getType()->getNumElements();
|
|
c = DtoConstSlice(DtoConstSize_t(vtblsz), c);
|
|
}
|
|
inits.push_back(c);
|
|
|
|
// interfaces array
|
|
IRStruct* irstruct = cd->llvmIRStruct;
|
|
if (cd->isInterfaceDeclaration() || !irstruct->interfaceInfos) {
|
|
c = cinfo->llvmInitZ->getOperand(5);
|
|
}
|
|
else {
|
|
const llvm::Type* t = cinfo->llvmInitZ->getOperand(5)->getType()->getContainedType(1);
|
|
c = llvm::ConstantExpr::getBitCast(irstruct->interfaceInfos, t);
|
|
size_t iisz = irstruct->interfaceInfosTy->getNumElements();
|
|
c = DtoConstSlice(DtoConstSize_t(iisz), c);
|
|
}
|
|
inits.push_back(c);
|
|
|
|
// base classinfo
|
|
if (cd->baseClass && !cd->isInterfaceDeclaration()) {
|
|
DtoDeclareClassInfo(cd->baseClass);
|
|
c = cd->baseClass->llvmClass;
|
|
assert(c);
|
|
inits.push_back(c);
|
|
}
|
|
else {
|
|
// null
|
|
c = cinfo->llvmInitZ->getOperand(6);
|
|
inits.push_back(c);
|
|
}
|
|
|
|
// destructor
|
|
if (cd->isInterfaceDeclaration()) {
|
|
c = cinfo->llvmInitZ->getOperand(7);
|
|
}
|
|
else {
|
|
c = build_class_dtor(cd);
|
|
}
|
|
inits.push_back(c);
|
|
|
|
// invariant
|
|
// TODO
|
|
c = cinfo->llvmInitZ->getOperand(8);
|
|
inits.push_back(c);
|
|
|
|
// uint flags
|
|
if (cd->isInterfaceDeclaration()) {
|
|
c = cinfo->llvmInitZ->getOperand(9);
|
|
}
|
|
else {
|
|
uint flags = build_classinfo_flags(cd);
|
|
c = DtoConstUint(flags);
|
|
}
|
|
inits.push_back(c);
|
|
|
|
// allocator
|
|
// TODO
|
|
c = cinfo->llvmInitZ->getOperand(10);
|
|
inits.push_back(c);
|
|
|
|
// offset typeinfo
|
|
if (cd->isInterfaceDeclaration()) {
|
|
c = cinfo->llvmInitZ->getOperand(11);
|
|
}
|
|
else {
|
|
c = build_offti_array(cd, cinfo->llvmInitZ->getOperand(11));
|
|
}
|
|
inits.push_back(c);
|
|
|
|
// default constructor
|
|
if (cd->defaultCtor && !cd->isInterfaceDeclaration()) {
|
|
DtoForceDeclareDsymbol(cd->defaultCtor);
|
|
c = isaConstant(cd->defaultCtor->llvmValue);
|
|
const llvm::Type* toTy = cinfo->llvmInitZ->getOperand(12)->getType();
|
|
c = llvm::ConstantExpr::getBitCast(c, toTy);
|
|
}
|
|
else {
|
|
c = cinfo->llvmInitZ->getOperand(12);
|
|
}
|
|
inits.push_back(c);
|
|
|
|
/*size_t n = inits.size();
|
|
for (size_t i=0; i<n; ++i)
|
|
{
|
|
Logger::cout() << "inits[" << i << "]: " << *inits[i] << '\n';
|
|
}*/
|
|
|
|
// build the initializer
|
|
const llvm::StructType* st = isaStruct(cinfo->llvmInitZ->getType());
|
|
llvm::Constant* finalinit = llvm::ConstantStruct::get(st, inits);
|
|
//Logger::cout() << "built the classinfo initializer:\n" << *finalinit <<'\n';
|
|
|
|
cd->llvmClassZ = finalinit;
|
|
cd->llvmClass->setInitializer(finalinit);
|
|
}
|