From f57eac796f50944a9c05b58d781f471586e1f9e0 Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Wed, 15 May 2013 20:19:44 +0200 Subject: [PATCH 01/10] Expand on IrType::get rationale. --- ir/irtype.h | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/ir/irtype.h b/ir/irtype.h index c3205350..6d2826ba 100644 --- a/ir/irtype.h +++ b/ir/irtype.h @@ -47,8 +47,16 @@ 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: From beed5c3044f241f7da407b09de4313640feff747 Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Wed, 15 May 2013 20:28:53 +0200 Subject: [PATCH 02/10] Get rid of empty IrBase class. It isn't useful in any way now that the GC is out of the picture, and the existance of a "dummy" toChars() method is annoying when debugging. --- ir/ir.h | 5 ----- ir/irfunction.h | 2 +- ir/irfuncty.h | 4 ++-- ir/irmodule.h | 2 +- ir/irstruct.h | 2 +- ir/irvar.h | 2 +- 6 files changed, 6 insertions(+), 11 deletions(-) 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.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/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); From b49cbeaaf1909539de166a9664e3ae7501fda705 Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Fri, 17 May 2013 01:02:02 +0200 Subject: [PATCH 03/10] Factored out struct initializer constant generation. --- ir/irstruct.cpp | 80 +++-------------------------------------- ir/irtype.h | 2 ++ ir/irtypestruct.cpp | 88 +++++++++++++++++++++++++++++++++++++++++++++ ir/irtypestruct.h | 25 +++++++++++++ 4 files changed, 120 insertions(+), 75 deletions(-) diff --git a/ir/irstruct.cpp b/ir/irstruct.cpp index 0873f403..3e749cf9 100644 --- a/ir/irstruct.cpp +++ b/ir/irstruct.cpp @@ -213,19 +213,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 +223,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 +332,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/irtype.h b/ir/irtype.h index 6d2826ba..21c78336 100644 --- a/ir/irtype.h +++ b/ir/irtype.h @@ -60,6 +60,8 @@ class IrTypeVector; class IrType { public: + virtual ~IrType() {} + /// virtual IrTypeAggr* isAggr() { return NULL; } /// diff --git a/ir/irtypestruct.cpp b/ir/irtypestruct.cpp index e46cdc9d..a7c473bc 100644 --- a/ir/irtypestruct.cpp +++ b/ir/irtypestruct.cpp @@ -36,6 +36,94 @@ 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); + +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(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); From b577d1cf0b6476ce8aa1f0a0f37da782d8466801 Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Sat, 18 May 2013 16:46:57 +0200 Subject: [PATCH 04/10] Handle different element initializer types in array literals. --- gen/toir.cpp | 47 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/gen/toir.cpp b/gen/toir.cpp index 5b801236..4192e01d 100644 --- a/gen/toir.cpp +++ b/gen/toir.cpp @@ -2783,20 +2783,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 +2830,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) From 7b435c2c87fa4b22afdc929cabbc066ed64bdbd8 Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Sat, 18 May 2013 16:48:03 +0200 Subject: [PATCH 05/10] Unify handling of struct initializers. GitHub: Fixes #351. --- gen/structs.cpp | 161 -------------------------------------------- gen/structs.h | 4 -- gen/toir.cpp | 39 ++++------- ir/irtypestruct.cpp | 16 ++++- 4 files changed, 27 insertions(+), 193 deletions(-) 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 4192e01d..a932e043 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 @@ -2988,37 +2989,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/irtypestruct.cpp b/ir/irtypestruct.cpp index a7c473bc..b233d161 100644 --- a/ir/irtypestruct.cpp +++ b/ir/irtypestruct.cpp @@ -47,6 +47,20 @@ static bool struct_init_data_sort(const IrTypeAggr::VarInitConst& a, 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) @@ -90,7 +104,7 @@ llvm::Constant* IrTypeAggr::createInitializerConstant( IF_LOG Logger::println("adding field %s", vd->toChars()); - constants.push_back(initializers[i].second); + constants.push_back(FillSArrayDims(vd->type, initializers[i].second)); offset = vd->offset + vd->type->size(); } From f8f405000b6a45a479eaba6ff26d9c993e50ac28 Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Sat, 18 May 2013 17:24:25 +0200 Subject: [PATCH 06/10] Avoid confusing naming convention. We generally do not really use underscore prefixes. But if they are used in other codebases, they typically refer to member variables. --- gen/declarations.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/gen/declarations.cpp b/gen/declarations.cpp index 2f32b110..8fa712b9 100644 --- a/gen/declarations.cpp +++ b/gen/declarations.cpp @@ -143,27 +143,26 @@ 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; + bool isLLConst = isConst() && init; Logger::println("Creating global variable"); assert(!ir.initialized); ir.initialized = gIR->dmodule; - std::string _name(mangle()); + std::string llName(mangle()); - LLType *_type = DtoConstInitializerType(type, init); + LLType *llType = 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, + LLGlobalVariable* gvar = new LLGlobalVariable(*gIR->module, llType, isLLConst, + DtoLinkage(this), NULL, llName, 0, isThreadlocal() ? LLGlobalVariable::GeneralDynamicTLSModel : LLGlobalVariable::NotThreadLocal); #else - LLGlobalVariable* gvar = new LLGlobalVariable(*gIR->module, _type, _isconst, - DtoLinkage(this), NULL, _name, 0, isThreadlocal()); + LLGlobalVariable* gvar = new LLGlobalVariable(*gIR->module, llType, isLLConst, + DtoLinkage(this), NULL, llName, 0, isThreadlocal()); #endif this->ir.irGlobal->value = gvar; From eef05ba0194c9c0c6e120bc0a20b6ea1cb55c0c2 Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Sat, 18 May 2013 17:35:31 +0200 Subject: [PATCH 07/10] Do not try to guess exact initializer type. This might have been required with the old (pre-3.0) LLVM type system, but the module linker handles global type resolution just fine now. Also, it is virtually impossible to determine the type in advance for some cases, e.g. an array of unions with an initializer that contains pointers to the array itself. --- gen/declarations.cpp | 112 ++++++++++++++++++++++++++----------------- 1 file changed, 67 insertions(+), 45 deletions(-) diff --git a/gen/declarations.cpp b/gen/declarations.cpp index 8fa712b9..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,65 +157,73 @@ void VarDeclaration::codegen(Ir* p) Logger::println("parent: %s (%s)", parent->toChars(), parent->kind()); - bool isLLConst = 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 llName(mangle()); - LLType *llType = 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, llType, isLLConst, - DtoLinkage(this), NULL, llName, 0, - isThreadlocal() ? LLGlobalVariable::GeneralDynamicTLSModel - : LLGlobalVariable::NotThreadLocal); -#else - LLGlobalVariable* gvar = new LLGlobalVariable(*gIR->module, llType, isLLConst, - DtoLinkage(this), NULL, llName, 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'; } } From 41e580a79f15f7ecb66dbcfe3586a9b369fa4d61 Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Sat, 18 May 2013 17:37:03 +0200 Subject: [PATCH 08/10] Get rid of now-obsolete DtoConstInitializerType. --- gen/arrays.cpp | 79 --------------------------------------------- gen/llvmhelpers.cpp | 54 ------------------------------- gen/llvmhelpers.h | 1 - 3 files changed, 134 deletions(-) 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/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); From 1fed92b9b7292fd6f46cef530a8b11340bed4d28 Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Sat, 18 May 2013 19:40:53 +0200 Subject: [PATCH 09/10] VarExp::toElem refactoring and global size assertion. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Frontend errors shouldn't be used for glue layer consistency checks – maybe the error was actually hit in ancient versions. --- gen/toir.cpp | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/gen/toir.cpp b/gen/toir.cpp index a932e043..3a6e3302 100644 --- a/gen/toir.cpp +++ b/gen/toir.cpp @@ -204,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); } } From 87c8a96545e7e693a373e3ab4add139680eaaecb Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Sat, 18 May 2013 19:42:39 +0200 Subject: [PATCH 10/10] Check initializer type size in IrStruct::getDefaultInit(). --- ir/irstruct.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ir/irstruct.cpp b/ir/irstruct.cpp index 3e749cf9..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;