2017-07-30 Yury Gribov PR tree-optimization/57371 gcc/ * match.pd: New pattern. gcc/testsuite/ * testsuite/c-c++-common/pr57371-1.c: New test. * testsuite/c-c++-common/pr57371-2.c: New test. * testsuite/c-c++-common/pr57371-3.c: New test. * testsuite/c-c++-common/pr57371-4.c: New test. * testsuite/gcc.dg/pr57371-5.c: New test. diff -rupN gcc/gcc/match.pd gcc-57371/gcc/match.pd --- gcc/gcc/match.pd 2017-07-18 22:21:16.000000000 +0200 +++ gcc-57371/gcc/match.pd 2017-07-28 05:00:05.000000000 +0200 @@ -2805,6 +2805,80 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) (if (! HONOR_NANS (@0)) (cmp @0 @1)))))) +/* Optimize various special cases of (FTYPE) N CMP CST. */ +(for cmp (lt le eq ne ge gt) + icmp (le le eq ne ge ge) + (simplify + (cmp (float @0) REAL_CST@1) + (if (SCALAR_FLOAT_TYPE_P (TREE_TYPE (@1)) + && ! DECIMAL_FLOAT_TYPE_P (TREE_TYPE (@1))) + (with + { + tree itype = TREE_TYPE (@0); + signop isign = TYPE_SIGN (itype); + format_helper fmt (REAL_MODE_FORMAT (TYPE_MODE (TREE_TYPE (@1)))); + const REAL_VALUE_TYPE *cst = TREE_REAL_CST_PTR (@1); + /* Be careful to preserve any potential exceptions due to + NaNs. qNaNs are ok in == or != context. + TODO: relax under -fno-trapping-math or + -fno-signaling-nans. */ + bool exception_p + = real_isnan (cst) && (cst->signalling + || (cmp != EQ_EXPR || cmp != NE_EXPR)); + /* INT?_MIN is power-of-two so it takes + only one mantissa bit. */ + bool signed_p = isign == SIGNED; + bool itype_fits_ftype_p + = TYPE_PRECISION (itype) - signed_p <= significand_size (fmt); + } + /* TODO: allow non-fitting itype and SNaNs when + -fno-trapping-math. */ + (if (itype_fits_ftype_p && ! exception_p) + (with + { + REAL_VALUE_TYPE imin, imax; + real_from_integer (&imin, fmt, wi::min_value (itype), isign); + real_from_integer (&imax, fmt, wi::max_value (itype), isign); + + REAL_VALUE_TYPE icst; + if (cmp == GT_EXPR || cmp == GE_EXPR) + real_ceil (&icst, fmt, cst); + else if (cmp == LT_EXPR || cmp == LE_EXPR) + real_floor (&icst, fmt, cst); + else + real_trunc (&icst, fmt, cst); + + bool cst_int_p = real_identical (&icst, cst); + + bool overflow_p = false; + wide_int icst_val + = real_to_integer (&icst, &overflow_p, TYPE_PRECISION (itype)); + } + (switch + /* Optimize cases when CST is outside of ITYPE's range. */ + (if (real_compare (LT_EXPR, cst, &imin)) + { constant_boolean_node (cmp == GT_EXPR || cmp == GE_EXPR || cmp == NE_EXPR, + type); }) + (if (real_compare (GT_EXPR, cst, &imax)) + { constant_boolean_node (cmp == LT_EXPR || cmp == LE_EXPR || cmp == NE_EXPR, + type); }) + /* Remove cast if CST is an integer representable by ITYPE. */ + (if (cst_int_p) + (cmp @0 { gcc_assert (!overflow_p); + wide_int_to_tree (itype, icst_val); }) + ) + /* When CST is fractional, optimize + (FTYPE) N == CST -> 0 + (FTYPE) N != CST -> 1. */ + (if (cmp == EQ_EXPR || cmp == NE_EXPR) + { constant_boolean_node (cmp == NE_EXPR, type); }) + /* Otherwise replace with sensible integer constant. */ + (with + { + gcc_checking_assert (!overflow_p); + } + (icmp @0 { wide_int_to_tree (itype, icst_val); }))))))))) + /* Fold A /[ex] B CMP C to A CMP B * C. */ (for cmp (eq ne) (simplify diff -rupN gcc/gcc/testsuite/c-c++-common/pr57371-1.c gcc-57371/gcc/testsuite/c-c++-common/pr57371-1.c --- gcc/gcc/testsuite/c-c++-common/pr57371-1.c 1970-01-01 01:00:00.000000000 +0100 +++ gcc-57371/gcc/testsuite/c-c++-common/pr57371-1.c 2017-07-28 05:00:05.000000000 +0200 @@ -0,0 +1,341 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-original" } */ + +#include + +/* Original testcase from PR. */ + +int foo1 (short x) { + return (double) x != 0; + /* { dg-final { scan-tree-dump "return ( = )?x != 0" "original" } } */ +} + +int foo2 (short x) { + return (float) x != 0; + /* { dg-final { scan-tree-dump "return ( = )?x != 0" "original" } } */ +} + +int foo3 (int x) { + return (double) x != 0; + /* { dg-final { scan-tree-dump "return ( = )?x != 0" "original" } } */ +} + +/* Tests when RHS is within range of integer type. */ + +void in_range (unsigned short x) +{ + { + volatile int in_range_1; + in_range_1 = (float) x > 100.0f; + /* { dg-final { scan-tree-dump "in_range_1 = x > 100" "original" } } */ + } + + { + volatile int in_range_2; + in_range_2 = (float) x < 100.0f; + /* { dg-final { scan-tree-dump "in_range_2 = x <= 99" "original" } } */ + } + + { + volatile int in_range_3; + in_range_3 = (float) x > 100.5f; + /* { dg-final { scan-tree-dump "in_range_3 = x (>= 101|> 100)" "original" } } */ + } + + { + volatile int in_range_4; + in_range_4 = (float) x < 100.5f; + /* { dg-final { scan-tree-dump "in_range_4 = x <= 100" "original" } } */ + } + + { + volatile int in_range_5; + in_range_5 = (float) x == 100.0f; + /* { dg-final { scan-tree-dump "in_range_5 = x == 100" "original" } } */ + } + + { + volatile int in_range_6; + in_range_6 = (float) x != 100.0f; + /* { dg-final { scan-tree-dump "in_range_6 = x != 100" "original" } } */ + } + + { + volatile int in_range_7; + in_range_7 = (float) x == 100.5f; + /* { dg-final { scan-tree-dump "in_range_7 = 0" "original" } } */ + } + + { + volatile int in_range_8; + in_range_8 = (float) x != 100.5f; + /* { dg-final { scan-tree-dump "in_range_8 = 1" "original" } } */ + } +} + +/* Tests for cases where RHS is out of range of integer type. */ + +void out_range (unsigned short x) +{ + { + volatile int out_range_1; + out_range_1 = (float) x > -100.5f; + /* { dg-final { scan-tree-dump "out_range_1 = 1" "original" } } */ + } + + { + volatile int out_range_2; + out_range_2 = (float) x >= -100.5f; + /* { dg-final { scan-tree-dump "out_range_2 = 1" "original" } } */ + } + + { + volatile int out_range_3; + out_range_3 = (float) x < -100.5f; + /* { dg-final { scan-tree-dump "out_range_3 = 0" "original" } } */ + } + + { + volatile int out_range_4; + out_range_4 = (float) x <= -100.5f; + /* { dg-final { scan-tree-dump "out_range_4 = 0" "original" } } */ + } + + { + volatile int out_range_5; + out_range_5 = (float) x == -100.5f; + /* { dg-final { scan-tree-dump "out_range_5 = 0" "original" } } */ + } + + { + volatile int out_range_6; + out_range_6 = (float) x != -100.5f; + /* { dg-final { scan-tree-dump "out_range_6 = 1" "original" } } */ + } +} + +/* Tests when RHS is at boundary of integer type. */ + +void lo_bounds (unsigned short x) +{ + { + volatile int lo_bounds_1; + lo_bounds_1 = (float) x > 0x0; + /* { dg-final { scan-tree-dump "lo_bounds_1 = x (>|!=) 0" "original" } } */ + } + + { + volatile int lo_bounds_2; + lo_bounds_2 = (float) x >= 0x0; + /* { dg-final { scan-tree-dump "lo_bounds_2 = 1" "original" } } */ + } + + { + volatile int lo_bounds_3; + lo_bounds_3 = (float) x < 0x0; + /* { dg-final { scan-tree-dump "lo_bounds_3 = 0" "original" } } */ + } + + { + volatile int lo_bounds_4; + lo_bounds_4 = (float) x <= 0x0; + /* { dg-final { scan-tree-dump "lo_bounds_4 = x (<=|==) 0" "original" } } */ + } + + { + volatile int lo_bounds_5; + lo_bounds_5 = (float) x > 0x0 - 0.5f; + /* { dg-final { scan-tree-dump "lo_bounds_5 = 1" "original" } } */ + } + + { + volatile int lo_bounds_6; + lo_bounds_6 = (float) x >= 0x0 - 0.5f; + /* { dg-final { scan-tree-dump "lo_bounds_6 = 1" "original" } } */ + } + + { + volatile int lo_bounds_7; + lo_bounds_7 = (float) x < 0x0 - 0.5f; + /* { dg-final { scan-tree-dump "lo_bounds_7 = 0" "original" } } */ + } + + { + volatile int lo_bounds_8; + lo_bounds_8 = (float) x <= 0x0 - 0.5f; + /* { dg-final { scan-tree-dump "lo_bounds_8 = 0" "original" } } */ + } + + { + volatile int lo_bounds_9; + lo_bounds_9 = (float) x > 0x0 + 0.5f; + /* { dg-final { scan-tree-dump "lo_bounds_9 = x (>= 1|!= 0)" "original" } } */ + } + + { + volatile int lo_bounds_10; + lo_bounds_10 = (float) x >= 0x0 + 0.5f; + /* { dg-final { scan-tree-dump "lo_bounds_10 = x (>= 1|!= 0)" "original" } } */ + } + + { + volatile int lo_bounds_11; + lo_bounds_11 = (float) x < 0x0 + 0.5f; + /* { dg-final { scan-tree-dump "lo_bounds_11 = x (<=|==) 0" "original" } } */ + } + + { + volatile int lo_bounds_12; + lo_bounds_12 = (float) x <= 0x0 + 0.5f; + /* { dg-final { scan-tree-dump "lo_bounds_12 = x (<=|==) 0" "original" } } */ + } +} + +void hi_bounds (unsigned short x) +{ + { + volatile int hi_bounds_1; + hi_bounds_1 = (float) x > USHRT_MAX; + /* { dg-final { scan-tree-dump "hi_bounds_1 = 0" "original" } } */ + } + + { + volatile int hi_bounds_2; + hi_bounds_2 = (float) x >= USHRT_MAX; + /* { dg-final { scan-tree-dump "hi_bounds_2 = x (>=|==) 65535" "original" } } */ + } + + { + volatile int hi_bounds_3; + hi_bounds_3 = (float) x < USHRT_MAX; + /* { dg-final { scan-tree-dump "hi_bounds_3 = x (<|!=) 65535" "original" } } */ + } + + { + volatile int hi_bounds_4; + hi_bounds_4 = (float) x <= USHRT_MAX; + /* { dg-final { scan-tree-dump "hi_bounds_4 = 1" "original" } } */ + } + + { + volatile int hi_bounds_5; + hi_bounds_5 = (float) x > USHRT_MAX - 0.5f; + /* { dg-final { scan-tree-dump "hi_bounds_5 = x (>=|==) 65535" "original" } } */ + } + + { + volatile int hi_bounds_6; + hi_bounds_6 = (float) x >= USHRT_MAX - 0.5f; + /* { dg-final { scan-tree-dump "hi_bounds_6 = x (>=|==) 65535" "original" } } */ + } + + { + volatile int hi_bounds_7; + hi_bounds_7 = (float) x < USHRT_MAX - 0.5f; + /* { dg-final { scan-tree-dump "hi_bounds_7 = x (<= 65534|!= 65535)" "original" } } */ + } + + { + volatile int hi_bounds_8; + hi_bounds_8 = (float) x <= USHRT_MAX - 0.5f; + /* { dg-final { scan-tree-dump "hi_bounds_8 = x (<= 65534|!= 65535)" "original" } } */ + } + + { + volatile int hi_bounds_9; + hi_bounds_9 = (float) x > USHRT_MAX + 0.5f; + /* { dg-final { scan-tree-dump "hi_bounds_9 = 0" "original" } } */ + } + + { + volatile int hi_bounds_10; + hi_bounds_10 = (float) x >= USHRT_MAX + 0.5f; + /* { dg-final { scan-tree-dump "hi_bounds_10 = 0" "original" } } */ + } + + { + volatile int hi_bounds_11; + hi_bounds_11 = (float) x < USHRT_MAX + 0.5f; + /* { dg-final { scan-tree-dump "hi_bounds_11 = 1" "original" } } */ + } + + { + volatile int hi_bounds_12; + hi_bounds_12 = (float) x <= USHRT_MAX + 0.5f; + /* { dg-final { scan-tree-dump "hi_bounds_12 = 1" "original" } } */ + } +} + +/* Tests with non-finite float consts. */ + +void nonfinite (unsigned short x) +{ +#define INFINITY __builtin_inff () + + { + volatile int nonfinite_1; + nonfinite_1 = (float) x > INFINITY; + /* { dg-final { scan-tree-dump "nonfinite_1 = 0" "original" } } */ + } + + { + volatile int nonfinite_2; + nonfinite_2 = (float) x >= INFINITY; + /* { dg-final { scan-tree-dump "nonfinite_2 = 0" "original" } } */ + } + + { + volatile int nonfinite_3; + nonfinite_3 = (float) x < INFINITY; + /* { dg-final { scan-tree-dump "nonfinite_3 = 1" "original" } } */ + } + + { + volatile int nonfinite_4; + nonfinite_4 = (float) x <= INFINITY; + /* { dg-final { scan-tree-dump "nonfinite_4 = 1" "original" } } */ + } + + { + volatile int nonfinite_5; + nonfinite_5 = (float) x > -INFINITY; + /* { dg-final { scan-tree-dump "nonfinite_5 = 1" "original" } } */ + } + + { + volatile int nonfinite_6; + nonfinite_6 = (float) x >= -INFINITY; + /* { dg-final { scan-tree-dump "nonfinite_6 = 1" "original" } } */ + } + + { + volatile int nonfinite_7; + nonfinite_7 = (float) x < -INFINITY; + /* { dg-final { scan-tree-dump "nonfinite_7 = 0" "original" } } */ + } + + { + volatile int nonfinite_8; + nonfinite_8 = (float) x <= -INFINITY; + /* { dg-final { scan-tree-dump "nonfinite_8 = 0" "original" } } */ + } + +#define QNAN __builtin_nanf ("0") + + /* Even for qNaNs, only == and != are quiet. */ + + { + volatile int nonfinite_9; + nonfinite_9 = (float) x == QNAN; + /* { dg-final { scan-tree-dump "nonfinite_9 = 0" "original" } } */ + } + + { + volatile int nonfinite_10; + nonfinite_10 = (float) x != QNAN; + /* { dg-final { scan-tree-dump "nonfinite_10 = 1" "original" } } */ + } +} + +/* { dg-final { scan-tree-dump-not "\\(float\\)" "original" } } */ +/* { dg-final { scan-tree-dump-not "\\(double\\)" "original" } } */ diff -rupN gcc/gcc/testsuite/c-c++-common/pr57371-2.c gcc-57371/gcc/testsuite/c-c++-common/pr57371-2.c --- gcc/gcc/testsuite/c-c++-common/pr57371-2.c 1970-01-01 01:00:00.000000000 +0100 +++ gcc-57371/gcc/testsuite/c-c++-common/pr57371-2.c 2017-07-28 05:00:05.000000000 +0200 @@ -0,0 +1,17 @@ +/* { dg-do compile } */ +/* { dg-options "-O -fdump-tree-optimized" } */ + +/* We can not get rid of comparison in tests below because of + potential inexact exception. + + TODO: enable when -fno-trapping-math. */ + +int foo1(int x) { + return (float) x != 0; + /* { dg-final { scan-tree-dump "\\(float\\)" "optimized" } } */ +} + +int foo2(long long x) { + /* { dg-final { scan-tree-dump "\\(double\\)" "optimized" } } */ + return (double) x != 0; +} diff -rupN gcc/gcc/testsuite/c-c++-common/pr57371-3.c gcc-57371/gcc/testsuite/c-c++-common/pr57371-3.c --- gcc/gcc/testsuite/c-c++-common/pr57371-3.c 1970-01-01 01:00:00.000000000 +0100 +++ gcc-57371/gcc/testsuite/c-c++-common/pr57371-3.c 2017-07-28 05:00:05.000000000 +0200 @@ -0,0 +1,13 @@ +/* { dg-do compile } */ +/* { dg-options "-O -fdump-tree-optimized" } */ +/* { dg-require-effective-target int128 } */ + +/* We can not get rid of comparison in tests below because of + potential overflow exception. + + TODO: enable when -fno-trapping-math. */ + +int foo(__int128_t x) { + /* { dg-final { scan-tree-dump "\\(float\\)" "optimized" } } */ + return (float) x != 0; +} diff -rupN gcc/gcc/testsuite/c-c++-common/pr57371-4.c gcc-57371/gcc/testsuite/c-c++-common/pr57371-4.c --- gcc/gcc/testsuite/c-c++-common/pr57371-4.c 1970-01-01 01:00:00.000000000 +0100 +++ gcc-57371/gcc/testsuite/c-c++-common/pr57371-4.c 2017-07-28 05:00:05.000000000 +0200 @@ -0,0 +1,72 @@ +/* { dg-do compile } */ +/* { dg-options "-O -fsignaling-nans -fdump-tree-original" } */ + +/* We can not get rid of comparison in tests below because of + pending NaN exceptions. + + TODO: avoid under -fno-trapping-math. */ + +#define QNAN __builtin_nanf ("0") +#define SNAN __builtin_nansf ("0") + +void nonfinite(unsigned short x) { + { + volatile int nonfinite_1; + nonfinite_1 = (float) x > QNAN; + /* { dg-final { scan-tree-dump "nonfinite_1 = \\(float\\)" "original" } } */ + } + + { + volatile int nonfinite_2; + nonfinite_2 = (float) x >= QNAN; + /* { dg-final { scan-tree-dump "nonfinite_2 = \\(float\\)" "original" } } */ + } + + { + volatile int nonfinite_3; + nonfinite_3 = (float) x < QNAN; + /* { dg-final { scan-tree-dump "nonfinite_3 = \\(float\\)" "original" } } */ + } + + { + volatile int nonfinite_4; + nonfinite_4 = (float) x <= QNAN; + /* { dg-final { scan-tree-dump "nonfinite_4 = \\(float\\)" "original" } } */ + } + + { + volatile int nonfinite_5; + nonfinite_5 = (float) x > SNAN; + /* { dg-final { scan-tree-dump "nonfinite_5 = \\(float\\)" "original" } } */ + } + + { + volatile int nonfinite_6; + nonfinite_6 = (float) x >= SNAN; + /* { dg-final { scan-tree-dump "nonfinite_6 = \\(float\\)" "original" } } */ + } + + { + volatile int nonfinite_7; + nonfinite_7 = (float) x < SNAN; + /* { dg-final { scan-tree-dump "nonfinite_7 = \\(float\\)" "original" } } */ + } + + { + volatile int nonfinite_8; + nonfinite_8 = (float) x <= SNAN; + /* { dg-final { scan-tree-dump "nonfinite_8 = \\(float\\)" "original" } } */ + } + + { + volatile int nonfinite_9; + nonfinite_9 = (float) x == SNAN; + /* { dg-final { scan-tree-dump "nonfinite_9 = \\(float\\)" "original" } } */ + } + + { + volatile int nonfinite_10; + nonfinite_10 = (float) x != SNAN; + /* { dg-final { scan-tree-dump "nonfinite_10 = \\(float\\)" "original" } } */ + } +} diff -rupN gcc/gcc/testsuite/gcc.dg/pr57371-5.c gcc-57371/gcc/testsuite/gcc.dg/pr57371-5.c --- gcc/gcc/testsuite/gcc.dg/pr57371-5.c 1970-01-01 01:00:00.000000000 +0100 +++ gcc-57371/gcc/testsuite/gcc.dg/pr57371-5.c 2017-07-28 05:00:05.000000000 +0200 @@ -0,0 +1,10 @@ +/* { dg-do compile } */ +/* { dg-options "-O -fdump-tree-optimized" } */ +/* { dg-require-effective-target dfp } */ + +/* We do not support DFPs. */ + +int foo(short x) { + /* { dg-final { scan-tree-dump "\\(_Decimal32\\)" "optimized" } } */ + return (_Decimal32) x != 0; +}