Split off record layouting

Such struct decl:

  struct S { char a; int i;} __attribute__((packed));

should be accepted and cause S to be five bytes long (i.e.
the packed attribute should matter).  So we can't layout
the members during parsing already.  Split off the offset
and alignment calculation for this.
This commit is contained in:
Michael Matz 2016-10-02 21:37:58 +02:00
parent 5d6a9e797a
commit ddecb0e685
2 changed files with 114 additions and 82 deletions

192
tccgen.c
View File

@ -3250,13 +3250,104 @@ static Sym * find_field (CType *type, int v)
return s; return s;
} }
static void struct_layout(CType *type, AttributeDef *ad)
{
int align, maxalign, offset, c;
Sym *f;
maxalign = 1;
offset = 0;
c = 0;
for (f = type->ref->next; f; f = f->next) {
int extra_bytes = f->c;
int bit_pos;
int size = type_size(&f->type, &align);
if (f->type.t & VT_BITFIELD)
bit_pos = (f->type.t >> VT_STRUCT_SHIFT) & 0x3f;
else
bit_pos = 0;
if (f->r) {
align = f->r;
} else if (ad->a.packed) {
align = 1;
}
if (extra_bytes) c += extra_bytes;
else if (bit_pos == 0) {
if (type->ref->type.t == TOK_STRUCT) {
c = (c + align - 1) & -align;
offset = c;
if (size > 0)
c += size;
} else {
offset = 0;
if (size > c)
c = size;
}
if (align > maxalign)
maxalign = align;
}
#if 0
printf("set field %s offset=%d",
get_tok_str(f->v & ~SYM_FIELD, NULL), offset);
if (f->type.t & VT_BITFIELD) {
printf(" pos=%d size=%d",
(f->type.t >> VT_STRUCT_SHIFT) & 0x3f,
(f->type.t >> (VT_STRUCT_SHIFT + 6)) & 0x3f);
}
printf("\n");
#endif
if (f->v & SYM_FIRST_ANOM && (f->type.t & VT_BTYPE) == VT_STRUCT) {
Sym *ass;
/* An anonymous struct/union. Adjust member offsets
to reflect the real offset of our containing struct.
Also set the offset of this anon member inside
the outer struct to be zero. Via this it
works when accessing the field offset directly
(from base object), as well as when recursing
members in initializer handling. */
int v2 = f->type.ref->v;
if (!(v2 & SYM_FIELD) &&
(v2 & ~SYM_STRUCT) < SYM_FIRST_ANOM) {
Sym **pps;
/* This happens only with MS extensions. The
anon member has a named struct type, so it
potentially is shared with other references.
We need to unshare members so we can modify
them. */
ass = f->type.ref;
f->type.ref = sym_push(anon_sym++ | SYM_FIELD,
&f->type.ref->type, 0,
f->type.ref->c);
pps = &f->type.ref->next;
while ((ass = ass->next) != NULL) {
*pps = sym_push(ass->v, &ass->type, 0, ass->c);
pps = &((*pps)->next);
}
*pps = NULL;
}
ass = f->type.ref;
while ((ass = ass->next) != NULL)
ass->c += offset;
f->c = 0;
} else {
f->c = offset;
}
f->r = 0;
}
/* store size and alignment */
type->ref->c = (c + maxalign - 1) & -maxalign;
type->ref->r = maxalign;
}
/* enum/struct/union declaration. u is either VT_ENUM or VT_STRUCT */ /* enum/struct/union declaration. u is either VT_ENUM or VT_STRUCT */
static void struct_decl(CType *type, AttributeDef *ad, int u) static void struct_decl(CType *type, AttributeDef *ad, int u)
{ {
int a, v, size, align, maxalign, offset, flexible, extra_bytes; int extra_bytes;
int a, v, size, align, flexible, alignoverride;
long c; long c;
int bit_size, bit_pos, bsize, bt, lbit_pos, prevbt; int bit_size, bit_pos, bsize, bt, prevbt;
Sym *s, *ss, *ass, **ps; Sym *s, *ss, **ps;
AttributeDef ad1; AttributeDef ad1;
CType type1, btype; CType type1, btype;
@ -3343,11 +3434,9 @@ static void struct_decl(CType *type, AttributeDef *ad, int u)
s->c = type_size(seen_wide ? &size_type : &int_type, &align); s->c = type_size(seen_wide ? &size_type : &int_type, &align);
skip('}'); skip('}');
} else { } else {
maxalign = 1;
ps = &s->next; ps = &s->next;
prevbt = VT_INT; prevbt = VT_INT;
bit_pos = 0; bit_pos = 0;
offset = 0;
flexible = 0; flexible = 0;
while (tok != '}') { while (tok != '}') {
if (!parse_btype(&btype, &ad1)) { if (!parse_btype(&btype, &ad1)) {
@ -3355,7 +3444,7 @@ static void struct_decl(CType *type, AttributeDef *ad, int u)
continue; continue;
} }
while (1) { while (1) {
extra_bytes = 0; extra_bytes = 0;
if (flexible) if (flexible)
tcc_error("flexible array member '%s' not at the end of struct", tcc_error("flexible array member '%s' not at the end of struct",
get_tok_str(v, NULL)); get_tok_str(v, NULL));
@ -3399,16 +3488,17 @@ static void struct_decl(CType *type, AttributeDef *ad, int u)
get_tok_str(v, NULL)); get_tok_str(v, NULL));
} }
size = type_size(&type1, &align); size = type_size(&type1, &align);
/* Only remember non-default alignment. */
alignoverride = 0;
if (ad1.a.aligned) { if (ad1.a.aligned) {
if (align < ad1.a.aligned) if (align < ad1.a.aligned)
align = ad1.a.aligned; alignoverride = ad1.a.aligned;
} else if (ad1.a.packed || ad->a.packed) { } else if (ad1.a.packed || ad->a.packed) {
align = 1; alignoverride = 1;
} else if (*tcc_state->pack_stack_ptr) { } else if (*tcc_state->pack_stack_ptr) {
if (align > *tcc_state->pack_stack_ptr) if (align > *tcc_state->pack_stack_ptr)
align = *tcc_state->pack_stack_ptr; alignoverride = *tcc_state->pack_stack_ptr;
} }
lbit_pos = 0;
if (bit_size >= 0) { if (bit_size >= 0) {
bt = type1.t & VT_BTYPE; bt = type1.t & VT_BTYPE;
if (bt != VT_INT && if (bt != VT_INT &&
@ -3437,96 +3527,42 @@ static void struct_decl(CType *type, AttributeDef *ad, int u)
if ((bit_pos + bit_size) > bsize || if ((bit_pos + bit_size) > bsize ||
bt != prevbt || a == TOK_UNION) bt != prevbt || a == TOK_UNION)
bit_pos = 0; bit_pos = 0;
lbit_pos = bit_pos;
/* XXX: handle LSB first */ /* XXX: handle LSB first */
type1.t |= VT_BITFIELD | type1.t |= VT_BITFIELD |
(bit_pos << VT_STRUCT_SHIFT) | (bit_pos << VT_STRUCT_SHIFT) |
(bit_size << (VT_STRUCT_SHIFT + 6)); (bit_size << (VT_STRUCT_SHIFT + 6));
bit_pos += bit_size;
/* without ms-bitfields, allocate the /* without ms-bitfields, allocate the
* minimum number of bytes necessary, * minimum number of bytes necessary,
* adding single bytes as needed */ * adding single bytes as needed */
if (!tcc_state->ms_bitfields) { if (!tcc_state->ms_bitfields) {
if (lbit_pos == 0) if (bit_pos == 0)
/* minimum bytes for new bitfield */ /* minimum bytes for new bitfield */
size = (bit_size + 7) / 8; size = (bit_size + 7) / 8;
else { else {
/* enough spare bits already allocated? */ /* enough spare bits already allocated? */
bit_size = (lbit_pos - 1) % 8 + 1 + bit_size; int add_size = (bit_pos - 1) % 8 + 1 + bit_size;
if (bit_size > 8) /* doesn't fit */ if (add_size > 8) /* doesn't fit */
extra_bytes = (bit_size - 1) / 8; extra_bytes = (add_size - 1) / 8;
} }
} }
bit_pos += bit_size;
} }
prevbt = bt; prevbt = bt;
} else { } else {
bit_pos = 0; bit_pos = 0;
} }
if (v != 0 || (type1.t & VT_BTYPE) == VT_STRUCT) { if (v != 0 || (type1.t & VT_BTYPE) == VT_STRUCT) {
/* add new memory data only if starting bit /* Remember we've seen a real field to check
field or adding bytes to existing bit field */ for placement of flexible array member. */
if (extra_bytes) c += extra_bytes; c = 1;
else if (lbit_pos == 0) {
if (a == TOK_STRUCT) {
c = (c + align - 1) & -align;
offset = c;
if (size > 0)
c += size;
} else {
offset = 0;
if (size > c)
c = size;
}
if (align > maxalign)
maxalign = align;
}
#if 0
printf("add field %s offset=%d",
get_tok_str(v, NULL), offset);
if (type1.t & VT_BITFIELD) {
printf(" pos=%d size=%d",
(type1.t >> VT_STRUCT_SHIFT) & 0x3f,
(type1.t >> (VT_STRUCT_SHIFT + 6)) & 0x3f);
}
printf("\n");
#endif
} }
if (v == 0 && (type1.t & VT_BTYPE) == VT_STRUCT) { if (v == 0 && (type1.t & VT_BTYPE) == VT_STRUCT) {
/* An anonymous struct/union. Adjust member offsets /* See struct_layout for special casing
to reflect the real offset of our containing struct. anonymous member of struct type. */
Also set the offset of this anon member inside
the outer struct to be zero. Via this it
works when accessing the field offset directly
(from base object), as well as when recursing
members in initializer handling. */
int v2 = btype.ref->v;
if (!(v2 & SYM_FIELD) &&
(v2 & ~SYM_STRUCT) < SYM_FIRST_ANOM) {
Sym **pps;
/* This happens only with MS extensions. The
anon member has a named struct type, so it
potentially is shared with other references.
We need to unshare members so we can modify
them. */
ass = type1.ref;
type1.ref = sym_push(anon_sym++ | SYM_FIELD,
&type1.ref->type, 0,
type1.ref->c);
pps = &type1.ref->next;
while ((ass = ass->next) != NULL) {
*pps = sym_push(ass->v, &ass->type, 0, ass->c);
pps = &((*pps)->next);
}
*pps = NULL;
}
ass = type1.ref;
while ((ass = ass->next) != NULL)
ass->c += offset;
offset = 0;
v = anon_sym++; v = anon_sym++;
} }
if (v) { if (v) {
ss = sym_push(v | SYM_FIELD, &type1, 0, offset); ss = sym_push(v | SYM_FIELD, &type1, alignoverride, extra_bytes);
*ps = ss; *ps = ss;
ps = &ss->next; ps = &ss->next;
} }
@ -3537,9 +3573,9 @@ static void struct_decl(CType *type, AttributeDef *ad, int u)
skip(';'); skip(';');
} }
skip('}'); skip('}');
/* store size and alignment */ if (tok == TOK_ATTRIBUTE1 || tok == TOK_ATTRIBUTE2)
s->c = (c + maxalign - 1) & -maxalign; parse_attribute(ad);
s->r = maxalign; struct_layout(type, ad);
} }
} }
} }

