From 6947cfa03098eee46669ec2902e5f6e33c3cbe9a Mon Sep 17 00:00:00 2001
From: Andrew MacLeod <amacleod@redhat.com>
Date: Fri, 20 Jan 2023 17:31:26 -0500
Subject: [PATCH 2/2] Add VREL_OTHER for FP unsupported relations.

Add VREL_OTHER to represent floating point relations we do not yet
support.

	PR tree-optimization/108447
	gcc/
	* value-relation.cc (kind_string): Add "unknown fp".
	(rr_negate_table): Add entry for VREL_OTHER.
	(rr_swap_table): Likewise.
	(rr_intersect_table): Likewise.
	(rr_union_table): Likewise.
	(rr_transitive_table): Likewise.
	(relation_to_code): Likewise.
	(value_relation::intersect): Handle floating point differences.
	(value_relation::union_): Likewise.
	* value-relation.h (enum relation_kind_t): Add VREL_OTHER.

	gcc/testsuite/
	* gcc.dg/pr108447.c: New.
---
 gcc/testsuite/gcc.dg/pr108447.c |  33 +++++++++
 gcc/value-relation.cc           | 118 +++++++++++++++++++++-----------
 gcc/value-relation.h            |   1 +
 3 files changed, 113 insertions(+), 39 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/pr108447.c

diff --git a/gcc/testsuite/gcc.dg/pr108447.c b/gcc/testsuite/gcc.dg/pr108447.c
new file mode 100644
index 00000000000..cfbaba6d0aa
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr108447.c
@@ -0,0 +1,33 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+__attribute__((noipa)) int
+foo (float x, float y)
+{
+  _Bool cmp1 = x <= y;
+  _Bool cmp2 = x >= y;
+  if (cmp1 && cmp2)
+    return 1;
+  else if (!cmp1 && !cmp2)
+    return -1;
+  return 0;
+}
+
+int
+main ()
+{
+  if (foo (0.0f, __builtin_nanf ("")) != -1)
+    __builtin_abort ();
+  if (foo (__builtin_nanf (""), -42.0f) != -1)
+    __builtin_abort ();
+  if (foo (0.0f, -0.0f) != 1)
+    __builtin_abort ();
+  if (foo (42.0f, 42.0f) != 1)
+    __builtin_abort ();
+  if (foo (42.0f, -0.0f) != 0)
+    __builtin_abort ();
+  if (foo (0.0f, -42.0f) != 0)
+    __builtin_abort ();
+  return 0;
+}
+
diff --git a/gcc/value-relation.cc b/gcc/value-relation.cc
index 432828e2b13..299a119827a 100644
--- a/gcc/value-relation.cc
+++ b/gcc/value-relation.cc
@@ -33,8 +33,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "dominance.h"
 
 static const char *kind_string[VREL_LAST] =
-{ "varying", "undefined", "<", "<=", ">", ">=", "==", "!=", "pe8", "pe16",
-  "pe32", "pe64" };
+{ "varying", "undefined", "<", "<=", ">", ">=", "==", "!=", "unknown fp",
+  "pe8", "pe16", "pe32", "pe64" };
 
 // Print a relation_kind REL to file F.
 
@@ -47,7 +47,7 @@ print_relation (FILE *f, relation_kind rel)
 // This table is used to negate the operands.  op1 REL op2 -> !(op1 REL op2).
 relation_kind rr_negate_table[VREL_LAST] = {
   VREL_VARYING, VREL_UNDEFINED, VREL_GE, VREL_GT, VREL_LE, VREL_LT, VREL_NE,
-  VREL_EQ };
+  VREL_EQ, VREL_OTHER };
 
 // Negate the relation, as in logical negation.
 
@@ -60,7 +60,7 @@ relation_negate (relation_kind r)
 // This table is used to swap the operands.  op1 REL op2 -> op2 REL op1.
 relation_kind rr_swap_table[VREL_LAST] = {
   VREL_VARYING, VREL_UNDEFINED, VREL_GT, VREL_GE, VREL_LT, VREL_LE, VREL_EQ,
-  VREL_NE };
+  VREL_NE, VREL_OTHER };
 
 // Return the relation as if the operands were swapped.
 
