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);
for (unsigned i = 0; i < ale->elements->dim; ++i) {
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) {
elementType = val->getType();
} else {

View file

@ -1099,48 +1099,73 @@ LLConstant *DtoConstExpInit(Loc &loc, Type *targetType, Expression *exp) {
LOG_SCOPE
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
// have val->getType() == DtoType(targetType). But there are two reasons
// 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
// 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
// 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
// inconsistency bugs.
Type *expBase = stripModifiers(exp->type->toBasetype())->merge();
Type *targetBase = stripModifiers(targetType->toBasetype())->merge();
LLType *llType = val->getType();
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;
}
llvm::Type *llType = val->getType();
llvm::Type *targetLLType = DtoMemType(targetBase);
if (llType == targetLLType) {
Logger::println("Matching LLVM types, ignoring frontend glitch.");
if (baseTargetType->ty == Tsarray) {
Logger::println("Building constant array initializer from scalar.");
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;
}
if (targetBase->ty == Tsarray) {
Logger::println("Building constant array initializer to single value.");
if (baseTargetType->ty == Tvector) {
Logger::println("Building constant vector initializer from scalar.");
assert(expBase->size() > 0);
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);
TypeVector *tv = static_cast<TypeVector *>(baseTargetType);
assert(tv->basetype->ty == Tsarray);
dinteger_t elemCount =
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 *target = llvm::cast<llvm::IntegerType>(targetLLType);
assert(
target->getBitWidth() > source->getBitWidth() &&
"On initializer "
"integer type mismatch, the target should be wider than the source.");
assert(target->getBitWidth() > source->getBitWidth() &&
"On initializer integer type mismatch, the target should be wider "
"than the source.");
return llvm::ConstantExpr::getZExtOrBitCast(val, target);
}

View file

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

View file

@ -33,7 +33,8 @@ class StructType;
// it is used during codegen to hold all the vital info we need
struct IrAggr {
/// Constructor.
explicit IrAggr(AggregateDeclaration *agg);
explicit IrAggr(AggregateDeclaration *aggr)
: aggrdecl(aggr), type(aggr->type) {}
//////////////////////////////////////////////////////////////////////////
// public fields,
@ -78,24 +79,16 @@ struct IrAggr {
/// fields set to the provided constants. The remaining space (not
/// 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
/// returned type is not necessarily the same as getLLType().
llvm::Constant *
createInitializerConstant(const VarInitMap &explicitInitializers,
llvm::StructType *initializerType = nullptr);
createInitializerConstant(const VarInitMap &explicitInitializers);
protected:
/// Static default initializer global.
llvm::GlobalVariable *init = nullptr;
/// Static default initializer constant.
llvm::Constant *constInit = nullptr;
/// Static default initialier type.
llvm::StructType *init_type = nullptr;
/// Vtbl global.
llvm::GlobalVariable *vtbl = nullptr;
@ -107,7 +100,8 @@ protected:
/// ClassInfo initializer constant.
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
/// by this class. The same interface can appear multiple times, so index is
@ -144,6 +138,10 @@ protected:
bool isPacked() const;
private:
llvm::StructType *llStructType = nullptr;
llvm::StructType *getLLStructType();
/// Recursively adds all the initializers for the given aggregate and, in
/// case of a class type, all its base classes.
void addFieldInitializers(llvm::SmallVectorImpl<llvm::Constant *> &constants,

View file

@ -9,10 +9,10 @@ struct Inner { align(32) int a; }
align(1) ubyte globalByte1;
// CHECK-DAG: align11globalByte1h = {{.*}} align 1
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
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
Outer passAndReturnOuterByVal(Outer arg) { return arg; }

View file

@ -16,9 +16,9 @@ void testNested() {
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 () {
// 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 } } ] } ];
// 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 } ];

View file

@ -108,9 +108,7 @@ struct Container { S s; }
void hierarchyOfLiterals()
{
// 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
// CHECK: ret void
// 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
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);