Skip to content

Commit 498b7cf

Browse files
committed
Merge pull request #493 from klickverbot/sarray-init
Avoid allocating array literals on initialization
2 parents d51d05c + 26e3cc8 commit 498b7cf

File tree

3 files changed

+162
-125
lines changed

3 files changed

+162
-125
lines changed

gen/arrays.cpp

+103
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "aggregate.h"
1212
#include "declaration.h"
1313
#include "dsymbol.h"
14+
#include "expression.h"
1415
#include "init.h"
1516
#include "module.h"
1617
#include "mtype.h"
@@ -387,6 +388,108 @@ LLConstant* DtoConstArrayInitializer(ArrayInitializer* arrinit)
387388
return DtoConstSlice(DtoConstSize_t(arrlen), gep, arrty);
388389
}
389390

391+
//////////////////////////////////////////////////////////////////////////////////////////
392+
393+
bool isConstLiteral(ArrayLiteralExp* ale)
394+
{
395+
for (size_t i = 0; i < ale->elements->dim; ++i)
396+
{
397+
// We have to check specifically for '1', as SymOffExp is classified as
398+
// '2' and the address of a local variable is not an LLVM constant.
399+
if ((*ale->elements)[i]->isConst() != 1)
400+
return false;
401+
}
402+
return true;
403+
}
404+
405+
//////////////////////////////////////////////////////////////////////////////////////////
406+
407+
llvm::Constant* arrayLiteralToConst(IRState* p, ArrayLiteralExp* ale)
408+
{
409+
assert(isConstLiteral(ale) && "Array literal cannot be represented as a constant.");
410+
411+
// Build the initializer. We have to take care as due to unions in the
412+
// element types (with different fields being initialized), we can end up
413+
// with different types for the initializer values. In this case, we
414+
// generate a packed struct constant instead of an array constant.
415+
LLType *elementType = NULL;
416+
bool differentTypes = false;
417+
418+
std::vector<LLConstant*> vals;
419+
vals.reserve(ale->elements->dim);
420+
for (unsigned i = 0; i < ale->elements->dim; ++i)
421+
{
422+
llvm::Constant *val = (*ale->elements)[i]->toConstElem(p);
423+
if (!elementType)
424+
elementType = val->getType();
425+
else
426+
differentTypes |= (elementType != val->getType());
427+
vals.push_back(val);
428+
}
429+
430+
if (differentTypes)
431+
return llvm::ConstantStruct::getAnon(vals, true);
432+
433+
if (!elementType)
434+
{
435+
assert(ale->elements->dim == 0);
436+
elementType = i1ToI8(voidToI8(DtoType(ale->type->toBasetype()->nextOf())));
437+
return llvm::ConstantArray::get(LLArrayType::get(elementType, 0), vals);
438+
}
439+
440+
llvm::ArrayType *t = llvm::ArrayType::get(elementType, ale->elements->dim);
441+
return llvm::ConstantArray::get(t, vals);
442+
}
443+
444+
//////////////////////////////////////////////////////////////////////////////////////////
445+
446+
void initializeArrayLiteral(IRState* p, ArrayLiteralExp* ale, LLValue* dstMem)
447+
{
448+
size_t elemCount = ale->elements->dim;
449+
450+
// Don't try to write nothing to a zero-element array, we might represent it
451+
// as a null pointer.
452+
if (elemCount == 0) return;
453+
454+
if (isConstLiteral(ale))
455+
{
456+
llvm::Constant* constarr = arrayLiteralToConst(p, ale);
457+
458+
// Emit a global for longer arrays, as an inline constant is always
459+
// lowered to a series of movs or similar at the asm level. The
460+
// optimizer can still decide to promote the memcpy intrinsic, so
461+
// the cutoff merely affects compilation speed.
462+
if (elemCount <= 4)
463+
{
464+
DtoStore(constarr, DtoBitCast(dstMem, getPtrToType(constarr->getType())));
465+
}
466+
else
467+
{
468+
llvm::GlobalVariable* gvar = new llvm::GlobalVariable(
469+
*gIR->module,
470+
constarr->getType(),
471+
true,
472+
LLGlobalValue::InternalLinkage,
473+
constarr,
474+
".arrayliteral"
475+
);
476+
DtoMemCpy(dstMem, gvar, DtoConstSize_t(getTypePaddedSize(constarr->getType())));
477+
}
478+
}
479+
else
480+
{
481+
// Store the elements one by one.
482+
for (size_t i = 0; i < elemCount; ++i)
483+
{
484+
DValue* e = (*ale->elements)[i]->toElem(p);
485+
486+
LLValue* elemAddr = DtoGEPi(dstMem, 0, i, "tmp", p->scopebb());
487+
DVarValue* vv = new DVarValue(e->type, elemAddr);
488+
DtoAssign(ale->loc, vv, e);
489+
}
490+
}
491+
}
492+
390493
//////////////////////////////////////////////////////////////////////////////////////////
391494
static LLValue* get_slice_ptr(DSliceValue* e, LLValue*& sz)
392495
{

gen/arrays.h

+13
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@
1818
#include "gen/llvm.h"
1919

2020
struct ArrayInitializer;
21+
struct ArrayLiteralExp;
2122
class DSliceValue;
2223
class DValue;
2324
struct Expression;
25+
struct IRState;
2426
struct Loc;
2527
struct Type;
2628

@@ -32,6 +34,17 @@ LLType* DtoConstArrayInitializerType(ArrayInitializer* arrinit);
3234
LLConstant* DtoConstArrayInitializer(ArrayInitializer* si);
3335
LLConstant* DtoConstSlice(LLConstant* dim, LLConstant* ptr, Type *type = 0);
3436

37+
/// Returns whether the array literal can be evaluated to a (LLVM) constant.
38+
bool isConstLiteral(ArrayLiteralExp* ale);
39+
40+
/// Returns the constant for the given array literal expression.
41+
llvm::Constant* arrayLiteralToConst(IRState* p, ArrayLiteralExp* ale);
42+
43+
/// Initializes a chunk of memory with the contents of an array literal.
44+
///
45+
/// dstMem is expected to be a pointer to the array allocation.
46+
void initializeArrayLiteral(IRState* p, ArrayLiteralExp* ale, LLValue* dstMem);
47+
3548
void DtoArrayCopySlices(DSliceValue* dst, DSliceValue* src);
3649
void DtoArrayCopyToSlice(DSliceValue* dst, DValue* src);
3750

gen/toir.cpp

+46-125
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,7 @@ DValue* AssignExp::toElem(IRState* p)
492492
{
493493
Logger::println("performing ref variable initialization");
494494
// Note that the variable value is accessed directly (instead
495-
// of via getLValue(), which would perform a load from the
495+
// of via getLVal(), which would perform a load from the
496496
// uninitialized location), and that rhs is stored as an l-value!
497497
DVarValue* lhs = e1->toElem(p)->isVar();
498498
assert(lhs);
@@ -508,6 +508,26 @@ DValue* AssignExp::toElem(IRState* p)
508508
}
509509
}
510510

