;; Toshiba Media Processor Machine description template
;; Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2009 Free
;; Software Foundation, Inc.
;; Contributed by Red Hat Inc
;;
;; This file is part of GCC.
;;
;; GCC 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 3, or (at your option)
;; any later version.
;;
;; GCC 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 GCC; see the file COPYING3.  If not see
;; <http://www.gnu.org/licenses/>.  */



;; Constraints:
;;
;;  a   $sp
;;  b   $tp
;;  c   control regs
;;  h   $hi ($23)
;;  l   $lo ($24)
;;  d   $hi/$lo pair (DImode)
;;  j   $rpc ($22)
;;  r   $0..$15
;;  t   $0..$7
;;  v   $gp
;;  x	$c0..$c31
;;  ex  coprocessor registers that can be moved to other coprocessor registers
;;  er  coprocessor registers that can be moved to and from core registers
;;  em  coprocessor registers that can be moves to and from memory
;;  y	$ccr0..$ccr31
;;  z   $0
;;
;;  I   sign imm16	mov/add
;;  J   zero imm16	mov/add
;;  K   zero imm24	mov
;;  L   sign imm6	add
;;  M   zero imm5	slt,shifts
;;  N   zero imm4	bCC
;;  O   high imm16	mov
;;
;;  R   near symbol
;;  S   sign imm8	mov
;;  T   tp or gp relative symbol
;;  U   non-absolute memory
;;  W   %hi(sym)
;;  Y   (Rn)
;;  Z   Control Bus Symbol
;;
;; Modifiers:
;;
;;  b   print unique bit in mask
;;  B   print bits required for value (for clip)
;;  h	print decimal >> 16.
;;  I   print decimal, with hex comment if more than 8 bits
;;  J   print unsigned hex
;;  L   print set, clr or not (for bitops)
;;  P	print memory as a post-inc with no increment
;;  U   print bits required for value (for clipu)
;;  x   print unsigned decimal or hex, depending on where set bits are

(define_constants [
		   (REGSAVE_CONTROL_TEMP 11)
		   (FP_REGNO 8)
		   (TP_REGNO 13)
		   (GP_REGNO 14)
		   (SP_REGNO 15)
		   (PSW_REGNO 16)
		   (LP_REGNO 17)
		   (SAR_REGNO 18)
		   (RPB_REGNO 20)
		   (RPE_REGNO 21)
		   (RPC_REGNO 22)
		   (HI_REGNO 23)
		   (LO_REGNO 24)
		   (CBCR_REGNO 81)
		   ])

(define_constants [
		   (UNS_BLOCKAGE 0)
		   (UNS_TPREL 2)
		   (UNS_GPREL 3)
		   (UNS_REPEAT_BEG 4)
		   (UNS_REPEAT_END 5)
		   (UNS_EH_EPILOGUE 6)
		   (UNS_EREPEAT_BEG 7)
		   (UNS_EREPEAT_END 8)
		   (UNS_BB_TRACE_RET 9)
		   (UNS_DISABLE_INT 10)
		   (UNS_ENABLE_INT 11)
		   (UNS_RETI 12)
		  ])

;; This attribute determines the VLIW packing mechanism.  The IVC2
;; coprocessor has two pipelines (P0 and P1), and a MeP+IVC2 can issue
;; up to three insns at a time.  Most IVC2 insns can run on either
;; pipeline, however, scheduling some insns on P0 precludes packing a
;; core insn with it, and only 16-bit core insns can pack with any P0
;; insn.
(define_attr "vliw" "basic,ivc2"
  (const (symbol_ref "TARGET_IVC2")))

;; This attribute describes the kind of memory operand present in the
;; instruction.  This is used to compute the length of the insn based
;; on the addressing mode used.
(define_attr "memop" "none,core0,core1,cop0,cop1"
  (const_string "none"))

(define_attr "intrinsic" "none,cmov,cmov1,cmov2,cmovc1,cmovc2,cmovh1,cmovh2"
  (const_string "none"))

;; This attribute describes how the instruction may be bundled in a
;; VLIW instruction.  Type MULTI is assumed to use both slots.
(define_attr "slot" "core,cop,multi"
  (cond [(eq_attr "intrinsic" "!none")
	   (const_string "cop")]
	(const_string "core")))

;; This attribute describes the latency of the opcode (ready delay).
;; The 0 is used to indicate "unspecified".  An instruction that
;; completes immediately with no potential stalls would have a value
;; of 1, a one cycle stall would be 2, etc.
(define_attr "latency" ""
  (const_int 0))

(define_attr "shiftop" "none,operand2"
  (const_string "none"))

;; This attribute describes the size of the instruction in bytes.
;; This *must* be exact unless the pattern is SLOT_MULTI, as this
;; is used by the VLIW bundling code.
(define_attr "length" ""
  (cond [(eq_attr "memop" "core0")
	   (symbol_ref "mep_core_address_length (insn, 0)")
	 (eq_attr "memop" "core1")
	   (symbol_ref "mep_core_address_length (insn, 1)")
	 (eq_attr "memop" "cop0")
	   (symbol_ref "mep_cop_address_length (insn, 0)")
	 (eq_attr "memop" "cop1")
	   (symbol_ref "mep_cop_address_length (insn, 1)")
         ]
	 ; Catch patterns that don't define the length properly.
         (symbol_ref "(abort (), 0)")))

;; This attribute describes a pipeline hazard seen in the insn.
(define_attr "stall" "none,int2,ssarb,load,store,ldc,stc,ldcb,stcb,ssrab,fsft,ret,advck,mul,mulr,div"
  (cond [(and (eq_attr "shiftop" "operand2")
	      (not (match_operand:SI 2 "mep_single_shift_operand" "")))
	 (const_string "int2")]
	(const_string "none")))

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

;; Describe a user's asm statement.
(define_asm_attributes
  [(set_attr "length" "4")
   (set_attr "slot" "multi")])

;; Each IVC2 instruction uses one of these two pipelines.  P0S insns
;; use P0; C3 insns use P1.
(define_automaton "mep_ivc2")
(define_cpu_unit "ivc2_core,ivc2_p0,ivc2_p1" "mep_ivc2")

;; Each core or IVC2 instruction is bundled into one of these slots.
;; Supported bundlings:
;; 
;; Core mode:
;;
;;  C1	[-----core-----]
;;  C2	[-------------core-------------]
;;  C3	[--------------c3--------------]
;;
;; VLIW mode:
;;
;;  V1	[-----core-----][--------p0s-------][------------p1------------]
;;  V2  [-------------core-------------]xxxx[------------p1------------]
;;  V3	1111[--p0--]0111[--------p0--------][------------p1------------]

(define_attr "slots" "core,c3,p0,p0_p0s,p0_p1,p0s,p0s_p1,p1" (const_string "core"))

(define_cpu_unit "ivc2_slot_c16,ivc2_slot_c32,ivc2_slot_c3,ivc2_slot_p0s,ivc2_slot_p0,ivc2_slot_p1" "mep_ivc2")

(define_insn_reservation "ivc2_insn_core16" 1
  (and (eq_attr "vliw" "ivc2")
       (and (eq (symbol_ref "get_attr_length(insn)") (const_int 2))
	    (and (eq_attr "intrinsic" "none")
		 (eq_attr "slot" "!cop"))))
  "ivc2_core+ivc2_slot_c16")

(define_insn_reservation "ivc2_insn_core32" 1
  (and (eq_attr "vliw" "ivc2")
       (and (eq (symbol_ref "get_attr_length(insn)") (const_int 4))
	    (and (eq_attr "intrinsic" "none")
		 (eq_attr "slot" "!cop"))))
  "ivc2_core+ivc2_slot_c32")

;; These shouldn't happen when in VLIW mode.
(define_insn_reservation "ivc2_insn_c3" 1
  (and (eq_attr "vliw" "ivc2")
       (eq_attr "slots" "c3"))
  "ivc2_p1+ivc2_slot_c3")

(define_insn_reservation "ivc2_insn_p0" 1
  (and (eq_attr "vliw" "ivc2")
       (eq_attr "slots" "p0"))
  "ivc2_p0+ivc2_slot_p0")

(define_insn_reservation "ivc2_insn_p0_p0s" 1
  (and (eq_attr "vliw" "ivc2")
       (eq_attr "slots" "p0_p0s"))
  "ivc2_p0+ivc2_slot_p0|ivc2_p0+ivc2_slot_p0s")

(define_insn_reservation "ivc2_insn_p0_p1" 1
  (and (eq_attr "vliw" "ivc2")
       (eq_attr "slots" "p0_p1"))
  "ivc2_p0+ivc2_slot_p0|ivc2_p1+ivc2_slot_p1")

(define_insn_reservation "ivc2_insn_p0s" 1
  (and (eq_attr "vliw" "ivc2")
       (eq_attr "slots" "p0s"))
  "ivc2_p0+ivc2_slot_p0s")

(define_insn_reservation "ivc2_insn_p0s_p1" 1
  (and (eq_attr "vliw" "ivc2")
       (eq_attr "slots" "p0s_p1"))
  "ivc2_p0+ivc2_slot_p0s|ivc2_p1+ivc2_slot_p1")

(define_insn_reservation "ivc2_insn_p1" 1
  (and (eq_attr "vliw" "ivc2")
       (eq_attr "slots" "p1"))
  "ivc2_p1+ivc2_slot_p1")

;; these run in C3 also, but when we're doing VLIW scheduling, they
;; only run in P0.
(define_insn_reservation "ivc2_insn_cmov" 1
  (and (eq_attr "vliw" "ivc2")
       (eq_attr "intrinsic" "!none"))
  "ivc2_p0+ivc2_slot_p0")


(exclusion_set "ivc2_slot_c32"
	       "ivc2_slot_p0,ivc2_slot_p0s")
(exclusion_set "ivc2_slot_p0"
	       "ivc2_slot_p0s")
(exclusion_set "ivc2_slot_c16"
	       "ivc2_slot_p0")
(exclusion_set "ivc2_slot_c16"
	       "ivc2_slot_c32")

;; Non-IVC2 scheduling.
(define_automaton "mep")
(define_cpu_unit "core,cop" "mep")

;; Latencies are the time between one insn entering the second pipeline
;; stage (E2, LD, A2 or V2) and the next instruction entering the same
;; stage.  When an instruction assigns to general registers, the default
;; latencies are for when the next instruction receives the register
;; through bypass 1.

;; Arithmetic instructions that execute in a single stage.
(define_insn_reservation "h1_int1" 2
  (and (eq_attr "slot" "!cop")
       (eq_attr "stall" "none"))
  "core")
(define_bypass 1 "h1_int1" "h1_int1,h1_ssarb")
(define_bypass 1 "h1_int1" "h1_store" "mep_store_data_bypass_p")

