OSDN Git Service

* config/rx/rx.md (return): Define pattern.
[pf3gnuchains/gcc-fork.git] / gcc / config / rx / rx.md
index d8cd66d..92768c6 100644 (file)
@@ -1,5 +1,5 @@
 ;;  Machine Description for Renesas RX processors
-;;  Copyright (C) 2008, 2009, 2010 Free Software Foundation, Inc.
+;;  Copyright (C) 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
 ;;  Contributed by Red Hat.
 
 ;; This file is part of GCC.
@@ -22,6 +22,9 @@
 ;; This code iterator is used for sign- and zero- extensions.
 (define_mode_iterator small_int_modes [(HI "") (QI "")])
 
+;; This code iterator is used for max and min operations.
+(define_mode_iterator int_modes [(SI "") (HI "") (QI "")])
+
 ;; We do not handle DFmode here because it is either
 ;; the same as SFmode, or if -m64bit-doubles is active
 ;; then all operations on doubles have to be handled by
 (define_mode_iterator register_modes
   [(SF "ALLOW_RX_FPU_INSNS") (SI "") (HI "") (QI "")])
 
-
-;; Used to map RX condition names to GCC
-;; condition names for builtin instructions.
-(define_code_iterator gcc_conds [eq ne gt ge lt le gtu geu ltu leu
-                               unge unlt uneq ltgt])
-(define_code_attr rx_conds [(eq "eq") (ne "ne") (gt "gt") (ge "ge") (lt "lt")
-                           (le "le") (gtu "gtu") (geu "geu") (ltu "ltu")
-                           (leu "leu") (unge "pz") (unlt "n") (uneq "o")
-                           (ltgt "no")])
-
 (define_constants
   [
    (SP_REG 0)
@@ -50,6 +43,7 @@
    (UNSPEC_RTE             10)
    (UNSPEC_RTFI            11)
    (UNSPEC_NAKED           12)
+   (UNSPEC_CONST           13)
    
    (UNSPEC_MOVSTR          20)
    (UNSPEC_MOVMEM          21)
@@ -79,6 +73,8 @@
    (UNSPEC_BUILTIN_SAT     49)
    (UNSPEC_BUILTIN_SETPSW  50)
    (UNSPEC_BUILTIN_WAIT           51)
+
+   (UNSPEC_PID_ADDR       52)
   ]
 )
 
 (define_expand "cbranchsf4"
   [(set (pc)
        (if_then_else
-         (match_operator 0 "comparison_operator"
+         (match_operator 0 "rx_fp_comparison_operator"
            [(match_operand:SF 1 "register_operand")
-            (match_operand:SF 2 "register_operand")])
-          (label_ref (match_operand 3 ""))
+            (match_operand:SF 2 "rx_source_operand")])
+         (label_ref (match_operand 3 ""))
          (pc)))]
   "ALLOW_RX_FPU_INSNS"
-{
-  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
          (match_operator 3 "rx_fp_comparison_operator"
            [(match_operand:SF  0 "register_operand"  "r")
-            (match_operand:SF  1 "rx_source_operand" "rFiQ")])
+            (match_operand:SF  1 "rx_source_operand" "rFQ")])
          (match_operand        2 "label_ref_operand" "")
          (pc)))]
   "ALLOW_RX_FPU_INSNS"
   "&& reload_completed"
   [(const_int 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);
+  rx_split_cbranch (CC_Fmode, GET_CODE (operands[3]),
+                   operands[0], operands[1], operands[2]);
   DONE;
 })
 
   [(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")))]
+         (match_operand:SF 1 "rx_source_operand" "r,F,Q")))]
   "ALLOW_RX_FPU_INSNS && reload_completed"
   "fcmp\t%1, %0"
   [(set_attr "timings" "11,11,33")
            [(reg CC_REG) (const_int 0)])
          (label_ref (match_operand 0 "" ""))
          (pc)))]
-  ""
+  "reload_completed"
   "b%B1\t%0"
   [(set_attr "length" "8")    ;; This length is wrong, but it is
                               ;; too hard to compute statically.
        (match_operand:SI          0 "register_operand" "r"))
    (use (label_ref (match_operand  1 "" "")))]
   ""
-  { return flag_pic ? (TARGET_AS100_SYNTAX ? "\n?:\tbra\t%0"
-                                          : "\n1:\tbra\t%0")
-                                          : "jmp\t%0";
+  { return TARGET_PID ? (TARGET_AS100_SYNTAX ? "\n?:\tbra\t%0"
+                                            : "\n1:\tbra\t%0")
+                                            : "\n1:jmp\t%0";
   }
   [(set_attr "timings" "33")
    (set_attr "length" "2")]
 )
 
+(define_expand "return"
+  [(return)]
+  ""
+  "rx_expand_epilogue (false); DONE;"
+)
+
 (define_insn "simple_return"
   [(return)]
   ""
    (set_attr "timings" "55")]
 )
 
