Patchwork [03/14] rx: Cleanup conditional branches.

login
register
mail settings
Submitter Richard Henderson
Date Jan. 14, 2011, 4:08 p.m.
Message ID <1295021309-28608-4-git-send-email-rth@redhat.com>
Download mbox | patch
Permalink /patch/78939/
State New
Headers show

Comments

Richard Henderson - Jan. 14, 2011, 4:08 p.m.
From: Richard Henderson <rth@twiddle.net>

Use match_operator, not code_iterators.  Use a new helper function,
rx_split_cbranch.  Get the modes right on the comparisons.  Distinguish
fp comparisons with CC_Fmode.
---
 gcc/config/rx/predicates.md |   17 ++
 gcc/config/rx/rx-protos.h   |    3 +-
 gcc/config/rx/rx.c          |  353 +++++++++++++++++++++++++++-------------
 gcc/config/rx/rx.md         |  381 ++++++++++++++++++++-----------------------
 4 files changed, 436 insertions(+), 318 deletions(-)
Paolo Bonzini - Jan. 18, 2011, 9:03 a.m.
On 01/14/2011 05:08 PM, rth@redhat.com wrote:
> +    case UNLE:
> +      cmp1 = UNORDERED;
> +      cmp2 = GT;
> +      swap = true;
> +      break;
> +    case UNGT:
> +      cmp1 = UNORDERED;
> +      cmp2 = LE;
> +      swap = true;
> +      break;

Why not return UNGT/UNLE for cmp2 here, instead of hacking around it in 
cstoresf4?  Both definitions are equally valid.

Also, a comment above rx_split_fp_compare (or at least in case LTGT) 
should say whether the resulting comparison must be ANDed or ORed.

Paolo
Paolo Bonzini - Jan. 19, 2011, 8:48 a.m.
On 01/18/2011 10:03 AM, Paolo Bonzini wrote:
>> +    case UNLE:
>> +      cmp1 = UNORDERED;
>> +      cmp2 = GT;
>> +      swap = true;
>> +      break;
>> +    case UNGT:
>> +      cmp1 = UNORDERED;
>> +      cmp2 = LE;
>> +      swap = true;
>> +      break;
>
> Why not return UNGT/UNLE for cmp2 here, instead of hacking around it in
> cstoresf4?  Both definitions are equally valid.

Ok, what I wrote made no sense, but I am officially confused.  In the 
cbranchsf4 expander you do:

> +  if (rx_split_fp_compare (GET_CODE (operands[0]), &cmp1, &cmp2))
> +    {
> +      rtx op1, op2;
> +
> +      if (cmp2 != UNKNOWN)
> +	{
> +	  gcc_assert (cmp1 == UNORDERED);
> +	  if (cmp2 == GT)
> +	    cmp1 = UNGT;
> +	  else if (cmp2 == LE)
> +	    cmp1 = UNLE;
> +	  else
> +	    gcc_unreachable ();
> +	}
> +
> +      op1 = operands[2];
> +      op2 = operands[1];
> +      operands[0] = gen_rtx_fmt_ee (cmp1, VOIDmode, op1, op2);
> +      operands[1] = op1;
> +      operands[2] = op2;
> +    }
> +})


This creates an UNGT or UNLE which is not recognized by the *cbranchsf4 
insn:

> +(define_predicate "rx_fp_comparison_operator"
> +  (match_code "eq,ne,lt,ge,ordered,unordered,uneq,unlt,unge,ltgt")
> +)
>
>  [...]
>
> +(define_insn_and_split "*cbranchsf4"
> +	(if_then_else
> +	  (match_operator 3 "rx_fp_comparison_operator"
> +	    [(match_operand:SF  0 "register_operand"  "r")
> +	     (match_operand:SF  1 "rx_source_operand" "rFiQ")])
> +	  (match_operand        2 "label_ref_operand" "")
> +	  (pc)))]

In the worst case, this will cause pessimizations; I am afraid this is 
what happens since can_compare_p looks at predicates only.  In the best 
case, the middle-end will take care itself of the split and swap in 
do_compare_rtx_and_jump and you have useless code in the backend. 
Likewise for cstore in emit_store_flag (which does splitting) and 
emit_store_flag_1 (which does swapping).

emit_store_flag also tries similar tricks to the ones you do using cmov, 
though in the case in which the cmove fails it will not try the OR of 
two conditions.

(All this stuff is new in GCC 4.5 from the cond-optab merge).

So, are you sure you cannot just remove the cbranchsf4 expander, and 
remove the asterisk from the insn?  It is even possible that the insn 
could be simplified, and similarly for cstoresf4.

