Hi! When we have (x >> y) & 0x1fff or similar (for non-constant y or even for constant y if y + 13 is bigger than x's bits) and x is a non-paradoxical lowpart subreg (in the testcase (subreg:SI (reg:DI ...) 0)) then the lshiftrt extracts some bits out (0 to 13) out of the wider DImode registers starting at y, but the upper bits if should be zeroed out. make_extraction happily changes it into an extraction out of the reg:DI directly and that (at least for initially valid y 0 to 31) will always extract exactly 13 bits out of the register; if there are any bits above the low SImode part that are non-zero, this results in different behavior.
The following patch stops doing that unless we can prove we don't care about any of the bits above it. Bootstrapped/regtested on x86_64-linux and i686-linux, extra statistics collection didn't reveal changes in combiner's total_* vars at the end of compilations except for this new testcase and combine.c itself. Preapproved by Segher in the PR, committed to trunk. For possible backports to release branches I'd like to wait some time. 2017-09-15 Jakub Jelinek <ja...@redhat.com> PR rtl-optimization/82192 * combine.c (make_extraction): Don't look through non-paradoxical SUBREGs or TRUNCATE if pos + len is or might be bigger than inner's mode. * gcc.c-torture/execute/pr82192.c: New test. --- gcc/combine.c.jj 2017-09-14 10:04:56.000000000 +0200 +++ gcc/combine.c 2017-09-14 16:59:28.529783572 +0200 @@ -7444,7 +7444,14 @@ make_extraction (machine_mode mode, rtx if (pos_rtx && CONST_INT_P (pos_rtx)) pos = INTVAL (pos_rtx), pos_rtx = 0; - if (GET_CODE (inner) == SUBREG && subreg_lowpart_p (inner)) + if (GET_CODE (inner) == SUBREG + && subreg_lowpart_p (inner) + && (paradoxical_subreg_p (inner) + /* If trying or potentionally trying to extract + bits outside of is_mode, don't look through + non-paradoxical SUBREGs. See PR82192. */ + || (pos_rtx == NULL_RTX + && pos + len <= GET_MODE_PRECISION (is_mode)))) { /* If going from (subreg:SI (mem:QI ...)) to (mem:QI ...), consider just the QI as the memory to extract from. @@ -7470,7 +7477,12 @@ make_extraction (machine_mode mode, rtx if (new_rtx != 0) return gen_rtx_ASHIFT (mode, new_rtx, XEXP (inner, 1)); } - else if (GET_CODE (inner) == TRUNCATE) + else if (GET_CODE (inner) == TRUNCATE + /* If trying or potentionally trying to extract + bits outside of is_mode, don't look through + TRUNCATE. See PR82192. */ + && pos_rtx == NULL_RTX + && pos + len <= GET_MODE_PRECISION (is_mode)) inner = XEXP (inner, 0); inner_mode = GET_MODE (inner); --- gcc/testsuite/gcc.c-torture/execute/pr82192.c.jj 2017-09-14 17:02:54.281234432 +0200 +++ gcc/testsuite/gcc.c-torture/execute/pr82192.c 2017-09-14 17:02:39.000000000 +0200 @@ -0,0 +1,22 @@ +/* PR rtl-optimization/82192 */ + +unsigned long long int a = 0x95dd3d896f7422e2ULL; +struct S { unsigned int m : 13; } b; + +__attribute__((noinline, noclone)) void +foo (void) +{ + b.m = ((unsigned) a) >> (0x644eee9667723bf7LL + | a & ~0xdee27af8U) - 0x644eee9667763bd8LL; +} + +int +main () +{ + if (__INT_MAX__ != 0x7fffffffULL) + return 0; + foo (); + if (b.m != 0) + __builtin_abort (); + return 0; +} Jakub