Hi! If inner is a MEM, make_extraction requires that pos is a multiple of bytes and deals with offsetting it. Or otherwise requires that pos is a multiple of BITS_PER_WORD and for REG inner it handles that too. But if inner is something different, it calls just force_to_mode to the target mode, which only really works if pos is 0.
Thus the following patch restricts it to that case. Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2016-11-16 Jakub Jelinek <ja...@redhat.com> PR rtl-optimization/78378 * combine.c (make_extraction): Use force_to_mode for non-{REG,MEM} inner only if pos is 0. Fix up formatting. * gcc.c-torture/execute/pr78378.c: New test. --- gcc/combine.c.jj 2016-11-16 18:51:58.000000000 +0100 +++ gcc/combine.c 2016-11-16 19:52:12.735674000 +0100 @@ -7382,6 +7382,7 @@ make_extraction (machine_mode mode, rtx if (tmode != BLKmode && ((pos_rtx == 0 && (pos % BITS_PER_WORD) == 0 && !MEM_P (inner) + && (pos == 0 || REG_P (inner)) && (inner_mode == tmode || !REG_P (inner) || TRULY_NOOP_TRUNCATION_MODES_P (tmode, inner_mode) @@ -7458,10 +7459,9 @@ make_extraction (machine_mode mode, rtx } else new_rtx = force_to_mode (inner, tmode, - len >= HOST_BITS_PER_WIDE_INT - ? HOST_WIDE_INT_M1U - : (HOST_WIDE_INT_1U << len) - 1, - 0); + len >= HOST_BITS_PER_WIDE_INT + ? HOST_WIDE_INT_M1U + : (HOST_WIDE_INT_1U << len) - 1, 0); /* If this extraction is going into the destination of a SET, make a STRICT_LOW_PART unless we made a MEM. */ --- gcc/testsuite/gcc.c-torture/execute/pr78378.c.jj 2016-11-16 20:02:16.975248597 +0100 +++ gcc/testsuite/gcc.c-torture/execute/pr78378.c 2016-11-16 20:02:03.000000000 +0100 @@ -0,0 +1,18 @@ +/* PR rtl-optimization/78378 */ + +unsigned long long __attribute__ ((noinline, noclone)) +foo (unsigned long long x) +{ + x <<= 41; + x /= 232; + return 1 + (unsigned short) x; +} + +int +main () +{ + unsigned long long x = foo (1); + if (x != 0x2c24) + __builtin_abort(); + return 0; +} Jakub