Paolo
Richard Henderson - Jan. 19, 2011, 4:22 p.m.
On 01/19/2011 12:48 AM, Paolo Bonzini wrote:
> On 01/18/2011 10:03 AM, Paolo Bonzini wrote:
>>> +    case UNLE:
>>> +      cmp1 = UNORDERED;
>>> +      cmp2 = GT;
>>> +      swap = true;
>>> +      break;
>>> +    case UNGT:
>>> +      cmp1 = UNORDERED;
>>> +      cmp2 = LE;
>>> +      swap = true;
>>> +      break;
>>
>> Why not return UNGT/UNLE for cmp2 here, instead of hacking around it in
>> cstoresf4?  Both definitions are equally valid.
> 
> Ok, what I wrote made no sense, but I am officially confused.  In the cbranchsf4 expander you do:
> 
>> +  if (rx_split_fp_compare (GET_CODE (operands[0]), &cmp1, &cmp2))
>> +    {
>> +      rtx op1, op2;
>> +
>> +      if (cmp2 != UNKNOWN)
>> +    {
>> +      gcc_assert (cmp1 == UNORDERED);
>> +      if (cmp2 == GT)
>> +        cmp1 = UNGT;
>> +      else if (cmp2 == LE)
>> +        cmp1 = UNLE;
>> +      else
>> +        gcc_unreachable ();
>> +    }
>> +
>> +      op1 = operands[2];
>> +      op2 = operands[1];
>> +      operands[0] = gen_rtx_fmt_ee (cmp1, VOIDmode, op1, op2);
>> +      operands[1] = op1;
>> +      operands[2] = op2;
>> +    }
>> +})
> 
> 
> This creates an UNGT or UNLE which is not recognized by the *cbranchsf4 insn:
> 
>> +(define_predicate "rx_fp_comparison_operator"
>> +  (match_code "eq,ne,lt,ge,ordered,unordered,uneq,unlt,unge,ltgt")
>> +)

You're right, there's something off here.  After the split indicates that
the operands must be swapped, I was attempting to re-assemble the two-part
comparison that would have resulted.  Except that after the split, we should
not have had UNGT/UNLE there, but UNLT/UNGE.  Which could just as easily be
had from swap_comparison (GET_CODE (operands[0])).

> So, are you sure you cannot just remove the cbranchsf4 expander, and
> remove the asterisk from the insn? It is even possible that the insn
> could be simplified, and similarly for cstoresf4.

I think you're right.  Certainly cstoresf4 is not important enough to
carry around all this extra code.  And get it wrong, as you've seen.

Consider all of this deleted, and rx_fp_compare_operator reduced to 
the bare essentials -- un/ordered + the non-swapping normal ops.


r~
Paolo Bonzini - Jan. 19, 2011, 4:43 p.m.
On 01/19/2011 05:22 PM, Richard Henderson wrote:
> Consider all of this deleted, and rx_fp_compare_operator reduced to
> the bare essentials -- un/ordered + the non-swapping normal ops.

I think you're getting everything else for free anyway (especially if 
you leave the non-swapping, native unordered ops in).

Paolo

Patch

diff --git a/gcc/config/rx/predicates.md b/gcc/config/rx/predicates.md
index e1b3f0c..0ab4df3 100644
--- a/gcc/config/rx/predicates.md
+++ b/gcc/config/rx/predicates.md
@@ -293,3 +293,20 @@ 
   element = XVECEXP (op, 0, count - 1);
   return GET_CODE (element) == RETURN;
 })
+
+(define_predicate "label_ref_operand"
+  (match_code "label_ref")
+)
+
+(define_predicate "rx_z_comparison_operator"
+  (match_code "eq,ne")
+)
+
+(define_predicate "rx_zs_comparison_operator"
+  (match_code "eq,ne,lt,ge")
+)
+
+;; GT, LE, UNLE, UNGT omitted due to operand swap required.
+(define_predicate "rx_fp_comparison_operator"
+  (match_code "eq,ne,lt,ge,ordered,unordered,uneq,unlt,unge,ltgt")
+)
diff --git a/gcc/config/rx/rx-protos.h b/gcc/config/rx/rx-protos.h
index 02e12ed..f0c2105 100644
--- a/gcc/config/rx/rx-protos.h
+++ b/gcc/config/rx/rx-protos.h
@@ -34,12 +34,13 @@  extern void             rx_emit_stack_popm (rtx *, bool);
 extern void             rx_emit_stack_pushm (rtx *);
 extern void		rx_expand_epilogue (bool);
 extern bool		rx_expand_insv (rtx *);
-extern const char *	rx_gen_cond_branch_template (rtx, bool);
 extern char *		rx_gen_move_template (rtx *, bool);
 extern bool		rx_is_legitimate_constant (rtx);
 extern bool 		rx_is_mode_dependent_addr (rtx);
 extern bool		rx_is_restricted_memory_address (rtx, Mmode);
 extern void		rx_notice_update_cc (rtx body, rtx insn);
+extern void		rx_split_cbranch (Mmode, Rcode, rtx, rtx, rtx);
+extern bool		rx_split_fp_compare (Rcode, Rcode *, Rcode *);
 extern Mmode		rx_select_cc_mode (Rcode, rtx, rtx);
 #endif
 
diff --git a/gcc/config/rx/rx.c b/gcc/config/rx/rx.c
index 9a163fe..a2ff95e 100644
--- a/gcc/config/rx/rx.c
+++ b/gcc/config/rx/rx.c
@@ -53,6 +53,15 @@ 
 
 static void rx_print_operand (FILE *, rtx, int);
 
+#define CC_FLAG_S	(1 << 0)
+#define CC_FLAG_Z	(1 << 1)
+#define CC_FLAG_O	(1 << 2)
+#define CC_FLAG_C	(1 << 3)
+#define CC_FLAG_FP	(1 << 4)	/* fake, to differentiate CC_Fmode */
+
+static unsigned int flags_from_mode (enum machine_mode mode);
+static unsigned int flags_from_code (enum rtx_code code);
+
 enum rx_cpu_types  rx_cpu_type = RX600;
 
 /* Return true if OP is a reference to an object in a small data area.  */
