mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-08 20:06:03 +03:00
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:
parent
b4f8bd6e52
commit
1958e17734
3 changed files with 33 additions and 138 deletions
|
@ -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: ");
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
97
gen/toir.cpp
97
gen/toir.cpp
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue