;; Frv Machine Description
;; Copyright (C) 1999-2018 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/>.

;;- See file "rtl.def" for documentation on define_insn, match_*, et. al.


;; ::::::::::::::::::::
;; ::
;; :: Unspec's used
;; ::
;; ::::::::::::::::::::

;; GOT constants must go 12/HI/LO for the splitter to work

(define_constants
  [(UNSPEC_BLOCKAGE		0)
   (UNSPEC_CC_TO_GPR		1)
   (UNSPEC_GPR_TO_CC		2)
   (UNSPEC_PIC_PROLOGUE		3)
   (UNSPEC_CR_LOGIC		4)
   (UNSPEC_STACK_ADJUST		5)
   (UNSPEC_EH_RETURN_EPILOGUE	6)
   (UNSPEC_GOT			7)
   (UNSPEC_LDD			8)
   (UNSPEC_OPTIONAL_MEMBAR	9)

   (UNSPEC_GETTLSOFF			200)
   (UNSPEC_TLS_LOAD_GOTTLSOFF12		201)
   (UNSPEC_TLS_INDIRECT_CALL		202)
   (UNSPEC_TLS_TLSDESC_LDD		203)
   (UNSPEC_TLS_TLSDESC_LDD_AUX		204)
   (UNSPEC_TLS_TLSOFF_LD		205)
   (UNSPEC_TLS_LDDI			206)
   (UNSPEC_TLSOFF_HILO			207)

   (R_FRV_GOT12			11)
   (R_FRV_GOTHI			12)
   (R_FRV_GOTLO			13)
   (R_FRV_FUNCDESC		14)
   (R_FRV_FUNCDESC_GOT12	15)
   (R_FRV_FUNCDESC_GOTHI	16)
   (R_FRV_FUNCDESC_GOTLO	17)
   (R_FRV_FUNCDESC_VALUE	18)
   (R_FRV_FUNCDESC_GOTOFF12	19)
   (R_FRV_FUNCDESC_GOTOFFHI	20)
   (R_FRV_FUNCDESC_GOTOFFLO	21)
   (R_FRV_GOTOFF12		22)
   (R_FRV_GOTOFFHI		23)
   (R_FRV_GOTOFFLO		24)
   (R_FRV_GPREL12		25)
   (R_FRV_GPRELHI		26)
   (R_FRV_GPRELLO		27)
   (R_FRV_GOTTLSOFF_HI		28)
   (R_FRV_GOTTLSOFF_LO		29)
   (R_FRV_TLSMOFFHI		30)
   (R_FRV_TLSMOFFLO           	31)
   (R_FRV_TLSMOFF12           	32)
   (R_FRV_TLSDESCHI           	33)
   (R_FRV_TLSDESCLO           	34)
   (R_FRV_GOTTLSDESCHI		35)
   (R_FRV_GOTTLSDESCLO		36)

   (GR8_REG			8)
   (GR9_REG			9)
   (GR14_REG			14)
   ;; LR_REG conflicts with definition in frv.h
   (LRREG                       169)
   (FDPIC_REG			15)
   ])

(define_mode_iterator IMODE [QI HI SI DI])
(define_mode_attr IMODEsuffix [(QI "b") (HI "h") (SI "") (DI "d")])
(define_mode_attr BREADsuffix [(QI "ub") (HI "uh") (SI "") (DI "d")])

(define_attr "length" "" (const_int 4))

;; Processor type -- this attribute must exactly match the processor_type
;; enumeration in frv-protos.h.

(define_attr "cpu" "generic,fr550,fr500,fr450,fr405,fr400,fr300,simple,tomcat"
  (const (symbol_ref "(enum attr_cpu) frv_cpu_type")))

;; Attribute is "yes" for branches and jumps that span too great a distance
;; to be implemented in the most natural way.  Such instructions will use
;; a call instruction in some way.

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

;; Instruction type
;; "unknown" must come last.
(define_attr "type"
  "int,sethi,setlo,mul,div,gload,gstore,fload,fstore,movfg,movgf,macc,scan,cut,branch,jump,jumpl,call,spr,trap,fnop,fsconv,fsadd,fscmp,fsmul,fsmadd,fsdiv,sqrt_single,fdconv,fdadd,fdcmp,fdmul,fdmadd,fddiv,sqrt_double,mnop,mlogic,maveh,msath,maddh,mqaddh,mpackh,munpackh,mdpackh,mbhconv,mrot,mshift,mexpdhw,mexpdhd,mwcut,mmulh,mmulxh,mmach,mmrdh,mqmulh,mqmulxh,mqmach,mcpx,mqcpx,mcut,mclracc,mclracca,mdunpackh,mbhconve,mrdacc,mwtacc,maddacc,mdaddacc,mabsh,mdrot,mcpl,mdcut,mqsath,mqlimh,mqshift,mset,ccr,multi,load_or_call,unknown"
  (const_string "unknown"))

(define_attr "acc_group" "none,even,odd"
  (symbol_ref "(enum attr_acc_group) frv_acc_group (insn)"))

;; Scheduling and Packing Overview
;; -------------------------------
;;
;; FR-V instructions are divided into five groups: integer, floating-point,
;; media, branch and control.  Each group is associated with a separate set
;; of processing units, the number and behavior of which depend on the target
;; target processor.  Integer units have names like I0 and I1, floating-point
;; units have names like F0 and F1, and so on.
;;
;; Each member of the FR-V family has its own restrictions on which
;; instructions can issue to which units.  For example, some processors
;; allow loads to issue to I0 or I1 while others only allow them to issue
;; to I0.  As well as these processor-specific restrictions, there is a
;; general rule that an instruction can only issue to unit X + 1 if an
;; instruction in the same packet issued to unit X.
;;
;; Sometimes the only way to honor these restrictions is by adding nops
;; to a packet.  For example, on the fr550, media instructions that access
;; ACC4-7 can only issue to M1 or M3.  It is therefore only possible to
;; execute these instructions by packing them with something that issues
;; to M0.  When no useful M0 instruction exists, an "mnop" can be used
;; instead.
;;
;; Having decided which instructions should issue to which units, the packet
;; should be ordered according to the following template:
;;
;;     I0 F0/M0 I1 F1/M1 .... B0 B1 ...
;;
;; Note that VLIW packets execute strictly in parallel.  Every instruction
;; in the packet will stall until all input operands are ready.  These
;; operands are then read simultaneously before any registers are modified.
;; This means that it's OK to have write-after-read hazards between
;; instructions in the same packet, even if the write is listed earlier
;; than the read.
;;
;; Three gcc passes are involved in generating VLIW packets:
;;
;;    (1) The scheduler.  This pass uses the standard scheduling code and
;;	  behaves in much the same way as it would for a superscalar RISC
;;	  architecture.
;;
;;    (2) frv_reorg.  This pass inserts nops into packets in order to meet
;;	  the processor's issue requirements.  It also has code to optimize
;;	  the type of padding used to align labels.
;;
;;    (3) frv_pack_insns.  The final packing phase, which puts the
;;	  instructions into assembly language order according to the
;;	  "I0 F0/M0 ..." template above.
;;
;; In the ideal case, these three passes will agree on which instructions
;; should be packed together, but this won't always happen.  In particular:
;;
;;    (a) (2) might not pack predicated instructions in the same way as (1).
;;	  The scheduler tries to schedule predicated instructions for the
;;	  worst case, assuming the predicate is true.  However, if we have
;;	  something like a predicated load, it isn't always possible to
;;	  fill the load delay with useful instructions.  (2) should then
;;	  pack the user of the loaded value as aggressively as possible,
;;	  in order to optimize the case when the predicate is false.
;;	  See frv_pack_insn_p for more details.
;;
;;    (b) The final shorten_branches pass runs between (2) and (3).
;;	  Since (2) inserts nops, it is possible that some branches
;;	  that were thought to be in range during (2) turned out to
;;	  out-of-range in (3).
;;
;; All three passes use DFAs to model issue restrictions.  The main
;; question that the DFAs are supposed to answer is simply: can these
;; instructions be packed together?  The DFAs are not responsible for
;; assigning instructions to execution units; that's the job of
;; frv_sort_insn_group, see below for details.
;;
;; To get the best results, the DFAs should try to allow packets to
;; be built in every possible order.  This gives the scheduler more
;; flexibility, removing the need for things like multipass lookahead.
;; It also means we can take more advantage of inter-packet dependencies.
;;
;; For example, suppose we're compiling for the fr400 and we have:
;;
;;	addi	gr4,#1,gr5
;;	ldi	@(gr6,gr0),gr4
;;
;; We can pack these instructions together by assigning the load to I0 and
;; the addition to I1.  However, because of the anti dependence between the
;; two instructions, the scheduler must schedule the addition first.
;; We should generally get better schedules if the DFA allows both
;; (ldi, addi) and (addi, ldi), leaving the final packing pass to
;; reorder the packet where appropriate.
;;
;; Almost all integer instructions can issue to any unit in the range I0
;; to Ix, where the value of "x" depends on the type of instruction and
;; on the target processor.  The rules for other instruction groups are
;; usually similar.
;;
;; When the restrictions are as regular as this, we can get the desired
;; behavior by claiming the DFA unit associated with the highest unused
;; execution unit.  For example, if an instruction can issue to I0 or I1,
;; the DFA first tries to take the DFA unit associated with I1, and will
;; only take I0's unit if I1 isn't free.  (Note that, as mentioned above,
;; the DFA does not assign instructions to units.  An instruction that
;; claims DFA unit I1 will not necessarily issue to I1 in the final packet.)
;;
;; There are some cases, such as the fr550 media restriction mentioned
;; above, where the rule is not as simple as "any unit between 0 and X".
;; Even so, allocating higher units first brings us close to the ideal.
;;
;; Having divided instructions into packets, passes (2) and (3) must
;; assign instructions to specific execution units.  They do this using
;; the following algorithm:
;;
;;    1. Partition the instructions into groups (integer, float/media, etc.)
;;
;;    2. For each group of instructions:
;;
;;	 (a) Issue each instruction in the reset DFA state and use the
;;	     DFA cpu_unit_query interface to find out which unit it picks
;;	     first.
;;
;;	 (b) Sort the instructions into ascending order of picked units.
;;	     Instructions that pick I1 first come after those that pick
;;	     I0 first, and so on.  Let S be the sorted sequence and S[i]
;;	     be the ith element of it (counting from zero).
;;
;;	 (c) If this is the control or branch group, goto (i)
;;
;;	 (d) Find the largest L such that S[0]...S[L-1] can be issued
;;	     consecutively from the reset state and such that the DFA
;;	     claims unit X when S[X] is added.  Let D be the DFA state
;;	     after instructions S[0]...S[L-1] have been issued.
;;
;;	 (e) If L is the length of S, goto (i)
;;
;;	 (f) Let U be the number of units belonging to this group and #S be
;;	     the length of S.  Create a new sequence S' by concatenating
;;	     S[L]...S[#S-1] and (U - #S) nops.
;;
;;	 (g) For each permutation S'' of S', try issuing S'' from last to
;;	     first, starting with state D.  See if the DFA claims unit
;;	     X + L when each S''[X] is added.  If so, set S to the
;;	     concatenation of S[0]...S[L-1] and S'', then goto (i).
;;
;;	 (h) If (g) found no permutation, abort.
;;
;;	 (i) S is now the sorted sequence for this group, meaning that S[X]
;;	     issues to unit X.  Trim any unwanted nops from the end of S.
;;
;; The sequence calculated by (b) is trivially correct for control
;; instructions since they can't be packed.  It is also correct for branch
;; instructions due to their simple issue requirements.  For integer and
;; floating-point/media instructions, the sequence calculated by (b) is
;; often the correct answer; the rest of the algorithm is optimized for
;; the case in which it is correct.
;;
;; If there were no irregularities in the issue restrictions then step
;; (d) would not be needed.  It is mainly there to cope with the fr550
;; integer restrictions, where a store can issue to I1, but only if a store
;; also issues to I0.  (Note that if a packet has two stores, they will be
;; at the beginning of the sequence calculated by (b).)  It also copes
;; with fr400 M-2 instructions, which must issue to M0, and which cannot
;; be issued together with an mnop in M1.
;;
;; Step (g) is the main one for integer and float/media instructions.
;; The first permutation it tries is S' itself (because, as noted above,
;; the sequence calculated by (b) is often correct).  If S' doesn't work,
;; the implementation tries varying the beginning of the sequence first.
;; Thus the nops towards the end of the sequence will only move to lower
;; positions if absolutely necessary.
;;
;; The algorithm is theoretically exponential in the number of instructions
;; in a group, although it's only O(n log(n)) if the sequence calculated by
;; (b) is acceptable.  In practice, the algorithm completes quickly even
;; in the rare cases where (g) needs to try other permutations.
(define_automaton "integer, float_media, branch, control, idiv, div")

;; The main issue units.  Note that not all units are available on
;; all processors.
(define_query_cpu_unit "i0,i1,i2,i3" "integer")
(define_query_cpu_unit "f0,f1,f2,f3" "float_media")
(define_query_cpu_unit "b0,b1" "branch")
(define_query_cpu_unit "c" "control")

;; Division units.
(define_cpu_unit "idiv1,idiv2" "idiv")
(define_cpu_unit "div1,div2,root" "div")

;; Control instructions cannot be packed with others.
(define_reservation "control" "i0+i1+i2+i3+f0+f1+f2+f3+b0+b1")

;; Generic reservation for control insns
(define_insn_reservation "control" 1
  (eq_attr "type" "trap,spr,unknown,multi")
  "c + control")

;; Reservation for relaxable calls to gettlsoff.
(define_insn_reservation "load_or_call" 3
  (eq_attr "type" "load_or_call")
  "c + control")

;; ::::::::::::::::::::
;; ::
;; :: Generic/FR500 scheduler description
;; ::
;; ::::::::::::::::::::

;; Integer insns
;; Synthetic units used to describe issue restrictions.
(define_automaton "fr500_integer")
(define_cpu_unit "fr500_load0,fr500_load1,fr500_store0" "fr500_integer")
(exclusion_set "fr500_load0,fr500_load1" "fr500_store0")

(define_bypass 0 "fr500_i1_sethi" "fr500_i1_setlo")
(define_insn_reservation "fr500_i1_sethi" 1
  (and (eq_attr "cpu" "generic,fr500,tomcat")
       (eq_attr "type" "sethi"))
  "i1|i0")

(define_insn_reservation "fr500_i1_setlo" 1
  (and (eq_attr "cpu" "generic,fr500,tomcat")
       (eq_attr "type" "setlo"))
  "i1|i0")

(define_insn_reservation "fr500_i1_int" 1
  (and (eq_attr "cpu" "generic,fr500,tomcat")
       (eq_attr "type" "int"))
  "i1|i0")

(define_insn_reservation "fr500_i1_mul" 3
  (and (eq_attr "cpu" "generic,fr500,tomcat")
       (eq_attr "type" "mul"))
  "i1|i0")

(define_insn_reservation "fr500_i1_div" 19
  (and (eq_attr "cpu" "generic,fr500,tomcat")
       (eq_attr "type" "div"))
  "(i1|i0),(idiv1*18|idiv2*18)")

(define_insn_reservation "fr500_i2" 4
  (and (eq_attr "cpu" "generic,fr500,tomcat")
       (eq_attr "type" "gload,fload"))
  "(i1|i0) + (fr500_load0|fr500_load1)")

(define_insn_reservation "fr500_i3" 0
  (and (eq_attr "cpu" "generic,fr500,tomcat")
       (eq_attr "type" "gstore,fstore"))
  "i0 + fr500_store0")

(define_insn_reservation "fr500_i4" 3
  (and (eq_attr "cpu" "generic,fr500,tomcat")
       (eq_attr "type" "movgf,movfg"))
  "i0")

(define_insn_reservation "fr500_i5" 0
  (and (eq_attr "cpu" "generic,fr500,tomcat")
       (eq_attr "type" "jumpl"))
  "i0")

;;
;; Branch-instructions
;;
(define_insn_reservation "fr500_branch" 0
  (and (eq_attr "cpu" "generic,fr500,tomcat")
       (eq_attr "type" "jump,branch,ccr"))
  "b1|b0")

(define_insn_reservation "fr500_call" 0
  (and (eq_attr "cpu" "generic,fr500,tomcat")
       (eq_attr "type" "call"))
  "b0")

;; Floating point insns.  The default latencies are for non-media
;; instructions; media instructions incur an extra cycle.

(define_bypass 4 "fr500_farith" "fr500_m1,fr500_m2,fr500_m3,
			         fr500_m4,fr500_m5,fr500_m6")
(define_insn_reservation "fr500_farith" 3
  (and (eq_attr "cpu" "generic,fr500,tomcat")
       (eq_attr "type" "fnop,fsconv,fsadd,fsmul,fsmadd,fdconv,fdadd,fdmul,fdmadd"))
  "(f1|f0)")

(define_insn_reservation "fr500_fcmp" 4
  (and (eq_attr "cpu" "generic,fr500,tomcat")
       (eq_attr "type" "fscmp,fdcmp"))
  "(f1|f0)")

(define_bypass 11 "fr500_fdiv" "fr500_m1,fr500_m2,fr500_m3,
			        fr500_m4,fr500_m5,fr500_m6")
(define_insn_reservation "fr500_fdiv" 10
  (and (eq_attr "cpu" "generic,fr500,tomcat")
       (eq_attr "type" "fsdiv,fddiv"))
  "(f1|f0),(div1*9 | div2*9)")

(define_bypass 16 "fr500_froot" "fr500_m1,fr500_m2,fr500_m3,
				 fr500_m4,fr500_m5,fr500_m6")
(define_insn_reservation "fr500_froot" 15
  (and (eq_attr "cpu" "generic,fr500,tomcat")
       (eq_attr "type" "sqrt_single,sqrt_double"))
  "(f1|f0) + root*15")

;; Media insns.  Conflict table is as follows:
;;
;;           M1  M2  M3  M4  M5  M6
;;        M1  -   -   -   -   -   -
;;        M2  -   -   -   -   X   X
;;        M3  -   -   -   -   X   X
;;        M4  -   -   -   -   -   X
;;        M5  -   X   X   -   X   X
;;        M6  -   X   X   X   X   X
;;
;; where X indicates an invalid combination.
;;
;; Target registers are as follows:
;;
;;	  M1 : FPRs
;;	  M2 : FPRs
;;	  M3 : ACCs
;;	  M4 : ACCs
;;	  M5 : FPRs
;;	  M6 : ACCs
;;
;; The default FPR latencies are for integer instructions.
;; Floating-point instructions need one cycle more and media
;; instructions need one cycle less.
(define_automaton "fr500_media")
(define_cpu_unit "fr500_m2_0,fr500_m2_1" "fr500_media")
(define_cpu_unit "fr500_m3_0,fr500_m3_1" "fr500_media")
(define_cpu_unit "fr500_m4_0,fr500_m4_1" "fr500_media")
(define_cpu_unit "fr500_m5" "fr500_media")
(define_cpu_unit "fr500_m6" "fr500_media")

(exclusion_set "fr500_m5,fr500_m6" "fr500_m2_0,fr500_m2_1,
				    fr500_m3_0,fr500_m3_1")
(exclusion_set "fr500_m6" "fr500_m4_0,fr500_m4_1,fr500_m5")

(define_bypass 2 "fr500_m1" "fr500_m1,fr500_m2,fr500_m3,
			     fr500_m4,fr500_m5,fr500_m6")
(define_bypass 4 "fr500_m1" "fr500_farith,fr500_fcmp,fr500_fdiv,fr500_froot")
(define_insn_reservation "fr500_m1" 3
  (and (eq_attr "cpu" "generic,fr500,tomcat")
       (eq_attr "type" "mnop,mlogic,maveh,msath,maddh,mqaddh"))
  "(f1|f0)")

(define_bypass 2 "fr500_m2" "fr500_m1,fr500_m2,fr500_m3,
			     fr500_m4,fr500_m5,fr500_m6")
(define_bypass 4 "fr500_m2" "fr500_farith,fr500_fcmp,fr500_fdiv,fr500_froot")
(define_insn_reservation "fr500_m2" 3
  (and (eq_attr "cpu" "generic,fr500,tomcat")
       (eq_attr "type" "mrdacc,mpackh,munpackh,mbhconv,mrot,mshift,mexpdhw,mexpdhd,mwcut,mcut,mdunpackh,mbhconve"))
  "(f1|f0) + (fr500_m2_0|fr500_m2_1)")

(define_bypass 1 "fr500_m3" "fr500_m4")
(define_insn_reservation "fr500_m3" 2
  (and (eq_attr "cpu" "generic,fr500,tomcat")
       (eq_attr "type" "mclracc,mwtacc"))
  "(f1|f0) + (fr500_m3_0|fr500_m3_1)")

(define_bypass 1 "fr500_m4" "fr500_m4")
(define_insn_reservation "fr500_m4" 2
  (and (eq_attr "cpu" "generic,fr500,tomcat")
       (eq_attr "type" "mmulh,mmulxh,mmach,mmrdh,mqmulh,mqmulxh,mqmach,mcpx,mqcpx"))
  "(f1|f0) + (fr500_m4_0|fr500_m4_1)")

(define_bypass 2 "fr500_m5" "fr500_m1,fr500_m2,fr500_m3,
			     fr500_m4,fr500_m5,fr500_m6")
(define_bypass 4 "fr500_m5" "fr500_farith,fr500_fcmp,fr500_fdiv,fr500_froot")
(define_insn_reservation "fr500_m5" 3
  (and (eq_attr "cpu" "generic,fr500,tomcat")
       (eq_attr "type" "mdpackh"))
  "(f1|f0) + fr500_m5")

(define_bypass 1 "fr500_m6" "fr500_m4")
(define_insn_reservation "fr500_m6" 2
  (and (eq_attr "cpu" "generic,fr500,tomcat")
       (eq_attr "type" "mclracca"))
  "(f1|f0) + fr500_m6")

;; ::::::::::::::::::::
;; ::
;; :: FR400 scheduler description
;; ::
;; ::::::::::::::::::::

;; Category 2 media instructions use both media units, but can be packed
;; with non-media instructions.  Use fr400_m1unit to claim the M1 unit
;; without claiming a slot.

;; Name		Class	Units	Latency
;; ====	        =====	=====	=======
;; int		I1	I0/I1	1
;; sethi	I1	I0/I1	0       -- does not interfere with setlo
;; setlo	I1	I0/I1	1
;; mul		I1	I0	3  (*)
;; div		I1	I0	20 (*)
;; gload	I2	I0	4  (*)
;; fload	I2	I0	4       -- only 3 if read by a media insn
;; gstore	I3	I0	0       -- provides no result
;; fstore	I3	I0	0       -- provides no result
;; movfg	I4	I0	3  (*)
;; movgf	I4	I0	3  (*)
;; jumpl	I5	I0	0       -- provides no result
;;
;; (*) The results of these instructions can be read one cycle earlier
;; than indicated.  The penalty given is for instructions with write-after-
;; write dependencies.

;; The FR400 can only do loads and stores in I0, so we there's no danger
;; of memory unit collision in the same packet.  There's only one divide
;; unit too.

(define_automaton "fr400_integer")
(define_cpu_unit "fr400_mul" "fr400_integer")

(define_insn_reservation "fr400_i1_int" 1
  (and (eq_attr "cpu" "fr400,fr405,fr450")
       (eq_attr "type" "int"))
  "i1|i0")

(define_bypass 0 "fr400_i1_sethi" "fr400_i1_setlo")
(define_insn_reservation "fr400_i1_sethi" 1
  (and (eq_attr "cpu" "fr400,fr405,fr450")
       (eq_attr "type" "sethi"))
  "i1|i0")

(define_insn_reservation "fr400_i1_setlo" 1
  (and (eq_attr "cpu" "fr400,fr405,fr450")
       (eq_attr "type" "setlo"))
  "i1|i0")

;; 3 is the worst case (write-after-write hazard).
(define_insn_reservation "fr400_i1_mul" 3
  (and (eq_attr "cpu" "fr400,fr405")
       (eq_attr "type" "mul"))
  "i0 + fr400_mul")

(define_insn_reservation "fr450_i1_mul" 2
  (and (eq_attr "cpu" "fr450")
       (eq_attr "type" "mul"))
  "i0 + fr400_mul")

(define_bypass 1 "fr400_i1_macc" "fr400_i1_macc")
(define_insn_reservation "fr400_i1_macc" 2
  (and (eq_attr "cpu" "fr405,fr450")
       (eq_attr "type" "macc"))
  "(i0|i1) + fr400_mul")

(define_insn_reservation "fr400_i1_scan" 1
  (and (eq_attr "cpu" "fr400,fr405,fr450")
       (eq_attr "type" "scan"))
  "i0")

(define_insn_reservation "fr400_i1_cut" 2
  (and (eq_attr "cpu" "fr405,fr450")
       (eq_attr "type" "cut"))
  "i0 + fr400_mul")

;; 20 is for a write-after-write hazard.
(define_insn_reservation "fr400_i1_div" 20
  (and (eq_attr "cpu" "fr400,fr405")
       (eq_attr "type" "div"))
  "i0 + idiv1*19")

(define_insn_reservation "fr450_i1_div" 19
  (and (eq_attr "cpu" "fr450")
       (eq_attr "type" "div"))
  "i0 + idiv1*19")

;; 4 is for a write-after-write hazard.
(define_insn_reservation "fr400_i2" 4
  (and (eq_attr "cpu" "fr400,fr405")
       (eq_attr "type" "gload,fload"))
  "i0")

(define_insn_reservation "fr450_i2_gload" 3
  (and (eq_attr "cpu" "fr450")
       (eq_attr "type" "gload"))
  "i0")

;; 4 is for a write-after-write hazard.
(define_insn_reservation "fr450_i2_fload" 4
  (and (eq_attr "cpu" "fr450")
       (eq_attr "type" "fload"))
  "i0")

(define_insn_reservation "fr400_i3" 0
  (and (eq_attr "cpu" "fr400,fr405,fr450")
       (eq_attr "type" "gstore,fstore"))
  "i0")

;; 3 is for a write-after-write hazard.
(define_insn_reservation "fr400_i4" 3
  (and (eq_attr "cpu" "fr400,fr405")
       (eq_attr "type" "movfg,movgf"))
  "i0")

(define_insn_reservation "fr450_i4_movfg" 2
  (and (eq_attr "cpu" "fr450")
       (eq_attr "type" "movfg"))
  "i0")

;; 3 is for a write-after-write hazard.
(define_insn_reservation "fr450_i4_movgf" 3
  (and (eq_attr "cpu" "fr450")
       (eq_attr "type" "movgf"))
  "i0")

(define_insn_reservation "fr400_i5" 0
  (and (eq_attr "cpu" "fr400,fr405,fr450")
       (eq_attr "type" "jumpl"))
  "i0")

;; The bypass between FPR loads and media instructions, described above.

(define_bypass 3
  "fr400_i2"
  "fr400_m1_1,fr400_m1_2,\
   fr400_m2_1,fr400_m2_2,\
   fr400_m3_1,fr400_m3_2,\
   fr400_m4_1,fr400_m4_2,\
   fr400_m5")

;; The branch instructions all use the B unit and produce no result.

(define_insn_reservation "fr400_b" 0
  (and (eq_attr "cpu" "fr400,fr405,fr450")
       (eq_attr "type" "jump,branch,ccr,call"))
  "b0")

;; FP->FP moves are marked as "fsconv" instructions in the define_insns
;; below, but are implemented on the FR400 using "mlogic" instructions.
;; It's easier to class "fsconv" as a "m1:1" instruction than provide
;; separate define_insns for the FR400.

;; M1 instructions store their results in FPRs.  Any instruction can read
;; the result in the following cycle, so no penalty occurs.

(define_automaton "fr400_media")
(define_cpu_unit "fr400_m1a,fr400_m1b,fr400_m2a" "fr400_media")
(exclusion_set "fr400_m1a,fr400_m1b" "fr400_m2a")

(define_reservation "fr400_m1" "(f1|f0) + (fr400_m1a|fr400_m1b)")
(define_reservation "fr400_m2" "f0 + fr400_m2a")

(define_insn_reservation "fr400_m1_1" 1
  (and (eq_attr "cpu" "fr400,fr405")
       (eq_attr "type" "fsconv,mnop,mlogic,maveh,msath,maddh,mabsh,mset"))
  "fr400_m1")

(define_insn_reservation "fr400_m1_2" 1
  (and (eq_attr "cpu" "fr400,fr405")
       (eq_attr "type" "mqaddh,mqsath,mqlimh,mqshift"))
  "fr400_m2")

;; M2 instructions store their results in accumulators, which are read
;; by M2 or M4 media commands.  M2 instructions can read the results in
;; the following cycle, but M4 instructions must wait a cycle more.

(define_bypass 1
  "fr400_m2_1,fr400_m2_2"
  "fr400_m2_1,fr400_m2_2")

(define_insn_reservation "fr400_m2_1" 2
  (and (eq_attr "cpu" "fr400,fr405")
       (eq_attr "type" "mmulh,mmulxh,mmach,mmrdh,mcpx,maddacc"))
  "fr400_m1")

(define_insn_reservation "fr400_m2_2" 2
  (and (eq_attr "cpu" "fr400,fr405")
       (eq_attr "type" "mqmulh,mqmulxh,mqmach,mqcpx,mdaddacc"))
  "fr400_m2")

;; For our purposes, there seems to be little real difference between
;; M1 and M3 instructions.  Keep them separate anyway in case the distinction
;; is needed later.

(define_insn_reservation "fr400_m3_1" 1
  (and (eq_attr "cpu" "fr400,fr405")
       (eq_attr "type" "mpackh,mrot,mshift,mexpdhw"))
  "fr400_m1")

(define_insn_reservation "fr400_m3_2" 1
  (and (eq_attr "cpu" "fr400,fr405")
       (eq_attr "type" "munpackh,mdpackh,mbhconv,mexpdhd,mwcut,mdrot,mcpl"))
  "fr400_m2")

;; M4 instructions write to accumulators or FPRs.  MOVFG and STF
;; instructions can read an FPR result in the following cycle, but
;; M-unit instructions must wait a cycle more for either kind of result.

(define_bypass 1 "fr400_m4_1,fr400_m4_2" "fr400_i3,fr400_i4")

(define_insn_reservation "fr400_m4_1" 2
  (and (eq_attr "cpu" "fr400,fr405")
       (eq_attr "type" "mrdacc,mcut,mclracc"))
  "fr400_m1")

(define_insn_reservation "fr400_m4_2" 2
  (and (eq_attr "cpu" "fr400,fr405")
       (eq_attr "type" "mclracca,mdcut"))
  "fr400_m2")

;; M5 instructions always incur a 1-cycle penalty.

(define_insn_reservation "fr400_m5" 2
  (and (eq_attr "cpu" "fr400,fr405")
       (eq_attr "type" "mwtacc"))
  "fr400_m2")

;; ::::::::::::::::::::
;; ::
;; :: FR450 media scheduler description
;; ::
;; ::::::::::::::::::::

;; The FR451 media restrictions are similar to the FR400's, but not as
;; strict and not as regular.  There are 6 categories with the following
;; restrictions:
;;
;;		          M1
;;	      M-1  M-2  M-3  M-4  M-5  M-6
;;	M-1:         x         x         x
;;	M-2:    x    x    x    x    x    x
;;  M0	M-3:         x         x         x
;;	M-4:    x    x    x    x
;;	M-5:         x         x         x
;;	M-6:    x    x    x    x    x    x
;;
;; where "x" indicates a conflict.
;;
;; There is no difference between M-1 and M-3 as far as issue
;; restrictions are concerned, so they are combined as "m13".

;; Units for odd-numbered categories.  There can be two of these
;; in a packet.
(define_cpu_unit "fr450_m13a,fr450_m13b" "float_media")
(define_cpu_unit "fr450_m5a,fr450_m5b" "float_media")

;; Units for even-numbered categories.  There can only be one per packet.
(define_cpu_unit "fr450_m2a,fr450_m4a,fr450_m6a" "float_media")

;; Enforce the restriction matrix above.
(exclusion_set "fr450_m2a,fr450_m4a,fr450_m6a" "fr450_m13a,fr450_m13b")
(exclusion_set "fr450_m2a,fr450_m6a" "fr450_m5a,fr450_m5b")
(exclusion_set "fr450_m4a,fr450_m6a" "fr450_m2a")

(define_reservation "fr450_m13" "(f1|f0) + (fr450_m13a|fr450_m13b)")
(define_reservation "fr450_m2" "f0 + fr450_m2a")
(define_reservation "fr450_m4" "f0 + fr450_m4a")
(define_reservation "fr450_m5" "(f1|f0) + (fr450_m5a|fr450_m5b)")
(define_reservation "fr450_m6" "(f0|f1) + fr450_m6a")

;; MD-1, MD-3 and MD-8 instructions, which are the same as far
;; as scheduling is concerned.  The inputs and outputs are FPRs.
;; Instructions that have 32-bit inputs and outputs belong to M-1 while
;; the rest belong to M-2.
;;
;; ??? Arithmetic shifts (MD-6) have an extra cycle latency, but we don't
;; make the distinction between them and logical shifts.
(define_insn_reservation "fr450_md138_1" 1
  (and (eq_attr "cpu" "fr450")
       (eq_attr "type" "fsconv,mnop,mlogic,maveh,msath,maddh,mabsh,mset,
			mrot,mshift,mexpdhw,mpackh"))
  "fr450_m13")

(define_insn_reservation "fr450_md138_2" 1
  (and (eq_attr "cpu" "fr450")
       (eq_attr "type" "mqaddh,mqsath,mqlimh,
			mdrot,mwcut,mqshift,mexpdhd,
			munpackh,mdpackh,mbhconv,mcpl"))
  "fr450_m2")

;; MD-2 instructions.  These take FPR or ACC inputs and produce an ACC output.
;; Instructions that write to double ACCs belong to M-3 while those that write
;; to quad ACCs belong to M-4.
(define_insn_reservation "fr450_md2_3" 2
  (and (eq_attr "cpu" "fr450")
       (eq_attr "type" "mmulh,mmach,mcpx,mmulxh,mmrdh,maddacc"))
  "fr450_m13")

(define_insn_reservation "fr450_md2_4" 2
  (and (eq_attr "cpu" "fr450")
       (eq_attr "type" "mqmulh,mqmach,mqcpx,mqmulxh,mdaddacc"))
  "fr450_m4")

;; Another MD-2 instruction can use the result on the following cycle.
(define_bypass 1 "fr450_md2_3,fr450_md2_4" "fr450_md2_3,fr450_md2_4")

;; MD-4 instructions that write to ACCs.
(define_insn_reservation "fr450_md4_3" 2
  (and (eq_attr "cpu" "fr450")
       (eq_attr "type" "mclracc"))
  "fr450_m13")

(define_insn_reservation "fr450_md4_4" 3
  (and (eq_attr "cpu" "fr450")
       (eq_attr "type" "mclracca"))
  "fr450_m4")

;; MD-4 instructions that write to FPRs.
(define_insn_reservation "fr450_md4_1" 2
  (and (eq_attr "cpu" "fr450")
       (eq_attr "type" "mcut"))
  "fr450_m13")

(define_insn_reservation "fr450_md4_5" 2
  (and (eq_attr "cpu" "fr450")
       (eq_attr "type" "mrdacc"))
  "fr450_m5")

(define_insn_reservation "fr450_md4_6" 2
  (and (eq_attr "cpu" "fr450")
       (eq_attr "type" "mdcut"))
  "fr450_m6")

;; Integer instructions can read the FPR result of an MD-4 instruction on
;; the following cycle.
(define_bypass 1 "fr450_md4_1,fr450_md4_5,fr450_md4_6"
		 "fr400_i3,fr450_i4_movfg")

;; MD-5 instructions, which belong to M-3.  They take FPR inputs and
;; write to ACCs.
(define_insn_reservation "fr450_md5_3" 2
  (and (eq_attr "cpu" "fr450")
       (eq_attr "type" "mwtacc"))
  "fr450_m13")

;; ::::::::::::::::::::
;; ::
;; :: FR550 scheduler description
;; ::
;; ::::::::::::::::::::

;; Prevent loads and stores from being issued in the same packet.
;; These units must go into the generic "integer" reservation because
;; of the constraints on fr550_store0 and fr550_store1.
(define_cpu_unit "fr550_load0,fr550_load1" "integer")
(define_cpu_unit "fr550_store0,fr550_store1" "integer")
(exclusion_set "fr550_load0,fr550_load1" "fr550_store0,fr550_store1")

;; A store can only issue to I1 if one has also been issued to I0.
(presence_set "fr550_store1" "fr550_store0")

(define_bypass 0 "fr550_sethi" "fr550_setlo")
(define_insn_reservation "fr550_sethi" 1
  (and (eq_attr "cpu" "fr550")
       (eq_attr "type" "sethi"))
  "i3|i2|i1|i0")

(define_insn_reservation "fr550_setlo" 1
  (and (eq_attr "cpu" "fr550")
       (eq_attr "type" "setlo"))
  "i3|i2|i1|i0")

(define_insn_reservation "fr550_int" 1
  (and (eq_attr "cpu" "fr550")
       (eq_attr "type" "int"))
  "i3|i2|i1|i0")

(define_insn_reservation "fr550_mul" 2
  (and (eq_attr "cpu" "fr550")
       (eq_attr "type" "mul"))
  "i1|i0")

(define_insn_reservation "fr550_div" 19
  (and (eq_attr "cpu" "fr550")
       (eq_attr "type" "div"))
  "(i1|i0),(idiv1*18 | idiv2*18)")

(define_insn_reservation "fr550_load" 3
  (and (eq_attr "cpu" "fr550")
       (eq_attr "type" "gload,fload"))
  "(i1|i0)+(fr550_load0|fr550_load1)")

;; We can only issue a store to I1 if one was also issued to I0.
;; This means that, as far as frv_reorder_packet is concerned,
;; the instruction has the same priority as an I0-only instruction.
(define_insn_reservation "fr550_store" 1
  (and (eq_attr "cpu" "fr550")
       (eq_attr "type" "gstore,fstore"))
  "(i0+fr550_store0)|(i1+fr550_store1)")

(define_insn_reservation "fr550_transfer" 2
  (and (eq_attr "cpu" "fr550")
       (eq_attr "type" "movgf,movfg"))
  "i0")

(define_insn_reservation "fr550_jumpl" 0
  (and (eq_attr "cpu" "fr550")
       (eq_attr "type" "jumpl"))
  "i0")

(define_cpu_unit "fr550_ccr0,fr550_ccr1" "float_media")

(define_insn_reservation "fr550_branch" 0
  (and (eq_attr "cpu" "fr550")
       (eq_attr "type" "jump,branch"))
  "b1|b0")

(define_insn_reservation "fr550_ccr" 0
  (and (eq_attr "cpu" "fr550")
       (eq_attr "type" "ccr"))
  "(b1|b0) + (fr550_ccr1|fr550_ccr0)")

(define_insn_reservation "fr550_call" 0
  (and (eq_attr "cpu" "fr550")
       (eq_attr "type" "call"))
  "b0")

(define_automaton "fr550_float_media")
(define_cpu_unit "fr550_add0,fr550_add1" "fr550_float_media")

;; There are three possible combinations of floating-point/media instructions:
;;
;;    - one media and one float
;;    - up to four float, no media
;;    - up to four media, no float
(define_cpu_unit "fr550_f0,fr550_f1,fr550_f2,fr550_f3" "fr550_float_media")
(define_cpu_unit "fr550_m0,fr550_m1,fr550_m2,fr550_m3" "fr550_float_media")
(exclusion_set "fr550_f1,fr550_f2,fr550_f3" "fr550_m1,fr550_m2,fr550_m3")
(exclusion_set "fr550_m0" "fr550_f1,fr550_f2,fr550_f3")
;; FIXME: This next exclusion set should be defined as well, so that we do
;; not get a packet containing multiple media instructions plus a single
;; floating point instruction.  At the moment we can get away with not
;; defining it because gcc does not seem to generate such packets.
;;
;; If we do enable the exclusion however the insertion of fnop insns into
;; a packet containing media instructions will stop working, because the
;; fnop insn counts as a floating point instruction.  The correct solution
;; is to fix the reservation for the fnop insn so that it does not have the
;; same restrictions as ordinary floating point insns.
;;(exclusion_set "fr550_f0" "fr550_m1,fr550_m2,fr550_m3")

(define_reservation "fr550_float" "fr550_f0|fr550_f1|fr550_f2|fr550_f3")
(define_reservation "fr550_media" "fr550_m0|fr550_m1|fr550_m2|fr550_m3")

(define_insn_reservation "fr550_f1" 0
  (and (eq_attr "cpu" "fr550")
       (eq_attr "type" "fnop"))
  "(f3|f2|f1|f0) + fr550_float")

(define_insn_reservation "fr550_f2" 3
  (and (eq_attr "cpu" "fr550")
       (eq_attr "type" "fsconv,fsadd,fscmp"))
  "(f3|f2|f1|f0) + (fr550_add0|fr550_add1) + fr550_float")

(define_insn_reservation "fr550_f3_mul" 3
  (and (eq_attr "cpu" "fr550")
       (eq_attr "type" "fsmul"))
  "(f1|f0) + fr550_float")

(define_insn_reservation "fr550_f3_div" 10
  (and (eq_attr "cpu" "fr550")
       (eq_attr "type" "fsdiv"))
  "(f1|f0) + fr550_float")

(define_insn_reservation "fr550_f3_sqrt" 15
  (and (eq_attr "cpu" "fr550")
       (eq_attr "type" "sqrt_single"))
  "(f1|f0) + fr550_float")

;; Synthetic units for enforcing media issue restrictions.  Certain types
;; of insn in M2 conflict with certain types in M0:
;;
;;			     M2
;;               MNOP   MALU   MSFT   MMAC   MSET
;;         MNOP     -      -      x      -      -
;;         MALU     -      x      x      -      -
;;   M0    MSFT     -      -      x      -      x
;;         MMAC     -      -      x      x      -
;;         MSET     -      -      x      -      -
;;
;; where "x" indicates a conflict.  The same restrictions apply to
;; M3 and M1.
;;
;; In addition -- and this is the awkward bit! -- instructions that
;; access ACC0-3 can only issue to M0 or M2.  Those that access ACC4-7
;; can only issue to M1 or M3.  We refer to such instructions as "even"
;; and "odd" respectively.
(define_cpu_unit "fr550_malu0,fr550_malu1" "float_media")
(define_cpu_unit "fr550_malu2,fr550_malu3" "float_media")
(define_cpu_unit "fr550_msft0,fr550_msft1" "float_media")
(define_cpu_unit "fr550_mmac0,fr550_mmac1" "float_media")
(define_cpu_unit "fr550_mmac2,fr550_mmac3" "float_media")
(define_cpu_unit "fr550_mset0,fr550_mset1" "float_media")
(define_cpu_unit "fr550_mset2,fr550_mset3" "float_media")

(exclusion_set "fr550_malu0" "fr550_malu2")
(exclusion_set "fr550_malu1" "fr550_malu3")

(exclusion_set "fr550_msft0" "fr550_mset2")
(exclusion_set "fr550_msft1" "fr550_mset3")

(exclusion_set "fr550_mmac0" "fr550_mmac2")
(exclusion_set "fr550_mmac1" "fr550_mmac3")

;; If an MSFT or MMAC instruction issues to a unit other than M0, we may
;; need to insert some nops.  In the worst case, the packet will end up
;; having 4 integer instructions and 4 media instructions, leaving no
;; room for any branch instructions that the DFA might have accepted.
;;
;; This doesn't matter for JUMP_INSNs and CALL_INSNs because they are
;; always the last instructions to be passed to the DFA, and could be
;; pushed out to a separate packet once the nops have been added.
;; However, it does cause problems for ccr instructions since they
;; can occur anywhere in the unordered packet.
(exclusion_set "fr550_msft1,fr550_mmac1,fr550_mmac2,fr550_mmac3"
	       "fr550_ccr0,fr550_ccr1")

(define_reservation "fr550_malu"
  "(f3 + fr550_malu3) | (f2 + fr550_malu2)
   | (f1 + fr550_malu1) | (f0 + fr550_malu0)")

(define_reservation "fr550_msft_even"
  "f0 + fr550_msft0")

(define_reservation "fr550_msft_odd"
  "f1 + fr550_msft1")

(define_reservation "fr550_msft_either"
  "(f1 + fr550_msft1) | (f0 + fr550_msft0)")

(define_reservation "fr550_mmac_even"
  "(f2 + fr550_mmac2) | (f0 + fr550_mmac0)")

(define_reservation "fr550_mmac_odd"
  "(f3 + fr550_mmac3) | (f1 + fr550_mmac1)")

(define_reservation "fr550_mset"
  "(f3 + fr550_mset3) | (f2 + fr550_mset2)
    | (f1 + fr550_mset1) | (f0 + fr550_mset0)")

(define_insn_reservation "fr550_mnop" 0
  (and (eq_attr "cpu" "fr550")
       (eq_attr "type" "mnop"))
  "fr550_media + (f3|f2|f1|f0)")

(define_insn_reservation "fr550_malu" 2
  (and (eq_attr "cpu" "fr550")
       (eq_attr "type" "mlogic,maveh,msath,mabsh,maddh,mqaddh,mqsath"))
  "fr550_media + fr550_malu")

;; These insns only operate on FPRs and so don't need to be classified
;; as even/odd.
(define_insn_reservation "fr550_msft_1_either" 2
  (and (eq_attr "cpu" "fr550")
       (eq_attr "type" "mrot,mwcut,mshift,mexpdhw,mexpdhd,mpackh,
			munpackh,mdpackh,mbhconv,mdrot,mcpl"))
  "fr550_media + fr550_msft_either")

;; These insns read from ACC0-3.
(define_insn_reservation "fr550_msft_1_even" 2
  (and (eq_attr "cpu" "fr550")
       (and (eq_attr "type" "mcut,mrdacc,mdcut")
	    (eq_attr "acc_group" "even")))
  "fr550_media + fr550_msft_even")

;; These insns read from ACC4-7.
(define_insn_reservation "fr550_msft_1_odd" 2
  (and (eq_attr "cpu" "fr550")
       (and (eq_attr "type" "mcut,mrdacc,mdcut")
	    (eq_attr "acc_group" "odd")))
  "fr550_media + fr550_msft_odd")

;; MCLRACC with A=1 can issue to either M0 or M1.
(define_insn_reservation "fr550_msft_2_either" 2
  (and (eq_attr "cpu" "fr550")
       (eq_attr "type" "mclracca"))
  "fr550_media + fr550_msft_either")

;; These insns write to ACC0-3.
(define_insn_reservation "fr550_msft_2_even" 2
  (and (eq_attr "cpu" "fr550")
       (and (eq_attr "type" "mclracc,mwtacc")
	    (eq_attr "acc_group" "even")))
  "fr550_media + fr550_msft_even")

;; These insns write to ACC4-7.
(define_insn_reservation "fr550_msft_2_odd" 2
  (and (eq_attr "cpu" "fr550")
       (and (eq_attr "type" "mclracc,mwtacc")
	    (eq_attr "acc_group" "odd")))
  "fr550_media + fr550_msft_odd")

;; These insns read from and write to ACC0-3.
(define_insn_reservation "fr550_mmac_even" 2
  (and (eq_attr "cpu" "fr550")
       (and (eq_attr "type" "mmulh,mmulxh,mmach,mmrdh,mqmulh,mqmulxh,mqmach,
			     maddacc,mdaddacc,mcpx,mqcpx")
	    (eq_attr "acc_group" "even")))
  "fr550_media + fr550_mmac_even")

;; These insns read from and write to ACC4-7.
(define_insn_reservation "fr550_mmac_odd" 2
  (and (eq_attr "cpu" "fr550")
       (and (eq_attr "type" "mmulh,mmulxh,mmach,mmrdh,mqmulh,mqmulxh,mqmach,
			     maddacc,mdaddacc,mcpx,mqcpx")
	    (eq_attr "acc_group" "odd")))
  "fr550_media + fr550_mmac_odd")

(define_insn_reservation "fr550_mset" 1
  (and (eq_attr "cpu" "fr550")
       (eq_attr "type" "mset"))
  "fr550_media + fr550_mset")

;; ::::::::::::::::::::
;; ::
;; :: Simple/FR300 scheduler description
;; ::
;; ::::::::::::::::::::

;; Fr300 or simple processor.  To describe it as 1 insn issue
;; processor, we use control unit.

(define_insn_reservation "fr300_lat1" 1
  (and (eq_attr "cpu" "fr300,simple")
       (eq_attr "type" "!gload,fload,movfg,movgf"))
  "c + control")

(define_insn_reservation "fr300_lat2" 2
  (and (eq_attr "cpu" "fr300,simple")
       (eq_attr "type" "gload,fload,movfg,movgf"))
  "c + control")


;; ::::::::::::::::::::
;; ::
;; :: Delay Slots
;; ::
;; ::::::::::::::::::::

;; The insn attribute mechanism can be used to specify the requirements for
;; delay slots, if any, on a target machine.  An instruction is said to require
;; a "delay slot" if some instructions that are physically after the
;; instruction are executed as if they were located before it.  Classic
;; examples are branch and call instructions, which often execute the following
;; instruction before the branch or call is performed.

;; On some machines, conditional branch instructions can optionally "annul"
;; instructions in the delay slot.  This means that the instruction will not be
;; executed for certain branch outcomes.  Both instructions that annul if the
;; branch is true and instructions that annul if the branch is false are
;; supported.

;; Delay slot scheduling differs from instruction scheduling in that
;; determining whether an instruction needs a delay slot is dependent only
;; on the type of instruction being generated, not on data flow between the
;; instructions.  See the next section for a discussion of data-dependent
;; instruction scheduling.

;; The requirement of an insn needing one or more delay slots is indicated via
;; the `define_delay' expression.  It has the following form:
;;
;; (define_delay TEST
;;   [DELAY-1 ANNUL-TRUE-1 ANNUL-FALSE-1
;;    DELAY-2 ANNUL-TRUE-2 ANNUL-FALSE-2
;;    ...])

;; TEST is an attribute test that indicates whether this `define_delay' applies
;; to a particular insn.  If so, the number of required delay slots is
;; determined by the length of the vector specified as the second argument.  An
;; insn placed in delay slot N must satisfy attribute test DELAY-N.
;; ANNUL-TRUE-N is an attribute test that specifies which insns may be annulled
;; if the branch is true.  Similarly, ANNUL-FALSE-N specifies which insns in
;; the delay slot may be annulled if the branch is false.  If annulling is not
;; supported for that delay slot, `(nil)' should be coded.

;; For example, in the common case where branch and call insns require a single
;; delay slot, which may contain any insn other than a branch or call, the
;; following would be placed in the `md' file:

;; (define_delay (eq_attr "type" "branch,call")
;;		 [(eq_attr "type" "!branch,call") (nil) (nil)])

;; Multiple `define_delay' expressions may be specified.  In this case, each
;; such expression specifies different delay slot requirements and there must
;; be no insn for which tests in two `define_delay' expressions are both true.

;; For example, if we have a machine that requires one delay slot for branches
;; but two for calls, no delay slot can contain a branch or call insn, and any
;; valid insn in the delay slot for the branch can be annulled if the branch is
;; true, we might represent this as follows:

;; (define_delay (eq_attr "type" "branch")
;;   [(eq_attr "type" "!branch,call")
;;    (eq_attr "type" "!branch,call")
;;    (nil)])
;;
;; (define_delay (eq_attr "type" "call")
;;   [(eq_attr "type" "!branch,call") (nil) (nil)
;;    (eq_attr "type" "!branch,call") (nil) (nil)])

;; Note - it is the backend's responsibility to fill any unfilled delay slots
;; at assembler generation time.  This is usually done by adding a special print
;; operand to the delayed instruction, and then in the PRINT_OPERAND function
;; calling dbr_sequence_length() to determine how many delay slots were filled.
;; For example:
;;
;; --------------<machine>.md-----------------
;; (define_insn "call"
;;  [(call (match_operand 0 "memory_operand" "m")
;;         (match_operand 1 "" ""))]
;;   ""
;;   "call_delayed %0,%1,%2%#"
;;  [(set_attr "length" "4")
;;   (set_attr "type" "call")])
;;
;; -------------<machine>.h-------------------
;; #define PRINT_OPERAND_PUNCT_VALID_P(CODE) (CODE == '#')
;;
;;  ------------<machine>.c------------------
;; void
;; machine_print_operand (file, x, code)
;;     FILE * file;
;;     rtx    x;
;;     int    code;
;; {
;;   switch (code)
;;   {
;;   case '#':
;;     if (dbr_sequence_length () == 0)
;;       fputs ("\n\tnop", file);
;;     return;

;; ::::::::::::::::::::
;; ::
;; :: Notes on Patterns
;; ::
;; ::::::::::::::::::::

;; If you need to construct a sequence of assembler instructions in order
;; to implement a pattern be sure to escape any backslashes and double quotes
;; that you use, e.g.:
;;
;; (define_insn "an example"
;;   [(some rtl)]
;;   ""
;;   "*
;;    { static char buffer [100];
;;      sprintf (buffer, \"insn \\t %d\", REGNO (operands[1]));
;;      return buffer;
;;    }"
;; )
;;
;; Also if there is more than one instruction, they can be separated by \\;
;; which is a space saving synonym for \\n\\t:
;;
;; (define_insn "another example"
;;   [(some rtl)]
;;   ""
;;   "*
;;    { static char buffer [100];
;;      sprintf (buffer, \"insn1 \\t %d\\;insn2 \\t %%1\",
;;        REGNO (operands[1]));
;;      return buffer;
;;    }"
;; )
;;

(include "predicates.md")
(include "constraints.md")

;; ::::::::::::::::::::
;; ::
;; :: Moves
;; ::
;; ::::::::::::::::::::

;; Wrap moves in define_expand to prevent memory->memory moves from being
;; generated at the RTL level, which generates better code for most machines
;; which can't do mem->mem moves.

;; If operand 0 is a `subreg' with mode M of a register whose own mode is wider
;; than M, the effect of this instruction is to store the specified value in
;; the part of the register that corresponds to mode M.  The effect on the rest
;; of the register is undefined.

;; This class of patterns is special in several ways.  First of all, each of
;; these names *must* be defined, because there is no other way to copy a datum
;; from one place to another.

;; Second, these patterns are not used solely in the RTL generation pass.  Even
;; the reload pass can generate move insns to copy values from stack slots into
;; temporary registers.  When it does so, one of the operands is a hard
;; register and the other is an operand that can need to be reloaded into a
;; register.

;; Therefore, when given such a pair of operands, the pattern must
;; generate RTL which needs no reloading and needs no temporary
;; registers--no registers other than the operands.  For example, if
;; you support the pattern with a `define_expand', then in such a
;; case the `define_expand' mustn't call `force_reg' or any other such
;; function which might generate new pseudo registers.

;; This requirement exists even for subword modes on a RISC machine
;; where fetching those modes from memory normally requires several
;; insns and some temporary registers.  Look in `spur.md' to see how
;; the requirement can be satisfied.

;; During reload a memory reference with an invalid address may be passed as an
;; operand.  Such an address will be replaced with a valid address later in the
;; reload pass.  In this case, nothing may be done with the address except to
;; use it as it stands.  If it is copied, it will not be replaced with a valid
;; address.  No attempt should be made to make such an address into a valid
;; address and no routine (such as `change_address') that will do so may be
;; called.  Note that `general_operand' will fail when applied to such an
;; address.
;;
;; The global variable `reload_in_progress' (which must be explicitly declared
;; if required) can be used to determine whether such special handling is
;; required.
;;
;; The variety of operands that have reloads depends on the rest of
;; the machine description, but typically on a RISC machine these can
;; only be pseudo registers that did not get hard registers, while on
;; other machines explicit memory references will get optional
;; reloads.
;;
;; If a scratch register is required to move an object to or from memory, it
;; can be allocated using `gen_reg_rtx' prior to reload.  But this is
;; impossible during and after reload.  If there are cases needing scratch
;; registers after reload, you must define `SECONDARY_INPUT_RELOAD_CLASS' and
;; perhaps also `SECONDARY_OUTPUT_RELOAD_CLASS' to detect them, and provide
;; patterns `reload_inM' or `reload_outM' to handle them.

;; The constraints on a `moveM' must permit moving any hard register to any
;; other hard register provided that `TARGET_HARD_REGNO_MODE_OK' permits
;; mode M in both registers and `REGISTER_MOVE_COST' applied to their
;; classes returns a value of 2.

;; It is obligatory to support floating point `moveM' instructions
;; into and out of any registers that can hold fixed point values,
;; because unions and structures (which have modes `SImode' or
;; `DImode') can be in those registers and they may have floating
;; point members.

;; There may also be a need to support fixed point `moveM' instructions
;; in and out of floating point registers.  Unfortunately, I have
;; forgotten why this was so, and I don't know whether it is still true.
;; If `TARGET_HARD_REGNO_MODE_OK' rejects fixed point values in floating
;; point registers, then the constraints of the fixed point `moveM'
;; instructions must be designed to avoid ever trying to reload into a
;; floating point register.

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

(define_insn "*movqi_load"
  [(set (match_operand:QI 0 "register_operand" "=d,f")
	(match_operand:QI 1 "frv_load_operand" "m,m"))]
  ""
  "* return output_move_single (operands, insn);"
  [(set_attr "length" "4")
   (set_attr "type" "gload,fload")])

(define_insn "*movqi_internal"
  [(set (match_operand:QI 0 "move_destination_operand" "=d,d,m,m,?f,?f,?d,?m,f,d,f")
	(match_operand:QI 1 "move_source_operand"       "L,d,d,O, d, f, f, f,GO,!m,!m"))]
  "register_operand(operands[0], QImode) || reg_or_0_operand (operands[1], QImode)"
  "* return output_move_single (operands, insn);"
  [(set_attr "length" "4")
   (set_attr "type" "int,int,gstore,gstore,movgf,fsconv,movfg,fstore,movgf,gload,fload")])

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

(define_insn "*movhi_load"
  [(set (match_operand:HI 0 "register_operand" "=d,f")
	(match_operand:HI 1 "frv_load_operand" "m,m"))]
  ""
  "* return output_move_single (operands, insn);"
  [(set_attr "length" "4")
   (set_attr "type" "gload,fload")])

(define_insn "*movhi_internal"
  [(set (match_operand:HI 0 "move_destination_operand" "=d,d,d,m,m,?f,?f,?d,?m,f,d,f")
	(match_operand:HI 1 "move_source_operand"       "L,n,d,d,O, d, f, f, f,GO,!m,!m"))]
  "register_operand(operands[0], HImode) || reg_or_0_operand (operands[1], HImode)"
  "* return output_move_single (operands, insn);"
  [(set_attr "length" "4,8,4,4,4,4,4,4,4,4,4,4")
   (set_attr "type" "int,multi,int,gstore,gstore,movgf,fsconv,movfg,fstore,movgf,gload,fload")])

;; Split 2 word load of constants into sethi/setlo instructions
(define_split
  [(set (match_operand:HI 0 "integer_register_operand" "")
	(match_operand:HI 1 "int_2word_operand" ""))]
  "reload_completed"
  [(set (match_dup 0)
	(high:HI (match_dup 1)))
   (set (match_dup 0)
	(lo_sum:HI (match_dup 0)
		(match_dup 1)))]
  "")

(define_insn "movhi_high"
  [(set (match_operand:HI 0 "integer_register_operand" "=d")
	(high:HI (match_operand:HI 1 "int_2word_operand" "i")))]
  ""
  "sethi #hi(%1), %0"
  [(set_attr "type" "sethi")
   (set_attr "length" "4")])

(define_insn "movhi_lo_sum"
  [(set (match_operand:HI 0 "integer_register_operand" "+d")
	(lo_sum:HI (match_dup 0)
		   (match_operand:HI 1 "int_2word_operand" "i")))]
  ""
  "setlo #lo(%1), %0"
  [(set_attr "type" "setlo")
   (set_attr "length" "4")])

(define_expand "movsi"
  [(set (match_operand:SI 0 "move_destination_operand" "")
	(match_operand:SI 1 "move_source_operand" ""))]
  ""
  "{ frv_emit_move (SImode, operands[0], operands[1]); DONE; }")

;; Note - it is best to only have one movsi pattern and to handle
;; all the various contingencies by the use of alternatives.  This
;; allows reload the greatest amount of flexibility (since reload will
;; only choose amongst alternatives for a selected insn, it will not
;; replace the insn with another one).

;; Unfortunately, we do have to separate out load-type moves from the rest,
;; and only allow memory source operands in the former.  If we do memory and
;; constant loads in a single pattern, reload will be tempted to force
;; constants into memory when the destination is a floating-point register.
;; That may make a function use a PIC pointer when it didn't before, and we
;; cannot change PIC usage (and hence stack layout) so late in the game.
;; The resulting sequences for loading constants into FPRs are preferable
;; even when we're not generating PIC code.

;; However, if we don't accept input from memory at all in the generic
;; movsi pattern, reloads for asm instructions that reference pseudos
;; that end up assigned to memory will fail to match, because we
;; recognize them right after they're emitted, and we don't
;; re-recognize them again after the substitution for memory.  So keep
;; a memory constraint available, just make sure reload won't be
;; tempted to use it.
;;
		   
		   
(define_insn "*movsi_load"
  [(set (match_operand:SI 0 "register_operand" "=d,f")
	(match_operand:SI 1 "frv_load_operand" "m,m"))]
  ""
  "* return output_move_single (operands, insn);"
  [(set_attr "length" "4")
   (set_attr "type" "gload,fload")])

(define_insn "*movsi_got"
  [(set (match_operand:SI 0 "integer_register_operand" "=d")
	(match_operand:SI 1 "got12_operand" ""))]
  ""
  "addi gr0, %1, %0"
  [(set_attr "type" "int")
   (set_attr "length" "4")])

(define_insn "*movsi_high_got"
  [(set (match_operand:SI 0 "integer_register_operand" "=d")
	(high:SI (match_operand:SI 1 "const_unspec_operand" "")))]
  ""
  "sethi %1, %0"
  [(set_attr "type" "sethi")
   (set_attr "length" "4")])

(define_insn "*movsi_lo_sum_got"
  [(set (match_operand:SI 0 "integer_register_operand" "=d")
	(lo_sum:SI (match_operand:SI 1 "integer_register_operand" "0")
		   (match_operand:SI 2 "const_unspec_operand" "")))]
  ""
  "setlo %2, %0"
  [(set_attr "type" "setlo")
   (set_attr "length" "4")])

(define_insn "*movsi_internal"
  [(set (match_operand:SI 0 "move_destination_operand" "=d,d,d,m,m,z,d,d,f,f,m,?f,?z,d,f")
	(match_operand:SI 1 "move_source_operand"      "L,n,d,d,O,d,z,f,d,f,f,GO,GO,!m,!m"))]
  "register_operand (operands[0], SImode) || reg_or_0_operand (operands[1], SImode)"
  "* return output_move_single (operands, insn);"
  [(set_attr "length" "4,8,4,4,4,4,4,4,4,4,4,4,4,4,4")
   (set_attr "type" "int,multi,int,gstore,gstore,spr,spr,movfg,movgf,fsconv,fstore,movgf,spr,gload,fload")])

;; Split 2 word load of constants into sethi/setlo instructions
(define_insn_and_split "*movsi_2word"
  [(set (match_operand:SI 0 "integer_register_operand" "=d")
	(match_operand:SI 1 "int_2word_operand" "i"))]
  ""
  "#"
  "reload_completed"
  [(set (match_dup 0)
	(high:SI (match_dup 1)))
   (set (match_dup 0)
	(lo_sum:SI (match_dup 0)
		(match_dup 1)))]
  ""
  [(set_attr "length" "8")
   (set_attr "type" "multi")])

(define_insn "movsi_high"
  [(set (match_operand:SI 0 "integer_register_operand" "=d")
	(high:SI (match_operand:SI 1 "int_2word_operand" "i")))]
  ""
  "sethi #hi(%1), %0"
  [(set_attr "type" "sethi")
   (set_attr "length" "4")])

(define_insn "movsi_lo_sum"
  [(set (match_operand:SI 0 "integer_register_operand" "+d")
	(lo_sum:SI (match_dup 0)
		   (match_operand:SI 1 "int_2word_operand" "i")))]
  ""
  "setlo #lo(%1), %0"
  [(set_attr "type" "setlo")
   (set_attr "length" "4")])

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

(define_insn "*movdi_double"
  [(set (match_operand:DI 0 "move_destination_operand" "=e,?h,??d,??f,R,?R,??m,??m,e,?h,??d,??f,?e,??d,?h,??f,R,m,e,??d,e,??d,?h,??f")
	(match_operand:DI 1 "move_source_operand"      " e,h,d,f,e,h,d,f,R,R,m,m,h,f,e,d,GO,GO,GO,GO,nF,nF,GO,GO"))]
  "TARGET_DOUBLE
   && (register_operand (operands[0], DImode)
       || reg_or_0_operand (operands[1], DImode))"
  "* return output_move_double (operands, insn);"
  [(set_attr "length" "8,4,8,8,4,4,8,8,4,4,8,8,4,8,4,8,4,8,8,8,16,16,8,8")
   (set_attr "type" "multi,fdconv,multi,multi,gstore,fstore,gstore,fstore,gload,fload,gload,fload,movfg,movfg,movgf,movgf,gstore,gstore,multi,multi,multi,multi,movgf,movgf")])

(define_insn "*movdi_nodouble"
  [(set (match_operand:DI 0 "move_destination_operand" "=e,?h,??d,??f,R,?R,??m,??m,e,?h,??d,??f,?e,??d,?h,??f,R,m,e,??d,e,??d,?h,??f")
	(match_operand:DI 1 "move_source_operand"      " e,h,d,f,e,h,d,f,R,R,m,m,h,f,e,d,GO,GO,GO,GO,nF,nF,GO,GO"))]
  "!TARGET_DOUBLE
   && (register_operand (operands[0], DImode)
       || reg_or_0_operand (operands[1], DImode))"
  "* return output_move_double (operands, insn);"
  [(set_attr "length" "8,8,8,8,4,4,8,8,4,4,8,8,8,8,8,8,4,8,8,8,16,16,8,8")
   (set_attr "type" "multi,multi,multi,multi,gstore,fstore,gstore,fstore,gload,fload,gload,fload,movfg,movfg,movgf,movgf,gstore,gstore,multi,multi,multi,multi,movgf,movgf")])

(define_split
  [(set (match_operand:DI 0 "register_operand" "")
	(match_operand:DI 1 "dbl_memory_two_insn_operand" ""))]
  "reload_completed"
  [(const_int 0)]
  "frv_split_double_load (operands[0], operands[1]);")

(define_split
  [(set (match_operand:DI 0 "odd_reg_operand" "")
	(match_operand:DI 1 "memory_operand" ""))]
  "reload_completed"
  [(const_int 0)]
  "frv_split_double_load (operands[0], operands[1]);")

(define_split
  [(set (match_operand:DI 0 "dbl_memory_two_insn_operand" "")
	(match_operand:DI 1 "reg_or_0_operand" ""))]
  "reload_completed"
  [(const_int 0)]
  "frv_split_double_store (operands[0], operands[1]);")

(define_split
  [(set (match_operand:DI 0 "memory_operand" "")
	(match_operand:DI 1 "odd_reg_operand" ""))]
  "reload_completed"
  [(const_int 0)]
  "frv_split_double_store (operands[0], operands[1]);")

(define_split
  [(set (match_operand:DI 0 "register_operand" "")
	(match_operand:DI 1 "register_operand" ""))]
  "reload_completed
   && (odd_reg_operand (operands[0], DImode)
       || odd_reg_operand (operands[1], DImode)
       || (integer_register_operand (operands[0], DImode)
	   && integer_register_operand (operands[1], DImode))
       || (!TARGET_DOUBLE
	   && fpr_operand (operands[0], DImode)
	   && fpr_operand (operands[1], DImode)))"
  [(set (match_dup 2) (match_dup 4))
   (set (match_dup 3) (match_dup 5))]
  "
{
  rtx op0      = operands[0];
  rtx op0_low  = gen_lowpart (SImode, op0);
  rtx op0_high = gen_highpart (SImode, op0);
  rtx op1      = operands[1];
  rtx op1_low  = gen_lowpart (SImode, op1);
  rtx op1_high = gen_highpart (SImode, op1);

  /* We normally copy the low-numbered register first.  However, if the first
     register operand 0 is the same as the second register of operand 1, we
     must copy in the opposite order.  */

  if (REGNO (op0_high) == REGNO (op1_low))
    {
      operands[2] = op0_low;
      operands[3] = op0_high;
      operands[4] = op1_low;
      operands[5] = op1_high;
    }
  else
    {
      operands[2] = op0_high;
      operands[3] = op0_low;
      operands[4] = op1_high;
      operands[5] = op1_low;
    }
}")

(define_split
  [(set (match_operand:DI 0 "register_operand" "")
	(match_operand:DI 1 "const_int_operand" ""))]
  "reload_completed"
  [(set (match_dup 2) (match_dup 4))
   (set (match_dup 3) (match_dup 5))]
  "
{
  rtx op0 = operands[0];
  rtx op1 = operands[1];

  operands[2] = gen_highpart (SImode, op0);
  operands[3] = gen_lowpart (SImode, op0);
  if (HOST_BITS_PER_WIDE_INT <= 32)
    {
      operands[4] = GEN_INT ((INTVAL (op1) < 0) ? -1 : 0);
      operands[5] = op1;
    }
  else
    {
      operands[4] = gen_int_mode ((INTVAL (op1) >> 16) >> 16, SImode);
      operands[5] = gen_int_mode (INTVAL (op1), SImode);
    }
}")

(define_split
  [(set (match_operand:DI 0 "register_operand" "")
	(match_operand:DI 1 "const_double_operand" ""))]
  "reload_completed"
  [(set (match_dup 2) (match_dup 4))
   (set (match_dup 3) (match_dup 5))]
  "
{
  rtx op0 = operands[0];
  rtx op1 = operands[1];

  operands[2] = gen_highpart (SImode, op0);
  operands[3] = gen_lowpart (SImode, op0);
  operands[4] = GEN_INT (CONST_DOUBLE_HIGH (op1));
  operands[5] = GEN_INT (CONST_DOUBLE_LOW (op1));
}")

;; Floating Point Moves
;;
;; Note - Patterns for SF mode moves are compulsory, but
;; patterns for DF are optional, as GCC can synthesize them.

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

(define_split
  [(set (match_operand:SF 0 "integer_register_operand" "")
	(match_operand:SF 1 "int_2word_operand" ""))]
  "reload_completed"
  [(set (match_dup 0)
	(high:SF (match_dup 1)))
   (set (match_dup 0)
	(lo_sum:SF (match_dup 0)
		(match_dup 1)))]
  "")

(define_insn "*movsf_load_has_fprs"
  [(set (match_operand:SF 0 "register_operand" "=f,d")
	(match_operand:SF 1 "frv_load_operand" "m,m"))]
  "TARGET_HAS_FPRS"
  "* return output_move_single (operands, insn);"
  [(set_attr "length" "4")
   (set_attr "type" "fload,gload")])

(define_insn "*movsf_internal_has_fprs"
  [(set (match_operand:SF 0 "move_destination_operand" "=f,f,m,m,?f,?d,?d,m,?d")
	(match_operand:SF 1 "move_source_operand" "f,OG,f,OG,d,f,d,d,F"))]
  "TARGET_HAS_FPRS
   && (register_operand (operands[0], SFmode) || reg_or_0_operand (operands[1], SFmode))"
  "* return output_move_single (operands, insn);"
  [(set_attr "length" "4,4,4,4,4,4,4,4,8")
   (set_attr "type" "fsconv,movgf,fstore,gstore,movgf,movfg,int,gstore,multi")])

;; If we don't support the double instructions, prefer gprs over fprs, since it
;; will all be emulated
(define_insn "*movsf_internal_no_fprs"
  [(set (match_operand:SF 0 "move_destination_operand" "=d,d,m,d,d")
	(match_operand:SF 1 "move_source_operand"      " d,OG,dOG,m,F"))]
  "!TARGET_HAS_FPRS
   && (register_operand (operands[0], SFmode) || reg_or_0_operand (operands[1], SFmode))"
  "* return output_move_single (operands, insn);"
  [(set_attr "length" "4,4,4,4,8")
   (set_attr "type" "int,int,gstore,gload,multi")])

(define_insn "movsf_high"
  [(set (match_operand:SF 0 "integer_register_operand" "=d")
	(high:SF (match_operand:SF 1 "int_2word_operand" "i")))]
  ""
  "sethi #hi(%1), %0"
  [(set_attr "type" "sethi")
   (set_attr "length" "4")])

(define_insn "movsf_lo_sum"
  [(set (match_operand:SF 0 "integer_register_operand" "+d")
	(lo_sum:SF (match_dup 0)
		   (match_operand:SF 1 "int_2word_operand" "i")))]
  ""
  "setlo #lo(%1), %0"
  [(set_attr "type" "setlo")
   (set_attr "length" "4")])

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

(define_insn "*movdf_double"
  [(set (match_operand:DF 0 "move_destination_operand" "=h,?e,??f,??d,R,?R,??m,??m,h,?e,??f,??d,?h,??f,?e,??d,R,m,h,??f,e,??d,e,??d")
	(match_operand:DF 1 "move_source_operand"      " h,e,f,d,h,e,f,d,R,R,m,m,e,d,h,f,GO,GO,GO,GO,GO,GO,F,F"))]
  "TARGET_DOUBLE
   && (register_operand (operands[0], DFmode)
       || reg_or_0_operand (operands[1], DFmode))"
  "* return output_move_double (operands, insn);"
  [(set_attr "length" "4,8,8,8,4,4,8,8,4,4,8,8,4,8,4,8,4,8,8,8,8,8,16,16")
   (set_attr "type" "fdconv,multi,multi,multi,fstore,gstore,fstore,gstore,fload,gload,fload,gload,movgf,movgf,movfg,movfg,gstore,gstore,movgf,movgf,multi,multi,multi,multi")])

;; If we don't support the double instructions, prefer gprs over fprs, since it
;; will all be emulated
(define_insn "*movdf_nodouble"
  [(set (match_operand:DF 0 "move_destination_operand" "=e,?h,??d,??f,R,?R,??m,??m,e,?h,??d,??f,?e,??d,?h,??f,R,m,e,??d,e,??d,?h,??f")
	(match_operand:DF 1 "move_source_operand"      " e,h,d,f,e,h,d,f,R,R,m,m,h,f,e,d,GO,GO,GO,GO,nF,nF,GO,GO"))]
  "!TARGET_DOUBLE
   && (register_operand (operands[0], DFmode)
       || reg_or_0_operand (operands[1], DFmode))"
  "* return output_move_double (operands, insn);"
  [(set_attr "length" "8,8,8,8,4,4,8,8,4,4,8,8,8,8,8,8,4,8,8,8,16,16,8,8")
   (set_attr "type" "multi,multi,multi,multi,gstore,fstore,gstore,fstore,gload,fload,gload,fload,movfg,movfg,movgf,movgf,gstore,gstore,multi,multi,multi,multi,movgf,movgf")])

(define_split
  [(set (match_operand:DF 0 "register_operand" "")
	(match_operand:DF 1 "dbl_memory_two_insn_operand" ""))]
  "reload_completed"
  [(const_int 0)]
  "frv_split_double_load (operands[0], operands[1]);")

(define_split
  [(set (match_operand:DF 0 "odd_reg_operand" "")
	(match_operand:DF 1 "memory_operand" ""))]
  "reload_completed"
  [(const_int 0)]
  "frv_split_double_load (operands[0], operands[1]);")

(define_split
  [(set (match_operand:DF 0 "dbl_memory_two_insn_operand" "")
	(match_operand:DF 1 "reg_or_0_operand" ""))]
  "reload_completed"
  [(const_int 0)]
  "frv_split_double_store (operands[0], operands[1]);")

(define_split
  [(set (match_operand:DF 0 "memory_operand" "")
	(match_operand:DF 1 "odd_reg_operand" ""))]
  "reload_completed"
  [(const_int 0)]
  "frv_split_double_store (operands[0], operands[1]);")

(define_split
  [(set (match_operand:DF 0 "register_operand" "")
	(match_operand:DF 1 "register_operand" ""))]
  "reload_completed
   && (odd_reg_operand (operands[0], DFmode)
       || odd_reg_operand (operands[1], DFmode)
       || (integer_register_operand (operands[0], DFmode)
	   && integer_register_operand (operands[1], DFmode))
       || (!TARGET_DOUBLE
	   && fpr_operand (operands[0], DFmode)
	   && fpr_operand (operands[1], DFmode)))"
  [(set (match_dup 2) (match_dup 4))
   (set (match_dup 3) (match_dup 5))]
  "
{
  rtx op0      = operands[0];
  rtx op0_low  = gen_lowpart (SImode, op0);
  rtx op0_high = gen_highpart (SImode, op0);
  rtx op1      = operands[1];
  rtx op1_low  = gen_lowpart (SImode, op1);
  rtx op1_high = gen_highpart (SImode, op1);

  /* We normally copy the low-numbered register first.  However, if the first
     register operand 0 is the same as the second register of operand 1, we
     must copy in the opposite order.  */

  if (REGNO (op0_high) == REGNO (op1_low))
    {
      operands[2] = op0_low;
      operands[3] = op0_high;
      operands[4] = op1_low;
      operands[5] = op1_high;
    }
  else
    {
      operands[2] = op0_high;
      operands[3] = op0_low;
      operands[4] = op1_high;
      operands[5] = op1_low;
    }
}")

(define_split
  [(set (match_operand:DF 0 "register_operand" "")
	(match_operand:DF 1 "const_int_operand" ""))]
  "reload_completed"
  [(set (match_dup 2) (match_dup 4))
   (set (match_dup 3) (match_dup 5))]
  "
{
  rtx op0 = operands[0];
  rtx op1 = operands[1];

  operands[2] = gen_highpart (SImode, op0);
  operands[3] = gen_lowpart (SImode, op0);
  if (HOST_BITS_PER_WIDE_INT <= 32)
    {
      operands[4] = GEN_INT ((INTVAL (op1) < 0) ? -1 : 0);
      operands[5] = op1;
    }
  else
    {
      operands[4] = GEN_INT (((((unsigned HOST_WIDE_INT)INTVAL (op1) >> 16)
			      >> 16) ^ ((unsigned HOST_WIDE_INT)1 << 31))
			     - ((unsigned HOST_WIDE_INT)1 << 31));
      operands[5] = GEN_INT (trunc_int_for_mode (INTVAL (op1), SImode));
    }
}")

(define_split
  [(set (match_operand:DF 0 "register_operand" "")
	(match_operand:DF 1 "const_double_operand" ""))]
  "reload_completed"
  [(set (match_dup 2) (match_dup 4))
   (set (match_dup 3) (match_dup 5))]
  "
{
  rtx op0 = operands[0];
  rtx op1 = operands[1];
  long l[2];

  REAL_VALUE_TO_TARGET_DOUBLE (*CONST_DOUBLE_REAL_VALUE (op1), l);

  operands[2] = gen_highpart (SImode, op0);
  operands[3] = gen_lowpart (SImode, op0);
  operands[4] = GEN_INT (l[0]);
  operands[5] = GEN_INT (l[1]);
}")

;; String/block move insn.
;; Argument 0 is the destination
;; Argument 1 is the source
;; Argument 2 is the length
;; Argument 3 is the alignment

(define_expand "movmemsi"
  [(parallel [(set (match_operand:BLK 0 "" "")
		   (match_operand:BLK 1 "" ""))
	      (use (match_operand:SI 2 "" ""))
	      (use (match_operand:SI 3 "" ""))])]
  ""
  "
{
  if (frv_expand_block_move (operands))
    DONE;
  else
    FAIL;
}")

;; String/block set insn.
;; Argument 0 is the destination
;; Argument 1 is the length
;; Argument 2 is the byte value -- ignore any value but zero
;; Argument 3 is the alignment

(define_expand "setmemsi"
  [(parallel [(set (match_operand:BLK 0 "" "")
		   (match_operand 2 "" ""))
	      (use (match_operand:SI 1 "" ""))
	      (use (match_operand:SI 3 "" ""))])]
  ""
  "
{
  /* If value to set is not zero, use the library routine.  */
  if (operands[2] != const0_rtx)
    FAIL;

  if (frv_expand_block_clear (operands))
    DONE;
  else
    FAIL;
}")


;; The "membar" part of a __builtin_read* or __builtin_write* function.
;; Operand 0 is a volatile reference to the memory that the function reads
;; or writes.  Operand 1 is the address being accessed, or zero if the
;; address isn't a known constant.  Operand 2 describes the __builtin
;; function (either FRV_IO_READ or FRV_IO_WRITE).
(define_insn "optional_membar_<mode>"
  [(set (match_operand:IMODE 0 "memory_operand" "=m")
	(unspec:IMODE [(match_operand 1 "const_int_operand" "")
		       (match_operand 2 "const_int_operand" "")]
		      UNSPEC_OPTIONAL_MEMBAR))]
  ""
  "membar"
  [(set_attr "length" "4")])

;; ::::::::::::::::::::
;; ::
;; :: Reload CC registers
;; ::
;; ::::::::::::::::::::

;; Use as a define_expand so that cse/gcse/combine can't accidentally
;; create movcc insns.

(define_expand "movcc"
  [(parallel [(set (match_operand:CC 0 "move_destination_operand" "")
		   (match_operand:CC 1 "move_source_operand" ""))
	      (clobber (match_dup 2))])]
  ""
  "
{
 if (! reload_in_progress && ! reload_completed)
    FAIL;

 operands[2] = gen_rtx_REG (CC_CCRmode, ICR_TEMP);
}")

(define_insn "*internal_movcc"
  [(set (match_operand:CC 0 "move_destination_operand" "=t,d,d,m,d")
	(match_operand:CC 1 "move_source_operand" "d,d,m,d,t"))
   (clobber (match_scratch:CC_CCR 2 "=X,X,X,X,&v"))]
  "reload_in_progress || reload_completed"
  "@
   cmpi %1, #0, %0
   mov %1, %0
   ld%I1%U1 %M1, %0
   st%I0%U0 %1, %M0
   #"
  [(set_attr "length" "4,4,4,4,20")
   (set_attr "type" "int,int,gload,gstore,multi")])

;; To move an ICC value to a GPR for a signed comparison, we create a value
;; that when compared to 0, sets the N and Z flags appropriately (we don't care
;; about the V and C flags, since these comparisons are signed).

(define_split
  [(set (match_operand:CC 0 "integer_register_operand" "")
	(match_operand:CC 1 "icc_operand" ""))
   (clobber (match_operand:CC_CCR 2 "icr_operand" ""))]
  "reload_in_progress || reload_completed"
  [(match_dup 3)]
  "
{
  rtx dest = simplify_gen_subreg (SImode, operands[0], CCmode, 0);
  rtx icc  = operands[1];
  rtx icr  = operands[2];

  start_sequence ();

  emit_insn (gen_rtx_SET (icr, gen_rtx_LT (CC_CCRmode, icc, const0_rtx)));

  emit_insn (gen_movsi (dest, const1_rtx));

  emit_insn (gen_rtx_COND_EXEC (VOIDmode,
				gen_rtx_NE (CC_CCRmode, icr, const0_rtx),
				gen_rtx_SET (dest,
					     gen_rtx_NEG (SImode, dest))));

  emit_insn (gen_rtx_SET (icr, gen_rtx_EQ (CC_CCRmode, icc, const0_rtx)));

  emit_insn (gen_rtx_COND_EXEC (VOIDmode,
				gen_rtx_NE (CC_CCRmode, icr, const0_rtx),
				gen_rtx_SET (dest, const0_rtx)));

  operands[3] = get_insns ();
  end_sequence ();
}")

;; Reload CC_UNSmode for unsigned integer comparisons
;; Use define_expand so that cse/gcse/combine can't create movcc_uns insns

(define_expand "movcc_uns"
  [(parallel [(set (match_operand:CC_UNS 0 "move_destination_operand" "")
		   (match_operand:CC_UNS 1 "move_source_operand" ""))
	      (clobber (match_dup 2))])]
  ""
  "
{
 if (! reload_in_progress && ! reload_completed)
    FAIL;
 operands[2] = gen_rtx_REG (CC_CCRmode, ICR_TEMP);
}")

(define_insn "*internal_movcc_uns"
  [(set (match_operand:CC_UNS 0 "move_destination_operand" "=t,d,d,m,d")
	(match_operand:CC_UNS 1 "move_source_operand" "d,d,m,d,t"))
   (clobber (match_scratch:CC_CCR 2 "=X,X,X,X,&v"))]
  "reload_in_progress || reload_completed"
  "@
   cmpi %1, #1, %0
   mov %1, %0
   ld%I1%U1 %M1, %0
   st%I0%U0 %1, %M0
   #"
  [(set_attr "length" "4,4,4,4,20")
   (set_attr "type" "int,int,gload,gstore,multi")])

;; To move an ICC value to a GPR for an unsigned comparison, we create a value
;; that when compared to 1, sets the Z, V, and C flags appropriately (we don't
;; care about the N flag, since these comparisons are unsigned).

(define_split
  [(set (match_operand:CC_UNS 0 "integer_register_operand" "")
	(match_operand:CC_UNS 1 "icc_operand" ""))
   (clobber (match_operand:CC_CCR 2 "icr_operand" ""))]
  "reload_in_progress || reload_completed"
  [(match_dup 3)]
  "
{
  rtx dest = simplify_gen_subreg (SImode, operands[0], CC_UNSmode, 0);
  rtx icc  = operands[1];
  rtx icr  = operands[2];

  start_sequence ();

  emit_insn (gen_rtx_SET (icr, gen_rtx_GTU (CC_CCRmode, icc, const0_rtx)));

  emit_insn (gen_movsi (dest, const1_rtx));

  emit_insn (gen_rtx_COND_EXEC (VOIDmode,
				gen_rtx_NE (CC_CCRmode, icr, const0_rtx),
				gen_addsi3 (dest, dest, dest)));

  emit_insn (gen_rtx_SET (icr, gen_rtx_LTU (CC_CCRmode, icc, const0_rtx)));

  emit_insn (gen_rtx_COND_EXEC (VOIDmode,
				gen_rtx_NE (CC_CCRmode, icr, const0_rtx),
				gen_rtx_SET (dest, const0_rtx)));

  operands[3] = get_insns ();
  end_sequence ();
}")

;; Reload CC_NZmode.  This is mostly the same as the CCmode and CC_UNSmode
;; handling, but it uses different sequences for moving between GPRs and ICCs.

(define_expand "movcc_nz"
  [(parallel [(set (match_operand:CC_NZ 0 "move_destination_operand" "")
		   (match_operand:CC_NZ 1 "move_source_operand" ""))
	      (clobber (match_dup 2))])]
  ""
  "
{
  if (!reload_in_progress && !reload_completed)
    FAIL;
  operands[2] = gen_rtx_REG (CC_CCRmode, ICR_TEMP);
}")

(define_insn "*internal_movcc_nz"
  [(set (match_operand:CC_NZ 0 "move_destination_operand" "=t,d,d,m,d")
	(match_operand:CC_NZ 1 "move_source_operand" "d,d,m,d,t"))
   (clobber (match_scratch:CC_CCR 2 "=X,X,X,X,&v"))]
  "reload_in_progress || reload_completed"
  "@
   cmpi %1, #0, %0
   mov %1, %0
   ld%I1%U1 %M1, %0
   st%I0%U0 %1, %M0
   #"
  [(set_attr "length" "4,4,4,4,20")
   (set_attr "type" "int,int,gload,gstore,multi")])

;; Set the destination to a value that, when compared with zero, will
;; restore the value of the Z and N flags.  The values of the other
;; flags don't matter.  The sequence is:
;;
;;     setlos op0,#-1
;;     ckp op1,op2
;;     csub gr0,op0,op0,op2
;;     ckeq op1,op2
;;     cmov gr0,op0,op2
(define_split
  [(set (match_operand:CC_NZ 0 "integer_register_operand" "")
	(match_operand:CC_NZ 1 "icc_operand" ""))
   (clobber (match_operand:CC_CCR 2 "icr_operand" ""))]
  "reload_in_progress || reload_completed"
  [(set (match_dup 3)
	(const_int -1))
   (set (match_dup 2)
	(ge:CC_CCR (match_dup 1)
		   (const_int 0)))
   (cond_exec (ne:CC_CCR (match_dup 2)
			 (const_int 0))
	      (set (match_dup 3)
		   (neg:SI (match_dup 3))))
   (set (match_dup 2)
	(eq:CC_CCR (match_dup 1)
		   (const_int 0)))
   (cond_exec (ne:CC_CCR (match_dup 2)
			 (const_int 0))
	      (set (match_dup 3) (const_int 0)))]
  "operands[3] = simplify_gen_subreg (SImode, operands[0], CC_NZmode, 0);")

;; Reload CC_FPmode for floating point comparisons
;; We use a define_expand here so that cse/gcse/combine can't accidentally
;; create movcc insns.  If this was a named define_insn, we would not be able
;; to make it conditional on reload.

(define_expand "movcc_fp"
  [(set (match_operand:CC_FP 0 "movcc_fp_destination_operand" "")
	(match_operand:CC_FP 1 "move_source_operand" ""))]
  "TARGET_HAS_FPRS"
  "
{
 if (! reload_in_progress && ! reload_completed)
    FAIL;
}")

(define_insn "*movcc_fp_internal"
  [(set (match_operand:CC_FP 0 "movcc_fp_destination_operand" "=d,d,d,m")
	(match_operand:CC_FP 1 "move_source_operand" "u,d,m,d"))]
  "TARGET_HAS_FPRS && (reload_in_progress || reload_completed)"
  "@
   #
   mov %1, %0
   ld%I1%U1 %M1, %0
   st%I0%U0 %1, %M0"
  [(set_attr "length" "12,4,4,4")
   (set_attr "type" "multi,int,gload,gstore")])


(define_expand "reload_incc_fp"
  [(match_operand:CC_FP 0 "fcc_operand" "=u")
   (match_operand:CC_FP 1 "gpr_or_memory_operand_with_scratch" "m")
   (match_operand:TI 2 "integer_register_operand" "=&d")]
  "TARGET_HAS_FPRS"
  "
{
  rtx cc_op2 = simplify_gen_subreg (CC_FPmode, operands[2], TImode, 0);
  rtx int_op2 = simplify_gen_subreg (SImode, operands[2], TImode, 0);
  rtx temp1 = simplify_gen_subreg (SImode, operands[2], TImode, 4);
  rtx temp2 = simplify_gen_subreg (SImode, operands[2], TImode, 8);
  int shift = CC_SHIFT_RIGHT (REGNO (operands[0]));
  HOST_WIDE_INT mask;

  if (!gpr_or_memory_operand (operands[1], CC_FPmode))
    {
      rtx addr;
      rtx temp3 = simplify_gen_subreg (SImode, operands[2], TImode, 12);

      gcc_assert (GET_CODE (operands[1]) == MEM);

      addr = XEXP (operands[1], 0);

      gcc_assert (GET_CODE (addr) == PLUS);

      emit_move_insn (temp3, XEXP (addr, 1));

      operands[1] = replace_equiv_address (operands[1],
					   gen_rtx_PLUS (GET_MODE (addr),
							 XEXP (addr, 0),
							 temp3));
    }

  emit_insn (gen_movcc_fp (cc_op2, operands[1]));
  if (shift)
    emit_insn (gen_ashlsi3 (int_op2, int_op2, GEN_INT (shift)));

  mask = ~ ((HOST_WIDE_INT)CC_MASK << shift);
  emit_insn (gen_movsi (temp1, GEN_INT (mask)));
  emit_insn (gen_update_fcc (operands[0], int_op2, temp1, temp2));
  DONE;
}")

(define_expand "reload_outcc_fp"
  [(set (match_operand:CC_FP 2 "integer_register_operand" "=&d")
	(match_operand:CC_FP 1 "fcc_operand" "u"))
   (set (match_operand:CC_FP 0 "memory_operand" "=m")
	(match_dup 2))]
  "TARGET_HAS_FPRS"
 "")

;; Convert a FCC value to gpr
(define_insn "read_fcc"
  [(set (match_operand:SI 0 "integer_register_operand" "=d")
	(unspec:SI [(match_operand:CC_FP 1 "fcc_operand" "u")]
		   UNSPEC_CC_TO_GPR))]
  "TARGET_HAS_FPRS"
  "movsg ccr, %0"
  [(set_attr "type" "spr")
   (set_attr "length" "4")])

(define_split
  [(set (match_operand:CC_FP 0 "integer_register_operand" "")
	(match_operand:CC_FP 1 "fcc_operand" ""))]
  "reload_completed && TARGET_HAS_FPRS"
  [(match_dup 2)]
  "
{
  rtx int_op0 = simplify_gen_subreg (SImode, operands[0], CC_FPmode, 0);
  int shift = CC_SHIFT_RIGHT (REGNO (operands[1]));

  start_sequence ();

  emit_insn (gen_read_fcc (int_op0, operands[1]));
  if (shift)
    emit_insn (gen_lshrsi3 (int_op0, int_op0, GEN_INT (shift)));

  emit_insn (gen_andsi3 (int_op0, int_op0, GEN_INT (CC_MASK)));

  operands[2] = get_insns ();
  end_sequence ();
}")

;; Move a gpr value to FCC.
;; Operand0 = FCC
;; Operand1 = reloaded value shifted appropriately
;; Operand2 = mask to eliminate current register
;; Operand3 = temporary to load/store ccr
(define_insn "update_fcc"
  [(set (match_operand:CC_FP 0 "fcc_operand" "=u")
	(unspec:CC_FP [(match_operand:SI 1 "integer_register_operand" "d")
		       (match_operand:SI 2 "integer_register_operand" "d")]
		      UNSPEC_GPR_TO_CC))
   (clobber (match_operand:SI 3 "integer_register_operand" "=&d"))]
  "TARGET_HAS_FPRS"
  "movsg ccr, %3\;and %2, %3, %3\;or %1, %3, %3\;movgs %3, ccr"
  [(set_attr "type" "multi")
   (set_attr "length" "16")])

;; Reload CC_CCRmode for conditional execution registers
(define_insn "movcc_ccr"
  [(set (match_operand:CC_CCR 0 "move_destination_operand" "=d,d,d,m,v,?w,C,d")
	(match_operand:CC_CCR 1 "move_source_operand" "C,d,m,d,n,n,C,L"))]
  ""
  "@
   #
   mov %1, %0
   ld%I1%U1 %M1, %0
   st%I0%U0 %1, %M0
   #
   #
   orcr %1, %1, %0
   setlos #%1, %0"
  [(set_attr "length" "8,4,4,4,8,12,4,4")
   (set_attr "type" "multi,int,gload,gstore,multi,multi,ccr,int")])

(define_expand "reload_incc_ccr"
  [(match_operand:CC_CCR 0 "cr_operand" "=C")
   (match_operand:CC_CCR 1 "memory_operand" "m")
   (match_operand:CC_CCR 2 "integer_register_operand" "=&d")]
  ""
  "
{
  rtx icc = gen_rtx_REG (CCmode, ICC_TEMP);
  rtx int_op2 = simplify_gen_subreg (SImode, operands[2], CC_CCRmode, 0);
  rtx icr = (ICR_P (REGNO (operands[0]))
	     ? operands[0] : gen_rtx_REG (CC_CCRmode, ICR_TEMP));

  emit_insn (gen_movcc_ccr (operands[2], operands[1]));
  emit_insn (gen_cmpsi_cc (icc, int_op2, const0_rtx));
  emit_insn (gen_movcc_ccr (icr, gen_rtx_NE (CC_CCRmode, icc, const0_rtx)));

  if (! ICR_P (REGNO (operands[0])))
    emit_insn (gen_movcc_ccr (operands[0], icr));

  DONE;
}")

(define_expand "reload_outcc_ccr"
  [(set (match_operand:CC_CCR 2 "integer_register_operand" "=&d")
	(match_operand:CC_CCR 1 "cr_operand" "C"))
   (set (match_operand:CC_CCR 0 "memory_operand" "=m")
	(match_dup 2))]
  ""
  "")

(define_split
  [(set (match_operand:CC_CCR 0 "integer_register_operand" "")
	(match_operand:CC_CCR 1 "cr_operand" ""))]
  "reload_completed"
  [(match_dup 2)]
  "
{
  rtx int_op0 = simplify_gen_subreg (SImode, operands[0], CC_CCRmode, 0);

  start_sequence ();
  emit_move_insn (operands[0], const1_rtx);
  emit_insn (gen_rtx_COND_EXEC (VOIDmode,
				gen_rtx_EQ (CC_CCRmode,
					    operands[1],
					    const0_rtx),
				gen_rtx_SET (int_op0, const0_rtx)));

  operands[2] = get_insns ();
  end_sequence ();
}")

(define_split
  [(set (match_operand:CC_CCR 0 "cr_operand" "")
	(match_operand:CC_CCR 1 "const_int_operand" ""))]
  "reload_completed"
  [(match_dup 2)]
  "
{
  rtx icc = gen_rtx_REG (CCmode, ICC_TEMP);
  rtx r0  = gen_rtx_REG (SImode, GPR_FIRST);
  rtx icr = (ICR_P (REGNO (operands[0]))
	     ? operands[0] : gen_rtx_REG (CC_CCRmode, ICR_TEMP));

  start_sequence ();

 emit_insn (gen_cmpsi_cc (icc, r0, const0_rtx));

  emit_insn (gen_movcc_ccr (icr,
			    gen_rtx_fmt_ee (((INTVAL (operands[1]) == 0)
					     ? EQ : NE), CC_CCRmode,
					    r0, const0_rtx)));

  if (! ICR_P (REGNO (operands[0])))
    emit_insn (gen_movcc_ccr (operands[0], icr));

  operands[2] = get_insns ();
  end_sequence ();
}")

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

;; Signed conversions from a smaller integer to a larger integer
;;
;; These operations are optional.  If they are not
;; present GCC will synthesize them for itself
;; Even though frv does not provide these instructions, we define them
;; to allow load + sign extend to be collapsed together
(define_insn "extendqihi2"
  [(set (match_operand:HI 0 "integer_register_operand" "=d,d")
	(sign_extend:HI (match_operand:QI 1 "gpr_or_memory_operand" "d,m")))]
  ""
  "@
   #
   ldsb%I1%U1 %M1,%0"
  [(set_attr "length" "8,4")
   (set_attr "type" "multi,gload")])

(define_split
  [(set (match_operand:HI 0 "integer_register_operand" "")
	(sign_extend:HI (match_operand:QI 1 "integer_register_operand" "")))]
  "reload_completed"
  [(match_dup 2)
   (match_dup 3)]
  "
{
  rtx op0   = gen_lowpart (SImode, operands[0]);
  rtx op1   = gen_lowpart (SImode, operands[1]);
  rtx shift = GEN_INT (24);

  operands[2] = gen_ashlsi3 (op0, op1, shift);
  operands[3] = gen_ashrsi3 (op0, op0, shift);
}")

(define_insn "extendqisi2"
  [(set (match_operand:SI 0 "integer_register_operand" "=d,d")
	(sign_extend:SI (match_operand:QI 1 "gpr_or_memory_operand" "d,m")))]
  ""
  "@
   #
   ldsb%I1%U1 %M1,%0"
  [(set_attr "length" "8,4")
   (set_attr "type" "multi,gload")])

(define_split
  [(set (match_operand:SI 0 "integer_register_operand" "")
	(sign_extend:SI (match_operand:QI 1 "integer_register_operand" "")))]
  "reload_completed"
  [(match_dup 2)
   (match_dup 3)]
  "
{
  rtx op0   = gen_lowpart (SImode, operands[0]);
  rtx op1   = gen_lowpart (SImode, operands[1]);
  rtx shift = GEN_INT (24);

  operands[2] = gen_ashlsi3 (op0, op1, shift);
  operands[3] = gen_ashrsi3 (op0, op0, shift);
}")

;;(define_insn "extendqidi2"
;;  [(set (match_operand:DI 0 "register_operand" "=r")
;;	(sign_extend:DI (match_operand:QI 1 "general_operand" "g")))]
;;  ""
;;  "extendqihi2 %0,%1"
;;  [(set_attr "length" "4")])

(define_insn "extendhisi2"
  [(set (match_operand:SI 0 "integer_register_operand" "=d,d")
	(sign_extend:SI (match_operand:HI 1 "gpr_or_memory_operand" "d,m")))]
  ""
  "@
   #
   ldsh%I1%U1 %M1,%0"
  [(set_attr "length" "8,4")
   (set_attr "type" "multi,gload")])

(define_split
  [(set (match_operand:SI 0 "integer_register_operand" "")
	(sign_extend:SI (match_operand:HI 1 "integer_register_operand" "")))]
  "reload_completed"
  [(match_dup 2)
   (match_dup 3)]
  "
{
  rtx op0   = gen_lowpart (SImode, operands[0]);
  rtx op1   = gen_lowpart (SImode, operands[1]);
  rtx shift = GEN_INT (16);

  operands[2] = gen_ashlsi3 (op0, op1, shift);
  operands[3] = gen_ashrsi3 (op0, op0, shift);
}")

;;(define_insn "extendhidi2"
;;  [(set (match_operand:DI 0 "register_operand" "=r")
;;	(sign_extend:DI (match_operand:HI 1 "general_operand" "g")))]
;;  ""
;;  "extendhihi2 %0,%1"
;;  [(set_attr "length" "4")])
;;
;;(define_insn "extendsidi2"
;;  [(set (match_operand:DI 0 "register_operand" "=r")
;;	(sign_extend:DI (match_operand:SI 1 "general_operand" "g")))]
;;  ""
;;  "extendsidi2 %0,%1"
;;  [(set_attr "length" "4")])

;; Unsigned conversions from a smaller integer to a larger integer
(define_insn "zero_extendqihi2"
  [(set (match_operand:HI 0 "integer_register_operand" "=d,d,d")
	(zero_extend:HI
	  (match_operand:QI 1 "gpr_or_memory_operand" "d,L,m")))]
  ""
  "@
   andi %1,#0xff,%0
   setlos %1,%0
   ldub%I1%U1 %M1,%0"
  [(set_attr "length" "4")
   (set_attr "type" "int,int,gload")])

(define_insn "zero_extendqisi2"
  [(set (match_operand:SI 0 "integer_register_operand" "=d,d,d")
	(zero_extend:SI
	  (match_operand:QI 1 "gpr_or_memory_operand" "d,L,m")))]
  ""
  "@
   andi %1,#0xff,%0
   setlos %1,%0
   ldub%I1%U1 %M1,%0"
  [(set_attr "length" "4")
   (set_attr "type" "int,int,gload")])

;;(define_insn "zero_extendqidi2"
;;  [(set (match_operand:DI 0 "register_operand" "=r")
;;	(zero_extend:DI (match_operand:QI 1 "general_operand" "g")))]
;;  ""
;;  "zero_extendqihi2 %0,%1"
;;  [(set_attr "length" "4")])

;; Do not set the type for the sethi to "sethi", since the scheduler will think
;; the sethi takes 0 cycles as part of allowing sethi/setlo to be in the same
;; VLIW instruction.
(define_insn "zero_extendhisi2"
  [(set (match_operand:SI 0 "integer_register_operand" "=d,d")
	(zero_extend:SI (match_operand:HI 1 "gpr_or_memory_operand" "0,m")))]
  ""
  "@
    sethi #hi(#0),%0
    lduh%I1%U1 %M1,%0"
  [(set_attr "length" "4")
   (set_attr "type" "int,gload")])

;;(define_insn "zero_extendhidi2"
;;  [(set (match_operand:DI 0 "register_operand" "=r")
;;	(zero_extend:DI (match_operand:HI 1 "general_operand" "g")))]
;;  ""
;;  "zero_extendhihi2 %0,%1"
;;  [(set_attr "length" "4")])
;;
;;(define_insn "zero_extendsidi2"
;;  [(set (match_operand:DI 0 "register_operand" "=r")
;;	(zero_extend:DI (match_operand:SI 1 "general_operand" "g")))]
;;  ""
;;  "zero_extendsidi2 %0,%1"
;;  [(set_attr "length" "4")])
;;
;;;; Convert between floating point types of different sizes.
;;
;;(define_insn "extendsfdf2"
;;  [(set (match_operand:DF 0 "register_operand" "=r")
;;	(float_extend:DF (match_operand:SF 1 "register_operand" "r")))]
;;  ""
;;  "extendsfdf2 %0,%1"
;;  [(set_attr "length" "4")])
;;
;;(define_insn "truncdfsf2"
;;  [(set (match_operand:SF 0 "register_operand" "=r")
;;	(float_truncate:SF (match_operand:DF 1 "register_operand" "r")))]
;;  ""
;;  "truncdfsf2 %0,%1"
;;  [(set_attr "length" "4")])

;;;; Convert between signed integer types and floating point.
(define_insn "floatsisf2"
  [(set (match_operand:SF 0 "fpr_operand" "=f")
	(float:SF (match_operand:SI 1 "fpr_operand" "f")))]
  "TARGET_HARD_FLOAT"
  "fitos %1,%0"
  [(set_attr "length" "4")
   (set_attr "type" "fsconv")])

(define_insn "floatsidf2"
  [(set (match_operand:DF 0 "fpr_operand" "=h")
	(float:DF (match_operand:SI 1 "fpr_operand" "f")))]
  "TARGET_HARD_FLOAT && TARGET_DOUBLE"
  "fitod %1,%0"
  [(set_attr "length" "4")
   (set_attr "type" "fdconv")])

;;(define_insn "floatdisf2"
;;  [(set (match_operand:SF 0 "register_operand" "=r")
;;	(float:SF (match_operand:DI 1 "register_operand" "r")))]
;;  ""
;;  "floatdisf2 %0,%1"
;;  [(set_attr "length" "4")])
;;
;;(define_insn "floatdidf2"
;;  [(set (match_operand:DF 0 "register_operand" "=r")
;;	(float:DF (match_operand:DI 1 "register_operand" "r")))]
;;  ""
;;  "floatdidf2 %0,%1"
;;  [(set_attr "length" "4")])

(define_insn "fix_truncsfsi2"
  [(set (match_operand:SI 0 "fpr_operand" "=f")
	(fix:SI (match_operand:SF 1 "fpr_operand" "f")))]
  "TARGET_HARD_FLOAT"
  "fstoi %1,%0"
  [(set_attr "length" "4")
   (set_attr "type" "fsconv")])

(define_insn "fix_truncdfsi2"
  [(set (match_operand:SI 0 "fpr_operand" "=f")
	(fix:SI (match_operand:DF 1 "fpr_operand" "h")))]
  "TARGET_HARD_FLOAT && TARGET_DOUBLE"
  "fdtoi %1,%0"
  [(set_attr "length" "4")
   (set_attr "type" "fdconv")])

;;(define_insn "fix_truncsfdi2"
;;  [(set (match_operand:DI 0 "register_operand" "=r")
;;	(fix:DI (match_operand:SF 1 "register_operand" "r")))]
;;  ""
;;  "fix_truncsfdi2 %0,%1"
;;  [(set_attr "length" "4")])
;;
;;(define_insn "fix_truncdfdi2"
;;  [(set (match_operand:DI 0 "register_operand" "=r")
;;	(fix:DI (match_operand:DF 1 "register_operand" "r")))]
;;  ""
;;  "fix_truncdfdi2 %0,%1"
;;  [(set_attr "length" "4")])
;;
;;;; Convert between unsigned integer types and floating point.
;;
;;(define_insn "floatunssisf2"
;;  [(set (match_operand:SF 0 "register_operand" "=r")
;;	(unsigned_float:SF (match_operand:SI 1 "register_operand" "r")))]
;;  ""
;;  "floatunssisf2 %0,%1"
;;  [(set_attr "length" "4")])
;;
;;(define_insn "floatunssidf2"
;;  [(set (match_operand:DF 0 "register_operand" "=r")
;;	(unsigned_float:DF (match_operand:SI 1 "register_operand" "r")))]
;;  ""
;;  "floatunssidf2 %0,%1"
;;  [(set_attr "length" "4")])
;;
;;(define_insn "floatunsdisf2"
;;  [(set (match_operand:SF 0 "register_operand" "=r")
;;	(unsigned_float:SF (match_operand:DI 1 "register_operand" "r")))]
;;  ""
;;  "floatunsdisf2 %0,%1"
;;  [(set_attr "length" "4")])
;;
;;(define_insn "floatunsdidf2"
;;  [(set (match_operand:DF 0 "register_operand" "=r")
;;	(unsigned_float:DF (match_operand:DI 1 "register_operand" "r")))]
;;  ""
;;  "floatunsdidf2 %0,%1"
;;  [(set_attr "length" "4")])
;;
;;(define_insn "fixuns_truncsfsi2"
;;  [(set (match_operand:SI 0 "register_operand" "=r")
;;	(unsigned_fix:SI (match_operand:SF 1 "register_operand" "r")))]
;;  ""
;;  "fixuns_truncsfsi2 %0,%1"
;;  [(set_attr "length" "4")])
;;
;;(define_insn "fixuns_truncdfsi2"
;;  [(set (match_operand:SI 0 "register_operand" "=r")
;;	(unsigned_fix:SI (match_operand:DF 1 "register_operand" "r")))]
;;  ""
;;  "fixuns_truncdfsi2 %0,%1"
;;  [(set_attr "length" "4")])
;;
;;(define_insn "fixuns_truncsfdi2"
;;  [(set (match_operand:DI 0 "register_operand" "=r")
;;	(unsigned_fix:DI (match_operand:SF 1 "register_operand" "r")))]
;;  ""
;;  "fixuns_truncsfdi2 %0,%1"
;;  [(set_attr "length" "4")])
;;
;;(define_insn "fixuns_truncdfdi2"
;;  [(set (match_operand:DI 0 "register_operand" "=r")
;;	(unsigned_fix:DI (match_operand:DF 1 "register_operand" "r")))]
;;  ""
;;  "fixuns_truncdfdi2 %0,%1"
;;  [(set_attr "length" "4")])


;; ::::::::::::::::::::
;; ::
;; :: 32-bit Integer arithmetic
;; ::
;; ::::::::::::::::::::

;; Addition
(define_insn "addsi3"
  [(set (match_operand:SI 0 "integer_register_operand" "=d")
	(plus:SI (match_operand:SI 1 "integer_register_operand" "%d")
		 (match_operand:SI 2 "gpr_or_int12_operand" "dNOPQ")))]
  ""
  "add%I2 %1,%2,%0"
  [(set_attr "length" "4")
   (set_attr "type" "int")])

;; Subtraction.  No need to worry about constants, since the compiler
;; canonicalizes them into addsi3's.  We prevent SUBREG's here to work around a
;; combine bug, that combines the 32x32->upper 32 bit multiply that uses a
;; SUBREG with a minus that shows up in modulus by constants.
(define_insn "subsi3"
  [(set (match_operand:SI 0 "integer_register_operand" "=d")
	(minus:SI (match_operand:SI 1 "gpr_no_subreg_operand" "d")
		  (match_operand:SI 2 "gpr_no_subreg_operand" "d")))]
  ""
  "sub %1,%2,%0"
  [(set_attr "length" "4")
   (set_attr "type" "int")])

;; Signed multiplication producing 64-bit results from 32-bit inputs
;; Note, frv doesn't have a 32x32->32 bit multiply, but the compiler
;; will do the 32x32->64 bit multiply and use the bottom word.
(define_expand "mulsidi3"
  [(set (match_operand:DI 0 "integer_register_operand" "")
	(mult:DI (sign_extend:DI (match_operand:SI 1 "integer_register_operand" ""))
		 (sign_extend:DI (match_operand:SI 2 "gpr_or_int12_operand" ""))))]
  ""
  "
{
  if (GET_CODE (operands[2]) == CONST_INT)
    {
      emit_insn (gen_mulsidi3_const (operands[0], operands[1], operands[2]));
      DONE;
    }
}")

(define_insn "*mulsidi3_reg"
  [(set (match_operand:DI 0 "even_gpr_operand" "=e")
	(mult:DI (sign_extend:DI (match_operand:SI 1 "integer_register_operand" "%d"))
		 (sign_extend:DI (match_operand:SI 2 "integer_register_operand" "d"))))]
  ""
  "smul %1,%2,%0"
  [(set_attr "length" "4")
   (set_attr "type" "mul")])

(define_insn "mulsidi3_const"
  [(set (match_operand:DI 0 "even_gpr_operand" "=e")
	(mult:DI (sign_extend:DI (match_operand:SI 1 "integer_register_operand" "d"))
		 (match_operand:SI 2 "int12_operand" "NOP")))]
  ""
  "smuli %1,%2,%0"
  [(set_attr "length" "4")
   (set_attr "type" "mul")])

;; Unsigned multiplication producing 64-bit results from 32-bit inputs
(define_expand "umulsidi3"
  [(set (match_operand:DI 0 "even_gpr_operand" "")
	(mult:DI (zero_extend:DI (match_operand:SI 1 "integer_register_operand" ""))
		 (zero_extend:DI (match_operand:SI 2 "gpr_or_int12_operand" ""))))]
  ""
  "
{
  if (GET_CODE (operands[2]) == CONST_INT)
    {
      emit_insn (gen_umulsidi3_const (operands[0], operands[1], operands[2]));
      DONE;
    }
}")

(define_insn "*mulsidi3_reg"
  [(set (match_operand:DI 0 "even_gpr_operand" "=e")
	(mult:DI (zero_extend:DI (match_operand:SI 1 "integer_register_operand" "%d"))
		 (zero_extend:DI (match_operand:SI 2 "integer_register_operand" "d"))))]
  ""
  "umul %1,%2,%0"
  [(set_attr "length" "4")
   (set_attr "type" "mul")])

(define_insn "umulsidi3_const"
  [(set (match_operand:DI 0 "even_gpr_operand" "=e")
	(mult:DI (zero_extend:DI (match_operand:SI 1 "integer_register_operand" "d"))
		 (match_operand:SI 2 "int12_operand" "NOP")))]
  ""
  "umuli %1,%2,%0"
  [(set_attr "length" "4")
   (set_attr "type" "mul")])

;; Signed Division
(define_insn "divsi3"
  [(set (match_operand:SI 0 "register_operand" "=d,d")
	(div:SI (match_operand:SI 1 "register_operand" "d,d")
		(match_operand:SI 2 "gpr_or_int12_operand" "d,NOP")))]
  ""
  "sdiv%I2 %1,%2,%0"
  [(set_attr "length" "4")
   (set_attr "type" "div")])

;; Unsigned Division
(define_insn "udivsi3"
  [(set (match_operand:SI 0 "register_operand" "=d,d")
	(udiv:SI (match_operand:SI 1 "register_operand" "d,d")
		 (match_operand:SI 2 "gpr_or_int12_operand" "d,NOP")))]
  ""
  "udiv%I2 %1,%2,%0"
  [(set_attr "length" "4")
   (set_attr "type" "div")])

;; Negation
(define_insn "negsi2"
  [(set (match_operand:SI 0 "integer_register_operand" "=d")
	(neg:SI (match_operand:SI 1 "integer_register_operand" "d")))]
  ""
  "sub %.,%1,%0"
  [(set_attr "length" "4")
   (set_attr "type" "int")])

;; Find first one bit
;; (define_insn "ffssi2"
;;   [(set (match_operand:SI 0 "register_operand" "=r")
;; 	(ffs:SI (match_operand:SI 1 "register_operand" "r")))]
;;   ""
;;   "ffssi2 %0,%1"
;;   [(set_attr "length" "4")])


;; ::::::::::::::::::::
;; ::
;; :: 64-bit Integer arithmetic
;; ::
;; ::::::::::::::::::::

;; Addition
(define_insn_and_split "adddi3"
  [(set (match_operand:DI 0 "integer_register_operand" "=&e,e")
	(plus:DI (match_operand:DI 1 "integer_register_operand" "%e,0")
		 (match_operand:DI 2 "gpr_or_int10_operand" "eJ,eJ")))
   (clobber (match_scratch:CC 3 "=t,t"))]
  ""
  "#"
  "reload_completed"
  [(match_dup 4)
   (match_dup 5)]
  "
{
  rtx parts[3][2];
  int op, part;

  for (op = 0; op < 3; op++)
    for (part = 0; part < 2; part++)
      parts[op][part] = simplify_gen_subreg (SImode, operands[op],
					     DImode, part * UNITS_PER_WORD);

  operands[4] = gen_adddi3_lower (parts[0][1], parts[1][1], parts[2][1],
				  operands[3]);
  operands[5] = gen_adddi3_upper (parts[0][0], parts[1][0], parts[2][0],
				  copy_rtx (operands[3]));
}"
  [(set_attr "length" "8")
   (set_attr "type" "multi")])

;; Subtraction  No need to worry about constants, since the compiler
;; canonicalizes them into adddi3's.
(define_insn_and_split "subdi3"
  [(set (match_operand:DI 0 "integer_register_operand" "=&e,e,e")
	(minus:DI (match_operand:DI 1 "integer_register_operand" "e,0,e")
		  (match_operand:DI 2 "integer_register_operand" "e,e,0")))
   (clobber (match_scratch:CC 3 "=t,t,t"))]
  ""
  "#"
  "reload_completed"
  [(match_dup 4)
   (match_dup 5)]
  "
{
  rtx op0_high = gen_highpart (SImode, operands[0]);
  rtx op1_high = gen_highpart (SImode, operands[1]);
  rtx op2_high = gen_highpart (SImode, operands[2]);
  rtx op0_low  = gen_lowpart (SImode, operands[0]);
  rtx op1_low  = gen_lowpart (SImode, operands[1]);
  rtx op2_low  = gen_lowpart (SImode, operands[2]);
  rtx op3 = operands[3];

  operands[4] = gen_subdi3_lower (op0_low, op1_low, op2_low, op3);
  operands[5] = gen_subdi3_upper (op0_high, op1_high, op2_high, op3);
}"
  [(set_attr "length" "8")
   (set_attr "type" "multi")])

;; Patterns for addsi3/subdi3 after splitting
(define_insn "adddi3_lower"
  [(set (match_operand:SI 0 "integer_register_operand" "=d")
	(plus:SI (match_operand:SI 1 "integer_register_operand" "d")
		 (match_operand:SI 2 "gpr_or_int10_operand" "dJ")))
   (set (match_operand:CC 3 "icc_operand" "=t")
	(compare:CC (plus:SI (match_dup 1)
			     (match_dup 2))
		    (const_int 0)))]
  ""
  "add%I2cc %1,%2,%0,%3"
  [(set_attr "length" "4")
   (set_attr "type" "int")])

(define_insn "adddi3_upper"
  [(set (match_operand:SI 0 "integer_register_operand" "=d")
	(plus:SI (match_operand:SI 1 "integer_register_operand" "d")
		 (plus:SI (match_operand:SI 2 "gpr_or_int10_operand" "dJ")
			  (match_operand:CC 3 "icc_operand" "t"))))]
  ""
  "addx%I2 %1,%2,%0,%3"
  [(set_attr "length" "4")
   (set_attr "type" "int")])

(define_insn "subdi3_lower"
  [(set (match_operand:SI 0 "integer_register_operand" "=d")
	(minus:SI (match_operand:SI 1 "integer_register_operand" "d")
		  (match_operand:SI 2 "integer_register_operand" "d")))
   (set (match_operand:CC 3 "icc_operand" "=t")
	(compare:CC (plus:SI (match_dup 1)
			     (match_dup 2))
		    (const_int 0)))]
  ""
  "subcc %1,%2,%0,%3"
  [(set_attr "length" "4")
   (set_attr "type" "int")])

(define_insn "subdi3_upper"
  [(set (match_operand:SI 0 "integer_register_operand" "=d")
	(minus:SI (match_operand:SI 1 "integer_register_operand" "d")
		  (minus:SI (match_operand:SI 2 "integer_register_operand" "d")
			    (match_operand:CC 3 "icc_operand" "t"))))]
  ""
  "subx %1,%2,%0,%3"
  [(set_attr "length" "4")
   (set_attr "type" "int")])

(define_insn_and_split "negdi2"
  [(set (match_operand:DI 0 "integer_register_operand" "=&e,e")
	(neg:DI (match_operand:DI 1 "integer_register_operand" "e,0")))
   (clobber (match_scratch:CC 2 "=t,t"))]
  ""
  "#"
  "reload_completed"
  [(match_dup 3)
   (match_dup 4)]
  "
{
  rtx op0_high = gen_highpart (SImode, operands[0]);
  rtx op1_high = gen_rtx_REG (SImode, GPR_FIRST);
  rtx op2_high = gen_highpart (SImode, operands[1]);
  rtx op0_low  = gen_lowpart (SImode, operands[0]);
  rtx op1_low  = op1_high;
  rtx op2_low  = gen_lowpart (SImode, operands[1]);
  rtx op3 = operands[2];

  operands[3] = gen_subdi3_lower (op0_low, op1_low, op2_low, op3);
  operands[4] = gen_subdi3_upper (op0_high, op1_high, op2_high, op3);
}"
  [(set_attr "length" "8")
   (set_attr "type" "multi")])

;; Multiplication (same size)
;; (define_insn "muldi3"
;;   [(set (match_operand:DI 0 "register_operand" "=r")
;; 	(mult:DI (match_operand:DI 1 "register_operand" "%r")
;; 		 (match_operand:DI 2 "nonmemory_operand" "ri")))]
;;   ""
;;   "muldi3 %0,%1,%2"
;;   [(set_attr "length" "4")])

;; Signed Division
;; (define_insn "divdi3"
;;   [(set (match_operand:DI 0 "register_operand" "=r")
;; 	(div:DI (match_operand:DI 1 "register_operand" "r")
;; 		(match_operand:DI 2 "nonmemory_operand" "ri")))]
;;   ""
;;   "divdi3 %0,%1,%2"
;;   [(set_attr "length" "4")])

;; Undsgned Division
;; (define_insn "udivdi3"
;;   [(set (match_operand:DI 0 "register_operand" "=r")
;; 	(udiv:DI (match_operand:DI 1 "register_operand" "r")
;; 		 (match_operand:DI 2 "nonmemory_operand" "ri")))]
;;   ""
;;   "udivdi3 %0,%1,%2"
;;   [(set_attr "length" "4")])

;; Negation
;; (define_insn "negdi2"
;;   [(set (match_operand:DI 0 "register_operand" "=r")
;; 	(neg:DI (match_operand:DI 1 "register_operand" "r")))]
;;   ""
;;   "negdi2 %0,%1"
;;   [(set_attr "length" "4")])

;; Find first one bit
;; (define_insn "ffsdi2"
;;   [(set (match_operand:DI 0 "register_operand" "=r")
;; 	(ffs:DI (match_operand:DI 1 "register_operand" "r")))]
;;   ""
;;   "ffsdi2 %0,%1"
;;   [(set_attr "length" "4")])


;; ::::::::::::::::::::
;; ::
;; :: 32-bit floating point arithmetic
;; ::
;; ::::::::::::::::::::

;; Addition
(define_insn "addsf3"
  [(set (match_operand:SF 0 "fpr_operand" "=f")
	(plus:SF (match_operand:SF 1 "fpr_operand" "%f")
		 (match_operand:SF 2 "fpr_operand" "f")))]
  "TARGET_HARD_FLOAT"
  "fadds %1,%2,%0"
  [(set_attr "length" "4")
   (set_attr "type" "fsadd")])

;; Subtraction
(define_insn "subsf3"
  [(set (match_operand:SF 0 "fpr_operand" "=f")
	(minus:SF (match_operand:SF 1 "fpr_operand" "f")
		  (match_operand:SF 2 "fpr_operand" "f")))]
  "TARGET_HARD_FLOAT"
  "fsubs %1,%2,%0"
  [(set_attr "length" "4")
   (set_attr "type" "fsadd")])

;; Multiplication
(define_insn "mulsf3"
  [(set (match_operand:SF 0 "fpr_operand" "=f")
	(mult:SF (match_operand:SF 1 "fpr_operand" "%f")
		 (match_operand:SF 2 "fpr_operand" "f")))]
  "TARGET_HARD_FLOAT"
  "fmuls %1,%2,%0"
  [(set_attr "length" "4")
   (set_attr "type" "fsmul")])

;; Multiplication with addition/subtraction
(define_insn "fmasf4"
  [(set (match_operand:SF 0 "fpr_operand" "=f")
	(fma:SF (match_operand:SF 1 "fpr_operand" "f")
		(match_operand:SF 2 "fpr_operand" "f")
		(match_operand:SF 3 "fpr_operand" "0")))]
  "TARGET_HARD_FLOAT && TARGET_MULADD"
  "fmadds %1,%2,%0"
  [(set_attr "length" "4")
   (set_attr "type" "fsmadd")])

(define_insn "fmssf4"
  [(set (match_operand:SF 0 "fpr_operand" "=f")
	(fma:SF (match_operand:SF 1 "fpr_operand" "f")
		(match_operand:SF 2 "fpr_operand" "f")
		(neg:SF (match_operand:SF 3 "fpr_operand" "0"))))]
  "TARGET_HARD_FLOAT && TARGET_MULADD"
  "fmsubs %1,%2,%0"
  [(set_attr "length" "4")
   (set_attr "type" "fsmadd")])

;; Division
(define_insn "divsf3"
  [(set (match_operand:SF 0 "fpr_operand" "=f")
	(div:SF (match_operand:SF 1 "fpr_operand" "f")
		(match_operand:SF 2 "fpr_operand" "f")))]
  "TARGET_HARD_FLOAT"
  "fdivs %1,%2,%0"
  [(set_attr "length" "4")
   (set_attr "type" "fsdiv")])

;; Negation
(define_insn "negsf2"
  [(set (match_operand:SF 0 "fpr_operand" "=f")
	(neg:SF (match_operand:SF 1 "fpr_operand" "f")))]
  "TARGET_HARD_FLOAT"
  "fnegs %1,%0"
  [(set_attr "length" "4")
   (set_attr "type" "fsconv")])

;; Absolute value
(define_insn "abssf2"
  [(set (match_operand:SF 0 "fpr_operand" "=f")
	(abs:SF (match_operand:SF 1 "fpr_operand" "f")))]
  "TARGET_HARD_FLOAT"
  "fabss %1,%0"
  [(set_attr "length" "4")
   (set_attr "type" "fsconv")])

;; Square root
(define_insn "sqrtsf2"
  [(set (match_operand:SF 0 "fpr_operand" "=f")
	(sqrt:SF (match_operand:SF 1 "fpr_operand" "f")))]
  "TARGET_HARD_FLOAT"
  "fsqrts %1,%0"
  [(set_attr "length" "4")
   (set_attr "type" "sqrt_single")])


;; ::::::::::::::::::::
;; ::
;; :: 64-bit floating point arithmetic
;; ::
;; ::::::::::::::::::::

;; Addition
(define_insn "adddf3"
  [(set (match_operand:DF 0 "even_fpr_operand" "=h")
	(plus:DF (match_operand:DF 1 "fpr_operand" "%h")
		 (match_operand:DF 2 "fpr_operand" "h")))]
  "TARGET_HARD_FLOAT && TARGET_DOUBLE"
  "faddd %1,%2,%0"
  [(set_attr "length" "4")
   (set_attr "type" "fdadd")])

;; Subtraction
(define_insn "subdf3"
  [(set (match_operand:DF 0 "even_fpr_operand" "=h")
	(minus:DF (match_operand:DF 1 "fpr_operand" "h")
		  (match_operand:DF 2 "fpr_operand" "h")))]
  "TARGET_HARD_FLOAT && TARGET_DOUBLE"
  "fsubd %1,%2,%0"
  [(set_attr "length" "4")
   (set_attr "type" "fdadd")])

;; Multiplication
(define_insn "muldf3"
  [(set (match_operand:DF 0 "even_fpr_operand" "=h")
	(mult:DF (match_operand:DF 1 "fpr_operand" "%h")
		 (match_operand:DF 2 "fpr_operand" "h")))]
  "TARGET_HARD_FLOAT && TARGET_DOUBLE"
  "fmuld %1,%2,%0"
  [(set_attr "length" "4")
   (set_attr "type" "fdmul")])

;; Multiplication with addition/subtraction
(define_insn "*muladddf4"
  [(set (match_operand:DF 0 "fpr_operand" "=f")
	(plus:DF (mult:DF (match_operand:DF 1 "fpr_operand" "%f")
			  (match_operand:DF 2 "fpr_operand" "f"))
		 (match_operand:DF 3 "fpr_operand" "0")))]
  "TARGET_HARD_FLOAT && TARGET_DOUBLE && TARGET_MULADD"
  "fmaddd %1,%2,%0"
  [(set_attr "length" "4")
   (set_attr "type" "fdmadd")])

(define_insn "*mulsubdf4"
  [(set (match_operand:DF 0 "fpr_operand" "=f")
	(minus:DF (mult:DF (match_operand:DF 1 "fpr_operand" "%f")
			   (match_operand:DF 2 "fpr_operand" "f"))
		  (match_operand:DF 3 "fpr_operand" "0")))]
  "TARGET_HARD_FLOAT && TARGET_DOUBLE && TARGET_MULADD"
  "fmsubd %1,%2,%0"
  [(set_attr "length" "4")
   (set_attr "type" "fdmadd")])

;; Division
(define_insn "divdf3"
  [(set (match_operand:DF 0 "even_fpr_operand" "=h")
	(div:DF (match_operand:DF 1 "fpr_operand" "h")
		(match_operand:DF 2 "fpr_operand" "h")))]
  "TARGET_HARD_FLOAT && TARGET_DOUBLE"
  "fdivd %1,%2,%0"
  [(set_attr "length" "4")
   (set_attr "type" "fddiv")])

;; Negation
(define_insn "negdf2"
  [(set (match_operand:DF 0 "even_fpr_operand" "=h")
	(neg:DF (match_operand:DF 1 "fpr_operand" "h")))]
  "TARGET_HARD_FLOAT && TARGET_DOUBLE"
  "fnegd %1,%0"
  [(set_attr "length" "4")
   (set_attr "type" "fdconv")])

;; Absolute value
(define_insn "absdf2"
  [(set (match_operand:DF 0 "even_fpr_operand" "=h")
	(abs:DF (match_operand:DF 1 "fpr_operand" "h")))]
  "TARGET_HARD_FLOAT && TARGET_DOUBLE"
  "fabsd %1,%0"
  [(set_attr "length" "4")
   (set_attr "type" "fdconv")])

;; Square root
(define_insn "sqrtdf2"
  [(set (match_operand:DF 0 "even_fpr_operand" "=h")
	(sqrt:DF (match_operand:DF 1 "fpr_operand" "h")))]
  "TARGET_HARD_FLOAT && TARGET_DOUBLE"
  "fsqrtd %1,%0"
  [(set_attr "length" "4")
   (set_attr "type" "sqrt_double")])


;; ::::::::::::::::::::
;; ::
;; :: 32-bit Integer Shifts and Rotates
;; ::
;; ::::::::::::::::::::

;; Arithmetic Shift Left
(define_insn "ashlsi3"
  [(set (match_operand:SI 0 "integer_register_operand" "=d,d")
	(ashift:SI (match_operand:SI 1 "integer_register_operand" "d,d")
		   (match_operand:SI 2 "gpr_or_int12_operand" "d,NOP")))]
  ""
  "sll%I2 %1,%2,%0"
  [(set_attr "length" "4")
   (set_attr "type" "int")])

;; Arithmetic Shift Right
(define_insn "ashrsi3"
  [(set (match_operand:SI 0 "integer_register_operand" "=d,d")
	(ashiftrt:SI (match_operand:SI 1 "integer_register_operand" "d,d")
		     (match_operand:SI 2 "gpr_or_int12_operand" "d,NOP")))]
  ""
  "sra%I2 %1, %2, %0"
  [(set_attr "length" "4")
   (set_attr "type" "int")])

;; Logical Shift Right
(define_insn "lshrsi3"
  [(set (match_operand:SI 0 "integer_register_operand" "=d,d")
	(lshiftrt:SI (match_operand:SI 1 "integer_register_operand" "d,d")
		     (match_operand:SI 2 "gpr_or_int12_operand" "d,NOP")))]
  ""
  "srl%I2 %1, %2, %0"
  [(set_attr "length" "4")
   (set_attr "type" "int")])

;; Rotate Left
;; (define_insn "rotlsi3"
;;   [(set (match_operand:SI 0 "register_operand" "=r")
;; 	(rotate:SI (match_operand:SI 1 "register_operand" "r")
;; 		   (match_operand:SI 2 "nonmemory_operand" "ri")))]
;;   ""
;;   "rotlsi3 %0,%1,%2"
;;   [(set_attr "length" "4")])

;; Rotate Right
;; (define_insn "rotrsi3"
;;   [(set (match_operand:SI 0 "register_operand" "=r")
;; 	(rotatert:SI (match_operand:SI 1 "register_operand" "r")
;; 		     (match_operand:SI 2 "nonmemory_operand" "ri")))]
;;   ""
;;   "rotrsi3 %0,%1,%2"
;;   [(set_attr "length" "4")])


;; ::::::::::::::::::::
;; ::
;; :: 64-bit Integer Shifts and Rotates
;; ::
;; ::::::::::::::::::::

;; Arithmetic Shift Left
;; (define_insn "ashldi3"
;;   [(set (match_operand:DI 0 "register_operand" "=r")
;; 	(ashift:DI (match_operand:DI 1 "register_operand" "r")
;; 		   (match_operand:SI 2 "nonmemory_operand" "ri")))]
;;   ""
;;   "ashldi3 %0,%1,%2"
;;   [(set_attr "length" "4")])

;; Arithmetic Shift Right
;; (define_insn "ashrdi3"
;;   [(set (match_operand:DI 0 "register_operand" "=r")
;; 	(ashiftrt:DI (match_operand:DI 1 "register_operand" "r")
;; 		     (match_operand:SI 2 "nonmemory_operand" "ri")))]
;;   ""
;;   "ashrdi3 %0,%1,%2"
;;   [(set_attr "length" "4")])

;; Logical Shift Right
;; (define_insn "lshrdi3"
;;   [(set (match_operand:DI 0 "register_operand" "=r")
;; 	(lshiftrt:DI (match_operand:DI 1 "register_operand" "r")
;; 		     (match_operand:SI 2 "nonmemory_operand" "ri")))]
;;   ""
;;   "lshrdi3 %0,%1,%2"
;;   [(set_attr "length" "4")])

;; Rotate Left
;; (define_insn "rotldi3"
;;   [(set (match_operand:DI 0 "register_operand" "=r")
;; 	(rotate:DI (match_operand:DI 1 "register_operand" "r")
;; 		   (match_operand:SI 2 "nonmemory_operand" "ri")))]
;;   ""
;;   "rotldi3 %0,%1,%2"
;;   [(set_attr "length" "4")])

;; Rotate Right
;; (define_insn "rotrdi3"
;;   [(set (match_operand:DI 0 "register_operand" "=r")
;; 	(rotatert:DI (match_operand:DI 1 "register_operand" "r")
;; 		     (match_operand:SI 2 "nonmemory_operand" "ri")))]
;;   ""
;;   "rotrdi3 %0,%1,%2"
;;   [(set_attr "length" "4")])


;; ::::::::::::::::::::
;; ::
;; :: 32-Bit Integer Logical operations
;; ::
;; ::::::::::::::::::::

;; Logical AND, 32-bit integers
(define_insn "andsi3_media"
  [(set (match_operand:SI 0 "gpr_or_fpr_operand" "=d,f")
	(and:SI (match_operand:SI 1 "gpr_or_fpr_operand" "%d,f")
		(match_operand:SI 2 "gpr_fpr_or_int12_operand" "dNOP,f")))]
  "TARGET_MEDIA"
  "@
   and%I2 %1, %2, %0
   mand %1, %2, %0"
  [(set_attr "length" "4")
   (set_attr "type" "int,mlogic")])

(define_insn "andsi3_nomedia"
  [(set (match_operand:SI 0 "integer_register_operand" "=d")
	(and:SI (match_operand:SI 1 "integer_register_operand" "%d")
		(match_operand:SI 2 "gpr_or_int12_operand" "dNOP")))]
  "!TARGET_MEDIA"
  "and%I2 %1, %2, %0"
  [(set_attr "length" "4")
   (set_attr "type" "int")])

(define_expand "andsi3"
  [(set (match_operand:SI 0 "gpr_or_fpr_operand" "")
	(and:SI (match_operand:SI 1 "gpr_or_fpr_operand" "")
		(match_operand:SI 2 "gpr_fpr_or_int12_operand" "")))]
  ""
  "")

;; Inclusive OR, 32-bit integers
(define_insn "iorsi3_media"
  [(set (match_operand:SI 0 "gpr_or_fpr_operand" "=d,f")
	(ior:SI (match_operand:SI 1 "gpr_or_fpr_operand" "%d,f")
		(match_operand:SI 2 "gpr_fpr_or_int12_operand" "dNOP,f")))]
  "TARGET_MEDIA"
  "@
   or%I2 %1, %2, %0
   mor %1, %2, %0"
  [(set_attr "length" "4")
   (set_attr "type" "int,mlogic")])

(define_insn "iorsi3_nomedia"
  [(set (match_operand:SI 0 "integer_register_operand" "=d")
	(ior:SI (match_operand:SI 1 "integer_register_operand" "%d")
		(match_operand:SI 2 "gpr_or_int12_operand" "dNOP")))]
  "!TARGET_MEDIA"
  "or%I2 %1, %2, %0"
  [(set_attr "length" "4")
   (set_attr "type" "int")])

(define_expand "iorsi3"
  [(set (match_operand:SI 0 "gpr_or_fpr_operand" "")
	(ior:SI (match_operand:SI 1 "gpr_or_fpr_operand" "")
		(match_operand:SI 2 "gpr_fpr_or_int12_operand" "")))]
  ""
  "")

;; Exclusive OR, 32-bit integers
(define_insn "xorsi3_media"
  [(set (match_operand:SI 0 "gpr_or_fpr_operand" "=d,f")
	(xor:SI (match_operand:SI 1 "gpr_or_fpr_operand" "%d,f")
		(match_operand:SI 2 "gpr_fpr_or_int12_operand" "dNOP,f")))]
  "TARGET_MEDIA"
  "@
   xor%I2 %1, %2, %0
   mxor %1, %2, %0"
  [(set_attr "length" "4")
   (set_attr "type" "int,mlogic")])

(define_insn "xorsi3_nomedia"
  [(set (match_operand:SI 0 "integer_register_operand" "=d")
	(xor:SI (match_operand:SI 1 "integer_register_operand" "%d")
		(match_operand:SI 2 "gpr_or_int12_operand" "dNOP")))]
  "!TARGET_MEDIA"
  "xor%I2 %1, %2, %0"
  [(set_attr "length" "4")
   (set_attr "type" "int")])

(define_expand "xorsi3"
  [(set (match_operand:SI 0 "gpr_or_fpr_operand" "")
	(xor:SI (match_operand:SI 1 "gpr_or_fpr_operand" "")
		(match_operand:SI 2 "gpr_fpr_or_int12_operand" "")))]
  ""
  "")

;; One's complement, 32-bit integers
(define_insn "one_cmplsi2_media"
  [(set (match_operand:SI 0 "gpr_or_fpr_operand" "=d,f")
	(not:SI (match_operand:SI 1 "gpr_or_fpr_operand" "d,f")))]
  "TARGET_MEDIA"
  "@
   not %1, %0
   mnot %1, %0"
  [(set_attr "length" "4")
   (set_attr "type" "int,mlogic")])

(define_insn "one_cmplsi2_nomedia"
  [(set (match_operand:SI 0 "integer_register_operand" "=d")
	(not:SI (match_operand:SI 1 "integer_register_operand" "d")))]
  "!TARGET_MEDIA"
  "not %1,%0"
  [(set_attr "length" "4")
   (set_attr "type" "int")])

(define_expand "one_cmplsi2"
  [(set (match_operand:SI 0 "gpr_or_fpr_operand" "")
	(not:SI (match_operand:SI 1 "gpr_or_fpr_operand" "")))]
  ""
  "")


;; ::::::::::::::::::::
;; ::
;; :: 64-Bit Integer Logical operations
;; ::
;; ::::::::::::::::::::

;; Logical AND, 64-bit integers
;; (define_insn "anddi3"
;;   [(set (match_operand:DI 0 "register_operand" "=r")
;; 	(and:DI (match_operand:DI 1 "register_operand" "%r")
;; 		(match_operand:DI 2 "nonmemory_operand" "ri")))]
;;   ""
;;   "anddi3 %0,%1,%2"
;;   [(set_attr "length" "4")])

;; Inclusive OR, 64-bit integers
;; (define_insn "iordi3"
;;   [(set (match_operand:DI 0 "register_operand" "=r")
;; 	(ior:DI (match_operand:DI 1 "register_operand" "%r")
;; 		(match_operand:DI 2 "nonmemory_operand" "ri")))]
;;   ""
;;   "iordi3 %0,%1,%2"
;;   [(set_attr "length" "4")])

;; Exclusive OR, 64-bit integers
;; (define_insn "xordi3"
;;   [(set (match_operand:DI 0 "register_operand" "=r")
;; 	(xor:DI (match_operand:DI 1 "register_operand" "%r")
;; 		(match_operand:DI 2 "nonmemory_operand" "ri")))]
;;   ""
;;   "xordi3 %0,%1,%2"
;;   [(set_attr "length" "4")])

;; One's complement, 64-bit integers
;; (define_insn "one_cmpldi2"
;;   [(set (match_operand:DI 0 "register_operand" "=r")
;; 	(not:DI (match_operand:DI 1 "register_operand" "r")))]
;;   ""
;;   "notdi3 %0,%1"
;;   [(set_attr "length" "4")])


;; ::::::::::::::::::::
;; ::
;; :: Combination of integer operation with comparison
;; ::
;; ::::::::::::::::::::

(define_insn "*combo_intop_compare1"
  [(set (match_operand:CC_NZ 0 "icc_operand" "=t")
	(compare:CC_NZ
	 (match_operator:SI 1 "intop_compare_operator"
		       [(match_operand:SI 2 "integer_register_operand" "d")
			(match_operand:SI 3 "gpr_or_int10_operand" "dJ")])
	 (const_int 0)))]
  ""
  "%O1%I3cc %2, %3, %., %0"
  [(set_attr "type" "int")
   (set_attr "length" "4")])

(define_insn "*combo_intop_compare2"
  [(set (match_operand:CC_NZ 0 "icc_operand" "=t")
	(compare:CC_NZ
	 (match_operator:SI 1 "intop_compare_operator"
			[(match_operand:SI 2 "integer_register_operand" "d")
			 (match_operand:SI 3 "gpr_or_int10_operand" "dJ")])
	 (const_int 0)))
   (set (match_operand:SI 4 "integer_register_operand" "=d")
	(match_operator:SI 5 "intop_compare_operator"
			   [(match_dup 2)
			    (match_dup 3)]))]
  "GET_CODE (operands[1]) == GET_CODE (operands[5])"
  "%O1%I3cc %2, %3, %4, %0"
  [(set_attr "type" "int")
   (set_attr "length" "4")])

;; ::::::::::::::::::::
;; ::
;; :: Comparisons
;; ::
;; ::::::::::::::::::::

;; The comparisons are generated by the branch and/or scc operations

(define_insn "cmpsi_cc"
  [(set (match_operand:CC 0 "icc_operand" "=t,t")
	(compare:CC (match_operand:SI 1 "integer_register_operand" "d,d")
		    (match_operand:SI 2 "gpr_or_int10_operand" "d,J")))]
  ""
  "cmp%I2 %1,%2,%0"
  [(set_attr "length" "4")
   (set_attr "type" "int")])

(define_insn "*cmpsi_cc_uns"
  [(set (match_operand:CC_UNS 0 "icc_operand" "=t,t")
	(compare:CC_UNS (match_operand:SI 1 "integer_register_operand" "d,d")
			(match_operand:SI 2 "gpr_or_int10_operand" "d,J")))]
  ""
  "cmp%I2 %1,%2,%0"
  [(set_attr "length" "4")
   (set_attr "type" "int")])

;; The only requirement for a CC_NZmode GPR or memory value is that
;; comparing it against zero must set the Z and N flags appropriately.
;; The source operand is therefore a valid CC_NZmode value.
(define_insn "*cmpsi_cc_nz"
  [(set (match_operand:CC_NZ 0 "nonimmediate_operand" "=t,d,m")
	(compare:CC_NZ (match_operand:SI 1 "integer_register_operand" "d,d,d")
		       (const_int 0)))]
  ""
  "@
   cmpi %1, #0, %0
   mov %1, %0
   st%I0%U0 %1, %M0"
  [(set_attr "length" "4,4,4")
   (set_attr "type" "int,int,gstore")])

(define_insn "*cmpsf_cc_fp"
  [(set (match_operand:CC_FP 0 "fcc_operand" "=u")
	(compare:CC_FP (match_operand:SF 1 "fpr_operand" "f")
		       (match_operand:SF 2 "fpr_operand" "f")))]
  "TARGET_HARD_FLOAT"
  "fcmps %1,%2,%0"
  [(set_attr "length" "4")
   (set_attr "type" "fscmp")])

(define_insn "*cmpdf_cc_fp"
  [(set (match_operand:CC_FP 0 "fcc_operand" "=u")
	(compare:CC_FP (match_operand:DF 1 "even_fpr_operand" "h")
		       (match_operand:DF 2 "even_fpr_operand" "h")))]
  "TARGET_HARD_FLOAT && TARGET_DOUBLE"
  "fcmpd %1,%2,%0"
  [(set_attr "length" "4")
   (set_attr "type" "fdcmp")])


;; ::::::::::::::::::::
;; ::
;; :: Branches
;; ::
;; ::::::::::::::::::::

;; Define_expands called by the machine independent part of the compiler
;; to allocate a new comparison register.

(define_expand "cbranchdf4"
  [(use (match_operator 0 "ordered_comparison_operator"
         [(match_operand:DF 1 "fpr_operand" "")
          (match_operand:DF 2 "fpr_operand" "")]))
   (use (match_operand 3 ""))]
  "TARGET_HARD_FLOAT && TARGET_DOUBLE"
  { if (frv_emit_cond_branch (operands)) DONE; gcc_unreachable (); })

(define_expand "cbranchsf4"
  [(use (match_operator 0 "ordered_comparison_operator"
         [(match_operand:SF 1 "fpr_operand" "")
          (match_operand:SF 2 "fpr_operand" "")]))
   (use (match_operand 3 ""))]
  "TARGET_HARD_FLOAT"
  { if (frv_emit_cond_branch (operands)) DONE; gcc_unreachable (); })

(define_expand "cbranchsi4"
  [(use (match_operator 0 "ordered_comparison_operator"
         [(match_operand:SI 1 "integer_register_operand" "")
          (match_operand:SI 2 "gpr_or_int10_operand" "")]))
   (use (match_operand 3 ""))]
  ""
  { if (frv_emit_cond_branch (operands)) DONE; gcc_unreachable (); })

;; Actual branches.  We must allow for the (label_ref) and the (pc) to be
;; swapped.  If they are swapped, it reverses the sense of the branch.
;;
;; Note - unlike the define expands above, these patterns can be amalgamated
;; into one pattern for branch-if-true and one for branch-if-false.  This does
;; require an operand operator to select the correct branch mnemonic.
;;
;; If a fixed condition code register is being used, (as opposed to, say,
;; using cc0), then the expands could look like this:
;;
;; (define_insn "*branch_true"
;;   [(set (pc)
;; 	(if_then_else (match_operator:CC 0 "comparison_operator"
;; 					 [(reg:CC <number_of_CC_register>)
;; 					  (const_int 0)])
;; 		      (label_ref (match_operand 1 "" ""))
;; 		      (pc)))]
;;   ""
;;   "b%B0 %1"
;;   [(set_attr "length" "4")]
;; )
;;
;; In the above example the %B is a directive to frv_print_operand()
;; to decode and print the correct branch mnemonic.

(define_insn "*branch_int_true"
  [(set (pc)
	(if_then_else (match_operator 0 "integer_relational_operator"
				      [(match_operand 1 "icc_operand" "t")
				       (const_int 0)])
		      (label_ref (match_operand 2 "" ""))
		      (pc)))]
  ""
  "*
{
  if (get_attr_length (insn) == 4)
    return \"b%c0 %1,%#,%l2\";
  else
    return \"b%C0 %1,%#,1f\;call %l2\\n1:\";
}"
  [(set (attr "length")
	(if_then_else
	    (and (ge (minus (match_dup 2) (pc)) (const_int -32768))
		 (le (minus (match_dup 2) (pc)) (const_int 32764)))
	    (const_int 4)
	    (const_int 8)))
   (set (attr "far_jump")
        (if_then_else
	    (eq_attr "length" "4")
	    (const_string "no")
	    (const_string "yes")))
   (set (attr "type")
	(if_then_else
	    (eq_attr "length" "4")
	    (const_string "branch")
	    (const_string "multi")))])

(define_insn "*branch_int_false"
  [(set (pc)
	(if_then_else (match_operator 0 "integer_relational_operator"
				      [(match_operand 1 "icc_operand" "t")
				       (const_int 0)])
		      (pc)
		      (label_ref (match_operand 2 "" ""))))]
  ""
  "*
{
  if (get_attr_length (insn) == 4)
    return \"b%C0 %1,%#,%l2\";
  else
    return \"b%c0 %1,%#,1f\;call %l2\\n1:\";
}"
  [(set (attr "length")
	(if_then_else
	    (and (ge (minus (match_dup 2) (pc)) (const_int -32768))
		 (le (minus (match_dup 2) (pc)) (const_int 32764)))
	    (const_int 4)
	    (const_int 8)))
   (set (attr "far_jump")
        (if_then_else
	    (eq_attr "length" "4")
	    (const_string "no")
	    (const_string "yes")))
   (set (attr "type")
	(if_then_else
	    (eq_attr "length" "4")
	    (const_string "branch")
	    (const_string "multi")))])

(define_insn "*branch_fp_true"
  [(set (pc)
	(if_then_else (match_operator:CC_FP 0 "float_relational_operator"
					    [(match_operand 1 "fcc_operand" "u")
					     (const_int 0)])
		      (label_ref (match_operand 2 "" ""))
		      (pc)))]
  ""
  "*
{
  if (get_attr_length (insn) == 4)
    return \"fb%f0 %1,%#,%l2\";
  else
    return \"fb%F0 %1,%#,1f\;call %l2\\n1:\";
}"
  [(set (attr "length")
	(if_then_else
	    (and (ge (minus (match_dup 2) (pc)) (const_int -32768))
		 (le (minus (match_dup 2) (pc)) (const_int 32764)))
	    (const_int 4)
	    (const_int 8)))
   (set (attr "far_jump")
        (if_then_else
	    (eq_attr "length" "4")
	    (const_string "no")
	    (const_string "yes")))
   (set (attr "type")
	(if_then_else
	    (eq_attr "length" "4")
	    (const_string "branch")
	    (const_string "multi")))])

(define_insn "*branch_fp_false"
  [(set (pc)
	(if_then_else (match_operator:CC_FP 0 "float_relational_operator"
					    [(match_operand 1 "fcc_operand" "u")
					     (const_int 0)])
		      (pc)
		      (label_ref (match_operand 2 "" ""))))]
  ""
  "*
{
  if (get_attr_length (insn) == 4)
    return \"fb%F0 %1,%#,%l2\";
  else
    return \"fb%f0 %1,%#,1f\;call %l2\\n1:\";
}"
  [(set (attr "length")
	(if_then_else
	    (and (ge (minus (match_dup 2) (pc)) (const_int -32768))
		 (le (minus (match_dup 2) (pc)) (const_int 32764)))
	    (const_int 4)
	    (const_int 8)))
   (set (attr "far_jump")
        (if_then_else
	    (eq_attr "length" "4")
	    (const_string "no")
	    (const_string "yes")))
   (set (attr "type")
	(if_then_else
	    (eq_attr "length" "4")
	    (const_string "branch")
	    (const_string "multi")))])


;; ::::::::::::::::::::
;; ::
;; :: Set flag operations
;; ::
;; ::::::::::::::::::::

;; Define_expands called by the machine independent part of the compiler
;; to allocate a new comparison register

(define_expand "cstoredf4"
  [(use (match_operator:SI 1 "ordered_comparison_operator"
         [(match_operand:DF 2 "fpr_operand")
          (match_operand:DF 3 "fpr_operand")]))
   (clobber (match_operand:SI 0 "register_operand"))]
  "TARGET_HARD_FLOAT && TARGET_DOUBLE"
  { if (frv_emit_scc (operands)) DONE; else FAIL; })

(define_expand "cstoresf4"
  [(use (match_operator:SI 1 "ordered_comparison_operator"
         [(match_operand:SF 2 "fpr_operand")
          (match_operand:SF 3 "fpr_operand")]))
   (clobber (match_operand:SI 0 "register_operand"))]
  "TARGET_HARD_FLOAT"
  { if (frv_emit_scc (operands)) DONE; else FAIL; })

(define_expand "cstoresi4"
  [(use (match_operator:SI 1 "ordered_comparison_operator"
         [(match_operand:SI 2 "integer_register_operand")
          (match_operand:SI 3 "gpr_or_int10_operand")]))
   (clobber (match_operand:SI 0 "register_operand"))]
  ""
  { if (frv_emit_scc (operands)) DONE; else FAIL; })

(define_insn "*scc_int"
  [(set (match_operand:SI 0 "integer_register_operand" "=d")
	(match_operator:SI 1 "integer_relational_operator"
			   [(match_operand 2 "icc_operand" "t")
			    (const_int 0)]))
   (clobber (match_operand:CC_CCR 3 "icr_operand" "=v"))]
  ""
  "#"
  [(set_attr "length" "12")
   (set_attr "type" "multi")])

(define_insn "*scc_float"
  [(set (match_operand:SI 0 "integer_register_operand" "=d")
	(match_operator:SI 1 "float_relational_operator"
			   [(match_operand:CC_FP 2 "fcc_operand" "u")
			    (const_int 0)]))
   (clobber (match_operand:CC_CCR 3 "fcr_operand" "=w"))]
  ""
  "#"
  [(set_attr "length" "12")
   (set_attr "type" "multi")])

;; XXX -- add reload_completed to the splits, because register allocation
;; currently isn't ready to see cond_exec packets.
(define_split
  [(set (match_operand:SI 0 "integer_register_operand" "")
	(match_operator:SI 1 "relational_operator"
			   [(match_operand 2 "cc_operand" "")
			    (const_int 0)]))
   (clobber (match_operand 3 "cr_operand" ""))]
  "reload_completed"
  [(match_dup 4)]
  "operands[4] = frv_split_scc (operands[0], operands[1], operands[2],
				operands[3], (HOST_WIDE_INT) 1);")

(define_insn "*scc_neg1_int"
  [(set (match_operand:SI 0 "integer_register_operand" "=d")
	(neg:SI (match_operator:SI 1 "integer_relational_operator"
				   [(match_operand 2 "icc_operand" "t")
				    (const_int 0)])))
   (clobber (match_operand:CC_CCR 3 "icr_operand" "=v"))]
  ""
  "#"
  [(set_attr "length" "12")
   (set_attr "type" "multi")])

(define_insn "*scc_neg1_float"
  [(set (match_operand:SI 0 "integer_register_operand" "=d")
	(neg:SI (match_operator:SI 1 "float_relational_operator"
				   [(match_operand:CC_FP 2 "fcc_operand" "u")
				    (const_int 0)])))
   (clobber (match_operand:CC_CCR 3 "fcr_operand" "=w"))]
  ""
  "#"
  [(set_attr "length" "12")
   (set_attr "type" "multi")])

(define_split
  [(set (match_operand:SI 0 "integer_register_operand" "")
	(neg:SI (match_operator:SI 1 "relational_operator"
				   [(match_operand 2 "cc_operand" "")
				    (const_int 0)])))
   (clobber (match_operand 3 "cr_operand" ""))]
  "reload_completed"
  [(match_dup 4)]
  "operands[4] = frv_split_scc (operands[0], operands[1], operands[2],
				operands[3], (HOST_WIDE_INT) -1);")


;; ::::::::::::::::::::
;; ::
;; :: Conditionally executed instructions
;; ::
;; ::::::::::::::::::::

;; Convert ICC/FCC comparison into CCR bits so we can do conditional execution
(define_insn "*ck_signed"
  [(set (match_operand:CC_CCR 0 "icr_operand" "=v")
	(match_operator:CC_CCR 1 "integer_relational_operator"
			       [(match_operand 2 "icc_operand" "t")
				(const_int 0)]))]
  ""
  "ck%c1 %2, %0"
  [(set_attr "length" "4")
   (set_attr "type" "ccr")])

(define_insn "*fck_float"
  [(set (match_operand:CC_CCR 0 "fcr_operand" "=w")
	(match_operator:CC_CCR 1 "float_relational_operator"
			       [(match_operand:CC_FP 2 "fcc_operand" "u")
				(const_int 0)]))]
  "TARGET_HAS_FPRS"
  "fck%c1 %2, %0"
  [(set_attr "length" "4")
   (set_attr "type" "ccr")])

;; Conditionally convert ICC/FCC comparison into CCR bits to provide && and ||
;; tests in conditional execution
(define_insn "cond_exec_ck"
  [(set (match_operand:CC_CCR 0 "cr_operand" "=v,w")
	(if_then_else:CC_CCR (match_operator 1 "ccr_eqne_operator"
					     [(match_operand 2 "cr_operand" "C,C")
					      (const_int 0)])
			     (match_operator 3 "relational_operator"
					     [(match_operand 4 "cc_operand" "t,u")
					      (const_int 0)])
			     (const_int 0)))]
  ""
  "@
   cck%c3 %4, %0, %2, %e1
   cfck%f3 %4, %0, %2, %e1"
  [(set_attr "length" "4")
   (set_attr "type" "ccr")])

;; Conditionally set a register to either 0 or another register
(define_insn "*cond_exec_movqi"
  [(cond_exec
    (match_operator 0 "ccr_eqne_operator"
		    [(match_operand 1 "cr_operand" "C,C,C,C,C,C")
		     (const_int 0)])
    (set (match_operand:QI 2 "condexec_dest_operand" "=d,d,U,?f,?f,?d")
	 (match_operand:QI 3 "condexec_source_operand" "dO,U,dO,f,d,f")))]
  "register_operand(operands[2], QImode) || reg_or_0_operand (operands[3], QImode)"
  "* return output_condmove_single (operands, insn);"
  [(set_attr "length" "4")
   (set_attr "type" "int,gload,gstore,fsconv,movgf,movfg")])

(define_insn "*cond_exec_movhi"
  [(cond_exec
    (match_operator 0 "ccr_eqne_operator"
		    [(match_operand 1 "cr_operand" "C,C,C,C,C,C")
		     (const_int 0)])
    (set (match_operand:HI 2 "condexec_dest_operand" "=d,d,U,?f,?f,?d")
	 (match_operand:HI 3 "condexec_source_operand" "dO,U,dO,f,d,f")))]
  "register_operand(operands[2], HImode) || reg_or_0_operand (operands[3], HImode)"
  "* return output_condmove_single (operands, insn);"
  [(set_attr "length" "4")
   (set_attr "type" "int,gload,gstore,fsconv,movgf,movfg")])

(define_insn "*cond_exec_movsi"
  [(cond_exec
    (match_operator 0 "ccr_eqne_operator"
		    [(match_operand 1 "cr_operand" "C,C,C,C,C,C,C,C")
		     (const_int 0)])
    (set (match_operand:SI 2 "condexec_dest_operand" "=d,d,U,?f,?f,?d,?f,?m")
	 (match_operand:SI 3 "condexec_source_operand" "dO,U,dO,f,d,f,m,f")))]
  "register_operand(operands[2], SImode) || reg_or_0_operand (operands[3], SImode)"
  "* return output_condmove_single (operands, insn);"
  [(set_attr "length" "4")
   (set_attr "type" "int,gload,gstore,fsconv,movgf,movfg,fload,fstore")])


(define_insn "*cond_exec_movsf_has_fprs"
  [(cond_exec
    (match_operator 0 "ccr_eqne_operator"
		    [(match_operand 1 "cr_operand" "C,C,C,C,C,C,C,C,C,C")
		     (const_int 0)])
    (set (match_operand:SF 2 "condexec_dest_operand" "=f,?d,?d,?f,f,f,?d,U,?U,U")
	 (match_operand:SF 3 "condexec_source_operand" "f,d,f,d,G,U,U,f,d,G")))]
  "TARGET_HAS_FPRS"
  "* return output_condmove_single (operands, insn);"
  [(set_attr "length" "4")
   (set_attr "type" "fsconv,int,movgf,movfg,movgf,fload,gload,fstore,gstore,gstore")])

(define_insn "*cond_exec_movsf_no_fprs"
  [(cond_exec
    (match_operator 0 "ccr_eqne_operator"
		    [(match_operand 1 "cr_operand" "C,C,C")
		     (const_int 0)])
    (set (match_operand:SF 2 "condexec_dest_operand" "=d,d,U")
	 (match_operand:SF 3 "condexec_source_operand" "d,U,dG")))]
  "! TARGET_HAS_FPRS"
  "* return output_condmove_single (operands, insn);"
  [(set_attr "length" "4")
   (set_attr "type" "int,gload,gstore")])

(define_insn "*cond_exec_si_binary1"
  [(cond_exec
    (match_operator 0 "ccr_eqne_operator"
		    [(match_operand 1 "cr_operand" "C")
		     (const_int 0)])
    (set (match_operand:SI 2 "integer_register_operand" "=d")
	 (match_operator:SI 3 "condexec_si_binary_operator"
			    [(match_operand:SI 4 "integer_register_operand" "d")
			     (match_operand:SI 5 "integer_register_operand" "d")])))]
  ""
  "*
{
  switch (GET_CODE (operands[3]))
    {
      case PLUS:     return \"cadd %4, %z5, %2, %1, %e0\";
      case MINUS:    return \"csub %4, %z5, %2, %1, %e0\";
      case AND:      return \"cand %4, %z5, %2, %1, %e0\";
      case IOR:      return \"cor %4, %z5, %2, %1, %e0\";
      case XOR:      return \"cxor %4, %z5, %2, %1, %e0\";
      case ASHIFT:   return \"csll %4, %z5, %2, %1, %e0\";
      case ASHIFTRT: return \"csra %4, %z5, %2, %1, %e0\";
      case LSHIFTRT: return \"csrl %4, %z5, %2, %1, %e0\";
      default:       gcc_unreachable ();
    }
}"
  [(set_attr "length" "4")
   (set_attr "type" "int")])

(define_insn "*cond_exec_si_binary2"
  [(cond_exec
    (match_operator 0 "ccr_eqne_operator"
		    [(match_operand 1 "cr_operand" "C")
		     (const_int 0)])
    (set (match_operand:SI 2 "fpr_operand" "=f")
	 (match_operator:SI 3 "condexec_si_media_operator"
			    [(match_operand:SI 4 "fpr_operand" "f")
			     (match_operand:SI 5 "fpr_operand" "f")])))]
  "TARGET_MEDIA"
  "*
{
  switch (GET_CODE (operands[3]))
    {
      case AND: return \"cmand %4, %5, %2, %1, %e0\";
      case IOR: return \"cmor %4, %5, %2, %1, %e0\";
      case XOR: return \"cmxor %4, %5, %2, %1, %e0\";
      default:  gcc_unreachable ();
    }
}"
  [(set_attr "length" "4")
   (set_attr "type" "mlogic")])

;; Note, flow does not (currently) know how to handle an operation that uses
;; only part of the hard registers allocated for a multiregister value, such as
;; DImode in this case if the user is only interested in the lower 32-bits.  So
;; we emit a USE of the entire register after the csmul instruction so it won't
;; get confused.  See frv_ifcvt_modify_insn for more details.

(define_insn "*cond_exec_si_smul"
  [(cond_exec
    (match_operator 0 "ccr_eqne_operator"
		    [(match_operand 1 "cr_operand" "C")
		     (const_int 0)])
    (set (match_operand:DI 2 "even_gpr_operand" "=e")
	 (mult:DI (sign_extend:DI (match_operand:SI 3 "integer_register_operand" "%d"))
		  (sign_extend:DI (match_operand:SI 4 "integer_register_operand" "d")))))]
  ""
  "csmul %3, %4, %2, %1, %e0"
  [(set_attr "length" "4")
   (set_attr "type" "mul")])

(define_insn "*cond_exec_si_divide"
  [(cond_exec
    (match_operator 0 "ccr_eqne_operator"
		    [(match_operand 1 "cr_operand" "C")
		     (const_int 0)])
    (set (match_operand:SI 2 "integer_register_operand" "=d")
	 (match_operator:SI 3 "condexec_si_divide_operator"
			    [(match_operand:SI 4 "integer_register_operand" "d")
			     (match_operand:SI 5 "integer_register_operand" "d")])))]
  ""
  "*
{
  switch (GET_CODE (operands[3]))
    {
      case DIV:  return \"csdiv %4, %z5, %2, %1, %e0\";
      case UDIV: return \"cudiv %4, %z5, %2, %1, %e0\";
      default:   gcc_unreachable ();
    }
}"
  [(set_attr "length" "4")
   (set_attr "type" "div")])

(define_insn "*cond_exec_si_unary1"
  [(cond_exec
    (match_operator 0 "ccr_eqne_operator"
		    [(match_operand 1 "cr_operand" "C")
		     (const_int 0)])
    (set (match_operand:SI 2 "integer_register_operand" "=d")
	 (match_operator:SI 3 "condexec_si_unary_operator"
			    [(match_operand:SI 4 "integer_register_operand" "d")])))]
  ""
  "*
{
  switch (GET_CODE (operands[3]))
    {
      case NOT: return \"cnot %4, %2, %1, %e0\";
      case NEG: return \"csub %., %4, %2, %1, %e0\";
      default:  gcc_unreachable ();
    }
}"
  [(set_attr "length" "4")
   (set_attr "type" "int")])

(define_insn "*cond_exec_si_unary2"
  [(cond_exec
    (match_operator 0 "ccr_eqne_operator"
		    [(match_operand 1 "cr_operand" "C")
		     (const_int 0)])
    (set (match_operand:SI 2 "fpr_operand" "=f")
	 (not:SI (match_operand:SI 3 "fpr_operand" "f"))))]
  "TARGET_MEDIA"
  "cmnot %3, %2, %1, %e0"
  [(set_attr "length" "4")
   (set_attr "type" "mlogic")])

(define_insn "*cond_exec_cmpsi_cc"
  [(cond_exec
    (match_operator 0 "ccr_eqne_operator"
		    [(match_operand 1 "cr_operand" "C")
		     (const_int 0)])
    (set (match_operand:CC 2 "icc_operand" "=t")
	 (compare:CC (match_operand:SI 3 "integer_register_operand" "d")
		     (match_operand:SI 4 "reg_or_0_operand" "dO"))))]
  "reload_completed
   && REGNO (operands[1]) == REGNO (operands[2]) - ICC_FIRST + ICR_FIRST"
  "ccmp %3, %z4, %1, %e0"
  [(set_attr "length" "4")
   (set_attr "type" "int")])

(define_insn "*cond_exec_cmpsi_cc_uns"
  [(cond_exec
    (match_operator 0 "ccr_eqne_operator"
		    [(match_operand 1 "cr_operand" "C")
		     (const_int 0)])
    (set (match_operand:CC_UNS 2 "icc_operand" "=t")
	 (compare:CC_UNS (match_operand:SI 3 "integer_register_operand" "d")
			 (match_operand:SI 4 "reg_or_0_operand" "dO"))))]
  "reload_completed
   && REGNO (operands[1]) == REGNO (operands[2]) - ICC_FIRST + ICR_FIRST"
  "ccmp %3, %z4, %1, %e0"
  [(set_attr "length" "4")
   (set_attr "type" "int")])

(define_insn "*cond_exec_cmpsi_cc_nz"
  [(cond_exec
    (match_operator 0 "ccr_eqne_operator"
		    [(match_operand 1 "cr_operand" "C")
		     (const_int 0)])
    (set (match_operand:CC_NZ 2 "icc_operand" "=t")
	 (compare:CC_NZ (match_operand:SI 3 "integer_register_operand" "d")
			(const_int 0))))]
  "reload_completed
   && REGNO (operands[1]) == REGNO (operands[2]) - ICC_FIRST + ICR_FIRST"
  "ccmp %3, %., %1, %e0"
  [(set_attr "length" "4")
   (set_attr "type" "int")])

(define_insn "*cond_exec_sf_conv"
  [(cond_exec
    (match_operator 0 "ccr_eqne_operator"
		    [(match_operand 1 "cr_operand" "C")
		     (const_int 0)])
    (set (match_operand:SF 2 "fpr_operand" "=f")
	 (match_operator:SF 3 "condexec_sf_conv_operator"
			    [(match_operand:SF 4 "fpr_operand" "f")])))]
  "TARGET_HARD_FLOAT"
  "*
{
  switch (GET_CODE (operands[3]))
    {
      case ABS: return \"cfabss %4, %2, %1, %e0\";
      case NEG: return \"cfnegs %4, %2, %1, %e0\";
      default:  gcc_unreachable ();
    }
}"
  [(set_attr "length" "4")
   (set_attr "type" "fsconv")])

(define_insn "*cond_exec_sf_add"
  [(cond_exec
    (match_operator 0 "ccr_eqne_operator"
		    [(match_operand 1 "cr_operand" "C")
		     (const_int 0)])
    (set (match_operand:SF 2 "fpr_operand" "=f")
	 (match_operator:SF 3 "condexec_sf_add_operator"
			    [(match_operand:SF 4 "fpr_operand" "f")
			     (match_operand:SF 5 "fpr_operand" "f")])))]
  "TARGET_HARD_FLOAT"
  "*
{
  switch (GET_CODE (operands[3]))
    {
      case PLUS:  return \"cfadds %4, %5, %2, %1, %e0\";
      case MINUS: return \"cfsubs %4, %5, %2, %1, %e0\";
      default:    gcc_unreachable ();
    }
}"
  [(set_attr "length" "4")
   (set_attr "type" "fsadd")])

(define_insn "*cond_exec_sf_mul"
  [(cond_exec
    (match_operator 0 "ccr_eqne_operator"
		    [(match_operand 1 "cr_operand" "C")
		     (const_int 0)])
    (set (match_operand:SF 2 "fpr_operand" "=f")
	 (mult:SF (match_operand:SF 3 "fpr_operand" "f")
		  (match_operand:SF 4 "fpr_operand" "f"))))]
  "TARGET_HARD_FLOAT"
  "cfmuls %3, %4, %2, %1, %e0"
  [(set_attr "length" "4")
   (set_attr "type" "fsmul")])

(define_insn "*cond_exec_sf_div"
  [(cond_exec
    (match_operator 0 "ccr_eqne_operator"
		    [(match_operand 1 "cr_operand" "C")
		     (const_int 0)])
    (set (match_operand:SF 2 "fpr_operand" "=f")
	 (div:SF (match_operand:SF 3 "fpr_operand" "f")
		 (match_operand:SF 4 "fpr_operand" "f"))))]
  "TARGET_HARD_FLOAT"
  "cfdivs %3, %4, %2, %1, %e0"
  [(set_attr "length" "4")
   (set_attr "type" "fsdiv")])

(define_insn "*cond_exec_sf_sqrt"
  [(cond_exec
    (match_operator 0 "ccr_eqne_operator"
		    [(match_operand 1 "cr_operand" "C")
		     (const_int 0)])
    (set (match_operand:SF 2 "fpr_operand" "=f")
	 (sqrt:SF (match_operand:SF 3 "fpr_operand" "f"))))]
  "TARGET_HARD_FLOAT"
  "cfsqrts %3, %2, %1, %e0"
  [(set_attr "length" "4")
   (set_attr "type" "fsdiv")])

(define_insn "*cond_exec_cmpsi_cc_fp"
  [(cond_exec
    (match_operator 0 "ccr_eqne_operator"
		    [(match_operand 1 "cr_operand" "C")
		     (const_int 0)])
    (set (match_operand:CC_FP 2 "fcc_operand" "=u")
	 (compare:CC_FP (match_operand:SF 3 "fpr_operand" "f")
			(match_operand:SF 4 "fpr_operand" "f"))))]
  "reload_completed && TARGET_HARD_FLOAT
   && REGNO (operands[1]) == REGNO (operands[2]) - FCC_FIRST + FCR_FIRST"
  "cfcmps %3, %4, %2, %1, %e0"
  [(set_attr "length" "4")
   (set_attr "type" "fsconv")])


;; ::::::::::::::::::::
;; ::
;; :: Logical operations on CR registers
;; ::
;; ::::::::::::::::::::

;; We use UNSPEC to encode andcr/iorcr/etc. rather than the normal RTL
;; operations, since the RTL operations only have an idea of TRUE and FALSE,
;; while the CRs have TRUE, FALSE, and UNDEFINED.

(define_expand "andcr"
  [(set (match_operand:CC_CCR 0 "cr_operand" "")
	(unspec:CC_CCR [(match_operand:CC_CCR 1 "cr_operand" "")
			(match_operand:CC_CCR 2 "cr_operand" "")
			(const_int 0)] UNSPEC_CR_LOGIC))]
  ""
  "")

(define_expand "orcr"
  [(set (match_operand:CC_CCR 0 "cr_operand" "")
	(unspec:CC_CCR [(match_operand:CC_CCR 1 "cr_operand" "")
			(match_operand:CC_CCR 2 "cr_operand" "")
			(const_int 1)] UNSPEC_CR_LOGIC))]
  ""
  "")

(define_expand "xorcr"
  [(set (match_operand:CC_CCR 0 "cr_operand" "")
	(unspec:CC_CCR [(match_operand:CC_CCR 1 "cr_operand" "")
			(match_operand:CC_CCR 2 "cr_operand" "")
			(const_int 2)] UNSPEC_CR_LOGIC))]
  ""
  "")

(define_expand "nandcr"
  [(set (match_operand:CC_CCR 0 "cr_operand" "")
	(unspec:CC_CCR [(match_operand:CC_CCR 1 "cr_operand" "")
			(match_operand:CC_CCR 2 "cr_operand" "")
			(const_int 3)] UNSPEC_CR_LOGIC))]
  ""
  "")

(define_expand "norcr"
  [(set (match_operand:CC_CCR 0 "cr_operand" "")
	(unspec:CC_CCR [(match_operand:CC_CCR 1 "cr_operand" "")
			(match_operand:CC_CCR 2 "cr_operand" "")
			(const_int 4)] UNSPEC_CR_LOGIC))]
  ""
  "")

(define_expand "andncr"
  [(set (match_operand:CC_CCR 0 "cr_operand" "")
	(unspec:CC_CCR [(match_operand:CC_CCR 1 "cr_operand" "")
			(match_operand:CC_CCR 2 "cr_operand" "")
			(const_int 5)] UNSPEC_CR_LOGIC))]
  ""
  "")

(define_expand "orncr"
  [(set (match_operand:CC_CCR 0 "cr_operand" "")
	(unspec:CC_CCR [(match_operand:CC_CCR 1 "cr_operand" "")
			(match_operand:CC_CCR 2 "cr_operand" "")
			(const_int 6)] UNSPEC_CR_LOGIC))]
  ""
  "")

(define_expand "nandncr"
  [(set (match_operand:CC_CCR 0 "cr_operand" "")
	(unspec:CC_CCR [(match_operand:CC_CCR 1 "cr_operand" "")
			(match_operand:CC_CCR 2 "cr_operand" "")
			(const_int 7)] UNSPEC_CR_LOGIC))]
  ""
  "")

(define_expand "norncr"
  [(set (match_operand:CC_CCR 0 "cr_operand" "")
	(unspec:CC_CCR [(match_operand:CC_CCR 1 "cr_operand" "")
			(match_operand:CC_CCR 2 "cr_operand" "")
			(const_int 8)] UNSPEC_CR_LOGIC))]
  ""
  "")

(define_expand "notcr"
  [(set (match_operand:CC_CCR 0 "cr_operand" "")
	(unspec:CC_CCR [(match_operand:CC_CCR 1 "cr_operand" "")
			(match_dup 1)
			(const_int 9)] UNSPEC_CR_LOGIC))]
  ""
  "")

(define_insn "*logical_cr"
  [(set (match_operand:CC_CCR 0 "cr_operand" "=C")
	(unspec:CC_CCR [(match_operand:CC_CCR 1 "cr_operand" "C")
			(match_operand:CC_CCR 2 "cr_operand" "C")
			(match_operand:SI 3 "const_int_operand" "n")]
		       UNSPEC_CR_LOGIC))]
  ""
  "*
{
  switch (INTVAL (operands[3]))
  {
  default: break;
  case 0: return \"andcr %1, %2, %0\";
  case 1: return \"orcr %1, %2, %0\";
  case 2: return \"xorcr %1, %2, %0\";
  case 3: return \"nandcr %1, %2, %0\";
  case 4: return \"norcr %1, %2, %0\";
  case 5: return \"andncr %1, %2, %0\";
  case 6: return \"orncr %1, %2, %0\";
  case 7: return \"nandncr %1, %2, %0\";
  case 8: return \"norncr %1, %2, %0\";
  case 9: return \"notcr %1, %0\";
  }

  fatal_insn (\"logical_cr\", insn);
}"
  [(set_attr "length" "4")
   (set_attr "type" "ccr")])


;; ::::::::::::::::::::
;; ::
;; :: Conditional move instructions
;; ::
;; ::::::::::::::::::::


;; - conditional moves based on floating-point comparisons require
;;   TARGET_HARD_FLOAT, because an FPU is required to do the comparison.

;; - conditional moves between FPRs based on integer comparisons
;;   require TARGET_HAS_FPRS.

(define_expand "movqicc"
  [(set (match_operand:QI 0 "integer_register_operand" "")
	(if_then_else:QI (match_operand 1 "" "")
			 (match_operand:QI 2 "gpr_or_int_operand" "")
			 (match_operand:QI 3 "gpr_or_int_operand" "")))]
  "TARGET_COND_MOVE"
  "
{
  if (!frv_emit_cond_move (operands[0], operands[1], operands[2], operands[3]))
    FAIL;

  DONE;
}")

(define_insn "*movqicc_internal1_int"
  [(set (match_operand:QI 0 "integer_register_operand" "=d,d,d")
	(if_then_else:QI (match_operator 1 "integer_relational_operator"
			     [(match_operand 2 "icc_operand" "t,t,t")
			      (const_int 0)])
			 (match_operand:QI 3 "reg_or_0_operand" "0,dO,dO")
			 (match_operand:QI 4 "reg_or_0_operand" "dO,0,dO")))
   (clobber (match_operand:CC_CCR 5 "icr_operand" "=v,v,v"))]
  ""
  "#"
  [(set_attr "length" "8,8,12")
   (set_attr "type" "multi")])

(define_insn "*movqicc_internal1_float"
  [(set (match_operand:QI 0 "integer_register_operand" "=d,d,d")
	(if_then_else:QI (match_operator:CC_FP 1 "float_relational_operator"
			     [(match_operand:CC_FP 2 "fcc_operand" "u,u,u")
			      (const_int 0)])
			 (match_operand:QI 3 "reg_or_0_operand" "0,dO,dO")
			 (match_operand:QI 4 "reg_or_0_operand" "dO,0,dO")))
   (clobber (match_operand:CC_CCR 5 "fcr_operand" "=w,w,w"))]
  "TARGET_HARD_FLOAT"
  "#"
  [(set_attr "length" "8,8,12")
   (set_attr "type" "multi")])

(define_insn "*movqicc_internal2_int"
  [(set (match_operand:QI 0 "integer_register_operand" "=d,d,d,d,d")
	(if_then_else:QI (match_operator 1 "integer_relational_operator"
			     [(match_operand 2 "icc_operand" "t,t,t,t,t")
			      (const_int 0)])
			 (match_operand:QI 3 "const_int_operand" "O,O,L,n,n")
			 (match_operand:QI 4 "const_int_operand" "L,n,O,O,n")))
   (clobber (match_operand:CC_CCR 5 "icr_operand" "=v,v,v,v,v"))]
  "(INTVAL (operands[3]) == 0
    || INTVAL (operands[4]) == 0
    || (IN_RANGE (INTVAL (operands[3]), -2048, 2047)
        && IN_RANGE (INTVAL (operands[4]) - INTVAL (operands[3]), -2048, 2047)))"
  "#"
  [(set_attr "length" "8,12,8,12,12")
   (set_attr "type" "multi")])

(define_insn "*movqicc_internal2_float"
  [(set (match_operand:QI 0 "integer_register_operand" "=d,d,d,d,d")
	(if_then_else:QI (match_operator:CC_FP 1 "float_relational_operator"
			     [(match_operand:CC_FP 2 "fcc_operand" "u,u,u,u,u")
			      (const_int 0)])
			 (match_operand:QI 3 "const_int_operand" "O,O,L,n,n")
			 (match_operand:QI 4 "const_int_operand" "L,n,O,O,n")))
   (clobber (match_operand:CC_CCR 5 "fcr_operand" "=w,w,w,w,w"))]
  "TARGET_HARD_FLOAT
   && (INTVAL (operands[3]) == 0
       || INTVAL (operands[4]) == 0
       || (IN_RANGE (INTVAL (operands[3]), -2048, 2047)
	   && IN_RANGE (INTVAL (operands[4]) - INTVAL (operands[3]), -2048, 2047)))"
  "#"
  [(set_attr "length" "8,12,8,12,12")
   (set_attr "type" "multi")])

(define_split
  [(set (match_operand:QI 0 "integer_register_operand" "")
	(if_then_else:QI (match_operator 1 "relational_operator"
			     [(match_operand 2 "cc_operand" "")
			      (const_int 0)])
			 (match_operand:QI 3 "gpr_or_int_operand" "")
			 (match_operand:QI 4 "gpr_or_int_operand" "")))
   (clobber (match_operand:CC_CCR 5 "cr_operand" ""))]
  "reload_completed"
  [(match_dup 6)]
  "operands[6] = frv_split_cond_move (operands);")

(define_expand "movhicc"
  [(set (match_operand:HI 0 "integer_register_operand" "")
	(if_then_else:HI (match_operand 1 "" "")
			 (match_operand:HI 2 "gpr_or_int_operand" "")
			 (match_operand:HI 3 "gpr_or_int_operand" "")))]
  "TARGET_COND_MOVE"
  "
{
  if (!frv_emit_cond_move (operands[0], operands[1], operands[2], operands[3]))
    FAIL;

  DONE;
}")

(define_insn "*movhicc_internal1_int"
  [(set (match_operand:HI 0 "integer_register_operand" "=d,d,d")
	(if_then_else:HI (match_operator 1 "integer_relational_operator"
			     [(match_operand 2 "icc_operand" "t,t,t")
			      (const_int 0)])
			 (match_operand:HI 3 "reg_or_0_operand" "0,dO,dO")
			 (match_operand:HI 4 "reg_or_0_operand" "dO,0,dO")))
   (clobber (match_operand:CC_CCR 5 "icr_operand" "=v,v,v"))]
  ""
  "#"
  [(set_attr "length" "8,8,12")
   (set_attr "type" "multi")])

(define_insn "*movhicc_internal1_float"
  [(set (match_operand:HI 0 "integer_register_operand" "=d,d,d")
	(if_then_else:HI (match_operator:CC_FP 1 "float_relational_operator"
			     [(match_operand:CC_FP 2 "fcc_operand" "u,u,u")
			      (const_int 0)])
			 (match_operand:HI 3 "reg_or_0_operand" "0,dO,dO")
			 (match_operand:HI 4 "reg_or_0_operand" "dO,0,dO")))
   (clobber (match_operand:CC_CCR 5 "fcr_operand" "=w,w,w"))]
  "TARGET_HARD_FLOAT"
  "#"
  [(set_attr "length" "8,8,12")
   (set_attr "type" "multi")])

(define_insn "*movhicc_internal2_int"
  [(set (match_operand:HI 0 "integer_register_operand" "=d,d,d,d,d")
	(if_then_else:HI (match_operator 1 "integer_relational_operator"
			     [(match_operand 2 "icc_operand" "t,t,t,t,t")
			      (const_int 0)])
			 (match_operand:HI 3 "const_int_operand" "O,O,L,n,n")
			 (match_operand:HI 4 "const_int_operand" "L,n,O,O,n")))
   (clobber (match_operand:CC_CCR 5 "icr_operand" "=v,v,v,v,v"))]
  "(INTVAL (operands[3]) == 0
    || INTVAL (operands[4]) == 0
    || (IN_RANGE (INTVAL (operands[3]), -2048, 2047)
        && IN_RANGE (INTVAL (operands[4]) - INTVAL (operands[3]), -2048, 2047)))"
  "#"
  [(set_attr "length" "8,12,8,12,12")
   (set_attr "type" "multi")])

(define_insn "*movhicc_internal2_float"
  [(set (match_operand:HI 0 "integer_register_operand" "=d,d,d,d,d")
	(if_then_else:HI (match_operator:CC_FP 1 "float_relational_operator"
			     [(match_operand:CC_FP 2 "fcc_operand" "u,u,u,u,u")
			      (const_int 0)])
			 (match_operand:HI 3 "const_int_operand" "O,O,L,n,n")
			 (match_operand:HI 4 "const_int_operand" "L,n,O,O,n")))
   (clobber (match_operand:CC_CCR 5 "fcr_operand" "=w,w,w,w,w"))]
  "TARGET_HARD_FLOAT
   && (INTVAL (operands[3]) == 0
       || INTVAL (operands[4]) == 0
       || (IN_RANGE (INTVAL (operands[3]), -2048, 2047)
	   && IN_RANGE (INTVAL (operands[4]) - INTVAL (operands[3]), -2048, 2047)))"
  "#"
  [(set_attr "length" "8,12,8,12,12")
   (set_attr "type" "multi")])

(define_split
  [(set (match_operand:HI 0 "integer_register_operand" "")
	(if_then_else:HI (match_operator 1 "relational_operator"
			     [(match_operand 2 "cc_operand" "")
			      (const_int 0)])
			 (match_operand:HI 3 "gpr_or_int_operand" "")
			 (match_operand:HI 4 "gpr_or_int_operand" "")))
   (clobber (match_operand:CC_CCR 5 "cr_operand" ""))]
  "reload_completed"
  [(match_dup 6)]
  "operands[6] = frv_split_cond_move (operands);")

(define_expand "movsicc"
  [(set (match_operand:SI 0 "integer_register_operand" "")
	(if_then_else:SI (match_operand 1 "" "")
			 (match_operand:SI 2 "gpr_or_int_operand" "")
			 (match_operand:SI 3 "gpr_or_int_operand" "")))]
  "TARGET_COND_MOVE"
  "
{
  if (!frv_emit_cond_move (operands[0], operands[1], operands[2], operands[3]))
    FAIL;

  DONE;
}")

(define_insn "*movsicc_internal1_int"
  [(set (match_operand:SI 0 "integer_register_operand" "=d,d,d")
	(if_then_else:SI (match_operator 1 "integer_relational_operator"
			     [(match_operand 2 "icc_operand" "t,t,t")
			      (const_int 0)])
			 (match_operand:SI 3 "reg_or_0_operand" "0,dO,dO")
			 (match_operand:SI 4 "reg_or_0_operand" "dO,0,dO")))
   (clobber (match_operand:CC_CCR 5 "icr_operand" "=v,v,v"))]
  ""
  "#"
  [(set_attr "length" "8,8,12")
   (set_attr "type" "multi")])

(define_insn "*movsicc_internal1_float"
  [(set (match_operand:SI 0 "integer_register_operand" "=d,d,d")
	(if_then_else:SI (match_operator:CC_FP 1 "float_relational_operator"
			     [(match_operand:CC_FP 2 "fcc_operand" "u,u,u")
			      (const_int 0)])
			 (match_operand:SI 3 "reg_or_0_operand" "0,dO,dO")
			 (match_operand:SI 4 "reg_or_0_operand" "dO,0,dO")))
   (clobber (match_operand:CC_CCR 5 "fcr_operand" "=w,w,w"))]
  "TARGET_HARD_FLOAT"
  "#"
  [(set_attr "length" "8,8,12")
   (set_attr "type" "multi")])

(define_insn "*movsicc_internal2_int"
  [(set (match_operand:SI 0 "integer_register_operand" "=d,d,d,d,d")
	(if_then_else:SI (match_operator 1 "integer_relational_operator"
			     [(match_operand 2 "icc_operand" "t,t,t,t,t")
			      (const_int 0)])
			 (match_operand:SI 3 "const_int_operand" "O,O,L,n,n")
			 (match_operand:SI 4 "const_int_operand" "L,n,O,O,n")))
   (clobber (match_operand:CC_CCR 5 "icr_operand" "=v,v,v,v,v"))]
  "(INTVAL (operands[3]) == 0
    || INTVAL (operands[4]) == 0
    || (IN_RANGE (INTVAL (operands[3]), -2048, 2047)
        && IN_RANGE (INTVAL (operands[4]) - INTVAL (operands[3]), -2048, 2047)))"
  "#"
  [(set_attr "length" "8,12,8,12,12")
   (set_attr "type" "multi")])

(define_insn "*movsicc_internal2_float"
  [(set (match_operand:SI 0 "integer_register_operand" "=d,d,d,d,d")
	(if_then_else:SI (match_operator:CC_FP 1 "float_relational_operator"
			     [(match_operand:CC_FP 2 "fcc_operand" "u,u,u,u,u")
			      (const_int 0)])
			 (match_operand:SI 3 "const_int_operand" "O,O,L,n,n")
			 (match_operand:SI 4 "const_int_operand" "L,n,O,O,n")))
   (clobber (match_operand:CC_CCR 5 "fcr_operand" "=w,w,w,w,w"))]
  "TARGET_HARD_FLOAT
   && (INTVAL (operands[3]) == 0
       || INTVAL (operands[4]) == 0
       || (IN_RANGE (INTVAL (operands[3]), -2048, 2047)
	   && IN_RANGE (INTVAL (operands[4]) - INTVAL (operands[3]), -2048, 2047)))"
  "#"
  [(set_attr "length" "8,12,8,12,12")
   (set_attr "type" "multi")])

(define_split
  [(set (match_operand:SI 0 "integer_register_operand" "")
	(if_then_else:SI (match_operator 1 "relational_operator"
			     [(match_operand 2 "cc_operand" "")
			      (const_int 0)])
			 (match_operand:SI 3 "gpr_or_int_operand" "")
			 (match_operand:SI 4 "gpr_or_int_operand" "")))
   (clobber (match_operand:CC_CCR 5 "cr_operand" ""))]
  "reload_completed"
  [(match_dup 6)]
  "operands[6] = frv_split_cond_move (operands);")

(define_expand "movsfcc"
  [(set (match_operand:SF 0 "register_operand" "")
	(if_then_else:SF (match_operand 1 "" "")
			 (match_operand:SF 2 "register_operand" "")
			 (match_operand:SF 3 "register_operand" "")))]
  "TARGET_COND_MOVE"
  "
{
  if (!frv_emit_cond_move (operands[0], operands[1], operands[2], operands[3]))
    FAIL;

  DONE;
}")

(define_insn "*movsfcc_has_fprs_int"
  [(set (match_operand:SF 0 "register_operand" "=f,f,f,?f,?f,?d")
	(if_then_else:SF (match_operator 1 "integer_relational_operator"
			     [(match_operand 2 "icc_operand" "t,t,t,t,t,t")
			      (const_int 0)])
			 (match_operand:SF 3 "register_operand" "0,f,f,f,d,fd")
			 (match_operand:SF 4 "register_operand" "f,0,f,d,fd,fd")))
   (clobber (match_operand:CC_CCR 5 "icr_operand" "=v,v,v,v,v,v"))]
  "TARGET_HAS_FPRS"
  "#"
  [(set_attr "length" "8,8,12,12,12,12")
   (set_attr "type" "multi")])

(define_insn "*movsfcc_hardfloat_float"
  [(set (match_operand:SF 0 "register_operand" "=f,f,f,?f,?f,?d")
	(if_then_else:SF (match_operator:CC_FP 1 "float_relational_operator"
			     [(match_operand:CC_FP 2 "fcc_operand" "u,u,u,u,u,u")
			      (const_int 0)])
			 (match_operand:SF 3 "register_operand" "0,f,f,f,d,fd")
			 (match_operand:SF 4 "register_operand" "f,0,f,d,fd,fd")))
   (clobber (match_operand:CC_CCR 5 "fcr_operand" "=w,w,w,w,w,w"))]
  "TARGET_HARD_FLOAT"
  "#"
  [(set_attr "length" "8,8,12,12,12,12")
   (set_attr "type" "multi")])

(define_insn "*movsfcc_no_fprs_int"
  [(set (match_operand:SF 0 "integer_register_operand" "=d,d,d")
	(if_then_else:SF (match_operator 1 "integer_relational_operator"
			     [(match_operand 2 "icc_operand" "t,t,t")
			      (const_int 0)])
			 (match_operand:SF 3 "integer_register_operand" "0,d,d")
			 (match_operand:SF 4 "integer_register_operand" "d,0,d")))
   (clobber (match_operand:CC_CCR 5 "icr_operand" "=v,v,v"))]
  "! TARGET_HAS_FPRS"
  "#"
  [(set_attr "length" "8,8,12")
   (set_attr "type" "multi")])

(define_split
  [(set (match_operand:SF 0 "register_operand" "")
	(if_then_else:SF (match_operator 1 "relational_operator"
			     [(match_operand 2 "cc_operand" "")
			      (const_int 0)])
			 (match_operand:SF 3 "register_operand" "")
			 (match_operand:SF 4 "register_operand" "")))
   (clobber (match_operand:CC_CCR 5 "cr_operand" ""))]
  "reload_completed"
  [(match_dup 6)]
  "operands[6] = frv_split_cond_move (operands);")


;; ::::::::::::::::::::
;; ::
;; :: Minimum, maximum, and integer absolute value
;; ::
;; ::::::::::::::::::::

;; These 'instructions' are provided to give the compiler a slightly better
;; nudge at register allocation, then it would if it constructed the
;; instructions from basic building blocks (since it indicates it prefers one
;; of the operands to be the same as the destination.  It also helps the
;; earlier passes of the compiler, by not breaking things into small basic
;; blocks.

(define_expand "abssi2"
  [(parallel [(set (match_operand:SI 0 "integer_register_operand" "")
		   (abs:SI (match_operand:SI 1 "integer_register_operand" "")))
	      (clobber (match_dup 2))
	      (clobber (match_dup 3))])]
  "TARGET_COND_MOVE"
  "
{
  operands[2] = gen_reg_rtx (CCmode);
  operands[3] = gen_reg_rtx (CC_CCRmode);
}")

(define_insn_and_split "*abssi2_internal"
  [(set (match_operand:SI 0 "integer_register_operand" "=d,d")
	(abs:SI (match_operand:SI 1 "integer_register_operand" "0,d")))
   (clobber (match_operand:CC 2 "icc_operand" "=t,t"))
   (clobber (match_operand:CC_CCR 3 "icr_operand" "=v,v"))]
  "TARGET_COND_MOVE"
  "#"
  "reload_completed"
  [(match_dup 4)]
  "operands[4] = frv_split_abs (operands);"
  [(set_attr "length" "12,16")
   (set_attr "type" "multi")])

(define_expand "sminsi3"
  [(parallel [(set (match_operand:SI 0 "integer_register_operand" "")
		   (smin:SI (match_operand:SI 1 "integer_register_operand" "")
			    (match_operand:SI 2 "gpr_or_int10_operand" "")))
	      (clobber (match_dup 3))
	      (clobber (match_dup 4))])]
  "TARGET_COND_MOVE"
  "
{
  operands[3] = gen_reg_rtx (CCmode);
  operands[4] = gen_reg_rtx (CC_CCRmode);
}")

(define_expand "smaxsi3"
  [(parallel [(set (match_operand:SI 0 "integer_register_operand" "")
		   (smax:SI (match_operand:SI 1 "integer_register_operand" "")
			    (match_operand:SI 2 "gpr_or_int10_operand" "")))
	      (clobber (match_dup 3))
	      (clobber (match_dup 4))])]
  "TARGET_COND_MOVE"
  "
{
  operands[3] = gen_reg_rtx (CCmode);
  operands[4] = gen_reg_rtx (CC_CCRmode);
}")

(define_insn_and_split "*minmax_si_signed"
  [(set (match_operand:SI 0 "integer_register_operand" "=d,d,&d")
	(match_operator:SI 1 "minmax_operator"
			   [(match_operand:SI 2 "integer_register_operand" "%0,dO,d")
			    (match_operand:SI 3 "gpr_or_int10_operand" "dO,0,dJ")]))
   (clobber (match_operand:CC 4 "icc_operand" "=t,t,t"))
   (clobber (match_operand:CC_CCR 5 "icr_operand" "=v,v,v"))]
  "TARGET_COND_MOVE"
  "#"
  "reload_completed"
  [(match_dup 6)]
  "operands[6] = frv_split_minmax (operands);"
  [(set_attr "length" "12,12,16")
   (set_attr "type" "multi")])

(define_expand "uminsi3"
  [(parallel [(set (match_operand:SI 0 "integer_register_operand" "")
		   (umin:SI (match_operand:SI 1 "integer_register_operand" "")
			    (match_operand:SI 2 "gpr_or_int10_operand" "")))
	      (clobber (match_dup 3))
	      (clobber (match_dup 4))])]
  "TARGET_COND_MOVE"
  "
{
  operands[3] = gen_reg_rtx (CC_UNSmode);
  operands[4] = gen_reg_rtx (CC_CCRmode);
}")

(define_expand "umaxsi3"
  [(parallel [(set (match_operand:SI 0 "integer_register_operand" "")
		   (umax:SI (match_operand:SI 1 "integer_register_operand" "")
			    (match_operand:SI 2 "gpr_or_int10_operand" "")))
	      (clobber (match_dup 3))
	      (clobber (match_dup 4))])]
  "TARGET_COND_MOVE"
  "
{
  operands[3] = gen_reg_rtx (CC_UNSmode);
  operands[4] = gen_reg_rtx (CC_CCRmode);
}")

(define_insn_and_split "*minmax_si_unsigned"
  [(set (match_operand:SI 0 "integer_register_operand" "=d,d,&d")
	(match_operator:SI 1 "minmax_operator"
			   [(match_operand:SI 2 "integer_register_operand" "%0,dO,d")
			    (match_operand:SI 3 "gpr_or_int10_operand" "dO,0,dJ")]))
   (clobber (match_operand:CC_UNS 4 "icc_operand" "=t,t,t"))
   (clobber (match_operand:CC_CCR 5 "icr_operand" "=v,v,v"))]
  "TARGET_COND_MOVE"
  "#"
  "reload_completed"
  [(match_dup 6)]
  "operands[6] = frv_split_minmax (operands);"
  [(set_attr "length" "12,12,16")
   (set_attr "type" "multi")])

(define_expand "sminsf3"
  [(parallel [(set (match_operand:SF 0 "fpr_operand" "")
		   (smin:SF (match_operand:SF 1 "fpr_operand" "")
			    (match_operand:SF 2 "fpr_operand" "")))
	      (clobber (match_dup 3))
	      (clobber (match_dup 4))])]
  "TARGET_COND_MOVE && TARGET_HARD_FLOAT"
  "
{
  operands[3] = gen_reg_rtx (CC_FPmode);
  operands[4] = gen_reg_rtx (CC_CCRmode);
}")

(define_expand "smaxsf3"
  [(parallel [(set (match_operand:SF 0 "fpr_operand" "")
		   (smax:SF (match_operand:SF 1 "fpr_operand" "")
			    (match_operand:SF 2 "fpr_operand" "")))
	      (clobber (match_dup 3))
	      (clobber (match_dup 4))])]
  "TARGET_COND_MOVE && TARGET_HARD_FLOAT"
  "
{
  operands[3] = gen_reg_rtx (CC_FPmode);
  operands[4] = gen_reg_rtx (CC_CCRmode);
}")

(define_insn_and_split "*minmax_sf"
  [(set (match_operand:SF 0 "fpr_operand" "=f,f,f")
	(match_operator:SF 1 "minmax_operator"
			   [(match_operand:SF 2 "fpr_operand" "%0,f,f")
			    (match_operand:SF 3 "fpr_operand" "f,0,f")]))
   (clobber (match_operand:CC_FP 4 "fcc_operand" "=u,u,u"))
   (clobber (match_operand:CC_CCR 5 "fcr_operand" "=w,w,w"))]
  "TARGET_COND_MOVE && TARGET_HARD_FLOAT"
  "#"
  "reload_completed"
  [(match_dup 6)]
  "operands[6] = frv_split_minmax (operands);"
  [(set_attr "length" "12,12,16")
   (set_attr "type" "multi")])

(define_expand "smindf3"
  [(parallel [(set (match_operand:DF 0 "fpr_operand" "")
		   (smin:DF (match_operand:DF 1 "fpr_operand" "")
			    (match_operand:DF 2 "fpr_operand" "")))
	      (clobber (match_dup 3))
	      (clobber (match_dup 4))])]
  "TARGET_COND_MOVE && TARGET_HARD_FLOAT && TARGET_DOUBLE"
  "
{
  operands[3] = gen_reg_rtx (CC_FPmode);
  operands[4] = gen_reg_rtx (CC_CCRmode);
}")

(define_expand "smaxdf3"
  [(parallel [(set (match_operand:DF 0 "fpr_operand" "")
		   (smax:DF (match_operand:DF 1 "fpr_operand" "")
			    (match_operand:DF 2 "fpr_operand" "")))
	      (clobber (match_dup 3))
	      (clobber (match_dup 4))])]
  "TARGET_COND_MOVE && TARGET_HARD_FLOAT && TARGET_DOUBLE"
  "
{
  operands[3] = gen_reg_rtx (CC_FPmode);
  operands[4] = gen_reg_rtx (CC_CCRmode);
}")

(define_insn_and_split "*minmax_df"
  [(set (match_operand:DF 0 "fpr_operand" "=f,f,f")
	(match_operator:DF 1 "minmax_operator"
			   [(match_operand:DF 2 "fpr_operand" "%0,f,f")
			    (match_operand:DF 3 "fpr_operand" "f,0,f")]))
   (clobber (match_operand:CC_FP 4 "fcc_operand" "=u,u,u"))
   (clobber (match_operand:CC_CCR 5 "fcr_operand" "=w,w,w"))]
  "TARGET_COND_MOVE && TARGET_HARD_FLOAT && TARGET_DOUBLE"
  "#"
  "reload_completed"
  [(match_dup 6)]
  "operands[6] = frv_split_minmax (operands);"
  [(set_attr "length" "12,12,16")
   (set_attr "type" "multi")])


;; ::::::::::::::::::::
;; ::
;; :: Call and branch instructions
;; ::
;; ::::::::::::::::::::

;; Subroutine call instruction returning no value.  Operand 0 is the function
;; to call; operand 1 is the number of bytes of arguments pushed (in mode
;; `SImode', except it is normally a `const_int'); operand 2 is the number of
;; registers used as operands.

;; On most machines, operand 2 is not actually stored into the RTL pattern.  It
;; is supplied for the sake of some RISC machines which need to put this
;; information into the assembler code; they can put it in the RTL instead of
;; operand 1.

(define_expand "call"
  [(use (match_operand:QI 0 "" ""))
   (use (match_operand 1 "" ""))
   (use (match_operand 2 "" ""))
   (use (match_operand 3 "" ""))]
  ""
  "
{
  rtx lr = gen_rtx_REG (Pmode, LR_REGNO);
  rtx addr;

  gcc_assert (GET_CODE (operands[0]) == MEM);

  addr = XEXP (operands[0], 0);
  if (! call_operand (addr, Pmode))
    addr = force_reg (Pmode, addr);

  if (! operands[2])
    operands[2] = const0_rtx;

  if (TARGET_FDPIC)
    frv_expand_fdpic_call (operands, false, false);
  else
    emit_call_insn (gen_call_internal (addr, operands[1], operands[2], lr));

  DONE;
}")

(define_insn "call_internal"
  [(call (mem:QI (match_operand:SI 0 "call_operand" "S,dNOP"))
	 (match_operand 1 "" ""))
   (use (match_operand 2 "" ""))
   (clobber (match_operand:SI 3 "lr_operand" "=l,l"))]
  "! TARGET_FDPIC"
  "@
   call %0
   call%i0l %M0"
  [(set_attr "length" "4")
   (set_attr "type" "call,jumpl")])

;; The odd use of GR0 within the UNSPEC below prevents cseing or
;; hoisting function descriptor loads out of loops.  This is almost
;; never desirable, since if we preserve the function descriptor in a
;; pair of registers, it takes two insns to move it to gr14/gr15, and
;; if it's in the stack, we just waste space with the store, since
;; we'll have to load back from memory anyway.  And, in the worst
;; case, we may end up reusing a function descriptor still pointing at
;; a PLT entry, instead of to the resolved function, which means going
;; through the resolver for every call that uses the outdated value.
;; Bad!

;; The explicit MEM inside the SPEC prevents the compiler from moving
;; the load before a branch after a NULL test, or before a store that
;; initializes a function descriptor.

(define_insn "movdi_ldd"
  [(set (match_operand:DI 0 "fdpic_fptr_operand" "=e")
	(unspec:DI [(mem:DI (match_operand:SI 1 "ldd_address_operand" "p"))
		    (reg:SI 0)] UNSPEC_LDD))]
  ""
  "ldd%I1 %M1, %0"
  [(set_attr "length" "4")
   (set_attr "type" "gload")])

(define_insn "call_fdpicdi"
  [(call (mem:QI (match_operand:DI 0 "fdpic_fptr_operand" "W"))
	 (match_operand 1 "" ""))
   (clobber (match_operand:SI 2 "lr_operand" "=l"))]
  "TARGET_FDPIC"
  "call%i0l %M0"
  [(set_attr "length" "4")
   (set_attr "type" "jumpl")])

(define_insn "call_fdpicsi"
  [(call (mem:QI (match_operand:SI 0 "call_operand" "S,dNOP"))
	 (match_operand 1 "" ""))
   (use (match_operand 2 "" ""))
   (use (match_operand:SI 3 "fdpic_operand" "Z,Z"))
   (clobber (match_operand:SI 4 "lr_operand" "=l,l"))]
  "TARGET_FDPIC"
  "@
   call %0
   call%i0l %M0"
  [(set_attr "length" "4")
   (set_attr "type" "call,jumpl")])

(define_expand "sibcall"
  [(use (match_operand:QI 0 "" ""))
   (use (match_operand 1 "" ""))
   (use (match_operand 2 "" ""))
   (use (match_operand 3 "" ""))]
  ""
  "
{
  rtx addr;

  gcc_assert (GET_CODE (operands[0]) == MEM);

  addr = XEXP (operands[0], 0);
  if (! sibcall_operand (addr, Pmode))
    addr = force_reg (Pmode, addr);

  if (! operands[2])
    operands[2] = const0_rtx;

  if (TARGET_FDPIC)
    frv_expand_fdpic_call (operands, false, true);
  else
    emit_call_insn (gen_sibcall_internal (addr, operands[1], operands[2]));

  DONE;
}")
  
;; It might seem that these sibcall patterns are missing references to
;; LR, but they're not necessary because sibcall_epilogue will make
;; sure LR is restored, and having LR here will set
;; regs_ever_used[REG_LR], forcing it to be saved on the stack, and
;; then restored in sibcalls and regular return code paths, even if
;; the function becomes a leaf function after tail-call elimination.

;; We must not use a call-saved register here.  `W' limits ourselves
;; to gr14 or gr15, but since we're almost running out of constraint
;; letters, and most other call-clobbered registers are often used for
;; argument-passing, this will do.
(define_insn "sibcall_internal"
  [(call (mem:QI (match_operand:SI 0 "sibcall_operand" "WNOP"))
	 (match_operand 1 "" ""))
   (use (match_operand 2 "" ""))
   (return)]
  "! TARGET_FDPIC"
  "jmp%i0l %M0"
  [(set_attr "length" "4")
   (set_attr "type" "jumpl")])

(define_insn "sibcall_fdpicdi"
  [(call (mem:QI (match_operand:DI 0 "fdpic_fptr_operand" "W"))
	 (match_operand 1 "" ""))
   (return)]
  "TARGET_FDPIC"
  "jmp%i0l %M0"
  [(set_attr "length" "4")
   (set_attr "type" "jumpl")])


;; Subroutine call instruction returning a value.  Operand 0 is the hard
;; register in which the value is returned.  There are three more operands, the
;; same as the three operands of the `call' instruction (but with numbers
;; increased by one).

;; Subroutines that return `BLKmode' objects use the `call' insn.

(define_expand "call_value"
  [(use (match_operand 0 "" ""))
   (use (match_operand:QI 1 "" ""))
   (use (match_operand 2 "" ""))
   (use (match_operand 3 "" ""))
   (use (match_operand 4 "" ""))]
  ""
  "
{
  rtx lr = gen_rtx_REG (Pmode, LR_REGNO);
  rtx addr;

  gcc_assert (GET_CODE (operands[1]) == MEM);

  addr = XEXP (operands[1], 0);
  if (! call_operand (addr, Pmode))
    addr = force_reg (Pmode, addr);

  if (! operands[3])
    operands[3] = const0_rtx;

  if (TARGET_FDPIC)
    frv_expand_fdpic_call (operands, true, false);
  else
    emit_call_insn (gen_call_value_internal (operands[0], addr, operands[2],
					     operands[3], lr));

  DONE;
}")

(define_insn "call_value_internal"
  [(set (match_operand 0 "register_operand" "=d,d")
	(call (mem:QI (match_operand:SI 1 "call_operand" "S,dNOP"))
		      (match_operand 2 "" "")))
   (use (match_operand 3 "" ""))
   (clobber (match_operand:SI 4 "lr_operand" "=l,l"))]
  "! TARGET_FDPIC"
  "@
   call %1
   call%i1l %M1"
  [(set_attr "length" "4")
   (set_attr "type" "call,jumpl")])

(define_insn "call_value_fdpicdi"
  [(set (match_operand 0 "register_operand" "=d")
	(call (mem:QI (match_operand:DI 1 "fdpic_fptr_operand" "W"))
	      (match_operand 2 "" "")))
   (clobber (match_operand:SI 3 "lr_operand" "=l"))]
  "TARGET_FDPIC"
  "call%i1l %M1"
  [(set_attr "length" "4")
   (set_attr "type" "jumpl")])

(define_insn "call_value_fdpicsi"
  [(set (match_operand 0 "register_operand" "=d,d")
	(call (mem:QI (match_operand:SI 1 "call_operand" "S,dNOP"))
		      (match_operand 2 "" "")))
   (use (match_operand 3 "" ""))
   (use (match_operand:SI 4 "fdpic_operand" "Z,Z"))
   (clobber (match_operand:SI 5 "lr_operand" "=l,l"))]
  "TARGET_FDPIC"
  "@
   call %1
   call%i1l %M1"
  [(set_attr "length" "4")
   (set_attr "type" "call,jumpl")])

(define_expand "sibcall_value"
  [(use (match_operand 0 "" ""))
   (use (match_operand:QI 1 "" ""))
   (use (match_operand 2 "" ""))
   (use (match_operand 3 "" ""))
   (use (match_operand 4 "" ""))]
  ""
  "
{
  rtx addr;

  gcc_assert (GET_CODE (operands[1]) == MEM);

  addr = XEXP (operands[1], 0);
  if (! sibcall_operand (addr, Pmode))
    addr = force_reg (Pmode, addr);

  if (! operands[3])
    operands[3] = const0_rtx;

  if (TARGET_FDPIC)
    frv_expand_fdpic_call (operands, true, true);
  else
    emit_call_insn (gen_sibcall_value_internal (operands[0], addr, operands[2],
						operands[3]));
  DONE;
}")

(define_insn "sibcall_value_internal"
  [(set (match_operand 0 "register_operand" "=d")
	(call (mem:QI (match_operand:SI 1 "sibcall_operand" "WNOP"))
		      (match_operand 2 "" "")))
   (use (match_operand 3 "" ""))
   (return)]
  "! TARGET_FDPIC"
  "jmp%i1l %M1"
  [(set_attr "length" "4")
   (set_attr "type" "jumpl")])

(define_insn "sibcall_value_fdpicdi"
  [(set (match_operand 0 "register_operand" "=d")
	(call (mem:QI (match_operand:DI 1 "fdpic_fptr_operand" "W"))
	      (match_operand 2 "" "")))
   (return)]
  "TARGET_FDPIC"
  "jmp%i1l %M1"
  [(set_attr "length" "4")
   (set_attr "type" "jumpl")])

;; return instruction generated instead of jmp to epilog
(define_expand "return"
  [(parallel [(return)
	      (use (match_dup 0))
	      (use (const_int 1))])]
  "direct_return_p ()"
  "
{
  operands[0] = gen_rtx_REG (Pmode, LR_REGNO);
}")

;; return instruction generated by the epilogue
(define_expand "epilogue_return"
  [(parallel [(return)
	      (use (match_operand:SI 0 "register_operand" ""))
	      (use (const_int 0))])]
  ""
  "")

(define_insn "*return_internal"
  [(return)
   (use (match_operand:SI 0 "register_operand" "l,d"))
   (use (match_operand:SI 1 "immediate_operand" "n,n"))]
  ""
  "@
    ret
    jmpl @(%0,%.)"
  [(set_attr "length" "4")
   (set_attr "type" "jump,jumpl")])

(define_insn "*return_true"
  [(set (pc)
	(if_then_else (match_operator 0 "integer_relational_operator"
				      [(match_operand 1 "icc_operand" "t")
				       (const_int 0)])
		      (return)
		      (pc)))]
  "direct_return_p ()"
  "b%c0lr %1,%#"
  [(set_attr "length" "4")
   (set_attr "type" "jump")])

(define_insn "*return_false"
  [(set (pc)
	(if_then_else (match_operator 0 "integer_relational_operator"
				      [(match_operand 1 "icc_operand" "t")
				       (const_int 0)])
		      (pc)
		      (return)))]
  "direct_return_p ()"
  "b%C0lr %1,%#"
  [(set_attr "length" "4")
   (set_attr "type" "jump")])

;; A version of addsi3 for deallocating stack space at the end of the
;; epilogue.  The addition is done in parallel with an (unspec_volatile),
;; which represents the clobbering of the deallocated space.
(define_insn "stack_adjust"
  [(set (match_operand:SI 0 "register_operand" "=d")
        (plus:SI (match_operand:SI 1 "register_operand" "d")
		 (match_operand:SI 2 "general_operand" "dNOP")))
   (unspec_volatile [(const_int 0)] UNSPEC_STACK_ADJUST)]
  ""
  "add%I2 %1,%2,%0"
  [(set_attr "length" "4")
   (set_attr "type" "int")])

;; Normal unconditional jump

;; Use the "call" instruction for long branches, but prefer to use "bra" for
;; short ones since it does not force us to save the link register.

;; This define_insn uses the branch-shortening code to decide which
;; instruction it emits.  Since the main branch-shortening interface is
;; through get_attr_length(), the two alternatives must be given different
;; lengths.  Here we pretend that the far jump is 8 rather than 4 bytes
;; long, though both alternatives are really the same size.
(define_insn "jump"
  [(set (pc) (label_ref (match_operand 0 "" "")))]
  ""
  "*
{
  if (get_attr_length (insn) == 4)
    return \"bra %l0\";
  else
    return \"call %l0\";
}"
  [(set (attr "length")
        (if_then_else
	    (and (ge (minus (match_dup 0) (pc)) (const_int -32768))
		 (le (minus (match_dup 0) (pc)) (const_int 32764)))
	    (const_int 4)
	    (const_int 8)))
   (set (attr "far_jump")
        (if_then_else
	    (eq_attr "length" "4")
	    (const_string "no")
	    (const_string "yes")))
   (set (attr "type")
	(if_then_else
	    (eq_attr "length" "4")
	    (const_string "jump")
	    (const_string "call")))])

;; Indirect jump through a register
(define_insn "indirect_jump"
  [(set (pc) (match_operand:SI 0 "register_operand" "d,l"))]
  ""
  "@
   jmpl @(%0,%.)
   bralr"
  [(set_attr "length" "4")
   (set_attr "type" "jumpl,branch")])

;; Instruction to jump to a variable address.  This is a low-level capability
;; which can be used to implement a dispatch table when there is no `casesi'
;; pattern.  Either the 'casesi' pattern or the 'tablejump' pattern, or both,
;; MUST be present in this file.

;; This pattern requires two operands: the address or offset, and a label which
;; should immediately precede the jump table.  If the macro
;; `CASE_VECTOR_PC_RELATIVE' is defined then the first operand is an offset
;; which counts from the address of the table; otherwise, it is an absolute
;; address to jump to.  In either case, the first operand has mode `Pmode'.

;; The `tablejump' insn is always the last insn before the jump table it uses.
;; Its assembler code normally has no need to use the second operand, but you
;; should incorporate it in the RTL pattern so that the jump optimizer will not
;; delete the table as unreachable code.

(define_expand "tablejump"
  [(parallel [(set (pc) (match_operand:SI 0 "address_operand" "p"))
	      (use (label_ref (match_operand 1 "" "")))])]
  "!flag_pic"
  "")

(define_insn "tablejump_insn"
  [(set (pc) (match_operand:SI 0 "address_operand" "p"))
   (use (label_ref (match_operand 1 "" "")))]
  ""
  "jmp%I0l %M0"
  [(set_attr "length" "4")
   (set_attr "type" "jumpl")])

;; Implement switch statements when generating PIC code.  Switches are
;; implemented by `tablejump' when not using -fpic.

;; Emit code here to do the range checking and make the index zero based.
;; operand 0 is the index
;; operand 1 is the lower bound
;; operand 2 is the range of indices (highest - lowest + 1)
;; operand 3 is the label that precedes the table itself
;; operand 4 is the fall through label

(define_expand "casesi"
  [(use (match_operand:SI 0 "integer_register_operand" ""))
   (use (match_operand:SI 1 "const_int_operand" ""))
   (use (match_operand:SI 2 "const_int_operand" ""))
   (use (match_operand 3 "" ""))
   (use (match_operand 4 "" ""))]
  "flag_pic"
  "
{
  rtx indx;
  rtx scale;
  rtx low = operands[1];
  rtx range = operands[2];
  rtx table = operands[3];
  rtx treg;
  rtx fail = operands[4];
  rtx mem;
  rtx reg2;
  rtx reg3;

  gcc_assert (GET_CODE (operands[1]) == CONST_INT);

  gcc_assert (GET_CODE (operands[2]) == CONST_INT);

  /* If we can't generate an immediate instruction, promote to register.  */
  if (! IN_RANGE (INTVAL (range), -2048, 2047))
    range = force_reg (SImode, range);

  /* If low bound is 0, we don't have to subtract it.  */
  if (INTVAL (operands[1]) == 0)
    indx = operands[0];
  else
    {
      indx = gen_reg_rtx (SImode);
      if (IN_RANGE (INTVAL (low), -2047, 2048))
	emit_insn (gen_addsi3 (indx, operands[0], GEN_INT (- INTVAL (low))));
      else
	emit_insn (gen_subsi3 (indx, operands[0], force_reg (SImode, low)));
    }

  /* Do an unsigned comparison (in the proper mode) between the index
     expression and the value which represents the length of the range.
     Since we just finished subtracting the lower bound of the range
     from the index expression, this comparison allows us to simultaneously
     check that the original index expression value is both greater than
     or equal to the minimum value of the range and less than or equal to
     the maximum value of the range.  */

  emit_cmp_and_jump_insns (indx, range, GTU, NULL_RTX, SImode, 1, fail);

  /* Move the table address to a register.  */
  treg = gen_reg_rtx (Pmode);
  emit_insn (gen_movsi (treg, gen_rtx_LABEL_REF (VOIDmode, table)));

  /* Scale index-low by wordsize.  */
  scale = gen_reg_rtx (SImode);
  emit_insn (gen_ashlsi3 (scale, indx, const2_rtx));

  /* Load the address, add the start of the table back in,
     and jump to it.  */
  mem = gen_rtx_MEM (SImode, gen_rtx_PLUS (Pmode, scale, treg));
  reg2 = gen_reg_rtx (SImode);
  reg3 = gen_reg_rtx (SImode);
  emit_insn (gen_movsi (reg2, mem));
  emit_insn (gen_addsi3 (reg3, reg2, treg));
  emit_jump_insn (gen_tablejump_insn (reg3, table));
  DONE;
}")

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

;; Called after register allocation to add any instructions needed for the
;; prologue.  Using a prologue insn is favored compared to putting all of the
;; instructions in the FUNCTION_PROLOGUE macro, since it allows the scheduler
;; to intermix instructions with the saves of the caller saved registers.  In
;; some cases, it might be necessary to emit a barrier instruction as the last
;; insn to prevent such scheduling.
(define_expand "prologue"
  [(const_int 1)]
  ""
  "
{
  frv_expand_prologue ();
  DONE;
}")

;; Called after register allocation to add any instructions needed for the
;; epilogue.  Using an epilogue insn is favored compared to putting all of the
;; instructions in the FUNCTION_EPILOGUE macro, since it allows the scheduler
;; to intermix instructions with the restores of the caller saved registers.
;; In some cases, it might be necessary to emit a barrier instruction as the
;; first insn to prevent such scheduling.
(define_expand "epilogue"
  [(const_int 2)]
  ""
  "
{
  frv_expand_epilogue (true);
  DONE;
}")

;; This pattern, if defined, emits RTL for exit from a function without the final
;; branch back to the calling function.  This pattern will be emitted before any
;; sibling call (aka tail call) sites.
;;
;; The sibcall_epilogue pattern must not clobber any arguments used for
;; parameter passing or any stack slots for arguments passed to the current
;; function.
(define_expand "sibcall_epilogue"
  [(const_int 3)]
  ""
  "
{
  frv_expand_epilogue (false);
  DONE;
}")

;; Set up the pic register to hold the address of the pic table
(define_insn "pic_prologue"
  [(set (match_operand:SI 0 "integer_register_operand" "=d")
        (unspec_volatile:SI [(const_int 0)] UNSPEC_PIC_PROLOGUE))
   (clobber (match_operand:SI 1 "lr_operand" "=l"))
   (clobber (match_operand:SI 2 "integer_register_operand" "=d"))]
  ""
  "*
{
  static int frv_pic_labelno = 0;

  operands[3] = GEN_INT (frv_pic_labelno++);
  return \"call %P3\\n%P3:\;movsg %1, %0\;sethi #gprelhi(%P3), %2\;setlo #gprello(%P3), %2\;sub %0,%2,%0\";
}"
  [(set_attr "length" "16")
   (set_attr "type" "multi")])

;; ::::::::::::::::::::
;; ::
;; :: Miscellaneous instructions
;; ::
;; ::::::::::::::::::::

;; No operation, needed in case the user uses -g but not -O.
(define_insn "nop"
  [(const_int 0)]
  ""
  "nop"
  [(set_attr "length" "4")
   (set_attr "type" "int")])

(define_insn "fnop"
  [(const_int 1)]
  ""
  "fnop"
  [(set_attr "length" "4")
   (set_attr "type" "fnop")])

(define_insn "mnop"
  [(const_int 2)]
  ""
  "mnop"
  [(set_attr "length" "4")
   (set_attr "type" "mnop")])

;; Pseudo instruction that prevents the scheduler from moving code above this
;; point.  Note, type unknown is used to make sure the VLIW instructions are
;; not continued past this point.
(define_insn "blockage"
  [(unspec_volatile [(const_int 0)] UNSPEC_BLOCKAGE)]
  ""
  "# blockage"
  [(set_attr "length" "0")
   (set_attr "type" "unknown")])

;; ::::::::::::::::::::
;; ::
;; :: Media instructions
;; ::
;; ::::::::::::::::::::

;; Unimplemented instructions:
;;   - MCMPSH, MCMPUH

(define_constants
  [(UNSPEC_MLOGIC		100)
   (UNSPEC_MNOT			101)
   (UNSPEC_MAVEH		102)
   (UNSPEC_MSATH		103)
   (UNSPEC_MADDH		104)
   (UNSPEC_MQADDH		105)
   (UNSPEC_MPACKH		106)
   (UNSPEC_MUNPACKH		107)
   (UNSPEC_MDPACKH		108)
   (UNSPEC_MBTOH		109)
   (UNSPEC_MHTOB		110)
   (UNSPEC_MROT			111)
   (UNSPEC_MSHIFT		112)
   (UNSPEC_MEXPDHW		113)
   (UNSPEC_MEXPDHD		114)
   (UNSPEC_MWCUT		115)
   (UNSPEC_MMULH		116)
   (UNSPEC_MMULXH		117)
   (UNSPEC_MMACH		118)
   (UNSPEC_MMRDH		119)
   (UNSPEC_MQMULH		120)
   (UNSPEC_MQMULXH		121)
   (UNSPEC_MQMACH		122)
   (UNSPEC_MCPX			123)
   (UNSPEC_MQCPX		124)
   (UNSPEC_MCUT			125)
   (UNSPEC_MRDACC		126)
   (UNSPEC_MRDACCG		127)
   (UNSPEC_MWTACC		128)
   (UNSPEC_MWTACCG		129)
   (UNSPEC_MTRAP		130)
   (UNSPEC_MCLRACC		131)
   (UNSPEC_MCLRACCA		132)
   (UNSPEC_MCOP1		133)
   (UNSPEC_MCOP2		134)
   (UNSPEC_MDUNPACKH		135)
   (UNSPEC_MDUNPACKH_INTERNAL	136)
   (UNSPEC_MBTOHE		137)
   (UNSPEC_MBTOHE_INTERNAL	138)
   (UNSPEC_MBTOHE		137)
   (UNSPEC_MBTOHE_INTERNAL	138)
   (UNSPEC_MQMACH2		139)
   (UNSPEC_MADDACC		140)
   (UNSPEC_MDADDACC		141)
   (UNSPEC_MABSHS		142)
   (UNSPEC_MDROTLI		143)
   (UNSPEC_MCPLHI		144)
   (UNSPEC_MCPLI		145)
   (UNSPEC_MDCUTSSI		146)
   (UNSPEC_MQSATHS		147)
   (UNSPEC_MHSETLOS		148)
   (UNSPEC_MHSETLOH		149)
   (UNSPEC_MHSETHIS		150)
   (UNSPEC_MHSETHIH		151)
   (UNSPEC_MHDSETS		152)
   (UNSPEC_MHDSETH		153)
   (UNSPEC_MQLCLRHS		154)
   (UNSPEC_MQLMTHS		155)
   (UNSPEC_MQSLLHI		156)
   (UNSPEC_MQSRAHI		157)
   (UNSPEC_MASACCS		158)
   (UNSPEC_MDASACCS		159)
])

;; Logic operations: type "mlogic"

(define_expand "mand"
  [(set (match_operand:SI 0 "fpr_operand" "")
        (unspec:SI [(match_operand:SI 1 "fpr_operand" "")
		    (match_operand:SI 2 "fpr_operand" "")
		    (match_dup 3)]
		   UNSPEC_MLOGIC))]
  "TARGET_MEDIA"
  "operands[3] = GEN_INT (FRV_BUILTIN_MAND);")

(define_expand "mor"
  [(set (match_operand:SI 0 "fpr_operand" "")
        (unspec:SI [(match_operand:SI 1 "fpr_operand" "")
		    (match_operand:SI 2 "fpr_operand" "")
		    (match_dup 3)]
		   UNSPEC_MLOGIC))]
  "TARGET_MEDIA"
  "operands[3] = GEN_INT (FRV_BUILTIN_MOR);")

(define_expand "mxor"
  [(set (match_operand:SI 0 "fpr_operand" "")
        (unspec:SI [(match_operand:SI 1 "fpr_operand" "")
		    (match_operand:SI 2 "fpr_operand" "")
		    (match_dup 3)]
		   UNSPEC_MLOGIC))]
  "TARGET_MEDIA"
  "operands[3] = GEN_INT (FRV_BUILTIN_MXOR);")

(define_insn "*mlogic"
  [(set (match_operand:SI 0 "fpr_operand" "=f")
        (unspec:SI [(match_operand:SI 1 "fpr_operand" "f")
		    (match_operand:SI 2 "fpr_operand" "f")
		    (match_operand:SI 3 "const_int_operand" "n")]
		   UNSPEC_MLOGIC))]
  "TARGET_MEDIA"
  "*
{
  switch (INTVAL (operands[3]))
  {
  default:		 break;
  case FRV_BUILTIN_MAND: return \"mand %1, %2, %0\";
  case FRV_BUILTIN_MOR:  return \"mor %1, %2, %0\";
  case FRV_BUILTIN_MXOR: return \"mxor %1, %2, %0\";
  }

  fatal_insn (\"Bad media insn, mlogic\", insn);
}"
  [(set_attr "length" "4")
   (set_attr "type" "mlogic")])

(define_insn "*cond_exec_mlogic"
  [(cond_exec
    (match_operator 0 "ccr_eqne_operator"
		    [(match_operand 1 "cr_operand" "C")
		     (const_int 0)])
    (set (match_operand:SI 2 "fpr_operand" "=f")
         (unspec:SI [(match_operand:SI 3 "fpr_operand" "f")
		     (match_operand:SI 4 "fpr_operand" "f")
		     (match_operand:SI 5 "const_int_operand" "n")]
		    UNSPEC_MLOGIC)))]
  "TARGET_MEDIA"
  "*
{
  switch (INTVAL (operands[5]))
  {
  default:		    break;
  case FRV_BUILTIN_MAND: return \"cmand %3, %4, %2, %1, %e0\";
  case FRV_BUILTIN_MOR:  return \"cmor %3, %4, %2, %1, %e0\";
  case FRV_BUILTIN_MXOR: return \"cmxor %3, %4, %2, %1, %e0\";
  }

  fatal_insn (\"Bad media insn, cond_exec_mlogic\", insn);
}"
  [(set_attr "length" "4")
   (set_attr "type" "mlogic")])

;; Logical not: type "mlogic"

(define_insn "mnot"
  [(set (match_operand:SI 0 "fpr_operand" "=f")
        (unspec:SI [(match_operand:SI 1 "fpr_operand" "f")] UNSPEC_MNOT))]
  "TARGET_MEDIA"
  "mnot %1, %0"
  [(set_attr "length" "4")
   (set_attr "type" "mlogic")])

(define_insn "*cond_exec_mnot"
  [(cond_exec
    (match_operator 0 "ccr_eqne_operator"
		    [(match_operand 1 "cr_operand" "C")
		     (const_int 0)])
    (set (match_operand:SI 2 "fpr_operand" "=f")
         (unspec:SI [(match_operand:SI 3 "fpr_operand" "f")] UNSPEC_MNOT)))]
  "TARGET_MEDIA"
  "cmnot %3, %2, %1, %e0"
  [(set_attr "length" "4")
   (set_attr "type" "mlogic")])

;; Dual average (halfword): type "maveh"

(define_insn "maveh"
  [(set (match_operand:SI 0 "fpr_operand" "=f")
        (unspec:SI [(match_operand:SI 1 "fpr_operand" "f")
                    (match_operand:SI 2 "fpr_operand" "f")]
		   UNSPEC_MAVEH))]
  "TARGET_MEDIA"
  "maveh %1, %2, %0"
  [(set_attr "length" "4")
   (set_attr "type" "maveh")])

;; Dual saturation (halfword): type "msath"

(define_expand "msaths"
  [(set (match_operand:SI 0 "fpr_operand" "=f")
        (unspec:SI [(match_operand:SI 1 "fpr_operand" "f")
                    (match_operand:SI 2 "fpr_operand" "f")
		    (match_dup 3)]
                   UNSPEC_MSATH))]
  "TARGET_MEDIA"
  "operands[3] = GEN_INT (FRV_BUILTIN_MSATHS);")

(define_expand "msathu"
  [(set (match_operand:SI 0 "fpr_operand" "=f")
        (unspec:SI [(match_operand:SI 1 "fpr_operand" "f")
                    (match_operand:SI 2 "fpr_operand" "f")
		    (match_dup 3)]
                   UNSPEC_MSATH))]
  "TARGET_MEDIA"
  "operands[3] = GEN_INT (FRV_BUILTIN_MSATHU);")

(define_insn "*msath"
  [(set (match_operand:SI 0 "fpr_operand" "=f")
        (unspec:SI [(match_operand:SI 1 "fpr_operand" "f")
                    (match_operand:SI 2 "fpr_operand" "f")
		    (match_operand:SI 3 "const_int_operand" "n")]
		   UNSPEC_MSATH))]
  "TARGET_MEDIA"
  "*
{
  switch (INTVAL (operands[3]))
  {
  default:		    break;
  case FRV_BUILTIN_MSATHS:  return \"msaths %1, %2, %0\";
  case FRV_BUILTIN_MSATHU:  return \"msathu %1, %2, %0\";
  }

  fatal_insn (\"Bad media insn, msath\", insn);
}"
  [(set_attr "length" "4")
   (set_attr "type" "msath")])

;; Dual addition/subtraction with saturation (halfword): type "maddh"

(define_expand "maddhss"
  [(set (match_operand:SI 0 "fpr_operand" "=f")
        (unspec:SI [(match_operand:SI 1 "fpr_operand" "f")
                    (match_operand:SI 2 "fpr_operand" "f")
		    (match_dup 3)]
		   UNSPEC_MADDH))]
  "TARGET_MEDIA"
  "operands[3] = GEN_INT (FRV_BUILTIN_MADDHSS);")

(define_expand "maddhus"
  [(set (match_operand:SI 0 "fpr_operand" "=f")
        (unspec:SI [(match_operand:SI 1 "fpr_operand" "f")
                    (match_operand:SI 2 "fpr_operand" "f")
		    (match_dup 3)]
                   UNSPEC_MADDH))]
  "TARGET_MEDIA"
  "operands[3] = GEN_INT (FRV_BUILTIN_MADDHUS);")

(define_expand "msubhss"
  [(set (match_operand:SI 0 "fpr_operand" "=f")
        (unspec:SI [(match_operand:SI 1 "fpr_operand" "f")
                    (match_operand:SI 2 "fpr_operand" "f")
		    (match_dup 3)]
                   UNSPEC_MADDH))]
  "TARGET_MEDIA"
  "operands[3] = GEN_INT (FRV_BUILTIN_MSUBHSS);")

(define_expand "msubhus"
  [(set (match_operand:SI 0 "fpr_operand" "=f")
        (unspec:SI [(match_operand:SI 1 "fpr_operand" "f")
                    (match_operand:SI 2 "fpr_operand" "f")
		    (match_dup 3)]
                   UNSPEC_MADDH))]
  "TARGET_MEDIA"
  "operands[3] = GEN_INT (FRV_BUILTIN_MSUBHUS);")

(define_insn "*maddh"
  [(set (match_operand:SI 0 "fpr_operand" "=f")
        (unspec:SI [(match_operand:SI 1 "fpr_operand" "f")
                    (match_operand:SI 2 "fpr_operand" "f")
		    (match_operand:SI 3 "const_int_operand" "n")]
		   UNSPEC_MADDH))]
  "TARGET_MEDIA"
  "*
{
  switch (INTVAL (operands[3]))
  {
  default:		    break;
  case FRV_BUILTIN_MADDHSS: return \"maddhss %1, %2, %0\";
  case FRV_BUILTIN_MADDHUS: return \"maddhus %1, %2, %0\";
  case FRV_BUILTIN_MSUBHSS: return \"msubhss %1, %2, %0\";
  case FRV_BUILTIN_MSUBHUS: return \"msubhus %1, %2, %0\";
  }

  fatal_insn (\"Bad media insn, maddh\", insn);
}"
  [(set_attr "length" "4")
   (set_attr "type" "maddh")])

(define_insn "*cond_exec_maddh"
  [(cond_exec
    (match_operator 0 "ccr_eqne_operator"
		    [(match_operand 1 "cr_operand" "C")
		     (const_int 0)])
    (set (match_operand:SI 2 "fpr_operand" "=f")
	 (unspec:SI [(match_operand:SI 3 "fpr_operand" "f")
		     (match_operand:SI 4 "fpr_operand" "f")
		     (match_operand:SI 5 "const_int_operand" "n")]
		    UNSPEC_MADDH)))]
  "TARGET_MEDIA"
  "*
{
  switch (INTVAL (operands[5]))
  {
  default:		    break;
  case FRV_BUILTIN_MADDHSS: return \"cmaddhss %3, %4, %2, %1, %e0\";
  case FRV_BUILTIN_MADDHUS: return \"cmaddhus %3, %4, %2, %1, %e0\";
  case FRV_BUILTIN_MSUBHSS: return \"cmsubhss %3, %4, %2, %1, %e0\";
  case FRV_BUILTIN_MSUBHUS: return \"cmsubhus %3, %4, %2, %1, %e0\";
  }

  fatal_insn (\"Bad media insn, cond_exec_maddh\", insn);
}"
  [(set_attr "length" "4")
   (set_attr "type" "maddh")])

;; Quad addition/subtraction with saturation (halfword): type "mqaddh"

(define_expand "mqaddhss"
  [(set (match_operand:DI 0 "even_fpr_operand" "=h")
        (unspec:DI [(match_operand:DI 1 "even_fpr_operand" "h")
                    (match_operand:DI 2 "even_fpr_operand" "h")
		    (match_dup 3)]
		   UNSPEC_MQADDH))]
  "TARGET_MEDIA"
  "operands[3] = GEN_INT (FRV_BUILTIN_MQADDHSS);")

(define_expand "mqaddhus"
  [(set (match_operand:DI 0 "even_fpr_operand" "=h")
        (unspec:DI [(match_operand:DI 1 "even_fpr_operand" "h")
                    (match_operand:DI 2 "even_fpr_operand" "h")
		    (match_dup 3)]
		   UNSPEC_MQADDH))]
  "TARGET_MEDIA"
  "operands[3] = GEN_INT (FRV_BUILTIN_MQADDHUS);")

(define_expand "mqsubhss"
  [(set (match_operand:DI 0 "even_fpr_operand" "=h")
        (unspec:DI [(match_operand:DI 1 "even_fpr_operand" "h")
                    (match_operand:DI 2 "even_fpr_operand" "h")
		    (match_dup 3)]
		   UNSPEC_MQADDH))]
  "TARGET_MEDIA"
  "operands[3] = GEN_INT (FRV_BUILTIN_MQSUBHSS);")

(define_expand "mqsubhus"
  [(set (match_operand:DI 0 "even_fpr_operand" "=h")
        (unspec:DI [(match_operand:DI 1 "even_fpr_operand" "h")
                    (match_operand:DI 2 "even_fpr_operand" "h")
		    (match_dup 3)]
		   UNSPEC_MQADDH))]
  "TARGET_MEDIA"
  "operands[3] = GEN_INT (FRV_BUILTIN_MQSUBHUS);")

(define_insn "*mqaddh"
  [(set (match_operand:DI 0 "even_fpr_operand" "=h")
        (unspec:DI [(match_operand:DI 1 "even_fpr_operand" "h")
                    (match_operand:DI 2 "even_fpr_operand" "h")
		    (match_operand:SI 3 "const_int_operand" "n")]
		   UNSPEC_MQADDH))]
  "TARGET_MEDIA"
  "*
{
  switch (INTVAL (operands[3]))
  {
  default:		     break;
  case FRV_BUILTIN_MQADDHSS: return \"mqaddhss %1, %2, %0\";
  case FRV_BUILTIN_MQADDHUS: return \"mqaddhus %1, %2, %0\";
  case FRV_BUILTIN_MQSUBHSS: return \"mqsubhss %1, %2, %0\";
  case FRV_BUILTIN_MQSUBHUS: return \"mqsubhus %1, %2, %0\";
  }

  fatal_insn (\"Bad media insn, mqaddh\", insn);
}"
  [(set_attr "length" "4")
   (set_attr "type" "mqaddh")])

(define_insn "*cond_exec_mqaddh"
  [(cond_exec
    (match_operator 0 "ccr_eqne_operator"
		    [(match_operand 1 "cr_operand" "C")
		     (const_int 0)])
    (set (match_operand:DI 2 "even_fpr_operand" "=h")
         (unspec:DI [(match_operand:DI 3 "even_fpr_operand" "h")
                     (match_operand:DI 4 "even_fpr_operand" "h")
		     (match_operand:SI 5 "const_int_operand" "n")]
		    UNSPEC_MQADDH)))]
  "TARGET_MEDIA"
  "*
{
  switch (INTVAL (operands[5]))
  {
  default:		     break;
  case FRV_BUILTIN_MQADDHSS: return \"cmqaddhss %3, %4, %2, %1, %e0\";
  case FRV_BUILTIN_MQADDHUS: return \"cmqaddhus %3, %4, %2, %1, %e0\";
  case FRV_BUILTIN_MQSUBHSS: return \"cmqsubhss %3, %4, %2, %1, %e0\";
  case FRV_BUILTIN_MQSUBHUS: return \"cmqsubhus %3, %4, %2, %1, %e0\";
  }

  fatal_insn (\"Bad media insn, cond_exec_mqaddh\", insn);
}"
  [(set_attr "length" "4")
   (set_attr "type" "mqaddh")])

;; Pack halfword: type "mpackh"

(define_insn "mpackh"
  [(set (match_operand:SI 0 "fpr_operand" "=f")
        (unspec:SI [(match_operand:HI 1 "fpr_operand" "f")
                    (match_operand:HI 2 "fpr_operand" "f")]
		   UNSPEC_MPACKH))]
  "TARGET_MEDIA"
  "mpackh %1, %2, %0"
  [(set_attr "length" "4")
   (set_attr "type" "mpackh")])

;; Unpack halfword: type "mpackh"

(define_insn "munpackh"
  [(set (match_operand:DI 0 "even_fpr_operand" "=h")
        (unspec:DI [(match_operand:SI 1 "fpr_operand" "f")]
		   UNSPEC_MUNPACKH))]
  "TARGET_MEDIA"
  "munpackh %1, %0"
  [(set_attr "length" "4")
   (set_attr "type" "munpackh")])

;; Dual pack halfword: type "mdpackh"

(define_insn "mdpackh"
    [(set (match_operand:DI 0 "even_fpr_operand" "=h")
	  (unspec:DI [(match_operand:DI 1 "even_fpr_operand" "h")
		      (match_operand:DI 2 "even_fpr_operand" "h")]
		     UNSPEC_MDPACKH))]
  "TARGET_MEDIA"
  "mdpackh %1, %2, %0"
  [(set_attr "length" "4")
   (set_attr "type" "mdpackh")])

;; Byte-halfword conversion: type "mbhconv"

(define_insn "mbtoh"
  [(set (match_operand:DI 0 "even_fpr_operand" "=h")
        (unspec:DI [(match_operand:SI 1 "fpr_operand" "f")]
		   UNSPEC_MBTOH))]
  "TARGET_MEDIA"
  "mbtoh %1, %0"
  [(set_attr "length" "4")
   (set_attr "type" "mbhconv")])

(define_insn "*cond_exec_mbtoh"
  [(cond_exec
    (match_operator 0 "ccr_eqne_operator"
		    [(match_operand 1 "cr_operand" "C")
		     (const_int 0)])
    (set (match_operand:DI 2 "even_fpr_operand" "=h")
	 (unspec:DI [(match_operand:SI 3 "fpr_operand" "f")]
		    UNSPEC_MBTOH)))]
  "TARGET_MEDIA"
  "cmbtoh %3, %2, %1, %e0"
  [(set_attr "length" "4")
   (set_attr "type" "mbhconv")])

(define_insn "mhtob"
  [(set (match_operand:SI 0 "fpr_operand" "=f")
        (unspec:SI [(match_operand:DI 1 "even_fpr_operand" "h")]
		   UNSPEC_MHTOB))]
  "TARGET_MEDIA"
  "mhtob %1, %0"
  [(set_attr "length" "4")
   (set_attr "type" "mbhconv")])

(define_insn "*cond_exec_mhtob"
  [(cond_exec
    (match_operator 0 "ccr_eqne_operator"
		    [(match_operand 1 "cr_operand" "C")
		     (const_int 0)])
    (set (match_operand:SI 2 "fpr_operand" "=f")
	 (unspec:SI [(match_operand:DI 3 "even_fpr_operand" "h")]
		    UNSPEC_MHTOB)))]
  "TARGET_MEDIA"
  "cmhtob %3, %2, %1, %e0"
  [(set_attr "length" "4")
   (set_attr "type" "mbhconv")])

;; Rotate: type "mrot"

(define_expand "mrotli"
  [(set (match_operand:SI 0 "fpr_operand" "")
        (unspec:SI [(match_operand:SI 1 "fpr_operand" "")
                    (match_operand:SI 2 "uint5_operand" "")
		    (match_dup 3)]
		   UNSPEC_MROT))]
  "TARGET_MEDIA"
  "operands[3] = GEN_INT (FRV_BUILTIN_MROTLI);")

(define_expand "mrotri"
  [(set (match_operand:SI 0 "fpr_operand" "")
        (unspec:SI [(match_operand:SI 1 "fpr_operand" "")
                    (match_operand:SI 2 "uint5_operand" "")
		    (match_dup 3)]
		   UNSPEC_MROT))]
  "TARGET_MEDIA"
  "operands[3] = GEN_INT (FRV_BUILTIN_MROTRI);")

(define_insn "*mrot"
  [(set (match_operand:SI 0 "fpr_operand" "=f")
        (unspec:SI [(match_operand:SI 1 "fpr_operand" "f")
                    (match_operand:SI 2 "uint5_operand" "I")
		    (match_operand:SI 3 "const_int_operand" "n")]
		   UNSPEC_MROT))]
  "TARGET_MEDIA"
  "*
{
  switch (INTVAL (operands[3]))
  {
  default:		   break;
  case FRV_BUILTIN_MROTLI: return \"mrotli %1, %2, %0\";
  case FRV_BUILTIN_MROTRI: return \"mrotri %1, %2, %0\";
  }

  fatal_insn (\"Bad media insn, mrot\", insn);
}"
  [(set_attr "length" "4")
   (set_attr "type" "mrot")])

;; Dual shift halfword: type "msh"

(define_expand "msllhi"
  [(set (match_operand:SI 0 "fpr_operand" "")
        (unspec:SI [(match_operand:SI 1 "fpr_operand" "")
                    (match_operand:SI 2 "uint4_operand" "")
		    (match_dup 3)]
		   UNSPEC_MSHIFT))]
  "TARGET_MEDIA"
  "operands[3] = GEN_INT (FRV_BUILTIN_MSLLHI);")

(define_expand "msrlhi"
  [(set (match_operand:SI 0 "fpr_operand" "")
        (unspec:SI [(match_operand:SI 1 "fpr_operand" "")
                    (match_operand:SI 2 "uint4_operand" "")
		    (match_dup 3)]
		   UNSPEC_MSHIFT))]
  "TARGET_MEDIA"
  "operands[3] = GEN_INT (FRV_BUILTIN_MSRLHI);")

(define_expand "msrahi"
  [(set (match_operand:SI 0 "fpr_operand" "")
        (unspec:SI [(match_operand:SI 1 "fpr_operand" "")
                    (match_operand:SI 2 "uint4_operand" "")
		    (match_dup 3)]
		   UNSPEC_MSHIFT))]
  "TARGET_MEDIA"
  "operands[3] = GEN_INT (FRV_BUILTIN_MSRAHI);")

(define_insn "*mshift"
  [(set (match_operand:SI 0 "fpr_operand" "=f")
        (unspec:SI [(match_operand:SI 1 "fpr_operand" "f")
                    (match_operand:SI 2 "uint4_operand" "I")
		    (match_operand:SI 3 "const_int_operand" "n")]
		   UNSPEC_MSHIFT))]
  "TARGET_MEDIA"
  "*
{
  switch (INTVAL (operands[3]))
  {
  default:		   break;
  case FRV_BUILTIN_MSLLHI: return \"msllhi %1, %2, %0\";
  case FRV_BUILTIN_MSRLHI: return \"msrlhi %1, %2, %0\";
  case FRV_BUILTIN_MSRAHI: return \"msrahi %1, %2, %0\";
  }

  fatal_insn (\"Bad media insn, mshift\", insn);
}"
  [(set_attr "length" "4")
   (set_attr "type" "mshift")])

;; Expand halfword to word: type "mexpdhw"

(define_insn "mexpdhw"
  [(set (match_operand:SI 0 "fpr_operand" "=f")
        (unspec:SI [(match_operand:SI 1 "fpr_operand" "f")
                    (match_operand:SI 2 "uint1_operand" "I")]
		   UNSPEC_MEXPDHW))]
  "TARGET_MEDIA"
  "mexpdhw %1, %2, %0"
  [(set_attr "length" "4")
   (set_attr "type" "mexpdhw")])

(define_insn "*cond_exec_mexpdhw"
  [(cond_exec
    (match_operator 0 "ccr_eqne_operator"
		    [(match_operand 1 "cr_operand" "C")
		     (const_int 0)])
    (set (match_operand:SI 2 "fpr_operand" "=f")
	 (unspec:SI [(match_operand:SI 3 "fpr_operand" "f")
		     (match_operand:SI 4 "uint1_operand" "I")]
		    UNSPEC_MEXPDHW)))]
  "TARGET_MEDIA"
  "cmexpdhw %3, %4, %2, %1, %e0"
  [(set_attr "length" "4")
   (set_attr "type" "mexpdhw")])

;; Expand halfword to double: type "mexpdhd"

(define_insn "mexpdhd"
  [(set (match_operand:DI 0 "even_fpr_operand" "=h")
        (unspec:DI [(match_operand:SI 1 "fpr_operand" "f")
                    (match_operand:SI 2 "uint1_operand" "I")]
		   UNSPEC_MEXPDHD))]
  "TARGET_MEDIA"
  "mexpdhd %1, %2, %0"
  [(set_attr "length" "4")
   (set_attr "type" "mexpdhd")])

(define_insn "*cond_exec_mexpdhd"
  [(cond_exec
    (match_operator 0 "ccr_eqne_operator"
		    [(match_operand 1 "cr_operand" "C")
		     (const_int 0)])
    (set (match_operand:DI 2 "even_fpr_operand" "=h")
	 (unspec:DI [(match_operand:SI 3 "fpr_operand" "f")
		     (match_operand:SI 4 "uint1_operand" "I")]
		    UNSPEC_MEXPDHD)))]
  "TARGET_MEDIA"
  "cmexpdhd %3, %4, %2, %1, %e0"
  [(set_attr "length" "4")
   (set_attr "type" "mexpdhd")])

;; FR cut: type "mwcut"

(define_insn "mwcut"
  [(set (match_operand:SI 0 "fpr_operand" "=f")
        (unspec:SI [(match_operand:DI 1 "fpr_operand" "f")
                    (match_operand:SI 2 "fpr_or_int6_operand" "fI")]
		   UNSPEC_MWCUT))]
  "TARGET_MEDIA"
  "mwcut%i2 %1, %2, %0"
  [(set_attr "length" "4")
   (set_attr "type" "mwcut")])

;; Dual multiplication (halfword): type "mmulh"

(define_expand "mmulhs"
  [(parallel [(set (match_operand:DI 0 "even_acc_operand" "=b")
		   (unspec:DI [(match_operand:SI 1 "fpr_operand" "f")
			       (match_operand:SI 2 "fpr_operand" "f")
			       (match_dup 4)]
			      UNSPEC_MMULH))
	      (set (match_operand:HI 3 "accg_operand" "=B")
		   (unspec:HI [(const_int 0)] UNSPEC_MMULH))])]
  "TARGET_MEDIA"
  "operands[4] = GEN_INT (FRV_BUILTIN_MMULHS);")

(define_expand "mmulhu"
  [(parallel [(set (match_operand:DI 0 "even_acc_operand" "=b")
		   (unspec:DI [(match_operand:SI 1 "fpr_operand" "f")
			       (match_operand:SI 2 "fpr_operand" "f")
			       (match_dup 4)]
			      UNSPEC_MMULH))
	      (set (match_operand:HI 3 "accg_operand" "=B")
		   (unspec:HI [(const_int 0)] UNSPEC_MMULH))])]
  "TARGET_MEDIA"
  "operands[4] = GEN_INT (FRV_BUILTIN_MMULHU);")

(define_insn "*mmulh"
  [(set (match_operand:DI 0 "even_acc_operand" "=b")
        (unspec:DI [(match_operand:SI 1 "fpr_operand" "f")
                    (match_operand:SI 2 "fpr_operand" "f")
		    (match_operand:SI 3 "const_int_operand" "n")]
		   UNSPEC_MMULH))
   (set (match_operand:HI 4 "accg_operand" "=B")
	(unspec:HI [(const_int 0)] UNSPEC_MMULH))]
  "TARGET_MEDIA"
  "*
{
  switch (INTVAL (operands[3]))
  {
  default:		    break;
  case FRV_BUILTIN_MMULHS:  return \"mmulhs %1, %2, %0\";
  case FRV_BUILTIN_MMULHU:  return \"mmulhu %1, %2, %0\";
  }

  fatal_insn (\"Bad media insn, mmulh\", insn);
}"
  [(set_attr "length" "4")
   (set_attr "type" "mmulh")])

(define_insn "*cond_exec_mmulh"
  [(cond_exec
    (match_operator 0 "ccr_eqne_operator"
		    [(match_operand 1 "cr_operand" "C")
		     (const_int 0)])
    (parallel [(set (match_operand:DI 2 "even_acc_operand" "=b")
		    (unspec:DI [(match_operand:SI 3 "fpr_operand" "f")
				(match_operand:SI 4 "fpr_operand" "f")
				(match_operand:SI 5 "const_int_operand" "n")]
			       UNSPEC_MMULH))
	       (set (match_operand:HI 6 "accg_operand" "=B")
		    (unspec:HI [(const_int 0)] UNSPEC_MMULH))]))]
  "TARGET_MEDIA"
  "*
{
  switch (INTVAL (operands[5]))
  {
  default:		    break;
  case FRV_BUILTIN_MMULHS:  return \"cmmulhs %3, %4, %2, %1, %e0\";
  case FRV_BUILTIN_MMULHU:  return \"cmmulhu %3, %4, %2, %1, %e0\";
  }

  fatal_insn (\"Bad media insn, cond_exec_mmulh\", insn);
}"
  [(set_attr "length" "4")
   (set_attr "type" "mmulh")])

;; Dual cross multiplication (halfword): type "mmulxh"

(define_expand "mmulxhs"
  [(parallel [(set (match_operand:DI 0 "even_acc_operand" "=b")
		   (unspec:DI [(match_operand:SI 1 "fpr_operand" "f")
			       (match_operand:SI 2 "fpr_operand" "f")
			       (match_dup 4)]
			      UNSPEC_MMULXH))
	      (set (match_operand:HI 3 "accg_operand" "=B")
		   (unspec:HI [(const_int 0)] UNSPEC_MMULXH))])]
  "TARGET_MEDIA"
  "operands[4] = GEN_INT (FRV_BUILTIN_MMULXHS);")

(define_expand "mmulxhu"
  [(parallel [(set (match_operand:DI 0 "even_acc_operand" "=b")
		   (unspec:DI [(match_operand:SI 1 "fpr_operand" "f")
			       (match_operand:SI 2 "fpr_operand" "f")
			       (match_dup 4)]
			      UNSPEC_MMULXH))
	      (set (match_operand:HI 3 "accg_operand" "=B")
		   (unspec:HI [(const_int 0)] UNSPEC_MMULXH))])]
  "TARGET_MEDIA"
  "operands[4] = GEN_INT (FRV_BUILTIN_MMULXHU);")

(define_insn "*mmulxh"
  [(set (match_operand:DI 0 "even_acc_operand" "=b")
        (unspec:DI [(match_operand:SI 1 "fpr_operand" "f")
                    (match_operand:SI 2 "fpr_operand" "f")
		    (match_operand:SI 3 "const_int_operand" "n")]
		   UNSPEC_MMULXH))
   (set (match_operand:HI 4 "accg_operand" "=B")
	(unspec:HI [(const_int 0)] UNSPEC_MMULXH))]
  "TARGET_MEDIA"
  "*
{
  switch (INTVAL (operands[3]))
  {
  default:		    break;
  case FRV_BUILTIN_MMULXHS: return \"mmulxhs %1, %2, %0\";
  case FRV_BUILTIN_MMULXHU: return \"mmulxhu %1, %2, %0\";
  }

  fatal_insn (\"Bad media insn, mmulxh\", insn);
}"
  [(set_attr "length" "4")
   (set_attr "type" "mmulxh")])

;; Dual product-sum (halfword): type "mmach"

(define_expand "mmachs"
  [(parallel [(set (match_operand:DI 0 "even_acc_operand" "+b")
		   (unspec:DI [(match_dup 0)
			       (match_operand:SI 1 "fpr_operand" "f")
			       (match_operand:SI 2 "fpr_operand" "f")
			       (match_operand:HI 3 "accg_operand" "+B")
			       (match_dup 4)]
			      UNSPEC_MMACH))
	      (set (match_dup 3)
		   (unspec:HI [(const_int 0)] UNSPEC_MMACH))])]
  "TARGET_MEDIA"
  "operands[4] = GEN_INT (FRV_BUILTIN_MMACHS);")

(define_expand "mmachu"
  [(parallel [(set (match_operand:DI 0 "even_acc_operand" "+b")
		   (unspec:DI [(match_dup 0)
			       (match_operand:SI 1 "fpr_operand" "f")
			       (match_operand:SI 2 "fpr_operand" "f")
			       (match_operand:HI 3 "accg_operand" "+B")
			       (match_dup 4)]
			      UNSPEC_MMACH))
	      (set (match_dup 3)
		   (unspec:HI [(const_int 0)] UNSPEC_MMACH))])]
  "TARGET_MEDIA"
  "operands[4] = GEN_INT (FRV_BUILTIN_MMACHU);")

(define_insn "*mmach"
  [(set (match_operand:DI 0 "even_acc_operand" "+b")
        (unspec:DI [(match_dup 0)
		    (match_operand:SI 1 "fpr_operand" "f")
                    (match_operand:SI 2 "fpr_operand" "f")
		    (match_operand:HI 3 "accg_operand" "+B")
		    (match_operand:SI 4 "const_int_operand" "n")]
		   UNSPEC_MMACH))
   (set (match_dup 3) (unspec:HI [(const_int 0)] UNSPEC_MMACH))]
  "TARGET_MEDIA"
  "*
{
  switch (INTVAL (operands[4]))
  {
  default:		   break;
  case FRV_BUILTIN_MMACHS: return \"mmachs %1, %2, %0\";
  case FRV_BUILTIN_MMACHU: return \"mmachu %1, %2, %0\";
  }

  fatal_insn (\"Bad media insn, mmach\", insn);
}"
  [(set_attr "length" "4")
   (set_attr "type" "mmach")])

(define_insn "*cond_exec_mmach"
  [(cond_exec
    (match_operator 0 "ccr_eqne_operator"
		    [(match_operand 1 "cr_operand" "C")
		     (const_int 0)])
    (parallel [(set (match_operand:DI 2 "even_acc_operand" "+b")
		    (unspec:DI [(match_dup 2)
				(match_operand:SI 3 "fpr_operand" "f")
				(match_operand:SI 4 "fpr_operand" "f")
				(match_operand:HI 5 "accg_operand" "+B")
				(match_operand:SI 6 "const_int_operand" "n")]
			       UNSPEC_MMACH))
	       (set (match_dup 5)
		    (unspec:HI [(const_int 0)] UNSPEC_MMACH))]))]
  "TARGET_MEDIA"
  "*
{
  switch (INTVAL (operands[6]))
  {
  default:		   break;
  case FRV_BUILTIN_MMACHS: return \"cmmachs %3, %4, %2, %1, %e0\";
  case FRV_BUILTIN_MMACHU: return \"cmmachu %3, %4, %2, %1, %e0\";
  }

  fatal_insn (\"Bad media insn, cond_exec_mmach\", insn);
}"
  [(set_attr "length" "4")
   (set_attr "type" "mmach")])

;; Dual product-difference: type "mmrdh"

(define_expand "mmrdhs"
  [(parallel [(set (match_operand:DI 0 "even_acc_operand" "+b")
		   (unspec:DI [(match_dup 0)
			       (match_operand:SI 1 "fpr_operand" "f")
			       (match_operand:SI 2 "fpr_operand" "f")
			       (match_operand:HI 3 "accg_operand" "+B")
			       (match_dup 4)]
			      UNSPEC_MMRDH))
	      (set (match_dup 3)
		   (unspec:HI [(const_int 0)] UNSPEC_MMRDH))])]
  "TARGET_MEDIA"
  "operands[4] = GEN_INT (FRV_BUILTIN_MMRDHS);")

(define_expand "mmrdhu"
  [(parallel [(set (match_operand:DI 0 "even_acc_operand" "+b")
		   (unspec:DI [(match_dup 0)
			       (match_operand:SI 1 "fpr_operand" "f")
			       (match_operand:SI 2 "fpr_operand" "f")
			       (match_operand:HI 3 "accg_operand" "+B")
			       (match_dup 4)]
			      UNSPEC_MMRDH))
	      (set (match_dup 3)
		   (unspec:HI [(const_int 0)] UNSPEC_MMRDH))])]
  "TARGET_MEDIA"
  "operands[4] = GEN_INT (FRV_BUILTIN_MMRDHU);")

(define_insn "*mmrdh"
  [(set (match_operand:DI 0 "even_acc_operand" "+b")
        (unspec:DI [(match_dup 0)
		    (match_operand:SI 1 "fpr_operand" "f")
                    (match_operand:SI 2 "fpr_operand" "f")
		    (match_operand:HI 3 "accg_operand" "+B")
		    (match_operand:SI 4 "const_int_operand" "n")]
		   UNSPEC_MMRDH))
   (set (match_dup 3)
	(unspec:HI [(const_int 0)] UNSPEC_MMRDH))]
  "TARGET_MEDIA"
  "*
{
  switch (INTVAL (operands[4]))
  {
  default:		   break;
  case FRV_BUILTIN_MMRDHS: return \"mmrdhs %1, %2, %0\";
  case FRV_BUILTIN_MMRDHU: return \"mmrdhu %1, %2, %0\";
  }

  fatal_insn (\"Bad media insn, mrdh\", insn);
}"
  [(set_attr "length" "4")
   (set_attr "type" "mmrdh")])

;; Quad multiply (halfword): type "mqmulh"

(define_expand "mqmulhs"
  [(parallel [(set (match_operand:V4SI 0 "quad_acc_operand" "=A")
		   (unspec:V4SI [(match_operand:DI 1 "even_fpr_operand" "h")
				 (match_operand:DI 2 "even_fpr_operand" "h")
				 (match_dup 4)]
				UNSPEC_MQMULH))
	      (set (match_operand:V4QI 3 "accg_operand" "=B")
		   (unspec:V4QI [(const_int 0)] UNSPEC_MQMULH))])]
  "TARGET_MEDIA"
  "operands[4] = GEN_INT (FRV_BUILTIN_MQMULHS);")

(define_expand "mqmulhu"
  [(parallel [(set (match_operand:V4SI 0 "quad_acc_operand" "=A")
		   (unspec:V4SI [(match_operand:DI 1 "even_fpr_operand" "h")
				 (match_operand:DI 2 "even_fpr_operand" "h")
				 (match_dup 4)]
				UNSPEC_MQMULH))
	      (set (match_operand:V4QI 3 "accg_operand" "=B")
		   (unspec:V4QI [(const_int 0)] UNSPEC_MQMULH))])]
  "TARGET_MEDIA"
  "operands[4] = GEN_INT (FRV_BUILTIN_MQMULHU);")

(define_insn "*mqmulh"
  [(set (match_operand:V4SI 0 "quad_acc_operand" "=A")
        (unspec:V4SI [(match_operand:DI 1 "even_fpr_operand" "h")
		      (match_operand:DI 2 "even_fpr_operand" "h")
		      (match_operand:SI 3 "const_int_operand" "n")]
		     UNSPEC_MQMULH))
   (set (match_operand:V4QI 4 "accg_operand" "=B")
	(unspec:V4QI [(const_int 0)] UNSPEC_MQMULH))]
  "TARGET_MEDIA"
  "*
{
  switch (INTVAL (operands[3]))
  {
  default:		     break;
  case FRV_BUILTIN_MQMULHS:  return \"mqmulhs %1, %2, %0\";
  case FRV_BUILTIN_MQMULHU:  return \"mqmulhu %1, %2, %0\";
  }

  fatal_insn (\"Bad media insn, mqmulh\", insn);
}"
  [(set_attr "length" "4")
   (set_attr "type" "mqmulh")])

(define_insn "*cond_exec_mqmulh"
  [(cond_exec
    (match_operator 0 "ccr_eqne_operator"
		    [(match_operand 1 "cr_operand" "C")
		     (const_int 0)])
    (parallel [(set (match_operand:V4SI 2 "quad_acc_operand" "=A")
		    (unspec:V4SI [(match_operand:DI 3 "even_fpr_operand" "h")
				  (match_operand:DI 4 "even_fpr_operand" "h")
				  (match_operand:SI 5 "const_int_operand" "n")]
				 UNSPEC_MQMULH))
	       (set (match_operand:V4QI 6 "accg_operand" "=B")
		    (unspec:V4QI [(const_int 0)] UNSPEC_MQMULH))]))]
  "TARGET_MEDIA"
  "*
{
  switch (INTVAL (operands[5]))
  {
  default:		     break;
  case FRV_BUILTIN_MQMULHS:  return \"cmqmulhs %3, %4, %2, %1, %e0\";
  case FRV_BUILTIN_MQMULHU:  return \"cmqmulhu %3, %4, %2, %1, %e0\";
  }

  fatal_insn (\"Bad media insn, cond_exec_mqmulh\", insn);
}"
  [(set_attr "length" "4")
   (set_attr "type" "mqmulh")])

;; Quad cross multiply (halfword): type "mqmulxh"

(define_expand "mqmulxhs"
  [(parallel [(set (match_operand:V4SI 0 "quad_acc_operand" "=A")
		   (unspec:V4SI [(match_operand:DI 1 "even_fpr_operand" "h")
				 (match_operand:DI 2 "even_fpr_operand" "h")
				 (match_dup 4)]
				UNSPEC_MQMULXH))
	      (set (match_operand:V4QI 3 "accg_operand" "=B")
		   (unspec:V4QI [(const_int 0)] UNSPEC_MQMULXH))])]
  "TARGET_MEDIA"
  "operands[4] = GEN_INT (FRV_BUILTIN_MQMULXHS);")

(define_expand "mqmulxhu"
  [(parallel [(set (match_operand:V4SI 0 "quad_acc_operand" "=A")
		   (unspec:V4SI [(match_operand:DI 1 "even_fpr_operand" "h")
				 (match_operand:DI 2 "even_fpr_operand" "h")
				 (match_dup 4)]
				UNSPEC_MQMULXH))
	      (set (match_operand:V4QI 3 "accg_operand" "=B")
		   (unspec:V4QI [(const_int 0)] UNSPEC_MQMULXH))])]
  "TARGET_MEDIA"
  "operands[4] = GEN_INT (FRV_BUILTIN_MQMULXHU);")

(define_insn "*mqmulxh"
  [(set (match_operand:V4SI 0 "quad_acc_operand" "=A")
        (unspec:V4SI [(match_operand:DI 1 "even_fpr_operand" "h")
		      (match_operand:DI 2 "even_fpr_operand" "h")
		      (match_operand:SI 3 "const_int_operand" "n")]
		     UNSPEC_MQMULXH))
   (set (match_operand:V4QI 4 "accg_operand" "=B")
	(unspec:V4QI [(const_int 0)] UNSPEC_MQMULXH))]
  "TARGET_MEDIA"
  "*
{
  switch (INTVAL (operands[3]))
  {
  default:		     break;
  case FRV_BUILTIN_MQMULXHS: return \"mqmulxhs %1, %2, %0\";
  case FRV_BUILTIN_MQMULXHU: return \"mqmulxhu %1, %2, %0\";
  }

  fatal_insn (\"Bad media insn, mqmulxh\", insn);
}"
  [(set_attr "length" "4")
   (set_attr "type" "mqmulxh")])

;; Quad product-sum (halfword): type "mqmach"

(define_expand "mqmachs"
  [(parallel [(set (match_operand:V4SI 0 "even_acc_operand" "+A")
		   (unspec:V4SI [(match_dup 0)
				 (match_operand:DI 1 "even_fpr_operand" "h")
				 (match_operand:DI 2 "even_fpr_operand" "h")
				 (match_operand:V4QI 3 "accg_operand" "+B")
				 (match_dup 4)]
				UNSPEC_MQMACH))
	      (set (match_dup 3)
		   (unspec:V4QI [(const_int 0)] UNSPEC_MQMACH))])]
  "TARGET_MEDIA"
  "operands[4] = GEN_INT (FRV_BUILTIN_MQMACHS);")

(define_expand "mqmachu"
  [(parallel [(set (match_operand:V4SI 0 "even_acc_operand" "+A")
		   (unspec:V4SI [(match_dup 0)
				 (match_operand:DI 1 "even_fpr_operand" "h")
				 (match_operand:DI 2 "even_fpr_operand" "h")
				 (match_operand:V4QI 3 "accg_operand" "+B")
				 (match_dup 4)]
				UNSPEC_MQMACH))
	      (set (match_dup 3)
		   (unspec:V4QI [(const_int 0)] UNSPEC_MQMACH))])]
  "TARGET_MEDIA"
  "operands[4] = GEN_INT (FRV_BUILTIN_MQMACHU);")

(define_insn "*mqmach"
  [(set (match_operand:V4SI 0 "even_acc_operand" "+A")
        (unspec:V4SI [(match_dup 0)
		      (match_operand:DI 1 "even_fpr_operand" "h")
		      (match_operand:DI 2 "even_fpr_operand" "h")
		      (match_operand:V4QI 3 "accg_operand" "+B")
		      (match_operand:SI 4 "const_int_operand" "n")]
		     UNSPEC_MQMACH))
   (set (match_dup 3)
	(unspec:V4QI [(const_int 0)] UNSPEC_MQMACH))]
  "TARGET_MEDIA"
  "*
{
  switch (INTVAL (operands[4]))
  {
  default:		    break;
  case FRV_BUILTIN_MQMACHS: return \"mqmachs %1, %2, %0\";
  case FRV_BUILTIN_MQMACHU: return \"mqmachu %1, %2, %0\";
  }

  fatal_insn (\"Bad media insn, mqmach\", insn);
}"
  [(set_attr "length" "4")
   (set_attr "type" "mqmach")])

(define_insn "*cond_exec_mqmach"
  [(cond_exec
    (match_operator 0 "ccr_eqne_operator"
		    [(match_operand 1 "cr_operand" "C")
		     (const_int 0)])
    (parallel [(set (match_operand:V4SI 2 "even_acc_operand" "+A")
		    (unspec:V4SI [(match_dup 2)
				  (match_operand:DI 3 "even_fpr_operand" "h")
				  (match_operand:DI 4 "even_fpr_operand" "h")
				  (match_operand:V4QI 5 "accg_operand" "+B")
				  (match_operand:SI 6 "const_int_operand" "n")]
				 UNSPEC_MQMACH))
	       (set (match_dup 5)
		    (unspec:V4QI [(const_int 0)] UNSPEC_MQMACH))]))]
  "TARGET_MEDIA"
  "*
{
  switch (INTVAL (operands[6]))
  {
  default:		    break;
  case FRV_BUILTIN_MQMACHS: return \"cmqmachs %3, %4, %2, %1, %e0\";
  case FRV_BUILTIN_MQMACHU: return \"cmqmachu %3, %4, %2, %1, %e0\";
  }

  fatal_insn (\"Bad media insn, cond_exec_mqmach\", insn);
}"
  [(set_attr "length" "4")
   (set_attr "type" "mqmach")])

;; Dual complex number product-sum (halfword)

(define_expand "mcpxrs"
  [(parallel [(set (match_operand:SI 0 "acc_operand" "=a")
		   (unspec:SI [(match_operand:SI 1 "fpr_operand" "f")
			       (match_operand:SI 2 "fpr_operand" "f")
			       (match_dup 4)]
			      UNSPEC_MCPX))
	      (set (match_operand:QI 3 "accg_operand" "=B")
		   (unspec:QI [(const_int 0)] UNSPEC_MCPX))])]
  "TARGET_MEDIA"
  "operands[4] = GEN_INT (FRV_BUILTIN_MCPXRS);")

(define_expand "mcpxru"
  [(parallel [(set (match_operand:SI 0 "acc_operand" "=a")
		   (unspec:SI [(match_operand:SI 1 "fpr_operand" "f")
			       (match_operand:SI 2 "fpr_operand" "f")
			       (match_dup 4)]
			      UNSPEC_MCPX))
	      (set (match_operand:QI 3 "accg_operand" "=B")
		   (unspec:QI [(const_int 0)] UNSPEC_MCPX))])]
  "TARGET_MEDIA"
  "operands[4] = GEN_INT (FRV_BUILTIN_MCPXRU);")

(define_expand "mcpxis"
  [(parallel [(set (match_operand:SI 0 "acc_operand" "=a")
		   (unspec:SI [(match_operand:SI 1 "fpr_operand" "f")
			       (match_operand:SI 2 "fpr_operand" "f")
			       (match_dup 4)]
			      UNSPEC_MCPX))
	      (set (match_operand:QI 3 "accg_operand" "=B")
		   (unspec:QI [(const_int 0)] UNSPEC_MCPX))])]
  "TARGET_MEDIA"
  "operands[4] = GEN_INT (FRV_BUILTIN_MCPXIS);")

(define_expand "mcpxiu"
  [(parallel [(set (match_operand:SI 0 "acc_operand" "=a")
		   (unspec:SI [(match_operand:SI 1 "fpr_operand" "f")
			       (match_operand:SI 2 "fpr_operand" "f")
			       (match_dup 4)]
			      UNSPEC_MCPX))
	      (set (match_operand:QI 3 "accg_operand" "=B")
		   (unspec:QI [(const_int 0)] UNSPEC_MCPX))])]
  "TARGET_MEDIA"
  "operands[4] = GEN_INT (FRV_BUILTIN_MCPXIU);")

(define_insn "*mcpx"
  [(parallel [(set (match_operand:SI 0 "acc_operand" "=a")
		   (unspec:SI [(match_operand:SI 1 "fpr_operand" "f")
			       (match_operand:SI 2 "fpr_operand" "f")
			       (match_operand:SI 3 "const_int_operand" "n")]
			      UNSPEC_MCPX))
	      (set (match_operand:QI 4 "accg_operand" "=B")
		   (unspec:QI [(const_int 0)] UNSPEC_MCPX))])]
  "TARGET_MEDIA"
  "*
{
  switch (INTVAL (operands[3]))
  {
  default:		   break;
  case FRV_BUILTIN_MCPXRS: return \"mcpxrs %1, %2, %0\";
  case FRV_BUILTIN_MCPXRU: return \"mcpxru %1, %2, %0\";
  case FRV_BUILTIN_MCPXIS: return \"mcpxis %1, %2, %0\";
  case FRV_BUILTIN_MCPXIU: return \"mcpxiu %1, %2, %0\";
  }

  fatal_insn (\"Bad media insn, mcpx\", insn);
}"
  [(set_attr "length" "4")
   (set_attr "type" "mcpx")])

(define_insn "*cond_exec_mcpx"
  [(cond_exec
    (match_operator 0 "ccr_eqne_operator"
		    [(match_operand 1 "cr_operand" "C")
		     (const_int 0)])
    (parallel [(set (match_operand:SI 2 "acc_operand" "=a")
		    (unspec:SI [(match_operand:SI 3 "fpr_operand" "f")
				(match_operand:SI 4 "fpr_operand" "f")
				(match_operand:SI 5 "const_int_operand" "n")]
			       UNSPEC_MCPX))
	       (set (match_operand:QI 6 "accg_operand" "=B")
		    (unspec:QI [(const_int 0)] UNSPEC_MCPX))]))]
  "TARGET_MEDIA"
  "*
{
  switch (INTVAL (operands[5]))
  {
  default:		   break;
  case FRV_BUILTIN_MCPXRS: return \"cmcpxrs %3, %4, %2, %1, %e0\";
  case FRV_BUILTIN_MCPXRU: return \"cmcpxru %3, %4, %2, %1, %e0\";
  case FRV_BUILTIN_MCPXIS: return \"cmcpxis %3, %4, %2, %1, %e0\";
  case FRV_BUILTIN_MCPXIU: return \"cmcpxiu %3, %4, %2, %1, %e0\";
  }

  fatal_insn (\"Bad media insn, cond_exec_mcpx\", insn);
}"
  [(set_attr "length" "4")
   (set_attr "type" "mcpx")])

;; Quad complex number product-sum (halfword): type "mqcpx"

(define_expand "mqcpxrs"
  [(parallel [(set (match_operand:DI 0 "even_acc_operand" "=b")
		   (unspec:DI [(match_operand:DI 1 "fpr_operand" "f")
			       (match_operand:DI 2 "fpr_operand" "f")
			       (match_dup 4)]
			      UNSPEC_MQCPX))
	      (set (match_operand:HI 3 "accg_operand" "=B")
		   (unspec:HI [(const_int 0)] UNSPEC_MQCPX))])]
  "TARGET_MEDIA"
  "operands[4] = GEN_INT (FRV_BUILTIN_MQCPXRS);")

(define_expand "mqcpxru"
  [(parallel [(set (match_operand:DI 0 "even_acc_operand" "=b")
		   (unspec:DI [(match_operand:DI 1 "fpr_operand" "f")
			       (match_operand:DI 2 "fpr_operand" "f")
			       (match_dup 4)]
			      UNSPEC_MQCPX))
	      (set (match_operand:HI 3 "accg_operand" "=B")
		   (unspec:HI [(const_int 0)] UNSPEC_MQCPX))])]
  "TARGET_MEDIA"
  "operands[4] = GEN_INT (FRV_BUILTIN_MQCPXRU);")

(define_expand "mqcpxis"
  [(parallel [(set (match_operand:DI 0 "even_acc_operand" "=b")
		   (unspec:DI [(match_operand:DI 1 "fpr_operand" "f")
			       (match_operand:DI 2 "fpr_operand" "f")
			       (match_dup 4)]
			      UNSPEC_MQCPX))
	      (set (match_operand:HI 3 "accg_operand" "=B")
		   (unspec:HI [(const_int 0)] UNSPEC_MQCPX))])]
  "TARGET_MEDIA"
  "operands[4] = GEN_INT (FRV_BUILTIN_MQCPXIS);")

(define_expand "mqcpxiu"
  [(parallel [(set (match_operand:DI 0 "even_acc_operand" "=b")
		   (unspec:DI [(match_operand:DI 1 "fpr_operand" "f")
			       (match_operand:DI 2 "fpr_operand" "f")
			       (match_dup 4)]
			      UNSPEC_MQCPX))
	      (set (match_operand:HI 3 "accg_operand" "=B")
		   (unspec:HI [(const_int 0)] UNSPEC_MQCPX))])]
  "TARGET_MEDIA"
  "operands[4] = GEN_INT (FRV_BUILTIN_MQCPXIU);")

(define_insn "*mqcpx"
  [(set (match_operand:DI 0 "even_acc_operand" "=b")
        (unspec:DI [(match_operand:DI 1 "fpr_operand" "f")
                    (match_operand:DI 2 "fpr_operand" "f")
		    (match_operand:SI 3 "const_int_operand" "n")]
		   UNSPEC_MQCPX))
   (set (match_operand:HI 4 "accg_operand" "=B")
	(unspec:HI [(const_int 0)] UNSPEC_MQCPX))]
  "TARGET_MEDIA"
  "*
{
  switch (INTVAL (operands[3]))
  {
  default:		    break;
  case FRV_BUILTIN_MQCPXRS: return \"mqcpxrs %1, %2, %0\";
  case FRV_BUILTIN_MQCPXRU: return \"mqcpxru %1, %2, %0\";
  case FRV_BUILTIN_MQCPXIS: return \"mqcpxis %1, %2, %0\";
  case FRV_BUILTIN_MQCPXIU: return \"mqcpxiu %1, %2, %0\";
  }

  fatal_insn (\"Bad media insn, mqcpx\", insn);
}"
  [(set_attr "length" "4")
   (set_attr "type" "mqcpx")])

;; Cut: type "mcut"

(define_expand "mcut"
  [(set (match_operand:SI 0 "fpr_operand" "=f")
        (unspec:SI [(match_operand:SI 1 "acc_operand" "a")
                    (match_operand:SI 2 "fpr_or_int6_operand" "fI")
		    (match_operand:QI 3 "accg_operand" "B")
		    (match_dup 4)]
		   UNSPEC_MCUT))]
  "TARGET_MEDIA"
  "operands[4] = GEN_INT (FRV_BUILTIN_MCUT);")

(define_expand "mcutss"
  [(set (match_operand:SI 0 "fpr_operand" "=f")
        (unspec:SI [(match_operand:SI 1 "acc_operand" "a")
                    (match_operand:SI 2 "fpr_or_int6_operand" "fI")
		    (match_operand:QI 3 "accg_operand" "B")
		    (match_dup 4)]
		   UNSPEC_MCUT))]
  "TARGET_MEDIA"
  "operands[4] = GEN_INT (FRV_BUILTIN_MCUTSS);")

(define_insn "*mcut"
  [(set (match_operand:SI 0 "fpr_operand" "=f")
        (unspec:SI [(match_operand:SI 1 "acc_operand" "a")
                    (match_operand:SI 2 "fpr_or_int6_operand" "fI")
		    (match_operand:QI 3 "accg_operand" "B")
		    (match_operand:SI 4 "const_int_operand" "n")]
		   UNSPEC_MCUT))]
  "TARGET_MEDIA"
  "*
{
  switch (INTVAL (operands[4]))
  {
  default:		   break;
  case FRV_BUILTIN_MCUT:   return \"mcut%i2 %1, %2, %0\";
  case FRV_BUILTIN_MCUTSS: return \"mcutss%i2 %1, %2, %0\";
  }

  fatal_insn (\"Bad media insn, mcut\", insn);
}"
  [(set_attr "length" "4")
   (set_attr "type" "mcut")])

;; Accumulator read: type "mrdacc"

(define_insn "mrdacc"
  [(set (match_operand:SI 0 "fpr_operand" "=f")
	(unspec:SI [(match_operand:SI 1 "acc_operand" "a")] UNSPEC_MRDACC))]
  "TARGET_MEDIA"
  "mrdacc %1, %0"
  [(set_attr "length" "4")
   (set_attr "type" "mrdacc")])

(define_insn "mrdaccg"
  [(set (match_operand:SI 0 "fpr_operand" "=f")
	(unspec:SI [(match_operand:QI 1 "accg_operand" "B")] UNSPEC_MRDACCG))]
  "TARGET_MEDIA"
  "mrdaccg %1, %0"
  [(set_attr "length" "4")
   (set_attr "type" "mrdacc")])

;; Accumulator write: type "mwtacc"

(define_insn "mwtacc"
  [(set (match_operand:SI 0 "acc_operand" "=a")
	(unspec:SI [(match_operand:SI 1 "fpr_operand" "f")] UNSPEC_MWTACC))]
  "TARGET_MEDIA"
  "mwtacc %1, %0"
  [(set_attr "length" "4")
   (set_attr "type" "mwtacc")])

(define_insn "mwtaccg"
  [(set (match_operand:QI 0 "accg_operand" "=B")
	(unspec:QI [(match_operand:SI 1 "fpr_operand" "f")] UNSPEC_MWTACCG))]
  "TARGET_MEDIA"
  "mwtaccg %1, %0"
  [(set_attr "length" "4")
   (set_attr "type" "mwtacc")])

;; Trap: This one executes on the control unit, not the media units.

(define_insn "mtrap"
  [(unspec_volatile [(const_int 0)] UNSPEC_MTRAP)]
  "TARGET_MEDIA"
  "mtrap"
  [(set_attr "length" "4")
   (set_attr "type" "trap")])

;; Clear single accumulator: type "mclracc"

(define_insn "mclracc_internal"
  [(set (match_operand:SI 0 "acc_operand" "=a")
	(unspec:SI [(const_int 0)] UNSPEC_MCLRACC))
   (set (match_operand:QI 1 "accg_operand" "=B")
	(unspec:QI [(const_int 0)] UNSPEC_MCLRACC))]
  "TARGET_MEDIA"
  "mclracc %0,#0"
  [(set_attr "length" "4")
   (set_attr "type" "mclracc")])

(define_expand "mclracc"
  [(parallel [(set (match_operand:SI 0 "acc_operand" "=a")
		   (unspec:SI [(const_int 0)] UNSPEC_MCLRACC))
	      (set (match_dup 1)
		   (unspec:QI [(const_int 0)] UNSPEC_MCLRACC))])]
  "TARGET_MEDIA"
  "
{
  if (GET_CODE (operands[0]) != REG || !ACC_P (REGNO (operands[0])))
    FAIL;

  operands[1] = frv_matching_accg_for_acc (operands[0]);
}")

;; Clear all accumulators: type "mclracca"

(define_insn "mclracca8_internal"
  [(set (match_operand:V4SI 0 "quad_acc_operand" "=b")
	(unspec:V4SI [(const_int 0)] UNSPEC_MCLRACCA))
   (set (match_operand:V4SI 1 "quad_acc_operand" "=b")
	(unspec:V4SI [(const_int 0)] UNSPEC_MCLRACCA))
   (set (match_operand:V4QI 2 "accg_operand" "=B")
	(unspec:V4QI [(const_int 0)] UNSPEC_MCLRACCA))
   (set (match_operand:V4QI 3 "accg_operand" "=B")
	(unspec:V4QI [(const_int 0)] UNSPEC_MCLRACCA))]
  "TARGET_MEDIA && TARGET_ACC_8"
  "mclracc acc0,#1"
  [(set_attr "length" "4")
   (set_attr "type" "mclracca")])

(define_insn "mclracca4_internal"
  [(set (match_operand:V4SI 0 "quad_acc_operand" "=b")
	(unspec:V4SI [(const_int 0)] UNSPEC_MCLRACCA))
   (set (match_operand:V4QI 1 "accg_operand" "=B")
	(unspec:V4QI [(const_int 0)] UNSPEC_MCLRACCA))]
  "TARGET_MEDIA && TARGET_ACC_4"
  "mclracc acc0,#1"
  [(set_attr "length" "4")
   (set_attr "type" "mclracca")])

(define_expand "mclracca8"
  [(parallel [(set (match_dup 0) (unspec:V4SI [(const_int 0)] UNSPEC_MCLRACCA))
	      (set (match_dup 1) (unspec:V4SI [(const_int 0)] UNSPEC_MCLRACCA))
	      (set (match_dup 2) (unspec:V4QI [(const_int 0)] UNSPEC_MCLRACCA))
	      (set (match_dup 3) (unspec:V4QI [(const_int 0)] UNSPEC_MCLRACCA))])]
  "TARGET_MEDIA && TARGET_ACC_8"
  "
{
  operands[0] = gen_rtx_REG (V4SImode, ACC_FIRST);
  operands[1] = gen_rtx_REG (V4SImode, ACC_FIRST + (~3 & ACC_MASK));
  operands[2] = gen_rtx_REG (V4QImode, ACCG_FIRST);
  operands[3] = gen_rtx_REG (V4QImode, ACCG_FIRST + (~3 & ACC_MASK));
}")

(define_expand "mclracca4"
  [(parallel [(set (match_dup 0) (unspec:V4SI [(const_int 0)] UNSPEC_MCLRACCA))
	      (set (match_dup 1) (unspec:V4QI [(const_int 0)] UNSPEC_MCLRACCA))])]
  "TARGET_MEDIA && TARGET_ACC_4"
  "
{
  operands[0] = gen_rtx_REG (V4SImode, ACC_FIRST);
  operands[1] = gen_rtx_REG (V4QImode, ACCG_FIRST);
}")

(define_insn "mcop1"
  [(set (match_operand:SI 0 "fpr_operand" "=f")
        (unspec:SI [(match_operand:SI 1 "fpr_operand" "f")
                    (match_operand:SI 2 "fpr_operand" "f")] UNSPEC_MCOP1))]
  "TARGET_MEDIA_REV1"
  "mcop1 %1, %2, %0"
  [(set_attr "length" "4")
;; What is the class of the insn ???
   (set_attr "type" "multi")])

(define_insn "mcop2"
  [(set (match_operand:SI 0 "fpr_operand" "=f")
        (unspec:SI [(match_operand:SI 1 "fpr_operand" "f")
                    (match_operand:SI 2 "fpr_operand" "f")] UNSPEC_MCOP2))]
  "TARGET_MEDIA_REV1"
  "mcop2 %1, %2, %0"
  [(set_attr "length" "4")
;; What is the class of the insn ???
   (set_attr "type" "multi")])

(define_insn "*mdunpackh_internal"
  [(set (match_operand:V4SI 0 "quad_fpr_operand" "=x")
        (unspec:V4SI [(match_operand:DI 1 "even_fpr_operand" "h")]
		     UNSPEC_MDUNPACKH_INTERNAL))]
  "TARGET_MEDIA_REV1"
  "mdunpackh %1, %0"
  [(set_attr "length" "4")
   (set_attr "type" "mdunpackh")])

(define_insn_and_split "mdunpackh"
  [(set (match_operand:V4SI 0 "memory_operand" "=o")
        (unspec:V4SI [(match_operand:DI 1 "even_fpr_operand" "h")]
		     UNSPEC_MDUNPACKH))
   (clobber (match_scratch:V4SI 2 "=x"))]
  "TARGET_MEDIA_REV1"
  "#"
  "reload_completed"
  [(set (match_dup 2)
	(unspec:V4SI [(match_dup 1)] UNSPEC_MDUNPACKH_INTERNAL))
   (set (match_dup 3)
	(match_dup 4))
   (set (match_dup 5)
	(match_dup 6))]
  "
{
  operands[3] = change_address (operands[0], DImode, NULL_RTX);
  operands[4] = gen_rtx_REG (DImode, REGNO (operands[2]));
  operands[5] = frv_index_memory (operands[0], DImode, 1);
  operands[6] = gen_rtx_REG (DImode, REGNO (operands[2])+2);
}"
  [(set_attr "length" "20")
   (set_attr "type" "multi")])

(define_insn "*mbtohe_internal"
  [(set (match_operand:V4SI 0 "quad_fpr_operand" "=x")
        (unspec:V4SI [(match_operand:SI 1 "fpr_operand" "f")]
		     UNSPEC_MBTOHE_INTERNAL))]
  "TARGET_MEDIA_REV1"
  "mbtohe %1, %0"
  [(set_attr "length" "4")
   (set_attr "type" "mbhconve")])

(define_insn_and_split "mbtohe"
  [(set (match_operand:V4SI 0 "memory_operand" "=o")
        (unspec:V4SI [(match_operand:SI 1 "fpr_operand" "f")]
		     UNSPEC_MBTOHE))
   (clobber (match_scratch:V4SI 2 "=x"))]
  "TARGET_MEDIA_REV1"
  "#"
  "reload_completed"
  [(set (match_dup 2)
	(unspec:V4SI [(match_dup 1)] UNSPEC_MBTOHE_INTERNAL))
   (set (match_dup 3)
	(match_dup 4))
   (set (match_dup 5)
	(match_dup 6))]
  "
{
  operands[3] = change_address (operands[0], DImode, NULL_RTX);
  operands[4] = gen_rtx_REG (DImode, REGNO (operands[2]));
  operands[5] = frv_index_memory (operands[0], DImode, 1);
  operands[6] = gen_rtx_REG (DImode, REGNO (operands[2])+2);
}"
  [(set_attr "length" "20")
   (set_attr "type" "multi")])

;; Quad product-sum (halfword) instructions only found on the FR400.
;; type "mqmach"

(define_expand "mqxmachs"
  [(parallel [(set (match_operand:V4SI 0 "quad_acc_operand" "")
	           (unspec:V4SI [(match_dup 0)
		   	         (match_operand:DI 1 "even_fpr_operand" "")
			         (match_operand:DI 2 "even_fpr_operand" "")
				 (match_operand:V4QI 3 "accg_operand" "")
				 (match_dup 4)]
				UNSPEC_MQMACH2))
		(set (match_dup 3)
		     (unspec:V4QI [(const_int 0)] UNSPEC_MQMACH2))])]
  "TARGET_MEDIA_REV2"
  "operands[4] = GEN_INT (FRV_BUILTIN_MQXMACHS);")

(define_expand "mqxmacxhs"
  [(parallel [(set (match_operand:V4SI 0 "quad_acc_operand" "")
		   (unspec:V4SI [(match_dup 0)
				 (match_operand:DI 1 "even_fpr_operand" "")
				 (match_operand:DI 2 "even_fpr_operand" "")
				 (match_operand:V4QI 3 "accg_operand" "")
				 (match_dup 4)]
				UNSPEC_MQMACH2))
	      (set (match_dup 3)
		   (unspec:V4QI [(const_int 0)] UNSPEC_MQMACH2))])]
  "TARGET_MEDIA_REV2"
  "operands[4] = GEN_INT (FRV_BUILTIN_MQXMACXHS);")

(define_expand "mqmacxhs"
  [(parallel [(set (match_operand:V4SI 0 "quad_acc_operand" "")
		   (unspec:V4SI [(match_dup 0)
				 (match_operand:DI 1 "even_fpr_operand" "")
				 (match_operand:DI 2 "even_fpr_operand" "")
				 (match_operand:V4QI 3 "accg_operand" "")
				 (match_dup 4)]
				UNSPEC_MQMACH2))
	      (set (match_dup 3)
		   (unspec:V4QI [(const_int 0)] UNSPEC_MQMACH2))])]
  "TARGET_MEDIA_REV2"
  "operands[4] = GEN_INT (FRV_BUILTIN_MQMACXHS);")

(define_insn "*mqmach2"
  [(set (match_operand:V4SI 0 "quad_acc_operand" "+A")
        (unspec:V4SI [(match_dup 0)
		      (match_operand:DI 1 "even_fpr_operand" "h")
		      (match_operand:DI 2 "even_fpr_operand" "h")
		      (match_operand:V4QI 3 "accg_operand" "+B")
		      (match_operand:SI 4 "const_int_operand" "n")]
		     UNSPEC_MQMACH2))
   (set (match_dup 3)
	(unspec:V4QI [(const_int 0)] UNSPEC_MQMACH2))]
  "TARGET_MEDIA_REV2"
  "*
{
  switch (INTVAL (operands[4]))
  {
  default:		      break;
  case FRV_BUILTIN_MQXMACHS:  return \"mqxmachs %1, %2, %0\";
  case FRV_BUILTIN_MQXMACXHS: return \"mqxmacxhs %1, %2, %0\";
  case FRV_BUILTIN_MQMACXHS:  return \"mqmacxhs %1, %2, %0\";
  }

  fatal_insn (\"Bad media insn, mqmach2\", insn);
}"
  [(set_attr "length" "4")
   (set_attr "type" "mqmach")])

;; Accumulator addition/subtraction: type "maddacc"

(define_expand "maddaccs"
  [(parallel [(set (match_operand:SI 0 "acc_operand" "")
		   (unspec:SI [(match_operand:DI 1 "even_acc_operand" "")]
			      UNSPEC_MADDACC))
	      (set (match_operand:QI 2 "accg_operand" "")
		   (unspec:QI [(match_operand:HI 3 "accg_operand" "")
			       (match_dup 4)]
			      UNSPEC_MADDACC))])]
  "TARGET_MEDIA_REV2"
  "operands[4] = GEN_INT (FRV_BUILTIN_MADDACCS);")

(define_expand "msubaccs"
  [(parallel [(set (match_operand:SI 0 "acc_operand" "")
		   (unspec:SI [(match_operand:DI 1 "even_acc_operand" "")]
			      UNSPEC_MADDACC))
	      (set (match_operand:QI 2 "accg_operand" "")
		   (unspec:QI [(match_operand:HI 3 "accg_operand" "")
			       (match_dup 4)]
			      UNSPEC_MADDACC))])]
  "TARGET_MEDIA_REV2"
  "operands[4] = GEN_INT (FRV_BUILTIN_MSUBACCS);")

(define_insn "masaccs"
  [(set (match_operand:DI 0 "even_acc_operand" "=b")
	(unspec:DI [(match_operand:DI 1 "even_acc_operand" "b")]
		   UNSPEC_MASACCS))
   (set (match_operand:HI 2 "accg_operand" "=B")
	(unspec:HI [(match_operand:HI 3 "accg_operand" "B")]
		   UNSPEC_MASACCS))]
  "TARGET_MEDIA_REV2"
  "masaccs %1, %0"
  [(set_attr "length" "4")
   (set_attr "type" "maddacc")])

(define_insn "*maddacc"
  [(set (match_operand:SI 0 "acc_operand" "=a")
	(unspec:SI [(match_operand:DI 1 "even_acc_operand" "b")]
		   UNSPEC_MADDACC))
   (set (match_operand:QI 2 "accg_operand" "=B")
	(unspec:QI [(match_operand:HI 3 "accg_operand" "B")
		    (match_operand:SI 4 "const_int_operand" "n")]
		   UNSPEC_MADDACC))]
  "TARGET_MEDIA_REV2"
  "*
{
  switch (INTVAL (operands[4]))
  {
  default:		     break;
  case FRV_BUILTIN_MADDACCS: return \"maddaccs %1, %0\";
  case FRV_BUILTIN_MSUBACCS: return \"msubaccs %1, %0\";
  }

  fatal_insn (\"Bad media insn, maddacc\", insn);
}"
  [(set_attr "length" "4")
   (set_attr "type" "maddacc")])

;; Dual accumulator addition/subtraction: type "mdaddacc"

(define_expand "mdaddaccs"
  [(parallel [(set (match_operand:DI 0 "even_acc_operand" "")
		   (unspec:DI [(match_operand:V4SI 1 "quad_acc_operand" "")]
			      UNSPEC_MDADDACC))
	      (set (match_operand:HI 2 "accg_operand" "")
		   (unspec:HI [(match_operand:V4QI 3 "accg_operand" "")
			       (match_dup 4)]
			      UNSPEC_MDADDACC))])]
  "TARGET_MEDIA_REV2"
  "operands[4] = GEN_INT (FRV_BUILTIN_MDADDACCS);")

(define_expand "mdsubaccs"
  [(parallel [(set (match_operand:DI 0 "even_acc_operand" "")
		   (unspec:DI [(match_operand:V4SI 1 "quad_acc_operand" "")]
			      UNSPEC_MDADDACC))
	      (set (match_operand:HI 2 "accg_operand" "")
	      	   (unspec:HI [(match_operand:V4QI 3 "accg_operand" "")
			       (match_dup 4)]
			      UNSPEC_MDADDACC))])]
  "TARGET_MEDIA_REV2"
  "operands[4] = GEN_INT (FRV_BUILTIN_MDSUBACCS);")

(define_insn "mdasaccs"
  [(set (match_operand:V4SI 0 "quad_acc_operand" "=A")
	(unspec:V4SI [(match_operand:V4SI 1 "quad_acc_operand" "A")]
		     UNSPEC_MDASACCS))
   (set (match_operand:V4QI 2 "accg_operand" "=B")
	(unspec:V4QI [(match_operand:V4QI 3 "accg_operand" "B")]
		     UNSPEC_MDASACCS))]
  "TARGET_MEDIA_REV2"
  "mdasaccs %1, %0"
  [(set_attr "length" "4")
   (set_attr "type" "mdaddacc")])

(define_insn "*mdaddacc"
  [(set (match_operand:DI 0 "even_acc_operand" "=b")
	(unspec:DI [(match_operand:V4SI 1 "quad_acc_operand" "A")]
		   UNSPEC_MDADDACC))
   (set (match_operand:HI 2 "accg_operand" "=B")
	(unspec:HI [(match_operand:V4QI 3 "accg_operand" "B")
		    (match_operand:SI 4 "const_int_operand" "n")]
		   UNSPEC_MDADDACC))]
  "TARGET_MEDIA_REV2"
  "*
{
  switch (INTVAL (operands[4]))
  {
  default:		      break;
  case FRV_BUILTIN_MDADDACCS: return \"mdaddaccs %1, %0\";
  case FRV_BUILTIN_MDSUBACCS: return \"mdsubaccs %1, %0\";
  }

  fatal_insn (\"Bad media insn, mdaddacc\", insn);
}"
  [(set_attr "length" "4")
   (set_attr "type" "mdaddacc")])

;; Dual absolute (halfword): type "mabsh"

(define_insn "mabshs"
  [(set (match_operand:SI 0 "fpr_operand" "=f")
        (unspec:SI [(match_operand:SI 1 "fpr_operand" "f")] UNSPEC_MABSHS))]
  "TARGET_MEDIA_REV2"
  "mabshs %1, %0"
  [(set_attr "length" "4")
   (set_attr "type" "mabsh")])

;; Dual rotate: type "mdrot"

(define_insn "mdrotli"
  [(set (match_operand:DI 0 "even_fpr_operand" "=h")
        (unspec:DI [(match_operand:DI 1 "even_fpr_operand" "h")
		    (match_operand:SI 2 "uint5_operand" "I")]
		   UNSPEC_MDROTLI))]
  "TARGET_MEDIA_REV2"
  "mdrotli %1, %2, %0"
  [(set_attr "length" "4")
   (set_attr "type" "mdrot")])

;; Dual coupling (concatenation): type "mcpl"

(define_insn "mcplhi"
  [(set (match_operand:SI 0 "fpr_operand" "=f")
        (unspec:SI [(match_operand:DI 1 "fpr_operand" "h")
		    (match_operand:SI 2 "uint4_operand" "I")]
		   UNSPEC_MCPLHI))]
  "TARGET_MEDIA_REV2"
  "mcplhi %1, %2, %0"
  [(set_attr "length" "4")
   (set_attr "type" "mcpl")])

(define_insn "mcpli"
  [(set (match_operand:SI 0 "fpr_operand" "=f")
        (unspec:SI [(match_operand:DI 1 "fpr_operand" "h")
		    (match_operand:SI 2 "uint5_operand" "I")]
		   UNSPEC_MCPLI))]
  "TARGET_MEDIA_REV2"
  "mcpli %1, %2, %0"
  [(set_attr "length" "4")
   (set_attr "type" "mcpl")])

;; Dual cut: type "mdcut"

(define_insn "mdcutssi"
  [(set (match_operand:DI 0 "even_fpr_operand" "=h")
        (unspec:DI [(match_operand:DI 1 "even_acc_operand" "b")
		    (match_operand:SI 2 "int6_operand" "I")
		    (match_operand:HI 3 "accg_operand" "B")]
		   UNSPEC_MDCUTSSI))]
  "TARGET_MEDIA_REV2"
  "mdcutssi %1, %2, %0"
  [(set_attr "length" "4")
   (set_attr "type" "mdcut")])

;; Quad saturate (halfword): type "mqsath"

(define_insn "mqsaths"
  [(set (match_operand:DI 0 "even_fpr_operand" "=h")
        (unspec:DI [(match_operand:DI 1 "even_fpr_operand" "h")
		    (match_operand:DI 2 "even_fpr_operand" "h")]
		   UNSPEC_MQSATHS))]
  "TARGET_MEDIA_REV2"
  "mqsaths %1, %2, %0"
  [(set_attr "length" "4")
   (set_attr "type" "mqsath")])

;; Quad limit instructions: type "mqlimh"

(define_insn "mqlclrhs"
  [(set (match_operand:DI 0 "even_fpr_operand" "=h")
        (unspec:DI [(match_operand:DI 1 "even_fpr_operand" "h")
		    (match_operand:DI 2 "even_fpr_operand" "h")]
		   UNSPEC_MQLCLRHS))]
  "TARGET_MEDIA_FR450"
  "mqlclrhs %1, %2, %0"
  [(set_attr "length" "4")
   (set_attr "type" "mqlimh")])

(define_insn "mqlmths"
  [(set (match_operand:DI 0 "even_fpr_operand" "=h")
        (unspec:DI [(match_operand:DI 1 "even_fpr_operand" "h")
		    (match_operand:DI 2 "even_fpr_operand" "h")]
		   UNSPEC_MQLMTHS))]
  "TARGET_MEDIA_FR450"
  "mqlmths %1, %2, %0"
  [(set_attr "length" "4")
   (set_attr "type" "mqlimh")])

(define_insn "mqsllhi"
  [(set (match_operand:DI 0 "even_fpr_operand" "=h")
        (unspec:DI [(match_operand:DI 1 "even_fpr_operand" "h")
		    (match_operand:SI 2 "int6_operand" "I")]
		   UNSPEC_MQSLLHI))]
  "TARGET_MEDIA_FR450"
  "mqsllhi %1, %2, %0"
  [(set_attr "length" "4")
   (set_attr "type" "mqshift")])

(define_insn "mqsrahi"
  [(set (match_operand:DI 0 "even_fpr_operand" "=h")
        (unspec:DI [(match_operand:DI 1 "even_fpr_operand" "h")
		    (match_operand:SI 2 "int6_operand" "I")]
		   UNSPEC_MQSRAHI))]
  "TARGET_MEDIA_FR450"
  "mqsrahi %1, %2, %0"
  [(set_attr "length" "4")
   (set_attr "type" "mqshift")])

;; Set hi/lo instructions: type "mset"

(define_insn "mhsetlos"
  [(set (match_operand:SI 0 "fpr_operand" "=f")
	(unspec:SI [(match_operand:SI 1 "fpr_operand" "0")
		    (match_operand:SI 2 "int12_operand" "NOP")]
		   UNSPEC_MHSETLOS))]
  "TARGET_MEDIA_REV2"
  "mhsetlos %2, %0"
  [(set_attr "length" "4")
   (set_attr "type" "mset")])

(define_insn "mhsetloh"
  [(set (match_operand:SI 0 "fpr_operand" "=f")
	(unspec:SI [(match_operand:SI 1 "fpr_operand" "0")
		    (match_operand:SI 2 "int5_operand" "I")]
		   UNSPEC_MHSETLOH))]
  "TARGET_MEDIA_REV2"
  "mhsetloh %2, %0"
  [(set_attr "length" "4")
   (set_attr "type" "mset")])

(define_insn "mhsethis"
  [(set (match_operand:SI 0 "fpr_operand" "=f")
	(unspec:SI [(match_operand:SI 1 "fpr_operand" "0")
		    (match_operand:SI 2 "int12_operand" "NOP")]
		   UNSPEC_MHSETHIS))]
  "TARGET_MEDIA_REV2"
  "mhsethis %2, %0"
  [(set_attr "length" "4")
   (set_attr "type" "mset")])

(define_insn "mhsethih"
  [(set (match_operand:SI 0 "fpr_operand" "=f")
	(unspec:SI [(match_operand:SI 1 "fpr_operand" "0")
		    (match_operand:SI 2 "int5_operand" "I")]
		   UNSPEC_MHSETHIH))]
  "TARGET_MEDIA_REV2"
  "mhsethih %2, %0"
  [(set_attr "length" "4")
   (set_attr "type" "mset")])

(define_insn "mhdsets"
  [(set (match_operand:SI 0 "fpr_operand" "=f")
	(unspec:SI [(match_operand:SI 1 "int12_operand" "NOP")]
		   UNSPEC_MHDSETS))]
  "TARGET_MEDIA_REV2"
  "mhdsets %1, %0"
  [(set_attr "length" "4")
   (set_attr "type" "mset")])

(define_insn "mhdseth"
  [(set (match_operand:SI 0 "fpr_operand" "=f")
	(unspec:SI [(match_operand:SI 1 "fpr_operand" "0")
		    (match_operand:SI 2 "int5_operand" "I")]
		   UNSPEC_MHDSETH))]
  "TARGET_MEDIA_REV2"
  "mhdseth %2, %0"
  [(set_attr "length" "4")
   (set_attr "type" "mset")])

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

(define_expand "symGOT2reg"
  [(match_operand:SI 0 "" "")
   (match_operand:SI 1 "" "")
   (match_operand:SI 2 "" "")
   (match_operand:SI 3 "" "")]
  ""
  "
{
  rtx_insn *insn = emit_insn (gen_symGOT2reg_i (operands[0], operands[1],
						operands[2], operands[3]));

  MEM_READONLY_P (SET_SRC (PATTERN (insn))) = 1;

  set_unique_reg_note (insn, REG_EQUAL, operands[1]);

  DONE;
}")

(define_expand "symGOT2reg_i"
  [(set (match_operand:SI 0 "" "")
	(mem:SI (plus:SI (match_operand:SI 2 "" "")
			 (const:SI (unspec:SI [(match_operand:SI 1 "" "")
					       (match_operand:SI 3 "" "")]
					      UNSPEC_GOT)))))]
  ""
  "")

(define_expand "symGOT2reg_hilo"
  [(set (match_dup 6)
	(high:SI (const:SI (unspec:SI [(match_operand:SI 1 "" "")
				       (match_dup 4)] UNSPEC_GOT))))
   (set (match_dup 5)
	(lo_sum:SI (match_dup 6)
		   (const:SI (unspec:SI [(match_dup 1)
					 (match_operand:SI 3 "" "")]
					UNSPEC_GOT))))
   (set (match_operand:SI 0 "" "")
	(mem:SI (plus:SI (match_dup 5)
			 (match_operand:SI 2 "" ""))))
   ]
  ""
  "
{
  if (!can_create_pseudo_p ())
    operands[6] = operands[5] = operands[0];
  else
    {
      operands[6] = gen_reg_rtx (SImode);
      operands[5] = gen_reg_rtx (SImode);
    }

  operands[4] = GEN_INT (INTVAL (operands[3]) + 1);
  operands[3] = GEN_INT (INTVAL (operands[3]) + 2);
}")

(define_expand "symGOTOFF2reg_hilo"
  [(set (match_dup 6)
	(high:SI (const:SI (unspec:SI [(match_operand:SI 1 "" "")
				       (match_dup 4)] UNSPEC_GOT))))
   (set (match_dup 5)
	(lo_sum:SI (match_dup 6)
		   (const:SI (unspec:SI [(match_dup 1)
					 (match_operand:SI 3 "" "")]
					UNSPEC_GOT))))
   (set (match_operand:SI 0 "" "")
	(plus:SI (match_dup 5)
		 (match_operand:SI 2 "" "")))
   ]
  ""
  "
{
  if (!can_create_pseudo_p ())
    operands[6] = operands[5] = operands[0];
  else
    {
      operands[6] = gen_reg_rtx (SImode);
      operands[5] = gen_reg_rtx (SImode);
    }

  operands[4] = GEN_INT (INTVAL (operands[3]) + 1);
  operands[3] = GEN_INT (INTVAL (operands[3]) + 2);
}")

(define_expand "symGOTOFF2reg"
  [(match_operand:SI 0 "" "")
   (match_operand:SI 1 "" "")
   (match_operand:SI 2 "" "")
   (match_operand:SI 3 "" "")]
  ""
  "
{
  rtx_insn *insn = emit_insn (gen_symGOTOFF2reg_i (operands[0], operands[1],
						   operands[2], operands[3]));

  set_unique_reg_note (insn, REG_EQUAL, operands[1]);

  DONE;
}")

(define_expand "symGOTOFF2reg_i"
  [(set (match_operand:SI 0 "" "")
	(plus:SI (match_operand:SI 2 "" "")
		 (const:SI
		  (unspec:SI [(match_operand:SI 1 "" "")
			     (match_operand:SI 3 "" "")]
			     UNSPEC_GOT))))]
  ""
  "")

(define_expand "symGPREL2reg"
  [(match_operand:SI 0 "" "")
   (match_operand:SI 1 "" "")
   (match_operand:SI 2 "" "")
   (match_operand:SI 3 "" "")
   (match_dup 4)]
  ""
  "
{
  if (!can_create_pseudo_p ())
    operands[4] = operands[0];
  else
    operands[4] = gen_reg_rtx (SImode);

  emit_insn (frv_gen_GPsym2reg (operands[4], operands[2]));

  rtx_insn *insn = emit_insn (gen_symGOTOFF2reg_i (operands[0], operands[1],
						   operands[4], operands[3]));

  set_unique_reg_note (insn, REG_EQUAL, operands[1]);

  DONE;
}")

(define_expand "symGPREL2reg_hilo"
  [(match_operand:SI 0 "" "")
   (match_operand:SI 1 "" "")
   (match_operand:SI 2 "" "")
   (match_operand:SI 3 "" "")
   (match_dup 4)]
  ""
  "
{
  if (!can_create_pseudo_p ())
    {
      emit_insn (gen_symGOT2reg (operands[0], operands[1], operands[2],
				 GEN_INT (R_FRV_GOT12)));
      DONE;
    }

  operands[4] = gen_reg_rtx (SImode);

  emit_insn (frv_gen_GPsym2reg (operands[4], operands[2]));

  rtx_insn *insn = emit_insn (gen_symGOTOFF2reg_hilo (operands[0], operands[1],
						      operands[4], operands[3]));

  set_unique_reg_note (insn, REG_EQUAL, operands[1]);

  DONE;
}")

(define_constants
  [
   (UNSPEC_SMUL			154)
   (UNSPEC_UMUL			155)
   (UNSPEC_SMU			156)
   (UNSPEC_ADDSS		157)
   (UNSPEC_SUBSS		158)
   (UNSPEC_SLASS		159)
   (UNSPEC_SCAN			160)
   (UNSPEC_INTSS                161)
   (UNSPEC_SCUTSS		162)
   (UNSPEC_PREFETCH0		163)
   (UNSPEC_PREFETCH		164)
   (UNSPEC_IACCreadll		165)
   (UNSPEC_IACCreadl		166)
   (UNSPEC_IACCsetll		167)
   (UNSPEC_IACCsetl		168)
   (UNSPEC_SMASS		169)
   (UNSPEC_SMSSS		170)
   (UNSPEC_IMUL			171)

   (IACC0_REG			171)
])

(define_insn "smul"
  [(set (match_operand:DI 0 "integer_register_operand" "=d")
        (unspec:DI [(match_operand:SI 1 "integer_register_operand" "d")
		    (match_operand:SI 2 "integer_register_operand" "d")]
		   UNSPEC_SMUL))]
  ""
  "smul %1, %2, %0"
  [(set_attr "length" "4")
   (set_attr "type" "mul")])

(define_insn "umul"
  [(set (match_operand:DI 0 "integer_register_operand" "=d")
        (unspec:DI [(match_operand:SI 1 "integer_register_operand" "d")
		    (match_operand:SI 2 "integer_register_operand" "d")]
		   UNSPEC_UMUL))]
  ""
  "umul %1, %2, %0"
  [(set_attr "length" "4")
   (set_attr "type" "mul")])

(define_insn "smass"
  [(set (reg:DI IACC0_REG)
	(unspec:DI [(match_operand:SI 0 "integer_register_operand" "d")
		    (match_operand:SI 1 "integer_register_operand" "d")
		    (reg:DI IACC0_REG)]
		   UNSPEC_SMASS))]
  "TARGET_FR405_BUILTINS"
  "smass %1, %0"
  [(set_attr "length" "4")
   (set_attr "type" "macc")])

(define_insn "smsss"
  [(set (reg:DI IACC0_REG)
	(unspec:DI [(match_operand:SI 0 "integer_register_operand" "d")
		    (match_operand:SI 1 "integer_register_operand" "d")
		    (reg:DI IACC0_REG)]
		   UNSPEC_SMSSS))]
  "TARGET_FR405_BUILTINS"
  "smsss %1, %0"
  [(set_attr "length" "4")
   (set_attr "type" "macc")])

(define_insn "smu"
  [(set (reg:DI IACC0_REG)
	(unspec:DI [(match_operand:SI 0 "integer_register_operand" "d")
		    (match_operand:SI 1 "integer_register_operand" "d")]
		   UNSPEC_SMU))]
  "TARGET_FR405_BUILTINS"
  "smu %1, %0"
  [(set_attr "length" "4")
   (set_attr "type" "macc")])

(define_insn "addss"
  [(set (match_operand:SI 0 "integer_register_operand" "=d")
        (unspec:SI [(match_operand:SI 1 "integer_register_operand" "d")
		    (match_operand:SI 2 "integer_register_operand" "d")]
		   UNSPEC_ADDSS))]
  "TARGET_FR405_BUILTINS"
  "addss %1, %2, %0"
  [(set_attr "length" "4")
   (set_attr "type" "int")])

(define_insn "subss"
  [(set (match_operand:SI 0 "integer_register_operand" "=d")
        (unspec:SI [(match_operand:SI 1 "integer_register_operand" "d")
		    (match_operand:SI 2 "integer_register_operand" "d")]
		   UNSPEC_SUBSS))]
  "TARGET_FR405_BUILTINS"
  "subss %1, %2, %0"
  [(set_attr "length" "4")
   (set_attr "type" "int")])

(define_insn "slass"
  [(set (match_operand:SI 0 "integer_register_operand" "=d")
        (unspec:SI [(match_operand:SI 1 "integer_register_operand" "d")
		    (match_operand:SI 2 "integer_register_operand" "d")]
		   UNSPEC_SLASS))]
  "TARGET_FR405_BUILTINS"
  "slass %1, %2, %0"
  [(set_attr "length" "4")
   (set_attr "type" "int")])

(define_insn "scan"
  [(set (match_operand:SI 0 "integer_register_operand" "=d")
        (unspec:SI [(match_operand:SI 1 "integer_register_operand" "d")
		    (match_operand:SI 2 "integer_register_operand" "d")]
		   UNSPEC_SCAN))]
  ""
  "scan %1, %2, %0"
  [(set_attr "length" "4")
   (set_attr "type" "scan")])

(define_insn "scutss"
  [(set (match_operand:SI 0 "integer_register_operand" "=d")
	(unspec:SI [(match_operand:SI 1 "integer_register_operand" "d")
		    (reg:DI IACC0_REG)]
		   UNSPEC_SCUTSS))]
  "TARGET_FR405_BUILTINS"
  "scutss %1,%0"
  [(set_attr "length" "4")
   (set_attr "type" "cut")])

(define_insn "frv_prefetch0"
  [(prefetch (unspec:SI [(match_operand:SI 0 "register_operand" "r")]
			UNSPEC_PREFETCH0)
	     (const_int 0)
	     (const_int 0))]
  ""
  "dcpl %0, gr0, #0"
  [(set_attr "length" "4")])

(define_insn "frv_prefetch"
  [(prefetch (unspec:SI [(match_operand:SI 0 "register_operand" "r")]
			UNSPEC_PREFETCH)
	     (const_int 0)
	     (const_int 0))]
  "TARGET_FR500_FR550_BUILTINS"
  "nop.p\\n\\tnldub @(%0, gr0), gr0"
  [(set_attr "length" "8")])

;; TLS patterns

(define_insn "call_gettlsoff"
  [(set (match_operand:SI 0 "register_operand" "=D09")
	(unspec:SI
	 [(match_operand:SI 1 "symbolic_operand" "")]
	 UNSPEC_GETTLSOFF))
   (clobber (reg:SI GR8_REG))
   (clobber (reg:SI LRREG))
   (use (match_operand:SI 2 "register_operand" "D15"))]
  "HAVE_AS_TLS"
  "call #gettlsoff(%a1)"
  [(set_attr "length" "4")
   (set_attr "type" "load_or_call")])

;; We have to expand this like a libcall (it sort of actually is)
;; because otherwise sched may move, for example, an insn that sets up
;; GR8 for a subsequence call before the *tls_indirect_call insn, and
;; then reload won't be able to fix things up.
(define_expand "tls_indirect_call"
  [(set (reg:DI GR8_REG)
	(match_operand:DI 2 "register_operand" ""))
   (parallel
    [(set (reg:SI GR9_REG)
	  (unspec:SI
	   [(match_operand:SI 1 "symbolic_operand" "")
	   (reg:DI GR8_REG)]
	   UNSPEC_TLS_INDIRECT_CALL))
    (clobber (reg:SI GR8_REG))
    (clobber (reg:SI LRREG))
    (use (match_operand:SI 3 "register_operand" ""))])
   (set (match_operand:SI 0 "register_operand" "")
	(reg:SI GR9_REG))]
  "HAVE_AS_TLS")

(define_insn "*tls_indirect_call"
  [(set (reg:SI GR9_REG)
	(unspec:SI
	 [(match_operand:SI 0 "symbolic_operand" "")
	  (reg:DI GR8_REG)]
	 UNSPEC_TLS_INDIRECT_CALL))
   (clobber (reg:SI GR8_REG))
   (clobber (reg:SI LRREG))
   ;; If there was a way to represent the fact that we don't need GR9
   ;; or GR15 to be set before this instruction (it could be in
   ;; parallel), we could use it here.  This change wouldn't apply to
   ;; call_gettlsoff, thought, since the linker may turn the latter
   ;; into ldi @(gr15,offset),gr9.
   (use (match_operand:SI 1 "register_operand" "D15"))]
  "HAVE_AS_TLS"
  "calll #gettlsoff(%a0)@(gr8,gr0)"
  [(set_attr "length" "4")
   (set_attr "type" "jumpl")])

(define_insn "tls_load_gottlsoff12"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(unspec:SI
	 [(match_operand:SI 1 "symbolic_operand" "")
	  (match_operand:SI 2 "register_operand" "r")]
	 UNSPEC_TLS_LOAD_GOTTLSOFF12))]
  "HAVE_AS_TLS"
  "ldi @(%2, #gottlsoff12(%1)), %0"
  [(set_attr "length" "4")])

(define_expand "tlsoff_hilo"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(high:SI (const:SI (unspec:SI
			    [(match_operand:SI 1 "symbolic_operand" "")
			     (match_operand:SI 2 "immediate_operand" "n")]
			    UNSPEC_GOT))))
   (set (match_dup 0)
	(lo_sum:SI (match_dup 0)
		   (const:SI (unspec:SI [(match_dup 1)
					 (match_dup 3)] UNSPEC_GOT))))]
  ""
  "
{
  operands[3] = GEN_INT (INTVAL (operands[2]) + 1);
}")

;; Just like movdi_ldd, but with relaxation annotations.
(define_insn "tls_tlsdesc_ldd"
  [(set (match_operand:DI 0 "register_operand" "=r")
	(unspec:DI [(mem:DI (unspec:SI
			     [(match_operand:SI 1 "register_operand" "r")
			      (match_operand:SI 2 "register_operand" "r")
			      (match_operand:SI 3 "symbolic_operand" "")]
			     UNSPEC_TLS_TLSDESC_LDD_AUX))]
		   UNSPEC_TLS_TLSDESC_LDD))]
  ""
  "ldd #tlsdesc(%a3)@(%1,%2), %0"
  [(set_attr "length" "4")
   (set_attr "type" "gload")])

(define_insn "tls_tlsoff_ld"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(mem:SI (unspec:SI
		 [(match_operand:SI 1 "register_operand" "r")
		  (match_operand:SI 2 "register_operand" "r")
		  (match_operand:SI 3 "symbolic_operand" "")]
		 UNSPEC_TLS_TLSOFF_LD)))]
  ""
  "ld #tlsoff(%a3)@(%1,%2), %0"
  [(set_attr "length" "4")
   (set_attr "type" "gload")])

(define_insn "tls_lddi"
  [(set (match_operand:DI 0 "register_operand" "=r")
	(unspec:DI [(match_operand:SI 1 "symbolic_operand" "")
		    (match_operand:SI 2 "register_operand" "d")]
		   UNSPEC_TLS_LDDI))]
  ""
  "lddi @(%2, #gottlsdesc12(%a1)), %0"
  [(set_attr "length" "4")
   (set_attr "type" "gload")])
