mirror of
https://github.com/xomboverlord/ldc.git
synced 2026-01-19 06:13:14 +01:00
Merge pull request #493 from klickverbot/sarray-init
Avoid allocating array literals on initialization
This commit is contained in:
103
gen/arrays.cpp
103
gen/arrays.cpp
@@ -11,6 +11,7 @@
|
||||
#include "aggregate.h"
|
||||
#include "declaration.h"
|
||||
#include "dsymbol.h"
|
||||
#include "expression.h"
|
||||
#include "init.h"
|
||||
#include "module.h"
|
||||
#include "mtype.h"
|
||||
@@ -387,6 +388,108 @@ LLConstant* DtoConstArrayInitializer(ArrayInitializer* arrinit)
|
||||
return DtoConstSlice(DtoConstSize_t(arrlen), gep, arrty);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool isConstLiteral(ArrayLiteralExp* ale)
|
||||
{
|
||||
for (size_t i = 0; i < ale->elements->dim; ++i)
|
||||
{
|
||||
// We have to check specifically for '1', as SymOffExp is classified as
|
||||
// '2' and the address of a local variable is not an LLVM constant.
|
||||
if ((*ale->elements)[i]->isConst() != 1)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
llvm::Constant* arrayLiteralToConst(IRState* p, ArrayLiteralExp* ale)
|
||||
{
|
||||
assert(isConstLiteral(ale) && "Array literal cannot be represented as a constant.");
|
||||
|
||||
// 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(ale->elements->dim);
|
||||
for (unsigned i = 0; i < ale->elements->dim; ++i)
|
||||
{
|
||||
llvm::Constant *val = (*ale->elements)[i]->toConstElem(p);
|
||||
if (!elementType)
|
||||
elementType = val->getType();
|
||||
else
|
||||
differentTypes |= (elementType != val->getType());
|
||||
vals.push_back(val);
|
||||
}
|
||||
|
||||
if (differentTypes)
|
||||
return llvm::ConstantStruct::getAnon(vals, true);
|
||||
|
||||
if (!elementType)
|
||||
{
|
||||
assert(ale->elements->dim == 0);
|
||||
elementType = i1ToI8(voidToI8(DtoType(ale->type->toBasetype()->nextOf())));
|
||||
return llvm::ConstantArray::get(LLArrayType::get(elementType, 0), vals);
|
||||
}
|
||||
|
||||
llvm::ArrayType *t = llvm::ArrayType::get(elementType, ale->elements->dim);
|
||||
return llvm::ConstantArray::get(t, vals);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void initializeArrayLiteral(IRState* p, ArrayLiteralExp* ale, LLValue* dstMem)
|
||||
{
|
||||
size_t elemCount = ale->elements->dim;
|
||||
|
||||
// Don't try to write nothing to a zero-element array, we might represent it
|
||||
// as a null pointer.
|
||||
if (elemCount == 0) return;
|
||||
|
||||
if (isConstLiteral(ale))
|
||||
{
|
||||
llvm::Constant* constarr = arrayLiteralToConst(p, ale);
|
||||
|
||||
// Emit a global for longer arrays, as an inline constant is always
|
||||
// lowered to a series of movs or similar at the asm level. The
|
||||
// optimizer can still decide to promote the memcpy intrinsic, so
|
||||
// the cutoff merely affects compilation speed.
|
||||
if (elemCount <= 4)
|
||||
{
|
||||
DtoStore(constarr, DtoBitCast(dstMem, getPtrToType(constarr->getType())));
|
||||
}
|
||||
else
|
||||
{
|
||||
llvm::GlobalVariable* gvar = new llvm::GlobalVariable(
|
||||
*gIR->module,
|
||||
constarr->getType(),
|
||||
true,
|
||||
LLGlobalValue::InternalLinkage,
|
||||
constarr,
|
||||
".arrayliteral"
|
||||
);
|
||||
DtoMemCpy(dstMem, gvar, DtoConstSize_t(getTypePaddedSize(constarr->getType())));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Store the elements one by one.
|
||||
for (size_t i = 0; i < elemCount; ++i)
|
||||
{
|
||||
DValue* e = (*ale->elements)[i]->toElem(p);
|
||||
|
||||
LLValue* elemAddr = DtoGEPi(dstMem, 0, i, "tmp", p->scopebb());
|
||||
DVarValue* vv = new DVarValue(e->type, elemAddr);
|
||||
DtoAssign(ale->loc, vv, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
static LLValue* get_slice_ptr(DSliceValue* e, LLValue*& sz)
|
||||
{
|
||||
|
||||
13
gen/arrays.h
13
gen/arrays.h
@@ -18,9 +18,11 @@
|
||||
#include "gen/llvm.h"
|
||||
|
||||
struct ArrayInitializer;
|
||||
struct ArrayLiteralExp;
|
||||
class DSliceValue;
|
||||
class DValue;
|
||||
struct Expression;
|
||||
struct IRState;
|
||||
struct Loc;
|
||||
struct Type;
|
||||
|
||||
@@ -32,6 +34,17 @@ LLType* DtoConstArrayInitializerType(ArrayInitializer* arrinit);
|
||||
LLConstant* DtoConstArrayInitializer(ArrayInitializer* si);
|
||||
LLConstant* DtoConstSlice(LLConstant* dim, LLConstant* ptr, Type *type = 0);
|
||||
|
||||
/// Returns whether the array literal can be evaluated to a (LLVM) constant.
|
||||
bool isConstLiteral(ArrayLiteralExp* ale);
|
||||
|
||||
/// Returns the constant for the given array literal expression.
|
||||
llvm::Constant* arrayLiteralToConst(IRState* p, ArrayLiteralExp* ale);
|
||||
|
||||
/// Initializes a chunk of memory with the contents of an array literal.
|
||||
///
|
||||
/// dstMem is expected to be a pointer to the array allocation.
|
||||
void initializeArrayLiteral(IRState* p, ArrayLiteralExp* ale, LLValue* dstMem);
|
||||
|
||||
void DtoArrayCopySlices(DSliceValue* dst, DSliceValue* src);
|
||||
void DtoArrayCopyToSlice(DSliceValue* dst, DValue* src);
|
||||
|
||||
|
||||
167
gen/toir.cpp
167
gen/toir.cpp
@@ -492,7 +492,7 @@ DValue* AssignExp::toElem(IRState* p)
|
||||
{
|
||||
Logger::println("performing ref variable initialization");
|
||||
// Note that the variable value is accessed directly (instead
|
||||
// of via getLValue(), which would perform a load from the
|
||||
// of via getLVal(), which would perform a load from the
|
||||
// uninitialized location), and that rhs is stored as an l-value!
|
||||
DVarValue* lhs = e1->toElem(p)->isVar();
|
||||
assert(lhs);
|
||||
@@ -508,6 +508,26 @@ DValue* AssignExp::toElem(IRState* p)
|
||||
}
|
||||
}
|
||||
|
||||
if (e1->op == TOKslice)
|
||||
{
|
||||
// Check if this is an initialization of a static array with an array
|
||||
// literal that the frontend has foolishly rewritten into an
|
||||
// assignment of a dynamic array literal to a slice.
|
||||
Logger::println("performing static array literal assignment");
|
||||
SliceExp * const se = static_cast<SliceExp *>(e1);
|
||||
Type * const t2 = e2->type->toBasetype();
|
||||
Type * const ta = se->e1->type->toBasetype();
|
||||
|
||||
if (se->lwr == NULL && ta->ty == Tsarray &&
|
||||
e2->op == TOKarrayliteral &&
|
||||
t2->nextOf()->mutableOf()->implicitConvTo(ta->nextOf()))
|
||||
{
|
||||
ArrayLiteralExp * const ale = static_cast<ArrayLiteralExp *>(e2);
|
||||
initializeArrayLiteral(p, ale, se->e1->toElem(p)->getLVal());
|
||||
return e1->toElem(p);
|
||||
}
|
||||
}
|
||||
|
||||
DValue* l = e1->toElem(p);
|
||||
DValue* r = e2->toElem(p);
|
||||
|
||||
@@ -2850,9 +2870,9 @@ DValue* ArrayLiteralExp::toElem(IRState* p)
|
||||
Type* elemType = arrayType->nextOf()->toBasetype();
|
||||
|
||||
// is dynamic ?
|
||||
bool dyn = (arrayType->ty == Tarray);
|
||||
bool const dyn = (arrayType->ty == Tarray);
|
||||
// length
|
||||
size_t len = elements->dim;
|
||||
size_t const len = elements->dim;
|
||||
|
||||
// llvm target type
|
||||
LLType* llType = DtoType(arrayType);
|
||||
@@ -2872,95 +2892,34 @@ DValue* ArrayLiteralExp::toElem(IRState* p)
|
||||
return new DSliceValue(type, DtoConstSize_t(0), getNullPtr(getPtrToType(llElemType)));
|
||||
}
|
||||
|
||||
// dst pointer
|
||||
LLValue* dstMem;
|
||||
DSliceValue* dynSlice = NULL;
|
||||
if(dyn)
|
||||
if (dyn)
|
||||
{
|
||||
dynSlice = DtoNewDynArray(loc, arrayType, new DConstValue(Type::tsize_t, DtoConstSize_t(len)), false);
|
||||
dstMem = dynSlice->ptr;
|
||||
}
|
||||
else
|
||||
dstMem = DtoRawAlloca(llStoType, 0, "arrayliteral");
|
||||
|
||||
// Check for const'ness
|
||||
bool isAllConst = true;
|
||||
for (size_t i = 0; i < len && isAllConst; ++i)
|
||||
{
|
||||
Expression *expr = static_cast<Expression *>(elements->data[i]);
|
||||
isAllConst = expr->isConst() == 1;
|
||||
}
|
||||
|
||||
if (isAllConst)
|
||||
{
|
||||
// allocate room for initializers
|
||||
std::vector<LLConstant *> initvals(len, NULL);
|
||||
|
||||
// true if array elements differ in type
|
||||
bool mismatch = false;
|
||||
|
||||
// store elements
|
||||
for (size_t i = 0; i < len; ++i)
|
||||
if (arrayType->isImmutable() && isConstLiteral(this))
|
||||
{
|
||||
Expression *expr = static_cast<Expression *>(elements->data[i]);
|
||||
llvm::Constant *c = expr->toConstElem(gIR);
|
||||
if (llElemType != c->getType())
|
||||
mismatch = true;
|
||||
initvals[i] = c;
|
||||
llvm::Constant* init = arrayLiteralToConst(p, this);
|
||||
llvm::GlobalVariable* global = new llvm::GlobalVariable(
|
||||
*gIR->module,
|
||||
init->getType(),
|
||||
true,
|
||||
llvm::GlobalValue::InternalLinkage,
|
||||
init,
|
||||
".immutablearray"
|
||||
);
|
||||
return new DSliceValue(arrayType, DtoConstSize_t(elements->dim),
|
||||
DtoBitCast(global, getPtrToType(llElemType)));
|
||||
}
|
||||
LLConstant *constarr;
|
||||
if (mismatch)
|
||||
constarr = LLConstantStruct::getAnon(gIR->context(), initvals); // FIXME should this pack?;
|
||||
else
|
||||
constarr = LLConstantArray::get(LLArrayType::get(llElemType, len), initvals);
|
||||
|
||||
#if LDC_LLVM_VER == 301
|
||||
// Simply storing the constant array triggers a problem in LLVM 3.1.
|
||||
// With -O3 the statement
|
||||
// void[0] sa0 = (void[0]).init;
|
||||
// is compiled to
|
||||
// tail call void @llvm.trap()
|
||||
// which is not what we want!
|
||||
// Therefore a global variable is always used.
|
||||
LLGlobalVariable *gvar = new LLGlobalVariable(*gIR->module, constarr->getType(), true, LLGlobalValue::InternalLinkage, constarr, ".constarrayliteral_init");
|
||||
DtoMemCpy(dstMem, gvar, DtoConstSize_t(getTypePaddedSize(constarr->getType())));
|
||||
#else
|
||||
// If the type pointed to by dstMem is different from the array type
|
||||
// then we must assign the value to a global variable.
|
||||
if (constarr->getType() != dstMem->getType()->getPointerElementType())
|
||||
{
|
||||
LLGlobalVariable *gvar = new LLGlobalVariable(*gIR->module, constarr->getType(), true, LLGlobalValue::InternalLinkage, constarr, ".constarrayliteral_init");
|
||||
DtoMemCpy(dstMem, gvar, DtoConstSize_t(getTypePaddedSize(constarr->getType())));
|
||||
}
|
||||
else
|
||||
DtoStore(constarr, dstMem);
|
||||
#endif
|
||||
DSliceValue* dynSlice = DtoNewDynArray(loc, arrayType,
|
||||
new DConstValue(Type::tsize_t, DtoConstSize_t(len)), false);
|
||||
initializeArrayLiteral(p, this, DtoBitCast(dynSlice->ptr, getPtrToType(llStoType)));
|
||||
return dynSlice;
|
||||
}
|
||||
else
|
||||
{
|
||||
// store elements
|
||||
for (size_t i = 0; i < len; ++i)
|
||||
{
|
||||
Expression *expr = static_cast<Expression *>(elements->data[i]);
|
||||
LLValue *elemAddr;
|
||||
if(dyn)
|
||||
elemAddr = DtoGEPi1(dstMem, i, "tmp", p->scopebb());
|
||||
else
|
||||
elemAddr = DtoGEPi(dstMem, 0, i, "tmp", p->scopebb());
|
||||
|
||||
// emulate assignment
|
||||
DVarValue *vv = new DVarValue(expr->type, elemAddr);
|
||||
DValue *e = expr->toElem(p);
|
||||
DtoAssign(loc, vv, e);
|
||||
}
|
||||
llvm::Value* storage = DtoRawAlloca(llStoType, 0, "arrayliteral");
|
||||
initializeArrayLiteral(p, this, storage);
|
||||
return new DImValue(type, storage);
|
||||
}
|
||||
|
||||
// return storage directly ?
|
||||
if (!dyn)
|
||||
return new DImValue(type, dstMem);
|
||||
|
||||
// return slice
|
||||
return dynSlice;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -2980,45 +2939,7 @@ LLConstant* ArrayLiteralExp::toConstElem(IRState* p)
|
||||
// dynamic arrays can occur here as well ...
|
||||
bool dyn = (bt->ty != Tsarray);
|
||||
|
||||
// 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)
|
||||
{
|
||||
LLConstant *val = (*elements)[i]->toConstElem(p);
|
||||
if (!elementType)
|
||||
elementType = val->getType();
|
||||
else
|
||||
differentTypes |= (elementType != val->getType());
|
||||
vals.push_back(val);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
llvm::Constant* initval = arrayLiteralToConst(p, this);
|
||||
|
||||
// if static array, we're done
|
||||
if (!dyn)
|
||||
|
||||
Reference in New Issue
Block a user