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. */