Date: Wed, 23 Sep 2009 22:39:45 +0200 From: Mikael Pettersson Subject: [PATCH] proposed fix for PR40987 if-conversion bug on HWI32 hosts List-Archive: This patch addresses PR rtl-optimization/40987, a wrong-code error in ifcvt.c (noce_try_store_flag_constants) on 32-bit HOST_WIDE_INT hosts. noce_try_store_flag_constants() tries to optimize "if (test) x = a; else x = b;" where a and b are constants. The problem is that it performs various compile-time computations on those CONST_INT values without first checking if they are fully representable in HOST_WIDE_INT integers. When that is not the case, it computes on truncated values resulting in incorrect code being generated. The problem is known to affect i386. It can be triggered with gcc-4.5 by compiling with -O1. With gcc-4.4 and gcc-4.3 it also requires a few -fno- options to disable optimizations that appear to mask it. It also occurs with older gcc-4.x releases with plain -O1, but gcc-3.4 appears immune to the problem. This proposed fix compares GET_MODE_BITSIZE of the mode of the destination variable with HOST_BITS_PER_WIDE_INT to see if compile-time arithmetic may be inadequate, and in that case performs an early failure return from the function. Bootstrapped and regtested on {i686,x86_64}-linux. 4.4 and 4.3 backports (with adjusted dg-options in the test case) bootstrapped and regtested on {i686,powerpc64}-linux. Ok for 4.5? (I don't have write access.) gcc/ 2009-09-23 Mikael Pettersson PR rtl-optimization/40987 * ifcvt.c (noce_try_store_flag_constants): Skip it if the integer constants are too wide for host arithmetic. gcc/testsuite/ 2009-09-23 Mikael Pettersson PR rtl-optimization/40987 * gcc.dg/torture/pr40987.c: New test. --- gcc-4.6.1/gcc/ifcvt.c.~1~ 2010-12-14 01:23:40.000000000 +0100 +++ gcc-4.6.1/gcc/ifcvt.c 2011-08-02 21:51:08.000000000 +0200 @@ -1075,6 +1075,9 @@ noce_try_store_flag_constants (struct no ifalse = INTVAL (if_info->a); itrue = INTVAL (if_info->b); + if (GET_MODE_BITSIZE (mode) > HOST_BITS_PER_WIDE_INT) + return FALSE; + /* Make sure we can represent the difference between the two values. */ if ((itrue - ifalse > 0) != ((ifalse < 0) != (itrue < 0) ? ifalse < 0 : ifalse < itrue)) --- gcc-4.6.1/gcc/testsuite/gcc.dg/torture/pr40987.c.~1~ 1970-01-01 01:00:00.000000000 +0100 +++ gcc-4.6.1/gcc/testsuite/gcc.dg/torture/pr40987.c 2011-08-02 21:51:08.000000000 +0200 @@ -0,0 +1,20 @@ +/* PR rtl-optimization/40987 */ +/* { do-do run } */ +/* { dg-options "-O1" } */ + +long long __attribute__((noinline)) func(long arg) +{ + long long val = 0; + if (arg < 0) + val = 0xffffffff80000000ull; + return val; +} + +extern void abort (void); +int main (void) +{ + long long result = func(-1); + if (result != 0xffffffff80000000ull) + abort (); + return 0; +}