@@ -75,28 +75,32 @@ relation_swap (relation_kind r)
 relation_kind rr_intersect_table[VREL_LAST][VREL_LAST] = {
 // VREL_VARYING
   { VREL_VARYING, VREL_UNDEFINED, VREL_LT, VREL_LE, VREL_GT, VREL_GE, VREL_EQ,
-    VREL_NE },
+    VREL_NE, VREL_OTHER },
 // VREL_UNDEFINED
   { VREL_UNDEFINED, VREL_UNDEFINED, VREL_UNDEFINED, VREL_UNDEFINED,
-    VREL_UNDEFINED, VREL_UNDEFINED, VREL_UNDEFINED, VREL_UNDEFINED },
+    VREL_UNDEFINED, VREL_UNDEFINED, VREL_UNDEFINED, VREL_UNDEFINED,
+    VREL_UNDEFINED },
 // VREL_LT
   { VREL_LT, VREL_UNDEFINED, VREL_LT, VREL_LT, VREL_UNDEFINED, VREL_UNDEFINED,
-    VREL_UNDEFINED, VREL_LT },
+    VREL_UNDEFINED, VREL_LT, VREL_OTHER },
 // VREL_LE
   { VREL_LE, VREL_UNDEFINED, VREL_LT, VREL_LE, VREL_UNDEFINED, VREL_EQ,
-    VREL_EQ, VREL_LT },
+    VREL_EQ, VREL_LT, VREL_OTHER },
 // VREL_GT
   { VREL_GT, VREL_UNDEFINED, VREL_UNDEFINED, VREL_UNDEFINED, VREL_GT, VREL_GT,
-    VREL_UNDEFINED, VREL_GT },
+    VREL_UNDEFINED, VREL_GT, VREL_OTHER },
 // VREL_GE
   { VREL_GE, VREL_UNDEFINED, VREL_UNDEFINED, VREL_EQ, VREL_GT, VREL_GE,
-    VREL_EQ, VREL_GT },
+    VREL_EQ, VREL_GT, VREL_OTHER },
 // VREL_EQ
   { VREL_EQ, VREL_UNDEFINED, VREL_UNDEFINED, VREL_EQ, VREL_UNDEFINED, VREL_EQ,
-    VREL_EQ, VREL_UNDEFINED },
+    VREL_EQ, VREL_UNDEFINED, VREL_OTHER },
 // VREL_NE
   { VREL_NE, VREL_UNDEFINED, VREL_LT, VREL_LT, VREL_GT, VREL_GT,
-    VREL_UNDEFINED, VREL_NE } };
+    VREL_UNDEFINED, VREL_NE, VREL_OTHER },
+// VREL_OTHER
+  { VREL_OTHER, VREL_UNDEFINED, VREL_OTHER, VREL_OTHER, VREL_OTHER,
+    VREL_OTHER, VREL_OTHER, VREL_OTHER, VREL_OTHER } };
 
 
 // Intersect relation R1 with relation R2 and return the resulting relation.
