mirror of
https://github.com/xomboverlord/ldc.git
synced 2026-01-18 13:53:14 +01:00
Merge pull request #360 from klickverbot/union-initializer
Unify struct initializer codegen, fix it for unions
This commit is contained in:
@@ -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());
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
161
gen/structs.cpp
161
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<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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
118
gen/toir.cpp
118
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 <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);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
5
ir/ir.h
5
ir/ir.h
@@ -23,11 +23,6 @@
|
||||
struct IRState;
|
||||
struct IrFunction;
|
||||
|
||||
struct IrBase : Object
|
||||
{
|
||||
virtual ~IrBase() {}
|
||||
};
|
||||
|
||||
class Ir
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -89,7 +89,7 @@ private:
|
||||
};
|
||||
|
||||
// represents a function
|
||||
struct IrFunction : IrBase
|
||||
struct IrFunction
|
||||
{
|
||||
// constructor
|
||||
IrFunction(FuncDeclaration* fd);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
struct Module;
|
||||
|
||||
struct IrModule : IrBase
|
||||
struct IrModule
|
||||
{
|
||||
IrModule(Module* module, const char* srcfilename);
|
||||
virtual ~IrModule();
|
||||
|
||||
@@ -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* <ype = 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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
14
ir/irtype.h
14
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; }
|
||||
///
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
struct IrFuncTyArg;
|
||||
|
||||
struct IrVar : IrBase
|
||||
struct IrVar
|
||||
{
|
||||
IrVar(VarDeclaration* var);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user