@@ -395,21 +404,84 @@  rx_print_operand (FILE * file, rtx op, int letter)
       break;
 
     case 'B':
-      switch (GET_CODE (op))
-	{
-	case LT:  fprintf (file, "lt"); break;
-	case GE:  fprintf (file, "ge"); break;
-	case GT:  fprintf (file, "gt"); break;
-	case LE:  fprintf (file, "le"); break;
-	case GEU: fprintf (file, "geu"); break;
-	case LTU: fprintf (file, "ltu"); break;
-	case GTU: fprintf (file, "gtu"); break;
-	case LEU: fprintf (file, "leu"); break;
-	case EQ:  fprintf (file, "eq"); break;
-	case NE:  fprintf (file, "ne"); break;
-	default:  debug_rtx (op); gcc_unreachable ();
-	}
-      break;
+      {
+	enum rtx_code code = GET_CODE (op);
+	enum machine_mode mode = GET_MODE (XEXP (op, 0));
+	const char *ret;
+
+	if (mode == CC_Fmode)
+	  {
+	    /* C flag is undefined, and O flag carries unordered.  None of the
+	       branch combinations that include O use it helpfully.  */
+	    switch (code)
+	      {
+	      case ORDERED:
+		ret = "no";
+		break;
+	      case UNORDERED:
+		ret = "o";
+		break;
+	      case LT:
+		ret = "n";
+		break;
+	      case GE:
+		ret = "pz";
+		break;
+	      case EQ:
+		ret = "eq";
+		break;
+	      case NE:
+		ret = "ne";
+		break;
+	      default:
+		gcc_unreachable ();
+	      }
+	  }
+	else
+	  {
+	    switch (code)
+	      {
+	      case LT:
+		ret = "n";
+		break;
+	      case GE:
+		ret = "pz";
+		break;
+	      case GT:
+		ret = "gt";
+		break;
+	      case LE:
+		ret = "le";
+		break;
+	      case GEU:
+		ret = "geu";
+		break;
+	      case LTU:
+		ret = "ltu";
+		break;
+	      case GTU:
+		ret = "gtu";
+		break;
+	      case LEU:
+		ret = "leu";
+		break;
+	      case EQ:
+		ret = "eq";
+		break;
+	      case NE:
+		ret = "ne";
+		break;
+	      default:
+		gcc_unreachable ();
+	      }
+	    /* ??? Removable when all of cbranch, cstore, cmove are updated. */
+	    if (GET_MODE_CLASS (mode) == MODE_CC)
+	    gcc_checking_assert ((flags_from_code (code)
+				  & ~flags_from_mode (mode)) == 0);
+	  }
+	fputs (ret, file);
+	break;
+      }
 
     case 'C':
       gcc_assert (CONST_INT_P (op));
@@ -700,51 +772,6 @@  rx_gen_move_template (rtx * operands, bool is_movu)
 	   extension, src_template, dst_template);
   return out_template;
 }
-
-/* Returns an assembler template for a conditional branch instruction.  */
-
-const char *
-rx_gen_cond_branch_template (rtx condition, bool reversed)
-{
-  enum rtx_code code = GET_CODE (condition);
-
-  if (reversed)
-    {
-      if (rx_float_compare_mode)
-	code = reverse_condition_maybe_unordered (code);
-      else
-	code = reverse_condition (code);
-    }
-
-  /* We do not worry about encoding the branch length here as GAS knows
-     how to choose the smallest version, and how to expand a branch that
-     is to a destination that is out of range.  */
-
-  switch (code)
-    {
-    case UNEQ:	    return "bo\t1f\n\tbeq\t%0\n1:";
-    case LTGT:	    return "bo\t1f\n\tbne\t%0\n1:";
-    case UNLT:      return "bo\t1f\n\tbn\t%0\n1:";
-    case UNGE:      return "bo\t1f\n\tbpz\t%0\n1:";
-    case UNLE:      return "bo\t1f\n\tbgt\t1f\n\tbra\t%0\n1:";
-    case UNGT:      return "bo\t1f\n\tble\t1f\n\tbra\t%0\n1:";
-    case UNORDERED: return "bo\t%0";
-    case ORDERED:   return "bno\t%0";
-
-    case LT:        return rx_float_compare_mode ? "bn\t%0" : "blt\t%0";
-    case GE:        return rx_float_compare_mode ? "bpz\t%0" : "bge\t%0";
-    case GT:        return "bgt\t%0";
-    case LE:        return "ble\t%0";
-    case GEU:       return "bgeu\t%0";
-    case LTU:       return "bltu\t%0";
-    case GTU:       return "bgtu\t%0";
-    case LEU:       return "bleu\t%0";
-    case EQ:        return "beq\t%0";
-    case NE:        return "bne\t%0";
-    default:
-      gcc_unreachable ();
-    }
-}
 
 /* Return VALUE rounded up to the next ALIGNMENT boundary.  */
 
