;;- Machine description for AMD Am29000 for GNU C compiler
;;   Copyright (C) 1991, 1992, 1994 Free Software Foundation, Inc.
;;   Contributed by Richard Kenner (kenner@vlsi1.ultra.nyu.edu)

;; 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.

;; The insns in this file are presented in the same order as the AMD 29000
;; User's Manual (i.e., alphabetical by machine op-code).
;;
;; DEFINE_EXPAND's are located near the first occurrence of the major insn
;; that they generate.

;; The only attribute we have is the type.  We only care about calls, branches,
;; loads, stores, floating-point operations, and multi-word insns.
;; Everything else is miscellaneous.

(define_attr "type"
  "call,branch,load,store,fadd,fmul,fam,fdiv,fsqrt,dmul,dam,ddiv,dsqrt,multi,misc"
  (const_string "misc"))

;; ASM insns cannot go into a delay slot, so call them "multi".
(define_asm_attributes [(set_attr "type" "multi")])

(define_attr "in_delay_slot" "yes,no"
  (if_then_else (eq_attr "type" "call,branch,multi")  (const_string "no")
		(const_string "yes")))

;; Branch and call insns require a single delay slot.  Annulling is not
;; supported.
(define_delay (eq_attr "type" "call,branch")
  [(eq_attr "in_delay_slot" "yes") (nil) (nil)])

;; Define the function unit usages.  We first define memory as a unit.
(define_function_unit "memory" 1 0 (eq_attr "type" "load") 6 5
  [(eq_attr "type" "load")])
(define_function_unit "memory" 1 0 (eq_attr "type" "load") 6 6
  [(eq_attr "type" "store")])
(define_function_unit "memory" 1 0 (eq_attr "type" "store") 1 0)

;; Now define the function units for the floating-point support.  Most
;; units are pipelined and can accept an input every cycle.
;;
;; Note that we have an inaccuracy here.  If a fmac insn is issued, followed
;; 2 cycles later by a fadd, there will be a conflict for the floating
;; adder that we can't represent.  Also, all insns will conflict for the
;; floating-point rounder.  It isn't clear how to represent this.

(define_function_unit "multiplier" 1 0 (eq_attr "type" "fmul") 3 0)
(define_function_unit "multiplier" 1 0 (eq_attr "type" "dmul") 6 4)
(define_function_unit "multiplier" 1 0 (eq_attr "type" "fam") 6 0)
(define_function_unit "multiplier" 1 0 (eq_attr "type" "dam") 9 4)

(define_function_unit "adder" 1 0 (eq_attr "type" "fadd,fam,dam") 3 0)

(define_function_unit "divider" 1 0 (eq_attr "type" "fdiv") 11 10)
(define_function_unit "divider" 1 0 (eq_attr "type" "fsqrt") 28 27)
(define_function_unit "divider" 1 0 (eq_attr "type" "ddiv") 18 17)
(define_function_unit "divider" 1 0 (eq_attr "type" "dsqrt") 57 56)

;; ADD
(define_insn "addsi3"
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r,r")
	(plus:SI (match_operand:SI 1 "gpc_reg_operand" "%r,r")
		 (match_operand:SI 2 "add_operand" "rI,N")))]
  ""
  "@
   add %0,%1,%2
   sub %0,%1,%n2")

(define_insn "adddi3"
  [(set (match_operand:DI 0 "gpc_reg_operand" "=r")
	(plus:DI (match_operand:DI 1 "gpc_reg_operand" "%r")
		 (match_operand:DI 2 "gpc_reg_operand" "r")))]
  ""
  "add %L0,%L1,%L2\;addc %0,%1,%2"
  [(set_attr "type" "multi")])

;; AND/ANDN
(define_insn "andsi3"
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r,r")
	(and:SI (match_operand:SI 1 "gpc_reg_operand" "%r,r")
		(match_operand:SI 2 "and_operand" "rI,K")))]
  ""
  "@
   and %0,%1,%2
   andn %0,%1,%C2")

(define_insn ""
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r,r")
	(and:SI (not:SI (match_operand:SI 1 "gpc_reg_operand" "r,r"))
		(match_operand:SI 2 "cmplsrcb_operand" "r,K")))]
  ""
  "@
   andn %0,%2,%1
   nor %0,%1,%C2")

;; CALLI
;;
;; Each call pattern is duplicated so that we can add CLOBBERs to the
;; resulting insn.
;;
;; We indicate that LR0 is clobbered in the CALL_INSN itself.  Otherwise,
;; reorg will think it is just clobbered by the called function.

(define_expand "call"
  [(use (match_operand:SI 0 "" ""))
   (use (match_operand 1 "" ""))
   (use (match_operand 2 "" ""))]
  ""
  "
{ rtx insn = emit_call_insn (gen_call_internal (operands[0], operands[1]));
  a29k_clobbers_to (insn, operands[2]);

  DONE;
}")
 
(define_expand "call_internal"
  [(parallel [(call (match_operand:SI 0 "" "")
		    (match_operand 1 "" ""))
	      (clobber (scratch:SI))])]
  ""
  "
{
  if (GET_CODE (operands[0]) != MEM)
    abort ();

  /* We tell here whether this is a recursive call, since this insn may
     later be inlined into another function.  */
  if (! TARGET_SMALL_MEMORY
      && GET_CODE (XEXP (operands[0], 0)) == SYMBOL_REF)
    operands[0] = gen_rtx (MEM, SImode,
			   force_reg (Pmode, XEXP (operands[0], 0)));
}")
 
(define_expand "call_value"
  [(use (match_operand:SI 0 "gpc_reg_operand" ""))
   (use (match_operand:SI 1 "" ""))
   (use (match_operand 2 "" ""))
   (use (match_operand 3 "" ""))]
  ""
  "
{ rtx insn = emit_call_insn (gen_call_value_internal (operands[0], operands[1],
						      operands[2]));

  a29k_clobbers_to (insn, operands[3]);
  DONE;
}")
 
(define_expand "call_value_internal"
  [(parallel [(set (match_operand:SI 0 "gpc_reg_operand" "")
		   (call (match_operand:SI 1 "" "")
			 (match_operand 2 "" "")))
		   (clobber (scratch:SI))])]
  ""
  "
{
  if (GET_CODE (operands[1]) != MEM)
    abort ();

  /* We tell here whether this is a recursive call, since this insn may
     later be inlined into another function.  */
  if (! TARGET_SMALL_MEMORY
      && GET_CODE (XEXP (operands[1], 0)) == SYMBOL_REF)
    operands[1] = gen_rtx (MEM, SImode,
			   force_reg (Pmode, XEXP (operands[1], 0)));

}")
 
(define_insn ""
  [(call (match_operand:SI 0 "memory_operand" "m")
	 (match_operand 1 "" ""))
   (clobber (match_scratch:SI 2 "=&l"))]
  "GET_CODE (XEXP (operands[0], 0)) != CONST_INT"
  "calli lr0,%0%#"
  [(set_attr "type" "call")])

(define_insn ""
  [(call (mem:SI (match_operand:SI 0 "call_operand" "i"))
	 (match_operand:SI 1 "general_operand" "g"))
   (clobber (match_scratch:SI 2 "=&l"))]
  ""
  "call lr0,%F0"
  [(set_attr "type" "call")])

(define_insn ""
  [(set (match_operand 0 "gpc_reg_operand" "=r")
	(call (match_operand:SI 1 "memory_operand" "m")
	      (match_operand 2 "" "")))
   (clobber (match_scratch:SI 3 "=&l"))]
  "GET_CODE (XEXP (operands[1], 0)) != CONST_INT"
  "calli lr0,%1%#"
  [(set_attr "type" "call")])

(define_insn ""
  [(set (match_operand 0 "gpc_reg_operand" "=r")
	(call (mem:SI (match_operand:SI 1 "call_operand" "i"))
	      (match_operand:SI 2 "general_operand" "g")))
   (clobber (match_scratch:SI 3 "=&l"))]
  ""
  "call lr0,%F1"
  [(set_attr "type" "call")])

(define_expand "probe"
  [(call (mem:SI (symbol_ref:SI "_msp_check"))
	 (const_int 1))]
  "TARGET_STACK_CHECK"
  "")

;; This is used for internal routine calls via TPC.  Currently used only
;; in probe, above.
(define_insn ""
  [(call (mem:SI (match_operand:SI 0 "immediate_operand" "s"))
	 (const_int 1))]
  ""
  "call %*,%0"
  [(set_attr "type" "call")])

;; CONST, CONSTH, CONSTN
;;
;; Many of these are generated from move insns.
(define_insn ""
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
	(and:SI (match_operand:SI 1 "immediate_operand" "i")
		(const_int 65535)))]
  ""
  "const %0,%1")

(define_insn ""
  [(set (zero_extract:SI (match_operand:SI 0 "gpc_reg_operand" "+r")
			 (const_int 16)
			 (match_operand:SI 1 "const_0_operand" ""))
	(ashiftrt:SI (match_operand:SI 2 "immediate_operand" "i")
		     (const_int 16)))]
  ""
  "consth %0,%2")

(define_insn ""
  [(set (zero_extract:SI (match_operand:SI 0 "gpc_reg_operand" "+r")
			 (const_int 16)
			 (match_operand:SI 1 "const_0_operand" ""))
	(match_operand:SI 2 "cint_16_operand" "J"))]
  ""
  "consth %0,%m2")

(define_insn ""
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
	(ior:SI (zero_extend:SI (match_operand:HI 1 "gpc_reg_operand" "0"))
		(match_operand:SI 2 "const_int_operand" "n")))]
  "(INTVAL (operands[2]) & 0xffff) == 0"
  "consth %0,%2")

(define_insn ""
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
	(ior:SI (zero_extend:SI (match_operand:HI 1 "gpc_reg_operand" "0"))
		(and:SI (match_operand:SI 2 "immediate_operand" "i")
			(const_int -65536))))]
  ""
  "consth %0,%2")

;; CONVERT
(define_insn "fix_truncsfsi2"
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
	(fix:SI (match_operand:SF 1 "register_operand" "r")))]
  "! TARGET_SOFT_FLOAT"
  "convert %0,%1,0,3,0,1")

(define_insn "fix_truncdfsi2"
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
	(fix:SI (match_operand:DF 1 "register_operand" "r")))]
  "! TARGET_SOFT_FLOAT"
  "convert %0,%1,0,3,0,2")

(define_insn "fixuns_truncsfsi2"
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
	(unsigned_fix:SI (match_operand:SF 1 "register_operand" "r")))]
  "! TARGET_SOFT_FLOAT"
  "convert %0,%1,1,3,0,1")

(define_insn "fixuns_truncdfsi2"
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
	(unsigned_fix:SI (match_operand:DF 1 "register_operand" "r")))]
  "! TARGET_SOFT_FLOAT"
  "convert %0,%1,1,3,0,2")

(define_insn "truncdfsf2"
  [(set (match_operand:SF 0 "register_operand" "=r")
	(float_truncate:SF (match_operand:DF 1 "register_operand" "r")))]
  "! TARGET_SOFT_FLOAT"
  "convert %0,%1,0,4,1,2")

(define_insn "extendsfdf2"
  [(set (match_operand:DF 0 "register_operand" "=r")
	(float_extend:DF (match_operand:SF 1 "register_operand" "r")))]
  "! TARGET_SOFT_FLOAT"
  "convert %0,%1,0,4,2,1")

(define_insn "floatsisf2"
  [(set (match_operand:SF 0 "register_operand" "=r")
	(float:SF (match_operand:SI 1 "gpc_reg_operand" "r")))]
  "! TARGET_SOFT_FLOAT"
  "convert %0,%1,0,4,1,0")

(define_insn "floatsidf2"
  [(set (match_operand:DF 0 "register_operand" "=r")
	(float:DF (match_operand:SI 1 "gpc_reg_operand" "r")))]
  "! TARGET_SOFT_FLOAT"
  "convert %0,%1,0,4,2,0")

(define_insn "floatunssisf2"
  [(set (match_operand:SF 0 "register_operand" "=r")
	(unsigned_float:SF (match_operand:SI 1 "gpc_reg_operand" "r")))]
  "! TARGET_SOFT_FLOAT"
  "convert %0,%1,1,4,1,0")

(define_insn "floatunssidf2"
  [(set (match_operand:DF 0 "register_operand" "=r")
	(unsigned_float:DF (match_operand:SI 1 "gpc_reg_operand" "r")))]
  "! TARGET_SOFT_FLOAT"
  "convert %0,%1,1,4,2,0")

;; CPxxx, DEQ, DGT, DGE, FEQ, FGT, FGE
(define_insn ""
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
	(match_operator 3 "comparison_operator"
			[(match_operand:SI 1 "gpc_reg_operand" "r")
			 (match_operand:SI 2 "srcb_operand" "rI")]))]
  ""
  "cp%J3 %0,%1,%2")

(define_insn ""
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
	(match_operator 3 "fp_comparison_operator"
			[(match_operand:SF 1 "register_operand" "r")
			 (match_operand:SF 2 "register_operand" "r")]))]
  "! TARGET_SOFT_FLOAT"
  "f%J3 %0,%1,%2"
  [(set_attr "type" "fadd")])

(define_insn ""
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
	(match_operator 3 "fp_comparison_operator"
			[(match_operand:DF 1 "register_operand" "r")
			 (match_operand:DF 2 "register_operand" "r")]))]
  "! TARGET_SOFT_FLOAT"
  "d%J3 %0,%1,%2"
  [(set_attr "type" "fadd")])

