phiopt: Improve spaceship_replacement for HONOR_NANS [PR117612]

The following patch optimizes spaceship followed by comparisons of the
spaceship value even for floating point spaceship when NaNs can appear.
operator<=> for this emits roughly
signed char c; if (i == j) c = 0; else if (i < j) c = -1; else if (i > j) c = 1; else c = 2;
and I believe the
/* The optimization may be unsafe due to NaNs.  */
comment just isn't true.
Sure, the i == j comparison doesn't raise exceptions on qNaNs, but if
one of the operands is qNaN, then i == j is false and i < j or i > j
is then executed and raises exceptions even on qNaNs.
And we can safely optimize say
c == -1 comparison after the above into i < j, that also raises
exceptions like before and handles NaNs the same way as the original.
The only unsafe transormation would be c == 0 or c != 0, turning it
into i == j or i != j wouldn't raise exception, so I'm not doing that
optimization (but other parts of the compiler optimize the i < j comparison
away anyway).

Anyway, to match the HONOR_NANS case, we need to verify that the
second comparison has true edge to the phi_bb (yielding there -1 or 1),
it can't be the false edge because when NaNs are honored, the false
edge is for both the case where the inverted comparison is true or when
one of the operands is NaN.  Similarly we need to ensure that the two
non-equality comparisons are the opposite, while for -ffast-math we can in
some cases get one comparison x >= 5.0 and the other x > 5.0 and it is fine,
because NaN is UB, when NaNs are honored, they must be different to leave
the unordered case with 2 value as the last one remaining.
The patch also punts if HONOR_NANS and the phi has just 3 arguments instead
of 4.
When NaNs are honored, we also in some cases need to perform some comparison
and then invert its result (so that exceptions are properly thrown and we
get the correct result).

2024-11-21  Jakub Jelinek  <jakub@redhat.com>

	PR tree-optimization/94589
	PR tree-optimization/117612
	* tree-ssa-phiopt.cc (spaceship_replacement): Handle
	HONOR_NANS (TREE_TYPE (lhs1)) case when possible.

	* gcc.dg/pr94589-5.c: New test.
	* gcc.dg/pr94589-6.c: New test.
	* g++.dg/opt/pr94589-5.C: New test.
	* g++.dg/opt/pr94589-6.C: New test.
This commit is contained in:
Jakub Jelinek 2024-11-21 09:39:06 +01:00 committed by Jakub Jelinek
parent ca7430f145
commit 05ab9447fe
5 changed files with 432 additions and 26 deletions

View File

@ -0,0 +1,26 @@
// PR tree-optimization/94589
// { dg-do compile { target c++20 } }
// { dg-require-effective-target inf }
// { dg-options "-O2 -g0 -fdump-tree-optimized" }
// { dg-final { scan-tree-dump-times "\[ij]_\[0-9]+\\(D\\) (?:<|<=|>|>=) \[ij]_\[0-9]+\\(D\\)" 8 "optimized" } }
// { dg-final { scan-tree-dump-times "i_\[0-9]+\\(D\\) (?:<|<=|>|>=) 5\\.0" 8 "optimized" } }
#include <compare>
#define A __attribute__((noipa))
A bool f3 (double i, double j) { auto c = i <=> j; return c > 0; }
A bool f4 (double i, double j) { auto c = i <=> j; return c < 0; }
A bool f5 (double i, double j) { auto c = i <=> j; return c >= 0; }
A bool f6 (double i, double j) { auto c = i <=> j; return c <= 0; }
A bool f7 (double i, double j) { auto c = i <=> j; return c == std::partial_ordering::less; }
A bool f8 (double i, double j) { auto c = i <=> j; return c != std::partial_ordering::less; }
A bool f11 (double i, double j) { auto c = i <=> j; return c == std::partial_ordering::greater; }
A bool f12 (double i, double j) { auto c = i <=> j; return c != std::partial_ordering::greater; }
A bool f15 (double i) { auto c = i <=> 5.0; return c > 0; }
A bool f16 (double i) { auto c = i <=> 5.0; return c < 0; }
A bool f17 (double i) { auto c = i <=> 5.0; return c >= 0; }
A bool f18 (double i) { auto c = i <=> 5.0; return c <= 0; }
A bool f19 (double i) { auto c = i <=> 5.0; return c == std::partial_ordering::less; }
A bool f20 (double i) { auto c = i <=> 5.0; return c != std::partial_ordering::less; }
A bool f23 (double i) { auto c = i <=> 5.0; return c == std::partial_ordering::greater; }
A bool f24 (double i) { auto c = i <=> 5.0; return c != std::partial_ordering::greater; }