@@ -2543,69 +2570,100 @@  rx_trampoline_init (rtx tramp, tree fndecl, rtx chain)
     }
 }
 
-
-static enum machine_mode
-rx_cc_modes_compatible (enum machine_mode m1, enum machine_mode m2)
+static int
+rx_memory_move_cost (enum machine_mode mode, reg_class_t regclass, bool in)
 {
-  if (m1 == CCmode)
-    return m2;
-  if (m2 == CCmode)
-    return m1;
-  if (m1 == m2)
-    return m1;
-  if (m1 == CC_ZSmode)
-    return m1;
-  if (m2 == CC_ZSmode)
-    return m2;
-  return VOIDmode;   
+  return 2 + memory_move_secondary_cost (mode, regclass, in);
 }
 
-#define CC_FLAG_S (1 << 0)
-#define CC_FLAG_Z (1 << 1)
-#define CC_FLAG_O (1 << 2)
-#define CC_FLAG_C (1 << 3)
+/* Convert a CC_MODE to the set of flags that it represents.  */
 
 static unsigned int
-flags_needed_for_conditional (rtx conditional)
+flags_from_mode (enum machine_mode mode)
 {
-  switch (GET_CODE (conditional))
+  switch (mode)
     {
-    case LE:
-    case GT:	return CC_FLAG_S | CC_FLAG_Z | CC_FLAG_O;
-
-    case LEU:
-    case GTU:	return CC_FLAG_Z | CC_FLAG_C;
-
-    case LT:
-    case GE:	return CC_FLAG_S | CC_FLAG_O;
-
-    case LTU:
-    case GEU:	return CC_FLAG_C;
+    case CC_ZSmode:
+      return CC_FLAG_S | CC_FLAG_Z;
+    case CC_ZSOmode:
+      return CC_FLAG_S | CC_FLAG_Z | CC_FLAG_O;
+    case CC_ZSCmode:
+      return CC_FLAG_S | CC_FLAG_Z | CC_FLAG_C;
+    case CCmode:
+      return CC_FLAG_S | CC_FLAG_Z | CC_FLAG_O | CC_FLAG_C;
+    case CC_Fmode:
+      return CC_FLAG_FP;
+    default:
+      gcc_unreachable ();
+    }
+}
 
-    case EQ:
-    case NE:	return CC_FLAG_Z;
+/* Convert a set of flags to a CC_MODE that can implement it.  */
 
-    default:	gcc_unreachable ();
+static enum machine_mode
+mode_from_flags (unsigned int f)
+{
+  if (f & CC_FLAG_FP)
+    return CC_Fmode;
+  if (f & CC_FLAG_O)
+    {
+      if (f & CC_FLAG_C)
+	return CCmode;
+      else
+	return CC_ZSOmode;
     }
+  else if (f & CC_FLAG_C)
+    return CC_ZSCmode;
+  else
+    return CC_ZSmode;
 }
 
+/* Convert an RTX_CODE to the set of flags needed to implement it.
+   This assumes an integer comparison.  */
+
 static unsigned int
-flags_from_mode (enum machine_mode mode)
+flags_from_code (enum rtx_code code)
 {
-  switch (mode)
+  switch (code)
     {
-    case CCmode:     return CC_FLAG_S | CC_FLAG_Z | CC_FLAG_O | CC_FLAG_C;
-    case CC_ZSmode:  return CC_FLAG_S | CC_FLAG_Z;
-    case CC_ZSOmode: return CC_FLAG_S | CC_FLAG_Z | CC_FLAG_O;
-    case CC_ZSCmode: return CC_FLAG_S | CC_FLAG_Z | CC_FLAG_C;
-    default:         gcc_unreachable ();
+    case LT:
+    case GE:
+      return CC_FLAG_S;
+    case GT:
+    case LE:
+      return CC_FLAG_S | CC_FLAG_O | CC_FLAG_Z;
+    case GEU:
+    case LTU:
+      return CC_FLAG_C;
+    case GTU:
+    case LEU:
+      return CC_FLAG_C | CC_FLAG_Z;
+    case EQ:
+    case NE:
+      return CC_FLAG_Z;
+    default:
+      gcc_unreachable ();
     }
 }
 
-static int
-rx_memory_move_cost (enum machine_mode mode, reg_class_t regclass, bool in)
+/* Return a CC_MODE of which both M1 and M2 are subsets.  */
+
+static enum machine_mode
+rx_cc_modes_compatible (enum machine_mode m1, enum machine_mode m2)
 {
-  return 2 + memory_move_secondary_cost (mode, regclass, in);
+  unsigned f;
+
+  /* Early out for identical modes.  */
+  if (m1 == m2)
+    return m1;
+
+  /* There's no valid combination for FP vs non-FP.  */
+  f = flags_from_mode (m1) | flags_from_mode (m2);
+  if (f & CC_FLAG_FP)
+    return VOIDmode;
+
+  /* Otherwise, see what mode can implement all the flags.  */
+  return mode_from_flags (f);
 }
 
 /* Return the minimal CC mode needed to implement (CMP_CODE X Y).  */
@@ -2616,24 +2674,89 @@  rx_select_cc_mode (enum rtx_code cmp_code, rtx x, rtx y ATTRIBUTE_UNUSED)
   if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
     return CC_Fmode;
 