+;; Unspec used so that the constant will not be invalid
+;; if -mmax-constant-size has been specified.
 (define_insn "deallocate_and_return"
   [(set (reg:SI SP_REG)
        (plus:SI (reg:SI SP_REG)
-                (match_operand:SI 0 "immediate_operand" "i")))
+                (const:SI (unspec:SI [(match_operand 0 "const_int_operand" "n")] UNSPEC_CONST))))
    (return)]
   ""
   "rtsd\t%0"
   [(match_parallel 1 "rx_rtsd_vector"
      [(set (reg:SI SP_REG)
           (plus:SI (reg:SI SP_REG)
-                   (match_operand:SI 0 "const_int_operand" "n")))])]
+                   (match_operand:SI 0 "const_int_operand" "n")))])
+   (return)]
   "reload_completed"
   {
     rx_emit_stack_popm (operands, false);
 
     if (! rx_call_operand (dest, Pmode))
       dest = force_reg (Pmode, dest);
-    emit_call_insn (gen_call_internal (dest, operands[1]));
+    emit_call_insn (gen_call_internal (dest));
     DONE;
   }
 )
 
 (define_insn "call_internal"
   [(call (mem:QI (match_operand:SI 0 "rx_call_operand" "r,Symbol"))
-        (match_operand:SI         1 "general_operand" "g,g"))
+        (const_int 0))
    (clobber (reg:CC CC_REG))]
   ""
   "@
 
     if (! rx_call_operand (dest, Pmode))
       dest = force_reg (Pmode, dest);
-    emit_call_insn (gen_call_value_internal (operands[0], dest, operands[2]));
+    emit_call_insn (gen_call_value_internal (operands[0], dest));
     DONE;
   }
 )
 (define_insn "call_value_internal"
   [(set (match_operand                  0 "register_operand" "=r,r")
        (call (mem:QI (match_operand:SI 1 "rx_call_operand"   "r,Symbol"))
-             (match_operand:SI         2 "general_operand"   "g,g")))
+             (const_int 0)))
    (clobber (reg:CC CC_REG))]
   ""
   "@
   {
     if (MEM_P (operands[0]))
       operands[0] = XEXP (operands[0], 0);
+    emit_call_insn (gen_sibcall_internal (operands[0]));
+    DONE;
   }
 )
 
 (define_insn "sibcall_internal"
   [(call (mem:QI (match_operand:SI 0 "rx_symbolic_call_operand" "Symbol"))
-        (match_operand:SI         1 "general_operand"          "g"))
+        (const_int 0))
    (return)]
   ""
   "bra\t%A0"
   {
     if (MEM_P (operands[1]))
       operands[1] = XEXP (operands[1], 0);
+    emit_call_insn (gen_sibcall_value_internal (operands[0], operands[1]));
+    DONE;
   }
 )
 
 (define_insn "sibcall_value_internal"
  [(set (match_operand                  0 "register_operand"         "=r")
        (call (mem:QI (match_operand:SI 1 "rx_symbolic_call_operand" "Symbol"))
-            (match_operand:SI         2 "general_operand"          "g")))
+            (const_int 0)))
   (return)]
   ""
   "bra\t%A1"
        (match_operand:register_modes 1 "general_operand"))]
   ""
   {
-    if (MEM_P (operand0) && MEM_P (operand1))
-      operands[1] = copy_to_mode_reg (<register_modes:MODE>mode, operand1);
+    if (MEM_P (operands[0]) && MEM_P (operands[1]))
+      operands[1] = copy_to_mode_reg (<register_modes:MODE>mode, operands[1]);
+    operands[0] = rx_maybe_pidify_operand (operands[0], 0);
+    operands[1] = rx_maybe_pidify_operand (operands[1], 0);
+    if (GET_CODE (operands[0]) != REG
+       && GET_CODE (operands[1]) == PLUS)
+      operands[1] = copy_to_mode_reg (<register_modes:MODE>mode, operands[1]);
+    if (GET_CODE (operands[1]) == PLUS && GET_MODE (operands[1]) == SImode)
+      {
+        emit_insn (gen_addsi3 (operands[0], XEXP (operands[1], 0), XEXP (operands[1], 1)));
+        DONE;
+      }
+    if (CONST_INT_P (operand1)
+        && ! rx_is_legitimate_constant (<register_modes:MODE>mode, operand1))
+      FAIL;
   }
 )
 
 (define_insn "*mov<register_modes:mode>_internal"
   [(set (match_operand:register_modes
-        0 "nonimmediate_operand" "=r,r,r,r,r,r,m,Q,Q,Q,Q")
+        0 "nonimmediate_operand" "=r,r,r,r,r,r,m,Q,Q,Q,Q,r")
        (match_operand:register_modes
-        1 "general_operand" "Int08,Sint16,Sint24,i,r,m,r,Int08,Sint16,Sint24,i"))]
+        1 "general_operand" "Int08,Sint16,Sint24,i,r,m,r,Int08,Sint16,Sint24,i,RpdaRpid"))]
   ""
   { return rx_gen_move_template (operands, false); }
-  [(set_attr "length" "3,4,5,6,2,4,6,5,6,7,8")
-   (set_attr "timings" "11,11,11,11,11,12,11,11,11,11,11")]
+  [(set_attr "length" "3,4,5,6,2,4,6,5,6,7,8,8")
+   (set_attr "timings" "11,11,11,11,11,12,11,11,11,11,11,11")]
 )
 
 (define_insn "extend<small_int_modes:mode>si2"
   [(set_attr "length" "3")]
 )
 