View File

@ -3539,7 +3539,6 @@ typedef struct __attribute__((__packed__)) {
int c; int c;
} Spacked2; } Spacked2;
Spacked2 spacked2; Spacked2 spacked2;
#ifdef BROKEN
/* This doesn't work for now. Requires adjusting field offsets/sizes /* This doesn't work for now. Requires adjusting field offsets/sizes
after parsing the struct members. */ after parsing the struct members. */
typedef struct Spacked3_s { typedef struct Spacked3_s {
@ -3548,14 +3547,11 @@ typedef struct Spacked3_s {
int c; int c;
} __attribute__((__packed__)) Spacked3; } __attribute__((__packed__)) Spacked3;
Spacked3 spacked3; Spacked3 spacked3;
#endif
void attrib_test(void) void attrib_test(void)
{ {
printf("attr: %d %d %d %d\n", sizeof(struct Spacked), printf("attr: %d %d %d %d\n", sizeof(struct Spacked),
sizeof(spacked), sizeof(Spacked2), sizeof(spacked2)); sizeof(spacked), sizeof(Spacked2), sizeof(spacked2));
#ifdef BROKEN
printf("attr: %d %d\n", sizeof(Spacked3), sizeof(spacked3)); printf("attr: %d %d\n", sizeof(Spacked3), sizeof(spacked3));
#endif
} }
extern __attribute__((__unused__)) char * __attribute__((__unused__)) * extern __attribute__((__unused__)) char * __attribute__((__unused__)) *
strange_attrib_placement (void); strange_attrib_placement (void);