Initial revision
authorRichard Stallman <rms@gnu.org>
Thu, 6 Feb 1992 02:36:40 +0000 (02:36 +0000)
committerRichard Stallman <rms@gnu.org>
Thu, 6 Feb 1992 02:36:40 +0000 (02:36 +0000)
From-SVN: r282

gcc/config/sparc/sparc.md [new file with mode: 0644]

diff --git a/gcc/config/sparc/sparc.md b/gcc/config/sparc/sparc.md
new file mode 100644 (file)
index 0000000..02776ab
--- /dev/null
@@ -0,0 +1,2645 @@
+;;- Machine description for SPARC chip for GNU C compiler
+;;   Copyright (C) 1987, 1988, 1989, 1992 Free Software Foundation, Inc.
+;;   Contributed by Michael Tiemann (tiemann@cygnus.com)
+
+;; This file is part of GNU CC.
+
+;; GNU CC is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+
+;; GNU CC is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU CC; see the file COPYING.  If not, write to
+;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+;;- See file "rtl.def" for documentation on define_insn, match_*, et. al.
+
+;; Insn type.  Used to default other attribute values.
+
+;; type "unary" insns have one input operand (1) and one output operand (0)
+;; type "binary" insns have two input operands (1,2) and one output (0)
+;; type "compare" insns have one or two input operands (0,1) and no output
+;; type "call_no_delay_slot" is a call followed by an unimp instruction.
+
+(define_attr "type"
+  "move,unary,binary,compare,load,store,branch,call,call_no_delay_slot,address,fpload,fpstore,fp,fpcmp,fpmul,fpdiv,fpsqrt,multi,misc"
+  (const_string "binary"))
+
+;; Set true if insn uses call-clobbered intermediate register.
+(define_attr "use_clobbered" "false,true"
+  (if_then_else (and (eq_attr "type" "address")
+                    (match_operand 0 "clobbered_register" ""))
+               (const_string "true")
+               (const_string "false")))
+
+;; Length (in # of insns).
+(define_attr "length" ""
+  (cond [(eq_attr "type" "load,fpload")
+        (if_then_else (match_operand 1 "symbolic_memory_operand" "")
+                      (const_int 2) (const_int 1))
+
+        (eq_attr "type" "store,fpstore")
+        (if_then_else (match_operand 0 "symbolic_memory_operand" "")
+                      (const_int 2) (const_int 1))
+
+        (eq_attr "type" "address") (const_int 2)
+
+        (eq_attr "type" "binary")
+        (if_then_else (ior (match_operand 2 "arith_operand" "")
+                           (match_operand 2 "arith_double_operand" ""))
+                      (const_int 1) (const_int 3))
+
+        (eq_attr "type" "move,unary")
+        (if_then_else (ior (match_operand 1 "arith_operand" "")
+                           (match_operand 1 "arith_double_operand" ""))
+                      (const_int 1) (const_int 2))]
+
+       (const_int 1)))
+
+(define_asm_attributes
+  [(set_attr "length" "1")
+   (set_attr "type" "multi")])
+
+;; Attributes for instruction and branch scheduling
+
+(define_attr "in_call_delay" "false,true"
+  (cond [(eq_attr "type" "branch,call,call_no_delay_slot,multi")
+               (const_string "false")
+        (eq_attr "type" "load,fpload,store,fpstore")
+               (if_then_else (eq_attr "length" "1")
+                             (const_string "true")
+                             (const_string "false"))
+        (eq_attr "type" "address")
+               (if_then_else (eq_attr "use_clobbered" "false")
+                             (const_string "true")
+                             (const_string "false"))]
+       (if_then_else (eq_attr "length" "1")
+                     (const_string "true")
+                     (const_string "false"))))
+
+(define_delay (eq_attr "type" "call")
+  [(eq_attr "in_call_delay" "true") (nil) (nil)])
+
+;; ??? Should implement the notion of predelay slots for floating point
+;; branches.  This would allow us to remove the nop always inserted before
+;; a floating point branch.
+
+(define_attr "in_branch_delay" "false,true"
+  (if_then_else (and (eq_attr "type" "!branch,call,call_no_delay_slot,multi")
+                    (eq_attr "length" "1"))
+               (const_string "true")
+               (const_string "false")))
+
+(define_delay (eq_attr "type" "branch")
+  [(eq_attr "in_branch_delay" "true")
+   (nil) (eq_attr "in_branch_delay" "true")])
+
+;; Function units of the SPARC
+
+;; (define_function_unit {name} {num-units} {n-users} {test}
+;;                       {ready-delay} {busy-delay} [{conflict-list}])
+
+;; The integer ALU.
+;; (Noted only for documentation; units that take one cycle do not need to
+;; be specified.)
+
+;; (define_function_unit "alu" 1 0
+;;  (eq_attr "type" "unary,binary,move,address") 1 0)
+
+;; Memory with load-delay of 1 (i.e., 2 cycle load).
+(define_function_unit "memory" 1 0 (eq_attr "type" "load,fpload") 2 0)
+
+;; SPARC has two floating-point units: the FP ALU,
+;; and the FP MUL/DIV/SQRT unit.
+;; Instruction timings on the CY7C602 are as follows
+;; FABSs       4
+;; FADDs/d     5/5
+;; FCMPs/d     4/4
+;; FDIVs/d     23/37
+;; FMOVs       4
+;; FMULs/d     5/7
+;; FNEGs       4
+;; FSQRTs/d    34/63
+;; FSUBs/d     5/5
+;; FdTOi/s     5/5
+;; FsTOi/d     5/5
+;; FiTOs/d     9/5
+
+;; The CY7C602 can only support 2 fp isnsn simultaneously.
+;; More insns cause the chip to stall.  Until we handle this
+;; better in the scheduler, we use excess cycle times to
+;; more evenly spread out fp insns.
+
+(define_function_unit "fp_alu" 1 2 (eq_attr "type" "fp") 8 0)
+(define_function_unit "fp_mul" 1 2 (eq_attr "type" "fpmul") 10 0)
+(define_function_unit "fp_div" 1 2 (eq_attr "type" "fpdiv") 23 0)
+(define_function_unit "fp_sqrt" 1 2 (eq_attr "type" "fpsqrt") 34 0)
+\f
+;; Compare instructions.
+;; This controls RTL generation and register allocation.
+
+;; We generate RTL for comparisons and branches by having the cmpxx 
+;; patterns store away the operands.  Then, the scc and bcc patterns
+;; emit RTL for both the compare and the branch.
+;;
+;; We do this because we want to generate different code for an sne and
+;; seq insn.  In those cases, if the second operand of the compare is not
+;; const0_rtx, we want to compute the xor of the two operands and test
+;; it against zero.
+;;
+;; We start with the DEFINE_EXPANDs, then then DEFINE_INSNs to match
+;; the patterns.  Finally, we have the DEFINE_SPLITs for some of the scc
+;; insns that actually require more than one machine instruction.
+
+;; Put cmpsi first among compare insns so it matches two CONST_INT operands.
+
+(define_expand "cmpsi"
+  [(set (reg:CC 0)
+       (compare:CC (match_operand:SI 0 "register_operand" "")
+                   (match_operand:SI 1 "arith_operand" "")))]
+  ""
+  "
+{
+  sparc_compare_op0 = operands[0];
+  sparc_compare_op1 = operands[1];
+  DONE;
+}")
+
+(define_expand "cmpsf"
+  [(set (reg:CCFP 0)
+       (compare:CCFP (match_operand:SF 0 "register_operand" "")
+                     (match_operand:SF 1 "register_operand" "")))]
+  ""
+  "
+{
+  sparc_compare_op0 = operands[0];
+  sparc_compare_op1 = operands[1];
+  DONE;
+}")
+
+(define_expand "cmpdf"
+  [(set (reg:CCFP 0)
+       (compare:CCFP (match_operand:DF 0 "register_operand" "")
+                     (match_operand:DF 1 "register_operand" "")))]
+  ""
+  "
+{
+  sparc_compare_op0 = operands[0];
+  sparc_compare_op1 = operands[1];
+  DONE;
+}")
+
+;; Next come the scc insns.  For seq, sne, sgeu, and sltu, we can do this
+;; without jumps using the addx/subx instructions.  For the rest, we do
+;; branches.  Seq_special and sne_special clobber the CC reg, because they
+;; generate addcc/subcc instructions.
+
+(define_expand "seq_special"
+  [(set (match_dup 3) (xor:SI (match_operand:SI 1 "register_operand" "")
+                             (match_operand:SI 2 "register_operand" "")))
+   (parallel [(set (match_operand:SI 0 "register_operand" "")
+                  (eq:SI (match_dup 3) (const_int 0)))
+             (clobber (reg:CC 0))])]
+            
+  ""
+  "{ operands[3] = gen_reg_rtx (SImode); }")
+
+(define_expand "sne_special"
+  [(set (match_dup 3) (xor:SI (match_operand:SI 1 "register_operand" "")
+                             (match_operand:SI 2 "register_operand" "")))
+   (parallel [(set (match_operand:SI 0 "register_operand" "")
+                  (ne:SI (match_dup 3) (const_int 0)))
+             (clobber (reg:CC 0))])]
+  ""
+  "{ operands[3] = gen_reg_rtx (SImode); }")
+
+(define_expand "seq"
+  [(set (match_operand:SI 0 "register_operand" "")
+       (eq:SI (match_dup 1) (const_int 0)))]
+  ""
+  "
+{ if (GET_MODE (sparc_compare_op0) == SImode)
+    {
+      emit_insn (gen_seq_special (operands[0], sparc_compare_op0,
+                                 sparc_compare_op1));
+      DONE;
+    }
+  else
+    operands[1] = gen_compare_reg (EQ, sparc_compare_op0, sparc_compare_op1);
+}")
+
+(define_expand "sne"
+  [(set (match_operand:SI 0 "register_operand" "")
+       (ne:SI (match_dup 1) (const_int 0)))]
+  ""
+  "
+{ if (GET_MODE (sparc_compare_op0) == SImode)
+    {
+      emit_insn (gen_sne_special (operands[0], sparc_compare_op0,
+                                 sparc_compare_op1));
+      DONE;
+    }
+  else
+    operands[1] = gen_compare_reg (NE, sparc_compare_op0, sparc_compare_op1);
+}")
+
+(define_expand "sgt"
+  [(set (match_operand:SI 0 "register_operand" "")
+       (gt:SI (match_dup 1) (const_int 0)))]
+  ""
+  "
+{ operands[1] = gen_compare_reg (GT, sparc_compare_op0, sparc_compare_op1); }")
+
+(define_expand "slt"
+  [(set (match_operand:SI 0 "register_operand" "")
+       (lt:SI (match_dup 1) (const_int 0)))]
+  ""
+  "
+{ operands[1] = gen_compare_reg (LT, sparc_compare_op0, sparc_compare_op1); }")
+
+(define_expand "sge"
+  [(set (match_operand:SI 0 "register_operand" "")
+       (ge:SI (match_dup 1) (const_int 0)))]
+  ""
+  "
+{ operands[1] = gen_compare_reg (GE, sparc_compare_op0, sparc_compare_op1); }")
+
+(define_expand "sle"
+  [(set (match_operand:SI 0 "register_operand" "")
+       (le:SI (match_dup 1) (const_int 0)))]
+  ""
+  "
+{ operands[1] = gen_compare_reg (LE, sparc_compare_op0, sparc_compare_op1); }")
+
+(define_expand "sgtu"
+  [(set (match_operand:SI 0 "register_operand" "")
+       (gtu:SI (match_dup 1) (const_int 0)))]
+  ""
+  "
+{
+  rtx tem;
+
+  /* We can do ltu easily, so if both operands are registers, swap them and
+     do a LTU.  */
+  if ((GET_CODE (sparc_compare_op0) == REG
+       || GET_CODE (sparc_compare_op0) == SUBREG)
+      && (GET_CODE (sparc_compare_op1) == REG
+         || GET_CODE (sparc_compare_op1) == SUBREG))
+    {
+      tem = sparc_compare_op0;
+      sparc_compare_op0 = sparc_compare_op1;
+      sparc_compare_op1 = tem;
+      emit_insn (gen_sltu (operands[0]));
+      DONE;
+    }
+
+  operands[1] = gen_compare_reg (LEU, sparc_compare_op0, sparc_compare_op1);
+}")
+
+(define_expand "sltu"
+  [(set (match_operand:SI 0 "register_operand" "")
+       (ltu:SI (match_dup 1) (const_int 0)))]
+  ""
+  "
+{ operands[1] = gen_compare_reg (LTU, sparc_compare_op0, sparc_compare_op1);
+}")
+
+(define_expand "sgeu"
+  [(set (match_operand:SI 0 "register_operand" "")
+       (geu:SI (match_dup 1) (const_int 0)))]
+  ""
+  "
+{ operands[1] = gen_compare_reg (GEU, sparc_compare_op0, sparc_compare_op1);
+}")
+
+(define_expand "sleu"
+  [(set (match_operand:SI 0 "register_operand" "")
+       (leu:SI (match_dup 1) (const_int 0)))]
+  ""
+  "
+{
+  rtx tem;
+
+  /* We can do geu easily, so if both operands are registers, swap them and
+     do a GEU.  */
+  if ((GET_CODE (sparc_compare_op0) == REG
+       || GET_CODE (sparc_compare_op0) == SUBREG)
+      && (GET_CODE (sparc_compare_op1) == REG
+         || GET_CODE (sparc_compare_op1) == SUBREG))
+    {
+      tem = sparc_compare_op0;
+      sparc_compare_op0 = sparc_compare_op1;
+      sparc_compare_op1 = tem;
+      emit_insn (gen_sgeu (operands[0]));
+      DONE;
+    }
+
+  operands[1] = gen_compare_reg (LEU, sparc_compare_op0, sparc_compare_op1);
+}")
+
+;; Now the DEFINE_INSNs for the compare and scc cases.  First the compares.
+
+(define_insn ""
+  [(set (reg:CC 0)
+       (compare:CC (match_operand:SI 0 "register_operand" "rJ")
+                   (match_operand:SI 1 "arith_operand" "rI")))]
+  ""
+  "cmp %r0,%1"
+  [(set_attr "type" "compare")])
+
+(define_insn ""
+  [(set (reg:CCFP 0)
+       (compare:CCFP (match_operand:DF 0 "register_operand" "f")
+                     (match_operand:DF 1 "register_operand" "f")))]
+  ""
+  "fcmped %0,%1"
+  [(set_attr "type" "fpcmp")])
+
+(define_insn ""
+  [(set (reg:CCFP 0)
+       (compare:CCFP (match_operand:SF 0 "register_operand" "f")
+                     (match_operand:SF 1 "register_operand" "f")))]
+  ""
+  "fcmpes %0,%1"
+  [(set_attr "type" "fpcmp")])
+
+;; The SEQ and SNE patterns are special because they can be done
+;; without any branching and do not involve a COMPARE.
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (ne:SI (match_operand:SI 1 "register_operand" "r") (const_int 0)))
+   (clobber (reg:CC 0))]
+  ""
+  "subcc %%g0,%1,%%g0\;addx %%g0,0,%0"
+  [(set_attr "type" "unary")
+   (set_attr "length" "2")])
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (neg:SI (ne:SI (match_operand:SI 1 "register_operand" "r")
+                      (const_int 0))))
+   (clobber (reg:CC 0))]
+  ""
+  "subcc %%g0,%1,%%g0\;subx %%g0,0,%0"
+  [(set_attr "type" "unary")
+   (set_attr "length" "2")])
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (eq:SI (match_operand:SI 1 "register_operand" "r") (const_int 0)))
+   (clobber (reg:CC 0))]
+  ""
+  "subcc %%g0,%1,%%g0\;subx %%g0,-1,%0"
+  [(set_attr "type" "unary")
+   (set_attr "length" "2")])
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (neg:SI (eq:SI (match_operand:SI 1 "register_operand" "r")
+                      (const_int 0))))
+   (clobber (reg:CC 0))]
+  ""
+  "subcc %%g0,%1,%%g0\;addx %%g0,-1,%0"
+  [(set_attr "type" "unary")
+   (set_attr "length" "2")])
+
+;; We can also do (x + (i == 0)) and related, so put them in.
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (plus:SI (ne:SI (match_operand:SI 1 "register_operand" "r")
+                       (const_int 0))
+                (match_operand:SI 2 "register_operand" "r")))
+   (clobber (reg:CC 0))]
+  ""
+  "subcc %%g0,%1,%%g0\;addx %2,0,%0"
+  [(set_attr "length" "2")])
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (minus:SI (match_operand:SI 2 "register_operand" "r")
+                 (ne:SI (match_operand:SI 1 "register_operand" "r")
+                        (const_int 0))))
+   (clobber (reg:CC 0))]
+  ""
+  "subcc %%g0,%1,%%g0\;subx %2,0,%0"
+  [(set_attr "length" "2")])
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (plus:SI (eq:SI (match_operand:SI 1 "register_operand" "r")
+                       (const_int 0))
+                (match_operand:SI 2 "register_operand" "r")))
+   (clobber (reg:CC 0))]
+  ""
+  "subcc %%g0,%1,%%g0\;subx %2,-1,%0"
+  [(set_attr "length" "2")])
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (minus:SI (match_operand:SI 2 "register_operand" "r")
+                 (eq:SI (match_operand:SI 1 "register_operand" "r")
+                        (const_int 0))))
+   (clobber (reg:CC 0))]
+  ""
+  "subcc %%g0,%1,%%g0\;addx %2,-1,%0"
+  [(set_attr "type" "unary")
+   (set_attr "length" "2")])
+
+;; We can also do GEU and LTU directly, but these operate after a
+;; compare.
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (ltu:SI (reg:CC 0) (const_int 0)))]
+  ""
+  "addx %%g0,0,%0"
+  [(set_attr "type" "misc")])
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (neg:SI (ltu:SI (reg:CC 0) (const_int 0))))]
+  ""
+  "subx %%g0,0,%0"
+  [(set_attr "type" "misc")])
+
+;; ??? Combine should canonicalize these next two to the same pattern.
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (minus:SI (neg:SI (ltu:SI (reg:CC 0) (const_int 0)))
+                 (match_operand:SI 1 "arith_operand" "rI")))]
+  ""
+  "subx %%g0,%1,%0"
+  [(set_attr "type" "unary")])
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (neg:SI (plus:SI (ltu:SI (reg:CC 0) (const_int 0))
+                        (match_operand:SI 1 "arith_operand" "rI"))))]
+  ""
+  "subx %%g0,%1,%0"
+  [(set_attr "type" "unary")])
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (geu:SI (reg:CC 0) (const_int 0)))]
+  ""
+  "subx %%g0,-1,%0"
+  [(set_attr "type" "misc")])
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (neg:SI (geu:SI (reg:CC 0) (const_int 0))))]
+  ""
+  "addx %%g0,-1,%0"
+  [(set_attr "type" "misc")])
+
+;; We can also do (x + ((unsigned) i >= 0)) and related, so put them in.
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (plus:SI (ltu:SI (reg:CC 0) (const_int 0))
+                (match_operand:SI 1 "arith_operand" "rI")))]
+  ""
+  "addx %%g0,%1,%0"
+  [(set_attr "type" "unary")])
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (plus:SI (ltu:SI (reg:CC 0) (const_int 0))
+                (plus:SI (match_operand:SI 1 "arith_operand" "%r")
+                         (match_operand:SI 2 "arith_operand" "rI"))))]
+  ""
+  "addx %1,%2,%0"
+  [(set_attr "type" "binary")])
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (minus:SI (match_operand:SI 1 "register_operand" "r")
+                 (ltu:SI (reg:CC 0) (const_int 0))))]
+  ""
+  "subx %1,0,%0"
+  [(set_attr "type" "unary")])
+
+;; ??? Combine should canonicalize these next two to the same pattern.
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (minus:SI (minus:SI (match_operand:SI 1 "register_operand" "r")
+                           (match_operand:SI 2 "arith_operand" "rI"))
+                 (ltu:SI (reg:CC 0) (const_int 0))))]
+  ""
+  "subx %1,%2,%0"
+  [(set_attr "type" "binary")])
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (minus:SI (match_operand:SI 1 "register_operand" "r")
+                 (plus:SI (ltu:SI (reg:CC 0) (const_int 0))
+                          (match_operand:SI 2 "arith_operand" "rI"))))]
+  ""
+  "subx %1,%2,%0"
+  [(set_attr "type" "binary")])
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (plus:SI (geu:SI (reg:CC 0) (const_int 0))
+                (match_operand:SI 1 "register_operand" "r")))]
+  ""
+  "subx %1,-1,%0"
+  [(set_attr "type" "unary")])
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (minus:SI (match_operand:SI 1 "register_operand" "r")
+                 (geu:SI (reg:CC 0) (const_int 0))))]
+  ""
+  "addx %1,-1,%0"
+  [(set_attr "type" "unary")])
+
+;; Now we have the generic scc insns.  These will be done using a jump.
+;; We have to exclude the cases above, since we will not want combine to
+;; turn something that does not require a jump into something that does.
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (match_operator:SI 1 "normal_comp_operator" [(reg 0) (const_int 0)]))]
+  ""
+  "* return output_scc_insn (operands, insn); "
+  [(set_attr "type" "multi")])
+\f
+;; These control RTL generation for conditional jump insns
+
+(define_expand "beq"
+  [(set (pc)
+       (if_then_else (eq (match_dup 1) (const_int 0))
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))]
+  ""
+  "
+{ operands[1] = gen_compare_reg (EQ, sparc_compare_op0, sparc_compare_op1); }")
+
+(define_expand "bne"
+  [(set (pc)
+       (if_then_else (ne (match_dup 1) (const_int 0))
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))]
+  ""
+  "
+{ operands[1] = gen_compare_reg (NE, sparc_compare_op0, sparc_compare_op1); }")
+
+(define_expand "bgt"
+  [(set (pc)
+       (if_then_else (gt (match_dup 1) (const_int 0))
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))]
+  ""
+  "
+{ operands[1] = gen_compare_reg (GT, sparc_compare_op0, sparc_compare_op1); }")
+
+(define_expand "bgtu"
+  [(set (pc)
+       (if_then_else (gtu (match_dup 1) (const_int 0))
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))]
+  ""
+  "
+{ operands[1] = gen_compare_reg (GTU, sparc_compare_op0, sparc_compare_op1);
+}")
+
+(define_expand "blt"
+  [(set (pc)
+       (if_then_else (lt (match_dup 1) (const_int 0))
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))]
+  ""
+  "
+{ operands[1] = gen_compare_reg (LT, sparc_compare_op0, sparc_compare_op1); }")
+
+(define_expand "bltu"
+  [(set (pc)
+       (if_then_else (ltu (match_dup 1) (const_int 0))
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))]
+  ""
+  "
+{ operands[1] = gen_compare_reg (LTU, sparc_compare_op0, sparc_compare_op1);
+}")
+
+(define_expand "bge"
+  [(set (pc)
+       (if_then_else (ge (match_dup 1) (const_int 0))
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))]
+  ""
+  "
+{ operands[1] = gen_compare_reg (GE, sparc_compare_op0, sparc_compare_op1); }")
+
+(define_expand "bgeu"
+  [(set (pc)
+       (if_then_else (geu (match_dup 1) (const_int 0))
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))]
+  ""
+  "
+{ operands[1] = gen_compare_reg (GEU, sparc_compare_op0, sparc_compare_op1);
+}")
+
+(define_expand "ble"
+  [(set (pc)
+       (if_then_else (le (match_dup 1) (const_int 0))
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))]
+  ""
+  "
+{ operands[1] = gen_compare_reg (LE, sparc_compare_op0, sparc_compare_op1); }")
+
+(define_expand "bleu"
+  [(set (pc)
+       (if_then_else (leu (match_dup 1) (const_int 0))
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))]
+  ""
+  "
+{ operands[1] = gen_compare_reg (LEU, sparc_compare_op0, sparc_compare_op1);
+}")
+\f
+;; Now match both normal and inverted jump.
+
+(define_insn ""
+  [(set (pc)
+       (if_then_else (match_operator 0 "noov_compare_op"
+                                     [(reg 0) (const_int 0)])
+                     (label_ref (match_operand 1 "" ""))
+                     (pc)))]
+  ""
+  "*
+{
+  return output_cbranch (operands[0], 1, 0,
+                        final_sequence && INSN_ANNULLED_BRANCH_P (insn),
+                        ! final_sequence);
+}"
+  [(set_attr "type" "branch")])
+
+(define_insn ""
+  [(set (pc)
+       (if_then_else (match_operator 0 "noov_compare_op"
+                                     [(reg 0) (const_int 0)])
+                     (pc)
+                     (label_ref (match_operand 1 "" ""))))]
+  ""
+  "*
+{
+  return output_cbranch (operands[0], 1, 1,
+                        final_sequence && INSN_ANNULLED_BRANCH_P (insn),
+                        ! final_sequence);
+}"
+  [(set_attr "type" "branch")])
+\f
+;; Move instructions
+
+(define_expand "movsi"
+  [(set (match_operand:SI 0 "general_operand" "")
+       (match_operand:SI 1 "general_operand" ""))]
+  ""
+  "
+{
+  if (emit_move_sequence (operands, SImode, 0))
+    DONE;
+}")
+
+(define_expand "reload_insi"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (match_operand:SI 1 "general_operand" ""))
+   (clobber (match_operand:SI 2 "register_operand" "=&r"))]
+  ""
+  "
+{
+  if (emit_move_sequence (operands, SImode, operands[2]))
+    DONE;
+
+  /* We don't want the clobber emitted, so handle this ourselves.  */
+  emit_insn (gen_rtx (SET, VOIDmode, operands[0], operands[1]));
+  DONE;
+}")
+
+;; We must support both 'r' and 'f' registers here, because combine may
+;; convert SFmode hard registers to SImode hard registers when simplifying
+;; subreg sets.
+
+;; We cannot combine the similar 'r' and 'f' constraints, because it causes
+;; problems with register allocation.  Reload might try to put an interger
+;; in an fp register, or an fp number is an integer register.
+
+(define_insn ""
+  [(set (match_operand:SI 0 "reg_or_nonsymb_mem_operand" "=r,r,r,f,Q,Q,rf")
+       (match_operand:SI 1 "move_operand" "rI,K,Q,!Q,rJ,!f,!fr"))]
+  "register_operand (operands[0], SImode)
+   || register_operand (operands[1], SImode)
+   || operands[1] == const0_rtx"
+  "@
+   mov %1,%0
+   sethi %%hi(%a1),%0
+   ld %1,%0
+   ld %1,%0
+   st %r1,%0
+   st %r1,%0
+   st %r1,[%%fp-4]\;ld [%%fp-4],%0"
+  [(set_attr "type" "move,move,load,load,store,store,misc")
+   (set_attr "length" "*,1,*,*,*,*,2")])
+
+;; Special pic pattern, for loading the address of a label into a register.
+;; It clobbers o7 because the call puts the return address (i.e. pc value)
+;; there.
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (match_operand:SI 1 "move_pic_label" "i"))
+   (set (reg:SI 15) (pc))]
+  ""
+  "\\n1:\;call 2f\;sethi %%hi(%l1-1b),%0\\n2:\\tor %0,%%lo(%l1-1b),%0\;add %0,%%o7,%0"
+  [(set_attr "type" "multi")
+   (set_attr "length" "4")])
+
+(define_insn ""
+  [(set (match_operand:DI 0 "register_operand" "=r")
+       (high:DI (match_operand 1 "" "")))]
+  "check_pic (1)"
+  "*
+{
+  rtx op0 = operands[0];
+  rtx op1 = operands[1];
+
+  if (GET_CODE (op1) == CONST_INT)
+    {
+      operands[0] = operand_subword (op0, 1, 0, DImode);
+      output_asm_insn (\"sethi %%hi(%a1),%0\", operands);
+
+      operands[0] = operand_subword (op0, 0, 0, DImode);
+      if (INTVAL (op1) < 0)
+       output_asm_insn (\"mov -1,%0\", operands);
+      else
+       output_asm_insn (\"mov 0,%0\", operands);
+    }
+  else if (GET_CODE (op1) == CONST_DOUBLE)
+    {
+      operands[0] = operand_subword (op0, 1, 0, DImode);
+      operands[1] = gen_rtx (CONST_INT, VOIDmode, CONST_DOUBLE_HIGH (op1));
+      output_asm_insn (\"sethi %%hi(%a1),%0\", operands);
+
+      operands[0] = operand_subword (op0, 0, 0, DImode);
+      operands[1] = gen_rtx (CONST_INT, VOIDmode, CONST_DOUBLE_LOW (op1));
+      output_asm_insn (singlemove_string (operands), operands);
+    }
+  else
+    abort ();
+}"
+  [(set_attr "type" "move")
+   (set_attr "length" "2")])
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (high:SI (match_operand 1 "" "")))]
+  "check_pic (1)"
+  "sethi %%hi(%a1),%0"
+  [(set_attr "type" "move")
+   (set_attr "length" "1")])
+
+(define_insn ""
+  [(set (match_operand:HI 0 "register_operand" "=r")
+       (high:HI (match_operand 1 "" "")))]
+  "check_pic (1)"
+  "sethi %%hi(%a1),%0"
+  [(set_attr "type" "move")
+   (set_attr "length" "1")])
+
+(define_insn ""
+  [(set (match_operand:DI 0 "register_operand" "=r")
+       (lo_sum:DI (match_operand:DI 1 "register_operand" "r")
+                  (match_operand:DI 2 "immediate_operand" "in")))]
+  ""
+  "or %R1,%%lo(%a2),%R0"
+  ;; Need to set length for this arith insn because operand2
+  ;; is not an "arith_operand".
+  [(set_attr "length" "1")])
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (lo_sum:SI (match_operand:SI 1 "register_operand" "r")
+                  (match_operand:SI 2 "immediate_operand" "in")))]
+  ""
+  "or %1,%%lo(%a2),%0"
+  ;; Need to set length for this arith insn because operand2
+  ;; is not an "arith_operand".
+  [(set_attr "length" "1")])
+
+(define_insn ""
+  [(set (mem:SI (match_operand:SI 0 "symbolic_operand" ""))
+       (match_operand:SI 1 "reg_or_0_operand" "rJ"))
+   (clobber (match_scratch:SI 2 "=&r"))]
+  ""
+  "sethi %%hi(%a0),%2\;st %r1,[%2+%%lo(%a0)]"
+  [(set_attr "type" "store")
+   (set_attr "length" "2")])
+
+(define_expand "movhi"
+  [(set (match_operand:HI 0 "general_operand" "")
+       (match_operand:HI 1 "general_operand" ""))]
+  ""
+  "
+{
+  if (emit_move_sequence (operands, HImode, 0))
+    DONE;
+}")
+
+(define_insn ""
+  [(set (match_operand:HI 0 "reg_or_nonsymb_mem_operand" "=r,r,r,Q")
+       (match_operand:HI 1 "move_operand" "rI,K,Q,rJ"))]
+  "register_operand (operands[0], HImode)
+   || register_operand (operands[1], HImode)
+   || operands[1] == const0_rtx"
+  "@
+   mov %1,%0
+   sethi %%hi(%a1),%0
+   lduh %1,%0
+   sth %r1,%0"
+  [(set_attr "type" "move,move,load,store")
+   (set_attr "length" "*,1,*,1")])
+
+(define_insn ""
+  [(set (match_operand:HI 0 "register_operand" "=r")
+       (lo_sum:HI (match_operand:HI 1 "register_operand" "r")
+                  (match_operand 2 "immediate_operand" "in")))]
+  ""
+  "or %1,%%lo(%a2),%0"
+  [(set_attr "length" "1")])
+
+(define_insn ""
+  [(set (mem:HI (match_operand:SI 0 "symbolic_operand" ""))
+       (match_operand:HI 1 "reg_or_0_operand" "rJ"))
+   (clobber (match_scratch:SI 2 "=&r"))]
+  ""
+  "sethi %%hi(%a0),%2\;sth %r1,[%2+%%lo(%a0)]"
+  [(set_attr "type" "store")
+   (set_attr "length" "2")])
+
+(define_expand "movqi"
+  [(set (match_operand:QI 0 "general_operand" "")
+       (match_operand:QI 1 "general_operand" ""))]
+  ""
+  "
+{
+  if (emit_move_sequence (operands, QImode, 0))
+    DONE;
+}")
+
+(define_insn ""
+  [(set (match_operand:QI 0 "reg_or_nonsymb_mem_operand" "=r,r,r,Q")
+       (match_operand:QI 1 "move_operand" "rI,K,Q,rJ"))]
+  "register_operand (operands[0], QImode)
+   || register_operand (operands[1], QImode)
+   || operands[1] == const0_rtx"
+  "@
+   mov %1,%0
+   sethi %%hi(%a1),%0
+   ldub %1,%0
+   stb %r1,%0"
+  [(set_attr "type" "move,move,load,store")
+   (set_attr "length" "*,1,*,1")])
+
+(define_insn ""
+  [(set (match_operand:QI 0 "register_operand" "=r")
+       (subreg:QI (lo_sum:SI (match_operand:QI 1 "register_operand" "r")
+                             (match_operand 2 "immediate_operand" "in")) 0))]
+  ""
+  "or %1,%%lo(%a2),%0"
+  [(set_attr "length" "1")])
+
+(define_insn ""
+  [(set (mem:QI (match_operand:SI 0 "symbolic_operand" ""))
+       (match_operand:QI 1 "reg_or_0_operand" "rJ"))
+   (clobber (match_scratch:SI 2 "=&r"))]
+  ""
+  "sethi %%hi(%a0),%2\;stb %r1,[%2+%%lo(%a0)]"
+  [(set_attr "type" "store")
+   (set_attr "length" "2")])
+
+;; The definition of this insn does not really explain what it does,
+;; but it should suffice
+;; that anything generated as this insn will be recognized as one
+;; and that it will not successfully combine with anything.
+(define_expand "movstrsi"
+  [(parallel [(set (mem:BLK (match_operand:BLK 0 "general_operand" ""))
+                  (mem:BLK (match_operand:BLK 1 "general_operand" "")))
+             (use (match_operand:SI 2 "arith32_operand" ""))
+             (use (match_operand:SI 3 "immediate_operand" ""))
+             (clobber (match_dup 0))
+             (clobber (match_dup 1))
+             (clobber (match_scratch:SI 4 ""))
+             (clobber (reg:SI 0))
+             (clobber (reg:SI 1))])]
+  ""
+  "
+{
+  if (GET_CODE (operands[2]) == CONST_INT
+      && GET_CODE (operands[3]) == CONST_INT
+      && INTVAL (operands[2]) / INTVAL (operands[3]) > 32)
+    FAIL;
+
+  operands[0] = copy_to_mode_reg (Pmode, XEXP (operands[0], 0));
+  operands[1] = copy_to_mode_reg (Pmode, XEXP (operands[1], 0));
+  operands[2] = force_not_mem (operands[2]);
+}")
+
+(define_insn ""
+  [(set (mem:BLK (match_operand:SI 0 "register_operand" "r"))
+       (mem:BLK (match_operand:SI 1 "register_operand" "r")))
+   (use (match_operand:SI 2 "arith32_operand" "rn"))
+   (use (match_operand:SI 3 "immediate_operand" "i"))
+   (clobber (match_dup 0))
+   (clobber (match_dup 1))
+   (clobber (match_scratch:SI 4 "=&r"))
+   (clobber (reg:SI 0))
+   (clobber (reg:SI 1))]
+  ""
+  "* return output_block_move (operands);"
+  [(set_attr "type" "multi")])
+\f
+;; Floating point move insns
+
+;; This pattern forces (set (reg:DF ...) (const_double ...))
+;; to be reloaded by putting the constant into memory.
+;; It must come before the more general movdf pattern.
+;; ??? A similar pattern for SF mode values would also be useful, but it
+;; is not as easy to write.
+(define_insn ""
+  [(set (match_operand:DF 0 "general_operand" "=?r,r,f,o")
+       (match_operand:DF 1 "" "?E,G,m,G"))]
+  "GET_CODE (operands[1]) == CONST_DOUBLE"
+  "*
+{
+  switch (which_alternative)
+    {
+    case 0:
+      return output_move_double (operands);
+    case 1:
+      return \"mov %%g0,%0\;mov %%g0,%R0\";
+    case 2:
+      return output_fp_move_double (operands);
+    case 3:
+      operands[1] = adj_offsettable_operand (operands[0], 4);
+      return \"st %%g0,%0\;st %%g0,%1\";
+    }
+}"
+  [(set_attr "type" "load,move,fpload,store")
+   (set_attr "length" "3,2,3,3")])
+
+(define_expand "movdf"
+  [(set (match_operand:DF 0 "general_operand" "")
+       (match_operand:DF 1 "general_operand" ""))]
+  ""
+  "
+{
+  if (emit_move_sequence (operands, DFmode, 0))
+    DONE;
+}")
+
+(define_insn ""
+  [(set (match_operand:DF 0 "reg_or_nonsymb_mem_operand" "=f,r,Q,Q,f,&r,?f,?r")
+       (match_operand:DF 1 "reg_or_nonsymb_mem_operand" "f,r,f,r,Q,Q,r,f"))]
+  "register_operand (operands[0], DFmode)
+   || register_operand (operands[1], DFmode)"
+  "*
+{
+  if (FP_REG_P (operands[0]) || FP_REG_P (operands[1]))
+    return output_fp_move_double (operands);
+  return output_move_double (operands);
+}"
+  [(set_attr "type" "fp,move,fpstore,store,fpload,load,multi,multi")
+   (set_attr "length" "2,2,3,3,3,3,3,3")])
+
+(define_insn ""
+  [(set (mem:DF (match_operand:SI 0 "symbolic_operand" "i,i"))
+       (match_operand:DF 1 "reg_or_0_operand" "rf,G"))
+   (clobber (match_scratch:SI 2 "=&r,&r"))]
+  ""
+  "*
+{
+  output_asm_insn (\"sethi %%hi(%a0),%2\", operands);
+  if (which_alternative == 0)
+    return \"std %1,[%2+%%lo(%a0)]\";
+  else
+    return \"st %%g0,[%2+%%lo(%a0)]\;st %%g0,[%2+%%lo(%a0+4)]\";
+}"
+  [(set_attr "type" "store")
+   (set_attr "length" "3")])
+\f
+;; Double-word move insns.
+
+(define_expand "movdi"
+  [(set (match_operand:DI 0 "reg_or_nonsymb_mem_operand" "")
+       (match_operand:DI 1 "general_operand" ""))]
+  ""
+  "
+{
+  if (emit_move_sequence (operands, DImode, 0))
+    DONE;
+}")
+
+(define_insn ""
+  [(set (match_operand:DI 0 "reg_or_nonsymb_mem_operand" "=r,Q,&r,&r,?f,?f,?f,?r,?Q")
+       (match_operand:DI 1 "general_operand" "r,r,Q,i,r,f,Q,f,f"))]
+  "register_operand (operands[0], DImode)
+   || register_operand (operands[1], DImode)
+   || operands[1] == const0_rtx"
+  "*
+{
+  if (FP_REG_P (operands[0]) || FP_REG_P (operands[1]))
+    return output_fp_move_double (operands);
+  return output_move_double (operands);
+}"
+  [(set_attr "type" "move,store,load,misc,multi,fp,fpload,multi,fpstore")
+   (set_attr "length" "2,3,3,3,3,2,3,3,3")])
+
+;; Floating-point move insns.
+
+(define_expand "movsf"
+  [(set (match_operand:SF 0 "general_operand" "")
+       (match_operand:SF 1 "general_operand" ""))]
+  ""
+  "
+{
+  if (emit_move_sequence (operands, SFmode, 0))
+    DONE;
+}")
+
+(define_insn ""
+  [(set (match_operand:SF 0 "reg_or_nonsymb_mem_operand" "=f,r,rf,f,r,Q,Q")
+       (match_operand:SF 1 "reg_or_nonsymb_mem_operand" "f,r,!rf,Q,Q,f,r"))]
+  "register_operand (operands[0], SFmode)
+   || register_operand (operands[1], SFmode)"
+  "@
+   fmovs %1,%0
+   mov %1,%0
+   st %r1,[%%fp-4]\;ld [%%fp-4],%0
+   ld %1,%0
+   ld %1,%0
+   st %r1,%0
+   st %r1,%0"
+  [(set_attr "type" "fp,move,multi,fpload,load,fpstore,store")
+   (set_attr "length" "*,*,2,*,*,*,*")])
+
+(define_insn ""
+  [(set (mem:SF (match_operand:SI 0 "symbolic_operand" "i"))
+       (match_operand:SF 1 "reg_or_0_operand" "rfG"))
+   (clobber (match_scratch:SI 2 "=&r"))]
+  ""
+  "sethi %%hi(%a0),%2\;st %r1,[%2+%%lo(%a0)]"
+  [(set_attr "type" "store")
+   (set_attr "length" "2")])
+\f
+;;- zero extension instructions
+
+;; These patterns originally accepted general_operands, however, slightly
+;; better code is generated by only accepting register_operands, and then
+;; letting combine generate the ldu[hb] insns.
+
+(define_expand "zero_extendhisi2"
+  [(set (match_operand:SI 0 "register_operand" "")
+       (zero_extend:SI (match_operand:HI 1 "register_operand" "")))]
+  ""
+  "
+{
+  rtx temp = gen_reg_rtx (SImode);
+  rtx shift_16 = gen_rtx (CONST_INT, VOIDmode, 16);
+
+  if (GET_CODE (operand1) == SUBREG)
+    operand1 = XEXP (operand1, 0);
+
+  emit_insn (gen_ashlsi3 (temp, gen_rtx (SUBREG, SImode, operand1, 0),
+                         shift_16));
+  emit_insn (gen_lshrsi3 (operand0, temp, shift_16));
+  DONE;
+}")
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (zero_extend:SI (match_operand:HI 1 "memory_operand" "m")))]
+  ""
+  "lduh %1,%0"
+  [(set_attr "type" "load")])
+
+(define_expand "zero_extendqihi2"
+  [(set (match_operand:HI 0 "register_operand" "")
+       (zero_extend:HI (match_operand:QI 1 "register_operand" "")))]
+  ""
+  "")
+
+(define_insn ""
+  [(set (match_operand:HI 0 "register_operand" "=r,r,r")
+       (zero_extend:HI (match_operand:QI 1 "sparc_operand" "r,I,Q")))]
+  "GET_CODE (operands[1]) != CONST_INT"
+  "@
+   and %1,0xff,%0;
+   mov (%1 & 0xff),%0
+   ldub %1,%0"
+  [(set_attr "type" "unary,move,load")
+   (set_attr "length" "1")])
+
+(define_expand "zero_extendqisi2"
+  [(set (match_operand:SI 0 "register_operand" "")
+       (zero_extend:SI (match_operand:QI 1 "register_operand" "")))]
+  ""
+  "")
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=r,r,r")
+       (zero_extend:SI (match_operand:QI 1 "sparc_operand" "r,I,Q")))]
+  "GET_CODE (operands[1]) != CONST_INT"
+  "@
+   and %1,0xff,%0
+   mov (%1 & 0xff),%0
+   ldub %1,%0"
+  [(set_attr "type" "unary,move,load")
+   (set_attr "length" "1")])
+
+(define_insn ""
+  [(set (reg:CC 0)
+       (compare:CC (zero_extend:SI (match_operand:QI 0 "register_operand" "r"))
+                   (const_int 0)))]
+  ""
+  "andcc %0,0xff,%%g0"
+  [(set_attr "type" "compare")])
+
+(define_insn ""
+  [(set (reg:CC 0)
+       (compare:CC (zero_extend:SI (match_operand:QI 1 "register_operand" "r"))
+                   (const_int 0)))
+   (set (match_operand:SI 0 "register_operand" "=r")
+       (zero_extend:SI (match_dup 1)))]
+  ""
+  "andcc %1,0xff,%0"
+  [(set_attr "type" "unary")])
+\f
+;;- sign extension instructions
+
+;; These patterns originally accepted general_operands, however, slightly
+;; better code is generated by only accepting register_operands, and then
+;; letting combine generate the lds[hb] insns.
+
+(define_expand "extendhisi2"
+  [(set (match_operand:SI 0 "register_operand" "")
+       (sign_extend:SI (match_operand:HI 1 "register_operand" "")))]
+  ""
+  "
+{
+  rtx temp = gen_reg_rtx (SImode);
+  rtx shift_16 = gen_rtx (CONST_INT, VOIDmode, 16);
+
+  if (GET_CODE (operand1) == SUBREG)
+    operand1 = XEXP (operand1, 0);
+
+  emit_insn (gen_ashlsi3 (temp, gen_rtx (SUBREG, SImode, operand1, 0),
+                         shift_16));
+  emit_insn (gen_ashrsi3 (operand0, temp, shift_16));
+  DONE;
+}")
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (sign_extend:SI (match_operand:HI 1 "memory_operand" "m")))]
+  ""
+  "ldsh %1,%0"
+  [(set_attr "type" "load")])
+
+(define_expand "extendqihi2"
+  [(set (match_operand:HI 0 "register_operand" "")
+       (sign_extend:HI (match_operand:QI 1 "register_operand" "")))]
+  ""
+  "
+{
+  rtx temp = gen_reg_rtx (SImode);
+  rtx shift_24 = gen_rtx (CONST_INT, VOIDmode, 24);
+
+  if (GET_CODE (operand1) == SUBREG)
+    operand1 = XEXP (operand1, 0);
+  if (GET_CODE (operand0) == SUBREG)
+    operand0 = XEXP (operand0, 0);
+  emit_insn (gen_ashlsi3 (temp, gen_rtx (SUBREG, SImode, operand1, 0),
+                         shift_24));
+  if (GET_MODE (operand0) != SImode)
+    operand0 = gen_rtx (SUBREG, SImode, operand0, 0);
+  emit_insn (gen_ashrsi3 (operand0, temp, shift_24));
+  DONE;
+}")
+
+(define_insn ""
+  [(set (match_operand:HI 0 "register_operand" "=r")
+       (sign_extend:HI (match_operand:QI 1 "memory_operand" "m")))]
+  ""
+  "ldsb %1,%0"
+  [(set_attr "type" "load")])
+
+(define_expand "extendqisi2"
+  [(set (match_operand:SI 0 "register_operand" "")
+       (sign_extend:SI (match_operand:QI 1 "register_operand" "")))]
+  ""
+  "
+{
+  rtx temp = gen_reg_rtx (SImode);
+  rtx shift_24 = gen_rtx (CONST_INT, VOIDmode, 24);
+
+  if (GET_CODE (operand1) == SUBREG)
+    operand1 = XEXP (operand1, 0);
+  emit_insn (gen_ashlsi3 (temp, gen_rtx (SUBREG, SImode, operand1, 0),
+                         shift_24));
+  emit_insn (gen_ashrsi3 (operand0, temp, shift_24));
+  DONE;
+}")
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (sign_extend:SI (match_operand:QI 1 "memory_operand" "m")))]
+  ""
+  "ldsb %1,%0"
+  [(set_attr "type" "load")])
+\f
+;; Conversions between float and double.
+
+(define_insn "extendsfdf2"
+  [(set (match_operand:DF 0 "register_operand" "=f")
+       (float_extend:DF
+        (match_operand:SF 1 "register_operand" "f")))]
+  ""
+  "fstod %1,%0"
+  [(set_attr "type" "fp")])
+
+(define_insn "truncdfsf2"
+  [(set (match_operand:SF 0 "register_operand" "=f")
+       (float_truncate:SF
+        (match_operand:DF 1 "register_operand" "f")))]
+  ""
+  "fdtos %1,%0"
+  [(set_attr "type" "fp")])
+\f
+;; Conversion between fixed point and floating point.
+
+(define_insn "floatsisf2"
+  [(set (match_operand:SF 0 "general_operand" "=f")
+       (float:SF (match_operand:SI 1 "nonimmediate_operand" "rfm")))]
+  ""
+  "* return output_floatsisf2 (operands);"
+  [(set_attr "type" "fp")
+   (set_attr "length" "3")])
+
+(define_insn "floatsidf2"
+  [(set (match_operand:DF 0 "general_operand" "=f")
+       (float:DF (match_operand:SI 1 "nonimmediate_operand" "rfm")))]
+  ""
+  "* return output_floatsidf2 (operands);"
+  [(set_attr "type" "fp")
+   (set_attr "length" "3")])
+
+;; Convert a float to an actual integer.
+;; Truncation is performed as part of the conversion.
+
+(define_insn "fix_truncsfsi2"
+  [(set (match_operand:SI 0 "general_operand" "=rm")
+       (fix:SI (fix:SF (match_operand:SF 1 "general_operand" "fm"))))
+   (clobber (match_scratch:SF 2 "=&f"))]
+  ""
+  "*
+{
+  if (FP_REG_P (operands[1]))
+    output_asm_insn (\"fstoi %1,%2\", operands);
+  else
+    output_asm_insn (\"ld %1,%2\;fstoi %2,%2\", operands);
+  if (GET_CODE (operands[0]) == MEM)
+    return \"st %2,%0\";
+  else
+    return \"st %2,[%%fp-4]\;ld [%%fp-4],%0\";
+}"
+  [(set_attr "type" "fp")
+   (set_attr "length" "3")])
+
+(define_insn "fix_truncdfsi2"
+  [(set (match_operand:SI 0 "general_operand" "=rm")
+       (fix:SI (fix:DF (match_operand:DF 1 "general_operand" "fm"))))
+   (clobber (match_scratch:DF 2 "=&f"))]
+  ""
+  "*
+{
+  if (FP_REG_P (operands[1]))
+    output_asm_insn (\"fdtoi %1,%2\", operands);
+  else
+    {
+      rtx xoperands[3];
+      xoperands[0] = operands[2];
+      xoperands[1] = operands[1];
+      output_asm_insn (output_fp_move_double (xoperands), xoperands);
+      output_asm_insn (\"fdtoi %2,%2\", operands);
+    }
+  if (GET_CODE (operands[0]) == MEM)
+    return \"st %2,%0\";
+  else
+    return \"st %2,[%%fp-4]\;ld [%%fp-4],%0\";
+}"
+  [(set_attr "type" "fp")
+   (set_attr "length" "3")])
+\f
+;;- arithmetic instructions
+
+(define_insn "adddi3"
+  [(set (match_operand:DI 0 "register_operand" "=r")
+       (plus:DI (match_operand:DI 1 "arith_double_operand" "%r")
+                (match_operand:DI 2 "arith_double_operand" "rHI")))
+   (clobber (reg:SI 0))]
+  ""
+  "*
+{
+  rtx op2 = operands[2];
+
+  /* If constant is postive, upper bits zeroed, otherwise unchanged
+   * give the assembler a chance to pick the move instruction. */
+  if (GET_CODE (op2) == CONST_INT)
+    {
+      int sign = INTVAL (op2);
+      if (sign < 0)
+       return \"addcc %R1,%2,%R0\;addx %1,-1,%0\";
+      return \"addcc %R1,%2,%R0\;addx %1,0,%0\";
+    }
+  else if (GET_CODE (op2) == CONST_DOUBLE)
+    {
+      int sign = CONST_DOUBLE_HIGH (op2);
+      operands[2] = gen_rtx (CONST_INT, VOIDmode,
+                            CONST_DOUBLE_LOW (operands[1]));
+      if (sign < 0)
+        return \"addcc %R1,%2,%R0\;addx %1,-1,%0\";
+      return \"addcc %R1,%2,%R0\;addx %1,0,%0\";
+    }
+  return \"addcc %R1,%R2,%R0\;addx %1,%2,%0\";
+}"
+  [(set_attr "length" "2")])
+
+(define_insn "addsi3"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (plus:SI (match_operand:SI 1 "arith_operand" "%r")
+                (match_operand:SI 2 "arith_operand" "rI")))]
+  ""
+  "add %1,%2,%0")
+
+(define_insn ""
+  [(set (reg:CC_NOOV 0)
+       (compare:CC_NOOV (plus:SI (match_operand:SI 0 "arith_operand" "%r")
+                                 (match_operand:SI 1 "arith_operand" "rI"))
+                        (const_int 0)))]
+  ""
+  "addcc %0,%1,%%g0"
+  [(set_attr "type" "compare")])
+
+(define_insn ""
+  [(set (reg:CC_NOOV 0)
+       (compare:CC_NOOV (plus:SI (match_operand:SI 1 "arith_operand" "%r")
+                                 (match_operand:SI 2 "arith_operand" "rI"))
+                        (const_int 0)))
+   (set (match_operand:SI 0 "register_operand" "=r")
+       (plus:SI (match_dup 1) (match_dup 2)))]
+  ""
+  "addcc %1,%2,%0")
+
+(define_insn "subdi3"
+  [(set (match_operand:DI 0 "register_operand" "=r")
+       (minus:DI (match_operand:DI 1 "register_operand" "r")
+                 (match_operand:DI 2 "arith_double_operand" "rHI")))
+   (clobber (reg:SI 0))]
+  ""
+  "*
+{
+  rtx op2 = operands[2];
+
+  /* If constant is postive, upper bits zeroed, otherwise unchanged
+   * give the assembler a chance to pick the move instruction. */
+  if (GET_CODE (op2) == CONST_INT)
+    {
+      int sign = INTVAL (op2);
+      if (sign < 0)
+       return \"subcc %R1,%2,%R0\;subx %1,-1,%0\";
+      return \"subcc %R1,%2,%R0\;subx %1,0,%0\";
+    }
+  else if (GET_CODE (op2) == CONST_DOUBLE)
+    {
+      int sign = CONST_DOUBLE_HIGH (op2);
+      operands[2] = gen_rtx (CONST_INT, VOIDmode,
+                            CONST_DOUBLE_LOW (operands[1]));
+      if (sign < 0)
+        return \"subcc %R1,%2,%R0\;subx %1,-1,%0\";
+      return \"subcc %R1,%2,%R0\;subx %1,0,%0\";
+    }
+  return \"subcc %R1,%R2,%R0\;subx %1,%2,%0\";
+}"
+  [(set_attr "length" "2")])
+
+(define_insn "subsi3"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (minus:SI (match_operand:SI 1 "register_operand" "r")
+                 (match_operand:SI 2 "arith_operand" "rI")))]
+  ""
+  "sub %1,%2,%0")
+
+(define_insn ""
+  [(set (reg:CC_NOOV 0)
+       (compare:CC_NOOV (minus:SI (match_operand:SI 0 "register_operand" "r")
+                                  (match_operand:SI 1 "arith_operand" "rI"))
+                        (const_int 0)))]
+  ""
+  "subcc %0,%1,%%g0"
+  [(set_attr "type" "compare")])
+
+(define_insn ""
+  [(set (reg:CC_NOOV 0)
+       (compare:CC_NOOV (minus:SI (match_operand:SI 1 "register_operand" "r")
+                                  (match_operand:SI 2 "arith_operand" "rI"))
+                        (const_int 0)))
+   (set (match_operand:SI 0 "register_operand" "=r")
+       (minus:SI (match_dup 1) (match_dup 2)))]
+  ""
+  "subcc %1,%2,%0")
+
+;;- and instructions
+;; We define DImode `and` so with DImode `not` we can get
+;; DImode `andn`.  Other combinations are possible.
+
+(define_expand "anddi3"
+  [(set (match_operand:DI 0 "register_operand" "")
+       (and:DI (match_operand:DI 1 "arith_double_operand" "")
+               (match_operand:DI 2 "arith_double_operand" "")))]
+  ""
+  "")
+
+(define_insn ""
+  [(set (match_operand:DI 0 "register_operand" "=r")
+       (and:DI (match_operand:DI 1 "arith_double_operand" "%r")
+               (match_operand:DI 2 "arith_double_operand" "rHI")))]
+  ""
+  "*
+{
+  rtx op2 = operands[2];
+
+  /* If constant is postive, upper bits zeroed, otherwise unchanged
+   * give the assembler a chance to pick the move instruction. */
+  if (GET_CODE (op2) == CONST_INT)
+    {
+      int sign = INTVAL (op2);
+      if (sign < 0)
+       return \"mov %1,%0\;and %R1,%2,%R0\";
+      return \"mov 0,%0\;and %R1,%2,%R0\";
+    }
+  else if (GET_CODE (op2) == CONST_DOUBLE)
+    {
+      int sign = CONST_DOUBLE_HIGH (op2);
+      operands[2] = gen_rtx (CONST_INT, VOIDmode,
+                            CONST_DOUBLE_LOW (operands[1]));
+      if (sign < 0)
+       return \"mov %1,%0\;and %R1,%2,%R0\";
+      return \"mov 0,%0\;and %R1,%2,%R0\";
+    }
+  return \"and %1,%2,%0\;and %R1,%R2,%R0\";
+}"
+  [(set_attr "length" "2")])
+
+(define_insn "andsi3"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (and:SI (match_operand:SI 1 "arith_operand" "%r")
+               (match_operand:SI 2 "arith_operand" "rI")))]
+  ""
+  "and %1,%2,%0")
+
+(define_insn ""
+  [(set (match_operand:DI 0 "register_operand" "=r")
+       (and:DI (not:DI (match_operand:DI 1 "register_operand" "r"))
+               (match_operand:DI 2 "register_operand" "r")))]
+  ""
+  "andn %2,%1,%0\;andn %R2,%R1,%R0"
+  [(set_attr "length" "2")])
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (and:SI (not:SI (match_operand:SI 1 "register_operand" "r"))
+               (match_operand:SI 2 "register_operand" "r")))]
+  ""
+  "andn %2,%1,%0")
+
+(define_expand "iordi3"
+  [(set (match_operand:DI 0 "register_operand" "")
+       (ior:DI (match_operand:DI 1 "arith_double_operand" "")
+               (match_operand:DI 2 "arith_double_operand" "")))]
+  ""
+  "")
+
+(define_insn ""
+  [(set (match_operand:DI 0 "register_operand" "=r")
+       (ior:DI (match_operand:DI 1 "arith_double_operand" "%r")
+               (match_operand:DI 2 "arith_double_operand" "rHI")))]
+  ""
+  "*
+{
+  rtx op2 = operands[2];
+
+  /* If constant is postive, upper bits zeroed, otherwise unchanged
+   * give the assembler a chance to pick the move instruction. */
+  if (GET_CODE (op2) == CONST_INT)
+    {
+      int sign = INTVAL (op2);
+      if (sign < 0)
+       return \"mov -1,%0\;or %R1,%2,%R0\";
+      return \"mov %1,%0\;or %R1,%2,%R0\";
+    }
+  else if (GET_CODE (op2) == CONST_DOUBLE)
+    {
+      int sign = CONST_DOUBLE_HIGH (op2);
+      operands[2] = gen_rtx (CONST_INT, VOIDmode,
+                            CONST_DOUBLE_LOW (operands[1]));
+      if (sign < 0)
+       return \"mov -1,%0\;or %R1,%2,%R0\";
+      return \"mov %1,%0\;or %R1,%2,%R0\";
+    }
+  return \"or %1,%2,%0\;or %R1,%R2,%R0\";
+}"
+  [(set_attr "length" "2")])
+
+(define_insn "iorsi3"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (ior:SI (match_operand:SI 1 "arith_operand" "%r")
+               (match_operand:SI 2 "arith_operand" "rI")))]
+  ""
+  "or %1,%2,%0")
+
+(define_insn ""
+  [(set (match_operand:DI 0 "register_operand" "=r")
+       (ior:DI (not:DI (match_operand:DI 1 "register_operand" "r"))
+               (match_operand:DI 2 "register_operand" "r")))]
+  ""
+  "orn %2,%1,%0\;orn %R2,%R1,%R0"
+  [(set_attr "length" "2")])
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (ior:SI (not:SI (match_operand:SI 1 "register_operand" "r"))
+               (match_operand:SI 2 "register_operand" "r")))]
+  ""
+  "orn %2,%1,%0")
+
+(define_expand "xordi3"
+  [(set (match_operand:DI 0 "register_operand" "")
+       (xor:DI (match_operand:DI 1 "arith_double_operand" "")
+               (match_operand:DI 2 "arith_double_operand" "")))]
+  ""
+  "")
+
+(define_insn ""
+  [(set (match_operand:DI 0 "register_operand" "=r")
+       (xor:DI (match_operand:DI 1 "arith_double_operand" "%r")
+               (match_operand:DI 2 "arith_double_operand" "rHI")))]
+  ""
+  "*
+{
+  rtx op2 = operands[2];
+
+  /* If constant is postive, upper bits zeroed, otherwise unchanged
+   * give the assembler a chance to pick the move instruction. */
+  if (GET_CODE (op2) == CONST_INT)
+    {
+      int sign = INTVAL (op2);
+      if (sign < 0)
+       return \"xor %1,-1,%0\;xor %R1,%2,%R0\";
+      return \"mov %1,%0\;xor %R1,%2,%R0\";
+    }
+  else if (GET_CODE (op2) == CONST_DOUBLE)
+    {
+      int sign = CONST_DOUBLE_HIGH (op2);
+      operands[2] = gen_rtx (CONST_INT, VOIDmode,
+                            CONST_DOUBLE_LOW (operands[1]));
+      if (sign < 0)
+       return \"xor %1,-1,%0\;xor %R1,%2,%R0\";
+      return \"mov %1,%0\;xor %R1,%2,%R0\";
+    }
+  return \"xor %1,%2,%0\;xor %R1,%R2,%R0\";
+}"
+  [(set_attr "length" "2")])
+
+(define_insn "xorsi3"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (xor:SI (match_operand:SI 1 "arith_operand" "%rJ")
+               (match_operand:SI 2 "arith_operand" "rI")))]
+  ""
+  "xor %r1,%2,%0")
+
+;; xnor patterns.  Note that (a ^ ~b) == (~a ^ b) == ~(a ^ b).
+;; Combine now canonicalizes to the rightmost expression.
+(define_insn ""
+  [(set (match_operand:DI 0 "register_operand" "=r")
+       (not:DI (xor:DI (match_operand:DI 1 "register_operand" "r")
+                       (match_operand:DI 2 "register_operand" "r"))))]
+  ""
+  "xnor %1,%2,%0\;xnor %R1,%R2,%R0"
+  [(set_attr "length" "2")])
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (not:SI (xor:SI (match_operand:SI 1 "reg_or_0_operand" "rJ")
+                       (match_operand:SI 2 "arith_operand" "rI"))))]
+  ""
+  "xnor %r1,%2,%0")
+
+;; These correspond to the above in the case where we also (or only)
+;; want to set the condition code.  
+
+(define_insn ""
+  [(set (reg:CC 0)
+       (compare:CC
+        (match_operator:SI 2 "cc_arithop"
+                           [(match_operand:SI 0 "arith_operand" "%r")
+                            (match_operand:SI 1 "arith_operand" "rI")])
+        (const_int 0)))]
+  ""
+  "%A2cc %0,%1,%%g0"
+  [(set_attr "type" "compare")])
+
+(define_insn ""
+  [(set (reg:CC 0)
+       (compare:CC
+        (match_operator:SI 3 "cc_arithop"
+                           [(match_operand:SI 1 "arith_operand" "%r")
+                            (match_operand:SI 2 "arith_operand" "rI")])
+        (const_int 0)))
+   (set (match_operand:SI 0 "register_operand" "=r")
+       (match_dup 3))]
+  ""
+  "%A3cc %1,%2,%0")
+
+(define_insn ""
+  [(set (reg:CC 0)
+       (compare:CC
+        (not:SI (xor:SI (match_operand:SI 0 "reg_or_0_operand" "%rJ")
+                        (match_operand:SI 1 "arith_operand" "rI")))
+        (const_int 0)))]
+  ""
+  "xnorcc %r0,%1,%%g0"
+  [(set_attr "type" "compare")])
+
+(define_insn ""
+  [(set (reg:CC 0)
+       (compare:CC
+        (not:SI (xor:SI (match_operand:SI 1 "reg_or_0_operand" "%rJ")
+                        (match_operand:SI 2 "arith_operand" "rI")))
+        (const_int 0)))
+   (set (match_operand:SI 0 "register_operand" "=r")
+       (not:SI (xor:SI (match_dup 1) (match_dup 2))))]
+  ""
+  "xnorcc %r1,%2,%0")
+
+(define_insn ""
+  [(set (reg:CC 0)
+       (compare:CC
+        (match_operator:SI 2 "cc_arithopn"
+                           [(not:SI (match_operand:SI 0 "arith_operand" "rI"))
+                            (match_operand:SI 1 "reg_or_0_operand" "rJ")])
+        (const_int 0)))]
+  ""
+  "%B2cc %r1,%0,%%g0"
+  [(set_attr "type" "compare")])
+
+(define_insn ""
+  [(set (reg:CC 0)
+       (compare:CC
+        (match_operator:SI 3 "cc_arithopn"
+                           [(not:SI (match_operand:SI 1 "arith_operand" "rI"))
+                            (match_operand:SI 2 "reg_or_0_operand" "rJ")])
+        (const_int 0)))
+   (set (match_operand:SI 0 "register_operand" "=r")
+       (match_dup 3))]
+  ""
+  "%B3cc %r2,%1,%0")
+
+;; We cannot use the "neg" pseudo insn because the Sun assembler
+;; does not know how to make it work for constants.
+
+(define_insn "negdi2"
+  [(set (match_operand:DI 0 "register_operand" "=r")
+       (neg:DI (match_operand:DI 1 "register_operand" "r")))
+   (clobber (reg:SI 0))]
+  ""
+  "subcc %%g0,%R1,%R0\;subx %%g0,%1,%0"
+  [(set_attr "type" "unary")
+   (set_attr "length" "2")])
+
+(define_insn "negsi2"
+  [(set (match_operand:SI 0 "general_operand" "=r")
+       (neg:SI (match_operand:SI 1 "arith_operand" "rI")))]
+  ""
+  "sub %%g0,%1,%0"
+  [(set_attr "type" "unary")])
+
+(define_insn ""
+  [(set (reg:CC_NOOV 0)
+       (compare:CC_NOOV (neg:SI (match_operand:SI 0 "arith_operand" "rI"))
+                        (const_int 0)))]
+  ""
+  "subcc %%g0,%0,%%g0"
+  [(set_attr "type" "compare")])
+
+(define_insn ""
+  [(set (reg:CC_NOOV 0)
+       (compare:CC_NOOV (neg:SI (match_operand:SI 1 "arith_operand" "rI"))
+                        (const_int 0)))
+   (set (match_operand:SI 0 "register_operand" "=r")
+       (neg:SI (match_dup 1)))]
+  ""
+  "subcc %%g0,%1,%0"
+  [(set_attr "type" "unary")])
+
+;; We cannot use the "not" pseudo insn because the Sun assembler
+;; does not know how to make it work for constants.
+(define_expand "one_cmpldi2"
+  [(set (match_operand:DI 0 "register_operand" "=r")
+       (not:DI (match_operand:DI 1 "arith_double_operand" "rHI")))]
+  ""
+  "")
+
+(define_insn ""
+  [(set (match_operand:DI 0 "register_operand" "=r")
+       (not:DI (match_operand:DI 1 "arith_double_operand" "rHI")))]
+  ""
+  "*
+{
+  rtx op1 = operands[1];
+
+  if (GET_CODE (op1) == CONST_INT)
+    {
+      int sign = INTVAL (op1);
+      if (sign < 0)
+       return \"xnor %%g0,%1,%R0\;xnor %%g0,-1,%0\";
+      return \"xnor %%g0,%1,%R0\;xnor %%g0,0,%0\";
+    }
+  else if (GET_CODE (op1) == CONST_DOUBLE)
+    {
+      int sign = CONST_DOUBLE_HIGH (op1);
+      operands[1] = gen_rtx (CONST_INT, VOIDmode,
+                            CONST_DOUBLE_LOW (operands[1]));
+      if (sign < 0)
+       return \"xnor %%g0,%1,%R0\;xnor %%g0,-1,%0\";
+      return \"xnor %%g0,%1,%R0\;xnor %%g0,0,%0\";
+    }
+  return \"xnor %%g0,%1,%0\;xnor %%g0,%R1,%R0\";
+}"
+  [(set_attr "type" "unary")
+   (set_attr "length" "2")])
+
+(define_insn "one_cmplsi2"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (not:SI (match_operand:SI 1 "arith_operand" "rI")))]
+  ""
+  "xnor %%g0,%1,%0"
+  [(set_attr "type" "unary")])
+
+(define_insn ""
+  [(set (reg:CC 0)
+       (compare:CC (not:SI (match_operand:SI 0 "arith_operand" "rI"))
+                   (const_int 0)))]
+  ""
+  "xnorcc %%g0,%0,%%g0"
+  [(set_attr "type" "compare")])
+
+(define_insn ""
+  [(set (reg:CC 0)
+       (compare:CC (not:SI (match_operand:SI 1 "arith_operand" "rI"))
+                   (const_int 0)))
+   (set (match_operand:SI 0 "register_operand" "=r")
+       (not:SI (match_dup 1)))]
+  ""
+  "xnorcc %%g0,%1,%0"
+  [(set_attr "type" "unary")])
+\f
+;; Floating point arithmetic instructions.
+
+(define_insn "adddf3"
+  [(set (match_operand:DF 0 "register_operand" "=f")
+       (plus:DF (match_operand:DF 1 "register_operand" "f")
+                (match_operand:DF 2 "register_operand" "f")))]
+  ""
+  "faddd %1,%2,%0"
+  [(set_attr "type" "fp")])
+
+(define_insn "addsf3"
+  [(set (match_operand:SF 0 "register_operand" "=f")
+       (plus:SF (match_operand:SF 1 "register_operand" "f")
+                (match_operand:SF 2 "register_operand" "f")))]
+  ""
+  "fadds %1,%2,%0"
+  [(set_attr "type" "fp")])
+
+(define_insn "subdf3"
+  [(set (match_operand:DF 0 "register_operand" "=f")
+       (minus:DF (match_operand:DF 1 "register_operand" "f")
+                 (match_operand:DF 2 "register_operand" "f")))]
+  ""
+  "fsubd %1,%2,%0"
+  [(set_attr "type" "fp")])
+
+(define_insn "subsf3"
+  [(set (match_operand:SF 0 "register_operand" "=f")
+       (minus:SF (match_operand:SF 1 "register_operand" "f")
+                 (match_operand:SF 2 "register_operand" "f")))]
+  ""
+  "fsubs %1,%2,%0"
+  [(set_attr "type" "fp")])
+
+(define_insn "muldf3"
+  [(set (match_operand:DF 0 "register_operand" "=f")
+       (mult:DF (match_operand:DF 1 "register_operand" "f")
+                (match_operand:DF 2 "register_operand" "f")))]
+  ""
+  "fmuld %1,%2,%0"
+  [(set_attr "type" "fpmul")])
+
+(define_insn "mulsf3"
+  [(set (match_operand:SF 0 "register_operand" "=f")
+       (mult:SF (match_operand:SF 1 "register_operand" "f")
+                (match_operand:SF 2 "register_operand" "f")))]
+  ""
+  "fmuls %1,%2,%0"
+  [(set_attr "type" "fpmul")])
+
+(define_insn "divdf3"
+  [(set (match_operand:DF 0 "register_operand" "=f")
+       (div:DF (match_operand:DF 1 "register_operand" "f")
+               (match_operand:DF 2 "register_operand" "f")))]
+  ""
+  "fdivd %1,%2,%0"
+  [(set_attr "type" "fpdiv")])
+
+(define_insn "divsf3"
+  [(set (match_operand:SF 0 "register_operand" "=f")
+       (div:SF (match_operand:SF 1 "register_operand" "f")
+               (match_operand:SF 2 "register_operand" "f")))]
+  ""
+  "fdivs %1,%2,%0"
+  [(set_attr "type" "fpdiv")])
+
+(define_insn "negdf2"
+  [(set (match_operand:DF 0 "register_operand" "=f,f")
+       (neg:DF (match_operand:DF 1 "register_operand" "0,f")))]
+  ""
+  "@
+   fnegs %0,%0
+   fnegs %1,%0\;fmovs %R1,%R0"
+  [(set_attr "type" "fp")
+   (set_attr "length" "1,2")])
+
+
+(define_insn "negsf2"
+  [(set (match_operand:SF 0 "register_operand" "=f")
+       (neg:SF (match_operand:SF 1 "register_operand" "f")))]
+  ""
+  "fnegs %1,%0"
+  [(set_attr "type" "fp")])
+
+(define_insn "absdf2"
+  [(set (match_operand:DF 0 "register_operand" "=f,f")
+       (abs:DF (match_operand:DF 1 "register_operand" "0,f")))]
+  ""
+  "@
+   fabss %0,%0
+   fabss %1,%0\;fmovs %R1,%R0"
+  [(set_attr "type" "fp")
+   (set_attr "length" "1,2")])
+
+(define_insn "abssf2"
+  [(set (match_operand:SF 0 "register_operand" "=f")
+       (abs:SF (match_operand:SF 1 "register_operand" "f")))]
+  ""
+  "fabss %1,%0"
+  [(set_attr "type" "fp")])
+
+(define_insn "sqrtdf2"
+  [(set (match_operand:DF 0 "register_operand" "=f")
+       (sqrt:DF (match_operand:DF 1 "register_operand" "f")))]
+  ""
+  "fsqrtd %1,%0"
+  [(set_attr "type" "fpsqrt")])
+
+(define_insn "sqrtsf2"
+  [(set (match_operand:SF 0 "register_operand" "=f")
+       (sqrt:SF (match_operand:SF 1 "register_operand" "f")))]
+  ""
+  "fsqrts %1,%0"
+  [(set_attr "type" "fpsqrt")])
+\f
+;;- arithmetic shift instructions
+
+;; We can trivially handle shifting the constant 1 by 64 bits.
+;; For other shifts we use the library routine.
+;; ??? Questionable, we can do better than this can't we?
+(define_expand "ashldi3"
+  [(parallel [(set (match_operand:DI 0 "register_operand" "")
+                  (ashift:DI (match_operand:DI 1 "const_double_operand" "")
+                             (match_operand:SI 2 "register_operand" "")))
+             (clobber (reg:SI 0))])]
+  ""
+  "
+{
+  if (GET_CODE (operands[1]) == CONST_DOUBLE
+      && CONST_DOUBLE_HIGH (operands[1]) == 0
+      && CONST_DOUBLE_LOW (operands[1]) == 1)
+    operands[1] = const1_rtx;
+  else if (operands[1] != const1_rtx)
+    FAIL;
+}")
+
+;; ??? Questionable, we can do better than this can't we?
+(define_insn ""
+  [(set (match_operand:DI 0 "register_operand" "=&r")
+       (ashift:DI (const_int 1)
+                  (match_operand:SI 2 "register_operand" "r")))
+   (clobber (reg:SI 0))]
+  ""
+  "subcc %2,32,%%g0\;addx %%g0,0,%R0\;xor %R0,1,%0\;sll %R0,%2,%R0\;sll %0,%2,%0"
+  [(set_attr "type" "multi")
+   (set_attr "length" "5")])
+
+(define_insn "ashlsi3"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (ashift:SI (match_operand:SI 1 "register_operand" "r")
+                  (match_operand:SI 2 "arith_operand" "rI")))]
+  ""
+  "sll %1,%2,%0")
+
+(define_insn "ashrsi3"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (ashiftrt:SI (match_operand:SI 1 "register_operand" "r")
+                    (match_operand:SI 2 "arith_operand" "rI")))]
+  ""
+  "sra %1,%2,%0")
+
+(define_insn "lshrsi3"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (lshiftrt:SI (match_operand:SI 1 "register_operand" "r")
+                    (match_operand:SI 2 "arith_operand" "rI")))]
+  ""
+  "srl %1,%2,%0")
+\f
+;; Unconditional and other jump instructions
+;; Note that for the Sparc, by setting the annul bit on an unconditional
+;; branch, the following insn is never executed.  This saves us a nop,
+;; but requires a debugger which can handle annuled branches.
+(define_insn "jump"
+  [(set (pc) (label_ref (match_operand 0 "" "")))]
+  ""
+  "b%* %l0"
+  [(set_attr "type" "branch")])
+
+(define_expand "tablejump"
+  [(parallel [(set (pc) (match_operand:SI 0 "register_operand" "r"))
+             (use (label_ref (match_operand 1 "" "")))])]
+  ""
+  "
+{
+  /* We need to use the PC value in %o7 that was set up when the address
+     of the label was loaded into a register, so we need different RTL.  */
+  if (flag_pic)
+    {
+      emit_insn (gen_pic_tablejump (operands[0], operands[1]));
+      DONE;
+    }
+}")
+
+(define_insn "pic_tablejump"
+  [(set (pc) (match_operand:SI 0 "register_operand" "r"))
+   (use (label_ref (match_operand 1 "" "")))
+   (use (reg:SI 15))]
+  ""
+  "jmp %%o7+%0%#"
+  [(set_attr "type" "branch")])
+
+(define_insn ""
+  [(set (pc) (match_operand:SI 0 "address_operand" "p"))
+   (use (label_ref (match_operand 1 "" "")))]
+  ""
+  "jmp %a0%#"
+  [(set_attr "type" "branch")])
+
+(define_insn ""
+  [(set (pc) (label_ref (match_operand 0 "" "")))
+   (set (reg:SI 15) (label_ref (match_dup 0)))]
+  ""
+  "call %l0%#"
+  [(set_attr "type" "branch")])
+
+;; This pattern recognizes the "instruction" that appears in 
+;; a function call that wants a structure value, 
+;; to inform the called function if compiled with Sun CC.
+;(define_insn ""
+;  [(match_operand:SI 0 "immediate_operand" "")]
+;  "GET_CODE (operands[0]) == CONST_INT && INTVAL (operands[0]) > 0"
+;  "unimp %0"
+;  [(set_attr "type" "marker")])
+
+;;- jump to subroutine
+(define_expand "call"
+  ;; Note that this expression is not used for generating RTL.
+  ;; All the RTL is generated explicitly below.
+  [(call (match_operand:SI 0 "call_operand" "")
+        (match_operand 3 "" "i"))]
+  ;; operands[2] is next_arg_register
+  ;; operands[3] is struct_value_size_rtx.
+  ""
+  "
+{
+  rtx fn_rtx, nregs_rtx;
+
+  if (GET_CODE (XEXP (operands[0], 0)) == LABEL_REF)
+    {
+      /* This is really a PIC sequence.  We want to represent
+        it as a funny jump so it's delay slots can be filled. 
+
+        ??? But if this really *is* a CALL, will not it clobber the
+        call-clobbered registers?  We lose this if it is a JUMP_INSN.
+        Why cannot we have delay slots filled if it were a CALL?  */
+
+      if (INTVAL (operands[3]) > 0)
+       emit_jump_insn (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (3,
+                                gen_rtx (SET, VOIDmode, pc_rtx,
+                                         XEXP (operands[0], 0)),
+                                operands[3],
+                                gen_rtx (CLOBBER, VOIDmode,
+                                         gen_rtx (REG, SImode, 15)))));
+      else
+       emit_jump_insn (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2,
+                                gen_rtx (SET, VOIDmode, pc_rtx,
+                                         XEXP (operands[0], 0)),
+                                gen_rtx (CLOBBER, VOIDmode,
+                                         gen_rtx (REG, SImode, 15)))));
+      goto finish_call;
+    }
+
+  fn_rtx = operands[0];
+
+  /* Count the number of parameter registers being used by this call.
+     if that argument is NULL, it means we are using them all, which
+     means 6 on the sparc.  */
+#if 0
+  if (operands[2])
+    nregs_rtx = gen_rtx (CONST_INT, VOIDmode, REGNO (operands[2]) - 8);
+  else
+    nregs_rtx = gen_rtx (CONST_INT, VOIDmode, 6);
+#else
+  nregs_rtx = const0_rtx;
+#endif
+
+  if (INTVAL (operands[3]) > 0)
+    emit_call_insn (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (3,
+                            gen_rtx (CALL, VOIDmode, fn_rtx, nregs_rtx),
+                            operands[3],
+                            gen_rtx (CLOBBER, VOIDmode,
+                                              gen_rtx (REG, SImode, 15)))));
+  else
+    emit_call_insn (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2,
+                            gen_rtx (CALL, VOIDmode, fn_rtx, nregs_rtx),
+                            gen_rtx (CLOBBER, VOIDmode,
+                                              gen_rtx (REG, SImode, 15)))));
+
+ finish_call:
+#if 0
+  /* If this call wants a structure value,
+     emit an unimp insn to let the called function know about this.  */
+  if (INTVAL (operands[3]) > 0)
+    {
+      rtx insn = emit_insn (operands[3]);
+      SCHED_GROUP_P (insn) = 1;
+    }
+#endif
+
+  DONE;
+}")
+
+(define_insn ""
+  [(call (mem:SI (match_operand:SI 0 "call_operand_address" "S,r"))
+        (match_operand 1 "" ""))
+   (clobber (reg:SI 15))]
+  ;;- Do not use operand 1 for most machines.
+  ""
+  "*
+{
+  return \"call %a0,%1%#\";
+}"
+  [(set_attr "type" "call")])
+
+;; This is a call that wants a structure value.
+(define_insn ""
+  [(call (mem:SI (match_operand:SI 0 "call_operand_address" "S,r"))
+        (match_operand 1 "" ""))
+   (match_operand 2 "immediate_operand" "")
+   (clobber (reg:SI 15))]
+  ;;- Do not use operand 1 for most machines.
+  "GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) > 0"
+  "*
+{
+  return \"call %a0,%1\;nop\;unimp %2\";
+}"
+  [(set_attr "type" "call_no_delay_slot")])
+
+(define_expand "call_value"
+  [(set (match_operand 0 "register_operand" "=rf")
+       (call (match_operand:SI 1 "" "")
+             (match_operand 4 "" "")))]
+  ;; operand 3 is next_arg_register
+  ""
+  "
+{
+  rtx fn_rtx, nregs_rtx;
+  rtvec vec;
+
+  fn_rtx = operands[1];
+
+#if 0
+  if (operands[3])
+    nregs_rtx = gen_rtx (CONST_INT, VOIDmode, REGNO (operands[3]) - 8);
+  else
+    nregs_rtx = gen_rtx (CONST_INT, VOIDmode, 6);
+#else
+  nregs_rtx = const0_rtx;
+#endif
+
+  vec = gen_rtvec (2,
+                  gen_rtx (SET, VOIDmode, operands[0],
+                           gen_rtx (CALL, VOIDmode, fn_rtx, nregs_rtx)),
+                  gen_rtx (CLOBBER, VOIDmode, gen_rtx (REG, SImode, 15)));
+
+  emit_call_insn (gen_rtx (PARALLEL, VOIDmode, vec));
+
+  DONE;
+}")
+
+(define_insn ""
+  [(set (match_operand 0 "" "=rf")
+       (call (mem:SI (match_operand:SI 1 "call_operand_address" "rS"))
+             (match_operand 2 "" "")))
+   (clobber (reg:SI 15))]
+  ;;- Do not use operand 2 for most machines.
+  ""
+  "*
+{
+  return \"call %a1,%2%#\";
+}"
+  [(set_attr "type" "call")])
+\f
+(define_insn "return"
+  [(return)]
+  "! TARGET_EPILOGUE"
+  "* return output_return (operands);"
+  [(set_attr "type" "multi")])
+
+(define_insn "nop"
+  [(const_int 0)]
+  ""
+  "nop")
+
+(define_insn "indirect_jump"
+  [(set (pc) (match_operand:SI 0 "address_operand" "p"))]
+  ""
+ "jmp %a0%#"
+ [(set_attr "type" "branch")])
+(define_expand "nonlocal_goto"
+  [(match_operand:SI 0 "general_operand" "")
+   (match_operand:SI 1 "general_operand" "")
+   (match_operand:SI 2 "general_operand" "")
+   (match_operand:SI 3 "" "")]
+  ""
+  "
+{
+  rtx temp;
+  /* Trap instruction to flush all the registers window.  */
+  emit_insn (gen_rtx (UNSPEC_VOLATILE, VOIDmode,
+                     gen_rtvec (1, const0_rtx), 0));
+  /* Load the fp value for the containing fn into %fp.
+     This is needed because operands[2] refers to %fp.  */
+  emit_move_insn (virtual_stack_vars_rtx, operands[0]);
+  /* Find the containing function's current nonlocal goto handler,
+     which will do any cleanups and then jump to the label.  */
+  emit_move_insn (gen_rtx (REG, SImode, 8), operands[1]);
+  /* Restore %fp from stack pointer value for containing function.
+     The restore insn that follows will move this to %sp,
+     and reload the appropriate value into %fp.  */
+  emit_move_insn (frame_pointer_rtx, operands[2]);
+  /* Put in the static chain register the nonlocal label address.  */
+  emit_move_insn (static_chain_rtx, operands[3]);
+  /* USE of frame_pointer_rtx added for consistency; not clear if
+     really needed.  */
+  emit_insn (gen_rtx (USE, VOIDmode, frame_pointer_rtx));
+  emit_insn (gen_rtx (USE, VOIDmode, stack_pointer_rtx));
+  emit_insn (gen_rtx (USE, VOIDmode, static_chain_rtx));
+  emit_insn (gen_rtx (USE, VOIDmode, gen_rtx (REG, SImode, 8)));
+  /* Return, restoring reg window and jumping to goto handler.  */
+  emit_insn (gen_rtx (UNSPEC_VOLATILE, VOIDmode,
+                     gen_rtvec (1, const0_rtx), 1));
+  DONE;
+}")
+
+;; Special trap insn to flush register windows.
+(define_insn ""
+  [(unspec_volatile [(const_int 0)] 0)]
+  ""
+  "ta 3")
+
+(define_insn ""
+  [(unspec_volatile [(const_int 0)] 1)]
+  ""
+  "jmp %%o0+0\;restore")
+
+;(define_insn "tail_call" ;; tail call
+;  [(set (pc) (match_operand 0 "memory_operand" "m"))]
+;  "tail_call_valid_p ()"
+;  "* return output_tail_call (operands, insn);"
+;  [(set_attr "type" "branch")])
+\f
+;; Split up troublesome insns for better scheduling.  */
+
+;; The following patterns are straightforward.  They can be applied
+;; either before or after register allocation.
+
+(define_split
+  [(set (match_operator 0 "memop" [(match_operand:SI 1 "symbolic_operand" "")])
+       (match_operand 2 "reg_or_0_operand" ""))
+   (clobber (match_operand:SI 3 "register_operand" ""))]
+  "! flag_pic"
+  [(set (match_dup 3) (high:SI (match_dup 1)))
+   (set (match_op_dup 0 [(lo_sum:SI (match_dup 3) (match_dup 1))])
+       (match_dup 2))]
+  "")
+
+(define_split
+  [(set (match_operator 0 "memop"
+                       [(match_operand:SI 1 "immediate_operand" "")])
+       (match_operand 2 "general_operand" ""))
+   (clobber (match_operand:SI 3 "register_operand" ""))]
+  "flag_pic"
+  [(set (match_op_dup 0 [(match_dup 1)])
+       (match_dup 2))]
+  "
+{
+  operands[1] = legitimize_pic_address (operands[1], GET_MODE (operands[0]),
+                                       operands[3], 0);
+}")
+
+(define_split
+  [(set (match_operand 0 "register_operand" "")
+       (match_operator 1 "memop"
+                       [(match_operand:SI 2 "immediate_operand" "")]))]
+  "flag_pic"
+  [(set (match_dup 0)
+       (match_op_dup 1 [(match_dup 2)]))]
+  "
+{
+  operands[2] = legitimize_pic_address (operands[2], GET_MODE (operands[1]),
+                                       operands[0], 0);
+}")
+
+;; Sign- and Zero-extend operations can have symbolic memory operands.
+
+(define_split
+  [(set (match_operand 0 "register_operand" "")
+       (match_operator 1 "extend_op"
+                       [(match_operator 2 "memop"
+                                        [(match_operand:SI 3 "immediate_operand" "")])]))]
+  "flag_pic"
+  [(set (match_dup 0)
+       (match_op_dup 1 [(match_op_dup 2 [(match_dup 3)])]))]
+  "
+{
+  operands[3] = legitimize_pic_address (operands[3], GET_MODE (operands[2]),
+                                       operands[0], 0);
+}")
+
+(define_split
+  [(set (match_operand:SI 0 "register_operand" "")
+       (match_operand:SI 1 "immediate_operand" ""))]
+  "! flag_pic && (GET_CODE (operands[1]) == SYMBOL_REF
+                 || GET_CODE (operands[1]) == CONST
+                 || GET_CODE (operands[1]) == LABEL_REF)"
+  [(set (match_dup 0) (high:SI (match_dup 1)))
+   (set (match_dup 0)
+       (lo_sum:SI (match_dup 0) (match_dup 1)))]
+  "")
+
+;; LABEL_REFs are not modified by `legitimize_pic_address`
+;; so do not recurse infinitely in the PIC case.
+(define_split
+  [(set (match_operand:SI 0 "register_operand" "")
+       (match_operand:SI 1 "immediate_operand" ""))]
+  "flag_pic && (GET_CODE (operands[1]) == SYMBOL_REF
+               || GET_CODE (operands[1]) == CONST)"
+  [(set (match_dup 0) (match_dup 1))]
+  "
+{
+  operands[1] = legitimize_pic_address (operands[1], Pmode, operands[0], 0);
+}")
+\f
+;; These split sne/seq insns.  The forms of the resulting insns are 
+;; somewhat bogus, but they avoid extra patterns and show data dependency.
+;; Nothing will look at these in detail after splitting has occurred.
+
+(define_split
+  [(set (match_operand:SI 0 "register_operand" "")
+       (ne:SI (match_operand:SI 1 "register_operand" "") (const_int 0)))
+   (clobber (reg:CC 0))]
+  ""
+  [(set (reg:CC_NOOV 0) (compare:CC_NOOV (neg:SI (match_dup 1))
+                                        (const_int 0)))
+   (set (match_dup 0) (ltu:SI (reg:CC 0) (const_int 0)))]
+  "")
+
+(define_split
+  [(set (match_operand:SI 0 "register_operand" "")
+       (neg:SI (ne:SI (match_operand:SI 1 "register_operand" "")
+                      (const_int 0))))
+   (clobber (reg:CC 0))]
+  ""
+  [(set (reg:CC_NOOV 0) (compare:CC_NOOV (neg:SI (match_dup 1))
+                                        (const_int 0)))
+   (set (match_dup 0) (neg:SI (ltu:SI (reg:CC 0) (const_int 0))))]
+  "")
+
+(define_split
+  [(set (match_operand:SI 0 "register_operand" "")
+       (eq:SI (match_operand:SI 1 "register_operand" "") (const_int 0)))
+   (clobber (reg:CC 0))]
+  ""
+  [(set (reg:CC_NOOV 0) (compare:CC_NOOV (neg:SI (match_dup 1))
+                                        (const_int 0)))
+   (set (match_dup 0) (geu:SI (reg:CC 0) (const_int 0)))]
+  "")
+
+(define_split
+  [(set (match_operand:SI 0 "register_operand" "")
+       (neg:SI (eq:SI (match_operand:SI 1 "register_operand" "")
+                      (const_int 0))))
+   (clobber (reg:CC 0))]
+  ""
+  [(set (reg:CC_NOOV 0) (compare:CC_NOOV (neg:SI (match_dup 1))
+                                        (const_int 0)))
+   (set (match_dup 0) (neg:SI (geu:SI (reg:CC 0) (const_int 0))))]
+  "")
+
+(define_split
+  [(set (match_operand:SI 0 "register_operand" "")
+       (plus:SI (ne:SI (match_operand:SI 1 "register_operand" "")
+                       (const_int 0))
+                (match_operand:SI 2 "register_operand" "")))
+   (clobber (reg:CC 0))]
+  ""
+  [(set (reg:CC_NOOV 0) (compare:CC_NOOV (neg:SI (match_dup 1))
+                                        (const_int 0)))
+   (set (match_dup 0) (plus:SI (ltu:SI (reg:CC 0) (const_int 0))
+                              (match_dup 2)))]
+  "")
+
+(define_split
+  [(set (match_operand:SI 0 "register_operand" "")
+       (minus:SI (match_operand:SI 2 "register_operand" "")
+                 (ne:SI (match_operand:SI 1 "register_operand" "")
+                        (const_int 0))))
+   (clobber (reg:CC 0))]
+  ""
+  [(set (reg:CC_NOOV 0) (compare:CC_NOOV (neg:SI (match_dup 1))
+                                        (const_int 0)))
+   (set (match_dup 0) (minus:SI (match_dup 2)
+                               (ltu:SI (reg:CC 0) (const_int 0))))]
+  "")
+
+(define_split
+  [(set (match_operand:SI 0 "register_operand" "")
+       (plus:SI (eq:SI (match_operand:SI 1 "register_operand" "")
+                       (const_int 0))
+                (match_operand:SI 2 "register_operand" "")))
+   (clobber (reg:CC 0))]
+  ""
+  [(set (reg:CC_NOOV 0) (compare:CC_NOOV (neg:SI (match_dup 1))
+                                        (const_int 0)))
+   (set (match_dup 0) (plus:SI (geu:SI (reg:CC 0) (const_int 0))
+                              (match_dup 2)))]
+  "")
+
+(define_split
+  [(set (match_operand:SI 0 "register_operand" "")
+       (minus:SI (match_operand:SI 2 "register_operand" "")
+                 (eq:SI (match_operand:SI 1 "register_operand" "")
+                        (const_int 0))))
+   (clobber (reg:CC 0))]
+  ""
+  [(set (reg:CC_NOOV 0) (compare:CC_NOOV (neg:SI (match_dup 1))
+                                        (const_int 0)))
+   (set (match_dup 0) (minus:SI (match_dup 2)
+                               (geu:SI (reg:CC 0) (const_int 0))))]
+  "")
+\f
+;; Peepholes go at the end.
+
+;; Optimize the case of following a reg-reg move with a test
+;; of reg just moved.
+
+(define_peephole
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (match_operand:SI 1 "register_operand" "r"))
+   (set (reg:CC 0)
+       (compare:CC (match_operand:SI 2 "register_operand" "r")
+                   (const_int 0)))]
+  "rtx_equal_p (operands[2], operands[0])
+   || rtx_equal_p (operands[2], operands[1])"
+  "orcc %1,%%g0,%0"
+  [(set_attr "type" "move")])
+
+;; Do {sign,zero}-extended compares somewhat more efficiently.
+;; ??? Is this now the Right Way to do this?  Or will SCRATCH
+;;     eventually have some impact here?
+
+(define_peephole
+  [(set (match_operand:HI 0 "register_operand" "")
+       (match_operand:HI 1 "memory_operand" ""))
+   (set (match_operand:SI 2 "register_operand" "")
+       (sign_extend:SI (match_dup 0)))
+   (set (reg:CC 0)
+       (compare:CC (match_dup 2)
+                   (const_int 0)))]
+  ""
+  "ldsh %1,%0\;orcc %0,%%g0,%2")
+
+(define_peephole
+  [(set (match_operand:QI 0 "register_operand" "")
+       (match_operand:QI 1 "memory_operand" ""))
+   (set (match_operand:SI 2 "register_operand" "")
+       (sign_extend:SI (match_dup 0)))
+   (set (reg:CC 0)
+       (compare:CC (match_dup 2)
+                   (const_int 0)))]
+  ""
+  "ldsb %1,%0\;orcc %0,%%g0,%2")
+
+(define_peephole
+  [(set (match_operand:HI 0 "register_operand" "")
+       (match_operand:HI 1 "memory_operand" ""))
+   (set (match_operand:SI 2 "register_operand" "")
+       (sign_extend:SI (match_dup 0)))]
+  "dead_or_set_p (insn, operands[0])"
+  "*
+{
+  warning (\"bad peephole\");
+  if (! MEM_VOLATILE_P (operands[1]))
+    abort ();
+  return \"ldsh %1,%2\";
+}")
+
+(define_peephole
+  [(set (match_operand:QI 0 "register_operand" "")
+       (match_operand:QI 1 "memory_operand" ""))
+   (set (match_operand:SI 2 "register_operand" "")
+       (sign_extend:SI (match_dup 0)))]
+  "dead_or_set_p (insn, operands[0])"
+  "*
+{
+  warning (\"bad peephole\");
+  if (! MEM_VOLATILE_P (operands[1]))
+    abort ();
+  return \"ldsb %1,%2\";
+}")
+
+;; Floating-point move peepholes
+
+(define_peephole
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (lo_sum:SI (match_dup 0)
+                  (match_operand:SI 1 "immediate_operand" "i")))
+   (set (match_operand:DF 2 "register_operand" "=fr")
+       (mem:DF (match_dup 0)))]
+  "RTX_UNCHANGING_P (operands[1]) && reg_unused_after (operands[0], insn)"
+  "*
+{
+  /* Go by way of output_move_double in case the register in operand 2
+     is not properly aligned for ldd.  */
+  operands[1] = gen_rtx (MEM, DFmode,
+                        gen_rtx (LO_SUM, SImode, operands[0], operands[1]));
+  operands[0] = operands[2];
+  return output_move_double (operands);
+}")
+
+(define_peephole
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (lo_sum:SI (match_dup 0)
+                  (match_operand:SI 1 "immediate_operand" "i")))
+   (set (match_operand:SF 2 "register_operand" "=fr")
+       (mem:SF (match_dup 0)))]
+  "RTX_UNCHANGING_P (operands[1]) && reg_unused_after (operands[0], insn)"
+  "ld [%0+%%lo(%a1)],%2")
+
+;; Return peepholes.  First the "normal" ones
+
+(define_insn ""
+  [(set (match_operand:SI 0 "restore_operand" "")
+       (match_operand:SI 1 "arith_operand" "rI"))
+   (return)]
+  "! TARGET_EPILOGUE"
+  "*
+{
+  if (current_function_returns_struct)
+    return \"jmp %%i7+12\;restore %%g0,%1,%Y0\";
+  else
+    return \"ret\;restore %%g0,%1,%Y0\";
+}"
+  [(set_attr "type" "multi")])
+
+(define_insn ""
+  [(set (match_operand:SI 0 "restore_operand" "")
+       (plus:SI (match_operand:SI 1 "arith_operand" "%r")
+                (match_operand:SI 2 "arith_operand" "rI")))
+   (return)]
+  "! TARGET_EPILOGUE"
+  "*
+{
+  if (current_function_returns_struct)
+    return \"jmp %%i7+12\;restore %r1,%2,%Y0\";
+  else
+    return \"ret\;restore %r1,%2,%Y0\";
+}"
+  [(set_attr "type" "multi")])
+
+;; Turned off because it should never match (subtracting a constant
+;; is turned into addition) and because it would do the wrong thing
+;; when operand 2 is -4096 (--4096 == 4096 is not a valid immediate).
+;;(define_insn ""
+;;  [(set (match_operand:SI 0 "restore_operand" "")
+;;     (minus:SI (match_operand:SI 1 "register_operand" "r")
+;;               (match_operand:SI 2 "small_int" "I")))
+;;   (return)]
+;;  "! TARGET_EPILOGUE"
+;;  "ret\;restore %1,-(%2),%Y0"
+;;  [(set_attr "type" "multi")])
+
+;; The following pattern is only generated by delayed-branch scheduling,
+;; when the insn winds up in the epilogue.
+(define_insn ""
+  [(set (reg:SF 32)
+       (match_operand:SF 0 "register_operand" "f"))
+   (return)]
+  "! TARGET_EPILOGUE"
+  "ret\;fmovs %0,%%f0")
+
+;; Now peepholes to go a call followed by a jump.
+
+(define_peephole
+  [(parallel [(set (match_operand 0 "" "")
+                  (call (mem:SI (match_operand:SI 1 "call_operand_address" "S,r"))
+                        (match_operand 2 "" "")))
+             (clobber (reg:SI 15))])
+   (set (pc) (label_ref (match_operand 3 "" "")))]
+  "short_branch (INSN_UID (insn), INSN_UID (operands[3]))"
+  "*
+{
+  return \"call %a1,%2\;add %%o7,(%l3-.-4),%%o7\";
+}")
+
+(define_peephole
+  [(parallel [(call (mem:SI (match_operand:SI 0 "call_operand_address" "S,r"))
+                   (match_operand 1 "" ""))
+             (clobber (reg:SI 15))])
+   (set (pc) (label_ref (match_operand 2 "" "")))]
+  "short_branch (INSN_UID (insn), INSN_UID (operands[2]))"
+  "*
+{
+  return \"call %a0,%1\;add %%o7,(%l2-.-4),%%o7\";
+}")
+
+(define_peephole
+  [(parallel [(set (match_operand:SI 0 "register_operand" "=r")
+                  (minus:SI (match_operand:SI 1 "reg_or_0_operand" "rJ")
+                            (reg:SI 0)))
+             (clobber (reg:CC 0))])
+   (set (reg:CC 0) (compare (match_dup 0) (const_int 0)))]
+  ""
+  "subxcc %r1,0,%0"
+  [(set_attr "type" "compare")])
+
+;;- Local variables:
+;;- mode:emacs-lisp
+;;- comment-start: ";;- "
+;;- eval: (set-syntax-table (copy-sequence (syntax-table)))
+;;- eval: (modify-syntax-entry ?[ "(]")
+;;- eval: (modify-syntax-entry ?] ")[")
+;;- eval: (modify-syntax-entry ?{ "(}")
+;;- eval: (modify-syntax-entry ?} "){")
+;;- End: