diff --git a/libtcc.c b/libtcc.c index f184502..b824c18 100644 --- a/libtcc.c +++ b/libtcc.c @@ -752,6 +752,7 @@ LIBTCCAPI TCCState *tcc_new(void) s->alacarte_link = 1; s->nocommon = 1; s->warn_implicit_function_declaration = 1; + s->ms_bitfields = 0; #ifdef CHAR_IS_UNSIGNED s->char_is_unsigned = 1; @@ -1508,6 +1509,7 @@ enum { TCC_OPTION_Wl, TCC_OPTION_W, TCC_OPTION_O, + TCC_OPTION_mms_bitfields, TCC_OPTION_m, TCC_OPTION_f, TCC_OPTION_isystem, @@ -1571,6 +1573,7 @@ static const TCCOption tcc_options[] = { { "Wl,", TCC_OPTION_Wl, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, { "W", TCC_OPTION_W, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, { "O", TCC_OPTION_O, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, + { "mms-bitfields", TCC_OPTION_mms_bitfields, 0}, /* must go before option 'm' */ { "m", TCC_OPTION_m, TCC_OPTION_HAS_ARG }, { "f", TCC_OPTION_f, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, { "isystem", TCC_OPTION_isystem, TCC_OPTION_HAS_ARG }, @@ -1854,6 +1857,9 @@ PUB_FUNC int tcc_parse_args(TCCState *s, int argc, char **argv) if (x > 0) tcc_define_symbol(s, "__OPTIMIZE__", NULL); break; + case TCC_OPTION_mms_bitfields: + s->ms_bitfields = 1; + break; case TCC_OPTION_traditional: case TCC_OPTION_pedantic: case TCC_OPTION_pipe: diff --git a/tcc-doc.texi b/tcc-doc.texi index 5aab13c..9f136ed 100644 --- a/tcc-doc.texi +++ b/tcc-doc.texi @@ -176,6 +176,10 @@ In a script, it gives the following header: #!/usr/local/bin/tcc -run -L/usr/X11R6/lib -lX11 @end example +@item -mms-bitfields +Use an algorithm for bitfield alignment consistent with MSVC. Default is +gcc's algorithm. + @item -mfloat-abi (ARM only) Select the float ABI. Possible values: @code{softfp} and @code{hard} diff --git a/tcc.c b/tcc.c index 28f3ae9..fbfbffa 100644 --- a/tcc.c +++ b/tcc.c @@ -97,6 +97,7 @@ static void help(void) " -o outfile set output filename\n" " -run run compiled source\n" " -fflag set or reset (with 'no-' prefix) 'flag' (see man page)\n" + " -mms-bitfields use bitfield alignment consistent with MSVC\n" " -Wwarning set or reset (with 'no-' prefix) 'warning' (see man page)\n" " -w disable all warnings\n" " -v show version\n" diff --git a/tcc.h b/tcc.h index 180838d..cbdebd3 100644 --- a/tcc.h +++ b/tcc.h @@ -605,6 +605,7 @@ struct TCCState { int old_struct_init_code; /* use old algorithm to init array in struct when there is no '{' used. Liuux 2.4.26 can't find initrd when compiled with a new algorithm */ int dollars_in_identifiers; /* allows '$' char in indentifiers */ + int ms_bitfields; /* if true, emulate MS algorithm for aligning bitfields */ /* warning switches */ int warn_write_strings; diff --git a/tccgen.c b/tccgen.c index ba1757e..d064024 100644 --- a/tccgen.c +++ b/tccgen.c @@ -3154,7 +3154,7 @@ static void parse_attribute(AttributeDef *ad) /* 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, c, offset, flexible; + int a, v, size, align, maxalign, c, offset, flexible, extra_bytes; int bit_size, bit_pos, bsize, bt, lbit_pos, prevbt; Sym *s, *ss, *ass, **ps; AttributeDef ad1; @@ -3235,6 +3235,7 @@ static void struct_decl(CType *type, AttributeDef *ad, int u) while (tok != '}') { parse_btype(&btype, &ad1); while (1) { + extra_bytes = 0; if (flexible) tcc_error("flexible array member '%s' not at the end of struct", get_tok_str(v, NULL)); @@ -3310,9 +3311,9 @@ static void struct_decl(CType *type, AttributeDef *ad, int u) /* zero size: means to pad */ bit_pos = 0; } else { - /* we do not have enough room ? - did the type change? - is it a union? */ + /* if type change, union, or will overrun + * allignment slot, start at a newly + * alligned slot */ if ((bit_pos + bit_size) > bsize || bt != prevbt || a == TOK_UNION) bit_pos = 0; @@ -3322,15 +3323,30 @@ static void struct_decl(CType *type, AttributeDef *ad, int u) (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) + /* 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; + } + } } prevbt = bt; } else { bit_pos = 0; } if (v != 0 || (type1.t & VT_BTYPE) == VT_STRUCT) { - /* add new memory data only if starting - bit field */ - if (lbit_pos == 0) { + /* 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; diff --git a/tests/tcctest.c b/tests/tcctest.c index 5851fb4..b0fe8f3 100644 --- a/tests/tcctest.c +++ b/tests/tcctest.c @@ -1634,6 +1634,15 @@ void bitfield_test(void) st2.f3 = a; st2.f2++; printf("%lld %lld %lld\n", st2.f1, st2.f2, st2.f3); + struct sbf3 { + int f1 : 7; + int f2 : 1; + char f3; + int f4 : 8; + int f5 : 1; + int f6 : 16; + } st3; + printf("sizeof(st3) = %d\n", sizeof(st3)); } #ifdef __x86_64__