-  switch (cmp_code)
+  return mode_from_flags (flags_from_code (cmp_code));
+}
+
+/* Split the floating-point comparison IN into individual comparisons
+   O1 and O2.  O2 may be UNKNOWN if there is no second comparison.
+   Return true iff the comparison operands must be swapped.  */
+
+bool
+rx_split_fp_compare (enum rtx_code in, enum rtx_code *o1, enum rtx_code *o2)
+{
+  enum rtx_code cmp1 = in, cmp2 = UNKNOWN;
+  bool swap = false;
+
+  switch (in)
     {
-    case EQ:
-    case NE:
+    case ORDERED:
+    case UNORDERED:
     case LT:
     case GE:
-      return CC_ZSmode;
+    case EQ:
+    case NE:
+      break;
+
     case GT:
     case LE:
-      return CC_ZSOmode;
-    case GEU:
-    case LTU:
-    case GTU:
-    case LEU:
-      return CC_ZSCmode;
+      cmp1 = swap_condition (cmp1);
+      swap = true;
+      break;
+
+    case UNEQ:
+      cmp1 = UNORDERED;
+      cmp2 = EQ;
+      break;
+    case UNLT:
+      cmp1 = UNORDERED;
+      cmp2 = LT;
+      break;
+    case UNGE:
+      cmp1 = UNORDERED;
+      cmp2 = GE;
+      break;
+    case UNLE:
+      cmp1 = UNORDERED;
+      cmp2 = GT;
+      swap = true;
+      break;
+    case UNGT:
+      cmp1 = UNORDERED;
+      cmp2 = LE;
+      swap = true;
+      break;
+    case LTGT:
+      cmp1 = ORDERED;
+      cmp2 = NE;
+      break;
+
     default:
-      return CCmode;
+      gcc_unreachable ();
     }
+
+  *o1 = cmp1;
+  *o2 = cmp2;
+  return swap;
+}
+
+/* Split the conditional branch.  Emit (COMPARE C1 C2) into CC_REG with
+   CC_MODE, and use that in branches based on that compare.  */
+
+void
+rx_split_cbranch (enum machine_mode cc_mode, enum rtx_code cmp1,
+		  rtx c1, rtx c2, rtx label)
+{
+  rtx flags, x;
+
+  flags = gen_rtx_REG (cc_mode, CC_REG);
+  x = gen_rtx_COMPARE (cc_mode, c1, c2);
+  x = gen_rtx_SET (VOIDmode, flags, x);
+  emit_insn (x);
+
+  x = gen_rtx_fmt_ee (cmp1, VOIDmode, flags, const0_rtx);
+  x = gen_rtx_IF_THEN_ELSE (VOIDmode, x, label, pc_rtx);
+  x = gen_rtx_SET (VOIDmode, pc_rtx, x);
+  emit_jump_insn (x);
 }
 
 
diff --git a/gcc/config/rx/rx.md b/gcc/config/rx/rx.md
index 545c2fb..c2161a2 100644
--- a/gcc/config/rx/rx.md
+++ b/gcc/config/rx/rx.md
@@ -19,14 +19,6 @@ 
 ;; <http://www.gnu.org/licenses/>.
 
 
-;; This code iterator allows all branch instructions to
-;; be generated from a single define_expand template.
-(define_code_iterator most_cond [eq ne gt ge lt le gtu geu ltu leu
-				 unordered ordered ])
-
-;; Likewise, but only the ones that use Z or S.
-(define_code_iterator zs_cond [eq ne gtu geu ltu leu ])
-
 ;; This code iterator is used for sign- and zero- extensions.
 (define_mode_iterator small_int_modes [(HI "") (QI "")])
 
@@ -150,6 +142,8 @@ 
 (define_insn_reservation "throughput_18_latency_18"  1
   (eq_attr "timings" "1818") "throughput*18")
 
+;; ----------------------------------------------------------------------------
+
 ;; Comparisons
 
 ;; Note - we do not specify the two instructions necessary to perform
@@ -160,245 +154,228 @@ 
 
 (define_expand "cbranchsi4"
   [(set (pc)
-	(if_then_else (match_operator 0 "comparison_operator"
-				      [(match_operand:SI 1 "register_operand")
-				       (match_operand:SI 2 "rx_source_operand")])
-		      (label_ref (match_operand 3 ""))
-		      (pc)))
-   ]
-  ""
+	(if_then_else
+	  (match_operator 0 "comparison_operator"
+	    [(match_operand:SI 1 "register_operand")
+	     (match_operand:SI 2 "rx_source_operand")])
+	  (label_ref (match_operand 3 ""))
+	  (pc)))]
   ""
 )
 
-(define_insn_and_split "*cbranchsi4_<code>"
+(define_insn_and_split "*cbranchsi4"
   [(set (pc)
-	(if_then_else (most_cond (match_operand:SI  0 "register_operand"  "r")
-				    (match_operand:SI  1 "rx_source_operand" "riQ"))
-		      (label_ref (match_operand        2 "" ""))
-		      (pc)))
-   ]
+	(if_then_else
+	  (match_operator 3 "comparison_operator"
+	    [(match_operand:SI  0 "register_operand"  "r")
+	     (match_operand:SI  1 "rx_source_operand" "riQ")])
+	  (match_operand        2 "label_ref_operand" "")
+	  (pc)))]
   ""
   "#"
   "reload_completed"
   [(const_int 0)]
