diff --git a/gen/arrays.cpp b/gen/arrays.cpp index 74ac87e1..46a19fce 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,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 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) { diff --git a/gen/arrays.h b/gen/arrays.h index a78d146d..f72a912a 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,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); diff --git a/gen/toir.cpp b/gen/toir.cpp index 8c6c6362..fe664974 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); @@ -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); @@ -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(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) + if (arrayType->isImmutable() && isConstLiteral(this)) { - Expression *expr = static_cast(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(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 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)