;; DADD
(define_expand "adddf3"
  [(set (match_operand:DF 0 "register_operand" "")
	(plus:DF (match_operand:DF 1 "register_operand" "")
		 (match_operand:DF 2 "register_operand" "")))]
  "! TARGET_SOFT_FLOAT"
  "")

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=r")
	(plus:DF (match_operand:DF 1 "register_operand" "%r")
		 (match_operand:DF 2 "register_operand" "r")))]
  "! TARGET_29050 "
  "dadd %0,%1,%2"
  [(set_attr "type" "fadd")])

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=r,a")
	(plus:DF (match_operand:DF 1 "register_operand" "%r,r")
		 (match_operand:DF 2 "register_operand" "r,0")))]
  "TARGET_29050"
  "@
   dadd %0,%1,%2
   dmac 8,%0,%1,%1"
  [(set_attr "type" "fadd,dam")])

;; DDIV
(define_insn "divdf3"
  [(set (match_operand:DF 0 "register_operand" "=r")
	(div:DF (match_operand:DF 1 "register_operand" "=r")
		(match_operand:DF 2 "register_operand" "r")))]
  "! TARGET_SOFT_FLOAT"
  "ddiv %0,%1,%2"
  [(set_attr "type" "ddiv")])

;; DIVIDE
;;
;; We must set Q to the sign extension of the dividend first.  For MOD, we
;; must get the remainder from Q.
;;
;; For divmod: operand 1 is divided by operand 2; quotient goes to operand
;; 0 and remainder to operand 3.
(define_expand "divmodsi4"
  [(set (match_dup 4)
	(ashiftrt:SI (match_operand:SI 1 "gpc_reg_operand" "")
		     (const_int 31)))
   (parallel [(set (match_operand:SI 0 "gpc_reg_operand" "")
		   (div:SI (match_dup 1)
			   (match_operand:SI 2 "gpc_reg_operand" "")))
	      (set (match_operand:SI 3 "gpc_reg_operand" "")
		   (mod:SI (match_dup 1)
			   (match_dup 2)))
	      (use (match_dup 4))])]
  ""
  "
{
  operands[4] = gen_reg_rtx (SImode);
}")

(define_insn ""
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
	(div:SI (match_operand:SI 1 "gpc_reg_operand" "r")
		(match_operand:SI 2 "gpc_reg_operand" "r")))
   (set (match_operand:SI 3 "register_operand" "=q")
	(mod:SI (match_dup 1)
		(match_dup 2)))
   (use (match_operand:SI 4 "register_operand" "3"))]
  ""
  "divide %0,%1,%2")

;; DIVIDU
;;
;; Similar to DIVIDE.
(define_expand "udivmodsi4"
  [(parallel [(set (match_operand:SI 0 "gpc_reg_operand" "")
		   (udiv:SI (match_operand:SI 1 "gpc_reg_operand" "")
			    (match_operand:SI 2 "gpc_reg_operand" "")))
	      (set (match_operand:SI 3 "gpc_reg_operand" "")
		   (umod:SI (match_dup 1)
			    (match_dup 2)))
	      (use (const_int 0))])]
  ""
  "")

(define_insn ""
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
	(udiv:SI (match_operand:SI 1 "gpc_reg_operand" "r")
		 (match_operand:SI 2 "gpc_reg_operand" "r")))
   (set (match_operand:SI 3 "register_operand" "=q")
	(umod:SI (match_dup 1)
		 (match_dup 2)))
   (use (match_operand:SI 4 "const_int_operand" "3"))]
  ""
  "dividu %0,%1,%2")

;; DMAC/DMSM
(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=a,*r")
	(plus:DF (mult:DF (match_operand:DF 1 "register_operand" "%r,A")
			  (match_operand:DF 2 "register_operand" "r,r"))
		 (match_operand:DF 3 "register_operand" "0,*r")))]
  "TARGET_29050"
  "@
   dmac 0,%0,%1,%2
   dmsm %0,%2,%3"
  [(set_attr "type" "dam")])

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=a")
	(plus:DF (mult:DF (neg:DF (match_operand:DF 1 "register_operand" "r"))
			  (match_operand:DF 2 "register_operand" "r"))
		 (match_operand:DF 3 "register_operand" "0")))]
  "TARGET_29050"
  "dmac 1,%0,%2,%1"
  [(set_attr "type" "dam")])

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=a")
	(minus:DF (mult:DF (match_operand:DF 1 "register_operand" "%r")
			   (match_operand:DF 2 "register_operand" "r"))
		  (match_operand:DF 3 "register_operand" "0")))]
  "TARGET_29050"
  "dmac 2,%0,%1,%2"
  [(set_attr "type" "dam")])

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=a")
	(minus:DF (mult:DF (match_operand:DF 1 "register_operand" "r")
			   (neg:DF (match_operand:DF 2 "register_operand" "r")))
		  (match_operand:DF 3 "register_operand" "0")))]
  "TARGET_29050"
  "dmac 3,%0,%1,%2"
  [(set_attr "type" "dam")])

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=a")
	(mult:DF (neg:DF (match_operand:DF 1 "register_operand" "r"))
		 (match_operand:DF 2 "register_operand" "r")))]
  "TARGET_29050"
  "dmac 5,%0,%2,%1"
  [(set_attr "type" "dam")])

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=a")
	(minus:DF (neg:DF (match_operand:DF 1 "register_operand" "r"))
		  (match_operand:DF 2 "register_operand" "0")))]
  "TARGET_29050"
  "dmac 11,%0,%1,%1"
  [(set_attr "type" "dam")])

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=a")
	(neg:DF (plus:DF (match_operand:DF 1 "register_operand" "%r")
			 (match_operand:DF 2 "register_operand" "0"))))]
  "TARGET_29050"
  "dmac 11,%0,%1,%1"
  [(set_attr "type" "dam")])

(define_insn ""
 [(set (match_operand:DF 0 "register_operand" "=r,r,a")
       (neg:DF (match_operand:DF 1 "register_operand" "0,r,r")))
  (clobber (match_scratch:SI 2 "=&r,&r,X"))]
 "TARGET_29050"
 "@
  cpeq %2,gr1,gr1\;xor %0,%1,%2
  cpeq %2,gr1,gr1\;xor %0,%1,%2\;sll %L0,%L1,0
  dmac 13,%0,%1,%1"
 [(set_attr "type" "multi,multi,dam")])

;; DMUL
(define_expand "muldf3"
  [(set (match_operand:DF 0 "register_operand" "")
	(mult:DF (match_operand:DF 1 "register_operand" "")
		 (match_operand:DF 2 "register_operand" "")))]
  "! TARGET_SOFT_FLOAT"
  "")

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=r")
	(mult:DF (match_operand:DF 1 "register_operand" "%r")
		 (match_operand:DF 2 "register_operand" "r")))]
  "! TARGET_29050"
  "dmul %0,%1,%2"
  [(set_attr "type" "dmul")])

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=r,a")
	(mult:DF (match_operand:DF 1 "register_operand" "%r,r")
		 (match_operand:DF 2 "register_operand" "r,r")))]
  "TARGET_29050"
  "@
   dmul %0,%1,%2
   dmac 4,%0,%1,%2"
  [(set_attr "type" "dmul,dam")])

;; DSUB
(define_expand "subdf3"
  [(set (match_operand:DF 0 "register_operand" "=r")
	(minus:DF (match_operand:DF 1 "register_operand" "r")
		  (match_operand:DF 2 "register_operand" "r")))]
  "! TARGET_SOFT_FLOAT"
  "")

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=r")
	(minus:DF (match_operand:DF 1 "register_operand" "r")
		  (match_operand:DF 2 "register_operand" "r")))]
  "! TARGET_29050"
  "dsub %0,%1,%2"
  [(set_attr "type" "fadd")])

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=r,a,a")
	(minus:DF (match_operand:DF 1 "register_operand" "r,0,r")
		  (match_operand:DF 2 "register_operand" "r,r,0")))]
  "TARGET_29050"
  "@
   dsub %0,%1,%2
   dmac 9,%0,%2,%2
   dmac 10,%0,%1,%1"
  [(set_attr "type" "fadd,dam,dam")])

;; EXBYTE
(define_insn ""
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
	(ior:SI (and:SI (match_operand:SI 1 "srcb_operand" "rI")
			(const_int -256))
		(zero_extract:SI (match_operand:SI 2 "gpc_reg_operand" "r")
				 (const_int 8)
				 (ashift:PSI
				  (match_operand:PSI 3 "register_operand" "b")
				  (const_int 3)))))]
  ""
  "exbyte %0,%2,%1")

(define_insn ""
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
	(zero_extract:SI (match_operand:SI 1 "gpc_reg_operand" "r")
			 (const_int 8)
			 (ashift:PSI
			  (match_operand:PSI 2 "register_operand" "b")
			  (const_int 3))))]
  ""
  "exbyte %0,%1,0")

(define_insn ""
  [(set (zero_extract:SI (match_operand:SI 0 "gpc_reg_operand" "+r")
			 (const_int 8)
			 (match_operand:PSI 1 "const_24_operand" ""))
	(zero_extract:SI (match_operand:SI 2 "gpc_reg_operand" "r")
			 (const_int 8)
			 (ashift:PSI
			  (match_operand:PSI 3 "register_operand" "b")
			  (const_int 3))))]
  ""
  "exbyte %0,%2,%0")

(define_expand "extzv"
  [(set (match_operand:SI 0 "gpc_reg_operand" "")
	(zero_extract:SI (match_operand:SI 1 "gpc_reg_operand" "")
			 (match_operand:SI 2 "general_operand" "")
			 (match_operand:SI 3 "general_operand" "")))]
  ""
  "
{
  int size, pos;

  if (GET_CODE (operands[2]) != CONST_INT
      || GET_CODE (operands[3]) != CONST_INT)
    FAIL;

  size = INTVAL (operands[2]);
  pos = INTVAL (operands[3]);

  /* Can't do this unless a byte extraction.  If extracting the high
     or low byte, don't do this because a shift or AND is shorter.
     Don't do 16-bit extracts, since the only two are the high and low
     ends, and it is faster to do them with CONSTH and SRL.  */

  if (size != 8 || (pos != 8 && pos != 16))
    FAIL;

  operands[3] = gen_rtx (ASHIFT, PSImode,
			 force_reg (PSImode, GEN_INT (pos / 8)),
			 GEN_INT (3));

}")

;; EXHW
(define_insn ""
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
	(ior:SI (and:SI (match_operand:SI 1 "srcb_operand" "rI")
		(const_int -65536))
		(zero_extract:SI (match_operand:SI 2 "gpc_reg_operand" "r")
				 (const_int 16)
				 (ashift:PSI
				  (match_operand:PSI 3 "register_operand" "b")
				  (const_int 3)))))]
  ""
  "exhw %0,%2,%1")

(define_insn ""
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
	(zero_extract:SI (match_operand:SI 1 "gpc_reg_operand" "r")
			 (const_int 16)
			 (ashift:PSI
			  (match_operand:PSI 2 "register_operand" "b")
			  (const_int 3))))]
  ""
  "exhw %0,%1,0")

(define_insn ""
  [(set (zero_extract:SI (match_operand:SI 0 "gpc_reg_operand" "+r")
			 (const_int 16)
			 (match_operand:PSI 1 "const_16_operand" ""))
	(zero_extract:SI (match_operand:SI 2 "gpc_reg_operand" "r")
			 (const_int 16)
			 (ashift:PSI
			  (match_operand:PSI 3 "register_operand" "b")
			  (const_int 3))))]
  ""
  "exhw %0,%2,%0")

;; EXHWS
;;
;; This is probably unused.  The high-order 16-bits are obtained with an SRA
;; insn.  The low-order 16 bits are a sign-extend, which is a pair of
;; shifts.  Setting BP followed by the insn is equivalent, so we don't
;; bother going to any trouble to generate this insn.

(define_insn ""
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
	(sign_extract:SI (match_operand:SI 1 "gpc_reg_operand" "r")
			 (const_int 16)
			 (ashift:PSI
			  (match_operand:PSI 2 "register_operand" "b")
			  (const_int 3))))]
  ""
  "exhws %0,%1")

;; EXTRACT
(define_insn ""
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
	(rotate:SI (match_operand:SI 1 "gpc_reg_operand" "r")
		   (match_operand:PSI 2 "register_operand" "f")))]
  ""
  "extract %0,%1,%1")

