Skip to content

Commit 1bf8763

Browse files
committed
Fix bit field IR storage from weird integer to i8 array
Fixes #4646.
1 parent da4a6e4 commit 1bf8763

File tree

5 files changed

+77
-17
lines changed

5 files changed

+77
-17
lines changed

gen/dvalue.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ DRValue *DBitFieldLValue::getRVal() {
183183
const auto sizeInBits = intType->getBitWidth();
184184
const auto ptr = val;
185185
LLValue *v = gIR->ir->CreateAlignedLoad(intType, ptr, llvm::MaybeAlign(1));
186+
// TODO: byte-swap v for big-endian targets?
186187

187188
if (bf->type->isunsigned()) {
188189
if (auto n = bf->bitOffset)
@@ -212,6 +213,7 @@ void DBitFieldLValue::store(LLValue *value) {
212213
llvm::APInt::getLowBitsSet(intType->getBitWidth(), bf->fieldWidth);
213214
const auto oldVal =
214215
gIR->ir->CreateAlignedLoad(intType, ptr, llvm::MaybeAlign(1));
216+
// TODO: byte-swap oldVal for big-endian targets?
215217
const auto maskedOldVal =
216218
gIR->ir->CreateAnd(oldVal, ~(mask << bf->bitOffset));
217219

@@ -221,6 +223,7 @@ void DBitFieldLValue::store(LLValue *value) {
221223
bfVal = gIR->ir->CreateShl(bfVal, n);
222224

223225
const auto newVal = gIR->ir->CreateOr(maskedOldVal, bfVal);
226+
// TODO: byte-swap newVal for big-endian targets?
224227
gIR->ir->CreateAlignedStore(newVal, ptr, llvm::MaybeAlign(1));
225228
}
226229

gen/toir.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ static void write_struct_literal(Loc loc, LLValue *mem, StructDeclaration *sd,
139139
// get a pointer to this group's IR field
140140
const auto ptr = DtoLVal(DtoIndexAggregate(mem, sd, vd));
141141

142-
// merge all initializers to a single value
142+
// merge all initializers to a single integer value
143143
const auto intType =
144144
LLIntegerType::get(gIR->context(), group.sizeInBytes * 8);
145145
LLValue *val = LLConstant::getNullValue(intType);
@@ -165,6 +165,7 @@ static void write_struct_literal(Loc loc, LLValue *mem, StructDeclaration *sd,
165165
}
166166

167167
IF_LOG Logger::cout() << "merged IR value: " << *val << '\n';
168+
// TODO: byte-swap val for big-endian targets?
168169
gIR->ir->CreateAlignedStore(val, ptr, llvm::MaybeAlign(1));
169170
offset += group.sizeInBytes;
170171

ir/iraggr.cpp

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,8 @@ void IrAggr::addFieldInitializers(
257257
const VarInitMap &explicitInitializers, AggregateDeclaration *decl,
258258
unsigned &offset, unsigned &interfaceVtblIndex, bool &isPacked) {
259259

260+
using llvm::APInt;
261+
260262
if (ClassDeclaration *cd = decl->isClassDeclaration()) {
261263
if (cd->baseClass) {
262264
addFieldInitializers(constants, explicitInitializers, cd->baseClass,
@@ -302,6 +304,9 @@ void IrAggr::addFieldInitializers(
302304
: getDefaultInitializer(field);
303305
};
304306

307+
// IR field index => APInt for bitfield group
308+
std::unordered_map<unsigned, APInt> bitFieldGroupConstants;
309+
305310
const auto addToBitFieldGroup = [&](BitFieldDeclaration *bf,
306311
unsigned fieldIndex, unsigned bitOffset) {
307312
LLConstant *init = getFieldInit(bf);
@@ -310,20 +315,24 @@ void IrAggr::addFieldInitializers(
310315
return;
311316
}
312317

313-
LLConstant *&constant = constants[baseLLFieldIndex + fieldIndex];
318+
if (bitFieldGroupConstants.find(fieldIndex) ==
319+
bitFieldGroupConstants.end()) {
320+
const auto fieldType =
321+
llvm::cast<LLArrayType>(b.defaultTypes()[fieldIndex]); // an i8 array
322+
const auto bitFieldSize = fieldType->getNumElements();
323+
bitFieldGroupConstants.emplace(fieldIndex, APInt(bitFieldSize * 8, 0));
324+
}
325+
326+
APInt &constant = bitFieldGroupConstants[fieldIndex];
327+
const auto intSizeInBits = constant.getBitWidth();
314328

315-
using llvm::APInt;
316-
const auto fieldType = b.defaultTypes()[fieldIndex];
317-
const auto intSizeInBits = fieldType->getIntegerBitWidth();
318-
const APInt oldVal =
319-
constant ? constant->getUniqueInteger() : APInt(intSizeInBits, 0);
329+
const APInt oldVal = constant;
320330
const APInt bfVal = init->getUniqueInteger().zextOrTrunc(intSizeInBits);
321331
const APInt mask = APInt::getLowBitsSet(intSizeInBits, bf->fieldWidth)
322332
<< bitOffset;
323333
assert(!oldVal.intersects(mask) && "has masked bits set already");
324-
const APInt newVal = oldVal | ((bfVal << bitOffset) & mask);
325334

326-
constant = LLConstant::getIntegerValue(fieldType, newVal);
335+
constant = oldVal | ((bfVal << bitOffset) & mask);
327336
};
328337

329338
// add explicit and non-overlapping implicit initializers
@@ -333,7 +342,7 @@ void IrAggr::addFieldInitializers(
333342
const auto fieldIndex = pair.second;
334343

335344
if (auto bf = field->isBitFieldDeclaration()) {
336-
// multiple bit fields can map to a single IR field (of integer type)
345+
// multiple bit fields can map to a single IR field for the whole group
337346
addToBitFieldGroup(bf, fieldIndex, bf->bitOffset);
338347
} else {
339348
LLConstant *&constant = constants[baseLLFieldIndex + fieldIndex];
@@ -356,6 +365,25 @@ void IrAggr::addFieldInitializers(
356365
(bf->offset - primary->offset) * 8 + bf->bitOffset);
357366
}
358367

368+
for (const auto &pair : bitFieldGroupConstants) {
369+
const unsigned fieldIndex = pair.first;
370+
const APInt intValue = pair.second;
371+
372+
LLConstant *&constant = constants[baseLLFieldIndex + fieldIndex];
373+
assert(!constant && "already have a constant for a bitfield group?");
374+
375+
// convert APInt to i8 array
376+
const auto i8Type = getI8Type();
377+
const auto numBytes = intValue.getBitWidth() / 8;
378+
llvm::SmallVector<LLConstant *, 8> bytes;
379+
for (unsigned i = 0; i < numBytes; ++i) {
380+
APInt byteVal = intValue.extractBits(8, i * 8);
381+
bytes.push_back(LLConstant::getIntegerValue(i8Type, byteVal));
382+
}
383+
384+
constant = llvm::ConstantArray::get(LLArrayType::get(i8Type, numBytes), bytes);
385+
}
386+
359387
// TODO: sanity check that all explicit initializers have been dealt with?
360388
// (potential issue for bitfields in unions...)
361389

ir/irtypeaggr.cpp

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,9 @@ void AggrTypeBuilder::addAggregate(
146146
const uint64_t f_begin = field->offset;
147147
const uint64_t f_end = f_begin + fieldSize;
148148

149+
// use an i8 array for bit field groups
149150
const auto llType =
150-
isBitField ? LLIntegerType::get(gIR->context(), fieldSize * 8)
151+
isBitField ? llvm::ArrayType::get(getI8Type(), fieldSize)
151152
: DtoMemType(field->type);
152153

153154
// check for overlap with existing fields (on a byte level, not bits)
@@ -212,12 +213,8 @@ void AggrTypeBuilder::addAggregate(
212213
fieldSize = af.size;
213214
} else {
214215
fieldAlignment = getABITypeAlign(llType);
215-
if (vd->isBitFieldDeclaration()) {
216-
fieldSize = af.size; // an IR integer of possibly non-power-of-2 size
217-
} else {
218-
fieldSize = getTypeAllocSize(llType);
219-
assert(fieldSize <= af.size);
220-
}
216+
fieldSize = getTypeAllocSize(llType);
217+
assert(fieldSize <= af.size);
221218
}
222219

223220
// advance offset to right past this field

tests/compilable/gh4646.d

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// RUN: %ldc -c -preview=bitfields %s
2+
3+
struct BitField {
4+
uint _0 : 1;
5+
uint _1 : 1;
6+
uint _2 : 1;
7+
uint _3 : 1;
8+
uint _4 : 1;
9+
uint _5 : 1;
10+
uint _6 : 1;
11+
uint _7 : 1;
12+
uint _8 : 1;
13+
uint _9 : 1;
14+
uint _10 : 1;
15+
uint _11 : 1;
16+
uint _12 : 1;
17+
uint _13 : 1;
18+
uint _14 : 1;
19+
uint _15 : 1;
20+
uint _16 : 1;
21+
}
22+
23+
static assert(BitField.sizeof == 4);
24+
static assert(BitField.alignof == 4);
25+
26+
struct Foo {
27+
BitField bf;
28+
}
29+
30+
static assert(Foo.sizeof == 4);
31+
static assert(Foo.alignof == 4);

0 commit comments

Comments
 (0)