https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84184
Ulya changed:
What|Removed |Added
CC||skvadrik at gmail dot com
--- Comment #12 from Ulya ---
(In reply to Eric Botcazou from comment #11)
> > Why those are handled differently? First looks like it works, second does
> > not. It was my main signal to file a bug against gcc as asymmetry looked
> > fishy.
>
> Because the problematic bitfield path is only used for fields in structures,
> i.e. misaligned integers are handled by another, simpler path.
More details on the problematic path for this simple example:
extern char __some_table[] __attribute__((visibility("hidden")));
struct s { long v; };
long end(void) { return ((struct s *)__some_table)[-1].v; }
1. The problematic path makes an illegal signed-to-unsigned integer conversion
in expand_expr_real_1 (expr.c) when passing the signed 'bitnum' variable, with
value -64, as the 3rd param of extract_bit_field (expmed.c), with value
18446744073709551552.
2. The conversion itself doesn't spoil the value (meaning that the bits of
'bitnum' are not changed), and the value is passed on to extract_bit_field_1,
extract_integral_bit_field and extract_fixed_bit_field unharmed.
3. Finally, 'bitnum' is passed as the 5th param 'bitpos' to
extract_split_bit_field, where it gets involved in unsigned integer arithmetics
and bad things start to happen:
static rtx
extract_split_bit_field (rtx op0, opt_scalar_int_mode op0_mode,
unsigned HOST_WIDE_INT bitsize,
unsigned HOST_WIDE_INT bitpos, int unsignedp,
bool reverse)
{
unsigned int unit;
unsigned int bitsdone = 0;
// ...
while (bitsdone < bitsize)
{
unsigned HOST_WIDE_INT thissize;
rtx part;
unsigned HOST_WIDE_INT thispos;
unsigned HOST_WIDE_INT offset;
offset = (bitpos + bitsdone) / unit; // <=== BAD THING 1
thispos = (bitpos + bitsdone) % unit; // <=== BAD THING 2
// ...
Regardless of whether GCC wants to handle this example or not, implicit
signed-to-unsigned conversion looks wrong to me. Even an assertion failure from
the compiler is better than this silent code corruption. Furthermore, can we
even guess all the possible cases when the problematic path is taken?
Full backtrace:
Breakpoint 15, extract_split_bit_field (op0=0x76cb7c60, op0_mode=...,
bitsize=64, bitpos=18446744073709551552, unsignedp=0, reverse=false)
at ../../gcc/gcc/expmed.c:2266
2266 thispos = (bitpos + bitsdone) % unit;
(gdb) bt
#0 extract_split_bit_field (op0=0x76cb7c60, op0_mode=..., bitsize=64,
bitpos=18446744073709551552, unsignedp=0, reverse=false) at
../../gcc/gcc/expmed.c:2266
#1 0x00aafff7 in extract_fixed_bit_field (tmode=E_DImode,
op0=0x76cb7c60, op0_mode=..., bitsize=64, bitnum=18446744073709551552,
target=0x76cb7bb8,
unsignedp=0, reverse=false) at ../../gcc/gcc/expmed.c:2125
#2 0x00aaf793 in extract_integral_bit_field (op0=0x76cb7c60,
op0_mode=..., bitsize=64, bitnum=18446744073709551552, unsignedp=0,
target=0x76cb7bb8,
mode=E_DImode, tmode=E_DImode, reverse=false, fallback_p=true) at
../../gcc/gcc/expmed.c:2016
#3 0x00aaeb74 in extract_bit_field_1 (str_rtx=0x76cb7c60,
bitsize=..., bitnum=..., unsignedp=0, target=0x76cb7bb8, mode=E_DImode,
tmode=E_DImode,
reverse=false, fallback_p=true, alt_rtl=0x0) at ../../gcc/gcc/expmed.c:1827
#4 0x00aafe8c in extract_bit_field (str_rtx=0x76cb7c60,
bitsize=..., bitnum=..., unsignedp=0, target=0x76cb7bb8, mode=E_DImode,
tmode=E_DImode,
reverse=false, alt_rtl=0x0) at ../../gcc/gcc/expmed.c:2096
#5 0x00aecaff in expand_expr_real_1 (exp=0x76ca6840,
target=0x76cb7bb8, tmode=E_DImode, modifier=EXPAND_NORMAL, alt_rtl=0x0,
inner_reference_p=false)
at ../../gcc/gcc/expr.c:10777
#6 0x0