View File

@ -0,0 +1,138 @@
// { dg-do run { target c++20 } }
// { dg-require-effective-target inf }
// { dg-options "-O2 -g" }
#include "pr94589-5.C"
A bool f1 (double i, double j) { auto c = i <=> j; return c == 0; }
A bool f2 (double i, double j) { auto c = i <=> j; return c != 0; }
A bool f9 (double i, double j) { auto c = i <=> j; return c == std::partial_ordering::equivalent; }
A bool f10 (double i, double j) { auto c = i <=> j; return c != std::partial_ordering::equivalent; }
A bool f13 (double i) { auto c = i <=> 5.0; return c == 0; }
A bool f14 (double i) { auto c = i <=> 5.0; return c != 0; }
A bool f21 (double i) { auto c = i <=> 5.0; return c == std::partial_ordering::equivalent; }
A bool f22 (double i) { auto c = i <=> 5.0; return c != std::partial_ordering::equivalent; }
A bool f25 (double i, double j) { auto c = i <=> j; return c == std::partial_ordering::unordered; }
A bool f26 (double i, double j) { auto c = i <=> j; return c != std::partial_ordering::unordered; }
A bool f27 (double i) { auto c = i <=> 5.0; return c == std::partial_ordering::unordered; }
A bool f28 (double i) { auto c = i <=> 5.0; return c != std::partial_ordering::unordered; }
#define C(fn, i, j, r) if (fn (i, j) != r) __builtin_abort ()
#define D(fn, i, r) if (fn (i) != r) __builtin_abort ()
int
main ()
{
C (f1, 7.0, 8.0, false);
C (f1, 8.0, 8.0, true);
C (f1, 9.0, 8.0, false);
C (f1, __builtin_nan (""), 8.0, false);
C (f2, 7.0, 8.0, true);
C (f2, 8.0, 8.0, false);
C (f2, 9.0, 8.0, true);
C (f2, __builtin_nan (""), 8.0, true);
C (f3, 7.0, 8.0, false);
C (f3, 8.0, 8.0, false);
C (f3, 9.0, 8.0, true);
C (f3, __builtin_nan (""), 8.0, false);
C (f4, 7.0, 8.0, true);
C (f4, 8.0, 8.0, false);
C (f4, 9.0, 8.0, false);
C (f4, __builtin_nan (""), 8.0, false);
C (f5, 7.0, 8.0, false);
C (f5, 8.0, 8.0, true);
C (f5, 9.0, 8.0, true);
C (f5, __builtin_nan (""), 8.0, false);
C (f6, 7.0, 8.0, true);
C (f6, 8.0, 8.0, true);
C (f6, 9.0, 8.0, false);
C (f6, __builtin_nan (""), 8.0, false);
C (f7, 7.0, 8.0, true);
C (f7, 8.0, 8.0, false);
C (f7, 9.0, 8.0, false);
C (f7, __builtin_nan (""), 8.0, false);
C (f8, 7.0, 8.0, false);
C (f8, 8.0, 8.0, true);
C (f8, 9.0, 8.0, true);
C (f8, __builtin_nan (""), 8.0, true);
C (f9, 7.0, 8.0, false);
C (f9, 8.0, 8.0, true);
C (f9, 9.0, 8.0, false);
C (f9, __builtin_nan (""), 8.0, false);
C (f10, 7.0, 8.0, true);
C (f10, 8.0, 8.0, false);
C (f10, 9.0, 8.0, true);
C (f10, __builtin_nan (""), 8.0, true);
C (f11, 7.0, 8.0, false);
C (f11, 8.0, 8.0, false);
C (f11, 9.0, 8.0, true);
C (f11, __builtin_nan (""), 8.0, false);
C (f12, 7.0, 8.0, true);
C (f12, 8.0, 8.0, true);
C (f12, 9.0, 8.0, false);
C (f12, __builtin_nan (""), 8.0, true);
D (f13, 4.0, false);
D (f13, 5.0, true);
D (f13, 6.0, false);
D (f13, __builtin_nan (""), false);
D (f14, 4.0, true);
D (f14, 5.0, false);
D (f14, 6.0, true);
D (f14, __builtin_nan (""), true);
D (f15, 4.0, false);
D (f15, 5.0, false);
D (f15, 6.0, true);
D (f15, __builtin_nan (""), false);
D (f16, 4.0, true);
D (f16, 5.0, false);
D (f16, 6.0, false);
D (f16, __builtin_nan (""), false);
D (f17, 4.0, false);
D (f17, 5.0, true);
D (f17, 6.0, true);
D (f17, __builtin_nan (""), false);
D (f18, 4.0, true);
D (f18, 5.0, true);
D (f18, 6.0, false);
D (f18, __builtin_nan (""), false);
D (f19, 4.0, true);
D (f19, 5.0, false);
D (f19, 6.0, false);
D (f19, __builtin_nan (""), false);
D (f20, 4.0, false);
D (f20, 5.0, true);
D (f20, 6.0, true);
D (f20, __builtin_nan (""), true);
D (f21, 4.0, false);
D (f21, 5.0, true);
D (f21, 6.0, false);
D (f21, __builtin_nan (""), false);
D (f22, 4.0, true);
D (f22, 5.0, false);
D (f22, 6.0, true);
D (f22, __builtin_nan (""), true);
D (f23, 4.0, false);
D (f23, 5.0, false);
D (f23, 6.0, true);
D (f23, __builtin_nan (""), false);
D (f24, 4.0, true);
D (f24, 5.0, true);
D (f24, 6.0, false);
D (f24, __builtin_nan (""), true);
C (f25, 7.0, 8.0, false);
C (f25, 8.0, 8.0, false);
C (f25, 9.0, 8.0, false);
C (f25, __builtin_nan (""), 8.0, true);
C (f26, 7.0, 8.0, true);
C (f26, 8.0, 8.0, true);
C (f26, 9.0, 8.0, true);
C (f26, __builtin_nan (""), 8.0, false);
D (f27, 4.0, false);
D (f27, 5.0, false);
D (f27, 6.0, false);
D (f27, __builtin_nan (""), true);
D (f28, 4.0, true);
D (f28, 5.0, true);
D (f28, 6.0, true);
D (f28, __builtin_nan (""), false);
}

