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


;; ??? Should prepend a * to all pattern names which are not used.
;; This will make the compiler smaller, and rebuilds after changes faster.

;; ??? Should be enhanced to include support for many more GNU superoptimizer
;; sequences.  Especially the sequences for arithmetic right shifts.

;; ??? Should check all DImode patterns for consistency and usefulness.

;; ??? Should add support for using BSR for short function calls.

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

;; ??? The cmp/str instruction is not supported.  Perhaps it can be used
;; for a str* inline function.

;; 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,sh3e"
  (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, which can't be put into delay slot of rts
;; pstore	store of pr reg, which can't be put into delay slot of jsr
;; pcload	pc relative load of constant value
;; rte		return from exception
;; sfunc	special function call with known used registers
;; call		function call
;; fp		floating point
;; fdiv		floating point divide (or square root)

(define_attr "type"
 "cbranch,jump,arith,other,load,store,move,smpy,dmpy,return,pload,pstore,pcload,rte,sfunc,call,fp,fdiv"
  (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}])

;; Load and store instructions save a cycle if they are aligned on a
;; four byte boundary.  Using a function unit for stores encourages
;; gcc to separate load and store instructions by one instruction,
;; which makes it more likely that the linker will be able to word
;; align them when relaxing.
(define_function_unit "memory" 1 0
  (eq_attr "type" "load,pcload,pload,store,pstore") 2 2)

;; ??? These are approximations.
(define_function_unit "mpy"    1 0 (eq_attr "type" "smpy") 2 2)
(define_function_unit "mpy"    1 0 (eq_attr "type" "dmpy") 3 3)

(define_function_unit "fp"     1 0 (eq_attr "type" "fp") 2 1)
(define_function_unit "fp"     1 0 (eq_attr "type" "fdiv") 13 12)

; 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.
;; ??? On the SH3, the rte instruction does not use the stack, so a pop
;; instruction can go in the delay slot.

;; Since a normal return (rts) implicitly uses the PR register,
;; we can't allow PR register loads in an rts delay slot.

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

;; Since a call implicitly uses the PR register, we can't allow
;; a PR register store in a jsr delay slot.

(define_delay
  (ior (eq_attr "type" "call") (eq_attr "type" "sfunc"))
  [(and (eq_attr "in_delay_slot" "yes")
	(eq_attr "type" "!pstore")) (nil) (nil)])

;; Say that we have annulled true branches, since this gives smaller and
;; faster code when branches are predicted as not taken.

;; ??? Branches which are out-of-range actually have two delay slots,
;; the first is either always executed or else annulled false, and the
;; second is always annulled false.  Handling these differently from
;; in range branches would give better code.

