mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-01 23:50:43 +03:00
Merge remote-tracking branch 'origin/master' into merge-2.110
This commit is contained in:
commit
c271cf9fae
6 changed files with 79 additions and 17 deletions
|
@ -5,6 +5,7 @@
|
||||||
#### Platform support
|
#### Platform support
|
||||||
|
|
||||||
#### Bug fixes
|
#### Bug fixes
|
||||||
|
- Fix potentially corrupt IR layouts for bit fields. (#4646, #4708)
|
||||||
|
|
||||||
# LDC 1.39.0 (2024-07-04)
|
# LDC 1.39.0 (2024-07-04)
|
||||||
|
|
||||||
|
|
|
@ -183,6 +183,7 @@ DRValue *DBitFieldLValue::getRVal() {
|
||||||
const auto sizeInBits = intType->getBitWidth();
|
const auto sizeInBits = intType->getBitWidth();
|
||||||
const auto ptr = val;
|
const auto ptr = val;
|
||||||
LLValue *v = gIR->ir->CreateAlignedLoad(intType, ptr, llvm::MaybeAlign(1));
|
LLValue *v = gIR->ir->CreateAlignedLoad(intType, ptr, llvm::MaybeAlign(1));
|
||||||
|
// TODO: byte-swap v for big-endian targets?
|
||||||
|
|
||||||
if (bf->type->isunsigned()) {
|
if (bf->type->isunsigned()) {
|
||||||
if (auto n = bf->bitOffset)
|
if (auto n = bf->bitOffset)
|
||||||
|
@ -212,6 +213,7 @@ void DBitFieldLValue::store(LLValue *value) {
|
||||||
llvm::APInt::getLowBitsSet(intType->getBitWidth(), bf->fieldWidth);
|
llvm::APInt::getLowBitsSet(intType->getBitWidth(), bf->fieldWidth);
|
||||||
const auto oldVal =
|
const auto oldVal =
|
||||||
gIR->ir->CreateAlignedLoad(intType, ptr, llvm::MaybeAlign(1));
|
gIR->ir->CreateAlignedLoad(intType, ptr, llvm::MaybeAlign(1));
|
||||||
|
// TODO: byte-swap oldVal for big-endian targets?
|
||||||
const auto maskedOldVal =
|
const auto maskedOldVal =
|
||||||
gIR->ir->CreateAnd(oldVal, ~(mask << bf->bitOffset));
|
gIR->ir->CreateAnd(oldVal, ~(mask << bf->bitOffset));
|
||||||
|
|
||||||
|
@ -221,6 +223,7 @@ void DBitFieldLValue::store(LLValue *value) {
|
||||||
bfVal = gIR->ir->CreateShl(bfVal, n);
|
bfVal = gIR->ir->CreateShl(bfVal, n);
|
||||||
|
|
||||||
const auto newVal = gIR->ir->CreateOr(maskedOldVal, bfVal);
|
const auto newVal = gIR->ir->CreateOr(maskedOldVal, bfVal);
|
||||||
|
// TODO: byte-swap newVal for big-endian targets?
|
||||||
gIR->ir->CreateAlignedStore(newVal, ptr, llvm::MaybeAlign(1));
|
gIR->ir->CreateAlignedStore(newVal, ptr, llvm::MaybeAlign(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -139,7 +139,7 @@ static void write_struct_literal(Loc loc, LLValue *mem, StructDeclaration *sd,
|
||||||
// get a pointer to this group's IR field
|
// get a pointer to this group's IR field
|
||||||
const auto ptr = DtoLVal(DtoIndexAggregate(mem, sd, vd));
|
const auto ptr = DtoLVal(DtoIndexAggregate(mem, sd, vd));
|
||||||
|
|
||||||
// merge all initializers to a single value
|
// merge all initializers to a single integer value
|
||||||
const auto intType =
|
const auto intType =
|
||||||
LLIntegerType::get(gIR->context(), group.sizeInBytes * 8);
|
LLIntegerType::get(gIR->context(), group.sizeInBytes * 8);
|
||||||
LLValue *val = LLConstant::getNullValue(intType);
|
LLValue *val = LLConstant::getNullValue(intType);
|
||||||
|
@ -165,6 +165,7 @@ static void write_struct_literal(Loc loc, LLValue *mem, StructDeclaration *sd,
|
||||||
}
|
}
|
||||||
|
|
||||||
IF_LOG Logger::cout() << "merged IR value: " << *val << '\n';
|
IF_LOG Logger::cout() << "merged IR value: " << *val << '\n';
|
||||||
|
// TODO: byte-swap val for big-endian targets?
|
||||||
gIR->ir->CreateAlignedStore(val, ptr, llvm::MaybeAlign(1));
|
gIR->ir->CreateAlignedStore(val, ptr, llvm::MaybeAlign(1));
|
||||||
offset += group.sizeInBytes;
|
offset += group.sizeInBytes;
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include "ir/irtypeclass.h"
|
#include "ir/irtypeclass.h"
|
||||||
#include "ir/irtypestruct.h"
|
#include "ir/irtypestruct.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
using namespace dmd;
|
using namespace dmd;
|
||||||
|
|
||||||
|
@ -257,6 +258,8 @@ void IrAggr::addFieldInitializers(
|
||||||
const VarInitMap &explicitInitializers, AggregateDeclaration *decl,
|
const VarInitMap &explicitInitializers, AggregateDeclaration *decl,
|
||||||
unsigned &offset, unsigned &interfaceVtblIndex, bool &isPacked) {
|
unsigned &offset, unsigned &interfaceVtblIndex, bool &isPacked) {
|
||||||
|
|
||||||
|
using llvm::APInt;
|
||||||
|
|
||||||
if (ClassDeclaration *cd = decl->isClassDeclaration()) {
|
if (ClassDeclaration *cd = decl->isClassDeclaration()) {
|
||||||
if (cd->baseClass) {
|
if (cd->baseClass) {
|
||||||
addFieldInitializers(constants, explicitInitializers, cd->baseClass,
|
addFieldInitializers(constants, explicitInitializers, cd->baseClass,
|
||||||
|
@ -302,6 +305,9 @@ void IrAggr::addFieldInitializers(
|
||||||
: getDefaultInitializer(field);
|
: getDefaultInitializer(field);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// IR field index => APInt for bitfield group
|
||||||
|
std::unordered_map<unsigned, APInt> bitFieldGroupConstants;
|
||||||
|
|
||||||
const auto addToBitFieldGroup = [&](BitFieldDeclaration *bf,
|
const auto addToBitFieldGroup = [&](BitFieldDeclaration *bf,
|
||||||
unsigned fieldIndex, unsigned bitOffset) {
|
unsigned fieldIndex, unsigned bitOffset) {
|
||||||
LLConstant *init = getFieldInit(bf);
|
LLConstant *init = getFieldInit(bf);
|
||||||
|
@ -310,20 +316,24 @@ void IrAggr::addFieldInitializers(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
LLConstant *&constant = constants[baseLLFieldIndex + fieldIndex];
|
if (bitFieldGroupConstants.find(fieldIndex) ==
|
||||||
|
bitFieldGroupConstants.end()) {
|
||||||
|
const auto fieldType =
|
||||||
|
llvm::cast<LLArrayType>(b.defaultTypes()[fieldIndex]); // an i8 array
|
||||||
|
const auto bitFieldSize = fieldType->getNumElements();
|
||||||
|
bitFieldGroupConstants.emplace(fieldIndex, APInt(bitFieldSize * 8, 0));
|
||||||
|
}
|
||||||
|
|
||||||
using llvm::APInt;
|
APInt &constant = bitFieldGroupConstants[fieldIndex];
|
||||||
const auto fieldType = b.defaultTypes()[fieldIndex];
|
const auto intSizeInBits = constant.getBitWidth();
|
||||||
const auto intSizeInBits = fieldType->getIntegerBitWidth();
|
|
||||||
const APInt oldVal =
|
const APInt oldVal = constant;
|
||||||
constant ? constant->getUniqueInteger() : APInt(intSizeInBits, 0);
|
|
||||||
const APInt bfVal = init->getUniqueInteger().zextOrTrunc(intSizeInBits);
|
const APInt bfVal = init->getUniqueInteger().zextOrTrunc(intSizeInBits);
|
||||||
const APInt mask = APInt::getLowBitsSet(intSizeInBits, bf->fieldWidth)
|
const APInt mask = APInt::getLowBitsSet(intSizeInBits, bf->fieldWidth)
|
||||||
<< bitOffset;
|
<< bitOffset;
|
||||||
assert(!oldVal.intersects(mask) && "has masked bits set already");
|
assert(!oldVal.intersects(mask) && "has masked bits set already");
|
||||||
const APInt newVal = oldVal | ((bfVal << bitOffset) & mask);
|
|
||||||
|
|
||||||
constant = LLConstant::getIntegerValue(fieldType, newVal);
|
constant = oldVal | ((bfVal << bitOffset) & mask);
|
||||||
};
|
};
|
||||||
|
|
||||||
// add explicit and non-overlapping implicit initializers
|
// add explicit and non-overlapping implicit initializers
|
||||||
|
@ -333,7 +343,7 @@ void IrAggr::addFieldInitializers(
|
||||||
const auto fieldIndex = pair.second;
|
const auto fieldIndex = pair.second;
|
||||||
|
|
||||||
if (auto bf = field->isBitFieldDeclaration()) {
|
if (auto bf = field->isBitFieldDeclaration()) {
|
||||||
// multiple bit fields can map to a single IR field (of integer type)
|
// multiple bit fields can map to a single IR field for the whole group
|
||||||
addToBitFieldGroup(bf, fieldIndex, bf->bitOffset);
|
addToBitFieldGroup(bf, fieldIndex, bf->bitOffset);
|
||||||
} else {
|
} else {
|
||||||
LLConstant *&constant = constants[baseLLFieldIndex + fieldIndex];
|
LLConstant *&constant = constants[baseLLFieldIndex + fieldIndex];
|
||||||
|
@ -356,6 +366,25 @@ void IrAggr::addFieldInitializers(
|
||||||
(bf->offset - primary->offset) * 8 + bf->bitOffset);
|
(bf->offset - primary->offset) * 8 + bf->bitOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const auto &pair : bitFieldGroupConstants) {
|
||||||
|
const unsigned fieldIndex = pair.first;
|
||||||
|
const APInt intValue = pair.second;
|
||||||
|
|
||||||
|
LLConstant *&constant = constants[baseLLFieldIndex + fieldIndex];
|
||||||
|
assert(!constant && "already have a constant for a bitfield group?");
|
||||||
|
|
||||||
|
// convert APInt to i8 array
|
||||||
|
const auto i8Type = getI8Type();
|
||||||
|
const auto numBytes = intValue.getBitWidth() / 8;
|
||||||
|
llvm::SmallVector<LLConstant *, 8> bytes;
|
||||||
|
for (unsigned i = 0; i < numBytes; ++i) {
|
||||||
|
APInt byteVal = intValue.extractBits(8, i * 8);
|
||||||
|
bytes.push_back(LLConstant::getIntegerValue(i8Type, byteVal));
|
||||||
|
}
|
||||||
|
|
||||||
|
constant = llvm::ConstantArray::get(LLArrayType::get(i8Type, numBytes), bytes);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: sanity check that all explicit initializers have been dealt with?
|
// TODO: sanity check that all explicit initializers have been dealt with?
|
||||||
// (potential issue for bitfields in unions...)
|
// (potential issue for bitfields in unions...)
|
||||||
|
|
||||||
|
|
|
@ -148,8 +148,9 @@ void AggrTypeBuilder::addAggregate(
|
||||||
const uint64_t f_begin = field->offset;
|
const uint64_t f_begin = field->offset;
|
||||||
const uint64_t f_end = f_begin + fieldSize;
|
const uint64_t f_end = f_begin + fieldSize;
|
||||||
|
|
||||||
|
// use an i8 array for bit field groups
|
||||||
const auto llType =
|
const auto llType =
|
||||||
isBitField ? LLIntegerType::get(gIR->context(), fieldSize * 8)
|
isBitField ? llvm::ArrayType::get(getI8Type(), fieldSize)
|
||||||
: DtoMemType(field->type);
|
: DtoMemType(field->type);
|
||||||
|
|
||||||
// check for overlap with existing fields (on a byte level, not bits)
|
// check for overlap with existing fields (on a byte level, not bits)
|
||||||
|
@ -214,12 +215,8 @@ void AggrTypeBuilder::addAggregate(
|
||||||
fieldSize = af.size;
|
fieldSize = af.size;
|
||||||
} else {
|
} else {
|
||||||
fieldAlignment = getABITypeAlign(llType);
|
fieldAlignment = getABITypeAlign(llType);
|
||||||
if (vd->isBitFieldDeclaration()) {
|
fieldSize = getTypeAllocSize(llType);
|
||||||
fieldSize = af.size; // an IR integer of possibly non-power-of-2 size
|
assert(fieldSize <= af.size);
|
||||||
} else {
|
|
||||||
fieldSize = getTypeAllocSize(llType);
|
|
||||||
assert(fieldSize <= af.size);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// advance offset to right past this field
|
// advance offset to right past this field
|
||||||
|
|
31
tests/compilable/gh4646.d
Normal file
31
tests/compilable/gh4646.d
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
// RUN: %ldc -c -preview=bitfields %s
|
||||||
|
|
||||||
|
struct BitField {
|
||||||
|
uint _0 : 1;
|
||||||
|
uint _1 : 1;
|
||||||
|
uint _2 : 1;
|
||||||
|
uint _3 : 1;
|
||||||
|
uint _4 : 1;
|
||||||
|
uint _5 : 1;
|
||||||
|
uint _6 : 1;
|
||||||
|
uint _7 : 1;
|
||||||
|
uint _8 : 1;
|
||||||
|
uint _9 : 1;
|
||||||
|
uint _10 : 1;
|
||||||
|
uint _11 : 1;
|
||||||
|
uint _12 : 1;
|
||||||
|
uint _13 : 1;
|
||||||
|
uint _14 : 1;
|
||||||
|
uint _15 : 1;
|
||||||
|
uint _16 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static assert(BitField.sizeof == 4);
|
||||||
|
static assert(BitField.alignof == 4);
|
||||||
|
|
||||||
|
struct Foo {
|
||||||
|
BitField bf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static assert(Foo.sizeof == 4);
|
||||||
|
static assert(Foo.alignof == 4);
|
Loading…
Add table
Add a link
Reference in a new issue