View File

@ -0,0 +1,35 @@
/* PR tree-optimization/94589 */
/* { dg-do compile { target inf } } */
/* { dg-options "-O2 -g0 -fdump-tree-optimized" } */
/* { dg-final { scan-tree-dump-times "\[ij]_\[0-9]+\\(D\\) (?:<|<=|>|>=) \[ij]_\[0-9]+\\(D\\)" 14 "optimized" } } */
/* { dg-final { scan-tree-dump-times "i_\[0-9]+\\(D\\) (?:<|<=|>|>=) 5\\.0" 14 "optimized" } } */
#define A __attribute__((noipa))
A int f3 (double i, double j) { int c; if (i == j) c = 0; else if (i < j) c = -1; else if (i > j) c = 1; else c = 2; return c > 0; }
A int f4 (double i, double j) { int c; if (i == j) c = 0; else if (i < j) c = -1; else if (i > j) c = 1; else c = 2; return c < 0; }
A int f5 (double i, double j) { int c; if (i == j) c = 0; else if (i < j) c = -1; else if (i > j) c = 1; else c = 2; return c >= 0; }
A int f6 (double i, double j) { int c; if (i == j) c = 0; else if (i < j) c = -1; else if (i > j) c = 1; else c = 2; return c <= 0; }
A int f7 (double i, double j) { int c; if (i == j) c = 0; else if (i < j) c = -1; else if (i > j) c = 1; else c = 2; return c == -1; }
A int f8 (double i, double j) { int c; if (i == j) c = 0; else if (i < j) c = -1; else if (i > j) c = 1; else c = 2; return c != -1; }
A int f9 (double i, double j) { int c; if (i == j) c = 0; else if (i < j) c = -1; else if (i > j) c = 1; else c = 2; return c > -1; }
A int f10 (double i, double j) { int c; if (i == j) c = 0; else if (i < j) c = -1; else if (i > j) c = 1; else c = 2; return c <= -1; }
A int f11 (double i, double j) { int c; if (i == j) c = 0; else if (i < j) c = -1; else if (i > j) c = 1; else c = 2; return c == 1; }
A int f12 (double i, double j) { int c; if (i == j) c = 0; else if (i < j) c = -1; else if (i > j) c = 1; else c = 2; return c != 1; }
A int f13 (double i, double j) { int c; if (i == j) c = 0; else if (i < j) c = -1; else if (i > j) c = 1; else c = 2; return c < 1; }
A int f14 (double i, double j) { int c; if (i == j) c = 0; else if (i < j) c = -1; else if (i > j) c = 1; else c = 2; return c >= 1; }
A int f17 (double i) { int c; if (i == 5.0) c = 0; else if (i < 5.0) c = -1; else if (i > 5.0) c = 1; else c = 2; return c > 0; }
A int f18 (double i) { int c; if (i == 5.0) c = 0; else if (i < 5.0) c = -1; else if (i > 5.0) c = 1; else c = 2; return c < 0; }
A int f19 (double i) { int c; if (i == 5.0) c = 0; else if (i < 5.0) c = -1; else if (i > 5.0) c = 1; else c = 2; return c >= 0; }
A int f20 (double i) { int c; if (i == 5.0) c = 0; else if (i < 5.0) c = -1; else if (i > 5.0) c = 1; else c = 2; return c <= 0; }
A int f21 (double i) { int c; if (i == 5.0) c = 0; else if (i < 5.0) c = -1; else if (i > 5.0) c = 1; else c = 2; return c == -1; }
A int f22 (double i) { int c; if (i == 5.0) c = 0; else if (i < 5.0) c = -1; else if (i > 5.0) c = 1; else c = 2; return c != -1; }
A int f23 (double i) { int c; if (i == 5.0) c = 0; else if (i < 5.0) c = -1; else if (i > 5.0) c = 1; else c = 2; return c > -1; }
A int f24 (double i) { int c; if (i == 5.0) c = 0; else if (i < 5.0) c = -1; else if (i > 5.0) c = 1; else c = 2; return c <= -1; }
A int f25 (double i) { int c; if (i == 5.0) c = 0; else if (i < 5.0) c = -1; else if (i > 5.0) c = 1; else c = 2; return c == 1; }
A int f26 (double i) { int c; if (i == 5.0) c = 0; else if (i < 5.0) c = -1; else if (i > 5.0) c = 1; else c = 2; return c != 1; }
A int f27 (double i) { int c; if (i == 5.0) c = 0; else if (i < 5.0) c = -1; else if (i > 5.0) c = 1; else c = 2; return c < 1; }
A int f28 (double i) { int c; if (i == 5.0) c = 0; else if (i < 5.0) c = -1; else if (i > 5.0) c = 1; else c = 2; return c >= 1; }
A int f29 (double i, double j) { int c; if (i == j) c = 0; else if (i < j) c = -1; else if (i > j) c = 1; else c = 2; return (c & ~1) == 0; }
A int f30 (double i, double j) { int c; if (i == j) c = 0; else if (i < j) c = -1; else if (i > j) c = 1; else c = 2; return (c & ~1) != 0; }
A int f31 (double i) { int c; if (i == 5.0) c = 0; else if (i < 5.0) c = -1; else if (i > 5.0) c = 1; else c = 2; return (c & ~1) == 0; }
A int f32 (double i) { int c; if (i == 5.0) c = 0; else if (i < 5.0) c = -1; else if (i > 5.0) c = 1; else c = 2; return (c & ~1) != 0; }

