diff --git a/tccgen.c b/tccgen.c index b6cae5d..b4bbc1b 100644 --- a/tccgen.c +++ b/tccgen.c @@ -3250,13 +3250,104 @@ static Sym * find_field (CType *type, int v) 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 */ 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; - int bit_size, bit_pos, bsize, bt, lbit_pos, prevbt; - Sym *s, *ss, *ass, **ps; + int bit_size, bit_pos, bsize, bt, prevbt; + Sym *s, *ss, **ps; AttributeDef ad1; 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); skip('}'); } else { - maxalign = 1; ps = &s->next; prevbt = VT_INT; bit_pos = 0; - offset = 0; flexible = 0; while (tok != '}') { if (!parse_btype(&btype, &ad1)) { @@ -3355,7 +3444,7 @@ static void struct_decl(CType *type, AttributeDef *ad, int u) continue; } while (1) { - extra_bytes = 0; + extra_bytes = 0; if (flexible) tcc_error("flexible array member '%s' not at the end of struct", get_tok_str(v, NULL)); @@ -3399,16 +3488,17 @@ static void struct_decl(CType *type, AttributeDef *ad, int u) get_tok_str(v, NULL)); } size = type_size(&type1, &align); + /* Only remember non-default alignment. */ + alignoverride = 0; if (ad1.a.aligned) { if (align < ad1.a.aligned) - align = ad1.a.aligned; + alignoverride = ad1.a.aligned; } else if (ad1.a.packed || ad->a.packed) { - align = 1; + alignoverride = 1; } else if (*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) { bt = type1.t & VT_BTYPE; if (bt != VT_INT && @@ -3437,96 +3527,42 @@ static void struct_decl(CType *type, AttributeDef *ad, int u) if ((bit_pos + bit_size) > bsize || bt != prevbt || a == TOK_UNION) bit_pos = 0; - lbit_pos = bit_pos; /* XXX: handle LSB first */ type1.t |= VT_BITFIELD | (bit_pos << VT_STRUCT_SHIFT) | (bit_size << (VT_STRUCT_SHIFT + 6)); - bit_pos += bit_size; /* without ms-bitfields, allocate the * minimum number of bytes necessary, * adding single bytes as needed */ if (!tcc_state->ms_bitfields) { - if (lbit_pos == 0) + if (bit_pos == 0) /* minimum bytes for new bitfield */ size = (bit_size + 7) / 8; else { /* enough spare bits already allocated? */ - bit_size = (lbit_pos - 1) % 8 + 1 + bit_size; - if (bit_size > 8) /* doesn't fit */ - extra_bytes = (bit_size - 1) / 8; + int add_size = (bit_pos - 1) % 8 + 1 + bit_size; + if (add_size > 8) /* doesn't fit */ + extra_bytes = (add_size - 1) / 8; } } + bit_pos += bit_size; } prevbt = bt; } else { bit_pos = 0; } if (v != 0 || (type1.t & VT_BTYPE) == VT_STRUCT) { - /* add new memory data only if starting bit - field or adding bytes to existing bit field */ - if (extra_bytes) c += extra_bytes; - 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 + /* Remember we've seen a real field to check + for placement of flexible array member. */ + c = 1; } if (v == 0 && (type1.t & VT_BTYPE) == VT_STRUCT) { - /* 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 = 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; + /* See struct_layout for special casing + anonymous member of struct type. */ v = anon_sym++; } 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->next; } @@ -3537,9 +3573,9 @@ static void struct_decl(CType *type, AttributeDef *ad, int u) skip(';'); } skip('}'); - /* store size and alignment */ - s->c = (c + maxalign - 1) & -maxalign; - s->r = maxalign; + if (tok == TOK_ATTRIBUTE1 || tok == TOK_ATTRIBUTE2) + parse_attribute(ad); + struct_layout(type, ad); } } } diff --git a/tests/tcctest.c b/tests/tcctest.c index 2249cb4..d1b7cb0 100644 --- a/tests/tcctest.c +++ b/tests/tcctest.c @@ -3539,7 +3539,6 @@ typedef struct __attribute__((__packed__)) { int c; } Spacked2; Spacked2 spacked2; -#ifdef BROKEN /* This doesn't work for now. Requires adjusting field offsets/sizes after parsing the struct members. */ typedef struct Spacked3_s { @@ -3548,14 +3547,11 @@ typedef struct Spacked3_s { int c; } __attribute__((__packed__)) Spacked3; Spacked3 spacked3; -#endif void attrib_test(void) { printf("attr: %d %d %d %d\n", sizeof(struct Spacked), sizeof(spacked), sizeof(Spacked2), sizeof(spacked2)); -#ifdef BROKEN printf("attr: %d %d\n", sizeof(Spacked3), sizeof(spacked3)); -#endif } extern __attribute__((__unused__)) char * __attribute__((__unused__)) * strange_attrib_placement (void);