;; $sar can be read by an immediately following fsft or ldc.
(define_insn_reservation "h1_ssarb" 1
  (eq_attr "stall" "ssarb")
  "core")

;; Arithmetic instructions that execute in two stages.
(define_insn_reservation "h1_int2" 2
  (eq_attr "stall" "int2,fsft")
  "core")
(define_bypass 1 "h1_int2" "h1_int1,h1_ssarb")
(define_bypass 1 "h1_int2" "h1_store" "mep_store_data_bypass_p")

(define_insn_reservation "h1_load" 4
  (eq_attr "stall" "load")
  "core")
(define_bypass 3 "h1_load" "h1_int1,h1_ssarb")
(define_bypass 3 "h1_load" "h1_store" "mep_store_data_bypass_p")

(define_insn_reservation "h1_store" 1
  (eq_attr "stall" "store")
  "core")

(define_insn_reservation "h1_ipipe_ldc" 2
  (and (eq_attr "stall" "ldc")
       (ne (symbol_ref "mep_ipipe_ldc_p(insn)") (const_int 0)))
  "core")
(define_bypass 1 "h1_ipipe_ldc" "h1_int1,h1_ssarb")
(define_bypass 1 "h1_ipipe_ldc" "h1_store" "mep_store_data_bypass_p")

(define_insn_reservation "h1_apipe_ldc" 2
  (and (eq_attr "stall" "ldc")
       (eq (symbol_ref "mep_ipipe_ldc_p(insn)") (const_int 0)))
  "core")

;; 2 is correct for stc->ret and stc->fsft.  The most important remaining
;; case is stc->madd, which induces no stall.
(define_insn_reservation "h1_stc" 2
  (eq_attr "stall" "stc")
  "core")
(define_bypass 1 "h1_stc" "h1_mul")

;; ??? Parameterised latency.
(define_insn_reservation "h1_ldcb" 5
  (eq_attr "stall" "ldcb")
  "core")

(define_insn_reservation "h1_stcb" 1
  (eq_attr "stall" "stcb")
  "core")

(define_insn_reservation "h1_advck" 6
  (eq_attr "stall" "advck")
  "core")

(define_insn_reservation "h1_mul" 5
  (eq_attr "stall" "mul,mulr")
  "core")
(define_bypass 4 "h1_mul" "h1_int1,h1_ssarb")
(define_bypass 4 "h1_mul" "h1_store" "mep_store_data_bypass_p")
(define_bypass 1 "h1_mul" "h1_mul" "mep_mul_hilo_bypass_p")

(define_insn_reservation "h1_div" 36
  (eq_attr "stall" "div")
  "core")

(define_insn_reservation "h1_cop" 1
  (eq_attr "slot" "cop")
  "cop")

(include "predicates.md")
(include "constraints.md")
(include "intrinsics.md")

;; ::::::::::::::::::::
;; ::
;; :: Moves
;; ::
;; ::::::::::::::::::::

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

;; The Idea here is to prefer the 16-bit tp-relative load, but to fall back
;; to the general 32-bit load rather than do silly things with spill regs.
(define_insn "*movqi_tprel_load"
  [(set (match_operand:QI 0 "mep_tprel_operand" "=t,*r")
	(mem:QI (plus:SI (match_operand:SI 1 "mep_tp_operand" "b,*r")
			 (const:SI (unspec:SI [(match_operand:SI 2
						"symbolic_operand" "s,s")]
					      UNS_TPREL)))))]
  ""
  "lb\\t%0, %%tpoff(%2)(%1)"
  [(set_attr "length" "2,4")
   (set_attr "stall" "load")])

(define_insn "*movqi_tprel_store"
  [(set (mem:QI (plus:SI (match_operand:SI 0 "mep_tp_operand" "b,*r")
			 (const:SI (unspec:SI [(match_operand:SI 1
						"symbolic_operand" "s,s")]
					      UNS_TPREL))))
	(match_operand:QI 2 "mep_tprel_operand" "t,*r"))]
  ""
  "sb\\t%2, %%tpoff(%1)(%0)"
  [(set_attr "length" "2,4")
   (set_attr "stall" "store")])

(define_insn "*movqi_internal"
  [(set (match_operand:QI 0 "nonimmediate_operand" "=r,r, r,m,r,c,r,y,r,er,ex,em,Y")
	(match_operand:QI 1 "general_operand" " r,n,rm,r,c,r,y,r,er,r,ex,Y,em"))]
  "mep_mov_ok (operands, QImode)"
  "@
   mov\\t%0, %1
   mov\\t%0, %1
   lb\\t%0, %1
   sb\\t%1, %0
   ldc\\t%0, %1
   stc\\t%1, %0
   cmovc\\t%0, %1
   cmovc\\t%0, %1
   cmov\\t%0, %1
   cmov\\t%0, %1
   %<\\t%0, %M1
   lbcpa\\t%0, %P1
   sbcpa\\t%1, %P0"
  [(set_attr "length" "2,2,*,*,2,2,4,4,4,4,*,4,4")
   (set_attr "intrinsic" "*,*,*,*,*,*,cmovc2,cmovc1,cmov2,cmov1,cmov,*,*")
   (set_attr "stall"  "*,*,load,store,ldc,stc,*,*,*,*,*,load,store")
   (set_attr "memop"  "*,*,core1,core0,*,*,*,*,*,*,*,*,*")])

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

(define_insn "*movhi_tprel_load"
  [(set (match_operand:HI 0 "mep_tprel_operand" "=t,*r")
	(mem:HI (plus:SI (match_operand:SI 1 "mep_tp_operand" "b,*r")
			 (const:SI (unspec:SI [(match_operand:SI 2
						"symbolic_operand" "s,s")]
					      UNS_TPREL)))))]
  ""
  "lh\\t%0, %%tpoff(%2)(%1)"
  [(set_attr "length" "2,4")
   (set_attr "stall" "load")])

(define_insn "*movhi_tprel_store"
  [(set (mem:HI (plus:SI (match_operand:SI 0 "mep_tp_operand" "b,*r")
			 (const:SI (unspec:SI [(match_operand:SI 1
						"symbolic_operand" "s,s")]
					      UNS_TPREL))))
	(match_operand:HI 2 "mep_tprel_operand" "t,*r"))]
  ""
  "sh\\t%2, %%tpoff(%1)(%0)"
  [(set_attr "length" "2,4")
   (set_attr "stall" "store")])

(define_insn "*movhi_internal"
  [(set (match_operand:HI 0 "nonimmediate_operand" "=r,r,r,r,m,r,c,r,y,r,er,ex,em,Y")
	(match_operand:HI 1 "general_operand" " r,S,n,m,r,c,r,y,r,er,r,ex,Y,em"))]
  "mep_mov_ok (operands, HImode)"
  "@
   mov\\t%0, %1
   mov\\t%0, %I1
   mov\\t%0, %I1
   lh\\t%0, %1
   sh\\t%1, %0
   ldc\\t%0, %1
   stc\\t%1, %0
   cmovc\\t%0, %1
   cmovc\\t%0, %1
   cmov\\t%0, %1
   cmov\\t%0, %1
   %<\\t%0, %M1
   lhcpa\\t%0, %P1
   shcpa\\t%1, %P0"
  [(set_attr "length" "2,2,4,*,*,2,2,4,4,4,4,*,4,4")
   (set_attr "intrinsic" "*,*,*,*,*,*,*,cmovc2,cmovc1,cmov2,cmov1,cmov,*,*")
   (set_attr "stall"  "*,*,*,load,store,ldc,stc,*,*,*,*,*,load,store")
   (set_attr "memop"  "*,*,*,core1,core0,*,*,*,*,*,*,*,*,*")])

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

(define_insn "*movsi_tprel_load"
  [(set (match_operand:SI 0 "mep_tprel_operand" "=t,*r")
	(mem:SI (plus:SI (match_operand:SI 1 "mep_tp_operand" "b,*r")
			 (const:SI (unspec:SI [(match_operand:SI 2
						"symbolic_operand" "s,s")]
					      UNS_TPREL)))))]
  ""
  "lw\\t%0, %%tpoff(%2)(%1)"
  [(set_attr "length" "2,4")
   (set_attr "stall" "load")])

(define_insn "*movsi_tprel_store"
  [(set (mem:SI (plus:SI (match_operand:SI 0 "mep_tp_operand" "b,*r")
			 (const:SI (unspec:SI [(match_operand:SI 1
						"symbolic_operand" "s,s")]
					      UNS_TPREL))))
	(match_operand:SI 2 "mep_tprel_operand" "t,*r"))]
  ""
  "sw\\t%2, %%tpoff(%1)(%0)"
  [(set_attr "length" "2,4")
   (set_attr "stall" "store")])

(define_insn "movsi_topsym_s"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(high:SI (match_operand:SI 1 "symbolic_operand" "s")))]
  ""
  "movh\\t%0, %%hi(%1)"
  [(set_attr "length" "4")])

(define_insn "movsi_botsym_s"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(lo_sum:SI (match_operand:SI 1 "register_operand" "0")
		   (match_operand:SI 2 "symbolic_operand" "s")))]
  ""
  "add3\\t%0, %1, %%lo(%2)"
  [(set_attr "length" "4")])



(define_insn "cmovh_getsub"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(subreg:SI (match_operand:DI 1 "register_operand" "er") 4))]
  "0 && TARGET_64BIT_CR_REGS"
  "cmovh\\t%0, %1"
  [(set_attr "intrinsic" "cmovh2")
   (set_attr "length" "4")])

(define_insn "*movsi_internal"
  [(set (match_operand:SI 0 "mep_movdest_operand"
	    "=r,r,r,r,r, t,t,r,r,r,Z,m,r,c,r,y,r, er,ex,em,U ")
	(match_operand:SI 1 "general_operand"
	    " r,S,I,J,OW,K,s,i,Z,m,r,r,c,r,y,r,er,r, ex,U, em"))]
  "mep_mov_ok (operands, SImode)"
  "@
   mov\\t%0, %1
   mov\\t%0, %I1
   mov\\t%0, %I1
   movu\\t%0, %J1
   movh\\t%0, %h1
   movu\\t%0, %x1
   movu\\t%0, %1
   #
   ldcb\\t%0, %1
   lw\\t%0, %1
   stcb\\t%1, %0
   sw\\t%1, %0
   ldc\\t%0, %1
   stc\\t%1, %0
   cmovc\\t%0, %1
   cmovc\\t%0, %1
   cmov\\t%0, %1
   cmov\\t%0, %1
   %<\\t%0, %M1
   lwcp\\t%0, %1
   swcp\\t%1, %0"
  [(set_attr "length" "2,2,4,4,4,4,4,*,4,*,4,*,2,2,4,4,4,4,4,*,*")
   (set_attr "intrinsic" "*,*,*,*,*,*,*,*,*,*,*,*,*,*,cmovc2,cmovc1,cmov2,cmov1,cmov,*,*")
   (set_attr "stall"  "*,*,*,*,*,*,*,*,ldcb,load,stcb,store,ldc,stc,*,*,*,*,*,load,store")
   (set_attr "memop"  "*,*,*,*,*,*,*,*,*,core1,*,core0,*,*,*,*,*,*,*,cop1,cop0")
   (set_attr "slot"   "*,*,*,*,*,*,*,multi,*,*,*,*,*,*,*,*,*,*,*,*,*")])