(define_expand "rotlsi3"
  [(set (match_dup 3)
	(match_operand:SI 2 "gpc_reg_or_immediate_operand" ""))
   (set (match_operand:SI 0 "gpc_reg_operand" "")
	(rotate:SI (match_operand:SI 1 "gpc_reg_operand" "")
		   (match_dup 3)))]
  ""
  "
{ operands[2] = gen_lowpart (PSImode, operands[2]);
  operands[3] = gen_reg_rtx (PSImode);
}")

;; It would be nice to be able to have a define_split corresponding to the
;; above, but there is no way to tell combine we need a PSImode temporary.
;; If we put a (clobber (scratch:PSI)) there, combine would merge the above
;; two insns.  This is bad because it then thinks only one insn is needed.

;; FADD
(define_expand "addsf3"
  [(set (match_operand:SF 0 "register_operand" "")
	(plus:SF (match_operand:SF 1 "register_operand" "")
		 (match_operand:SF 2 "register_operand" "")))]
  "! TARGET_SOFT_FLOAT"
  "")

(define_insn ""
  [(set (match_operand:SF 0 "register_operand" "=r")
	(plus:SF (match_operand:SF 1 "register_operand" "%r")
		 (match_operand:SF 2 "register_operand" "r")))]
  "! TARGET_29050"
  "fadd %0,%1,%2"
  [(set_attr "type" "fadd")])

(define_insn ""
  [(set (match_operand:SF 0 "register_operand" "=r,a")
	(plus:SF (match_operand:SF 1 "register_operand" "%r,r")
		 (match_operand:SF 2 "register_operand" "r,0")))]
  "TARGET_29050"
  "@
   fadd %0,%1,%2
   fmac 8,%0,%1,%1"
  [(set_attr "type" "fadd,fam")])

;; FDIV
(define_insn "divsf3"
  [(set (match_operand:SF 0 "register_operand" "=r")
	(div:SF (match_operand:SF 1 "register_operand" "=r")
		(match_operand:SF 2 "register_operand" "r")))]
  "! TARGET_SOFT_FLOAT"
  "fdiv %0,%1,%2"
  [(set_attr "type" "fdiv")])

;; FDMUL
(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=r")
	(mult:DF (float_extend:DF (match_operand:SF 1 "register_operand" "%r"))
		 (float_extend:DF (match_operand:SF 2 "register_operand" "r"))))]
  "! TARGET_SOFT_FLOAT"
  "fdmul %0,%1,%2")

;; FMAC/FMSM
(define_insn ""
  [(set (match_operand:SF 0 "register_operand" "=a,*r")
	(plus:SF (mult:SF (match_operand:SF 1 "register_operand" "%r,A")
			  (match_operand:SF 2 "register_operand" "r,r"))
		 (match_operand:SF 3 "register_operand" "0,*r")))]
  "TARGET_29050"
  "@
   fmac 0,%0,%1,%2
   fmsm %0,%2,%3"
  [(set_attr "type" "fam")])

(define_insn ""
  [(set (match_operand:SF 0 "register_operand" "=a")
	(plus:SF (mult:SF (neg:SF (match_operand:SF 1 "register_operand" "r"))
			  (match_operand:SF 2 "register_operand" "r"))
		 (match_operand:SF 3 "register_operand" "0")))]
  "TARGET_29050"
  "fmac 1,%0,%2,%1"
  [(set_attr "type" "fam")])

(define_insn ""
  [(set (match_operand:SF 0 "register_operand" "=a")
	(minus:SF (mult:SF (match_operand:SF 1 "register_operand" "%r")
			   (match_operand:SF 2 "register_operand" "r"))
		  (match_operand:SF 3 "register_operand" "0")))]
  "TARGET_29050"
  "fmac 2,%0,%1,%2"
  [(set_attr "type" "fam")])

(define_insn ""
  [(set (match_operand:SF 0 "register_operand" "=a")
	(minus:SF (mult:SF (neg:SF (match_operand:SF 1 "register_operand" "r"))
			   (match_operand:SF 2 "register_operand" "r"))
		  (match_operand:SF 3 "register_operand" "0")))]
  "TARGET_29050"
  "fmac 3,%0,%2,%1"
  [(set_attr "type" "fam")])

(define_insn ""
  [(set (match_operand:SF 0 "register_operand" "=a")
	(mult:SF (neg:SF (match_operand:SF 1 "register_operand" "r"))
		 (match_operand:SF 2 "register_operand" "r")))]
  "TARGET_29050"
  "fmac 5,%0,%2,%1"
  [(set_attr "type" "fam")])

(define_insn ""
  [(set (match_operand:SF 0 "register_operand" "=a")
	(minus:SF (neg:SF (match_operand:SF 1 "register_operand" "%r"))
		  (match_operand:SF 2 "register_operand" "0")))]
  "TARGET_29050"
  "fmac 11,%0,%1,%1"
  [(set_attr "type" "fam")])

(define_insn ""
  [(set (match_operand:SF 0 "register_operand" "=a")
	(neg:SF (plus:SF (match_operand:SF 1 "register_operand" "%r")
			 (match_operand:SF 2 "register_operand" "0"))))]
  "TARGET_29050"
  "fmac 11,%0,%1,%1"
  [(set_attr "type" "fam")])

(define_insn ""
  [(set (match_operand:SF 0 "register_operand" "=r,a")
	(neg:SF (match_operand:SF 1 "register_operand" "r,r")))
   (clobber (match_scratch:SI 2 "=&r,X"))]
  "TARGET_29050"
  "@
   cpeq %2,gr1,gr1\;xor %0,%1,%2
   fmac 13,%0,%1,%1"
  [(set_attr "type" "multi,fam")])

;; FMUL
(define_expand "mulsf3"
  [(set (match_operand:SF 0 "register_operand" "")
	(mult:SF (match_operand:SF 1 "register_operand" "")
		 (match_operand:SF 2 "register_operand" "")))]
  "! TARGET_SOFT_FLOAT"
  "")

(define_insn ""
  [(set (match_operand:SF 0 "register_operand" "=r")
	(mult:SF (match_operand:SF 1 "register_operand" "%r")
		 (match_operand:SF 2 "register_operand" "r")))]
  "! TARGET_29050"
  "fmul %0,%1,%2"
  [(set_attr "type" "fmul")])

(define_insn ""
  [(set (match_operand:SF 0 "register_operand" "=r,a")
	(mult:SF (match_operand:SF 1 "register_operand" "%r,r")
		 (match_operand:SF 2 "register_operand" "r,r")))]
  "TARGET_29050"
  "@
   fmul %0,%1,%2
   fmac 4,%0,%1,%2"
  [(set_attr "type" "fmul,fam")])

;; FSUB
(define_expand "subsf3"
  [(set (match_operand:SF 0 "register_operand" "")
	(minus:SF (match_operand:SF 1 "register_operand" "")
		  (match_operand:SF 2 "register_operand" "")))]
  "! TARGET_SOFT_FLOAT"
  "")

(define_insn ""
  [(set (match_operand:SF 0 "register_operand" "=r")
	(minus:SF (match_operand:SF 1 "register_operand" "r")
		  (match_operand:SF 2 "register_operand" "r")))]
  "! TARGET_29050"
  "fsub %0,%1,%2"
  [(set_attr "type" "fadd")])

(define_insn ""
  [(set (match_operand:SF 0 "register_operand" "=r,a,a")
	(minus:SF (match_operand:SF 1 "register_operand" "r,0,r")
		  (match_operand:SF 2 "register_operand" "r,r,0")))]
  "TARGET_29050"
  "@
   fsub %0,%1,%2
   fmac 9,%0,%2,%2
   fmac 10,%0,%1,%1"
  [(set_attr "type" "fadd,fam,fam")])

;; INBYTE
(define_insn ""
  [(set (zero_extract:SI (match_operand:SI 0 "gpc_reg_operand" "+r")
			 (const_int 8)
			 (ashift:PSI
			  (match_operand:PSI 2 "register_operand" "b")
			  (const_int 3)))
	(match_operand:SI 1 "srcb_operand" "rI"))]
  ""
  "inbyte %0,%0,%1")

(define_insn ""
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
	(ior:SI (and:SI
		 (not:SI
		  (ashift:SI (const_int 255)
			     (ashift:PSI
			      (match_operand:PSI 3 "register_operand" "b")
			      (const_int 3))))
		 (match_operand:SI 1 "gpc_reg_operand" "r"))
		(ashift:SI (zero_extend:SI
			    (match_operand:QI 2 "srcb_operand" "rI"))
			   (ashift:PSI (match_dup 3) (const_int 3)))))]
  ""
  "inbyte %0,%1,%2")

;; INHW
(define_insn ""
  [(set (zero_extract:SI (match_operand:SI 0 "gpc_reg_operand" "+r")
			 (const_int 16)
			 (ashift:PSI
			  (match_operand:PSI 2 "register_operand" "b")
			  (const_int 3)))
	(match_operand:SI 1 "srcb_operand" "rI"))]
  ""
  "inhw %0,%0,%1")

(define_insn ""
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
	(ior:SI (and:SI
		 (not:SI
		  (ashift:SI (const_int 65535)
			     (ashift:PSI
			      (match_operand:PSI 3 "register_operand" "b")
			      (const_int 3))))
		 (match_operand:SI 1 "gpc_reg_operand" "r"))
		(ashift:SI (zero_extend:SI
			    (match_operand:HI 2 "srcb_operand" "rI"))
			   (ashift:PSI (match_dup 3) (const_int 3)))))]
  ""
  "inhw %0,%1,%2")

(define_expand "insv"
  [(set (zero_extract:SI (match_operand:SI 0 "gpc_reg_operand" "")
			 (match_operand:SI 1 "general_operand" "")
			 (match_operand:SI 2 "general_operand" ""))
	(match_operand:SI 3 "srcb_operand" ""))]
  ""
  "
{
  int size, pos;

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

  size = INTVAL (operands[1]);
  pos = INTVAL (operands[2]);
  if ((size != 8 && size != 16) || pos % size != 0)
    FAIL;

  operands[2] = gen_rtx (ASHIFT, PSImode,
			 force_reg (PSImode, GEN_INT (pos / 8)),
			 GEN_INT (3));
}")

;; LOAD (also used by move insn).
(define_insn ""
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
	(mem:SI (and:SI (match_operand:SI 1 "gpc_reg_operand" "r")
			(const_int -4))))
   (set (match_operand:PSI 2 "register_operand" "=b")
	(truncate:PSI (match_dup 1)))]
  "! TARGET_DW_ENABLE"
  "load 0,16,%0,%1"
  [(set_attr "type" "load")])

(define_insn ""
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
	(zero_extend:SI (match_operand:QI 1 "memory_operand" "m")))]
  "TARGET_DW_ENABLE"
  "load 0,1,%0,%1"
  [(set_attr "type" "load")])

(define_insn ""
  [(set (match_operand:HI 0 "gpc_reg_operand" "=r")
	(zero_extend:HI (match_operand:QI 1 "memory_operand" "m")))]
  "TARGET_DW_ENABLE"
  "load 0,1,%0,%1"
  [(set_attr "type" "load")])

(define_insn ""
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
	(zero_extend:SI (match_operand:HI 1 "memory_operand" "m")))]
  "TARGET_DW_ENABLE"
  "load 0,2,%0,%1"
  [(set_attr "type" "load")])

(define_insn ""
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
	(sign_extend:SI (match_operand:QI 1 "memory_operand" "m")))
   (clobber (match_scratch:PSI 2 "=&b"))]
  "TARGET_DW_ENABLE"
  "load 0,17,%0,%1"
  [(set_attr "type" "load")])

(define_insn ""
  [(set (match_operand:HI 0 "gpc_reg_operand" "=r")
	(sign_extend:HI (match_operand:QI 1 "memory_operand" "m")))
   (clobber (match_scratch:PSI 2 "=&b"))]
  "TARGET_DW_ENABLE"
  "load 0,17,%0,%1"
  [(set_attr "type" "load")])

(define_insn ""
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
	(sign_extend:SI (match_operand:HI 1 "memory_operand" "m")))
   (clobber (match_scratch:PSI 2 "=&b"))]
  "TARGET_DW_ENABLE"
  "load 0,18,%0,%1"
  [(set_attr "type" "load")])

;; LOADM
(define_expand "load_multiple"
  [(set (match_dup 4)
	(match_operand:PSI 2 "const_int_operand" ""))
   (match_par_dup 3 [(set (match_operand:SI 0 "" "")
			  (match_operand:SI 1 "" ""))])]
  ""
  "
{
  int regno;
  int count;
  rtx from;
  int i;

  /* Support only loading a constant number of hard registers from memory.  */
  if (GET_CODE (operands[2]) != CONST_INT
      || operands[2] == const1_rtx
      || GET_CODE (operands[1]) != MEM
      || GET_CODE (operands[0]) != REG
      || REGNO (operands[0]) >= FIRST_PSEUDO_REGISTER)
    FAIL;

  count = INTVAL (operands[2]);
  regno = REGNO (operands[0]);

  /* CR gets set to the number of registers minus one.  */
  operands[2] = GEN_INT(count - 1);

  operands[3] = gen_rtx (PARALLEL, VOIDmode, rtvec_alloc (count + 2));
  from = memory_address (SImode, XEXP (operands[1], 0));
  XVECEXP (operands[3], 0, 0) = gen_rtx (SET, VOIDmode,
					 gen_rtx (REG, SImode, regno),
					 gen_rtx (MEM, SImode, from));
  operands[4] = gen_reg_rtx (PSImode);

  XVECEXP (operands[3], 0, 1) = gen_rtx (USE, VOIDmode, operands[4]);
  XVECEXP (operands[3], 0, 2) = gen_rtx (CLOBBER, VOIDmode, operands[4]);

  for (i = 1; i < count; i++)
    XVECEXP (operands[3], 0, i + 2)
      = gen_rtx (SET, VOIDmode, gen_rtx (REG, SImode, regno + i),
		 gen_rtx (MEM, SImode, plus_constant (from, i * 4)));
}")

;; Indicate that CR is used and is then clobbered.
(define_insn ""
  [(set (match_operand 0 "gpc_reg_operand" "=r")
	(match_operand 1 "memory_operand" "m"))
   (use (match_operand:PSI 2 "register_operand" "+c"))
   (clobber (match_dup 2))]
  "GET_MODE (operands[0]) == GET_MODE (operands[1])
   && GET_MODE_SIZE (GET_MODE (operands[0])) > UNITS_PER_WORD"
  "loadm 0,0,%0,%1"
  [(set_attr "type" "load")])

(define_insn ""
  [(match_parallel 0 "load_multiple_operation"
		   [(set (match_operand:SI 1 "gpc_reg_operand" "=r")
			 (match_operand:SI 2 "memory_operand" "m"))
		    (use (match_operand:PSI 3 "register_operand" "+c"))
		    (clobber (match_dup 3))])]
  ""
  "loadm 0,0,%1,%2"
  [(set_attr "type" "load")])

;; MTSR (used also by move insn)
(define_insn ""
  [(set (match_operand:SI 0 "spec_reg_operand" "=*h,*h")
	(and:SI (match_operand:SI 1 "gpc_reg_or_immediate_operand" "r,i")
		(match_operand:SI 2 "const_int_operand" "n,n")))]
  "masks_bits_for_special (operands[0], operands[2])"
  "@
   mtsr %0,%1
   mtsrim %0,%1")

(define_insn ""
  [(set (match_operand:PSI 0 "register_operand" "=h,h")
	(truncate:PSI
	 (match_operand:SI 1 "gpc_reg_or_immediate_operand" "r,i")))]
  ""
  "@
   mtsr %0,%1
   mtsrim %0,%1")

;; MULTIPLY, MULTM, MULTMU
(define_insn "mulsi3"
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
	(mult:SI (match_operand:SI 1 "gpc_reg_operand" "%r")
		 (match_operand:SI 2 "gpc_reg_operand" "r")))
   (clobber (match_scratch:SI 3 "=&q"))]
  ""
  "multiply %0,%1,%2")

(define_insn ""
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
	(subreg:SI
	 (mult:DI
	  (sign_extend:DI (match_operand:SI 1 "gpc_reg_operand" "%r"))
	  (sign_extend:DI (match_operand:SI 2 "gpc_reg_operand" "r"))) 0))
   (clobber (match_scratch:SI 3 "=&q"))]
  ""
  "multm %0,%1,%2")

