mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-04-30 23:20:40 +03:00
778 lines
24 KiB
C++
778 lines
24 KiB
C++
//===-- classes.cpp -------------------------------------------------------===//
|
||
//
|
||
// LDC – the LLVM D compiler
|
||
//
|
||
// This file is distributed under the BSD-style LDC license. See the LICENSE
|
||
// file for details.
|
||
//
|
||
//===----------------------------------------------------------------------===//
|
||
|
||
#include "gen/llvm.h"
|
||
#include "aggregate.h"
|
||
#include "declaration.h"
|
||
#include "init.h"
|
||
#include "mtype.h"
|
||
#include "target.h"
|
||
#include "gen/arrays.h"
|
||
#include "gen/classes.h"
|
||
#include "gen/dvalue.h"
|
||
#include "gen/functions.h"
|
||
#include "gen/irstate.h"
|
||
#include "gen/llvmhelpers.h"
|
||
#include "gen/logger.h"
|
||
#include "gen/nested.h"
|
||
#include "gen/rttibuilder.h"
|
||
#include "gen/runtime.h"
|
||
#include "gen/structs.h"
|
||
#include "gen/tollvm.h"
|
||
#include "ir/iraggr.h"
|
||
#include "ir/irtypeclass.h"
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
// FIXME: this needs to be cleaned up
|
||
|
||
void DtoResolveClass(ClassDeclaration* cd)
|
||
{
|
||
if (cd->ir.resolved) return;
|
||
cd->ir.resolved = true;
|
||
|
||
IF_LOG Logger::println("DtoResolveClass(%s): %s", cd->toPrettyChars(), cd->loc.toChars());
|
||
LOG_SCOPE;
|
||
|
||
// make sure the base classes are processed first
|
||
for (BaseClasses::iterator I = cd->baseclasses->begin(),
|
||
E = cd->baseclasses->end();
|
||
I != E; ++I)
|
||
{
|
||
DtoResolveClass((*I)->base);
|
||
}
|
||
|
||
// make sure type exists
|
||
DtoType(cd->type);
|
||
|
||
// create IrAggr
|
||
assert(cd->ir.irAggr == NULL);
|
||
IrAggr* irAggr = new IrAggr(cd);
|
||
cd->ir.irAggr = irAggr;
|
||
|
||
// make sure all fields really get their ir field
|
||
for (VarDeclarations::iterator I = cd->fields.begin(),
|
||
E = cd->fields.end();
|
||
I != E; ++I)
|
||
{
|
||
VarDeclaration* vd = *I;
|
||
if (vd->ir.irField == NULL) {
|
||
new IrField(vd);
|
||
} else {
|
||
IF_LOG Logger::println("class field already exists!!!");
|
||
}
|
||
}
|
||
|
||
// emit the interfaceInfosZ symbol if necessary
|
||
if (cd->vtblInterfaces && cd->vtblInterfaces->dim > 0)
|
||
irAggr->getInterfaceArraySymbol(); // initializer is applied when it's built
|
||
|
||
// interface only emit typeinfo and classinfo
|
||
if (cd->isInterfaceDeclaration())
|
||
{
|
||
irAggr->initializeInterface();
|
||
}
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
DValue* DtoNewClass(Loc& loc, TypeClass* tc, NewExp* newexp)
|
||
{
|
||
// resolve type
|
||
DtoResolveClass(tc->sym);
|
||
|
||
// allocate
|
||
LLValue* mem;
|
||
if (newexp->onstack)
|
||
{
|
||
// FIXME align scope class to its largest member
|
||
mem = DtoRawAlloca(DtoType(tc)->getContainedType(0), 0, ".newclass_alloca");
|
||
}
|
||
// custom allocator
|
||
else if (newexp->allocator)
|
||
{
|
||
DtoResolveFunction(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(loc, gIR->module, "_d_newclass");
|
||
LLConstant* ci = DtoBitCast(tc->sym->ir.irAggr->getClassInfoSymbol(), DtoType(Type::typeinfoclass->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 = toElem(newexp->thisexp);
|
||
size_t idx = tc->sym->vthis->ir.irField->index;
|
||
LLValue* src = thisval->getRVal();
|
||
LLValue* dst = DtoGEPi(mem,0,idx,"tmp");
|
||
IF_LOG Logger::cout() << "dst: " << *dst << "\nsrc: " << *src << '\n';
|
||
DtoStore(src, DtoBitCast(dst, getPtrToType(src->getType())));
|
||
}
|
||
// set the context for nested classes
|
||
else if (tc->sym->isNested() && tc->sym->vthis)
|
||
{
|
||
DtoResolveNestedContext(loc, tc->sym, mem);
|
||
}
|
||
|
||
// call constructor
|
||
if (newexp->member)
|
||
{
|
||
Logger::println("Calling constructor");
|
||
assert(newexp->arguments != NULL);
|
||
DtoResolveFunction(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)
|
||
{
|
||
DtoResolveClass(tc->sym);
|
||
|
||
// Set vtable field. Doing this seperately might be optimized better.
|
||
LLValue* tmp = DtoGEPi(dst, 0, 0, "vtbl");
|
||
LLValue* val = DtoBitCast(tc->sym->ir.irAggr->getVtblSymbol(),
|
||
tmp->getType()->getContainedType(0));
|
||
DtoStore(val, tmp);
|
||
|
||
// For D classes, set the monitor field to null.
|
||
const bool isCPPclass = tc->sym->isCPPclass() ? true : false;
|
||
if (!isCPPclass)
|
||
{
|
||
tmp = DtoGEPi(dst, 0, 1, "monitor");
|
||
val = LLConstant::getNullValue(tmp->getType()->getContainedType(0));
|
||
DtoStore(val, tmp);
|
||
}
|
||
|
||
// Copy the rest from the static initializer, if any.
|
||
unsigned const firstDataIdx = isCPPclass ? 1 : 2;
|
||
uint64_t const dataBytes = tc->sym->structsize - Target::ptrsize * firstDataIdx;
|
||
if (dataBytes == 0)
|
||
return;
|
||
|
||
LLValue* dstarr = DtoGEPi(dst, 0, firstDataIdx, "tmp");
|
||
|
||
// init symbols might not have valid types
|
||
LLValue* initsym = tc->sym->ir.irAggr->getInitSymbol();
|
||
initsym = DtoBitCast(initsym, DtoType(tc));
|
||
LLValue* srcarr = DtoGEPi(initsym, 0, firstDataIdx, "tmp");
|
||
|
||
DtoMemCpy(dstarr, srcarr, DtoConstSize_t(dataBytes));
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
void DtoFinalizeClass(Loc& loc, LLValue* inst)
|
||
{
|
||
// get runtime function
|
||
llvm::Function* fn = LLVM_D_GetRuntimeFunction(loc, gIR->module, "_d_callfinalizer");
|
||
// build args
|
||
LLValue* arg[] = {
|
||
DtoBitCast(inst, fn->getFunctionType()->getParamType(0), ".tmp")
|
||
};
|
||
// call
|
||
gIR->CreateCallOrInvoke(fn, arg, "");
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
DValue* DtoCastClass(Loc& loc, DValue* val, Type* _to)
|
||
{
|
||
IF_LOG Logger::println("DtoCastClass(%s, %s)", val->getType()->toChars(), _to->toChars());
|
||
LOG_SCOPE;
|
||
|
||
Type* to = _to->toBasetype();
|
||
|
||
// class -> pointer
|
||
if (to->ty == Tpointer) {
|
||
IF_LOG Logger::println("to pointer");
|
||
LLType* tolltype = DtoType(_to);
|
||
LLValue* rval = DtoBitCast(val->getRVal(), tolltype);
|
||
return new DImValue(_to, rval);
|
||
}
|
||
// class -> bool
|
||
else if (to->ty == Tbool) {
|
||
IF_LOG Logger::println("to bool");
|
||
LLValue* llval = val->getRVal();
|
||
LLValue* zero = LLConstant::getNullValue(llval->getType());
|
||
return new DImValue(_to, gIR->ir->CreateICmpNE(llval, zero, "tmp"));
|
||
}
|
||
// class -> integer
|
||
else if (to->isintegral()) {
|
||
IF_LOG Logger::println("to %s", to->toChars());
|
||
|
||
// get class ptr
|
||
LLValue* v = val->getRVal();
|
||
// cast to size_t
|
||
v = gIR->ir->CreatePtrToInt(v, DtoSize_t(), "");
|
||
// cast to the final int type
|
||
DImValue im(Type::tsize_t, v);
|
||
return DtoCastInt(loc, &im, _to);
|
||
}
|
||
|
||
// must be class/interface
|
||
assert(to->ty == Tclass);
|
||
TypeClass* tc = static_cast<TypeClass*>(to);
|
||
|
||
// from type
|
||
Type* from = val->getType()->toBasetype();
|
||
TypeClass* fc = static_cast<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(loc, 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
|
||
IrTypeClass* typeclass = stripModifiers(fc)->irtype->isClass();
|
||
|
||
// find interface impl
|
||
|
||
size_t i_index = typeclass->getInterfaceIndex(it);
|
||
assert(i_index != ~0UL && "requesting interface that is not implemented by this class");
|
||
|
||
// offset pointer
|
||
LLValue* v = val->getRVal();
|
||
LLValue* orig = v;
|
||
v = DtoGEPi(v, 0, i_index);
|
||
LLType* ifType = DtoType(_to);
|
||
IF_LOG {
|
||
Logger::cout() << "V = " << *v << std::endl;
|
||
Logger::cout() << "T = " << *ifType << std::endl;
|
||
}
|
||
v = DtoBitCast(v, ifType);
|
||
|
||
// Check whether the original value was null, and return null if so.
|
||
// Sure we could have jumped over the code above in this case, but
|
||
// it's just a GEP and (maybe) a pointer-to-pointer BitCast, so it
|
||
// should be pretty cheap and perfectly safe even if the original was null.
|
||
LLValue* isNull = gIR->ir->CreateICmpEQ(orig, LLConstant::getNullValue(orig->getType()), ".nullcheck");
|
||
v = gIR->ir->CreateSelect(isNull, LLConstant::getNullValue(ifType), v, ".interface");
|
||
|
||
// return r-value
|
||
return new DImValue(_to, v);
|
||
}
|
||
// class -> interface
|
||
else {
|
||
Logger::println("from object");
|
||
return DtoDynamicCastObject(loc, val, _to);
|
||
}
|
||
}
|
||
// x -> class
|
||
else {
|
||
Logger::println("to class");
|
||
// interface -> class
|
||
if (fc->sym->isInterfaceDeclaration()) {
|
||
Logger::println("interface cast");
|
||
return DtoDynamicCastInterface(loc, val, _to);
|
||
}
|
||
// class -> class - static down cast
|
||
else if (tc->sym->isBaseOf(fc->sym,NULL)) {
|
||
Logger::println("static down cast");
|
||
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(loc, val, _to);
|
||
}
|
||
}
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
DValue* DtoDynamicCastObject(Loc& loc, DValue* val, Type* _to)
|
||
{
|
||
// call:
|
||
// Object _d_dynamic_cast(Object o, ClassInfo c)
|
||
|
||
DtoResolveClass(ClassDeclaration::object);
|
||
DtoResolveClass(Type::typeinfoclass);
|
||
|
||
llvm::Function* func = LLVM_D_GetRuntimeFunction(loc, gIR->module, "_d_dynamic_cast");
|
||
LLFunctionType* funcTy = func->getFunctionType();
|
||
|
||
// Object o
|
||
LLValue* obj = val->getRVal();
|
||
obj = DtoBitCast(obj, funcTy->getParamType(0));
|
||
assert(funcTy->getParamType(0) == obj->getType());
|
||
|
||
// ClassInfo c
|
||
TypeClass* to = static_cast<TypeClass*>(_to->toBasetype());
|
||
DtoResolveClass(to->sym);
|
||
|
||
LLValue* cinfo = to->sym->ir.irAggr->getClassInfoSymbol();
|
||
// 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(Loc& loc, DValue* val, Type* to)
|
||
{
|
||
// call:
|
||
// Object _d_toObject(void* p)
|
||
|
||
llvm::Function* func = LLVM_D_GetRuntimeFunction(loc, gIR->module, "_d_toObject");
|
||
LLFunctionType* 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(Loc& loc, DValue* val, Type* _to)
|
||
{
|
||
// call:
|
||
// Object _d_interface_cast(void* p, ClassInfo c)
|
||
|
||
DtoResolveClass(ClassDeclaration::object);
|
||
DtoResolveClass(Type::typeinfoclass);
|
||
|
||
llvm::Function* func = LLVM_D_GetRuntimeFunction(loc, gIR->module, "_d_interface_cast");
|
||
LLFunctionType* funcTy = func->getFunctionType();
|
||
|
||
// void* p
|
||
LLValue* ptr = val->getRVal();
|
||
ptr = DtoBitCast(ptr, funcTy->getParamType(0));
|
||
|
||
// ClassInfo c
|
||
TypeClass* to = static_cast<TypeClass*>(_to->toBasetype());
|
||
DtoResolveClass(to->sym);
|
||
LLValue* cinfo = to->sym->ir.irAggr->getClassInfoSymbol();
|
||
// 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)
|
||
{
|
||
IF_LOG Logger::println("indexing class field %s:", vd->toPrettyChars());
|
||
LOG_SCOPE;
|
||
|
||
IF_LOG 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
|
||
LLType* st = DtoType(cd->type);
|
||
// cast to the struct type
|
||
src = DtoBitCast(src, st);
|
||
|
||
// gep to the index
|
||
#if 0
|
||
IF_LOG {
|
||
Logger::cout() << "src2: " << *src << '\n';
|
||
Logger::cout() << "index: " << field->index << '\n';
|
||
Logger::cout() << "srctype: " << *src->getType() << '\n';
|
||
}
|
||
#endif
|
||
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(i1ToI8(DtoType(vd->type))));
|
||
|
||
IF_LOG Logger::cout() << "value: " << *val << '\n';
|
||
|
||
return val;
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
LLValue* DtoVirtualFunctionPointer(DValue* inst, FuncDeclaration* fdecl, char* name)
|
||
{
|
||
// sanity checks
|
||
assert(fdecl->isVirtual());
|
||
assert(!fdecl->isFinalFunc());
|
||
assert(inst->getType()->toBasetype()->ty == Tclass);
|
||
// 0 is always ClassInfo/Interface* unless it is a CPP interface
|
||
assert(fdecl->vtblIndex > 0 || (fdecl->vtblIndex == 0 && fdecl->linkage == LINKcpp));
|
||
|
||
// get instance
|
||
LLValue* vthis = inst->getRVal();
|
||
IF_LOG Logger::cout() << "vthis: " << *vthis << '\n';
|
||
|
||
LLValue* funcval = vthis;
|
||
// get the vtbl for objects
|
||
funcval = DtoGEPi(funcval, 0, 0, "tmp");
|
||
// load vtbl ptr
|
||
funcval = DtoLoad(funcval);
|
||
// index vtbl
|
||
std::string vtblname = name;
|
||
vtblname.append("@vtbl");
|
||
funcval = DtoGEPi(funcval, 0, fdecl->vtblIndex, vtblname.c_str());
|
||
// load funcptr
|
||
funcval = DtoAlignedLoad(funcval);
|
||
|
||
IF_LOG Logger::cout() << "funcval: " << *funcval << '\n';
|
||
|
||
// cast to final funcptr type
|
||
funcval = DtoBitCast(funcval, getPtrToType(DtoFunctionType(fdecl)));
|
||
|
||
// postpone naming until after casting to get the name in call instructions
|
||
funcval->setName(name);
|
||
|
||
IF_LOG Logger::cout() << "funcval casted: " << *funcval << '\n';
|
||
|
||
return funcval;
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
#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 = gDataLayout->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, LLType* arrayT)
|
||
{
|
||
IrAggr* iraggr = cd->ir.irAggr;
|
||
|
||
size_t nvars = iraggr->varDecls.size();
|
||
std::vector<LLConstant*> arrayInits(nvars);
|
||
|
||
for (size_t i=0; i<nvars; i++)
|
||
{
|
||
arrayInits[i] = build_offti_entry(cd, iraggr->varDecls[i]);
|
||
}
|
||
|
||
LLConstant* size = DtoConstSize_t(nvars);
|
||
LLConstant* ptr;
|
||
|
||
if (nvars == 0)
|
||
return LLConstant::getNullValue( arrayT );
|
||
|
||
// array type
|
||
LLArrayType* arrTy = llvm::ArrayType::get(arrayInits[0]->getType(), nvars);
|
||
LLConstant* arrInit = LLConstantArray::get(arrTy, arrayInits);
|
||
|
||
// mangle
|
||
std::string name(cd->type->vtinfo->toChars());
|
||
name.append("__OffsetTypeInfos");
|
||
|
||
// create symbol
|
||
llvm::GlobalVariable* gvar = getOrCreateGlobal(cd->loc, *gIR->module, arrTy,
|
||
true,DtoInternalLinkage(cd),arrInit,name);
|
||
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());
|
||
|
||
DtoResolveFunction(dtor);
|
||
return llvm::ConstantExpr::getBitCast(dtor->ir.irFunc->func, getPtrToType(LLType::getInt8Ty(gIR->context())));
|
||
}
|
||
|
||
static unsigned build_classinfo_flags(ClassDeclaration* cd)
|
||
{
|
||
// adapted from original dmd code
|
||
unsigned flags = 0;
|
||
flags |= cd->isCOMclass(); // IUnknown
|
||
bool hasOffTi = false;
|
||
if (cd->ctor)
|
||
flags |= 8;
|
||
if (cd->isabstract)
|
||
flags |= 64;
|
||
if (cd->isCPPclass())
|
||
flags |= 128;
|
||
for (ClassDeclaration *cd2 = cd; cd2; cd2 = cd2->baseClass)
|
||
{
|
||
if (!cd2->members)
|
||
continue;
|
||
for (size_t i = 0; i < cd2->members->dim; i++)
|
||
{
|
||
Dsymbol *sm = static_cast<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;
|
||
|
||
// always define the typeinfo field.
|
||
// why would ever not do this?
|
||
flags |= 32;
|
||
|
||
return flags;
|
||
}
|
||
|
||
LLConstant* 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;
|
||
// version(D_Version2)
|
||
// immutable(void)* m_RTInfo;
|
||
// else
|
||
// TypeInfo typeinfo; // since dmd 1.045
|
||
// }
|
||
|
||
IF_LOG Logger::println("DtoDefineClassInfo(%s)", cd->toChars());
|
||
LOG_SCOPE;
|
||
|
||
assert(cd->type->ty == Tclass);
|
||
TypeClass* cdty = static_cast<TypeClass*>(cd->type);
|
||
|
||
IrAggr* ir = cd->ir.irAggr;
|
||
assert(ir);
|
||
|
||
ClassDeclaration* cinfo = Type::typeinfoclass;
|
||
|
||
if (cinfo->fields.dim != 12)
|
||
{
|
||
error(Loc(), "object.d ClassInfo class is incorrect");
|
||
fatal();
|
||
}
|
||
|
||
// use the rtti builder
|
||
RTTIBuilder b(cinfo);
|
||
|
||
LLConstant* c;
|
||
|
||
LLType* voidPtr = getVoidPtrType();
|
||
LLType* voidPtrPtr = getPtrToType(voidPtr);
|
||
|
||
// byte[] init
|
||
if (cd->isInterfaceDeclaration())
|
||
{
|
||
b.push_null_void_array();
|
||
}
|
||
else
|
||
{
|
||
size_t initsz = cd->size(Loc());
|
||
b.push_void_array(initsz, ir->getInitSymbol());
|
||
}
|
||
|
||
// class name
|
||
// code from dmd
|
||
const char *name = cd->ident->toChars();
|
||
size_t namelen = strlen(name);
|
||
if (!(namelen > 9 && memcmp(name, "TypeInfo_", 9) == 0))
|
||
{
|
||
name = cd->toPrettyChars();
|
||
namelen = strlen(name);
|
||
}
|
||
b.push_string(name);
|
||
|
||
// vtbl array
|
||
if (cd->isInterfaceDeclaration())
|
||
{
|
||
b.push_array(0, getNullValue(voidPtrPtr));
|
||
}
|
||
else
|
||
{
|
||
c = DtoBitCast(ir->getVtblSymbol(), voidPtrPtr);
|
||
b.push_array(cd->vtbl.dim, c);
|
||
}
|
||
|
||
// interfaces array
|
||
b.push(ir->getClassInfoInterfaces());
|
||
|
||
// base classinfo
|
||
// interfaces never get a base , just the interfaces[]
|
||
if (cd->baseClass && !cd->isInterfaceDeclaration())
|
||
b.push_classinfo(cd->baseClass);
|
||
else
|
||
b.push_null(cinfo->type);
|
||
|
||
// destructor
|
||
if (cd->isInterfaceDeclaration())
|
||
b.push_null_vp();
|
||
else
|
||
b.push(build_class_dtor(cd));
|
||
|
||
// invariant
|
||
VarDeclaration* invVar = static_cast<VarDeclaration*>(cinfo->fields.data[6]);
|
||
b.push_funcptr(cd->inv, invVar->type);
|
||
|
||
// uint flags
|
||
unsigned flags;
|
||
if (cd->isInterfaceDeclaration())
|
||
flags = 4 | cd->isCOMinterface() | 32;
|
||
else
|
||
flags = build_classinfo_flags(cd);
|
||
b.push_uint(flags);
|
||
|
||
// deallocator
|
||
b.push_funcptr(cd->aggDelete, Type::tvoid->pointerTo());
|
||
|
||
// offset typeinfo
|
||
VarDeclaration* offTiVar = static_cast<VarDeclaration*>(cinfo->fields.data[9]);
|
||
|
||
#if GENERATE_OFFTI
|
||
|
||
if (cd->isInterfaceDeclaration())
|
||
b.push_null(offTiVar->type);
|
||
else
|
||
b.push(build_offti_array(cd, DtoType(offTiVar->type)));
|
||
|
||
#else // GENERATE_OFFTI
|
||
|
||
b.push_null(offTiVar->type);
|
||
|
||
#endif // GENERATE_OFFTI
|
||
|
||
// default constructor
|
||
VarDeclaration* defConstructorVar = static_cast<VarDeclaration*>(cinfo->fields.data[10]);
|
||
b.push_funcptr(cd->defaultCtor, defConstructorVar->type);
|
||
|
||
// immutable(void)* m_RTInfo;
|
||
// The cases where getRTInfo is null are not quite here, but the code is
|
||
// modelled after what DMD does.
|
||
if (cd->getRTInfo)
|
||
b.push(toConstElem(cd->getRTInfo, gIR));
|
||
else if (flags & 2)
|
||
b.push_size_as_vp(0); // no pointers
|
||
else
|
||
b.push_size_as_vp(1); // has pointers
|
||
|
||
/*size_t n = inits.size();
|
||
for (size_t i=0; i<n; ++i)
|
||
{
|
||
Logger::cout() << "inits[" << i << "]: " << *inits[i] << '\n';
|
||
}*/
|
||
|
||
// build the initializer
|
||
LLType *initType = ir->classInfo->getType()->getContainedType(0);
|
||
LLConstant* finalinit = b.get_constant(isaStruct(initType));
|
||
|
||
//Logger::cout() << "built the classinfo initializer:\n" << *finalinit <<'\n';
|
||
ir->constClassInfo = finalinit;
|
||
|
||
// sanity check
|
||
assert(finalinit->getType() == initType &&
|
||
"__ClassZ initializer does not match the ClassInfo type");
|
||
|
||
// return initializer
|
||
return finalinit;
|
||
}
|