(define_split
  [(set (match_operand:SI 0 "register_operand" "")
        (match_operand:SI 1 "const_int_operand" ""))]
  "mep_split_mov (operands, 0)"
  [(set (match_dup 0) (match_dup 2))
   (set (match_dup 0) (ior:SI (match_dup 0) (match_dup 3)))]
  "
{
  HOST_WIDE_INT value;
  int lo, hi;

  value = INTVAL (operands[1]);

  lo = value & 0xffff;
  hi = trunc_int_for_mode (value & 0xffff0000, SImode);

  operands[2] = GEN_INT (hi);
  operands[3] = GEN_INT (lo);
}")

(define_split
  [(set (match_operand:SI 0 "register_operand" "")
        (match_operand:SI 1 "immediate_operand" ""))]
  "mep_split_mov (operands, 1)"
  [(set (match_dup 0) (high:SI (match_dup 1)))
   (set (match_dup 0) (lo_sum:SI (match_dup 0) (match_dup 1)))]
  "")

;; ??? What purpose do these two serve that high+lo_sum do not?
(define_insn "movsi_topsym_u"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(and:SI (match_operand:SI 1 "symbolic_operand" "s")
		(const_int -65536)))]
  ""
  "movh\\t%0, %%uhi(%1)"
  [(set_attr "length" "4")])

(define_insn "movsi_botsym_u"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(ior:SI (match_operand:SI 1 "register_operand" "0")
		(and:SI (match_operand:SI 2 "symbolic_operand" "s")
			(const_int 65535))))]
  ""
  "or3\\t%0, %1, %%lo(%2)"
  [(set_attr "length" "4")])

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

(define_insn "*movdi_internal_32"
  [(set (match_operand:DI 0 "mep_movdest_operand" "= r,m,r,c,r,er,ex,em,U")
	(match_operand:DI 1 "general_operand"     "rim,r,c,r,er,r,ex,U,em"))]
  "TARGET_32BIT_CR_REGS && mep_mov_ok (operands, DImode)"
  "#"
  [(set_attr "slot" "multi")])

(define_insn "*movdi_internal_64"
  [(set (match_operand:DI 0 "mep_movdest_operand" "=r,r,m,r,c,r,er,ex,em,U")
	(match_operand:DI 1 "general_operand"     "r,im,r,c,r,er,r,ex,U,em"))]
  "TARGET_64BIT_CR_REGS && mep_mov_ok (operands, DImode)"
  "@
   #
   #
   #
   #
   #
   #
   #
   %<\\t%0, %M1
   lmcp\\t%0, %1
   smcp\\t%1, %0"
  [(set_attr "slot"  "multi,multi,multi,multi,multi,multi,multi,*,*,*")
   (set_attr "intrinsic" "*,*,*,*,*,*,*,cmov,*,*")
   (set_attr "memop" "*,*,*,*,*,*,*,cop0,cop1,cop0")
   (set_attr "stall" "*,*,*,*,*,*,*,*,load,store")])

(define_insn "*movdi_cop_postinc"
  [(parallel [(set (match_operand:DI 0 "register_operand" "=em")
		   (mem:DI (reg:SI SP_REGNO)))
	      (set (reg:SI SP_REGNO)
		   (plus:SI (reg:SI SP_REGNO)
			    (const_int 8)))
	      ]
	     )]
  "TARGET_COP"
  "lmcpi\\t%0,($sp+)"
  [(set_attr "length" "2")])

(define_insn "*movdi_cop_postinc"
  [(parallel [(set (match_operand:DI 0 "register_operand" "=em")
		   (mem:DI (match_operand:SI 2 "register_operand" "r")))
	      (set (match_operand:SI 1 "register_operand" "=0")
		   (plus:SI (match_operand:SI 3 "register_operand" "0")
			    (const_int 8)))
	      ]
	     )]
  "TARGET_COP"
  "lmcpi\\t%0,(%1+)"
  [(set_attr "length" "2")])

(define_insn "*cmovh_set"
  [(set (zero_extract:SI (match_operand:DI 0 "register_operand" "+er")
			 (const_int 32)
			 (const_int 32))
	(match_operand:SI 1 "register_operand" "r"))]
  "TARGET_64BIT_CR_REGS"
  "cmovh\\t%0, %1"
  [(set_attr "intrinsic" "cmovh1")
   (set_attr "length" "4")])

(define_insn "cmovh_get"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(zero_extract:SI (match_operand:DI 1 "register_operand" "er")
			 (const_int 32)
			 (const_int 32)))]
  "TARGET_64BIT_CR_REGS"
  "cmovh\\t%0, %1"
  [(set_attr "intrinsic" "cmovh2")
   (set_attr "length" "4")])

(define_split
  [(set (match_operand:DI 0 "mep_movdest_operand" "")
        (match_operand:DI 1 "general_operand" ""))]
  "reload_completed && mep_multi_slot (insn)"
  [(set (match_dup 2) (match_dup 3))
   (set (match_dup 4) (match_dup 5))]
  "mep_split_wide_move (operands, DImode);")

;; Floating Point Moves

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

(define_insn "*movsf_tprel_load"
  [(set (match_operand:SF 0 "mep_tprel_operand" "=t,*r")
	(mem:SF (plus:SI (match_operand:SI 1 "mep_tp_operand" "b,*r")
			 (const:SI (unspec:SI [(match_operand:SI 2
						"symbolic_operand" "s,s")]
					      UNS_TPREL)))))]
  ""
  "lw\\t%0, %%tpoff(%2)(%1)"
  [(set_attr "length" "2,4")
   (set_attr "stall" "load")])

(define_insn "*movsf_tprel_store"
  [(set (mem:SF (plus:SI (match_operand:SI 0 "mep_tp_operand" "b,*r")
			 (const:SI (unspec:SI [(match_operand:SI 1
						"symbolic_operand" "s,s")]
					      UNS_TPREL))))
	(match_operand:SF 2 "mep_tprel_operand" "t,*r"))]
  ""
  "sw\\t%2, %%tpoff(%1)(%0)"
  [(set_attr "length" "2,4")
   (set_attr "stall" "store")])

(define_insn "*movsf_internal"
  [(set (match_operand:SF 0 "mep_movdest_operand"
	    "=r,r,r,r,Z,m,r,c,r,y,r,er,ex,em,U")
	(match_operand:SF 1 "general_operand"
	    " r,F,Z,m,r,r,c,r,y,r,er,r,ex,U,em"))]
  "mep_mov_ok (operands, SFmode)"
  "@
   mov\\t%0, %1
   #
   ldcb\\t%0, %1
   lw\\t%0, %1
   stcb\\t%1, %0
   sw\\t%1, %0
   ldc\\t%0, %1
   stc\\t%1, %0
   cmovc\\t%0, %1
   cmovc\\t%0, %1
   cmov\\t%0, %1
   cmov\\t%0, %1
   %<\\t%0, %M1
   lwcp\\t%0, %1
   swcp\\t%1, %0"
  [(set_attr "length" "2,*,2,*,2,*,2,2,*,*,4,4,*,*,*")
   (set_attr "intrinsic" "*,*,*,*,*,*,*,*,cmovc2,cmovc1,cmov2,cmov1,cmov,*,*")
   (set_attr "stall"  "*,*,ldcb,load,stcb,store,ldc,stc,*,*,*,*,*,load,store")
   (set_attr "memop"  "*,*,*,core1,*,core0,*,*,*,*,*,*,*,cop1,cop0")])

