;;- Machine description for Intel 80960 chip for GNU C compiler
;;   Copyright (C) 1992, 1995, 1998 Free Software Foundation, Inc.
;;   Contributed by Steven McGeady, Intel Corp.
;;   Additional work by Glenn Colon-Bonet, Jonathan Shapiro, Andy Wilson
;;   Converted to GCC 2.0 by Jim Wilson and Michael Tiemann, Cygnus Support.

;; 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, 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.

;;- See file "rtl.def" for documentation on define_insn, match_*, et. al.

;; There are very few (4) 'f' registers, they can't be loaded/stored from/to
;; memory, and some instructions explicitly require them, so we get better
;; code by discouraging pseudo-registers from being allocated to them.
;; However, we do want to allow all patterns which can store to them to
;; include them in their constraints, so we always use '*f' in a destination
;; constraint except when 'f' is the only alternative.

;; Insn attributes which describe the i960.

;; Modscan is not used, since the compiler never emits any of these insns.
(define_attr "type"
  "move,arith,alu2,mult,div,modscan,load,store,branch,call,address,compare,fpload,fpstore,fpmove,fpcvt,fpcc,fpadd,fpmul,fpdiv,multi,misc"
  (const_string "arith"))

;; 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)]
	(const_int 1)))

(define_asm_attributes
  [(set_attr "length" "1")
   (set_attr "type" "multi")])

;; (define_function_unit {name} {num-units} {n-users} {test}
;;                       {ready-delay} {issue-delay} [{conflict-list}])

;; The integer ALU
(define_function_unit "alu" 2 0 (eq_attr "type" "arith,compare,move,address") 1 0)
(define_function_unit "alu" 2 0 (eq_attr "type" "alu2") 2 0)
(define_function_unit "alu" 2 0 (eq_attr "type" "mult") 5 0)
(define_function_unit "alu" 2 0 (eq_attr "type" "div") 35 0)
(define_function_unit "alu" 2 0 (eq_attr "type" "modscan") 3 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)

;; Floating point operations.
(define_function_unit "fp" 1 2 (eq_attr "type" "fpmove") 5 0)
(define_function_unit "fp" 1 2 (eq_attr "type" "fpcvt") 35 0)
(define_function_unit "fp" 1 2 (eq_attr "type" "fpcc") 10 0)
(define_function_unit "fp" 1 2 (eq_attr "type" "fpadd") 10 0)
(define_function_unit "fp" 1 2 (eq_attr "type" "fpmul") 20 0)
(define_function_unit "fp" 1 2 (eq_attr "type" "fpdiv") 35 0)

;; 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 start with the DEFINE_EXPANDs, 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 because it is expected to be the most common.

