Fix issue #2235 - IR struct packedness (#2247)

Unnaturally aligned aggregates were potentially not marked as packed,
leading to LLVM inserting additional padding and screwing up the memory
layout.
This commit is contained in:
kinke 2017-08-11 19:05:09 +02:00 committed by GitHub
parent ec9ffe29f1
commit ab37d5ba99
7 changed files with 45 additions and 65 deletions

View file

@ -8308,13 +8308,6 @@ extern (C++) final class TypeStruct : Type
AliasThisRec att = RECfwdref; AliasThisRec att = RECfwdref;
CPPMANGLE cppmangle = CPPMANGLE.def; CPPMANGLE cppmangle = CPPMANGLE.def;
version(IN_LLVM)
{
// cache the hasUnalignedFields check
// 0 = not checked, 1 = aligned, 2 = unaligned
int unaligned;
}
extern (D) this(StructDeclaration sym) extern (D) this(StructDeclaration sym)
{ {
super(Tstruct); super(Tstruct);

View file

@ -757,12 +757,6 @@ public:
AliasThisRec att; AliasThisRec att;
CPPMANGLE cppmangle; CPPMANGLE cppmangle;
#if IN_LLVM
// cache the hasUnalignedFields check
// 0 = not checked, 1 = aligned, 2 = unaligned
int32_t unaligned;
#endif
const char *kind(); const char *kind();
d_uns64 size(Loc loc); d_uns64 size(Loc loc);
unsigned alignsize(); unsigned alignsize();

View file

@ -1301,42 +1301,6 @@ void DtoSetFuncDeclIntrinsicName(TemplateInstance *ti, TemplateDeclaration *td,
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
bool hasUnalignedFields(Type *t) {
t = t->toBasetype();
if (t->ty == Tsarray) {
assert(t->nextOf()->size() % t->nextOf()->alignsize() == 0);
return hasUnalignedFields(t->nextOf());
}
if (t->ty != Tstruct) {
return false;
}
TypeStruct *ts = static_cast<TypeStruct *>(t);
if (ts->unaligned) {
return (ts->unaligned == 2);
}
StructDeclaration *sym = ts->sym;
// go through all the fields and try to find something unaligned
ts->unaligned = 2;
for (unsigned i = 0; i < sym->fields.dim; i++) {
VarDeclaration *f = static_cast<VarDeclaration *>(sym->fields.data[i]);
unsigned a = f->type->alignsize() - 1;
if (((f->offset + a) & ~a) != f->offset) {
return true;
}
if (f->type->toBasetype()->ty == Tstruct && hasUnalignedFields(f->type)) {
return true;
}
}
ts->unaligned = 1;
return false;
}
////////////////////////////////////////////////////////////////////////////////
size_t getMemberSize(Type *type) { size_t getMemberSize(Type *type) {
const dinteger_t dSize = type->size(); const dinteger_t dSize = type->size();
llvm::Type *const llType = DtoType(type); llvm::Type *const llType = DtoType(type);

View file

@ -127,9 +127,6 @@ LLConstant *DtoTypeInfoOf(Type *ty, bool base = true);
// target stuff // target stuff
void findDefaultTarget(); void findDefaultTarget();
/// Returns true if there is any unaligned type inside the aggregate.
bool hasUnalignedFields(Type *t);
/// Returns a pointer to the given member field of an aggregate. /// Returns a pointer to the given member field of an aggregate.
/// ///
/// 'src' is a pointer to the start of the memory of an 'ad' instance. /// 'src' is a pointer to the start of the memory of an 'ad' instance.

View file

@ -203,16 +203,24 @@ IrTypeAggr::IrTypeAggr(AggregateDeclaration *ad)
aggr(ad) {} aggr(ad) {}
bool IrTypeAggr::isPacked(AggregateDeclaration *ad) { bool IrTypeAggr::isPacked(AggregateDeclaration *ad) {
if (ad->isUnionDeclaration()) { const auto aggregateSize = (ad->sizeok == SIZEOKdone ? ad->structsize : ~0u);
return true;
} // For unions, only a subset of the fields are actually used for the IR type -
for (unsigned i = 0; i < ad->fields.dim; i++) { // don't care.
VarDeclaration *vd = static_cast<VarDeclaration *>(ad->fields.data[i]); for (const auto field : ad->fields) {
unsigned a = vd->type->alignsize() - 1; // The aggregate's size and the field offset need to be multiples of the
if (((vd->offset + a) & ~a) != vd->offset) { // field's natural alignment, otherwise the aggregate type is unnaturally
// aligned, and LLVM would insert padding.
const auto naturalFieldAlignment = field->type->alignsize();
const auto mask = naturalFieldAlignment - 1;
// If the aggregate's size is unknown, any field with natural alignment > 1
// will make it packed.
if ((aggregateSize & mask) != 0 || (field->offset & mask) != 0) {
return true; return true;
} }
} }
return false; return false;
} }

View file

@ -45,12 +45,7 @@ IrTypeStruct *IrTypeStruct::get(StructDeclaration *sd) {
return t; return t;
} }
t->packed = sd->alignment == 1; t->packed = isPacked(sd);
if (!t->packed) {
// Unfortunately, the previous check is not enough in case the struct
// contains an align declaration. See issue 726.
t->packed = isPacked(sd);
}
// For ldc.dcomptetypes.Pointer!(uint n,T), // For ldc.dcomptetypes.Pointer!(uint n,T),
// emit { T addrspace(gIR->dcomputetarget->mapping[n])* } // emit { T addrspace(gIR->dcomputetarget->mapping[n])* }

29
tests/codegen/gh2235.d Normal file
View file

@ -0,0 +1,29 @@
// RUN: %ldc -output-ll -of=%t.ll %s && FileCheck %s < %t.ll
// RUN: %ldc -run %s
// CHECK: %gh2235.Foo = type <{
align(2) struct Foo {
long y;
byte z;
}
// CHECK: %gh2235.Bar = type <{
class Bar {
union {
bool b;
Foo foo;
}
byte x;
void set(Foo f) {
x = 99;
foo = f;
}
}
void main() {
Bar bar = new Bar();
Foo f;
bar.set(f);
assert(bar.x == 99);
}