;;- Machine description for the Hitachi SH.
;;  Copyright (C) 1993, 1994, 1995 Free Software Foundation, Inc.
;;  Contributed by Steve Chamberlain (sac@cygnus.com).
;;  Improved by Jim Wilson (wilson@cygnus.com).

;; This file is part of GNU CC.

;; GNU CC is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.

;; GNU CC is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GNU CC; see the file COPYING.  If not, write to
;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.


;; ??? The MAC.W and MAC.L instructions are not supported.  There is no
;; way to generate them.

;; ??? The BSR instruction is not supported.  It might be possible to
;; generate it by keeping track of function sizes (and hence relative
;; addresses), and then using it only if the target is earlier in the same
;; file, and is within range.  Better would be assembler/linker relaxing,
;; but that is much harder.

;; Special constraints for SH machine description:
;;
;;    t -- T
;;    x -- mac
;;    l -- pr
;;    z -- r0
;;
;; Special formats used for outputting SH instructions:
;;
;;   %.  --  print a .s if insn needs delay slot
;;   %@  --  print rte/rts if is/isn't an interrupt function
;;   %#  --  output a nop if there is nothing to put in the delay slot
;;   %O  --  print a constant without the #
;;   %R  --  print the lsw reg of a double
;;   %S  --  print the msw reg of a double
;;   %T  --  print next word of a double REG or MEM
;;
;; Special predicates:
;;
;;  arith_operand          -- operand is valid source for arithmetic op
;;  arith_reg_operand      -- operand is valid register for arithmetic op
;;  general_movdst_operand -- operand is valid move destination
;;  general_movsrc_operand -- operand is valid move source
;;  logical_operand        -- operand is valid source for logical op
;; -------------------------------------------------------------------------
;; Attributes
;; -------------------------------------------------------------------------

; Target CPU.

(define_attr "cpu" "sh0,sh1,sh2,sh3"
  (const (symbol_ref "sh_cpu_attr")))

;;
;; cbranch	conditional branch instructions
;; jump		unconditional jumps
;; arith	ordinary arithmetic
;; load		from memory
;; store	to memory
;; move		register to register
;; smpy		word precision integer multiply
;; dmpy		longword or doublelongword precision integer multiply
;; return	rts
;; pload	load of pr reg (can't be put into delay slot of rts)
;; pcload	pc relative load of constant value
;; rte		return from exception
;; sfunc	special function call with known used registers

(define_attr "type"
 "cbranch,jump,arith,other,load,store,move,smpy,dmpy,return,pload,pcload,rte,sfunc"
  (const_string "other"))

; If a conditional branch destination is within -252..258 bytes away
; from the instruction it can be 2 bytes long.  Something in the
; range -4090..4100 bytes can be 6 bytes long.  All other conditional
; branches are 16 bytes long.

; An unconditional jump in the range -4092..4098 can be 2 bytes long.
; Otherwise, it must be 14 bytes long.

; All other instructions are two bytes long by default.

; All positive offsets have an adjustment added, which is the number of bytes
; difference between this instruction length and the next larger instruction
; length.  This is because shorten_branches starts with the largest
; instruction size and then tries to reduce them.

(define_attr "length" ""
  (cond [(eq_attr "type" "cbranch")
	 (if_then_else (and (ge (minus (match_dup 0) (pc))
				(const_int -252))
			    (le (minus (match_dup 0) (pc))
				(const_int 262)))
		       (const_int 2)
		       (if_then_else (and (ge (minus (match_dup 0) (pc))
					      (const_int -4090))
					  (le (minus (match_dup 0) (pc))
					      (const_int 4110)))
				     (const_int 6)
				     (const_int 16)))

	 (eq_attr "type" "jump")
	 (if_then_else (and (ge (minus (match_dup 0) (pc))
				(const_int -4092))
			    (le (minus (match_dup 0) (pc))
				(const_int 4110)))
		       (const_int 2)
		       (const_int 14))
	 ] (const_int 2)))

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

(define_function_unit "memory" 1 0 (eq_attr "type" "load,pcload,pload") 2 2)
(define_function_unit "mpy"    1 0 (eq_attr "type" "smpy") 2 2)
(define_function_unit "mpy"    1 0 (eq_attr "type" "dmpy") 3 3)

; Definitions for filling branch delay slots.

(define_attr "needs_delay_slot" "yes,no" (const_string "no"))

(define_attr "hit_stack" "yes,no" (const_string "no"))

(define_attr "interrupt_function" "no,yes"
  (const (symbol_ref "pragma_interrupt")))

(define_attr "in_delay_slot" "yes,no"
  (cond [(eq_attr "type" "cbranch") (const_string "no")
	 (eq_attr "type" "pcload") (const_string "no")
	 (eq_attr "needs_delay_slot" "yes") (const_string "no")
	 (eq_attr "length" "2") (const_string "yes")
	 ] (const_string "no")))

(define_delay
  (eq_attr "needs_delay_slot" "yes")
  [(eq_attr "in_delay_slot" "yes") (nil) (nil)])

;; On the SH and SH2, the rte instruction reads the return pc from the stack,
;; and thus we can't put a pop instruction in its delay slot.

(define_delay
  (eq_attr "type" "return")
  [(and (eq_attr "in_delay_slot" "yes")
	(and (ior (eq_attr "interrupt_function" "no")
		  (eq_attr "hit_stack" "no"))
	     (ior (eq_attr "interrupt_function" "yes")
		  (eq_attr "type" "!pload")))) (nil) (nil)])

(define_delay
  (and (eq_attr "type" "cbranch")
       (eq_attr "cpu" "sh2,sh3"))
  [(eq_attr "in_delay_slot" "yes") (nil) (nil)])

;; -------------------------------------------------------------------------
;; SImode signed integer comparisons
;; -------------------------------------------------------------------------

(define_insn ""
  [(set (match_operand:SI 0 "arith_reg_operand" "=r")
	(eq:SI (reg:SI 18)
	       (const_int 1)))]
  ""
  "movt	%0")

;; ??? This combiner pattern does not work, because combine does not combine
;; instructions that set a hard register when SMALL_REGISTER_CLASSES is
;; defined.  Perhaps use a pseudo-reg for the T bit?

(define_insn ""
  [(set (reg:SI 18)
	(eq:SI (and:SI (match_operand:SI 0 "arith_reg_operand" "z,r")
		       (match_operand:SI 1 "arith_operand" "L,r"))
	       (const_int 0)))]
  ""
  "tst	%1,%0")

;; ??? Perhaps should only accept reg/constant if the register is reg 0.
;; That would still allow reload to create cmpi instructions, but would
;; perhaps allow forcing the constant into a register when that is better.

(define_insn "cmpeqsi_t"
  [(set (reg:SI 18) (eq:SI (match_operand:SI 0 "arith_reg_operand" "r,z,r")
			   (match_operand:SI 1 "arith_operand" "N,rI,r")))]
  ""
  "@
	tst	%0,%0
	cmp/eq	%1,%0
	cmp/eq	%1,%0")

(define_insn "cmpgtsi_t"
  [(set (reg:SI 18) (gt:SI (match_operand:SI 0 "arith_reg_operand" "r,r")
			   (match_operand:SI 1 "arith_reg_or_0_operand" "r,N")))]
  ""
  "@
	cmp/gt	%1,%0
	cmp/pl	%0")

(define_insn "cmpgesi_t"
  [(set (reg:SI 18) (ge:SI (match_operand:SI 0 "arith_reg_operand" "r,r")
			   (match_operand:SI 1 "arith_reg_or_0_operand" "r,N")))]
  ""
  "@
	cmp/ge	%1,%0
	cmp/pz	%0")

;; -------------------------------------------------------------------------
;; SImode unsigned integer comparisons
;; -------------------------------------------------------------------------

(define_insn "cmpgeusi_t"
  [(set (reg:SI 18) (geu:SI (match_operand:SI 0 "arith_reg_operand" "r")
			    (match_operand:SI 1 "arith_reg_operand" "r")))]
  ""
  "cmp/hs	%1,%0")

(define_insn "cmpgtusi_t"
  [(set (reg:SI 18) (gtu:SI (match_operand:SI 0 "arith_reg_operand" "r")
			    (match_operand:SI 1 "arith_reg_operand" "r")))]
  ""
  "cmp/hi	%1,%0")

;; We save the compare operands in the cmpxx patterns and use them when
;; we generate the branch.

(define_expand "cmpsi"
  [(set (reg:SI 18) (compare (match_operand:SI 0 "arith_operand" "")
			     (match_operand:SI 1 "arith_operand" "")))]
  ""
  "
{
  sh_compare_op0 = operands[0];
  sh_compare_op1 = operands[1];
  DONE;
}")

;; -------------------------------------------------------------------------
;; Addition instructions
;; -------------------------------------------------------------------------

;; this should be a define split.

(define_insn "adddi3"
  [(set (match_operand:DI 0 "arith_reg_operand" "=r")
	(plus:DI (match_operand:DI 1 "arith_reg_operand" "%0")
		 (match_operand:DI 2 "arith_reg_operand" "r")))
   (clobber (reg:SI 18))]
  ""
  "clrt\;addc	%R2,%R0\;addc	%S2,%S0"
  [(set_attr "length" "6")])


(define_insn "addsi3"
  [(set (match_operand:SI 0 "arith_reg_operand" "=r")
	(plus:SI (match_operand:SI 1 "arith_operand" "%0")
		 (match_operand:SI 2 "arith_operand" "rI")))]
  ""
  "add	%2,%0"
  [(set_attr "type" "arith")])

;; -------------------------------------------------------------------------
;; Subtraction instructions
;; -------------------------------------------------------------------------


(define_insn "subdi3"
  [(set (match_operand:DI 0 "arith_reg_operand" "=r")
	(minus:DI (match_operand:DI 1 "arith_reg_operand" "0")
		 (match_operand:DI 2 "arith_reg_operand" "r")))
   (clobber (reg:SI 18))]
  ""
  "clrt\;subc	%R2,%R0\;subc	%S2,%S0"
  [(set_attr "length" "6")])

(define_insn "subsi3"
  [(set (match_operand:SI 0 "arith_reg_operand" "=r")
	(minus:SI (match_operand:SI 1 "arith_reg_operand" "0")
		  (match_operand:SI 2 "arith_operand" "r")))]
  ""
  "sub	%2,%0"
  [(set_attr "type" "arith")])

;; -------------------------------------------------------------------------
;; Division instructions
;; -------------------------------------------------------------------------

;; we take advantage of the library routines which don't clobber as many
;; registers as a normal function call would.

;; We must use a psuedo-reg forced to reg 0 in the SET_DEST rather than
;; hard register 0.  If we used hard register 0, then the next instruction
;; would be a move from hard register 0 to a pseudo-reg.  If the pseudo-reg
;; gets allocated to a stack slot that needs its address reloaded, then
;; there is nothing to prevent reload from using r0 to reload the address.
;; This reload would clobber the value in r0 we are trying to store.
;; If we let reload allocate r0, then this problem can never happen.

(define_insn ""
  [(set (match_operand:SI 1 "register_operand" "=z")
	(udiv:SI (reg:SI 4) (reg:SI 5)))
   (clobber (reg:SI 18))
   (clobber (reg:SI 17))
   (clobber (reg:SI 4))
   (use (match_operand:SI 0 "arith_reg_operand" "r"))]
  ""
  "jsr	@%0%#"
  [(set_attr "type" "sfunc")
   (set_attr "needs_delay_slot" "yes")])

(define_expand "udivsi3"
  [(set (reg:SI 4) (match_operand:SI 1 "general_operand" ""))
   (set (reg:SI 5) (match_operand:SI 2 "general_operand" ""))
   (set (match_dup 3) (symbol_ref:SI "__udivsi3"))
   (parallel[(set (match_operand:SI 0 "register_operand" "")
		  (udiv:SI (reg:SI 4)
			   (reg:SI 5)))
	     (clobber (reg:SI 18))
	     (clobber (reg:SI 17))
	     (clobber (reg:SI 4))
	     (use (match_dup 3))])]
  ""
  "operands[3] = gen_reg_rtx(SImode);")

(define_insn ""
  [(set (match_operand:SI 1 "register_operand" "=z")
	(div:SI (reg:SI 4) (reg:SI 5)))
   (clobber (reg:SI 18))
   (clobber (reg:SI 17))
   (clobber (reg:SI 1))
   (clobber (reg:SI 2))
   (clobber (reg:SI 3))
   (use (match_operand:SI 0 "arith_reg_operand" "r"))]
  ""
  "jsr	@%0%#"
  [(set_attr "type" "sfunc")
   (set_attr "needs_delay_slot" "yes")])

(define_expand "divsi3"
  [(set (reg:SI 4) (match_operand:SI 1 "general_operand" ""))
   (set (reg:SI 5) (match_operand:SI 2 "general_operand" ""))
   (set (match_dup 3) (symbol_ref:SI "__sdivsi3"))
   (parallel[(set (match_operand:SI 0 "register_operand" "")
		  (div:SI (reg:SI 4)
			   (reg:SI 5)))
	     (clobber (reg:SI 18))
	     (clobber (reg:SI 17))
	     (clobber (reg:SI 1))
	     (clobber (reg:SI 2))
	     (clobber (reg:SI 3))
	     (use (match_dup 3))])]
  ""
  "operands[3] = gen_reg_rtx(SImode);")

;; -------------------------------------------------------------------------
;; Multiplication instructions
;; -------------------------------------------------------------------------

(define_insn ""
  [(set (reg:SI 21)
	(mult:SI (zero_extend:SI (match_operand:HI 1 "arith_reg_operand" "r"))
		 (zero_extend:SI (match_operand:HI 2 "arith_reg_operand" "r"))))]
  ""
  "mulu	%2,%1"
  [(set_attr "type" "smpy")])

(define_insn ""
  [(set (reg:SI 21)
	(mult:SI (sign_extend:SI
		  (match_operand:HI 1 "arith_reg_operand" "r"))
		 (sign_extend:SI
		  (match_operand:HI 2 "arith_reg_operand" "r"))))]
  ""
  "muls	%2,%1"
  [(set_attr "type" "smpy")])

(define_expand "mulhisi3"
  [(set (reg:SI 21)
	(mult:SI (sign_extend:SI
		  (match_operand:HI 1 "arith_reg_operand" ""))
		 (sign_extend:SI
		  (match_operand:HI 2 "arith_reg_operand" ""))))
   (set (match_operand:SI 0 "arith_reg_operand" "")
	(reg:SI 21))]
  ""
  "")

(define_expand "umulhisi3"
  [(set (reg:SI 21)
	(mult:SI (zero_extend:SI
		  (match_operand:HI 1 "arith_reg_operand" ""))
		 (zero_extend:SI
		  (match_operand:HI 2 "arith_reg_operand" ""))))
   (set (match_operand:SI 0 "arith_reg_operand" "")
	(reg:SI 21))]
  ""
  "")

;; mulsi3 on the SH2 can be done in one instruction, on the SH1 we generate
;; a call to a routine which clobbers known registers.

(define_insn ""
  [(set (match_operand:SI 1 "register_operand" "=z")
	(mult:SI (reg:SI 4) (reg:SI 5)))
   (clobber (reg:SI 21))
   (clobber (reg:SI 18))
   (clobber (reg:SI 17))
   (clobber (reg:SI 3))
   (clobber (reg:SI 2))
   (clobber (reg:SI 1))
   (use (match_operand:SI 0 "arith_reg_operand" "r"))]
  ""
  "jsr	@%0%#"
  [(set_attr "type" "sfunc")
   (set_attr "needs_delay_slot" "yes")])

(define_expand "mulsi3_call"
  [(set (reg:SI 4) (match_operand:SI 1 "general_operand" ""))
   (set (reg:SI 5) (match_operand:SI 2 "general_operand" ""))
   (set (match_dup 3) (symbol_ref:SI "__mulsi3"))
   (parallel[(set (match_operand:SI 0 "register_operand" "")
		  (mult:SI (reg:SI 4)
			   (reg:SI 5)))
	     (clobber (reg:SI 21))
	     (clobber (reg:SI 18))
	     (clobber (reg:SI 17))
	     (clobber (reg:SI 3))
	     (clobber (reg:SI 2))
	     (clobber (reg:SI 1))
	     (use (match_dup 3))])]
  ""
  "operands[3] = gen_reg_rtx(SImode);")

(define_insn "mul_l"
  [(set (reg:SI 21)
	(mult:SI (match_operand:SI 0 "arith_reg_operand" "r")
		 (match_operand:SI 1 "arith_reg_operand" "r")))]
  "TARGET_SH2"
  "mul.l	%1,%0"
  [(set_attr "type" "dmpy")])

(define_expand "mulsi3"
  [(set (reg:SI 21)
	(mult:SI  (match_operand:SI 1 "arith_reg_operand" "")
		  (match_operand:SI 2 "arith_reg_operand" "")))
   (set (match_operand:SI 0 "arith_reg_operand" "")
	(reg:SI 21))]
  ""
  "
{
  if (!TARGET_SH2)
    {
      FAIL;
      /* ??? Does this give worse or better code?  */
      emit_insn (gen_mulsi3_call (operands[0], operands[1], operands[2]));
      DONE;
    }
}")

(define_insn ""
  [(set (reg:DI 20)
	(mult:DI (sign_extend:DI (match_operand:SI 1 "arith_reg_operand" "r"))
		 (sign_extend:DI (match_operand:SI 2 "arith_reg_operand" "r"))))]
  "(TARGET_SH2) && 0"
  "dmuls.l	%2,%1"
  [(set_attr "type" "dmpy")])

(define_expand "mulsidi3"
  [(set (reg:DI 20)
	(mult:DI (sign_extend:DI (match_operand:SI 1 "arith_reg_operand" ""))
		 (sign_extend:DI (match_operand:SI 2 "arith_reg_operand" ""))))
   (set (match_operand:DI 0 "arith_reg_operand" "")
	(reg:DI 20))]
  "(TARGET_SH2) && 0"
  "")

(define_insn ""
  [(set (reg:DI 20)
	(mult:DI (zero_extend:DI (match_operand:SI 1 "arith_reg_operand" "r"))
		 (zero_extend:DI (match_operand:SI 2 "arith_reg_operand" "r"))))]
  "(TARGET_SH2) && 0"
  "dmulu.l	%2,%1"
  [(set_attr "type" "dmpy")])

(define_expand "umulsidi3"
  [(set (reg:DI 20)
	(mult:DI (zero_extend:DI (match_operand:SI 1 "arith_reg_operand" ""))
		 (zero_extend:DI (match_operand:SI 2 "arith_reg_operand" ""))))
   (set (match_operand:DI 0 "arith_reg_operand" "")
	(reg:DI 20))]
  "(TARGET_SH2) && 0"
  "")

;; -------------------------------------------------------------------------
;; Logical operations
;; -------------------------------------------------------------------------

(define_insn ""
  [(set (match_operand:SI 0 "arith_reg_operand" "=r,z")
	(and:SI (match_operand:SI 1 "arith_reg_operand" "%0,0")
		(match_operand:SI 2 "logical_operand" "r,L")))]
  ""
  "and	%2,%0"
  [(set_attr "type" "arith")])

;; If the constant is 255, then emit a extu.b instruction instead of an
;; and, since that will give better code.

(define_expand "andsi3"
  [(set (match_operand:SI 0 "arith_reg_operand" "")
	(and:SI (match_operand:SI 1 "arith_reg_operand" "")
		(match_operand:SI 2 "logical_operand" "")))]
  ""
  "
{
  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 255)
    {
      emit_insn (gen_zero_extendqisi2 (operands[0],
				       gen_lowpart (QImode, operands[1])));
      DONE;
    }
}")

(define_insn "iorsi3"
  [(set (match_operand:SI 0 "arith_reg_operand" "=r,z")
	(ior:SI (match_operand:SI 1 "arith_reg_operand" "%0,0")
		(match_operand:SI 2 "logical_operand" "r,L")))]
  ""
  "or	%2,%0")

(define_insn "xorsi3"
  [(set (match_operand:SI 0 "arith_reg_operand" "=z,r")
	(xor:SI (match_operand:SI 1 "arith_reg_operand" "%0,0")
		(match_operand:SI 2 "logical_operand" "L,r")))]
  ""
  "xor	%2,%0"
  [(set_attr "type" "arith")])

;; -------------------------------------------------------------------------
;; Shifts and rotates
;; -------------------------------------------------------------------------

(define_insn "rotlsi3_1"
  [(set (match_operand:SI 0 "arith_reg_operand" "=r")
	(rotate:SI (match_operand:SI 1 "arith_reg_operand" "0")
		   (const_int 1)))
   (set (reg:SI 18)
	(lshiftrt:SI (match_dup 1) (const_int 31)))]
  ""
  "rotl	%0")

(define_insn "rotlsi3_31"
  [(set (match_operand:SI 0 "arith_reg_operand" "=r")
	(rotate:SI (match_operand:SI 1 "arith_reg_operand" "0")
		   (const_int 31)))
   (clobber (reg:SI 18))]
  ""
  "rotr	%0")

(define_insn ""
  [(set (match_operand:SI 0 "arith_reg_operand" "=r")
	(rotate:SI (match_operand:SI 1 "arith_reg_operand" "r")
		   (const_int 16)))]
  ""
  "swap.w	%1,%0")

(define_expand "rotlsi3"
  [(set (match_operand:SI 0 "arith_reg_operand" "")
	(rotate:SI (match_operand:SI 1 "arith_reg_operand" "")
		   (match_operand:SI 2 "immediate_operand" "")))]
  ""
  "
{
  if (GET_CODE (operands[2]) != CONST_INT)
    FAIL;

  if (INTVAL (operands[2]) == 1)
    {
      emit_insn (gen_rotlsi3_1 (operands[0], operands[1]));
      DONE;
    }
  else if (INTVAL (operands[2]) == 31)
    {
      emit_insn (gen_rotlsi3_31 (operands[0], operands[1]));
      DONE;
    }
  else if (INTVAL (operands[2]) != 16)
    FAIL;
}")

(define_insn ""
  [(set (match_operand:HI 0 "arith_reg_operand" "=r")
	(rotate:HI (match_operand:HI 1 "arith_reg_operand" "r")
		   (const_int 8)))]
  ""
  "swap.b	%1,%0")

(define_expand "rotlhi3"
  [(set (match_operand:HI 0 "arith_reg_operand" "")
	(rotate:HI (match_operand:HI 1 "arith_reg_operand" "")
		   (match_operand:HI 2 "immediate_operand" "")))]
  ""
  "
{
  if (GET_CODE (operands[2]) != CONST_INT || INTVAL (operands[2]) != 8)
    FAIL;
}")

;;
;; shift left


(define_insn "ashlsi3_k"
  [(set (match_operand:SI 0 "arith_reg_operand" "=r,r")
	(ashift:SI (match_operand:SI 1 "arith_reg_operand" "0,0")
		   (match_operand:SI 2 "immediate_operand" "M,K")))]
  "CONST_OK_FOR_K (INTVAL (operands[2]))"
  "@
	add	%0,%0
	shll%O2	%0")

(define_insn "ashlsi3_n"
  [(set (match_operand:SI 0 "arith_reg_operand" "=r")
	(ashift:SI (match_operand:SI 1 "arith_reg_operand" "0")
		   (match_operand:SI 2 "immediate_operand" "n")))
   (clobber (reg:SI 18))]
  ""
  "#"
  [(set (attr "length")
	(cond [(eq (symbol_ref "shift_insns_rtx (insn)") (const_int 1))
	       (const_string "2")
	       (eq (symbol_ref "shift_insns_rtx (insn)") (const_int 2))
	       (const_string "4")
	       (eq (symbol_ref "shift_insns_rtx (insn)") (const_int 3))
	       (const_string "6")]
	      (const_string "8")))
   (set_attr "type" "arith")])

(define_split
  [(set (match_operand:SI 0 "arith_reg_operand" "")
	(ashift:SI (match_operand:SI 1 "arith_reg_operand" "")
		   (match_operand:SI 2 "immediate_operand" "n")))
   (clobber (reg:SI 18))]
  ""
  [(use (reg:SI 0))]
  "
{
  gen_shifty_op (ASHIFT, operands);
  DONE;
}")

(define_expand "ashlsi3"
  [(parallel[(set (match_operand:SI 0 "arith_reg_operand" "")
		  (ashift:SI (match_operand:SI 1 "arith_reg_operand" "")
			     (match_operand:SI 2 "nonmemory_operand" "")))
	     (clobber (reg:SI 18))])]
  ""
  "
{
  if (! immediate_operand (operands[2], GET_MODE (operands[2])))
    FAIL;
}")

;
; arithmetic shift right
;

(define_insn "ashrsi3_k"
  [(set (match_operand:SI 0 "arith_reg_operand" "=r")
	(ashiftrt:SI (match_operand:SI 1 "arith_reg_operand" "0")
		     (match_operand:SI 2 "immediate_operand" "M")))
   (clobber (reg:SI 18))]
  "INTVAL(operands[2]) == 1"
  "shar	%0"
  [(set_attr "type" "arith")])

(define_insn "ashrsi2_16"
  [(set (match_operand:SI 0 "arith_reg_operand" "=r")
        (ashiftrt:SI (match_operand:SI 1 "arith_reg_operand" "r")
                     (const_int 16)))]
  ""
  "swap.w	%1,%0\;exts.w	%0,%0"
  [(set_attr "length" "4")])

(define_insn "ashrsi2_31"
  [(set (match_operand:SI 0 "arith_reg_operand" "=r,r")
        (ashiftrt:SI (match_operand:SI 1 "arith_reg_operand" "0,!r")
                     (const_int 31)))
   (clobber (reg:SI 18))]
  ""
  "*
{
  if (which_alternative == 1)
    {
      if (dead_or_set_p (insn, operands[1]))
        return \"shll	%1\;subc	%0,%0\";
      else
        return \"mov	%1,%0\;shll	%0\;subc	%0,%0\";
    }
  return \"shll	%0\;subc	%0,%0\";
}"
  [(set_attr "length" "4,6")])


(define_insn "ashrsi3_n"
  [(set (reg:SI 4)
	(ashiftrt:SI (reg:SI 4)
		     (match_operand:SI 0 "immediate_operand" "i")))
   (clobber (reg:SI 18))
   (clobber (reg:SI 17))
   (use (match_operand:SI 1 "arith_reg_operand" "r"))]
  ""
  "jsr	@%1%#"
  [(set_attr "type" "sfunc")
   (set_attr "needs_delay_slot" "yes")])

(define_expand "ashrsi3"
  [(parallel[(set (match_operand:SI 0 "arith_reg_operand" "")
		  (ashiftrt:SI (match_operand:SI 1 "arith_reg_operand" "")
			       (match_operand:SI 2 "nonmemory_operand" "")))
	     (clobber (reg:SI 18))])]
  ""
  "if (expand_ashiftrt (operands)) DONE; else FAIL;")

;; logical shift right


;; ??? Only the single bit shift clobbers the T bit.

(define_insn "lshrsi3_m"
  [(set (match_operand:SI 0 "arith_reg_operand" "=r")
	(lshiftrt:SI (match_operand:SI 1 "arith_reg_operand" "0")
		     (match_operand:SI 2 "immediate_operand" "M")))
   (clobber (reg:SI 18))]
  "CONST_OK_FOR_M (INTVAL (operands[2]))"
  "shlr	%0")

(define_insn "lshrsi3_k"
  [(set (match_operand:SI 0 "arith_reg_operand" "=r")
	(lshiftrt:SI (match_operand:SI 1 "arith_reg_operand" "0")
		     (match_operand:SI 2 "immediate_operand" "K")))]
  "CONST_OK_FOR_K (INTVAL (operands[2]))
   && ! CONST_OK_FOR_M (INTVAL (operands[2]))"
  "shlr%O2	%0")

(define_insn "lshrsi3_n"
  [(set (match_operand:SI 0 "arith_reg_operand" "=r")
	(lshiftrt:SI (match_operand:SI 1 "arith_reg_operand" "0")
		     (match_operand:SI 2 "immediate_operand" "n")))
   (clobber (reg:SI 18))]
  ""
  "#"
  [(set (attr "length")
	(cond [(eq (symbol_ref "shift_insns_rtx (insn)") (const_int 1))
	       (const_string "2")
	       (eq (symbol_ref "shift_insns_rtx (insn)") (const_int 2))
	       (const_string "4")
	       (eq (symbol_ref "shift_insns_rtx (insn)") (const_int 3))
	       (const_string "6")]
	      (const_string "8")))
   (set_attr "type" "arith")])

(define_split
  [(set (match_operand:SI 0 "arith_reg_operand" "")
	(lshiftrt:SI (match_operand:SI 1 "arith_reg_operand" "")
		     (match_operand:SI 2 "immediate_operand" "n")))
   (clobber (reg:SI 18))]
  ""
  [(use (reg:SI 0))]
  "
{
  gen_shifty_op (LSHIFTRT, operands);
  DONE;
}")

(define_expand "lshrsi3"
  [(parallel[(set (match_operand:SI 0 "arith_reg_operand" "")
		  (lshiftrt:SI (match_operand:SI 1 "arith_reg_operand" "")
			       (match_operand:SI 2 "nonmemory_operand" "")))
	     (clobber (reg:SI 18))])]
  ""
  "
{
  if (! immediate_operand (operands[2], GET_MODE (operands[2])))
    FAIL;
}")

(define_insn "ashldi3_k"
  [(set (match_operand:DI 0 "arith_reg_operand" "=r")
	(ashift:DI (match_operand:DI 1 "arith_reg_operand" "0")
		   (const_int 1)))
   (clobber (reg:SI 18))]
  ""
  "shll	%R0\;rotcl	%S0"
  [(set_attr "length" "4")])

(define_expand "ashldi3"
  [(parallel[(set (match_operand:DI 0 "arith_reg_operand" "")
		  (ashift:DI (match_operand:DI 1 "arith_reg_operand" "")
			     (match_operand:DI 2 "immediate_operand" "")))
	     (clobber (reg:SI 18))])]

  ""
  "{ if (GET_CODE (operands[2]) != CONST_INT
	|| INTVAL (operands[2]) != 1) FAIL;} ")

(define_insn "lshrdi3_k"
  [(set (match_operand:DI 0 "arith_reg_operand" "=r")
	(lshiftrt:DI (match_operand:DI 1 "arith_reg_operand" "0")
		     (const_int 1)))
   (clobber (reg:SI 18))]
  ""
  "shlr	%S0\;rotcr	%R0"
  [(set_attr "length" "4")])

(define_expand "lshrdi3"
  [(parallel[(set (match_operand:DI 0 "arith_reg_operand" "")
		  (lshiftrt:DI (match_operand:DI 1 "arith_reg_operand" "")
			       (match_operand:DI 2 "immediate_operand" "")))
	     (clobber (reg:SI 18))])]
  ""
  "{ if (GET_CODE (operands[2]) != CONST_INT
	|| INTVAL (operands[2]) != 1) FAIL;} ")

(define_insn "ashrdi3_k"
  [(set (match_operand:DI 0 "arith_reg_operand" "=r")
	(ashiftrt:DI (match_operand:DI 1 "arith_reg_operand" "0")
		     (const_int 1)))
   (clobber (reg:SI 18))]
  ""
  "shar	%S0\;rotcr	%R0"
  [(set_attr "length" "4")])

(define_expand "ashrdi3"
  [(parallel[(set (match_operand:DI 0 "arith_reg_operand" "")
		  (ashiftrt:DI (match_operand:DI 1 "arith_reg_operand" "")
			       (match_operand:DI 2 "immediate_operand" "")))
	     (clobber (reg:SI 18))])]
  ""
  "{ if (GET_CODE (operands[2]) != CONST_INT
	|| INTVAL (operands[2]) != 1) FAIL; } ")

;; -------------------------------------------------------------------------
;; Unary arithmetic
;; -------------------------------------------------------------------------


(define_insn "negc"
  [(set (match_operand:SI 0 "arith_reg_operand" "=r")
	(neg:SI (plus:SI (reg:SI 18)
			 (match_operand:SI 1 "arith_reg_operand" "r"))))
   (set (reg:SI 18)
	(ne:SI (ior:SI (reg:SI 18) (match_dup 1))
	       (const_int 0)))]
  ""
  "negc	%1,%0"
  [(set_attr "type" "arith")])

(define_expand "negdi2"
  [(set (match_operand:DI 0 "arith_reg_operand" "")
	(neg:DI (match_operand:DI 1 "arith_reg_operand" "")))
   (clobber (reg:SI 18))]
  ""
  "{
   rtx low_src = operand_subword (operands[1], 1, 0, DImode);
   rtx high_src = operand_subword (operands[1], 0, 0, DImode);

   rtx low_dst = operand_subword (operands[0], 1, 1, DImode);
   rtx high_dst = operand_subword (operands[0], 0, 1, DImode);

   emit_insn (gen_clrt ());
   emit_insn (gen_negc (low_dst, low_src));
   emit_insn (gen_negc (high_dst, high_src));
   DONE;
   }
   ")


(define_insn "negsi2"
  [(set (match_operand:SI 0 "arith_reg_operand" "=r")
	(neg:SI (match_operand:SI 1 "arith_reg_operand" "r")))]
  ""
  "neg	%1,%0"
  [(set_attr "type" "arith")])

(define_insn "one_cmplsi2"
  [(set (match_operand:SI 0 "arith_reg_operand" "=r")
	(not:SI (match_operand:SI 1 "arith_reg_operand" "r")))]
  ""
  "not	%1,%0"
  [(set_attr "type" "arith")])

;; -------------------------------------------------------------------------
;; Zero extension instructions
;; -------------------------------------------------------------------------

(define_insn "zero_extendhisi2"
  [(set (match_operand:SI 0 "arith_reg_operand" "=r")
	(zero_extend:SI (match_operand:HI 1 "arith_reg_operand" "r")))]
  ""
  "extu.w	%1,%0"
  [(set_attr "type" "arith")])

(define_insn "zero_extendqisi2"
  [(set (match_operand:SI 0 "arith_reg_operand" "=r")
	(zero_extend:SI (match_operand:QI 1 "arith_reg_operand" "r")))]
  ""
  "extu.b	%1,%0"
  [(set_attr "type" "arith")])

(define_insn "zero_extendqihi2"
  [(set (match_operand:HI 0 "arith_reg_operand" "=r")
	(zero_extend:HI (match_operand:QI 1 "arith_reg_operand" "r")))]
  ""
  "extu.b	%1,%0"
  [(set_attr "type" "arith")])

;; -------------------------------------------------------------------------
;; Sign extension instructions
;; -------------------------------------------------------------------------

(define_insn "extendsidi2"
  [(set (match_operand:DI 0 "arith_reg_operand" "=r")
	(sign_extend:DI (match_operand:SI 1 "arith_reg_operand" "r")))
   (clobber (reg:SI 18))]
  ""
  "mov	%1,%S0\;mov	%1,%R0\;shll	%S0\;subc	%S0,%S0"
  [(set_attr "length" "8")])

(define_insn "extendhisi2"
  [(set (match_operand:SI 0 "arith_reg_operand" "=r,r")
	(sign_extend:SI (match_operand:HI 1 "general_movsrc_operand" "r,m")))]
  ""
  "@
	exts.w	%1,%0
   	mov.w	%1,%0"
  [(set_attr "type" "arith,load")])

(define_insn "extendqisi2"
  [(set (match_operand:SI 0 "arith_reg_operand" "=r,r")
	(sign_extend:SI (match_operand:QI 1 "general_movsrc_operand" "r,m")))]
  ""
  "@
	exts.b	%1,%0
	mov.b	%1,%0"
  [(set_attr "type" "arith,load")])

(define_insn "extendqihi2"
  [(set (match_operand:HI 0 "arith_reg_operand" "=r,r")
	(sign_extend:HI (match_operand:QI 1 "general_movsrc_operand" "r,m")))]
  ""
  "@
	exts.b	%1,%0
	mov.b	%1,%0"
  [(set_attr "type" "arith,load")])

;; -------------------------------------------------------------------------
;; Move instructions
;; -------------------------------------------------------------------------

;; define push and pop so it is easy for sh.c

(define_insn "push"
  [(set (mem:SI (pre_dec:SI (reg:SI 15)))
	(match_operand:SI 0 "register_operand" "r,lx"))]
  ""
  "@
	mov.l	%0,@-r15
	sts.l	%0,@-r15"
  [(set_attr "type" "store")
   (set_attr "hit_stack" "yes")])

(define_insn "pop"
  [(set (match_operand:SI 0 "register_operand" "=r,l,x")
	(mem:SI (post_inc:SI (reg:SI 15))))]
  ""
  "@
	mov.l	@r15+,%0
	lds.l	@r15+,%0
	lds.l	@r15+,%0"
  [(set_attr "type" "load,pload,load")
   (set_attr "hit_stack" "yes")])

(define_insn "clrt"
  [(set (reg:SI 18) (const_int 0))]
  ""
  "clrt")

(define_insn "movsi_i"
  [(set (match_operand:SI 0 "general_movdst_operand" "=r,r,r,r,r,m,<,xl,xl,t,r")
	(match_operand:SI 1 "general_movsrc_operand" "Q,rI,m,xl,t,r,xl,r,>,r,i"))]
  "register_operand (operands[0], SImode)
   || register_operand (operands[1], SImode)"
  "@
	mov.l	%1,%0
	mov	%1,%0
	mov.l	%1,%0
	sts	%1,%0
	movt	%0
	mov.l	%1,%0
	sts.l	%1,%0
	lds	%1,%0
	lds.l	%1,%0
	tst	%1,%1\;rotcl	%1\;xor	#1,%1\;rotcr	%1
	fake	%1,%0"
  [(set_attr "type" "pcload,move,load,move,store,store,move,load,move,move,move")
   (set_attr "length" "*,*,*,*,*,*,*,*,*,8,*")])

(define_expand "movsi"
  [(set (match_operand:SI 0 "general_movdst_operand" "")
	(match_operand:SI 1 "general_movsrc_operand" ""))]
  ""

  "{ if (prepare_move_operands(operands, SImode)) DONE; } ")


(define_insn "movqi_i"
  [(set (match_operand:QI 0 "general_movdst_operand" "=r,r,m,r,r,l")
	(match_operand:QI 1 "general_movsrc_operand"  "ri,m,r,t,l,r"))]
  "arith_reg_operand (operands[0], QImode)
   || arith_reg_operand (operands[1], QImode)"
  "@
	mov	%1,%0
	mov.b	%1,%0
	mov.b	%1,%0
	movt	%0
	sts	%1,%0
	lds	%1,%0"
 [(set_attr "type" "move,load,store,move,move,move")])

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

(define_insn "movhi_i"
  [(set (match_operand:HI 0 "general_movdst_operand" "=r,r,r,r,m,r,l,r")
	(match_operand:HI 1 "general_movsrc_operand" "Q,rI,m,t,r,l,r,i"))]
  "arith_reg_operand (operands[0], HImode)
   || arith_reg_operand (operands[1], HImode)"
  "@
	mov.w	%1,%0
	mov	%1,%0
	mov.w	%1,%0
	movt	%0
	mov.w	%1,%0
	sts	%1,%0
	lds	%1,%0
	fake	%1,%0"
  [(set_attr "type" "pcload,move,load,move,store,move,move,move")])

(define_expand "movhi"
  [(set (match_operand:HI 0 "general_movdst_operand" "")
	(match_operand:HI 1 "general_movsrc_operand"  ""))]
  ""
  "if (prepare_move_operands (operands, HImode)) DONE;")

(define_insn ""
  [(set (match_operand:DI 0 "general_movdst_operand" "=r,r,r,m,r")
	(match_operand:DI 1 "general_movsrc_operand" "Q,r,m,r,i"))]
  "arith_reg_operand (operands[0], DImode)
   || arith_reg_operand (operands[1], DImode)"
  "* return output_movedouble (insn, operands, DImode);"
  [(set_attr "length" "4")
   (set_attr "type" "pcload,move,load,store,move")])

;; If the output is a register and the input is memory, we have to be careful
;; and see which word needs to be loaded first.

;; ??? Why are Q constraint addresses rejected here but not in the DFmode
;; split pattern?

(define_split
  [(set (match_operand:DI 0 "general_movdst_operand" "")
	(match_operand:DI 1 "general_movsrc_operand" ""))]
 "! (GET_CODE (operands[0]) == REG
     && REGNO (operands[0]) >= FIRST_PSEUDO_REGISTER)
   && ! (GET_CODE (operands[1]) == REG
         && REGNO (operands[1]) >= FIRST_PSEUDO_REGISTER)
   && ! (GET_CODE (operands[0]) == REG && GET_CODE (operands[1]) == REG
	 && ! reload_completed
	 && reg_overlap_mentioned_p (operands[0], operands[1]))
   && ! (GET_CODE (operands[0]) == MEM
	 && GET_CODE (XEXP (operands[0], 0)) == PRE_DEC)
   && ! (GET_CODE (operands[1]) == MEM
	 && GET_CODE (XEXP (operands[1], 0)) == POST_INC)
   && ! EXTRA_CONSTRAINT_Q (operands[1])"
  [(set (match_dup 2) (match_dup 3))
   (set (match_dup 4) (match_dup 5))]
  "
{ if (GET_CODE (operands[0]) != REG
      || ! refers_to_regno_p (REGNO (operands[0]), REGNO (operands[0]) + 1,
			      operands[1], 0))
    {
      operands[2] = operand_subword (operands[0], 0, 0, DImode);
      operands[3] = operand_subword (operands[1], 0, 0, DImode);
      operands[4] = operand_subword (operands[0], 1, 0, DImode);
      operands[5] = operand_subword (operands[1], 1, 0, DImode);
    }
  else
    {
      operands[2] = operand_subword (operands[0], 1, 0, DImode);
      operands[3] = operand_subword (operands[1], 1, 0, DImode);
      operands[4] = operand_subword (operands[0], 0, 0, DImode);
      operands[5] = operand_subword (operands[1], 0, 0, DImode);
    }

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


(define_expand "movdi"
  [(set (match_operand:DI 0 "general_movdst_operand" "")
	(match_operand:DI 1 "general_movsrc_operand" ""))]
  ""
  "if ( prepare_move_operands(operands, DImode)) DONE; ")

(define_insn "movdf_k"
  [(set (match_operand:DF 0 "general_movdst_operand" "=r,r,m")
	(match_operand:DF 1 "general_movsrc_operand" "r,m,r"))]
  "arith_reg_operand (operands[0], DFmode)
   || arith_reg_operand (operands[1], DFmode)"
  "* return output_movedouble (insn, operands, DFmode);"
  [(set_attr "length" "4")
   (set_attr "type" "move,load,store")])

;; If the output is a register and the input is memory, we have to be careful
;; and see which word needs to be loaded first.

(define_split
  [(set (match_operand:DF 0 "general_movdst_operand" "")
	(match_operand:DF 1 "general_movsrc_operand" ""))]
 "! (GET_CODE (operands[0]) == REG
     && REGNO (operands[0]) >= FIRST_PSEUDO_REGISTER)
   && ! (GET_CODE (operands[1]) == REG
         && REGNO (operands[1]) >= FIRST_PSEUDO_REGISTER)
   && ! (GET_CODE (operands[0]) == REG && GET_CODE (operands[1]) == REG
	 && ! reload_completed
	 && reg_overlap_mentioned_p (operands[0], operands[1]))
   && ! (GET_CODE (operands[0]) == MEM
	 && GET_CODE (XEXP (operands[0], 0)) == PRE_DEC)
   && ! (GET_CODE (operands[1]) == MEM
	 && GET_CODE (XEXP (operands[1], 0)) == POST_INC)"
  [(set (match_dup 2) (match_dup 3))
   (set (match_dup 4) (match_dup 5))]
  "
{ if (GET_CODE (operands[0]) != REG
      || ! refers_to_regno_p (REGNO (operands[0]), REGNO (operands[0]) + 1,
			      operands[1], 0))
    {
      operands[2] = operand_subword (operands[0], 0, 0, DFmode);
      operands[3] = operand_subword (operands[1], 0, 0, DFmode);
      operands[4] = operand_subword (operands[0], 1, 0, DFmode);
      operands[5] = operand_subword (operands[1], 1, 0, DFmode);
    }
  else
    {
      operands[2] = operand_subword (operands[0], 1, 0, DFmode);
      operands[3] = operand_subword (operands[1], 1, 0, DFmode);
      operands[4] = operand_subword (operands[0], 0, 0, DFmode);
      operands[5] = operand_subword (operands[1], 0, 0, DFmode);
    }

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

(define_expand "movdf"
  [(set (match_operand:DF 0 "general_movdst_operand" "")
	(match_operand:DF 1 "general_movsrc_operand" ""))]
  ""
 "{ if (prepare_move_operands(operands, DFmode)) DONE; } ")

(define_insn "movsf_i"
  [(set (match_operand:SF 0 "general_movdst_operand" "=r,r,r,m,l,r")
	(match_operand:SF 1 "general_movsrc_operand"  "r,I,m,r,r,l"))]
  "arith_reg_operand (operands[0], SFmode)
   || arith_reg_operand (operands[1], SFmode)"
  "@
	mov	%1,%0
	mov	%1,%0
	mov.l	%1,%0
	mov.l	%1,%0
	lds	%1,%0
	sts	%1,%0"
  [(set_attr "type" "move,move,load,store,move,move")])

(define_expand "movsf"
  [(set (match_operand:SF 0 "general_movdst_operand" "")
	(match_operand:SF 1 "general_movsrc_operand" ""))]
  ""
  "if (prepare_move_operands (operands, SFmode)) DONE;")

;; ------------------------------------------------------------------------
;; Define the real conditional branch instructions.
;; ------------------------------------------------------------------------

(define_insn "branch_true"
  [(set (pc) (if_then_else (eq (reg:SI 18) (const_int 1))
			   (label_ref (match_operand 0 "" ""))
			   (pc)))]
  ""
  "* return output_branch (1, insn, operands);"
  [(set_attr "type" "cbranch")])

(define_insn "branch_false"
  [(set (pc) (if_then_else (ne (reg:SI 18) (const_int 1))
			   (label_ref (match_operand 0 "" ""))
			   (pc)))]
  ""
  "* return output_branch (0, insn, operands);"
  [(set_attr "type" "cbranch")])

(define_insn "inverse_branch_true"
  [(set (pc) (if_then_else (eq (reg:SI 18) (const_int 1))
			   (pc)
			   (label_ref (match_operand 0 "" ""))))]
  ""
  "* return output_branch (0, insn, operands);"
  [(set_attr "type" "cbranch")])

(define_insn "inverse_branch_false"
  [(set (pc) (if_then_else (ne (reg:SI 18) (const_int 1))
   			   (pc)
			   (label_ref (match_operand 0 "" ""))))]
  ""
  "* return output_branch (1, insn, operands);"
  [(set_attr "type" "cbranch")])

;; Conditional branch insns

(define_expand "beq"
  [(set (reg:SI 18) (eq:SI (match_dup 1) (match_dup 2)))
   (set (pc)
	(if_then_else (eq (reg:SI 18)
			  (const_int 1))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "from_compare (operands, EQ);")

; There is no bne compare, so we reverse the branch arms.

(define_expand "bne"
  [(set (reg:SI 18) (eq:SI (match_dup 1) (match_dup 2)))
   (set (pc)
	(if_then_else (eq (reg:SI 18)
			  (const_int 1))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "from_compare (operands, NE);")

(define_expand "bgt"
  [(set (reg:SI 18) (gt:SI (match_dup 1) (match_dup 2)))
   (set (pc)
	(if_then_else (eq (reg:SI 18)
			  (const_int 1))
		      (label_ref (match_operand 0 "" ""))
		      (pc))) ]
  ""
  "from_compare (operands, GT);")

(define_expand "blt"
  [(set (reg:SI 18) (ge:SI (match_dup 1) (match_dup 2)))
   (set (pc)
	(if_then_else (eq (reg:SI 18)(const_int 1))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "from_compare (operands, LT);")

(define_expand "ble"
  [(set (reg:SI 18) (gt:SI (match_dup 1) (match_dup 2)))
   (set (pc)
	(if_then_else (eq (reg:SI 18)
			  (const_int 1))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))
   ]
  ""
  "from_compare (operands, LE);")

(define_expand "bge"
  [(set (reg:SI 18) (ge:SI (match_dup 1) (match_dup 2)))
   (set (pc)
	(if_then_else (eq (reg:SI 18)
			  (const_int 1))
		      (label_ref (match_operand 0 "" ""))
		      (pc))) ]
  ""
  "from_compare (operands, GE);")

(define_expand "bgtu"
  [(set (reg:SI 18) (gtu:SI (match_dup 1) (match_dup 2)))
   (set (pc)
	(if_then_else (eq (reg:SI 18)
			  (const_int 1))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "from_compare (operands, GTU); ")

(define_expand "bltu"
  [(set (reg:SI 18) (geu:SI (match_dup 1) (match_dup 2)))
   (set (pc)
		  (if_then_else (eq (reg:SI 18)
				    (const_int 1))
				(pc)
				(label_ref (match_operand 0 "" ""))))]
  ""
  "from_compare (operands, LTU);")

(define_expand "bgeu"
  [(set (reg:SI 18) (geu:SI (match_dup 1) (match_dup 2)))
   (set (pc)
	(if_then_else (eq (reg:SI 18)
			  (const_int 1))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))
   ]
  ""
  "from_compare (operands, GEU);")

(define_expand "bleu"
  [(set (reg:SI 18) (gtu:SI (match_dup 1) (match_dup 2)))
   (set (pc)
	(if_then_else (eq (reg:SI 18)
			  (const_int 1))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "from_compare (operands, LEU);")

;; ------------------------------------------------------------------------
;; Jump and linkage insns
;; ------------------------------------------------------------------------

(define_insn "jump"
  [(set (pc)
	(label_ref (match_operand 0 "" "")))]
  ""
  "*
{
  /* The length is 16 if the delay slot is unfilled.  */
  if (get_attr_length(insn) >= 14)
    return output_far_jump(insn, operands[0]);
  else
    return   \"bra	%l0%#\";
}"
  [(set_attr "type" "jump")
   (set_attr "needs_delay_slot" "yes")])

(define_insn "calli"
  [(call (mem:SI (match_operand:SI 0 "arith_reg_operand" "r"))
	 (match_operand 1 "" ""))
   (clobber (reg:SI 17))]
  ""
  "jsr	@%0%#"
  [(set_attr "needs_delay_slot" "yes")])

(define_insn "call_valuei"
  [(set (match_operand 0 "" "=rf")
	(call (mem:SI (match_operand:SI 1 "arith_reg_operand" "r"))
	      (match_operand 2 "" "")))
   (clobber (reg:SI 17))]
  ""
  "jsr	@%1%#"
  [(set_attr "needs_delay_slot" "yes")])

(define_expand "call"
  [(parallel [(call (mem:SI (match_operand 0 "arith_reg_operand" ""))
			    (match_operand 1 "" ""))
	      (clobber (reg:SI 17))])]
  ""
  "operands[0] = force_reg (SImode, XEXP (operands[0], 0));")

(define_expand "call_value"
  [(parallel [(set (match_operand 0 "arith_reg_operand" "")
		   (call (mem:SI (match_operand 1 "arith_reg_operand" ""))
				 (match_operand 2 "" "")))
	      (clobber (reg:SI 17))])]
  ""
  "operands[1] = force_reg (SImode, XEXP (operands[1], 0));")

(define_insn "indirect_jump"
  [(set (pc)
	(match_operand:SI 0 "arith_reg_operand" "r"))]
  ""
  "jmp	@%0%#"
  [(set_attr "needs_delay_slot" "yes")])

;; ------------------------------------------------------------------------
;; Misc insns
;; ------------------------------------------------------------------------

;; ??? This combiner pattern does not work, because combine does not combine
;; instructions that set a hard register when SMALL_REGISTER_CLASSES is
;; defined.  Perhaps use a pseudo-reg for the T bit?

(define_insn "dect"
  [(parallel[
	     (set (match_operand:SI 0 "arith_reg_operand" "=r")
		  (plus:SI (match_dup 0)
			   (const_int -1)))
	     (set (reg:SI 18)
		  (eq:SI (plus:SI (match_dup 0) (const_int -1))
			 (const_int 0)))])]
  "TARGET_SH2"
  "dt	%0")

(define_insn "nop"
  [(const_int 0)]
  ""
  "nop")

;; Load address of a label. This is only generated by the casesi expand.
;; This must use unspec, because this only works immediately before a casesi.

(define_insn "mova"
  [(set (reg:SI 0)
	(unspec [(label_ref (match_operand 0 "" ""))] 1))]
  ""
  "mova	%O0,r0"
  [(set_attr "in_delay_slot" "no")])

;; case instruction for switch statements.

;; Operand 0 is index
;; operand 1 is the minimum bound
;; operand 2 is the maximum bound - minimum bound + 1
;; operand 3 is CODE_LABEL for the table;
;; operand 4 is the CODE_LABEL to go to if index out of range.

;; ??? There should be a barrier after the jump at the end.

(define_expand "casesi"
  [(set (match_dup 5) (match_operand:SI 0 "arith_reg_operand" ""))
   (set (match_dup 5) (minus:SI (match_dup 5)
				(match_operand:SI 1 "arith_operand" "")))
   (set (reg:SI 18)
	(gtu:SI (match_dup 5)
		(match_operand:SI 2 "arith_reg_operand" "")))
   (set (pc)
	(if_then_else (eq (reg:SI 18)
			  (const_int 1))
		      (label_ref (match_operand 4 "" ""))
		      (pc)))
   (set (match_dup 6) (match_dup 5))
   (set (match_dup 6) (ashift:SI (match_dup 6) (match_dup 7)))
   (set (reg:SI 0) (unspec [(label_ref (match_operand 3 "" ""))] 1))
   (parallel[(set (reg:SI 0) (plus:SI (reg:SI 0)
				      (mem:HI (plus:SI (reg:SI 0)
						       (match_dup 6)))))
	     (set (match_dup 6) (mem:HI (plus:SI (reg:SI 0) (match_dup 6))))])
   (set (pc) (reg:SI 0))]
  ""
  "
{
  operands[1] = copy_to_mode_reg (SImode, operands[1]);
  operands[2] = copy_to_mode_reg (SImode, operands[2]);
  operands[5] = gen_reg_rtx (SImode);
  operands[6] = gen_reg_rtx (SImode);
  operands[7] = GEN_INT (TARGET_BIGTABLE  ? 2 : 1);
}")

(define_insn "casesi_worker"
  [(set (reg:SI 0)
	(plus:SI (reg:SI 0)
		 (mem:HI (plus:SI (reg:SI 0)
				  (match_operand:SI 0 "arith_reg_operand" "=r")))))
   (set (match_dup 0) (mem:HI (plus:SI (reg:SI 0)
				       (match_dup 0))))]
  ""
  "*
	if (TARGET_BIGTABLE)
		return \"mov.l	@(r0,%0),%0\;add	%0,r0\";
	else
	   	return \"mov.w	@(r0,%0),%0\;add	%0,r0\";"
  [(set_attr "length" "4")])

(define_insn "return"
  [(return)]
  "reload_completed"
  "%@	%#"
  [(set_attr "type" "return")
   (set_attr "needs_delay_slot" "yes")])

(define_expand "prologue"
  [(const_int 0)]
  ""
  "sh_expand_prologue (); DONE;")

(define_expand "epilogue"
  [(return)]
  ""
  "sh_expand_epilogue ();")

(define_insn "blockage"
  [(unspec_volatile [(const_int 0)] 0)]
  ""
  ""
  [(set_attr "length" "0")])

;; ------------------------------------------------------------------------
;; Scc instructions
;; ------------------------------------------------------------------------

(define_insn "movt"
  [(set (match_operand:SI 0 "arith_reg_operand" "=r")
	(eq:SI (reg:SI 18) (const_int 1)))]
  ""
  "movt	%0")

(define_expand "seq"
  [(set (match_operand:SI 0 "arith_reg_operand" "")
	(match_dup 1))]
  ""
  "operands[1] = prepare_scc_operands (EQ);")

(define_expand "slt"
  [(set (match_operand:SI 0 "arith_reg_operand" "")
	(match_dup 1))]
  ""
  "operands[1] = prepare_scc_operands (LT);")

(define_expand "sle"
  [(set (match_operand:SI 0 "arith_reg_operand" "")
	(match_dup 1))]
  ""
  "operands[1] = prepare_scc_operands (LE);")

(define_expand "sgt"
  [(set (match_operand:SI 0 "arith_reg_operand" "")
	(match_dup 1))]
  ""
  "operands[1] = prepare_scc_operands (GT);")

(define_expand "sge"
  [(set (match_operand:SI 0 "arith_reg_operand" "")
	(match_dup 1))]
  ""
  "operands[1] = prepare_scc_operands (GE);")

(define_expand "sgtu"
  [(set (match_operand:SI 0 "arith_reg_operand" "")
	(match_dup 1))]
  ""
  "operands[1] = prepare_scc_operands (GTU);")

(define_expand "sltu"
  [(set (match_operand:SI 0 "arith_reg_operand" "")
	(match_dup 1))]
  ""
  "operands[1] = prepare_scc_operands (LTU);")

(define_expand "sleu"
  [(set (match_operand:SI 0 "arith_reg_operand" "")
	(match_dup 1))]
  ""
  "operands[1] = prepare_scc_operands (LEU);")

(define_expand "sgeu"
  [(set (match_operand:SI 0 "arith_reg_operand" "")
	(match_dup 1))]
  ""
  "operands[1] = prepare_scc_operands (GEU);")

(define_expand "sne"
  [(set (match_operand:SI 0 "arith_reg_operand" "")
	(match_dup 1))
   (set (match_dup 0) (xor:SI (match_dup 0) (const_int 1)))]
  ""
  "operands[1] = prepare_scc_operands (EQ);")

;; -------------------------------------------------------------------------
;; Instructions to cope with inline literal tables
;; -------------------------------------------------------------------------

; 2 byte integer in line

(define_insn "consttable_2"
 [(unspec_volatile [(match_operand:SI 0 "general_operand" "=g")] 2)]
 ""
 "*
{
  assemble_integer (operands[0], 2, 1);
  return \"\";
}"
 [(set_attr "length" "2")
 (set_attr "in_delay_slot" "no")])

; 4 byte integer in line

(define_insn "consttable_4"
 [(unspec_volatile [(match_operand:SI 0 "general_operand" "=g")] 4)]
 ""
 "*
{
  assemble_integer (operands[0], 4, 1);
  return \"\";
}"
 [(set_attr "length" "4")
  (set_attr "in_delay_slot" "no")])

; 8 byte integer in line

(define_insn "consttable_8"
 [(unspec_volatile [(match_operand:SI 0 "general_operand" "=g")] 6)]
 ""
 "*
{
  assemble_integer (operands[0], 8, 1);
  return \"\";
}"
 [(set_attr "length" "8")
  (set_attr "in_delay_slot" "no")])

; align to a two byte boundary

(define_insn "align_2"
 [(unspec_volatile [(const_int 0)] 10)]
 ""
 ".align 1"
 [(set_attr "length" "0")
  (set_attr "in_delay_slot" "no")])

; align to a four byte boundary

(define_insn "align_4"
 [(unspec_volatile [(const_int 0)] 5)]
 ""
 ".align 2"
 [(set_attr "in_delay_slot" "no")])

; emitted at the end of the literal table, used to emit the
; 32bit branch labels if needed.

(define_insn "consttable_end"
  [(unspec_volatile [(const_int 0)] 11)]
  ""
  "* return output_jump_label_table ();"
  [(set_attr "in_delay_slot" "no")])

;; -------------------------------------------------------------------------
;; Misc
;; -------------------------------------------------------------------------

;; String/block move insn.

(define_expand "movstrsi"
  [(parallel [(set (mem:BLK (match_operand:BLK 0 "" ""))
		   (mem:BLK (match_operand:BLK 1 "" "")))
	      (use (match_operand:SI 2 "nonmemory_operand" ""))
	      (use (match_operand:SI 3 "immediate_operand" ""))
	      (clobber (reg:SI 17))
	      (clobber (reg:SI 4))
	      (clobber (reg:SI 5))
	      (clobber (reg:SI 0))])]
  ""
  "
{
  if(expand_block_move (operands))
     DONE;
  else FAIL;
}")

(define_insn "block_move_real"
  [(parallel [(set (mem:BLK (reg:SI 4))
		   (mem:BLK (reg:SI 5)))
	      (use (match_operand:SI 0 "arith_reg_operand" "r"))
	      (clobber (reg:SI 17))
	      (clobber (reg:SI 0))])]
  ""
  "jsr	@%0%#"
  [(set_attr "type" "sfunc")
   (set_attr "needs_delay_slot" "yes")])

(define_insn "block_lump_real"
  [(parallel [(set (mem:BLK (reg:SI 4))
		   (mem:BLK (reg:SI 5)))
	      (use (match_operand:SI 0 "arith_reg_operand" "r"))
	      (use (reg:SI 6))
	      (clobber (reg:SI 17))
	      (clobber (reg:SI 4))
	      (clobber (reg:SI 5))
	      (clobber (reg:SI 6))
	      (clobber (reg:SI 0))])]
  ""
  "jsr	@%0%#"
  [(set_attr "type" "sfunc")
   (set_attr "needs_delay_slot" "yes")])

;; -------------------------------------------------------------------------
;; Peepholes
;; -------------------------------------------------------------------------

;; This matches cases where a stack pointer increment at the start of the
;; epilogue combines with a stack slot read loading the return value.

(define_peephole
  [(set (match_operand:SI 0 "arith_reg_operand" "")
	(mem:SI (match_operand:SI 1 "arith_reg_operand" "")))
   (set (match_dup 1) (plus:SI (match_dup 1) (const_int 4)))]
  "REGNO (operands[1]) != REGNO (operands[0])"
  "mov.l	@%1+,%0")

;; See the comment on the dt combiner pattern above.

(define_peephole
  [(set (match_operand:SI 0 "arith_reg_operand" "=r")
	(plus:SI (match_dup 0)
		 (const_int -1)))
   (set (reg:SI 18)
	(eq:SI (match_dup 0)
	       (const_int 0)))]
  "TARGET_SH2"
  "dt	%0")