-  "
-  /* We contstruct the split by hand as otherwise the JUMP_LABEL
-     attribute is not set correctly on the jump insn.  */
-  emit_insn (gen_cmpsi (operands[0], operands[1]));
-  
-  emit_jump_insn (gen_conditional_branch (operands[2],
-  		 gen_rtx_fmt_ee (<most_cond:CODE>, CCmode,
-				 gen_rtx_REG (CCmode, CC_REG), const0_rtx)));
-  "
-)
+{
+  rx_split_cbranch (CCmode, GET_CODE (operands[3]),
+		    operands[0], operands[1], operands[2]);
+  DONE;
+})
 
-;; -----------------------------------------------------------------------------
-;; These two are the canonical TST/branch insns.  However, GCC
-;; generates a wide variety of tst-like patterns, we catch those
-;; below.
-(define_insn_and_split "*tstbranchsi4_<code>"
-  [(set (pc)
-	(if_then_else (zs_cond (and:SI (match_operand:SI  0 "register_operand"  "r")
-				       (match_operand:SI  1 "rx_source_operand" "riQ"))
-			       (const_int 0))
-		      (label_ref (match_operand 2 "" ""))
-		      (pc)))
-   ]
-  ""
-  "#"
+(define_insn "*cmpsi"
+  [(set (reg:CC CC_REG)
+	(compare:CC (match_operand:SI 0 "register_operand"  "r,r,r,r,r,r,r")
+		    (match_operand:SI 1 "rx_source_operand" "r,Uint04,Int08,Sint16,Sint24,i,Q")))]
   "reload_completed"
-  [(const_int 0)]
-  "
-  emit_insn (gen_tstsi (operands[0], operands[1]));
-  
-  emit_jump_insn (gen_conditional_branch (operands[2],
-  		 gen_rtx_fmt_ee (<zs_cond:CODE>, CCmode,
-				 gen_rtx_REG (CCmode, CC_REG), const0_rtx)));
-  "
+  "cmp\t%Q1, %0"
+  [(set_attr "timings" "11,11,11,11,11,11,33")
+   (set_attr "length"  "2,2,3,4,5,6,5")]
 )
 
-;; Inverse of above
-(define_insn_and_split "*tstbranchsi4r_<code>"
+;; Canonical method for representing TST.
+(define_insn_and_split "*cbranchsi4_tst"
   [(set (pc)
-	(if_then_else (zs_cond (and:SI (match_operand:SI  0 "register_operand"  "r")
-				       (match_operand:SI  1 "rx_source_operand" "riQ"))
-			       (const_int 0))
-		      (pc)
-		      (label_ref (match_operand 2 "" ""))))
-   ]
+	(if_then_else
+	  (match_operator 3 "rx_zs_comparison_operator"
+	    [(and:SI (match_operand:SI  0 "register_operand"  "r")
+		     (match_operand:SI  1 "rx_source_operand" "riQ"))
+	     (const_int 0)])
+	  (match_operand 2 "label_ref_operand" "")
+	  (pc)))]
   ""
   "#"
   "reload_completed"
   [(const_int 0)]
-  "
-  emit_insn (gen_tstsi (operands[0], operands[1]));
-  
-  emit_jump_insn (gen_conditional_branch (operands[2],
-  		 gen_rtx_fmt_ee (reverse_condition (<zs_cond:CODE>), CCmode,
-				 gen_rtx_REG (CCmode, CC_REG), const0_rtx)));
-  "
-)
+{
+  rx_split_cbranch (CC_ZSmode, GET_CODE (operands[3]),
+		    XEXP (operands[3], 0), XEXP (operands[3], 1),
+		    operands[2]);
+  DONE;
+})
 
 ;; Various other ways that GCC codes "var & const"
-
-(define_insn_and_split "*tstbranchsi4m_eq"
-  [(set (pc)
-	(if_then_else (eq (zero_extract:SI (match_operand:SI  0 "register_operand"  "r")
-					   (match_operand  1 "rx_constshift_operand" "i")
-					   (match_operand  2 "rx_constshift_operand" "i"))
-			  (const_int 0))
-		      (label_ref (match_operand        3 "" ""))
-		      (pc)))
-   ]
-  ""
-  "#"
-  ""
-  [(set (pc)
-	(if_then_else (eq (and:SI (match_dup  0)
-				  (match_dup 4))
-			  (const_int 0))
-		      (label_ref (match_dup 3))
-		      (pc)))
-   ]
-  "operands[4] = GEN_INT (((1 << INTVAL (operands[1]))-1) << INTVAL (operands[2]));"
-)
-
-(define_insn_and_split "*tstbranchsi4m_ne"
+(define_insn_and_split "*cbranchsi4_tst_ext"
   [(set (pc)
-	(if_then_else (ne (zero_extract:SI (match_operand:SI  0 "register_operand"  "r")
-					   (match_operand  1 "rx_constshift_operand" "i")
-					   (match_operand  2 "rx_constshift_operand" "i"))
-			  (const_int 0))
-		      (label_ref (match_operand        3 "" ""))
-		      (pc)))
-   ]
+	(if_then_else
+	  (match_operator 4 "rx_z_comparison_operator"
+	    [(zero_extract:SI
+		(match_operand:SI 0 "register_operand" "r")
+		(match_operand:SI 1 "rx_constshift_operand" "")
+		(match_operand:SI 2 "rx_constshift_operand" ""))
+	     (const_int 0)])
+	  (match_operand 3 "label_ref_operand" "")
+	  (pc)))]
   ""
   "#"
