Eric Botcazou <ebotca...@adacore.com> writes:
>> AIUI, neither ORDERED nor UNEQ trap on signalling NaNs.  Without this,
>> the follow-on patch would fold
>> 
>>    (and (ordered x y) (uneq x y)) -> (eq x y)
>> 
>> which is the same thing for quiet NaNs but not for signalling NaNs.
>
> Note that GCC defaults to -fno-signaling-nans and the transformation would be 
> valid in this mode.

OK, how about this version?  Tested on aarch64-linux-gnu so far.

Thanks,
Richard


2019-07-15  Richard Sandiford  <richard.sandif...@arm.com>

gcc/
        * jump.c (FLAGS_EQ, FLAGS_LT, FLAGS_GT, FLAGS_UNORDERED, FLAGS_ORDER)
        (FLAGS_SIGNED, FLAGS_UNSIGNED, FLAGS_SIGN_AGNOSTIC, FLAGS_SIGNEDNESS)
        (FLAGS_TRAP_QNANS, FLAGS_TRAP_SNANS, FLAGS_TRAP_NANS, FLAGS_TRAP_NONE)
        (FLAGS_TRAPS): New constants.
        (condition_to_flags, flags_to_condition): New functions.
        (swap_condition, unsigned_condition, signed_condition)
        (comparison_dominates_p): Use them.