(define_insn ""
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
	(subreg:SI
	 (mult:DI
	  (zero_extend:DI (match_operand:SI 1 "gpc_reg_operand" "%r"))
	  (zero_extend:DI (match_operand:SI 2 "gpc_reg_operand" "r"))) 0))
   (clobber (match_scratch:SI 3 "=&q"))]
  ""
  "multmu %0,%1,%2")

(define_insn "mulsidi3"
  [(set (match_operand:DI 0 "gpc_reg_operand" "=r")
	(mult:DI (sign_extend:DI (match_operand:SI 1 "gpc_reg_operand" "r"))
		 (sign_extend:DI (match_operand:SI 2 "gpc_reg_operand" "r"))))
   (clobber (match_scratch:SI 3 "=&q"))]
  ""
  "multiply %L0,%1,%2\;multm %0,%1,%2"
  [(set_attr "type" "multi")])

(define_split
  [(set (match_operand:DI 0 "gpc_reg_operand" "")
	(mult:DI (sign_extend:DI (match_operand:SI 1 "gpc_reg_operand" ""))
		 (sign_extend:DI (match_operand:SI 2 "gpc_reg_operand" ""))))
   (clobber (reg:SI 180))]
  "reload_completed"
  [(parallel [(set (match_dup 3)
		   (mult:SI (match_dup 1) (match_dup 2)))
	      (clobber (reg:SI 180))])
   (parallel [(set (match_dup 4)
		   (subreg:SI (mult:DI
			       (sign_extend:DI (match_dup 1))
			       (sign_extend:DI (match_dup 2))) 0))
	      (clobber (reg:SI 180))])]
  "
{ operands[3] = operand_subword (operands[0], 1, 1, DImode);
  operands[4] = operand_subword (operands[1], 0, 1, DImode); } ")
			    
(define_insn "umulsidi3"
  [(set (match_operand:DI 0 "gpc_reg_operand" "=r")
	(mult:DI (zero_extend:DI (match_operand:SI 1 "gpc_reg_operand" "r"))
		 (zero_extend:DI (match_operand:SI 2 "gpc_reg_operand" "r"))))
   (clobber (match_scratch:SI 3 "=&q"))]
  ""
  "multiplu %L0,%1,%2\;multmu %0,%1,%2"
  [(set_attr "type" "multi")])

(define_split
  [(set (match_operand:DI 0 "gpc_reg_operand" "")
	(mult:DI (zero_extend:DI (match_operand:SI 1 "gpc_reg_operand" ""))
		 (zero_extend:DI (match_operand:SI 2 "gpc_reg_operand" ""))))
   (clobber (reg:SI 180))]
  "reload_completed"
  [(parallel [(set (match_dup 3)
		   (mult:SI (match_dup 1) (match_dup 2)))
	      (clobber (reg:SI 180))])
   (parallel [(set (match_dup 4)
		   (subreg:SI (mult:DI (zero_extend:DI (match_dup 1))
				       (zero_extend:DI (match_dup 2))) 0))
	      (clobber (reg:SI 180))])]
  "
{ operands[3] = operand_subword (operands[0], 1, 1, DImode);
  operands[4] = operand_subword (operands[1], 0, 1, DImode); } ")
			    
(define_insn "smulsi3_highpart"
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
	(truncate:SI
	 (lshiftrt:DI
	  (mult:DI (sign_extend:DI (match_operand:SI 1 "gpc_reg_operand" "%r"))
		   (sign_extend:DI (match_operand:SI 2 "gpc_reg_operand" "r")))
	  (const_int 32))))
   (clobber (match_scratch:SI 3 "=&q"))]
  ""
  "multm %0,%1,%2")

(define_insn "umulsi3_highpart"
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
	(truncate:SI
	 (lshiftrt:DI
	  (mult:DI (zero_extend:DI (match_operand:SI 1 "gpc_reg_operand" "%r"))
		   (zero_extend:DI (match_operand:SI 2 "gpc_reg_operand" "r")))
	  (const_int 32))))
   (clobber (match_scratch:SI 3 "=&q"))]
  ""
  "multmu %0,%1,%2")

;; NAND
(define_insn ""
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
	(ior:SI (not:SI (match_operand:SI 1 "gpc_reg_operand" "%r"))
		(not:SI (match_operand:SI 2 "gpc_reg_operand" "r"))))]
  ""
  "nand %0,%1,%2")

(define_insn ""
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
	(ior:SI (not:SI (match_operand:SI 1 "gpc_reg_operand" "r"))
		(match_operand:SI 2 "const_int_operand" "K")))]
  ; Match TARGET_29050 in "orn" pattern for slightly better reload.
  "! TARGET_29050 && ((unsigned) ~ INTVAL (operands[2])) < 256"
  "nand %0,%1,%C2")

;; NOR
(define_insn ""
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
	(and:SI (not:SI (match_operand:SI 1 "gpc_reg_operand" "%r"))
		(not:SI (match_operand:SI 2 "gpc_reg_operand" "r"))))]
  ""
  "nor %0,%1,%2")

(define_insn "one_cmplsi2"
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
	(not:SI (match_operand:SI 1 "gpc_reg_operand" "r")))]
  ""
  "nor %0,%1,0")

;; OR/ORN
(define_expand "iorsi3"
  [(set (match_operand:SI 0 "gpc_reg_operand" "")
	(ior:SI (match_operand:SI 1 "gpc_reg_operand" "")
		(match_operand:SI 2 "srcb_operand" "")))]
  ""
  "")

(define_insn ""
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
	(ior:SI (match_operand:SI 1 "gpc_reg_operand" "%r")
		(match_operand:SI 2 "srcb_operand" "rI")))]
  "! TARGET_29050"
  "or %0,%1,%2")

(define_insn ""
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r,r")
	(ior:SI (match_operand:SI 1 "gpc_reg_operand" "%r,r")
		(match_operand:SI 2 "and_operand" "rI,K")))]
  "TARGET_29050"
  "@
   or %0,%1,%2
   orn %0,%1,%C2")

(define_insn ""
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r,r")
	(ior:SI (not:SI (match_operand:SI 1 "gpc_reg_operand" "r,r"))
		(match_operand:SI 2 "cmplsrcb_operand" "r,K")))]
  "TARGET_29050"
  "@
   orn %0,%2,%1
   nand %0,%1,%C2")


;; SLL (also used by move insn)
(define_insn "nop"
  [(const_int 0)]
  ""
  "aseq 0x40,gr1,gr1")

(define_insn "ashlsi3"
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
	(ashift:SI (match_operand:SI 1 "gpc_reg_operand" "r")
		   (match_operand:QI 2 "srcb_operand" "rn")))]
  ""
  "sll %0,%1,%Q2")

;; SQRT
(define_insn "sqrtsf2"
  [(set (match_operand:SF 0 "gpc_reg_operand" "=r")
	(sqrt:SF (match_operand:SF 1 "gpc_reg_operand" "r")))]
  "TARGET_29050"
  "sqrt %0,%1,1"
  [(set_attr "type" "fsqrt")])

(define_insn "sqrtdf2"
  [(set (match_operand:DF 0 "gpc_reg_operand" "=r")
	(sqrt:DF (match_operand:DF 1 "gpc_reg_operand" "r")))]
  "TARGET_29050"
  "sqrt %0,%1,2"
  [(set_attr "type" "dsqrt")])

;; SRA
(define_insn "ashrsi3"
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
	(ashiftrt:SI (match_operand:SI 1 "gpc_reg_operand" "r")
		     (match_operand:QI 2 "srcb_operand" "rn")))]
  ""
  "sra %0,%1,%Q2")

;; SRL
(define_insn "lshrsi3"
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
	(lshiftrt:SI (match_operand:SI 1 "gpc_reg_operand" "r")
		     (match_operand:QI 2 "srcb_operand" "rn")))]
  ""
  "srl %0,%1,%Q2")

;; STORE
;;
;; These somewhat bogus patterns exist to set OPT = 001/010 for partial-word
;; stores on systems with DW not set.
(define_insn ""
  [(set (mem:SI (and:SI (match_operand:SI 0 "gpc_reg_operand" "r")
			(const_int -4)))
	(match_operand:SI 1 "gpc_reg_operand" "r"))]
  "! TARGET_DW_ENABLE"
  "store 0,1,%1,%0"
  [(set_attr "type" "store")])

(define_insn ""
  [(set (mem:SI (and:SI (match_operand:SI 0 "gpc_reg_operand" "r")
			(const_int -3)))
	(match_operand:SI 1 "gpc_reg_operand" "r"))]
  "! TARGET_DW_ENABLE"
  "store 0,2,%1,%0"
  [(set_attr "type" "store")])

