Rewrite StructLiteralExp::toElem to store individual fields instead of

generating a constant to fill the entire struct with a single `store`.
This is much more efficient at compile time (fixing #320) and vastly reduces
the size of the emitted code. Since LLVM no longer needs to keep the data for
all fields in "registers" until the store happens, it should also be more
efficient at run time in cases where the fields aren't assigned with constants.

There's also some code clean-up by removing duplicated logic.
This commit is contained in:
Frits van Bommel 2009-06-06 20:16:13 +02:00
parent b4f8bd6e52
commit 1958e17734
3 changed files with 33 additions and 138 deletions

View file

@ -1226,78 +1226,6 @@ LLConstant* DtoConstExpInit(Loc loc, Type* type, Expression* exp)
//////////////////////////////////////////////////////////////////////////////////////////
static LLValue* expand_value_to_sarray(Type *base, Expression* exp)
{
Logger::println("building type %s from expression (%s) of type %s", base->toChars(), exp->toChars(), exp->type->toChars());
const LLType* dstTy = DtoType(base);
if (Logger::enabled())
Logger::cout() << "final llvm type requested: " << *dstTy << '\n';
// get initial value
LLValue* val = exp->toElem(gIR)->getRVal();
if (DtoIsPassedByRef(exp->type))
val = DtoLoad(val);
Type* expbase = exp->type->toBasetype();
Logger::println("expbase: %s", expbase->toChars());
Type* t = base->toBasetype();
LLSmallVector<size_t, 4> dims;
while(1)
{
Logger::println("t: %s", t->toChars());
if (t->equals(expbase))
break;
assert(t->ty == Tsarray);
TypeSArray* tsa = (TypeSArray*)t;
dims.push_back(tsa->dim->toInteger());
assert(t->nextOf());
t = t->nextOf()->toBasetype();
}
size_t i = dims.size();
assert(i);
std::vector<LLValue*> inits;
while (i--)
{
// start with undefined array
const LLArrayType* arrty = LLArrayType::get(val->getType(), dims[i]);
LLValue* tmp = llvm::UndefValue::get(arrty);
for (size_t j = 0; j < dims[i]; j++)
{
tmp = gIR->ir->CreateInsertValue(tmp, val, j);
}
val = tmp;
}
return val;
}
LLValue* DtoExprValue(Type* type, Expression* e)
{
Type* t1 = e->type->toBasetype();
Type* t2 = type->toBasetype();
// expand static arrays
if (t2->ty == Tsarray && !t1->equals(t2))
{
return expand_value_to_sarray(t2, e);
}
// or not
else
{
DValue* dv = e->toElem(gIR);
LLValue* v = dv->getRVal();
if (DtoIsPassedByRef(e->type))
v = DtoLoad(v);
return v;
}
}
//////////////////////////////////////////////////////////////////////////////////////////
void DtoAnnotation(const char* str)
{
std::string s("CODE: ");

View file

@ -105,8 +105,6 @@ LLConstant* DtoConstInitializer(Loc loc, Type* type, Initializer* init);
LLConstant* DtoConstExpInit(Loc loc, Type* t, Expression* exp);
DValue* DtoInitializer(LLValue* target, Initializer* init);
LLValue* DtoExprValue(Type* type, Expression* e);
// annotation generator
void DtoAnnotation(const char* str);

View file

@ -2409,11 +2409,15 @@ LLConstant* ArrayLiteralExp::toConstElem(IRState* p)
//////////////////////////////////////////////////////////////////////////////////////////
// building a struct literal is pretty much the same as building a default initializer.
extern size_t add_zeros(std::vector<llvm::Value*>& values, size_t diff);
extern LLConstant* get_default_initializer(VarDeclaration* vd, Initializer* init);
static LLValue* write_zeroes(LLValue* mem, unsigned start, unsigned end) {
mem = DtoBitCast(mem, getVoidPtrType());
LLValue* gep = DtoGEPi1(mem, start, ".padding");
DtoMemSetZero(gep, DtoConstSize_t(end - start));
return mem;
}
DValue* StructLiteralExp::toElem(IRState* p)
{
Logger::print("StructLiteralExp::toElem: %s @ %s\n", toChars(), type->toChars());
@ -2422,97 +2426,62 @@ DValue* StructLiteralExp::toElem(IRState* p)
// make sure the struct is fully resolved
sd->codegen(Type::sir);
// final list of values to put in the struct
std::vector<LLValue*> initvalues;
// offset tracker
size_t offset = 0;
// align(1) struct S { ... }
bool packed = sd->type->alignsize() == 1;
// alloca a stack slot
LLValue* mem = DtoRawAlloca(DtoType(type), 0, ".structliteral");
// ready elements data
assert(elements && "struct literal has null elements");
size_t nexprs = elements->dim;;
size_t nexprs = elements->dim;
Expression** exprs = (Expression**)elements->data;
LLValue* voidptr = mem;
unsigned offset = 0;
// go through fields
ArrayIter<VarDeclaration> it(sd->fields);
for (; !it.done(); it.next())
{
VarDeclaration* vd = it.get();
// don't re-initialize unions
if (vd->offset < offset)
{
IF_LOG Logger::println("skipping field: %s %s (+%u)", vd->type->toChars(), vd->toChars(), vd->offset);
continue;
}
// initialize any padding so struct comparisons work
if (vd->offset != offset)
voidptr = write_zeroes(voidptr, offset, vd->offset);
offset = vd->offset + vd->type->size();
IF_LOG Logger::println("using field: %s %s (+%u)", vd->type->toChars(), vd->toChars(), vd->offset);
IF_LOG Logger::println("initializing field: %s %s (+%u)", vd->type->toChars(), vd->toChars(), vd->offset);
// 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)
{
add_zeros(initvalues, vd->offset - alignedoffset);
}
// add initializer
// get initializer
Expression* expr = (it.index < nexprs) ? exprs[it.index] : NULL;
IF_LOG Logger::println("expr: %p", expr);
DValue* val;
DConstValue cv(vd->type, NULL); // Only used in one branch; value is set beforehand
if (expr)
{
IF_LOG Logger::println("expr %zu = %s", it.index, expr->toChars());
LLValue* v = DtoExprValue(vd->type, expr);
initvalues.push_back(v);
val = expr->toElem(gIR);
}
else
{
IF_LOG Logger::println("using default initializer");
initvalues.push_back(get_default_initializer(vd, NULL));
cv.c = get_default_initializer(vd, NULL);
val = &cv;
}
// advance offset to right past this field
offset = vd->offset + vd->type->size();
// get a pointer to this field
DVarValue field(vd->type, vd, DtoIndexStruct(mem, sd, vd));
// store the initializer there
DtoAssign(loc, &field, val);
}
// tail padding?
if (offset < sd->structsize)
{
add_zeros(initvalues, sd->structsize - offset);
}
// build type
std::vector<const LLType*> valuetypes;
size_t n = initvalues.size();
valuetypes.reserve(n);
for (size_t i = 0; i < n; i++)
{
valuetypes.push_back(initvalues[i]->getType());
}
const LLType* st = llvm::StructType::get(valuetypes, packed);
// alloca a stack slot
LLValue* mem = DtoRawAlloca(st, 0, ".structliteral");
// fill in values
for (size_t i = 0; i < n; i++)
{
LLValue* addr = DtoGEPi(mem, 0, i);
p->ir->CreateStore(initvalues[i], addr);
}
// cast to default struct type
mem = DtoBitCast(mem, DtoType(sd->type->pointerTo()));
// initialize trailing padding
if (sd->structsize != offset)
write_zeroes(voidptr, offset, sd->structsize);
// return as a var
return new DVarValue(type, mem);