;;- Machine description for Intel 80960 chip for GNU C compiler
;;   Copyright (C) 1992 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, 675 Mass Ave, Cambridge, MA 02139, 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 psuedo-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 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")
	(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.  This value of this 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
    && 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
    || 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
    && 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
    || 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
    && 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
    || 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,m,o")
	(match_operand:DI 1 "general_operand" "dI,i,m,d,J"))]
  "(current_function_args_size == 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:
      return \"movl	%1,%0\";
    case 1:
      return i960_output_ldconst (operands[0], operands[1]);
    case 2:
      return \"ldl	%1,%0\";
    case 3:
      return \"stl	%1,%0\";
    case 4:
      operands[1] = adj_offsettable_operand (operands[0], 4);
      return \"st	g14,%0\;st	g14,%1\";
    }
}"
  [(set_attr "type" "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,m")
	(match_operand:DI 1 "general_operand" "dI,i,m,d"))]
  "(current_function_args_size != 0
    || rtx_equal_function_value_matters != 0)
   && (register_operand (operands[0], DImode)
       || register_operand (operands[1], DImode))"
  "*
{
  switch (which_alternative)
    {
    case 0:
      return \"movl	%1,%0\";
    case 1:
      return i960_output_ldconst (operands[0], operands[1]);
    case 2:
      return \"ldl	%1,%0\";
    case 3:
      return \"stl	%1,%0\";
    }
}"
  [(set_attr "type" "move,load,load,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,m,o")
	(match_operand:TI 1 "general_operand" "dI,i,m,d,J"))]
  "(current_function_args_size == 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:
      return \"movq	%1,%0\";
    case 1:
      return i960_output_ldconst (operands[0], operands[1]);
    case 2:
      return \"ldq	%1,%0\";
    case 3:
      return \"stq	%1,%0\";
    case 4:
      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,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,m")
	(match_operand:TI 1 "general_operand" "dI,i,m,d"))]
  "(current_function_args_size != 0
    || rtx_equal_function_value_matters != 0)
   && (register_operand (operands[0], TImode)
       || register_operand (operands[1], TImode))"
  "*
{
  switch (which_alternative)
    {
    case 0:
      return \"movq	%1,%0\";
    case 1:
      return i960_output_ldconst (operands[0], operands[1]);
    case 2:
      return \"ldq	%1,%0\";
    case 3:
      return \"stq	%1,%0\";
    }
}"
  [(set_attr "type" "move,load,load,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
    && 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
    || 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
    && 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
    || 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_rtx (CONST_INT, VOIDmode, 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_rtx (CONST_INT, VOIDmode, 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_rtx (CONST_INT, VOIDmode, 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_rtx (CONST_INT, VOIDmode, 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_rtx (CONST_INT, VOIDmode, 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_rtx (CONST_INT, VOIDmode, 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.

(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\";
    }
  if (CONSTANT_P (operands[1]))
    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_rtx(CONST_INT, VOIDmode,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 "arith_operand" "%dI")
		(match_operand:SI 2 "arith_operand" "dI")))]
  ""
  "*
{
  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 (not: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 \"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 "arith_operand" "%dI"))
		(not:SI (match_operand:SI 2 "arith_operand" "dI"))))]
  ""
  "*
{
  if (i960_bypass (insn, operands[1], operands[2], 0))
    return \"nand	%2,%1,%0\";
  return \"nand	%1,%2,%0\";
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=d")
	(ior:SI (match_operand:SI 1 "arith_operand" "dI")
		(match_operand:SI 2 "power2_operand" "n")))]
  ""
  "*
{
  operands[2] = gen_rtx (CONST_INT, VOIDmode,
			 bitpos (INTVAL (operands[2])));
  return \"setbit	%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")

(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_rtx (CONST_INT, VOIDmode,
			 bitpos (~INTVAL (operands[2])));
  return \"clrbit	%2,%1,%0\";
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=d")
	(and:SI (ashift:SI (const_int 1)
			   (match_operand:SI 1 "register_operand" "d"))
		(match_operand:SI 2 "arith_operand" "dI")))]
  ""
  "clrbit	%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_rtx (CONST_INT, VOIDmode,
			 bitpos (INTVAL (operands[2])));
  return \"notbit	%2,%1,%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 "iorsi3"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(ior: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 \"or	%2,%1,%0\";
  return \"or	%1,%2,%0\";
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=d")
	(ior:SI (not: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 \"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 "arith_operand" "%dI"))
		(not:SI (match_operand:SI 2 "arith_operand" "dI"))))]
  ""
  "*
{
  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 "arith_operand" "%dI")
		(match_operand:SI 2 "arith_operand" "dI")))]
  ""
  "*
{
  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")
	(not:SI (xor: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 \"xnor	%2,%1,%0\";
  return \"xnor	%2,%1,%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_insn "cmptf"
  [(set (reg:CC 36)
	(compare:CC (match_operand:TF 0 "register_operand" "f")
		    (match_operand:TF 1 "nonmemory_operand" "fG")))]
  "TARGET_NUMERICS"
  "cmpr	%0,%1"
  [(set_attr "type" "fpcc")])

(define_expand "movtf"
  [(set (match_operand:TF 0 "general_operand" "")
	(match_operand:TF 1 "fpmove_src_operand" ""))]
  ""
  "
{
  if (emit_move_sequence (operands, TFmode))
    DONE;
}")

(define_insn ""
  [(set (match_operand:TF 0 "general_operand" "=r,*f,d,d,m,o")
	(match_operand:TF 1 "fpmove_src_operand" "r,GH,F,m,d,G"))]
  "(current_function_args_size == 0
    && rtx_equal_function_value_matters == 0)
   && (register_operand (operands[0], TFmode)
       || register_operand (operands[1], TFmode)
       || operands[1] == CONST0_RTX (TFmode))"
  "*
{
  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 \"ldq	%1,%0\";
    case 4:
      return \"stq	%1,%0\";
    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,fpload,fpstore,fpstore")])

(define_insn ""
  [(set (match_operand:TF 0 "general_operand" "=r,*f,d,d,m")
	(match_operand:TF 1 "fpmove_src_operand" "r,GH,F,m,d"))]
  "(current_function_args_size != 0
    || rtx_equal_function_value_matters != 0)
   && (register_operand (operands[0], TFmode)
       || register_operand (operands[1], TFmode))"
  "*
{
  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 \"ldq	%1,%0\";
    case 4:
      return \"stq	%1,%0\";
    }
}"
  [(set_attr "type" "move,move,load,fpload,fpstore")])

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

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

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

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

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

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

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

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

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

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

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

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

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

(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")))]
  ""
  "shli	%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 "" "")))]
  ""
  "bx	(%0)"
  [(set_attr "type" "branch")])

;;- jump to subroutine

(define_expand "call"
  [(call (match_operand:SI 0 "general_operand" "g")
	 (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 "general_operand" "g")
	 (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 "general_operand" "g")
	      (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 "general_operand" "g")
	      (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")])

(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)")
