Hi! When val_so_far is signed, we can end up in UB in various places, e.g. for the multiplication by 0x7fffffffffffffff, which is done as << 63 shift followed by subtracting one.
Fixed by computing this in UHWI instead, and only cast at the end to SHWI. Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2016-08-16 Jakub Jelinek <ja...@redhat.com> PR middle-end/67485 * expmed.c (expand_mult_const): Change val_so_far's type to UHWI, only cast it to SHWI for the final comparison. * gcc.c-torture/compile/pr67485.c: New test. --- gcc/expmed.c.jj 2016-08-12 17:33:46.000000000 +0200 +++ gcc/expmed.c 2016-08-15 13:45:59.197300977 +0200 @@ -3055,7 +3055,7 @@ expand_mult_const (machine_mode mode, rt rtx target, const struct algorithm *alg, enum mult_variant variant) { - HOST_WIDE_INT val_so_far; + unsigned HOST_WIDE_INT val_so_far; rtx_insn *insn; rtx accum, tem; int opno; @@ -3105,14 +3105,14 @@ expand_mult_const (machine_mode mode, rt tem = expand_shift (LSHIFT_EXPR, mode, op0, log, NULL_RTX, 0); accum = force_operand (gen_rtx_PLUS (mode, accum, tem), add_target ? add_target : accum_target); - val_so_far += HOST_WIDE_INT_1 << log; + val_so_far += HOST_WIDE_INT_1U << log; break; case alg_sub_t_m2: tem = expand_shift (LSHIFT_EXPR, mode, op0, log, NULL_RTX, 0); accum = force_operand (gen_rtx_MINUS (mode, accum, tem), add_target ? add_target : accum_target); - val_so_far -= HOST_WIDE_INT_1 << log; + val_so_far -= HOST_WIDE_INT_1U << log; break; case alg_add_t2_m: @@ -3188,7 +3188,7 @@ expand_mult_const (machine_mode mode, rt nmode = GET_MODE_INNER (mode); val &= GET_MODE_MASK (nmode); val_so_far &= GET_MODE_MASK (nmode); - gcc_assert (val == val_so_far); + gcc_assert (val == (HOST_WIDE_INT) val_so_far); return accum; } --- gcc/testsuite/gcc.c-torture/compile/pr67485.c.jj 2016-08-15 13:48:13.747639954 +0200 +++ gcc/testsuite/gcc.c-torture/compile/pr67485.c 2016-08-15 13:48:17.113598401 +0200 @@ -0,0 +1,7 @@ +/* PR middle-end/67485 */ + +long int +foo (long int x) +{ + return x * __LONG_MAX__; +} Jakub