(define_split
  [(set (match_operand:SF 0 "register_operand" "")
        (match_operand:SF 1 "const_double_operand" ""))]
  "reload_completed"
  [(const_int 0)]
  "
{
  REAL_VALUE_TYPE rv;
  HOST_WIDE_INT value;
  HOST_WIDE_INT lo, hi;
  rtx out;

  REAL_VALUE_FROM_CONST_DOUBLE (rv, operands[1]);
  REAL_VALUE_TO_TARGET_SINGLE (rv, value);

  lo = value & 0xffff;
  hi = trunc_int_for_mode (value & 0xffff0000, SImode);

  out = gen_rtx_REG (SImode, REGNO (operands[0]));
  emit_move_insn (out, GEN_INT (hi));
  if (lo != 0)
    emit_insn (gen_iorsi3 (out, out, GEN_INT (lo)));
  DONE;
}")

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

(define_insn "*movdf_internal_32"
  [(set (match_operand:DF 0 "mep_movdest_operand" "= r,m,r,c,r,er,ex,em,U")
	(match_operand:DF 1 "general_operand"     "rFm,r,c,r,er,r,ex,U,em"))]
  "TARGET_32BIT_CR_REGS && mep_mov_ok (operands, DFmode)"
  "#"
  [(set_attr "slot" "multi")])

(define_insn "*movdf_internal_64"
  [(set (match_operand:DF 0 "mep_movdest_operand" "= r,m,r,c,r,er,ex,em,U")
	(match_operand:DF 1 "general_operand"     "rFm,r,c,r,er,r,ex,U,em"))]
  "TARGET_64BIT_CR_REGS && mep_mov_ok (operands, DFmode)"
  "@
   #
   #
   #
   #
   #
   #
   %<\\t%0, %M1
   lmcp\\t%0, %1
   smcp\\t%1, %0"
  [(set_attr "slot"  "multi,multi,multi,multi,multi,multi,*,*,*")
   (set_attr "intrinsic" "*,*,*,*,*,*,cmov,*,*")
   (set_attr "memop" "*,*,*,*,*,*,*,cop1,cop0")
   (set_attr "stall" "*,*,*,*,*,*,*,load,store")])

(define_split
  [(set (match_operand:DF 0 "mep_movdest_operand" "")
        (match_operand:DF 1 "general_operand" ""))]
  "reload_completed && mep_multi_slot (insn)"
  [(set (match_dup 2) (match_dup 3))
   (set (match_dup 4) (match_dup 5))]
  "mep_split_wide_move (operands, DFmode);")


(define_insn "*lbcpa"
  [(set (match_operand:SI 0 "register_operand" "=em")
	(sign_extend:SI (mem:QI (match_operand:SI 2 "register_operand" "1"))))
   (set (match_operand:SI 1 "register_operand" "=r")
	(plus:SI (match_dup 2)
		 (match_operand:SI 3 "cgen_h_sint_8a1_immediate" "")))]
  "TARGET_COP && reload_completed"
  "lbcpa\t%0, (%1+), %3"
  [(set_attr "length" "4")
   (set_attr "stall" "load")])

(define_insn "*sbcpa"
  [(set (mem:QI (match_operand:SI 1 "register_operand" "0"))
	(match_operand:QI 2 "register_operand" "em"))
   (set (match_operand:SI 0 "register_operand" "=r")
	(plus:SI (match_dup 1)
		 (match_operand:SI 3 "cgen_h_sint_8a1_immediate" "")))]
  "TARGET_COP && reload_completed"
  "sbcpa\t%2, (%0+), %3"
  [(set_attr "length" "4")
   (set_attr "stall" "store")])

(define_insn "*lhcpa"
  [(set (match_operand:SI 0 "register_operand" "=em")
	(sign_extend:SI (mem:HI (match_operand:SI 2 "register_operand" "1"))))
   (set (match_operand:SI 1 "register_operand" "=r")
	(plus:SI (match_dup 2)
		 (match_operand:SI 3 "cgen_h_sint_7a2_immediate" "")))]
  "TARGET_COP && reload_completed"
  "lhcpa\t%0, (%1+), %3"
  [(set_attr "length" "4")
   (set_attr "stall" "load")])

(define_insn "*shcpa"
  [(set (mem:HI (match_operand:SI 1 "register_operand" "0"))
	(match_operand:HI 2 "register_operand" "em"))
   (set (match_operand:SI 0 "register_operand" "=r")
	(plus:SI (match_dup 1)
		 (match_operand:SI 3 "cgen_h_sint_7a2_immediate" "")))]
  "TARGET_COP && reload_completed"
  "shcpa\t%2, (%0+), %3"
  [(set_attr "length" "4")
   (set_attr "stall" "store")])

(define_insn "*lwcpi"
  [(set (match_operand:SI 0 "register_operand" "=em")
	(mem:SI (match_operand:SI 2 "register_operand" "1")))
   (set (match_operand:SI 1 "register_operand" "=r")
	(plus:SI (match_dup 2)
		 (const_int 4)))]
  "TARGET_COP && reload_completed"
  "lwcpi\t%0, (%1+)"
  [(set_attr "length" "2")
   (set_attr "stall" "load")])

(define_insn "*lwcpa"
  [(set (match_operand:SI 0 "register_operand" "=em")
	(mem:SI (match_operand:SI 2 "register_operand" "1")))
   (set (match_operand:SI 1 "register_operand" "=r")
	(plus:SI (match_dup 2)
		 (match_operand:SI 3 "cgen_h_sint_6a4_immediate" "")))]
  "TARGET_COP && reload_completed"
  "lwcpa\t%0, (%1+), %3"
  [(set_attr "length" "4")
   (set_attr "stall" "load")])

(define_insn "*swcpi"
  [(set (mem:SI (match_operand:SI 1 "register_operand" "0"))
	(match_operand:SI 2 "register_operand" "em"))
   (set (match_operand:SI 0 "register_operand" "=r")
	(plus:SI (match_dup 1)
		 (const_int 4)))]
  "TARGET_COP && reload_completed"
  "swcpi\t%2, (%0+)"
  [(set_attr "length" "2")
   (set_attr "stall" "store")])

(define_insn "*swcpa"
  [(set (mem:SI (match_operand:SI 1 "register_operand" "0"))
	(match_operand:SI 2 "register_operand" "em"))
   (set (match_operand:SI 0 "register_operand" "=r")
	(plus:SI (match_dup 1)
		 (match_operand:SI 3 "cgen_h_sint_6a4_immediate" "")))]
  "TARGET_COP && reload_completed"
  "swcpa\t%2, (%0+), %3"
  [(set_attr "length" "4")
   (set_attr "stall" "store")])

(define_peephole2
  [(set (match_operand:SI 0 "register_operand" "")
	(plus:SI (match_dup 0)
		 (match_operand:SI 1 "cgen_h_sint_8a1_immediate" "")))]
  "TARGET_COP && mep_use_post_modify_p (insn, operands[0], operands[1])"
  [(const_int 0)]
{
  emit_note (NOTE_INSN_DELETED);
  DONE;
})

;; ::::::::::::::::::::
;; ::
;; :: Reloads
;; ::
;; ::::::::::::::::::::

(define_expand "reload_insi"
  [(set (match_operand:SI 0 "mep_reload_operand" "")
        (match_operand:SI 1 "mep_reload_operand" "r"))
   (clobber (match_operand:SI 2 "register_operand" "=&r"))]
  ""
  "
{
  mep_expand_reload (operands, SImode);
  DONE;
}")

(define_expand "reload_outsi"
  [(set (match_operand:SI 0 "mep_reload_operand" "=r")
        (match_operand:SI 1 "mep_reload_operand" ""))
   (clobber (match_operand:SI 2 "register_operand" "=&r"))]
  ""
  "
{
  mep_expand_reload (operands, SImode);
  DONE;
}")


;; ::::::::::::::::::::
;; ::
;; :: Conversions
;; ::
;; ::::::::::::::::::::

(define_insn "extendqisi2"
  [(set (match_operand:SI 0 "register_operand" "=r,r,em")
	(sign_extend:SI
	  (match_operand:QI 1 "nonimmediate_operand" "0,m,Y")))]
  ""
  "@
   extb\\t%0
   lb\\t%0, %1
   lbcpa\\t%0, %P1"
  [(set_attr "length" "2,*,*")
   (set_attr "stall"  "*,load,load")
   (set_attr "memop"  "*,core1,cop1")])

(define_insn "extendhisi2"
  [(set (match_operand:SI 0 "register_operand" "=r,r,em")
	(sign_extend:SI
	  (match_operand:HI 1 "nonimmediate_operand" "0,m,Y")))]
  ""
  "@
   exth\\t%0
   lh\\t%0, %1
   lhcpa\\t%0, %P1"
  [(set_attr "length" "2,*,*")
   (set_attr "stall"  "*,load,load")
   (set_attr "memop"  "*,core1,cop1")])

(define_insn "zero_extendqisi2"
  [(set (match_operand:SI 0 "register_operand" "=r,r,r")
	(zero_extend:SI
	  (match_operand:QI 1 "nonimmediate_operand" "0,r,m")))]
  ""
  "@
   extub\\t%0
   and3\\t%0, %1, 255
   lbu\\t%0, %1"
  [(set_attr "length" "2,4,*")
   (set_attr "stall" "*,*,load")
   (set_attr "memop"  "*,*,core1")])

(define_insn "zero_extendhisi2"
  [(set (match_operand:SI 0 "register_operand" "=r,r,r")
	(zero_extend:SI
	  (match_operand:HI 1 "nonimmediate_operand" "0,r,m")))]
  ""
  "@
   extuh\\t%0
   and3\\t%0, %1, 65535
   lhu\\t%0, %1"
  [(set_attr "length" "2,4,*")
   (set_attr "stall" "*,*,load")
   (set_attr "memop"  "*,*,core1")])

;; ::::::::::::::::::::
;; ::
;; :: 32 bit Integer arithmetic
;; ::
;; ::::::::::::::::::::

(define_insn "addsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r,r")
	(plus:SI (match_operand:SI 1 "register_operand" "%r,0,r")
		 (match_operand:SI 2 "mep_add_operand" "r,L,IT")))]
  ""
  "@
   add3\\t%0, %1, %2
   add\\t%0, %2
   add3\\t%0, %1, %I2"
  [(set (attr "length")
	(if_then_else (eq_attr "alternative" "2")
	  (if_then_else (and (match_operand:SI 1 "mep_sp_operand" "")
			     (match_operand:SI 2 "mep_imm7a4_operand" ""))
	    (const_int 2)
	    (const_int 4))
	  (const_int 2)))])

;; The intention here is to combine the 16-bit add with the 16-bit
;; move to create a 32-bit add.  It's the same size, but takes one
;; less machine cycle.  It will happen to match a 32-bit add with a
;; 16-bit move also, but gcc shouldn't be doing that ;)
(define_peephole2
  [(set (match_operand:SI 0 "register_operand" "")
	(plus:SI (match_operand:SI 1 "register_operand" "")
		 (match_operand:SI 2 "immediate_operand" "")))
   (set (match_operand:SI 3 "register_operand" "")
	(match_operand:SI 4 "register_operand" ""))]
  "REGNO (operands[0]) == REGNO (operands[1])
   && REGNO (operands[0]) == REGNO (operands[4])
   && GR_REGNO_P (REGNO (operands[3]))
   && dead_or_set_p (peep2_next_insn (1), operands[4])"
  [(set (match_dup 3)
	(plus:SI (match_dup 1)
		 (match_dup 2)))]
  "")

(define_insn "subsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(minus:SI (match_operand:SI 1 "register_operand" "0")
		  (match_operand:SI 2 "register_operand" "r")))]
  ""
  "sub\\t%0, %2"
  [(set_attr "length" "2")])

(define_expand "mulsi3"
  [(set (match_operand:SI 0 "register_operand" "")
        (mult:SI (match_operand:SI 1 "register_operand" "")
                 (match_operand:SI 2 "register_operand" "")))]
  "TARGET_OPT_MULT || TARGET_COPRO_MULT"
{
  emit_insn (gen_mulsi3_1 (operands[0], operands[1], operands[2]));
  DONE;
})

;; Generated by mep_reuse_lo_p when no GPR destination is needed.
(define_insn "mulsi3_lo"
  [(set (match_operand:SI 0 "mep_lo_operand" "=l")
	(mult:SI (match_operand:SI 1 "register_operand" "r")
		 (match_operand:SI 2 "register_operand" "r")))
   (clobber (match_scratch:SI 3 "=h"))]
  "TARGET_OPT_MULT && reload_completed"
  "mul\\t%1, %2"
  [(set_attr "length" "2")
   (set_attr "stall" "mul")])

;; Generated by mep_reuse_lo_p when both destinations of a mulr
;; are needed.
(define_insn "mulsi3r"
  [(set (match_operand:SI 0 "mep_lo_operand" "=l")
	(mult:SI (match_operand:SI 2 "register_operand" "1")
		 (match_operand:SI 3 "register_operand" "r")))
   (set (match_operand:SI 1 "register_operand" "=r")
	(mult:SI (match_dup 2)
		 (match_dup 3)))
   (clobber (match_scratch:SI 4 "=h"))]
  "TARGET_OPT_MULT && reload_completed"
  "mulr\\t%2, %3"
  [(set_attr "length" "2")
   (set_attr "stall" "mulr")])