511+
if (e1->op == TOKslice)
512+
{
513+
// Check if this is an initialization of a static array with an array
514+
// literal that the frontend has foolishly rewritten into an
515+
// assignment of a dynamic array literal to a slice.
516+
Logger::println("performing static array literal assignment");
517+
SliceExp * const se = static_cast<SliceExp *>(e1);
518+
Type * const t2 = e2->type->toBasetype();
519+
Type * const ta = se->e1->type->toBasetype();
520+
521+
if (se->lwr == NULL && ta->ty == Tsarray &&
522+
e2->op == TOKarrayliteral &&
523+
t2->nextOf()->mutableOf()->implicitConvTo(ta->nextOf()))
524+
{
525+
ArrayLiteralExp * const ale = static_cast<ArrayLiteralExp *>(e2);
526+
initializeArrayLiteral(p, ale, se->e1->toElem(p)->getLVal());
527+
return e1->toElem(p);
528+
}
529+
}
530+
511531
DValue* l = e1->toElem(p);
512532
DValue* r = e2->toElem(p);
513533

@@ -2850,9 +2870,9 @@ DValue* ArrayLiteralExp::toElem(IRState* p)
28502870
Type* elemType = arrayType->nextOf()->toBasetype();
28512871

28522872
// is dynamic ?
2853-
bool dyn = (arrayType->ty == Tarray);
2873+
bool const dyn = (arrayType->ty == Tarray);
28542874
// length
2855-
size_t len = elements->dim;
2875+
size_t const len = elements->dim;
28562876