@@ -113,28 +117,31 @@ relation_intersect (relation_kind r1, relation_kind r2)
 relation_kind rr_union_table[VREL_LAST][VREL_LAST] = {
 // VREL_VARYING
   { VREL_VARYING, VREL_VARYING, VREL_VARYING, VREL_VARYING, VREL_VARYING,
-    VREL_VARYING, VREL_VARYING, VREL_VARYING },
+    VREL_VARYING, VREL_VARYING, VREL_VARYING, VREL_VARYING },
 // VREL_UNDEFINED
   { VREL_VARYING, VREL_UNDEFINED, VREL_LT, VREL_LE, VREL_GT, VREL_GE,
-    VREL_EQ, VREL_NE },
+    VREL_EQ, VREL_NE, VREL_OTHER },
 // VREL_LT
   { VREL_VARYING, VREL_LT, VREL_LT, VREL_LE, VREL_NE, VREL_VARYING, VREL_LE,
-    VREL_NE },
+    VREL_NE, VREL_OTHER },
 // VREL_LE
   { VREL_VARYING, VREL_LE, VREL_LE, VREL_LE, VREL_VARYING, VREL_VARYING,
-    VREL_LE, VREL_VARYING },
+    VREL_LE, VREL_VARYING, VREL_OTHER },
 // VREL_GT
   { VREL_VARYING, VREL_GT, VREL_NE, VREL_VARYING, VREL_GT, VREL_GE, VREL_GE,
-    VREL_NE },
+    VREL_NE, VREL_OTHER },
 // VREL_GE
   { VREL_VARYING, VREL_GE, VREL_VARYING, VREL_VARYING, VREL_GE, VREL_GE,
-    VREL_GE, VREL_VARYING },
+    VREL_GE, VREL_VARYING, VREL_OTHER },
 // VREL_EQ
   { VREL_VARYING, VREL_EQ, VREL_LE, VREL_LE, VREL_GE, VREL_GE, VREL_EQ,
-    VREL_VARYING },
+    VREL_VARYING, VREL_OTHER },
 // VREL_NE
   { VREL_VARYING, VREL_NE, VREL_NE, VREL_VARYING, VREL_NE, VREL_VARYING,
-    VREL_VARYING, VREL_NE } };
+    VREL_VARYING, VREL_NE, VREL_OTHER },
+// VREL_OTHER
+  { VREL_VARYING, VREL_OTHER, VREL_OTHER, VREL_OTHER, VREL_OTHER,
+    VREL_OTHER, VREL_OTHER, VREL_OTHER, VREL_OTHER } };
 
 // Union relation R1 with relation R2 and return the result.
 
@@ -151,28 +158,31 @@ relation_union (relation_kind r1, relation_kind r2)
 relation_kind rr_transitive_table[VREL_LAST][VREL_LAST] = {
 // VREL_VARYING
   { VREL_VARYING, VREL_VARYING, VREL_VARYING, VREL_VARYING, VREL_VARYING,
-    VREL_VARYING, VREL_VARYING, VREL_VARYING },
+    VREL_VARYING, VREL_VARYING, VREL_VARYING, VREL_VARYING },
 // VREL_UNDEFINED
   { VREL_VARYING, VREL_VARYING, VREL_VARYING, VREL_VARYING, VREL_VARYING,
-    VREL_VARYING, VREL_VARYING, VREL_VARYING },
+    VREL_VARYING, VREL_VARYING, VREL_VARYING, VREL_VARYING },
 // VREL_LT
   { VREL_VARYING, VREL_VARYING, VREL_LT, VREL_LT, VREL_VARYING, VREL_VARYING,
-    VREL_LT, VREL_VARYING },
+    VREL_LT, VREL_VARYING, VREL_VARYING },
 // VREL_LE
   { VREL_VARYING, VREL_VARYING, VREL_LT, VREL_LE, VREL_VARYING, VREL_VARYING,
-    VREL_LE, VREL_VARYING },
+    VREL_LE, VREL_VARYING, VREL_VARYING },
 // VREL_GT
   { VREL_VARYING, VREL_VARYING, VREL_VARYING, VREL_VARYING, VREL_GT, VREL_GT,
-    VREL_GT, VREL_VARYING },
+    VREL_GT, VREL_VARYING, VREL_VARYING },
 // VREL_GE
   { VREL_VARYING, VREL_VARYING, VREL_VARYING, VREL_VARYING, VREL_GT, VREL_GE,
-    VREL_GE, VREL_VARYING },
+    VREL_GE, VREL_VARYING, VREL_VARYING },
 // VREL_EQ
   { VREL_VARYING, VREL_VARYING, VREL_LT, VREL_LE, VREL_GT, VREL_GE, VREL_EQ,
-    VREL_VARYING },
+    VREL_VARYING, VREL_VARYING },
 // VREL_NE
   { VREL_VARYING, VREL_VARYING, VREL_VARYING, VREL_VARYING, VREL_VARYING,
-    VREL_VARYING, VREL_VARYING, VREL_VARYING } };
+    VREL_VARYING, VREL_VARYING, VREL_VARYING, VREL_VARYING },
+// VREL_OTHER
+  { VREL_VARYING, VREL_VARYING, VREL_VARYING, VREL_VARYING, VREL_VARYING,
+    VREL_VARYING, VREL_VARYING, VREL_VARYING, VREL_VARYING } };
 
 // Apply transitive operation between relation R1 and relation R2, and
 // return the resulting relation, if any.