(define_insn "mulsi3_1"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(mult:SI (match_operand:SI 1 "register_operand" "%0")
		 (match_operand:SI 2 "register_operand" "r")))
   (clobber (match_scratch:SI 3 "=l"))
   (clobber (match_scratch:SI 4 "=h"))]
  "TARGET_OPT_MULT"
  "mulr\\t%1, %2"
  [(set_attr "length" "2")
   (set_attr "stall" "mulr")])

(define_expand "mulsidi3"
  [(set (match_operand:DI 0 "register_operand" "")
	(mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" ""))
		 (sign_extend:DI (match_operand:SI 2 "register_operand" ""))))]
  "TARGET_OPT_MULT"
  "
{
  rtx hi = gen_reg_rtx (SImode);
  rtx lo = gen_reg_rtx (SImode);

  emit_insn (gen_mulsidi3_i (hi, lo, operands[1], operands[2]));
  emit_move_insn (gen_lowpart (SImode, operands[0]), lo);
  emit_move_insn (gen_highpart (SImode, operands[0]), hi);
  DONE;
}")

(define_insn "mulsidi3_i"
  [(set (match_operand:SI 0 "mep_hi_operand" "=h")
	(truncate:SI
	 (lshiftrt:DI
	  (mult:DI (sign_extend:DI
		    (match_operand:SI 2 "register_operand" "r"))
		   (sign_extend:DI
		    (match_operand:SI 3 "register_operand" "r")))
	  (const_int 32))))
   (set (match_operand:SI 1 "mep_lo_operand" "=l")
	(mult:SI (match_dup 2)
		 (match_dup 3)))]
  "TARGET_OPT_MULT"
  "mul\\t%2, %3"
  [(set_attr "length" "2")
   (set_attr "stall" "mul")])

(define_insn "smulsi3_highpart"
  [(set (match_operand:SI 0 "mep_hi_operand" "=h")
	(truncate:SI
	 (lshiftrt:DI
	  (mult:DI (sign_extend:DI
		    (match_operand:SI 1 "register_operand" "r"))
		   (sign_extend:DI
		    (match_operand:SI 2 "register_operand" "r")))
	  (const_int 32))))
   (clobber (reg:SI LO_REGNO))]
  "TARGET_OPT_MULT"
  "mul\\t%1, %2"
  [(set_attr "length" "2")
   (set_attr "stall" "mul")])

(define_expand "umulsidi3"
  [(set (match_operand:DI 0 "mep_hi_operand" "")
	(mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" ""))
		 (zero_extend:DI (match_operand:SI 2 "register_operand" ""))))]
  "TARGET_OPT_MULT"
  "
{
  rtx hi = gen_reg_rtx (SImode);
  rtx lo = gen_reg_rtx (SImode);

  emit_insn (gen_umulsidi3_i (hi, lo, operands[1], operands[2]));
  emit_move_insn (gen_lowpart (SImode, operands[0]), lo);
  emit_move_insn (gen_highpart (SImode, operands[0]), hi);
  DONE;
}")

(define_insn "umulsidi3_i"
  [(set (match_operand:SI 0 "mep_hi_operand" "=h")
	(truncate:SI
	 (lshiftrt:DI
	  (mult:DI (zero_extend:DI
		    (match_operand:SI 2 "register_operand" "r"))
		   (zero_extend:DI
		    (match_operand:SI 3 "register_operand" "r")))
	  (const_int 32))))
   (set (match_operand:SI 1 "mep_lo_operand" "=l")
	(mult:SI (match_dup 2)
		 (match_dup 3)))]
  "TARGET_OPT_MULT"
  "mulu\\t%2, %3"
  [(set_attr "length" "2")
   (set_attr "stall" "mul")])

(define_insn "umulsi3_highpart"
  [(set (match_operand:SI 0 "mep_hi_operand" "=h")
	(truncate:SI
	 (lshiftrt:DI
	  (mult:DI (zero_extend:DI
		    (match_operand:SI 1 "register_operand" "r"))
		   (zero_extend:DI
		    (match_operand:SI 2 "register_operand" "r")))
	  (const_int 32))))
   (clobber (reg:SI LO_REGNO))]
  "TARGET_OPT_MULT"
  "mulu %1, %2"
  [(set_attr "length" "2")
   (set_attr "stall" "mul")])

;; These two don't currently match because we don't have an adddi3 pattern.
(define_insn "*smultdi_and_add"
  [(set (match_operand:DI 0 "mep_hi_operand" "=d")
	(plus:DI (mult:DI (zero_extend:DI
			   (match_operand:SI 1 "register_operand" "r"))
			  (zero_extend:DI
			   (match_operand:SI 2 "register_operand" "r")))
		 (match_operand:DI 3 "mep_hi_operand" "0")))]
  "TARGET_OPT_MULT && TARGET_BIG_ENDIAN"
  "maddu\\t%1, %2"
  [(set_attr "length" "4")
   (set_attr "stall" "mul")])

(define_insn "*umultdi_and_add"
  [(set (match_operand:DI 0 "mep_hi_operand" "=d")
	(plus:DI (mult:DI (sign_extend:DI
			   (match_operand:SI 1 "register_operand" "r"))
			  (sign_extend:DI
			   (match_operand:SI 2 "register_operand" "r")))
		 (match_operand:DI 3 "mep_hi_operand" "0")))]
  "TARGET_OPT_MULT && TARGET_BIG_ENDIAN"
  "madd\\t%1, %2"
  [(set_attr "length" "4")
   (set_attr "stall" "mul")])

;; A pattern for 'r1 = r2 * r3 + r4'.  There are three possible
;; implementations:
;;
;;    (1) 'mulr;add3'.  This is usually the best choice if the instruction
;;	  is not part of a natural multiply-accumulate chain.  It has the
;;	  same latency as 'stc;maddr' but doesn't tie up $lo for as long.
;;
;;    (2) 'madd'.  This is the best choice if the instruction is in the
;;	  middle of a natural multiply-accumulate chain.  r4 will already
;;	  be in $lo and r1 will also be needed in $lo.
;;
;;    (3) 'maddr'.  This is the best choice if the instruction is at the
;;	  end of a natural multiply-accumulate chain.  r4 will be in $lo
;;	  but r1 will be needed in a GPR.
;;
;; In theory, we could put all the alternatives into a single pattern and
;; leave the register allocator to choose between them.  However, this can
;; sometimes produce poor results in practice.
;;
;; This pattern therefore describes a general GPR-to-GPR operation that
;; has a slight preference for cases in which operands 0 and 1 are tied.
;; After reload, we try to rewrite the patterns using peephole2s (if
;; enabled), falling back on define_splits if that fails.  See also
;; mep_reuse_lo_p.
(define_insn "maddsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(plus:SI (mult:SI (match_operand:SI 1 "register_operand" "%0,r")
			  (match_operand:SI 2 "register_operand" "r,r"))
		 (match_operand:SI 3 "register_operand" "r,r")))
   (clobber (match_scratch:SI 4 "=l,l"))
   (clobber (match_scratch:SI 5 "=h,h"))]
  "TARGET_OPT_MULT"
  "#"
  [(set_attr "length" "8")
   (set_attr "stall" "mulr")])

;; Implement maddsi3s using maddr if operand 3 is already available in $lo.
(define_peephole2
  [(parallel
	[(set (match_operand:SI 0 "register_operand" "")
	      (plus:SI (mult:SI (match_operand:SI 1 "register_operand" "")
				(match_operand:SI 2 "register_operand" ""))
		       (match_operand:SI 3 "register_operand" "")))
	 (clobber (match_scratch:SI 4 ""))
	 (clobber (match_scratch:SI 5 ""))])]
  "TARGET_OPT_MULT
   && reload_completed
   && mep_reuse_lo_p (operands[4], operands[3], insn,
		      !rtx_equal_p (operands[1], operands[3])
		      && !rtx_equal_p (operands[2], operands[3])
		      && (rtx_equal_p (operands[0], operands[3])
			  || peep2_reg_dead_p (1, operands[3])))"
  [(parallel
	[(set (match_dup 4)
	      (plus:SI (mult:SI (match_dup 0)
			        (match_dup 2))
		       (match_dup 4)))
	 (set (match_dup 0)
	      (plus:SI (mult:SI (match_dup 0)
				(match_dup 2))
		       (match_dup 4)))
	 (clobber (match_dup 5))])]
  "operands[2] = mep_mulr_source (0, operands[0], operands[1], operands[2]);")

;; This splitter implements maddsi3 as "mulr;add3".  It only works if
;; operands 0 and 3 are distinct, since operand 0 is clobbered before
;; operand 3 is used.
(define_split
  [(set (match_operand:SI 0 "register_operand" "")
	(plus:SI (mult:SI (match_operand:SI 1 "register_operand" "")
			  (match_operand:SI 2 "register_operand" ""))
		 (match_operand:SI 3 "register_operand" "")))
   (clobber (match_scratch:SI 4 ""))
   (clobber (match_scratch:SI 5 ""))]
  "TARGET_OPT_MULT
   && reload_completed
   && !rtx_equal_p (operands[0], operands[3])"
  [(parallel [(set (match_dup 0)
		   (mult:SI (match_dup 0)
			    (match_dup 2)))
	      (clobber (match_dup 4))
	      (clobber (match_dup 5))])
   (set (match_dup 0)
	(plus:SI (match_dup 0)
		 (match_dup 3)))]
  "operands[2] = mep_mulr_source (0, operands[0], operands[1], operands[2]);")

;; This is the fallback splitter for maddsi3.  It moves operand 3 into
;; $lo and then uses maddr.
(define_split
  [(set (match_operand:SI 0 "register_operand" "")
	(plus:SI (mult:SI (match_operand:SI 1 "register_operand" "")
			  (match_operand:SI 2 "register_operand" ""))
		 (match_operand:SI 3 "register_operand" "")))
   (clobber (match_scratch:SI 4 ""))
   (clobber (match_scratch:SI 5 ""))]
  "TARGET_OPT_MULT
   && reload_completed"
  [(parallel [(set (match_dup 4)
		   (plus:SI (mult:SI (match_dup 0)
				     (match_dup 2))
			    (match_dup 4)))
	      (set (match_dup 0)
		   (plus:SI (mult:SI (match_dup 0)
				     (match_dup 2))
			    (match_dup 4)))
	      (clobber (match_dup 5))])]
{
  emit_move_insn (operands[4], operands[3]);
  operands[2] = mep_mulr_source (0, operands[0], operands[1], operands[2]);
})