-(define_expand "cstoresf4"
-  [(parallel [(set (match_operand:SI 0 "register_operand" "")
-                  (match_operator:SI 1 "comparison_operator"
-                   [(match_operand:SF 2 "register_operand" "")
-                    (match_operand:SF 3 "register_operand" "")]))
-            (clobber (match_scratch:SI 4))])]
-  "ALLOW_RX_FPU_INSNS"
-{
-  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 op2, op3;
-
-      if (cmp2 != UNKNOWN)
-       {
-         gcc_assert (cmp1 == UNORDERED);
-         if (cmp2 == GT)
-           cmp1 = UNGT;
-         else if (cmp2 == LE)
-           cmp1 = UNLE;
-         else
-           gcc_unreachable ();
-       }
-
-      op2 = operands[3];
-      op3 = operands[2];
-      operands[0] = gen_rtx_fmt_ee (cmp1, VOIDmode, op2, op3);
-      operands[2] = op2;
-      operands[3] = op3;
-    }
-})
-
-(define_insn_and_split "*cstoresf4"
+(define_insn_and_split "cstoresf4"
   [(set (match_operand:SI 0 "register_operand" "=r")
-       (match_operator:SI 4 "rx_fp_comparison_operator"
+       (match_operator:SI 1 "rx_fp_comparison_operator"
         [(match_operand:SF 2 "register_operand" "r")
-         (match_operand:SF 3 "rx_source_operand" "rFiQ")]))
-   (clobber (match_scratch:SI 1 "=r"))]
+         (match_operand:SF 3 "rx_source_operand" "rFQ")]))]
   "ALLOW_RX_FPU_INSNS"
   "#"
   "reload_completed"
   [(const_int 0)]
 {
-  enum rtx_code cmp0, cmp1, cmp2;
   rtx flags, x;
-  bool swap;
-
-  cmp0 = GET_CODE (operands[4]);
-  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[2], operands[3]);
   x = gen_rtx_SET (VOIDmode, flags, x);
   emit_insn (x);
 
-  x = gen_rtx_fmt_ee (cmp1, SImode, flags, const0_rtx);
+  x = gen_rtx_fmt_ee (GET_CODE (operands[1]), SImode, flags, const0_rtx);
   x = gen_rtx_SET (VOIDmode, operands[0], x);
   emit_insn (x);
-
-  if (cmp0 == LTGT)
-    {
-      /* The one case of LTGT needs to be split into ORDERED && NE.  */
-      x = gen_rtx_fmt_ee (EQ, VOIDmode, flags, const0_rtx);
-      x = gen_rtx_IF_THEN_ELSE (SImode, x, const0_rtx, operands[0]);
-      x = gen_rtx_SET (VOIDmode, operands[0], x);
-      emit_insn (x);
-    }
-  else if (cmp2 == EQ || cmp2 == NE)
-    {
-      /* Oring the two flags can be performed with a movcc operation.  */
-      x = gen_rtx_fmt_ee (cmp2, VOIDmode, flags, const0_rtx);
-      x = gen_rtx_IF_THEN_ELSE (SImode, x, const1_rtx, operands[0]);
-      x = gen_rtx_SET (VOIDmode, operands[0], x);
-      emit_insn (x);
-    }
-  else if (cmp2 != UNKNOWN)
-    {
-      /* We can't use movcc, but need to or in another compare.
-        Do this by storing the second operation into the scratch.  */
-      x = gen_rtx_fmt_ee (cmp2, SImode, flags, const0_rtx);
-      x = gen_rtx_SET (VOIDmode, operands[1], x);
-      emit_insn (x);
-
-      emit_insn (gen_iorsi3 (operands[0], operands[0], operands[1]));
-    }
   DONE;
 })
 
      (clobber (reg:CC CC_REG))])]
   ""
 {
-  /* ??? Support other conditions via cstore into a temporary?  */
-  if (GET_CODE (operands[1]) != EQ && GET_CODE (operands[1]) != NE)
-    FAIL;
-  /* One operand must be a constant.  */
-  if (!CONSTANT_P (operands[2]) && !CONSTANT_P (operands[3]))
+  /* One operand must be a constant or a register, the other must be a register.  */
+  if (   ! CONSTANT_P (operands[2])
+      && ! CONSTANT_P (operands[3])
+      && ! (REG_P (operands[2]) && REG_P (operands[3])))
     FAIL;
 })
 
 (define_insn_and_split "*movsicc"
-  [(set (match_operand:SI     0 "register_operand" "=r,r")
+  [(set (match_operand:SI     0 "register_operand" "=r,r,r")
        (if_then_else:SI
-         (match_operator 5 "rx_z_comparison_operator"
-          [(match_operand:SI 3 "register_operand"  "r,r")
-           (match_operand:SI 4 "rx_source_operand" "riQ,riQ")])
-         (match_operand:SI   1 "nonmemory_operand" "i,ri")
-         (match_operand:SI   2 "nonmemory_operand" "ri,i")))
+         (match_operator     5 "comparison_operator"
+          [(match_operand:SI 3 "register_operand"  "r,r,r")
+           (match_operand:SI 4 "rx_source_operand" "riQ,riQ,riQ")])
+         (match_operand:SI   1 "nonmemory_operand" "i,ri,r")
+         (match_operand:SI   2 "nonmemory_operand" "ri,i,r")))
    (clobber (reg:CC CC_REG))]
-  "CONSTANT_P (operands[1]) || CONSTANT_P (operands[2])"
+  "(CONSTANT_P (operands[1]) || CONSTANT_P (operands[2]))
+    || (REG_P (operands[1]) && REG_P (operands[2]))"
   "#"
   "&& reload_completed"
   [(const_int 0)]
   op1 = operands[1];
   op2 = operands[2];
 
-  /* If OP2 is the constant, reverse the sense of the move.  */
-  if (!CONSTANT_P (operands[1]))
+  /* If OP2 is the constant, reverse the sense of the move.
+     Likewise if both operands are registers but OP1 == OP0.  */
+  if ((! CONSTANT_P (operands[1]) && CONSTANT_P (operands[2]))
+      || (REG_P (operands[1]) && REG_P (operands[2])
+          && rtx_equal_p (op0, op1)))
     {
       x = op1, op1 = op2, op2 = x;
       cmp_code = reverse_condition (cmp_code);
   /* If OP2 does not match the output, copy it into place.  We have allowed
      these alternatives so that the destination can legitimately be one of
      the comparison operands without increasing register pressure.  */
-  if (!rtx_equal_p (op0, op2))
+  if (! rtx_equal_p (op0, op2))
     emit_move_insn (op0, op2);
 
   x = gen_rtx_fmt_ee (cmp_code, VOIDmode, flags, const0_rtx);
            [(reg CC_REG) (const_int 0)])
          (match_operand:SI 1 "immediate_operand" "Sint08,Sint16,Sint24,i")
          (match_dup 0)))]
