From 44f60199ad5b9478c1f932d08cb9016573532c80 Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Wed, 9 Oct 2013 01:04:23 +0200 Subject: [PATCH 1/6] Factored actual literal building code out of ArrayLiteralExp::toElem. --- gen/arrays.cpp | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++ gen/arrays.h | 7 ++++ gen/toir.cpp | 91 +++++------------------------------------------- 3 files changed, 108 insertions(+), 83 deletions(-) diff --git a/gen/arrays.cpp b/gen/arrays.cpp index 74ac87e10f9..6f1994796b8 100644 --- a/gen/arrays.cpp +++ b/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,98 @@ LLConstant* DtoConstArrayInitializer(ArrayInitializer* arrinit) return DtoConstSlice(DtoConstSize_t(arrlen), gep, arrty); } +////////////////////////////////////////////////////////////////////////////////////////// + +void initializeArrayLiteral(IRState* p, ArrayLiteralExp* ale, LLValue* dstMem) +{ + Type* const arrayType = ale->type->toBasetype(); + Type* const elemType = arrayType->nextOf()->toBasetype(); + size_t const len = ale->elements->dim; + + // Check for const'ness + bool isAllConst = true; + for (size_t i = 0; i < len && isAllConst; ++i) + { + Expression *expr = static_cast(ale->elements->data[i]); + isAllConst = expr->isConst() == 1; + } + + if (isAllConst) + { + // allocate room for initializers + std::vector initvals(len, NULL); + + LLType* llElemType = voidToI8(DtoType(elemType)); + + // true if array elements differ in type + bool mismatch = false; + + // store elements + for (size_t i = 0; i < len; ++i) + { + Expression *expr = static_cast(ale->elements->data[i]); + llvm::Constant *c = expr->toConstElem(gIR); + if (llElemType != c->getType()) + mismatch = true; + initvals[i] = c; + } + 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 + } + else + { + // store elements + for (size_t i = 0; i < len; ++i) + { + LLValue *elemAddr = DtoGEPi(dstMem, 0, i, "tmp", p->scopebb()); + + // emulate assignment + DVarValue *vv = new DVarValue((*ale->elements)[i]->type, elemAddr); + DValue *e = (*ale->elements)[i]->toElem(p); + DtoAssign(ale->loc, vv, e); + } + } +} + ////////////////////////////////////////////////////////////////////////////////////////// static LLValue* get_slice_ptr(DSliceValue* e, LLValue*& sz) { diff --git a/gen/arrays.h b/gen/arrays.h index a78d146d590..957a03e09b8 100644 --- a/gen/arrays.h +++ b/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,11 @@ LLType* DtoConstArrayInitializerType(ArrayInitializer* arrinit); LLConstant* DtoConstArrayInitializer(ArrayInitializer* si); LLConstant* DtoConstSlice(LLConstant* dim, LLConstant* ptr, Type *type = 0); +/// 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); diff --git a/gen/toir.cpp b/gen/toir.cpp index 8c6c6362a7b..cead06b4951 100644 --- a/gen/toir.cpp +++ b/gen/toir.cpp @@ -2873,94 +2873,19 @@ DValue* ArrayLiteralExp::toElem(IRState* p) } // 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; + 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 - 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(elements->data[i]); - isAllConst = expr->isConst() == 1; - } - - if (isAllConst) { - // allocate room for initializers - std::vector initvals(len, NULL); - - // true if array elements differ in type - bool mismatch = false; - - // store elements - for (size_t i = 0; i < len; ++i) - { - Expression *expr = static_cast(elements->data[i]); - llvm::Constant *c = expr->toConstElem(gIR); - if (llElemType != c->getType()) - mismatch = true; - initvals[i] = c; - } - 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 + llvm::Value* storage = DtoRawAlloca(llStoType, 0, "arrayliteral"); + initializeArrayLiteral(p, this, storage); + return new DImValue(type, storage); } - else - { - // store elements - for (size_t i = 0; i < len; ++i) - { - Expression *expr = static_cast(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); - } - } - - // return storage directly ? - if (!dyn) - return new DImValue(type, dstMem); - - // return slice - return dynSlice; } ////////////////////////////////////////////////////////////////////////////////////////// From 9866e5a14f970f6359af3a6c780d0bf204081501 Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Wed, 9 Oct 2013 01:05:26 +0200 Subject: [PATCH 2/6] Comment typo fix. --- gen/toir.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gen/toir.cpp b/gen/toir.cpp index cead06b4951..f229d3145fe 100644 --- a/gen/toir.cpp +++ b/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); From eb3261f93c4fa029dc4a8a17ab15c21d45ee9599 Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Wed, 9 Oct 2013 01:05:46 +0200 Subject: [PATCH 3/6] Do not GC-allocate temporary for static array initialization. --- gen/toir.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/gen/toir.cpp b/gen/toir.cpp index f229d3145fe..58b3c792f4d 100644 --- a/gen/toir.cpp +++ b/gen/toir.cpp @@ -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(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(e2); + initializeArrayLiteral(p, ale, se->e1->toElem(p)->getLVal()); + return e1->toElem(p); + } + } + DValue* l = e1->toElem(p); DValue* r = e2->toElem(p); From 60cdf581680554d28fdfe3cef90af3489d7e12ad Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Wed, 9 Oct 2013 02:32:15 +0200 Subject: [PATCH 4/6] Combine ArrayLiteralExp::toConstElem and initializeArrayLiteral implementations. --- gen/arrays.cpp | 85 ++++++++++++++++++++++++++++++++------------------ gen/arrays.h | 6 ++++ gen/toir.cpp | 40 +----------------------- 3 files changed, 62 insertions(+), 69 deletions(-) diff --git a/gen/arrays.cpp b/gen/arrays.cpp index 6f1994796b8..fdc8eebb2c8 100644 --- a/gen/arrays.cpp +++ b/gen/arrays.cpp @@ -390,44 +390,69 @@ LLConstant* DtoConstArrayInitializer(ArrayInitializer* arrinit) ////////////////////////////////////////////////////////////////////////////////////////// -void initializeArrayLiteral(IRState* p, ArrayLiteralExp* ale, LLValue* dstMem) +bool isConstLiteral(ArrayLiteralExp* ale) { - Type* const arrayType = ale->type->toBasetype(); - Type* const elemType = arrayType->nextOf()->toBasetype(); - size_t const len = ale->elements->dim; + 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; +} - // Check for const'ness - bool isAllConst = true; - for (size_t i = 0; i < len && isAllConst; ++i) +////////////////////////////////////////////////////////////////////////////////////////// + +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 vals; + vals.reserve(ale->elements->dim); + for (unsigned i = 0; i < ale->elements->dim; ++i) { - Expression *expr = static_cast(ale->elements->data[i]); - isAllConst = expr->isConst() == 1; + llvm::Constant *val = (*ale->elements)[i]->toConstElem(p); + if (!elementType) + elementType = val->getType(); + else + differentTypes |= (elementType != val->getType()); + vals.push_back(val); } - if (isAllConst) + if (differentTypes) + return llvm::ConstantStruct::getAnon(vals, true); + + if (!elementType) { - // allocate room for initializers - std::vector initvals(len, NULL); + assert(ale->elements->dim == 0); + elementType = i1ToI8(voidToI8(DtoType(ale->type->toBasetype()->nextOf()))); + return llvm::ConstantArray::get(LLArrayType::get(elementType, 0), vals); + } - LLType* llElemType = voidToI8(DtoType(elemType)); + llvm::ArrayType *t = llvm::ArrayType::get(elementType, ale->elements->dim); + return llvm::ConstantArray::get(t, vals); +} - // true if array elements differ in type - bool mismatch = false; +////////////////////////////////////////////////////////////////////////////////////////// - // store elements - for (size_t i = 0; i < len; ++i) - { - Expression *expr = static_cast(ale->elements->data[i]); - llvm::Constant *c = expr->toConstElem(gIR); - if (llElemType != c->getType()) - mismatch = true; - initvals[i] = c; - } - LLConstant *constarr; - if (mismatch) - constarr = LLConstantStruct::getAnon(gIR->context(), initvals); // FIXME should this pack?; - else - constarr = LLConstantArray::get(LLArrayType::get(llElemType, len), initvals); +void initializeArrayLiteral(IRState* p, ArrayLiteralExp* ale, LLValue* dstMem) +{ + // Don't try to write nothing to a zero-element array, we might represent it + // as a null pointer. + if (ale->elements->dim == 0) return; + + if (isConstLiteral(ale)) + { + // allocate room for initializers + llvm::Constant* constarr = arrayLiteralToConst(p, ale); #if LDC_LLVM_VER == 301 // Simply storing the constant array triggers a problem in LLVM 3.1. @@ -468,7 +493,7 @@ void initializeArrayLiteral(IRState* p, ArrayLiteralExp* ale, LLValue* dstMem) else { // store elements - for (size_t i = 0; i < len; ++i) + for (size_t i = 0; i < ale->elements->dim; ++i) { LLValue *elemAddr = DtoGEPi(dstMem, 0, i, "tmp", p->scopebb()); diff --git a/gen/arrays.h b/gen/arrays.h index 957a03e09b8..f72a912abf8 100644 --- a/gen/arrays.h +++ b/gen/arrays.h @@ -34,6 +34,12 @@ 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. diff --git a/gen/toir.cpp b/gen/toir.cpp index 58b3c792f4d..0d2a9901377 100644 --- a/gen/toir.cpp +++ b/gen/toir.cpp @@ -2925,45 +2925,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 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 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) From 0cdb74aae71c60104188ddd831fae5e7923d74ef Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Wed, 9 Oct 2013 03:00:07 +0200 Subject: [PATCH 5/6] Do not heap-allocate immutable array literals. --- gen/toir.cpp | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/gen/toir.cpp b/gen/toir.cpp index 0d2a9901377..fe664974a37 100644 --- a/gen/toir.cpp +++ b/gen/toir.cpp @@ -2870,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); @@ -2892,9 +2892,23 @@ DValue* ArrayLiteralExp::toElem(IRState* p) return new DSliceValue(type, DtoConstSize_t(0), getNullPtr(getPtrToType(llElemType))); } - // dst pointer if (dyn) { + if (arrayType->isImmutable() && isConstLiteral(this)) + { + 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))); + } + DSliceValue* dynSlice = DtoNewDynArray(loc, arrayType, new DConstValue(Type::tsize_t, DtoConstSize_t(len)), false); initializeArrayLiteral(p, this, DtoBitCast(dynSlice->ptr, getPtrToType(llStoType))); From 26e3cc8a4078fb90b35d60b336dfc4c24fe18f2b Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Wed, 9 Oct 2013 04:23:51 +0200 Subject: [PATCH 6/6] Never emit long array literals as inline constant. Also cleaned up rest of the code a bit. The LLVM 3.1 problem was already fixed before by not doing anything on zero-element arrays anyway. --- gen/arrays.cpp | 53 ++++++++++++++++++-------------------------------- 1 file changed, 19 insertions(+), 34 deletions(-) diff --git a/gen/arrays.cpp b/gen/arrays.cpp index fdc8eebb2c8..46a19fce3c1 100644 --- a/gen/arrays.cpp +++ b/gen/arrays.cpp @@ -445,61 +445,46 @@ llvm::Constant* arrayLiteralToConst(IRState* p, ArrayLiteralExp* ale) 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 (ale->elements->dim == 0) return; + if (elemCount == 0) return; if (isConstLiteral(ale)) { - // allocate room for initializers llvm::Constant* constarr = arrayLiteralToConst(p, ale); -#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()) + // 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 { - LLGlobalVariable *gvar = new LLGlobalVariable( + llvm::GlobalVariable* gvar = new llvm::GlobalVariable( *gIR->module, constarr->getType(), true, LLGlobalValue::InternalLinkage, constarr, - ".constarrayliteral_init" + ".arrayliteral" ); DtoMemCpy(dstMem, gvar, DtoConstSize_t(getTypePaddedSize(constarr->getType()))); } - else - DtoStore(constarr, dstMem); -#endif } else { - // store elements - for (size_t i = 0; i < ale->elements->dim; ++i) + // Store the elements one by one. + for (size_t i = 0; i < elemCount; ++i) { - LLValue *elemAddr = DtoGEPi(dstMem, 0, i, "tmp", p->scopebb()); + DValue* e = (*ale->elements)[i]->toElem(p); - // emulate assignment - DVarValue *vv = new DVarValue((*ale->elements)[i]->type, elemAddr); - 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); } }