28572877
// llvm target type
28582878
LLType* llType = DtoType(arrayType);
@@ -2872,95 +2892,34 @@ DValue* ArrayLiteralExp::toElem(IRState* p)
28722892
return new DSliceValue(type, DtoConstSize_t(0), getNullPtr(getPtrToType(llElemType)));
28732893
}
28742894

2875-
// dst pointer
2876-
LLValue* dstMem;
2877-
DSliceValue* dynSlice = NULL;
2878-
if(dyn)
2879-
{
2880-
dynSlice = DtoNewDynArray(loc, arrayType, new DConstValue(Type::tsize_t, DtoConstSize_t(len)), false);
2881-
dstMem = dynSlice->ptr;
2882-
}
2883-
else
2884-
dstMem = DtoRawAlloca(llStoType, 0, "arrayliteral");
2885-
2886-
// Check for const'ness
2887-
bool isAllConst = true;
2888-
for (size_t i = 0; i < len && isAllConst; ++i)
2895+
if (dyn)
28892896
{
2890-
Expression *expr = static_cast<Expression *>(elements->data[i]);
2891-
isAllConst = expr->isConst() == 1;
2892-
}
2893-
2894-
if (isAllConst)
2895-
{
2896-
// allocate room for initializers
2897-
std::vector<LLConstant *> initvals(len, NULL);
2898-
2899-
// true if array elements differ in type
2900-
bool mismatch = false;
2901-
2902-
// store elements
2903-
for (size_t i = 0; i < len; ++i)
2897+
if (arrayType->isImmutable() && isConstLiteral(this))
29042898
{
2905-
Expression *expr = static_cast<Expression *>(elements->data[i]);
2906-
llvm::Constant *c = expr->toConstElem(gIR);
2907-
if (llElemType != c->getType())
2908-
mismatch = true;
2909-
initvals[i] = c;
2910-
}
2911-
LLConstant *constarr;
2912-
if (mismatch)
2913-
constarr = LLConstantStruct::getAnon(gIR->context(), initvals); // FIXME should this pack?;
2914-
else
2915-
constarr = LLConstantArray::get(LLArrayType::get(llElemType, len), initvals);
2916-
2917-
#if LDC_LLVM_VER == 301
2918-
// Simply storing the constant array triggers a problem in LLVM 3.1.
2919-
// With -O3 the statement
2920-
// void[0] sa0 = (void[0]).init;
2921-
// is compiled to
2922-
// tail call void @llvm.trap()
2923-
// which is not what we want!
2924-
// Therefore a global variable is always used.
2925-
LLGlobalVariable *gvar = new LLGlobalVariable(*gIR->module, constarr->getType(), true, LLGlobalValue::InternalLinkage, constarr, ".constarrayliteral_init");
2926-
DtoMemCpy(dstMem, gvar, DtoConstSize_t(getTypePaddedSize(constarr->getType())));
2927-
#else
2928-
// If the type pointed to by dstMem is different from the array type
2929-
// then we must assign the value to a global variable.
2930-
if (constarr->getType() != dstMem->getType()->getPointerElementType())
2931-
{
2932-
LLGlobalVariable *gvar = new LLGlobalVariable(*gIR->module, constarr->getType(), true, LLGlobalValue::InternalLinkage, constarr, ".constarrayliteral_init");
2933-
DtoMemCpy(dstMem, gvar, DtoConstSize_t(getTypePaddedSize(constarr->getType())));
2934-
}
2935-
else
2936-
DtoStore(constarr, dstMem);
2937-
#endif
2899+
llvm::Constant* init = arrayLiteralToConst(p, this);
2900+
llvm::GlobalVariable* global = new llvm::GlobalVariable(
2901+
*gIR->module,
2902+
init->getType(),
2903+
true,
2904+
llvm::GlobalValue::InternalLinkage,
2905+
init,
2906+
".immutablearray"
2907+
);
2908+
return new DSliceValue(arrayType, DtoConstSize_t(elements->dim),
2909+
DtoBitCast(global, getPtrToType(llElemType)));
2910+
}
2911+
2912+
DSliceValue* dynSlice = DtoNewDynArray(loc, arrayType,
2913+
new DConstValue(Type::tsize_t, DtoConstSize_t(len)), false);
2914+
initializeArrayLiteral(p, this, DtoBitCast(dynSlice->ptr, getPtrToType(llStoType)));
2915+
return dynSlice;
29382916
}
29392917
else
29402918
{
2941-
// store elements
2942-
for (size_t i = 0; i < len; ++i)
2943-
{
2944-
Expression *expr = static_cast<Expression *>(elements->data[i]);
2945-
LLValue *elemAddr;
2946-
if(dyn)
2947-
elemAddr = DtoGEPi1(dstMem, i, "tmp", p->scopebb());
2948-
else
2949-
elemAddr = DtoGEPi(dstMem, 0, i, "tmp", p->scopebb());
2950-
2951-
// emulate assignment
2952-
DVarValue *vv = new DVarValue(expr->type, elemAddr);
2953-
DValue *e = expr->toElem(p);
2954-
DtoAssign(loc, vv, e);
2955-
}
2919+
llvm::Value* storage = DtoRawAlloca(llStoType, 0, "arrayliteral");
2920+
initializeArrayLiteral(p, this, storage);
2921+
return new DImValue(type, storage);
29562922
}
2957-
2958-
// return storage directly ?
2959-
if (!dyn)
2960-
return new DImValue(type, dstMem);
2961-
2962-
// return slice
2963-
return dynSlice;
29642923
}
29652924