;; Remove unnecessary stcs to $lo.  This cleans up the moves generated
;; by earlier calls to mep_reuse_lo_p.
(define_peephole2
  [(set (match_operand:SI 0 "mep_lo_operand" "")
	(match_operand:SI 1 "register_operand" ""))]
  "TARGET_OPT_MULT
   && mep_reuse_lo_p (operands[0], operands[1], insn,
		      peep2_reg_dead_p (1, operands[1]))"
  [(const_int 0)]
{
  emit_note (NOTE_INSN_DELETED);
  DONE;
})

(define_insn "maddsi3_lo"
  [(set (match_operand:SI 0 "mep_lo_operand" "=l")
	(plus:SI (mult:SI (match_operand:SI 1 "register_operand" "r")
			  (match_operand:SI 2 "register_operand" "r"))
		 (match_operand:SI 3 "mep_lo_operand" "0")))
   (clobber (match_scratch:SI 4 "=h"))]
  "TARGET_OPT_MULT && reload_completed"
  "madd\\t%1, %2"
  [(set_attr "length" "4")
   (set_attr "stall" "mul")])

(define_insn "maddsi3r"
  [(set (match_operand:SI 0 "mep_lo_operand" "=l")
	(plus:SI (mult:SI (match_operand:SI 2 "register_operand" "1")
			  (match_operand:SI 3 "register_operand" "r"))
		 (match_operand:SI 4 "register_operand" "0")))
   (set (match_operand:SI 1 "register_operand" "=r")
	(plus:SI (mult:SI (match_dup 2)
			  (match_dup 3))
		 (match_dup 4)))
   (clobber (match_scratch:SI 5 "=h"))]
  "TARGET_OPT_MULT && reload_completed"
  "maddr\\t%2, %3"
  [(set_attr "length" "4")
   (set_attr "stall" "mulr")])

(define_insn "*shift_1_or_2_and_add"
  [(set (match_operand:SI 0 "mep_r0_operand" "=z")
	(plus:SI (mult:SI (match_operand:SI 1 "register_operand" "r")
			  (match_operand:SI 2 "mep_slad_operand" "n"))
		 (match_operand:SI 3 "register_operand" "r")))]
  ""
  "sl%b2ad3\\t%0, %1, %3"
  [(set_attr "length" "2")
   (set_attr "stall" "int2")])

(define_insn "divmodsi4"
  [(set (match_operand:SI 0 "mep_lo_operand" "=l")
	(div:SI (match_operand:SI 1 "register_operand" "r")
		(match_operand:SI 2 "register_operand" "r")))
   (set (match_operand:SI 3 "mep_hi_operand" "=h")
	(mod:SI (match_dup 1)
		(match_dup 2)))]
  "TARGET_OPT_DIV"
  "div\\t%1, %2"
  [(set_attr "length" "2")
   (set_attr "stall" "div")
   (set_attr "may_trap" "yes")])

(define_insn "udivmodsi4"
  [(set (match_operand:SI 0 "mep_lo_operand" "=l")
	(udiv:SI (match_operand:SI 1 "register_operand" "r")
		 (match_operand:SI 2 "register_operand" "r")))
   (set (match_operand:SI 3 "mep_hi_operand" "=h")
	(umod:SI (match_dup 1)
		(match_dup 2)))]
  "TARGET_OPT_DIV"
  "divu\\t%1, %2"
  [(set_attr "length" "2")
   (set_attr "stall" "div")
   (set_attr "may_trap" "yes")])

(define_insn "negsi2"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(neg:SI (match_operand:SI 1 "register_operand" "r")))]
  ""
  "neg\\t%0, %1"
  [(set_attr "length" "2")])

;; We have "absolute difference between two regs" which isn't quite
;; what gcc is expecting.
(define_expand "abssi2"
  [(set (match_dup 2) (const_int 0))
   (set (match_operand:SI 0 "register_operand" "")
	(abs:SI (minus:SI (match_operand:SI 1 "register_operand" "")
			  (match_dup 2))
		))]
  "TARGET_OPT_ABSDIFF"
  "operands[2] = gen_reg_rtx (SImode);")

(define_insn "*absdiff"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(abs:SI (minus:SI (match_operand:SI 1 "register_operand" "0")
			  (match_operand:SI 2 "register_operand" "r"))))]
  "TARGET_OPT_ABSDIFF"
  "abs\\t%0, %2"
  [(set_attr "length" "4")])

(define_split
  [(set (match_operand:SI 0 "register_operand" "")
	(abs:SI (plus:SI (match_operand:SI 1 "register_operand" "")
			 (match_operand:SI 2 "immediate_operand" ""))))
   (clobber (match_operand:SI 3 "register_operand" ""))]
  "!reload_completed"
  [(set (match_dup 3)
	(match_dup 4))
   (set (match_operand:SI 0 "register_operand" "")
	(abs:SI (minus:SI (match_operand:SI 1 "register_operand" "")
			  (match_dup 3))))]
  "operands[4] = GEN_INT (-INTVAL (operands[2]));")

(define_insn "sminsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(smin:SI (match_operand:SI 1 "register_operand" "0")
		 (match_operand:SI 2 "nonmemory_operand" "r")))]
  "TARGET_OPT_MINMAX"
  "min\\t%0, %2"
  [(set_attr "length" "4")])

(define_insn "smaxsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(smax:SI (match_operand:SI 1 "register_operand" "0")
		 (match_operand:SI 2 "nonmemory_operand" "r")))]
  "TARGET_OPT_MINMAX"
  "max\\t%0, %2"
  [(set_attr "length" "4")])

(define_insn "uminsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(umin:SI (match_operand:SI 1 "register_operand" "0")
		 (match_operand:SI 2 "nonmemory_operand" "r")))]
  "TARGET_OPT_MINMAX"
  "minu\\t%0, %2"
  [(set_attr "length" "4")])

(define_insn "umaxsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(umax:SI (match_operand:SI 1 "register_operand" "0")
		 (match_operand:SI 2 "nonmemory_operand" "r")))]
  "TARGET_OPT_MINMAX"
  "maxu\\t%0, %2"
  [(set_attr "length" "4")])

;; Average:  a = (b+c+1)>>1
(define_insn "*averagesi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(ashiftrt:SI (plus:SI (plus:SI
				(match_operand:SI 1 "register_operand" "0")
				(match_operand:SI 2 "register_operand" "r"))
			      (const_int 1))
		     (const_int 1)))]
  "TARGET_OPT_AVERAGE"
  "ave\\t%0, %2"
  [(set_attr "length" "4")])

;; clip support

(define_insn "clip_maxmin"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(smax:SI (smin:SI (match_operand:SI 1 "register_operand" "0")
			  (match_operand:SI 2 "immediate_operand" "n"))
		 (match_operand:SI 3 "immediate_operand" "n")))]
  "mep_allow_clip (operands[2], operands[3], 1)"
  "clip\\t%0, %B2"
  [(set_attr "length" "4")])

(define_insn "clip_minmax"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(smin:SI (smax:SI (match_operand:SI 1 "register_operand" "0")
			  (match_operand:SI 2 "immediate_operand" "n"))
		 (match_operand:SI 3 "immediate_operand" "n")))]
  "mep_allow_clip (operands[3], operands[2], 1)"
  "clip\\t%0, %B3"
  [(set_attr "length" "4")])

(define_insn "clipu_maxmin"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(smax:SI (smin:SI (match_operand:SI 1 "register_operand" "0")
			  (match_operand:SI 2 "immediate_operand" "n"))
		 (match_operand:SI 3 "immediate_operand" "n")))]
  "mep_allow_clip (operands[2], operands[3], 0)"
  "clipu\\t%0, %U2"
  [(set_attr "length" "4")])

(define_insn "clipu_minmax"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(smin:SI (smax:SI (match_operand:SI 1 "register_operand" "0")
			  (match_operand:SI 2 "immediate_operand" "n"))
		 (match_operand:SI 3 "immediate_operand" "n")))]
  "mep_allow_clip (operands[3], operands[2], 0)"
  "clipu\\t%0, %U3"
  [(set_attr "length" "4")])

;; ::::::::::::::::::::
;; ::
;; :: 32 bit Integer Shifts and Rotates
;; ::
;; ::::::::::::::::::::

(define_insn "ashlsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,z")
	(ashift:SI (match_operand:SI 1 "register_operand" "0,r")
		   (match_operand:SI 2 "nonmemory_operand" "rM,M")))]
  ""
  "@
   sll\\t%0, %2
   sll3\\t%0, %1, %2"
  [(set_attr "length" "2,2")
   (set_attr "shiftop" "operand2")])

(define_insn "ashrsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(ashiftrt:SI (match_operand:SI 1 "register_operand" "0")
		     (match_operand:SI 2 "nonmemory_operand" "rM")))]
  ""
  "sra\\t%0, %2"
  [(set_attr "length" "2")
   (set_attr "shiftop" "operand2")])

(define_insn "lshrsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(lshiftrt:SI (match_operand:SI 1 "register_operand" "0")
		     (match_operand:SI 2 "nonmemory_operand" "rM")))]
  ""
  "srl\\t%0, %2"
  [(set_attr "length" "2")
   (set_attr "shiftop" "operand2")])

;; ::::::::::::::::::::
;; ::
;; :: 32 Bit Integer Logical operations
;; ::
;; ::::::::::::::::::::

(define_insn "andsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(and:SI (match_operand:SI 1 "register_operand" "%0,r")
		(match_operand:SI 2 "nonmemory_operand" "r,J")))]
  ""
  "@
   and\\t%0, %2
   and3\\t%0, %1, %J2"
  [(set_attr "length" "2,4")])

(define_insn "iorsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(ior:SI (match_operand:SI 1 "register_operand" "%0,r")
		(match_operand:SI 2 "nonmemory_operand" "r,J")))]
  ""
  "@
   or\\t%0, %2
   or3\\t%0, %1, %J2"
  [(set_attr "length" "2,4")])

(define_insn "xorsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(xor:SI (match_operand:SI 1 "register_operand" "%0,r")
		(match_operand:SI 2 "nonmemory_operand" "r,J")))]
  ""
  "@
   xor\\t%0, %2
   xor3\\t%0, %1, %J2"
  [(set_attr "length" "2,4")])

(define_expand "one_cmplsi2"
  [(set (match_operand:SI 0 "register_operand" "")
	(not:SI (match_operand:SI 1 "register_operand" "")))]
  ""
  "operands[2] = operands[1];
   ")

;; No separate insn for this; use NOR
(define_insn "*one_cmplsi3_internal"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(not:SI (match_operand:SI 1 "register_operand" "0")))]
  ""
  "nor\\t%0, %0"
  [(set_attr "length" "2")])