;; STOREM
(define_expand "store_multiple"
  [(use (match_operand 0 "" ""))
   (use (match_operand 1 "" ""))
   (use (match_operand 2 "" ""))]
  ""
  "
{ rtx pat;

 if (TARGET_NO_STOREM_BUG)
    pat = gen_store_multiple_no_bug (operands[0], operands[1], operands[2]);
  else
    pat = gen_store_multiple_bug (operands[0], operands[1], operands[2]);

  if (pat)
    emit_insn (pat);
  else
    FAIL;

  DONE;
}")

(define_expand "store_multiple_no_bug"
  [(set (match_dup 4)
	(match_operand:PSI 2 "const_int_operand" ""))
   (match_par_dup 3 [(set (match_operand:SI 0 "" "")
			  (match_operand:SI 1 "" ""))])]
  ""
  "
{
  int regno;
  int count;
  rtx from;
  int i;

  /* Support only storing a constant number of hard registers to memory.  */
  if (GET_CODE (operands[2]) != CONST_INT
      || operands[2] == const1_rtx
      || GET_CODE (operands[0]) != MEM
      || GET_CODE (operands[1]) != REG
      || REGNO (operands[1]) >= FIRST_PSEUDO_REGISTER)
    FAIL;

  count = INTVAL (operands[2]);
  regno = REGNO (operands[1]);

  /* CR gets set to the number of registers minus one.  */
  operands[2] = GEN_INT(count - 1);

  operands[3] = gen_rtx (PARALLEL, VOIDmode, rtvec_alloc (count + 2));
  from = memory_address (SImode, XEXP (operands[0], 0));
  XVECEXP (operands[3], 0, 0) = gen_rtx (SET, VOIDmode,
					 gen_rtx (MEM, SImode, from),
					 gen_rtx (REG, SImode, regno));
  operands[4] = gen_reg_rtx (PSImode);
  XVECEXP (operands[3], 0, 1) = gen_rtx (USE, VOIDmode, operands[4]);
  XVECEXP (operands[3], 0, 2) = gen_rtx (CLOBBER, VOIDmode, operands[4]);

  for (i = 1; i < count; i++)
    XVECEXP (operands[3], 0, i + 2)
      = gen_rtx (SET, VOIDmode,
		 gen_rtx (MEM, SImode, plus_constant (from, i * 4)),
		 gen_rtx (REG, SImode, regno + i));
}")

(define_expand "store_multiple_bug"
  [(match_par_dup 3 [(set (match_operand:SI 0 "" "")
			  (match_operand:SI 1 "" ""))
		     (use (match_operand:SI 2 "" ""))])]
  ""
  "
{
  int regno;
  int count;
  rtx from;
  int i;

  /* Support only storing a constant number of hard registers to memory.  */
  if (GET_CODE (operands[2]) != CONST_INT
      || operands[2] == const1_rtx
      || GET_CODE (operands[0]) != MEM
      || GET_CODE (operands[1]) != REG
      || REGNO (operands[1]) >= FIRST_PSEUDO_REGISTER)
    FAIL;

  count = INTVAL (operands[2]);
  regno = REGNO (operands[1]);

  operands[3] = gen_rtx (PARALLEL, VOIDmode, rtvec_alloc (count + 1));
  from = memory_address (SImode, XEXP (operands[0], 0));
  XVECEXP (operands[3], 0, 0) = gen_rtx (SET, VOIDmode,
					 gen_rtx (MEM, SImode, from),
					 gen_rtx (REG, SImode, regno));
  XVECEXP (operands[3], 0, 1)
    = gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, PSImode));

  for (i = 1; i < count; i++)
    XVECEXP (operands[3], 0, i + 1)
      = gen_rtx (SET, VOIDmode,
		 gen_rtx (MEM, SImode, plus_constant (from, i * 4)),
		 gen_rtx (REG, SImode, regno + i));
}")

(define_insn ""
  [(set (match_operand 0 "memory_operand" "=m")
	(match_operand 1 "gpc_reg_operand" "r"))
   (clobber (match_scratch:PSI 2 "=&c"))]
  "!TARGET_NO_STOREM_BUG
   && GET_MODE (operands[0]) == GET_MODE (operands[1])
   && GET_MODE_SIZE (GET_MODE (operands[0])) > UNITS_PER_WORD"
  "mtsrim cr,%S1\;storem 0,0,%1,%0"
  [(set_attr "type" "multi")])

(define_insn ""
  [(match_parallel 0 "store_multiple_operation"
		   [(set (match_operand:SI 1 "memory_operand" "=m")
			 (match_operand:SI 2 "gpc_reg_operand" "r"))
		    (clobber (match_scratch:PSI 3 "=&c"))])]
  "!TARGET_NO_STOREM_BUG"
  "mtsrim cr,%V0\;storem 0,0,%2,%1"
  [(set_attr "type" "multi")])

(define_insn ""
  [(set (match_operand 0 "memory_operand" "=m")
	(match_operand 1 "gpc_reg_operand" "r"))
   (use (match_operand:PSI 2 "register_operand" "+c"))
   (clobber (match_dup 2))]
  "TARGET_NO_STOREM_BUG
   && GET_MODE (operands[0]) == GET_MODE (operands[1])
   && GET_MODE_SIZE (GET_MODE (operands[0])) > UNITS_PER_WORD"
  "storem 0,0,%1,%0"
  [(set_attr "type" "store")])

(define_insn ""
  [(match_parallel 0 "store_multiple_operation"
		   [(set (match_operand:SI 1 "memory_operand" "=m")
			 (match_operand:SI 2 "gpc_reg_operand" "r"))
		    (use (match_operand:PSI 3 "register_operand" "+c"))
		    (clobber (match_dup 3))])]
  "TARGET_NO_STOREM_BUG"
  "storem 0,0,%2,%1"
  [(set_attr "type" "store")])

;; SUB
;;
;; Either operand can be a register or an 8-bit constant, but both cannot be
;; constants (can't usually occur anyway).
(define_expand "subsi3"
  [(set (match_operand:SI 0 "gpc_reg_operand" "")
	(minus:SI (match_operand:SI 1 "srcb_operand" "")
		  (match_operand:SI 2 "srcb_operand" "")))]
  ""
  "
{
  if (GET_CODE (operands[0]) == CONST_INT
      && GET_CODE (operands[1]) == CONST_INT)
    operands[1] = force_reg (SImode, operands[1]);
}")

(define_insn ""
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r,r")
	(minus:SI (match_operand:SI 1 "srcb_operand" "r,I")
		  (match_operand:SI 2 "srcb_operand" "rI,r")))]
  "register_operand (operands[1], SImode)
   || register_operand (operands[2], SImode)"
  "@
   sub %0,%1,%2
   subr %0,%2,%1")

(define_insn "subdi3"
  [(set (match_operand:DI 0 "gpc_reg_operand" "=r")
	(minus:DI (match_operand:DI 1 "gpc_reg_operand" "r")
		  (match_operand:DI 2 "gpc_reg_operand" "r")))]
  ""
  "sub %L0,%L1,%L2\;subc %0,%1,%2"
  [(set_attr "type" "multi")])

;; SUBR (also used above in SUB)
(define_insn "negdi2"
  [(set (match_operand:DI 0 "gpc_reg_operand" "=r")
	(neg:DI (match_operand:DI 1 "gpc_reg_operand" "r")))]
  ""
  "subr %L0,%L1,0\;subrc %0,%1,0"
  [(set_attr "type" "multi")])

(define_insn "negsi2"
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
	(neg:SI (match_operand:SI 1 "gpc_reg_operand" "r")))]
  ""
  "subr %0,%1,0")

;; XNOR
(define_insn ""
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
	(not:SI (xor:SI (match_operand:SI 1 "gpc_reg_operand" "%r")
			(match_operand:SI 2 "gpc_reg_operand" "r"))))]
  ""
  "xnor %0,%1,%2")

;; XOR

(define_insn "xorsi3"
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r,r")
	(xor:SI (match_operand:SI 1 "gpc_reg_operand" "%r,r")
		(match_operand:SI 2 "and_operand" "rI,K")))]
  ""
  "@
   xor %0,%1,%2
   xnor %0,%1,%C2")

;; Can use XOR to negate floating-point values, but we are better off not doing
;; it that way on the 29050 so it can combine with the fmac insns.
(define_expand "negsf2"
  [(parallel [(set (match_operand:SF 0 "register_operand" "")
		   (neg:SF (match_operand:SF 1 "register_operand" "")))
	      (clobber (match_scratch:SI 2 ""))])]
  "! TARGET_SOFT_FLOAT"
  "
{
  rtx result;
  rtx target;

  if (! TARGET_29050)
    {
      target = operand_subword_force (operands[0], 0, SFmode);
      result = expand_binop (SImode, xor_optab,
			     operand_subword_force (operands[1], 0, SFmode),
			     GEN_INT(0x80000000), target, 0, OPTAB_WIDEN);
      if (result == 0)
	abort ();

      if (result != target)
	emit_move_insn (result, target);

      /* Make a place for REG_EQUAL.  */
      emit_move_insn (operands[0], operands[0]);
      DONE;
    }
}")

(define_expand "negdf2"
  [(parallel [(set (match_operand:DF 0 "register_operand" "")
		   (neg:DF (match_operand:DF 1 "register_operand" "")))
	      (clobber (match_scratch:SI 2 ""))])]
  "! TARGET_SOFT_FLOAT"
  "
{
  rtx result;
  rtx target;
  rtx insns;

  if (! TARGET_29050)
    {
      start_sequence ();
      target = operand_subword (operands[0], 0, 1, DFmode);
      result = expand_binop (SImode, xor_optab,
			     operand_subword_force (operands[1], 0, DFmode),
			     GEN_INT(0x80000000), target, 0, OPTAB_WIDEN);
      if (result == 0)
	abort ();

      if (result != target)
	emit_move_insn (result, target);
  
      emit_move_insn (operand_subword (operands[0], 1, 1, DFmode),
		      operand_subword_force (operands[1], 1, DFmode));

      insns = get_insns ();
      end_sequence ();

      emit_no_conflict_block (insns, operands[0], operands[1], 0, 0);
      DONE;
    }
}")

;; Sign extend and truncation operations.
(define_insn "zero_extendqihi2"
  [(set (match_operand:HI 0 "gpc_reg_operand" "=r")
	(zero_extend:HI (match_operand:QI 1 "gpc_reg_operand" "r")))]
  ""
  "and %0,%1,255")

(define_insn "zero_extendqisi2"
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
	(zero_extend:SI (match_operand:QI 1 "gpc_reg_operand" "r")))]
  ""
  "and %0,%1,255")

(define_insn "zero_extendhisi2"
  [(set (match_operand:SI 0 "gpc_reg_operand" "=r")
	(zero_extend:SI (match_operand:HI 1 "gpc_reg_operand" "0")))]
  ""
  "consth %0,0")

(define_expand "extendqihi2"
  [(set (match_dup 2)
	(ashift:SI (match_operand:QI 1 "gpc_reg_operand" "")
		   (const_int 24)))
   (set (match_operand:HI 0 "gpc_reg_operand" "")
	(ashiftrt:SI (match_dup 2)
		     (const_int 24)))]
  ""
  "
{ operands[0] = gen_lowpart (SImode, operands[0]);
  operands[1] = gen_lowpart (SImode, operands[1]);
  operands[2] = gen_reg_rtx (SImode); }")

(define_expand "extendqisi2"
  [(set (match_dup 2)
	(ashift:SI (match_operand:QI 1 "gpc_reg_operand" "")
		   (const_int 24)))
   (set (match_operand:SI 0 "gpc_reg_operand" "")
	(ashiftrt:SI (match_dup 2)
		     (const_int 24)))]
  ""
  "
{ operands[1] = gen_lowpart (SImode, operands[1]);
  operands[2] = gen_reg_rtx (SImode); }")

(define_expand "extendhisi2"
  [(set (match_dup 2)
	(ashift:SI (match_operand:HI 1 "gpc_reg_operand" "")
		   (const_int 16)))
   (set (match_operand:SI 0 "gpc_reg_operand" "")
	(ashiftrt:SI (match_dup 2)
		     (const_int 16)))]
  ""
  "
{ operands[1] = gen_lowpart (SImode, operands[1]);
  operands[2] = gen_reg_rtx (SImode); }")

;; Define the methods used to move data around.
;;
;; movsi:
;;
;; If storing into memory, force source into register.
(define_expand "movsi"
  [(set (match_operand:SI 0 "general_operand" "")
	(match_operand:SI 1 "general_operand" ""))]
  ""
  "
{
  if (GET_CODE (operands[0]) == MEM && ! gpc_reg_operand (operands[1], SImode))
    operands[1] = copy_to_mode_reg (SImode, operands[1]);
  else if (spec_reg_operand (operands[0], SImode)
	   && ! (register_operand (operands[1], SImode)
		 || cint_16_operand (operands[1], SImode)))
    operands[1] = force_reg (SImode, operands[1]);
}")

(define_expand "movpsi"
  [(set (match_operand:PSI 0 "general_operand" "")
	(match_operand:PSI 1 "general_operand" ""))]
  ""
  "
{
  if (GET_CODE (operands[0]) == MEM
      && ! gpc_reg_operand (operands[1], PSImode))
    operands[1] = copy_to_mode_reg (PSImode, operands[1]);
  else if (spec_reg_operand (operands[0], PSImode)
	   && ! (register_operand (operands[1], PSImode)
		 || cint_16_operand (operands[1], PSImode)))
    operands[1] = force_reg (PSImode, operands[1]);
}")

(define_split
  [(set (match_operand:SI 0 "gpc_reg_operand" "")
	(match_operand:SI 1 "long_const_operand" ""))]
  ""
  [(set (match_dup 0)
	(and:SI (match_dup 1)
		(const_int 65535)))
   (set (match_dup 0)
	(ior:SI (zero_extend:SI (match_dup 2))
		(and:SI (match_dup 1)
			(const_int -65536))))]
  " operands[2] = gen_lowpart (HImode, operands[0]); ")

;; Subroutines to load/store halfwords.  Operands 0 and 1 are the output and
;; input, respectively, except that the address is passed for a MEM instead 
;; of the MEM itself and the short item is passed in QImode.
;;
;; Operand 2 is a scratch general register and operand 3 is a scratch register
;; used for BP.  When called before reload, pseudos are passed for both
;; operands.  During reload, R_TAV is used for the general register, and
;; a reload register of class BR_REGS (R_VP) for BP.
;;
;; We have two versions of the store operations, for when halfword writes are
;; supported and when they are not.
(define_expand "loadhi"
  [(parallel [(set (match_operand:SI 2 "gpc_reg_operand" "")
		   (mem:SI (and:SI (match_operand:SI 1 "gpc_reg_operand" "")
				   (const_int -4))))
	      (set (match_operand:PSI 3 "register_operand" "")
		   (truncate:PSI (match_dup 1)))])
   (set (match_operand:SI 0 "gpc_reg_operand" "")
	(zero_extract:SI (match_dup 2)
			 (const_int 16)
			 (ashift:PSI (match_dup 3) (const_int 3))))]
  ""
  "")

(define_expand "storehinhww"
 [(parallel [(set (match_operand:SI 2 "gpc_reg_operand" "")
		   (mem:SI (and:SI (match_operand:SI 0 "gpc_reg_operand" "")
				   (const_int -4))))
	      (set (match_operand:PSI 3 "register_operand" "")
		   (truncate:PSI (match_dup 0)))])
   (set (zero_extract:SI (match_dup 2)
			 (const_int 16)
			 (ashift:PSI (match_dup 3) (const_int 3)))
	(match_operand:SI 1 "gpc_reg_operand" ""))
   (set (mem:SI (match_dup 0))
	(match_dup 2))]
  ""
  "")

(define_expand "storehihww"
  [(set (match_operand:PSI 3 "register_operand" "")
	(truncate:PSI (match_operand:SI 0 "gpc_reg_operand" "")))
   (set (match_operand:SI 2 "gpc_reg_operand" "")
	(ior:SI (and:SI (not:SI (ashift:SI (const_int 65535)
					   (ashift:PSI (match_dup 3)
						       (const_int 3))))
			(match_operand:SI 1 "gpc_reg_operand" ""))
		(ashift:SI (zero_extend:SI (match_dup 4))
			   (ashift:PSI (match_dup 3) (const_int 3)))))
   (set (mem:SI (and:SI (match_dup 0)
			(const_int -3)))
	(match_dup 2))]
  ""
  "
{ operands[4] = gen_lowpart (HImode, operands[1]); }")

(define_expand "movhi"
  [(set (match_operand:HI 0 "general_operand" "")
	(match_operand:HI 1 "general_operand" ""))]
  ""
  "
{ if (GET_CODE (operands[0]) == MEM) 
    {
      if (! gpc_reg_operand (operands[1], HImode))
	operands[1] = copy_to_mode_reg (HImode, operands[1]);
      if (! TARGET_DW_ENABLE)
	{
	  rtx general = gen_reg_rtx (SImode);
	  rtx bp = gen_reg_rtx (PSImode);
	  rtx (*fcn) ()
	    = TARGET_BYTE_WRITES ? gen_storehihww : gen_storehinhww;
	  rtx seq = (*fcn) (XEXP (operands[0], 0),
			    gen_lowpart (SImode, operands[1]),
			    general, bp);
	    
	  a29k_set_memflags (seq, operands[0]);
	  emit_insn (seq);
	  DONE;
	}
    }
  else if (GET_CODE (operands[1]) == MEM)
    {
      if (! TARGET_DW_ENABLE)
	{
	  rtx general = gen_reg_rtx (SImode);
	  rtx bp = gen_reg_rtx (PSImode);
	  rtx seq = gen_loadhi (gen_lowpart (SImode, operands[0]),
				XEXP (operands[1], 0), general, bp);

	  a29k_set_memflags (seq, operands[1]);
	  emit_insn (seq);
	  DONE;
	}
    }
}")

(define_expand "reload_inhi"
  [(parallel [(match_operand:SI 0 "register_operand" "=r")
	      (match_operand:SI 1 "reload_memory_operand" "m")
	      (match_operand:PSI 2 "register_operand" "=b")])]
  "! TARGET_DW_ENABLE"
  "
{ rtx seq = gen_loadhi (gen_lowpart (SImode, operands[0]),
			a29k_get_reloaded_address (operands[1]),
			gen_rtx (REG, SImode, R_TAV),
			operands[2]);

  a29k_set_memflags (seq, operands[1]);
  emit_insn (seq);
  DONE;
}")

(define_expand "reload_outhi"
  [(parallel [(match_operand:SI 0 "reload_memory_operand" "=m")
	      (match_operand:SI 1 "register_operand" "m")
	      (match_operand:PSI 2 "register_operand" "=b")])]
  "! TARGET_DW_ENABLE"
  "
{ rtx (*fcn) () = TARGET_BYTE_WRITES ? gen_storehihww : gen_storehinhww;
  rtx seq = (*fcn) (a29k_get_reloaded_address (operands[0]),
		    gen_lowpart (SImode, operands[1]),
		    gen_rtx (REG, SImode, R_TAV), operands[2]);

  a29k_set_memflags (seq, operands[0]);
  emit_insn (seq);
  DONE;
}")

;; Subroutines to load/store bytes.  Operands 0 and 1 are the output and
;; input, respectively, except that the address is passed for a MEM instead 
;; of the MEM itself and the short item is passed in QImode.
;;
;; Operand 2 is a scratch general register and operand 3 is a scratch register
;; used for BP.  When called before reload, pseudos are passed for both
;; operands.  During reload, R_TAV is used for the general register, and
;; a reload register of class BR_REGS (R_VP) for BP.
;;
;; We have two versions of the store operations, for when byte writes are
;; supported and when they are not.
(define_expand "loadqi"
  [(parallel [(set (match_operand:SI 2 "gpc_reg_operand" "")
		   (mem:SI (and:SI (match_operand:SI 1 "gpc_reg_operand" "")
				   (const_int -4))))
	      (set (match_operand:PSI 3 "register_operand" "")
		   (truncate:PSI (match_dup 1)))])
   (set (match_operand:SI 0 "gpc_reg_operand" "")
	(zero_extract:SI (match_dup 2)
			 (const_int 8)
			 (ashift:PSI (match_dup 3) (const_int 3))))]
  ""
  "")

(define_expand "storeqinhww"
  [(parallel [(set (match_operand:SI 2 "gpc_reg_operand" "")
		   (mem:SI (and:SI (match_operand:SI 0 "gpc_reg_operand" "")
				   (const_int -4))))
	      (set (match_operand:PSI 3 "register_operand" "")
		   (truncate:PSI (match_dup 0)))])
   (set (zero_extract:SI (match_dup 2)
			 (const_int 8)
			 (ashift:PSI (match_dup 3)
				     (const_int 3)))
	(match_operand:SI 1 "gpc_reg_operand" ""))
   (set (mem:SI (match_dup 0))
	(match_dup 2))]
  ""
  "")

(define_expand "storeqihww"
  [(set (match_operand:PSI 3 "register_operand" "")
	(truncate:PSI (match_operand:SI 0 "gpc_reg_operand" "")))
   (set (match_operand:SI 2 "gpc_reg_operand" "")
	(ior:SI (and:SI (not:SI (ashift:SI (const_int 255)
					   (ashift:PSI (match_dup 3)
						       (const_int 3))))
			(match_operand:SI 1 "gpc_reg_operand" ""))
		(ashift:SI (zero_extend:SI (match_dup 4))
			   (ashift:PSI (match_dup 3)
				       (const_int 3)))))
   (set (mem:SI (and:SI (match_dup 0)
		        (const_int -4)))
	(match_dup 2))]
  ""
  "
{ operands[4] = gen_lowpart (QImode, operands[1]); }")

(define_expand "movqi"
  [(set (match_operand:QI 0 "general_operand" "")
	(match_operand:QI 1 "general_operand" ""))]
  ""
  "
{ if (GET_CODE (operands[0]) == MEM)
    {
      if (! gpc_reg_operand (operands[1], QImode))
	operands[1] = copy_to_mode_reg (QImode, operands[1]);
      if (! TARGET_DW_ENABLE)
	{
	  rtx general = gen_reg_rtx (SImode);
	  rtx bp = gen_reg_rtx (PSImode);
	  rtx (*fcn) ()
	    = TARGET_BYTE_WRITES ? gen_storeqihww : gen_storeqinhww;
	  rtx seq = (*fcn) (XEXP (operands[0], 0),
			    gen_lowpart (SImode, operands[1]),
			    general, bp);
	    
	  a29k_set_memflags (seq, operands[0]);
	  emit_insn (seq);
	  DONE;
	}
    }
  else if (GET_CODE (operands[1]) == MEM)
    {
      if (! TARGET_DW_ENABLE)
	{
	  rtx general = gen_reg_rtx (SImode);
	  rtx bp = gen_reg_rtx (PSImode);
	  rtx seq = gen_loadqi (gen_lowpart (SImode, operands[0]),
				XEXP (operands[1], 0), general, bp);

	  a29k_set_memflags (seq, operands[1]);
	  emit_insn (seq);
	  DONE;
	}
    }
}")

(define_expand "reload_inqi"
  [(parallel [(match_operand:SI 0 "register_operand" "=r")
	      (match_operand:SI 1 "reload_memory_operand" "m")
	      (match_operand:PSI 2 "register_operand" "=b")])]
  "! TARGET_DW_ENABLE"
  "
{ rtx seq = gen_loadqi (gen_lowpart (SImode, operands[0]),
			a29k_get_reloaded_address (operands[1]),
			gen_rtx (REG, SImode, R_TAV),
			operands[2]);

  a29k_set_memflags (seq, operands[1]);
  emit_insn (seq);
  DONE;
}")

(define_expand "reload_outqi"
  [(parallel [(match_operand:SI 0 "reload_memory_operand" "=m")
	      (match_operand:SI 1 "register_operand" "m")
	      (match_operand:PSI 2 "register_operand" "=b")])]
  "! TARGET_DW_ENABLE"
  "
{ rtx (*fcn) () = TARGET_BYTE_WRITES ? gen_storeqihww : gen_storeqinhww;
  rtx seq = (*fcn) (a29k_get_reloaded_address (operands[0]),
		    gen_lowpart (SImode, operands[1]),
		    gen_rtx (REG, SImode, R_TAV), operands[2]);

  a29k_set_memflags (seq, operands[0]);
  emit_insn (seq);
  DONE;
}")

;; Now the actual insns used to move data around.  We include here the
;; DEFINE_SPLITs that may be needed.  In some cases these will be
;; split again.  For floating-point, if we can look inside the constant,
;; always split it.  This can eliminate unnecessary insns.
(define_insn ""
  [(set (match_operand:SF 0 "out_operand" "=r,r,r,r,m")
	(match_operand:SF 1 "in_operand" "r,E,F,m,r"))]
  "(gpc_reg_operand (operands[0], SFmode)
    || gpc_reg_operand (operands[1], SFmode))
   && ! TARGET_29050"
  "@
   sll %0,%1,0
   #
   const %0,%1\;consth %0,%1
   load 0,0,%0,%1
   store 0,0,%1,%0"
  [(set_attr "type" "misc,multi,multi,load,store")])

(define_insn ""
  [(set (match_operand:SF 0 "out_operand" "=r,r,r,r,m,*a,r")
	(match_operand:SF 1 "in_operand" "r,E,F,m,r,r,*a"))]
  "(gpc_reg_operand (operands[0], SFmode)
    || gpc_reg_operand (operands[1], SFmode))
   && TARGET_29050"
  "@
   sll %0,%1,0
   #
   const %0,%1\;consth %0,%1
   load 0,0,%0,%1
   store 0,0,%1,%0
   mtacc %1,1,%0
   mfacc %0,1,%1"
  [(set_attr "type" "misc,multi,multi,load,store,fadd,fadd")])

;; Turn this into SImode.  It will then be split up that way.
(define_split
  [(set (match_operand:SF 0 "register_operand" "")
	(match_operand:SF 1 "float_const_operand" ""))]
  "HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT"
  [(set (match_dup 0)
	(match_dup 1))]
  "
{ operands[0] = operand_subword (operands[0], 0, 0, SFmode);
  operands[1] = operand_subword (operands[1], 0, 0, SFmode);

  if (operands[0] == 0 || operands[1] == 0)
    FAIL;
}")

(define_insn ""
  [(set (match_operand:DF 0 "out_operand" "=?r,?r,r,m")
	(match_operand:DF 1 "in_operand" "rE,F,m,r"))
   (clobber (match_scratch:PSI 2 "=X,X,&c,&c"))]
  "(gpc_reg_operand (operands[0], DFmode)
    || gpc_reg_operand (operands[1], DFmode))
   && ! TARGET_29050"
  "@
   #
   const %0,%1\;consth %0,%1\;const %L0,%L1\;consth %L0,%L1
   mtsrim cr,1\;loadm 0,0,%0,%1
   mtsrim cr,1\;storem 0,0,%1,%0"
  [(set_attr "type" "multi")])

(define_insn ""
  [(set (match_operand:DF 0 "out_operand" "=?r,?r,r,m,?*a,?r")
	(match_operand:DF 1 "in_operand" "rE,F,m,r,r,*a"))
   (clobber (match_scratch:PSI 2 "=X,X,&c,&c,X,X"))]
  "(gpc_reg_operand (operands[0], DFmode)
    || gpc_reg_operand (operands[1], DFmode))
   && TARGET_29050"
  "@
   #
   const %0,%1\;consth %0,%1\;const %L0,%L1\;consth %L0,%L1
   mtsrim cr,1\;loadm 0,0,%0,%1
   mtsrim cr,1\;storem 0,0,%1,%0
   mtacc %1,2,%0
   mfacc %0,2,%1"
  [(set_attr "type" "multi,multi,multi,multi,fadd,fadd")])

;; Split register-register copies and constant loads into two SImode loads,
;; one for each word.  In the constant case, they will get further split.
;; Don't so this until register allocation, though, since it will
;; interfere with register allocation.  Normally copy the lowest-addressed
;; word first; the exception is if we are copying register to register and
;; the lowest register of the first operand is the highest register of the
;; second operand.
(define_split
  [(set (match_operand:DF 0 "gpc_reg_operand" "")
	(match_operand:DF 1 "gpc_reg_or_float_constant_operand" ""))
   (clobber (match_scratch:PSI 2 ""))]
  "reload_completed"
  [(set (match_dup 3) (match_dup 4))
   (set (match_dup 5) (match_dup 6))]
  "
{ if (GET_CODE (operands[1]) == REG
      && REGNO (operands[0]) == REGNO (operands[1]) + 1)
    {
      operands[3] = operand_subword (operands[0], 1, 1, DFmode);
      operands[4] = operand_subword (operands[1], 1, 1, DFmode);
      operands[5] = operand_subword (operands[0], 0, 1, DFmode);
      operands[6] = operand_subword (operands[1], 0, 1, DFmode);
    }
  else
    {
      operands[3] = operand_subword (operands[0], 0, 1, DFmode);
      operands[4] = operand_subword (operands[1], 0, 1, DFmode);
      operands[5] = operand_subword (operands[0], 1, 1, DFmode);
      operands[6] = operand_subword (operands[1], 1, 1, DFmode);
    }

  if (operands[3] == 0 || operands[4] == 0
      || operands[5] == 0 || operands[6] == 0)
    FAIL;
}")

;; Split memory loads and stores into the MTSR and LOADM/STOREM.
(define_split
  [(set (match_operand:DF 0 "out_operand" "")
	(match_operand:DF 1 "in_operand" ""))
   (clobber (reg:PSI 179))]
  "TARGET_NO_STOREM_BUG
   && (memory_operand (operands[0], DFmode)
       || memory_operand (operands[1], DFmode))"
  [(set (reg:PSI 179) (const_int 1))
   (parallel [(set (match_dup 0) (match_dup 1))
	      (use (reg:PSI 179))
	      (clobber (reg:PSI 179))])]
  "")

;; DI move is similar to DF move.
(define_insn ""
  [(set (match_operand:DI 0 "out_operand" "=?r,r,m")
	(match_operand:DI 1 "in_operand" "rn,m,r"))
   (clobber (match_scratch:PSI 2 "=X,&c,&c"))]
  "(gpc_reg_operand (operands[0], DImode)
     || gpc_reg_operand (operands[1], DImode))"
  "@
   #
   mtsrim cr,1\;loadm 0,0,%0,%1
   mtsrim cr,1\;storem 0,0,%1,%0"
  [(set_attr "type" "multi")])

(define_split
  [(set (match_operand:DI 0 "gpc_reg_operand" "")
	(match_operand:DI 1 "gpc_reg_or_integer_constant_operand" ""))
   (clobber (match_scratch:PSI 2 ""))]
  "reload_completed"
  [(set (match_dup 3) (match_dup 4))
   (set (match_dup 5) (match_dup 6))]
  "
{ if (GET_CODE (operands[1]) == REG
      && REGNO (operands[0]) == REGNO (operands[1]) + 1)
    {
      operands[3] = operand_subword (operands[0], 1, 1, DImode);
      operands[4] = operand_subword (operands[1], 1, 1, DImode);
      operands[5] = operand_subword (operands[0], 0, 1, DImode);
      operands[6] = operand_subword (operands[1], 0, 1, DImode);
    }
  else
    {
      operands[3] = operand_subword (operands[0], 0, 1, DImode);
      operands[4] = operand_subword (operands[1], 0, 1, DImode);
      operands[5] = operand_subword (operands[0], 1, 1, DImode);
      operands[6] = operand_subword (operands[1], 1, 1, DImode);
    }
}")

(define_split
  [(set (match_operand:DI 0 "out_operand" "")
	(match_operand:DI 1 "in_operand" ""))
   (clobber (reg:PSI 179))]
  "TARGET_NO_STOREM_BUG
   && (memory_operand (operands[0], DImode)
       || memory_operand (operands[1], DImode))"
  [(set (reg:PSI 179) (const_int 1))
   (parallel [(set (match_dup 0) (match_dup 1))
	      (use (reg:PSI 179))
	      (clobber (reg:PSI 179))])]
  "")

;; TImode moves are very similar to DImode moves, except that we can't
;; have constants.
(define_insn ""
  [(set (match_operand:TI 0 "out_operand" "=?r,r,m")
	(match_operand:TI 1 "in_operand" "r,m,r"))
   (clobber (match_scratch:PSI 2 "=X,&c,&c"))]
  "(gpc_reg_operand (operands[0], TImode)
    || gpc_reg_operand (operands[1], TImode))"
  "@
   #
   mtsrim cr,3\;loadm 0,0,%0,%1
   mtsrim cr,3\;storem 0,0,%1,%0"
  [(set_attr "type" "multi,multi,multi")])

(define_split
  [(set (match_operand:TI 0 "gpc_reg_operand" "")
	(match_operand:TI 1 "gpc_reg_operand" ""))
   (clobber (match_scratch:PSI 2 ""))]
  "reload_completed"
  [(set (match_dup 3) (match_dup 4))
   (set (match_dup 5) (match_dup 6))
   (set (match_dup 7) (match_dup 8))
   (set (match_dup 9) (match_dup 10))]
  "
{
  if (REGNO (operands[0]) >= REGNO (operands[1]) + 1
      && REGNO (operands[0]) <= REGNO (operands[1]) + 3)
    {
      operands[3] = gen_rtx (REG, SImode, REGNO (operands[0]) + 3);
      operands[4] = gen_rtx (REG, SImode, REGNO (operands[1]) + 3);
      operands[5] = gen_rtx (REG, SImode, REGNO (operands[0]) + 2);
      operands[6] = gen_rtx (REG, SImode, REGNO (operands[1]) + 2);
      operands[7] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1);
      operands[8] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1);
      operands[9] = gen_rtx (REG, SImode, REGNO (operands[0]));
      operands[10] = gen_rtx (REG, SImode, REGNO (operands[1]));
    }
  else
    {
      operands[3] = gen_rtx (REG, SImode, REGNO (operands[0]));
      operands[4] = gen_rtx (REG, SImode, REGNO (operands[1]));
      operands[5] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1);
      operands[6] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1);
      operands[7] = gen_rtx (REG, SImode, REGNO (operands[0]) + 2);
      operands[8] = gen_rtx (REG, SImode, REGNO (operands[1]) + 2);
      operands[9] = gen_rtx (REG, SImode, REGNO (operands[0]) + 3);
      operands[10] = gen_rtx (REG, SImode, REGNO (operands[1]) + 3); 
    }
}")

(define_split
  [(set (match_operand:TI 0 "out_operand" "")
	(match_operand:TI 1 "in_operand" ""))
   (clobber (reg:PSI 179))]
  "TARGET_NO_STOREM_BUG
   && (memory_operand (operands[0], TImode)
       || memory_operand (operands[1], TImode))"
  [(set (reg:PSI 179) (const_int 3))
   (parallel [(set (match_dup 0) (match_dup 1))
	      (use (reg:PSI 179))
	      (clobber (reg:PSI 179))])]
  "")

(define_insn ""
  [(set (match_operand:SI 0 "out_operand" "=r,r,r,r,r,r,r,m,*h,*h")
        (match_operand:SI 1 "in_operand" "r,J,M,O,i,m,*h,r,r,J"))]
  "(gpc_reg_operand (operands[0], SImode)
    || gpc_reg_operand (operands[1], SImode)
    || (spec_reg_operand (operands[0], SImode)
        && cint_16_operand (operands[1], SImode)))
   && ! TARGET_29050"
  "@
   sll %0,%1,0
   const %0,%1
   constn %0,%M1
   cpeq %0,gr1,gr1
   #
   load 0,0,%0,%1
   mfsr %0,%1
   store 0,0,%1,%0
   mtsr %0,%1
   mtsrim %0,%1"
  [(set_attr "type" "misc,misc,misc,misc,multi,load,misc,store,misc,misc")])

(define_insn ""
  [(set (match_operand:SI 0 "out_operand" "=r,r,r,r,r,r,r,m,*h,*h")
        (match_operand:SI 1 "in_operand" "r,J,M,O,i,m,*h,r,r,J"))]
  "(gpc_reg_operand (operands[0], SImode)
    || gpc_reg_operand (operands[1], SImode)
    || (spec_reg_operand (operands[0], SImode)
        && cint_16_operand (operands[1], SImode)))
   && TARGET_29050"
  "@
   sll %0,%1,0
   const %0,%1
   constn %0,%M1
   consthz %0,%1
   #
   load 0,0,%0,%1
   mfsr %0,%1
   store 0,0,%1,%0
   mtsr %0,%1
   mtsrim %0,%1"
  [(set_attr "type" "misc,misc,misc,misc,multi,load,misc,store,misc,misc")])

(define_insn ""
  [(set (match_operand:PSI 0 "out_operand" "=*r,*r,*r,*r,m,h,h")
        (match_operand:PSI 1 "in_operand" "r,i,m,h,r,r,J"))]
  "(gpc_reg_operand (operands[0], PSImode)
    || gpc_reg_operand (operands[1], PSImode)
    || (spec_reg_operand (operands[0], PSImode)
        && cint_16_operand (operands[1], PSImode)))"
  "@
   sll %0,%1,0
   const %0,%1
   load 0,0,%0,%1
   mfsr %0,%1
   store 0,0,%1,%0
   mtsr %0,%1
   mtsrim %0,%1"
  [(set_attr "type" "misc,multi,load,misc,store,misc,misc")])

(define_insn ""
  [(set (match_operand:HI 0 "out_operand" "=r,r,r,m,r,*h,*h")
        (match_operand:HI 1 "in_operand" "r,i,m,r,*h,r,i"))]
  "gpc_reg_operand (operands[0], HImode)
   || gpc_reg_operand (operands[1], HImode)
   || (spec_reg_operand (operands[0], HImode)
       && cint_16_operand (operands[1], HImode))"
  "@
   sll %0,%1,0
   const %0,%1
   load 0,2,%0,%1
   store 0,2,%1,%0
   mfsr %0,%1
   mtsr %0,%1
   mtsrim %0,%1"
  [(set_attr "type" "misc,misc,load,store,misc,misc,misc")])

(define_insn ""
  [(set (match_operand:QI 0 "out_operand" "=r,r,r,m,r,*h,*h")
        (match_operand:QI 1 "in_operand" "r,i,m,r,*h,r,i"))]
  "gpc_reg_operand (operands[0], QImode)
   || gpc_reg_operand (operands[1], QImode)
   || (spec_reg_operand (operands[0], HImode)
       && cint_16_operand (operands[1], HImode))"
  "@
   sll %0,%1,0
   const %0,%1
   load 0,1,%0,%1
   store 0,1,%1,%0
   mfsr %0,%1
   mtsr %0,%1
   mtsrim %0,%1"
  [(set_attr "type" "misc,misc,load,store,misc,misc,misc")])

;; Define move insns for DI, TI, SF, and DF.
;;
;; In no case do we support mem->mem directly.
;;
;; For DI move of constant to register, split apart at this time since these
;; can require anywhere from 2 to 4 insns and determining which is complex.
;;
;; In other cases, handle similarly to SImode moves.
;;
;; However, indicate that DI, TI, and DF moves may clobber CR (reg 179).
(define_expand "movdi"
  [(parallel [(set (match_operand:DI 0 "general_operand" "")
		   (match_operand:DI 1 "general_operand" ""))
	      (clobber (scratch:PSI))])]
  ""
  "
{
  if (GET_CODE (operands[0]) == MEM)
    operands[1] = force_reg (DImode, operands[1]);
}")

(define_expand "movsf"
  [(set (match_operand:SF 0 "general_operand" "")
	(match_operand:SF 1 "general_operand" ""))]
  ""
  "
{ if (GET_CODE (operands[0]) == MEM)
    operands[1] = force_reg (SFmode, operands[1]);
}")

(define_expand "movdf"
  [(parallel [(set (match_operand:DF 0 "general_operand" "")
		   (match_operand:DF 1 "general_operand" ""))
	      (clobber (scratch:PSI))])]
  ""
  "
{ if (GET_CODE (operands[0]) == MEM)
    operands[1] = force_reg (DFmode, operands[1]);
}")

(define_expand "movti"
  [(parallel [(set (match_operand:TI 0 "general_operand" "")
		   (match_operand:TI 1 "general_operand" ""))
	      (clobber (scratch:PSI))])]
  ""
  "
{
  if (GET_CODE (operands[0]) == MEM)
    operands[1] = force_reg (TImode, operands[1]);

  /* We can't handle constants in general because there is no rtl to represent
     128 bit constants.  Splitting happens to work for CONST_INTs so we split
     them for good code.  Other constants will get forced to memory.  */

  if (GET_CODE (operands[1]) == CONST_INT)
    {
     rtx part0, part1, part2, part3;

     part0 = operand_subword (operands[0], 0, 1, TImode);
     part1 = operand_subword (operands[0], 1, 1, TImode);
     part2 = operand_subword (operands[0], 2, 1, TImode);
     part3 = operand_subword (operands[0], 3, 1, TImode);

     emit_move_insn (part0, const0_rtx);
     emit_move_insn (part1, const0_rtx);
     emit_move_insn (part2, const0_rtx);
     emit_move_insn (part3, const0_rtx);

     DONE;
    }
  else if (CONSTANT_P (operands[1]))
    {
      operands[1] = force_const_mem (TImode, operands[1]);
      if (! memory_address_p (TImode, XEXP (operands[1], 0))
	  && ! reload_in_progress)
        operands[1] = change_address (operands[1], TImode,
				      XEXP (operands[1], 0));
    }
}")

;; Here are the variants of the above for use during reload.

(define_expand "reload_indf"
  [(parallel [(set (match_operand:DF 0 "register_operand" "=r")
		   (match_operand:DF 1 "reload_memory_operand" "m"))
	      (clobber (match_operand:PSI 2 "register_operand" "=&c"))])]
  ""
  "")

(define_expand "reload_outdf"
  [(parallel [(set (match_operand:DF 0 "reload_memory_operand" "=m")
		   (match_operand:DF 1 "register_operand" "r"))
	      (clobber (match_operand:PSI 2 "register_operand" "=&c"))])]
  ""
  "")

(define_expand "reload_indi"
  [(parallel [(set (match_operand:DI 0 "register_operand" "=r")
		   (match_operand:DI 1 "reload_memory_operand" "m"))
	      (clobber (match_operand:PSI 2 "register_operand" "=&c"))])]
  ""
  "")

(define_expand "reload_outdi"
  [(parallel [(set (match_operand:DI 0 "reload_memory_operand" "=m")
		   (match_operand:DI 1 "register_operand" "r"))
	      (clobber (match_operand:PSI 2 "register_operand" "=&c"))])]
  ""
  "")

(define_expand "reload_inti"
  [(parallel [(set (match_operand:TI 0 "register_operand" "=r")
		   (match_operand:TI 1 "reload_memory_operand" "m"))
	      (clobber (match_operand:PSI 2 "register_operand" "=&c"))])]
  ""
  "")

(define_expand "reload_outti"
  [(parallel [(set (match_operand:TI 0 "reload_memory_operand" "=m")
		   (match_operand:TI 1 "register_operand" "r"))
	      (clobber (match_operand:PSI 2 "register_operand" "=&c"))])]
  ""
  "")

;; For compare operations, we simply store the comparison operands and
;; do nothing else.  The following branch or scc insn will output whatever
;; is needed.
(define_expand "cmpsi"
  [(set (cc0)
	(compare (match_operand:SI 0 "gpc_reg_operand" "")
		 (match_operand:SI 1 "srcb_operand" "")))]
  ""
  "
{
  a29k_compare_op0 = operands[0];
  a29k_compare_op1 = operands[1];
  a29k_compare_fp_p = 0;
  DONE;
}")

(define_expand "cmpsf"
  [(set (cc0)
	(compare (match_operand:SF 0 "gpc_reg_operand" "")
		 (match_operand:SF 1 "gpc_reg_operand" "")))]
  "! TARGET_SOFT_FLOAT"
  "
{
  a29k_compare_op0 = operands[0];
  a29k_compare_op1 = operands[1];
  a29k_compare_fp_p = 1;
  DONE;
}")

(define_expand "cmpdf"
  [(set (cc0)
	(compare (match_operand:DF 0 "gpc_reg_operand" "")
		 (match_operand:DF 1 "gpc_reg_operand" "")))]
  "! TARGET_SOFT_FLOAT"
  "
{
  a29k_compare_op0 = operands[0];
  a29k_compare_op1 = operands[1];
  a29k_compare_fp_p = 1;
  DONE;
}")

;; We can generate bit-tests better if we use NE instead of EQ, but we
;; don't have an NE for floating-point.  So we have to have two patterns
;; for EQ and two for NE.

(define_expand "beq"
  [(set (match_dup 1) (ne:SI (match_dup 2) (match_dup 3)))
   (set (pc)
	(if_then_else (ge (match_dup 1) (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "
{
  if (GET_MODE_CLASS (GET_MODE (a29k_compare_op0)) == MODE_FLOAT)
    {
      emit_insn (gen_beq_fp (operands[0]));
      DONE;
    }

  operands[1] = gen_reg_rtx (SImode);
  operands[2] = a29k_compare_op0;
  operands[3] = a29k_compare_op1;
}")

(define_expand "beq_fp"
  [(set (match_dup 1) (eq:SI (match_dup 2) (match_dup 3)))
   (set (pc)
	(if_then_else (lt (match_dup 1) (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "
{
  operands[1] = gen_reg_rtx (SImode);
  operands[2] = a29k_compare_op0;
  operands[3] = a29k_compare_op1;
}")

(define_expand "bne"
  [(set (match_dup 1) (ne:SI (match_dup 2) (match_dup 3)))
   (set (pc)
	(if_then_else (lt (match_dup 1) (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "
{
  if (GET_MODE_CLASS (GET_MODE (a29k_compare_op0)) == MODE_FLOAT)
    {
      emit_insn (gen_bne_fp (operands[0]));
      DONE;
    }

  operands[1] = gen_reg_rtx (SImode);
  operands[2] = a29k_compare_op0;
  operands[3] = a29k_compare_op1;
}")

(define_expand "bne_fp"
  [(set (match_dup 1) (eq:SI (match_dup 2) (match_dup 3)))
   (set (pc)
	(if_then_else (ge (match_dup 1) (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "
{
  operands[1] = gen_reg_rtx (SImode);
  operands[2] = a29k_compare_op0;
  operands[3] = a29k_compare_op1;
}")

;; We don't have a floating-point "lt" insn, so we have to use "gt" in that
;; case with the operands swapped.  The operands must both be registers in
;; the floating-point case, so we know that swapping them is OK.
(define_expand "blt"
  [(set (match_dup 1) (match_dup 2))
   (set (pc)
	(if_then_else (lt (match_dup 1) (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "
{
  operands[1] = gen_reg_rtx (SImode);
  if (a29k_compare_fp_p)
    operands[2] = gen_rtx (GT, SImode, a29k_compare_op1, a29k_compare_op0);
  else
    operands[2] = gen_rtx (LT, SImode, a29k_compare_op0, a29k_compare_op1);
}")

;; Similarly for "le".
(define_expand "ble"
  [(set (match_dup 1) (match_dup 2))
   (set (pc)
	(if_then_else (lt (match_dup 1) (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "
{
  operands[1] = gen_reg_rtx (SImode);
  if (a29k_compare_fp_p)
    operands[2] = gen_rtx (GE, SImode, a29k_compare_op1, a29k_compare_op0);
  else
    operands[2] = gen_rtx (LE, SImode, a29k_compare_op0, a29k_compare_op1);
}")

(define_expand "bltu"
  [(set (match_dup 1) (ltu:SI (match_dup 2) (match_dup 3)))
   (set (pc)
	(if_then_else (lt (match_dup 1) (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "
{
  operands[1] = gen_reg_rtx (SImode);
  operands[2] = a29k_compare_op0;
  operands[3] = a29k_compare_op1;
}")

(define_expand "bleu"
  [(set (match_dup 1) (leu:SI (match_dup 2) (match_dup 3)))
   (set (pc)
	(if_then_else (lt (match_dup 1) (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "
{
  operands[1] = gen_reg_rtx (SImode);
  operands[2] = a29k_compare_op0;
  operands[3] = a29k_compare_op1;
}")

(define_expand "bgt"
  [(set (match_dup 1) (gt:SI (match_dup 2) (match_dup 3)))
   (set (pc)
	(if_then_else (lt (match_dup 1) (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "
{
  operands[1] = gen_reg_rtx (SImode);
  operands[2] = a29k_compare_op0;
  operands[3] = a29k_compare_op1;
}")

(define_expand "bge"
  [(set (match_dup 1) (ge:SI (match_dup 2) (match_dup 3)))
   (set (pc)
	(if_then_else (lt (match_dup 1) (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "
{
  operands[1] = gen_reg_rtx (SImode);
  operands[2] = a29k_compare_op0;
  operands[3] = a29k_compare_op1;
}")

(define_expand "bgtu"
  [(set (match_dup 1) (gtu:SI (match_dup 2) (match_dup 3)))
   (set (pc)
	(if_then_else (lt (match_dup 1) (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "
{
  operands[1] = gen_reg_rtx (SImode);
  operands[2] = a29k_compare_op0;
  operands[3] = a29k_compare_op1;
}")

(define_expand "bgeu"
  [(set (match_dup 1) (geu:SI (match_dup 2) (match_dup 3)))
   (set (pc)
	(if_then_else (lt (match_dup 1) (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "
{
  operands[1] = gen_reg_rtx (SImode);
  operands[2] = a29k_compare_op0;
  operands[3] = a29k_compare_op1;
}")

(define_expand "seq"
  [(set (match_operand:SI 0 "gpc_reg_operand" "")
	(eq:SI (match_dup 1) (match_dup 2)))]
  ""
  "
{
  operands[1] = a29k_compare_op0;
  operands[2] = a29k_compare_op1;
}")
		 
;; This is the most complicated case, because we don't have a floating-point
;; "ne" insn.  If integer, handle normally.  If floating-point, write the
;; compare and then write an insn to reverse the test.
(define_expand "sne_fp"
  [(set (match_dup 3)
	(eq:SI (match_operand 1 "gpc_reg_operand" "")
	       (match_operand 2 "gpc_reg_operand" "")))
   (set (match_operand:SI 0 "gpc_reg_operand" "")
	(ge:SI (match_dup 3) (const_int 0)))]
  "! TARGET_SOFT_FLOAT"
  "
{ operands[3] = gen_reg_rtx (SImode);
}");

(define_expand "sne"
  [(set (match_operand:SI 0 "gpc_reg_operand" "")
	(ne:SI (match_dup 1) (match_dup 2)))]
  ""
  "
{
  operands[1] = a29k_compare_op0;
  operands[2] = a29k_compare_op1;

  if (a29k_compare_fp_p)
    {
      emit_insn (gen_sne_fp (operands[0], operands[1], operands[2]));
      DONE;
    }
}")
		 
;; We don't have a floating-point "lt" insn, so use "gt" and swap the
;; operands, the same as we do "blt".
(define_expand "slt"
  [(set (match_operand:SI 0 "gpc_reg_operand" "")
	(match_dup 1))]
  ""
  "
{
  if (a29k_compare_fp_p)
    operands[1] = gen_rtx (GT, SImode, a29k_compare_op1, a29k_compare_op0);
  else
    operands[1] = gen_rtx (LT, SImode, a29k_compare_op0, a29k_compare_op1);
}")

;; Similarly for "le"
(define_expand "sle"
  [(set (match_operand:SI 0 "gpc_reg_operand" "")
	(match_dup 1))]
  ""
  "
{
  if (a29k_compare_fp_p)
    operands[1] = gen_rtx (GE, SImode, a29k_compare_op1, a29k_compare_op0);
  else
    operands[1] = gen_rtx (LE, SImode, a29k_compare_op0, a29k_compare_op1);
}")

(define_expand "sltu"
  [(set (match_operand:SI 0 "gpc_reg_operand" "")
	(ltu:SI (match_dup 1) (match_dup 2)))]
  ""
  "
{
  operands[1] = a29k_compare_op0;
  operands[2] = a29k_compare_op1;
}")

(define_expand "sleu"
  [(set (match_operand:SI 0 "gpc_reg_operand" "")
	(leu:SI (match_dup 1) (match_dup 2)))]
  ""
  "
{
  operands[1] = a29k_compare_op0;
  operands[2] = a29k_compare_op1;
}")

(define_expand "sgt"
  [(set (match_operand:SI 0 "gpc_reg_operand" "")
	(gt:SI (match_dup 1) (match_dup 2)))]
  ""
  "
{
  operands[1] = a29k_compare_op0;
  operands[2] = a29k_compare_op1;
}")

(define_expand "sge"
  [(set (match_operand:SI 0 "gpc_reg_operand" "")
	(ge:SI (match_dup 1) (match_dup 2)))]
  ""
  "
{
  operands[1] = a29k_compare_op0;
  operands[2] = a29k_compare_op1;
}")
		 
(define_expand "sgtu"
  [(set (match_operand:SI 0 "gpc_reg_operand" "")
	(gtu:SI (match_dup 1) (match_dup 2)))]
  ""
  "
{
  operands[1] = a29k_compare_op0;
  operands[2] = a29k_compare_op1;
}")

(define_expand "sgeu"
  [(set (match_operand:SI 0 "gpc_reg_operand" "")
	(geu:SI (match_dup 1) (match_dup 2)))]
  ""
  "
{
  operands[1] = a29k_compare_op0;
  operands[2] = a29k_compare_op1;
}")

;; Now define the actual jump insns.
(define_insn ""
  [(set (pc)
	(if_then_else (match_operator 0 "branch_operator"
				      [(match_operand:SI 1 "gpc_reg_operand" "r")
				       (const_int 0)])
		      (label_ref (match_operand 2 "" ""))
		      (pc)))]
  ""
  "jmp%b0 %1,%l2%#"
  [(set_attr "type" "branch")])

(define_insn ""
  [(set (pc)
	(if_then_else (match_operator 0 "branch_operator"
				      [(match_operand:SI 1 "gpc_reg_operand" "r")
				       (const_int 0)])
		      (return)
		      (pc)))]
  "null_epilogue ()"
  "jmp%b0i %1,lr0%#"
  [(set_attr "type" "branch")])

(define_insn ""
  [(set (pc)
	(if_then_else (match_operator 0 "branch_operator"
				      [(match_operand:SI 1 "gpc_reg_operand" "r")
				       (const_int 0)])
		      (pc)
		      (label_ref (match_operand 2 "" ""))))]
  ""
  "jmp%B0 %1,%l2%#"
  [(set_attr "type" "branch")])

(define_insn ""
  [(set (pc)
	(if_then_else (match_operator 0 "branch_operator"
				      [(match_operand:SI 1 "gpc_reg_operand" "r")
				       (const_int 0)])
		      (pc)
		      (return)))]
  "null_epilogue ()"
  "jmp%B0i %1,lr0%#"
  [(set_attr "type" "branch")])

(define_insn "jump"
  [(set (pc)
	(label_ref (match_operand 0 "" "")))]
  ""
  "jmp %e0%E0"
  [(set_attr "type" "branch")])

(define_insn "return"
  [(return)]
  "null_epilogue ()"
  "jmpi lr0%#"
  [(set_attr "type" "branch")])

(define_insn "indirect_jump"
  [(set (pc)
	(match_operand:SI 0 "gpc_reg_operand" "r"))]
  ""
  "jmpi %0%#"
  [(set_attr "type" "branch")])

(define_insn "tablejump"
  [(set (pc)
	(match_operand:SI 0 "gpc_reg_operand" "r"))
   (use (label_ref (match_operand 1 "" "")))]
  ""
  "jmpi %0%#"
  [(set_attr "type" "branch")])

;; JMPFDEC
(define_insn ""
  [(set (pc)
	(if_then_else (ge (match_operand:SI 0 "gpc_reg_operand" "r")
			  (const_int 0))
		      (label_ref (match_operand 1 "" ""))
		      (pc)))
   (set (match_dup 0)
	(plus:SI (match_dup 0)
		 (const_int -1)))]
  ""
  "jmpfdec %0,%l1%#"
  [(set_attr "type" "branch")])
