blob: 6258fe3b99eb634017cd4e38e8a239e4e1ab74fb [file] [log] [blame] [view]
;; Frv Machine Description
;; Copyright (C) 1999-2022 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 "cpymemsi"
[(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 &quo