-  "reload_completed"
-{
-  if (GET_CODE (operands[2]) == EQ)
-    return "stz\t%1, %0";
-  else
-    return "stnz\t%1, %0";
-}
+  "reload_completed
+   && ((GET_CODE (operands[2]) == EQ) || (GET_CODE (operands[2]) == NE))"
+  {
+    if (GET_CODE (operands[2]) == EQ)
+      return "stz\t%1, %0";
+    else
+     return "stnz\t%1, %0";
+  }
   [(set_attr "length" "4,5,6,7")]
 )
 
+(define_insn "*stcc_reg"
+  [(set (match_operand:SI 0 "register_operand" "+r,r,r,r,r,r")
+       (if_then_else:SI
+         (match_operator 2 "comparison_operator"
+           [(reg CC_REG) (const_int 0)])
+         (match_operand:SI 1 "nonmemory_operand"
+                             "r,Uint04,Sint08,Sint16,Sint24,i")
+         (match_dup 0)))]
+  "reload_completed"
+  {
+    PUT_CODE (operands[2], reverse_condition (GET_CODE (operands[2])));
+    return "b%B2 1f\n\tmov %1, %0\n1:";
+  }
+  [(set_attr "length" "3,3,4,5,6,7")]
+)
+
 ;; Arithmetic Instructions
 
 (define_insn "abssi2"
    (set (reg CC_REG)
        (compare (abs:SI (match_dup 1))
                 (const_int 0)))]
-  "reload_completed && rx_match_ccmode (insn, CC_ZSOmode)"
+  ;; Note - although the ABS instruction does set the O bit in the processor
+  ;; status word, it does not do so in a way that is comparable with the CMP
+  ;; instruction.  Hence we use CC_ZSmode rather than CC_ZSOmode.
+  "reload_completed && rx_match_ccmode (insn, CC_ZSmode)"
   "@
   abs\t%0
   abs\t%1, %0"
   [(set_attr "length" "2,3")]
 )
 
-(define_insn "addsi3"
+(define_expand "addsi3"
+  [(parallel [(set (match_operand:SI          0 "register_operand"  "")
+       (plus:SI (match_operand:SI 1 "register_operand"  "")
+                (match_operand:SI 2 "rx_source_operand" "")))
+    (clobber (reg:CC CC_REG))])]
+  ""
+  "
+      operands[0] = rx_maybe_pidify_operand (operands[0], 1);
+      operands[1] = rx_maybe_pidify_operand (operands[1], 1);
+      operands[2] = rx_maybe_pidify_operand (operands[2], 1);
+  "
+)
+
+(define_insn "addsi3_internal"
   [(set (match_operand:SI          0 "register_operand"  "=r,r,r,r,r,r,r,r,r,r,r,r,r,r")
        (plus:SI (match_operand:SI 1 "register_operand"  "%0,0,0,0,0,0,0,r,r,r,r,r,r,0")
                 (match_operand:SI 2 "rx_source_operand" "r,Uint04,NEGint4,Sint08,Sint16,Sint24,i,0,r,Sint08,Sint16,Sint24,i,Q")))
          (match_operand:SI   2 "rx_source_operand" "r,Sint08,Sint16,Sint24,i,Q")))
     (clobber (reg:CC CC_REG))]
   "reload_completed"
-  "adc %2,%0"
+  "adc\t%2, %0"
   [(set_attr "timings" "11,11,11,11,11,33")
    (set_attr "length"   "3,4,5,6,7,6")]
 )
            (match_dup 2))
          (const_int 0)))]
   "reload_completed && rx_match_ccmode (insn, CC_ZSCmode)"
-  "adc %2,%0"
+  "adc\t%2, %0"
   [(set_attr "timings" "11,11,11,11,11,33")
    (set_attr "length"   "3,4,5,6,7,6")]
 )
 