(define_expand "cmpsi"
  [(set (reg:CC 36)
	(compare:CC (match_operand:SI 0 "nonimmediate_operand" "")
		    (match_operand:SI 1 "general_operand" "")))]
  ""
  "
{
  i960_compare_op0 = operands[0];
  i960_compare_op1 = operands[1];
  DONE;
}")

(define_expand "cmpdf"
  [(set (reg:CC 36)
	(compare:CC (match_operand:DF 0 "register_operand" "r")
		    (match_operand:DF 1 "nonmemory_operand" "rGH")))]
  "TARGET_NUMERICS"
  "
{
  i960_compare_op0 = operands[0];
  i960_compare_op1 = operands[1];
  DONE;
}")

(define_expand "cmpsf"
  [(set (reg:CC 36)
	(compare:CC (match_operand:SF 0 "register_operand" "r")
		    (match_operand:SF 1 "nonmemory_operand" "rGH")))]
  "TARGET_NUMERICS"
  "
{
  i960_compare_op0 = operands[0];
  i960_compare_op1 = operands[1];
  DONE;
}")

;; Now the DEFINE_INSNs for the compare and scc cases.  First the compares.

(define_insn ""
  [(set (reg:CC 36)
	(compare:CC (match_operand:SI 0 "register_operand" "d")
		    (match_operand:SI 1 "arith_operand" "dI")))]
  ""
  "cmpi	%0,%1"
  [(set_attr "type" "compare")])

(define_insn ""
  [(set (reg:CC_UNS 36)
	(compare:CC_UNS (match_operand:SI 0 "register_operand" "d")
			(match_operand:SI 1 "arith_operand" "dI")))]
  ""
  "cmpo	%0,%1"
  [(set_attr "type" "compare")])

(define_insn ""
  [(set (reg:CC 36)
	(compare:CC (match_operand:DF 0 "register_operand" "r")
		    (match_operand:DF 1 "nonmemory_operand" "rGH")))]
  "TARGET_NUMERICS"
  "cmprl %0,%1"
  [(set_attr "type" "fpcc")])

(define_insn ""
  [(set (reg:CC 36)
	(compare:CC (match_operand:SF 0 "register_operand" "r")
		    (match_operand:SF 1 "nonmemory_operand" "rGH")))]
  "TARGET_NUMERICS"
  "cmpr %0,%1"
  [(set_attr "type" "fpcc")])

;; Instruction definitions for branch-on-bit-set and clear insns.

(define_insn ""
  [(set (pc)
	(if_then_else
	 (ne (sign_extract:SI (match_operand:SI 1 "register_operand" "d")
			      (const_int 1)
			      (match_operand:SI 2 "arith_operand" "dI"))
	     (const_int 0))
	 (label_ref (match_operand 3 "" ""))
	 (pc)))]
  ""
  "bbs	%2,%1,%l3"
  [(set_attr "type" "branch")])

(define_insn ""
  [(set (pc)
	(if_then_else
	 (eq (sign_extract:SI (match_operand:SI 1 "register_operand" "d")
			      (const_int 1)
			      (match_operand:SI 2 "arith_operand" "dI"))
	     (const_int 0))
	 (label_ref (match_operand 3 "" ""))
	 (pc)))]
  ""
  "bbc	%2,%1,%l3"
  [(set_attr "type" "branch")])

(define_insn ""
  [(set (pc)
	(if_then_else
	 (ne (zero_extract:SI (match_operand:SI 1 "register_operand" "d")
			      (const_int 1)
			      (match_operand:SI 2 "arith_operand" "dI"))
	     (const_int 0))
	 (label_ref (match_operand 3 "" ""))
	 (pc)))]
  ""
  "bbs	%2,%1,%l3"
  [(set_attr "type" "branch")])

(define_insn ""
  [(set (pc)
	(if_then_else
	 (eq (zero_extract:SI (match_operand:SI 1 "register_operand" "d")
			      (const_int 1)
			      (match_operand:SI 2 "arith_operand" "dI"))
	     (const_int 0))
	 (label_ref (match_operand 3 "" ""))
	 (pc)))]
  ""
  "bbc	%2,%1,%l3"
  [(set_attr "type" "branch")])

;; ??? These will never match.  The LOG_LINKs necessary to make these match
;; are not created by flow.  These remain as a reminder to make this work
;; some day.

(define_insn ""
  [(set (reg:CC 36)
	(compare (match_operand:SI 0 "arith_operand" "d")
		 (match_operand:SI 1 "arith_operand" "d")))
   (set (match_dup 1) (plus:SI (match_dup 1) (const_int 1)))]
  "0"
  "cmpinci	%0,%1"
  [(set_attr "type" "compare")])

(define_insn ""
  [(set (reg:CC_UNS 36)
	(compare (match_operand:SI 0 "arith_operand" "d")
		 (match_operand:SI 1 "arith_operand" "d")))
   (set (match_dup 1) (plus:SI (match_dup 1) (const_int 1)))]
  "0"
  "cmpinco	%0,%1"
  [(set_attr "type" "compare")])

(define_insn ""
  [(set (reg:CC 36)
	(compare (match_operand:SI 0 "arith_operand" "d")
		 (match_operand:SI 1 "arith_operand" "d")))
   (set (match_dup 1) (minus:SI (match_dup 1) (const_int 1)))]
  "0"
  "cmpdeci	%0,%1"
  [(set_attr "type" "compare")])

(define_insn ""
  [(set (reg:CC_UNS 36)
	(compare (match_operand:SI 0 "arith_operand" "d")
		 (match_operand:SI 1 "arith_operand" "d")))
   (set (match_dup 1) (minus:SI (match_dup 1) (const_int 1)))]
  "0"
  "cmpdeco	%0,%1"
  [(set_attr "type" "compare")])

;; Templates to store result of condition.
;; '1' is stored if condition is true.
;; '0' is stored if condition is false.
;; These should use predicate "general_operand", since
;; gcc seems to be creating mem references which use these
;; templates.

(define_expand "seq"
  [(set (match_operand:SI 0 "general_operand" "=d")
	(eq:SI (match_dup 1) (const_int 0)))]
  ""
  "
{
  operands[1] = gen_compare_reg (EQ, i960_compare_op0, i960_compare_op1);
}")

(define_expand "sne"
  [(set (match_operand:SI 0 "general_operand" "=d")
	(ne:SI (match_dup 1) (const_int 0)))]
  ""
  "
{
  operands[1] = gen_compare_reg (NE, i960_compare_op0, i960_compare_op1);
}")

(define_expand "sgt"
  [(set (match_operand:SI 0 "general_operand" "=d")
	(gt:SI (match_dup 1) (const_int 0)))]
  ""
  "
{
  operands[1] = gen_compare_reg (GT, i960_compare_op0, i960_compare_op1);
}")

(define_expand "sgtu"
  [(set (match_operand:SI 0 "general_operand" "=d")
	(gtu:SI (match_dup 1) (const_int 0)))]
  ""
  "
{
  operands[1] = gen_compare_reg (GTU, i960_compare_op0, i960_compare_op1);
}")

(define_expand "slt"
  [(set (match_operand:SI 0 "general_operand" "=d")
	(lt:SI (match_dup 1) (const_int 0)))]
  ""
  "
{
  operands[1] = gen_compare_reg (LT, i960_compare_op0, i960_compare_op1);
}")

(define_expand "sltu"
  [(set (match_operand:SI 0 "general_operand" "=d")
	(ltu:SI (match_dup 1) (const_int 0)))]
  ""
  "
{
  operands[1] = gen_compare_reg (LTU, i960_compare_op0, i960_compare_op1);
}")

(define_expand "sge"
  [(set (match_operand:SI 0 "general_operand" "=d")
	(ge:SI (match_dup 1) (const_int 0)))]
  ""
  "
{
  operands[1] = gen_compare_reg (GE, i960_compare_op0, i960_compare_op1);
}")

(define_expand "sgeu"
  [(set (match_operand:SI 0 "general_operand" "=d")
	(geu:SI (match_dup 1) (const_int 0)))]
  ""
  "
{
  operands[1] = gen_compare_reg (GEU, i960_compare_op0, i960_compare_op1);
}")

(define_expand "sle"
  [(set (match_operand:SI 0 "general_operand" "=d")
	(le:SI (match_dup 1) (const_int 0)))]
  ""
  "
{
  operands[1] = gen_compare_reg (LE, i960_compare_op0, i960_compare_op1);
}")

(define_expand "sleu"
  [(set (match_operand:SI 0 "general_operand" "=d")
	(leu:SI (match_dup 1) (const_int 0)))]
  ""
  "
{
  operands[1] = gen_compare_reg (LEU, i960_compare_op0, i960_compare_op1);
}")

(define_insn ""
  [(set (match_operand:SI 0 "general_operand" "=d")
	(eq:SI (match_operand:SI 1 "register_operand" "d") (const_int 0)))]
  ""
  "shro	%1,1,%0"
  [(set_attr "type" "alu2")])

(define_insn ""
  [(set (match_operand:SI 0 "general_operand" "=d")
	(match_operator:SI 1 "comparison_operator" [(reg:CC 36) (const_int 0)]))]
  ""
  "test%C1	%0"
  [(set_attr "type" "compare")])

(define_insn ""
  [(set (match_operand:SI 0 "general_operand" "=d")
	(match_operator:SI 1 "comparison_operator" [(reg:CC_UNS 36) (const_int 0)]))]
  ""
  "test%C1	%0"
  [(set_attr "type" "compare")])

;; These control RTL generation for conditional jump insns
;; and match them for register allocation.

(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, i960_compare_op0, i960_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, i960_compare_op0, i960_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, i960_compare_op0, i960_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, i960_compare_op0, i960_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, i960_compare_op0, i960_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, i960_compare_op0, i960_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, i960_compare_op0, i960_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, i960_compare_op0, i960_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, i960_compare_op0, i960_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, i960_compare_op0, i960_compare_op1); }")

;; Now the normal branch insns (forward and reverse).

(define_insn ""
  [(set (pc)
	(if_then_else (match_operator 0 "comparison_operator"
				      [(reg:CC 36) (const_int 0)])
		      (label_ref (match_operand 1 "" ""))
		      (pc)))]
  ""
  "b%C0 %l1"
  [(set_attr "type" "branch")])

(define_insn ""
  [(set (pc)
	(if_then_else (match_operator 0 "comparison_operator"
				      [(reg:CC 36) (const_int 0)])
		      (pc)
		      (label_ref (match_operand 1 "" ""))))]
  ""
  "b%I0 %l1"
  [(set_attr "type" "branch")])

(define_insn ""
  [(set (pc)
	(if_then_else (match_operator 0 "comparison_operator"
				      [(reg:CC_UNS 36) (const_int 0)])
		      (label_ref (match_operand 1 "" ""))
		      (pc)))]
  ""
  "b%C0 %l1"
  [(set_attr "type" "branch")])

(define_insn ""
  [(set (pc)
	(if_then_else (match_operator 0 "comparison_operator"
				      [(reg:CC_UNS 36) (const_int 0)])
		      (pc)
		      (label_ref (match_operand 1 "" ""))))]
  ""
  "b%I0 %l1"
  [(set_attr "type" "branch")])

(define_insn ""
  [(set (pc)
	(if_then_else
	 (match_operator 0 "comparison_operator"
			 [(match_operand:SI 1 "arith_operand" "d")
			  (match_operand:SI 2 "arith_operand" "dI")])
	 (label_ref (match_operand 3 "" ""))
	 (pc)))]
  ""
  "cmp%S0%B0%R0	%2,%1,%l3"
  [(set_attr "type" "branch")])

(define_insn ""
  [(set (pc)
	(if_then_else
	 (match_operator 0 "comparison_operator"
			 [(match_operand:SI 1 "arith_operand" "d")
			  (match_operand:SI 2 "arith_operand" "dI")])
	 (pc)
	 (label_ref (match_operand 3 "" ""))))]
  ""
  "cmp%S0%B0%X0	%2,%1,%l3"
  [(set_attr "type" "branch")])

;; Normal move instructions.
;; This code is based on the sparc machine description.

(define_expand "movsi"
  [(set (match_operand:SI 0 "general_operand" "")
	(match_operand:SI 1 "general_operand" ""))]
  ""
  "
{
  if (emit_move_sequence (operands, SImode))
    DONE;
}")

;; The store case can not be separate, because reload may convert a register
;; to register move insn to a store (or load) insn without rerecognizing
;; the insn.

;; The i960 does not have any store constant to memory instruction.  However,
;; the calling convention is defined so that the arg pointer when it is not
;; overwise being used is zero.  Thus, we can handle store zero to memory
;; by storing an unused arg pointer.  The arg pointer will be unused if
;; current_function_args_size is zero and this is not a stdarg/varargs
;; function.  This value of the former variable is not valid until after
;; all rtl generation is complete, including function inlining (because a
;; function that doesn't need an arg pointer may be inlined into a function
;; that does need an arg pointer), so we must also check that
;; rtx_equal_function_value_matters is zero.

(define_insn ""
  [(set (match_operand:SI 0 "general_operand" "=d,d,d,m")
	(match_operand:SI 1 "general_operand" "dI,i,m,dJ"))]
  "(current_function_args_size == 0
    && current_function_varargs == 0
    && current_function_stdarg == 0
    && rtx_equal_function_value_matters == 0)
   && (register_operand (operands[0], SImode)
       || register_operand (operands[1], SImode)
       || operands[1] == const0_rtx)"
  "*
{
  switch (which_alternative)
    {
    case 0:
      if (i960_last_insn_type == I_TYPE_REG && TARGET_C_SERIES)
	{
	  if (GET_CODE (operands[1]) == REG)
	    return \"lda	(%1),%0\";
	  else
	    return \"lda	%1,%0\";
	}
      return \"mov	%1,%0\";
    case 1:
      return i960_output_ldconst (operands[0], operands[1]);
    case 2:
      return \"ld	%1,%0\";
    case 3:
      if (operands[1] == const0_rtx)
	return \"st	g14,%0\";
      return \"st	%1,%0\";      
    }
}"
  [(set_attr "type" "move,address,load,store")
   (set_attr "length" "*,3,*,*")])

(define_insn ""
  [(set (match_operand:SI 0 "general_operand" "=d,d,d,m")
	(match_operand:SI 1 "general_operand" "dI,i,m,d"))]
  "(current_function_args_size != 0
    || current_function_varargs != 0
    || current_function_stdarg != 0
    || rtx_equal_function_value_matters != 0)
   && (register_operand (operands[0], SImode)
       || register_operand (operands[1], SImode))"
  "*
{
  switch (which_alternative)
    {
    case 0:
      if (i960_last_insn_type == I_TYPE_REG && TARGET_C_SERIES)
	{
	  if (GET_CODE (operands[1]) == REG)
	    return \"lda	(%1),%0\";
	  else
	    return \"lda	%1,%0\";
	}
      return \"mov	%1,%0\";
    case 1:
      return i960_output_ldconst (operands[0], operands[1]);
    case 2:
      return \"ld	%1,%0\";
    case 3:
      return \"st	%1,%0\";      
    }
}"
  [(set_attr "type" "move,address,load,store")
   (set_attr "length" "*,3,*,*")])

(define_expand "movhi"
  [(set (match_operand:HI 0 "general_operand" "")
	(match_operand:HI 1 "general_operand" ""))]
  ""
  "
{
  if (emit_move_sequence (operands, HImode))
    DONE;
}")

;; Special pattern for zero stores to memory for functions which don't use
;; the arg pointer.

;; The store case can not be separate.  See above.
(define_insn ""
  [(set (match_operand:HI 0 "general_operand" "=d,d,d,m")
	(match_operand:HI 1 "general_operand" "dI,i,m,dJ"))]
  "(current_function_args_size == 0
    && current_function_varargs == 0
    && current_function_stdarg == 0
    && rtx_equal_function_value_matters == 0)
   && (register_operand (operands[0], HImode)
       || register_operand (operands[1], HImode)
       || operands[1] == const0_rtx)"
  "*
{
  switch (which_alternative)
    {
    case 0:
      if (i960_last_insn_type == I_TYPE_REG && TARGET_C_SERIES)
	{
	  if (GET_CODE (operands[1]) == REG)
	    return \"lda	(%1),%0\";
	  else
	    return \"lda	%1,%0\";
	}
      return \"mov	%1,%0\";
    case 1:
      return i960_output_ldconst (operands[0], operands[1]);
    case 2:
      return \"ldos	%1,%0\";
    case 3:
      if (operands[1] == const0_rtx)
	return \"stos	g14,%0\";
      return \"stos	%1,%0\";
    }
}"
  [(set_attr "type" "move,misc,load,store")
   (set_attr "length" "*,3,*,*")])

;; The store case can not be separate.  See above.
(define_insn ""
  [(set (match_operand:HI 0 "general_operand" "=d,d,d,m")
	(match_operand:HI 1 "general_operand" "dI,i,m,d"))]
  "(current_function_args_size != 0
    || current_function_varargs != 0
    || current_function_stdarg != 0
    || rtx_equal_function_value_matters != 0)
   && (register_operand (operands[0], HImode)
       || register_operand (operands[1], HImode))"
  "*
{
  switch (which_alternative)
    {
    case 0:
      if (i960_last_insn_type == I_TYPE_REG && TARGET_C_SERIES)
	{
	  if (GET_CODE (operands[1]) == REG)
	    return \"lda	(%1),%0\";
	  else
	    return \"lda	%1,%0\";
	}
      return \"mov	%1,%0\";
    case 1:
      return i960_output_ldconst (operands[0], operands[1]);
    case 2:
      return \"ldos	%1,%0\";
    case 3:
      return \"stos	%1,%0\";
    }
}"
  [(set_attr "type" "move,misc,load,store")
   (set_attr "length" "*,3,*,*")])

(define_expand "movqi"
  [(set (match_operand:QI 0 "general_operand" "")
	(match_operand:QI 1 "general_operand" ""))]
  ""
  "
{
  if (emit_move_sequence (operands, QImode))
    DONE;
}")

;; The store case can not be separate.  See comment above.
(define_insn ""
  [(set (match_operand:QI 0 "general_operand" "=d,d,d,m")
	(match_operand:QI 1 "general_operand" "dI,i,m,dJ"))]
  "(current_function_args_size == 0
    && current_function_varargs == 0
    && current_function_stdarg == 0
    && rtx_equal_function_value_matters == 0)
   && (register_operand (operands[0], QImode)
       || register_operand (operands[1], QImode)
       || operands[1] == const0_rtx)"
  "*
{
  switch (which_alternative)
    {
    case 0:
      if (i960_last_insn_type == I_TYPE_REG && TARGET_C_SERIES)
	{
	  if (GET_CODE (operands[1]) == REG)
	    return \"lda	(%1),%0\";
	  else
	    return \"lda	%1,%0\";
	}
      return \"mov	%1,%0\";
    case 1:
      return i960_output_ldconst (operands[0], operands[1]);
    case 2:
      return \"ldob	%1,%0\";
    case 3:
      if (operands[1] == const0_rtx)
	return \"stob	g14,%0\";
      return \"stob	%1,%0\";
    }
}"
  [(set_attr "type" "move,misc,load,store")
   (set_attr "length" "*,3,*,*")])

;; The store case can not be separate.  See comment above.
(define_insn ""
  [(set (match_operand:QI 0 "general_operand" "=d,d,d,m")
	(match_operand:QI 1 "general_operand" "dI,i,m,d"))]
  "(current_function_args_size != 0
    || current_function_varargs != 0
    || current_function_stdarg != 0
    || rtx_equal_function_value_matters != 0)
   && (register_operand (operands[0], QImode)
       || register_operand (operands[1], QImode))"
  "*
{
  switch (which_alternative)
    {
    case 0:
      if (i960_last_insn_type == I_TYPE_REG && TARGET_C_SERIES)
	{
	  if (GET_CODE (operands[1]) == REG)
	    return \"lda	(%1),%0\";
	  else
	    return \"lda	%1,%0\";
	}
      return \"mov	%1,%0\";
    case 1:
      return i960_output_ldconst (operands[0], operands[1]);
    case 2:
      return \"ldob	%1,%0\";
    case 3:
      return \"stob	%1,%0\";
    }
}"
  [(set_attr "type" "move,misc,load,store")
   (set_attr "length" "*,3,*,*")])

(define_expand "movdi"
  [(set (match_operand:DI 0 "general_operand" "")
	(match_operand:DI 1 "general_operand" ""))]
  ""
  "
{
  if (emit_move_sequence (operands, DImode))
    DONE;
}")

;; The store case can not be separate.  See comment above.
(define_insn ""
  [(set (match_operand:DI 0 "general_operand" "=d,d,d,d,m,o")
	(match_operand:DI 1 "general_operand" "d,I,i,m,d,J"))]
  "(current_function_args_size == 0
    && current_function_varargs == 0
    && current_function_stdarg == 0
    && rtx_equal_function_value_matters == 0)
   && (register_operand (operands[0], DImode)
       || register_operand (operands[1], DImode)
       || operands[1] == const0_rtx)"
  "*
{
  switch (which_alternative)
    {
    case 0:
    case 1:
    case 3:
    case 4:
      return i960_output_move_double (operands[0], operands[1]);
    case 2:
      return i960_output_ldconst (operands[0], operands[1]);
    case 5:
      operands[1] = adj_offsettable_operand (operands[0], 4);
      return \"st	g14,%0\;st	g14,%1\";
    }
}"
  [(set_attr "type" "move,move,load,load,store,store")])

;; The store case can not be separate.  See comment above.
(define_insn ""
  [(set (match_operand:DI 0 "general_operand" "=d,d,d,d,m")
	(match_operand:DI 1 "general_operand" "d,I,i,m,d"))]
  "(current_function_args_size != 0
    || current_function_varargs != 0
    || current_function_stdarg != 0
    || rtx_equal_function_value_matters != 0)
   && (register_operand (operands[0], DImode)
       || register_operand (operands[1], DImode))"
  "*
{
  switch (which_alternative)
    {
    case 0:
    case 1:
    case 3:
    case 4:
      return i960_output_move_double (operands[0], operands[1]);
    case 2:
      return i960_output_ldconst (operands[0], operands[1]);
    }
}"
  [(set_attr "type" "move,move,load,load,store")])

(define_insn "*store_unaligned_di_reg"
  [(set (match_operand:DI 0 "general_operand" "=d,m")
	(match_operand:DI 1 "register_operand" "d,d"))
   (clobber (match_scratch:SI 2 "=X,&d"))]
  ""
  "*
{
  if (which_alternative == 0)
    return i960_output_move_double (operands[0], operands[1]);
    
  operands[3] = gen_rtx (MEM, word_mode, operands[2]);
  operands[4] = adj_offsettable_operand (operands[3], UNITS_PER_WORD);
  return \"lda	%0,%2\;st	%1,%3\;st	%D1,%4\";
}"
  [(set_attr "type" "move,store")])

(define_expand "movti"
  [(set (match_operand:TI 0 "general_operand" "")
	(match_operand:TI 1 "general_operand" ""))]
  ""
  "
{
  if (emit_move_sequence (operands, TImode))
    DONE;
}")

;; The store case can not be separate.  See comment above.
(define_insn ""
  [(set (match_operand:TI 0 "general_operand" "=d,d,d,d,m,o")
	(match_operand:TI 1 "general_operand" "d,I,i,m,d,J"))]
  "(current_function_args_size == 0
    && current_function_varargs == 0
    && current_function_stdarg == 0
    && rtx_equal_function_value_matters == 0)
   && (register_operand (operands[0], TImode)
       || register_operand (operands[1], TImode)
       || operands[1] == const0_rtx)"
  "*
{
  switch (which_alternative)
    {
    case 0:
    case 1:
    case 3:
    case 4:
      return i960_output_move_quad (operands[0], operands[1]);
    case 2:
      return i960_output_ldconst (operands[0], operands[1]);
    case 5:
      operands[1] = adj_offsettable_operand (operands[0], 4);
      operands[2] = adj_offsettable_operand (operands[0], 8);
      operands[3] = adj_offsettable_operand (operands[0], 12);
      return \"st	g14,%0\;st	g14,%1\;st	g14,%2\;st	g14,%3\";
    }
}"
  [(set_attr "type" "move,move,load,load,store,store")])

;; The store case can not be separate.  See comment above.
(define_insn ""
  [(set (match_operand:TI 0 "general_operand" "=d,d,d,d,m")
	(match_operand:TI 1 "general_operand" "d,I,i,m,d"))]
  "(current_function_args_size != 0
    || current_function_varargs != 0
    || current_function_stdarg != 0
    || rtx_equal_function_value_matters != 0)
   && (register_operand (operands[0], TImode)
       || register_operand (operands[1], TImode))"
  "*
{
  switch (which_alternative)
    {
    case 0:
    case 1:
    case 3:
    case 4:
      return i960_output_move_quad (operands[0], operands[1]);
    case 2:
      return i960_output_ldconst (operands[0], operands[1]);
    }
}"
  [(set_attr "type" "move,move,load,load,store")])

(define_insn "*store_unaligned_ti_reg"
  [(set (match_operand:TI 0 "general_operand" "=d,m")
	(match_operand:TI 1 "register_operand" "d,d"))
   (clobber (match_scratch:SI 2 "=X,&d"))]
  ""
  "*
{
  if (which_alternative == 0)
    return i960_output_move_quad (operands[0], operands[1]);

  operands[3] = gen_rtx (MEM, word_mode, operands[2]);
  operands[4] = adj_offsettable_operand (operands[3], UNITS_PER_WORD);
  operands[5] = adj_offsettable_operand (operands[4], UNITS_PER_WORD);
  operands[6] = adj_offsettable_operand (operands[5], UNITS_PER_WORD);
  return \"lda	%0,%2\;st	%1,%3\;st	%D1,%4\;st	%E1,%5\;st	%F1,%6\";
}"
  [(set_attr "type" "move,store")])

(define_expand "store_multiple"
  [(set (match_operand:SI 0 "" "")	;;- dest
	(match_operand:SI 1 "" ""))	;;- src
   (use (match_operand:SI 2 "" ""))]	;;- nregs
  ""
  "
{
  int regno;
  int count;
  rtx from;
  int i;

  if (GET_CODE (operands[0]) != MEM
      || GET_CODE (operands[1]) != REG
      || GET_CODE (operands[2]) != CONST_INT)
    FAIL;

  count = INTVAL (operands[2]);
  if (count > 12)
    FAIL;

  regno = REGNO (operands[1]);
  from = memory_address (SImode, XEXP (operands[0], 0));
  while (count >= 4 && ((regno & 3) == 0))
    {
      emit_insn (gen_rtx (SET, VOIDmode,
			  gen_rtx (MEM, TImode, from),
			  gen_rtx (REG, TImode, regno)));
      count -= 4;
      regno += 4;
      from = memory_address (TImode, plus_constant (from, 16));
    }
  while (count >= 2 && ((regno & 1) == 0))
    {
      emit_insn (gen_rtx (SET, VOIDmode,
			  gen_rtx (MEM, DImode, from),
			  gen_rtx (REG, DImode, regno)));
      count -= 2;
      regno += 2;
      from = memory_address (DImode, plus_constant (from, 8));
    }
  while (count > 0)
    {
      emit_insn (gen_rtx (SET, VOIDmode,
			  gen_rtx (MEM, SImode, from),
			  gen_rtx (REG, SImode, regno)));
      count -= 1;
      regno += 1;
      from = memory_address (SImode, plus_constant (from, 4));
    }
  DONE;
}")

;; Floating point move insns

(define_expand "movdf"
  [(set (match_operand:DF 0 "general_operand" "")
	(match_operand:DF 1 "fpmove_src_operand" ""))]
  ""
  "
{
  if (emit_move_sequence (operands, DFmode))
    DONE;
}")

(define_insn ""
  [(set (match_operand:DF 0 "general_operand" "=r,*f,d,d,m,o")
	(match_operand:DF 1 "fpmove_src_operand" "r,GH,F,m,d,G"))]
  "(current_function_args_size == 0
    && current_function_varargs == 0
    && current_function_stdarg == 0
    && rtx_equal_function_value_matters == 0)
   && (register_operand (operands[0], DFmode)
       || register_operand (operands[1], DFmode)
       || operands[1] == CONST0_RTX (DFmode))"
  "*
{
  switch (which_alternative)
    {
    case 0:
      if (FP_REG_P (operands[0]) || FP_REG_P (operands[1]))
	return \"movrl	%1,%0\";
      else
	return \"movl	%1,%0\";
    case 1:
      return \"movrl	%1,%0\";
    case 2:
      return i960_output_ldconst (operands[0], operands[1]);
    case 3:
      return \"ldl	%1,%0\";
    case 4:
      return \"stl	%1,%0\";
    case 5:
      operands[1] = adj_offsettable_operand (operands[0], 4);
      return \"st	g14,%0\;st	g14,%1\";
    }
}"
  [(set_attr "type" "move,move,load,fpload,fpstore,fpstore")])

(define_insn ""
  [(set (match_operand:DF 0 "general_operand" "=r,*f,d,d,m")
	(match_operand:DF 1 "fpmove_src_operand" "r,GH,F,m,d"))]
  "(current_function_args_size != 0
    || current_function_varargs != 0
    || current_function_stdarg != 0
    || rtx_equal_function_value_matters != 0)
   && (register_operand (operands[0], DFmode)
       || register_operand (operands[1], DFmode))"
  "*
{
  switch (which_alternative)
    {
    case 0:
      if (FP_REG_P (operands[0]) || FP_REG_P (operands[1]))
	return \"movrl	%1,%0\";
      else
	return \"movl	%1,%0\";
    case 1:
      return \"movrl	%1,%0\";
    case 2:
      return i960_output_ldconst (operands[0], operands[1]);
    case 3:
      return \"ldl	%1,%0\";
    case 4:
      return \"stl	%1,%0\";
    }
}"
  [(set_attr "type" "move,move,load,fpload,fpstore")])

(define_expand "movsf"
  [(set (match_operand:SF 0 "general_operand" "")
	(match_operand:SF 1 "fpmove_src_operand" ""))]
  ""
  "
{
  if (emit_move_sequence (operands, SFmode))
    DONE;
}")

(define_insn ""
  [(set (match_operand:SF 0 "general_operand" "=r,*f,d,d,m")
	(match_operand:SF 1 "fpmove_src_operand" "r,GH,F,m,dG"))]
  "(current_function_args_size == 0
    && current_function_varargs == 0
    && current_function_stdarg == 0
    && rtx_equal_function_value_matters == 0)
   && (register_operand (operands[0], SFmode)
       || register_operand (operands[1], SFmode)
       || operands[1] == CONST0_RTX (SFmode))"
  "*
{
  switch (which_alternative)
    {
    case 0:
      if (FP_REG_P (operands[0]) || FP_REG_P (operands[1]))
	return \"movr	%1,%0\";
      else
	return \"mov	%1,%0\";
    case 1:
      return \"movr	%1,%0\";
    case 2:
      return i960_output_ldconst (operands[0], operands[1]);
    case 3:
      return \"ld	%1,%0\";
    case 4:
      if (operands[1] == CONST0_RTX (SFmode))
	return \"st	g14,%0\";
      return \"st	%1,%0\";
    }
}"
  [(set_attr "type" "move,move,load,fpload,fpstore")])

(define_insn ""
  [(set (match_operand:SF 0 "general_operand" "=r,*f,d,d,m")
	(match_operand:SF 1 "fpmove_src_operand" "r,GH,F,m,d"))]
  "(current_function_args_size != 0
    || current_function_varargs != 0
    || current_function_stdarg != 0
    || rtx_equal_function_value_matters != 0)
   && (register_operand (operands[0], SFmode)
       || register_operand (operands[1], SFmode))"
  "*
{
  switch (which_alternative)
    {
    case 0:
      if (FP_REG_P (operands[0]) || FP_REG_P (operands[1]))
	return \"movr	%1,%0\";
      else
	return \"mov	%1,%0\";
    case 1:
      return \"movr	%1,%0\";
    case 2:
      return i960_output_ldconst (operands[0], operands[1]);
    case 3:
      return \"ld	%1,%0\";
    case 4:
      return \"st	%1,%0\";
    }
}"
  [(set_attr "type" "move,move,load,fpload,fpstore")])

;; Mixed-mode moves with sign and zero-extension.

;; Note that the one starting from HImode comes before those for QImode
;; so that a constant operand will match HImode, not QImode.

(define_expand "extendhisi2"
  [(set (match_operand:SI 0 "register_operand" "")
	(sign_extend:SI
	 (match_operand:HI 1 "nonimmediate_operand" "")))]
 ""
 "
{
  if (GET_CODE (operand1) == REG
      || (GET_CODE (operand1) == SUBREG
	  && GET_CODE (XEXP (operand1, 0)) == REG))
    {
      rtx temp = gen_reg_rtx (SImode);
      rtx shift_16 = GEN_INT (16);
      int op1_subreg_word = 0;

      if (GET_CODE (operand1) == SUBREG)
	{
	  op1_subreg_word = SUBREG_WORD (operand1);
	  operand1 = SUBREG_REG (operand1);
	}
      operand1 = gen_rtx (SUBREG, SImode, operand1, op1_subreg_word);

      emit_insn (gen_ashlsi3 (temp, operand1, shift_16));
      emit_insn (gen_ashrsi3 (operand0, temp, shift_16));
      DONE;
    }
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=d")
	(sign_extend:SI (match_operand:HI 1 "memory_operand" "m")))]
  ""
  "ldis	%1,%0"
  [(set_attr "type" "load")])

(define_expand "extendqisi2"
  [(set (match_operand:SI 0 "register_operand" "")
	(sign_extend:SI (match_operand:QI 1 "nonimmediate_operand" "")))]
  ""
  "
{
  if (GET_CODE (operand1) == REG
      || (GET_CODE (operand1) == SUBREG
	  && GET_CODE (XEXP (operand1, 0)) == REG))
    {
      rtx temp = gen_reg_rtx (SImode);
      rtx shift_24 = GEN_INT (24);
      int op1_subreg_word = 0;

      if (GET_CODE (operand1) == SUBREG)
	{
	  op1_subreg_word = SUBREG_WORD (operand1);
	  operand1 = SUBREG_REG (operand1);
	}
      operand1 = gen_rtx (SUBREG, SImode, operand1, op1_subreg_word),

      emit_insn (gen_ashlsi3 (temp, operand1, shift_24));
      emit_insn (gen_ashrsi3 (operand0, temp, shift_24));
      DONE;
    }
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=d")
	(sign_extend:SI (match_operand:QI 1 "memory_operand" "m")))]
  ""
  "ldib	%1,%0"
  [(set_attr "type" "load")])

(define_expand "extendqihi2"
  [(set (match_operand:HI 0 "register_operand" "")
	(sign_extend:HI
	 (match_operand:QI 1 "nonimmediate_operand" "")))]
  ""
  "
{
  if (GET_CODE (operand1) == REG
      || (GET_CODE (operand1) == SUBREG
	  && GET_CODE (XEXP (operand1, 0)) == REG))
    {
      rtx temp = gen_reg_rtx (SImode);
      rtx shift_24 = GEN_INT (24);
      int op0_subreg_word = 0;
      int op1_subreg_word = 0;

      if (GET_CODE (operand1) == SUBREG)
	{
	  op1_subreg_word = SUBREG_WORD (operand1);
	  operand1 = SUBREG_REG (operand1);
	}
      operand1 = gen_rtx (SUBREG, SImode, operand1, op1_subreg_word);

      if (GET_CODE (operand0) == SUBREG)
	{
	  op0_subreg_word = SUBREG_WORD (operand0);
	  operand0 = SUBREG_REG (operand0);
	}
      if (GET_MODE (operand0) != SImode)
	operand0 = gen_rtx (SUBREG, SImode, operand0, op0_subreg_word);

      emit_insn (gen_ashlsi3 (temp, operand1, shift_24));
      emit_insn (gen_ashrsi3 (operand0, temp, shift_24));
      DONE;
    }
}")

(define_insn ""
  [(set (match_operand:HI 0 "register_operand" "=d")
	(sign_extend:HI (match_operand:QI 1 "memory_operand" "m")))]
  ""
  "ldib	%1,%0"
  [(set_attr "type" "load")])

(define_expand "zero_extendhisi2"
  [(set (match_operand:SI 0 "register_operand" "")
	(zero_extend:SI
	 (match_operand:HI 1 "nonimmediate_operand" "")))]
 ""
 "
{
  if (GET_CODE (operand1) == REG
      || (GET_CODE (operand1) == SUBREG
	  && GET_CODE (XEXP (operand1, 0)) == REG))
    {
      rtx temp = gen_reg_rtx (SImode);
      rtx shift_16 = GEN_INT (16);
      int op1_subreg_word = 0;

      if (GET_CODE (operand1) == SUBREG)
	{
	  op1_subreg_word = SUBREG_WORD (operand1);
	  operand1 = SUBREG_REG (operand1);
	}
      operand1 = gen_rtx (SUBREG, SImode, operand1, op1_subreg_word);

      emit_insn (gen_ashlsi3 (temp, operand1, shift_16));
      emit_insn (gen_lshrsi3 (operand0, temp, shift_16));
      DONE;
    }
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=d")
	(zero_extend:SI (match_operand:HI 1 "memory_operand" "m")))]
  ""
  "ldos	%1,%0"
  [(set_attr "type" "load")])

;; Using shifts here generates much better code than doing an `and 255'.
;; This is mainly because the `and' requires loading the constant separately,
;; the constant is likely to get optimized, and then the compiler can't
;; optimize the `and' because it doesn't know that one operand is a constant.

(define_expand "zero_extendqisi2"
  [(set (match_operand:SI 0 "register_operand" "")
	(zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "")))]
  ""
  "
{
  if (GET_CODE (operand1) == REG
      || (GET_CODE (operand1) == SUBREG
	  && GET_CODE (XEXP (operand1, 0)) == REG))
    {
      rtx temp = gen_reg_rtx (SImode);
      rtx shift_24 = GEN_INT (24);
      int op1_subreg_word = 0;

      if (GET_CODE (operand1) == SUBREG)
	{
	  op1_subreg_word = SUBREG_WORD (operand1);
	  operand1 = SUBREG_REG (operand1);
	}
      operand1 = gen_rtx (SUBREG, SImode, operand1, op1_subreg_word);

      emit_insn (gen_ashlsi3 (temp, operand1, shift_24));
      emit_insn (gen_lshrsi3 (operand0, temp, shift_24));
      DONE;
    }
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=d")
	(zero_extend:SI (match_operand:QI 1 "memory_operand" "m")))]
  ""
  "ldob	%1,%0"
  [(set_attr "type" "load")])

(define_expand "zero_extendqihi2"
  [(set (match_operand:HI 0 "register_operand" "")
	(zero_extend:HI
	 (match_operand:QI 1 "nonimmediate_operand" "")))]
  ""
  "
{
  if (GET_CODE (operand1) == REG
      || (GET_CODE (operand1) == SUBREG
	  && GET_CODE (XEXP (operand1, 0)) == REG))
    {
      rtx temp = gen_reg_rtx (SImode);
      rtx shift_24 = GEN_INT (24);
      int op0_subreg_word = 0;
      int op1_subreg_word = 0;

      if (GET_CODE (operand1) == SUBREG)
	{
	  op1_subreg_word = SUBREG_WORD (operand1);
	  operand1 = SUBREG_REG (operand1);
	}
      operand1 = gen_rtx (SUBREG, SImode, operand1, op1_subreg_word);

      if (GET_CODE (operand0) == SUBREG)
	{
	  op0_subreg_word = SUBREG_WORD (operand0);
	  operand0 = SUBREG_REG (operand0);
	}
      if (GET_MODE (operand0) != SImode)
	operand0 = gen_rtx (SUBREG, SImode, operand0, op0_subreg_word);

      emit_insn (gen_ashlsi3 (temp, operand1, shift_24));
      emit_insn (gen_lshrsi3 (operand0, temp, shift_24));
      DONE;
    }
}")

(define_insn ""
  [(set (match_operand:HI 0 "register_operand" "=d")
	(zero_extend:HI (match_operand:QI 1 "memory_operand" "m")))]
  ""
  "ldob	%1,%0"
  [(set_attr "type" "load")])

;; Conversions between float and double.

(define_insn "extendsfdf2"
  [(set (match_operand:DF 0 "register_operand" "=*f,d")
	(float_extend:DF (match_operand:SF 1 "fp_arith_operand" "dGH,fGH")))]
  "TARGET_NUMERICS"
  "@
  movr	%1,%0
  movrl	%1,%0"
  [(set_attr "type" "fpmove")])

(define_insn "truncdfsf2"
  [(set (match_operand:SF 0 "register_operand" "=d")
	(float_truncate:SF
	 (match_operand:DF 1 "fp_arith_operand" "fGH")))]
  "TARGET_NUMERICS"
  "movr	%1,%0"
  [(set_attr "type" "fpmove")])

;; Conversion between fixed point and floating point.

(define_insn "floatsidf2"
  [(set (match_operand:DF 0 "register_operand" "=f")
	(float:DF (match_operand:SI 1 "register_operand" "d")))]
  "TARGET_NUMERICS"
  "cvtir	%1,%0"
  [(set_attr "type" "fpcvt")])

(define_insn "floatsisf2"
  [(set (match_operand:SF 0 "register_operand" "=d*f")
	(float:SF (match_operand:SI 1 "register_operand" "d")))]
  "TARGET_NUMERICS"
  "cvtir	%1,%0"
  [(set_attr "type" "fpcvt")])

;; Convert a float to an actual integer.
;; Truncation is performed as part of the conversion.
;; The i960 requires conversion from DFmode to DImode to make
;; unsigned conversions work properly.

(define_insn "fixuns_truncdfdi2"
  [(set (match_operand:DI 0 "register_operand" "=d")
	(unsigned_fix:DI (fix:DF (match_operand:DF 1 "fp_arith_operand" "fGH"))))]
  "TARGET_NUMERICS"
  "cvtzril	%1,%0"
  [(set_attr "type" "fpcvt")])

(define_insn "fixuns_truncsfdi2"
  [(set (match_operand:DI 0 "register_operand" "=d")
	(unsigned_fix:DI (fix:SF (match_operand:SF 1 "fp_arith_operand" "fGH"))))]
  "TARGET_NUMERICS"
  "cvtzril	%1,%0"
  [(set_attr "type" "fpcvt")])

(define_insn "fix_truncdfsi2"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(fix:SI (fix:DF (match_operand:DF 1 "fp_arith_operand" "fGH"))))]
  "TARGET_NUMERICS"
  "cvtzri	%1,%0"
  [(set_attr "type" "fpcvt")])

(define_expand "fixuns_truncdfsi2"
  [(set (match_operand:SI 0 "register_operand" "")
	(unsigned_fix:SI (fix:DF (match_operand:DF 1 "fp_arith_operand" ""))))]
  "TARGET_NUMERICS"
  "
{
  rtx temp = gen_reg_rtx (DImode);
  emit_insn (gen_rtx (SET, VOIDmode, temp,
		      gen_rtx (UNSIGNED_FIX, DImode,
			       gen_rtx (FIX, DFmode, operands[1]))));
  emit_insn (gen_rtx (SET, VOIDmode, operands[0],
		      gen_rtx (SUBREG, SImode, temp, 0)));
  DONE;
}")

(define_insn "fix_truncsfsi2"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(fix:SI (fix:SF (match_operand:SF 1 "fp_arith_operand" "dfGH"))))]
  "TARGET_NUMERICS"
  "cvtzri	%1,%0"
  [(set_attr "type" "fpcvt")])

(define_expand "fixuns_truncsfsi2"
  [(set (match_operand:SI 0 "register_operand" "")
	(unsigned_fix:SI (fix:SF (match_operand:SF 1 "fp_arith_operand" ""))))]
  "TARGET_NUMERICS"
  "
{
  rtx temp = gen_reg_rtx (DImode);
  emit_insn (gen_rtx (SET, VOIDmode, temp,
		      gen_rtx (UNSIGNED_FIX, DImode,
			       gen_rtx (FIX, SFmode, operands[1]))));
  emit_insn (gen_rtx (SET, VOIDmode, operands[0],
		      gen_rtx (SUBREG, SImode, temp, 0)));
  DONE;
}")

;; Arithmetic instructions.

(define_insn "subsi3"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(minus:SI (match_operand:SI 1 "arith_operand" "dI")
		  (match_operand:SI 2 "arith_operand" "dI")))]
  ""
  "subo	%2,%1,%0")

;; Try to generate an lda instruction when it would be faster than an
;; add instruction.
;; Some assemblers apparently won't accept two addresses added together.

;; ??? The condition should be improved to reject the case of two
;; symbolic constants.

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=d,d,d")
	(plus:SI (match_operand:SI 1 "arith32_operand" "%dn,i,dn")
		 (match_operand:SI 2 "arith32_operand" "dn,dn,i")))]
  "(TARGET_C_SERIES) && (CONSTANT_P (operands[1]) || CONSTANT_P (operands[2]))"
  "*
{
  if (GET_CODE (operands[1]) == CONST_INT)
    {
      rtx tmp = operands[1];
      operands[1] = operands[2];
      operands[2] = tmp;
    }
  if (GET_CODE (operands[2]) == CONST_INT
      && GET_CODE (operands[1]) == REG
      && i960_last_insn_type != I_TYPE_REG)
    {
      if (INTVAL (operands[2]) < 0 && INTVAL (operands[2]) > -32)
	return \"subo	%n2,%1,%0\";
      else if (INTVAL (operands[2]) >= 0 && INTVAL (operands[2]) < 32)
	return \"addo	%1,%2,%0\";
    }
  /* Non-canonical results (op1 == const, op2 != const) have been seen
     in reload output when both operands were symbols before reload, so
     we deal with it here.  This may be a fault of the constraints above.  */
  if (CONSTANT_P (operands[1]))
    {
      if (CONSTANT_P (operands[2]))
	return \"lda	%1+%2,%0\";
      else
	return \"lda	%1(%2),%0\";
    }
  return \"lda	%2(%1),%0\";
}")

(define_insn "addsi3"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(plus:SI (match_operand:SI 1 "signed_arith_operand" "%dI")
		 (match_operand:SI 2 "signed_arith_operand" "dIK")))]
  ""
  "*
{
  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) < 0)
    return \"subo	%n2,%1,%0\";
  if (i960_bypass (insn, operands[1], operands[2], 0))
    return \"addo	%2,%1,%0\";
  return \"addo	%1,%2,%0\";
}")

(define_insn "mulsi3"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(mult:SI (match_operand:SI 1 "arith_operand" "%dI")
		 (match_operand:SI 2 "arith_operand" "dI")))]
  ""
  "*
{
  if (i960_bypass (insn, operands[1], operands[2], 0))
    return \"mulo	%2,%1,%0\";
  return \"mulo	%1,%2,%0\";
}"
  [(set_attr "type" "mult")])

(define_insn "umulsidi3"
  [(set (match_operand:DI 0 "register_operand" "=d")
	(mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "d"))
		 (zero_extend:DI (match_operand:SI 2 "register_operand" "d"))))]
  ""
  "*
{
  if (i960_bypass (insn, operands[1], operands[2], 0))
    return \"emul	%2,%1,%0\";
  return \"emul	%1,%2,%0\";
}"
  [(set_attr "type" "mult")])

(define_insn ""
  [(set (match_operand:DI 0 "register_operand" "=d")
	(mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "%d"))
		 (match_operand:SI 2 "literal" "I")))]
  ""
  "*
{
  if (i960_bypass (insn, operands[1], operands[2], 0))
    return \"emul	%2,%1,%0\";
  return \"emul	%1,%2,%0\";
}"
  [(set_attr "type" "mult")])

;; This goes after the move/add/sub/mul instructions  
;; because those instructions are better when they apply.

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=d")
	(match_operand:SI 1 "address_operand" "p"))]
  ""
  "lda	%a1,%0"
  [(set_attr "type" "load")])

;; This will never be selected because of an "optimization" that GCC does.
;; It always converts divides by a power of 2 into a sequence of instructions
;; that does a right shift, and then corrects the result if it was negative.

;; (define_insn ""
;;   [(set (match_operand:SI 0 "register_operand" "=d")
;;         (div:SI (match_operand:SI 1 "arith_operand" "dI")
;;                 (match_operand:SI 2 "power2_operand" "nI")))]
;;   ""
;;   "*{
;; 	operands[2] = GEN_INT (bitpos (INTVAL (operands[2])));
;; 	return \"shrdi	%2,%1,%0\";
;;   }"

(define_insn "divsi3"
  [(set (match_operand:SI 0 "register_operand" "=d")
        (div:SI (match_operand:SI 1 "arith_operand" "dI")
                (match_operand:SI 2 "arith_operand" "dI")))]
  ""
  "divi	%2,%1,%0"
  [(set_attr "type" "div")])

(define_insn "udivsi3"
  [(set (match_operand:SI 0 "register_operand" "=d")
        (udiv:SI (match_operand:SI 1 "arith_operand" "dI")
		 (match_operand:SI 2 "arith_operand" "dI")))]
  ""
  "divo	%2,%1,%0"
  [(set_attr "type" "div")])

;; We must use `remi' not `modi' here, to ensure that `%' has the effects
;; specified by the ANSI C standard.

(define_insn "modsi3"
  [(set (match_operand:SI 0 "register_operand" "=d")
        (mod:SI (match_operand:SI 1 "arith_operand" "dI")
                (match_operand:SI 2 "arith_operand" "dI")))]
  ""
  "remi	%2,%1,%0"
  [(set_attr "type" "div")])

(define_insn "umodsi3"
  [(set (match_operand:SI 0 "register_operand" "=d")
        (umod:SI (match_operand:SI 1 "arith_operand" "dI")
		 (match_operand:SI 2 "arith_operand" "dI")))]
  ""
  "remo	%2,%1,%0"
  [(set_attr "type" "div")])

;; And instructions (with complement also).

(define_insn "andsi3"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(and:SI (match_operand:SI 1 "register_operand" "%d")
		(match_operand:SI 2 "logic_operand" "dIM")))]
  ""
  "*
{
  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) < 0)
    return \"andnot	%C2,%1,%0\";
  if (i960_bypass (insn, operands[1], operands[2], 0))
    return \"and	%2,%1,%0\";
  return \"and	%1,%2,%0\";
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=d")
	(and:SI (match_operand:SI 1 "arith_operand" "dI")
		(match_operand:SI 2 "cmplpower2_operand" "n")))]
  ""
  "*
{
  operands[2] = GEN_INT (bitpos (~INTVAL (operands[2])));
  return \"clrbit	%2,%1,%0\";
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=d")
	(and:SI (not:SI (match_operand:SI 1 "register_operand" "d"))
		(match_operand:SI 2 "logic_operand" "dIM")))]
  ""
  "*
{
  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) < 0)
    return \"nor	%C2,%1,%0\";
  if (i960_bypass (insn, operands[1], operands[2], 0))
    return \"notand	%2,%1,%0\";
  return \"andnot	%1,%2,%0\";
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=d")
	(ior:SI (not:SI (match_operand:SI 1 "register_operand" "%d"))
		(not:SI (match_operand:SI 2 "register_operand" "d"))))]
  ""
  "*
{
  if (i960_bypass (insn, operands[1], operands[2], 0))
    return \"nand	%2,%1,%0\";
  return \"nand	%1,%2,%0\";
}")

(define_insn "iorsi3"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(ior:SI (match_operand:SI 1 "register_operand" "%d")
		(match_operand:SI 2 "logic_operand" "dIM")))]
  ""
  "*
{
  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) < 0)
    return \"ornot	%C2,%1,%0\";
  if (i960_bypass (insn, operands[1], operands[2], 0))
    return \"or	%2,%1,%0\";
  return \"or	%1,%2,%0\";
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=d")
	(ior:SI (match_operand:SI 1 "register_operand" "d")
		(match_operand:SI 2 "power2_operand" "n")))]
  ""
  "*
{
  operands[2] = GEN_INT (bitpos (INTVAL (operands[2])));
  return \"setbit	%2,%1,%0\";
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=d")
	(ior:SI (not:SI (match_operand:SI 1 "register_operand" "d"))
		(match_operand:SI 2 "logic_operand" "dIM")))]
  ""
  "*
{
  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) < 0)
    return \"nand	%C2,%1,%0\";
  if (i960_bypass (insn, operands[1], operands[2], 0))
    return \"notor	%2,%1,%0\";
  return \"ornot	%1,%2,%0\";
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=d")
	(and:SI (not:SI (match_operand:SI 1 "register_operand" "%d"))
		(not:SI (match_operand:SI 2 "register_operand" "d"))))]
  ""
  "*
{
  if (i960_bypass (insn, operands[1], operands[2], 0))
    return \"nor	%2,%1,%0\";
  return \"nor	%1,%2,%0\";
}")

(define_insn "xorsi3"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(xor:SI (match_operand:SI 1 "register_operand" "%d")
		(match_operand:SI 2 "logic_operand" "dIM")))]
  ""
  "*
{
  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) < 0)
    return \"xnor	%C2,%1,%0\";
  if (i960_bypass (insn, operands[1], operands[2], 0))
    return \"xor	%2,%1,%0\";
  return \"xor	%1,%2,%0\";
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=d")
	(xor:SI (match_operand:SI 1 "arith_operand" "dI")
		(match_operand:SI 2 "power2_operand" "n")))]
  ""
  "*
{
  operands[2] = GEN_INT (bitpos (INTVAL (operands[2])));
  return \"notbit	%2,%1,%0\";
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=d")
	(not:SI (xor:SI (match_operand:SI 1 "register_operand" "%d")
			(match_operand:SI 2 "register_operand" "d"))))]
  ""
  "*
{
  if (i960_bypass (insn, operands[1], operands[2], 0))
    return \"xnor	%2,%1,%0\";
  return \"xnor	%2,%1,%0\";
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=d")
	(ior:SI (ashift:SI (const_int 1)
			   (match_operand:SI 1 "register_operand" "d"))
		(match_operand:SI 2 "arith_operand" "dI")))]
  ""
  "setbit	%1,%2,%0")

;; (not (ashift 1 reg)) canonicalizes to (rotate -2 reg)
(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=d")
	(and:SI (rotate:SI (const_int -2)
			   (match_operand:SI 1 "register_operand" "d"))
		(match_operand:SI 2 "register_operand" "d")))]
  ""
  "clrbit	%1,%2,%0")

;; The above pattern canonicalizes to this when both the input and output
;; are the same pseudo-register.
(define_insn ""
  [(set (zero_extract:SI (match_operand:SI 0 "register_operand" "=d")
			 (const_int 1)
			 (match_operand:SI 1 "register_operand" "d"))
	(const_int 0))]
  ""
  "clrbit	%1,%0,%0")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=d")
	(xor:SI (ashift:SI (const_int 1)
			   (match_operand:SI 1 "register_operand" "d"))
		(match_operand:SI 2 "arith_operand" "dI")))]
  ""
  "notbit	%1,%2,%0")

(define_insn "negsi2"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(neg:SI (match_operand:SI 1 "arith_operand" "dI")))]
  ""
  "subo	%1,0,%0"
  [(set_attr "length" "1")])

(define_insn "one_cmplsi2"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(not:SI (match_operand:SI 1 "arith_operand" "dI")))]
  ""
  "not	%1,%0"
  [(set_attr "length" "1")])

;; Floating point arithmetic instructions.

(define_insn "adddf3"
  [(set (match_operand:DF 0 "register_operand" "=d*f")
	(plus:DF (match_operand:DF 1 "fp_arith_operand" "%rGH")
		 (match_operand:DF 2 "fp_arith_operand" "rGH")))]
  "TARGET_NUMERICS"
  "addrl	%1,%2,%0"
  [(set_attr "type" "fpadd")])

(define_insn "addsf3"
  [(set (match_operand:SF 0 "register_operand" "=d*f")
	(plus:SF (match_operand:SF 1 "fp_arith_operand" "%rGH")
		 (match_operand:SF 2 "fp_arith_operand" "rGH")))]
  "TARGET_NUMERICS"
  "addr	%1,%2,%0"
  [(set_attr "type" "fpadd")])


(define_insn "subdf3"
  [(set (match_operand:DF 0 "register_operand" "=d*f")
	(minus:DF (match_operand:DF 1 "fp_arith_operand" "rGH")
		  (match_operand:DF 2 "fp_arith_operand" "rGH")))]
  "TARGET_NUMERICS"
  "subrl	%2,%1,%0"
  [(set_attr "type" "fpadd")])

(define_insn "subsf3"
  [(set (match_operand:SF 0 "register_operand" "=d*f")
	(minus:SF (match_operand:SF 1 "fp_arith_operand" "rGH")
		  (match_operand:SF 2 "fp_arith_operand" "rGH")))]
  "TARGET_NUMERICS"
  "subr	%2,%1,%0"
  [(set_attr "type" "fpadd")])


(define_insn "muldf3"
  [(set (match_operand:DF 0 "register_operand" "=d*f")
	(mult:DF (match_operand:DF 1 "fp_arith_operand" "%rGH")
		 (match_operand:DF 2 "fp_arith_operand" "rGH")))]
  "TARGET_NUMERICS"
  "mulrl	%1,%2,%0"
  [(set_attr "type" "fpmul")])

(define_insn "mulsf3"
  [(set (match_operand:SF 0 "register_operand" "=d*f")
	(mult:SF (match_operand:SF 1 "fp_arith_operand" "%rGH")
		 (match_operand:SF 2 "fp_arith_operand" "rGH")))]
  "TARGET_NUMERICS"
  "mulr	%1,%2,%0"
  [(set_attr "type" "fpmul")])


(define_insn "divdf3"
  [(set (match_operand:DF 0 "register_operand" "=d*f")
	(div:DF (match_operand:DF 1 "fp_arith_operand" "rGH")
		(match_operand:DF 2 "fp_arith_operand" "rGH")))]
  "TARGET_NUMERICS"
  "divrl	%2,%1,%0"
  [(set_attr "type" "fpdiv")])

(define_insn "divsf3"
  [(set (match_operand:SF 0 "register_operand" "=d*f")
	(div:SF (match_operand:SF 1 "fp_arith_operand" "rGH")
		(match_operand:SF 2 "fp_arith_operand" "rGH")))]
  "TARGET_NUMERICS"
  "divr	%2,%1,%0"
  [(set_attr "type" "fpdiv")])

(define_insn "negdf2"
  [(set (match_operand:DF 0 "register_operand" "=d,d*f")
	(neg:DF (match_operand:DF 1 "register_operand" "d,r")))]
  ""
  "*
{
  if (which_alternative == 0)
    {
      if (REGNO (operands[0]) == REGNO (operands[1]))
	return \"notbit	31,%D1,%D0\";
      return \"mov	%1,%0\;notbit	31,%D1,%D0\";
    }
  return \"subrl	%1,0f0.0,%0\";
}"
  [(set_attr "type" "fpadd")])

(define_insn "negsf2"
  [(set (match_operand:SF 0 "register_operand" "=d,d*f")
	(neg:SF (match_operand:SF 1 "register_operand" "d,r")))]
  ""
  "@
  notbit	31,%1,%0
  subr	%1,0f0.0,%0"
  [(set_attr "type" "fpadd")])

;;; The abs patterns also work even if the target machine doesn't have
;;; floating point, because in that case dstreg and srcreg will always be
;;; less than 32.

(define_insn "absdf2"
  [(set (match_operand:DF 0 "register_operand" "=d*f")
	(abs:DF (match_operand:DF 1 "register_operand" "df")))]
  ""
  "*
{
  int dstreg = REGNO (operands[0]);
  int srcreg = REGNO (operands[1]);

  if (dstreg < 32)
    {
      if (srcreg < 32)
	{
	  if (dstreg != srcreg)
	    output_asm_insn (\"mov	%1,%0\", operands);
	  return \"clrbit	31,%D1,%D0\";
	}
      /* Src is an fp reg.  */
      return \"movrl	%1,%0\;clrbit	31,%D1,%D0\";
    }
  if (srcreg >= 32)
    return \"cpysre	%1,0f0.0,%0\";
  return \"movrl	%1,%0\;cpysre	%0,0f0.0,%0\";
}"
  [(set_attr "type" "multi")])

(define_insn "abssf2"
  [(set (match_operand:SF 0 "register_operand" "=d*f")
	(abs:SF (match_operand:SF 1 "register_operand" "df")))]
  ""
  "*
{
  int dstreg = REGNO (operands[0]);
  int srcreg = REGNO (operands[1]);

  if (dstreg < 32 && srcreg < 32)
    return \"clrbit	31,%1,%0\";

  if (dstreg >= 32 && srcreg >= 32)
    return \"cpysre	%1,0f0.0,%0\";

  if (dstreg < 32)
    return \"movr	%1,%0\;clrbit	31,%0,%0\";

  return \"movr	%1,%0\;cpysre	%0,0f0.0,%0\";
}"
  [(set_attr "type" "multi")])

;; Tetra (16 byte) float support.

(define_expand "cmpxf"
  [(set (reg:CC 36)
	(compare:CC (match_operand:XF 0 "register_operand" "")
		    (match_operand:XF 1 "nonmemory_operand" "")))]
  "TARGET_NUMERICS"
  "
{
  i960_compare_op0 = operands[0];
  i960_compare_op1 = operands[1];
  DONE;
}")

(define_insn ""
  [(set (reg:CC 36)
	(compare:CC (match_operand:XF 0 "register_operand" "f")
		    (match_operand:XF 1 "nonmemory_operand" "fGH")))]
  "TARGET_NUMERICS"
  "cmpr %0,%1"
  [(set_attr "type" "fpcc")])

(define_expand "movxf"
  [(set (match_operand:XF 0 "general_operand" "")
	(match_operand:XF 1 "fpmove_src_operand" ""))]
  ""
  "
{
  if (emit_move_sequence (operands, XFmode))
    DONE;
}")

(define_insn ""
  [(set (match_operand:XF 0 "general_operand" "=r,f,d,d,m")
	(match_operand:XF 1 "fpmove_src_operand" "r,GH,F,m,d"))]
  "register_operand (operands[0], XFmode)
   || register_operand (operands[1], XFmode)"
  "*
{
  switch (which_alternative)
    {
    case 0:
      if (FP_REG_P (operands[0]) || FP_REG_P (operands[1]))
	return \"movre	%1,%0\";
      else
	return \"movq	%1,%0\";
    case 1:
      return \"movre	%1,%0\";
    case 2:
      return i960_output_ldconst (operands[0], operands[1]);
    case 3:
      return \"ldt	%1,%0\";
    case 4:
      return \"stt	%1,%0\";
    }
}"
  [(set_attr "type" "move,move,load,fpload,fpstore")])

(define_insn "extendsfxf2"
  [(set (match_operand:XF 0 "register_operand" "=f,d")
	(float_extend:XF
	 (match_operand:SF 1 "register_operand" "d,f")))]
  "TARGET_NUMERICS"
  "@
  movr	%1,%0
  movre	%1,%0"
  [(set_attr "type" "fpmove")])

(define_insn "extenddfxf2"
  [(set (match_operand:XF 0 "register_operand" "=f,d")
	(float_extend:XF
	 (match_operand:DF 1 "register_operand" "d,f")))]
  "TARGET_NUMERICS"
  "@
  movrl	%1,%0
  movre	%1,%0"
  [(set_attr "type" "fpmove")])

(define_insn "truncxfdf2"
  [(set (match_operand:DF 0 "register_operand" "=d")
	(float_truncate:DF
	 (match_operand:XF 1 "register_operand" "f")))]
  "TARGET_NUMERICS"
  "movrl	%1,%0"
  [(set_attr "type" "fpmove")])

(define_insn "truncxfsf2"
  [(set (match_operand:SF 0 "register_operand" "=d")
	(float_truncate:SF
	 (match_operand:XF 1 "register_operand" "f")))]
  "TARGET_NUMERICS"
  "movr	%1,%0"
  [(set_attr "type" "fpmove")])

(define_insn "floatsixf2"
  [(set (match_operand:XF 0 "register_operand" "=f")
	(float:XF (match_operand:SI 1 "register_operand" "d")))]
  "TARGET_NUMERICS"
  "cvtir	%1,%0"
  [(set_attr "type" "fpcvt")])

(define_insn "fix_truncxfsi2"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(fix:SI (fix:XF (match_operand:XF 1 "register_operand" "f"))))]
  "TARGET_NUMERICS"
  "cvtzri	%1,%0"
  [(set_attr "type" "fpcvt")])

(define_insn "fixuns_truncxfsi2"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(unsigned_fix:SI (fix:XF (match_operand:XF 1 "register_operand" "f"))))]
  "TARGET_NUMERICS"
  "cvtzri	%1,%0"
  [(set_attr "type" "fpcvt")])

(define_insn "addxf3"
  [(set (match_operand:XF 0 "register_operand" "=f")
	(plus:XF (match_operand:XF 1 "nonmemory_operand" "%fGH")
		 (match_operand:XF 2 "nonmemory_operand" "fGH")))]
  "TARGET_NUMERICS"
  "addr	%1,%2,%0"
  [(set_attr "type" "fpadd")])

(define_insn "subxf3"
  [(set (match_operand:XF 0 "register_operand" "=f")
	(minus:XF (match_operand:XF 1 "nonmemory_operand" "fGH")
		  (match_operand:XF 2 "nonmemory_operand" "fGH")))]
  "TARGET_NUMERICS"
  "subr	%2,%1,%0"
  [(set_attr "type" "fpadd")])

(define_insn "mulxf3"
  [(set (match_operand:XF 0 "register_operand" "=f")
	(mult:XF (match_operand:XF 1 "nonmemory_operand" "%fGH")
		 (match_operand:XF 2 "nonmemory_operand" "fGH")))]
  "TARGET_NUMERICS"
  "mulr	%1,%2,%0"
  [(set_attr "type" "fpmul")])

(define_insn "divxf3"
  [(set (match_operand:XF 0 "register_operand" "=f")
	(div:XF (match_operand:XF 1 "nonmemory_operand" "fGH")
		(match_operand:XF 2 "nonmemory_operand" "fGH")))]
  "TARGET_NUMERICS"
  "divr	%2,%1,%0"
  [(set_attr "type" "fpdiv")])

(define_insn "negxf2"
  [(set (match_operand:XF 0 "register_operand" "=f")
	(neg:XF (match_operand:XF 1 "register_operand" "f")))]
  "TARGET_NUMERICS"
  "subr	%1,0f0.0,%0"
  [(set_attr "type" "fpadd")])

(define_insn "absxf2"
  [(set (match_operand:XF 0 "register_operand" "=f")
	(abs:XF (match_operand:XF 1 "register_operand" "f")))]
  "(TARGET_NUMERICS)"
  "cpysre	%1,0f0.0,%0"
  [(set_attr "type" "fpmove")])

;; Arithmetic shift instructions.

;; The shli instruction generates an overflow fault if the sign changes.
;; In the case of overflow, it does not give the natural result, it instead
;; gives the last shift value before the overflow.  We can not use this
;; instruction because gcc thinks that arithmetic left shift and logical
;; left shift are identical, and sometimes canonicalizes the logical left
;; shift to an arithmetic left shift.  Therefore we must always use the
;; logical left shift instruction.

(define_insn "ashlsi3"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(ashift:SI (match_operand:SI 1 "arith_operand" "dI")
		   (match_operand:SI 2 "arith_operand" "dI")))]
  ""
  "shlo	%2,%1,%0"
  [(set_attr "type" "alu2")])

(define_insn "ashrsi3"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(ashiftrt:SI (match_operand:SI 1 "arith_operand" "dI")
		     (match_operand:SI 2 "arith_operand" "dI")))]
  ""
  "shri	%2,%1,%0"
  [(set_attr "type" "alu2")])

(define_insn "lshrsi3"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(lshiftrt:SI (match_operand:SI 1 "arith_operand" "dI")
		   (match_operand:SI 2 "arith_operand" "dI")))]
  ""
  "shro	%2,%1,%0"
  [(set_attr "type" "alu2")])

;; Unconditional and other jump instructions.

(define_insn "jump"
  [(set (pc)
	(label_ref (match_operand 0 "" "")))]
  ""
  "b	%l0"
  [(set_attr "type" "branch")])

(define_insn "indirect_jump"
  [(set (pc) (match_operand:SI 0 "address_operand" "p"))]
  ""
  "bx	%a0"
  [(set_attr "type" "branch")])

(define_insn "tablejump"
  [(set (pc) (match_operand:SI 0 "register_operand" "d"))
   (use (label_ref (match_operand 1 "" "")))]
  ""
  "*
{
  if (flag_pic)
    return \"bx	%l1(%0)\";
  else
    return \"bx	(%0)\";
}"
  [(set_attr "type" "branch")])

;;- jump to subroutine

(define_expand "call"
  [(call (match_operand:SI 0 "memory_operand" "m")
	 (match_operand:SI 1 "immediate_operand" "i"))]
  ""
  "
{
  emit_insn (gen_call_internal (operands[0], operands[1],
				virtual_outgoing_args_rtx));
  DONE;
}")

;; We need a call saved register allocated for the match_scratch, so we use
;; 'l' because all local registers are call saved.

;; ??? I would prefer to use a match_scratch here, but match_scratch allocated
;; registers can't be used for spills.  In a function with lots of calls,
;; local-alloc may allocate all local registers to a match_scratch, leaving
;; no local registers available for spills.

(define_insn "call_internal"
  [(call (match_operand:SI 0 "memory_operand" "m")
	 (match_operand:SI 1 "immediate_operand" "i"))
   (use (match_operand:SI 2 "address_operand" "p"))
   (clobber (reg:SI 19))]
  ""
  "* return i960_output_call_insn (operands[0], operands[1], operands[2],
				   insn);"
  [(set_attr "type" "call")])

(define_expand "call_value"
  [(set (match_operand 0 "register_operand" "=d")
	(call (match_operand:SI 1 "memory_operand" "m")
	      (match_operand:SI 2 "immediate_operand" "i")))]
  ""
  "
{
  emit_insn (gen_call_value_internal (operands[0], operands[1], operands[2],
				      virtual_outgoing_args_rtx));
  DONE;
}")

;; We need a call saved register allocated for the match_scratch, so we use
;; 'l' because all local registers are call saved.

(define_insn "call_value_internal"
  [(set (match_operand 0 "register_operand" "=d")
	(call (match_operand:SI 1 "memory_operand" "m")
	      (match_operand:SI 2 "immediate_operand" "i")))
   (use (match_operand:SI 3 "address_operand" "p"))
   (clobber (reg:SI 19))]
  ""
  "* return i960_output_call_insn (operands[1], operands[2], operands[3],
				   insn);"
  [(set_attr "type" "call")])

(define_insn "return"
  [(return)]
  ""
  "* return i960_output_ret_insn (insn);"
  [(set_attr "type" "branch")])

;; A return instruction.  Used only by nonlocal_goto to change the
;; stack pointer, frame pointer, previous frame pointer and the return
;; instruction pointer.
(define_insn "ret"
  [(use (reg:SI 16))
   (unspec_volatile [(const_int 0)] 3)]
  ""
  "ret"
  [(set_attr "type" "branch")
   (set_attr "length" "1")])

(define_expand "nonlocal_goto"
  [(match_operand:SI 0 "" "")
   (match_operand:SI 1 "general_operand" "")
   (match_operand:SI 2 "general_operand" "")
   (match_operand:SI 3 "general_operand" "")]
  ""
  "
{
  rtx fp = operands[1];
  rtx new_pc = operands[3];
  rtx stack = operands[2];
  rtx val = operands[0];

  /* This code isn't sufficient to make nonlocal_gotos for nested
     functions to work fully.  Here we assume that the passed frame
     pointer is a real hard frame pointer, not a
     virtual_stack_vars_rtx type of frame.  */

  /* We must restore the stack pointer, frame pointer, previous frame
     pointer and the return instruction pointer.  Since the ret
     instruction does all this for us with one instruction, we arrange
     everything so that ret will do everything we need done.  */

  if (GET_CODE (fp) != REG)
    fp = force_reg (Pmode, fp);
  if (GET_CODE (val) != REG)
    val = force_reg (Pmode, val);
  if (GET_CODE (new_pc) != REG)
    new_pc = force_reg (Pmode, new_pc);


  /* First, we must flush the register windows, so that we can modify
     the saved local registers on the stack directly and because we
     are going to change the previous frame pointer.  */

  emit_insn (gen_flush_register_windows ());

  /* Next, we put the address that we want to transfer to, into the
     saved $rip value on the stack.  Once we ret below, that value
     will be loaded into the pc (IP).  */

  emit_move_insn (gen_rtx (MEM, SImode,
			   plus_constant (fp, 8)),
		  new_pc);

  /* Next, we put the value into the static chain register's save
     area on the stack.  After the ret below, this will be loaded into
     r3 (the static chain).  */
     
  emit_move_insn (gen_rtx (MEM, SImode,
			   plus_constant (fp, 12)),
		  val);

  /* We now load pfp (the previous frame pointer) with the value that
     we want fp to be.  */

  emit_move_insn (gen_rtx (REG, SImode, 16), fp);

  /* And finally, we can now just ret to get all the values saved
     above into all the right registers, and also, all the local
     register that were in use in the function, are restored from
     their saved values (from the call instruction) on the stack
     because we are very careful to ret from the exact save area in
     use during the original call.  */

  emit_insn (gen_ret ());
  emit_barrier ();
  DONE;
}")

;; Special insn to flush register windows.
(define_insn "flush_register_windows"
  [(unspec_volatile [(const_int 0)] 1)]
  ""
  "flushreg"
  [(set_attr "type" "misc")
   (set_attr "length" "1")])

(define_insn "nop"
  [(const_int 0)]
  ""
  "")

;; Various peephole optimizations for multiple-word moves, loads, and stores.
;; Multiple register moves.

;; Matched 5/28/91
(define_peephole
  [(set (match_operand:SI 0 "register_operand" "=r")
	(match_operand:SI 1 "register_operand" "r"))
   (set (match_operand:SI 2 "register_operand" "=r")
	(match_operand:SI 3 "register_operand" "r"))
   (set (match_operand:SI 4 "register_operand" "=r")
	(match_operand:SI 5 "register_operand" "r"))
   (set (match_operand:SI 6 "register_operand" "=r")
	(match_operand:SI 7 "register_operand" "r"))]
  "((REGNO (operands[0]) & 3) == 0)
   && ((REGNO (operands[1]) & 3) == 0)
   && (REGNO (operands[0]) + 1 == REGNO (operands[2]))
   && (REGNO (operands[1]) + 1 == REGNO (operands[3]))
   && (REGNO (operands[0]) + 2 == REGNO (operands[4]))
   && (REGNO (operands[1]) + 2 == REGNO (operands[5]))
   && (REGNO (operands[0]) + 3 == REGNO (operands[6]))
   && (REGNO (operands[1]) + 3 == REGNO (operands[7]))"
  "movq	%1,%0")

;; Matched 4/17/92
(define_peephole
  [(set (match_operand:DI 0 "register_operand" "=r")
	(match_operand:DI 1 "register_operand" "r"))
   (set (match_operand:DI 2 "register_operand" "=r")
	(match_operand:DI 3 "register_operand" "r"))]
  "((REGNO (operands[0]) & 3) == 0)
   && ((REGNO (operands[1]) & 3) == 0)
   && (REGNO (operands[0]) + 2 == REGNO (operands[2]))
   && (REGNO (operands[1]) + 2 == REGNO (operands[3]))"
  "movq	%1,%0")

;; Matched 4/17/92
(define_peephole
  [(set (match_operand:DI 0 "register_operand" "=r")
	(match_operand:DI 1 "register_operand" "r"))
   (set (match_operand:SI 2 "register_operand" "=r")
	(match_operand:SI 3 "register_operand" "r"))
   (set (match_operand:SI 4 "register_operand" "=r")
	(match_operand:SI 5 "register_operand" "r"))]
  "((REGNO (operands[0]) & 3) == 0)
   && ((REGNO (operands[1]) & 3) == 0)
   && (REGNO (operands[0]) + 2 == REGNO (operands[2]))
   && (REGNO (operands[1]) + 2 == REGNO (operands[3]))
   && (REGNO (operands[0]) + 3 == REGNO (operands[4]))
   && (REGNO (operands[1]) + 3 == REGNO (operands[5]))"
  "movq	%1,%0")

;; Matched 4/17/92
(define_peephole
  [(set (match_operand:SI 0 "register_operand" "=r")
	(match_operand:SI 1 "register_operand" "r"))
   (set (match_operand:SI 2 "register_operand" "=r")
	(match_operand:SI 3 "register_operand" "r"))
   (set (match_operand:DI 4 "register_operand" "=r")
	(match_operand:DI 5 "register_operand" "r"))]
  "((REGNO (operands[0]) & 3) == 0)
   && ((REGNO (operands[1]) & 3) == 0)
   && (REGNO (operands[0]) + 1 == REGNO (operands[2]))
   && (REGNO (operands[1]) + 1 == REGNO (operands[3]))
   && (REGNO (operands[0]) + 2 == REGNO (operands[4]))
   && (REGNO (operands[1]) + 2 == REGNO (operands[5]))"
  "movq	%1,%0")

;; Matched 4/17/92
(define_peephole
  [(set (match_operand:DI 0 "register_operand" "=r")
	(match_operand:DI 1 "register_operand" "r"))
   (set (match_operand:SI 2 "register_operand" "=r")
	(match_operand:SI 3 "register_operand" "r"))]
  "((REGNO (operands[0]) & 3) == 0)
   && ((REGNO (operands[1]) & 3) == 0)
   && (REGNO (operands[0]) + 2 == REGNO (operands[2]))
   && (REGNO (operands[1]) + 2 == REGNO (operands[3]))"
  "movt	%1,%0")

;; Matched 5/28/91
(define_peephole
  [(set (match_operand:SI 0 "register_operand" "=r")
	(match_operand:SI 1 "register_operand" "r"))
   (set (match_operand:SI 2 "register_operand" "=r")
	(match_operand:SI 3 "register_operand" "r"))
   (set (match_operand:SI 4 "register_operand" "=r")
	(match_operand:SI 5 "register_operand" "r"))]
  "((REGNO (operands[0]) & 3) == 0)
   && ((REGNO (operands[1]) & 3) == 0)
   && (REGNO (operands[0]) + 1 == REGNO (operands[2]))
   && (REGNO (operands[1]) + 1 == REGNO (operands[3]))
   && (REGNO (operands[0]) + 2 == REGNO (operands[4]))
   && (REGNO (operands[1]) + 2 == REGNO (operands[5]))"
  "movt	%1,%0")

;; Matched 5/28/91
(define_peephole
  [(set (match_operand:SI 0 "register_operand" "=r")
	(match_operand:SI 1 "register_operand" "r"))
   (set (match_operand:SI 2 "register_operand" "=r")
	(match_operand:SI 3 "register_operand" "r"))]
  "((REGNO (operands[0]) & 1) == 0)
   && ((REGNO (operands[1]) & 1) == 0)
   && (REGNO (operands[0]) + 1 == REGNO (operands[2]))
   && (REGNO (operands[1]) + 1 == REGNO (operands[3]))"
  "movl	%1,%0")

; Multiple register loads.

;; Matched 6/15/91
(define_peephole
  [(set (match_operand:SI 0 "register_operand" "=r")
	(mem:SI (plus:SI (match_operand:SI 1 "register_operand" "r")
			 (match_operand:SI 2 "immediate_operand" "n"))))
   (set (match_operand:SI 3 "register_operand" "=r")
	(mem:SI (plus:SI (match_dup 1)
			 (match_operand:SI 4 "immediate_operand" "n"))))
   (set (match_operand:SI 5 "register_operand" "=r")
	(mem:SI (plus:SI (match_dup 1)
			 (match_operand:SI 6 "immediate_operand" "n"))))
   (set (match_operand:SI 7 "register_operand" "=r")
	(mem:SI (plus:SI (match_dup 1)
			 (match_operand:SI 8 "immediate_operand" "n"))))]
  "(i960_si_ti (operands[1], operands[2]) && ((REGNO (operands[0]) & 3) == 0)
   && (REGNO (operands[1]) != REGNO (operands[0]))
   && (REGNO (operands[0]) + 1 == REGNO (operands[3]))
   && (REGNO (operands[1]) != REGNO (operands[3]))
   && (REGNO (operands[0]) + 2 == REGNO (operands[5]))
   && (REGNO (operands[1]) != REGNO (operands[5]))
   && (REGNO (operands[0]) + 3 == REGNO (operands[7]))
   && (INTVAL (operands[2]) + 4 == INTVAL (operands[4]))
   && (INTVAL (operands[2]) + 8 == INTVAL (operands[6]))
   && (INTVAL (operands[2]) + 12 == INTVAL (operands[8])))"
  "ldq	%2(%1),%0")

;; Matched 5/28/91
(define_peephole
  [(set (match_operand:DF 0 "register_operand" "=d")
	(mem:DF (plus:SI (match_operand:SI 1 "register_operand" "d")
			 (match_operand:SI 2 "immediate_operand" "n"))))
   (set (match_operand:DF 3 "register_operand" "=d")
	(mem:DF (plus:SI (match_dup 1)
			 (match_operand:SI 4 "immediate_operand" "n"))))]
  "(i960_si_ti (operands[1], operands[2]) && ((REGNO (operands[0]) & 3) == 0)
   && (REGNO (operands[1]) != REGNO (operands[0]))
   && (REGNO (operands[0]) + 2 == REGNO (operands[3]))
   && (REGNO (operands[1]) != REGNO (operands[3]))
   && (INTVAL (operands[2]) + 8 == INTVAL (operands[4])))"
  "ldq	%2(%1),%0")

;; Matched 1/24/92
(define_peephole
  [(set (match_operand:DI 0 "register_operand" "=d")
	(mem:DI (plus:SI (match_operand:SI 1 "register_operand" "d")
			 (match_operand:SI 2 "immediate_operand" "n"))))
   (set (match_operand:DI 3 "register_operand" "=d")
	(mem:DI (plus:SI (match_dup 1)
			 (match_operand:SI 4 "immediate_operand" "n"))))]
  "(i960_si_ti (operands[1], operands[2]) && ((REGNO (operands[0]) & 3) == 0)
   && (REGNO (operands[1]) != REGNO (operands[0]))
   && (REGNO (operands[0]) + 2 == REGNO (operands[3]))
   && (REGNO (operands[1]) != REGNO (operands[3]))
   && (INTVAL (operands[2]) + 8 == INTVAL (operands[4])))"
  "ldq	%2(%1),%0")

;; Matched 4/17/92
(define_peephole
  [(set (match_operand:SI 0 "register_operand" "=d")
	(mem:SI (match_operand:SI 1 "register_operand" "d")))
   (set (match_operand:SI 2 "register_operand" "=d")
	(mem:SI (plus:SI (match_dup 1)
			 (match_operand:SI 3 "immediate_operand" "n"))))
   (set (match_operand:SI 4 "register_operand" "=d")
	(mem:SI (plus:SI (match_dup 1)
			 (match_operand:SI 5 "immediate_operand" "n"))))
   (set (match_operand:SI 6 "register_operand" "=d")
	(mem:SI (plus:SI (match_dup 1)
			 (match_operand:SI 7 "immediate_operand" "n"))))]
  "(i960_si_ti (operands[1], 0) && ((REGNO (operands[0]) & 3) == 0)
   && (REGNO (operands[1]) != REGNO (operands[0]))
   && (REGNO (operands[0]) + 1 == REGNO (operands[2]))
   && (REGNO (operands[1]) != REGNO (operands[2]))
   && (REGNO (operands[0]) + 2 == REGNO (operands[4]))
   && (REGNO (operands[1]) != REGNO (operands[4]))
   && (REGNO (operands[0]) + 3 == REGNO (operands[6]))
   && (INTVAL (operands[3]) == 4)
   && (INTVAL (operands[5]) == 8)
   && (INTVAL (operands[7]) == 12))"
  "ldq	(%1),%0")

;; Matched 5/28/91
(define_peephole
  [(set (match_operand:SI 0 "register_operand" "=d")
	(mem:SI (plus:SI (match_operand:SI 1 "register_operand" "d")
			 (match_operand:SI 2 "immediate_operand" "n"))))
   (set (match_operand:SI 3 "register_operand" "=d")
	(mem:SI (plus:SI (match_dup 1)
			 (match_operand:SI 4 "immediate_operand" "n"))))
   (set (match_operand:SI 5 "register_operand" "=d")
	(mem:SI (plus:SI (match_dup 1)
			 (match_operand:SI 6 "immediate_operand" "n"))))]
  "(i960_si_ti (operands[1], operands[2]) && ((REGNO (operands[0]) & 3) == 0)
   && (REGNO (operands[1]) != REGNO (operands[0]))
   && (REGNO (operands[0]) + 1 == REGNO (operands[3]))
   && (REGNO (operands[1]) != REGNO (operands[3]))
   && (REGNO (operands[0]) + 2 == REGNO (operands[5]))
   && (INTVAL (operands[2]) + 4 == INTVAL (operands[4]))
   && (INTVAL (operands[2]) + 8 == INTVAL (operands[6])))"
  "ldt	%2(%1),%0")

;; Matched 6/15/91
(define_peephole
  [(set (match_operand:SI 0 "register_operand" "=d")
	(mem:SI (match_operand:SI 1 "register_operand" "d")))
   (set (match_operand:SI 2 "register_operand" "=d")
	(mem:SI (plus:SI (match_dup 1)
			 (match_operand:SI 3 "immediate_operand" "n"))))
   (set (match_operand:SI 4 "register_operand" "=d")
	(mem:SI (plus:SI (match_dup 1)
			 (match_operand:SI 5 "immediate_operand" "n"))))]
  "(i960_si_ti (operands[1], 0) && ((REGNO (operands[0]) & 3) == 0)
   && (REGNO (operands[1]) != REGNO (operands[0]))
   && (REGNO (operands[0]) + 1 == REGNO (operands[2]))
   && (REGNO (operands[1]) != REGNO (operands[2]))
   && (REGNO (operands[0]) + 2 == REGNO (operands[4]))
   && (INTVAL (operands[3]) == 4)
   && (INTVAL (operands[5]) == 8))"
  "ldt	(%1),%0")

;; Matched 5/28/91
(define_peephole
  [(set (match_operand:SI 0 "register_operand" "=d")
	(mem:SI (plus:SI (match_operand:SI 1 "register_operand" "d")
			 (match_operand:SI 2 "immediate_operand" "n"))))
   (set (match_operand:SI 3 "register_operand" "=d")
	(mem:SI (plus:SI (match_dup 1)
			 (match_operand:SI 4 "immediate_operand" "n"))))]
  "(i960_si_di (operands[1], operands[2]) && ((REGNO (operands[0]) & 1) == 0)
   && (REGNO (operands[1]) != REGNO (operands[0]))
   && (REGNO (operands[0]) + 1 == REGNO (operands[3]))
   && (INTVAL (operands[2]) + 4 == INTVAL (operands[4])))"
  "ldl	%2(%1),%0")

;; Matched 5/28/91
(define_peephole
  [(set (match_operand:SI 0 "register_operand" "=d")
	(mem:SI (match_operand:SI 1 "register_operand" "d")))
   (set (match_operand:SI 2 "register_operand" "=d")
	(mem:SI (plus:SI (match_dup 1)
			 (match_operand:SI 3 "immediate_operand" "n"))))]
  "(i960_si_di (operands[1], 0) && ((REGNO (operands[0]) & 1) == 0)
   && (REGNO (operands[1]) != REGNO (operands[0]))
   && (REGNO (operands[0]) + 1 == REGNO (operands[2]))
   && (INTVAL (operands[3]) == 4))"
  "ldl	(%1),%0")

; Multiple register stores.

;; Matched 5/28/91
(define_peephole
  [(set (mem:SI (plus:SI (match_operand:SI 0 "register_operand" "d")
			 (match_operand:SI 1 "immediate_operand" "n")))
	(match_operand:SI 2 "register_operand" "d"))
   (set (mem:SI (plus:SI (match_dup 0)
			 (match_operand:SI 3 "immediate_operand" "n")))
	(match_operand:SI 4 "register_operand" "d"))
   (set (mem:SI (plus:SI (match_dup 0)
			 (match_operand:SI 5 "immediate_operand" "n")))
	(match_operand:SI 6 "register_operand" "d"))
   (set (mem:SI (plus:SI (match_dup 0)
			 (match_operand:SI 7 "immediate_operand" "n")))
	(match_operand:SI 8 "register_operand" "d"))]
  "(i960_si_ti (operands[0], operands[1]) && ((REGNO (operands[2]) & 3) == 0)
   && (REGNO (operands[2]) + 1 == REGNO (operands[4]))
   && (REGNO (operands[2]) + 2 == REGNO (operands[6]))
   && (REGNO (operands[2]) + 3 == REGNO (operands[8]))
   && (INTVAL (operands[1]) + 4 == INTVAL (operands[3]))
   && (INTVAL (operands[1]) + 8 == INTVAL (operands[5]))
   && (INTVAL (operands[1]) + 12 == INTVAL (operands[7])))"
  "stq	%2,%1(%0)")

;; Matched 6/16/91
(define_peephole
  [(set (mem:DF (plus:SI (match_operand:SI 0 "register_operand" "d")
			 (match_operand:SI 1 "immediate_operand" "n")))
	(match_operand:DF 2 "register_operand" "d"))
   (set (mem:DF (plus:SI (match_dup 0)
			 (match_operand:SI 3 "immediate_operand" "n")))
	(match_operand:DF 4 "register_operand" "d"))]
  "(i960_si_ti (operands[0], operands[1]) && ((REGNO (operands[2]) & 3) == 0)
   && (REGNO (operands[2]) + 2 == REGNO (operands[4]))
   && (INTVAL (operands[1]) + 8 == INTVAL (operands[3])))"
  "stq	%2,%1(%0)")

;; Matched 4/17/92
(define_peephole
  [(set (mem:DI (plus:SI (match_operand:SI 0 "register_operand" "d")
			 (match_operand:SI 1 "immediate_operand" "n")))
	(match_operand:DI 2 "register_operand" "d"))
   (set (mem:DI (plus:SI (match_dup 0)
			 (match_operand:SI 3 "immediate_operand" "n")))
	(match_operand:DI 4 "register_operand" "d"))]
  "(i960_si_ti (operands[0], operands[1]) && ((REGNO (operands[2]) & 3) == 0)
   && (REGNO (operands[2]) + 2 == REGNO (operands[4]))
   && (INTVAL (operands[1]) + 8 == INTVAL (operands[3])))"
  "stq	%2,%1(%0)")

;; Matched 1/23/92
(define_peephole
  [(set (mem:SI (match_operand:SI 0 "register_operand" "d"))
	(match_operand:SI 1 "register_operand" "d"))
   (set (mem:SI (plus:SI (match_dup 0)
			 (match_operand:SI 2 "immediate_operand" "n")))
	(match_operand:SI 3 "register_operand" "d"))
   (set (mem:SI (plus:SI (match_dup 0)
			 (match_operand:SI 4 "immediate_operand" "n")))
	(match_operand:SI 5 "register_operand" "d"))
   (set (mem:SI (plus:SI (match_dup 0)
			 (match_operand:SI 6 "immediate_operand" "n")))
	(match_operand:SI 7 "register_operand" "d"))]
  "(i960_si_ti (operands[0], 0) && ((REGNO (operands[1]) & 3) == 0)
   && (REGNO (operands[1]) + 1 == REGNO (operands[3]))
   && (REGNO (operands[1]) + 2 == REGNO (operands[5]))
   && (REGNO (operands[1]) + 3 == REGNO (operands[7]))
   && (INTVAL (operands[2]) == 4)
   && (INTVAL (operands[4]) == 8)
   && (INTVAL (operands[6]) == 12))"
  "stq	%1,(%0)")

;; Matched 5/29/91
(define_peephole
  [(set (mem:SI (plus:SI (match_operand:SI 0 "register_operand" "d")
			 (match_operand:SI 1 "immediate_operand" "n")))
	(match_operand:SI 2 "register_operand" "d"))
   (set (mem:SI (plus:SI (match_dup 0)
			 (match_operand:SI 3 "immediate_operand" "n")))
	(match_operand:SI 4 "register_operand" "d"))
   (set (mem:SI (plus:SI (match_dup 0)
			 (match_operand:SI 5 "immediate_operand" "n")))
	(match_operand:SI 6 "register_operand" "d"))]
  "(i960_si_ti (operands[0], operands[1]) && ((REGNO (operands[2]) & 3) == 0)
   && (REGNO (operands[2]) + 1 == REGNO (operands[4]))
   && (REGNO (operands[2]) + 2 == REGNO (operands[6]))
   && (INTVAL (operands[1]) + 4 == INTVAL (operands[3]))
   && (INTVAL (operands[1]) + 8 == INTVAL (operands[5])))"
  "stt	%2,%1(%0)")

;; Matched 5/29/91
(define_peephole
  [(set (mem:SI (match_operand:SI 0 "register_operand" "d"))
	(match_operand:SI 1 "register_operand" "d"))
   (set (mem:SI (plus:SI (match_dup 0)
			 (match_operand:SI 2 "immediate_operand" "n")))
	(match_operand:SI 3 "register_operand" "d"))
   (set (mem:SI (plus:SI (match_dup 0)
			 (match_operand:SI 4 "immediate_operand" "n")))
	(match_operand:SI 5 "register_operand" "d"))]
  "(i960_si_ti (operands[0], 0) && ((REGNO (operands[1]) & 3) == 0)
   && (REGNO (operands[1]) + 1 == REGNO (operands[3]))
   && (REGNO (operands[1]) + 2 == REGNO (operands[5]))
   && (INTVAL (operands[2]) == 4)
   && (INTVAL (operands[4]) == 8))"
  "stt	%1,(%0)")

;; Matched 5/28/91
(define_peephole
  [(set (mem:SI (plus:SI (match_operand:SI 0 "register_operand" "d")
			 (match_operand:SI 1 "immediate_operand" "n")))
	(match_operand:SI 2 "register_operand" "d"))
   (set (mem:SI (plus:SI (match_dup 0)
			 (match_operand:SI 3 "immediate_operand" "n")))
	(match_operand:SI 4 "register_operand" "d"))]
  "(i960_si_di (operands[0], operands[1]) && ((REGNO (operands[2]) & 1) == 0)
   && (REGNO (operands[2]) + 1 == REGNO (operands[4]))
   && (INTVAL (operands[1]) + 4 == INTVAL (operands[3])))"
  "stl	%2,%1(%0)")

;; Matched 5/28/91
(define_peephole
  [(set (mem:SI (match_operand:SI 0 "register_operand" "d"))
	(match_operand:SI 1 "register_operand" "d"))
   (set (mem:SI (plus:SI (match_dup 0)
			 (match_operand:SI 2 "immediate_operand" "n")))
	(match_operand:SI 3 "register_operand" "d"))]
  "(i960_si_di (operands[0], 0) && ((REGNO (operands[1]) & 1) == 0)
   && (REGNO (operands[1]) + 1 == REGNO (operands[3]))
   && (INTVAL (operands[2]) == 4))"
  "stl	%1,(%0)")