@@ -187,7 +197,7 @@ relation_transitive (relation_kind r1, relation_kind r2)
 
 tree_code relation_to_code [VREL_LAST] = {
   ERROR_MARK, ERROR_MARK, LT_EXPR, LE_EXPR, GT_EXPR, GE_EXPR, EQ_EXPR,
-  NE_EXPR };
+  NE_EXPR, ERROR_MARK };
 
 // This routine validates that a relation can be applied to a specific set of
 // ranges.  In particular, floating point x == x may not be true if the NaN bit
@@ -784,17 +794,28 @@ value_relation::negate ()
 bool
 value_relation::intersect (const value_relation &p)
 {
-  // Save previous value
-  relation_kind old = related;
+  relation_kind k;
 
   if (p.op1 () == op1 () && p.op2 () == op2 ())
-    related = relation_intersect (kind (), p.kind ());
+    k = relation_intersect (kind (), p.kind ());
   else if (p.op2 () == op1 () && p.op1 () == op2 ())
-    related = relation_intersect (kind (), relation_swap (p.kind ()));
+    k = relation_intersect (kind (), relation_swap (p.kind ()));
   else
     return false;
 
-  return old != related;
+  if (related == k)
+    return false;
+
+  bool float_p = name1 && FLOAT_TYPE_P (TREE_TYPE (name1));
+  if (float_p && p.kind () != k && k == VREL_UNDEFINED)
+    {
+      if (relation_lt_le_gt_ge_p (kind ())
+	  || relation_lt_le_gt_ge_p (p.kind ()))
+	k = VREL_OTHER;
+    }
+
+  related = k;
+  return true;
 }
 
 // Perform a union between 2 relations. *this ||= p.
@@ -802,17 +823,36 @@ value_relation::intersect (const value_relation &p)
 bool
 value_relation::union_ (const value_relation &p)
 {
-  // Save previous value
-  relation_kind old = related;
+  relation_kind k;
 
   if (p.op1 () == op1 () && p.op2 () == op2 ())
-    related = relation_union (kind(), p.kind());
+    k = relation_union (kind (), p.kind ());
   else if (p.op2 () == op1 () && p.op1 () == op2 ())
-    related = relation_union (kind(), relation_swap (p.kind ()));
+    k = relation_union (kind (), relation_swap (p.kind ()));
   else
     return false;
 
-  return old != related;
+  if (related == k)
+    return false;
+
+  // (x < y) || (x > y) produces x != y, but this is not true with floats.
+  // (x <= y) || (x >= y) produces VARYING, which is also not true for floats.
+  // As they cannot be properly represented, use VREL_OTHER.
+  bool float_p = name1 && FLOAT_TYPE_P (TREE_TYPE (name1));
+  if (float_p && p.kind () != k)
+    {
+      if (kind () == VREL_LT && p.kind () == VREL_GT)
+	k = VREL_OTHER;
+      else if (kind () == VREL_GT && p.kind () == VREL_LT)
+	k = VREL_OTHER;
+      else if (kind () == VREL_LE && p.kind () == VREL_GE)
+	k = VREL_OTHER;
+      else if (kind () == VREL_GE && p.kind () == VREL_LE)
+	k = VREL_OTHER;
+    }
+
+  related = k;
+  return true;
 }
 
 // Identify and apply any transitive relations between REL
diff --git a/gcc/value-relation.h b/gcc/value-relation.h
index 354a0fd4130..c191de292c7 100644
--- a/gcc/value-relation.h
+++ b/gcc/value-relation.h
@@ -70,6 +70,7 @@ typedef enum relation_kind_t
   VREL_GE,		// r1 >= r2
   VREL_EQ,		// r1 == r2
   VREL_NE,		// r1 != r2
+  VREL_OTHER,		// unrepresentatble floating point relation.
   VREL_PE8,		// 8 bit partial equivalency
   VREL_PE16,		// 16 bit partial equivalency
   VREL_PE32,		// 32 bit partial equivalency
-- 
2.39.0

