This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[PATCH] PR optimization/9325: Saturating FP-to-int overflow.
- From: Roger Sayle <roger at eyesopen dot com>
- To: gcc-patches at gcc dot gnu dot org
- Cc: Tom Tromey <tromey at redhat dot com>
- Date: Mon, 29 Sep 2003 20:22:31 -0600 (MDT)
- Subject: [PATCH] PR optimization/9325: Saturating FP-to-int overflow.
The following patch resolves PR optimization/9325 and PR java/6391.
Tom Tromey suggested that I investigate the latter as an example of
the middle-end not respecting java semantics, which turned out to be
identical to PR9325 which is reported against the C-like front-ends.
The issue is that the C/C++ language standards state that the behaviour
of floating-point to integer conversion is undefined, when the resulting
value doesn't fit the destination mode. The Java language specification
however requires that NaNs are transformed into zero, and that overflows
saturate, i.e. values larger than INT_MAX produce INT_MAX and values
less than INT_MIN return INT_MIN.
The solution for the middle-end is to perform constant folding using
the Java semantics, i.e. saturating overflow, as allowed by the C and
C++ language standards. Indeed PR9325 isn't actually a "bug" at all,
it invokes unspecified C/C++ behaviour that produces surprising results.
Implementing saturating overflow semantics, at compile-time atleast,
produces less surprising results.
In addition to changing the semantics it turns out that there really
were bugs in the old code. For example, (int)2147483648.0f didn't
set the TREE_OVERFLOW field correctly, as the type's upper bound was
being cast to a float (with loss of precision) prior to the range
check. Also we were handling FIX and UNSIGNED_FIX identically, and
would never simplify RTL conversions to integers wider than the host's
HOST_WIDE_INT. These are all resolved below.
Of course, we still don't require any particular *run-time* semantics
for fix:?I or unsigned_fix:?i patterns, so the behaviour of FP-to-int
overflow during a program's execution is still undefined (unless like
the Java front-end, you insert additional instructions to catch the
exceptional cases).
The following patch has been tested on i686-pc-linux-gnu with a complete
"make bootstrap", all languages except treelang, and regression tested
by a top-level "make -k check" with no new failures.
Ok for mainline?
2003-09-29 Roger Sayle <roger@eyesopen.com>
PR optimization/9325, PR java/6391
* fold-const.c (fold_convert): For floating point to integer
conversions, return the maximum/minimum representable integer
value if the real constant overflows the destination type.
* tree.c (real_value_from_int_cst): Allow the type to be NULL,
meaning don't truncate the result to a floating point mode.
Simplify the logic by calling real_from_integer directly.
* simplify-rtx.c (simplify_unary_operation): Implement the
same semantics for folding floating point to integer conversions
in RTL.
* gcc.c-torture/execute/20030929-1.c: New test case.
Index: fold-const.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/fold-const.c,v
retrieving revision 1.306
diff -c -3 -p -r1.306 fold-const.c
*** fold-const.c 25 Sep 2003 02:12:13 -0000 1.306
--- fold-const.c 29 Sep 2003 21:35:55 -0000
*************** fold_convert (tree t, tree arg1)
*** 1599,1639 ****
}
else if (TREE_CODE (arg1) == REAL_CST)
{
! /* Don't initialize these, use assignments.
! Initialized local aggregates don't work on old compilers. */
! REAL_VALUE_TYPE x;
! REAL_VALUE_TYPE l;
! REAL_VALUE_TYPE u;
! tree type1 = TREE_TYPE (arg1);
! int no_upper_bound;
!
! x = TREE_REAL_CST (arg1);
! l = real_value_from_int_cst (type1, TYPE_MIN_VALUE (type));
!
! no_upper_bound = (TYPE_MAX_VALUE (type) == NULL);
! if (!no_upper_bound)
! u = real_value_from_int_cst (type1, TYPE_MAX_VALUE (type));
/* See if X will be in range after truncation towards 0.
To compensate for truncation, move the bounds away from 0,
but reject if X exactly equals the adjusted bounds. */
- REAL_ARITHMETIC (l, MINUS_EXPR, l, dconst1);
- if (!no_upper_bound)
- REAL_ARITHMETIC (u, PLUS_EXPR, u, dconst1);
- /* If X is a NaN, use zero instead and show we have an overflow.
- Otherwise, range check. */
- if (REAL_VALUE_ISNAN (x))
- overflow = 1, x = dconst0;
- else if (! (REAL_VALUES_LESS (l, x)
- && !no_upper_bound
- && REAL_VALUES_LESS (x, u)))
- overflow = 1;
! {
! HOST_WIDE_INT low, high;
REAL_VALUE_TO_INT (&low, &high, x);
! t = build_int_2 (low, high);
! }
TREE_TYPE (t) = type;
TREE_OVERFLOW (t)
= TREE_OVERFLOW (arg1) | force_fit_type (t, overflow);
--- 1599,1652 ----
}
else if (TREE_CODE (arg1) == REAL_CST)
{
! HOST_WIDE_INT high, low;
!
! REAL_VALUE_TYPE x = TREE_REAL_CST (arg1);
! /* If x is NaN, return zero and show we have an overflow. */
! if (REAL_VALUE_ISNAN (x))
! {
! overflow = 1;
! high = 0;
! low = 0;
! }
/* See if X will be in range after truncation towards 0.
To compensate for truncation, move the bounds away from 0,
but reject if X exactly equals the adjusted bounds. */
! if (! overflow)
! {
! tree lt = TYPE_MIN_VALUE (type);
! REAL_VALUE_TYPE l = real_value_from_int_cst (NULL_TREE, lt);
! REAL_ARITHMETIC (l, MINUS_EXPR, l, dconst1);
! if (! REAL_VALUES_LESS (l, x))
! {
! overflow = 1;
! high = TREE_INT_CST_HIGH (lt);
! low = TREE_INT_CST_LOW (lt);
! }
! }
!
! if (! overflow)
! {
! tree ut = TYPE_MAX_VALUE (type);
! if (ut)
! {
! REAL_VALUE_TYPE u = real_value_from_int_cst (NULL_TREE, ut);
! REAL_ARITHMETIC (u, PLUS_EXPR, u, dconst1);
! if (! REAL_VALUES_LESS (x, u))
! {
! overflow = 1;
! high = TREE_INT_CST_HIGH (ut);
! low = TREE_INT_CST_LOW (ut);
! }
! }
! }
!
! if (! overflow)
REAL_VALUE_TO_INT (&low, &high, x);
!
! t = build_int_2 (low, high);
TREE_TYPE (t) = type;
TREE_OVERFLOW (t)
= TREE_OVERFLOW (arg1) | force_fit_type (t, overflow);
Index: tree.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/tree.c,v
retrieving revision 1.330
diff -c -3 -p -r1.330 tree.c
*** tree.c 22 Sep 2003 05:09:12 -0000 1.330
--- tree.c 29 Sep 2003 21:35:57 -0000
*************** build_real (tree type, REAL_VALUE_TYPE d
*** 488,494 ****
and whose value is the integer value of the INTEGER_CST node I. */
REAL_VALUE_TYPE
! real_value_from_int_cst (tree type ATTRIBUTE_UNUSED, tree i)
{
REAL_VALUE_TYPE d;
--- 488,494 ----
and whose value is the integer value of the INTEGER_CST node I. */
REAL_VALUE_TYPE
! real_value_from_int_cst (tree type, tree i)
{
REAL_VALUE_TYPE d;
*************** real_value_from_int_cst (tree type ATTRI
*** 496,507 ****
bitwise comparisons to see if two values are the same. */
memset (&d, 0, sizeof d);
! if (! TREE_UNSIGNED (TREE_TYPE (i)))
! REAL_VALUE_FROM_INT (d, TREE_INT_CST_LOW (i), TREE_INT_CST_HIGH (i),
! TYPE_MODE (type));
! else
! REAL_VALUE_FROM_UNSIGNED_INT (d, TREE_INT_CST_LOW (i),
! TREE_INT_CST_HIGH (i), TYPE_MODE (type));
return d;
}
--- 496,504 ----
bitwise comparisons to see if two values are the same. */
memset (&d, 0, sizeof d);
! real_from_integer (&d, type ? TYPE_MODE (type) : VOIDmode,
! TREE_INT_CST_LOW (i), TREE_INT_CST_HIGH (i),
! TREE_UNSIGNED (TREE_TYPE (i)));
return d;
}
Index: simplify-rtx.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/simplify-rtx.c,v
retrieving revision 1.160
diff -c -3 -p -r1.160 simplify-rtx.c
*** simplify-rtx.c 18 Sep 2003 19:07:04 -0000 1.160
--- simplify-rtx.c 29 Sep 2003 21:35:58 -0000
*************** simplify_unary_operation (enum rtx_code
*** 775,793 ****
else if (GET_CODE (trueop) == CONST_DOUBLE
&& GET_MODE_CLASS (GET_MODE (trueop)) == MODE_FLOAT
&& GET_MODE_CLASS (mode) == MODE_INT
! && width <= HOST_BITS_PER_WIDE_INT && width > 0)
{
! HOST_WIDE_INT i;
! REAL_VALUE_TYPE d;
! REAL_VALUE_FROM_CONST_DOUBLE (d, trueop);
switch (code)
{
! case FIX: i = REAL_VALUE_FIX (d); break;
! case UNSIGNED_FIX: i = REAL_VALUE_UNSIGNED_FIX (d); break;
default:
abort ();
}
! return gen_int_mode (i, mode);
}
/* This was formerly used only for non-IEEE float.
--- 775,868 ----
else if (GET_CODE (trueop) == CONST_DOUBLE
&& GET_MODE_CLASS (GET_MODE (trueop)) == MODE_FLOAT
&& GET_MODE_CLASS (mode) == MODE_INT
! && width <= 2*HOST_BITS_PER_WIDE_INT && width > 0)
{
! HOST_WIDE_INT xh, xl, th, tl;
! REAL_VALUE_TYPE x, t;
! REAL_VALUE_FROM_CONST_DOUBLE (x, trueop);
switch (code)
{
! case FIX:
! if (REAL_VALUE_ISNAN (x))
! return const0_rtx;
!
! /* Test against the signed upper bound. */
! if (width > HOST_BITS_PER_WIDE_INT)
! {
! th = ((unsigned HOST_WIDE_INT) 1
! << (width - HOST_BITS_PER_WIDE_INT - 1)) - 1;
! tl = -1;
! }
! else
! {
! th = 0;
! tl = ((unsigned HOST_WIDE_INT) 1 << (width - 1)) - 1;
! }
! real_from_integer (&t, VOIDmode, tl, th, 0);
! if (REAL_VALUES_LESS (t, x))
! {
! xh = th;
! xl = tl;
! break;
! }
!
! /* Test against the signed lower bound. */
! if (width > HOST_BITS_PER_WIDE_INT)
! {
! th = (HOST_WIDE_INT) -1 << (width - HOST_BITS_PER_WIDE_INT - 1);
! tl = 0;
! }
! else
! {
! th = -1;
! tl = (HOST_WIDE_INT) -1 << (width - 1);
! }
! real_from_integer (&t, VOIDmode, tl, th, 0);
! if (REAL_VALUES_LESS (x, t))
! {
! xh = th;
! xl = tl;
! break;
! }
! REAL_VALUE_TO_INT (&xl, &xh, x);
! break;
!
! case UNSIGNED_FIX:
! if (REAL_VALUE_ISNAN (x) || REAL_VALUE_NEGATIVE (x))
! return const0_rtx;
!
! /* Test against the unsigned upper bound. */
! if (width == 2*HOST_BITS_PER_WIDE_INT)
! {
! th = -1;
! tl = -1;
! }
! else if (width >= HOST_BITS_PER_WIDE_INT)
! {
! th = ((unsigned HOST_WIDE_INT) 1
! << (width - HOST_BITS_PER_WIDE_INT)) - 1;
! tl = -1;
! }
! else
! {
! th = 0;
! tl = ((unsigned HOST_WIDE_INT) 1 << width) - 1;
! }
! real_from_integer (&t, VOIDmode, tl, th, 1);
! if (REAL_VALUES_LESS (t, x))
! {
! xh = th;
! xl = tl;
! break;
! }
!
! REAL_VALUE_TO_INT (&xl, &xh, x);
! break;
!
default:
abort ();
}
! return immed_double_const (xl, xh, mode);
}
/* This was formerly used only for non-IEEE float.
/* PR optimization/9325 */
extern void abort (void);
int f1()
{
return (int)2147483648.0f;
}
int f2()
{
return (int)(float)(2147483647);
}
int f3()
{
float a = 2147483648.0f;
return (int)a;
}
int f4()
{
int a = 2147483647;
float b = (float)a;
return (int)b;
}
int main()
{
if (f1() != 2147483647)
abort ();
if (f2() != 2147483647)
abort ();
#ifdef __OPTIMIZE__
if (f3() != 2147483647)
abort ();
if (f4() != 2147483647)
abort ();
#endif
return 0;
}
Roger
--
Roger Sayle, E-mail: roger@eyesopen.com
OpenEye Scientific Software, WWW: http://www.eyesopen.com/
Suite 1107, 3600 Cerrillos Road, Tel: (+1) 505-473-7385
Santa Fe, New Mexico, 87507. Fax: (+1) 505-473-0833