diff --git a/gen/arrays.cpp b/gen/arrays.cpp index 53daa91d..2dbc8efd 100644 --- a/gen/arrays.cpp +++ b/gen/arrays.cpp @@ -263,85 +263,6 @@ void DtoSetArray(DValue* array, LLValue* dim, LLValue* ptr) ////////////////////////////////////////////////////////////////////////////////////////// -// The function is almost identical copy of DtoConstArrayInitializer but it returns -// initializer type not the initializer itself. -// FIXME: is there any way to merge this next two functions? -LLType* DtoConstArrayInitializerType(ArrayInitializer* arrinit) -{ - Type* arrty = arrinit->type->toBasetype(); - if (arrty->ty != Tsarray) - return DtoType(arrinit->type); - - TypeSArray* tsa = static_cast(arrty); - size_t arrlen = static_cast(tsa->dim->toInteger()); - - // get elem type - Type* elemty = arrty->nextOf(); - LLType* llelemty = DtoTypeNotVoid(elemty); - - // make sure the number of initializers is sane - if (arrinit->index.dim > arrlen || arrinit->dim > arrlen) - { - error(arrinit->loc, "too many initializers, %u, for array[%zu]", arrinit->index.dim, arrlen); - fatal(); - } - - // true if array elements differ in type, can happen with array of unions - bool mismatch = false; - - // allocate room for types - std::vector types(arrlen, NULL); - - // go through each initializer, they're not sorted by index by the frontend - size_t j = 0; - for (size_t i = 0; i < arrinit->index.dim; i++) - { - // get index - Expression* idx = static_cast(arrinit->index.data[i]); - - // idx can be null, then it's just the next element - if (idx) - j = idx->toInteger(); - assert(j < arrlen); - - // get value - Initializer* val = static_cast(arrinit->value.data[i]); - assert(val); - - LLType* c = DtoConstInitializerType(elemty, val); - assert(c); - if (c != llelemty) - mismatch = true; - - types[j] = c; - j++; - } - - // fill out any null entries still left with default type - - // element default types - LLType* deftype = DtoConstInitializerType(elemty, 0); - bool mismatch2 = (deftype != llelemty); - - for (size_t i = 0; i < arrlen; i++) - { - if (types[i] != NULL) - continue; - - types[i] = deftype; - - if (mismatch2) - mismatch = true; - } - - if (mismatch) - return LLStructType::get(gIR->context(), types); // FIXME should this pack? - else - return LLArrayType::get(deftype, arrlen); -} - -////////////////////////////////////////////////////////////////////////////////////////// - LLConstant* DtoConstArrayInitializer(ArrayInitializer* arrinit) { Logger::println("DtoConstArrayInitializer: %s | %s", arrinit->toChars(), arrinit->type->toChars()); diff --git a/gen/declarations.cpp b/gen/declarations.cpp index 2f32b110..8fc3c22e 100644 --- a/gen/declarations.cpp +++ b/gen/declarations.cpp @@ -99,7 +99,22 @@ void TupleDeclaration::codegen(Ir* p) /* ================================================================== */ -// FIXME: this is horrible!!! +static llvm::GlobalVariable* createGlobal(llvm::Type* type, bool isConst, + llvm::GlobalValue::LinkageTypes linkage, llvm::StringRef name, + bool isThreadLocal) +{ +#if LDC_LLVM_VER >= 302 + // FIXME: clang uses a command line option for the thread model + const llvm::GlobalVariable::ThreadLocalMode tlsModel = + isThreadLocal ? llvm::GlobalVariable::GeneralDynamicTLSModel + : llvm::GlobalVariable::NotThreadLocal; + return new llvm::GlobalVariable(*gIR->module, type, isConst, linkage, + NULL, name, 0, tlsModel); +#else + return new llvm::GlobalVariable(*gIR->module, type, isConst, linkage, + NULL, name, 0, isThreadLocal); +#endif +} void VarDeclaration::codegen(Ir* p) { @@ -124,7 +139,6 @@ void VarDeclaration::codegen(Ir* p) ad->codegen(p); // global variable - // taken from dmd2/structs if (isDataseg() || (storage_class & (STCconst | STCimmutable) && init)) { Logger::println("data segment"); @@ -143,66 +157,73 @@ void VarDeclaration::codegen(Ir* p) Logger::println("parent: %s (%s)", parent->toChars(), parent->kind()); - // not sure why this is only needed for d2 - bool _isconst = isConst() && init; - - Logger::println("Creating global variable"); + const bool isLLConst = isConst() && init; + const llvm::GlobalValue::LinkageTypes llLinkage = DtoLinkage(this); assert(!ir.initialized); ir.initialized = gIR->dmodule; - std::string _name(mangle()); + std::string llName(mangle()); - LLType *_type = DtoConstInitializerType(type, init); - - // create the global variable -#if LDC_LLVM_VER >= 302 - // FIXME: clang uses a command line option for the thread model - LLGlobalVariable* gvar = new LLGlobalVariable(*gIR->module, _type, _isconst, - DtoLinkage(this), NULL, _name, 0, - isThreadlocal() ? LLGlobalVariable::GeneralDynamicTLSModel - : LLGlobalVariable::NotThreadLocal); -#else - LLGlobalVariable* gvar = new LLGlobalVariable(*gIR->module, _type, _isconst, - DtoLinkage(this), NULL, _name, 0, isThreadlocal()); -#endif + // Since the type of a global must exactly match the type of its + // initializer, we cannot know the type until after we have emitted the + // latter (e.g. in case of unions, …). However, it is legal for the + // initializer to refer to the address of the variable. Thus, we first + // create a global with the generic type (note the assignment to + // this->ir.irGlobal->value!), and in case we also do an initializer + // with a different type later, swap it out and replace any existing + // uses with bitcasts to the previous type. + llvm::GlobalVariable* gvar = createGlobal(DtoType(type), isLLConst, + llLinkage, llName, isThreadlocal()); this->ir.irGlobal->value = gvar; + // Check if we are defining or just declaring the global in this module. + if (!(storage_class & STCextern) && mustDefineSymbol(this)) + { + // Build the initializer. Might use this->ir.irGlobal->value! + LLConstant *initVal = DtoConstInitializer(loc, type, init); + + // In case of type mismatch, swap out the variable. + if (initVal->getType() != gvar->getType()->getElementType()) + { + llvm::GlobalVariable* newGvar = createGlobal( + initVal->getType(), isLLConst, llLinkage, + "", // We take on the name of the old global below. + isThreadlocal()); + + newGvar->takeName(gvar); + + llvm::Constant* newValue = + llvm::ConstantExpr::getBitCast(newGvar, gvar->getType()); + gvar->replaceAllUsesWith(newValue); + + gvar->eraseFromParent(); + gvar = newGvar; + this->ir.irGlobal->value = newGvar; + } + + // Now, set the initializer. + assert(!ir.irGlobal->constInit); + ir.irGlobal->constInit = initVal; + gvar->setInitializer(initVal); + + // Also set up the debug info. + DtoDwarfGlobalVariable(gvar, this); + } + // Set the alignment (it is important not to use type->alignsize because // VarDeclarations can have an align() attribute independent of the type // as well). if (alignment != STRUCTALIGN_DEFAULT) gvar->setAlignment(alignment); - if (Logger::enabled()) - Logger::cout() << *gvar << '\n'; - - // if this global is used from a nested function, this is necessary or - // optimization could potentially remove the global (if it's the only use) + // If this global is used from a naked function, we need to create an + // artificial "use" for it, or it could be removed by the optimizer if + // the only reference to it is in inline asm. if (nakedUse) gIR->usedArray.push_back(DtoBitCast(gvar, getVoidPtrType())); - // assign the initializer - if (!(storage_class & STCextern) && mustDefineSymbol(this)) - { - if (Logger::enabled()) - { - Logger::println("setting initializer"); - Logger::cout() << "global: " << *gvar << '\n'; - #if 0 - Logger::cout() << "init: " << *initVal << '\n'; - #endif - } - // build the initializer - LLConstant *initVal = DtoConstInitializer(loc, type, init); - - // set the initializer - assert(!ir.irGlobal->constInit); - ir.irGlobal->constInit = initVal; - gvar->setInitializer(initVal); - - // do debug info - DtoDwarfGlobalVariable(gvar, this); - } + if (Logger::enabled()) + Logger::cout() << *gvar << '\n'; } } diff --git a/gen/llvmhelpers.cpp b/gen/llvmhelpers.cpp index 90546920..c36d5641 100644 --- a/gen/llvmhelpers.cpp +++ b/gen/llvmhelpers.cpp @@ -1304,60 +1304,6 @@ LLValue* DtoRawVarDeclaration(VarDeclaration* var, LLValue* addr) // INITIALIZER HELPERS ////////////////////////////////////////////////////////////////////////////////////////*/ -LLType* DtoConstInitializerType(Type* type, Initializer* init) -{ - if (type->ty == Ttypedef) { - TypeTypedef *td = static_cast(type); - if (td->sym->init) - return DtoConstInitializerType(td->sym->basetype, td->sym->init); - } - - type = type->toBasetype(); - if (type->ty == Tsarray) - { - if (!init) - { - TypeSArray *tsa = static_cast(type); - LLType *llnext = DtoConstInitializerType(type->nextOf(), init); - return LLArrayType::get(llnext, tsa->dim->toUInteger()); - } - else if (ArrayInitializer* ai = init->isArrayInitializer()) - { - return DtoConstArrayInitializerType(ai); - } - } - else if (type->ty == Tstruct) - { - if (!init) - { - LdefaultInit: - TypeStruct *ts = static_cast(type); - DtoResolveStruct(ts->sym); - return ts->sym->ir.irStruct->getDefaultInit()->getType(); - } - else if (ExpInitializer* ex = init->isExpInitializer()) - { - if (ex->exp->op == TOKstructliteral) { - StructLiteralExp* le = static_cast(ex->exp); - if (!le->constType) - le->constType = LLStructType::create(gIR->context(), std::string(type->toChars()) + "_init"); - return le->constType; - } else if (ex->exp->op == TOKvar) { - if (static_cast(ex->exp)->var->isStaticStructInitDeclaration()) - goto LdefaultInit; - } - } - else if (StructInitializer* si = init->isStructInitializer()) - { - if (!si->ltype) - si->ltype = LLStructType::create(gIR->context(), std::string(type->toChars()) + "_init"); - return si->ltype; - } - } - - return DtoTypeNotVoid(type); -} - LLConstant* DtoConstInitializer(Loc loc, Type* type, Initializer* init) { LLConstant* _init = 0; // may return zero diff --git a/gen/llvmhelpers.h b/gen/llvmhelpers.h index ab4c8d45..efd37be2 100644 --- a/gen/llvmhelpers.h +++ b/gen/llvmhelpers.h @@ -117,7 +117,6 @@ DValue* DtoDeclarationExp(Dsymbol* declaration); LLValue* DtoRawVarDeclaration(VarDeclaration* var, LLValue* addr = 0); // initializer helpers -LLType* DtoConstInitializerType(Type* type, Initializer* init); LLConstant* DtoConstInitializer(Loc loc, Type* type, Initializer* init); LLConstant* DtoConstExpInit(Loc loc, Type* t, Expression* exp); DValue* DtoInitializer(LLValue* target, Initializer* init); diff --git a/gen/structs.cpp b/gen/structs.cpp index 3835022f..c5a57a3c 100644 --- a/gen/structs.cpp +++ b/gen/structs.cpp @@ -151,167 +151,6 @@ LLValue* DtoIndexStruct(LLValue* src, StructDeclaration* sd, VarDeclaration* vd) // helper function that adds zero bytes to a vector of constants extern size_t add_zeros(std::vector& values, size_t diff); -// return a constant array of type arrTypeD initialized with a constant value, or that constant value -LLConstant* FillSArrayDims(Type* arrTypeD, LLConstant* init) -{ - if (arrTypeD->ty == Tsarray) - { - init = FillSArrayDims(arrTypeD->nextOf(), init); - size_t dim = static_cast(arrTypeD)->dim->toUInteger(); - LLArrayType* arrty = LLArrayType::get(init->getType(), dim); - return LLConstantArray::get(arrty, std::vector(dim, init)); - } - return init; -} - -std::vector DtoStructLiteralValues(const StructDeclaration* sd, - const std::vector& inits) -{ - // get arrays - size_t nvars = sd->fields.dim; - VarDeclaration** vars = (VarDeclaration**)sd->fields.data; - - assert(inits.size() == nvars); - - // first locate all explicit initializers - std::vector explicitInits; - for (size_t i=0; i < nvars; i++) - { - if (inits[i]) - { - explicitInits.push_back(vars[i]); - } - } - - // vector of values to build aggregate from - std::vector values; - - // offset trackers - size_t lastoffset = 0; - size_t lastsize = 0; - - // index of next explicit init - size_t exidx = 0; - // number of explicit inits - size_t nex = explicitInits.size(); - - // for through each field and build up the struct, padding with zeros - size_t i; - for (i=0; ioffset; - size_t sz = var->type->size(); - - // get next explicit - VarDeclaration* nextVar = NULL; - size_t nextOs = 0; - if (exidx < nex) - { - nextVar = explicitInits[exidx]; - nextOs = nextVar->offset; - } - // none, rest is defaults - else - { - break; - } - - // not explicit initializer, default initialize if there is room, otherwise skip - if (!inits[i]) - { - // default init if there is room - // (past current offset) and (small enough to fit before next explicit) - if ((os >= lastoffset + lastsize) && (os+sz <= nextOs)) - { - // add any 0 padding needed before this field - if (os > lastoffset + lastsize) - { - //printf("1added %lu zeros\n", os - lastoffset - lastsize); - add_zeros(values, os - lastoffset - lastsize); - } - - // get field default init - IrField* f = var->ir.irField; - assert(f); - values.push_back(f->getDefaultInit()); - - lastoffset = os; - lastsize = sz; - //printf("added default: %s : %lu (%lu)\n", var->toChars(), os, sz); - } - // skip - continue; - } - - assert(nextVar == var); - - LLConstant* init = FillSArrayDims(var->type, inits[i]); - values.push_back(init); - - // update offsets - lastoffset = os; - // sometimes size of the initializer is less than size of the variable, - // so make sure that lastsize is correct - if (inits[i]->getType()->isSized()) - sz = ceil(gDataLayout->getTypeSizeInBits(init->getType()) / 8.0); - lastsize = sz; - - // go to next explicit init - exidx++; - - //printf("added field: %s : %lu (%lu)\n", var->toChars(), os, sz); - } - - // fill out rest with default initializers - LLType* structtype = DtoType(sd->type); - size_t structsize = getTypePaddedSize(structtype); - - // FIXME: this could probably share some code with the above - if (structsize > lastoffset+lastsize) - { - for (/*continue from first loop*/; i < nvars; i++) - { - VarDeclaration* var = vars[i]; - - // get var info - size_t os = var->offset; - size_t sz = var->type->size(); - - // skip? - if (os < lastoffset + lastsize) - continue; - - // add any 0 padding needed before this field - if (os > lastoffset + lastsize) - { - //printf("2added %lu zeros\n", os - lastoffset - lastsize); - add_zeros(values, os - lastoffset - lastsize); - } - - // get field default init - IrField* f = var->ir.irField; - assert(f); - values.push_back(f->getDefaultInit()); - - lastoffset = os; - lastsize = sz; - //printf("2added default: %s : %lu (%lu)\n", var->toChars(), os, sz); - } - } - - // add any 0 padding needed at the end of the literal - if (structsize > lastoffset+lastsize) - { - //printf("3added %lu zeros\n", structsize - lastoffset - lastsize); - add_zeros(values, structsize - lastoffset - lastsize); - } - - return values; -} - /// Return the type returned by DtoUnpaddedStruct called on a value of the /// specified type. /// Union types will get expanded into a struct, with a type for each member. diff --git a/gen/structs.h b/gen/structs.h index 8613744a..5af27956 100644 --- a/gen/structs.h +++ b/gen/structs.h @@ -39,10 +39,6 @@ void DtoResolveStruct(StructDeclaration* sd); /// Build constant struct initializer. llvm::Constant* DtoConstStructInitializer(StructInitializer* si); -/// Build values for a struct literal. -std::vector DtoStructLiteralValues(const StructDeclaration* sd, - const std::vector& inits); - /// Returns a boolean=true if the two structs are equal. llvm::Value* DtoStructEquals(TOK op, DValue* lhs, DValue* rhs); diff --git a/gen/toir.cpp b/gen/toir.cpp index 5b801236..3a6e3302 100644 --- a/gen/toir.cpp +++ b/gen/toir.cpp @@ -36,6 +36,7 @@ #include "gen/typeinf.h" #include "gen/utils.h" #include "gen/warnings.h" +#include "ir/irtypestruct.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/ManagedStatic.h" #include @@ -203,24 +204,30 @@ DValue* VarExp::toElem(IRState* p) Logger::println("a normal variable"); // take care of forward references of global variables - if (vd->isDataseg() || (vd->storage_class & STCextern)) + const bool isGlobal = vd->isDataseg() || (vd->storage_class & STCextern); + if (isGlobal) vd->codegen(Type::sir); - LLValue* val; + assert(vd->ir.isSet() && "Variable not resolved."); - if (!vd->ir.isSet() || !(val = vd->ir.getIrValue())) { - // FIXME: this error is bad! - // We should be VERY careful about adding errors in general, as they have - // a tendency to "mask" out the underlying problems ... - error("variable %s not resolved", vd->toChars()); - if (Logger::enabled()) - Logger::cout() << "unresolved variable had type: " << *DtoType(vd->type) << '\n'; - fatal(); + llvm::Value* val = vd->ir.getIrValue(); + assert(val && "Variable value not set yet."); + + if (isGlobal) + { + llvm::Type* expectedType = DtoType(type->pointerTo()); + // The type of globals is determined by their initializer, so + // we might need to cast. Make sure that the type sizes fit - + // '==' instead of '<=' should probably work as well. + if (val->getType() != expectedType) + { + llvm::Type* t = llvm::cast(val->getType())->getElementType(); + assert(getTypeStoreSize(DtoType(type)) <= getTypeStoreSize(t) && + "Global type mismatch, encountered type too small."); + val = DtoBitCast(val, expectedType); + } } - if (vd->isDataseg() || (vd->storage_class & STCextern)) - val = DtoBitCast(val, DtoType(type->pointerTo())); - return new DVarValue(type, vd, val); } } @@ -2783,20 +2790,45 @@ LLConstant* ArrayLiteralExp::toConstElem(IRState* p) // dynamic arrays can occur here as well ... bool dyn = (bt->ty != Tsarray); - // build the initializer + // Build the initializer. We have to take care as due to unions in the + // element types (with different fields being initialized), we can end up + // with different types for the initializer values. In this case, we + // generate a packed struct constant instead of an array constant. + LLType *elementType = NULL; + bool differentTypes = false; + std::vector vals; vals.reserve(elements->dim); - for (unsigned i=0; idim; ++i) + for (unsigned i = 0; i < elements->dim; ++i) { - Expression* expr = static_cast(elements->data[i]); - vals.push_back(expr->toConstElem(p)); + LLConstant *val = (*elements)[i]->toConstElem(p); + if (!elementType) + elementType = val->getType(); + else + differentTypes |= (elementType != val->getType()); + vals.push_back(val); } - // build the constant array initialize - LLArrayType *t = elements->dim == 0 ? - arrtype : - LLArrayType::get(vals.front()->getType(), elements->dim); - LLConstant* initval = LLConstantArray::get(t, vals); + LLConstant *initval; + if (differentTypes) + { + std::vector types; + types.reserve(elements->dim); + for (unsigned i = 0; i < elements->dim; ++i) + types.push_back(vals[i]->getType()); + LLStructType *t = llvm::StructType::get(gIR->context(), types, true); + initval = LLConstantStruct::get(t, vals); + } + else if (!elementType) + { + assert(elements->dim == 0); + initval = LLConstantArray::get(arrtype, vals); + } + else + { + LLArrayType *t = LLArrayType::get(elementType, elements->dim); + initval = LLConstantArray::get(t, vals); + } // if static array, we're done if (!dyn) @@ -2805,7 +2837,9 @@ LLConstant* ArrayLiteralExp::toConstElem(IRState* p) // we need to put the initializer in a global, and so we have a pointer to the array // Important: don't make the global constant, since this const initializer might // be used as an initializer for a static T[] - where modifying contents is allowed. - LLConstant* globalstore = new LLGlobalVariable(*gIR->module, t, false, LLGlobalValue::InternalLinkage, initval, ".dynarrayStorage"); + LLConstant* globalstore = new LLGlobalVariable(*gIR->module, + initval->getType(), false, LLGlobalValue::InternalLinkage, initval, + ".dynarrayStorage"); globalstore = DtoBitCast(globalstore, getPtrToType(arrtype)); if (bt->ty == Tpointer) @@ -2961,37 +2995,21 @@ LLConstant* StructLiteralExp::toConstElem(IRState* p) sd->codegen(Type::sir); // get inits - std::vector inits(sd->fields.dim, 0); - - size_t nexprs = elements->dim;; - Expression** exprs = (Expression**)elements->data; + llvm::SmallVector varInits; + size_t nexprs = elements->dim; for (size_t i = 0; i < nexprs; i++) - if (exprs[i]) - inits[i] = exprs[i]->toConstElem(p); - - // vector of values to build aggregate from - std::vector values = DtoStructLiteralValues(sd, inits); - - // we know those values are constants.. cast them - std::vector constvals(values.size(), 0); - std::vector types(values.size(), 0); - for (size_t i = 0; i < values.size(); ++i) { - constvals[i] = llvm::cast(values[i]); - types[i] = values[i]->getType(); - } - - // return constant struct - if (!constType) { - if (type->ty == Ttypedef) // hack, see DtoConstInitializer. - constType = LLStructType::get(gIR->context(), types); - else - constType = isaStruct(DtoType(type)); + if ((*elements)[i]) + { + IrTypeStruct::VarInitConst v; + v.first = sd->fields[i]; + v.second = (*elements)[i]->toConstElem(p); + varInits.push_back(v); + } } - else if (constType->isOpaque()) - constType->setBody(types); - return LLConstantStruct::get(constType, llvm::makeArrayRef(constvals)); + + return sd->type->irtype->isStruct()->createInitializerConstant(varInits); } ////////////////////////////////////////////////////////////////////////////////////////// diff --git a/ir/ir.h b/ir/ir.h index ecf28dc7..fa20b998 100644 --- a/ir/ir.h +++ b/ir/ir.h @@ -23,11 +23,6 @@ struct IRState; struct IrFunction; -struct IrBase : Object -{ - virtual ~IrBase() {} -}; - class Ir { public: diff --git a/ir/irfunction.h b/ir/irfunction.h index fcc1082d..28573049 100644 --- a/ir/irfunction.h +++ b/ir/irfunction.h @@ -89,7 +89,7 @@ private: }; // represents a function -struct IrFunction : IrBase +struct IrFunction { // constructor IrFunction(FuncDeclaration* fd); diff --git a/ir/irfuncty.h b/ir/irfuncty.h index 0beac0cc..30c2cdf2 100644 --- a/ir/irfuncty.h +++ b/ir/irfuncty.h @@ -37,7 +37,7 @@ namespace llvm { // represents a function type argument // both explicit and implicit as well as return values -struct IrFuncTyArg : IrBase +struct IrFuncTyArg { /** This is the original D type as the frontend knows it * May NOT be rewritten!!! */ @@ -85,7 +85,7 @@ struct IrFuncTyArg : IrBase }; // represents a function type -struct IrFuncTy : IrBase +struct IrFuncTy { // return value IrFuncTyArg* ret; diff --git a/ir/irmodule.h b/ir/irmodule.h index 8fcd2d6e..e69545e2 100644 --- a/ir/irmodule.h +++ b/ir/irmodule.h @@ -19,7 +19,7 @@ struct Module; -struct IrModule : IrBase +struct IrModule { IrModule(Module* module, const char* srcfilename); virtual ~IrModule(); diff --git a/ir/irstruct.cpp b/ir/irstruct.cpp index 0873f403..c8f0f15a 100644 --- a/ir/irstruct.cpp +++ b/ir/irstruct.cpp @@ -92,6 +92,11 @@ llvm::Constant * IrStruct::getDefaultInit() types.push_back((*itr)->getType()); init_type->setBody(types, packed); + // Whatever type we end up with due to unions, ..., it should match the + // the LLVM type corresponding to the D struct type in size. + assert(getTypeStoreSize(DtoType(type)) <= getTypeStoreSize(init_type) && + "Struct initializer type mismatch, encountered type too small."); + // build constant struct constInit = LLConstantStruct::get(init_type, constants); IF_LOG Logger::cout() << "final default initializer: " << *constInit << std::endl; @@ -213,19 +218,6 @@ std::vector IrStruct::createStructDefaultInitializer() ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// -// yet another rewrite of the notorious StructInitializer. - -typedef std::pair VCPair; - -bool struct_init_data_sort(const VCPair& a, const VCPair& b) -{ - return (a.first && b.first) - ? a.first->offset < b.first->offset - : false; -} - -// this time a bit more inspired by the DMD code. - LLConstant * IrStruct::createStructInitializer(StructInitializer * si) { IF_LOG Logger::println("Building StructInitializer of type %s", si->ad->toPrettyChars()); @@ -236,7 +228,7 @@ LLConstant * IrStruct::createStructInitializer(StructInitializer * si) assert(si->vars.dim == si->value.dim && "inconsistent StructInitializer"); // array of things to build - llvm::SmallVector data(aggrdecl->fields.dim); + llvm::SmallVector data(aggrdecl->fields.dim); // start by creating a map from initializer indices to field indices. // I have no fucking clue why dmd represents it like this :/ @@ -345,65 +337,8 @@ LLConstant * IrStruct::createStructInitializer(StructInitializer * si) fatal(); } - // sort data array by offset - std::sort(data.begin(), data.end(), struct_init_data_sort); - - // build array of constants and make sure explicit zero padding is inserted when necessary. - size_t offset = 0; - std::vector constants; - constants.reserve(n); - - for (size_t i = 0; i < n; i++) - { - VarDeclaration* vd = data[i].first; - if (vd == NULL) - continue; - - // get next aligned offset for this field - size_t alignedoffset = offset; - if (!packed) - { - alignedoffset = realignOffset(alignedoffset, vd->type); - } - - // insert explicit padding? - if (alignedoffset < vd->offset) - { - size_t diff = vd->offset - alignedoffset; - IF_LOG Logger::println("adding %zu bytes zero padding", diff); - add_zeros(constants, diff); - } - - IF_LOG Logger::println("adding field %s", vd->toChars()); - - constants.push_back(data[i].second); - offset = vd->offset + vd->type->size(); - } - - // tail padding? - if (offset < aggrdecl->structsize) - { - size_t diff = aggrdecl->structsize - offset; - IF_LOG Logger::println("adding %zu bytes zero padding", diff); - add_zeros(constants, diff); - } - - // get initializer type - LLStructType* <ype = si->ltype; - if (!ltype || ltype->isOpaque()) { - std::vector::iterator itr, end = constants.end(); - std::vector types; - for (itr = constants.begin(); itr != end; ++itr) - types.push_back((*itr)->getType()); - if (!ltype) - ltype = LLStructType::get(gIR->context(), types, packed); - else - ltype->setBody(types, packed); - } - - // build constant - assert(!constants.empty()); - llvm::Constant* c = LLConstantStruct::get(ltype, constants); - IF_LOG Logger::cout() << "final struct initializer: " << *c << std::endl; - return c; + llvm::Constant* init = + type->irtype->isStruct()->createInitializerConstant(data, si->ltype); + si->ltype = static_cast(init->getType()); + return init; } diff --git a/ir/irstruct.h b/ir/irstruct.h index 28c8f01d..7159149d 100644 --- a/ir/irstruct.h +++ b/ir/irstruct.h @@ -26,7 +26,7 @@ struct StructInitializer; // represents a struct or class // it is used during codegen to hold all the vital info we need -struct IrStruct : IrBase +struct IrStruct { /// Constructor. IrStruct(AggregateDeclaration* agg); diff --git a/ir/irtype.h b/ir/irtype.h index c3205350..21c78336 100644 --- a/ir/irtype.h +++ b/ir/irtype.h @@ -47,11 +47,21 @@ class IrTypeVector; /// Derived classes should be created using their static get() methods, which /// makes sure that uniqueness is preserved in the face of forward references. /// Note that the get() methods expect the IrType of the passed type/symbol to -/// be not yet set. This could be altered to just return the existing IrType -/// in order to bring the API entirely in line with the LLVM type get() methods. +/// be not yet set. +/// +/// This could be altered to just return the existing IrType in order to bring +/// the API entirely in line with the LLVM type get() methods. It has not been +/// changed so far since currently all clients use the DtoType wrapper rather +/// than handling IrType instances directly, and keeping it this way allows to +/// easily check for uniqueness violations in the face of forward references. +/// TODO: Implement the described changes (now that the forward reference +/// handling logic seems to work correctly) and get rid of the "no-op" DtoType +/// calls in IrStruct, ... that only exist for their side effect. class IrType { public: + virtual ~IrType() {} + /// virtual IrTypeAggr* isAggr() { return NULL; } /// diff --git a/ir/irtypestruct.cpp b/ir/irtypestruct.cpp index e46cdc9d..b233d161 100644 --- a/ir/irtypestruct.cpp +++ b/ir/irtypestruct.cpp @@ -36,6 +36,108 @@ IrTypeAggr::IrTypeAggr(AggregateDeclaration * ad) } ////////////////////////////////////////////////////////////////////////////// + +static bool struct_init_data_sort(const IrTypeAggr::VarInitConst& a, + const IrTypeAggr::VarInitConst& b) +{ + return (a.first && b.first) + ? a.first->offset < b.first->offset + : false; +} + +extern size_t add_zeros(std::vector& constants, size_t diff); + +// return a constant array of type arrTypeD initialized with a constant value, or that constant value +static llvm::Constant* FillSArrayDims(Type* arrTypeD, llvm::Constant* init) +{ + if (arrTypeD->ty == Tsarray) + { + init = FillSArrayDims(arrTypeD->nextOf(), init); + size_t dim = static_cast(arrTypeD)->dim->toUInteger(); + llvm::ArrayType* arrty = llvm::ArrayType::get(init->getType(), dim); + return llvm::ConstantArray::get(arrty, + std::vector(dim, init)); + } + return init; +} + +llvm::Constant* IrTypeAggr::createInitializerConstant( + llvm::ArrayRef initializers, + llvm::StructType* initializerType) +{ + const bool packed = (dtype->ty == Tstruct) + ? dtype->alignsize() == 1 + : false; + + const size_t n = initializers.size(); + + // sort data array by offset + llvm::SmallVector data( + initializers.begin(), initializers.end()); + std::sort(data.begin(), data.end(), struct_init_data_sort); + + // build array of constants and make sure explicit zero padding is inserted when necessary. + size_t offset = 0; + std::vector constants; + constants.reserve(n); + + for (size_t i = 0; i < n; i++) + { + VarDeclaration* vd = data[i].first; + if (vd == NULL) + continue; + + // get next aligned offset for this field + size_t alignedoffset = offset; + if (!packed) + { + alignedoffset = realignOffset(alignedoffset, vd->type); + } + + // insert explicit padding? + if (alignedoffset < vd->offset) + { + size_t diff = vd->offset - alignedoffset; + IF_LOG Logger::println("adding %zu bytes zero padding", diff); + add_zeros(constants, diff); + } + + IF_LOG Logger::println("adding field %s", vd->toChars()); + + constants.push_back(FillSArrayDims(vd->type, initializers[i].second)); + offset = vd->offset + vd->type->size(); + } + + // tail padding? + const size_t structsize = getTypePaddedSize(type); + if (offset < structsize) + { + size_t diff = structsize - offset; + IF_LOG Logger::println("adding %zu bytes zero padding", diff); + add_zeros(constants, diff); + } + + // get initializer type + if (!initializerType || initializerType->isOpaque()) + { + std::vector::iterator itr, end = constants.end(); + std::vector types; + types.reserve(constants.size()); + for (itr = constants.begin(); itr != end; ++itr) + types.push_back((*itr)->getType()); + if (!initializerType) + initializerType = LLStructType::get(gIR->context(), types, packed); + else + initializerType->setBody(types, packed); + } + + // build constant + assert(!constants.empty()); + llvm::Constant* c = LLConstantStruct::get(initializerType, constants); + IF_LOG Logger::cout() << "final struct initializer: " << *c << std::endl; + return c; +} + ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// diff --git a/ir/irtypestruct.h b/ir/irtypestruct.h index f52dff45..eb64d988 100644 --- a/ir/irtypestruct.h +++ b/ir/irtypestruct.h @@ -16,9 +16,15 @@ #define __LDC_IR_IRTYPESTRUCT_H__ #include "ir/irtype.h" +#include "llvm/ADT/ArrayRef.h" +#include ////////////////////////////////////////////////////////////////////////////// +namespace llvm { + class StructType; +} + struct AggregateDeclaration; struct StructDeclaration; struct TypeStruct; @@ -40,6 +46,25 @@ public: /// iterator def_end() { return default_fields.end(); } + /// A pair of a member variable declaration and an associated initializer + /// constant. + typedef std::pair VarInitConst; + + /// Creates an initializer constant for the struct type with the given + /// fields set to the provided constants. The remaining space (not + /// explicitly specified fields, padding) is default-initialized. + /// + /// The optional initializerType parmeter can be used to specify the exact + /// LLVM type to use for the initializer. If non-null and non-opaque, the + /// type must exactly match the generated constant. This parameter is used + /// mainly for supporting legacy code. + /// + /// Note that in the general case (if e.g. unions are involved), the + /// returned type is not necessarily the same as getLLType(). + llvm::Constant* createInitializerConstant( + llvm::ArrayRef initializers, + llvm::StructType* initializerType = 0); + protected: /// IrTypeAggr(AggregateDeclaration* ad); diff --git a/ir/irvar.h b/ir/irvar.h index 8b12e8b3..50ab184a 100644 --- a/ir/irvar.h +++ b/ir/irvar.h @@ -24,7 +24,7 @@ struct IrFuncTyArg; -struct IrVar : IrBase +struct IrVar { IrVar(VarDeclaration* var);