diff --git a/dmd2/expression.h b/dmd2/expression.h index b0f040b1..c9650a10 100644 --- a/dmd2/expression.h +++ b/dmd2/expression.h @@ -236,9 +236,15 @@ public: virtual elem *toElem(IRState *irs); elem *toElemDtor(IRState *irs); #if IN_LLVM + /// Emits an LLVM constant corresponding to the expression. + /// + /// Due to the current implementation of AssocArrayLiteralExp::toElem,the + /// implementations have to be able to handle being called on expressions + /// that are not actually constant. In such a case, an LLVM undef of the + /// expected type should be returned (_not_ null). virtual llvm::Constant *toConstElem(IRState *irs); - virtual void cacheLvalue(IRState* irs); + virtual void cacheLvalue(IRState* irs); llvm::Value* cachedLvalue; virtual AssignExp* isAssignExp() { return NULL; } diff --git a/gen/structs.cpp b/gen/structs.cpp index 0f82c84a..55274b26 100644 --- a/gen/structs.cpp +++ b/gen/structs.cpp @@ -121,9 +121,6 @@ LLValue* DtoIndexStruct(LLValue* src, StructDeclaration* sd, VarDeclaration* vd) ////////////////////////////////////////////////////////////////////////////////////////// -// helper function that adds zero bytes to a vector of constants -extern size_t add_zeros(std::vector& values, size_t diff); - /// Return the type returned by DtoUnpaddedStruct called on a value of the /// specified type. /// Union types will get expanded into a struct, with a type for each member. diff --git a/gen/toir.cpp b/gen/toir.cpp index c75b5fd0..9f3b660f 100644 --- a/gen/toir.cpp +++ b/gen/toir.cpp @@ -652,8 +652,8 @@ LLConstant* AddExp::toConstElem(IRState* p) } error("expression '%s' is not a constant", toChars()); - fatal(); - return NULL; + if (!global.gag) fatal(); + return llvm::UndefValue::get(DtoType(type)); } /// Tries to remove a MulExp by a constant value of baseSize from e. Returns @@ -766,8 +766,8 @@ LLConstant* MinExp::toConstElem(IRState* p) } error("expression '%s' is not a constant", toChars()); - fatal(); - return NULL; + if (!global.gag) fatal(); + return llvm::UndefValue::get(DtoType(type)); } DValue* MinExp::toElem(IRState* p) @@ -1231,7 +1231,7 @@ Lerr: error("cannot cast %s to %s at compile time", e1->type->toChars(), type->toChars()); if (!global.gag) fatal(); - return NULL; + return llvm::UndefValue::get(DtoType(type)); } ////////////////////////////////////////////////////////////////////////////////////////// @@ -1290,11 +1290,7 @@ llvm::Constant* SymOffExp::toConstElem(IRState* p) IF_LOG Logger::println("SymOffExp::toConstElem: %s @ %s", toChars(), type->toChars()); LOG_SCOPE; - // We might get null here due to the hackish implementation of - // AssocArrayLiteralExp::toElem. llvm::Constant* base = DtoConstSymbolAddress(loc, var); - if (!base) return 0; - llvm::Constant* result; if (offset == 0) { @@ -1462,7 +1458,8 @@ LLConstant* AddrExp::toConstElem(IRState* p) else if (e1->op == TOKslice) { error("non-constant expression '%s'", toChars()); - fatal(); + if (!global.gag) fatal(); + return llvm::UndefValue::get(DtoType(type)); } // not yet supported else @@ -2872,7 +2869,8 @@ LLConstant* FuncExp::toConstElem(IRState* p) { assert(fd->tok == TOKdelegate || fd->tok == TOKreserved); error("delegate literals as constant expressions are not yet allowed"); - return 0; + if (!global.gag) fatal(); + return llvm::UndefValue::get(DtoType(type)); } // We need to actually codegen the function here, as literals are not added @@ -3302,6 +3300,36 @@ DValue* RemoveExp::toElem(IRState* p) ////////////////////////////////////////////////////////////////////////////////////////// +/// Constructs an array initializer constant with the given constants as its +/// elements. If the element types differ (unions, …), an anonymous struct +/// literal is emitted (as for array constant initializers). +static llvm::Constant* arrayConst(std::vector& vals, + Type* nominalElemType) +{ + if (vals.size() == 0) + { + llvm::ArrayType* type = llvm::ArrayType::get(DtoType(nominalElemType), 0); + return llvm::ConstantArray::get(type, vals); + } + + llvm::Type* elementType = NULL; + bool differentTypes = false; + for (std::vector::iterator i = vals.begin(), end = vals.end(); + i != end; ++i) + { + if (!elementType) + elementType = (*i)->getType(); + else + differentTypes |= (elementType != (*i)->getType()); + } + + if (differentTypes) + return llvm::ConstantStruct::getAnon(vals, true); + + llvm::ArrayType *t = llvm::ArrayType::get(elementType, vals.size()); + return llvm::ConstantArray::get(t, vals); +} + DValue* AssocArrayLiteralExp::toElem(IRState* p) { Logger::print("AssocArrayLiteralExp::toElem: %s @ %s\n", toChars(), type->toChars()); @@ -3356,16 +3384,16 @@ DValue* AssocArrayLiteralExp::toElem(IRState* p) LLConstant* idxs[2] = { DtoConstUint(0), DtoConstUint(0) }; - LLArrayType* arrtype = LLArrayType::get(DtoType(indexType), keys->dim); - LLConstant* initval = LLConstantArray::get(arrtype, keysInits); - LLConstant* globalstore = new LLGlobalVariable(*gIR->module, arrtype, false, LLGlobalValue::InternalLinkage, initval, ".aaKeysStorage"); + LLConstant* initval = arrayConst(keysInits, indexType); + LLConstant* globalstore = new LLGlobalVariable(*gIR->module, initval->getType(), + false, LLGlobalValue::InternalLinkage, initval, ".aaKeysStorage"); LLConstant* slice = llvm::ConstantExpr::getGetElementPtr(globalstore, idxs, true); slice = DtoConstSlice(DtoConstSize_t(keys->dim), slice); LLValue* keysArray = DtoAggrPaint(slice, funcTy->getParamType(1)); - arrtype = LLArrayType::get(DtoType(vtype), values->dim); - initval = LLConstantArray::get(arrtype, valuesInits); - globalstore = new LLGlobalVariable(*gIR->module, arrtype, false, LLGlobalValue::InternalLinkage, initval, ".aaValuesStorage"); + initval = arrayConst(valuesInits, vtype); + globalstore = new LLGlobalVariable(*gIR->module, initval->getType(), + false, LLGlobalValue::InternalLinkage, initval, ".aaValuesStorage"); slice = llvm::ConstantExpr::getGetElementPtr(globalstore, idxs, true); slice = DtoConstSlice(DtoConstSize_t(keys->dim), slice); LLValue* valuesArray = DtoAggrPaint(slice, funcTy->getParamType(2)); @@ -3527,5 +3555,10 @@ llvm::Constant* Expression::toConstElem(IRState * p) error("expression '%s' is not a constant", toChars()); if (!global.gag) fatal(); - return NULL; + + // Do not return null here, as AssocArrayLiteralExp::toElem determines + // whether it can allocate the needed arrays statically by just invoking + // toConstElem on its key/value expressions, and handling the null value + // consequently would require error-prone adaptions in all other code. + return llvm::UndefValue::get(DtoType(type)); } diff --git a/gen/tollvm.cpp b/gen/tollvm.cpp index 170f6e3e..07253bd1 100644 --- a/gen/tollvm.cpp +++ b/gen/tollvm.cpp @@ -382,6 +382,10 @@ LLGlobalValue::LinkageTypes DtoLinkage(Dsymbol* sym) llvm_unreachable("not global/function"); } + // The logic here should be sound in theory, but as long as the frontend + // keeps inserting templates into wrong modules, this yields to linking + // errors (see e.g. GitHub issue #558). +#if 0 // Check if sym is a nested function and we can declare it as internal. // // Nested naked functions and the implicitly generated __require/__ensure @@ -427,7 +431,7 @@ LLGlobalValue::LinkageTypes DtoLinkage(Dsymbol* sym) } } } - +#endif // default to external linkage return llvm::GlobalValue::ExternalLinkage; } diff --git a/ir/iraggr.cpp b/ir/iraggr.cpp index 904c96e3..4e9b6b34 100644 --- a/ir/iraggr.cpp +++ b/ir/iraggr.cpp @@ -87,34 +87,50 @@ llvm::Constant * IrAggr::getDefaultInit() ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// +static bool isAligned(llvm::Type* type, size_t offset) { + return gDataLayout->getABITypeAlignment(type) % offset == 0; +} + // helper function that adds zero bytes to a vector of constants -size_t add_zeros(llvm::SmallVectorImpl& constants, size_t diff) +size_t add_zeros(llvm::SmallVectorImpl& constants, + size_t startOffset, size_t endOffset) { - size_t n = constants.size(); - while (diff) + size_t const oldLength = constants.size(); + + llvm::Type* const eightByte = llvm::Type::getInt64Ty(gIR->context()); + llvm::Type* const fourByte = llvm::Type::getInt32Ty(gIR->context()); + llvm::Type* const twoByte = llvm::Type::getInt16Ty(gIR->context()); + + assert(startOffset <= endOffset); + size_t paddingLeft = endOffset - startOffset; + while (paddingLeft) { - if (global.params.is64bit && diff % 8 == 0) + if (global.params.is64bit && paddingLeft >= 8 && isAligned(eightByte, startOffset)) { - constants.push_back(LLConstant::getNullValue(llvm::Type::getInt64Ty(gIR->context()))); - diff -= 8; + constants.push_back(llvm::Constant::getNullValue(eightByte)); + startOffset += 8; } - else if (diff % 4 == 0) + else if (paddingLeft >= 4 && isAligned(fourByte, startOffset)) { - constants.push_back(LLConstant::getNullValue(llvm::Type::getInt32Ty(gIR->context()))); - diff -= 4; + constants.push_back(llvm::Constant::getNullValue(fourByte)); + startOffset += 4; } - else if (diff % 2 == 0) + else if (paddingLeft >= 2 && isAligned(twoByte, startOffset)) { - constants.push_back(LLConstant::getNullValue(llvm::Type::getInt16Ty(gIR->context()))); - diff -= 2; + constants.push_back(llvm::Constant::getNullValue(twoByte)); + startOffset += 2; } else { - constants.push_back(LLConstant::getNullValue(llvm::Type::getInt8Ty(gIR->context()))); - diff -= 1; + constants.push_back(llvm::Constant::getNullValue( + llvm::Type::getInt8Ty(gIR->context()))); + startOffset += 1; } + + paddingLeft = endOffset - startOffset; } - return constants.size() - n; + + return constants.size() - oldLength; } ////////////////////////////////////////////////////////////////////////////// @@ -201,9 +217,7 @@ llvm::Constant* IrAggr::createInitializerConstant( const size_t structsize = type->size(); if (offset < structsize) { - size_t diff = structsize - offset; - IF_LOG Logger::println("adding %zu bytes zero padding", diff); - add_zeros(constants, diff); + add_zeros(constants, offset, structsize); } // get initializer type @@ -324,9 +338,7 @@ void IrAggr::addFieldInitializers( // insert explicit padding? if (alignedoffset < vd->offset) { - size_t diff = vd->offset - alignedoffset; - IF_LOG Logger::println("adding %zu bytes zero padding", diff); - add_zeros(constants, diff); + add_zeros(constants, alignedoffset, vd->offset); } IF_LOG Logger::println("adding field %s", vd->toChars()); diff --git a/ir/irclass.cpp b/ir/irclass.cpp index ec027f1d..26aa1283 100644 --- a/ir/irclass.cpp +++ b/ir/irclass.cpp @@ -37,7 +37,6 @@ ////////////////////////////////////////////////////////////////////////////// extern LLConstant* get_default_initializer(VarDeclaration* vd, Initializer* init); -extern size_t add_zeros(std::vector& constants, size_t diff); extern LLConstant* DtoDefineClassInfo(ClassDeclaration* cd); diff --git a/ir/irfuncty.h b/ir/irfuncty.h index 8d6877f0..9a416ea0 100644 --- a/ir/irfuncty.h +++ b/ir/irfuncty.h @@ -131,7 +131,7 @@ struct IrFuncTy // Copy constructor and operator= seems to be required for MSC IrFuncTy(const IrFuncTy& rhs) - : funcType(ths.funcType), + : funcType(rhs.funcType), ret(rhs.ret), args(IrFuncTy::ArgList(rhs.args)), arg_sret(rhs.arg_sret), diff --git a/ir/irtypeclass.cpp b/ir/irtypeclass.cpp index f719cece..00cba824 100644 --- a/ir/irtypeclass.cpp +++ b/ir/irtypeclass.cpp @@ -30,7 +30,8 @@ ////////////////////////////////////////////////////////////////////////////// -extern size_t add_zeros(std::vector& defaultTypes, size_t diff); +extern size_t add_zeros(std::vector& defaultTypes, + size_t startOffset, size_t endOffset); extern bool var_offset_sort_cb(const VarDeclaration* v1, const VarDeclaration* v2); ////////////////////////////////////////////////////////////////////////////// @@ -167,7 +168,7 @@ void IrTypeClass::addBaseClassData( // insert explicit padding? if (alignedoffset < vd->offset) { - field_index += add_zeros(defaultTypes, vd->offset - alignedoffset); + field_index += add_zeros(defaultTypes, alignedoffset, vd->offset); } // add default type @@ -220,7 +221,7 @@ void IrTypeClass::addBaseClassData( // tail padding? if (offset < base->structsize) { - field_index += add_zeros(defaultTypes, base->structsize - offset); + field_index += add_zeros(defaultTypes, offset, base->structsize); offset = base->structsize; } #endif @@ -268,7 +269,7 @@ IrTypeClass* IrTypeClass::get(ClassDeclaration* cd) // tail padding? if (offset < cd->structsize) { - field_index += add_zeros(defaultTypes, cd->structsize - offset); + field_index += add_zeros(defaultTypes, offset, cd->structsize); offset = cd->structsize; } #endif diff --git a/ir/irtypestruct.cpp b/ir/irtypestruct.cpp index 44b805fa..291a1963 100644 --- a/ir/irtypestruct.cpp +++ b/ir/irtypestruct.cpp @@ -38,35 +38,51 @@ IrTypeStruct::IrTypeStruct(StructDeclaration * sd) ////////////////////////////////////////////////////////////////////////////// -size_t add_zeros(std::vector& defaultTypes, size_t diff) +static bool isAligned(llvm::Type* type, size_t offset) { + return gDataLayout->getABITypeAlignment(type) % offset == 0; +} + +size_t add_zeros(std::vector& defaultTypes, + size_t startOffset, size_t endOffset) { - size_t n = defaultTypes.size(); - while (diff) + size_t const oldLength = defaultTypes.size(); + + llvm::Type* const eightByte = llvm::Type::getInt64Ty(gIR->context()); + llvm::Type* const fourByte = llvm::Type::getInt32Ty(gIR->context()); + llvm::Type* const twoByte = llvm::Type::getInt16Ty(gIR->context()); + + assert(startOffset <= endOffset); + size_t paddingLeft = endOffset - startOffset; + while (paddingLeft) { - if (global.params.is64bit && diff % 8 == 0) + if (global.params.is64bit && paddingLeft >= 8 && isAligned(eightByte, startOffset)) { - defaultTypes.push_back(llvm::Type::getInt64Ty(gIR->context())); - diff -= 8; + defaultTypes.push_back(eightByte); + startOffset += 8; } - else if (diff % 4 == 0) + else if (paddingLeft >= 4 && isAligned(fourByte, startOffset)) { - defaultTypes.push_back(llvm::Type::getInt32Ty(gIR->context())); - diff -= 4; + defaultTypes.push_back(fourByte); + startOffset += 4; } - else if (diff % 2 == 0) + else if (paddingLeft >= 2 && isAligned(twoByte, startOffset)) { - defaultTypes.push_back(llvm::Type::getInt16Ty(gIR->context())); - diff -= 2; + defaultTypes.push_back(twoByte); + startOffset += 2; } else { defaultTypes.push_back(llvm::Type::getInt8Ty(gIR->context())); - diff -= 1; + startOffset += 1; } + + paddingLeft = endOffset - startOffset; } - return defaultTypes.size() - n; + + return defaultTypes.size() - oldLength; } + bool var_offset_sort_cb(const VarDeclaration* v1, const VarDeclaration* v2) { if (v1 && v2) @@ -209,7 +225,7 @@ IrTypeStruct* IrTypeStruct::get(StructDeclaration* sd) // insert explicit padding? if (alignedoffset < vd->offset) { - field_index += add_zeros(defaultTypes, vd->offset - alignedoffset); + field_index += add_zeros(defaultTypes, alignedoffset, vd->offset); } // add default type @@ -225,7 +241,7 @@ IrTypeStruct* IrTypeStruct::get(StructDeclaration* sd) // tail padding? if (offset < sd->structsize) { - add_zeros(defaultTypes, sd->structsize - offset); + add_zeros(defaultTypes, offset, sd->structsize); } // set struct body diff --git a/tests/d2/dmd-testsuite b/tests/d2/dmd-testsuite index 2834b349..fa4a570d 160000 --- a/tests/d2/dmd-testsuite +++ b/tests/d2/dmd-testsuite @@ -1 +1 @@ -Subproject commit 2834b34907483afc81f36e29e7963b490ba73813 +Subproject commit fa4a570db2c4257a0c0cec0a3ea96e82af244599