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

- Implement x86-64 extern(C), hopefully correctly. - Tried to be a bit smarter about extern(D) while I was there. Interestingly, this code seems to be generating more efficient code than gcc and llvm-gcc in some edge cases, like returning a `{ [7 x i8] }` loaded from a stack slot from an extern(C) function. (gcc generates 7 1-byte loads, while this code generates a 4-byte, a 2-byte and a 1-byte load) I also added some changes to make sure structs being returned from functions or passed in as parameters are stored in memory where the rest of the backend seems to expect them to be. These should be removed when support for first-class aggregates improves.
1654 lines
49 KiB
C++
1654 lines
49 KiB
C++
#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/llvmhelpers.h"
|
|
#include "gen/arrays.h"
|
|
#include "gen/logger.h"
|
|
#include "gen/classes.h"
|
|
#include "gen/structs.h"
|
|
#include "gen/functions.h"
|
|
#include "gen/runtime.h"
|
|
#include "gen/dvalue.h"
|
|
|
|
#include "ir/irstruct.h"
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// adds the base interfaces of b and the given iri to IrStruct's interfaceMap
|
|
static void add_base_interfaces(IrStruct* to, IrInterface* iri, BaseClass* b)
|
|
{
|
|
for (unsigned j = 0; j < b->baseInterfaces_dim; j++)
|
|
{
|
|
BaseClass *bc = &b->baseInterfaces[j];
|
|
// add to map
|
|
if (to->interfaceMap.find(bc->base) == to->interfaceMap.end())
|
|
{
|
|
to->interfaceMap.insert(std::make_pair(bc->base, iri));
|
|
}
|
|
// add base interfaces
|
|
add_base_interfaces(to, iri, bc);
|
|
}
|
|
}
|
|
|
|
// adds interface b to target, if newinstance != 0, then target must provide all
|
|
// functions required to implement b (it reimplements b)
|
|
static void add_interface(ClassDeclaration* target, BaseClass* b, int newinstance)
|
|
{
|
|
Logger::println("adding interface: %s", b->base->toChars());
|
|
LOG_SCOPE;
|
|
|
|
InterfaceDeclaration* inter = b->base->isInterfaceDeclaration();
|
|
DtoResolveClass(inter);
|
|
|
|
assert(inter);
|
|
IrStruct* irstruct = target->ir.irStruct;
|
|
assert(irstruct);
|
|
|
|
// add interface to map/list
|
|
// if it's already inserted in the map, it's because another interface has it as baseclass
|
|
// but if it appears here, it's because we're reimplementing it, so we overwrite the IrInterface entry
|
|
IrInterface* iri;
|
|
bool overwrite = false;
|
|
if (irstruct->interfaceMap.find(inter) != irstruct->interfaceMap.end())
|
|
{
|
|
overwrite = true;
|
|
}
|
|
|
|
iri = new IrInterface(b);
|
|
// add to map
|
|
if (overwrite)
|
|
irstruct->interfaceMap[b->base] = iri;
|
|
else
|
|
irstruct->interfaceMap.insert(std::make_pair(b->base, iri));
|
|
|
|
// add to ordered list
|
|
irstruct->interfaceVec.push_back(iri);
|
|
|
|
// add to classinfo interfaces
|
|
if (newinstance)
|
|
irstruct->classInfoInterfaces.push_back(iri);
|
|
|
|
// recursively assign this iri to all base interfaces
|
|
add_base_interfaces(irstruct, iri, b);
|
|
|
|
// build the interface vtable
|
|
b->fillVtbl(target, &iri->vtblDecls, newinstance);
|
|
|
|
// add the vtable type
|
|
assert(inter->type->ir.type);
|
|
irstruct->types.push_back( inter->type->ir.type->get() );
|
|
// set and increment index
|
|
iri->index = irstruct->index++;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void add_class_data(ClassDeclaration* target, ClassDeclaration* cd)
|
|
{
|
|
Logger::println("Adding data from class: %s", cd->toChars());
|
|
LOG_SCOPE;
|
|
|
|
// recurse into baseClasses
|
|
if (cd->baseClass)
|
|
{
|
|
add_class_data(target, cd->baseClass);
|
|
//offset = baseClass->structsize;
|
|
}
|
|
|
|
// add members
|
|
Array* arr = cd->members;
|
|
for (int k=0; k < arr->dim; k++) {
|
|
Dsymbol* s = (Dsymbol*)arr->data[k];
|
|
s->toObjFile(0);
|
|
}
|
|
|
|
// add interfaces
|
|
if (cd->vtblInterfaces)
|
|
{
|
|
Logger::println("num vtbl interfaces: %u", cd->vtblInterfaces->dim);
|
|
for (int i = 0; i < cd->vtblInterfaces->dim; i++)
|
|
{
|
|
BaseClass *b = (BaseClass *)cd->vtblInterfaces->data[i];
|
|
assert(b);
|
|
// create new instances only for explicitly derived interfaces
|
|
add_interface(target, b, (cd == target));
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void DtoResolveInterface(InterfaceDeclaration* cd)
|
|
{
|
|
if (cd->ir.resolved) return;
|
|
cd->ir.resolved = true;
|
|
|
|
Logger::println("DtoResolveInterface(%s): %s", cd->toPrettyChars(), cd->loc.toChars());
|
|
LOG_SCOPE;
|
|
|
|
// get the TypeClass
|
|
assert(cd->type->ty == Tclass);
|
|
TypeClass* ts = (TypeClass*)cd->type;
|
|
|
|
// create the IrStruct, we need somewhere to store the classInfo
|
|
assert(!cd->ir.irStruct);
|
|
IrStruct* irstruct = new IrStruct(cd);
|
|
cd->ir.irStruct = irstruct;
|
|
|
|
// handle base interfaces
|
|
if (cd->baseclasses.dim)
|
|
{
|
|
Logger::println("num baseclasses: %u", cd->baseclasses.dim);
|
|
LOG_SCOPE;
|
|
|
|
for (int i=0; i<cd->baseclasses.dim; i++)
|
|
{
|
|
BaseClass* bc = (BaseClass*)cd->baseclasses.data[i];
|
|
Logger::println("baseclass %d: %s", i, bc->base->toChars());
|
|
|
|
InterfaceDeclaration* id = bc->base->isInterfaceDeclaration();
|
|
assert(id);
|
|
|
|
DtoResolveInterface(id);
|
|
|
|
// add to interfaceInfos
|
|
IrInterface* iri = new IrInterface(bc);
|
|
irstruct->interfaceVec.push_back(iri);
|
|
irstruct->classInfoInterfaces.push_back(iri);
|
|
}
|
|
}
|
|
|
|
// create the type
|
|
const LLType* t = LLArrayType::get(getVoidPtrType(), cd->vtbl.dim);
|
|
assert(!ts->ir.type);
|
|
ts->ir.type = new LLPATypeHolder(getPtrToType(t));
|
|
|
|
// request declaration
|
|
gIR->declareList.push_back(cd);
|
|
|
|
// handle members
|
|
// like "nested" interfaces
|
|
Array* arr = cd->members;
|
|
for (int k=0; k < arr->dim; k++) {
|
|
Dsymbol* s = (Dsymbol*)arr->data[k];
|
|
s->toObjFile(0);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// FIXME: this needs to be cleaned up
|
|
|
|
void DtoResolveClass(ClassDeclaration* cd)
|
|
{
|
|
if (InterfaceDeclaration* id = cd->isInterfaceDeclaration())
|
|
{
|
|
DtoResolveInterface(id);
|
|
return;
|
|
}
|
|
|
|
if (cd->ir.resolved) return;
|
|
cd->ir.resolved = true;
|
|
|
|
Logger::println("DtoResolveClass(%s): %s", cd->toPrettyChars(), cd->loc.toChars());
|
|
LOG_SCOPE;
|
|
|
|
//printf("resolve class: %s\n", cd->toPrettyChars());
|
|
|
|
// get the TypeClass
|
|
assert(cd->type->ty == Tclass);
|
|
TypeClass* ts = (TypeClass*)cd->type;
|
|
|
|
// create the IrStruct
|
|
assert(!cd->ir.irStruct);
|
|
IrStruct* irstruct = new IrStruct(cd);
|
|
cd->ir.irStruct = irstruct;
|
|
|
|
// create the type
|
|
ts->ir.type = new LLPATypeHolder(llvm::OpaqueType::get());
|
|
|
|
// if it just a forward declaration?
|
|
if (cd->sizeok != 1)
|
|
{
|
|
// just name the type
|
|
gIR->module->addTypeName(cd->mangle(), ts->ir.type->get());
|
|
return;
|
|
}
|
|
|
|
// resolve the base class
|
|
if (cd->baseClass) {
|
|
DtoResolveClass(cd->baseClass);
|
|
}
|
|
|
|
// push state
|
|
gIR->structs.push_back(irstruct);
|
|
|
|
// add vtable
|
|
irstruct->types.push_back(getPtrToType(irstruct->vtblTy.get()));
|
|
irstruct->index++;
|
|
|
|
// add monitor
|
|
irstruct->types.push_back(getVoidPtrType());
|
|
irstruct->index++;
|
|
|
|
// add class data fields and interface vtables recursively
|
|
add_class_data(cd, cd);
|
|
|
|
// check if errors occured while building interface vtables
|
|
if (global.errors)
|
|
fatal();
|
|
|
|
// create type
|
|
assert(irstruct->index == irstruct->types.size());
|
|
const LLType* structtype = irstruct->build();
|
|
|
|
// refine abstract types for stuff like: class C {C next;}
|
|
llvm::PATypeHolder* spa = ts->ir.type;
|
|
llvm::cast<llvm::OpaqueType>(spa->get())->refineAbstractTypeTo(structtype);
|
|
structtype = isaStruct(spa->get());
|
|
|
|
// name the type
|
|
gIR->module->addTypeName(cd->mangle(), ts->ir.type->get());
|
|
|
|
// refine vtable type
|
|
|
|
// void*[vtbl.dim]
|
|
llvm::cast<llvm::OpaqueType>(irstruct->vtblTy.get())->refineAbstractTypeTo(LLArrayType::get(getVoidPtrType(), cd->vtbl.dim));
|
|
|
|
// log
|
|
// if (Logger::enabled())
|
|
// Logger::cout() << "final class type: " << *ts->ir.type->get() << '\n';
|
|
|
|
// pop state
|
|
gIR->structs.pop_back();
|
|
|
|
// queue declare
|
|
gIR->declareList.push_back(cd);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void DtoDeclareInterface(InterfaceDeclaration* cd)
|
|
{
|
|
if (cd->ir.declared) return;
|
|
cd->ir.declared = true;
|
|
|
|
Logger::println("DtoDeclareInterface(%s): %s", cd->toPrettyChars(), cd->locToChars());
|
|
LOG_SCOPE;
|
|
|
|
assert(cd->ir.irStruct);
|
|
IrStruct* irstruct = cd->ir.irStruct;
|
|
|
|
// get interface info type
|
|
const llvm::StructType* infoTy = DtoInterfaceInfoType();
|
|
|
|
// interface info array
|
|
if (!irstruct->interfaceVec.empty()) {
|
|
// symbol name
|
|
std::string nam = "_D";
|
|
nam.append(cd->mangle());
|
|
nam.append("16__interfaceInfosZ");
|
|
|
|
llvm::GlobalValue::LinkageTypes linkage = DtoLinkage(cd);
|
|
|
|
// resolve array type
|
|
const llvm::ArrayType* arrTy = llvm::ArrayType::get(infoTy, irstruct->interfaceVec.size());
|
|
// declare global
|
|
irstruct->interfaceInfos = new llvm::GlobalVariable(arrTy, true, linkage, NULL, nam, gIR->module);
|
|
|
|
// do each interface info
|
|
unsigned idx = 0;
|
|
size_t n = irstruct->interfaceVec.size();
|
|
for (size_t i=0; i < n; i++)
|
|
{
|
|
IrInterface* iri = irstruct->interfaceVec[i];
|
|
ClassDeclaration* id = iri->decl;
|
|
|
|
// always create interfaceinfos
|
|
LLConstant* idxs[2] = {DtoConstUint(0), DtoConstUint(idx)};
|
|
iri->info = llvm::ConstantExpr::getGetElementPtr(irstruct->interfaceInfos, idxs, 2);
|
|
idx++;
|
|
}
|
|
}
|
|
|
|
// declare the classinfo
|
|
DtoDeclareClassInfo(cd);
|
|
|
|
// request const init
|
|
gIR->constInitList.push_back(cd);
|
|
|
|
// emit typeinfo and request definition
|
|
if (mustDefineSymbol(cd))
|
|
{
|
|
gIR->defineList.push_back(cd);
|
|
DtoTypeInfoOf(cd->type, false);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// FIXME: this needs to be cleaned up
|
|
|
|
void DtoDeclareClass(ClassDeclaration* cd)
|
|
{
|
|
if (InterfaceDeclaration* id = cd->isInterfaceDeclaration())
|
|
{
|
|
DtoDeclareInterface(id);
|
|
return;
|
|
}
|
|
|
|
if (cd->ir.declared) return;
|
|
cd->ir.declared = true;
|
|
|
|
Logger::println("DtoDeclareClass(%s): %s", cd->toPrettyChars(), cd->locToChars());
|
|
LOG_SCOPE;
|
|
|
|
//printf("declare class: %s\n", cd->toPrettyChars());
|
|
|
|
assert(cd->type->ty == Tclass);
|
|
TypeClass* ts = (TypeClass*)cd->type;
|
|
|
|
assert(cd->ir.irStruct);
|
|
IrStruct* irstruct = cd->ir.irStruct;
|
|
|
|
gIR->structs.push_back(irstruct);
|
|
|
|
bool needs_definition = false;
|
|
if (mustDefineSymbol(cd)) {
|
|
needs_definition = true;
|
|
}
|
|
|
|
llvm::GlobalValue::LinkageTypes _linkage = DtoLinkage(cd);
|
|
|
|
// create vtbl symbol
|
|
std::string varname("_D");
|
|
varname.append(cd->mangle());
|
|
varname.append("6__vtblZ");
|
|
irstruct->vtbl = new llvm::GlobalVariable(irstruct->vtblInitTy.get(), true, _linkage, 0, varname, gIR->module);
|
|
|
|
// get interface info type
|
|
const llvm::StructType* infoTy = DtoInterfaceInfoType();
|
|
|
|
// interface info array
|
|
if (!irstruct->interfaceVec.empty()) {
|
|
// 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, irstruct->interfaceVec.size());
|
|
// declare global
|
|
irstruct->interfaceInfos = new llvm::GlobalVariable(arrTy, true, _linkage, NULL, nam, gIR->module);
|
|
}
|
|
|
|
// DMD gives abstract classes a full ClassInfo, so we do it as well
|
|
|
|
// interface vtables
|
|
unsigned idx = 0;
|
|
for (IrStruct::InterfaceVectorIter i=irstruct->interfaceVec.begin(); i!=irstruct->interfaceVec.end(); ++i)
|
|
{
|
|
IrInterface* iri = *i;
|
|
ClassDeclaration* id = iri->decl;
|
|
|
|
std::string nam("_D");
|
|
nam.append(cd->mangle());
|
|
nam.append("11__interface");
|
|
nam.append(id->mangle());
|
|
nam.append("6__vtblZ");
|
|
|
|
iri->vtbl = new llvm::GlobalVariable(iri->vtblInitTy.get(), true, _linkage, 0, nam, gIR->module);
|
|
|
|
// always set the interface info as it's need as the first vtbl entry
|
|
LLConstant* idxs[2] = {DtoConstUint(0), DtoConstUint(idx)};
|
|
iri->info = llvm::ConstantExpr::getGetElementPtr(irstruct->interfaceInfos, idxs, 2);
|
|
idx++;
|
|
}
|
|
|
|
// initZ init
|
|
std::string initname("_D");
|
|
initname.append(cd->mangle());
|
|
initname.append("6__initZ");
|
|
|
|
// initZ global
|
|
llvm::GlobalVariable* initvar = new llvm::GlobalVariable(irstruct->initOpaque.get(), true, _linkage, NULL, initname, gIR->module);
|
|
irstruct->init = initvar;
|
|
|
|
gIR->structs.pop_back();
|
|
|
|
// request const init
|
|
gIR->constInitList.push_back(cd);
|
|
|
|
// define ? (set initializers)
|
|
if (needs_definition)
|
|
gIR->defineList.push_back(cd);
|
|
|
|
// classinfo
|
|
DtoDeclareClassInfo(cd);
|
|
|
|
// do typeinfo ?
|
|
if (needs_definition)
|
|
DtoTypeInfoOf(cd->type, false);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
// adds data fields and interface vtables to the constant initializer of class cd
|
|
static size_t init_class_initializer(std::vector<LLConstant*>& inits, ClassDeclaration* target, ClassDeclaration* cd, size_t offsetbegin)
|
|
{
|
|
// first do baseclasses
|
|
if (cd->baseClass)
|
|
{
|
|
offsetbegin = init_class_initializer(inits, target, cd->baseClass, offsetbegin);
|
|
}
|
|
|
|
Logger::println("adding data of %s to %s starting at %lu", cd->toChars(), target->toChars(), offsetbegin);
|
|
LOG_SCOPE;
|
|
|
|
// add default fields
|
|
VarDeclaration** fields = (VarDeclaration**)cd->fields.data;
|
|
size_t nfields = cd->fields.dim;
|
|
|
|
std::vector<VarDeclaration*> defVars;
|
|
defVars.reserve(nfields);
|
|
|
|
size_t lastoffset = offsetbegin;
|
|
size_t lastsize = 0;
|
|
|
|
// find fields that contribute to default
|
|
for (size_t i=0; i<nfields; i++)
|
|
{
|
|
VarDeclaration* var = fields[i];
|
|
// only add vars that don't overlap
|
|
size_t offset = var->offset;
|
|
size_t size = var->type->size();
|
|
if (offset >= lastoffset+lastsize)
|
|
{
|
|
Logger::println(" added %s", var->toChars());
|
|
lastoffset = offset;
|
|
lastsize = size;
|
|
defVars.push_back(var);
|
|
}
|
|
else
|
|
{
|
|
Logger::println(" skipped %s at offset %u, current pos is %lu", var->toChars(), var->offset, lastoffset+lastsize);
|
|
}
|
|
}
|
|
|
|
// reset offsets, we're going from beginning again
|
|
lastoffset = offsetbegin;
|
|
lastsize = 0;
|
|
|
|
// go through the default vars and build the default constant initializer
|
|
// adding zeros along the way to live up to alignment expectations
|
|
size_t nvars = defVars.size();
|
|
for (size_t i=0; i<nvars; i++)
|
|
{
|
|
VarDeclaration* var = defVars[i];
|
|
|
|
Logger::println("field %s %s = %s : +%u", var->type->toChars(), var->toChars(), var->init ? var->init->toChars() : var->type->defaultInit(var->loc)->toChars(), var->offset);
|
|
|
|
// get offset and size
|
|
size_t offset = var->offset;
|
|
size_t size = var->type->size();
|
|
|
|
// is there space in between last last offset and this one?
|
|
// if so, fill it with zeros
|
|
if (offset > lastoffset+lastsize)
|
|
{
|
|
size_t pos = lastoffset + lastsize;
|
|
addZeros(inits, pos, offset);
|
|
}
|
|
|
|
// add the field
|
|
// and build its constant initializer lazily
|
|
if (!var->ir.irField->constInit)
|
|
var->ir.irField->constInit = DtoConstInitializer(var->loc, var->type, var->init);
|
|
inits.push_back(var->ir.irField->constInit);
|
|
|
|
lastoffset = offset;
|
|
lastsize = var->type->size();
|
|
}
|
|
|
|
// if it's a class, and it implements interfaces, add the vtables - as found in the target class!
|
|
IrStruct* irstruct = target->ir.irStruct;
|
|
|
|
size_t nvtbls = cd->vtblInterfaces->dim;
|
|
for(size_t i=0; i<nvtbls; i++)
|
|
{
|
|
BaseClass* bc = (BaseClass*)cd->vtblInterfaces->data[i];
|
|
IrStruct::InterfaceMap::iterator iter = irstruct->interfaceMap.find(bc->base);
|
|
assert(iter != irstruct->interfaceMap.end());
|
|
|
|
IrInterface* iri = iter->second;
|
|
if (iri->vtbl)
|
|
inits.push_back(iri->vtbl);
|
|
else // abstract impl
|
|
inits.push_back(getNullPtr(getVoidPtrType()));
|
|
|
|
lastoffset += lastsize;
|
|
lastsize = PTRSIZE;
|
|
}
|
|
|
|
// return next offset
|
|
return lastoffset + lastsize;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
// build the vtable initializer for class cd
|
|
static void init_class_vtbl_initializer(ClassDeclaration* cd)
|
|
{
|
|
// generate vtable initializer
|
|
std::vector<LLConstant*> sinits(cd->vtbl.dim, NULL);
|
|
|
|
IrStruct* irstruct = cd->ir.irStruct;
|
|
|
|
assert(cd->vtbl.dim > 1);
|
|
|
|
// first entry always classinfo
|
|
assert(irstruct->classInfo);
|
|
sinits[0] = DtoBitCast(irstruct->classInfo, DtoType(ClassDeclaration::classinfo->type));
|
|
|
|
// add virtual functions
|
|
for (int k=1; k < cd->vtbl.dim; k++)
|
|
{
|
|
Dsymbol* dsym = (Dsymbol*)cd->vtbl.data[k];
|
|
assert(dsym);
|
|
|
|
// Logger::println("vtbl[%d] = %s", k, dsym->toChars());
|
|
|
|
FuncDeclaration* fd = dsym->isFuncDeclaration();
|
|
assert(fd);
|
|
|
|
// if function is abstract,
|
|
// or class is abstract, and func has no body,
|
|
// emit a null vtbl entry
|
|
if (fd->isAbstract() || (cd->isAbstract() && !fd->fbody))
|
|
{
|
|
sinits[k] = getNullPtr(getVoidPtrType());
|
|
}
|
|
else
|
|
{
|
|
DtoForceDeclareDsymbol(fd);
|
|
assert(fd->ir.irFunc->func);
|
|
sinits[k] = fd->ir.irFunc->func;
|
|
}
|
|
|
|
// if (Logger::enabled())
|
|
// Logger::cout() << "vtbl[" << k << "] = " << *sinits[k] << std::endl;
|
|
}
|
|
|
|
irstruct->constVtbl = LLConstantStruct::get(sinits);
|
|
|
|
// refine type
|
|
llvm::cast<llvm::OpaqueType>(irstruct->vtblInitTy.get())->refineAbstractTypeTo(irstruct->constVtbl->getType());
|
|
|
|
// if (Logger::enabled())
|
|
// Logger::cout() << "vtbl initializer: " << *irstruct->constVtbl << std::endl;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void init_class_interface_vtbl_initializers(ClassDeclaration* cd)
|
|
{
|
|
IrStruct* irstruct = cd->ir.irStruct;
|
|
|
|
// don't do anything if list is empty
|
|
if (irstruct->interfaceVec.empty())
|
|
return;
|
|
|
|
std::vector<LLConstant*> inits;
|
|
std::vector<LLConstant*> infoInits(3);
|
|
|
|
// go through each interface
|
|
size_t ninter = irstruct->interfaceVec.size();
|
|
for (size_t i=0; i<ninter; i++)
|
|
{
|
|
IrInterface* iri = irstruct->interfaceVec[i];
|
|
Logger::println("interface %s", iri->decl->toChars());
|
|
|
|
// build vtable intializer for this interface implementation
|
|
Array& arr = iri->vtblDecls;
|
|
size_t narr = arr.dim;
|
|
|
|
if (narr > 0)
|
|
{
|
|
inits.resize(narr, NULL);
|
|
|
|
// first is always the interface info
|
|
assert(iri->info);
|
|
inits[0] = iri->info;
|
|
|
|
// build vtable
|
|
for (size_t j=1; j < narr; j++)
|
|
{
|
|
Dsymbol* dsym = (Dsymbol*)arr.data[j];
|
|
if (!dsym)
|
|
{
|
|
inits[j] = getNullPtr(getVoidPtrType());
|
|
continue;
|
|
}
|
|
|
|
//Logger::println("ivtbl[%d] = %s", j, dsym->toChars());
|
|
|
|
// must all be functions
|
|
FuncDeclaration* fd = dsym->isFuncDeclaration();
|
|
assert(fd);
|
|
|
|
if (fd->isAbstract())
|
|
inits[j] = getNullPtr(getVoidPtrType());
|
|
else
|
|
{
|
|
DtoForceDeclareDsymbol(fd);
|
|
|
|
assert(fd->ir.irFunc->func);
|
|
inits[j] = fd->ir.irFunc->func;
|
|
}
|
|
|
|
//if (Logger::enabled())
|
|
// Logger::cout() << "ivtbl[" << j << "] = " << *inits[j] << std::endl;
|
|
}
|
|
|
|
// build the constant
|
|
iri->vtblInit = LLConstantStruct::get(inits);
|
|
}
|
|
|
|
// build the interface info for ClassInfo
|
|
// generate interface info initializer
|
|
|
|
DtoForceDeclareDsymbol(iri->decl);
|
|
|
|
// classinfo
|
|
IrStruct* iris = iri->decl->ir.irStruct;
|
|
assert(iris);
|
|
assert(iris->classInfo);
|
|
infoInits[0] = DtoBitCast(iris->classInfo, DtoType(ClassDeclaration::classinfo->type));
|
|
|
|
// vtbl
|
|
LLConstant* c;
|
|
if (iri->vtbl)
|
|
c = llvm::ConstantExpr::getBitCast(iri->vtbl, getPtrToType(getVoidPtrType()));
|
|
else
|
|
c = getNullPtr(getPtrToType(getVoidPtrType()));
|
|
infoInits[1] = DtoConstSlice(DtoConstSize_t(narr), c);
|
|
|
|
// offset
|
|
size_t ioff;
|
|
if (iri->index == 0)
|
|
ioff = 0;
|
|
else
|
|
ioff = gTargetData->getStructLayout(isaStruct(cd->type->ir.type->get()))->getElementOffset(iri->index);
|
|
|
|
Logger::println("DMD interface offset: %d, LLVM interface offset: %lu", iri->base->offset, ioff);
|
|
assert(iri->base->offset == ioff);
|
|
infoInits[2] = DtoConstUint(ioff);
|
|
|
|
// create interface info initializer constant
|
|
iri->infoInit = llvm::cast<llvm::ConstantStruct>(llvm::ConstantStruct::get(infoInits));
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void DtoConstInitInterface(InterfaceDeclaration* cd)
|
|
{
|
|
if (cd->ir.initialized) return;
|
|
cd->ir.initialized = true;
|
|
|
|
Logger::println("DtoConstInitClass(%s): %s", cd->toPrettyChars(), cd->loc.toChars());
|
|
LOG_SCOPE;
|
|
|
|
init_class_interface_vtbl_initializers(cd);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
void DtoConstInitClass(ClassDeclaration* cd)
|
|
{
|
|
if (InterfaceDeclaration* it = cd->isInterfaceDeclaration())
|
|
{
|
|
DtoConstInitInterface(it);
|
|
return;
|
|
}
|
|
|
|
if (cd->ir.initialized) return;
|
|
cd->ir.initialized = true;
|
|
|
|
Logger::println("DtoConstInitClass(%s): %s", cd->toPrettyChars(), cd->loc.toChars());
|
|
LOG_SCOPE;
|
|
|
|
assert(!cd->isInterfaceDeclaration());
|
|
|
|
// make sure the baseclass is const initialized
|
|
if (cd->baseClass)
|
|
DtoForceConstInitDsymbol(cd->baseClass);
|
|
|
|
// get IrStruct
|
|
IrStruct* irstruct = cd->ir.irStruct;
|
|
gIR->structs.push_back(irstruct);
|
|
|
|
// get types
|
|
TypeClass* tc = (TypeClass*)cd->type;
|
|
const llvm::StructType* structtype = isaStruct(tc->ir.type->get());
|
|
assert(structtype);
|
|
const llvm::ArrayType* vtbltype = isaArray(irstruct->vtblTy.get());
|
|
assert(vtbltype);
|
|
|
|
// build initializer list
|
|
std::vector<LLConstant*> inits;
|
|
inits.reserve(irstruct->varDecls.size());
|
|
|
|
// vtable is always first
|
|
assert(irstruct->vtbl != 0);
|
|
inits.push_back(irstruct->vtbl);
|
|
|
|
// then comes monitor
|
|
inits.push_back(LLConstant::getNullValue(getVoidPtrType()));
|
|
|
|
// recursively do data and interface vtables
|
|
init_class_initializer(inits, cd, cd, 2 * PTRSIZE);
|
|
|
|
// build vtable initializer
|
|
init_class_vtbl_initializer(cd);
|
|
|
|
// build interface vtables
|
|
init_class_interface_vtbl_initializers(cd);
|
|
|
|
// build constant from inits
|
|
assert(!irstruct->constInit);
|
|
irstruct->constInit = LLConstantStruct::get(inits); // classes are never packed
|
|
|
|
// refine __initZ global type to the one of the initializer
|
|
llvm::cast<llvm::OpaqueType>(irstruct->initOpaque.get())->refineAbstractTypeTo(irstruct->constInit->getType());
|
|
|
|
// if (Logger::enabled())
|
|
// {
|
|
// Logger::cout() << "class " << cd->toChars() << std::endl;
|
|
// Logger::cout() << "type " << *cd->type->ir.type->get() << std::endl;
|
|
// Logger::cout() << "initializer " << *irstruct->constInit << std::endl;
|
|
// }
|
|
|
|
gIR->structs.pop_back();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void DefineInterfaceInfos(IrStruct* irstruct)
|
|
{
|
|
// always do interface info array when possible
|
|
std::vector<LLConstant*> infoInits;
|
|
|
|
size_t n = irstruct->interfaceVec.size();
|
|
infoInits.reserve(n);
|
|
|
|
for (size_t i=0; i < n; i++)
|
|
{
|
|
IrInterface* iri = irstruct->interfaceVec[i];
|
|
assert(iri->infoInit);
|
|
infoInits.push_back(iri->infoInit);
|
|
}
|
|
|
|
// set initializer
|
|
if (!infoInits.empty())
|
|
{
|
|
const LLArrayType* arrty = LLArrayType::get(infoInits[0]->getType(), infoInits.size());
|
|
LLConstant* arrInit = llvm::ConstantArray::get(arrty, infoInits);
|
|
irstruct->interfaceInfos->setInitializer(arrInit);
|
|
}
|
|
else
|
|
{
|
|
assert(irstruct->interfaceInfos == NULL);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void DtoDefineInterface(InterfaceDeclaration* cd)
|
|
{
|
|
if (cd->ir.defined) return;
|
|
cd->ir.defined = true;
|
|
|
|
Logger::println("DtoDefineClass(%s): %s", cd->toPrettyChars(), cd->loc.toChars());
|
|
LOG_SCOPE;
|
|
|
|
// defined interface infos
|
|
DefineInterfaceInfos(cd->ir.irStruct);
|
|
|
|
// define the classinfo
|
|
if (mustDefineSymbol(cd))
|
|
{
|
|
DtoDefineClassInfo(cd);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// FIXME: clean this up
|
|
|
|
void DtoDefineClass(ClassDeclaration* cd)
|
|
{
|
|
if (InterfaceDeclaration* id = cd->isInterfaceDeclaration())
|
|
{
|
|
DtoDefineInterface(id);
|
|
return;
|
|
}
|
|
|
|
if (cd->ir.defined) return;
|
|
cd->ir.defined = true;
|
|
|
|
Logger::println("DtoDefineClass(%s): %s", cd->toPrettyChars(), cd->loc.toChars());
|
|
LOG_SCOPE;
|
|
|
|
// get the struct (class) type
|
|
assert(cd->type->ty == Tclass);
|
|
TypeClass* ts = (TypeClass*)cd->type;
|
|
|
|
IrStruct* irstruct = cd->ir.irStruct;
|
|
|
|
assert(mustDefineSymbol(cd));
|
|
|
|
// sanity check
|
|
assert(irstruct->init);
|
|
assert(irstruct->constInit);
|
|
assert(irstruct->vtbl);
|
|
assert(irstruct->constVtbl);
|
|
|
|
// if (Logger::enabled())
|
|
// {
|
|
// Logger::cout() << "initZ: " << *irstruct->init << std::endl;
|
|
// Logger::cout() << "cinitZ: " << *irstruct->constInit << std::endl;
|
|
// Logger::cout() << "vtblZ: " << *irstruct->vtbl << std::endl;
|
|
// Logger::cout() << "cvtblZ: " << *irstruct->constVtbl << std::endl;
|
|
// }
|
|
|
|
// set initializers
|
|
irstruct->init->setInitializer(irstruct->constInit);
|
|
irstruct->vtbl->setInitializer(irstruct->constVtbl);
|
|
|
|
// initialize interface vtables
|
|
size_t n = irstruct->interfaceVec.size();
|
|
for (size_t i=0; i<n; i++)
|
|
{
|
|
IrInterface* iri = irstruct->interfaceVec[i];
|
|
Logger::println("interface %s", iri->base->base->toChars());
|
|
assert(iri->vtblInit);
|
|
|
|
// refine the init type
|
|
llvm::cast<llvm::OpaqueType>(iri->vtblInitTy.get())->refineAbstractTypeTo(iri->vtblInit->getType());
|
|
|
|
// apply initializer
|
|
assert(iri->vtbl);
|
|
iri->vtbl->setInitializer(iri->vtblInit);
|
|
}
|
|
|
|
DefineInterfaceInfos(irstruct);
|
|
|
|
// generate classinfo
|
|
DtoDefineClassInfo(cd);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* DtoNewClass(Loc loc, TypeClass* tc, NewExp* newexp)
|
|
{
|
|
// resolve type
|
|
DtoForceDeclareDsymbol(tc->sym);
|
|
|
|
// allocate
|
|
LLValue* mem;
|
|
if (newexp->onstack)
|
|
{
|
|
mem = DtoAlloca(DtoType(tc)->getContainedType(0), ".newclass_alloca");
|
|
}
|
|
// custom allocator
|
|
else if (newexp->allocator)
|
|
{
|
|
DtoForceDeclareDsymbol(newexp->allocator);
|
|
DFuncValue dfn(newexp->allocator, newexp->allocator->ir.irFunc->func);
|
|
DValue* res = DtoCallFunction(newexp->loc, NULL, &dfn, newexp->newargs);
|
|
mem = DtoBitCast(res->getRVal(), DtoType(tc), ".newclass_custom");
|
|
}
|
|
// default allocator
|
|
else
|
|
{
|
|
llvm::Function* fn = LLVM_D_GetRuntimeFunction(gIR->module, "_d_allocclass");
|
|
LLConstant* ci = DtoBitCast(tc->sym->ir.irStruct->classInfo, DtoType(ClassDeclaration::classinfo->type));
|
|
mem = gIR->CreateCallOrInvoke(fn, ci, ".newclass_gc_alloc").getInstruction();
|
|
mem = DtoBitCast(mem, DtoType(tc), ".newclass_gc");
|
|
}
|
|
|
|
// init
|
|
DtoInitClass(tc, mem);
|
|
|
|
// init inner-class outer reference
|
|
if (newexp->thisexp)
|
|
{
|
|
Logger::println("Resolving outer class");
|
|
LOG_SCOPE;
|
|
DValue* thisval = newexp->thisexp->toElem(gIR);
|
|
size_t idx = tc->sym->vthis->ir.irField->index;
|
|
LLValue* src = thisval->getRVal();
|
|
LLValue* dst = DtoGEPi(mem,0,idx,"tmp");
|
|
if (Logger::enabled())
|
|
Logger::cout() << "dst: " << *dst << "\nsrc: " << *src << '\n';
|
|
DtoStore(src, dst);
|
|
}
|
|
// set the context for nested classes
|
|
else if (tc->sym->isNested() && tc->sym->vthis)
|
|
{
|
|
Logger::println("Resolving nested context");
|
|
LOG_SCOPE;
|
|
|
|
// get context
|
|
LLValue* nest = DtoNestedContext(loc, tc->sym);
|
|
|
|
// store into right location
|
|
size_t idx = tc->sym->vthis->ir.irField->index;
|
|
LLValue* gep = DtoGEPi(mem,0,idx,"tmp");
|
|
DtoStore(DtoBitCast(nest, gep->getType()->getContainedType(0)), gep);
|
|
}
|
|
|
|
// call constructor
|
|
if (newexp->member)
|
|
{
|
|
Logger::println("Calling constructor");
|
|
assert(newexp->arguments != NULL);
|
|
DtoForceDeclareDsymbol(newexp->member);
|
|
DFuncValue dfn(newexp->member, newexp->member->ir.irFunc->func, mem);
|
|
return DtoCallFunction(newexp->loc, tc, &dfn, newexp->arguments);
|
|
}
|
|
|
|
// return default constructed class
|
|
return new DImValue(tc, mem);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void DtoInitClass(TypeClass* tc, LLValue* dst)
|
|
{
|
|
DtoForceConstInitDsymbol(tc->sym);
|
|
|
|
size_t presz = 2*getTypePaddedSize(DtoSize_t());
|
|
uint64_t n = getTypePaddedSize(tc->ir.type->get()) - presz;
|
|
|
|
// set vtable field seperately, this might give better optimization
|
|
assert(tc->sym->ir.irStruct->vtbl);
|
|
LLValue* tmp = DtoGEPi(dst,0,0,"vtbl");
|
|
LLValue* val = DtoBitCast(tc->sym->ir.irStruct->vtbl, tmp->getType()->getContainedType(0));
|
|
DtoStore(val, tmp);
|
|
|
|
// monitor always defaults to zero
|
|
tmp = DtoGEPi(dst,0,1,"monitor");
|
|
val = llvm::Constant::getNullValue(tmp->getType()->getContainedType(0));
|
|
DtoStore(val, tmp);
|
|
|
|
// done?
|
|
if (n == 0)
|
|
return;
|
|
|
|
// copy the rest from the static initializer
|
|
assert(tc->sym->ir.irStruct->init);
|
|
|
|
LLValue* dstarr = DtoGEPi(dst,0,2,"tmp");
|
|
LLValue* srcarr = DtoGEPi(tc->sym->ir.irStruct->init,0,2,"tmp");
|
|
|
|
DtoMemCpy(dstarr, srcarr, DtoConstSize_t(n));
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void DtoFinalizeClass(LLValue* inst)
|
|
{
|
|
// get runtime function
|
|
llvm::Function* fn = LLVM_D_GetRuntimeFunction(gIR->module, "_d_callfinalizer");
|
|
// build args
|
|
LLSmallVector<LLValue*,1> arg;
|
|
arg.push_back(DtoBitCast(inst, fn->getFunctionType()->getParamType(0), ".tmp"));
|
|
// call
|
|
gIR->CreateCallOrInvoke(fn, arg.begin(), arg.end(), "");
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* DtoCastClass(DValue* val, Type* _to)
|
|
{
|
|
Logger::println("DtoCastClass(%s, %s)", val->getType()->toChars(), _to->toChars());
|
|
LOG_SCOPE;
|
|
|
|
Type* to = _to->toBasetype();
|
|
|
|
// class -> pointer
|
|
if (to->ty == Tpointer) {
|
|
Logger::println("to pointer");
|
|
const LLType* tolltype = DtoType(_to);
|
|
LLValue* rval = DtoBitCast(val->getRVal(), tolltype);
|
|
return new DImValue(_to, rval);
|
|
}
|
|
// class -> bool
|
|
else if (to->ty == Tbool) {
|
|
Logger::println("to bool");
|
|
LLValue* llval = val->getRVal();
|
|
LLValue* zero = LLConstant::getNullValue(llval->getType());
|
|
return new DImValue(_to, gIR->ir->CreateICmpNE(llval, zero, "tmp"));
|
|
}
|
|
|
|
// must be class/interface
|
|
assert(to->ty == Tclass);
|
|
TypeClass* tc = (TypeClass*)to;
|
|
|
|
// from type
|
|
Type* from = val->getType()->toBasetype();
|
|
TypeClass* fc = (TypeClass*)from;
|
|
|
|
// x -> interface
|
|
if (InterfaceDeclaration* it = tc->sym->isInterfaceDeclaration()) {
|
|
Logger::println("to interface");
|
|
// interface -> interface
|
|
if (fc->sym->isInterfaceDeclaration()) {
|
|
Logger::println("from interface");
|
|
return DtoDynamicCastInterface(val, _to);
|
|
}
|
|
// class -> interface - static cast
|
|
else if (it->isBaseOf(fc->sym,NULL)) {
|
|
Logger::println("static down cast");
|
|
// get the from class
|
|
ClassDeclaration* cd = fc->sym->isClassDeclaration();
|
|
DtoResolveClass(cd); // add this
|
|
IrStruct* irstruct = cd->ir.irStruct;
|
|
// find interface impl
|
|
IrStruct::InterfaceMapIter iriter = irstruct->interfaceMap.find(it);
|
|
assert(iriter != irstruct->interfaceMap.end());
|
|
IrInterface* iri = iriter->second;
|
|
// offset pointer
|
|
LLValue* v = val->getRVal();
|
|
v = DtoGEPi(v, 0, iri->index);
|
|
if (Logger::enabled())
|
|
{
|
|
Logger::cout() << "V = " << *v << std::endl;
|
|
Logger::cout() << "T = " << *DtoType(_to) << std::endl;
|
|
}
|
|
v = DtoBitCast(v, DtoType(_to));
|
|
// return r-value
|
|
return new DImValue(_to, v);
|
|
}
|
|
// class -> interface
|
|
else {
|
|
Logger::println("from object");
|
|
return DtoDynamicCastObject(val, _to);
|
|
}
|
|
}
|
|
// x -> class
|
|
else {
|
|
Logger::println("to class");
|
|
int poffset;
|
|
// interface -> class
|
|
if (fc->sym->isInterfaceDeclaration()) {
|
|
Logger::println("interface cast");
|
|
return DtoDynamicCastInterface(val, _to);
|
|
}
|
|
// class -> class - static down cast
|
|
else if (tc->sym->isBaseOf(fc->sym,NULL)) {
|
|
Logger::println("static down cast");
|
|
const LLType* tolltype = DtoType(_to);
|
|
LLValue* rval = DtoBitCast(val->getRVal(), tolltype);
|
|
return new DImValue(_to, rval);
|
|
}
|
|
// class -> class - dynamic up cast
|
|
else {
|
|
Logger::println("dynamic up cast");
|
|
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<LLValue*> args;
|
|
|
|
// Object o
|
|
LLValue* obj = val->getRVal();
|
|
obj = DtoBitCast(obj, funcTy->getParamType(0));
|
|
assert(funcTy->getParamType(0) == obj->getType());
|
|
|
|
// ClassInfo c
|
|
TypeClass* to = (TypeClass*)_to->toBasetype();
|
|
DtoForceDeclareDsymbol(to->sym);
|
|
assert(to->sym->ir.irStruct->classInfo);
|
|
LLValue* cinfo = to->sym->ir.irStruct->classInfo;
|
|
// unfortunately this is needed as the implementation of object differs somehow from the declaration
|
|
// this could happen in user code as well :/
|
|
cinfo = DtoBitCast(cinfo, funcTy->getParamType(1));
|
|
assert(funcTy->getParamType(1) == cinfo->getType());
|
|
|
|
// call it
|
|
LLValue* ret = gIR->CreateCallOrInvoke2(func, obj, cinfo, "tmp").getInstruction();
|
|
|
|
// 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
|
|
LLValue* tmp = val->getRVal();
|
|
tmp = DtoBitCast(tmp, funcTy->getParamType(0));
|
|
|
|
// call it
|
|
LLValue* ret = gIR->CreateCallOrInvoke(func, tmp, "tmp").getInstruction();
|
|
|
|
// cast return value
|
|
if (to != NULL)
|
|
ret = DtoBitCast(ret, DtoType(to));
|
|
else
|
|
to = ClassDeclaration::object->type;
|
|
|
|
return new DImValue(to, ret);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* DtoDynamicCastInterface(DValue* val, Type* _to)
|
|
{
|
|
// call:
|
|
// Object _d_interface_cast(void* p, ClassInfo c)
|
|
|
|
DtoForceDeclareDsymbol(ClassDeclaration::object);
|
|
DtoForceDeclareDsymbol(ClassDeclaration::classinfo);
|
|
|
|
llvm::Function* func = LLVM_D_GetRuntimeFunction(gIR->module, "_d_interface_cast");
|
|
const llvm::FunctionType* funcTy = func->getFunctionType();
|
|
|
|
std::vector<LLValue*> args;
|
|
|
|
// void* p
|
|
LLValue* ptr = val->getRVal();
|
|
ptr = DtoBitCast(ptr, funcTy->getParamType(0));
|
|
|
|
// ClassInfo c
|
|
TypeClass* to = (TypeClass*)_to->toBasetype();
|
|
DtoForceDeclareDsymbol(to->sym);
|
|
assert(to->sym->ir.irStruct->classInfo);
|
|
LLValue* cinfo = to->sym->ir.irStruct->classInfo;
|
|
// unfortunately this is needed as the implementation of object differs somehow from the declaration
|
|
// this could happen in user code as well :/
|
|
cinfo = DtoBitCast(cinfo, funcTy->getParamType(1));
|
|
|
|
// call it
|
|
LLValue* ret = gIR->CreateCallOrInvoke2(func, ptr, cinfo, "tmp").getInstruction();
|
|
|
|
// cast return value
|
|
ret = DtoBitCast(ret, DtoType(_to));
|
|
|
|
return new DImValue(_to, ret);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
LLValue* DtoIndexClass(LLValue* src, ClassDeclaration* cd, VarDeclaration* vd)
|
|
{
|
|
Logger::println("indexing class field %s:", vd->toPrettyChars());
|
|
LOG_SCOPE;
|
|
|
|
if (Logger::enabled())
|
|
Logger::cout() << "src: " << *src << '\n';
|
|
|
|
// make sure class is resolved
|
|
DtoResolveClass(cd);
|
|
|
|
// vd must be a field
|
|
IrField* field = vd->ir.irField;
|
|
assert(field);
|
|
|
|
// get the start pointer
|
|
const LLType* st = DtoType(cd->type);
|
|
// cast to the struct type
|
|
src = DtoBitCast(src, st);
|
|
|
|
// gep to the index
|
|
if (Logger::enabled())
|
|
{
|
|
Logger::cout() << "src2: " << *src << '\n';
|
|
Logger::cout() << "index: " << field->index << '\n';
|
|
Logger::cout() << "srctype: " << *src->getType() << '\n';
|
|
}
|
|
LLValue* val = DtoGEPi(src, 0, field->index);
|
|
|
|
// do we need to offset further? (union area)
|
|
if (field->unionOffset)
|
|
{
|
|
// cast to void*
|
|
val = DtoBitCast(val, getVoidPtrType());
|
|
// offset
|
|
val = DtoGEPi1(val, field->unionOffset);
|
|
}
|
|
|
|
// cast it to the right type
|
|
val = DtoBitCast(val, getPtrToType(DtoType(vd->type)));
|
|
|
|
if (Logger::enabled())
|
|
Logger::cout() << "value: " << *val << '\n';
|
|
|
|
return val;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
LLValue* DtoVirtualFunctionPointer(DValue* inst, FuncDeclaration* fdecl)
|
|
{
|
|
// sanity checks
|
|
assert(fdecl->isVirtual());
|
|
assert(!fdecl->isFinal());
|
|
assert(fdecl->vtblIndex > 0); // 0 is always ClassInfo/Interface*
|
|
assert(inst->getType()->toBasetype()->ty == Tclass);
|
|
|
|
// get instance
|
|
LLValue* vthis = inst->getRVal();
|
|
if (Logger::enabled())
|
|
Logger::cout() << "vthis: " << *vthis << '\n';
|
|
|
|
LLValue* funcval = vthis;
|
|
// get the vtbl for objects
|
|
if (!fdecl->isMember()->isInterfaceDeclaration())
|
|
funcval = DtoGEPi(funcval, 0, 0, "tmp");
|
|
// load vtbl ptr
|
|
funcval = DtoLoad(funcval);
|
|
// index vtbl
|
|
funcval = DtoGEPi(funcval, 0, fdecl->vtblIndex, fdecl->toChars());
|
|
// load funcptr
|
|
funcval = DtoLoad(funcval);
|
|
|
|
if (Logger::enabled())
|
|
Logger::cout() << "funcval: " << *funcval << '\n';
|
|
|
|
// cast to final funcptr type
|
|
funcval = DtoBitCast(funcval, getPtrToType(DtoType(fdecl->type)));
|
|
if (Logger::enabled())
|
|
Logger::cout() << "funcval casted: " << *funcval << '\n';
|
|
|
|
return funcval;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void DtoDeclareClassInfo(ClassDeclaration* cd)
|
|
{
|
|
IrStruct* irstruct = cd->ir.irStruct;
|
|
|
|
if (irstruct->classInfoDeclared) return;
|
|
irstruct->classInfoDeclared = true;
|
|
|
|
Logger::println("DtoDeclareClassInfo(%s)", cd->toChars());
|
|
LOG_SCOPE;
|
|
|
|
// resovle ClassInfo
|
|
ClassDeclaration* cinfo = ClassDeclaration::classinfo;
|
|
DtoResolveClass(cinfo);
|
|
|
|
// do the mangle
|
|
std::string gname("_D");
|
|
gname.append(cd->mangle());
|
|
if (!cd->isInterfaceDeclaration())
|
|
gname.append("7__ClassZ");
|
|
else
|
|
gname.append("11__InterfaceZ");
|
|
|
|
// create global
|
|
irstruct->classInfo = new llvm::GlobalVariable(irstruct->classInfoOpaque.get(), false, DtoLinkage(cd), NULL, gname, gIR->module);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#if GENERATE_OFFTI
|
|
|
|
// build a single element for the OffsetInfo[] of ClassInfo
|
|
static LLConstant* build_offti_entry(ClassDeclaration* cd, VarDeclaration* vd)
|
|
{
|
|
std::vector<LLConstant*> inits(2);
|
|
|
|
// size_t offset;
|
|
//
|
|
assert(vd->ir.irField);
|
|
// grab the offset from llvm and the formal class type
|
|
size_t offset = gTargetData->getStructLayout(isaStruct(cd->type->ir.type->get()))->getElementOffset(vd->ir.irField->index);
|
|
// offset nested struct/union fields
|
|
offset += vd->ir.irField->unionOffset;
|
|
|
|
// assert that it matches DMD
|
|
Logger::println("offsets: %lu vs %u", offset, vd->offset);
|
|
assert(offset == vd->offset);
|
|
|
|
inits[0] = DtoConstSize_t(offset);
|
|
|
|
// TypeInfo ti;
|
|
inits[1] = DtoTypeInfoOf(vd->type, true);
|
|
|
|
// done
|
|
return llvm::ConstantStruct::get(inits);
|
|
}
|
|
|
|
static LLConstant* build_offti_array(ClassDeclaration* cd, const LLType* arrayT)
|
|
{
|
|
IrStruct* irstruct = cd->ir.irStruct;
|
|
|
|
size_t nvars = irstruct->varDecls.size();
|
|
std::vector<LLConstant*> arrayInits(nvars);
|
|
|
|
for (size_t i=0; i<nvars; i++)
|
|
{
|
|
arrayInits[i] = build_offti_entry(cd, irstruct->varDecls[i]);
|
|
}
|
|
|
|
LLConstant* size = DtoConstSize_t(nvars);
|
|
LLConstant* ptr;
|
|
|
|
if (nvars == 0)
|
|
return LLConstant::getNullValue( arrayT );
|
|
|
|
// array type
|
|
const llvm::ArrayType* arrTy = llvm::ArrayType::get(arrayInits[0]->getType(), nvars);
|
|
LLConstant* arrInit = llvm::ConstantArray::get(arrTy, arrayInits);
|
|
|
|
// mangle
|
|
std::string name(cd->type->vtinfo->toChars());
|
|
name.append("__OffsetTypeInfos");
|
|
|
|
// create symbol
|
|
llvm::GlobalVariable* gvar = new llvm::GlobalVariable(arrTy,true,DtoInternalLinkage(cd),arrInit,name,gIR->module);
|
|
ptr = DtoBitCast(gvar, getPtrToType(arrTy->getElementType()));
|
|
|
|
return DtoConstSlice(size, ptr);
|
|
}
|
|
|
|
#endif // GENERATE_OFFTI
|
|
|
|
static LLConstant* build_class_dtor(ClassDeclaration* cd)
|
|
{
|
|
FuncDeclaration* dtor = cd->dtor;
|
|
|
|
// if no destructor emit a null
|
|
if (!dtor)
|
|
return getNullPtr(getVoidPtrType());
|
|
|
|
DtoForceDeclareDsymbol(dtor);
|
|
return llvm::ConstantExpr::getBitCast(dtor->ir.irFunc->func, getPtrToType(LLType::Int8Ty));
|
|
}
|
|
|
|
static unsigned build_classinfo_flags(ClassDeclaration* cd)
|
|
{
|
|
// adapted from original dmd code
|
|
unsigned 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() && !sm->isVarDeclaration()->isDataseg()) // 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;
|
|
// }
|
|
|
|
IrStruct* ir = cd->ir.irStruct;
|
|
|
|
if (ir->classInfoDefined) return;
|
|
ir->classInfoDefined = true;
|
|
|
|
Logger::println("DtoDefineClassInfo(%s)", cd->toChars());
|
|
LOG_SCOPE;
|
|
|
|
assert(cd->type->ty == Tclass);
|
|
assert(ir->classInfo);
|
|
|
|
TypeClass* cdty = (TypeClass*)cd->type;
|
|
if (!cd->isInterfaceDeclaration())
|
|
{
|
|
assert(ir->init);
|
|
assert(ir->constInit);
|
|
assert(ir->vtbl);
|
|
assert(ir->constVtbl);
|
|
}
|
|
|
|
// holds the list of initializers for llvm
|
|
std::vector<LLConstant*> inits;
|
|
|
|
ClassDeclaration* cinfo = ClassDeclaration::classinfo;
|
|
DtoForceConstInitDsymbol(cinfo);
|
|
|
|
LLConstant* c;
|
|
|
|
const LLType* voidPtr = getVoidPtrType();
|
|
const LLType* voidPtrPtr = getPtrToType(voidPtr);
|
|
|
|
// own vtable
|
|
c = cinfo->ir.irStruct->vtbl;
|
|
assert(c);
|
|
inits.push_back(c);
|
|
|
|
// monitor
|
|
c = LLConstant::getNullValue(voidPtr);
|
|
inits.push_back(c);
|
|
|
|
// byte[] init
|
|
if (cd->isInterfaceDeclaration())
|
|
c = DtoConstSlice(DtoConstSize_t(0), LLConstant::getNullValue(voidPtr));
|
|
else
|
|
{
|
|
c = DtoBitCast(ir->init, voidPtr);
|
|
//Logger::cout() << *ir->constInit->getType() << std::endl;
|
|
size_t initsz = getTypePaddedSize(ir->init->getType()->getContainedType(0));
|
|
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 = DtoConstSlice(DtoConstSize_t(0), LLConstant::getNullValue(getPtrToType(voidPtr)));
|
|
else {
|
|
c = DtoBitCast(ir->vtbl, voidPtrPtr);
|
|
c = DtoConstSlice(DtoConstSize_t(cd->vtbl.dim), c);
|
|
}
|
|
inits.push_back(c);
|
|
|
|
// interfaces array
|
|
VarDeclaration* intersVar = (VarDeclaration*)cinfo->fields.data[3];
|
|
const LLType* intersTy = DtoType(intersVar->type);
|
|
if (!ir->interfaceInfos)
|
|
c = LLConstant::getNullValue(intersTy);
|
|
else {
|
|
const LLType* t = intersTy->getContainedType(1); // .ptr
|
|
// cast to Interface*
|
|
c = DtoBitCast(ir->interfaceInfos, t);
|
|
size_t isz = ir->interfaceVec.size();
|
|
size_t iisz = ir->classInfoInterfaces.size();
|
|
assert(iisz <= isz);
|
|
// offset - we only want the 'iisz' last ones
|
|
LLConstant* idx = DtoConstUint(isz - iisz);
|
|
c = llvm::ConstantExpr::getGetElementPtr(c, &idx, 1);
|
|
// make array
|
|
c = DtoConstSlice(DtoConstSize_t(iisz), c);
|
|
}
|
|
inits.push_back(c);
|
|
|
|
// base classinfo
|
|
// interfaces never get a base , just the interfaces[]
|
|
if (cd->baseClass && !cd->isInterfaceDeclaration()) {
|
|
DtoDeclareClassInfo(cd->baseClass);
|
|
c = cd->baseClass->ir.irStruct->classInfo;
|
|
assert(c);
|
|
inits.push_back(c);
|
|
}
|
|
else {
|
|
// null
|
|
c = LLConstant::getNullValue(DtoType(cinfo->type));
|
|
inits.push_back(c);
|
|
}
|
|
|
|
// destructor
|
|
if (cd->isInterfaceDeclaration())
|
|
c = LLConstant::getNullValue(voidPtr);
|
|
else
|
|
c = build_class_dtor(cd);
|
|
inits.push_back(c);
|
|
|
|
// invariant
|
|
VarDeclaration* invVar = (VarDeclaration*)cinfo->fields.data[6];
|
|
const LLType* invTy = DtoType(invVar->type);
|
|
if (cd->inv)
|
|
{
|
|
DtoForceDeclareDsymbol(cd->inv);
|
|
c = cd->inv->ir.irFunc->func;
|
|
c = DtoBitCast(c, invTy);
|
|
}
|
|
else
|
|
c = LLConstant::getNullValue(invTy);
|
|
inits.push_back(c);
|
|
|
|
// uint flags
|
|
if (cd->isInterfaceDeclaration())
|
|
c = DtoConstUint(0);
|
|
else {
|
|
unsigned flags = build_classinfo_flags(cd);
|
|
c = DtoConstUint(flags);
|
|
}
|
|
inits.push_back(c);
|
|
|
|
// deallocator
|
|
if (cd->aggDelete)
|
|
{
|
|
DtoForceDeclareDsymbol(cd->aggDelete);
|
|
c = cd->aggDelete->ir.irFunc->func;
|
|
c = DtoBitCast(c, voidPtr);
|
|
}
|
|
else
|
|
c = LLConstant::getNullValue(voidPtr);
|
|
inits.push_back(c);
|
|
|
|
// offset typeinfo
|
|
VarDeclaration* offTiVar = (VarDeclaration*)cinfo->fields.data[9];
|
|
const LLType* offTiTy = DtoType(offTiVar->type);
|
|
|
|
#if GENERATE_OFFTI
|
|
|
|
if (cd->isInterfaceDeclaration())
|
|
c = LLConstant::getNullValue(offTiTy);
|
|
else
|
|
c = build_offti_array(cd, offTiTy);
|
|
|
|
#else // GENERATE_OFFTI
|
|
|
|
c = LLConstant::getNullValue(offTiTy);
|
|
|
|
#endif // GENERATE_OFFTI
|
|
|
|
inits.push_back(c);
|
|
|
|
// default constructor
|
|
if (cd->defaultCtor)
|
|
{
|
|
DtoForceDeclareDsymbol(cd->defaultCtor);
|
|
c = isaConstant(cd->defaultCtor->ir.irFunc->func);
|
|
c = DtoBitCast(c, voidPtr);
|
|
}
|
|
else
|
|
c = LLConstant::getNullValue(voidPtr);
|
|
inits.push_back(c);
|
|
|
|
#if DMDV2
|
|
|
|
// xgetMembers
|
|
VarDeclaration* xgetVar = (VarDeclaration*)cinfo->fields.data[11];
|
|
const LLType* xgetTy = DtoType(xgetVar->type);
|
|
|
|
// FIXME: fill it out!
|
|
inits.push_back( LLConstant::getNullValue(xgetTy) );
|
|
#endif
|
|
|
|
/*size_t n = inits.size();
|
|
for (size_t i=0; i<n; ++i)
|
|
{
|
|
Logger::cout() << "inits[" << i << "]: " << *inits[i] << '\n';
|
|
}*/
|
|
|
|
// build the initializer
|
|
LLConstant* finalinit = llvm::ConstantStruct::get(inits);
|
|
//Logger::cout() << "built the classinfo initializer:\n" << *finalinit <<'\n';
|
|
ir->constClassInfo = finalinit;
|
|
|
|
// refine the type
|
|
llvm::cast<llvm::OpaqueType>(ir->classInfoOpaque.get())->refineAbstractTypeTo(finalinit->getType());
|
|
|
|
// apply initializer
|
|
ir->classInfo->setInitializer(finalinit);
|
|
}
|