29662925
//////////////////////////////////////////////////////////////////////////////////////////
@@ -2980,45 +2939,7 @@ LLConstant* ArrayLiteralExp::toConstElem(IRState* p)
29802939
// dynamic arrays can occur here as well ...
29812940
bool dyn = (bt->ty != Tsarray);
29822941

2983-
// Build the initializer. We have to take care as due to unions in the
2984-
// element types (with different fields being initialized), we can end up
2985-
// with different types for the initializer values. In this case, we
2986-
// generate a packed struct constant instead of an array constant.
2987-
LLType *elementType = NULL;
2988-
bool differentTypes = false;
2989-
2990-
std::vector<LLConstant*> vals;
2991-
vals.reserve(elements->dim);
2992-
for (unsigned i = 0; i < elements->dim; ++i)
2993-
{
2994-
LLConstant *val = (*elements)[i]->toConstElem(p);
2995-
if (!elementType)
2996-
elementType = val->getType();
2997-
else
2998-
differentTypes |= (elementType != val->getType());
2999-
vals.push_back(val);
3000-
}
3001-
3002-
LLConstant *initval;
3003-
if (differentTypes)
3004-
{
3005-
std::vector<llvm::Type*> types;
3006-
types.reserve(elements->dim);
3007-
for (unsigned i = 0; i < elements->dim; ++i)
3008-
types.push_back(vals[i]->getType());
3009-
LLStructType *t = llvm::StructType::get(gIR->context(), types, true);
3010-
initval = LLConstantStruct::get(t, vals);
3011-
}
3012-
else if (!elementType)
3013-
{
3014-
assert(elements->dim == 0);
3015-
initval = LLConstantArray::get(arrtype, vals);
3016-
}
3017-
else
3018-
{
3019-
LLArrayType *t = LLArrayType::get(elementType, elements->dim);
3020-
initval = LLConstantArray::get(t, vals);
3021-
}
2942+
llvm::Constant* initval = arrayLiteralToConst(p, this);
30222943

30232944
// if static array, we're done
30242945
if (!dyn)

0 commit comments

Comments
 (0)