Patchwork [rs6000] Improve FP comparisons with -fno-trapping-math

login
register
mail settings
Submitter Eric Botcazou
Date May 29, 2013, 8:53 a.m.
Message ID <1897087.gMVATXCth1@polaris>
Download mbox | patch
Permalink /patch/247179/
State New
Headers show

Comments

Eric Botcazou - May 29, 2013, 8:53 a.m.
Hi,

on most platforms the Ada compiler doesn't enable trap-on-FP-exceptions, so 
it's appropriate to set -fno-trapping-math there, which has been done in
  http://gcc.gnu.org/ml/gcc-patches/2013-05/msg01461.html

However doing so can have an adverse effect if the architecture doesn't have a 
sufficiently rich set of FP comparison instructions because -fno-trapping-math 
allows the middle-end and the optimizers to turn the negation of signaling FP 
comparison operators into quiet FP unordered operators, e.g. not(<) into !>= 
and the quiet unordered operators aren't supported universally.  The effect is 
that you need 2 comparisons instead of just 1 to achieve the desired result.

The second (less surprising) example is the e500 architecture which features 
FP instructions operating on GP registers.  It has neither ORDERED/UNORDERED 
nor the UNLT/UNLE/UNGT/UNGE operators so, in this case, you get a new call to 
the libgcc routine implementing UNORDERED in addition to the comparison.  The 
fix is to make it possible to use the (negated form of the) ordered comparison 
instructions, which are signaling, for the quiet unordered operators when the 
flag -fno-trapping-math is in effect.  As a matter of fact, a partial support 
was already there (e.g. in rs6000_generate_compare) but it was disabled.

Tested on e500v2/Linux and PowerPC/Linux, OK for the mainline?


2013-05-29  Eric Botcazou  <ebotcazou@adacore.com>

	* config/rs6000/predicates.md (rs6000_cbranch_operator): Accept some
	unordered comparison operators when -fno-trapping-math is in effect
	on the e500.
	* config/rs6000/rs6000.c (rs6000_generate_compare): Remove dead code
	and implement unordered comparison operators properly on the e500.

Patch

Index: config/rs6000/predicates.md
===================================================================
--- config/rs6000/predicates.md	(revision 199343)
+++ config/rs6000/predicates.md	(working copy)
@@ -1121,9 +1121,16 @@  (define_predicate "branch_comparison_ope
 						   GET_MODE (XEXP (op, 0))),
 			  1"))))
 
+;; Return 1 if OP is a valid comparison operator for "cbranch" instructions.
+;; If we're assuming that FP operations cannot generate user-visible traps,
+;; then on e500 we can use the ordered-signaling instructions to implement
+;; the unordered-quiet FP comparison predicates modulo a reversal.
 (define_predicate "rs6000_cbranch_operator"
   (if_then_else (match_test "TARGET_HARD_FLOAT && !TARGET_FPRS")
-		(match_operand 0 "ordered_comparison_operator")
+		(if_then_else (match_test "flag_trapping_math")
+			      (match_operand 0 "ordered_comparison_operator")
+			      (ior (match_operand 0 "ordered_comparison_operator")
+				   (match_code ("unlt,unle,ungt,unge"))))
 		(match_operand 0 "comparison_operator")))
 
 ;; Return 1 if OP is a comparison operation that is valid for an SCC insn --
Index: config/rs6000/rs6000.c
===================================================================
--- config/rs6000/rs6000.c	(revision 199343)
+++ config/rs6000/rs6000.c	(working copy)
@@ -16086,16 +16086,41 @@  rs6000_generate_compare (rtx cmp, enum m
     {
       rtx cmp, or_result, compare_result2;
       enum machine_mode op_mode = GET_MODE (op0);
+      bool reverse_p;
 
       if (op_mode == VOIDmode)
 	op_mode = GET_MODE (op1);
 
+      /* First reverse the condition codes that aren't directly supported.  */
+      switch (code)
+	{
+	  case NE:
+	  case UNLT:
+	  case UNLE:
+	  case UNGT:
+	  case UNGE:
+	    code = reverse_condition_maybe_unordered (code);
+	    reverse_p = true;
+	    break;
+
+	  case EQ:
+	  case LT:
+	  case LE:
+	  case GT:
+	  case GE:
+	    reverse_p = false;
+	    break;
+
+	  default:
+	    gcc_unreachable ();
+	}
+
       /* The E500 FP compare instructions toggle the GT bit (CR bit 1) only.
 	 This explains the following mess.  */
 
       switch (code)
 	{
-	case EQ: case UNEQ: case NE: case LTGT:
+	case EQ:
 	  switch (op_mode)
 	    {
 	    case SFmode:
@@ -16121,7 +16146,8 @@  rs6000_generate_compare (rtx cmp, enum m
 	    }
 	  break;
 
-	case GT: case GTU: case UNGT: case UNGE: case GE: case GEU:
+	case GT:
+	case GE:
 	  switch (op_mode)
 	    {
 	    case SFmode:
@@ -16147,7 +16173,8 @@  rs6000_generate_compare (rtx cmp, enum m
 	    }
 	  break;
 
-	case LT: case LTU: case UNLT: case UNLE: case LE: case LEU:
+	case LT: 
+	case LE:
 	  switch (op_mode)
 	    {
 	    case SFmode:
@@ -16172,24 +16199,16 @@  rs6000_generate_compare (rtx cmp, enum m
 	      gcc_unreachable ();
 	    }
 	  break;
+
         default:
           gcc_unreachable ();
 	}
 
       /* Synthesize LE and GE from LT/GT || EQ.  */
-      if (code == LE || code == GE || code == LEU || code == GEU)
+      if (code == LE || code == GE)
 	{
 	  emit_insn (cmp);
 
-	  switch (code)
-	    {
-	    case LE: code = LT; break;
-	    case GE: code = GT; break;
-	    case LEU: code = LT; break;
-	    case GEU: code = GT; break;
-	    default: gcc_unreachable ();
-	    }
-
 	  compare_result2 = gen_reg_rtx (CCFPmode);
 
 	  /* Do the EQ.  */
@@ -16216,23 +16235,18 @@  rs6000_generate_compare (rtx cmp, enum m
 	    default:
 	      gcc_unreachable ();
 	    }
+
 	  emit_insn (cmp);
 
 	  /* OR them together.  */
 	  or_result = gen_reg_rtx (CCFPmode);
 	  cmp = gen_e500_cr_ior_compare (or_result, compare_result,
-					   compare_result2);
+					 compare_result2);
 	  compare_result = or_result;
-	  code = EQ;
-	}
-      else
-	{
-	  if (code == NE || code == LTGT)
-	    code = NE;
-	  else
-	    code = EQ;
 	}
 
+      code = reverse_p ? NE : EQ;
+
       emit_insn (cmp);
     }
   else