#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->codegen(Type::sir); } // 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 DtoDeclareInterface(InterfaceDeclaration* cd); static void DtoConstInitInterface(InterfaceDeclaration* cd); static void DtoDefineInterface(InterfaceDeclaration* cd); 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; // create the type const LLType* t = LLArrayType::get(getVoidPtrType(), cd->vtbl.dim); assert(!ts->ir.type); ts->ir.type = new LLPATypeHolder(getPtrToType(t)); // ... and ClassInfo std::string varname("_D"); varname.append(cd->mangle()); varname.append("11__InterfaceZ"); // create global irstruct->classInfo = new llvm::GlobalVariable(irstruct->classInfoOpaque.get(), false, DtoLinkage(cd), NULL, varname, gIR->module); // handle base interfaces if (cd->baseclasses.dim) { Logger::println("num baseclasses: %u", cd->baseclasses.dim); LOG_SCOPE; for (int i=0; ibaseclasses.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); } } // request declaration DtoDeclareInterface(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->codegen(Type::sir); } } ////////////////////////////////////////////////////////////////////////////////////////// // FIXME: this needs to be cleaned up void DtoResolveClass(ClassDeclaration* cd) { if (InterfaceDeclaration* id = cd->isInterfaceDeclaration()) { DtoResolveInterface(id); return; } // make sure the base classes are processed first if (cd->baseClass) cd->baseClass->codegen(Type::sir); if (cd->ir.resolved) return; cd->ir.resolved = true; Logger::println("DtoResolveClass(%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 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; } // create the symbols we're going to need // avoids chicken egg problems llvm::GlobalValue::LinkageTypes _linkage = DtoLinkage(cd); // there is always a vtbl symbol std::string varname("_D"); varname.append(cd->mangle()); varname.append("6__vtblZ"); irstruct->vtbl = new llvm::GlobalVariable(irstruct->vtblInitTy.get(), true, _linkage, NULL, varname, gIR->module); // ... and initZ varname = "_D"; varname.append(cd->mangle()); varname.append("6__initZ"); irstruct->init = new llvm::GlobalVariable(irstruct->initOpaque.get(), true, _linkage, NULL, varname, gIR->module); // ... and ClassInfo varname = "_D"; varname.append(cd->mangle()); varname.append("7__ClassZ"); // create global irstruct->classInfo = new llvm::GlobalVariable(irstruct->classInfoOpaque.get(), false, _linkage, NULL, varname, gIR->module); // 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(spa->get())->refineAbstractTypeTo(structtype); structtype = isaStruct(spa->get()); assert(structtype); // name the type gIR->module->addTypeName(cd->mangle(), ts->ir.type->get()); // refine vtable type // void*[vtbl.dim] llvm::cast(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 DtoDeclareClass(cd); } ////////////////////////////////////////////////////////////////////////////////////////// static void DtoDeclareInterface(InterfaceDeclaration* cd) { DtoResolveInterface(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 DtoConstInitInterface(cd); // emit typeinfo and request definition if (mustDefineSymbol(cd)) { DtoDefineInterface(cd); DtoTypeInfoOf(cd->type, false); } } ////////////////////////////////////////////////////////////////////////////////////////// // FIXME: this needs to be cleaned up void DtoDeclareClass(ClassDeclaration* cd) { if (InterfaceDeclaration* id = cd->isInterfaceDeclaration()) { DtoDeclareInterface(id); return; } DtoResolveClass(cd); 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); // 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++; } gIR->structs.pop_back(); // request const init DtoConstInitClass(cd); // define ? (set initializers) if (needs_definition) DtoDefineClass(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& 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 defVars; defVars.reserve(nfields); size_t lastoffset = offsetbegin; size_t lastsize = 0; // find fields that contribute to default for (size_t i=0; ioffset; 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; itype->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; ivtblInterfaces->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) { cd->codegen(Type::sir); // generate vtable initializer std::vector sinits(cd->vtbl.dim, NULL); IrStruct* irstruct = cd->ir.irStruct; assert(cd->vtbl.dim > 1); DtoDeclareClassInfo(cd); // 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 { fd->codegen(Type::sir); Logger::println("F = %s", fd->toPrettyChars()); assert(fd->ir.irFunc); 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(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 inits; std::vector infoInits(3); // go through each interface size_t ninter = irstruct->interfaceVec.size(); for (size_t i=0; iinterfaceVec[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 { fd->codegen(Type::sir); 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 iri->decl->codegen(Type::sir); // 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::get(infoInits)); } } ////////////////////////////////////////////////////////////////////////////// static void DtoConstInitInterface(InterfaceDeclaration* cd) { DtoDeclareInterface(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; } DtoDeclareClass(cd); if (cd->ir.initialized) return; cd->ir.initialized = true; Logger::println("DtoConstInitClass(%s): %s", cd->toPrettyChars(), cd->loc.toChars()); LOG_SCOPE; // 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 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(irstruct->initOpaque.get())->refineAbstractTypeTo(irstruct->constInit->getType()); // build initializers for static member variables size_t n = irstruct->staticVars.size(); for (size_t i = 0; i < n; ++i) { DtoConstInitGlobal(irstruct->staticVars[i]); } // This is all we use it for. Clear the memory! irstruct->staticVars.clear(); // 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 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) { DtoConstInitInterface(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; } DtoConstInitClass(cd); 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; iinterfaceVec[i]; Logger::println("interface %s", iri->base->base->toChars()); assert(iri->vtblInit); // refine the init type llvm::cast(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 tc->sym->codegen(Type::sir); // allocate LLValue* mem; if (newexp->onstack) { mem = DtoAlloca(DtoType(tc)->getContainedType(0), ".newclass_alloca"); } // custom allocator else if (newexp->allocator) { newexp->allocator->codegen(Type::sir); 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); newexp->member->codegen(Type::sir); 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) { tc->sym->codegen(Type::sir); 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 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(); LLValue* orig = v; v = DtoGEPi(v, 0, iri->index); const LLType* ifType = DtoType(_to); if (Logger::enabled()) { 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(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) ClassDeclaration::object->codegen(Type::sir); ClassDeclaration::classinfo->codegen(Type::sir); llvm::Function* func = LLVM_D_GetRuntimeFunction(gIR->module, "_d_dynamic_cast"); const llvm::FunctionType* funcTy = func->getFunctionType(); std::vector 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(); to->sym->codegen(Type::sir); 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) ClassDeclaration::object->codegen(Type::sir); ClassDeclaration::classinfo->codegen(Type::sir); llvm::Function* func = LLVM_D_GetRuntimeFunction(gIR->module, "_d_interface_cast"); const llvm::FunctionType* funcTy = func->getFunctionType(); std::vector args; // void* p LLValue* ptr = val->getRVal(); ptr = DtoBitCast(ptr, funcTy->getParamType(0)); // ClassInfo c TypeClass* to = (TypeClass*)_to->toBasetype(); to->sym->codegen(Type::sir); 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); } ////////////////////////////////////////////////////////////////////////////////////////// #if GENERATE_OFFTI // build a single element for the OffsetInfo[] of ClassInfo static LLConstant* build_offti_entry(ClassDeclaration* cd, VarDeclaration* vd) { std::vector 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 arrayInits(nvars); for (size_t i=0; ivarDecls[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()); dtor->codegen(Type::sir); 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 inits; ClassDeclaration* cinfo = ClassDeclaration::classinfo; cinfo->codegen(Type::sir); 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) { cd->inv->codegen(Type::sir); 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) { cd->aggDelete->codegen(Type::sir); 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) { cd->defaultCtor->codegen(Type::sir); 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; iconstClassInfo = finalinit; // refine the type llvm::cast(ir->classInfoOpaque.get())->refineAbstractTypeTo(finalinit->getType()); // apply initializer ir->classInfo->setInitializer(finalinit); }