(define_delay
  (and (eq_attr "type" "cbranch")
       (eq_attr "cpu" "sh2,sh3"))
  [(eq_attr "in_delay_slot" "yes") (eq_attr "in_delay_slot" "yes") (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.
;; Probably should use r0 for mem/imm compares, but force constant into a
;; register for pseudo/imm compares.

(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 expand.

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

;; ??? This should be a define expand.

(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_internal"
  [(set (match_operand:SI 0 "arith_reg_operand" "=r")
	(minus:SI (match_operand:SI 1 "arith_reg_operand" "0")
		  (match_operand:SI 2 "arith_reg_operand" "r")))]
  ""
  "sub	%2,%0"
  [(set_attr "type" "arith")])

;; Convert `constant - reg' to `neg rX; add rX, #const' since this
;; will sometimes save one instruction.  Otherwise we might get
;; `mov #const, rY; sub rY,rX; mov rX, rY' if the source and dest regs
;; are the same.

(define_expand "subsi3"
  [(set (match_operand:SI 0 "arith_reg_operand" "")
	(minus:SI (match_operand:SI 1 "arith_operand" "")
		  (match_operand:SI 2 "arith_reg_operand" "")))]
  ""
  "
{
  if (GET_CODE (operands[1]) == CONST_INT)
    {
      emit_insn (gen_negsi2 (operands[0], operands[2]));
      emit_insn (gen_addsi3 (operands[0], operands[0], operands[1]));
      DONE;
    }
}")

;; -------------------------------------------------------------------------
;; 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 pseudo-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 0 "arith_reg_operand" "r"))
		 (zero_extend:SI (match_operand:HI 1 "arith_reg_operand" "r"))))]
  ""
  "mulu	%1,%0"
  [(set_attr "type" "smpy")])

(define_insn ""
  [(set (reg:SI 21)
	(mult:SI (sign_extend:SI
		  (match_operand:HI 0 "arith_reg_operand" "r"))
		 (sign_extend:SI
		  (match_operand:HI 1 "arith_reg_operand" "r"))))]
  ""
  "muls	%1,%0"
  [(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 "mulsidi3_i"
  [(set (reg:DI 20)
	(mult:DI (sign_extend:DI (match_operand:SI 0 "arith_reg_operand" "r"))
		 (sign_extend:DI (match_operand:SI 1 "arith_reg_operand" "r"))))]
  "TARGET_SH2"
  "dmuls.l	%1,%0"
  [(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"
  "
{
  /* We must swap the two words when copying them from MACH/MACL to the
     output register.  */
  if (TARGET_LITTLE_ENDIAN)
    {
      rtx low_dst = operand_subword (operands[0], 0, 1, DImode);
      rtx high_dst = operand_subword (operands[0], 1, 1, DImode);

      emit_insn (gen_mulsidi3_i (operands[1], operands[2]));

      emit_insn (gen_rtx (CLOBBER, VOIDmode, operands[0]));
      emit_move_insn (low_dst, gen_rtx (REG, SImode, 21));
      emit_move_insn (high_dst, gen_rtx (REG, SImode, 20));
      DONE;
    }
}")

(define_insn "umulsidi3_i"
  [(set (reg:DI 20)
	(mult:DI (zero_extend:DI (match_operand:SI 0 "arith_reg_operand" "r"))
		 (zero_extend:DI (match_operand:SI 1 "arith_reg_operand" "r"))))]
  "TARGET_SH2"
  "dmulu.l	%1,%0"
  [(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"
  "
{
  /* We must swap the two words when copying them from MACH/MACL to the
     output register.  */
  if (TARGET_LITTLE_ENDIAN)
    {
      rtx low_dst = operand_subword (operands[0], 0, 1, DImode);
      rtx high_dst = operand_subword (operands[0], 1, 1, DImode);

      emit_insn (gen_umulsidi3_i (operands[1], operands[2]));

      emit_insn (gen_rtx (CLOBBER, VOIDmode, operands[0]));
      emit_move_insn (low_dst, gen_rtx (REG, SImode, 21));
      emit_move_insn (high_dst, gen_rtx (REG, SImode, 20));
      DONE;
    }
}")

(define_insn ""
  [(set (reg:SI 20)
	(truncate:SI
	 (lshiftrt:DI (mult:DI (sign_extend:DI (match_operand:SI 0 "arith_reg_operand" "r"))
			       (sign_extend:DI (match_operand:SI 1 "arith_reg_operand" "r")))
		      (const_int 32))))
   (clobber (reg:SI 21))]
  "TARGET_SH2"
  "dmuls.l	%1,%0"
  [(set_attr "type" "dmpy")])

(define_expand "smulsi3_highpart"
  [(parallel [(set (reg:SI 20)
		   (truncate:SI
		    (lshiftrt:DI (mult:DI (sign_extend:DI (match_operand:SI 1 "arith_reg_operand" ""))
					  (sign_extend:DI (match_operand:SI 2 "arith_reg_operand" "")))
				 (const_int 32))))
	      (clobber (reg:SI 21))])
   (set (match_operand:SI 0 "arith_reg_operand" "")
	(reg:SI 20))]
  "TARGET_SH2"
  "")

(define_insn ""
  [(set (reg:SI 20)
	(truncate:SI
	 (lshiftrt:DI (mult:DI (zero_extend:DI (match_operand:SI 0 "arith_reg_operand" "r"))
			       (zero_extend:DI (match_operand:SI 1 "arith_reg_operand" "r")))
		      (const_int 32))))
   (clobber (reg:SI 21))]
  "TARGET_SH2"
  "dmulu.l	%1,%0"
  [(set_attr "type" "dmpy")])

(define_expand "umulsi3_highpart"
  [(parallel [(set (reg:SI 20)
		   (truncate:SI
		    (lshiftrt:DI (mult:DI (zero_extend:DI (match_operand:SI 1 "arith_reg_operand" ""))
					  (zero_extend:DI (match_operand:SI 2 "arith_reg_operand" "")))
				 (const_int 32))))
	      (clobber (reg:SI 21))])
   (set (match_operand:SI 0 "arith_reg_operand" "")
	(reg:SI 20))]
  "TARGET_SH2"
  "")

;; -------------------------------------------------------------------------
;; 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_d"
  [(set (match_operand:SI 0 "arith_reg_operand" "=r")
	(ashift:SI (match_operand:SI 1 "arith_reg_operand" "0")
		   (match_operand:SI 2 "arith_reg_operand" "r")))]
  "TARGET_SH3"
  "shld	%2,%0")

(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 "const_int_operand" "M,K")))]
  "CONST_OK_FOR_K (INTVAL (operands[2]))"
  "@
	add	%0,%0
	shll%O2	%0")

(define_insn "ashlhi3_k"
  [(set (match_operand:HI 0 "arith_reg_operand" "=r,r")
	(ashift:HI (match_operand:HI 1 "arith_reg_operand" "0,0")
		   (match_operand:HI 2 "const_int_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 "const_int_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 "const_int_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 (TARGET_SH3 && arith_reg_operand (operands[2], GET_MODE (operands[2])))
    {
      emit_insn (gen_ashlsi3_d (operands[0], operands[1], operands[2]));
      DONE;
    }
  if (! immediate_operand (operands[2], GET_MODE (operands[2])))
    FAIL;
}")

(define_insn "ashlhi3"
  [(set (match_operand:HI 0 "arith_reg_operand" "=r")
	(ashift:HI (match_operand:HI 1 "arith_reg_operand" "0")
		   (match_operand:HI 2 "const_int_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")]
	      (const_string "6")))
   (set_attr "type" "arith")])

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

;
; 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 "const_int_operand" "M")))
   (clobber (reg:SI 18))]
  "INTVAL (operands[2]) == 1"
  "shar	%0"
  [(set_attr "type" "arith")])

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

;; ??? This should be a define expand.

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

;; ??? This should be a define expand.

(define_insn "ashrsi2_31"
  [(set (match_operand:SI 0 "arith_reg_operand" "=r")
        (ashiftrt:SI (match_operand:SI 1 "arith_reg_operand" "0")
                     (const_int 31)))
   (clobber (reg:SI 18))]
  ""
  "@
   shll	%0\;subc	%0,%0"
  [(set_attr "length" "4")])

(define_insn "ashrsi3_d"
  [(set (match_operand:SI 0 "arith_reg_operand" "=r")
	(ashiftrt:SI (match_operand:SI 1 "arith_reg_operand" "0")
		     (neg:SI (match_operand:SI 2 "arith_reg_operand" "r"))))]
  "TARGET_SH3"
  "shad	%2,%0")

(define_insn "ashrsi3_n"
  [(set (reg:SI 4)
	(ashiftrt:SI (reg:SI 4)
		     (match_operand:SI 0 "const_int_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

(define_insn "lshrsi3_d"
  [(set (match_operand:SI 0 "arith_reg_operand" "=r")
	(lshiftrt:SI (match_operand:SI 1 "arith_reg_operand" "0")
		     (neg:SI (match_operand:SI 2 "arith_reg_operand" "r"))))]
  "TARGET_SH3"
  "shld	%2,%0")

;;  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 "const_int_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 "const_int_operand" "K")))]
  "CONST_OK_FOR_K (INTVAL (operands[2]))
   && ! CONST_OK_FOR_M (INTVAL (operands[2]))"
  "shlr%O2	%0")

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

(define_insn "lshrhi3_k"
  [(set (match_operand:HI 0 "arith_reg_operand" "=r")
	(lshiftrt:HI (match_operand:HI 1 "arith_reg_operand" "0")
		     (match_operand:HI 2 "const_int_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 "const_int_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 "const_int_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 (TARGET_SH3 && arith_reg_operand (operands[2], GET_MODE (operands[2])))
    {
      rtx count = copy_to_mode_reg (SImode, operands[2]);
      emit_insn (gen_negsi2 (count, count));
      emit_insn (gen_lshrsi3_d (operands[0], operands[1], count));
      DONE;
    }
  if (! immediate_operand (operands[2], GET_MODE (operands[2])))
    FAIL;
}")

(define_insn "lshrhi3"
  [(set (match_operand:HI 0 "arith_reg_operand" "=r")
	(lshiftrt:HI (match_operand:HI 1 "arith_reg_operand" "0")
		     (match_operand:HI 2 "const_int_operand" "n")))
   (clobber (reg:SI 18))]
  ""
  "#"
;; ??? length attribute is sometimes six instead of four.
  [(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")]
	      (const_string "6")))
   (set_attr "type" "arith")])

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

;; ??? This should be a define expand.

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

;; ??? This should be a define expand.

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

;; ??? This should be a define expand.

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

;; combined left/right shift

(define_split
  [(set (match_operand:SI 0 "register_operand" "")
	(and:SI (ashift:SI (match_operand:SI 1 "register_operand" "")
			   (match_operand:SI 2 "const_int_operand" "n"))
		(match_operand:SI 3 "const_int_operand" "n")))]
  "(unsigned)INTVAL (operands[2]) < 32"
  [(use (reg:SI 0))]
  "if (gen_shl_and (operands[0], operands[2], operands[3], operands[1])) FAIL;
   DONE;")

(define_split
  [(set (match_operand:SI 0 "register_operand" "")
	(and:SI (ashift:SI (match_operand:SI 1 "register_operand" "")
			   (match_operand:SI 2 "const_int_operand" "n"))
		(match_operand:SI 3 "const_int_operand" "n")))
   (clobber (reg:SI 18))]
  "(unsigned)INTVAL (operands[2]) < 32"
  [(use (reg:SI 0))]
  "if (gen_shl_and (operands[0], operands[2], operands[3], operands[1])) FAIL;
   DONE;")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(and:SI (ashift:SI (match_operand:SI 1 "register_operand" "0")
			   (match_operand:SI 2 "const_int_operand" "n"))
		(match_operand:SI 3 "const_int_operand" "n")))
   (clobber (reg:SI 18))]
  "shl_and_kind (operands[2], operands[3], 0) == 1"
 "#"
  [(set (attr "length")
	(cond [(eq (symbol_ref "shl_and_length (insn)") (const_int 2))
	       (const_string "4")
	       (eq (symbol_ref "shl_and_length (insn)") (const_int 3))
	       (const_string "6")
	       (eq (symbol_ref "shl_and_length (insn)") (const_int 4))
	       (const_string "8")
	       (eq (symbol_ref "shl_and_length (insn)") (const_int 5))
	       (const_string "10")
	       (eq (symbol_ref "shl_and_length (insn)") (const_int 6))
	       (const_string "12")
	       (eq (symbol_ref "shl_and_length (insn)") (const_int 7))
	       (const_string "14")
	       (eq (symbol_ref "shl_and_length (insn)") (const_int 8))
	       (const_string "16")]
	      (const_string "18")))
   (set_attr "type" "arith")])

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=z")
	(and:SI (ashift:SI (match_operand:SI 1 "register_operand" "0")
			   (match_operand:SI 2 "const_int_operand" "n"))
		(match_operand:SI 3 "const_int_operand" "n")))
   (clobber (reg:SI 18))]
  "shl_and_kind (operands[2], operands[3], 0) == 2"
 "#"
  [(set (attr "length")
	(cond [(eq (symbol_ref "shl_and_length (insn)") (const_int 2))
	       (const_string "4")
	       (eq (symbol_ref "shl_and_length (insn)") (const_int 3))
	       (const_string "6")
	       (eq (symbol_ref "shl_and_length (insn)") (const_int 4))
	       (const_string "8")]
	      (const_string "10")))
   (set_attr "type" "arith")])

;; shift left / and combination with a scratch register: The combine pass
;; does not accept the individual instructions, even though they are
;; cheap.  But it needs a precise description so that it is usable after
;; reload.
(define_insn "and_shl_scratch"
  [(set (match_operand:SI 0 "register_operand" "=r,&r")
	(lshiftrt:SI (ashift:SI (and:SI (lshiftrt:SI (match_operand:SI 1 "register_operand" "r,0")
						     (match_operand:SI 2 "const_int_operand" "N,n"))
					(match_operand:SI 3 "" "0,r"))
				(match_operand:SI 4 "const_int_operand" "n,n"))
		     (match_operand:SI 5 "const_int_operand" "n,n")))
   (clobber (reg:SI 18))]
  ""
  "#"
  [(set (attr "length")
	(cond [(eq (symbol_ref "shl_and_scr_length (insn)") (const_int 2))
	       (const_string "4")
	       (eq (symbol_ref "shl_and_scr_length (insn)") (const_int 3))
	       (const_string "6")
	       (eq (symbol_ref "shl_and_scr_length (insn)") (const_int 4))
	       (const_string "6")
	       (eq (symbol_ref "shl_and_scr_length (insn)") (const_int 5))
	       (const_string "10")]
	      (const_string "12")))
   (set_attr "type" "arith")])

(define_split
  [(set (match_operand:SI 0 "register_operand" "=r,&r")
	(lshiftrt:SI (ashift:SI (and:SI (lshiftrt:SI (match_operand:SI 1 "register_operand" "r,0")
						     (match_operand:SI 2 "const_int_operand" "N,n"))
					(match_operand:SI 3 "register_operand" "0,r"))
				(match_operand:SI 4 "const_int_operand" "n,n"))
		     (match_operand:SI 5 "const_int_operand" "n,n")))
   (clobber (reg:SI 18))]
  ""
  [(use (reg:SI 0))]
  "
{
  rtx and_source = operands[1];

  if (INTVAL (operands[2]))
    {
      gen_shifty_op (LSHIFTRT, operands);
      and_source = operands[3];
    }
  emit_insn (gen_andsi3 (operands[0], operands[0], and_source));
  operands[2] = operands[4];
  gen_shifty_op (ASHIFT, operands);
  if (INTVAL (operands[5]))
    {
      operands[2] = operands[5];
      gen_shifty_op (LSHIFTRT, operands);
    }
  DONE;
}")

;; signed left/right shift combination.
(define_split
  [(set (match_operand:SI 0 "register_operand" "=r")
        (sign_extract:SI (ashift:SI (match_operand:SI 1 "register_operand" "r")
				    (match_operand:SI 2 "const_int_operand" "n"))
			 (match_operand:SI 3 "const_int_operand" "n")
			 (const_int 0)))
   (clobber (reg:SI 18))]
  ""
  [(use (reg:SI 0))]
  "if (gen_shl_sext (operands[0], operands[2], operands[3], operands[1])) FAIL;
   DONE;")

(define_insn "shl_sext_ext"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (sign_extract:SI (ashift:SI (match_operand:SI 1 "register_operand" "0")
				    (match_operand:SI 2 "const_int_operand" "n"))
			 (match_operand:SI 3 "const_int_operand" "n")
			 (const_int 0)))
   (clobber (reg:SI 18))]
  "shl_sext_kind (operands[2], operands[3], 0) <= 5"
  "#"
  [(set (attr "length")
	(cond [(eq (symbol_ref "shl_sext_length (insn)") (const_int 1))
	       (const_string "2")
	       (eq (symbol_ref "shl_sext_length (insn)") (const_int 2))
	       (const_string "4")
	       (eq (symbol_ref "shl_sext_length (insn)") (const_int 3))
	       (const_string "6")
	       (eq (symbol_ref "shl_sext_length (insn)") (const_int 4))
	       (const_string "8")
	       (eq (symbol_ref "shl_sext_length (insn)") (const_int 5))
	       (const_string "10")
	       (eq (symbol_ref "shl_sext_length (insn)") (const_int 6))
	       (const_string "12")
	       (eq (symbol_ref "shl_sext_length (insn)") (const_int 7))
	       (const_string "14")
	       (eq (symbol_ref "shl_sext_length (insn)") (const_int 8))
	       (const_string "16")]
	      (const_string "18")))
    (set_attr "type" "arith")])

(define_insn "shl_sext_sub"
  [(set (match_operand:SI 0 "register_operand" "=z")
        (sign_extract:SI (ashift:SI (match_operand:SI 1 "register_operand" "0")
				    (match_operand:SI 2 "const_int_operand" "n"))
			 (match_operand:SI 3 "const_int_operand" "n")
			 (const_int 0)))
   (clobber (reg:SI 18))]
  "(shl_sext_kind (operands[2], operands[3], 0) & ~1) == 6"
  "#"
  [(set (attr "length")
	(cond [(eq (symbol_ref "shl_sext_length (insn)") (const_int 3))
	       (const_string "6")
	       (eq (symbol_ref "shl_sext_length (insn)") (const_int 4))
	       (const_string "8")
	       (eq (symbol_ref "shl_sext_length (insn)") (const_int 5))
	       (const_string "10")
	       (eq (symbol_ref "shl_sext_length (insn)") (const_int 6))
	       (const_string "12")]
	      (const_string "14")))
    (set_attr "type" "arith")])


;; -------------------------------------------------------------------------
;; 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))]
  ""
  "
{
  int low_word = (TARGET_LITTLE_ENDIAN ? 0 : 1);
  int high_word = (TARGET_LITTLE_ENDIAN ? 1 : 0);

  rtx low_src = operand_subword (operands[1], low_word, 0, DImode);
  rtx high_src = operand_subword (operands[1], high_word, 0, DImode);

  rtx low_dst = operand_subword (operands[0], low_word, 1, DImode);
  rtx high_dst = operand_subword (operands[0], high_word, 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
;; -------------------------------------------------------------------------

;; ??? This should be a define expand.
;; ??? Or perhaps it should be dropped?

(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,l,x"))]
  ""
  "@
	mov.l	%0,@-r15
	sts.l	%0,@-r15
	sts.l	%0,@-r15"
  [(set_attr "type" "store,pstore,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 "push_e"
  [(set (mem:SF (pre_dec:SI (reg:SI 15)))
	(match_operand:SF 0 "register_operand" "r,f,y"))]
  "TARGET_SH3E"
  "@
	mov.l	%0,@-r15
	fmov.s	%0,@-r15
	sts.l	%0,@-r15"
  [(set_attr "type" "store")
   (set_attr "hit_stack" "yes")])

(define_insn "pop_e"
  [(set (match_operand:SF 0 "register_operand" "=r,f,y")
	(mem:SF (post_inc:SI (reg:SI 15))))]
  "TARGET_SH3E"
  "@
	mov.l	@r15+,%0
	fmov.s	@r15+,%0
	lds.l	@r15+,%0"
  [(set_attr "type" "load")
   (set_attr "hit_stack" "yes")])

;; These two patterns can happen as the result of optimization, when
;; comparisons get simplified to a move of zero or 1 into the T reg.
;; They don't disappear completely, because the T reg is a fixed hard reg.

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

(define_insn "sett"
  [(set (reg:SI 18) (const_int 1))]
  ""
  "sett")

;; t/z is first, so that it will be preferred over r/r when reloading a move
;; of a pseudo-reg into the T reg
(define_insn "movsi_i"
  [(set (match_operand:SI 0 "general_movdst_operand" "=t,r,r,r,r,r,m,<,xl,xl,r")
	(match_operand:SI 1 "general_movsrc_operand" "z,Q,rI,m,xl,t,r,xl,r,>,i"))]
  "
   ! TARGET_SH3E &&
   (register_operand (operands[0], SImode)
    || register_operand (operands[1], SImode))"
  "@
	tst	%1,%1\;rotcl	%1\;xor	#1,%1\;rotcr	%1
	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
	fake	%1,%0"
  [(set_attr "type" "move,pcload,move,load,move,store,store,move,load,move,move")
   (set_attr "length" "8,*,*,*,*,*,*,*,*,*,*")])

;; t/z is first, so that it will be preferred over r/r when reloading a move
;; of a pseudo-reg into the T reg
;; ??? This allows moves from macl to fpul to be recognized, but these moves
;; will require a reload.
(define_insn "movsi_ie"
  [(set (match_operand:SI 0 "general_movdst_operand" "=t,r,r,r,r,r,m,<,xl,xl,r,y,r")
	(match_operand:SI 1 "general_movsrc_operand" "z,Q,rI,m,xl,t,r,xl,r,>,i,r,y"))]
  "TARGET_SH3E
   && (register_operand (operands[0], SImode)
       || register_operand (operands[1], SImode))"
  "@
	tst	%1,%1\;rotcl	%1\;xor	#1,%1\;rotcr	%1
	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
	fake	%1,%0
	lds	%1,%0
	sts	%1,%0"
  [(set_attr "type" "move,pcload,move,load,move,store,store,move,load,move,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; }")

;; ??? This should be a define expand.

(define_insn ""
  [(set (match_operand:DI 0 "general_movdst_operand" "=r,r,r,m,r,r")
	(match_operand:DI 1 "general_movsrc_operand" "Q,r,m,r,i,x"))]
  "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,move")])

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

(define_split
  [(set (match_operand:DI 0 "general_movdst_operand" "")
	(match_operand:DI 1 "general_movsrc_operand" ""))]
  "reload_completed"
  [(set (match_dup 2) (match_dup 3))
   (set (match_dup 4) (match_dup 5))]
  "
{
  int regno;

  if ((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))
    FAIL;

  if (GET_CODE (operands[0]) == REG)
    regno = REGNO (operands[0]);
  else if (GET_CODE (operands[0]) == SUBREG)
    regno = REGNO (SUBREG_REG (operands[0])) + SUBREG_WORD (operands[0]);
  else if (GET_CODE (operands[0]) == MEM)
    regno = -1;

  if (regno == -1
      || ! refers_to_regno_p (regno, regno + 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; }")

;; ??? This should be a define expand.

(define_insn "movdf_k"
  [(set (match_operand:DF 0 "general_movdst_operand" "=r,r,r,m")
	(match_operand:DF 1 "general_movsrc_operand" "r,FQ,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,pcload,load,store")])

;; If the output is a register and the input is memory or a register, 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" ""))]
  "reload_completed"
  [(set (match_dup 2) (match_dup 3))
   (set (match_dup 4) (match_dup 5))]
  "
{
  int regno;

  if ((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))
    FAIL;

  if (GET_CODE (operands[0]) == REG)
    regno = REGNO (operands[0]);
  else if (GET_CODE (operands[0]) == SUBREG)
    regno = REGNO (SUBREG_REG (operands[0])) + SUBREG_WORD (operands[0]);
  else if (GET_CODE (operands[0]) == MEM)
    regno = -1;

  if (regno == -1
      || ! refers_to_regno_p (regno, regno + 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;
}")

;; If a base address generated by LEGITIMIZE_ADDRESS for SImode is
;; used only once, let combine add in the index again.

(define_split
  [(set (match_operand:SI 0 "register_operand" "")
	(match_operand:SI 1 "" ""))
   (clobber (match_operand 2 "register_operand" ""))]
  "! reload_in_progress && ! reload_completed"
  [(use (reg:SI 0))]
  "
{
  rtx addr, reg, const_int;

  if (GET_CODE (operands[1]) != MEM)
    FAIL;
  addr = XEXP (operands[1], 0);
  if (GET_CODE (addr) != PLUS)
    FAIL;
  reg = XEXP (addr, 0);
  const_int = XEXP (addr, 1);
  if (GET_CODE (reg) != REG || GET_CODE (const_int) != CONST_INT)
    FAIL;
  emit_move_insn (operands[2], const_int);
  emit_move_insn (operands[0],
		  change_address (operands[1], VOIDmode,
				  gen_rtx (PLUS, SImode, reg, operands[2])));
  DONE;
}")

(define_split
  [(set (match_operand:SI 1 "" "")
	(match_operand:SI 0 "register_operand" ""))
   (clobber (match_operand 2 "register_operand" ""))]
  "! reload_in_progress && ! reload_completed"
  [(use (reg:SI 0))]
  "
{
  rtx addr, reg, const_int;

  if (GET_CODE (operands[1]) != MEM)
    FAIL;
  addr = XEXP (operands[1], 0);
  if (GET_CODE (addr) != PLUS)
    FAIL;
  reg = XEXP (addr, 0);
  const_int = XEXP (addr, 1);
  if (GET_CODE (reg) != REG || GET_CODE (const_int) != CONST_INT)
    FAIL;
  emit_move_insn (operands[2], const_int);
  emit_move_insn (change_address (operands[1], VOIDmode,
				  gen_rtx (PLUS, SImode, reg, operands[2])),
		  operands[0]);
  DONE;
}")

(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,r,m,l,r")
	(match_operand:SF 1 "general_movsrc_operand"  "r,I,FQ,m,r,r,l"))]
  "
   ! TARGET_SH3E &&
   (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
	mov.l	%1,%0
	lds	%1,%0
	sts	%1,%0"
  [(set_attr "type" "move,move,pcload,load,store,move,move")])

(define_insn "movsf_ie"
  [(set (match_operand:SF 0 "general_movdst_operand"
	 "=f,r,f,f,?f,f,m,r,r,m,!??r,!??f")
	(match_operand:SF 1 "general_movsrc_operand"
	  "f,r,G,H,FQ,m,f,FQ,m,r,f,r"))
   (clobber (match_scratch:SI 2 "=X,X,X,X,&z,X,X,X,X,X,X,X"))]

  "TARGET_SH3E
   && (arith_reg_operand (operands[0], SFmode)
       || arith_reg_operand (operands[1], SFmode))"
  "@
	fmov	%1,%0
	mov	%1,%0
	fldi0	%0
	fldi1	%0
	#
	fmov.s	%1,%0
	fmov.s	%1,%0
	mov.l	%1,%0
	mov.l	%1,%0
	mov.l	%1,%0
	flds	%1,fpul\;sts	fpul,%0
	lds	%1,fpul\;fsts	fpul,%0"
  [(set_attr "type" "move,move,fp,fp,pcload,load,store,pcload,load,store,move,fp")
   (set_attr "length" "*,*,*,*,4,*,*,*,*,*,4,4")])

(define_split
  [(set (match_operand:SF 0 "general_movdst_operand" "")
	(match_operand:SF 1 "general_movsrc_operand"  ""))
   (clobber (reg:SI 0))]
  "GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER"
  [(parallel [(set (match_dup 0) (match_dup 1))
	      (clobber (scratch:SI))])]
  "
{
  if (REGNO (operands[0]) >= FIRST_FP_REG && REGNO (operands[0]) <= LAST_FP_REG)
    {
      emit_insn (gen_mova (XEXP (operands[1], 0)));
      XEXP (operands[1], 0) = gen_rtx (REG, Pmode, 0);
    }
}")

(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;
  if (TARGET_SH3E)
    {
      emit_insn (gen_movsf_ie (operands[0], operands[1]));
      DONE;
    }
}")

(define_expand "reload_insf"
  [(parallel [(set (match_operand:SF 0 "register_operand" "=f")
		   (match_operand:SF 1 "immediate_operand" "FQ"))
	      (clobber (match_operand:SI 2 "register_operand" "=&z"))])]
  ""
  "")

;; ------------------------------------------------------------------------
;; Define the real conditional branch instructions.
;; ------------------------------------------------------------------------

(define_insn "branch_true"
  [(set (pc) (if_then_else (ne (reg:SI 18) (const_int 0))
			   (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 (eq (reg:SI 18) (const_int 0))
			   (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 (ne (reg:SI 18) (const_int 0))
			   (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 (eq (reg:SI 18) (const_int 0))
   			   (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 (ne (reg:SI 18) (const_int 0))
		      (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 (ne (reg:SI 18) (const_int 0))
		      (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 (ne (reg:SI 18) (const_int 0))
		      (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 (ne (reg:SI 18) (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "
{
  if (GET_MODE (sh_compare_op0) == SFmode)
    {
      rtx tmp = sh_compare_op0;
      sh_compare_op0 = sh_compare_op1;
      sh_compare_op1 = tmp;
      emit_insn (gen_bgt (operands[0]));
      DONE;
    }
  from_compare (operands, LT);
}")

(define_expand "ble"
  [(set (reg:SI 18) (gt:SI (match_dup 1) (match_dup 2)))
   (set (pc)
	(if_then_else (ne (reg:SI 18) (const_int 0))
		      (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 (ne (reg:SI 18) (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "
{
  if (GET_MODE (sh_compare_op0) == SFmode)
    {
      rtx tmp = sh_compare_op0;
      sh_compare_op0 = sh_compare_op1;
      sh_compare_op1 = tmp;
      emit_insn (gen_ble (operands[0]));
      DONE;
    }
  from_compare (operands, GE);
}")

(define_expand "bgtu"
  [(set (reg:SI 18) (gtu:SI (match_dup 1) (match_dup 2)))
   (set (pc)
	(if_then_else (ne (reg:SI 18) (const_int 0))
		      (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 (ne (reg:SI 18) (const_int 0))
				(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 (ne (reg:SI 18) (const_int 0))
		      (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 (ne (reg:SI 18) (const_int 0))
		      (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 "type" "call")
   (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 "type" "call")
   (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")])

;; This might seem redundant, but it helps us distinguish case table jumps
;; which can be present in structured code from indirect jumps which can not
;; be present in structured code.  This allows -fprofile-arcs to work.

(define_insn "*casesi_jump"
  [(set (pc)
	(match_operand:SI 0 "arith_reg_operand" "r"))
   (use (label_ref (match_operand 1 "" "")))]
  ""
  "jmp	@%0%#"
  [(set_attr "needs_delay_slot" "yes")])

;; Call subroutine returning any type.
;; ??? This probably doesn't work.

(define_expand "untyped_call"
  [(parallel [(call (match_operand 0 "" "")
		    (const_int 0))
	      (match_operand 1 "" "")
	      (match_operand 2 "" "")])]
  "TARGET_SH3E"
  "
{
  int i;

  emit_call_insn (gen_call (operands[0], const0_rtx, const0_rtx, const0_rtx));

  for (i = 0; i < XVECLEN (operands[2], 0); i++)
    {
      rtx set = XVECEXP (operands[2], 0, i);
      emit_move_insn (SET_DEST (set), SET_SRC (set));
    }

  /* The optimizer does not know that the call sets the function value
     registers we stored in the result block.  We avoid problems by
     claiming that all hard registers are used and clobbered at this
     point.  */
  emit_insn (gen_blockage ());

  DONE;
}")

;; ------------------------------------------------------------------------
;; 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 (ne (reg:SI 18)
			  (const_int 0))
		      (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))))])
   (parallel [(set (pc) (reg:SI 0))
	      (use (label_ref (match_dup 3)))])]
  ""
  "
{
  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))]
  ""
  "
{
  if (GET_MODE (sh_compare_op0) == SFmode)
    {
      emit_insn (gen_sgt (operands[0]));
      emit_insn (gen_xorsi3 (operands[0], operands[0], const1_rtx));
      DONE;
    }
  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))]
  ""
  "
{
  if (GET_MODE (sh_compare_op0) == SFmode)
    {
      emit_insn (gen_slt (operands[0]));
      emit_insn (gen_xorsi3 (operands[0], operands[0], const1_rtx));
      DONE;
    }
  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")])

; 4 byte floating point

(define_insn "consttable_sf"
 [(unspec_volatile [(match_operand:SF 0 "general_operand" "=g")] 4)]
 ""
 "*
{
  union real_extract u;
  bcopy ((char *) &CONST_DOUBLE_LOW (operands[0]), (char *) &u, sizeof u);
  assemble_real (u.d, SFmode);
  return \"\";
}"
 [(set_attr "length" "4")
  (set_attr "in_delay_slot" "no")])

; 8 byte floating point

(define_insn "consttable_df"
 [(unspec_volatile [(match_operand:DF 0 "general_operand" "=g")] 6)]
 ""
 "*
{
  union real_extract u;
  bcopy ((char *) &CONST_DOUBLE_LOW (operands[0]), (char *) &u, sizeof u);
  assemble_real (u.d, DFmode);
  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")])

;; -------------------------------------------------------------------------
;; Floating point instructions.
;; -------------------------------------------------------------------------

;; ??? All patterns should have a type attribute.

(define_insn "addsf3"
  [(set (match_operand:SF 0 "arith_reg_operand" "=f")
	(plus:SF (match_operand:SF 1 "arith_reg_operand" "%0")
		 (match_operand:SF 2 "arith_reg_operand" "f")))]
  "TARGET_SH3E"
  "fadd	%2,%0"
  [(set_attr "type" "fp")])

(define_insn "subsf3"
  [(set (match_operand:SF 0 "arith_reg_operand" "=f")
	(minus:SF (match_operand:SF 1 "arith_reg_operand" "0")
		  (match_operand:SF 2 "arith_reg_operand" "f")))]
  "TARGET_SH3E"
  "fsub	%2,%0"
  [(set_attr "type" "fp")])

(define_insn "mulsf3"
  [(set (match_operand:SF 0 "arith_reg_operand" "=f")
	(mult:SF (match_operand:SF 1 "arith_reg_operand" "%0")
		 (match_operand:SF 2 "arith_reg_operand" "f")))]
  "TARGET_SH3E"
  "fmul	%2,%0"
  [(set_attr "type" "fp")])

(define_insn "*macsf3"
  [(set (match_operand:SF 0 "arith_reg_operand" "=f")
	(plus:SF (mult:SF (match_operand:SF 1 "arith_reg_operand" "%w")
			  (match_operand:SF 2 "arith_reg_operand" "f"))
		 (match_operand:SF 3 "arith_reg_operand" "0")))]
  "TARGET_SH3E"
  "fmac	fr0,%2,%0"
  [(set_attr "type" "fp")])

(define_insn "divsf3"
  [(set (match_operand:SF 0 "arith_reg_operand" "=f")
	(div:SF (match_operand:SF 1 "arith_reg_operand" "0")
		(match_operand:SF 2 "arith_reg_operand" "f")))]
  "TARGET_SH3E"
  "fdiv	%2,%0"
  [(set_attr "type" "fdiv")])

;; ??? This is the right solution, but it fails because the movs[if] patterns
;; silently clobber FPUL (r22) for int<->fp moves.  Thus we can not explicitly
;; use FPUL here.
;;
;;(define_expand "floatsisf2"
;;  [(set (reg:SI 22)
;;	(match_operand:SI 1 "arith_reg_operand" ""))
;;   (set (match_operand:SF 0 "arith_reg_operand" "")
;;        (float:SF (reg:SI 22)))]
;;  "TARGET_SH3E"
;;  "")
;;
;;(define_insn "*floatsisf"
;;  [(set (match_operand:SF 0 "arith_reg_operand" "=f")
;;	(float:SF (reg:SI 22)))]
;;  "TARGET_SH3E"
;;  "float	fpul,%0")

(define_insn "floatsisf2"
  [(set (match_operand:SF 0 "arith_reg_operand" "=f")
	(float:SF (match_operand:SI 1 "arith_reg_operand" "r")))]
  "TARGET_SH3E"
  "lds	%1,fpul\;float	fpul,%0"
  [(set_attr "length" "4")
   (set_attr "type" "fp")])

;; ??? This is the right solution, but it fails because the movs[if] patterns
;; silently clobber FPUL (r22) for int<->fp moves.  Thus we can not explicitly
;; use FPUL here.
;;
;;(define_expand "fix_truncsfsi2"
;;  [(set (reg:SI 22)
;;	(fix:SI (match_operand:SF 1 "arith_reg_operand" "f")))
;;   (set (match_operand:SI 0 "arith_reg_operand" "=r")
;;	(reg:SI 22))]
;;  "TARGET_SH3E"
;;  "")
;;
;;(define_insn "*fixsfsi"
;;  [(set (reg:SI 22)
;;	(fix:SI (match_operand:SF 0 "arith_reg_operand" "f")))]
;;  "TARGET_SH3E"
;;  "ftrc	%0,fpul")

(define_insn "fix_truncsfsi2"
  [(set (match_operand:SI 0 "arith_reg_operand" "=r")
	(fix:SI (match_operand:SF 1 "arith_reg_operand" "f")))]
  "TARGET_SH3E"
  "ftrc	%1,fpul\;sts	fpul,%0"
  [(set_attr "length" "4")
   (set_attr "type" "move")])

;; ??? This should be SFmode not SImode in the compare, but that would
;; require fixing the branch patterns too.
(define_insn "*cmpgtsf_t"
  [(set (reg:SI 18) (gt:SI (match_operand:SF 0 "arith_reg_operand" "f")
			   (match_operand:SF 1 "arith_reg_operand" "f")))]
  "TARGET_SH3E"
  "fcmp/gt	%1,%0"
  [(set_attr "type" "fp")])

;; ??? This should be SFmode not SImode in the compare, but that would
;; require fixing the branch patterns too.
(define_insn "*cmpeqsf_t"
  [(set (reg:SI 18) (eq:SI (match_operand:SF 0 "arith_reg_operand" "f")
			   (match_operand:SF 1 "arith_reg_operand" "f")))]
  "TARGET_SH3E"
  "fcmp/eq	%1,%0"
  [(set_attr "type" "fp")])

(define_expand "cmpsf"
  [(set (reg:SI 18) (compare (match_operand:SF 0 "arith_operand" "")
			     (match_operand:SF 1 "arith_operand" "")))]
  "TARGET_SH3E"
  "
{
  sh_compare_op0 = operands[0];
  sh_compare_op1 = operands[1];
  DONE;
}")

(define_insn "negsf2"
  [(set (match_operand:SF 0 "arith_reg_operand" "=f")
	(neg:SF (match_operand:SF 1 "arith_reg_operand" "0")))]
  "TARGET_SH3E"
  "fneg	%0"
  [(set_attr "type" "fp")])

(define_insn "sqrtsf2"
  [(set (match_operand:SF 0 "arith_reg_operand" "=f")
	(sqrt:DF (match_operand:SF 1 "arith_reg_operand" "0")))]
  "TARGET_SH3E"
  "fsqrt	%0"
  [(set_attr "type" "fdiv")])

(define_insn "abssf2"
  [(set (match_operand:SF 0 "arith_reg_operand" "=f")
	(abs:SF (match_operand:SF 1 "arith_reg_operand" "0")))]
  "TARGET_SH3E"
  "fabs	%0"
  [(set_attr "type" "fp")])

;; Bit field extract patterns.  These give better code for packed bitfields,
;; because they allow auto-increment addresses to be generated.

(define_expand "insv"
  [(set (zero_extract:SI (match_operand:QI 0 "memory_operand" "")
			 (match_operand:SI 1 "immediate_operand" "")
			 (match_operand:SI 2 "immediate_operand" ""))
	(match_operand:SI 3 "general_operand" ""))]
  "! TARGET_LITTLE_ENDIAN"
  "
{
  rtx addr_target, orig_address, shift_reg;
  HOST_WIDE_INT size;

  /* ??? expmed doesn't care for non-register predicates.  */
  if (! memory_operand (operands[0], VOIDmode)
      || ! immediate_operand (operands[1], VOIDmode)
      || ! immediate_operand (operands[2], VOIDmode)
      || ! general_operand (operands[3], VOIDmode))
    FAIL;
  /* If this isn't a 16 / 24 / 32 bit field, or if
     it doesn't start on a byte boundary, then fail.  */
  size = INTVAL (operands[1]);
  if (size < 16 || size > 32 || size % 8 != 0
      || (INTVAL (operands[2]) % 8) != 0)
    FAIL;

  size /= 8;
  orig_address = XEXP (operands[0], 0);
  addr_target = gen_reg_rtx (SImode);
  shift_reg = gen_reg_rtx (SImode);
  emit_insn (gen_movsi (shift_reg, operands[3]));
  emit_insn (gen_addsi3 (addr_target, orig_address, GEN_INT (size - 1)));

  operands[0] = change_address (operands[0], QImode, addr_target);
  emit_insn (gen_movqi (operands[0], gen_rtx (SUBREG, QImode, shift_reg, 0)));

  while (size -= 1)
    {
      emit_insn (gen_lshrsi3_k (shift_reg, shift_reg, GEN_INT (8)));
      emit_insn (gen_addsi3 (addr_target, addr_target, GEN_INT (-1)));
      emit_insn (gen_movqi (operands[0],
			    gen_rtx (SUBREG, QImode, shift_reg, 0)));
    }

  DONE;
}")

;; -------------------------------------------------------------------------
;; 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")

;; These convert sequences such as `mov #k,r0; add r15,r0; mov.l @r0,rn'
;; to `mov #k,r0; mov.l @(r0,r15),rn'.  These sequences are generated by
;; reload when the constant is too large for a reg+offset address.

;; ??? We would get much better code if this was done in reload.  This would
;; require modifying find_reloads_address to recognize that if the constant
;; is out-of-range for an immediate add, then we get better code by reloading
;; the constant into a register than by reloading the sum into a register,
;; since the former is one instruction shorter if the address does not need
;; to be offsettable.  Unfortunately this does not work, because there is
;; only one register, r0, that can be used as an index register.  This register
;; is also the function return value register.  So, if we try to force reload
;; to use double-reg addresses, then we end up with some instructions that
;; need to use r0 twice.  The only way to fix this is to change the calling
;; convention so that r0 is not used to return values.

(define_peephole
  [(set (match_operand:SI 0 "register_operand" "=r")
	(plus:SI (match_dup 0) (match_operand:SI 1 "register_operand" "r")))
   (set (mem:SI (match_dup 0))
	(match_operand:SI 2 "general_movsrc_operand" ""))]
  "REGNO (operands[0]) == 0 && reg_unused_after (operands[0], insn)"
  "mov.l	%2,@(%0,%1)")

(define_peephole
  [(set (match_operand:SI 0 "register_operand" "=r")
	(plus:SI (match_dup 0) (match_operand:SI 1 "register_operand" "r")))
   (set (match_operand:SI 2 "general_movdst_operand" "")
	(mem:SI (match_dup 0)))]
  "REGNO (operands[0]) == 0 && reg_unused_after (operands[0], insn)"
  "mov.l	@(%0,%1),%2")

(define_peephole
  [(set (match_operand:SI 0 "register_operand" "=r")
	(plus:SI (match_dup 0) (match_operand:SI 1 "register_operand" "r")))
   (set (mem:HI (match_dup 0))
	(match_operand:HI 2 "general_movsrc_operand" ""))]
  "REGNO (operands[0]) == 0 && reg_unused_after (operands[0], insn)"
  "mov.w	%2,@(%0,%1)")

(define_peephole
  [(set (match_operand:SI 0 "register_operand" "=r")
	(plus:SI (match_dup 0) (match_operand:SI 1 "register_operand" "r")))
   (set (match_operand:HI 2 "general_movdst_operand" "")
	(mem:HI (match_dup 0)))]
  "REGNO (operands[0]) == 0 && reg_unused_after (operands[0], insn)"
  "mov.w	@(%0,%1),%2")

(define_peephole
  [(set (match_operand:SI 0 "register_operand" "=r")
	(plus:SI (match_dup 0) (match_operand:SI 1 "register_operand" "r")))
   (set (mem:QI (match_dup 0))
	(match_operand:QI 2 "general_movsrc_operand" ""))]
  "REGNO (operands[0]) == 0 && reg_unused_after (operands[0], insn)"
  "mov.b	%2,@(%0,%1)")

(define_peephole
  [(set (match_operand:SI 0 "register_operand" "=r")
	(plus:SI (match_dup 0) (match_operand:SI 1 "register_operand" "r")))
   (set (match_operand:QI 2 "general_movdst_operand" "")
	(mem:QI (match_dup 0)))]
  "REGNO (operands[0]) == 0 && reg_unused_after (operands[0], insn)"
  "mov.b	@(%0,%1),%2")

(define_peephole
  [(set (match_operand:SI 0 "register_operand" "=r")
	(plus:SI (match_dup 0) (match_operand:SI 1 "register_operand" "r")))
   (set (mem:SF (match_dup 0))
	(match_operand:SF 2 "general_movsrc_operand" ""))]
  "REGNO (operands[0]) == 0
   && ((GET_CODE (operands[2]) == REG && REGNO (operands[2]) < 16)
       || (GET_CODE (operands[2]) == SUBREG
	   && REGNO (SUBREG_REG (operands[2])) < 16))
   && reg_unused_after (operands[0], insn)"
  "mov.l	%2,@(%0,%1)")

(define_peephole
  [(set (match_operand:SI 0 "register_operand" "=r")
	(plus:SI (match_dup 0) (match_operand:SI 1 "register_operand" "r")))
   (set (match_operand:SF 2 "general_movdst_operand" "")

	(mem:SF (match_dup 0)))]
  "REGNO (operands[0]) == 0
   && ((GET_CODE (operands[2]) == REG && REGNO (operands[2]) < 16)
       || (GET_CODE (operands[2]) == SUBREG
	   && REGNO (SUBREG_REG (operands[2])) < 16))
   && reg_unused_after (operands[0], insn)"
  "mov.l	@(%0,%1),%2")

(define_peephole
  [(set (match_operand:SI 0 "register_operand" "=r")
	(plus:SI (match_dup 0) (match_operand:SI 1 "register_operand" "r")))
   (set (mem:SF (match_dup 0))
	(match_operand:SF 2 "general_movsrc_operand" ""))]
  "REGNO (operands[0]) == 0
   && ((GET_CODE (operands[2]) == REG && REGNO (operands[2]) >= FIRST_FP_REG)
       || (GET_CODE (operands[2]) == SUBREG
	   && REGNO (SUBREG_REG (operands[2])) >= FIRST_FP_REG))
   && reg_unused_after (operands[0], insn)"
  "fmov.s	%2,@(%0,%1)")

(define_peephole
  [(set (match_operand:SI 0 "register_operand" "=r")
	(plus:SI (match_dup 0) (match_operand:SI 1 "register_operand" "r")))
   (set (match_operand:SF 2 "general_movdst_operand" "")

	(mem:SF (match_dup 0)))]
  "REGNO (operands[0]) == 0
   && ((GET_CODE (operands[2]) == REG && REGNO (operands[2]) >= FIRST_FP_REG)
       || (GET_CODE (operands[2]) == SUBREG
	   && REGNO (SUBREG_REG (operands[2])) >= FIRST_FP_REG))
   && reg_unused_after (operands[0], insn)"
  "fmov.s	@(%0,%1),%2")
