Hi! As this testcase shows, shorten_compare misbehaves if *restype_ptr is wider than one of the operands and get_narrower returns something even narrower than opN. If primopN is sign-extended to opN, but opN is unsigned, unsignedpN is zero, but the mixed extension behaves differently from sign-extension only. This patch fixes it by ignoring what get_narrower did in that case. For first zero-extending to opN and then sign-extending this isn't a problem, as zero-extension means the sign-extension will fill bits with zero anyway.
Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk/4.6.1? 2011-03-20 Jakub Jelinek <ja...@redhat.com> PR c/48197 * c-common.c (shorten_compare): If primopN is first sign-extended to opN and then zero-extended to result type, set primopN to opN. * gcc.c-torture/execute/pr48197.c: New test. --- gcc/c-family/c-common.c.jj 2011-03-16 18:29:52.000000000 +0100 +++ gcc/c-family/c-common.c 2011-03-19 15:52:10.000000000 +0100 @@ -3303,6 +3303,20 @@ shorten_compare (tree *op0_ptr, tree *op primop0 = get_narrower (op0, &unsignedp0); primop1 = get_narrower (op1, &unsignedp1); + /* If primopN is first sign-extended from primopN's precision to opN's + precision, then zero-extended from opN's precision to + *restype_ptr precision, shortenings might be invalid. */ + if (TYPE_PRECISION (TREE_TYPE (primop0)) < TYPE_PRECISION (TREE_TYPE (op0)) + && TYPE_PRECISION (TREE_TYPE (op0)) < TYPE_PRECISION (*restype_ptr) + && !unsignedp0 + && TYPE_UNSIGNED (TREE_TYPE (op0))) + primop0 = op0; + if (TYPE_PRECISION (TREE_TYPE (primop1)) < TYPE_PRECISION (TREE_TYPE (op1)) + && TYPE_PRECISION (TREE_TYPE (op1)) < TYPE_PRECISION (*restype_ptr) + && !unsignedp1 + && TYPE_UNSIGNED (TREE_TYPE (op1))) + primop1 = op1; + /* Handle the case that OP0 does not *contain* a conversion but it *requires* conversion to FINAL_TYPE. */ --- gcc/testsuite/gcc.c-torture/execute/pr48197.c.jj 2011-03-19 14:11:53.000000000 +0100 +++ gcc/testsuite/gcc.c-torture/execute/pr48197.c 2011-03-19 14:11:05.000000000 +0100 @@ -0,0 +1,25 @@ +/* PR c/48197 */ + +extern void abort (void); +static int y = 0x8000; + +int +main () +{ + unsigned int x = (short)y; + if (sizeof (0LL) == sizeof (0U)) + return 0; + if (0LL > (0U ^ (short)-0x8000)) + abort (); + if (0LL > (0U ^ x)) + abort (); + if (0LL > (0U ^ (short)y)) + abort (); + if ((0U ^ (short)-0x8000) < 0LL) + abort (); + if ((0U ^ x) < 0LL) + abort (); + if ((0U ^ (short)y) < 0LL) + abort (); + return 0; +} Jakub