View File

@ -0,0 +1,146 @@
/* { dg-do run { target inf } } */
/* { dg-options "-O2 -g" } */
#include "pr94589-5.c"
A int f1 (double i, double j) { int c; if (i == j) c = 0; else if (i < j) c = -1; else if (i > j) c = 1; else c = 2; return c == 0; }
A int f2 (double i, double j) { int c; if (i == j) c = 0; else if (i < j) c = -1; else if (i > j) c = 1; else c = 2; return c != 0; }
A int f15 (double i) { int c; if (i == 5.0) c = 0; else if (i < 5.0) c = -1; else if (i > 5.0) c = 1; else c = 2; return c == 0; }
A int f16 (double i) { int c; if (i == 5.0) c = 0; else if (i < 5.0) c = -1; else if (i > 5.0) c = 1; else c = 2; return c != 0; }
#define C(fn, i, j, r) if (fn (i, j) != r) __builtin_abort ()
#define D(fn, i, r) if (fn (i) != r) __builtin_abort ()
int
main ()
{
C (f1, 7.0, 8.0, 0);
C (f1, 8.0, 8.0, 1);
C (f1, 9.0, 8.0, 0);
C (f1, __builtin_nan (""), 8.0, 0);
C (f2, 7.0, 8.0, 1);
C (f2, 8.0, 8.0, 0);
C (f2, 9.0, 8.0, 1);
C (f2, __builtin_nan (""), 8.0, 1);
C (f3, 7.0, 8.0, 0);
C (f3, 8.0, 8.0, 0);
C (f3, 9.0, 8.0, 1);
C (f3, __builtin_nan (""), 8.0, 1);
C (f4, 7.0, 8.0, 1);
C (f4, 8.0, 8.0, 0);
C (f4, 9.0, 8.0, 0);
C (f4, __builtin_nan (""), 8.0, 0);
C (f5, 7.0, 8.0, 0);
C (f5, 8.0, 8.0, 1);
C (f5, 9.0, 8.0, 1);
C (f5, __builtin_nan (""), 8.0, 1);
C (f6, 7.0, 8.0, 1);
C (f6, 8.0, 8.0, 1);
C (f6, 9.0, 8.0, 0);
C (f6, __builtin_nan (""), 8.0, 0);
C (f7, 7.0, 8.0, 1);
C (f7, 8.0, 8.0, 0);
C (f7, 9.0, 8.0, 0);
C (f7, __builtin_nan (""), 8.0, 0);
C (f8, 7.0, 8.0, 0);
C (f8, 8.0, 8.0, 1);
C (f8, 9.0, 8.0, 1);
C (f8, __builtin_nan (""), 8.0, 1);
C (f9, 7.0, 8.0, 0);
C (f9, 8.0, 8.0, 1);
C (f9, 9.0, 8.0, 1);
C (f9, __builtin_nan (""), 8.0, 1);
C (f10, 7.0, 8.0, 1);
C (f10, 8.0, 8.0, 0);
C (f10, 9.0, 8.0, 0);
C (f10, __builtin_nan (""), 8.0, 0);
C (f11, 7.0, 8.0, 0);
C (f11, 8.0, 8.0, 0);
C (f11, 9.0, 8.0, 1);
C (f11, __builtin_nan (""), 8.0, 0);
C (f12, 7.0, 8.0, 1);
C (f12, 8.0, 8.0, 1);
C (f12, 9.0, 8.0, 0);
C (f12, __builtin_nan (""), 8.0, 1);
C (f13, 7.0, 8.0, 1);
C (f13, 8.0, 8.0, 1);
C (f13, 9.0, 8.0, 0);
C (f13, __builtin_nan (""), 8.0, 0);
C (f14, 7.0, 8.0, 0);
C (f14, 8.0, 8.0, 0);
C (f14, 9.0, 8.0, 1);
C (f14, __builtin_nan (""), 8.0, 1);
D (f15, 4.0, 0);
D (f15, 5.0, 1);
D (f15, 6.0, 0);
D (f15, __builtin_nan (""), 0);
D (f16, 4.0, 1);
D (f16, 5.0, 0);
D (f16, 6.0, 1);
D (f16, __builtin_nan (""), 1);
D (f17, 4.0, 0);
D (f17, 5.0, 0);
D (f17, 6.0, 1);
D (f17, __builtin_nan (""), 1);
D (f18, 4.0, 1);
D (f18, 5.0, 0);
D (f18, 6.0, 0);
D (f18, __builtin_nan (""), 0);
D (f19, 4.0, 0);
D (f19, 5.0, 1);
D (f19, 6.0, 1);
D (f19, __builtin_nan (""), 1);
D (f20, 4.0, 1);
D (f20, 5.0, 1);
D (f20, 6.0, 0);
D (f20, __builtin_nan (""), 0);
D (f21, 4.0, 1);
D (f21, 5.0, 0);
D (f21, 6.0, 0);
D (f21, __builtin_nan (""), 0);
D (f22, 4.0, 0);
D (f22, 5.0, 1);
D (f22, 6.0, 1);
D (f22, __builtin_nan (""), 1);
D (f23, 4.0, 0);
D (f23, 5.0, 1);
D (f23, 6.0, 1);
D (f23, __builtin_nan (""), 1);
D (f24, 4.0, 1);
D (f24, 5.0, 0);
D (f24, 6.0, 0);
D (f24, __builtin_nan (""), 0);
D (f25, 4.0, 0);
D (f25, 5.0, 0);
D (f25, 6.0, 1);
D (f25, __builtin_nan (""), 0);
D (f26, 4.0, 1);
D (f26, 5.0, 1);
D (f26, 6.0, 0);
D (f26, __builtin_nan (""), 1);
D (f27, 4.0, 1);
D (f27, 5.0, 1);
D (f27, 6.0, 0);
D (f27, __builtin_nan (""), 0);
D (f28, 4.0, 0);
D (f28, 5.0, 0);
D (f28, 6.0, 1);
D (f28, __builtin_nan (""), 1);
C (f29, 7.0, 8.0, 0);
C (f29, 8.0, 8.0, 1);
C (f29, 9.0, 8.0, 1);
C (f29, __builtin_nan (""), 8.0, 0);
C (f30, 7.0, 8.0, 1);
C (f30, 8.0, 8.0, 0);
C (f30, 9.0, 8.0, 0);
C (f30, __builtin_nan (""), 8.0, 1);
D (f31, 4.0, 0);
D (f31, 5.0, 1);
D (f31, 6.0, 1);
D (f31, __builtin_nan (""), 0);
D (f32, 4.0, 1);
D (f32, 5.0, 0);
D (f32, 6.0, 0);
D (f32, __builtin_nan (""), 1);
return 0;
}