-  ""
-  [(set (pc)
-	(if_then_else (ne (and:SI (match_dup  0)
-				  (match_dup 4))
-			  (const_int 0))
-		      (label_ref (match_dup 3))
-		      (pc)))
-   ]
-  "operands[4] = GEN_INT (((1 << INTVAL (operands[1]))-1) << INTVAL (operands[2]));"
+  "reload_completed"
+  [(const_int 0)]
+{
+  HOST_WIDE_INT mask;
+  rtx x;
+
+  mask = 1;
+  mask <<= INTVAL (operands[1]);
+  mask -= 1;
+  mask <<= INTVAL (operands[2]);
+  x = gen_rtx_AND (SImode, operands[0], gen_int_mode (mask, SImode));
+
+  rx_split_cbranch (CC_ZSmode, GET_CODE (operands[4]),
+		    x, const0_rtx, operands[3]);
+  DONE;
+})
+
+(define_insn "*tstsi"
+  [(set (reg:CC_ZS CC_REG)
+	(compare:CC_ZS
+	  (and:SI (match_operand:SI 0 "register_operand"  "r,r,r")
+		  (match_operand:SI 1 "rx_source_operand" "r,i,Q"))
+	  (const_int 0)))]
+  "reload_completed"
+  "tst\t%Q1, %0"
+  [(set_attr "timings" "11,11,33")
+   (set_attr "length"  "3,7,6")]
 )
 
-;; -----------------------------------------------------------------------------
-
 (define_expand "cbranchsf4"
   [(set (pc)
-	(if_then_else (match_operator 0 "comparison_operator"
-				      [(match_operand:SF 1 "register_operand")
-				       (match_operand:SF 2 "rx_source_operand")])
-		      (label_ref (match_operand 3 ""))
-		      (pc)))
-   ]
+	(if_then_else
+	  (match_operator 0 "comparison_operator"
+	    [(match_operand:SF 1 "register_operand")
+	     (match_operand:SF 2 "register_operand")])
+          (label_ref (match_operand 3 ""))
+	  (pc)))]
   "ALLOW_RX_FPU_INSNS"
-  ""
-)
-
-(define_insn_and_split "*cbranchsf4_<code>"
+{
+  enum rtx_code cmp1, cmp2;
+
+  /* If the comparison needs swapping of operands, do that now.
+     Do not split the comparison in two yet.  */
+  if (rx_split_fp_compare (GET_CODE (operands[0]), &cmp1, &cmp2))
+    {
+      rtx op1, op2;
+
+      if (cmp2 != UNKNOWN)
+	{
+	  gcc_assert (cmp1 == UNORDERED);
+	  if (cmp2 == GT)
+	    cmp1 = UNGT;
+	  else if (cmp2 == LE)
+	    cmp1 = UNLE;
+	  else
+	    gcc_unreachable ();
+	}
+
+      op1 = operands[2];
+      op2 = operands[1];
+      operands[0] = gen_rtx_fmt_ee (cmp1, VOIDmode, op1, op2);
+      operands[1] = op1;
+      operands[2] = op2;
+    }
+})
+
+(define_insn_and_split "*cbranchsf4"
   [(set (pc)
-	(if_then_else (most_cond (match_operand:SF  0 "register_operand"  "r")
-				 (match_operand:SF  1 "rx_source_operand" "rFiQ"))
-		      (label_ref (match_operand        2 "" ""))
-		      (pc)))
-   ]
+	(if_then_else
+	  (match_operator 3 "rx_fp_comparison_operator"
+	    [(match_operand:SF  0 "register_operand"  "r")
+	     (match_operand:SF  1 "rx_source_operand" "rFiQ")])
+	  (match_operand        2 "label_ref_operand" "")
+	  (pc)))]
   "ALLOW_RX_FPU_INSNS"
   "#"
   "&& reload_completed"
   [(const_int 0)]
