mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-03 16:41:06 +03:00
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:
parent
e6537ba4dc
commit
ab1432ed06
8 changed files with 164 additions and 71 deletions
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
20
ir/iraggr.h
20
ir/iraggr.h
|
@ -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,
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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 } ];
|
||||
|
|
|
@ -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
58
tests/codegen/union.d
Normal 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);
|
Loading…
Add table
Add a link
Reference in a new issue