;; ::::::::::::::::::::
;; ::
;; :: Bit Manipulation
;; ::
;; ::::::::::::::::::::

(define_insn "*bitop_be"
  [(set (match_operand:QI 0 "mep_Y_operand" "=Y")
	(subreg:QI (match_operator:SI 3 "mep_bit_operator"
			[(subreg:SI (match_operand:QI 1 "mep_Y_operand" "0") 0)
			 (match_operand 2 "immediate_operand" "n")])
		   3)
	)]
  "TARGET_BIG_ENDIAN && TARGET_OPT_BITOPS
   && rtx_equal_p (operands[0], operands[1])"
  "b%L3m\\t%0, %b2"
  [(set_attr "length" "2")])

(define_insn "*bitop_le"
  [(set (match_operand:QI 0 "mep_Y_operand" "=Y")
	(subreg:QI (match_operator:SI 3 "mep_bit_operator"
			[(subreg:SI (match_operand:QI 1 "mep_Y_operand" "0") 0)
			 (match_operand 2 "immediate_operand" "n")])
		   0)
	)]
  "!TARGET_BIG_ENDIAN && TARGET_OPT_BITOPS
   && rtx_equal_p (operands[0], operands[1])"
  "b%L3m\\t%0, %b2"
  [(set_attr "length" "2")])

(define_insn "btstm"
  [(set (match_operand:SI 0 "mep_r0_operand" "=z")
	(and:SI (subreg:SI (match_operand:QI 1 "mep_Y_operand" "Y") 0)
		(match_operand 2 "immediate_operand" "n"))
	)]
  "TARGET_OPT_BITOPS && mep_bit_position_p (operands[2], 1)"
  "btstm\\t%0, %1, %b2"
  [(set_attr "length" "2")])

(define_insn "tas"
  [(parallel [(set (match_operand:SI 0 "mep_r0_operand" "=z")
		   (zero_extend:SI (match_operand:QI 1 "mep_Y_operand" "+Y")))
	      (set (match_dup 1)
		   (const_int 1))
	      ]
	     )]
  "TARGET_OPT_BITOPS"
  "tas\\t%0, %1"
  [(set_attr "length" "2")])

(define_peephole2
  [(set (match_operand:SI 0 "mep_r0_operand" "")
	(zero_extend:SI (match_operand:QI 1 "mep_Y_operand" "")))
   (set (match_operand:QI 2 "register_operand" "")
	(const_int 1))
   (set (match_dup 1)
	(match_dup 2))
   ]
  "TARGET_OPT_BITOPS"
  [(parallel [(set (match_dup 0)
		   (zero_extend:SI (match_dup 1)))
	      (set (match_dup 1)
		   (const_int 1))
	      ])]
  "")

(define_peephole2
  [(set (match_operand:SI 0 "mep_r0_operand" "")
	(sign_extend:SI (match_operand:QI 1 "mep_Y_operand" "")))
   (set (match_operand:QI 2 "register_operand" "")
	(const_int 1))
   (set (match_dup 1)
	(match_dup 2))
   ]
  "TARGET_OPT_BITOPS"
  [(parallel [(set (match_dup 0)
		   (zero_extend:SI (match_dup 1)))
	      (set (match_dup 1)
		   (const_int 1))
	      ])
   (set (match_dup 0)
	(sign_extend:SI (match_dup 3)))]
  "operands[3] = gen_lowpart (QImode, operands[0]);")


;; ::::::::::::::::::::
;; ::
;; :: Conditional branches and stores
;; ::
;; ::::::::::::::::::::

(define_expand "cbranchsi4"
  [(set (pc)
	(if_then_else (match_operator 0 "ordered_comparison_operator"
				      [(match_operand:SI 1 "register_operand" "")
				       (match_operand:SI 2 "nonmemory_operand" "")])
		      (label_ref (match_operand 3 "" ""))
		      (pc)))]
  ""
  "emit_jump_insn (gen_branch_true (operands[3],
			       mep_expand_cbranch (operands)));
   DONE;")
  