-  "
-  /* We contstruct the split by hand as otherwise the JUMP_LABEL
-     attribute is not set correctly on the jump insn.  */
-  emit_insn (gen_cmpsf (operands[0], operands[1]));
-  
-  emit_jump_insn (gen_conditional_branch (operands[2],
-  		 gen_rtx_fmt_ee (<most_cond:CODE>, CCmode,
- 		 		 gen_rtx_REG (CCmode, CC_REG), const0_rtx)));
-  "
-)
-
-(define_insn "tstsi"
-  [(set (reg:CC_ZS CC_REG)
-	(compare:CC_ZS (and:SI (match_operand:SI 0 "register_operand"  "r,r,r")
-			       (match_operand:SI 1 "rx_source_operand" "r,i,Q"))
-		       (const_int 0)))]
-  ""
-  {
-    rx_float_compare_mode = false;
-    return "tst\t%Q1, %0";
-  }
-  [(set_attr "timings" "11,11,33")
-   (set_attr "length"   "3,7,6")]
-)
-
-(define_insn "cmpsi"
-  [(set (reg:CC CC_REG)
-	(compare:CC (match_operand:SI 0 "register_operand"  "r,r,r,r,r,r,r")
-		    (match_operand:SI 1 "rx_source_operand" "r,Uint04,Int08,Sint16,Sint24,i,Q")))]
-  ""
-  "cmp\t%Q1, %0"
-  [(set_attr "timings" "11,11,11,11,11,11,33")
-   (set_attr "length"  "2,2,3,4,5,6,5")]
-)
-
-;; ??? g++.dg/eh/080514-1.C to see this happen.
-(define_insn "cmpsf"
-  [(set (reg:CC_ZSO CC_REG)
-	(compare:CC_ZSO (match_operand:SF 0 "register_operand"  "r,r,r")
-			(match_operand:SF 1 "rx_source_operand" "r,iF,Q")))]
-  "ALLOW_RX_FPU_INSNS"
-  {
-    rx_float_compare_mode = true;
-    return "fcmp\t%1, %0";
-  }
+{
+  enum rtx_code cmp0, cmp1, cmp2;
+  rtx flags, lab1, lab2, over, x;
+  bool swap;
+
+  cmp0 = GET_CODE (operands[3]);
+  swap = rx_split_fp_compare (cmp0, &cmp1, &cmp2);
+  gcc_assert (!swap);
+
+  flags = gen_rtx_REG (CC_Fmode, CC_REG);
+  x = gen_rtx_COMPARE (CC_Fmode, operands[0], operands[1]);
+  x = gen_rtx_SET (VOIDmode, flags, x);
+  emit_insn (x);
+
+  over = NULL;
+  lab1 = lab2 = operands[2];
+
+  /* The one case of LTGT needs to be split into cmp1 && cmp2.  */
+  if (cmp0 == LTGT)
+    {
+      over = gen_label_rtx ();
+      lab1 = gen_rtx_LABEL_REF (VOIDmode, over);
+      cmp1 = reverse_condition_maybe_unordered (cmp1);
+    }
+
+  /* Otherwise we split into cmp1 || cmp2.  */
+  x = gen_rtx_fmt_ee (cmp1, VOIDmode, flags, const0_rtx);
+  x = gen_rtx_IF_THEN_ELSE (VOIDmode, x, lab1, pc_rtx);
+  x = gen_rtx_SET (VOIDmode, pc_rtx, x);
+  emit_jump_insn (x);
+
+  if (cmp2 != UNKNOWN)
+    {
+      x = gen_rtx_fmt_ee (cmp2, VOIDmode, flags, const0_rtx);
+      x = gen_rtx_IF_THEN_ELSE (VOIDmode, x, lab2, pc_rtx);
+      x = gen_rtx_SET (VOIDmode, pc_rtx, x);
+      emit_jump_insn (x);
+    }
+
+  if (over)
+    emit_label (over);
+  DONE;
+})
+
+(define_insn "*cmpsf"
+  [(set (reg:CC_F CC_REG)
+	(compare:CC_F
+	  (match_operand:SF 0 "register_operand"  "r,r,r")
+	  (match_operand:SF 1 "rx_source_operand" "r,iF,Q")))]
+  "ALLOW_RX_FPU_INSNS && reload_completed"
+  "fcmp\t%1, %0"
   [(set_attr "timings" "11,11,33")
    (set_attr "length" "3,7,5")]
 )
 
 ;; Flow Control Instructions:
 
-(define_expand "b<code>"
-  [(set (pc)
-        (if_then_else (most_cond (reg:CC CC_REG) (const_int 0))
-                      (label_ref (match_operand 0))
-                      (pc)))]
-  ""
-  ""
-)
-
-(define_insn "conditional_branch"
+(define_insn "*conditional_branch"
   [(set (pc)
-	(if_then_else (match_operator           1 "comparison_operator"
-						[(reg:CC CC_REG) (const_int 0)])
-		      (label_ref (match_operand 0 "" ""))
-		      (pc)))]
+	(if_then_else
+	  (match_operator 1 "comparison_operator"
+	    [(reg CC_REG) (const_int 0)])
+	  (label_ref (match_operand 0 "" ""))
+	  (pc)))]
   ""
-  {
-    return rx_gen_cond_branch_template (operands[1], false);
-  }
+  "b%B1\t%0"
   [(set_attr "length" "8")    ;; This length is wrong, but it is
                               ;; too hard to compute statically.
    (set_attr "timings" "33")] ;; The timing assumes that the branch is taken.
 )
 
-(define_insn "*reveresed_conditional_branch"
-  [(set (pc)
-	(if_then_else (match_operator 1 "comparison_operator"
-				      [(reg:CC CC_REG) (const_int 0)])
-		      (pc)
-		      (label_ref (match_operand 0 "" ""))))]
-  ""
-  {
-    return rx_gen_cond_branch_template (operands[1], true);
-  }
-  [(set_attr "length" "8")    ;; This length is wrong, but it is
-                              ;; too hard to compute statically.
-   (set_attr "timings" "33")] ;; The timing assumes that the branch is taken.
-)
+;; ----------------------------------------------------------------------------
 
 (define_insn "jump"
   [(set (pc)