+;; Peepholes to match:
+;;   (set (reg A) (reg B))
+;;   (set (CC) (compare:CC (reg A/reg B) (const_int 0)))
+;; and replace them with the addsi3_flags pattern, using an add
+;; of zero to copy the register and set the condition code bits.
+(define_peephole2
+  [(set (match_operand:SI 0 "register_operand")
+        (match_operand:SI 1 "register_operand"))
+   (set (reg:CC CC_REG)
+        (compare:CC (match_dup 0)
+                    (const_int 0)))]
+  ""
+  [(parallel [(set (match_dup 0)
+                  (plus:SI (match_dup 1) (const_int 0)))
+             (set (reg:CC_ZSC CC_REG)
+                  (compare:CC_ZSC (plus:SI (match_dup 1) (const_int 0))
+                                  (const_int 0)))])]
+)
+
+(define_peephole2
+  [(set (match_operand:SI 0 "register_operand")
+        (match_operand:SI 1 "register_operand"))
+   (set (reg:CC CC_REG)
+        (compare:CC (match_dup 1)
+                    (const_int 0)))]
+  ""
+  [(parallel [(set (match_dup 0)
+                  (plus:SI (match_dup 1) (const_int 0)))
+             (set (reg:CC_ZSC CC_REG)
+                  (compare:CC_ZSC (plus:SI (match_dup 1) (const_int 0))
+                                  (const_int 0)))])]
+)
+
 (define_expand "adddi3"
-  [(set (match_operand:DI          0 "register_operand" "")
-       (plus:DI (match_operand:DI 1 "register_operand" "")
-                (match_operand:DI 2 "rx_source_operand" "")))]
+  [(set (match_operand:DI          0 "register_operand")
+       (plus:DI (match_operand:DI 1 "register_operand")
+                (match_operand:DI 2 "rx_source_operand")))]
   ""
 {
   rtx op0l, op0h, op1l, op1h, op2l, op2h;
 })
 
 (define_insn_and_split "adddi3_internal"
-  [(set (match_operand:SI          0 "register_operand"  "=r")
+  [(set (match_operand:SI          0 "register_operand"  "=&r")
        (plus:SI (match_operand:SI 2 "register_operand"  "r")
                 (match_operand:SI 3 "rx_source_operand" "riQ")))
    (set (match_operand:SI          1 "register_operand"  "=r")
 
   if (rtx_equal_p (op0l, op1l))
     ;
+  /* It is preferable that op0l == op1l...  */
   else if (rtx_equal_p (op0l, op2l))
     x = op1l, op1l = op2l, op2l = x;
+  /* ... but it is only a requirement if op2l == MEM.  */
+  else if (MEM_P (op2l))
+    {
+      /* Let's hope that we still have a scratch register free.  */
+      gcc_assert (op1h != scratch);
+      emit_move_insn (scratch, op2l);
+      op2l = scratch;
+    }
+
   emit_insn (gen_addsi3_flags (op0l, op1l, op2l));
 
   if (rtx_equal_p (op0h, op1h))
   and\t%1, %0
   and\t%2, %1, %0
   and\t%Q2, %0"
-  [(set_attr "timings" "11,11,11,11,11,11,11,33,33")
+  [(set_attr "timings" "11,11,11,11,11,11,11,11,33")
    (set_attr "length" "2,2,3,4,5,6,2,5,5")]
 )
 
   and\t%1, %0
   and\t%2, %1, %0
   and\t%Q2, %0"
-  [(set_attr "timings" "11,11,11,11,11,11,11,33,33")
+  [(set_attr "timings" "11,11,11,11,11,11,11,11,33")
    (set_attr "length" "2,2,3,4,5,6,2,5,5")]
 )
 
    (set_attr "timings" "11,11,11,11,11,33")]
 )
 
+(define_insn "umax<small_int_modes:mode>3_u"
+  [(set (match_operand:SI          0 "register_operand" "=r,r,r,r,r,r")
+       (smax:SI (match_operand:SI 1 "register_operand" "%0,0,0,0,0,0")
+                (zero_extend:SI (match_operand:small_int_modes 2 "rx_minmaxex_operand"
+                                                               "r,Sint08,Sint16,Sint24,i,Q"))))]
+  ""
+  "max\t%R2, %0"
+  [(set_attr "length"  "3,4,5,6,7,6")
+   (set_attr "timings" "11,11,11,11,11,33")]
+)
+
+(define_insn "umin<small_int_modes:mode>3_ur"
+  [(set (match_operand:SI          0 "register_operand" "=r,r,r,r,r,r")
+       (smin:SI (zero_extend:SI (match_operand:small_int_modes 2 "rx_minmaxex_operand"
+                                                               "r,Sint08,Sint16,Sint24,i,Q"))
+                (match_operand:SI 1 "register_operand" "%0,0,0,0,0,0")))]
+  ""
+  "min\t%R2, %0"
+  [(set_attr "length"  "3,4,5,6,7,6")
+   (set_attr "timings" "11,11,11,11,11,33")]
+)
+
+(define_insn "umax<small_int_modes:mode>3_ur"
+  [(set (match_operand:SI          0 "register_operand" "=r,r,r,r,r,r")
+       (smax:SI (zero_extend:SI (match_operand:small_int_modes 2 "rx_minmaxex_operand"
+                                                               "r,Sint08,Sint16,Sint24,i,Q"))
+                (match_operand:SI 1 "register_operand" "%0,0,0,0,0,0")))]
+  ""
+  "max\t%R2, %0"
+  [(set_attr "length"  "3,4,5,6,7,6")
+   (set_attr "timings" "11,11,11,11,11,33")]
+)
+
+(define_expand "umax<small_int_modes:mode>3"
+  [(set (match_dup 4)
+       (zero_extend:SI (match_operand:small_int_modes 1 "register_operand" "%0,0,0,0,0,0")))
+   (set (match_dup 3)
+       (smax:SI (match_dup 4)
+                (match_operand:small_int_modes 2 "rx_source_operand"
+                                               "r,Sint08,Sint16,Sint24,i,Q")))
+   (set (match_operand:small_int_modes          0 "register_operand" "=r,r,r,r,r,r")
+       (match_dup 6))
+   ]
+  ""
+  "operands[3] = gen_reg_rtx (SImode);
+   operands[4] = gen_reg_rtx (SImode);
+   operands[5] = gen_reg_rtx (SImode);
+   operands[6] = gen_rtx_SUBREG (GET_MODE (operands[0]), operands[3],
+     TARGET_BIG_ENDIAN_DATA ? (GET_MODE (operands[0]) == HImode ? 2 : 3) : 0);
+   if (GET_CODE (operands[2]) != CONST_INT)
+     {
+       emit_move_insn (operands[5], gen_rtx_ZERO_EXTEND (SImode, operands[2]));
+       operands[2] = operands[5];
+     }
+  "
+)
+
+(define_expand "umin<small_int_modes:mode>3"
+  [(set (match_dup 4)
+       (zero_extend:SI (match_operand:small_int_modes 1 "register_operand" "%0,0,0,0,0,0")))
+   (set (match_dup 3)
+       (smin:SI (match_dup 4)
+                (match_operand:small_int_modes 2 "rx_source_operand"
+                                               "r,Sint08,Sint16,Sint24,i,Q")))
+   (set (match_operand:small_int_modes          0 "register_operand" "=r,r,r,r,r,r")
+       (match_dup 6))
+   ]
+  ""
+  "operands[3] = gen_reg_rtx (SImode);
+   operands[4] = gen_reg_rtx (SImode);
+   operands[5] = gen_reg_rtx (SImode);
+   operands[6] = gen_rtx_SUBREG (GET_MODE (operands[0]), operands[3],
+     TARGET_BIG_ENDIAN_DATA ? (GET_MODE (operands[0]) == HImode ? 2 : 3) : 0);
+   if (GET_CODE (operands[2]) != CONST_INT)
+     {
+       emit_move_insn (operands[5], gen_rtx_ZERO_EXTEND (SImode, operands[2]));
+       operands[2] = operands[5];
+     }
+   "
+)
+
 (define_insn "mulsi3"
   [(set (match_operand:SI          0 "register_operand" "=r,r,r,r,r,r,r,r,r")
         (mult:SI (match_operand:SI 1 "register_operand" "%0,0,0,0,0,0,0,r,r")
 )
 
 (define_expand "subdi3"
-  [(set (match_operand:DI           0 "register_operand" "")
-       (minus:DI (match_operand:DI 1 "register_operand" "")
-                 (match_operand:DI 2 "rx_source_operand" "")))]
+  [(set (match_operand:DI           0 "register_operand")
+       (minus:DI (match_operand:DI 1 "register_operand")
+                 (match_operand:DI 2 "rx_compare_operand")))]
   ""
 {
   rtx op0l, op0h, op1l, op1h, op2l, op2h;
 (define_insn_and_split "subdi3_internal"
   [(set (match_operand:SI          0 "register_operand"   "=&r,&r")
        (minus:SI (match_operand:SI 2 "register_operand"  "  0, r")
-                 (match_operand:SI 3 "rx_source_operand" "rnQ, r")))
+                 (match_operand:SI 3 "rx_compare_operand" "rQ, r")))
    (set (match_operand:SI          1 "register_operand"   "= r, r")
        (minus:SI
          (minus:SI
    (set_attr "length" "3,4,5,6,7,6")]
 )
 \f
+;; A set of peepholes to catch extending loads followed by arithmetic operations.
+;; We use iterators where possible to reduce the amount of typing and hence the
+;; possibilities for typos.
+
+(define_code_iterator extend_types [(zero_extend "") (sign_extend "")])
+(define_code_attr     letter       [(zero_extend "R") (sign_extend "Q")])
+
+(define_code_iterator memex_commutative [(plus "") (and "") (ior "") (xor "")])
+(define_code_iterator memex_noncomm     [(div "") (udiv "") (minus "")])
+(define_code_iterator memex_nocc        [(smax "") (smin "") (mult "")])
+
+(define_code_attr     op                [(plus "add") (and "and") (div "div") (udiv "divu") (smax "max") (smin "min") (mult "mul") (ior "or") (minus "sub") (xor "xor")])
+
+(define_peephole2
+  [(set (match_operand:SI                               0 "register_operand")
+       (extend_types:SI (match_operand:small_int_modes 1 "rx_restricted_mem_operand")))
+   (parallel [(set (match_operand:SI                    2 "register_operand")
+                  (memex_commutative:SI (match_dup 0)
+                                        (match_dup 2)))
+             (clobber (reg:CC CC_REG))])]
+  "peep2_regno_dead_p (2, REGNO (operands[0])) && (optimize < 3 || optimize_size)"
+  [(parallel [(set:SI (match_dup 2)
+                     (memex_commutative:SI (match_dup 2)
+                                           (extend_types:SI (match_dup 1))))
+             (clobber (reg:CC CC_REG))])]
+)
+
+(define_peephole2
+  [(set (match_operand:SI                               0 "register_operand")
+       (extend_types:SI (match_operand:small_int_modes 1 "rx_restricted_mem_operand")))
+   (parallel [(set (match_operand:SI                    2 "register_operand")
+                  (memex_commutative:SI (match_dup 2)
+                                        (match_dup 0)))
+             (clobber (reg:CC CC_REG))])]
+  "peep2_regno_dead_p (2, REGNO (operands[0])) && (optimize < 3 || optimize_size)"
+  [(parallel [(set:SI (match_dup 2)
+                     (memex_commutative:SI (match_dup 2)
+                                           (extend_types:SI (match_dup 1))))
+             (clobber (reg:CC CC_REG))])]
+)
+
+(define_peephole2
+  [(set (match_operand:SI                               0 "register_operand")
+       (extend_types:SI (match_operand:small_int_modes 1 "rx_restricted_mem_operand")))
+   (parallel [(set (match_operand:SI                    2 "register_operand")
+                  (memex_noncomm:SI (match_dup 2)
+                                    (match_dup 0)))
+             (clobber (reg:CC CC_REG))])]
+  "peep2_regno_dead_p (2, REGNO (operands[0])) && (optimize < 3 || optimize_size)"
+  [(parallel [(set:SI (match_dup 2)
+                     (memex_noncomm:SI (match_dup 2)
+                                       (extend_types:SI (match_dup 1))))
+             (clobber (reg:CC CC_REG))])]
+)
+
+(define_peephole2
+  [(set (match_operand:SI                               0 "register_operand")
+       (extend_types:SI (match_operand:small_int_modes 1 "rx_restricted_mem_operand")))
+   (set (match_operand:SI                               2 "register_operand")
+       (memex_nocc:SI (match_dup 0)
+                      (match_dup 2)))]
+  "peep2_regno_dead_p (2, REGNO (operands[0])) && (optimize < 3 || optimize_size)"
+  [(set:SI (match_dup 2)
+          (memex_nocc:SI (match_dup 2)
+                         (extend_types:SI (match_dup 1))))]
+)
+
+(define_peephole2
+  [(set (match_operand:SI                               0 "register_operand")
+       (extend_types:SI (match_operand:small_int_modes 1 "rx_restricted_mem_operand")))
+   (set (match_operand:SI                               2 "register_operand")
+       (memex_nocc:SI (match_dup 2)
+                      (match_dup 0)))]
+  "peep2_regno_dead_p (2, REGNO (operands[0])) && (optimize < 3 || optimize_size)"
+  [(set:SI (match_dup 2)
+          (memex_nocc:SI (match_dup 2)
+                         (extend_types:SI (match_dup 1))))]
+)
+
+(define_insn "<memex_commutative:code>si3_<extend_types:code><small_int_modes:mode>"
+  [(set (match_operand:SI                                                     0 "register_operand" "=r")
+       (memex_commutative:SI (match_operand:SI                               1 "register_operand" "%0")
+                             (extend_types:SI (match_operand:small_int_modes 2 "rx_restricted_mem_operand" "Q"))))
+   (clobber (reg:CC CC_REG))]
+  "(optimize < 3 || optimize_size)"
+  "<memex_commutative:op>\t%<extend_types:letter>2, %0"
+  [(set_attr "timings" "33")
+   (set_attr "length"  "5")] ;; This length is corrected in rx_adjust_insn_length
+)
+
+(define_insn "<memex_noncomm:code>si3_<extend_types:code><small_int_modes:mode>"
+  [(set (match_operand:SI                                                 0 "register_operand" "=r")
+       (memex_noncomm:SI (match_operand:SI                               1 "register_operand" "0")
+                          (extend_types:SI (match_operand:small_int_modes 2 "rx_restricted_mem_operand" "Q"))))
+   (clobber (reg:CC CC_REG))]
+  "(optimize < 3 || optimize_size)"
+  "<memex_noncomm:op>\t%<extend_types:letter>2, %0"
+  [(set_attr "timings" "33")
+   (set_attr "length"  "5")] ;; This length is corrected in rx_adjust_insn_length
+)
+
+(define_insn "<memex_nocc:code>si3_<extend_types:code><small_int_modes:mode>"
+  [(set (match_operand:SI                                              0 "register_operand" "=r")
+       (memex_nocc:SI (match_operand:SI                               1 "register_operand" "%0")
+                      (extend_types:SI (match_operand:small_int_modes 2 "rx_restricted_mem_operand" "Q"))))]
+  "(optimize < 3 || optimize_size)"
+  "<memex_nocc:op>\t%<extend_types:letter>2, %0"
+  [(set_attr "timings" "33")
+   (set_attr "length"  "5")] ;; This length is corrected in rx_adjust_insn_length
+)
+
+(define_peephole2
+  [(set (match_operand:SI                               0 "register_operand")
+       (extend_types:SI (match_operand:small_int_modes 1 "rx_restricted_mem_operand")))
+   (set (reg:CC CC_REG)
+       (compare:CC (match_operand:SI                   2 "register_operand")
+                   (match_dup 0)))]
+  "peep2_regno_dead_p (2, REGNO (operands[0])) && (optimize < 3 || optimize_size)"
+  [(set (reg:CC CC_REG)
+       (compare:CC (match_dup 2)
+                   (extend_types:SI (match_dup 1))))]
+)
+
+;; Convert:
+;;   (set (reg1) (sign_extend (mem))
+;;   (set (reg2) (zero_extend (reg1))
+;; into
+;;   (set (reg2) (zero_extend (mem)))
+(define_peephole2
+  [(set (match_operand:SI                              0 "register_operand")
+       (sign_extend:SI (match_operand:small_int_modes 1 "memory_operand")))
+   (set (match_operand:SI                              2 "register_operand")
+       (zero_extend:SI (match_operand:small_int_modes 3 "register_operand")))]
+  "REGNO (operands[0]) == REGNO (operands[3])
+   && (REGNO (operands[0]) == REGNO (operands[2])
+       || peep2_regno_dead_p (2, REGNO (operands[0])))"
+  [(set (match_dup 2)
+       (zero_extend:SI (match_dup 1)))]
+)
+
+;; Remove the redundant sign extension from:
+;;   (set (reg) (extend (mem)))
+;;   (set (reg) (extend (reg)))
+(define_peephole2
+  [(set (match_operand:SI                               0 "register_operand")
+       (extend_types:SI (match_operand:small_int_modes 1 "memory_operand")))
+   (set (match_dup 0)
+       (extend_types:SI (match_operand:small_int_modes 2 "register_operand")))]
+  "REGNO (operands[0]) == REGNO (operands[2])"
+  [(set (match_dup 0) (extend_types:SI (match_dup 1)))]
+)
+
+(define_insn "comparesi3_<extend_types:code><small_int_modes:mode>"
+  [(set (reg:CC CC_REG)
+       (compare:CC (match_operand:SI                               0 "register_operand" "=r")
+                   (extend_types:SI (match_operand:small_int_modes 1 "rx_restricted_mem_operand" "Q"))))]
+  "(optimize < 3 || optimize_size)"
+  "cmp\t%<extend_types:letter>1, %0"
+  [(set_attr "timings" "33")
+   (set_attr "length"  "5")] ;; This length is corrected in rx_adjust_insn_length
+)
+\f
 ;; Floating Point Instructions
 
 (define_insn "addsf3"
 )
 
 (define_insn "*bitset_in_memory"
-  [(set (match_operand:QI                    0 "memory_operand" "+Q")
+  [(set (match_operand:QI                    0 "rx_restricted_mem_operand" "+Q")
        (ior:QI (ashift:QI (const_int 1)
                           (match_operand:QI 1 "nonmemory_operand" "ri"))
                (match_dup 0)))]
   ""
   "bset\t%1, %0.B"
   [(set_attr "length" "3")
-   (set_attr "timings" "34")]
+   (set_attr "timings" "33")]
 )
 
 (define_insn "*bitinvert"
 )
 
 (define_insn "*bitinvert_in_memory"
-  [(set (match_operand:QI 0 "memory_operand" "+Q")
+  [(set (match_operand:QI 0 "rx_restricted_mem_operand" "+Q")
        (xor:QI (ashift:QI (const_int 1)
                           (match_operand:QI 1 "nonmemory_operand" "ri"))
                (match_dup 0)))]
 )
 
 (define_insn "*bitclr_in_memory"
-  [(set (match_operand:QI 0 "memory_operand" "+Q")
+  [(set (match_operand:QI 0 "rx_restricted_mem_operand" "+Q")
        (and:QI (not:QI
                  (ashift:QI
                    (const_int 1)
   ""
   "bclr\t%1, %0.B"
   [(set_attr "length" "3")
-   (set_attr "timings" "34")]
+   (set_attr "timings" "33")]
 )
 
 (define_insn "*insv_imm"
     rtx addr2 = gen_rtx_REG (SImode, 2);
     rtx len   = gen_rtx_REG (SImode, 3);
 
+    /* Do not use when the source or destination are volatile - the SMOVF
+       instruction will read and write in word sized blocks, which may be
+       outside of the valid address range.  */
+    if (MEM_P (operands[0]) && MEM_VOLATILE_P (operands[0]))
+      FAIL;
+    if (MEM_P (operands[1]) && MEM_VOLATILE_P (operands[1]))
+      FAIL;
+
     if (REG_P (operands[0]) && (REGNO (operands[0]) == 2
                                      || REGNO (operands[0]) == 3))
       FAIL;
     if (REG_P (operands[2]) && (REGNO (operands[2]) == 1
                                      || REGNO (operands[2]) == 2))
       FAIL;
+
     emit_move_insn (addr1, force_operand (XEXP (operands[0], 0), NULL_RTX));
     emit_move_insn (addr2, force_operand (XEXP (operands[1], 0), NULL_RTX));
     emit_move_insn (len, force_operand (operands[2], NULL_RTX));
   "nop"
   [(set_attr "length" "1")]
 )
+
+(define_expand "pid_addr"
+  [(plus:SI (match_operand:SI 0)
+           (const:SI (unspec:SI [(match_operand:SI 1)] UNSPEC_PID_ADDR)))]
+  ""
+  ""
+)