Hi! As described in the PR, in some cases it is unsafe for make_compound_operation, if called with in_code == COMPARE, to pass through that value to make_compound_operation on the SUBREG_REG of a SUBREG.
My understanding is that in_code == COMPARE (as opposed to in_code == SET) is mostly harmless, just tells make_extraction to no longer special case zero extraction at position 0, but there is one exception - AND with constant power of two CONST_INT. If we have make_compound_operation ( (subreg:SI (and:DI (reg:DI) (const_int 0x800000000)) 0), COMPARE) then make_compound_operation ( (and:DI (reg:DI) (const_int 0x800000000)), COMPARE) returns extraction of the 35th bit, and subreg of that is again either zero or one, but the original subreg is always 0. Fixed by passing through SET instead of in_code to the recursive invocation, if 1) the subreg isn't lowpart 2) nested SUBREGs (should be usually simplified, but just in case it hasn't been yet) 3) if subreg's operand is AND with power of two CONST_INT above the bitsize of the outer mode Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk/4.8? 2013-05-02 Jakub Jelinek <ja...@redhat.com> PR rtl-optimization/57130 * combine.c (make_compound_operation): For SUBREG, pass SET instead of COMPARE as in_code to the recursive call if needed. * gcc.c-torture/execute/pr57130.c: New test. --- gcc/combine.c.jj 2013-04-11 09:09:39.000000000 +0200 +++ gcc/combine.c 2013-05-02 12:46:07.540196281 +0200 @@ -7697,8 +7697,24 @@ make_compound_operation (rtx x, enum rtx what it originally did, do this SUBREG as a force_to_mode. */ { rtx inner = SUBREG_REG (x), simplified; - - tem = make_compound_operation (inner, in_code); + enum rtx_code subreg_code = in_code; + + /* If in_code is COMPARE, it isn't always safe to pass it through + to the recursive make_compound_operation call. */ + if (subreg_code == COMPARE + && (!subreg_lowpart_p (x) + || GET_CODE (inner) == SUBREG + /* (subreg:SI (and:DI (reg:DI) (const_int 0x800000000)) 0) + is (const_int 0), rather than + (subreg:SI (lshiftrt:DI (reg:DI) (const_int 35)) 0). */ + || (GET_CODE (inner) == AND + && CONST_INT_P (XEXP (inner, 1)) + && GET_MODE_SIZE (mode) < GET_MODE_SIZE (GET_MODE (inner)) + && exact_log2 (UINTVAL (XEXP (inner, 1))) + >= GET_MODE_BITSIZE (mode)))) + subreg_code = SET; + + tem = make_compound_operation (inner, subreg_code); simplified = simplify_subreg (mode, tem, GET_MODE (inner), SUBREG_BYTE (x)); --- gcc/testsuite/gcc.c-torture/execute/pr57130.c.jj 2013-05-02 10:52:00.389263977 +0200 +++ gcc/testsuite/gcc.c-torture/execute/pr57130.c 2013-05-02 10:51:45.000000000 +0200 @@ -0,0 +1,21 @@ +/* PR rtl-optimization/57130 */ + +struct S { int a, b, c, d; } s[2] = { { 6, 8, -8, -5 }, { 0, 2, -1, 2 } }; + +__attribute__((noinline, noclone)) void +foo (struct S r) +{ + static int cnt; + if (__builtin_memcmp (&r, &s[cnt++], sizeof r) != 0) + __builtin_abort (); +} + +int +main () +{ + struct S r = { 6, 8, -8, -5 }; + foo (r); + r = (struct S) { 0, 2, -1, 2 }; + foo (r); + return 0; +} Jakub