Index: gcc/jump.c
===================================================================
--- gcc/jump.c  2019-07-15 16:22:55.000000000 +0100
+++ gcc/jump.c  2019-07-15 16:22:55.342699887 +0100
@@ -65,6 +65,99 @@ static void mark_jump_label_asm (rtx, rt
 static void redirect_exp_1 (rtx *, rtx, rtx, rtx_insn *);
 static int invert_exp_1 (rtx, rtx_insn *);
 
+/* Flags that describe when a condition is true.  */
+const int FLAGS_EQ = 0x1;
+const int FLAGS_LT = 0x2;
+const int FLAGS_GT = 0x4;
+const int FLAGS_UNORDERED = 0x8;
+const int FLAGS_ORDER = 0xf;
+
+/* When describing an existing condition, these flags say whether the
+   inputs are interpreted as signed and whether they are interpreted as
+   unsigned.  When asking for a new condition, the flags say whether
+   the comparison must handle signed values and whether it must handle
+   unsigned values.  Floats are treated as signed in both cases.  */
+const int FLAGS_SIGNED = 0x10;
+const int FLAGS_UNSIGNED = 0x20;
+const int FLAGS_SIGN_AGNOSTIC = 0;
+const int FLAGS_SIGNEDNESS = FLAGS_SIGNED | FLAGS_UNSIGNED;
+
+/* When describing an existing condition, these flag say whether the
+   comparison traps for quiet NaNs and signaling NaNs.  When asking for
+   a new condition, the flag says whether the comparison is allowed to
+   trap for such NaNs.  */
+const int FLAGS_TRAP_QNANS = 0x40;
+const int FLAGS_TRAP_SNANS = 0x80;
+const int FLAGS_TRAP_NANS = FLAGS_TRAP_SNANS | FLAGS_TRAP_QNANS;
+const int FLAGS_TRAP_NONE = 0;
+const int FLAGS_TRAPS = FLAGS_TRAP_SNANS | FLAGS_TRAP_QNANS;
+
+/* Invoke T (CODE, ORDER, SIGNEDNESS, TRAPS) for each comparison, where:
+
+   - CODE is the rtl comparison code
+   - ORDER is the OR of the conditions under which CODE returns true
+   - SIGNEDNESS is the FLAGS_* suffix describing the sigedness of COND
+   - TRAPS is the FLAGS_* suffix describing when COND can trap.  */
+#define FOR_MAPPING(T) \
+  T (EQ,       FLAGS_EQ,                       SIGN_AGNOSTIC,  TRAP_SNANS) \
+  T (NE,       ~FLAGS_EQ,                      SIGN_AGNOSTIC,  TRAP_SNANS) \
+  T (LTGT,     FLAGS_LT | FLAGS_GT,            SIGNED,         TRAP_NANS) \
+  T (LT,       FLAGS_LT,                       SIGNED,         TRAP_NANS) \
+  T (LE,       FLAGS_LT | FLAGS_EQ,            SIGNED,         TRAP_NANS) \
+  T (GT,       FLAGS_GT,                       SIGNED,         TRAP_NANS) \
+  T (GE,       FLAGS_GT | FLAGS_EQ,            SIGNED,         TRAP_NANS) \
+  T (LTU,      FLAGS_LT,                       UNSIGNED,       TRAP_NONE) \
+  T (LEU,      FLAGS_LT | FLAGS_EQ,            UNSIGNED,       TRAP_NONE) \
+  T (GTU,      FLAGS_GT,                       UNSIGNED,       TRAP_NONE) \
+  T (GEU,      FLAGS_GT | FLAGS_EQ,            UNSIGNED,       TRAP_NONE) \
+  T (ORDERED,  ~FLAGS_UNORDERED,               SIGNED,         TRAP_NONE) \
+  T (UNORDERED,        FLAGS_UNORDERED,                SIGNED,         
TRAP_NONE) \
+  T (UNEQ,     FLAGS_UNORDERED | FLAGS_EQ,     SIGNED,         TRAP_NONE) \
+  T (UNLT,     FLAGS_UNORDERED | FLAGS_LT,     SIGNED,         TRAP_NONE) \
+  T (UNLE,     ~FLAGS_GT,                      SIGNED,         TRAP_NONE) \
+  T (UNGT,     FLAGS_UNORDERED | FLAGS_GT,     SIGNED,         TRAP_NONE) \
+  T (UNGE,     ~FLAGS_LT,                      SIGNED,         TRAP_NONE)
+
+/* Describe comparison CODE as a bitmask of FLAGS_*.  */
+
+static unsigned int
+condition_to_flags (rtx_code code)
+{
+#define CASE(CODE, ORDER, SIGNEDNESS, TRAPS)           \
+  case CODE:                                           \
+    return ((ORDER) & FLAGS_ORDER) | FLAGS_##SIGNEDNESS | FLAGS_##TRAPS;
+
+  switch (code)
+    {
+    FOR_MAPPING (CASE);
+    default:
+      gcc_unreachable ();
+    }
+
+#undef CASE
+}
+
+/* Return the comparison code that implements FLAGS_* bitmask FLAGS.
+   Assert on failure if FORCE, otherwise return UNKNOWN.  */
+
+static rtx_code
+flags_to_condition (unsigned int flags, bool force)
+{
+#define TEST(CODE, ORDER, SIGNEDNESS, TRAPS)                           \
+  if (((flags ^ (ORDER)) & FLAGS_ORDER) == 0                           \
+      && (FLAGS_##SIGNEDNESS == 0                                      \
+         || ((FLAGS_##SIGNEDNESS ^ flags) & FLAGS_SIGNEDNESS) == 0)    \
+      && (FLAGS_##TRAPS & ~flags & FLAGS_TRAPS) == 0)                  \
+    return CODE;
+
+  FOR_MAPPING (TEST);
+
+  gcc_assert (!force);
+  return UNKNOWN;
+#undef TEST
+}
+#undef FOR_MAPPING
+
 /* Worker for rebuild_jump_labels and rebuild_jump_labels_chain.  */
 static void
 rebuild_jump_labels_1 (rtx_insn *f, bool count_forced)
@@ -583,44 +676,11 @@ reverse_condition_maybe_unordered (enum
 enum rtx_code
 swap_condition (enum rtx_code code)
 {
-  switch (code)
-    {
-    case EQ:
-    case NE:
-    case UNORDERED:
-    case ORDERED:
-    case UNEQ:
-    case LTGT:
-      return code;
-
-    case GT:
-      return LT;
-    case GE:
-      return LE;
-    case LT:
-      return GT;
-    case LE:
-      return GE;
-    case GTU:
-      return LTU;
-    case GEU:
-      return LEU;
-    case LTU:
-      return GTU;
-    case LEU:
-      return GEU;
-    case UNLT:
-      return UNGT;
-    case UNLE:
-      return UNGE;
-    case UNGT:
-      return UNLT;
-    case UNGE:
-      return UNLE;
-
-    default:
-      gcc_unreachable ();
-    }
+  unsigned int flags = condition_to_flags (code);
+  flags = ((flags & ~(FLAGS_GT | FLAGS_LT))
+          | (flags & FLAGS_GT ? FLAGS_LT : 0)
+          | (flags & FLAGS_LT ? FLAGS_GT : 0));
+  return flags_to_condition (flags, true);
 }
 
 /* Given a comparison CODE, return the corresponding unsigned comparison.
@@ -630,28 +690,8 @@ swap_condition (enum rtx_code code)
 enum rtx_code
 unsigned_condition (enum rtx_code code)
 {
-  switch (code)
-    {
-    case EQ:
-    case NE:
-    case GTU:
-    case GEU:
-    case LTU:
-    case LEU:
-      return code;
-
-    case GT:
-      return GTU;
-    case GE:
-      return GEU;
-    case LT:
-      return LTU;
-    case LE:
-      return LEU;
-
-    default:
-      gcc_unreachable ();
-    }
+  unsigned int flags = condition_to_flags (code);
+  return flags_to_condition ((flags & ~FLAGS_SIGNED) | FLAGS_UNSIGNED, true);
 }
 
 /* Similarly, return the signed version of a comparison.  */
@@ -659,28 +699,8 @@ unsigned_condition (enum rtx_code code)
 enum rtx_code
 signed_condition (enum rtx_code code)
 {
-  switch (code)
-    {
-    case EQ:
-    case NE:
-    case GT:
-    case GE:
-    case LT:
-    case LE:
-      return code;
-
-    case GTU:
-      return GT;
-    case GEU:
-      return GE;
-    case LTU:
-      return LT;
-    case LEU:
-      return LE;
-
-    default:
-      gcc_unreachable ();
-    }
+  unsigned int flags = condition_to_flags (code);
+  return flags_to_condition ((flags & ~FLAGS_UNSIGNED) | FLAGS_SIGNED, true);
 }
 
 /* Return nonzero if CODE1 is more strict than CODE2, i.e., if the
@@ -695,74 +715,12 @@ comparison_dominates_p (enum rtx_code co
   if (code1 == UNKNOWN || code2 == UNKNOWN)
     return 0;
 
-  if (code1 == code2)
-    return 1;
-
-  switch (code1)
-    {
-    case UNEQ:
-      if (code2 == UNLE || code2 == UNGE)
-       return 1;
-      break;
-
-    case EQ:
-      if (code2 == LE || code2 == LEU || code2 == GE || code2 == GEU
-         || code2 == ORDERED)
-       return 1;
-      break;
-
-    case UNLT:
-      if (code2 == UNLE || code2 == NE)
-       return 1;
-      break;
-
-    case LT:
-      if (code2 == LE || code2 == NE || code2 == ORDERED || code2 == LTGT)
-       return 1;
-      break;
-
-    case UNGT:
-      if (code2 == UNGE || code2 == NE)
-       return 1;
-      break;
-
-    case GT:
-      if (code2 == GE || code2 == NE || code2 == ORDERED || code2 == LTGT)
-       return 1;
-      break;
-
-    case GE:
-    case LE:
-      if (code2 == ORDERED)
-       return 1;
-      break;
-
-    case LTGT:
-      if (code2 == NE || code2 == ORDERED)
-       return 1;
-      break;
-
-    case LTU:
-      if (code2 == LEU || code2 == NE)
-       return 1;
-      break;
-
-    case GTU:
-      if (code2 == GEU || code2 == NE)
-       return 1;
-      break;
-
-    case UNORDERED:
-      if (code2 == NE || code2 == UNEQ || code2 == UNLE || code2 == UNLT
-         || code2 == UNGE || code2 == UNGT)
-       return 1;
-      break;
-
-    default:
-      break;
-    }
-
-  return 0;
+  unsigned int flags1 = condition_to_flags (code1);
+  unsigned int flags2 = condition_to_flags (code2);
+  /* Make sure that the conditions do not use different sign interpretations
+     and that FLAGS2 contains every condition that FLAGS1 contains.  */
+  return (((flags1 | flags2) & FLAGS_SIGNEDNESS) != FLAGS_SIGNEDNESS
+         && (flags1 & ~flags2 & FLAGS_ORDER) == 0);
 }
 
 /* Return 1 if INSN is an unconditional jump and nothing else.  */

Reply via email to