Merge pull request #360 from klickverbot/union-initializer

Unify struct initializer codegen, fix it for unions
This commit is contained in:
David Nadlinger
2013-05-19 11:49:55 -07:00
17 changed files with 291 additions and 484 deletions

View File

@@ -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<TypeSArray*>(arrty);
size_t arrlen = static_cast<size_t>(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<LLType*> 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<Expression*>(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<Initializer*>(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());

View File

@@ -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';
}
}

View File

@@ -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<TypeTypedef*>(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<TypeSArray*>(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<TypeStruct*>(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<StructLiteralExp*>(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<VarExp*>(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

View File

@@ -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);

View File

@@ -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<LLConstant*>& 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<TypeSArray*>(arrTypeD)->dim->toUInteger();
LLArrayType* arrty = LLArrayType::get(init->getType(), dim);
return LLConstantArray::get(arrty, std::vector<LLConstant*>(dim, init));
}
return init;
}
std::vector<LLConstant*> DtoStructLiteralValues(const StructDeclaration* sd,
const std::vector<LLConstant*>& 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<VarDeclaration*> 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<LLConstant*> 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; i<nvars; i++)
{
VarDeclaration* var = vars[i];
// get var info
size_t os = var->offset;
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.

View File

@@ -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<llvm::Constant*> DtoStructLiteralValues(const StructDeclaration* sd,
const std::vector<llvm::Constant*>& inits);
/// Returns a boolean=true if the two structs are equal.
llvm::Value* DtoStructEquals(TOK op, DValue* lhs, DValue* rhs);

View File

@@ -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 <fstream>
@@ -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<llvm::PointerType>(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<LLConstant*> vals;
vals.reserve(elements->dim);
for (unsigned i=0; i<elements->dim; ++i)
for (unsigned i = 0; i < elements->dim; ++i)
{
Expression* expr = static_cast<Expression*>(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<llvm::Type*> 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<LLConstant*> inits(sd->fields.dim, 0);
size_t nexprs = elements->dim;;
Expression** exprs = (Expression**)elements->data;
llvm::SmallVector<IrTypeStruct::VarInitConst, 16> 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<LLConstant*> values = DtoStructLiteralValues(sd, inits);
// we know those values are constants.. cast them
std::vector<LLConstant*> constvals(values.size(), 0);
std::vector<LLType*> types(values.size(), 0);
for (size_t i = 0; i < values.size(); ++i) {
constvals[i] = llvm::cast<LLConstant>(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);
}
//////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -23,11 +23,6 @@
struct IRState;
struct IrFunction;
struct IrBase : Object
{
virtual ~IrBase() {}
};
class Ir
{
public:

View File

@@ -89,7 +89,7 @@ private:
};
// represents a function
struct IrFunction : IrBase
struct IrFunction
{
// constructor
IrFunction(FuncDeclaration* fd);

View File

@@ -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;

View File

@@ -19,7 +19,7 @@
struct Module;
struct IrModule : IrBase
struct IrModule
{
IrModule(Module* module, const char* srcfilename);
virtual ~IrModule();

View File

@@ -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<llvm::Constant*> IrStruct::createStructDefaultInitializer()
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// yet another rewrite of the notorious StructInitializer.
typedef std::pair<VarDeclaration*, llvm::Constant*> 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<VCPair, 16> data(aggrdecl->fields.dim);
llvm::SmallVector<IrTypeStruct::VarInitConst, 16> 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<llvm::Constant*> 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* &ltype = si->ltype;
if (!ltype || ltype->isOpaque()) {
std::vector<LLConstant*>::iterator itr, end = constants.end();
std::vector<LLType*> 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<llvm::StructType*>(init->getType());
return init;
}

View File

@@ -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);

View File

@@ -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; }
///

View File

@@ -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<llvm::Constant*>& 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<TypeSArray*>(arrTypeD)->dim->toUInteger();
llvm::ArrayType* arrty = llvm::ArrayType::get(init->getType(), dim);
return llvm::ConstantArray::get(arrty,
std::vector<llvm::Constant*>(dim, init));
}
return init;
}
llvm::Constant* IrTypeAggr::createInitializerConstant(
llvm::ArrayRef<IrTypeAggr::VarInitConst> 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<IrTypeAggr::VarInitConst, 16> 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<llvm::Constant*> 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<llvm::Constant*>::iterator itr, end = constants.end();
std::vector<llvm::Type*> 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;
}
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////

View File

@@ -16,9 +16,15 @@
#define __LDC_IR_IRTYPESTRUCT_H__
#include "ir/irtype.h"
#include "llvm/ADT/ArrayRef.h"
#include <utility>
//////////////////////////////////////////////////////////////////////////////
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<VarDeclaration*, llvm::Constant*> 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<VarInitConst> initializers,
llvm::StructType* initializerType = 0);
protected:
///
IrTypeAggr(AggregateDeclaration* ad);

View File

@@ -24,7 +24,7 @@
struct IrFuncTyArg;
struct IrVar : IrBase
struct IrVar
{
IrVar(VarDeclaration* var);