(define_expand "branch_true"
  [(set (pc)
	(if_then_else (match_operand 1 "" "")
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "")
  
(define_expand "cstoresi4"
  [(set (match_operand:SI 0 "register_operand" "")
	(match_operator:SI 1 "ordered_comparison_operator"
			   [(match_operand:SI 2 "register_operand" "")
			    (match_operand:SI 3 "nonmemory_operand" "")]))]
  ""
  "if (mep_expand_setcc (operands)) DONE; else FAIL;")

;; ------------------------------------------------------------

(define_insn "*slt"
  [(set (match_operand:SI 0 "register_operand" "=z,z,r")
	(lt:SI (match_operand:SI 1 "register_operand" "r,r,r")
	    (match_operand:SI 2 "nonmemory_operand" "r,M,I")))]
  ""
  "slt3\\t%0, %1, %2"
  [(set_attr "length" "2,2,4")])

(define_insn "*sltu"
  [(set (match_operand:SI 0 "register_operand" "=z,z,r")
	(ltu:SI (match_operand:SI 1 "register_operand" "r,r,r")
	     (match_operand:SI 2 "nonmemory_operand" "r,M,J")))]
  ""
  "sltu3\\t%0, %1, %2"
  [(set_attr "length" "2,2,4")])

(define_insn "*bcpeq_true"
  [(set (pc)
	(if_then_else (eq:SI (reg:SI CBCR_REGNO)
			     (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "bcpeq\t0, %l0"
  [(set_attr "length" "4")])

(define_insn "*bcpeq_false"
  [(set (pc)
	(if_then_else (eq:SI (reg:SI CBCR_REGNO)
			     (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "bcpne\t0, %l0"
  [(set_attr "length" "4")])

(define_insn "*bcpne_true"
  [(set (pc)
	(if_then_else (ne:SI (reg:SI CBCR_REGNO)
			     (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "bcpne\t0, %l0"
  [(set_attr "length" "4")])

(define_insn "*bcpne_false"
  [(set (pc)
	(if_then_else (ne:SI (reg:SI CBCR_REGNO)
			     (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "bcpeq\t0, %l0"
  [(set_attr "length" "4")])

;; ??? The lengths here aren't correct, since no attempt it made to
;; find "beqz" in the 256-byte range.  However, this should not affect
;; bundling, since we never run core branches in parallel.

(define_insn "mep_beq_true"
  [(set (pc)
	(if_then_else (eq (match_operand:SI 0 "register_operand" "r")
			  (match_operand:SI 1 "mep_reg_or_imm4_operand" "rN"))
		      (label_ref (match_operand 2 "" ""))
		      (pc)))]
  ""
  "* return mep_emit_cbranch (operands, 0);"
  [(set_attr "length" "4")]  )

(define_insn "*beq_false"
  [(set (pc)
	(if_then_else (eq (match_operand:SI 0 "register_operand" "r")
			  (match_operand:SI 1 "mep_reg_or_imm4_operand" "rN"))
		      (pc)
		      (label_ref (match_operand 2 "" ""))))]
  ""
  "* return mep_emit_cbranch (operands, 1);"
  [(set_attr "length" "4")])

(define_insn "mep_bne_true"
  [(set (pc)
	(if_then_else (ne (match_operand:SI 0 "register_operand" "r")
			  (match_operand:SI 1 "mep_reg_or_imm4_operand" "rN"))
		      (label_ref (match_operand 2 "" ""))
		      (pc)))]
  ""
  "* return mep_emit_cbranch (operands, 1); "
  [(set_attr "length" "4")])

(define_insn "*bne_false"
  [(set (pc)
	(if_then_else (ne (match_operand:SI 0 "register_operand" "r")
			  (match_operand:SI 1 "mep_reg_or_imm4_operand" "rN"))
		      (pc)
		      (label_ref (match_operand 2 "" ""))))]
  ""
  "* return mep_emit_cbranch (operands, 0); "
  [(set_attr "length" "4")])

(define_insn "mep_blti"
  [(set (pc)
	(if_then_else (lt (match_operand:SI 0 "register_operand" "r")
			  (match_operand:SI 1 "mep_imm4_operand" "N"))
		      (label_ref (match_operand 2 "" ""))
		      (pc)))]
  ""
  "blti\\t%0, %1, %l2"
  [(set_attr "length" "4")])

(define_insn "*bgei"
  [(set (pc)
	(if_then_else (ge (match_operand:SI 0 "register_operand" "r")
			  (match_operand:SI 1 "mep_imm4_operand" "N"))
		      (label_ref (match_operand 2 "" ""))
		      (pc)))]
  ""
  "bgei\\t%0, %1, %l2"
  [(set_attr "length" "4")])

;; ::::::::::::::::::::
;; ::
;; :: Call and branch instructions
;; ::
;; ::::::::::::::::::::

(define_expand "call"
  [(parallel [(call (match_operand:QI 0 "" "")
		    (match_operand:SI 1 "" ""))
	      (use (match_operand:SI 2 "" ""))
	      (clobber (reg:SI REGSAVE_CONTROL_TEMP))
	      ])]
  ""
  "
{
  mep_expand_call (operands, 0);
  DONE;
}")

(define_insn "call_internal"
  [(call (mem (match_operand:SI 0 "mep_call_address_operand" "R,r"))
	 (match_operand:SI 1 "" ""))
   (use (match_operand:SI 2 "const_int_operand" ""))
   (use (match_operand:SI 3 "mep_tp_operand" "b,b"))
   (use (match_operand:SI 4 "mep_gp_operand" "v,v"))
   (clobber (reg:SI LP_REGNO))
   (clobber (reg:SI REGSAVE_CONTROL_TEMP))
  ]
  ""
{
  static char const pattern[2][2][8] = 
  {
    { "bsrv\t%0", "jsrv\t%0" },
    { "bsr\t%0", "jsr\t%0" }
  };

  return pattern[mep_vliw_mode_match (operands[2])][which_alternative];
}
  [(set_attr "length" "4,2")])

(define_expand "sibcall"
  [(parallel [(call (match_operand:QI 0 "" "")
		    (match_operand:SI 1 "" ""))
	      (use (match_operand:SI 2 "" ""))
	      (use (reg:SI LP_REGNO))
	      (clobber (reg:SI REGSAVE_CONTROL_TEMP))
	      ])]
  ""
  "")

(define_insn "*sibcall_internal"
  [(call (mem (match_operand:SI 0 "mep_nearsym_operand" "s"))
	 (match_operand:SI 1 "" ""))
   (use (match_operand:SI 2 "const_int_operand" ""))
   (use (reg:SI LP_REGNO))
   (clobber (reg:SI REGSAVE_CONTROL_TEMP))
  ]
  "SIBLING_CALL_P (insn)"
{
  if (mep_vliw_jmp_match (operands[2]))
    return "jmp\t%0";
  else if (mep_vliw_mode_match (operands[2]))
    return
        "movu	$0, %0\n\
	jmp	$0";
  else
    return
	"ldc	$12, $lp\n\
	movh	$11, %%hi(%0)\n\
	xor3	$12, $12, 1\n\
	add3	$11, $11, %%lo(%0+1)\n\
	stc	$12, $lp\n\
	jmp	$11";
}
  [(set_attr "length" "48")
   (set_attr "slot" "multi")])

(define_expand "call_value"
  [(parallel [(set (match_operand 0 "" "")
		   (call (match_operand:QI 1 "" "")
		         (match_operand:SI 2 "" "")))
	      (use (match_operand:SI 3 "" ""))
	      (clobber (reg:SI REGSAVE_CONTROL_TEMP))
	      ])]
  ""
  "
{
  mep_expand_call (operands, 1);
  DONE;
}")

(define_insn "call_value_internal"
  [(set (match_operand 0 "register_operand" "=rx,rx")
	(call (mem:SI (match_operand:SI 1 "mep_call_address_operand" "R,r"))
	      (match_operand:SI 2 "" "")))
   (use (match_operand:SI 3 "const_int_operand" ""))
   (use (match_operand:SI 4 "mep_tp_operand" "b,b"))
   (use (match_operand:SI 5 "mep_gp_operand" "v,v"))
   (clobber (reg:SI LP_REGNO))
   (clobber (reg:SI REGSAVE_CONTROL_TEMP))
  ]
  ""
{
  static char const pattern[2][2][8] = 
  {
    { "bsrv\t%1", "jsrv\t%1" },
    { "bsr\t%1", "jsr\t%1" }
  };

  return pattern[mep_vliw_mode_match (operands[3])][which_alternative];
}
  [(set_attr "length" "4,2")])

(define_expand "sibcall_value"
  [(parallel [(set (match_operand 0 "" "")
		   (call (match_operand:QI 1 "" "")
		         (match_operand:SI 2 "" "")))
	      (use (match_operand:SI 3 "" ""))
	      (use (reg:SI LP_REGNO))
	      (clobber (reg:SI REGSAVE_CONTROL_TEMP))
	      ])]
  ""
  "")

(define_insn "*sibcall_value_internal"
  [(set (match_operand 0 "register_operand" "=rx")
	(call (mem (match_operand:SI 1 "mep_nearsym_operand" "s"))
	      (match_operand:SI 2 "" "")))
   (use (match_operand:SI 3 "const_int_operand" ""))
   (use (reg:SI LP_REGNO))
   (clobber (reg:SI REGSAVE_CONTROL_TEMP))
  ]
  "SIBLING_CALL_P (insn)"
{
  if (mep_vliw_jmp_match (operands[3]))
    return "jmp\t%1";
  else if (mep_vliw_mode_match (operands[3]))
    return
        "movu	$0, %1\n\
	jmp	$0";
  else
    return
	"ldc	$12, $lp\n\
	movh	$11, %%hi(%1)\n\
	xor3	$12, $12, 1\n\
	add3	$11, $11, %%lo(%1+1)\n\
	stc	$12, $lp\n\
	jmp	$11";
}
  [(set_attr "length" "48")
   (set_attr "slot" "multi")])

(define_insn "return_internal"
  [(return)
   (use (match_operand:SI 0 "register_operand" ""))]
  ""
  "* return (REGNO (operands[0]) == LP_REGNO) ? \"ret\" : \"jmp\\t%0\";"
  [(set_attr "length" "2")
   (set_attr "stall" "ret")])

(define_insn "eh_return_internal"
  [(return)
   (use (reg:SI 10))
   (use (reg:SI 11))
   (use (reg:SI LP_REGNO))
   (clobber (reg:SI REGSAVE_CONTROL_TEMP))
  ]
  ""
  "ret"
  [(set_attr "length" "2")
   (set_attr "stall" "ret")])

;; The assembler replaces short jumps with long jumps as needed.
(define_insn "jump"
  [(set (pc) (label_ref (match_operand 0 "" "")))]
  ""
  "bra\\t%l0"
  [(set_attr "length" "4")])

(define_insn "indirect_jump"
  [(set (pc) (match_operand:SI 0 "register_operand" "r"))]
  ""
  "jmp\\t%0"
  [(set_attr "length" "2")])

(define_insn "tablejump"
  [(set (pc) (match_operand:SI 0 "register_operand" "r"))
   (use (label_ref (match_operand 1 "" "")))]
  ""
  "jmp\\t%0"
  [(set_attr "length" "2")])


;; ::::::::::::::::::::
;; ::
;; :: Low Overhead Looping
;; ::
;; ::::::::::::::::::::

;; This insn is volatile because we'd like it to stay in its original
;; position, just before the loop header.  If it stays there, we might
;; be able to convert it into a "repeat" insn.
(define_insn "doloop_begin_internal"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(unspec_volatile:SI
	 [(match_operand:SI 1 "register_operand" "0")
	  (match_operand 2 "const_int_operand" "")] UNS_REPEAT_BEG))]
  ""
  { gcc_unreachable (); }
  [(set_attr "length" "4")])

(define_expand "doloop_begin"
  [(use (match_operand 0 "register_operand" ""))
   (use (match_operand:QI 1 "const_int_operand" ""))
   (use (match_operand:QI 2 "const_int_operand" ""))
   (use (match_operand:QI 3 "const_int_operand" ""))]
  "!profile_arc_flag && TARGET_OPT_REPEAT"
  "if (INTVAL (operands[3]) > 1)
     FAIL;
   mep_emit_doloop (operands, 0);
   DONE;
  ")

(define_insn "doloop_end_internal"
  [(set (pc)
	(if_then_else (ne (match_operand:SI 0 "nonimmediate_operand" "+r,cxy,*m")
			  (const_int 0))
		      (label_ref (match_operand 1 "" ""))
		      (pc)))
   (set (match_dup 0)
	(plus:SI (match_dup 0)
		 (const_int -1)))
   (unspec [(match_operand 2 "const_int_operand" "")] UNS_REPEAT_END)
   (clobber (match_scratch:SI 3 "=X,&r,&r"))]
  ""
  { gcc_unreachable (); }
  ;; Worst case length:
  ;;
  ;;      lw <op3>,<op0>	4
  ;;      add <op3>,-1		2
  ;;      sw <op3>,<op0>	4
  ;;      jmp <op1>		4
  ;; 1f:
  [(set_attr "length" "14")
   (set_attr "slot" "multi")])

(define_expand "doloop_end"
  [(use (match_operand 0 "nonimmediate_operand" ""))
   (use (match_operand:QI 1 "const_int_operand" ""))
   (use (match_operand:QI 2 "const_int_operand" ""))
   (use (match_operand:QI 3 "const_int_operand" ""))
   (use (label_ref (match_operand 4 "" "")))]
  "!profile_arc_flag && TARGET_OPT_REPEAT"
  "if (INTVAL (operands[3]) > 1)
     FAIL;
   if (GET_CODE (operands[0]) == REG && GET_MODE (operands[0]) != SImode)
     FAIL;
   mep_emit_doloop (operands, 1);
   DONE;
  ")

(define_insn "repeat"
  [(set (reg:SI RPC_REGNO)
	(unspec:SI [(match_operand:SI 0 "mep_r0_15_operand" "r")
		    (match_operand:SI 1 "" "")]
		   UNS_REPEAT_BEG))]
  ""
  "repeat\\t%0,%l1"
  [(set_attr "length" "4")])

(define_insn "repeat_end"
  [(unspec [(const_int 0)] UNS_REPEAT_END)]
  ""
  "# repeat end"
  [(set_attr "length" "0")])

(define_insn "erepeat"
  [(unspec [(match_operand 0 "" "")] UNS_EREPEAT_BEG)]
  ""
  "erepeat\\t%l0"
  [(set_attr "length" "4")])

(define_insn "erepeat_end"
  [(unspec [(const_int 0)] UNS_EREPEAT_END)]
  ""
  "# erepeat end"
  [(set_attr "length" "0")
   (set_attr "slot" "multi")])


;; ::::::::::::::::::::
;; ::
;; :: Prologue and Epilogue instructions
;; ::
;; ::::::::::::::::::::

(define_expand "prologue"
  [(const_int 1)]
  ""
  "
{
  mep_expand_prologue ();
  DONE;
}")

(define_expand "epilogue"
  [(return)]
  ""
  "
{
  mep_expand_epilogue ();
  DONE;
}")

(define_expand "eh_return"
  [(use (match_operand:SI 0 "register_operand" "r"))]
  ""
  "
{
  mep_expand_eh_return (operands);
  DONE;
}")

(define_insn_and_split "eh_epilogue"
  [(unspec [(match_operand:SI 0 "register_operand" "r")] UNS_EH_EPILOGUE)
   (use (reg:SI LP_REGNO))]
  ""
  "#"
  "reload_completed"
  [(const_int 1)]
  "mep_emit_eh_epilogue (operands); DONE;"
  [(set_attr "slot" "multi")])

(define_expand "sibcall_epilogue"
  [(const_int 0)]
  ""
  "
{
  mep_expand_sibcall_epilogue ();
  DONE;
}")

(define_insn "mep_bb_trace_ret"
  [(unspec_volatile [(const_int 0)] UNS_BB_TRACE_RET)]
  ""
  "* return mep_emit_bb_trace_ret ();"
  [(set_attr "slot" "multi")])

(define_insn "mep_disable_int"
  [(unspec_volatile [(const_int 0)] UNS_DISABLE_INT)]
  ""
  "di"
  [(set_attr "length" "2")])

(define_insn "mep_enable_int"
  [(unspec_volatile [(const_int 0)] UNS_ENABLE_INT)]
  ""
  "ei"
  [(set_attr "length" "2")])

(define_insn "mep_reti"
  [(return)
   (unspec_volatile [(const_int 0)] UNS_RETI)]
  ""
  "reti"
  [(set_attr "length" "2")])

;; ::::::::::::::::::::
;; ::
;; :: Miscellaneous instructions
;; ::
;; ::::::::::::::::::::

(define_insn "nop"
  [(const_int 0)]
  ""
  "nop"
  [(set_attr "length" "2")])

(define_insn "nop32"
  [(const_int 1)]
  ""
  "or3\\t$0, $0, 0"
  [(set_attr "length" "4")])

(define_insn "blockage"
  [(unspec_volatile [(const_int 0)] UNS_BLOCKAGE)]
  ""
  ""
  [(set_attr "length" "0")
   (set_attr "slot" "multi")])


(define_insn "djmark"
  [(unspec_volatile [(const_int 0)] 999)]
  ""
  "# dj"
  [(set_attr "length" "0")
   (set_attr "slot" "multi")])

