Use the regular LL type for all init symbols and most globals

There's no <Type>_init type for aggregates (structs and classes) anymore,
effectively eliminating a *lot* of named LLVM types, some bitcasts as well
as replacements of globals etc.

To get there, it was even required to use the regular type for compatible
literals too, otherwise structs embedded as fields in other aggregates had
an anonymous type (well, the LLVM constant for the field initializer had)
and so the container initializer wasn't compatible with the regular type
anymore.

What was also necessary was a fix wrt. static arrays of bools (LLVM
constant of type `[N x i1]` vs. `[N x i8]` for regular type).
I also had to change the initializer for `char[2][3] x = 0xff` from
`[6 x i8]` to `[3 x [2 x i8]]`, i.e., NOT flattening multi-dimensional
inits from a scalar.

So only literals with overlapping (union) fields and an explicit
initializer initializing dominated non-alias union fields should still
have a mismatching anonymous type - i.e., very, very few cases.
This commit is contained in:
Martin 2016-10-22 20:00:15 +02:00
parent e6537ba4dc
commit ab1432ed06
8 changed files with 164 additions and 71 deletions

View file

@ -544,6 +544,9 @@ llvm::Constant *arrayLiteralToConst(IRState *p, ArrayLiteralExp *ale) {
vals.reserve(ale->elements->dim); vals.reserve(ale->elements->dim);
for (unsigned i = 0; i < ale->elements->dim; ++i) { for (unsigned i = 0; i < ale->elements->dim; ++i) {
llvm::Constant *val = toConstElem(indexArrayLiteral(ale, i), p); llvm::Constant *val = toConstElem(indexArrayLiteral(ale, i), p);
// extend i1 to i8
if (val->getType() == LLType::getInt1Ty(p->context()))
val = llvm::ConstantExpr::getZExt(val, LLType::getInt8Ty(p->context()));
if (!elementType) { if (!elementType) {
elementType = val->getType(); elementType = val->getType();
} else { } else {

View file

@ -1099,48 +1099,73 @@ LLConstant *DtoConstExpInit(Loc &loc, Type *targetType, Expression *exp) {
LOG_SCOPE LOG_SCOPE
LLConstant *val = toConstElem(exp, gIR); LLConstant *val = toConstElem(exp, gIR);
Type *baseValType = exp->type->toBasetype();
Type *baseTargetType = targetType->toBasetype();
// The situation here is a bit tricky: In an ideal world, we would always // The situation here is a bit tricky: In an ideal world, we would always
// have val->getType() == DtoType(targetType). But there are two reasons // have val->getType() == DtoType(targetType). But there are two reasons
// why this is not true. One is that the LLVM type system cannot represent // why this is not true. One is that the LLVM type system cannot represent
// all the C types, leading to differences in types being necessary e.g. for // all the C types, leading to differences in types being necessary e.g. for
// union initializers. The second is that the frontend actually does not // union initializers. The second is that the frontend actually does not
// explicitly lowers things like initializing an array/vector with a scalar // explicitly lower things like initializing an array/vector with a scalar
// constant, or since 2.061 sometimes does not get implicit conversions for // constant, or since 2.061 sometimes does not get implicit conversions for
// integers right. However, we cannot just rely on the actual Types being // integers right. However, we cannot just rely on the actual Types being
// equal if there are no rewrites to do because of as usual AST // equal if there are no rewrites to do because of as usual AST
// inconsistency bugs. // inconsistency bugs.
Type *expBase = stripModifiers(exp->type->toBasetype())->merge(); LLType *llType = val->getType();
Type *targetBase = stripModifiers(targetType->toBasetype())->merge(); LLType *targetLLType = DtoMemType(baseTargetType);
if (expBase->equals(targetBase) && targetBase->ty != Tbool) { // shortcut for zeros
if (val->isNullValue())
return llvm::Constant::getNullValue(targetLLType);
if (llType == targetLLType)
return val;
// extend i1 to i8
if (llType == LLType::getInt1Ty(gIR->context())) {
llType = LLType::getInt8Ty(gIR->context());
val = llvm::ConstantExpr::getZExt(val, llType);
if (llType == targetLLType)
return val; return val;
} }
llvm::Type *llType = val->getType(); if (baseTargetType->ty == Tsarray) {
llvm::Type *targetLLType = DtoMemType(targetBase); Logger::println("Building constant array initializer from scalar.");
if (llType == targetLLType) {
Logger::println("Matching LLVM types, ignoring frontend glitch."); assert(baseValType->size() > 0);
const auto numTotalVals = baseTargetType->size() / baseValType->size();
assert(baseTargetType->size() % baseValType->size() == 0);
// may be a multi-dimensional array init, e.g., `char[2][3] x = 0xff`
baseValType = stripModifiers(baseValType);
LLSmallVector<size_t, 4> dims; // { 3, 2 }
for (auto t = baseTargetType; t->ty == Tsarray;) {
dims.push_back(static_cast<TypeSArray *>(t)->dim->toUInteger());
auto elementType = stripModifiers(t->nextOf()->toBasetype());
if (elementType->equals(baseValType))
break;
t = elementType;
}
size_t product = 1;
for (size_t i = dims.size(); i--;) {
product *= dims[i];
auto at = llvm::ArrayType::get(val->getType(), dims[i]);
LLSmallVector<llvm::Constant *, 16> elements(dims[i], val);
val = llvm::ConstantArray::get(at, elements);
}
assert(product == numTotalVals);
return val; return val;
} }
if (targetBase->ty == Tsarray) { if (baseTargetType->ty == Tvector) {
Logger::println("Building constant array initializer to single value."); Logger::println("Building constant vector initializer from scalar.");
assert(expBase->size() > 0); TypeVector *tv = static_cast<TypeVector *>(baseTargetType);
d_uns64 elemCount = targetBase->size() / expBase->size();
assert(targetBase->size() % expBase->size() == 0);
std::vector<llvm::Constant *> initVals(elemCount, val);
return llvm::ConstantArray::get(llvm::ArrayType::get(llType, elemCount),
initVals);
}
if (targetBase->ty == Tvector) {
Logger::println("Building vector initializer from scalar.");
TypeVector *tv = static_cast<TypeVector *>(targetBase);
assert(tv->basetype->ty == Tsarray); assert(tv->basetype->ty == Tsarray);
dinteger_t elemCount = dinteger_t elemCount =
static_cast<TypeSArray *>(tv->basetype)->dim->toInteger(); static_cast<TypeSArray *>(tv->basetype)->dim->toInteger();
@ -1154,10 +1179,9 @@ LLConstant *DtoConstExpInit(Loc &loc, Type *targetType, Expression *exp) {
llvm::IntegerType *source = llvm::cast<llvm::IntegerType>(llType); llvm::IntegerType *source = llvm::cast<llvm::IntegerType>(llType);
llvm::IntegerType *target = llvm::cast<llvm::IntegerType>(targetLLType); llvm::IntegerType *target = llvm::cast<llvm::IntegerType>(targetLLType);
assert( assert(target->getBitWidth() > source->getBitWidth() &&
target->getBitWidth() > source->getBitWidth() && "On initializer integer type mismatch, the target should be wider "
"On initializer " "than the source.");
"integer type mismatch, the target should be wider than the source.");
return llvm::ConstantExpr::getZExtOrBitCast(val, target); return llvm::ConstantExpr::getZExtOrBitCast(val, target);
} }

View file

@ -26,11 +26,18 @@
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
IrAggr::IrAggr(AggregateDeclaration *aggr) llvm::StructType *IrAggr::getLLStructType() {
: aggrdecl(aggr), type(aggr->type), if (llStructType)
// above still need to be looked at return llStructType;
init_type(LLStructType::create(
gIR->context(), std::string(aggr->toPrettyChars()) + "_init")) {} LLType *llType = DtoType(type);
if (auto irClassType = type->ctype->isClass())
llType = irClassType->getMemoryLLType();
llStructType = llvm::dyn_cast<LLStructType>(llType);
return llStructType;
}
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
@ -43,7 +50,7 @@ LLGlobalVariable *IrAggr::getInitSymbol() {
auto initname = getMangledInitSymbolName(aggrdecl); auto initname = getMangledInitSymbolName(aggrdecl);
init = init =
getOrCreateGlobal(aggrdecl->loc, gIR->module, init_type, true, getOrCreateGlobal(aggrdecl->loc, gIR->module, getLLStructType(), true,
llvm::GlobalValue::ExternalLinkage, nullptr, initname); llvm::GlobalValue::ExternalLinkage, nullptr, initname);
// set alignment // set alignment
@ -63,9 +70,8 @@ llvm::Constant *IrAggr::getDefaultInit() {
aggrdecl->toPrettyChars()); aggrdecl->toPrettyChars());
LOG_SCOPE; LOG_SCOPE;
DtoType(type);
VarInitMap noExplicitInitializers; VarInitMap noExplicitInitializers;
constInit = createInitializerConstant(noExplicitInitializers, init_type); constInit = createInitializerConstant(noExplicitInitializers);
return constInit; return constInit;
} }
@ -134,8 +140,7 @@ static llvm::Constant *FillSArrayDims(Type *arrTypeD, llvm::Constant *init) {
} }
llvm::Constant * llvm::Constant *
IrAggr::createInitializerConstant(const VarInitMap &explicitInitializers, IrAggr::createInitializerConstant(const VarInitMap &explicitInitializers) {
llvm::StructType *initializerType) {
IF_LOG Logger::println("Creating initializer constant for %s", IF_LOG Logger::println("Creating initializer constant for %s",
aggrdecl->toChars()); aggrdecl->toChars());
LOG_SCOPE; LOG_SCOPE;
@ -167,23 +172,30 @@ IrAggr::createInitializerConstant(const VarInitMap &explicitInitializers,
if (offset < structsize) if (offset < structsize)
add_zeros(constants, offset, structsize); add_zeros(constants, offset, structsize);
// get initializer type assert(!constants.empty());
if (!initializerType || initializerType->isOpaque()) {
// get LL field types
llvm::SmallVector<llvm::Type *, 16> types; llvm::SmallVector<llvm::Type *, 16> types;
types.reserve(constants.size()); types.reserve(constants.size());
for (auto c : constants) { for (auto c : constants)
types.push_back(c->getType()); types.push_back(c->getType());
auto llStructType = getLLStructType();
bool isCompatible = (types.size() == llStructType->getNumElements());
if (isCompatible) {
for (size_t i = 0; i < types.size(); i++) {
if (types[i] != llStructType->getElementType(i)) {
isCompatible = false;
break;
} }
if (!initializerType) {
initializerType = LLStructType::get(gIR->context(), types, isPacked());
} else {
initializerType->setBody(types, isPacked());
} }
} }
// build constant // build constant
assert(!constants.empty()); LLStructType *llType =
llvm::Constant *c = LLConstantStruct::get(initializerType, constants); isCompatible ? llStructType
: LLStructType::get(gIR->context(), types, isPacked());
llvm::Constant *c = LLConstantStruct::get(llType, constants);
IF_LOG Logger::cout() << "final initializer: " << *c << std::endl; IF_LOG Logger::cout() << "final initializer: " << *c << std::endl;
return c; return c;
} }

View file

@ -33,7 +33,8 @@ class StructType;
// it is used during codegen to hold all the vital info we need // it is used during codegen to hold all the vital info we need
struct IrAggr { struct IrAggr {
/// Constructor. /// Constructor.
explicit IrAggr(AggregateDeclaration *agg); explicit IrAggr(AggregateDeclaration *aggr)
: aggrdecl(aggr), type(aggr->type) {}
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// public fields, // public fields,
@ -78,24 +79,16 @@ struct IrAggr {
/// fields set to the provided constants. The remaining space (not /// fields set to the provided constants. The remaining space (not
/// explicitly specified fields, padding) is default-initialized. /// explicitly specified fields, padding) is default-initialized.
/// ///
/// The optional initializerType parmeter can be used to specify the exact
/// LLVM type to use for the initializer. If non-null and non-opaque, the
/// type must exactly match the generated constant. This parameter is used
/// mainly for supporting legacy code.
///
/// Note that in the general case (if e.g. unions are involved), the /// Note that in the general case (if e.g. unions are involved), the
/// returned type is not necessarily the same as getLLType(). /// returned type is not necessarily the same as getLLType().
llvm::Constant * llvm::Constant *
createInitializerConstant(const VarInitMap &explicitInitializers, createInitializerConstant(const VarInitMap &explicitInitializers);
llvm::StructType *initializerType = nullptr);
protected: protected:
/// Static default initializer global. /// Static default initializer global.
llvm::GlobalVariable *init = nullptr; llvm::GlobalVariable *init = nullptr;
/// Static default initializer constant. /// Static default initializer constant.
llvm::Constant *constInit = nullptr; llvm::Constant *constInit = nullptr;
/// Static default initialier type.
llvm::StructType *init_type = nullptr;
/// Vtbl global. /// Vtbl global.
llvm::GlobalVariable *vtbl = nullptr; llvm::GlobalVariable *vtbl = nullptr;
@ -107,7 +100,8 @@ protected:
/// ClassInfo initializer constant. /// ClassInfo initializer constant.
llvm::Constant *constClassInfo = nullptr; llvm::Constant *constClassInfo = nullptr;
using ClassGlobalMap = std::map<std::pair<ClassDeclaration *, size_t>, llvm::GlobalVariable *>; using ClassGlobalMap =
std::map<std::pair<ClassDeclaration *, size_t>, llvm::GlobalVariable *>;
/// Map from pairs of <interface vtbl,index> to global variable, implemented /// Map from pairs of <interface vtbl,index> to global variable, implemented
/// by this class. The same interface can appear multiple times, so index is /// by this class. The same interface can appear multiple times, so index is
@ -144,6 +138,10 @@ protected:
bool isPacked() const; bool isPacked() const;
private: private:
llvm::StructType *llStructType = nullptr;
llvm::StructType *getLLStructType();
/// Recursively adds all the initializers for the given aggregate and, in /// Recursively adds all the initializers for the given aggregate and, in
/// case of a class type, all its base classes. /// case of a class type, all its base classes.
void addFieldInitializers(llvm::SmallVectorImpl<llvm::Constant *> &constants, void addFieldInitializers(llvm::SmallVectorImpl<llvm::Constant *> &constants,

View file

@ -9,10 +9,10 @@ struct Inner { align(32) int a; }
align(1) ubyte globalByte1; align(1) ubyte globalByte1;
// CHECK-DAG: align11globalByte1h = {{.*}} align 1 // CHECK-DAG: align11globalByte1h = {{.*}} align 1
static Outer globalOuter; static Outer globalOuter;
// CHECK-DAG: constant %align.Outer_init zeroinitializer{{(, comdat)?}}, align 32 // CHECK-DAG: constant %align.Outer zeroinitializer{{(, comdat)?}}, align 32
// CHECK-DAG: align11globalOuterS5align5Outer = {{.*}} align 32 // CHECK-DAG: align11globalOuterS5align5Outer = {{.*}} align 32
static Inner globalInner; static Inner globalInner;
// CHECK-DAG: constant %align.Inner_init zeroinitializer{{(, comdat)?}}, align 32 // CHECK-DAG: constant %align.Inner zeroinitializer{{(, comdat)?}}, align 32
// CHECK-DAG: align11globalInnerS5align5Inner = {{.*}} align 32 // CHECK-DAG: align11globalInnerS5align5Inner = {{.*}} align 32
Outer passAndReturnOuterByVal(Outer arg) { return arg; } Outer passAndReturnOuterByVal(Outer arg) { return arg; }

View file

@ -16,9 +16,9 @@ void testNested() {
assert(x == 3); assert(x == 3);
} }
// CHECK: @.immutablearray = internal constant [1 x { [3 x { { i32 } }] }] [{ [3 x { { i32 } }] } { [3 x { { i32 } }] [{ { i32 } } { { i32 } { i32 42 } }, { { i32 } } { { i32 } { i32 43 } }, { { i32 } } { { i32 } { i32 44 } }] }] ; [#uses = 1] // CHECK: @.immutablearray = internal constant [1 x %const_struct.S2] [%const_struct.S2 { [3 x %const_struct.S1] [%const_struct.S1 { %const_struct.S0 { i32 42 } }, %const_struct.S1 { %const_struct.S0 { i32 43 } }, %const_struct.S1 { %const_struct.S0 { i32 44 } }] }] ;
void main () { void main () {
// CHECK: store %const_struct.S2* bitcast ([1 x { [3 x { { i32 } }] }]* @.immutablearray to %const_struct.S2*), %const_struct.S2** %2 // CHECK: store %const_struct.S2* getelementptr inbounds ({{.*}}[1 x %const_struct.S2]* @.immutablearray, i32 0, i32 0), %const_struct.S2** %2
immutable S2[] xyz = [ { [ { { 42 } }, { { 43 } }, { { 44 } } ] } ]; immutable S2[] xyz = [ { [ { { 42 } }, { { 43 } }, { { 44 } } ] } ];
// CHECK: %.gc_mem = call {{{.*}}} @_d_newarrayU(%object.TypeInfo* bitcast (%"typeid(immutable(C0[]))"* @{{.*}} to %object.TypeInfo*), i{{32|64}} 3) // CHECK: %.gc_mem = call {{{.*}}} @_d_newarrayU(%object.TypeInfo* bitcast (%"typeid(immutable(C0[]))"* @{{.*}} to %object.TypeInfo*), i{{32|64}} 3)
immutable C0[] zyx = [ { new int(42) }, { null }, { null } ]; immutable C0[] zyx = [ { new int(42) }, { null }, { null } ];

View file

@ -108,9 +108,7 @@ struct Container { S s; }
void hierarchyOfLiterals() void hierarchyOfLiterals()
{ {
// CHECK: %sa = alloca [1 x %in_place_construct.Container] // CHECK: %sa = alloca [1 x %in_place_construct.Container]
// CHECK: %1 = bitcast [1 x %in_place_construct.Container]* %sa to [1 x { { i64, i64, i64, i64 } }]* // CHECK: store [1 x %in_place_construct.Container] [%in_place_construct.Container { %in_place_construct.S { i64 11, i64 12, i64 13, i64 14 } }], [1 x %in_place_construct.Container]* %sa
// CHECK: store [{{.*}}]* %1
// CHECK: ret void
Container[1] sa = [ Container(S(11, 12, 13, 14)) ]; Container[1] sa = [ Container(S(11, 12, 13, 14)) ];
} }

58
tests/codegen/union.d Normal file
View file

@ -0,0 +1,58 @@
// Tests LL types and constant initializers of init symbols and globals of
// structs with and without overlapping (union) fields.
// RUN: %ldc -c -output-ll -of=%t.ll %s && FileCheck %s < %t.ll
struct S
{
char c; // default initializer: 0xff
uint ui;
bool[2] bools; // make sure the 2 bools are extended to 2 bytes
char[2][4] multidim; // multidimensional init based on a single 0xff char
}
// CHECK-DAG: %union.S = type { i8, [3 x i8], i32, [2 x i8], [4 x [2 x i8]], [2 x i8] }
// CHECK-DAG: @_D5union1S6__initZ = constant %union.S { i8 -1, [3 x i8] zeroinitializer, i32 0, [2 x i8] zeroinitializer, [4 x [2 x i8]] {{\[}}[2 x i8] c"\FF\FF", [2 x i8] c"\FF\FF", [2 x i8] c"\FF\FF", [2 x i8] c"\FF\FF"], [2 x i8] zeroinitializer }
// CHECK-DAG: @_D5union8defaultSS5union1S = global %union.S { i8 -1, [3 x i8] zeroinitializer, i32 0, [2 x i8] zeroinitializer, [4 x [2 x i8]] {{\[}}[2 x i8] c"\FF\FF", [2 x i8] c"\FF\FF", [2 x i8] c"\FF\FF", [2 x i8] c"\FF\FF"], [2 x i8] zeroinitializer }
__gshared S defaultS;
// CHECK-DAG: @_D5union9explicitSS5union1S = global %union.S { i8 3, [3 x i8] zeroinitializer, i32 56, [2 x i8] c"\00\01", [4 x [2 x i8]] {{\[}}[2 x i8] c"\FF\FF", [2 x i8] c"\FF\FF", [2 x i8] c"\FF\FF", [2 x i8] c"\FF\FF"], [2 x i8] zeroinitializer }
__gshared S explicitS = S(3, 56, [false, true] /* implicit multidim */);
struct SWithUnion
{
char c;
S nested;
union
{
struct { ubyte ub = 6; ushort us = 33; ulong ul_dummy = void; }
struct { uint ui1; uint ui2 = 84; ulong ul = 666; }
ubyte ub_direct;
}
this(ubyte ub)
{
this.ub_direct = ub; // ub_direct is an alias for dominant ub
}
this(uint ui1, uint ui2)
{
this.ui1 = ui1; // dominated by ub and us
this.ui2 = ui2;
}
}
// CHECK-DAG: %union.SWithUnion = type { i8, [3 x i8], %union.S, i8, [1 x i8], i16, i32, i64 }
// CHECK-DAG: @_D5union10SWithUnion6__initZ = constant %union.SWithUnion { i8 -1, [3 x i8] zeroinitializer, %union.S { i8 -1, [3 x i8] zeroinitializer, i32 0, [2 x i8] zeroinitializer, [4 x [2 x i8]] {{\[}}[2 x i8] c"\FF\FF", [2 x i8] c"\FF\FF", [2 x i8] c"\FF\FF", [2 x i8] c"\FF\FF"], [2 x i8] zeroinitializer }, i8 6, [1 x i8] zeroinitializer, i16 33, i32 84, i64 666 }
// CHECK-DAG: @_D5union17defaultSWithUnionS5union10SWithUnion = global %union.SWithUnion { i8 -1, [3 x i8] zeroinitializer, %union.S { i8 -1, [3 x i8] zeroinitializer, i32 0, [2 x i8] zeroinitializer, [4 x [2 x i8]] {{\[}}[2 x i8] c"\FF\FF", [2 x i8] c"\FF\FF", [2 x i8] c"\FF\FF", [2 x i8] c"\FF\FF"], [2 x i8] zeroinitializer }, i8 6, [1 x i8] zeroinitializer, i16 33, i32 84, i64 666 }
__gshared SWithUnion defaultSWithUnion;
// CHECK-DAG: @_D5union28explicitCompatibleSWithUnionS5union10SWithUnion = global %union.SWithUnion { i8 -1, [3 x i8] zeroinitializer, %union.S { i8 -1, [3 x i8] zeroinitializer, i32 0, [2 x i8] zeroinitializer, [4 x [2 x i8]] {{\[}}[2 x i8] c"\FF\FF", [2 x i8] c"\FF\FF", [2 x i8] c"\FF\FF", [2 x i8] c"\FF\FF"], [2 x i8] zeroinitializer }, i8 53, [1 x i8] zeroinitializer, i16 33, i32 84, i64 666 }
__gshared SWithUnion explicitCompatibleSWithUnion = SWithUnion(53);
// When using the 2nd constructor, a dominated union field (ui1) is initialized.
// The regular LL type can thus not be used, an anonymous one is used instead.
// CHECK-DAG: @_D5union30explicitIncompatibleSWithUnionS5union10SWithUnion = global { i8, [3 x i8], %union.S, i32, i32, i64 } { i8 -1, [3 x i8] zeroinitializer, %union.S { i8 -1, [3 x i8] zeroinitializer, i32 0, [2 x i8] zeroinitializer, [4 x [2 x i8]] {{\[}}[2 x i8] c"\FF\FF", [2 x i8] c"\FF\FF", [2 x i8] c"\FF\FF", [2 x i8] c"\FF\FF"], [2 x i8] zeroinitializer }, i32 23, i32 256, i64 666 }
__gshared SWithUnion explicitIncompatibleSWithUnion = SWithUnion(23, 256);