Unify handling of struct initializers.

GitHub: Fixes #351.
This commit is contained in:
David Nadlinger
2013-05-18 16:48:03 +02:00
parent b577d1cf0b
commit 7b435c2c87
4 changed files with 27 additions and 193 deletions

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>
@@ -2988,37 +2989,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

@@ -47,6 +47,20 @@ static bool struct_init_data_sort(const IrTypeAggr::VarInitConst& a,
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)
@@ -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();
}