Skip to content

Commit b731db4

Browse files
authored
Merge pull request #4736 from kinke/fix4734
Get rid of cycles in DtoType()
2 parents 6bd5d9a + 2c04d86 commit b731db4

File tree

10 files changed

+99
-55
lines changed

10 files changed

+99
-55
lines changed

.github/workflows/main.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,9 @@ jobs:
9494
with:
9595
submodules: true
9696
fetch-depth: 50
97-
- name: 'macOS 14: Switch to Xcode 16 Beta 4'
97+
- name: 'macOS 14: Switch to Xcode 16 Beta 5'
9898
if: matrix.os == 'macos-14'
99-
run: sudo xcode-select -switch /Applications/Xcode_16_beta_4.app
99+
run: sudo xcode-select -switch /Applications/Xcode_16_beta_5.app
100100
- name: Install prerequisites
101101
uses: ./.github/actions/1-setup
102102
with:

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
#### Bug fixes
1212
- Fix potentially corrupt IR layouts for bit fields. (#4646, #4708)
13+
- Fix potentially corrupt IR layouts for explicitly under-aligned aggregates, a regression introduced in LDC v1.31. (#4734, #4736)
1314

1415
# LDC 1.39.0 (2024-07-04)
1516

ir/iraggr.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
#include "dmd/aggregate.h"
1313
#include "dmd/declaration.h"
14+
#include "dmd/errors.h"
1415
#include "dmd/expression.h"
1516
#include "dmd/identifier.h"
1617
#include "dmd/init.h"
@@ -217,8 +218,12 @@ IrAggr::createInitializerConstant(const VarInitMap &explicitInitializers) {
217218

218219
// tail padding?
219220
const size_t structsize = aggrdecl->size(Loc());
220-
if (offset < structsize)
221+
if (offset < structsize) {
221222
add_zeros(constants, offset, structsize);
223+
} else if (offset > structsize) {
224+
error(Loc(), "ICE: IR aggregate constant size exceeds the frontend size");
225+
fatal();
226+
}
222227

223228
// get LL field types
224229
llvm::SmallVector<llvm::Type *, 16> types;

ir/irtype.cpp

Lines changed: 8 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -117,25 +117,12 @@ IrTypePointer *IrTypePointer::get(Type *dt) {
117117
auto &ctype = getIrType(dt);
118118
assert(!ctype);
119119

120-
LLType *elemType;
121-
unsigned addressSpace = 0;
122-
if (dt->ty == TY::Tnull) {
123-
elemType = llvm::Type::getInt8Ty(getGlobalContext());
124-
} else {
125-
elemType = DtoMemType(dt->nextOf());
126-
if (dt->nextOf()->ty == TY::Tfunction) {
127-
addressSpace = gDataLayout->getProgramAddressSpace();
128-
}
129-
130-
// DtoType could have already created the same type, e.g. for
131-
// dt == Node* in struct Node { Node* n; }.
132-
if (ctype) {
133-
return ctype->isPointer();
134-
}
135-
}
120+
unsigned addressSpace =
121+
dt->ty == TY::Tpointer && dt->nextOf()->ty == TY::Tfunction
122+
? gDataLayout->getProgramAddressSpace()
123+
: 0;
136124

137-
auto t =
138-
new IrTypePointer(dt, llvm::PointerType::get(elemType, addressSpace));
125+
auto t = new IrTypePointer(dt, getOpaquePtrType(addressSpace));
139126
ctype = t;
140127
return t;
141128
}
@@ -173,15 +160,9 @@ IrTypeArray *IrTypeArray::get(Type *dt) {
173160
auto &ctype = getIrType(dt);
174161
assert(!ctype);
175162

176-
LLType *elemType = DtoMemType(dt->nextOf());
177-
178-
// Could have already built the type as part of a struct forward reference,
179-
// just as for pointers.
180-
if (!ctype) {
181-
llvm::Type *types[] = {DtoSize_t(), llvm::PointerType::get(elemType, 0)};
182-
LLType *at = llvm::StructType::get(getGlobalContext(), types, false);
183-
ctype = new IrTypeArray(dt, at);
184-
}
163+
llvm::Type *types[] = {DtoSize_t(), getOpaquePtrType()};
164+
LLType *at = llvm::StructType::get(getGlobalContext(), types, false);
165+
ctype = new IrTypeArray(dt, at);
185166

186167
return ctype->isArray();
187168
}

ir/irtypeaggr.cpp

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -212,17 +212,18 @@ void AggrTypeBuilder::addAggregate(
212212
// add default type
213213
m_defaultTypes.push_back(llType);
214214

215-
unsigned fieldAlignment, fieldSize;
216215
if (!llType->isSized()) {
217-
// forward reference in a cycle or similar, we need to trust the D type
218-
fieldAlignment = DtoAlignment(vd->type);
219-
fieldSize = af.size;
220-
} else {
221-
fieldAlignment = getABITypeAlign(llType);
222-
fieldSize = getTypeAllocSize(llType);
223-
assert(fieldSize <= af.size);
216+
error(vd->loc,
217+
"unexpected IR type forward declaration for aggregate member of "
218+
"type `%s`. This is an ICE, please file an LDC issue.",
219+
vd->type->toPrettyChars());
220+
fatal();
224221
}
225222

223+
const unsigned fieldAlignment = getABITypeAlign(llType);
224+
const unsigned fieldSize = getTypeAllocSize(llType);
225+
assert(fieldSize <= af.size);
226+
226227
// advance offset to right past this field
227228
if (!m_packed) {
228229
assert(fieldAlignment);
@@ -278,10 +279,11 @@ IrTypeAggr::IrTypeAggr(AggregateDeclaration *ad)
278279
LLStructType::create(gIR->context(), ad->toPrettyChars())),
279280
aggr(ad) {}
280281

281-
unsigned IrTypeAggr::getMemberLocation(VarDeclaration *var, bool& isFieldIdx) const {
282+
unsigned IrTypeAggr::getMemberLocation(VarDeclaration *var, bool& isFieldIdx) {
282283
// Note: The interface is a bit more general than what we actually return.
283284
// Specifically, the frontend offset information we use for overlapping
284285
// fields is always based at the object start.
286+
const auto &varGEPIndices = getVarGEPIndices();
285287
auto it = varGEPIndices.find(var);
286288
if (it != varGEPIndices.end()) {
287289
isFieldIdx = true;

ir/irtypeaggr.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ class IrTypeAggr : public IrType {
7272
/// Returns the index of the field in the LLVM struct type that corresponds
7373
/// to the given member variable, plus the offset to the actual field start
7474
/// due to overlapping (union) fields, if any.
75-
unsigned getMemberLocation(VarDeclaration *var, bool& isFieldIdx) const;
75+
unsigned getMemberLocation(VarDeclaration *var, bool& isFieldIdx);
7676

7777
protected:
7878
///
@@ -90,6 +90,8 @@ class IrTypeAggr : public IrType {
9090
/// the field index of a variable in the frontend, it only stores the byte
9191
/// offset.
9292
VarGEPIndices varGEPIndices;
93+
94+
virtual const VarGEPIndices &getVarGEPIndices() { return varGEPIndices; }
9395
};
9496

9597
// A helper for aggregating consecutive bit fields to a group.

ir/irtypeclass.cpp

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "dmd/aggregate.h"
1313
#include "dmd/declaration.h"
1414
#include "dmd/dsymbol.h"
15+
#include "dmd/errors.h"
1516
#include "dmd/mtype.h"
1617
#include "dmd/target.h"
1718
#include "dmd/template.h"
@@ -60,24 +61,34 @@ void IrTypeClass::addClassData(AggrTypeBuilder &builder,
6061
}
6162

6263
IrTypeClass *IrTypeClass::get(ClassDeclaration *cd) {
64+
const auto t = new IrTypeClass(cd);
65+
getIrType(cd->type) = t;
66+
return t;
67+
}
68+
69+
llvm::Type *IrTypeClass::getLLType() { return getOpaquePtrType(); }
70+
71+
// Lazily build the actual IR struct type when needed.
72+
// Note that it is this function that initializes most fields!
73+
llvm::Type *IrTypeClass::getMemoryLLType() {
74+
if (!isaStruct(type)->isOpaque())
75+
return type;
76+
6377
IF_LOG Logger::println("Building class type %s @ %s", cd->toPrettyChars(),
6478
cd->loc.toChars());
6579
LOG_SCOPE;
6680

67-
const auto t = new IrTypeClass(cd);
68-
getIrType(cd->type) = t;
69-
7081
const unsigned instanceSize = cd->structsize;
7182
IF_LOG Logger::println("Instance size: %u", instanceSize);
7283

7384
AggrTypeBuilder builder;
7485

7586
// add vtbl
76-
builder.addType(llvm::PointerType::get(t->vtbl_type, 0), target.ptrsize);
87+
builder.addType(llvm::PointerType::get(vtbl_type, 0), target.ptrsize);
7788

7889
if (cd->isInterfaceDeclaration()) {
7990
// interfaces are just a vtable
80-
t->num_interface_vtbls =
91+
num_interface_vtbls =
8192
cd->vtblInterfaces ? cd->vtblInterfaces->length : 0;
8293
} else {
8394
// classes have monitor and fields
@@ -87,34 +98,48 @@ IrTypeClass *IrTypeClass::get(ClassDeclaration *cd) {
8798
}
8899

89100
// add data members recursively
90-
t->addClassData(builder, cd);
101+
addClassData(builder, cd);
91102

92103
// add tail padding
93104
if (instanceSize) // can be 0 for opaque classes
94105
builder.addTailPadding(instanceSize);
95106
}
96107

97108
// set struct body and copy GEP indices
98-
isaStruct(t->type)->setBody(builder.defaultTypes(), builder.isPacked());
99-
t->varGEPIndices = builder.varGEPIndices();
109+
isaStruct(type)->setBody(builder.defaultTypes(), builder.isPacked());
110+
varGEPIndices = builder.varGEPIndices();
100111

101-
IF_LOG Logger::cout() << "class type: " << *t->type << std::endl;
102-
103-
return t;
104-
}
112+
if (!cd->isInterfaceDeclaration() && instanceSize &&
113+
getTypeAllocSize(type) != instanceSize) {
114+
error(cd->loc, "ICE: class IR size does not match the frontend size");
115+
fatal();
116+
}
105117

106-
llvm::Type *IrTypeClass::getLLType() { return llvm::PointerType::get(type, 0); }
118+
IF_LOG Logger::cout() << "class type: " << *type << std::endl;
107119

108-
llvm::Type *IrTypeClass::getMemoryLLType() { return type; }
120+
return type;
121+
}
109122

110123
size_t IrTypeClass::getInterfaceIndex(ClassDeclaration *inter) {
124+
getMemoryLLType(); // lazily resolve
125+
111126
auto it = interfaceMap.find(inter);
112127
if (it == interfaceMap.end()) {
113128
return ~0UL;
114129
}
115130
return it->second;
116131
}
117132

133+
unsigned IrTypeClass::getNumInterfaceVtbls() {
134+
getMemoryLLType(); // lazily resolve
135+
return num_interface_vtbls;
136+
}
137+
138+
const VarGEPIndices &IrTypeClass::getVarGEPIndices() {
139+
getMemoryLLType(); // lazily resolve
140+
return varGEPIndices;
141+
}
142+
118143
void IrTypeClass::addInterfaceToMap(ClassDeclaration *inter, size_t index) {
119144
// don't duplicate work or overwrite indices
120145
if (interfaceMap.find(inter) != interfaceMap.end()) {

ir/irtypeclass.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ class IrTypeClass : public IrTypeAggr {
4747

4848
/// Returns the number of interface implementations (vtables) in this
4949
/// class.
50-
unsigned getNumInterfaceVtbls() { return num_interface_vtbls; }
50+
unsigned getNumInterfaceVtbls();
5151

5252
protected:
5353
///
@@ -73,6 +73,8 @@ class IrTypeClass : public IrTypeAggr {
7373

7474
//////////////////////////////////////////////////////////////////////////
7575

76+
const VarGEPIndices &getVarGEPIndices() override;
77+
7678
/// Adds the data members for the given class to the type builder, including
7779
/// those inherited from base classes/interfaces.
7880
void addClassData(AggrTypeBuilder &builder, ClassDeclaration *currCd);

ir/irtypestruct.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
#include "dmd/aggregate.h"
1313
#include "dmd/declaration.h"
14+
#include "dmd/errors.h"
1415
#include "dmd/init.h"
1516
#include "dmd/mtype.h"
1617
#include "dmd/template.h"
@@ -91,6 +92,11 @@ IrTypeStruct *IrTypeStruct::get(StructDeclaration *sd) {
9192
builder.addTailPadding(sd->structsize);
9293
isaStruct(t->type)->setBody(builder.defaultTypes(), builder.isPacked());
9394
t->varGEPIndices = builder.varGEPIndices();
95+
96+
if (getTypeAllocSize(t->type) != sd->structsize) {
97+
error(sd->loc, "ICE: struct IR size does not match the frontend size");
98+
fatal();
99+
}
94100
}
95101

96102
IF_LOG Logger::cout() << "final struct type: " << *t->type << std::endl;

tests/compilable/gh4734.d

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// RUN: %ldc -c %s
2+
3+
align(1) struct Item {
4+
KV v;
5+
uint i;
6+
}
7+
8+
struct KV {
9+
align(1) S* s;
10+
uint k;
11+
}
12+
13+
struct S {
14+
Table table;
15+
}
16+
17+
struct Table {
18+
char a;
19+
Item v;
20+
}

0 commit comments

Comments
 (0)