View File

@ -2588,9 +2588,6 @@ spaceship_replacement (basic_block cond_bb, basic_block middle_bb,
}
tree lhs1 = gimple_cond_lhs (cond1);
tree rhs1 = gimple_cond_rhs (cond1);
/* The optimization may be unsafe due to NaNs. */
if (HONOR_NANS (TREE_TYPE (lhs1)))
return false;
if (TREE_CODE (lhs1) == SSA_NAME && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (lhs1))
return false;
if (TREE_CODE (rhs1) == SSA_NAME && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (rhs1))
@ -2681,6 +2678,9 @@ spaceship_replacement (basic_block cond_bb, basic_block middle_bb,
{
if (absu_hwi (tree_to_shwi (arg2)) != 1)
return false;
if ((cond2_phi_edge->flags & EDGE_FALSE_VALUE)
&& HONOR_NANS (TREE_TYPE (lhs1)))
return false;
if (e1->flags & EDGE_TRUE_VALUE)
{
if (tree_to_shwi (arg0) != 2
@ -2708,14 +2708,20 @@ spaceship_replacement (basic_block cond_bb, basic_block middle_bb,
phi_bb:;
is ok, but if x and y are swapped in one of the comparisons,
or the comparisons are the same and operands not swapped,
or the true and false edges are swapped, it is not. */
or the true and false edges are swapped, it is not.
For HONOR_NANS, the edge flags are irrelevant and the comparisons
must be different for non-swapped operands and same for swapped
operands. */
if ((lhs2 == lhs1)
^ (((cond2_phi_edge->flags
& ((cmp2 == LT_EXPR || cmp2 == LE_EXPR)
? EDGE_TRUE_VALUE : EDGE_FALSE_VALUE)) != 0)
!= ((e1->flags
& ((cmp1 == LT_EXPR || cmp1 == LE_EXPR)
? EDGE_TRUE_VALUE : EDGE_FALSE_VALUE)) != 0)))
^ (HONOR_NANS (TREE_TYPE (lhs1))
? ((cmp2 == LT_EXPR || cmp2 == LE_EXPR)
!= (cmp1 == LT_EXPR || cmp1 == LE_EXPR))
: (((cond2_phi_edge->flags
& ((cmp2 == LT_EXPR || cmp2 == LE_EXPR)
? EDGE_TRUE_VALUE : EDGE_FALSE_VALUE)) != 0)
!= ((e1->flags
& ((cmp1 == LT_EXPR || cmp1 == LE_EXPR)
? EDGE_TRUE_VALUE : EDGE_FALSE_VALUE)) != 0))))
return false;
if (!single_pred_p (cond2_bb) || !cond_only_block_p (cond2_bb))
return false;
@ -2754,7 +2760,8 @@ spaceship_replacement (basic_block cond_bb, basic_block middle_bb,
}
else if (absu_hwi (tree_to_shwi (arg0)) != 1
|| absu_hwi (tree_to_shwi (arg1)) != 1
|| wi::to_widest (arg0) == wi::to_widest (arg1))
|| wi::to_widest (arg0) == wi::to_widest (arg1)
|| HONOR_NANS (TREE_TYPE (lhs1)))
return false;
if (!integer_zerop (arg3) || (cmp3 != EQ_EXPR && cmp3 != NE_EXPR))
@ -2772,10 +2779,11 @@ spaceship_replacement (basic_block cond_bb, basic_block middle_bb,
one_cmp = GT_EXPR;
enum tree_code res_cmp;
bool negate_p = false;
switch (cmp)
{
case EQ_EXPR:
if (integer_zerop (rhs))
if (integer_zerop (rhs) && !HONOR_NANS (TREE_TYPE (lhs1)))
res_cmp = EQ_EXPR;
else if (integer_minus_onep (rhs))
res_cmp = one_cmp == LT_EXPR ? GT_EXPR : LT_EXPR;
@ -2785,7 +2793,7 @@ spaceship_replacement (basic_block cond_bb, basic_block middle_bb,
return false;
break;
case NE_EXPR:
if (integer_zerop (rhs))
if (integer_zerop (rhs) && !HONOR_NANS (TREE_TYPE (lhs1)))
res_cmp = NE_EXPR;
else if (integer_minus_onep (rhs))
res_cmp = one_cmp == LT_EXPR ? LE_EXPR : GE_EXPR;
@ -2793,12 +2801,18 @@ spaceship_replacement (basic_block cond_bb, basic_block middle_bb,
res_cmp = one_cmp == LT_EXPR ? GE_EXPR : LE_EXPR;
else
return false;
if (HONOR_NANS (TREE_TYPE (lhs1)))
negate_p = true;
break;
case LT_EXPR:
if (integer_onep (rhs))
res_cmp = one_cmp == LT_EXPR ? GE_EXPR : LE_EXPR;
else if (integer_zerop (rhs))
res_cmp = one_cmp == LT_EXPR ? GT_EXPR : LT_EXPR;
{
if (HONOR_NANS (TREE_TYPE (lhs1)) && orig_use_lhs)
negate_p = true;
res_cmp = one_cmp == LT_EXPR ? GT_EXPR : LT_EXPR;
}
else
return false;
break;
@ -2817,12 +2831,22 @@ spaceship_replacement (basic_block cond_bb, basic_block middle_bb,
res_cmp = one_cmp;
else
return false;
if (HONOR_NANS (TREE_TYPE (lhs1)))
negate_p = true;
break;
case GE_EXPR:
if (integer_zerop (rhs))
res_cmp = one_cmp == LT_EXPR ? LE_EXPR : GE_EXPR;
{
if (HONOR_NANS (TREE_TYPE (lhs1)) && !orig_use_lhs)
negate_p = true;
res_cmp = one_cmp == LT_EXPR ? LE_EXPR : GE_EXPR;
}
else if (integer_onep (rhs))
res_cmp = one_cmp;
{
if (HONOR_NANS (TREE_TYPE (lhs1)))
negate_p = true;
res_cmp = one_cmp;
}
else
return false;
break;
@ -2830,23 +2854,37 @@ spaceship_replacement (basic_block cond_bb, basic_block middle_bb,
gcc_unreachable ();
}
tree clhs1 = lhs1, crhs1 = rhs1;
if (negate_p)
{
if (cfun->can_throw_non_call_exceptions)
return false;
res_cmp = invert_tree_comparison (res_cmp, false);
clhs1 = make_ssa_name (boolean_type_node);
gimple *g = gimple_build_assign (clhs1, res_cmp, lhs1, rhs1);
gimple_stmt_iterator gsi = gsi_for_stmt (use_stmt);
gsi_insert_before (&gsi, g, GSI_SAME_STMT);
crhs1 = boolean_false_node;
res_cmp = EQ_EXPR;
}
if (gimple_code (use_stmt) == GIMPLE_COND)
{
gcond *use_cond = as_a <gcond *> (use_stmt);
gimple_cond_set_code (use_cond, res_cmp);
gimple_cond_set_lhs (use_cond, lhs1);
gimple_cond_set_rhs (use_cond, rhs1);
gimple_cond_set_lhs (use_cond, clhs1);
gimple_cond_set_rhs (use_cond, crhs1);
}
else if (gimple_assign_rhs_class (use_stmt) == GIMPLE_BINARY_RHS)
{
gimple_assign_set_rhs_code (use_stmt, res_cmp);
gimple_assign_set_rhs1 (use_stmt, lhs1);
gimple_assign_set_rhs2 (use_stmt, rhs1);
gimple_assign_set_rhs1 (use_stmt, clhs1);
gimple_assign_set_rhs2 (use_stmt, crhs1);
}
else
{
tree cond = build2 (res_cmp, TREE_TYPE (gimple_assign_rhs1 (use_stmt)),
lhs1, rhs1);
clhs1, crhs1);
gimple_assign_set_rhs1 (use_stmt, cond);
}
update_stmt (use_stmt);
@ -2890,14 +2928,31 @@ spaceship_replacement (basic_block cond_bb, basic_block middle_bb,
# DEBUG D#2 => i_2(D) == j_3(D) ? 0 : D#1
where > stands for the comparison that yielded 1
and replace debug uses of phi result with that D#2.
Ignore the value of 2, because if NaNs aren't expected,
all floating point numbers should be comparable. */
Ignore the value of 2 if !HONOR_NANS, because if NaNs
aren't expected, all floating point numbers should be
comparable. If HONOR_NANS, emit something like:
# DEBUG D#1 => i_2(D) < j_3(D) ? -1 : 2
# DEBUG D#2 => i_2(D) > j_3(D) ? 1 : D#1
# DEBUG D#3 => i_2(D) == j_3(D) ? 0 : D#2
instead. */
gimple_stmt_iterator gsi = gsi_after_labels (gimple_bb (phi));
tree type = TREE_TYPE (phires);
tree minus_one = build_int_cst (type, -1);
if (HONOR_NANS (TREE_TYPE (lhs1)))
{
tree temp3 = build_debug_expr_decl (type);
tree t = build2 (one_cmp == LT_EXPR ? GT_EXPR : LT_EXPR,
boolean_type_node, lhs1, rhs2);
t = build3 (COND_EXPR, type, t, minus_one,
build_int_cst (type, 2));
gimple *g = gimple_build_debug_bind (temp3, t, phi);
gsi_insert_before (&gsi, g, GSI_SAME_STMT);
minus_one = temp3;
}
tree temp1 = build_debug_expr_decl (type);
tree t = build2 (one_cmp, boolean_type_node, lhs1, rhs2);
t = build3 (COND_EXPR, type, t, build_one_cst (type),
build_int_cst (type, -1));
minus_one);
gimple *g = gimple_build_debug_bind (temp1, t, phi);
gsi_insert_before (&gsi, g, GSI_SAME_STMT);
tree temp2 = build_debug_expr_decl (type);
@ -2908,13 +2963,19 @@ spaceship_replacement (basic_block cond_bb, basic_block middle_bb,
replace_uses_by (phires, temp2);
if (orig_use_lhs)
{
if (has_cast_debug_uses)
if (has_cast_debug_uses
|| (HONOR_NANS (TREE_TYPE (lhs1)) && !is_cast))
{
tree temp3 = make_node (DEBUG_EXPR_DECL);
DECL_ARTIFICIAL (temp3) = 1;
TREE_TYPE (temp3) = TREE_TYPE (orig_use_lhs);
SET_DECL_MODE (temp3, TYPE_MODE (type));
t = fold_convert (TREE_TYPE (temp3), temp2);
if (has_cast_debug_uses)
t = fold_convert (TREE_TYPE (temp3), temp2);
else
t = build2 (BIT_AND_EXPR, TREE_TYPE (temp3),
temp2, build_int_cst (TREE_TYPE (temp3),
~1));
g = gimple_build_debug_bind (temp3, t, phi);
gsi_insert_before (&gsi, g, GSI_SAME_STMT);
replace_uses_by (orig_use_lhs, temp3);