summaryrefslogtreecommitdiff
path: root/gcc/config/mips
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/config/mips')
-rw-r--r--gcc/config/mips/10000.md253
-rw-r--r--gcc/config/mips/20kc.md284
-rw-r--r--gcc/config/mips/24k.md457
-rw-r--r--gcc/config/mips/3000.md71
-rw-r--r--gcc/config/mips/4000.md32
-rw-r--r--gcc/config/mips/4100.md51
-rw-r--r--gcc/config/mips/4130.md153
-rw-r--r--gcc/config/mips/4300.md85
-rw-r--r--gcc/config/mips/4600.md87
-rw-r--r--gcc/config/mips/4k.md153
-rw-r--r--gcc/config/mips/5000.md80
-rw-r--r--gcc/config/mips/5400.md184
-rw-r--r--gcc/config/mips/5500.md227
-rw-r--r--gcc/config/mips/5k.md229
-rw-r--r--gcc/config/mips/6000.md56
-rw-r--r--gcc/config/mips/7000.md214
-rw-r--r--gcc/config/mips/74k.md418
-rw-r--r--gcc/config/mips/9000.md151
-rw-r--r--gcc/config/mips/constraints.md233
-rw-r--r--gcc/config/mips/crtfastmath.c53
-rw-r--r--gcc/config/mips/crti.asm49
-rw-r--r--gcc/config/mips/crtn.asm52
-rw-r--r--gcc/config/mips/driver-native.c81
-rw-r--r--gcc/config/mips/elf.h51
-rw-r--r--gcc/config/mips/elfoabi.h40
-rw-r--r--gcc/config/mips/elforion.h20
-rw-r--r--gcc/config/mips/generic.md105
-rw-r--r--gcc/config/mips/iris6.h341
-rw-r--r--gcc/config/mips/iris6.opt45
-rw-r--r--gcc/config/mips/irix-crti.asm81
-rw-r--r--gcc/config/mips/irix-crtn.asm50
-rw-r--r--gcc/config/mips/libgcc-mips16.ver86
-rw-r--r--gcc/config/mips/linux-unwind.h121
-rw-r--r--gcc/config/mips/linux.h151
-rw-r--r--gcc/config/mips/linux64.h70
-rw-r--r--gcc/config/mips/loongson.h690
-rw-r--r--gcc/config/mips/loongson.md529
-rw-r--r--gcc/config/mips/loongson2ef.md252
-rw-r--r--gcc/config/mips/loongson3a.md137
-rw-r--r--gcc/config/mips/mips-dsp.md1198
-rw-r--r--gcc/config/mips/mips-dspr2.md623
-rw-r--r--gcc/config/mips/mips-fixed.md156
-rw-r--r--gcc/config/mips/mips-ftypes.def126
-rw-r--r--gcc/config/mips/mips-modes.def48
-rw-r--r--gcc/config/mips/mips-protos.h341
-rw-r--r--gcc/config/mips/mips-ps-3d.md635
-rw-r--r--gcc/config/mips/mips.c16662
-rw-r--r--gcc/config/mips/mips.h2984
-rw-r--r--gcc/config/mips/mips.md6486
-rw-r--r--gcc/config/mips/mips.opt310
-rw-r--r--gcc/config/mips/mips16.S712
-rw-r--r--gcc/config/mips/netbsd.h187
-rw-r--r--gcc/config/mips/octeon.md88
-rw-r--r--gcc/config/mips/openbsd.h101
-rw-r--r--gcc/config/mips/predicates.md342
-rw-r--r--gcc/config/mips/r3900.h40
-rw-r--r--gcc/config/mips/rtems.h35
-rw-r--r--gcc/config/mips/sb1.md579
-rw-r--r--gcc/config/mips/sdb.h87
-rw-r--r--gcc/config/mips/sde.h135
-rw-r--r--gcc/config/mips/sde.opt28
-rw-r--r--gcc/config/mips/sdemtk.h103
-rw-r--r--gcc/config/mips/sr71k.md337
-rw-r--r--gcc/config/mips/st.h31
-rw-r--r--gcc/config/mips/sync.md560
-rw-r--r--gcc/config/mips/t-elf44
-rw-r--r--gcc/config/mips/t-iris9
-rw-r--r--gcc/config/mips/t-iris638
-rw-r--r--gcc/config/mips/t-isa326454
-rw-r--r--gcc/config/mips/t-libgcc-mips1645
-rw-r--r--gcc/config/mips/t-linux6440
-rw-r--r--gcc/config/mips/t-mips41
-rw-r--r--gcc/config/mips/t-r390035
-rw-r--r--gcc/config/mips/t-rtems34
-rw-r--r--gcc/config/mips/t-sb162
-rw-r--r--gcc/config/mips/t-sde53
-rw-r--r--gcc/config/mips/t-sdemtk44
-rw-r--r--gcc/config/mips/t-slibgcc-irix52
-rw-r--r--gcc/config/mips/t-sr71k67
-rw-r--r--gcc/config/mips/t-st32
-rw-r--r--gcc/config/mips/t-vr130
-rw-r--r--gcc/config/mips/t-vxworks35
-rw-r--r--gcc/config/mips/vr.h58
-rw-r--r--gcc/config/mips/vr4120-div.S74
-rw-r--r--gcc/config/mips/vxworks.h82
-rw-r--r--gcc/config/mips/x-native3
-rw-r--r--gcc/config/mips/xlr.md89
87 files changed, 40477 insertions, 0 deletions
diff --git a/gcc/config/mips/10000.md b/gcc/config/mips/10000.md
new file mode 100644
index 000000000..ad21e9e93
--- /dev/null
+++ b/gcc/config/mips/10000.md
@@ -0,0 +1,253 @@
+;; DFA-based pipeline description for the VR1x000.
+;; Copyright (C) 2005, 2006, 2008 Free Software Foundation, 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/>.
+
+
+;; R12K/R14K/R16K are derivatives of R10K, thus copy its description
+;; until specific tuning for each is added.
+
+;; R10000 has an int queue, fp queue, address queue.
+;; The int queue feeds ALU1 and ALU2.
+;; The fp queue feeds the fp-adder and fp-multiplier.
+;; The addr queue feeds the Load/Store unit.
+;;
+;; However, we define the fp-adder and fp-multiplier as
+;; separate automatons, because the fp-multiplier is
+;; divided into fp-multiplier, fp-division, and
+;; fp-squareroot units, all of which share the same
+;; issue and completion logic, yet can operate in
+;; parallel.
+;;
+;; This is based on the model described in the R10K Manual
+;; and it helps to reduce the size of the automata.
+(define_automaton "r10k_a_int, r10k_a_fpadder, r10k_a_addr,
+ r10k_a_fpmpy, r10k_a_fpdiv, r10k_a_fpsqrt")
+
+(define_cpu_unit "r10k_alu1" "r10k_a_int")
+(define_cpu_unit "r10k_alu2" "r10k_a_int")
+(define_cpu_unit "r10k_fpadd" "r10k_a_fpadder")
+(define_cpu_unit "r10k_fpmpy" "r10k_a_fpmpy")
+(define_cpu_unit "r10k_fpdiv" "r10k_a_fpdiv")
+(define_cpu_unit "r10k_fpsqrt" "r10k_a_fpsqrt")
+(define_cpu_unit "r10k_loadstore" "r10k_a_addr")
+
+
+;; R10k Loads and Stores.
+(define_insn_reservation "r10k_load" 2
+ (and (eq_attr "cpu" "r10000")
+ (eq_attr "type" "load,prefetch,prefetchx"))
+ "r10k_loadstore")
+
+(define_insn_reservation "r10k_store" 0
+ (and (eq_attr "cpu" "r10000")
+ (eq_attr "type" "store,fpstore,fpidxstore"))
+ "r10k_loadstore")
+
+(define_insn_reservation "r10k_fpload" 3
+ (and (eq_attr "cpu" "r10000")
+ (eq_attr "type" "fpload,fpidxload"))
+ "r10k_loadstore")
+
+
+;; Integer add/sub + logic ops, and mt hi/lo can be done by alu1 or alu2.
+;; Miscellaneous arith goes here too (this is a guess).
+(define_insn_reservation "r10k_arith" 1
+ (and (eq_attr "cpu" "r10000")
+ (eq_attr "type" "arith,mthilo,slt,clz,const,nop,trap,logical"))
+ "r10k_alu1 | r10k_alu2")
+
+;; We treat mfhilo differently, because we need to know when
+;; it's HI and when it's LO.
+(define_insn_reservation "r10k_mfhi" 1
+ (and (eq_attr "cpu" "r10000")
+ (and (eq_attr "type" "mfhilo")
+ (not (match_operand 1 "lo_operand"))))
+ "r10k_alu1 | r10k_alu2")
+
+(define_insn_reservation "r10k_mflo" 1
+ (and (eq_attr "cpu" "r10000")
+ (and (eq_attr "type" "mfhilo")
+ (match_operand 1 "lo_operand")))
+ "r10k_alu1 | r10k_alu2")
+
+
+;; ALU1 handles shifts, branch eval, and condmove.
+;;
+;; Brancher is separate, but part of ALU1, but can only
+;; do one branch per cycle (is this even implementable?).
+;;
+;; Unsure if the brancher handles jumps and calls as well, but since
+;; they're related, we'll add them here for now.
+(define_insn_reservation "r10k_brancher" 1
+ (and (eq_attr "cpu" "r10000")
+ (eq_attr "type" "shift,branch,jump,call"))
+ "r10k_alu1")
+
+(define_insn_reservation "r10k_int_cmove" 1
+ (and (eq_attr "cpu" "r10000")
+ (and (eq_attr "type" "condmove")
+ (eq_attr "mode" "SI,DI")))
+ "r10k_alu1")
+
+
+;; Coprocessor Moves.
+;; mtc1/dmtc1 are handled by ALU1.
+;; mfc1/dmfc1 are handled by the fp-multiplier.
+(define_insn_reservation "r10k_mt_xfer" 3
+ (and (eq_attr "cpu" "r10000")
+ (eq_attr "type" "mtc"))
+ "r10k_alu1")
+
+(define_insn_reservation "r10k_mf_xfer" 2
+ (and (eq_attr "cpu" "r10000")
+ (eq_attr "type" "mfc"))
+ "r10k_fpmpy")
+
+
+;; Only ALU2 does int multiplications and divisions.
+;;
+;; According to the Vr10000 series user manual,
+;; integer mult and div insns can be issued one
+;; cycle earlier if using register Lo. We model
+;; this by using the Lo value by default, as it
+;; is the more common value, and use a bypass
+;; for the Hi value when needed.
+;;
+;; Also of note, There are different latencies
+;; for MULT/DMULT (Lo 5/Hi 6) and MULTU/DMULTU (Lo 6/Hi 7).
+;; However, gcc does not have separate types
+;; for these insns. Thus to strike a balance,
+;; we use the Hi latency value for imul
+;; operations until the imul type can be split.
+(define_insn_reservation "r10k_imul_single" 6
+ (and (eq_attr "cpu" "r10000")
+ (and (eq_attr "type" "imul,imul3")
+ (eq_attr "mode" "SI")))
+ "r10k_alu2 * 6")
+
+(define_insn_reservation "r10k_imul_double" 10
+ (and (eq_attr "cpu" "r10000")
+ (and (eq_attr "type" "imul,imul3")
+ (eq_attr "mode" "DI")))
+ "r10k_alu2 * 10")
+
+;; Divides keep ALU2 busy.
+(define_insn_reservation "r10k_idiv_single" 34
+ (and (eq_attr "cpu" "r10000")
+ (and (eq_attr "type" "idiv")
+ (eq_attr "mode" "SI")))
+ "r10k_alu2 * 35")
+
+(define_insn_reservation "r10k_idiv_double" 66
+ (and (eq_attr "cpu" "r10000")
+ (and (eq_attr "type" "idiv")
+ (eq_attr "mode" "DI")))
+ "r10k_alu2 * 67")
+
+(define_bypass 35 "r10k_idiv_single" "r10k_mfhi")
+(define_bypass 67 "r10k_idiv_double" "r10k_mfhi")
+
+
+;; Floating point add/sub, mul, abs value, neg, comp, & moves.
+(define_insn_reservation "r10k_fp_miscadd" 2
+ (and (eq_attr "cpu" "r10000")
+ (eq_attr "type" "fadd,fabs,fneg,fcmp"))
+ "r10k_fpadd")
+
+(define_insn_reservation "r10k_fp_miscmul" 2
+ (and (eq_attr "cpu" "r10000")
+ (eq_attr "type" "fmul,fmove"))
+ "r10k_fpmpy")
+
+(define_insn_reservation "r10k_fp_cmove" 2
+ (and (eq_attr "cpu" "r10000")
+ (and (eq_attr "type" "condmove")
+ (eq_attr "mode" "SF,DF")))
+ "r10k_fpmpy")
+
+
+;; The fcvt.s.[wl] insn has latency 4, repeat 2.
+;; All other fcvt insns have latency 2, repeat 1.
+(define_insn_reservation "r10k_fcvt_single" 4
+ (and (eq_attr "cpu" "r10000")
+ (and (eq_attr "type" "fcvt")
+ (eq_attr "cnv_mode" "I2S")))
+ "r10k_fpadd * 2")
+
+(define_insn_reservation "r10k_fcvt_other" 2
+ (and (eq_attr "cpu" "r10000")
+ (and (eq_attr "type" "fcvt")
+ (eq_attr "cnv_mode" "!I2S")))
+ "r10k_fpadd")
+
+
+;; Run the fmadd insn through fp-adder first, then fp-multiplier.
+;;
+;; The latency for fmadd is 2 cycles if the result is used
+;; by another fmadd instruction.
+(define_insn_reservation "r10k_fmadd" 4
+ (and (eq_attr "cpu" "r10000")
+ (eq_attr "type" "fmadd"))
+ "r10k_fpadd, r10k_fpmpy")
+
+(define_bypass 2 "r10k_fmadd" "r10k_fmadd")
+
+
+;; Floating point Divisions & square roots.
+(define_insn_reservation "r10k_fdiv_single" 12
+ (and (eq_attr "cpu" "r10000")
+ (and (eq_attr "type" "fdiv,frdiv")
+ (eq_attr "mode" "SF")))
+ "r10k_fpdiv * 14")
+
+(define_insn_reservation "r10k_fdiv_double" 19
+ (and (eq_attr "cpu" "r10000")
+ (and (eq_attr "type" "fdiv,frdiv")
+ (eq_attr "mode" "DF")))
+ "r10k_fpdiv * 21")
+
+(define_insn_reservation "r10k_fsqrt_single" 18
+ (and (eq_attr "cpu" "r10000")
+ (and (eq_attr "type" "fsqrt")
+ (eq_attr "mode" "SF")))
+ "r10k_fpsqrt * 20")
+
+(define_insn_reservation "r10k_fsqrt_double" 33
+ (and (eq_attr "cpu" "r10000")
+ (and (eq_attr "type" "fsqrt")
+ (eq_attr "mode" "DF")))
+ "r10k_fpsqrt * 35")
+
+(define_insn_reservation "r10k_frsqrt_single" 30
+ (and (eq_attr "cpu" "r10000")
+ (and (eq_attr "type" "frsqrt")
+ (eq_attr "mode" "SF")))
+ "r10k_fpsqrt * 20")
+
+(define_insn_reservation "r10k_frsqrt_double" 52
+ (and (eq_attr "cpu" "r10000")
+ (and (eq_attr "type" "frsqrt")
+ (eq_attr "mode" "DF")))
+ "r10k_fpsqrt * 35")
+
+
+;; Handle unknown/multi insns here (this is a guess).
+(define_insn_reservation "r10k_unknown" 1
+ (and (eq_attr "cpu" "r10000")
+ (eq_attr "type" "unknown,multi"))
+ "r10k_alu1 + r10k_alu2")
diff --git a/gcc/config/mips/20kc.md b/gcc/config/mips/20kc.md
new file mode 100644
index 000000000..1d3aadf69
--- /dev/null
+++ b/gcc/config/mips/20kc.md
@@ -0,0 +1,284 @@
+;; Copyright (C) 2007 Free Software Foundation, 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/>. */
+;;
+;; .........................
+;;
+;; DFA-based pipeline description for MIPS64 model R20Kc.
+;; Contributed by Jason Eckhardt (jle@cygnus.com).
+;;
+;; The R20Kc is a dual-issue processor that can generally bundle
+;; instructions as follows:
+;; 1. integer with integer
+;; 2. integer with fp
+;; 3. fp with fpload/fpstore
+;;
+;; Of course, there are various restrictions.
+;; Reference:
+;; "Ruby (R20K) Technical Specification Rev. 1.2, December 28, 1999."
+;;
+;; .........................
+
+;; Use three automata to isolate long latency operations, reducing space.
+(define_automaton "r20kc_other, r20kc_fdiv, r20kc_idiv")
+
+;;
+;; Describe the resources.
+;;
+
+;; Global.
+(define_cpu_unit "r20kc_iss0, r20kc_iss1" "r20kc_other")
+
+;; Integer execution unit (pipeline A).
+(define_cpu_unit "r20kc_ixua_addsub_agen" "r20kc_other")
+(define_cpu_unit "r20kc_ixua_shift" "r20kc_other")
+
+(exclusion_set "r20kc_ixua_addsub_agen" "r20kc_ixua_shift")
+
+;; Integer execution unit (pipeline B).
+(define_cpu_unit "r20kc_ixub_addsub" "r20kc_other")
+(define_cpu_unit "r20kc_ixub_branch" "r20kc_other")
+(define_cpu_unit "r20kc_ixub_mpydiv" "r20kc_other")
+(define_cpu_unit "r20kc_ixub_mpydiv_iter" "r20kc_idiv")
+
+(exclusion_set "r20kc_ixub_addsub" "r20kc_ixub_branch, r20kc_ixub_mpydiv")
+(exclusion_set "r20kc_ixub_branch" "r20kc_ixub_mpydiv")
+
+;; Cache / memory interface.
+(define_cpu_unit "r20kc_cache" "r20kc_other")
+
+;; Floating-point unit.
+(define_cpu_unit "r20kc_fpu_add" "r20kc_other")
+(define_cpu_unit "r20kc_fpu_mpy" "r20kc_other")
+(define_cpu_unit "r20kc_fpu_mpy_iter" "r20kc_fdiv")
+(define_cpu_unit "r20kc_fpu_divsqrt" "r20kc_other")
+(define_cpu_unit "r20kc_fpu_divsqrt_iter" "r20kc_fdiv")
+
+(exclusion_set "r20kc_fpu_add" "r20kc_fpu_mpy, r20kc_fpu_divsqrt")
+(exclusion_set "r20kc_fpu_mpy" "r20kc_fpu_divsqrt")
+
+;; After branch any insn can not be issued.
+(absence_set "r20kc_iss0,r20kc_iss1" "r20kc_ixub_branch")
+
+;;
+;; Define reservations for unit name mnemonics or combinations.
+;;
+
+(define_reservation "r20kc_iss"
+ "r20kc_iss0|r20kc_iss1")
+(define_reservation "r20kc_single_dispatch"
+ "r20kc_iss0+r20kc_iss1")
+(define_reservation "r20kc_iaddsub"
+ "r20kc_iss+(r20kc_ixua_addsub_agen|r20kc_ixub_addsub)")
+(define_reservation "r20kc_ishift"
+ "r20kc_iss+r20kc_ixua_shift")
+(define_reservation "r20kc_fpmove"
+ "r20kc_iss+r20kc_ixua_addsub_agen")
+(define_reservation "r20kc_imem"
+ "r20kc_iss+r20kc_ixua_addsub_agen+r20kc_cache")
+(define_reservation "r20kc_icache"
+ "r20kc_cache")
+(define_reservation "r20kc_impydiv"
+ "r20kc_iss+r20kc_ixub_mpydiv")
+(define_reservation "r20kc_impydiv_iter"
+ "r20kc_ixub_mpydiv_iter")
+(define_reservation "r20kc_ibranch"
+ "r20kc_iss+r20kc_ixub_branch")
+
+(define_reservation "r20kc_fpadd"
+ "r20kc_iss+r20kc_fpu_add")
+(define_reservation "r20kc_fpmpy"
+ "r20kc_iss+r20kc_fpu_mpy")
+(define_reservation "r20kc_fpmpy_iter"
+ "r20kc_fpu_mpy_iter")
+(define_reservation "r20kc_fpdivsqrt"
+ "r20kc_iss+r20kc_fpu_divsqrt")
+(define_reservation "r20kc_fpdivsqrt_iter"
+ "r20kc_fpu_divsqrt_iter")
+
+;;
+;; Describe instruction reservations for integer operations.
+;;
+
+;; Conditional moves always force single-dispatch.
+(define_insn_reservation "r20kc_cond_move_int" 1
+ (and (eq_attr "cpu" "20kc")
+ (and (eq_attr "type" "condmove")
+ (eq_attr "mode" "!SF,DF")))
+ "r20kc_single_dispatch")
+
+(define_insn_reservation "r20kc_cond_move_fp" 4
+ (and (eq_attr "cpu" "20kc")
+ (and (eq_attr "type" "condmove")
+ (eq_attr "mode" "SF,DF")))
+ "r20kc_single_dispatch")
+
+(define_insn_reservation "r20kc_int_other" 1
+ (and (eq_attr "cpu" "20kc")
+ (eq_attr "type" "move,arith,const,nop"))
+ "r20kc_iaddsub")
+
+;; Shifts can only execute on ixu pipeline A.
+(define_insn_reservation "r20kc_int_shift" 1
+ (and (eq_attr "cpu" "20kc")
+ (eq_attr "type" "shift"))
+ "r20kc_ishift")
+
+(define_insn_reservation "r20kc_ld" 2
+ (and (eq_attr "cpu" "20kc")
+ (eq_attr "type" "load,prefetch,prefetchx"))
+ "r20kc_imem")
+
+
+;; A load immediately following a store will stall, so
+;; say that a store uses the cache for an extra cycle.
+(define_insn_reservation "r20kc_st" 2
+ (and (eq_attr "cpu" "20kc")
+ (eq_attr "type" "store"))
+ "r20kc_imem,r20kc_icache")
+
+(define_insn_reservation "r20kc_fld" 3
+ (and (eq_attr "cpu" "20kc")
+ (eq_attr "type" "fpload"))
+ "r20kc_imem")
+
+(define_insn_reservation "r20kc_ffst" 3
+ (and (eq_attr "cpu" "20kc")
+ (eq_attr "type" "fpstore"))
+ "r20kc_imem,r20kc_icache*2")
+
+;; Integer divide latency is between 13 and 42 cycles for DIV[U] and between
+;; 13 and 72 cycles for DDIV[U]. This depends on the value of the inputs
+;; so we just choose the worst case latency.
+(define_insn_reservation "r20kc_idiv_si" 42
+ (and (eq_attr "cpu" "20kc")
+ (and (eq_attr "type" "idiv")
+ (eq_attr "mode" "SI")))
+ "r20kc_impydiv+(r20kc_impydiv_iter*42)")
+
+(define_insn_reservation "r20kc_idiv_di" 72
+ (and (eq_attr "cpu" "20kc")
+ (and (eq_attr "type" "idiv")
+ (eq_attr "mode" "DI")))
+ "r20kc_impydiv+(r20kc_impydiv_iter*72)")
+
+;; Integer multiply latency is 4 or 7 cycles for word and double-word
+;; respectively.
+(define_insn_reservation "r20kc_impy_si" 4
+ (and (eq_attr "cpu" "20kc")
+ (and (eq_attr "type" "imadd,imul,imul3")
+ (eq_attr "mode" "SI")))
+ "r20kc_impydiv+(r20kc_impydiv_iter*2)")
+
+(define_insn_reservation "r20kc_impy_di" 7
+ (and (eq_attr "cpu" "20kc")
+ (and (eq_attr "type" "imadd,imul,imul3")
+ (eq_attr "mode" "DI")))
+ "r20kc_impydiv+(r20kc_impydiv_iter*7)")
+
+;; Move to/from HI/LO.
+;; Moving to HI/LO has a 3 cycle latency while moving from only has a 1
+;; cycle latency. Repeat rate is 3 for both.
+(define_insn_reservation "r20kc_imthilo" 3
+ (and (eq_attr "cpu" "20kc")
+ (eq_attr "type" "mthilo"))
+ "r20kc_impydiv+(r20kc_impydiv_iter*3)")
+
+(define_insn_reservation "r20kc_imfhilo" 1
+ (and (eq_attr "cpu" "20kc")
+ (eq_attr "type" "mfhilo"))
+ "r20kc_impydiv+(r20kc_impydiv_iter*3)")
+
+;; Move to fp coprocessor.
+(define_insn_reservation "r20kc_ixfer_mt" 3
+ (and (eq_attr "cpu" "20kc")
+ (eq_attr "type" "mtc"))
+ "r20kc_fpmove")
+
+;; Move from fp coprocessor.
+(define_insn_reservation "r20kc_ixfer_mf" 2
+ (and (eq_attr "cpu" "20kc")
+ (eq_attr "type" "mfc"))
+ "r20kc_fpmove")
+
+;; Assume branch predicted correctly.
+(define_insn_reservation "r20kc_ibr" 1
+ (and (eq_attr "cpu" "20kc")
+ (eq_attr "type" "branch,jump,call"))
+ "r20kc_ibranch")
+
+;;
+;; Describe instruction reservations for the floating-point operations.
+;;
+(define_insn_reservation "r20kc_fp_other" 4
+ (and (eq_attr "cpu" "20kc")
+ (eq_attr "type" "fmove,fadd,fabs,fneg,fcmp"))
+ "r20kc_fpadd")
+
+(define_insn_reservation "r20kc_fp_cvt_a" 4
+ (and (eq_attr "cpu" "20kc")
+ (and (eq_attr "type" "fcvt")
+ (eq_attr "cnv_mode" "I2S,I2D,S2D")))
+ "r20kc_fpadd")
+
+(define_insn_reservation "r20kc_fp_cvt_b" 5
+ (and (eq_attr "cpu" "20kc")
+ (and (eq_attr "type" "fcvt")
+ (eq_attr "cnv_mode" "D2S,S2I")))
+ "r20kc_fpadd")
+
+(define_insn_reservation "r20kc_fp_divsqrt_df" 32
+ (and (eq_attr "cpu" "20kc")
+ (and (eq_attr "type" "fdiv,fsqrt")
+ (eq_attr "mode" "DF")))
+ "r20kc_fpdivsqrt+(r20kc_fpdivsqrt_iter*32)")
+
+(define_insn_reservation "r20kc_fp_divsqrt_sf" 17
+ (and (eq_attr "cpu" "20kc")
+ (and (eq_attr "type" "fdiv,fsqrt")
+ (eq_attr "mode" "SF")))
+ "r20kc_fpdivsqrt+(r20kc_fpdivsqrt_iter*17)")
+
+(define_insn_reservation "r20kc_fp_rsqrt_df" 35
+ (and (eq_attr "cpu" "20kc")
+ (and (eq_attr "type" "frsqrt")
+ (eq_attr "mode" "DF")))
+ "r20kc_fpdivsqrt+(r20kc_fpdivsqrt_iter*35)")
+
+(define_insn_reservation "r20kc_fp_rsqrt_sf" 17
+ (and (eq_attr "cpu" "20kc")
+ (and (eq_attr "type" "frsqrt")
+ (eq_attr "mode" "SF")))
+ "r20kc_fpdivsqrt+(r20kc_fpdivsqrt_iter*17)")
+
+(define_insn_reservation "r20kc_fp_mpy_sf" 4
+ (and (eq_attr "cpu" "20kc")
+ (and (eq_attr "type" "fmul,fmadd")
+ (eq_attr "mode" "SF")))
+ "r20kc_fpmpy+r20kc_fpmpy_iter")
+
+(define_insn_reservation "r20kc_fp_mpy_df" 5
+ (and (eq_attr "cpu" "20kc")
+ (and (eq_attr "type" "fmul,fmadd")
+ (eq_attr "mode" "DF")))
+ "r20kc_fpmpy+(r20kc_fpmpy_iter*2)")
+
+;; Force single-dispatch for unknown or multi.
+(define_insn_reservation "r20kc_unknown" 1
+ (and (eq_attr "cpu" "20kc")
+ (eq_attr "type" "unknown,multi"))
+ "r20kc_single_dispatch")
diff --git a/gcc/config/mips/24k.md b/gcc/config/mips/24k.md
new file mode 100644
index 000000000..5df8a32dc
--- /dev/null
+++ b/gcc/config/mips/24k.md
@@ -0,0 +1,457 @@
+;; DFA-based pipeline descriptions for MIPS Technologies 24K core.
+;; Contributed by Chao-ying Fu (fu@mips.com), Nigel Stephens (nigel@mips.com)
+;; and David Ung (davidu@mips.com)
+;;
+;; The 24kf2_1 is a single-issue processor with a half-clocked fpu.
+;; The 24kf1_1 is 24k with 1:1 clocked fpu.
+;;
+;; References:
+;; "MIPS32 24K Processor Core Family Software User's Manual, Rev 3.04."
+;;
+;; Copyright (C) 2005, 2007 Free Software Foundation, 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/>.
+
+(define_automaton "r24k_cpu, r24k_mdu, r24k_fpu")
+
+;; Integer execution unit.
+(define_cpu_unit "r24k_iss" "r24k_cpu")
+(define_cpu_unit "r24k_ixu_arith" "r24k_cpu")
+(define_cpu_unit "r24k_mul3a" "r24k_mdu")
+(define_cpu_unit "r24k_mul3b" "r24k_mdu")
+(define_cpu_unit "r24k_mul3c" "r24k_mdu")
+
+;; --------------------------------------------------------------
+;; Producers
+;; --------------------------------------------------------------
+
+;; 1. Loads: lb, lbu, lh, lhu, ll, lw, lwl, lwr, lwpc, lwxs
+(define_insn_reservation "r24k_int_load" 2
+ (and (eq_attr "cpu" "24kc,24kf2_1,24kf1_1")
+ (eq_attr "type" "load"))
+ "r24k_iss+r24k_ixu_arith")
+
+
+;; 2. Arithmetic: add, addi, addiu, addiupc, addu, and, andi, clo, clz,
+;; ext, ins, lui, movn, movz, nor, or, ori, rotr, rotrv, seb, seh, sll,
+;; sllv, slt, slti, sltiu, sltu, sra, srav, srl, srlv, sub, subu, wsbh,
+;; xor, xori
+;; (movn/movz is not matched, we'll need to split condmov to
+;; differentiate between integer/float moves)
+(define_insn_reservation "r24k_int_arith" 1
+ (and (eq_attr "cpu" "24kc,24kf2_1,24kf1_1")
+ (eq_attr "type" "arith,const,logical,move,nop,shift,signext,slt"))
+ "r24k_iss+r24k_ixu_arith")
+
+
+;; 3. Links: bgezal, bgezall, bltzal, bltzall, jal, jalr, jalx
+;; 3a. jr/jalr consumer
+(define_insn_reservation "r24k_int_jump" 1
+ (and (eq_attr "cpu" "24kc,24kf2_1,24kf1_1")
+ (eq_attr "type" "call,jump"))
+ "r24k_iss+r24k_ixu_arith")
+
+;; 3b. branch consumer
+(define_insn_reservation "r24k_int_branch" 1
+ (and (eq_attr "cpu" "24kc,24kf2_1,24kf1_1")
+ (eq_attr "type" "branch"))
+ "r24k_iss+r24k_ixu_arith")
+
+
+;; 4. MDU: fully pipelined multiplier
+;; mult - delivers result to hi/lo in 1 cycle (pipelined)
+(define_insn_reservation "r24k_int_mult" 1
+ (and (eq_attr "cpu" "24kc,24kf2_1,24kf1_1")
+ (eq_attr "type" "imul"))
+ "r24k_iss+(r24k_mul3a|r24k_mul3b|r24k_mul3c)")
+
+;; madd, msub - delivers result to hi/lo in 1 cycle (pipelined)
+(define_insn_reservation "r24k_int_madd" 1
+ (and (eq_attr "cpu" "24kc,24kf2_1,24kf1_1")
+ (eq_attr "type" "imadd"))
+ "r24k_iss+(r24k_mul3a|r24k_mul3b|r24k_mul3c)")
+
+;; mul - delivers result to gpr in 5 cycles
+(define_insn_reservation "r24k_int_mul3" 5
+ (and (eq_attr "cpu" "24kc,24kf2_1,24kf1_1")
+ (eq_attr "type" "imul3"))
+ "r24k_iss+(r24k_mul3a|r24k_mul3b|r24k_mul3c)*5")
+
+;; mfhi, mflo, mflhxu - deliver result to gpr in 5 cycles
+(define_insn_reservation "r24k_int_mfhilo" 5
+ (and (eq_attr "cpu" "24kc,24kf2_1,24kf1_1")
+ (eq_attr "type" "mfhilo"))
+ "r24k_iss+(r24k_mul3a|r24k_mul3b|r24k_mul3c)")
+
+;; mthi, mtlo, mtlhx - deliver result to hi/lo, thence madd, handled as bypass
+(define_insn_reservation "r24k_int_mthilo" 1
+ (and (eq_attr "cpu" "24kc,24kf2_1,24kf1_1")
+ (eq_attr "type" "mthilo"))
+ "r24k_iss+(r24k_mul3a|r24k_mul3b|r24k_mul3c)")
+
+;; div - default to 36 cycles for 32bit operands. Faster for 24bit, 16bit and
+;; 8bit, but is tricky to identify.
+(define_insn_reservation "r24k_int_div" 36
+ (and (eq_attr "cpu" "24kc,24kf2_1,24kf1_1")
+ (eq_attr "type" "idiv"))
+ "r24k_iss+(r24k_mul3a+r24k_mul3b+r24k_mul3c)*36")
+
+
+;; 5. Cop: cfc1, di, ei, mfc0, mtc0
+;; (Disabled until we add proper cop0 support)
+;;(define_insn_reservation "r24k_int_cop" 3
+;; (and (eq_attr "cpu" "24kc,24kf2_1,24kf1_1")
+;; (eq_attr "type" "cop0"))
+;; "r24k_iss+r24k_ixu_arith")
+
+
+;; 6. Store
+(define_insn_reservation "r24k_int_store" 1
+ (and (eq_attr "cpu" "24kc,24kf2_1,24kf1_1")
+ (and (eq_attr "type" "store")
+ (eq_attr "mode" "!unknown")))
+ "r24k_iss+r24k_ixu_arith")
+
+;; 6.1 Special case - matches the cprestore pattern which don't set the mode
+;; attrib. This avoids being set as r24k_int_store and have it checked
+;; against store_data_bypass_p, which would then fail because cprestore
+;; does not have a normal SET pattern.
+(define_insn_reservation "r24k_unknown_store" 1
+ (and (eq_attr "cpu" "24kc,24kf2_1,24kf1_1")
+ (and (eq_attr "type" "store")
+ (eq_attr "mode" "unknown")))
+ "r24k_iss+r24k_ixu_arith")
+
+
+;; 7. Multiple instructions
+(define_insn_reservation "r24k_int_multi" 1
+ (and (eq_attr "cpu" "24kc,24kf2_1,24kf1_1")
+ (eq_attr "type" "multi"))
+ "r24k_iss+r24k_ixu_arith+r24k_fpu_arith+(r24k_mul3a+r24k_mul3b+r24k_mul3c)")
+
+
+;; 8. Unknowns - Currently these include blockage, consttable and alignment
+;; rtls. They do not really affect scheduling latency, (blockage affects
+;; scheduling via log links, but not used here).
+(define_insn_reservation "r24k_int_unknown" 0
+ (and (eq_attr "cpu" "24kc,24kf2_1,24kf1_1")
+ (eq_attr "type" "unknown"))
+ "r24k_iss")
+
+
+;; 9. Prefetch
+(define_insn_reservation "r24k_int_prefetch" 1
+ (and (eq_attr "cpu" "24kc,24kf2_1,24kf1_1")
+ (eq_attr "type" "prefetch,prefetchx"))
+ "r24k_iss+r24k_ixu_arith")
+
+
+;; --------------------------------------------------------------
+;; Bypass to Consumer
+;; --------------------------------------------------------------
+
+;; load->next use : 2 cycles (Default)
+;; load->load base: 3 cycles
+;; load->store base: 3 cycles
+;; load->prefetch: 3 cycles
+(define_bypass 3 "r24k_int_load" "r24k_int_load")
+(define_bypass 3 "r24k_int_load" "r24k_int_store" "!store_data_bypass_p")
+(define_bypass 3 "r24k_int_load" "r24k_int_prefetch")
+
+;; arith->next use : 1 cycles (Default)
+;; arith->load base: 2 cycles
+;; arith->store base: 2 cycles
+;; arith->prefetch: 2 cycles
+(define_bypass 2 "r24k_int_arith" "r24k_int_load")
+(define_bypass 2 "r24k_int_arith" "r24k_int_store" "!store_data_bypass_p")
+(define_bypass 2 "r24k_int_arith" "r24k_int_prefetch")
+
+;; mul3->next use : 5 cycles (default)
+;; mul3->l/s base : 6 cycles
+;; mul3->prefetch : 6 cycles
+(define_bypass 6 "r24k_int_mul3" "r24k_int_load")
+(define_bypass 6 "r24k_int_mul3" "r24k_int_store" "!store_data_bypass_p")
+(define_bypass 6 "r24k_int_mul3" "r24k_int_prefetch")
+
+;; mul3->madd/msub : 1 cycle
+(define_bypass 1 "r24k_int_mul3" "r24k_int_madd" "mips_linked_madd_p")
+
+;; mfhilo->next use : 5 cycles (default)
+;; mfhilo->l/s base : 6 cycles
+;; mfhilo->prefetch : 6 cycles
+;; mthilo->madd/msub : 2 cycle (only for mthi/lo not mfhi/lo)
+(define_bypass 6 "r24k_int_mfhilo" "r24k_int_load")
+(define_bypass 6 "r24k_int_mfhilo" "r24k_int_store" "!store_data_bypass_p")
+(define_bypass 6 "r24k_int_mfhilo" "r24k_int_prefetch")
+(define_bypass 2 "r24k_int_mthilo" "r24k_int_madd")
+
+;; cop->next use : 3 cycles (Default)
+;; cop->l/s base : 4 cycles
+;; (define_bypass 4 "r24k_int_cop" "r24k_int_load")
+;; (define_bypass 4 "r24k_int_cop" "r24k_int_store" "!store_data_bypass_p")
+
+;; multi->next use : 1 cycles (Default)
+;; multi->l/s base : 2 cycles
+;; multi->prefetch : 2 cycles
+(define_bypass 2 "r24k_int_multi" "r24k_int_load")
+(define_bypass 2 "r24k_int_multi" "r24k_int_store" "!store_data_bypass_p")
+(define_bypass 2 "r24k_int_multi" "r24k_int_prefetch")
+
+
+;; --------------------------------------------------------------
+;; Floating Point Instructions
+;; --------------------------------------------------------------
+
+(define_cpu_unit "r24k_fpu_arith" "r24k_fpu")
+
+;; The 24k is a single issue cpu, and the fpu runs at half clock speed,
+;; so each fpu instruction ties up the shared instruction scheduler for
+;; 1 cycle, and the fpu scheduler for 2 cycles.
+;;
+;; These timings are therefore twice the values in the 24K manual,
+;; which are quoted in fpu clocks.
+;;
+;; The 24kf1_1 is a 24k configured with 1:1 cpu and fpu, so use
+;; the unscaled timings
+
+(define_reservation "r24kf2_1_fpu_iss" "r24k_iss+(r24k_fpu_arith*2)")
+
+;; fadd, fabs, fneg
+(define_insn_reservation "r24kf2_1_fadd" 8
+ (and (eq_attr "cpu" "24kf2_1")
+ (eq_attr "type" "fadd,fabs,fneg"))
+ "r24kf2_1_fpu_iss")
+
+;; fmove, fcmove
+(define_insn_reservation "r24kf2_1_fmove" 8
+ (and (eq_attr "cpu" "24kf2_1")
+ (eq_attr "type" "fmove,condmove"))
+ "r24kf2_1_fpu_iss")
+
+;; fload
+(define_insn_reservation "r24kf2_1_fload" 6
+ (and (eq_attr "cpu" "24kf2_1")
+ (eq_attr "type" "fpload,fpidxload"))
+ "r24kf2_1_fpu_iss")
+
+;; fstore
+(define_insn_reservation "r24kf2_1_fstore" 2
+ (and (eq_attr "cpu" "24kf2_1")
+ (eq_attr "type" "fpstore"))
+ "r24kf2_1_fpu_iss")
+
+;; fmul, fmadd
+(define_insn_reservation "r24kf2_1_fmul_sf" 8
+ (and (eq_attr "cpu" "24kf2_1")
+ (and (eq_attr "type" "fmul,fmadd")
+ (eq_attr "mode" "SF")))
+ "r24kf2_1_fpu_iss")
+
+(define_insn_reservation "r24kf2_1_fmul_df" 10
+ (and (eq_attr "cpu" "24kf2_1")
+ (and (eq_attr "type" "fmul,fmadd")
+ (eq_attr "mode" "DF")))
+ "r24kf2_1_fpu_iss,(r24k_fpu_arith*2)")
+
+
+;; fdiv, fsqrt, frsqrt
+(define_insn_reservation "r24kf2_1_fdiv_sf" 34
+ (and (eq_attr "cpu" "24kf2_1")
+ (and (eq_attr "type" "fdiv,fsqrt,frsqrt")
+ (eq_attr "mode" "SF")))
+ "r24kf2_1_fpu_iss,(r24k_fpu_arith*26)")
+
+(define_insn_reservation "r24kf2_1_fdiv_df" 64
+ (and (eq_attr "cpu" "24kf2_1")
+ (and (eq_attr "type" "fdiv,fsqrt")
+ (eq_attr "mode" "DF")))
+ "r24kf2_1_fpu_iss,(r24k_fpu_arith*56)")
+
+;; frsqrt
+(define_insn_reservation "r24kf2_1_frsqrt_df" 70
+ (and (eq_attr "cpu" "24kf2_1")
+ (and (eq_attr "type" "frsqrt")
+ (eq_attr "mode" "DF")))
+ "r24kf2_1_fpu_iss,(r24k_fpu_arith*60)")
+
+;; fcmp
+(define_insn_reservation "r24kf2_1_fcmp" 4
+ (and (eq_attr "cpu" "24kf2_1")
+ (eq_attr "type" "fcmp"))
+ "r24kf2_1_fpu_iss")
+
+;; fcmp -> movf.fmt & movt.fmt bypass (dependency must be on the condition)
+(define_bypass 2 "r24kf2_1_fcmp" "r24kf2_1_fmove")
+
+;; fcvt (cvt.d.s, cvt.[sd].[wl])
+(define_insn_reservation "r24kf2_1_fcvt_i2f_s2d" 8
+ (and (eq_attr "cpu" "24kf2_1")
+ (and (eq_attr "type" "fcvt")
+ (eq_attr "cnv_mode" "I2S,I2D,S2D")))
+ "r24kf2_1_fpu_iss")
+
+;; fcvt (cvt.s.d)
+(define_insn_reservation "r24kf2_1_fcvt_s2d" 12
+ (and (eq_attr "cpu" "24kf2_1")
+ (and (eq_attr "type" "fcvt")
+ (eq_attr "cnv_mode" "D2S")))
+ "r24kf2_1_fpu_iss")
+
+;; fcvt (cvt.[wl].[sd], etc)
+(define_insn_reservation "r24kf2_1_fcvt_f2i" 10
+ (and (eq_attr "cpu" "24kf2_1")
+ (and (eq_attr "type" "fcvt")
+ (eq_attr "cnv_mode" "S2I,D2I")))
+ "r24kf2_1_fpu_iss")
+
+;; fxfer (mfc1, mfhc1, mtc1, mthc1)
+(define_insn_reservation "r24kf2_1_fxfer" 4
+ (and (eq_attr "cpu" "24kf2_1")
+ (eq_attr "type" "mfc,mtc"))
+ "r24kf2_1_fpu_iss")
+
+;; --------------------------------------------------------------
+;; Bypass to Consumer
+;; --------------------------------------------------------------
+;; r24kf2_1_fcvt_f2i->l/s base : 11 cycles
+;; r24kf2_1_fcvt_f2i->prefetch : 11 cycles
+(define_bypass 11 "r24kf2_1_fcvt_f2i" "r24k_int_load")
+(define_bypass 11 "r24kf2_1_fcvt_f2i" "r24k_int_store" "!store_data_bypass_p")
+(define_bypass 11 "r24kf2_1_fcvt_f2i" "r24k_int_prefetch")
+
+;; r24kf2_1_fxfer->l/s base : 5 cycles
+;; r24kf2_1_fxfer->prefetch : 5 cycles
+(define_bypass 5 "r24kf2_1_fxfer" "r24k_int_load")
+(define_bypass 5 "r24kf2_1_fxfer" "r24k_int_store" "!store_data_bypass_p")
+(define_bypass 5 "r24kf2_1_fxfer" "r24k_int_prefetch")
+
+;; --------------------------------------------------------------
+;; The 24kf1_1 is a 24k configured with 1:1 cpu and fpu, so use
+;; the unscaled timings
+;; --------------------------------------------------------------
+
+(define_reservation "r24kf1_1_fpu_iss" "r24k_iss+r24k_fpu_arith")
+
+;; fadd, fabs, fneg
+(define_insn_reservation "r24kf1_1_fadd" 4
+ (and (eq_attr "cpu" "24kf1_1")
+ (eq_attr "type" "fadd,fabs,fneg"))
+ "r24kf1_1_fpu_iss")
+
+;; fmove, fcmove
+(define_insn_reservation "r24kf1_1_fmove" 4
+ (and (eq_attr "cpu" "24kf1_1")
+ (eq_attr "type" "fmove,condmove"))
+ "r24kf1_1_fpu_iss")
+
+;; fload
+(define_insn_reservation "r24kf1_1_fload" 3
+ (and (eq_attr "cpu" "24kf1_1")
+ (eq_attr "type" "fpload,fpidxload"))
+ "r24kf1_1_fpu_iss")
+
+;; fstore
+(define_insn_reservation "r24kf1_1_fstore" 1
+ (and (eq_attr "cpu" "24kf1_1")
+ (eq_attr "type" "fpstore"))
+ "r24kf1_1_fpu_iss")
+
+;; fmul, fmadd
+(define_insn_reservation "r24kf1_1_fmul_sf" 4
+ (and (eq_attr "cpu" "24kf1_1")
+ (and (eq_attr "type" "fmul,fmadd")
+ (eq_attr "mode" "SF")))
+ "r24kf1_1_fpu_iss")
+
+(define_insn_reservation "r24kf1_1_fmul_df" 5
+ (and (eq_attr "cpu" "24kf1_1")
+ (and (eq_attr "type" "fmul,fmadd")
+ (eq_attr "mode" "DF")))
+ "r24kf1_1_fpu_iss,r24k_fpu_arith")
+
+
+;; fdiv, fsqrt, frsqrt
+(define_insn_reservation "r24kf1_1_fdiv_sf" 17
+ (and (eq_attr "cpu" "24kf1_1")
+ (and (eq_attr "type" "fdiv,fsqrt,frsqrt")
+ (eq_attr "mode" "SF")))
+ "r24kf1_1_fpu_iss,(r24k_fpu_arith*13)")
+
+(define_insn_reservation "r24kf1_1_fdiv_df" 32
+ (and (eq_attr "cpu" "24kf1_1")
+ (and (eq_attr "type" "fdiv,fsqrt")
+ (eq_attr "mode" "DF")))
+ "r24kf1_1_fpu_iss,(r24k_fpu_arith*28)")
+
+;; frsqrt
+(define_insn_reservation "r24kf1_1_frsqrt_df" 35
+ (and (eq_attr "cpu" "24kf1_1")
+ (and (eq_attr "type" "frsqrt")
+ (eq_attr "mode" "DF")))
+ "r24kf1_1_fpu_iss,(r24k_fpu_arith*30)")
+
+;; fcmp
+(define_insn_reservation "r24kf1_1_fcmp" 2
+ (and (eq_attr "cpu" "24kf1_1")
+ (eq_attr "type" "fcmp"))
+ "r24kf1_1_fpu_iss")
+
+;; fcmp -> movf.fmt & movt.fmt bypass (dependency must be on the condition)
+(define_bypass 1 "r24kf1_1_fcmp" "r24kf1_1_fmove")
+
+;; fcvt (cvt.d.s, cvt.[sd].[wl])
+(define_insn_reservation "r24kf1_1_fcvt_i2f_s2d" 4
+ (and (eq_attr "cpu" "24kf1_1")
+ (and (eq_attr "type" "fcvt")
+ (eq_attr "cnv_mode" "I2S,I2D,S2D")))
+ "r24kf1_1_fpu_iss")
+
+;; fcvt (cvt.s.d)
+(define_insn_reservation "r24kf1_1_fcvt_s2d" 6
+ (and (eq_attr "cpu" "24kf1_1")
+ (and (eq_attr "type" "fcvt")
+ (eq_attr "cnv_mode" "D2S")))
+ "r24kf1_1_fpu_iss")
+
+;; fcvt (cvt.[wl].[sd], etc)
+(define_insn_reservation "r24kf1_1_fcvt_f2i" 5
+ (and (eq_attr "cpu" "24kf1_1")
+ (and (eq_attr "type" "fcvt")
+ (eq_attr "cnv_mode" "S2I,D2I")))
+ "r24kf1_1_fpu_iss")
+
+;; fxfer (mfc1, mfhc1, mtc1, mthc1)
+(define_insn_reservation "r24kf1_1_fxfer" 2
+ (and (eq_attr "cpu" "24kf1_1")
+ (eq_attr "type" "mfc,mtc"))
+ "r24kf1_1_fpu_iss")
+
+;; --------------------------------------------------------------
+;; Bypass to Consumer
+;; --------------------------------------------------------------
+;; r24kf1_1_fcvt_f2i->l/s base : 6 cycles
+;; r24kf1_1_fcvt_f2i->prefetch : 6 cycles
+(define_bypass 6 "r24kf1_1_fcvt_f2i" "r24k_int_load")
+(define_bypass 6 "r24kf1_1_fcvt_f2i" "r24k_int_store" "!store_data_bypass_p")
+(define_bypass 6 "r24kf1_1_fcvt_f2i" "r24k_int_prefetch")
+
+;; r24kf1_1_fxfer->l/s base : 3 cycles
+;; r24kf1_1_fxfer->prefetch : 3 cycles
+(define_bypass 3 "r24kf1_1_fxfer" "r24k_int_load")
+(define_bypass 3 "r24kf1_1_fxfer" "r24k_int_store" "!store_data_bypass_p")
+(define_bypass 3 "r24kf1_1_fxfer" "r24k_int_prefetch")
+
diff --git a/gcc/config/mips/3000.md b/gcc/config/mips/3000.md
new file mode 100644
index 000000000..64bdfe113
--- /dev/null
+++ b/gcc/config/mips/3000.md
@@ -0,0 +1,71 @@
+;; R3000 and TX39 pipeline description.
+;; Copyright (C) 2004, 2005, 2007 Free Software Foundation, 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/>.
+
+
+;; This file overrides parts of generic.md. It is derived from the
+;; old define_function_unit description.
+
+(define_insn_reservation "r3k_load" 2
+ (and (eq_attr "cpu" "r3000,r3900")
+ (eq_attr "type" "load,fpload,fpidxload"))
+ "alu")
+
+(define_insn_reservation "r3k_imul" 12
+ (and (eq_attr "cpu" "r3000,r3900")
+ (eq_attr "type" "imul,imul3,imadd"))
+ "imuldiv*12")
+
+(define_insn_reservation "r3k_idiv" 35
+ (and (eq_attr "cpu" "r3000,r3900")
+ (eq_attr "type" "idiv"))
+ "imuldiv*35")
+
+(define_insn_reservation "r3k_fmove" 1
+ (and (eq_attr "cpu" "r3000,r3900")
+ (eq_attr "type" "fabs,fneg,fmove"))
+ "alu")
+
+(define_insn_reservation "r3k_fadd" 2
+ (and (eq_attr "cpu" "r3000,r3900")
+ (eq_attr "type" "fcmp,fadd"))
+ "alu")
+
+(define_insn_reservation "r3k_fmul_single" 4
+ (and (eq_attr "cpu" "r3000,r3900")
+ (and (eq_attr "type" "fmul,fmadd")
+ (eq_attr "mode" "SF")))
+ "alu")
+
+(define_insn_reservation "r3k_fmul_double" 5
+ (and (eq_attr "cpu" "r3000,r3900")
+ (and (eq_attr "type" "fmul,fmadd")
+ (eq_attr "mode" "DF")))
+ "alu")
+
+(define_insn_reservation "r3k_fdiv_single" 12
+ (and (eq_attr "cpu" "r3000,r3900")
+ (and (eq_attr "type" "fdiv,frdiv")
+ (eq_attr "mode" "SF")))
+ "alu")
+
+(define_insn_reservation "r3k_fdiv_double" 19
+ (and (eq_attr "cpu" "r3000,r3900")
+ (and (eq_attr "type" "fdiv,frdiv")
+ (eq_attr "mode" "DF")))
+ "alu")
diff --git a/gcc/config/mips/4000.md b/gcc/config/mips/4000.md
new file mode 100644
index 000000000..e6e23289f
--- /dev/null
+++ b/gcc/config/mips/4000.md
@@ -0,0 +1,32 @@
+;; R4000 pipeline description.
+;; Copyright (C) 2004, 2005, 2007 Free Software Foundation, 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/>.
+
+
+;; This file overrides parts of generic.md. It is derived from the
+;; old define_function_unit description.
+
+(define_insn_reservation "r4k_imul" 10
+ (and (eq_attr "cpu" "r4000")
+ (eq_attr "type" "imul,imul3,imadd"))
+ "imuldiv*10")
+
+(define_insn_reservation "r4k_idiv" 69
+ (and (eq_attr "cpu" "r4000")
+ (eq_attr "type" "idiv"))
+ "imuldiv*69")
diff --git a/gcc/config/mips/4100.md b/gcc/config/mips/4100.md
new file mode 100644
index 000000000..dc36384c9
--- /dev/null
+++ b/gcc/config/mips/4100.md
@@ -0,0 +1,51 @@
+;; VR4100 and VR4120 pipeline description.
+;; Copyright (C) 2004, 2005, 2007 Free Software Foundation, 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/>.
+
+
+;; This file overrides parts of generic.md. It is derived from the
+;; old define_function_unit description.
+
+(define_insn_reservation "r4100_load" 2
+ (and (eq_attr "cpu" "r4100,r4120")
+ (eq_attr "type" "load,fpload,fpidxload,mfc,mtc"))
+ "alu")
+
+(define_insn_reservation "r4100_imul_si" 1
+ (and (eq_attr "cpu" "r4100,r4120")
+ (and (eq_attr "type" "imul,imul3,imadd")
+ (eq_attr "mode" "SI")))
+ "imuldiv")
+
+(define_insn_reservation "r4100_imul_di" 4
+ (and (eq_attr "cpu" "r4100,r4120")
+ (and (eq_attr "type" "imul,imul3,imadd")
+ (eq_attr "mode" "DI")))
+ "imuldiv*4")
+
+(define_insn_reservation "r4100_idiv_si" 35
+ (and (eq_attr "cpu" "r4100,r4120")
+ (and (eq_attr "type" "idiv")
+ (eq_attr "mode" "SI")))
+ "imuldiv*35")
+
+(define_insn_reservation "r4100_idiv_di" 67
+ (and (eq_attr "cpu" "r4100,r4120")
+ (and (eq_attr "type" "idiv")
+ (eq_attr "mode" "DI")))
+ "imuldiv*67")
diff --git a/gcc/config/mips/4130.md b/gcc/config/mips/4130.md
new file mode 100644
index 000000000..6de814fc7
--- /dev/null
+++ b/gcc/config/mips/4130.md
@@ -0,0 +1,153 @@
+;; Copyright (C) 2004, 2005, 2007 Free Software Foundation, 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/>. */
+;;
+;; Pipeline description for the VR4130 family.
+;;
+;; The processor issues each 8-byte aligned pair of instructions together,
+;; stalling the second instruction if it depends on the first. Thus, if we
+;; want two instructions to issue in parallel, we need to make sure that the
+;; first one is 8-byte aligned.
+;;
+;; For the purposes of this pipeline description, we treat the processor
+;; like a standard two-way superscalar architecture. If scheduling were
+;; the last pass to run, we could use the scheduler hooks to vary the
+;; issue rate depending on whether an instruction is at an aligned or
+;; unaligned address. Unfortunately, delayed branch scheduling and
+;; hazard avoidance are done after the final scheduling pass, and they
+;; can change the addresses of many instructions.
+;;
+;; We get around this in two ways:
+;;
+;; (1) By running an extra pass at the end of compilation. This pass goes
+;; through the function looking for pairs of instructions that could
+;; execute in parallel. It makes sure that the first instruction in
+;; each pair is suitably aligned, inserting nops if necessary. Doing
+;; this gives the same kind of pipeline behavior we would see on a
+;; normal superscalar target.
+;;
+;; This pass is generally a speed improvement, but the extra nops will
+;; obviously make the program bigger. It is therefore unsuitable for
+;; -Os (at the very least).
+;;
+;; (2) By modifying the scheduler hooks so that, where possible:
+;;
+;; (a) dependent instructions are separated by a non-dependent
+;; instruction;
+;;
+;; (b) instructions that use the multiplication unit are separated
+;; by non-multiplication instructions; and
+;;
+;; (c) memory access instructions are separated by non-memory
+;; instructions.
+;;
+;; The idea is to keep conflicting instructions apart wherever possible
+;; and thus make the schedule less dependent on alignment.
+
+(define_automaton "vr4130_main, vr4130_muldiv, vr4130_mulpre")
+
+(define_cpu_unit "vr4130_alu1, vr4130_alu2, vr4130_dcache" "vr4130_main")
+(define_cpu_unit "vr4130_muldiv" "vr4130_muldiv")
+
+;; This is a fake unit for pre-reload scheduling of multiplications.
+;; It enforces the true post-reload repeat rate.
+(define_cpu_unit "vr4130_mulpre" "vr4130_mulpre")
+
+;; The scheduling hooks use this attribute for (b) above.
+(define_attr "vr4130_class" "mul,mem,alu"
+ (cond [(eq_attr "type" "load,store")
+ (const_string "mem")
+
+ (eq_attr "type" "mfhilo,mthilo,imul,imul3,imadd,idiv")
+ (const_string "mul")]
+ (const_string "alu")))
+
+(define_insn_reservation "vr4130_multi" 1
+ (and (eq_attr "cpu" "r4130")
+ (eq_attr "type" "multi,unknown"))
+ "vr4130_alu1 + vr4130_alu2 + vr4130_dcache + vr4130_muldiv")
+
+(define_insn_reservation "vr4130_int" 1
+ (and (eq_attr "cpu" "r4130")
+ (eq_attr "type" "arith,const,logical,move,nop,shift,signext,slt"))
+ "vr4130_alu1 | vr4130_alu2")
+
+(define_insn_reservation "vr4130_load" 3
+ (and (eq_attr "cpu" "r4130")
+ (eq_attr "type" "load"))
+ "vr4130_dcache")
+
+(define_insn_reservation "vr4130_store" 1
+ (and (eq_attr "cpu" "r4130")
+ (eq_attr "type" "store"))
+ "vr4130_dcache")
+
+(define_insn_reservation "vr4130_mfhilo" 3
+ (and (eq_attr "cpu" "r4130")
+ (eq_attr "type" "mfhilo"))
+ "vr4130_muldiv")
+
+(define_insn_reservation "vr4130_mthilo" 1
+ (and (eq_attr "cpu" "r4130")
+ (eq_attr "type" "mthilo"))
+ "vr4130_muldiv")
+
+;; The product is available in LO & HI after one cycle. Moving the result
+;; into an integer register will take an additional three cycles, see mflo
+;; & mfhi above. Note that the same latencies and repeat rates apply if we
+;; use "mtlo; macc" instead of "mult; mflo".
+(define_insn_reservation "vr4130_mulsi" 4
+ (and (eq_attr "cpu" "r4130")
+ (and (eq_attr "type" "imul,imul3")
+ (eq_attr "mode" "SI")))
+ "vr4130_muldiv + (vr4130_mulpre * 2)")
+
+;; As for vr4130_mulsi, but the product is available in LO and HI
+;; after 3 cycles.
+(define_insn_reservation "vr4130_muldi" 6
+ (and (eq_attr "cpu" "r4130")
+ (and (eq_attr "type" "imul,imul3")
+ (eq_attr "mode" "DI")))
+ "(vr4130_muldiv * 3) + (vr4130_mulpre * 4)")
+
+;; maccs can execute in consecutive cycles without stalling, but it
+;; is 3 cycles before the integer destination can be read.
+(define_insn_reservation "vr4130_macc" 3
+ (and (eq_attr "cpu" "r4130")
+ (eq_attr "type" "imadd"))
+ "vr4130_muldiv")
+
+(define_bypass 1 "vr4130_mulsi,vr4130_macc" "vr4130_macc" "mips_linked_madd_p")
+(define_bypass 1 "vr4130_mulsi,vr4130_macc" "vr4130_mfhilo")
+(define_bypass 3 "vr4130_muldi" "vr4130_mfhilo")
+
+(define_insn_reservation "vr4130_divsi" 36
+ (and (eq_attr "cpu" "r4130")
+ (and (eq_attr "type" "idiv")
+ (eq_attr "mode" "SI")))
+ "vr4130_muldiv * 36")
+
+(define_insn_reservation "vr4130_divdi" 72
+ (and (eq_attr "cpu" "r4130")
+ (and (eq_attr "type" "idiv")
+ (eq_attr "mode" "DI")))
+ "vr4130_muldiv * 72")
+
+(define_insn_reservation "vr4130_branch" 0
+ (and (eq_attr "cpu" "r4130")
+ (eq_attr "type" "branch,jump,call"))
+ "vr4130_alu1 | vr4130_alu2")
diff --git a/gcc/config/mips/4300.md b/gcc/config/mips/4300.md
new file mode 100644
index 000000000..b55180b0c
--- /dev/null
+++ b/gcc/config/mips/4300.md
@@ -0,0 +1,85 @@
+;; VR4300 pipeline description.
+;; Copyright (C) 2004, 2005, 2007 Free Software Foundation, 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/>.
+
+
+;; This file overrides parts of generic.md. It is derived from the
+;; old define_function_unit description.
+
+(define_insn_reservation "r4300_load" 2
+ (and (eq_attr "cpu" "r4300")
+ (eq_attr "type" "load,fpload,fpidxload,mfc,mtc"))
+ "alu")
+
+(define_insn_reservation "r4300_imul_si" 5
+ (and (eq_attr "cpu" "r4300")
+ (and (eq_attr "type" "imul,imul3,imadd")
+ (eq_attr "mode" "SI")))
+ "imuldiv*5")
+
+(define_insn_reservation "r4300_imul_di" 8
+ (and (eq_attr "cpu" "r4300")
+ (and (eq_attr "type" "imul,imul3,imadd")
+ (eq_attr "mode" "DI")))
+ "imuldiv*8")
+
+(define_insn_reservation "r4300_idiv_si" 37
+ (and (eq_attr "cpu" "r4300")
+ (and (eq_attr "type" "idiv")
+ (eq_attr "mode" "SI")))
+ "imuldiv*37")
+
+(define_insn_reservation "r4300_idiv_di" 69
+ (and (eq_attr "cpu" "r4300")
+ (and (eq_attr "type" "idiv")
+ (eq_attr "mode" "DI")))
+ "imuldiv*69")
+
+(define_insn_reservation "r4300_fmove" 1
+ (and (eq_attr "cpu" "r4300")
+ (eq_attr "type" "fcmp,fabs,fneg,fmove"))
+ "imuldiv")
+
+(define_insn_reservation "r4300_fadd" 3
+ (and (eq_attr "cpu" "r4300")
+ (eq_attr "type" "fadd"))
+ "imuldiv*3")
+
+(define_insn_reservation "r4300_fmul_single" 5
+ (and (eq_attr "cpu" "r4300")
+ (and (eq_attr "type" "fmul,fmadd")
+ (eq_attr "mode" "SF")))
+ "imuldiv*5")
+
+(define_insn_reservation "r4300_fmul_double" 8
+ (and (eq_attr "cpu" "r4300")
+ (and (eq_attr "type" "fmul,fmadd")
+ (eq_attr "mode" "DF")))
+ "imuldiv*8")
+
+(define_insn_reservation "r4300_fdiv_single" 29
+ (and (eq_attr "cpu" "r4300")
+ (and (eq_attr "type" "fdiv,frdiv,fsqrt,frsqrt")
+ (eq_attr "mode" "SF")))
+ "imuldiv*29")
+
+(define_insn_reservation "r4300_fdiv_double" 58
+ (and (eq_attr "cpu" "r4300")
+ (and (eq_attr "type" "fdiv,frdiv,fsqrt,frsqrt")
+ (eq_attr "mode" "DF")))
+ "imuldiv*58")
diff --git a/gcc/config/mips/4600.md b/gcc/config/mips/4600.md
new file mode 100644
index 000000000..c645cbc5d
--- /dev/null
+++ b/gcc/config/mips/4600.md
@@ -0,0 +1,87 @@
+;; R4600 and R4650 pipeline description.
+;; Copyright (C) 2004, 2005, 2007 Free Software Foundation, 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/>.
+
+
+;; This file overrides parts of generic.md. It is derived from the
+;; old define_function_unit description.
+;;
+;; We handle the R4600 and R4650 in much the same way. The only difference
+;; is in the integer multiplication and division costs.
+
+(define_insn_reservation "r4600_imul" 10
+ (and (eq_attr "cpu" "r4600")
+ (eq_attr "type" "imul,imul3,imadd"))
+ "imuldiv*10")
+
+(define_insn_reservation "r4600_idiv" 42
+ (and (eq_attr "cpu" "r4600")
+ (eq_attr "type" "idiv"))
+ "imuldiv*42")
+
+
+(define_insn_reservation "r4650_imul" 4
+ (and (eq_attr "cpu" "r4650")
+ (eq_attr "type" "imul,imul3,imadd"))
+ "imuldiv*4")
+
+(define_insn_reservation "r4650_idiv" 36
+ (and (eq_attr "cpu" "r4650")
+ (eq_attr "type" "idiv"))
+ "imuldiv*36")
+
+
+(define_insn_reservation "r4600_load" 2
+ (and (eq_attr "cpu" "r4600,r4650")
+ (eq_attr "type" "load,fpload,fpidxload"))
+ "alu")
+
+(define_insn_reservation "r4600_fmove" 1
+ (and (eq_attr "cpu" "r4600,r4650")
+ (eq_attr "type" "fabs,fneg,fmove"))
+ "alu")
+
+(define_insn_reservation "r4600_fmul_single" 8
+ (and (eq_attr "cpu" "r4600,r4650")
+ (and (eq_attr "type" "fmul,fmadd")
+ (eq_attr "mode" "SF")))
+ "alu")
+
+(define_insn_reservation "r4600_fdiv_single" 32
+ (and (eq_attr "cpu" "r4600,r4650")
+ (and (eq_attr "type" "fdiv,frdiv")
+ (eq_attr "mode" "SF")))
+ "alu")
+
+(define_insn_reservation "r4600_fdiv_double" 61
+ (and (eq_attr "cpu" "r4600,r4650")
+ (and (eq_attr "type" "fdiv,frdiv")
+ (eq_attr "mode" "DF")))
+ "alu")
+
+(define_insn_reservation "r4600_fsqrt_single" 31
+ (and (eq_attr "cpu" "r4600,r4650")
+ (and (eq_attr "type" "fsqrt,frsqrt")
+ (eq_attr "mode" "SF")))
+ "alu")
+
+(define_insn_reservation "r4600_fsqrt_double" 60
+ (and (eq_attr "cpu" "r4600,r4650")
+ (and (eq_attr "type" "fsqrt,frsqrt")
+ (eq_attr "mode" "DF")))
+ "alu")
diff --git a/gcc/config/mips/4k.md b/gcc/config/mips/4k.md
new file mode 100644
index 000000000..88cdbd195
--- /dev/null
+++ b/gcc/config/mips/4k.md
@@ -0,0 +1,153 @@
+;; DFA-based pipeline descriptions for MIPS32 4K processor family
+;; Contributed by Nigel Stephens (nigel@mips.com)
+;; and David Ung (davidu@mips.com)
+;;
+;; References:
+;; "MIPS32 4K Processor Core Family Software User's Manual,
+;; Doc no: MD00016, Rev 1.18, Nov 15, 2004."
+;;
+;; 4Kc - pipelined multiplier and translation lookaside buffer (TLB)
+;; 4km - pipelined multiplier and block address translator (BAT)
+;; 4kp - non-pipelined multiplier and block address translator (BAT)
+;;
+;; Copyright (C) 2005, 2007 Free Software Foundation, 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/>.
+
+(define_automaton "r4k_cpu, r4k_mdu")
+
+;; Integer execution unit.
+(define_cpu_unit "r4k_ixu_arith" "r4k_cpu")
+(define_cpu_unit "r4k_ixu_mpydiv" "r4k_mdu")
+
+(define_insn_reservation "r4k_int_load" 2
+ (and (eq_attr "cpu" "4kc,4kp")
+ (eq_attr "type" "load"))
+ "r4k_ixu_arith")
+
+(define_insn_reservation "r4k_int_prefetch" 1
+ (and (eq_attr "cpu" "4kc,4kp")
+ (eq_attr "type" "prefetch"))
+ "r4k_ixu_arith")
+
+(define_insn_reservation "r4k_int_store" 1
+ (and (eq_attr "cpu" "4kc,4kp")
+ (eq_attr "type" "store"))
+ "r4k_ixu_arith")
+
+;; 4Kc/4Km
+;; unsigned divide - 8/16/24/32-bit operand have latencies 9/17/25/33
+;; signed divide - 8/16/24/32-bit operand have latencies 10/18/26/34
+(define_insn_reservation "r4k_idiv_4kc" 34
+ (and (eq_attr "cpu" "4kc")
+ (and (eq_attr "type" "idiv")
+ (eq_attr "mode" "!DI")))
+ "r4k_ixu_arith+(r4k_ixu_mpydiv*34)")
+
+;; 4Kp
+;; unsigned divide - 33
+;; signed divide - 33-35
+(define_insn_reservation "r4k_idiv_4kp" 35
+ (and (eq_attr "cpu" "4kp")
+ (and (eq_attr "type" "idiv")
+ (eq_attr "mode" "!DI")))
+ "r4k_ixu_arith+(r4k_ixu_mpydiv*35)")
+
+;; 4Kc/4Km fast 32x32 multiply
+;; 16x32 is faster, but there's no way to detect this
+(define_insn_reservation "r4k_mult_4kc" 2
+ (and (eq_attr "cpu" "4kc")
+ (and (eq_attr "type" "imul,imadd")
+ (eq_attr "mode" "SI")))
+ "r4k_ixu_arith+(r4k_ixu_mpydiv*2)")
+
+;; 4Kc/4Km MUL has 2 cycle latency, but has the special property that it will
+;; stall the integer unit pipeline. MUL 16x16 or 32x16 forces 1 cycle stall,
+;; while MUL 32x32 forces 2 cycle stall. If next insn use the result, an
+;; additional stall is forced.
+(define_insn_reservation "r4k_mul_4kc" 4
+ (and (eq_attr "cpu" "4kc")
+ (and (eq_attr "type" "imul3")
+ (eq_attr "mode" "SI")))
+ "(r4k_ixu_arith+r4k_ixu_mpydiv)*3")
+
+;; 4Kp slow iterative 2-op MULT
+;; Latency of 32 if next insn is MADD/MSUB,MFHI/MFLO.
+;; Repeat rate of 33 cycles.
+(define_insn_reservation "r4k_mult_4kp" 32
+ (and (eq_attr "cpu" "4kp")
+ (and (eq_attr "type" "imul")
+ (eq_attr "mode" "SI")))
+ "r4k_ixu_arith+(r4k_ixu_mpydiv*32)")
+
+;; 4Kp slow iterative 3-op MUL
+;; Latency of 32 cycles, but stalls the whole pipeline until complete.
+(define_insn_reservation "r4k_mul_4kp" 32
+ (and (eq_attr "cpu" "4kp")
+ (and (eq_attr "type" "imul3")
+ (eq_attr "mode" "SI")))
+ "(r4k_ixu_arith+r4k_ixu_mpydiv)*32")
+
+;; 4Kp slow iterative MADD
+;; Latency of 34 if next use insn is MADD/MSUB,MFHI/MFLO.
+;; Repeat rate of 35 cycles.
+(define_insn_reservation "r4k_madd_4kp" 34
+ (and (eq_attr "cpu" "4kp")
+ (and (eq_attr "type" "imadd")
+ (eq_attr "mode" "SI")))
+ "r4k_ixu_arith+(r4k_ixu_mpydiv*34)")
+
+;; Move to HI/LO -> MADD/MSUB,MFHI/MFLO has a 1 cycle latency.
+(define_insn_reservation "r4k_int_mthilo" 1
+ (and (eq_attr "cpu" "4kc,4kp")
+ (eq_attr "type" "mthilo"))
+ "r4k_ixu_arith+r4k_ixu_mpydiv")
+
+;; Move from HI/LO -> integer operation has a 2 cycle latency.
+(define_insn_reservation "r4k_int_mfhilo" 2
+ (and (eq_attr "cpu" "4kc,4kp")
+ (eq_attr "type" "mfhilo"))
+ "r4k_ixu_arith+r4k_ixu_mpydiv")
+
+;; All other integer insns.
+(define_insn_reservation "r4k_int_alu" 1
+ (and (eq_attr "cpu" "4kc,4kp")
+ (eq_attr "type" "arith,condmove,const,logical,move,nop,shift,signext,slt"))
+ "r4k_ixu_arith")
+
+(define_insn_reservation "r4k_int_branch" 1
+ (and (eq_attr "cpu" "4kc,4kp")
+ (eq_attr "type" "branch"))
+ "r4k_ixu_arith")
+
+(define_insn_reservation "r4k_int_jump_4k" 1
+ (and (eq_attr "cpu" "4kc,4kp")
+ (eq_attr "type" "jump,call"))
+ "r4k_ixu_arith")
+
+;; mfcx/mtcx - non FPU
+;; (Disabled until we add cop0 support)
+;; (define_insn_reservation "r4k_int_cop" 2
+;; (and (eq_attr "cpu" "4kc,4kp")
+;; (eq_attr "type" "cop0"))
+;; "r4k_ixu_arith")
+
+;; Unknown or multi - single issue
+(define_insn_reservation "r4k_unknown" 1
+ (and (eq_attr "cpu" "4kc,4kp")
+ (eq_attr "type" "unknown,multi"))
+ "r4k_ixu_arith+r4k_ixu_mpydiv")
diff --git a/gcc/config/mips/5000.md b/gcc/config/mips/5000.md
new file mode 100644
index 000000000..0ad12ba7e
--- /dev/null
+++ b/gcc/config/mips/5000.md
@@ -0,0 +1,80 @@
+;; VR5000 pipeline description.
+;; Copyright (C) 2004, 2005, 2007 Free Software Foundation, 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/>.
+
+
+;; This file overrides parts of generic.md. It is derived from the
+;; old define_function_unit description.
+
+(define_insn_reservation "r5k_load" 2
+ (and (eq_attr "cpu" "r5000")
+ (eq_attr "type" "load,fpload,fpidxload,mfc,mtc"))
+ "alu")
+
+(define_insn_reservation "r5k_imul_si" 5
+ (and (eq_attr "cpu" "r5000")
+ (and (eq_attr "type" "imul,imul3,imadd")
+ (eq_attr "mode" "SI")))
+ "imuldiv*5")
+
+(define_insn_reservation "r5k_imul_di" 9
+ (and (eq_attr "cpu" "r5000")
+ (and (eq_attr "type" "imul,imul3,imadd")
+ (eq_attr "mode" "DI")))
+ "imuldiv*9")
+
+(define_insn_reservation "r5k_idiv_si" 36
+ (and (eq_attr "cpu" "r5000")
+ (and (eq_attr "type" "idiv")
+ (eq_attr "mode" "SI")))
+ "imuldiv*36")
+
+(define_insn_reservation "r5k_idiv_di" 68
+ (and (eq_attr "cpu" "r5000")
+ (and (eq_attr "type" "idiv")
+ (eq_attr "mode" "DI")))
+ "imuldiv*68")
+
+(define_insn_reservation "r5k_fmove" 1
+ (and (eq_attr "cpu" "r5000")
+ (eq_attr "type" "fcmp,fabs,fneg,fmove"))
+ "alu")
+
+(define_insn_reservation "r5k_fmul_single" 4
+ (and (eq_attr "cpu" "r5000")
+ (and (eq_attr "type" "fmul,fmadd")
+ (eq_attr "mode" "SF")))
+ "alu")
+
+(define_insn_reservation "r5k_fmul_double" 5
+ (and (eq_attr "cpu" "r5000")
+ (and (eq_attr "type" "fmul,fmadd")
+ (eq_attr "mode" "DF")))
+ "alu")
+
+(define_insn_reservation "r5k_fdiv_single" 21
+ (and (eq_attr "cpu" "r5000")
+ (and (eq_attr "type" "fdiv,frdiv,fsqrt,frsqrt")
+ (eq_attr "mode" "SF")))
+ "alu")
+
+(define_insn_reservation "r5k_fsqrt_double" 36
+ (and (eq_attr "cpu" "r5000")
+ (and (eq_attr "type" "fsqrt,frsqrt")
+ (eq_attr "mode" "DF")))
+ "alu")
diff --git a/gcc/config/mips/5400.md b/gcc/config/mips/5400.md
new file mode 100644
index 000000000..362999d7b
--- /dev/null
+++ b/gcc/config/mips/5400.md
@@ -0,0 +1,184 @@
+;; Copyright (C) 2002, 2003, 2004, 2005, 2007 Free Software Foundation, 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/>.
+;;
+;; DFA-based pipeline description for 5400
+(define_automaton "vr54")
+(define_cpu_unit "vr54_dp0" "vr54")
+(define_cpu_unit "vr54_dp1" "vr54")
+(define_cpu_unit "vr54_mem" "vr54")
+(define_cpu_unit "vr54_mac" "vr54")
+
+;;
+;; The ordering of the instruction-execution-path/resource-usage
+;; descriptions (also known as reservation RTL) is roughly ordered
+;; based on the define attribute RTL for the "type" classification.
+;; When modifying, remember that the first test that matches is the
+;; reservation used!
+;;
+
+(define_insn_reservation "ir_vr54_unknown" 1
+ (and (eq_attr "cpu" "r5400")
+ (eq_attr "type" "unknown"))
+ "vr54_dp0+vr54_dp1+vr54_mem+vr54_mac")
+
+;; Assume prediction fails.
+(define_insn_reservation "ir_vr54_branch" 3
+ (and (eq_attr "cpu" "r5400")
+ (eq_attr "type" "branch,jump,call"))
+ "vr54_dp0|vr54_dp1")
+
+(define_insn_reservation "ir_vr54_load" 2
+ (and (eq_attr "cpu" "r5400")
+ (eq_attr "type" "load,fpload,fpidxload"))
+ "vr54_mem")
+
+(define_insn_reservation "ir_vr54_store" 1
+ (and (eq_attr "cpu" "r5400")
+ (eq_attr "type" "store"))
+ "vr54_mem")
+
+(define_insn_reservation "ir_vr54_fstore" 1
+ (and (eq_attr "cpu" "r5400")
+ (eq_attr "type" "fpstore,fpidxstore"))
+ "vr54_mem")
+
+
+;; This reservation is for conditional move based on integer
+;; or floating point CC.
+(define_insn_reservation "ir_vr54_condmove" 4
+ (and (eq_attr "cpu" "r5400")
+ (eq_attr "type" "condmove"))
+ "vr54_dp0|vr54_dp1")
+
+;; Move to/from FPU registers
+(define_insn_reservation "ir_vr54_xfer" 2
+ (and (eq_attr "cpu" "r5400")
+ (eq_attr "type" "mfc,mtc"))
+ "vr54_dp0|vr54_dp1")
+
+(define_insn_reservation "ir_vr54_hilo" 1
+ (and (eq_attr "cpu" "r5400")
+ (eq_attr "type" "mthilo,mfhilo"))
+ "vr54_dp0|vr54_dp1")
+
+(define_insn_reservation "ir_vr54_arith" 1
+ (and (eq_attr "cpu" "r5400")
+ (eq_attr "type" "arith,shift,signext,slt,clz,const,logical,move,nop,trap"))
+ "vr54_dp0|vr54_dp1")
+
+(define_insn_reservation "ir_vr54_imul_si" 3
+ (and (eq_attr "cpu" "r5400")
+ (and (eq_attr "type" "imul,imul3")
+ (eq_attr "mode" "SI")))
+ "vr54_dp0|vr54_dp1")
+
+(define_insn_reservation "ir_vr54_imul_di" 4
+ (and (eq_attr "cpu" "r5400")
+ (and (eq_attr "type" "imul,imul3")
+ (eq_attr "mode" "DI")))
+ "vr54_dp0|vr54_dp1")
+
+(define_insn_reservation "ir_vr54_imadd_si" 3
+ (and (eq_attr "cpu" "r5400")
+ (eq_attr "type" "imul,imul3"))
+ "vr54_mac")
+
+(define_insn_reservation "ir_vr54_idiv_si" 42
+ (and (eq_attr "cpu" "r5400")
+ (and (eq_attr "type" "idiv")
+ (eq_attr "mode" "SI")))
+ "vr54_dp0|vr54_dp1")
+
+(define_insn_reservation "ir_vr54_idiv_di" 74
+ (and (eq_attr "cpu" "r5400")
+ (and (eq_attr "type" "idiv")
+ (eq_attr "mode" "DI")))
+ "vr54_dp0|vr54_dp1")
+
+(define_insn_reservation "ir_vr54_fadd" 4
+ (and (eq_attr "cpu" "r5400")
+ (eq_attr "type" "fadd"))
+ "vr54_dp0|vr54_dp1")
+
+(define_insn_reservation "ir_vr54_fmul_sf" 5
+ (and (eq_attr "cpu" "r5400")
+ (and (eq_attr "type" "fmul")
+ (eq_attr "mode" "SF")))
+ "vr54_dp0|vr54_dp1")
+
+(define_insn_reservation "ir_vr54_fmul_df" 6
+ (and (eq_attr "cpu" "r5400")
+ (and (eq_attr "type" "fmul")
+ (eq_attr "mode" "DF")))
+ "vr54_dp0|vr54_dp1")
+
+(define_insn_reservation "ir_vr54_fmadd_sf" 9
+ (and (eq_attr "cpu" "r5400")
+ (and (eq_attr "type" "fmadd")
+ (eq_attr "mode" "SF")))
+ "vr54_dp0|vr54_dp1")
+
+(define_insn_reservation "ir_vr54_fmadd_df" 10
+ (and (eq_attr "cpu" "r5400")
+ (and (eq_attr "type" "fmadd")
+ (eq_attr "mode" "DF")))
+ "vr54_dp0|vr54_dp1")
+
+(define_insn_reservation "ir_vr54_fdiv_sf" 42
+ (and (eq_attr "cpu" "r5400")
+ (and (eq_attr "type" "fdiv,frdiv,fsqrt")
+ (eq_attr "mode" "SF")))
+ "vr54_dp0|vr54_dp1")
+
+(define_insn_reservation "ir_vr54_fdiv_df" 72
+ (and (eq_attr "cpu" "r5400")
+ (and (eq_attr "type" "fdiv,frdiv,fsqrt")
+ (eq_attr "mode" "DF")))
+ "vr54_dp0|vr54_dp1")
+
+(define_insn_reservation "ir_vr54_fabs" 2
+ (and (eq_attr "cpu" "r5400")
+ (eq_attr "type" "fabs,fneg,fmove"))
+ "vr54_dp0|vr54_dp1")
+
+(define_insn_reservation "ir_vr54_fcmp" 2
+ (and (eq_attr "cpu" "r5400")
+ (eq_attr "type" "fcmp"))
+ "vr54_dp0|vr54_dp1")
+
+(define_insn_reservation "ir_vr54_fcvt" 6
+ (and (eq_attr "cpu" "r5400")
+ (eq_attr "type" "fcvt"))
+ "vr54_dp0|vr54_dp1")
+
+(define_insn_reservation "ir_vr54_frsqrt_sf" 61
+ (and (eq_attr "cpu" "r5400")
+ (and (eq_attr "type" "frsqrt")
+ (eq_attr "mode" "SF")))
+ "vr54_dp0|vr54_dp1")
+
+(define_insn_reservation "ir_vr54_frsqrt_df" 121
+ (and (eq_attr "cpu" "r5400")
+ (and (eq_attr "type" "frsqrt")
+ (eq_attr "mode" "DF")))
+ "vr54_dp0|vr54_dp1")
+
+(define_insn_reservation "ir_vr54_multi" 1
+ (and (eq_attr "cpu" "r5400")
+ (eq_attr "type" "multi"))
+ "vr54_dp0+vr54_dp1+vr54_mem+vr54_mac")
diff --git a/gcc/config/mips/5500.md b/gcc/config/mips/5500.md
new file mode 100644
index 000000000..0b59af15d
--- /dev/null
+++ b/gcc/config/mips/5500.md
@@ -0,0 +1,227 @@
+;; Copyright (C) 2002, 2003, 2004, 2005, 2007 Free Software Foundation, 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/>.
+;;
+;; DFA-based pipeline description for 5500
+(define_automaton "vr55")
+(define_cpu_unit "vr55_dp0" "vr55")
+(define_cpu_unit "vr55_dp1" "vr55")
+(define_cpu_unit "vr55_mem" "vr55")
+(define_cpu_unit "vr55_mac" "vr55")
+(define_cpu_unit "vr55_fp" "vr55")
+(define_cpu_unit "vr55_bru" "vr55")
+
+;;
+;; The ordering of the instruction-execution-path/resource-usage
+;; descriptions (also known as reservation RTL) is roughly ordered
+;; based on the define attribute RTL for the "type" classification.
+;; When modifying, remember that the first test that matches is the
+;; reservation used!
+;;
+
+(define_insn_reservation "ir_vr55_unknown" 1
+ (and (eq_attr "cpu" "r5500")
+ (eq_attr "type" "unknown"))
+ "vr55_dp0+vr55_dp1+vr55_mem+vr55_mac+vr55_fp+vr55_bru")
+
+;; Assume prediction fails.
+(define_insn_reservation "ir_vr55_branch" 2
+ (and (eq_attr "cpu" "r5500")
+ (eq_attr "type" "branch,jump,call"))
+ "vr55_bru")
+
+(define_insn_reservation "ir_vr55_load" 3
+ (and (eq_attr "cpu" "r5500")
+ (eq_attr "type" "load,fpload,fpidxload"))
+ "vr55_mem")
+
+(define_bypass 4
+ "ir_vr55_load"
+ "ir_vr55_mthilo,ir_vr55_imul_si,ir_vr55_imul_di,ir_vr55_imadd,
+ ir_vr55_idiv_si,ir_vr55_idiv_di")
+
+(define_insn_reservation "ir_vr55_store" 0
+ (and (eq_attr "cpu" "r5500")
+ (eq_attr "type" "store,fpstore,fpidxstore"))
+ "vr55_mem")
+
+;; This reservation is for conditional move based on integer
+;; or floating point CC.
+(define_insn_reservation "ir_vr55_condmove" 2
+ (and (eq_attr "cpu" "r5500")
+ (eq_attr "type" "condmove"))
+ "vr55_dp0|vr55_dp1")
+
+;; Move to/from FPU registers
+(define_insn_reservation "ir_vr55_xfer" 2
+ (and (eq_attr "cpu" "r5500")
+ (eq_attr "type" "mfc,mtc"))
+ "vr55_dp0|vr55_dp1")
+
+(define_insn_reservation "ir_vr55_arith" 1
+ (and (eq_attr "cpu" "r5500")
+ (eq_attr "type" "arith,shift,signext,slt,clz,const,logical,move,nop,trap"))
+ "vr55_dp0|vr55_dp1")
+
+(define_bypass 2
+ "ir_vr55_arith"
+ "ir_vr55_mthilo,ir_vr55_imul_si,ir_vr55_imul_di,ir_vr55_imadd,
+ ir_vr55_idiv_si,ir_vr55_idiv_di")
+
+(define_insn_reservation "ir_vr55_mthilo" 1
+ (and (eq_attr "cpu" "r5500")
+ (eq_attr "type" "mthilo"))
+ "vr55_mac")
+
+(define_insn_reservation "ir_vr55_mfhilo" 5
+ (and (eq_attr "cpu" "r5500")
+ (eq_attr "type" "mfhilo"))
+ "vr55_mac")
+
+;; The default latency is for the GPR result of a mul. Bypasses handle the
+;; latency of {mul,mult}->{mfhi,mflo}.
+(define_insn_reservation "ir_vr55_imul_si" 5
+ (and (eq_attr "cpu" "r5500")
+ (and (eq_attr "type" "imul,imul3")
+ (eq_attr "mode" "SI")))
+ "vr55_mac")
+
+;; The default latency is for pre-reload scheduling and handles the case
+;; where a pseudo destination will be stored in a GPR (as it usually is).
+;; The delay includes the latency of the dmult itself and the anticipated
+;; mflo or mfhi.
+;;
+;; Once the mflo or mfhi has been created, bypasses handle the latency
+;; between it and the dmult.
+(define_insn_reservation "ir_vr55_imul_di" 9
+ (and (eq_attr "cpu" "r5500")
+ (and (eq_attr "type" "imul,imul3")
+ (eq_attr "mode" "DI")))
+ "vr55_mac*4")
+
+;; The default latency is as for ir_vr55_imul_si.
+(define_insn_reservation "ir_vr55_imadd" 5
+ (and (eq_attr "cpu" "r5500")
+ (eq_attr "type" "imadd"))
+ "vr55_mac")
+
+(define_bypass 1
+ "ir_vr55_imul_si,ir_vr55_imadd"
+ "ir_vr55_imadd"
+ "mips_linked_madd_p")
+
+(define_bypass 2
+ "ir_vr55_imul_si,ir_vr55_imadd"
+ "ir_vr55_mfhilo")
+
+(define_bypass 4
+ "ir_vr55_imul_di"
+ "ir_vr55_mfhilo")
+
+;; Divide algorithm is early out with best latency of 7 pcycles.
+;; Use worst case for scheduling purposes.
+(define_insn_reservation "ir_vr55_idiv_si" 42
+ (and (eq_attr "cpu" "r5500")
+ (and (eq_attr "type" "idiv")
+ (eq_attr "mode" "SI")))
+ "vr55_mac")
+
+(define_insn_reservation "ir_vr55_idiv_di" 74
+ (and (eq_attr "cpu" "r5500")
+ (and (eq_attr "type" "idiv")
+ (eq_attr "mode" "DI")))
+ "vr55_mac")
+
+(define_insn_reservation "ir_vr55_fadd" 4
+ (and (eq_attr "cpu" "r5500")
+ (eq_attr "type" "fadd"))
+ "vr55_fp")
+
+(define_insn_reservation "ir_vr55_fmul_sf" 5
+ (and (eq_attr "cpu" "r5500")
+ (and (eq_attr "type" "fmul")
+ (eq_attr "mode" "SF")))
+ "vr55_mac")
+
+(define_insn_reservation "ir_vr55_fmul_df" 6
+ (and (eq_attr "cpu" "r5500")
+ (and (eq_attr "type" "fmul")
+ (eq_attr "mode" "DF")))
+ "vr55_mac")
+
+(define_insn_reservation "ir_vr55_fmadd_sf" 9
+ (and (eq_attr "cpu" "r5500")
+ (and (eq_attr "type" "fmadd")
+ (eq_attr "mode" "SF")))
+ "vr55_mac")
+
+(define_insn_reservation "ir_vr55_fmadd_df" 10
+ (and (eq_attr "cpu" "r5500")
+ (and (eq_attr "type" "fmadd")
+ (eq_attr "mode" "DF")))
+ "vr55_mac")
+
+(define_insn_reservation "ir_vr55_fdiv_sf" 30
+ (and (eq_attr "cpu" "r5500")
+ (and (eq_attr "type" "fdiv,frdiv,fsqrt")
+ (eq_attr "mode" "SF")))
+ "vr55_mac")
+
+(define_insn_reservation "ir_vr55_fdiv_df" 59
+ (and (eq_attr "cpu" "r5500")
+ (and (eq_attr "type" "fdiv,frdiv,fsqrt")
+ (eq_attr "mode" "DF")))
+ "vr55_mac")
+
+(define_insn_reservation "ir_vr55_fabs" 2
+ (and (eq_attr "cpu" "r5500")
+ (eq_attr "type" "fabs,fneg,fmove"))
+ "vr55_fp")
+
+(define_insn_reservation "ir_vr55_fcmp" 2
+ (and (eq_attr "cpu" "r5500")
+ (eq_attr "type" "fcmp"))
+ "vr55_fp")
+
+(define_insn_reservation "ir_vr55_fcvt_sf" 4
+ (and (eq_attr "cpu" "r5500")
+ (and (eq_attr "type" "fcvt")
+ (eq_attr "mode" "SF")))
+ "vr55_fp")
+
+(define_insn_reservation "ir_vr55_fcvt_df" 6
+ (and (eq_attr "cpu" "r5500")
+ (and (eq_attr "type" "fcvt")
+ (eq_attr "mode" "DF")))
+ "vr55_fp")
+
+(define_insn_reservation "ir_vr55_frsqrt_sf" 60
+ (and (eq_attr "cpu" "r5500")
+ (and (eq_attr "type" "frsqrt")
+ (eq_attr "mode" "SF")))
+ "vr55_mac")
+
+(define_insn_reservation "ir_vr55_frsqrt_df" 118
+ (and (eq_attr "cpu" "r5500")
+ (and (eq_attr "type" "frsqrt")
+ (eq_attr "mode" "DF")))
+ "vr55_mac")
+
+(define_insn_reservation "ir_vr55_multi" 1
+ (and (eq_attr "cpu" "r5500")
+ (eq_attr "type" "multi"))
+ "vr55_dp0+vr55_dp1+vr55_mem+vr55_mac+vr55_fp+vr55_bru")
diff --git a/gcc/config/mips/5k.md b/gcc/config/mips/5k.md
new file mode 100644
index 000000000..ade06ec44
--- /dev/null
+++ b/gcc/config/mips/5k.md
@@ -0,0 +1,229 @@
+;; DFA-based pipeline descriptions for MIPS32 5K processor family
+;; Contributed by David Ung (davidu@mips.com)
+;; and Nigel Stephens (nigel@mips.com)
+;;
+;; References:
+;; "MIPS64 5K Processor Core Family Software User's Manual,
+;; Doc no: MD00012, Rev 2.09, Jan 28, 2005."
+;;
+;; 5Kc - Single issue with no floating point unit.
+;; 5kf - Separate floating point pipe which can dual-issue with the
+;; integer pipe.
+;;
+;; Copyright (C) 2005, 2007 Free Software Foundation, 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/>.
+
+(define_automaton "r5k_cpu, r5k_mdu, r5k_fpu")
+
+;; Integer execution unit.
+(define_cpu_unit "r5k_ixu_arith" "r5k_cpu")
+(define_cpu_unit "r5k_ixu_mpydiv" "r5k_mdu")
+(define_cpu_unit "r5kf_fpu_arith" "r5k_fpu")
+
+(define_insn_reservation "r5k_int_load" 2
+ (and (eq_attr "cpu" "5kc,5kf")
+ (eq_attr "type" "load"))
+ "r5k_ixu_arith")
+
+(define_insn_reservation "r5k_int_prefetch" 1
+ (and (eq_attr "cpu" "5kc,5kf")
+ (eq_attr "type" "prefetch,prefetchx"))
+ "r5k_ixu_arith")
+
+(define_insn_reservation "r5k_int_store" 1
+ (and (eq_attr "cpu" "5kc,5kf")
+ (eq_attr "type" "store"))
+ "r5k_ixu_arith")
+
+;; Divides
+(define_insn_reservation "r5k_int_divsi" 34
+ (and (eq_attr "cpu" "5kc,5kf")
+ (and (eq_attr "type" "idiv")
+ (eq_attr "mode" "!DI")))
+ "r5k_ixu_arith+(r5k_ixu_mpydiv*34)")
+
+(define_insn_reservation "r5k_int_divdi" 66
+ (and (eq_attr "cpu" "5kc,5kf")
+ (and (eq_attr "type" "idiv")
+ (eq_attr "mode" "DI")))
+ "r5k_ixu_arith+(r5k_ixu_mpydiv*66)")
+
+;; 32x32 multiply
+;; 32x16 is faster, but there's no way to detect this
+(define_insn_reservation "r5k_int_mult" 2
+ (and (eq_attr "cpu" "5kc,5kf")
+ (and (eq_attr "type" "imul,imadd")
+ (eq_attr "mode" "SI")))
+ "r5k_ixu_arith+(r5k_ixu_mpydiv*2)")
+
+;; 64x64 multiply
+(define_insn_reservation "r5k_int_mult_64" 9
+ (and (eq_attr "cpu" "5kc,5kf")
+ (and (eq_attr "type" "imul,imadd")
+ (eq_attr "mode" "DI")))
+ "r5k_ixu_arith+(r5k_ixu_mpydiv*2)")
+
+;; 3 operand MUL 32x32
+(define_insn_reservation "r5k_int_mul" 4
+ (and (eq_attr "cpu" "5kc,5kf")
+ (and (eq_attr "type" "imul3")
+ (eq_attr "mode" "SI")))
+ "r5k_ixu_arith+(r5k_ixu_mpydiv*2)")
+
+;; Move to HI/LO -> MADD/MSUB,MFHI/MFLO has a 1 cycle latency.
+(define_insn_reservation "r5k_int_mthilo" 1
+ (and (eq_attr "cpu" "5kc,5kf")
+ (eq_attr "type" "mthilo"))
+ "r5k_ixu_arith+r5k_ixu_mpydiv")
+
+;; Move from HI/LO -> integer operation has a 2 cycle latency.
+(define_insn_reservation "r5k_int_mfhilo" 2
+ (and (eq_attr "cpu" "5kc,5kf")
+ (eq_attr "type" "mfhilo"))
+ "r5k_ixu_arith+r5k_ixu_mpydiv")
+
+;; All other integer insns.
+(define_insn_reservation "r5k_int_alu" 1
+ (and (eq_attr "cpu" "5kc,5kf")
+ (eq_attr "type" "arith,condmove,const,logical,move,nop,shift,signext,slt"))
+ "r5k_ixu_arith")
+
+(define_insn_reservation "r5k_int_branch" 1
+ (and (eq_attr "cpu" "5kc,5kf")
+ (eq_attr "type" "branch"))
+ "r5k_ixu_arith")
+
+;; JR/JALR always cause one pipeline bubble because of interlock.
+(define_insn_reservation "r5k_int_jump" 2
+ (and (eq_attr "cpu" "5kc,5kf")
+ (eq_attr "type" "jump,call"))
+ "r5k_ixu_arith")
+
+;; Any -> JR/JALR (without dependency) : 1 clock issue delay
+;; Any -> JR/JALR (with dependency) : 2 clock issue delay
+;; load -> JR/JALR (with dependency) : 3 clock issue delay
+;; mfhilo -> JR/JALR (with dependency) : 3 clock issue delay
+;; mul -> JR/JALR (with dependency) : 3 clock issue delay
+(define_bypass 2 "r5k_int_alu" "r5k_int_jump")
+(define_bypass 3 "r5k_int_load" "r5k_int_jump")
+(define_bypass 3 "r5k_int_mfhilo" "r5k_int_jump")
+(define_bypass 3 "r5k_int_mul" "r5k_int_jump")
+
+;; Unknown or multi - single issue
+(define_insn_reservation "r5k_int_unknown" 1
+ (and (eq_attr "cpu" "5kc,5kf")
+ (eq_attr "type" "unknown,multi"))
+ "r5k_ixu_arith+r5k_ixu_mpydiv")
+
+
+;; Floating Point Instructions
+;; The 5Kf is a partial dual-issue cpu which can dual issue an integer
+;; and floating-point instruction in the same cycle.
+
+;; fadd, fabs, fneg
+(define_insn_reservation "r5kf_fadd" 4
+ (and (eq_attr "cpu" "5kf")
+ (eq_attr "type" "fadd,fabs,fneg"))
+ "r5kf_fpu_arith")
+
+;; fmove, fcmove
+(define_insn_reservation "r5kf_fmove" 4
+ (and (eq_attr "cpu" "5kf")
+ (eq_attr "type" "fmove"))
+ "r5kf_fpu_arith")
+
+;; fload
+(define_insn_reservation "r5kf_fload" 3
+ (and (eq_attr "cpu" "5kf")
+ (eq_attr "type" "fpload,fpidxload"))
+ "r5kf_fpu_arith")
+
+;; fstore
+(define_insn_reservation "r5kf_fstore" 1
+ (and (eq_attr "cpu" "5kf")
+ (eq_attr "type" "fpstore"))
+ "r5kf_fpu_arith")
+
+;; fmul, fmadd
+(define_insn_reservation "r5kf_fmul_sf" 4
+ (and (eq_attr "cpu" "5kf")
+ (and (eq_attr "type" "fmul,fmadd")
+ (eq_attr "mode" "SF")))
+ "r5kf_fpu_arith")
+
+(define_insn_reservation "r5kf_fmul_df" 5
+ (and (eq_attr "cpu" "5kf")
+ (and (eq_attr "type" "fmul,fmadd")
+ (eq_attr "mode" "DF")))
+ "r5kf_fpu_arith*2")
+
+;; fdiv, fsqrt, frsqrt
+(define_insn_reservation "r5kf_fdiv_sf" 17
+ (and (eq_attr "cpu" "5kf")
+ (and (eq_attr "type" "fdiv,fsqrt,frsqrt")
+ (eq_attr "mode" "SF")))
+ "r5kf_fpu_arith*14")
+
+(define_insn_reservation "r5kf_fdiv_df" 32
+ (and (eq_attr "cpu" "5kf")
+ (and (eq_attr "type" "fdiv,fsqrt")
+ (eq_attr "mode" "DF")))
+ "r5kf_fpu_arith*29")
+
+;; frsqrt
+(define_insn_reservation "r5kf_frsqrt_df" 35
+ (and (eq_attr "cpu" "5kf")
+ (and (eq_attr "type" "frsqrt")
+ (eq_attr "mode" "DF")))
+ "r5kf_fpu_arith*31")
+
+;; fcmp
+(define_insn_reservation "r5kf_fcmp" 2
+ (and (eq_attr "cpu" "5kf")
+ (eq_attr "type" "fcmp"))
+ "r5kf_fpu_arith")
+
+;; fcmp -> movf.fmt & movt.fmt bypass (dependency must be on condition)
+(define_bypass 1 "r5kf_fcmp" "r5kf_fmove")
+
+;; fcvt (cvt.d.s, cvt.[sd].[wl]
+(define_insn_reservation "r5kf_fcvt_d2s" 4
+ (and (eq_attr "cpu" "5kf")
+ (and (eq_attr "type" "fcvt")
+ (eq_attr "cnv_mode" "I2S,I2D,S2D")))
+ "r5kf_fpu_arith")
+
+;; fcvt (cvt.s.d)
+(define_insn_reservation "r5kf_fcvt_s2d" 6
+ (and (eq_attr "cpu" "5kc")
+ (and (eq_attr "type" "fcvt")
+ (eq_attr "cnv_mode" "D2S")))
+ "r5kf_fpu_arith")
+
+;; fcvt (cvt.[wl].[sd], etc)
+(define_insn_reservation "r5kf_fcvt_f2i" 5
+ (and (eq_attr "cpu" "5kf")
+ (and (eq_attr "type" "fcvt")
+ (eq_attr "cnv_mode" "S2I,D2I")))
+ "r5kf_fpu_arith")
+
+;; fxfer (mfc1, mfhc1, mtc1, mthc1) - single issue
+(define_insn_reservation "r5kf_fxfer" 2
+ (and (eq_attr "cpu" "5kf")
+ (eq_attr "type" "mfc,mtc"))
+ "r5k_ixu_arith+r5kf_fpu_arith")
diff --git a/gcc/config/mips/6000.md b/gcc/config/mips/6000.md
new file mode 100644
index 000000000..51730fb08
--- /dev/null
+++ b/gcc/config/mips/6000.md
@@ -0,0 +1,56 @@
+;; R6000 pipeline description.
+;; Copyright (C) 2004, 2007 Free Software Foundation, 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/>.
+
+
+;; This file overrides parts of generic.md. It is derived from the
+;; old define_function_unit description.
+
+(define_insn_reservation "r6k_fcmp" 2
+ (and (eq_attr "cpu" "r6000")
+ (eq_attr "type" "fcmp"))
+ "alu")
+
+(define_insn_reservation "r6k_fadd" 3
+ (and (eq_attr "cpu" "r6000")
+ (eq_attr "type" "fadd"))
+ "alu")
+
+(define_insn_reservation "r6k_fmul_single" 5
+ (and (eq_attr "cpu" "r6000")
+ (and (eq_attr "type" "fmul,fmadd")
+ (eq_attr "mode" "SF")))
+ "alu")
+
+(define_insn_reservation "r6k_fmul_double" 6
+ (and (eq_attr "cpu" "r6000")
+ (and (eq_attr "type" "fmul,fmadd")
+ (eq_attr "mode" "DF")))
+ "alu")
+
+(define_insn_reservation "r6k_fdiv_single" 15
+ (and (eq_attr "cpu" "r6000")
+ (and (eq_attr "type" "fdiv,frdiv")
+ (eq_attr "mode" "SF")))
+ "alu")
+
+(define_insn_reservation "r6k_fdiv_double" 16
+ (and (eq_attr "cpu" "r6000")
+ (and (eq_attr "type" "fdiv,frdiv")
+ (eq_attr "mode" "DF")))
+ "alu")
diff --git a/gcc/config/mips/7000.md b/gcc/config/mips/7000.md
new file mode 100644
index 000000000..6c91d0472
--- /dev/null
+++ b/gcc/config/mips/7000.md
@@ -0,0 +1,214 @@
+;; DFA-based pipeline description for the RM7000.
+;; Copyright (C) 2003, 2004, 2005, 2007 Free Software Foundation, 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/>.
+
+;; .........................
+;;
+;; The RM7000 is a dual-issue processor that can bundle instructions as:
+;; {arith|load|store}{arith|imul|idiv|branch|float}
+;;
+;; Reference:
+;; "RM7000 Family User Manual, PMC-2002296"
+;;
+;; .........................
+
+;; Use three automata to isolate long latency operations, reducing space.
+(define_automaton "rm7000_other, rm7000_fdiv, rm7000_idiv")
+
+;;
+;; Describe the resources.
+;;
+
+;; Global
+(define_cpu_unit "rm7_iss0,rm7_iss1" "rm7000_other")
+
+;; Integer execution unit (M-Pipe).
+(define_cpu_unit "ixum_addsub_agen" "rm7000_other")
+
+;; Integer execution unit (F-Pipe).
+(define_cpu_unit "ixuf_addsub" "rm7000_other")
+(define_cpu_unit "ixuf_branch" "rm7000_other")
+(define_cpu_unit "ixuf_mpydiv" "rm7000_other")
+(define_cpu_unit "ixuf_mpydiv_iter" "rm7000_idiv")
+;; Floating-point unit (F-Pipe).
+(define_cpu_unit "fxuf_add" "rm7000_other")
+(define_cpu_unit "fxuf_mpy" "rm7000_other")
+(define_cpu_unit "fxuf_mpy_iter" "rm7000_fdiv")
+(define_cpu_unit "fxuf_divsqrt" "rm7000_other")
+(define_cpu_unit "fxuf_divsqrt_iter" "rm7000_fdiv")
+
+(exclusion_set "ixuf_addsub"
+ "ixuf_branch,ixuf_mpydiv,fxuf_add,fxuf_mpy,fxuf_divsqrt")
+(exclusion_set "ixuf_branch" "ixuf_mpydiv,fxuf_add,fxuf_mpy,fxuf_divsqrt")
+(exclusion_set "ixuf_mpydiv" "fxuf_add,fxuf_mpy,fxuf_divsqrt")
+(exclusion_set "fxuf_add" "fxuf_mpy,fxuf_divsqrt")
+(exclusion_set "fxuf_mpy" "fxuf_divsqrt")
+
+;; After branch any insn cannot be issued.
+(absence_set "rm7_iss0,rm7_iss1" "ixuf_branch")
+
+;;
+;; Define reservations for unit name mnemonics or combinations.
+;;
+
+(define_reservation "rm7_iss" "rm7_iss0|rm7_iss1")
+(define_reservation "rm7_single_dispatch" "rm7_iss0+rm7_iss1")
+
+(define_reservation "rm7_iaddsub" "rm7_iss+(ixum_addsub_agen|ixuf_addsub)")
+(define_reservation "rm7_imem" "rm7_iss+ixum_addsub_agen")
+(define_reservation "rm7_impydiv" "rm7_iss+ixuf_mpydiv")
+(define_reservation "rm7_impydiv_iter" "ixuf_mpydiv_iter")
+(define_reservation "rm7_branch" "rm7_iss+ixuf_branch")
+
+(define_reservation "rm7_fpadd" "rm7_iss+fxuf_add")
+(define_reservation "rm7_fpmpy" "rm7_iss+fxuf_mpy")
+(define_reservation "rm7_fpmpy_iter" "fxuf_mpy_iter")
+(define_reservation "rm7_fpdivsqr" "rm7_iss+fxuf_divsqrt")
+(define_reservation "rm7_fpdivsqr_iter" "fxuf_divsqrt_iter")
+
+;;
+;; Describe instruction reservations for integer operations.
+;;
+
+(define_insn_reservation "rm7_int_other" 1
+ (and (eq_attr "cpu" "r7000")
+ (eq_attr "type" "arith,shift,signext,slt,clz,const,condmove,logical,move,nop,trap"))
+ "rm7_iaddsub")
+
+(define_insn_reservation "rm7_ld" 2
+ (and (eq_attr "cpu" "r7000")
+ (eq_attr "type" "load,fpload,fpidxload"))
+ "rm7_imem")
+
+(define_insn_reservation "rm7_st" 1
+ (and (eq_attr "cpu" "r7000")
+ (eq_attr "type" "store,fpstore,fpidxstore"))
+ "rm7_imem")
+
+(define_insn_reservation "rm7_idiv_si" 36
+ (and (eq_attr "cpu" "r7000")
+ (and (eq_attr "type" "idiv")
+ (eq_attr "mode" "SI")))
+ "rm7_impydiv+(rm7_impydiv_iter*36)")
+
+(define_insn_reservation "rm7_idiv_di" 68
+ (and (eq_attr "cpu" "r7000")
+ (and (eq_attr "type" "idiv")
+ (eq_attr "mode" "DI")))
+ "rm7_impydiv+(rm7_impydiv_iter*68)")
+
+(define_insn_reservation "rm7_impy_si_mult" 5
+ (and (eq_attr "cpu" "r7000")
+ (and (eq_attr "type" "imul,imadd")
+ (eq_attr "mode" "SI")))
+ "rm7_impydiv+(rm7_impydiv_iter*3)")
+
+;; There are an additional 2 stall cycles.
+(define_insn_reservation "rm7_impy_si_mul" 2
+ (and (eq_attr "cpu" "r7000")
+ (and (eq_attr "type" "imul3")
+ (eq_attr "mode" "SI")))
+ "rm7_impydiv")
+
+(define_insn_reservation "rm7_impy_di" 9
+ (and (eq_attr "cpu" "r7000")
+ (and (eq_attr "type" "imul,imul3")
+ (eq_attr "mode" "DI")))
+ "rm7_impydiv+(rm7_impydiv_iter*8)")
+
+;; Move to/from HI/LO.
+(define_insn_reservation "rm7_mthilo" 3
+ (and (eq_attr "cpu" "r7000")
+ (eq_attr "type" "mthilo"))
+ "rm7_impydiv")
+
+(define_insn_reservation "rm7_mfhilo" 1
+ (and (eq_attr "cpu" "r7000")
+ (eq_attr "type" "mfhilo"))
+ "rm7_impydiv")
+
+;; Move to/from fp coprocessor.
+(define_insn_reservation "rm7_ixfer" 2
+ (and (eq_attr "cpu" "r7000")
+ (eq_attr "type" "mfc,mtc"))
+ "rm7_iaddsub")
+
+(define_insn_reservation "rm7_ibr" 3
+ (and (eq_attr "cpu" "r7000")
+ (eq_attr "type" "branch,jump,call"))
+ "rm7_branch")
+
+;;
+;; Describe instruction reservations for the floating-point operations.
+;;
+(define_insn_reservation "rm7_fp_quick" 4
+ (and (eq_attr "cpu" "r7000")
+ (eq_attr "type" "fneg,fcmp,fabs,fmove"))
+ "rm7_fpadd")
+
+(define_insn_reservation "rm7_fp_other" 4
+ (and (eq_attr "cpu" "r7000")
+ (eq_attr "type" "fadd"))
+ "rm7_fpadd")
+
+(define_insn_reservation "rm7_fp_cvt" 4
+ (and (eq_attr "cpu" "r7000")
+ (eq_attr "type" "fcvt"))
+ "rm7_fpadd")
+
+(define_insn_reservation "rm7_fp_divsqrt_df" 36
+ (and (eq_attr "cpu" "r7000")
+ (and (eq_attr "type" "fdiv,frdiv,fsqrt")
+ (eq_attr "mode" "DF")))
+ "rm7_fpdivsqr+(rm7_fpdivsqr_iter*36)")
+
+(define_insn_reservation "rm7_fp_divsqrt_sf" 21
+ (and (eq_attr "cpu" "r7000")
+ (and (eq_attr "type" "fdiv,frdiv,fsqrt")
+ (eq_attr "mode" "SF")))
+ "rm7_fpdivsqr+(rm7_fpdivsqr_iter*21)")
+
+(define_insn_reservation "rm7_fp_rsqrt_df" 68
+ (and (eq_attr "cpu" "r7000")
+ (and (eq_attr "type" "frsqrt")
+ (eq_attr "mode" "DF")))
+ "rm7_fpdivsqr+(rm7_fpdivsqr_iter*68)")
+
+(define_insn_reservation "rm7_fp_rsqrt_sf" 38
+ (and (eq_attr "cpu" "r7000")
+ (and (eq_attr "type" "frsqrt")
+ (eq_attr "mode" "SF")))
+ "rm7_fpdivsqr+(rm7_fpdivsqr_iter*38)")
+
+(define_insn_reservation "rm7_fp_mpy_sf" 4
+ (and (eq_attr "cpu" "r7000")
+ (and (eq_attr "type" "fmul,fmadd")
+ (eq_attr "mode" "SF")))
+ "rm7_fpmpy+rm7_fpmpy_iter")
+
+(define_insn_reservation "rm7_fp_mpy_df" 5
+ (and (eq_attr "cpu" "r7000")
+ (and (eq_attr "type" "fmul,fmadd")
+ (eq_attr "mode" "DF")))
+ "rm7_fpmpy+(rm7_fpmpy_iter*2)")
+
+;; Force single-dispatch for unknown or multi.
+(define_insn_reservation "rm7_unknown" 1
+ (and (eq_attr "cpu" "r7000")
+ (eq_attr "type" "unknown,multi"))
+ "rm7_single_dispatch")
diff --git a/gcc/config/mips/74k.md b/gcc/config/mips/74k.md
new file mode 100644
index 000000000..b75bfc4b9
--- /dev/null
+++ b/gcc/config/mips/74k.md
@@ -0,0 +1,418 @@
+;; DFA-based pipeline description for MIPS32 model 74k.
+;; Contributed by MIPS Technologies and CodeSourcery.
+;;
+;; Reference:
+;; "MIPS32 74K Microarchitecure Specification Rev. 01.02 Jun 15, 2006"
+;; "MIPS32 74Kf Processor Core Datasheet Jun 2, 2006"
+;;
+;; Copyright (C) 2007 Free Software Foundation, 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/>.
+
+(define_automaton "r74k_mdu_pipe, r74k_alu_pipe, r74k_agen_pipe, r74k_fpu")
+(define_cpu_unit "r74k_mul" "r74k_mdu_pipe")
+(define_cpu_unit "r74k_alu" "r74k_alu_pipe")
+(define_cpu_unit "r74k_agen" "r74k_agen_pipe")
+(define_cpu_unit "r74k_fpu_arith" "r74k_fpu")
+(define_cpu_unit "r74k_fpu_ldst" "r74k_fpu")
+
+;; --------------------------------------------------------------
+;; Producers
+;; --------------------------------------------------------------
+
+;; ALU: Logicals/Arithmetics
+;; - Logicals, move (addu/addiu with rt = 0), Set less than,
+;; sign extend - 1 cycle
+(define_insn_reservation "r74k_int_logical" 1
+ (and (eq_attr "cpu" "74kc,74kf2_1,74kf1_1,74kf3_2")
+ (eq_attr "type" "logical,move,signext,slt"))
+ "r74k_alu")
+
+;; - Arithmetics - 2 cycles
+(define_insn_reservation "r74k_int_arith" 2
+ (and (eq_attr "cpu" "74kc,74kf2_1,74kf1_1,74kf3_2")
+ (eq_attr "type" "arith,const,shift,clz"))
+ "r74k_alu")
+
+(define_insn_reservation "r74k_int_nop" 0
+ (and (eq_attr "cpu" "74kc,74kf2_1,74kf1_1,74kf3_2")
+ (eq_attr "type" "nop"))
+ "nothing")
+
+(define_insn_reservation "r74k_int_cmove" 4
+ (and (eq_attr "cpu" "74kc,74kf2_1,74kf1_1,74kf3_2")
+ (eq_attr "type" "condmove"))
+ "r74k_agen*2")
+
+;; MDU: fully pipelined multiplier
+;; mult - delivers result to hi/lo in 4 cycle (pipelined)
+(define_insn_reservation "r74k_int_mult" 4
+ (and (eq_attr "cpu" "74kc,74kf2_1,74kf1_1,74kf3_2")
+ (eq_attr "type" "imul"))
+ "r74k_alu+r74k_mul")
+
+;; madd, msub - delivers result to hi/lo in 4 cycle (pipelined)
+(define_insn_reservation "r74k_int_madd" 4
+ (and (eq_attr "cpu" "74kc,74kf2_1,74kf1_1,74kf3_2")
+ (eq_attr "type" "imadd"))
+ "r74k_alu+r74k_mul")
+
+;; mul - delivers result to general register in 7 cycles
+(define_insn_reservation "r74k_int_mul3" 7
+ (and (eq_attr "cpu" "74kc,74kf2_1,74kf1_1,74kf3_2")
+ (eq_attr "type" "imul3"))
+ "r74k_alu+r74k_mul")
+
+;; mfhi, mflo, mflhxu - deliver result to gpr in 7 cycles
+(define_insn_reservation "r74k_int_mfhilo" 7
+ (and (eq_attr "cpu" "74kc,74kf2_1,74kf1_1,74kf3_2")
+ (eq_attr "type" "mfhilo"))
+ "r74k_alu+r74k_mul")
+
+;; mthi, mtlo, mtlhx - deliver result to hi/lo, thence madd, handled as bypass
+(define_insn_reservation "r74k_int_mthilo" 7
+ (and (eq_attr "cpu" "74kc,74kf2_1,74kf1_1,74kf3_2")
+ (eq_attr "type" "mthilo"))
+ "r74k_alu+r74k_mul")
+
+;; div - default to 50 cycles for 32bit operands. Faster for 8 bit,
+;; but is tricky to identify.
+(define_insn_reservation "r74k_int_div" 50
+ (and (eq_attr "cpu" "74kc,74kf2_1,74kf1_1,74kf3_2")
+ (eq_attr "type" "idiv"))
+ "r74k_alu+r74k_mul*50")
+
+;; call
+(define_insn_reservation "r74k_int_call" 1
+ (and (eq_attr "cpu" "74kc,74kf2_1,74kf1_1,74kf3_2")
+ (eq_attr "type" "call"))
+ "r74k_agen")
+
+;; branch/jump
+(define_insn_reservation "r74k_int_jump" 1
+ (and (eq_attr "cpu" "74kc,74kf2_1,74kf1_1,74kf3_2")
+ (eq_attr "type" "branch,jump"))
+ "r74k_agen")
+
+;; loads: lb, lbu, lh, lhu, ll, lw, lwl, lwr, lwpc, lwxs
+;; prefetch: prefetch, prefetchx
+(define_insn_reservation "r74k_int_load" 3
+ (and (eq_attr "cpu" "74kc,74kf2_1,74kf1_1,74kf3_2")
+ (eq_attr "type" "load,prefetch,prefetchx"))
+ "r74k_agen")
+
+;; stores
+(define_insn_reservation "r74k_int_store" 1
+ (and (eq_attr "cpu" "74kc,74kf2_1,74kf1_1,74kf3_2")
+ (and (eq_attr "type" "store")
+ (eq_attr "mode" "!unknown")))
+ "r74k_agen")
+
+
+;; Unknowns - Currently these include blockage, consttable and alignment
+;; rtls. They do not really affect scheduling latency, (blockage
+;; affects scheduling via log links, but not used here).
+;;
+(define_insn_reservation "r74k_unknown" 1
+ (and (eq_attr "cpu" "74kc,74kf2_1,74kf1_1,74kf3_2")
+ (eq_attr "type" "unknown"))
+ "r74k_alu")
+
+(define_insn_reservation "r74k_multi" 10
+ (and (eq_attr "cpu" "74kc,74kf2_1,74kf1_1,74kf3_2")
+ (eq_attr "type" "multi"))
+ "(r74k_alu+r74k_agen)*10")
+
+;; --------------------------------------------------------------
+;; Bypass to Consumer
+;; --------------------------------------------------------------
+
+;; load->next use : 3 cycles (Default)
+;; load->load base: 4 cycles
+;; load->store base: 4 cycles
+(define_bypass 4 "r74k_int_load" "r74k_int_load")
+(define_bypass 4 "r74k_int_load" "r74k_int_store" "!store_data_bypass_p")
+
+;; logical/move/slt/signext->next use : 1 cycles (Default)
+;; logical/move/slt/signext->load base: 2 cycles
+;; logical/move/slt/signext->store base: 2 cycles
+(define_bypass 2 "r74k_int_logical" "r74k_int_load")
+(define_bypass 2 "r74k_int_logical" "r74k_int_store" "!store_data_bypass_p")
+
+;; arith->next use : 2 cycles (Default)
+;; arith->load base: 3 cycles
+;; arith->store base: 3 cycles
+(define_bypass 3 "r74k_int_arith" "r74k_int_load")
+(define_bypass 3 "r74k_int_arith" "r74k_int_store" "!store_data_bypass_p")
+
+;; cmove->next use : 4 cycles (Default)
+;; cmove->load base: 5 cycles
+;; cmove->store base: 5 cycles
+(define_bypass 5 "r74k_int_cmove" "r74k_int_load")
+(define_bypass 5 "r74k_int_cmove" "r74k_int_store" "!store_data_bypass_p")
+
+;; mult/madd/msub->int_mfhilo : 4 cycles (default)
+;; mult->madd/msub : 1 cycles
+;; madd/msub->madd/msub : 1 cycles
+(define_bypass 1 "r74k_int_mult,r74k_int_mul3" "r74k_int_madd"
+ "mips_linked_madd_p")
+(define_bypass 1 "r74k_int_madd" "r74k_int_madd"
+ "mips_linked_madd_p")
+
+;; --------------------------------------------------------------
+;; Floating Point Instructions
+;; --------------------------------------------------------------
+
+;; 74Kf FPU runs at 1:1 or 2:1 core/FPU clock ratio.
+
+;; fadd, fabs, fneg,
+(define_insn_reservation "r74kf1_1_fadd" 4
+ (and (eq_attr "cpu" "74kf1_1")
+ (eq_attr "type" "fadd,fabs,fneg"))
+ "r74k_fpu_arith")
+
+(define_insn_reservation "r74kf2_1_fadd" 8
+ (and (eq_attr "cpu" "74kf2_1")
+ (eq_attr "type" "fadd,fabs,fneg"))
+ "r74k_fpu_arith*2")
+
+(define_insn_reservation "r74kf3_2_fadd" 6
+ (and (eq_attr "cpu" "74kf3_2")
+ (eq_attr "type" "fadd,fabs,fneg"))
+ "r74k_fpu_arith")
+
+;; fmove, fcmove
+(define_insn_reservation "r74kf1_1_fmove" 4
+ (and (eq_attr "cpu" "74kf1_1")
+ (eq_attr "type" "fmove"))
+ "r74k_fpu_arith")
+
+(define_insn_reservation "r74kf2_1_fmove" 8
+ (and (eq_attr "cpu" "74kf2_1")
+ (eq_attr "type" "fmove"))
+ "r74k_fpu_arith*2")
+
+(define_insn_reservation "r74kf3_2_fmove" 6
+ (and (eq_attr "cpu" "74kf3_2")
+ (eq_attr "type" "fmove"))
+ "r74k_fpu_arith")
+
+;; fload
+(define_insn_reservation "r74kf1_1_fload" 4
+ (and (eq_attr "cpu" "74kf1_1")
+ (eq_attr "type" "fpload,fpidxload"))
+ "r74k_agen+r74k_fpu_ldst")
+
+(define_insn_reservation "r74kf2_1_fload" 8
+ (and (eq_attr "cpu" "74kf2_1")
+ (eq_attr "type" "fpload,fpidxload"))
+ "r74k_agen+(r74k_fpu_ldst*2)")
+
+(define_insn_reservation "r74kf3_2_fload" 6
+ (and (eq_attr "cpu" "74kf3_2")
+ (eq_attr "type" "fpload,fpidxload"))
+ "r74k_agen+r74k_fpu_ldst")
+
+;; fstore
+(define_insn_reservation "r74kf1_1_fstore" 1
+ (and (eq_attr "cpu" "74kf1_1")
+ (eq_attr "type" "fpstore,fpidxstore"))
+ "r74k_agen+r74k_fpu_ldst")
+
+(define_insn_reservation "r74kf2_1_fstore" 2
+ (and (eq_attr "cpu" "74kf2_1")
+ (eq_attr "type" "fpstore,fpidxstore"))
+ "r74k_agen+(r74k_fpu_ldst*2)")
+
+(define_insn_reservation "r74kf3_2_fstore" 1
+ (and (eq_attr "cpu" "74kf3_2")
+ (eq_attr "type" "fpstore,fpidxstore"))
+ "r74k_agen+r74k_fpu_ldst")
+
+;; fmul, fmadd
+(define_insn_reservation "r74kf1_1_fmul_sf" 4
+ (and (eq_attr "cpu" "74kf1_1")
+ (and (eq_attr "type" "fmul,fmadd")
+ (eq_attr "mode" "SF")))
+ "r74k_fpu_arith")
+
+(define_insn_reservation "r74kf2_1_fmul_sf" 8
+ (and (eq_attr "cpu" "74kf2_1")
+ (and (eq_attr "type" "fmul,fmadd")
+ (eq_attr "mode" "SF")))
+ "r74k_fpu_arith*2")
+
+(define_insn_reservation "r74kf3_2_fmul_sf" 6
+ (and (eq_attr "cpu" "74kf3_2")
+ (and (eq_attr "type" "fmul,fmadd")
+ (eq_attr "mode" "SF")))
+ "r74k_fpu_arith")
+
+(define_insn_reservation "r74kf1_1_fmul_df" 5
+ (and (eq_attr "cpu" "74kf1_1")
+ (and (eq_attr "type" "fmul,fmadd")
+ (eq_attr "mode" "DF")))
+ "r74k_fpu_arith*2")
+
+(define_insn_reservation "r74kf2_1_fmul_df" 10
+ (and (eq_attr "cpu" "74kf2_1")
+ (and (eq_attr "type" "fmul,fmadd")
+ (eq_attr "mode" "DF")))
+ "r74k_fpu_arith*4")
+
+(define_insn_reservation "r74kf3_2_fmul_df" 7
+ (and (eq_attr "cpu" "74kf3_2")
+ (and (eq_attr "type" "fmul,fmadd")
+ (eq_attr "mode" "DF")))
+ "r74k_fpu_arith*2")
+
+;; fdiv, fsqrt
+(define_insn_reservation "r74kf1_1_fdiv_sf" 17
+ (and (eq_attr "cpu" "74kf1_1")
+ (and (eq_attr "type" "fdiv,fsqrt")
+ (eq_attr "mode" "SF")))
+ "r74k_fpu_arith*14")
+
+(define_insn_reservation "r74kf2_1_fdiv_sf" 34
+ (and (eq_attr "cpu" "74kf2_1")
+ (and (eq_attr "type" "fdiv,fsqrt")
+ (eq_attr "mode" "SF")))
+ "r74k_fpu_arith*28")
+
+(define_insn_reservation "r74kf3_2_fdiv_sf" 25
+ (and (eq_attr "cpu" "74kf3_2")
+ (and (eq_attr "type" "fdiv,fsqrt")
+ (eq_attr "mode" "SF")))
+ "r74k_fpu_arith*14")
+
+(define_insn_reservation "r74kf1_1_fdiv_df" 32
+ (and (eq_attr "cpu" "74kf1_1")
+ (and (eq_attr "type" "fdiv,fsqrt")
+ (eq_attr "mode" "DF")))
+ "r74k_fpu_arith*29")
+
+(define_insn_reservation "r74kf2_1_fdiv_df" 64
+ (and (eq_attr "cpu" "74kf2_1")
+ (and (eq_attr "type" "fdiv,fsqrt")
+ (eq_attr "mode" "DF")))
+ "r74k_fpu_arith*58")
+
+(define_insn_reservation "r74kf3_2_fdiv_df" 48
+ (and (eq_attr "cpu" "74kf3_2")
+ (and (eq_attr "type" "fdiv,fsqrt")
+ (eq_attr "mode" "DF")))
+ "r74k_fpu_arith*29")
+
+;; frsqrt
+(define_insn_reservation "r74kf1_1_frsqrt_sf" 17
+ (and (eq_attr "cpu" "74kf1_1")
+ (and (eq_attr "type" "frsqrt")
+ (eq_attr "mode" "SF")))
+ "r74k_fpu_arith*14")
+
+(define_insn_reservation "r74kf2_1_frsqrt_sf" 34
+ (and (eq_attr "cpu" "74kf2_1")
+ (and (eq_attr "type" "frsqrt")
+ (eq_attr "mode" "SF")))
+ "r74k_fpu_arith*28")
+
+(define_insn_reservation "r74kf3_2_frsqrt_sf" 25
+ (and (eq_attr "cpu" "74kf3_2")
+ (and (eq_attr "type" "frsqrt")
+ (eq_attr "mode" "SF")))
+ "r74k_fpu_arith*14")
+
+(define_insn_reservation "r74kf1_1_frsqrt_df" 36
+ (and (eq_attr "cpu" "74kf1_1")
+ (and (eq_attr "type" "frsqrt")
+ (eq_attr "mode" "DF")))
+ "r74k_fpu_arith*31")
+
+(define_insn_reservation "r74kf2_1_frsqrt_df" 72
+ (and (eq_attr "cpu" "74kf2_1")
+ (and (eq_attr "type" "frsqrt")
+ (eq_attr "mode" "DF")))
+ "r74k_fpu_arith*62")
+
+(define_insn_reservation "r74kf3_2_frsqrt_df" 54
+ (and (eq_attr "cpu" "74kf3_2")
+ (and (eq_attr "type" "frsqrt")
+ (eq_attr "mode" "DF")))
+ "r74k_fpu_arith*31")
+
+;; fcmp
+(define_insn_reservation "r74kf1_1_fcmp" 4
+ (and (eq_attr "cpu" "74kf1_1")
+ (eq_attr "type" "fcmp"))
+ "r74k_fpu_arith")
+
+(define_insn_reservation "r74kf2_1_fcmp" 8
+ (and (eq_attr "cpu" "74kf2_1")
+ (eq_attr "type" "fcmp"))
+ "r74k_fpu_arith*2")
+
+(define_insn_reservation "r74kf3_2_fcmp" 6
+ (and (eq_attr "cpu" "74kf3_2")
+ (eq_attr "type" "fcmp"))
+ "r74k_fpu_arith")
+
+;; fcvt
+(define_insn_reservation "r74kf1_1_fcvt" 4
+ (and (eq_attr "cpu" "74kf1_1")
+ (eq_attr "type" "fcvt"))
+ "r74k_fpu_arith")
+
+(define_insn_reservation "r74kf2_1_fcvt" 8
+ (and (eq_attr "cpu" "74kf2_1")
+ (eq_attr "type" "fcvt"))
+ "r74k_fpu_arith*2")
+
+(define_insn_reservation "r74kf3_2_fcvt" 6
+ (and (eq_attr "cpu" "74kf3_2")
+ (eq_attr "type" "fcvt"))
+ "r74k_fpu_arith")
+
+;; fxfer (MTC1, DMTC1: latency is 4) (MFC1, DMFC1: latency is 1)
+(define_insn_reservation "r74kf1_1_fxfer_to_c1" 4
+ (and (eq_attr "cpu" "74kf1_1")
+ (eq_attr "type" "mtc"))
+ "r74k_fpu_arith")
+
+(define_insn_reservation "r74kf2_1_fxfer_to_c1" 8
+ (and (eq_attr "cpu" "74kf2_1")
+ (eq_attr "type" "mtc"))
+ "r74k_fpu_arith*2")
+
+(define_insn_reservation "r74kf3_2_fxfer_to_c1" 6
+ (and (eq_attr "cpu" "74kf3_2")
+ (eq_attr "type" "mtc"))
+ "r74k_fpu_arith")
+
+(define_insn_reservation "r74kf1_1_fxfer_from_c1" 1
+ (and (eq_attr "cpu" "74kf1_1")
+ (eq_attr "type" "mfc"))
+ "r74k_fpu_arith")
+
+(define_insn_reservation "r74kf2_1_fxfer_from_c1" 2
+ (and (eq_attr "cpu" "74kf2_1")
+ (eq_attr "type" "mfc"))
+ "r74k_fpu_arith*2")
+
+(define_insn_reservation "r74kf3_2_fxfer_from_c1" 1
+ (and (eq_attr "cpu" "74kf3_2")
+ (eq_attr "type" "mfc"))
+ "r74k_fpu_arith")
diff --git a/gcc/config/mips/9000.md b/gcc/config/mips/9000.md
new file mode 100644
index 000000000..c0c8d3ac8
--- /dev/null
+++ b/gcc/config/mips/9000.md
@@ -0,0 +1,151 @@
+;; DFA-based pipeline description for the RM9000.
+;; Copyright (C) 2003, 2004, 2005, 2007 Free Software Foundation, 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/>.
+
+(define_automaton "rm9k_main, rm9k_imul, rm9k_fdiv")
+
+;; These units are for insns that can issue in either pipe. We don't
+;; want to use constructs like "rm9k_m | rm9k_f_int" since that would
+;; needlessly make an insn prefer the M pipe.
+(define_cpu_unit "rm9k_any1" "rm9k_main")
+(define_cpu_unit "rm9k_any2" "rm9k_main")
+
+;; F and M pipe units, for instructions that must be issued by a
+;; particular pipe. Split the F pipe into two units so that integer
+;; instructions can issue while the FPU is busy. We don't need to
+;; split M because it is only ever reserved for a single cycle.
+(define_cpu_unit "rm9k_m" "rm9k_main")
+(define_cpu_unit "rm9k_f_int" "rm9k_main")
+(define_cpu_unit "rm9k_f_float" "rm9k_main")
+
+(exclusion_set "rm9k_f_int" "rm9k_f_float")
+
+;; Multiply/divide units.
+(define_cpu_unit "rm9k_imul" "rm9k_imul")
+(define_cpu_unit "rm9k_fdiv" "rm9k_fdiv")
+
+(define_insn_reservation "rm9k_load" 3
+ (and (eq_attr "cpu" "r9000")
+ (eq_attr "type" "load,fpload,fpidxload"))
+ "rm9k_m")
+
+(define_insn_reservation "rm9k_store" 1
+ (and (eq_attr "cpu" "r9000")
+ (eq_attr "type" "store,fpstore,fpidxstore"))
+ "rm9k_m")
+
+(define_insn_reservation "rm9k_int" 1
+ (and (eq_attr "cpu" "r9000")
+ (eq_attr "type" "arith,shift,signext,slt,clz,const,logical,move,nop,trap"))
+ "rm9k_any1 | rm9k_any2")
+
+(define_insn_reservation "rm9k_int_cmove" 2
+ (and (eq_attr "cpu" "r9000")
+ (and (eq_attr "type" "condmove")
+ (eq_attr "mode" "SI,DI")))
+ "rm9k_any1 | rm9k_any2")
+
+;; This applies to both 'mul' and 'mult'.
+(define_insn_reservation "rm9k_mulsi" 3
+ (and (eq_attr "cpu" "r9000")
+ (and (eq_attr "type" "imul,imul3,imadd")
+ (eq_attr "mode" "!DI")))
+ "rm9k_f_int")
+
+(define_insn_reservation "rm9k_muldi" 7
+ (and (eq_attr "cpu" "r9000")
+ (and (eq_attr "type" "imul,imul3,imadd")
+ (eq_attr "mode" "DI")))
+ "rm9k_f_int + rm9k_imul * 7")
+
+(define_insn_reservation "rm9k_divsi" 38
+ (and (eq_attr "cpu" "r9000")
+ (and (eq_attr "type" "idiv")
+ (eq_attr "mode" "!DI")))
+ "rm9k_f_int + rm9k_imul * 38")
+
+(define_insn_reservation "rm9k_divdi" 70
+ (and (eq_attr "cpu" "r9000")
+ (and (eq_attr "type" "idiv")
+ (eq_attr "mode" "DI")))
+ "rm9k_f_int + rm9k_imul * 70")
+
+(define_insn_reservation "rm9k_mfhilo" 1
+ (and (eq_attr "cpu" "r9000")
+ (eq_attr "type" "mfhilo"))
+ "rm9k_f_int")
+
+(define_insn_reservation "rm9k_mthilo" 5
+ (and (eq_attr "cpu" "r9000")
+ (eq_attr "type" "mthilo"))
+ "rm9k_f_int")
+
+(define_insn_reservation "rm9k_xfer" 2
+ (and (eq_attr "cpu" "r9000")
+ (eq_attr "type" "mfc,mtc"))
+ "rm9k_m")
+
+(define_insn_reservation "rm9k_fquick" 2
+ (and (eq_attr "cpu" "r9000")
+ (eq_attr "type" "fabs,fneg,fcmp,fmove"))
+ "rm9k_f_float")
+
+(define_insn_reservation "rm9k_fcmove" 2
+ (and (eq_attr "cpu" "r9000")
+ (and (eq_attr "type" "condmove")
+ (eq_attr "mode" "SF,DF")))
+ "rm9k_m")
+
+(define_insn_reservation "rm9k_fadd" 6
+ (and (eq_attr "cpu" "r9000")
+ (eq_attr "type" "fadd,fcvt"))
+ "rm9k_f_float")
+
+(define_insn_reservation "rm9k_fmuls" 6
+ (and (eq_attr "cpu" "r9000")
+ (and (eq_attr "type" "fmul,fmadd")
+ (eq_attr "mode" "SF")))
+ "rm9k_f_float")
+
+(define_insn_reservation "rm9k_fmuld" 9
+ (and (eq_attr "cpu" "r9000")
+ (and (eq_attr "type" "fmul,fmadd")
+ (eq_attr "mode" "DF")))
+ "rm9k_f_float * 3")
+
+(define_insn_reservation "rm9k_fdivs" 22
+ (and (eq_attr "cpu" "r9000")
+ (and (eq_attr "type" "fdiv,frdiv,fsqrt,frsqrt")
+ (eq_attr "mode" "SF")))
+ "rm9k_f_float + rm9k_fdiv * 22")
+
+(define_insn_reservation "rm9k_fdivd" 37
+ (and (eq_attr "cpu" "r9000")
+ (and (eq_attr "type" "fdiv,frdiv,fsqrt,frsqrt")
+ (eq_attr "mode" "DF")))
+ "rm9k_f_float + rm9k_fdiv * 37")
+
+(define_insn_reservation "rm9k_branch" 2
+ (and (eq_attr "cpu" "r9000")
+ (eq_attr "type" "branch,jump,call"))
+ "rm9k_any1 | rm9k_any2")
+
+(define_insn_reservation "rm9k_unknown" 1
+ (and (eq_attr "cpu" "r9000")
+ (eq_attr "type" "unknown,multi"))
+ "rm9k_m + rm9k_f_int + rm9k_any1 + rm9k_any2")
diff --git a/gcc/config/mips/constraints.md b/gcc/config/mips/constraints.md
new file mode 100644
index 000000000..37eee875a
--- /dev/null
+++ b/gcc/config/mips/constraints.md
@@ -0,0 +1,233 @@
+;; Constraint definitions for MIPS.
+;; Copyright (C) 2006, 2007, 2008, 2010 Free Software Foundation, 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/>.
+
+;; Register constraints
+
+(define_register_constraint "d" "BASE_REG_CLASS"
+ "An address register. This is equivalent to @code{r} unless
+ generating MIPS16 code.")
+
+(define_register_constraint "t" "T_REG"
+ "@internal")
+
+(define_register_constraint "f" "TARGET_HARD_FLOAT ? FP_REGS : NO_REGS"
+ "A floating-point register (if available).")
+
+(define_register_constraint "h" "NO_REGS"
+ "Formerly the @code{hi} register. This constraint is no longer supported.")
+
+(define_register_constraint "l" "TARGET_BIG_ENDIAN ? MD1_REG : MD0_REG"
+ "The @code{lo} register. Use this register to store values that are
+ no bigger than a word.")
+
+(define_register_constraint "x" "MD_REGS"
+ "The concatenated @code{hi} and @code{lo} registers. Use this register
+ to store doubleword values.")
+
+(define_register_constraint "b" "ALL_REGS"
+ "@internal")
+
+;; MIPS16 code always calls through a MIPS16 register; see mips_emit_call_insn
+;; for details.
+(define_register_constraint "c" "TARGET_MIPS16 ? M16_REGS
+ : TARGET_USE_PIC_FN_ADDR_REG ? PIC_FN_ADDR_REG
+ : GR_REGS"
+ "A register suitable for use in an indirect jump. This will always be
+ @code{$25} for @option{-mabicalls}.")
+
+(define_register_constraint "e" "LEA_REGS"
+ "@internal")
+
+(define_register_constraint "j" "PIC_FN_ADDR_REG"
+ "@internal")
+
+;; Don't use this constraint in gcc code! It runs the risk of
+;; introducing a spill failure; see tls_get_tp_<mode>.
+(define_register_constraint "v" "V1_REG"
+ "Register @code{$3}. Do not use this constraint in new code;
+ it is retained only for compatibility with glibc.")
+
+(define_register_constraint "y" "GR_REGS"
+ "Equivalent to @code{r}; retained for backwards compatibility.")
+
+(define_register_constraint "z" "ST_REGS"
+ "A floating-point condition code register.")
+
+(define_register_constraint "A" "DSP_ACC_REGS"
+ "@internal")
+
+(define_register_constraint "a" "ACC_REGS"
+ "@internal")
+
+(define_register_constraint "B" "COP0_REGS"
+ "@internal")
+
+(define_register_constraint "C" "COP2_REGS"
+ "@internal")
+
+(define_register_constraint "D" "COP3_REGS"
+ "@internal")
+
+;; Registers that can be used as the target of multiply-accumulate
+;; instructions. The core MIPS32 ISA provides a hi/lo madd,
+;; but the DSP version allows any accumulator target.
+(define_register_constraint "ka" "ISA_HAS_DSP_MULT ? ACC_REGS : MD_REGS")
+
+(define_constraint "kf"
+ "@internal"
+ (match_operand 0 "force_to_mem_operand"))
+
+;; This is a normal rather than a register constraint because we can
+;; never use the stack pointer as a reload register.
+(define_constraint "ks"
+ "@internal"
+ (and (match_code "reg")
+ (match_test "REGNO (op) == STACK_POINTER_REGNUM")))
+
+;; Integer constraints
+
+(define_constraint "I"
+ "A signed 16-bit constant (for arithmetic instructions)."
+ (and (match_code "const_int")
+ (match_test "SMALL_OPERAND (ival)")))
+
+(define_constraint "J"
+ "Integer zero."
+ (and (match_code "const_int")
+ (match_test "ival == 0")))
+
+(define_constraint "K"
+ "An unsigned 16-bit constant (for logic instructions)."
+ (and (match_code "const_int")
+ (match_test "SMALL_OPERAND_UNSIGNED (ival)")))
+
+(define_constraint "L"
+ "A signed 32-bit constant in which the lower 16 bits are zero.
+ Such constants can be loaded using @code{lui}."
+ (and (match_code "const_int")
+ (match_test "LUI_OPERAND (ival)")))
+
+(define_constraint "M"
+ "A constant that cannot be loaded using @code{lui}, @code{addiu}
+ or @code{ori}."
+ (and (match_code "const_int")
+ (match_test "!SMALL_OPERAND (ival)")
+ (match_test "!SMALL_OPERAND_UNSIGNED (ival)")
+ (match_test "!LUI_OPERAND (ival)")))
+
+(define_constraint "N"
+ "A constant in the range -65535 to -1 (inclusive)."
+ (and (match_code "const_int")
+ (match_test "ival >= -0xffff && ival < 0")))
+
+(define_constraint "O"
+ "A signed 15-bit constant."
+ (and (match_code "const_int")
+ (match_test "ival >= -0x4000 && ival < 0x4000")))
+
+(define_constraint "P"
+ "A constant in the range 1 to 65535 (inclusive)."
+ (and (match_code "const_int")
+ (match_test "ival > 0 && ival < 0x10000")))
+
+;; Floating-point constraints
+
+(define_constraint "G"
+ "Floating-point zero."
+ (and (match_code "const_double")
+ (match_test "op == CONST0_RTX (mode)")))
+
+;; General constraints
+
+(define_constraint "Q"
+ "@internal"
+ (match_operand 0 "const_arith_operand"))
+
+(define_memory_constraint "R"
+ "An address that can be used in a non-macro load or store."
+ (and (match_code "mem")
+ (match_test "mips_address_insns (XEXP (op, 0), mode, false) == 1")))
+
+(define_constraint "S"
+ "@internal
+ A constant call address."
+ (and (match_operand 0 "call_insn_operand")
+ (match_test "CONSTANT_P (op)")))
+
+(define_constraint "T"
+ "@internal
+ A constant @code{move_operand} that cannot be safely loaded into @code{$25}
+ using @code{la}."
+ (and (match_operand 0 "move_operand")
+ (match_test "CONSTANT_P (op)")
+ (match_test "mips_dangerous_for_la25_p (op)")))
+
+(define_constraint "U"
+ "@internal
+ A constant @code{move_operand} that can be safely loaded into @code{$25}
+ using @code{la}."
+ (and (match_operand 0 "move_operand")
+ (match_test "CONSTANT_P (op)")
+ (match_test "!mips_dangerous_for_la25_p (op)")))
+
+(define_memory_constraint "W"
+ "@internal
+ A memory address based on a member of @code{BASE_REG_CLASS}. This is
+ true for all non-mips16 references (although it can sometimes be implicit
+ if @samp{!TARGET_EXPLICIT_RELOCS}). For MIPS16, it excludes stack and
+ constant-pool references."
+ (and (match_code "mem")
+ (match_operand 0 "memory_operand")
+ (ior (match_test "!TARGET_MIPS16")
+ (and (not (match_operand 0 "stack_operand"))
+ (not (match_test "CONSTANT_P (XEXP (op, 0))"))))))
+
+(define_constraint "YG"
+ "@internal
+ A vector zero."
+ (and (match_code "const_vector")
+ (match_test "op == CONST0_RTX (mode)")))
+
+(define_constraint "YA"
+ "@internal
+ An unsigned 6-bit constant."
+ (and (match_code "const_int")
+ (match_test "UIMM6_OPERAND (ival)")))
+
+(define_constraint "YB"
+ "@internal
+ A signed 10-bit constant."
+ (and (match_code "const_int")
+ (match_test "IMM10_OPERAND (ival)")))
+
+(define_constraint "Yb"
+ "@internal"
+ (match_operand 0 "qi_mask_operand"))
+
+(define_constraint "Yh"
+ "@internal"
+ (match_operand 0 "hi_mask_operand"))
+
+(define_constraint "Yw"
+ "@internal"
+ (match_operand 0 "si_mask_operand"))
+
+(define_constraint "Yx"
+ "@internal"
+ (match_operand 0 "low_bitmask_operand"))
diff --git a/gcc/config/mips/crtfastmath.c b/gcc/config/mips/crtfastmath.c
new file mode 100644
index 000000000..a9586b0a7
--- /dev/null
+++ b/gcc/config/mips/crtfastmath.c
@@ -0,0 +1,53 @@
+/* Copyright (C) 2010 Free Software Foundation, 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.
+
+ Under Section 7 of GPL version 3, you are granted additional
+ permissions described in the GCC Runtime Library Exception, version
+ 3.1, as published by the Free Software Foundation.
+
+ You should have received a copy of the GNU General Public License
+ and a copy of the GCC Runtime Library Exception along with this
+ program; see the files COPYING3 and COPYING.RUNTIME respectively.
+ If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef __mips_hard_float
+
+/* Flush denormalized numbers to zero. */
+#define _FPU_FLUSH_TZ 0x1000000
+
+/* Rounding control. */
+#define _FPU_RC_NEAREST 0x0 /* RECOMMENDED */
+#define _FPU_RC_ZERO 0x1
+#define _FPU_RC_UP 0x2
+#define _FPU_RC_DOWN 0x3
+
+/* Enable interrupts for IEEE exceptions. */
+#define _FPU_IEEE 0x00000F80
+
+/* Macros for accessing the hardware control word. */
+#define _FPU_GETCW(cw) __asm__ ("cfc1 %0,$31" : "=r" (cw))
+#define _FPU_SETCW(cw) __asm__ ("ctc1 %0,$31" : : "r" (cw))
+
+static void __attribute__((constructor))
+set_fast_math (void)
+{
+ unsigned int fcr;
+
+ /* Flush to zero, round to nearest, IEEE exceptions disabled. */
+ fcr = _FPU_FLUSH_TZ | _FPU_RC_NEAREST;
+
+ _FPU_SETCW(fcr);
+}
+
+#endif /* __mips_hard_float */
diff --git a/gcc/config/mips/crti.asm b/gcc/config/mips/crti.asm
new file mode 100644
index 000000000..ac04271c5
--- /dev/null
+++ b/gcc/config/mips/crti.asm
@@ -0,0 +1,49 @@
+/* Copyright (C) 2001, 2002 Free Software Foundation, 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.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+<http://www.gnu.org/licenses/>. */
+
+/* 4 slots for argument spill area. 1 for cpreturn, 1 for stack.
+ Return spill offset of 40 and 20. Aligned to 16 bytes for n32. */
+
+ .section .init,"ax",@progbits
+ .globl _init
+ .type _init,@function
+_init:
+#ifdef __mips64
+ daddu $sp,$sp,-48
+ sd $31,40($sp)
+#else
+ addu $sp,$sp,-32
+ sw $31,20($sp)
+#endif
+
+ .section .fini,"ax",@progbits
+ .globl _fini
+ .type _fini,@function
+_fini:
+#ifdef __mips64
+ daddu $sp,$sp,-48
+ sd $31,40($sp)
+#else
+ addu $sp,$sp,-32
+ sw $31,20($sp)
+#endif
diff --git a/gcc/config/mips/crtn.asm b/gcc/config/mips/crtn.asm
new file mode 100644
index 000000000..03a6b68c9
--- /dev/null
+++ b/gcc/config/mips/crtn.asm
@@ -0,0 +1,52 @@
+/* Copyright (C) 2001, 2002 Free Software Foundation, 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.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+<http://www.gnu.org/licenses/>. */
+
+/* 4 slots for argument spill area. 1 for cpreturn, 1 for stack.
+ Return spill offset of 40 and 20. Aligned to 16 bytes for n32. */
+
+#ifdef __mips16
+#define RA $7
+#else
+#define RA $31
+#endif
+
+ .section .init,"ax",@progbits
+#ifdef __mips64
+ ld RA,40($sp)
+ daddu $sp,$sp,48
+#else
+ lw RA,20($sp)
+ addu $sp,$sp,32
+#endif
+ j RA
+
+ .section .fini,"ax",@progbits
+#ifdef __mips64
+ ld RA,40($sp)
+ daddu $sp,$sp,48
+#else
+ lw RA,20($sp)
+ addu $sp,$sp,32
+#endif
+ j RA
+
diff --git a/gcc/config/mips/driver-native.c b/gcc/config/mips/driver-native.c
new file mode 100644
index 000000000..1947d7198
--- /dev/null
+++ b/gcc/config/mips/driver-native.c
@@ -0,0 +1,81 @@
+/* Subroutines for the gcc driver.
+ Copyright (C) 2008 Free Software Foundation, 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/>. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+
+/* This will be called by the spec parser in gcc.c when it sees
+ a %:local_cpu_detect(args) construct. Currently it will be called
+ with either "arch" or "tune" as argument depending on if -march=native
+ or -mtune=native is to be substituted.
+
+ It returns a string containing new command line parameters to be
+ put at the place of the above two options, depending on what CPU
+ this is executed. E.g. "-march=loongson2f" on a Loongson 2F for
+ -march=native. If the routine can't detect a known processor,
+ the -march or -mtune option is discarded.
+
+ ARGC and ARGV are set depending on the actual arguments given
+ in the spec. */
+const char *
+host_detect_local_cpu (int argc, const char **argv)
+{
+ const char *cpu = NULL;
+ char buf[128];
+ FILE *f;
+ bool arch;
+
+ if (argc < 1)
+ return NULL;
+
+ arch = strcmp (argv[0], "arch") == 0;
+ if (!arch && strcmp (argv[0], "tune"))
+ return NULL;
+
+ f = fopen ("/proc/cpuinfo", "r");
+ if (f == NULL)
+ return NULL;
+
+ while (fgets (buf, sizeof (buf), f) != NULL)
+ if (strncmp (buf, "cpu model", sizeof ("cpu model") - 1) == 0)
+ {
+ if (strstr (buf, "Godson2 V0.2") != NULL
+ || strstr (buf, "Loongson-2 V0.2") != NULL)
+ cpu = "loongson2e";
+ else if (strstr (buf, "Godson2 V0.3") != NULL
+ || strstr (buf, "Loongson-2 V0.3") != NULL)
+ cpu = "loongson2f";
+ else if (strstr (buf, "SiByte SB1") != NULL)
+ cpu = "sb1";
+ else if (strstr (buf, "R5000") != NULL)
+ cpu = "r5000";
+ else if (strstr (buf, "Octeon") != NULL)
+ cpu = "octeon";
+ break;
+ }
+
+ fclose (f);
+
+ if (cpu == NULL)
+ return NULL;
+
+ return concat ("-m", argv[0], "=", cpu, NULL);
+}
diff --git a/gcc/config/mips/elf.h b/gcc/config/mips/elf.h
new file mode 100644
index 000000000..572553742
--- /dev/null
+++ b/gcc/config/mips/elf.h
@@ -0,0 +1,51 @@
+/* Target macros for mips*-elf targets.
+ Copyright (C) 1994, 1997, 1999, 2000, 2002, 2003, 2004, 2007, 2010
+ Free Software Foundation, 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/>. */
+
+/* MIPS assemblers don't have the usual .set foo,bar construct;
+ .set is used for assembler options instead. */
+#undef SET_ASM_OP
+#define ASM_OUTPUT_DEF(FILE, LABEL1, LABEL2) \
+ do \
+ { \
+ fputc ('\t', FILE); \
+ assemble_name (FILE, LABEL1); \
+ fputs (" = ", FILE); \
+ assemble_name (FILE, LABEL2); \
+ fputc ('\n', FILE); \
+ } \
+ while (0)
+
+#undef ASM_DECLARE_OBJECT_NAME
+#define ASM_DECLARE_OBJECT_NAME mips_declare_object_name
+
+#undef ASM_FINISH_DECLARE_OBJECT
+#define ASM_FINISH_DECLARE_OBJECT mips_finish_declare_object
+
+/* Leave the linker script to choose the appropriate libraries. */
+#undef LIB_SPEC
+#define LIB_SPEC ""
+
+#undef STARTFILE_SPEC
+#define STARTFILE_SPEC "crti%O%s crtbegin%O%s"
+
+#undef ENDFILE_SPEC
+#define ENDFILE_SPEC "crtend%O%s crtn%O%s"
+
+#define NO_IMPLICIT_EXTERN_C 1
diff --git a/gcc/config/mips/elfoabi.h b/gcc/config/mips/elfoabi.h
new file mode 100644
index 000000000..a3d92bf87
--- /dev/null
+++ b/gcc/config/mips/elfoabi.h
@@ -0,0 +1,40 @@
+/* Target macros for mips*-elf targets that selected between o32 and o64
+ based on the target architecture.
+ Copyright (C) 1994, 1997, 1999, 2000, 2002, 2003, 2004, 2007, 2008
+ Free Software Foundation, 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/>. */
+
+#undef DRIVER_SELF_SPECS
+#define DRIVER_SELF_SPECS \
+ /* Make sure a -mips option is present. This helps us to pick \
+ the right multilib, and also makes the later specs easier \
+ to write. */ \
+ MIPS_ISA_LEVEL_SPEC, \
+ \
+ /* If no ABI option is specified, infer one from the ISA level \
+ or -mgp setting. */ \
+ "%{!mabi=*: %{" MIPS_32BIT_OPTION_SPEC ": -mabi=32;: -mabi=o64}}", \
+ \
+ /* Remove a redundant -mfp64 for -mabi=o64; we want the !mfp64 \
+ multilibs. There's no need to check whether the architecture \
+ is 64-bit; cc1 will complain if it isn't. */ \
+ "%{mabi=o64: %<mfp64}", \
+ \
+ /* Configuration-independent MIPS rules.*/ \
+ BASE_DRIVER_SELF_SPECS
+
diff --git a/gcc/config/mips/elforion.h b/gcc/config/mips/elforion.h
new file mode 100644
index 000000000..5560580c2
--- /dev/null
+++ b/gcc/config/mips/elforion.h
@@ -0,0 +1,20 @@
+/* Definitions of target machine for GNU compiler. MIPS ORION version.
+ Copyright (C) 1994, 1998, 2007, 2010 Free Software Foundation, 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/>. */
+
+#define MIPS_CPU_STRING_DEFAULT "orion"
diff --git a/gcc/config/mips/generic.md b/gcc/config/mips/generic.md
new file mode 100644
index 000000000..d61511f33
--- /dev/null
+++ b/gcc/config/mips/generic.md
@@ -0,0 +1,105 @@
+;; Generic DFA-based pipeline description for MIPS targets
+;; Copyright (C) 2004, 2005, 2007 Free Software Foundation, 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/>.
+
+
+;; This file is derived from the old define_function_unit description.
+;; Each reservation can be overridden on a processor-by-processor basis.
+
+(define_insn_reservation "generic_alu" 1
+ (eq_attr "type" "unknown,prefetch,prefetchx,condmove,const,arith,
+ shift,slt,clz,trap,multi,nop,logical,signext,move")
+ "alu")
+
+(define_insn_reservation "generic_load" 3
+ (eq_attr "type" "load,fpload,fpidxload")
+ "alu")
+
+(define_insn_reservation "generic_store" 1
+ (eq_attr "type" "store,fpstore,fpidxstore")
+ "alu")
+
+(define_insn_reservation "generic_xfer" 2
+ (eq_attr "type" "mfc,mtc")
+ "alu")
+
+(define_insn_reservation "generic_branch" 1
+ (eq_attr "type" "branch,jump,call")
+ "alu")
+
+(define_insn_reservation "generic_hilo" 1
+ (eq_attr "type" "mfhilo,mthilo")
+ "imuldiv*3")
+
+(define_insn_reservation "generic_imul" 17
+ (eq_attr "type" "imul,imul3,imadd")
+ "imuldiv*17")
+
+(define_insn_reservation "generic_idiv" 38
+ (eq_attr "type" "idiv")
+ "imuldiv*38")
+
+(define_insn_reservation "generic_fcvt" 1
+ (eq_attr "type" "fcvt")
+ "alu")
+
+(define_insn_reservation "generic_fmove" 2
+ (eq_attr "type" "fabs,fneg,fmove")
+ "alu")
+
+(define_insn_reservation "generic_fcmp" 3
+ (eq_attr "type" "fcmp")
+ "alu")
+
+(define_insn_reservation "generic_fadd" 4
+ (eq_attr "type" "fadd")
+ "alu")
+
+(define_insn_reservation "generic_fmul_single" 7
+ (and (eq_attr "type" "fmul,fmadd")
+ (eq_attr "mode" "SF"))
+ "alu")
+
+(define_insn_reservation "generic_fmul_double" 8
+ (and (eq_attr "type" "fmul,fmadd")
+ (eq_attr "mode" "DF"))
+ "alu")
+
+(define_insn_reservation "generic_fdiv_single" 23
+ (and (eq_attr "type" "fdiv,frdiv")
+ (eq_attr "mode" "SF"))
+ "alu")
+
+(define_insn_reservation "generic_fdiv_double" 36
+ (and (eq_attr "type" "fdiv,frdiv")
+ (eq_attr "mode" "DF"))
+ "alu")
+
+(define_insn_reservation "generic_fsqrt_single" 54
+ (and (eq_attr "type" "fsqrt,frsqrt")
+ (eq_attr "mode" "SF"))
+ "alu")
+
+(define_insn_reservation "generic_fsqrt_double" 112
+ (and (eq_attr "type" "fsqrt,frsqrt")
+ (eq_attr "mode" "DF"))
+ "alu")
+
+(define_insn_reservation "generic_frecip_fsqrt_step" 5
+ (eq_attr "type" "frdiv1,frdiv2,frsqrt1,frsqrt2")
+ "alu")
diff --git a/gcc/config/mips/iris6.h b/gcc/config/mips/iris6.h
new file mode 100644
index 000000000..edf8020f5
--- /dev/null
+++ b/gcc/config/mips/iris6.h
@@ -0,0 +1,341 @@
+/* Definitions of target machine for GNU compiler. IRIX 6.5 version.
+ Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 2000,
+ 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
+ Free Software Foundation, 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/>. */
+
+/* We are compiling for IRIX 6 now. */
+#undef TARGET_IRIX6
+#define TARGET_IRIX6 1
+
+#undef MACHINE_TYPE
+#define MACHINE_TYPE "SGI running IRIX 6.5"
+
+/* Default to -mabi=n32 and -mips3. */
+#undef MULTILIB_DEFAULTS
+#define MULTILIB_DEFAULTS { "mabi=n32" }
+
+/* Force the default ABI onto the command line in order to make the specs
+ easier to write. */
+#undef DRIVER_SELF_SPECS
+#define DRIVER_SELF_SPECS \
+ "%{!mabi=*: -mabi=n32}", \
+ /* Configuration-independent MIPS rules. */ \
+ BASE_DRIVER_SELF_SPECS
+
+/* IRIX 6.5 has the float and long double forms of math functions. */
+#define TARGET_C99_FUNCTIONS 1
+
+/* MIPS specific debugging info */
+#define MIPS_DEBUGGING_INFO 1
+
+/* Force the generation of dwarf .debug_frame sections even if not
+ compiling -g. This guarantees that we can unwind the stack. */
+#define DWARF2_FRAME_INFO 1
+
+/* The system unwinder in libexc requires a specific dwarf return address
+ column to work. */
+#undef DWARF_FRAME_RETURN_COLUMN
+#define DWARF_FRAME_RETURN_COLUMN (FP_REG_LAST + 1)
+
+/* The size in bytes of a DWARF field indicating an offset or length
+ relative to a debug info section, specified to be 4 bytes in the DWARF-2
+ specification. The SGI/MIPS ABI defines it to be the same as PTR_SIZE. */
+#define DWARF_OFFSET_SIZE PTR_SIZE
+
+/* The size in bytes of the initial length field in a debug info
+ section. The DWARF 3 (draft) specification defines this to be
+ either 4 or 12 (with a 4-byte "escape" word when it's 12), but the
+ SGI/MIPS ABI predates this standard and defines it to be the same
+ as DWARF_OFFSET_SIZE. */
+#define DWARF_INITIAL_LENGTH_SIZE DWARF_OFFSET_SIZE
+
+/* MIPS assemblers don't have the usual .set foo,bar construct;
+ .set is used for assembler options instead. */
+#undef SET_ASM_OP
+#define ASM_OUTPUT_DEF(FILE, LABEL1, LABEL2) \
+ do \
+ { \
+ fputc ('\t', FILE); \
+ assemble_name (FILE, LABEL1); \
+ fputs (" = ", FILE); \
+ assemble_name (FILE, LABEL2); \
+ fputc ('\n', FILE); \
+ } \
+ while (0)
+
+#undef LOCAL_LABEL_PREFIX
+#define LOCAL_LABEL_PREFIX (TARGET_NEWABI ? "." : "$")
+
+#undef ASM_DECLARE_OBJECT_NAME
+#define ASM_DECLARE_OBJECT_NAME mips_declare_object_name
+
+#undef ASM_FINISH_DECLARE_OBJECT
+#define ASM_FINISH_DECLARE_OBJECT mips_finish_declare_object
+
+/* The native IRIX 6 linker does not support merging without a special
+ elspec(5) file. */
+#ifndef IRIX_USING_GNU_LD
+#undef HAVE_GAS_SHF_MERGE
+#define HAVE_GAS_SHF_MERGE 0
+#endif
+
+/* Specify wchar_t types. */
+#undef WCHAR_TYPE
+#define WCHAR_TYPE (Pmode == DImode ? "int" : "long int")
+
+#undef WCHAR_TYPE_SIZE
+#define WCHAR_TYPE_SIZE INT_TYPE_SIZE
+
+/* Same for wint_t. */
+#undef WINT_TYPE
+#define WINT_TYPE (Pmode == DImode ? "int" : "long int")
+
+#undef WINT_TYPE_SIZE
+#define WINT_TYPE_SIZE 32
+
+/* C99 stdint.h types. */
+#define INT8_TYPE "signed char"
+#define INT16_TYPE "short int"
+#define INT32_TYPE "int"
+#define INT64_TYPE "long long int"
+#define UINT8_TYPE "unsigned char"
+#define UINT16_TYPE "short unsigned int"
+#define UINT32_TYPE "unsigned int"
+#define UINT64_TYPE "long long unsigned int"
+
+#define INT_LEAST8_TYPE "signed char"
+#define INT_LEAST16_TYPE "short int"
+#define INT_LEAST32_TYPE "int"
+#define INT_LEAST64_TYPE "long long int"
+#define UINT_LEAST8_TYPE "unsigned char"
+#define UINT_LEAST16_TYPE "short unsigned int"
+#define UINT_LEAST32_TYPE "unsigned int"
+#define UINT_LEAST64_TYPE "long long unsigned int"
+
+#define INT_FAST8_TYPE "signed char"
+#define INT_FAST16_TYPE "short int"
+#define INT_FAST32_TYPE "int"
+#define INT_FAST64_TYPE "long long int"
+#define UINT_FAST8_TYPE "unsigned char"
+#define UINT_FAST16_TYPE "short unsigned int"
+#define UINT_FAST32_TYPE "unsigned int"
+#define UINT_FAST64_TYPE "long long unsigned int"
+
+#define INTMAX_TYPE "long long int"
+#define UINTMAX_TYPE "long long unsigned int"
+
+#define INTPTR_TYPE "long int"
+#define UINTPTR_TYPE "long unsigned int"
+
+#define SIG_ATOMIC_TYPE "int"
+
+/* Plain char is unsigned in the SGI compiler. */
+#undef DEFAULT_SIGNED_CHAR
+#define DEFAULT_SIGNED_CHAR 0
+
+#define TARGET_OS_CPP_BUILTINS() \
+ do \
+ { \
+ builtin_define_std ("host_mips"); \
+ builtin_define_std ("sgi"); \
+ builtin_define_std ("unix"); \
+ builtin_define_std ("SYSTYPE_SVR4"); \
+ builtin_define ("_MODERN_C"); \
+ builtin_define ("_SVR4_SOURCE"); \
+ builtin_define ("__DSO__"); \
+ builtin_assert ("system=unix"); \
+ builtin_assert ("system=svr4"); \
+ builtin_assert ("machine=sgi"); \
+ \
+ if (!ISA_MIPS1 && !ISA_MIPS2) \
+ builtin_define ("_COMPILER_VERSION=601"); \
+ \
+ /* We must always define _LONGLONG, even when -ansi is \
+ used, because IRIX 5 system header files require it. \
+ This is OK, because gcc never warns when long long \
+ is used in system header files. \
+ \
+ An alternative would be to support the SGI builtin \
+ type __long_long. */ \
+ builtin_define ("_LONGLONG"); \
+ \
+ /* IRIX 6.5.18 and above provide many ISO C99 \
+ features protected by the __c99 macro. \
+ libstdc++ v3 needs them as well. */ \
+ if (TARGET_IRIX6) \
+ if (flag_isoc99 || c_dialect_cxx ()) \
+ builtin_define ("__c99"); \
+ \
+ /* The GNU C++ standard library requires that \
+ __EXTENSIONS__ and _SGI_SOURCE be defined on at \
+ least IRIX 6.2 and probably all IRIX 6 prior to 6.5. \
+ We don't need this on IRIX 6.5 itself, but it \
+ shouldn't hurt other than the namespace pollution. */ \
+ if (!flag_iso || (TARGET_IRIX6 && c_dialect_cxx ())) \
+ { \
+ builtin_define ("__EXTENSIONS__"); \
+ builtin_define ("_SGI_SOURCE"); \
+ } \
+ } \
+ while (0)
+
+#undef SUBTARGET_CC1_SPEC
+#define SUBTARGET_CC1_SPEC "%{static: -mno-abicalls}"
+
+#undef SUBTARGET_CPP_SPEC
+#define SUBTARGET_CPP_SPEC "%{pthread:-D_REENTRANT}"
+
+#undef INIT_SECTION_ASM_OP
+#define INIT_SECTION_ASM_OP "\t.section\t.gcc_init,\"ax\",@progbits"
+
+#undef FINI_SECTION_ASM_OP
+#define FINI_SECTION_ASM_OP "\t.section\t.gcc_fini,\"ax\",@progbits"
+
+#ifdef IRIX_USING_GNU_LD
+#define IRIX_NO_UNRESOLVED ""
+#else
+#define IRIX_NO_UNRESOLVED "-no_unresolved"
+#endif
+
+#ifdef IRIX_USING_GNU_LD
+#define SUBTARGET_DONT_WARN_UNUSED_SPEC ""
+#define SUBTARGET_WARN_UNUSED_SPEC ""
+#else
+#define SUBTARGET_DONT_WARN_UNUSED_SPEC "-dont_warn_unused"
+#define SUBTARGET_WARN_UNUSED_SPEC "-warn_unused"
+#endif
+
+/* Profiling is supported via libprof1.a not -lc_p as in IRIX 3. */
+#undef STARTFILE_SPEC
+#define STARTFILE_SPEC \
+ "%{!shared: \
+ %{mabi=n32: \
+ %{mips4:%{pg:/usr/lib32/mips4/gcrt1.o%s} \
+ %{!pg:%{p:/usr/lib32/mips4/mcrt1.o%s /usr/lib32/mips4/libprof1.a%s} \
+ %{!p:/usr/lib32/mips4/crt1.o%s}}} \
+ %{!mips4:%{pg:/usr/lib32/mips3/gcrt1.o%s} \
+ %{!pg:%{p:/usr/lib32/mips3/mcrt1.o%s /usr/lib32/mips3/libprof1.a%s} \
+ %{!p:/usr/lib32/mips3/crt1.o%s}}}} \
+ %{mabi=64: \
+ %{mips4:%{pg:/usr/lib64/mips4/gcrt1.o} \
+ %{!pg:%{p:/usr/lib64/mips4/mcrt1.o /usr/lib64/mips4/libprof1.a} \
+ %{!p:/usr/lib64/mips4/crt1.o}}} \
+ %{!mips4:%{pg:/usr/lib64/mips3/gcrt1.o} \
+ %{!pg:%{p:/usr/lib64/mips3/mcrt1.o /usr/lib64/mips3/libprof1.a} \
+ %{!p:/usr/lib64/mips3/crt1.o}}}}} \
+ irix-crti.o%s crtbegin.o%s"
+
+#undef LIB_SPEC
+#define LIB_SPEC \
+ "%{mabi=n32: %{mips4:-L/usr/lib32/mips4} %{!mips4:-L/usr/lib32/mips3} \
+ -L/usr/lib32} \
+ %{mabi=64: %{mips4:-L/usr/lib64/mips4} %{!mips4:-L/usr/lib64/mips3} \
+ -L/usr/lib64} \
+ %{!shared:" \
+ SUBTARGET_DONT_WARN_UNUSED_SPEC \
+ " %{pthread:-lpthread} %{p:libprof1.a%s}%{pg:libprof1.a%s} -lc " \
+ SUBTARGET_WARN_UNUSED_SPEC "}"
+
+/* Avoid getting two warnings for libgcc.a everytime we link. libgcc.a
+ contains references to copysignl, so link with libm to resolve them. */
+#undef LIBGCC_SPEC
+#define LIBGCC_SPEC \
+ SUBTARGET_DONT_WARN_UNUSED_SPEC " -lgcc -lm " SUBTARGET_WARN_UNUSED_SPEC
+
+#undef ENDFILE_SPEC
+#define ENDFILE_SPEC \
+ "crtend.o%s irix-crtn.o%s \
+ %{!shared: \
+ %{mabi=n32:%{mips4:/usr/lib32/mips4/crtn.o%s}\
+ %{!mips4:/usr/lib32/mips3/crtn.o%s}}\
+ %{mabi=64:%{mips4:/usr/lib64/mips4/crtn.o%s}\
+ %{!mips4:/usr/lib64/mips3/crtn.o%s}}}"
+
+/* Generic part of the LINK_SPEC. */
+#undef LINK_SPEC
+#define LINK_SPEC "\
+%{G*} %{EB} %{EL} %{mips1} %{mips2} %{mips3} %{mips4} \
+%{bestGnum} %{shared} %{non_shared} \
+%{call_shared} %{no_archive} %{exact_version} \
+%{!shared: \
+ %{!non_shared: %{!call_shared:%{!r: -call_shared " IRIX_NO_UNRESOLVED "}}}} \
+%{rpath} %{!r: -init __gcc_init -fini __gcc_fini} " IRIX_SUBTARGET_LINK_SPEC
+
+#ifdef IRIX_USING_GNU_LD
+#define IRIX_SUBTARGET_LINK_SPEC \
+ "%{mabi=n32: -melf32bmipn32}%{mabi=64: -melf64bmip}"
+#else
+ /* Explicitly hide crt symbols that would normally be marked with
+ a "hidden" visibility attribute.
+
+ We have traditionally disabled this attribute when using the
+ native linker because the native linker's visibility support is
+ not fully-compatible with the GNU linker's. In particular, the
+ native linker does not pull in archive objects purely to resolve
+ references to the object's hidden symbols, whereas the GNU
+ linker does.
+
+ The gcc build system currently hides symbols in some static
+ libraries (typically libgcov.a or libgcc.a) whenever visibility
+ attributes are supported. On targets with GNU semantics, this
+ makes sure that uses of libx.so symbols in one dynamic object are
+ not resolved to libx.a symbols in another dynamic object. But
+ on targets with IRIX semantics, hiding the symbols prevents the
+ static archive from working at all.
+
+ It would probably be better to enable visiblity attributes for
+ IRIX ld and disable the static archives versioning. It shouldn't
+ make anything worse, since libx.a symbols are global by default
+ anyway. However, no-one has volunteered to do this yet. */
+
+#define IRIX_SUBTARGET_LINK_SPEC \
+ "%{w} -_SYSTYPE_SVR4 -woff 131 \
+ %{shared:-hidden_symbol __dso_handle} \
+ %{mabi=n32: -n32}%{mabi=64: -64}%{!mabi*: -n32}"
+#endif
+
+/* A linker error can empirically be avoided by removing duplicate
+ library search directories. */
+#define LINK_ELIMINATE_DUPLICATE_LDIRECTORIES 1
+
+/* The SGI linker doesn't understand constructor priorities. */
+#ifndef IRIX_USING_GNU_LD
+#define SUPPORTS_INIT_PRIORITY 0
+#endif
+
+/* Add -g to mips.h default to avoid confusing gas with local symbols
+ generated from stabs info. */
+#undef NM_FLAGS
+#define NM_FLAGS "-Bng"
+
+/* The system header files are C++ aware. */
+/* ??? Unfortunately, most but not all of the headers are C++ aware.
+ Specifically, curses.h is not, and as a consequence, defining this
+ used to prevent libg++ building. This is no longer the case so
+ define it again to prevent other problems, e.g. with getopt in
+ unistd.h. We still need some way to fix just those files that need
+ fixing. */
+#define NO_IMPLICIT_EXTERN_C 1
+
+/* -G is incompatible with -KPIC which is the default, so only allow objects
+ in the small data section if the user explicitly asks for it. */
+#undef MIPS_DEFAULT_GVALUE
+#define MIPS_DEFAULT_GVALUE 0
+
+#define MIPS_TFMODE_FORMAT mips_extended_format
diff --git a/gcc/config/mips/iris6.opt b/gcc/config/mips/iris6.opt
new file mode 100644
index 000000000..05fc378c5
--- /dev/null
+++ b/gcc/config/mips/iris6.opt
@@ -0,0 +1,45 @@
+; IRIX 6.5 options.
+
+; Copyright (C) 2011
+; Free Software Foundation, 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 the GCC internals manual (options.texi) for a description of
+; this file's format.
+
+; Please try to keep this file in ASCII collating order.
+
+bestGnum
+Driver
+
+call_shared
+Driver
+
+exact_version
+Driver
+
+no_archive
+Driver
+
+non_shared
+Driver
+
+pthread
+Driver
+
+; This comment is to ensure we retain the blank line above.
diff --git a/gcc/config/mips/irix-crti.asm b/gcc/config/mips/irix-crti.asm
new file mode 100644
index 000000000..0e52e6194
--- /dev/null
+++ b/gcc/config/mips/irix-crti.asm
@@ -0,0 +1,81 @@
+/* Copyright (C) 2004, 2008 Free Software Foundation, 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.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+<http://www.gnu.org/licenses/>. */
+
+ .abicalls
+ .set noreorder
+ .set nomacro
+
+/* The GNU and SGI linkers differ in their implementation of -init and -fini.
+ With the GNU linker, there can only be a single -init option, and the
+ linker simply sets DT_INIT to that value. gcc's initialization and
+ finalization code can go directly in .init, with the prologue and
+ epilogue of the main initialization routine being provided by external
+ object files (*crti.o and *crtn.o in this case).
+
+ The SGI linker instead accepts several -init options. It will set DT_INIT
+ to a linker-created function (placed in .init) that calls each of the -init
+ functions in turn. If there is any user code in .init, this linker-created
+ function will be placed after it. Note that such user code is not treated
+ specially; it will only be called if the -init options arrange for it to
+ be called.
+
+ In theory, the SGI model should allow the crti, crtn and intermediate code
+ to go in .init, just like it can with the GNU linker. However, doing this
+ seems to confuse the linker and triggers an internal error:
+
+ ld32: FATAL 2 : Internal: at ../../ld/mips_code.c mips_code_fixup()
+ text section overflow!
+
+ (seen with MIPSpro 7.30). We therefore put everything in a special
+ .gcc_init section instead. */
+
+ .section .gcc_init,"ax",@progbits
+ .globl __gcc_init
+__gcc_init:
+#if _MIPS_SIM == _ABIO32
+ addiu $sp,$sp,-16
+ sw $31,0($sp)
+#else
+ daddiu $sp,$sp,-16
+ sd $31,0($sp)
+ sd $28,8($sp)
+#endif
+
+ .section .gcc_fini,"ax",@progbits
+ .globl __gcc_fini
+__gcc_fini:
+#if _MIPS_SIM == _ABIO32
+ addiu $sp,$sp,-16
+ sw $31,0($sp)
+#else
+ daddiu $sp,$sp,-16
+ sd $31,0($sp)
+ sd $28,8($sp)
+#endif
+
+/* This object will typically be included in the final link for both
+ shared libraries and executable, and we need to hide the symbols to
+ prevent possible symbol preemption warnings from the SGI linker. */
+.hidden __gcc_init
+.hidden __gcc_fini
+
diff --git a/gcc/config/mips/irix-crtn.asm b/gcc/config/mips/irix-crtn.asm
new file mode 100644
index 000000000..7c28c6ff4
--- /dev/null
+++ b/gcc/config/mips/irix-crtn.asm
@@ -0,0 +1,50 @@
+/* Copyright (C) 2004 Free Software Foundation, 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.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+<http://www.gnu.org/licenses/>. */
+
+ .abicalls
+ .set noreorder
+ .set nomacro
+
+ .section .gcc_init,"ax",@progbits
+#if _MIPS_SIM == _ABIO32
+ lw $31,0($sp)
+ jr $31
+ addiu $sp,$sp,16
+#else
+ ld $31,0($sp)
+ ld $28,8($sp)
+ jr $31
+ daddiu $sp,$sp,16
+#endif
+
+ .section .gcc_fini,"ax",@progbits
+#if _MIPS_SIM == _ABIO32
+ lw $31,0($sp)
+ jr $31
+ addiu $sp,$sp,16
+#else
+ ld $31,0($sp)
+ ld $28,8($sp)
+ jr $31
+ daddiu $sp,$sp,16
+#endif
diff --git a/gcc/config/mips/libgcc-mips16.ver b/gcc/config/mips/libgcc-mips16.ver
new file mode 100644
index 000000000..ddb23e7e7
--- /dev/null
+++ b/gcc/config/mips/libgcc-mips16.ver
@@ -0,0 +1,86 @@
+# Copyright (C) 2008 Free Software Foundation, 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/>.
+
+GCC_4.4.0 {
+ __mips16_addsf3
+ __mips16_subsf3
+ __mips16_mulsf3
+ __mips16_divsf3
+ __mips16_eqsf2
+ __mips16_nesf2
+ __mips16_gtsf2
+ __mips16_gesf2
+ __mips16_lesf2
+ __mips16_ltsf2
+ __mips16_floatsisf
+ __mips16_floatunsisf
+ __mips16_fix_truncsfsi
+ __mips16_adddf3
+ __mips16_subdf3
+ __mips16_muldf3
+ __mips16_divdf3
+ __mips16_extendsfdf2
+ __mips16_truncdfsf2
+ __mips16_eqdf2
+ __mips16_nedf2
+ __mips16_gtdf2
+ __mips16_gedf2
+ __mips16_ledf2
+ __mips16_ltdf2
+ __mips16_floatsidf
+ __mips16_floatunsidf
+ __mips16_fix_truncdfsi
+ __mips16_ret_sf
+ __mips16_ret_sc
+ __mips16_ret_df
+ __mips16_ret_dc
+ __mips16_call_stub_1
+ __mips16_call_stub_5
+ __mips16_call_stub_2
+ __mips16_call_stub_6
+ __mips16_call_stub_9
+ __mips16_call_stub_10
+ __mips16_call_stub_sf_0
+ __mips16_call_stub_sf_1
+ __mips16_call_stub_sf_5
+ __mips16_call_stub_sf_2
+ __mips16_call_stub_sf_6
+ __mips16_call_stub_sf_9
+ __mips16_call_stub_sf_10
+ __mips16_call_stub_sc_0
+ __mips16_call_stub_sc_1
+ __mips16_call_stub_sc_5
+ __mips16_call_stub_sc_2
+ __mips16_call_stub_sc_6
+ __mips16_call_stub_sc_9
+ __mips16_call_stub_sc_10
+ __mips16_call_stub_df_0
+ __mips16_call_stub_df_1
+ __mips16_call_stub_df_5
+ __mips16_call_stub_df_2
+ __mips16_call_stub_df_6
+ __mips16_call_stub_df_9
+ __mips16_call_stub_df_10
+ __mips16_call_stub_dc_0
+ __mips16_call_stub_dc_1
+ __mips16_call_stub_dc_5
+ __mips16_call_stub_dc_2
+ __mips16_call_stub_dc_6
+ __mips16_call_stub_dc_9
+ __mips16_call_stub_dc_10
+}
diff --git a/gcc/config/mips/linux-unwind.h b/gcc/config/mips/linux-unwind.h
new file mode 100644
index 000000000..094ff58cb
--- /dev/null
+++ b/gcc/config/mips/linux-unwind.h
@@ -0,0 +1,121 @@
+/* DWARF2 EH unwinding support for MIPS Linux.
+ Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2012 Free Software
+ Foundation, 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.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+<http://www.gnu.org/licenses/>. */
+
+#ifndef inhibit_libc
+/* Do code reading to identify a signal frame, and set the frame
+ state data appropriately. See unwind-dw2.c for the structs. */
+
+#include <signal.h>
+#include <asm/unistd.h>
+
+/* The third parameter to the signal handler points to something with
+ * this structure defined in asm/ucontext.h, but the name clashes with
+ * struct ucontext from sys/ucontext.h so this private copy is used. */
+typedef struct _sig_ucontext {
+ unsigned long uc_flags;
+ struct _sig_ucontext *uc_link;
+ stack_t uc_stack;
+ struct sigcontext uc_mcontext;
+ sigset_t uc_sigmask;
+} _sig_ucontext_t;
+
+#define MD_FALLBACK_FRAME_STATE_FOR mips_fallback_frame_state
+
+static _Unwind_Reason_Code
+mips_fallback_frame_state (struct _Unwind_Context *context,
+ _Unwind_FrameState *fs)
+{
+ u_int32_t *pc = (u_int32_t *) context->ra;
+ struct sigcontext *sc;
+ _Unwind_Ptr new_cfa, reg_offset;
+ int i;
+
+ /* 24021061 li v0, 0x1061 (rt_sigreturn)*/
+ /* 0000000c syscall */
+ /* or */
+ /* 24021017 li v0, 0x1017 (sigreturn) */
+ /* 0000000c syscall */
+ if (pc[1] != 0x0000000c)
+ return _URC_END_OF_STACK;
+#if _MIPS_SIM == _ABIO32
+ if (pc[0] == (0x24020000 | __NR_sigreturn))
+ {
+ struct sigframe {
+ u_int32_t ass[4]; /* Argument save space for o32. */
+ u_int32_t trampoline[2];
+ struct sigcontext sigctx;
+ } *rt_ = context->cfa;
+ sc = &rt_->sigctx;
+ }
+ else
+#endif
+ if (pc[0] == (0x24020000 | __NR_rt_sigreturn))
+ {
+ struct rt_sigframe {
+ u_int32_t ass[4]; /* Argument save space for o32. */
+ u_int32_t trampoline[2];
+ siginfo_t info;
+ _sig_ucontext_t uc;
+ } *rt_ = context->cfa;
+ sc = &rt_->uc.uc_mcontext;
+ }
+ else
+ return _URC_END_OF_STACK;
+
+ new_cfa = (_Unwind_Ptr) sc;
+ fs->regs.cfa_how = CFA_REG_OFFSET;
+ fs->regs.cfa_reg = STACK_POINTER_REGNUM;
+ fs->regs.cfa_offset = new_cfa - (_Unwind_Ptr) context->cfa;
+
+ /* On o32 Linux, the register save slots in the sigcontext are
+ eight bytes. We need the lower half of each register slot,
+ so slide our view of the structure back four bytes. */
+#if _MIPS_SIM == _ABIO32 && defined __MIPSEB__
+ reg_offset = 4;
+#else
+ reg_offset = 0;
+#endif
+
+ for (i = 0; i < 32; i++) {
+ fs->regs.reg[i].how = REG_SAVED_OFFSET;
+ fs->regs.reg[i].loc.offset
+ = (_Unwind_Ptr)&(sc->sc_regs[i]) + reg_offset - new_cfa;
+ }
+ /* "PC & -2" points to the faulting instruction, but the unwind code
+ searches for "(ADDR & -2) - 1". (See MASK_RETURN_ADDR for the source
+ of the -2 mask.) Adding 2 here ensures that "(ADDR & -2) - 1" is the
+ address of the second byte of the faulting instruction.
+
+ Note that setting fs->signal_frame would not work. As the comment
+ above MASK_RETURN_ADDR explains, MIPS unwinders must earch for an
+ odd-valued address. */
+ fs->regs.reg[DWARF_ALT_FRAME_RETURN_COLUMN].how = REG_SAVED_VAL_OFFSET;
+ fs->regs.reg[DWARF_ALT_FRAME_RETURN_COLUMN].loc.offset
+ = (_Unwind_Ptr)(sc->sc_pc) + 2 - new_cfa;
+ fs->retaddr_column = DWARF_ALT_FRAME_RETURN_COLUMN;
+
+ return _URC_NO_REASON;
+}
+#endif
diff --git a/gcc/config/mips/linux.h b/gcc/config/mips/linux.h
new file mode 100644
index 000000000..a78f6bcbb
--- /dev/null
+++ b/gcc/config/mips/linux.h
@@ -0,0 +1,151 @@
+/* Definitions for MIPS running Linux-based GNU systems with ELF format.
+ Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
+ 2007, 2008, 2010, 2011 Free Software Foundation, 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/>. */
+
+#undef WCHAR_TYPE
+#define WCHAR_TYPE "int"
+
+#undef WCHAR_TYPE_SIZE
+#define WCHAR_TYPE_SIZE 32
+
+#undef ASM_DECLARE_OBJECT_NAME
+#define ASM_DECLARE_OBJECT_NAME mips_declare_object_name
+
+#undef TARGET_VERSION
+#if TARGET_ENDIAN_DEFAULT == 0
+#define TARGET_VERSION fprintf (stderr, " (MIPSel GNU/Linux with ELF)");
+#else
+#define TARGET_VERSION fprintf (stderr, " (MIPS GNU/Linux with ELF)");
+#endif
+
+/* If we don't set MASK_ABICALLS, we can't default to PIC. */
+#undef TARGET_DEFAULT
+#define TARGET_DEFAULT MASK_ABICALLS
+
+#define TARGET_OS_CPP_BUILTINS() \
+ do { \
+ LINUX_TARGET_OS_CPP_BUILTINS(); \
+ /* The GNU C++ standard library requires this. */ \
+ if (c_dialect_cxx ()) \
+ builtin_define ("_GNU_SOURCE"); \
+ } while (0)
+
+#undef SUBTARGET_CPP_SPEC
+#define SUBTARGET_CPP_SPEC "%{posix:-D_POSIX_SOURCE} %{pthread:-D_REENTRANT}"
+
+/* A standard GNU/Linux mapping. On most targets, it is included in
+ CC1_SPEC itself by config/linux.h, but mips.h overrides CC1_SPEC
+ and provides this hook instead. */
+#undef SUBTARGET_CC1_SPEC
+#define SUBTARGET_CC1_SPEC "%{profile:-p}"
+
+/* From iris5.h */
+/* -G is incompatible with -KPIC which is the default, so only allow objects
+ in the small data section if the user explicitly asks for it. */
+#undef MIPS_DEFAULT_GVALUE
+#define MIPS_DEFAULT_GVALUE 0
+
+#define GLIBC_DYNAMIC_LINKER "/lib/ld.so.1"
+
+/* Borrowed from sparc/linux.h */
+#undef LINK_SPEC
+#define LINK_SPEC \
+ "%(endian_spec) \
+ %{shared:-shared} \
+ %{!shared: \
+ %{!static: \
+ %{rdynamic:-export-dynamic} \
+ -dynamic-linker " LINUX_DYNAMIC_LINKER "} \
+ %{static:-static}}"
+
+#undef SUBTARGET_ASM_SPEC
+#define SUBTARGET_ASM_SPEC \
+ "%{!mno-abicalls:%{mplt:-call_nonpic;:-KPIC}}"
+
+/* The MIPS assembler has different syntax for .set. We set it to
+ .dummy to trap any errors. */
+#undef SET_ASM_OP
+#define SET_ASM_OP "\t.dummy\t"
+
+#undef ASM_OUTPUT_DEF
+#define ASM_OUTPUT_DEF(FILE,LABEL1,LABEL2) \
+ do { \
+ fputc ( '\t', FILE); \
+ assemble_name (FILE, LABEL1); \
+ fputs ( " = ", FILE); \
+ assemble_name (FILE, LABEL2); \
+ fputc ( '\n', FILE); \
+ } while (0)
+
+/* The glibc _mcount stub will save $v0 for us. Don't mess with saving
+ it, since ASM_OUTPUT_REG_PUSH/ASM_OUTPUT_REG_POP do not work in the
+ presence of $gp-relative calls. */
+#undef ASM_OUTPUT_REG_PUSH
+#undef ASM_OUTPUT_REG_POP
+
+#undef LIB_SPEC
+#define LIB_SPEC "\
+%{pthread:-lpthread} \
+%{shared:-lc} \
+%{!shared: \
+ %{profile:-lc_p} %{!profile:-lc}}"
+
+#define MD_UNWIND_SUPPORT "config/mips/linux-unwind.h"
+
+#ifdef HAVE_AS_NO_SHARED
+/* Default to -mno-shared for non-PIC. */
+# define NO_SHARED_SPECS \
+ "%{mshared|mno-shared|fpic|fPIC|fpie|fPIE:;:-mno-shared}"
+#else
+# define NO_SHARED_SPECS ""
+#endif
+
+/* -march=native handling only makes sense with compiler running on
+ a MIPS chip. */
+#if defined(__mips__)
+extern const char *host_detect_local_cpu (int argc, const char **argv);
+# define EXTRA_SPEC_FUNCTIONS \
+ { "local_cpu_detect", host_detect_local_cpu },
+
+# define MARCH_MTUNE_NATIVE_SPECS \
+ " %{march=native:%<march=native %:local_cpu_detect(arch)}" \
+ " %{mtune=native:%<mtune=native %:local_cpu_detect(tune)}"
+#else
+# define MARCH_MTUNE_NATIVE_SPECS ""
+#endif
+
+#define LINUX_DRIVER_SELF_SPECS \
+ NO_SHARED_SPECS \
+ MARCH_MTUNE_NATIVE_SPECS, \
+ /* -mplt has no effect without -mno-shared. Simplify later \
+ specs handling by removing a redundant option. */ \
+ "%{!mno-shared:%<mplt}", \
+ /* -mplt likewise has no effect for -mabi=64 without -msym32. */ \
+ "%{mabi=64:%{!msym32:%<mplt}}"
+
+#undef DRIVER_SELF_SPECS
+#define DRIVER_SELF_SPECS \
+ BASE_DRIVER_SELF_SPECS, \
+ LINUX_DRIVER_SELF_SPECS
+
+/* Similar to standard Linux, but adding -ffast-math support. */
+#undef ENDFILE_SPEC
+#define ENDFILE_SPEC \
+ "%{Ofast|ffast-math|funsafe-math-optimizations:crtfastmath.o%s} \
+ %{shared|pie:crtendS.o%s;:crtend.o%s} crtn.o%s"
diff --git a/gcc/config/mips/linux64.h b/gcc/config/mips/linux64.h
new file mode 100644
index 000000000..987a9915c
--- /dev/null
+++ b/gcc/config/mips/linux64.h
@@ -0,0 +1,70 @@
+/* Definitions for MIPS running Linux-based GNU systems with ELF format
+ using n32/64 abi.
+ Copyright 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2010, 2011
+ Free Software Foundation, 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/>. */
+
+/* Force the default endianness and ABI flags onto the command line
+ in order to make the other specs easier to write. */
+#undef DRIVER_SELF_SPECS
+#define DRIVER_SELF_SPECS \
+ BASE_DRIVER_SELF_SPECS, \
+ LINUX_DRIVER_SELF_SPECS \
+ " %{!EB:%{!EL:%(endian_spec)}}" \
+ " %{!mabi=*: -mabi=n32}"
+
+#undef LIB_SPEC
+#define LIB_SPEC "\
+%{pthread:-lpthread} \
+%{shared:-lc} \
+%{!shared: \
+ %{profile:-lc_p} %{!profile:-lc}}"
+
+#define GLIBC_DYNAMIC_LINKER32 "/lib/ld.so.1"
+#define GLIBC_DYNAMIC_LINKER64 "/lib64/ld.so.1"
+#define GLIBC_DYNAMIC_LINKERN32 "/lib32/ld.so.1"
+#define UCLIBC_DYNAMIC_LINKERN32 "/lib32/ld-uClibc.so.0"
+#define BIONIC_DYNAMIC_LINKERN32 "/system/bin/linker32"
+#define LINUX_DYNAMIC_LINKERN32 \
+ CHOOSE_DYNAMIC_LINKER (GLIBC_DYNAMIC_LINKERN32, UCLIBC_DYNAMIC_LINKERN32, \
+ BIONIC_DYNAMIC_LINKERN32)
+
+#undef LINK_SPEC
+#define LINK_SPEC "\
+%{G*} %{EB} %{EL} %{mips1} %{mips2} %{mips3} %{mips4} \
+%{shared} \
+ %(endian_spec) \
+ %{!shared: \
+ %{!static: \
+ %{rdynamic:-export-dynamic} \
+ %{mabi=n32: -dynamic-linker " LINUX_DYNAMIC_LINKERN32 "} \
+ %{mabi=64: -dynamic-linker " LINUX_DYNAMIC_LINKER64 "} \
+ %{mabi=32: -dynamic-linker " LINUX_DYNAMIC_LINKER32 "}} \
+ %{static:-static}} \
+%{mabi=n32:-melf32%{EB:b}%{EL:l}tsmipn32} \
+%{mabi=64:-melf64%{EB:b}%{EL:l}tsmip} \
+%{mabi=32:-melf32%{EB:b}%{EL:l}tsmip}"
+
+#undef LOCAL_LABEL_PREFIX
+#define LOCAL_LABEL_PREFIX (TARGET_OLDABI ? "$" : ".")
+
+/* GNU/Linux doesn't use the same floating-point format that IRIX uses
+ for long double. There's no need to override this here, since
+ ieee_quad_format is the default, but let's put this here to make
+ sure nobody thinks we just forgot to set it to something else. */
+#define MIPS_TFMODE_FORMAT mips_quad_format
diff --git a/gcc/config/mips/loongson.h b/gcc/config/mips/loongson.h
new file mode 100644
index 000000000..6bfd4d7e5
--- /dev/null
+++ b/gcc/config/mips/loongson.h
@@ -0,0 +1,690 @@
+/* Intrinsics for ST Microelectronics Loongson-2E/2F SIMD operations.
+
+ Copyright (C) 2008, 2009 Free Software Foundation, Inc.
+ Contributed by CodeSourcery.
+
+ 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.
+
+ Under Section 7 of GPL version 3, you are granted additional
+ permissions described in the GCC Runtime Library Exception, version
+ 3.1, as published by the Free Software Foundation.
+
+ You should have received a copy of the GNU General Public License and
+ a copy of the GCC Runtime Library Exception along with this program;
+ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+ <http://www.gnu.org/licenses/>. */
+
+#ifndef _GCC_LOONGSON_H
+#define _GCC_LOONGSON_H
+
+#if !defined(__mips_loongson_vector_rev)
+# error "You must select -march=loongson2e or -march=loongson2f to use loongson.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+/* Vectors of unsigned bytes, halfwords and words. */
+typedef uint8_t uint8x8_t __attribute__((vector_size (8)));
+typedef uint16_t uint16x4_t __attribute__((vector_size (8)));
+typedef uint32_t uint32x2_t __attribute__((vector_size (8)));
+
+/* Vectors of signed bytes, halfwords and words. */
+typedef int8_t int8x8_t __attribute__((vector_size (8)));
+typedef int16_t int16x4_t __attribute__((vector_size (8)));
+typedef int32_t int32x2_t __attribute__((vector_size (8)));
+
+/* SIMD intrinsics.
+ Unless otherwise noted, calls to the functions below will expand into
+ precisely one machine instruction, modulo any moves required to
+ satisfy register allocation constraints. */
+
+/* Pack with signed saturation. */
+__extension__ static __inline int16x4_t __attribute__ ((__always_inline__))
+packsswh (int32x2_t s, int32x2_t t)
+{
+ return __builtin_loongson_packsswh (s, t);
+}
+
+__extension__ static __inline int8x8_t __attribute__ ((__always_inline__))
+packsshb (int16x4_t s, int16x4_t t)
+{
+ return __builtin_loongson_packsshb (s, t);
+}
+
+/* Pack with unsigned saturation. */
+__extension__ static __inline uint8x8_t __attribute__ ((__always_inline__))
+packushb (uint16x4_t s, uint16x4_t t)
+{
+ return __builtin_loongson_packushb (s, t);
+}
+
+/* Vector addition, treating overflow by wraparound. */
+__extension__ static __inline uint32x2_t __attribute__ ((__always_inline__))
+paddw_u (uint32x2_t s, uint32x2_t t)
+{
+ return __builtin_loongson_paddw_u (s, t);
+}
+
+__extension__ static __inline uint16x4_t __attribute__ ((__always_inline__))
+paddh_u (uint16x4_t s, uint16x4_t t)
+{
+ return __builtin_loongson_paddh_u (s, t);
+}
+
+__extension__ static __inline uint8x8_t __attribute__ ((__always_inline__))
+paddb_u (uint8x8_t s, uint8x8_t t)
+{
+ return __builtin_loongson_paddb_u (s, t);
+}
+
+__extension__ static __inline int32x2_t __attribute__ ((__always_inline__))
+paddw_s (int32x2_t s, int32x2_t t)
+{
+ return __builtin_loongson_paddw_s (s, t);
+}
+
+__extension__ static __inline int16x4_t __attribute__ ((__always_inline__))
+paddh_s (int16x4_t s, int16x4_t t)
+{
+ return __builtin_loongson_paddh_s (s, t);
+}
+
+__extension__ static __inline int8x8_t __attribute__ ((__always_inline__))
+paddb_s (int8x8_t s, int8x8_t t)
+{
+ return __builtin_loongson_paddb_s (s, t);
+}
+
+/* Addition of doubleword integers, treating overflow by wraparound. */
+__extension__ static __inline uint64_t __attribute__ ((__always_inline__))
+paddd_u (uint64_t s, uint64_t t)
+{
+ return __builtin_loongson_paddd_u (s, t);
+}
+
+__extension__ static __inline int64_t __attribute__ ((__always_inline__))
+paddd_s (int64_t s, int64_t t)
+{
+ return __builtin_loongson_paddd_s (s, t);
+}
+
+/* Vector addition, treating overflow by signed saturation. */
+__extension__ static __inline int16x4_t __attribute__ ((__always_inline__))
+paddsh (int16x4_t s, int16x4_t t)
+{
+ return __builtin_loongson_paddsh (s, t);
+}
+
+__extension__ static __inline int8x8_t __attribute__ ((__always_inline__))
+paddsb (int8x8_t s, int8x8_t t)
+{
+ return __builtin_loongson_paddsb (s, t);
+}
+
+/* Vector addition, treating overflow by unsigned saturation. */
+__extension__ static __inline uint16x4_t __attribute__ ((__always_inline__))
+paddush (uint16x4_t s, uint16x4_t t)
+{
+ return __builtin_loongson_paddush (s, t);
+}
+
+__extension__ static __inline uint8x8_t __attribute__ ((__always_inline__))
+paddusb (uint8x8_t s, uint8x8_t t)
+{
+ return __builtin_loongson_paddusb (s, t);
+}
+
+/* Logical AND NOT. */
+__extension__ static __inline uint64_t __attribute__ ((__always_inline__))
+pandn_ud (uint64_t s, uint64_t t)
+{
+ return __builtin_loongson_pandn_ud (s, t);
+}
+
+__extension__ static __inline uint32x2_t __attribute__ ((__always_inline__))
+pandn_uw (uint32x2_t s, uint32x2_t t)
+{
+ return __builtin_loongson_pandn_uw (s, t);
+}
+
+__extension__ static __inline uint16x4_t __attribute__ ((__always_inline__))
+pandn_uh (uint16x4_t s, uint16x4_t t)
+{
+ return __builtin_loongson_pandn_uh (s, t);
+}
+
+__extension__ static __inline uint8x8_t __attribute__ ((__always_inline__))
+pandn_ub (uint8x8_t s, uint8x8_t t)
+{
+ return __builtin_loongson_pandn_ub (s, t);
+}
+
+__extension__ static __inline int64_t __attribute__ ((__always_inline__))
+pandn_sd (int64_t s, int64_t t)
+{
+ return __builtin_loongson_pandn_sd (s, t);
+}
+
+__extension__ static __inline int32x2_t __attribute__ ((__always_inline__))
+pandn_sw (int32x2_t s, int32x2_t t)
+{
+ return __builtin_loongson_pandn_sw (s, t);
+}
+
+__extension__ static __inline int16x4_t __attribute__ ((__always_inline__))
+pandn_sh (int16x4_t s, int16x4_t t)
+{
+ return __builtin_loongson_pandn_sh (s, t);
+}
+
+__extension__ static __inline int8x8_t __attribute__ ((__always_inline__))
+pandn_sb (int8x8_t s, int8x8_t t)
+{
+ return __builtin_loongson_pandn_sb (s, t);
+}
+
+/* Average. */
+__extension__ static __inline uint16x4_t __attribute__ ((__always_inline__))
+pavgh (uint16x4_t s, uint16x4_t t)
+{
+ return __builtin_loongson_pavgh (s, t);
+}
+
+__extension__ static __inline uint8x8_t __attribute__ ((__always_inline__))
+pavgb (uint8x8_t s, uint8x8_t t)
+{
+ return __builtin_loongson_pavgb (s, t);
+}
+
+/* Equality test. */
+__extension__ static __inline uint32x2_t __attribute__ ((__always_inline__))
+pcmpeqw_u (uint32x2_t s, uint32x2_t t)
+{
+ return __builtin_loongson_pcmpeqw_u (s, t);
+}
+
+__extension__ static __inline uint16x4_t __attribute__ ((__always_inline__))
+pcmpeqh_u (uint16x4_t s, uint16x4_t t)
+{
+ return __builtin_loongson_pcmpeqh_u (s, t);
+}
+
+__extension__ static __inline uint8x8_t __attribute__ ((__always_inline__))
+pcmpeqb_u (uint8x8_t s, uint8x8_t t)
+{
+ return __builtin_loongson_pcmpeqb_u (s, t);
+}
+
+__extension__ static __inline int32x2_t __attribute__ ((__always_inline__))
+pcmpeqw_s (int32x2_t s, int32x2_t t)
+{
+ return __builtin_loongson_pcmpeqw_s (s, t);
+}
+
+__extension__ static __inline int16x4_t __attribute__ ((__always_inline__))
+pcmpeqh_s (int16x4_t s, int16x4_t t)
+{
+ return __builtin_loongson_pcmpeqh_s (s, t);
+}
+
+__extension__ static __inline int8x8_t __attribute__ ((__always_inline__))
+pcmpeqb_s (int8x8_t s, int8x8_t t)
+{
+ return __builtin_loongson_pcmpeqb_s (s, t);
+}
+
+/* Greater-than test. */
+__extension__ static __inline uint32x2_t __attribute__ ((__always_inline__))
+pcmpgtw_u (uint32x2_t s, uint32x2_t t)
+{
+ return __builtin_loongson_pcmpgtw_u (s, t);
+}
+
+__extension__ static __inline uint16x4_t __attribute__ ((__always_inline__))
+pcmpgth_u (uint16x4_t s, uint16x4_t t)
+{
+ return __builtin_loongson_pcmpgth_u (s, t);
+}
+
+__extension__ static __inline uint8x8_t __attribute__ ((__always_inline__))
+pcmpgtb_u (uint8x8_t s, uint8x8_t t)
+{
+ return __builtin_loongson_pcmpgtb_u (s, t);
+}
+
+__extension__ static __inline int32x2_t __attribute__ ((__always_inline__))
+pcmpgtw_s (int32x2_t s, int32x2_t t)
+{
+ return __builtin_loongson_pcmpgtw_s (s, t);
+}
+
+__extension__ static __inline int16x4_t __attribute__ ((__always_inline__))
+pcmpgth_s (int16x4_t s, int16x4_t t)
+{
+ return __builtin_loongson_pcmpgth_s (s, t);
+}
+
+__extension__ static __inline int8x8_t __attribute__ ((__always_inline__))
+pcmpgtb_s (int8x8_t s, int8x8_t t)
+{
+ return __builtin_loongson_pcmpgtb_s (s, t);
+}
+
+/* Extract halfword. */
+__extension__ static __inline uint16x4_t __attribute__ ((__always_inline__))
+pextrh_u (uint16x4_t s, int field /* 0--3 */)
+{
+ return __builtin_loongson_pextrh_u (s, field);
+}
+
+__extension__ static __inline int16x4_t __attribute__ ((__always_inline__))
+pextrh_s (int16x4_t s, int field /* 0--3 */)
+{
+ return __builtin_loongson_pextrh_s (s, field);
+}
+
+/* Insert halfword. */
+__extension__ static __inline uint16x4_t __attribute__ ((__always_inline__))
+pinsrh_0_u (uint16x4_t s, uint16x4_t t)
+{
+ return __builtin_loongson_pinsrh_0_u (s, t);
+}
+
+__extension__ static __inline uint16x4_t __attribute__ ((__always_inline__))
+pinsrh_1_u (uint16x4_t s, uint16x4_t t)
+{
+ return __builtin_loongson_pinsrh_1_u (s, t);
+}
+
+__extension__ static __inline uint16x4_t __attribute__ ((__always_inline__))
+pinsrh_2_u (uint16x4_t s, uint16x4_t t)
+{
+ return __builtin_loongson_pinsrh_2_u (s, t);
+}
+
+__extension__ static __inline uint16x4_t __attribute__ ((__always_inline__))
+pinsrh_3_u (uint16x4_t s, uint16x4_t t)
+{
+ return __builtin_loongson_pinsrh_3_u (s, t);
+}
+
+__extension__ static __inline int16x4_t __attribute__ ((__always_inline__))
+pinsrh_0_s (int16x4_t s, int16x4_t t)
+{
+ return __builtin_loongson_pinsrh_0_s (s, t);
+}
+
+__extension__ static __inline int16x4_t __attribute__ ((__always_inline__))
+pinsrh_1_s (int16x4_t s, int16x4_t t)
+{
+ return __builtin_loongson_pinsrh_1_s (s, t);
+}
+
+__extension__ static __inline int16x4_t __attribute__ ((__always_inline__))
+pinsrh_2_s (int16x4_t s, int16x4_t t)
+{
+ return __builtin_loongson_pinsrh_2_s (s, t);
+}
+
+__extension__ static __inline int16x4_t __attribute__ ((__always_inline__))
+pinsrh_3_s (int16x4_t s, int16x4_t t)
+{
+ return __builtin_loongson_pinsrh_3_s (s, t);
+}
+
+/* Multiply and add. */
+__extension__ static __inline int32x2_t __attribute__ ((__always_inline__))
+pmaddhw (int16x4_t s, int16x4_t t)
+{
+ return __builtin_loongson_pmaddhw (s, t);
+}
+
+/* Maximum of signed halfwords. */
+__extension__ static __inline int16x4_t __attribute__ ((__always_inline__))
+pmaxsh (int16x4_t s, int16x4_t t)
+{
+ return __builtin_loongson_pmaxsh (s, t);
+}
+
+/* Maximum of unsigned bytes. */
+__extension__ static __inline uint8x8_t __attribute__ ((__always_inline__))
+pmaxub (uint8x8_t s, uint8x8_t t)
+{
+ return __builtin_loongson_pmaxub (s, t);
+}
+
+/* Minimum of signed halfwords. */
+__extension__ static __inline int16x4_t __attribute__ ((__always_inline__))
+pminsh (int16x4_t s, int16x4_t t)
+{
+ return __builtin_loongson_pminsh (s, t);
+}
+
+/* Minimum of unsigned bytes. */
+__extension__ static __inline uint8x8_t __attribute__ ((__always_inline__))
+pminub (uint8x8_t s, uint8x8_t t)
+{
+ return __builtin_loongson_pminub (s, t);
+}
+
+/* Move byte mask. */
+__extension__ static __inline uint8x8_t __attribute__ ((__always_inline__))
+pmovmskb_u (uint8x8_t s)
+{
+ return __builtin_loongson_pmovmskb_u (s);
+}
+
+__extension__ static __inline int8x8_t __attribute__ ((__always_inline__))
+pmovmskb_s (int8x8_t s)
+{
+ return __builtin_loongson_pmovmskb_s (s);
+}
+
+/* Multiply unsigned integers and store high result. */
+__extension__ static __inline uint16x4_t __attribute__ ((__always_inline__))
+pmulhuh (uint16x4_t s, uint16x4_t t)
+{
+ return __builtin_loongson_pmulhuh (s, t);
+}
+
+/* Multiply signed integers and store high result. */
+__extension__ static __inline int16x4_t __attribute__ ((__always_inline__))
+pmulhh (int16x4_t s, int16x4_t t)
+{
+ return __builtin_loongson_pmulhh (s, t);
+}
+
+/* Multiply signed integers and store low result. */
+__extension__ static __inline int16x4_t __attribute__ ((__always_inline__))
+pmullh (int16x4_t s, int16x4_t t)
+{
+ return __builtin_loongson_pmullh (s, t);
+}
+
+/* Multiply unsigned word integers. */
+__extension__ static __inline int64_t __attribute__ ((__always_inline__))
+pmuluw (uint32x2_t s, uint32x2_t t)
+{
+ return __builtin_loongson_pmuluw (s, t);
+}
+
+/* Absolute difference. */
+__extension__ static __inline uint8x8_t __attribute__ ((__always_inline__))
+pasubub (uint8x8_t s, uint8x8_t t)
+{
+ return __builtin_loongson_pasubub (s, t);
+}
+
+/* Sum of unsigned byte integers. */
+__extension__ static __inline uint16x4_t __attribute__ ((__always_inline__))
+biadd (uint8x8_t s)
+{
+ return __builtin_loongson_biadd (s);
+}
+
+/* Sum of absolute differences.
+ Note that this intrinsic expands into two machine instructions:
+ PASUBUB followed by BIADD. */
+__extension__ static __inline uint16x4_t __attribute__ ((__always_inline__))
+psadbh (uint8x8_t s, uint8x8_t t)
+{
+ return __builtin_loongson_psadbh (s, t);
+}
+
+/* Shuffle halfwords. */
+__extension__ static __inline uint16x4_t __attribute__ ((__always_inline__))
+pshufh_u (uint16x4_t dest, uint16x4_t s, uint8_t order)
+{
+ return __builtin_loongson_pshufh_u (dest, s, order);
+}
+
+__extension__ static __inline int16x4_t __attribute__ ((__always_inline__))
+pshufh_s (int16x4_t dest, int16x4_t s, uint8_t order)
+{
+ return __builtin_loongson_pshufh_s (dest, s, order);
+}
+
+/* Shift left logical. */
+__extension__ static __inline uint16x4_t __attribute__ ((__always_inline__))
+psllh_u (uint16x4_t s, uint8_t amount)
+{
+ return __builtin_loongson_psllh_u (s, amount);
+}
+
+__extension__ static __inline int16x4_t __attribute__ ((__always_inline__))
+psllh_s (int16x4_t s, uint8_t amount)
+{
+ return __builtin_loongson_psllh_s (s, amount);
+}
+
+__extension__ static __inline uint32x2_t __attribute__ ((__always_inline__))
+psllw_u (uint32x2_t s, uint8_t amount)
+{
+ return __builtin_loongson_psllw_u (s, amount);
+}
+
+__extension__ static __inline int32x2_t __attribute__ ((__always_inline__))
+psllw_s (int32x2_t s, uint8_t amount)
+{
+ return __builtin_loongson_psllw_s (s, amount);
+}
+
+/* Shift right logical. */
+__extension__ static __inline uint16x4_t __attribute__ ((__always_inline__))
+psrlh_u (uint16x4_t s, uint8_t amount)
+{
+ return __builtin_loongson_psrlh_u (s, amount);
+}
+
+__extension__ static __inline int16x4_t __attribute__ ((__always_inline__))
+psrlh_s (int16x4_t s, uint8_t amount)
+{
+ return __builtin_loongson_psrlh_s (s, amount);
+}
+
+__extension__ static __inline uint32x2_t __attribute__ ((__always_inline__))
+psrlw_u (uint32x2_t s, uint8_t amount)
+{
+ return __builtin_loongson_psrlw_u (s, amount);
+}
+
+__extension__ static __inline int32x2_t __attribute__ ((__always_inline__))
+psrlw_s (int32x2_t s, uint8_t amount)
+{
+ return __builtin_loongson_psrlw_s (s, amount);
+}
+
+/* Shift right arithmetic. */
+__extension__ static __inline uint16x4_t __attribute__ ((__always_inline__))
+psrah_u (uint16x4_t s, uint8_t amount)
+{
+ return __builtin_loongson_psrah_u (s, amount);
+}
+
+__extension__ static __inline int16x4_t __attribute__ ((__always_inline__))
+psrah_s (int16x4_t s, uint8_t amount)
+{
+ return __builtin_loongson_psrah_s (s, amount);
+}
+
+__extension__ static __inline uint32x2_t __attribute__ ((__always_inline__))
+psraw_u (uint32x2_t s, uint8_t amount)
+{
+ return __builtin_loongson_psraw_u (s, amount);
+}
+
+__extension__ static __inline int32x2_t __attribute__ ((__always_inline__))
+psraw_s (int32x2_t s, uint8_t amount)
+{
+ return __builtin_loongson_psraw_s (s, amount);
+}
+
+/* Vector subtraction, treating overflow by wraparound. */
+__extension__ static __inline uint32x2_t __attribute__ ((__always_inline__))
+psubw_u (uint32x2_t s, uint32x2_t t)
+{
+ return __builtin_loongson_psubw_u (s, t);
+}
+
+__extension__ static __inline uint16x4_t __attribute__ ((__always_inline__))
+psubh_u (uint16x4_t s, uint16x4_t t)
+{
+ return __builtin_loongson_psubh_u (s, t);
+}
+
+__extension__ static __inline uint8x8_t __attribute__ ((__always_inline__))
+psubb_u (uint8x8_t s, uint8x8_t t)
+{
+ return __builtin_loongson_psubb_u (s, t);
+}
+
+__extension__ static __inline int32x2_t __attribute__ ((__always_inline__))
+psubw_s (int32x2_t s, int32x2_t t)
+{
+ return __builtin_loongson_psubw_s (s, t);
+}
+
+__extension__ static __inline int16x4_t __attribute__ ((__always_inline__))
+psubh_s (int16x4_t s, int16x4_t t)
+{
+ return __builtin_loongson_psubh_s (s, t);
+}
+
+__extension__ static __inline int8x8_t __attribute__ ((__always_inline__))
+psubb_s (int8x8_t s, int8x8_t t)
+{
+ return __builtin_loongson_psubb_s (s, t);
+}
+
+/* Subtraction of doubleword integers, treating overflow by wraparound. */
+__extension__ static __inline uint64_t __attribute__ ((__always_inline__))
+psubd_u (uint64_t s, uint64_t t)
+{
+ return __builtin_loongson_psubd_u (s, t);
+}
+
+__extension__ static __inline int64_t __attribute__ ((__always_inline__))
+psubd_s (int64_t s, int64_t t)
+{
+ return __builtin_loongson_psubd_s (s, t);
+}
+
+/* Vector subtraction, treating overflow by signed saturation. */
+__extension__ static __inline int16x4_t __attribute__ ((__always_inline__))
+psubsh (int16x4_t s, int16x4_t t)
+{
+ return __builtin_loongson_psubsh (s, t);
+}
+
+__extension__ static __inline int8x8_t __attribute__ ((__always_inline__))
+psubsb (int8x8_t s, int8x8_t t)
+{
+ return __builtin_loongson_psubsb (s, t);
+}
+
+/* Vector subtraction, treating overflow by unsigned saturation. */
+__extension__ static __inline uint16x4_t __attribute__ ((__always_inline__))
+psubush (uint16x4_t s, uint16x4_t t)
+{
+ return __builtin_loongson_psubush (s, t);
+}
+
+__extension__ static __inline uint8x8_t __attribute__ ((__always_inline__))
+psubusb (uint8x8_t s, uint8x8_t t)
+{
+ return __builtin_loongson_psubusb (s, t);
+}
+
+/* Unpack high data. */
+__extension__ static __inline uint32x2_t __attribute__ ((__always_inline__))
+punpckhwd_u (uint32x2_t s, uint32x2_t t)
+{
+ return __builtin_loongson_punpckhwd_u (s, t);
+}
+
+__extension__ static __inline uint16x4_t __attribute__ ((__always_inline__))
+punpckhhw_u (uint16x4_t s, uint16x4_t t)
+{
+ return __builtin_loongson_punpckhhw_u (s, t);
+}
+
+__extension__ static __inline uint8x8_t __attribute__ ((__always_inline__))
+punpckhbh_u (uint8x8_t s, uint8x8_t t)
+{
+ return __builtin_loongson_punpckhbh_u (s, t);
+}
+
+__extension__ static __inline int32x2_t __attribute__ ((__always_inline__))
+punpckhwd_s (int32x2_t s, int32x2_t t)
+{
+ return __builtin_loongson_punpckhwd_s (s, t);
+}
+
+__extension__ static __inline int16x4_t __attribute__ ((__always_inline__))
+punpckhhw_s (int16x4_t s, int16x4_t t)
+{
+ return __builtin_loongson_punpckhhw_s (s, t);
+}
+
+__extension__ static __inline int8x8_t __attribute__ ((__always_inline__))
+punpckhbh_s (int8x8_t s, int8x8_t t)
+{
+ return __builtin_loongson_punpckhbh_s (s, t);
+}
+
+/* Unpack low data. */
+__extension__ static __inline uint32x2_t __attribute__ ((__always_inline__))
+punpcklwd_u (uint32x2_t s, uint32x2_t t)
+{
+ return __builtin_loongson_punpcklwd_u (s, t);
+}
+
+__extension__ static __inline uint16x4_t __attribute__ ((__always_inline__))
+punpcklhw_u (uint16x4_t s, uint16x4_t t)
+{
+ return __builtin_loongson_punpcklhw_u (s, t);
+}
+
+__extension__ static __inline uint8x8_t __attribute__ ((__always_inline__))
+punpcklbh_u (uint8x8_t s, uint8x8_t t)
+{
+ return __builtin_loongson_punpcklbh_u (s, t);
+}
+
+__extension__ static __inline int32x2_t __attribute__ ((__always_inline__))
+punpcklwd_s (int32x2_t s, int32x2_t t)
+{
+ return __builtin_loongson_punpcklwd_s (s, t);
+}
+
+__extension__ static __inline int16x4_t __attribute__ ((__always_inline__))
+punpcklhw_s (int16x4_t s, int16x4_t t)
+{
+ return __builtin_loongson_punpcklhw_s (s, t);
+}
+
+__extension__ static __inline int8x8_t __attribute__ ((__always_inline__))
+punpcklbh_s (int8x8_t s, int8x8_t t)
+{
+ return __builtin_loongson_punpcklbh_s (s, t);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/gcc/config/mips/loongson.md b/gcc/config/mips/loongson.md
new file mode 100644
index 000000000..225f4d16d
--- /dev/null
+++ b/gcc/config/mips/loongson.md
@@ -0,0 +1,529 @@
+;; Machine description for Loongson-specific patterns, such as
+;; ST Microelectronics Loongson-2E/2F etc.
+;; Copyright (C) 2008, 2009, 2010 Free Software Foundation, Inc.
+;; Contributed by CodeSourcery.
+;;
+;; 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/>.
+
+(define_c_enum "unspec" [
+ UNSPEC_LOONGSON_PAVG
+ UNSPEC_LOONGSON_PCMPEQ
+ UNSPEC_LOONGSON_PCMPGT
+ UNSPEC_LOONGSON_PEXTR
+ UNSPEC_LOONGSON_PINSR_0
+ UNSPEC_LOONGSON_PINSR_1
+ UNSPEC_LOONGSON_PINSR_2
+ UNSPEC_LOONGSON_PINSR_3
+ UNSPEC_LOONGSON_PMADD
+ UNSPEC_LOONGSON_PMOVMSK
+ UNSPEC_LOONGSON_PMULHU
+ UNSPEC_LOONGSON_PMULH
+ UNSPEC_LOONGSON_PMULU
+ UNSPEC_LOONGSON_PASUBUB
+ UNSPEC_LOONGSON_BIADD
+ UNSPEC_LOONGSON_PSADBH
+ UNSPEC_LOONGSON_PSHUFH
+ UNSPEC_LOONGSON_PUNPCKH
+ UNSPEC_LOONGSON_PUNPCKL
+ UNSPEC_LOONGSON_PADDD
+ UNSPEC_LOONGSON_PSUBD
+])
+
+;; Mode iterators and attributes.
+
+;; 64-bit vectors of bytes.
+(define_mode_iterator VB [V8QI])
+
+;; 64-bit vectors of halfwords.
+(define_mode_iterator VH [V4HI])
+
+;; 64-bit vectors of words.
+(define_mode_iterator VW [V2SI])
+
+;; 64-bit vectors of halfwords and bytes.
+(define_mode_iterator VHB [V4HI V8QI])
+
+;; 64-bit vectors of words and halfwords.
+(define_mode_iterator VWH [V2SI V4HI])
+
+;; 64-bit vectors of words, halfwords and bytes.
+(define_mode_iterator VWHB [V2SI V4HI V8QI])
+
+;; 64-bit vectors of words, halfwords and bytes; and DImode.
+(define_mode_iterator VWHBDI [V2SI V4HI V8QI DI])
+
+;; The Loongson instruction suffixes corresponding to the modes in the
+;; VWHBDI iterator.
+(define_mode_attr V_suffix [(V2SI "w") (V4HI "h") (V8QI "b") (DI "d")])
+
+;; Given a vector type T, the mode of a vector half the size of T
+;; and with the same number of elements.
+(define_mode_attr V_squash [(V2SI "V2HI") (V4HI "V4QI")])
+
+;; Given a vector type T, the mode of a vector the same size as T
+;; but with half as many elements.
+(define_mode_attr V_stretch_half [(V2SI "DI") (V4HI "V2SI") (V8QI "V4HI")])
+
+;; The Loongson instruction suffixes corresponding to the transformation
+;; expressed by V_stretch_half.
+(define_mode_attr V_stretch_half_suffix [(V2SI "wd") (V4HI "hw") (V8QI "bh")])
+
+;; Given a vector type T, the mode of a vector the same size as T
+;; but with twice as many elements.
+(define_mode_attr V_squash_double [(V2SI "V4HI") (V4HI "V8QI")])
+
+;; The Loongson instruction suffixes corresponding to the conversions
+;; specified by V_half_width.
+(define_mode_attr V_squash_double_suffix [(V2SI "wh") (V4HI "hb")])
+
+;; Move patterns.
+
+;; Expander to legitimize moves involving values of vector modes.
+(define_expand "mov<mode>"
+ [(set (match_operand:VWHB 0)
+ (match_operand:VWHB 1))]
+ "TARGET_HARD_FLOAT && TARGET_LOONGSON_VECTORS"
+{
+ if (mips_legitimize_move (<MODE>mode, operands[0], operands[1]))
+ DONE;
+})
+
+;; Handle legitimized moves between values of vector modes.
+(define_insn "mov<mode>_internal"
+ [(set (match_operand:VWHB 0 "nonimmediate_operand" "=m,f,d,f, d, m, d")
+ (match_operand:VWHB 1 "move_operand" "f,m,f,dYG,dYG,dYG,m"))]
+ "TARGET_HARD_FLOAT && TARGET_LOONGSON_VECTORS"
+ { return mips_output_move (operands[0], operands[1]); }
+ [(set_attr "move_type" "fpstore,fpload,mfc,mtc,move,store,load")
+ (set_attr "mode" "DI")])
+
+;; Initialization of a vector.
+
+(define_expand "vec_init<mode>"
+ [(set (match_operand:VWHB 0 "register_operand")
+ (match_operand 1 ""))]
+ "TARGET_HARD_FLOAT && TARGET_LOONGSON_VECTORS"
+{
+ mips_expand_vector_init (operands[0], operands[1]);
+ DONE;
+})
+
+;; Instruction patterns for SIMD instructions.
+
+;; Pack with signed saturation.
+(define_insn "vec_pack_ssat_<mode>"
+ [(set (match_operand:<V_squash_double> 0 "register_operand" "=f")
+ (vec_concat:<V_squash_double>
+ (ss_truncate:<V_squash>
+ (match_operand:VWH 1 "register_operand" "f"))
+ (ss_truncate:<V_squash>
+ (match_operand:VWH 2 "register_operand" "f"))))]
+ "TARGET_HARD_FLOAT && TARGET_LOONGSON_VECTORS"
+ "packss<V_squash_double_suffix>\t%0,%1,%2"
+ [(set_attr "type" "fmul")])
+
+;; Pack with unsigned saturation.
+(define_insn "vec_pack_usat_<mode>"
+ [(set (match_operand:<V_squash_double> 0 "register_operand" "=f")
+ (vec_concat:<V_squash_double>
+ (us_truncate:<V_squash>
+ (match_operand:VH 1 "register_operand" "f"))
+ (us_truncate:<V_squash>
+ (match_operand:VH 2 "register_operand" "f"))))]
+ "TARGET_HARD_FLOAT && TARGET_LOONGSON_VECTORS"
+ "packus<V_squash_double_suffix>\t%0,%1,%2"
+ [(set_attr "type" "fmul")])
+
+;; Addition, treating overflow by wraparound.
+(define_insn "add<mode>3"
+ [(set (match_operand:VWHB 0 "register_operand" "=f")
+ (plus:VWHB (match_operand:VWHB 1 "register_operand" "f")
+ (match_operand:VWHB 2 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT && TARGET_LOONGSON_VECTORS"
+ "padd<V_suffix>\t%0,%1,%2"
+ [(set_attr "type" "fadd")])
+
+;; Addition of doubleword integers stored in FP registers.
+;; Overflow is treated by wraparound.
+;; We use 'unspec' instead of 'plus' here to avoid clash with
+;; mips.md::add<mode>3. If 'plus' was used, then such instruction
+;; would be recognized as adddi3 and reload would make it use
+;; GPRs instead of FPRs.
+(define_insn "loongson_paddd"
+ [(set (match_operand:DI 0 "register_operand" "=f")
+ (unspec:DI [(match_operand:DI 1 "register_operand" "f")
+ (match_operand:DI 2 "register_operand" "f")]
+ UNSPEC_LOONGSON_PADDD))]
+ "TARGET_HARD_FLOAT && TARGET_LOONGSON_VECTORS"
+ "paddd\t%0,%1,%2"
+ [(set_attr "type" "fadd")])
+
+;; Addition, treating overflow by signed saturation.
+(define_insn "ssadd<mode>3"
+ [(set (match_operand:VHB 0 "register_operand" "=f")
+ (ss_plus:VHB (match_operand:VHB 1 "register_operand" "f")
+ (match_operand:VHB 2 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT && TARGET_LOONGSON_VECTORS"
+ "padds<V_suffix>\t%0,%1,%2"
+ [(set_attr "type" "fadd")])
+
+;; Addition, treating overflow by unsigned saturation.
+(define_insn "usadd<mode>3"
+ [(set (match_operand:VHB 0 "register_operand" "=f")
+ (us_plus:VHB (match_operand:VHB 1 "register_operand" "f")
+ (match_operand:VHB 2 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT && TARGET_LOONGSON_VECTORS"
+ "paddus<V_suffix>\t%0,%1,%2"
+ [(set_attr "type" "fadd")])
+
+;; Logical AND NOT.
+(define_insn "loongson_pandn_<V_suffix>"
+ [(set (match_operand:VWHBDI 0 "register_operand" "=f")
+ (and:VWHBDI
+ (not:VWHBDI (match_operand:VWHBDI 1 "register_operand" "f"))
+ (match_operand:VWHBDI 2 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT && TARGET_LOONGSON_VECTORS"
+ "pandn\t%0,%1,%2"
+ [(set_attr "type" "fmul")])
+
+;; Average.
+(define_insn "loongson_pavg<V_suffix>"
+ [(set (match_operand:VHB 0 "register_operand" "=f")
+ (unspec:VHB [(match_operand:VHB 1 "register_operand" "f")
+ (match_operand:VHB 2 "register_operand" "f")]
+ UNSPEC_LOONGSON_PAVG))]
+ "TARGET_HARD_FLOAT && TARGET_LOONGSON_VECTORS"
+ "pavg<V_suffix>\t%0,%1,%2"
+ [(set_attr "type" "fadd")])
+
+;; Equality test.
+(define_insn "loongson_pcmpeq<V_suffix>"
+ [(set (match_operand:VWHB 0 "register_operand" "=f")
+ (unspec:VWHB [(match_operand:VWHB 1 "register_operand" "f")
+ (match_operand:VWHB 2 "register_operand" "f")]
+ UNSPEC_LOONGSON_PCMPEQ))]
+ "TARGET_HARD_FLOAT && TARGET_LOONGSON_VECTORS"
+ "pcmpeq<V_suffix>\t%0,%1,%2"
+ [(set_attr "type" "fadd")])
+
+;; Greater-than test.
+(define_insn "loongson_pcmpgt<V_suffix>"
+ [(set (match_operand:VWHB 0 "register_operand" "=f")
+ (unspec:VWHB [(match_operand:VWHB 1 "register_operand" "f")
+ (match_operand:VWHB 2 "register_operand" "f")]
+ UNSPEC_LOONGSON_PCMPGT))]
+ "TARGET_HARD_FLOAT && TARGET_LOONGSON_VECTORS"
+ "pcmpgt<V_suffix>\t%0,%1,%2"
+ [(set_attr "type" "fadd")])
+
+;; Extract halfword.
+(define_insn "loongson_pextr<V_suffix>"
+ [(set (match_operand:VH 0 "register_operand" "=f")
+ (unspec:VH [(match_operand:VH 1 "register_operand" "f")
+ (match_operand:SI 2 "register_operand" "f")]
+ UNSPEC_LOONGSON_PEXTR))]
+ "TARGET_HARD_FLOAT && TARGET_LOONGSON_VECTORS"
+ "pextr<V_suffix>\t%0,%1,%2"
+ [(set_attr "type" "fmul")])
+
+;; Insert halfword.
+(define_insn "loongson_pinsr<V_suffix>_0"
+ [(set (match_operand:VH 0 "register_operand" "=f")
+ (unspec:VH [(match_operand:VH 1 "register_operand" "f")
+ (match_operand:VH 2 "register_operand" "f")]
+ UNSPEC_LOONGSON_PINSR_0))]
+ "TARGET_HARD_FLOAT && TARGET_LOONGSON_VECTORS"
+ "pinsr<V_suffix>_0\t%0,%1,%2"
+ [(set_attr "type" "fdiv")])
+
+(define_insn "loongson_pinsr<V_suffix>_1"
+ [(set (match_operand:VH 0 "register_operand" "=f")
+ (unspec:VH [(match_operand:VH 1 "register_operand" "f")
+ (match_operand:VH 2 "register_operand" "f")]
+ UNSPEC_LOONGSON_PINSR_1))]
+ "TARGET_HARD_FLOAT && TARGET_LOONGSON_VECTORS"
+ "pinsr<V_suffix>_1\t%0,%1,%2"
+ [(set_attr "type" "fdiv")])
+
+(define_insn "loongson_pinsr<V_suffix>_2"
+ [(set (match_operand:VH 0 "register_operand" "=f")
+ (unspec:VH [(match_operand:VH 1 "register_operand" "f")
+ (match_operand:VH 2 "register_operand" "f")]
+ UNSPEC_LOONGSON_PINSR_2))]
+ "TARGET_HARD_FLOAT && TARGET_LOONGSON_VECTORS"
+ "pinsr<V_suffix>_2\t%0,%1,%2"
+ [(set_attr "type" "fdiv")])
+
+(define_insn "loongson_pinsr<V_suffix>_3"
+ [(set (match_operand:VH 0 "register_operand" "=f")
+ (unspec:VH [(match_operand:VH 1 "register_operand" "f")
+ (match_operand:VH 2 "register_operand" "f")]
+ UNSPEC_LOONGSON_PINSR_3))]
+ "TARGET_HARD_FLOAT && TARGET_LOONGSON_VECTORS"
+ "pinsr<V_suffix>_3\t%0,%1,%2"
+ [(set_attr "type" "fdiv")])
+
+;; Multiply and add packed integers.
+(define_insn "loongson_pmadd<V_stretch_half_suffix>"
+ [(set (match_operand:<V_stretch_half> 0 "register_operand" "=f")
+ (unspec:<V_stretch_half> [(match_operand:VH 1 "register_operand" "f")
+ (match_operand:VH 2 "register_operand" "f")]
+ UNSPEC_LOONGSON_PMADD))]
+ "TARGET_HARD_FLOAT && TARGET_LOONGSON_VECTORS"
+ "pmadd<V_stretch_half_suffix>\t%0,%1,%2"
+ [(set_attr "type" "fmul")])
+
+;; Maximum of signed halfwords.
+(define_insn "smax<mode>3"
+ [(set (match_operand:VH 0 "register_operand" "=f")
+ (smax:VH (match_operand:VH 1 "register_operand" "f")
+ (match_operand:VH 2 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT && TARGET_LOONGSON_VECTORS"
+ "pmaxs<V_suffix>\t%0,%1,%2"
+ [(set_attr "type" "fadd")])
+
+;; Maximum of unsigned bytes.
+(define_insn "umax<mode>3"
+ [(set (match_operand:VB 0 "register_operand" "=f")
+ (umax:VB (match_operand:VB 1 "register_operand" "f")
+ (match_operand:VB 2 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT && TARGET_LOONGSON_VECTORS"
+ "pmaxu<V_suffix>\t%0,%1,%2"
+ [(set_attr "type" "fadd")])
+
+;; Minimum of signed halfwords.
+(define_insn "smin<mode>3"
+ [(set (match_operand:VH 0 "register_operand" "=f")
+ (smin:VH (match_operand:VH 1 "register_operand" "f")
+ (match_operand:VH 2 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT && TARGET_LOONGSON_VECTORS"
+ "pmins<V_suffix>\t%0,%1,%2"
+ [(set_attr "type" "fadd")])
+
+;; Minimum of unsigned bytes.
+(define_insn "umin<mode>3"
+ [(set (match_operand:VB 0 "register_operand" "=f")
+ (umin:VB (match_operand:VB 1 "register_operand" "f")
+ (match_operand:VB 2 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT && TARGET_LOONGSON_VECTORS"
+ "pminu<V_suffix>\t%0,%1,%2"
+ [(set_attr "type" "fadd")])
+
+;; Move byte mask.
+(define_insn "loongson_pmovmsk<V_suffix>"
+ [(set (match_operand:VB 0 "register_operand" "=f")
+ (unspec:VB [(match_operand:VB 1 "register_operand" "f")]
+ UNSPEC_LOONGSON_PMOVMSK))]
+ "TARGET_HARD_FLOAT && TARGET_LOONGSON_VECTORS"
+ "pmovmsk<V_suffix>\t%0,%1"
+ [(set_attr "type" "fabs")])
+
+;; Multiply unsigned integers and store high result.
+(define_insn "umul<mode>3_highpart"
+ [(set (match_operand:VH 0 "register_operand" "=f")
+ (unspec:VH [(match_operand:VH 1 "register_operand" "f")
+ (match_operand:VH 2 "register_operand" "f")]
+ UNSPEC_LOONGSON_PMULHU))]
+ "TARGET_HARD_FLOAT && TARGET_LOONGSON_VECTORS"
+ "pmulhu<V_suffix>\t%0,%1,%2"
+ [(set_attr "type" "fmul")])
+
+;; Multiply signed integers and store high result.
+(define_insn "smul<mode>3_highpart"
+ [(set (match_operand:VH 0 "register_operand" "=f")
+ (unspec:VH [(match_operand:VH 1 "register_operand" "f")
+ (match_operand:VH 2 "register_operand" "f")]
+ UNSPEC_LOONGSON_PMULH))]
+ "TARGET_HARD_FLOAT && TARGET_LOONGSON_VECTORS"
+ "pmulh<V_suffix>\t%0,%1,%2"
+ [(set_attr "type" "fmul")])
+
+;; Multiply signed integers and store low result.
+(define_insn "mul<mode>3"
+ [(set (match_operand:VH 0 "register_operand" "=f")
+ (mult:VH (match_operand:VH 1 "register_operand" "f")
+ (match_operand:VH 2 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT && TARGET_LOONGSON_VECTORS"
+ "pmull<V_suffix>\t%0,%1,%2"
+ [(set_attr "type" "fmul")])
+
+;; Multiply unsigned word integers.
+(define_insn "loongson_pmulu<V_suffix>"
+ [(set (match_operand:DI 0 "register_operand" "=f")
+ (unspec:DI [(match_operand:VW 1 "register_operand" "f")
+ (match_operand:VW 2 "register_operand" "f")]
+ UNSPEC_LOONGSON_PMULU))]
+ "TARGET_HARD_FLOAT && TARGET_LOONGSON_VECTORS"
+ "pmulu<V_suffix>\t%0,%1,%2"
+ [(set_attr "type" "fmul")])
+
+;; Absolute difference.
+(define_insn "loongson_pasubub"
+ [(set (match_operand:VB 0 "register_operand" "=f")
+ (unspec:VB [(match_operand:VB 1 "register_operand" "f")
+ (match_operand:VB 2 "register_operand" "f")]
+ UNSPEC_LOONGSON_PASUBUB))]
+ "TARGET_HARD_FLOAT && TARGET_LOONGSON_VECTORS"
+ "pasubub\t%0,%1,%2"
+ [(set_attr "type" "fadd")])
+
+;; Sum of unsigned byte integers.
+(define_insn "loongson_biadd"
+ [(set (match_operand:<V_stretch_half> 0 "register_operand" "=f")
+ (unspec:<V_stretch_half> [(match_operand:VB 1 "register_operand" "f")]
+ UNSPEC_LOONGSON_BIADD))]
+ "TARGET_HARD_FLOAT && TARGET_LOONGSON_VECTORS"
+ "biadd\t%0,%1"
+ [(set_attr "type" "fabs")])
+
+;; Sum of absolute differences.
+(define_insn "loongson_psadbh"
+ [(set (match_operand:<V_stretch_half> 0 "register_operand" "=f")
+ (unspec:<V_stretch_half> [(match_operand:VB 1 "register_operand" "f")
+ (match_operand:VB 2 "register_operand" "f")]
+ UNSPEC_LOONGSON_PSADBH))]
+ "TARGET_HARD_FLOAT && TARGET_LOONGSON_VECTORS"
+ "pasubub\t%0,%1,%2;biadd\t%0,%0"
+ [(set_attr "type" "fadd")])
+
+;; Shuffle halfwords.
+(define_insn "loongson_pshufh"
+ [(set (match_operand:VH 0 "register_operand" "=f")
+ (unspec:VH [(match_operand:VH 1 "register_operand" "0")
+ (match_operand:VH 2 "register_operand" "f")
+ (match_operand:SI 3 "register_operand" "f")]
+ UNSPEC_LOONGSON_PSHUFH))]
+ "TARGET_HARD_FLOAT && TARGET_LOONGSON_VECTORS"
+ "pshufh\t%0,%2,%3"
+ [(set_attr "type" "fmul")])
+
+;; Shift left logical.
+(define_insn "ashl<mode>3"
+ [(set (match_operand:VWH 0 "register_operand" "=f")
+ (ashift:VWH (match_operand:VWH 1 "register_operand" "f")
+ (match_operand:SI 2 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT && TARGET_LOONGSON_VECTORS"
+ "psll<V_suffix>\t%0,%1,%2"
+ [(set_attr "type" "fmul")])
+
+;; Shift right arithmetic.
+(define_insn "ashr<mode>3"
+ [(set (match_operand:VWH 0 "register_operand" "=f")
+ (ashiftrt:VWH (match_operand:VWH 1 "register_operand" "f")
+ (match_operand:SI 2 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT && TARGET_LOONGSON_VECTORS"
+ "psra<V_suffix>\t%0,%1,%2"
+ [(set_attr "type" "fdiv")])
+
+;; Shift right logical.
+(define_insn "lshr<mode>3"
+ [(set (match_operand:VWH 0 "register_operand" "=f")
+ (lshiftrt:VWH (match_operand:VWH 1 "register_operand" "f")
+ (match_operand:SI 2 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT && TARGET_LOONGSON_VECTORS"
+ "psrl<V_suffix>\t%0,%1,%2"
+ [(set_attr "type" "fdiv")])
+
+;; Subtraction, treating overflow by wraparound.
+(define_insn "sub<mode>3"
+ [(set (match_operand:VWHB 0 "register_operand" "=f")
+ (minus:VWHB (match_operand:VWHB 1 "register_operand" "f")
+ (match_operand:VWHB 2 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT && TARGET_LOONGSON_VECTORS"
+ "psub<V_suffix>\t%0,%1,%2"
+ [(set_attr "type" "fadd")])
+
+;; Subtraction of doubleword integers stored in FP registers.
+;; Overflow is treated by wraparound.
+;; See loongson_paddd for the reason we use 'unspec' rather than
+;; 'minus' here.
+(define_insn "loongson_psubd"
+ [(set (match_operand:DI 0 "register_operand" "=f")
+ (unspec:DI [(match_operand:DI 1 "register_operand" "f")
+ (match_operand:DI 2 "register_operand" "f")]
+ UNSPEC_LOONGSON_PSUBD))]
+ "TARGET_HARD_FLOAT && TARGET_LOONGSON_VECTORS"
+ "psubd\t%0,%1,%2"
+ [(set_attr "type" "fadd")])
+
+;; Subtraction, treating overflow by signed saturation.
+(define_insn "sssub<mode>3"
+ [(set (match_operand:VHB 0 "register_operand" "=f")
+ (ss_minus:VHB (match_operand:VHB 1 "register_operand" "f")
+ (match_operand:VHB 2 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT && TARGET_LOONGSON_VECTORS"
+ "psubs<V_suffix>\t%0,%1,%2"
+ [(set_attr "type" "fadd")])
+
+;; Subtraction, treating overflow by unsigned saturation.
+(define_insn "ussub<mode>3"
+ [(set (match_operand:VHB 0 "register_operand" "=f")
+ (us_minus:VHB (match_operand:VHB 1 "register_operand" "f")
+ (match_operand:VHB 2 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT && TARGET_LOONGSON_VECTORS"
+ "psubus<V_suffix>\t%0,%1,%2"
+ [(set_attr "type" "fadd")])
+
+;; Unpack high data.
+(define_insn "vec_interleave_high<mode>"
+ [(set (match_operand:VWHB 0 "register_operand" "=f")
+ (unspec:VWHB [(match_operand:VWHB 1 "register_operand" "f")
+ (match_operand:VWHB 2 "register_operand" "f")]
+ UNSPEC_LOONGSON_PUNPCKH))]
+ "TARGET_HARD_FLOAT && TARGET_LOONGSON_VECTORS"
+ "punpckh<V_stretch_half_suffix>\t%0,%1,%2"
+ [(set_attr "type" "fdiv")])
+
+;; Unpack low data.
+(define_insn "vec_interleave_low<mode>"
+ [(set (match_operand:VWHB 0 "register_operand" "=f")
+ (unspec:VWHB [(match_operand:VWHB 1 "register_operand" "f")
+ (match_operand:VWHB 2 "register_operand" "f")]
+ UNSPEC_LOONGSON_PUNPCKL))]
+ "TARGET_HARD_FLOAT && TARGET_LOONGSON_VECTORS"
+ "punpckl<V_stretch_half_suffix>\t%0,%1,%2"
+ [(set_attr "type" "fdiv")])
+
+;; Integer division and modulus. For integer multiplication, see mips.md.
+
+(define_insn "<u>div<mode>3"
+ [(set (match_operand:GPR 0 "register_operand" "=&d")
+ (any_div:GPR (match_operand:GPR 1 "register_operand" "d")
+ (match_operand:GPR 2 "register_operand" "d")))]
+ "TARGET_LOONGSON_2EF || TARGET_LOONGSON_3A"
+ {
+ if (TARGET_LOONGSON_2EF)
+ return mips_output_division ("<d>div<u>.g\t%0,%1,%2", operands);
+ else
+ return mips_output_division ("gs<d>div<u>\t%0,%1,%2", operands);
+ }
+ [(set_attr "type" "idiv3")
+ (set_attr "mode" "<MODE>")])
+
+(define_insn "<u>mod<mode>3"
+ [(set (match_operand:GPR 0 "register_operand" "=&d")
+ (any_mod:GPR (match_operand:GPR 1 "register_operand" "d")
+ (match_operand:GPR 2 "register_operand" "d")))]
+ "TARGET_LOONGSON_2EF || TARGET_LOONGSON_3A"
+ {
+ if (TARGET_LOONGSON_2EF)
+ return mips_output_division ("<d>mod<u>.g\t%0,%1,%2", operands);
+ else
+ return mips_output_division ("gs<d>mod<u>\t%0,%1,%2", operands);
+ }
+ [(set_attr "type" "idiv3")
+ (set_attr "mode" "<MODE>")])
diff --git a/gcc/config/mips/loongson2ef.md b/gcc/config/mips/loongson2ef.md
new file mode 100644
index 000000000..fa5ae7e9f
--- /dev/null
+++ b/gcc/config/mips/loongson2ef.md
@@ -0,0 +1,252 @@
+;; Pipeline model for ST Microelectronics Loongson-2E/2F cores.
+
+;; Copyright (C) 2008, 2010 Free Software Foundation, Inc.
+;; Contributed by CodeSourcery.
+;;
+;; 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/>.
+
+(define_c_enum "unspec" [
+ UNSPEC_LOONGSON_ALU1_TURN_ENABLED_INSN
+ UNSPEC_LOONGSON_ALU2_TURN_ENABLED_INSN
+ UNSPEC_LOONGSON_FALU1_TURN_ENABLED_INSN
+ UNSPEC_LOONGSON_FALU2_TURN_ENABLED_INSN
+])
+
+;; Automaton for integer instructions.
+(define_automaton "ls2_alu")
+
+;; ALU1 and ALU2.
+;; We need to query these units to adjust round-robin counter.
+(define_query_cpu_unit "ls2_alu1_core,ls2_alu2_core" "ls2_alu")
+
+;; Pseudo units to help modeling of ALU1/2 round-robin dispatch strategy.
+(define_cpu_unit "ls2_alu1_turn,ls2_alu2_turn" "ls2_alu")
+
+;; Pseudo units to enable/disable ls2_alu[12]_turn units.
+;; ls2_alu[12]_turn unit can be subscribed only after ls2_alu[12]_turn_enabled
+;; unit is subscribed.
+(define_cpu_unit "ls2_alu1_turn_enabled,ls2_alu2_turn_enabled" "ls2_alu")
+(presence_set "ls2_alu1_turn" "ls2_alu1_turn_enabled")
+(presence_set "ls2_alu2_turn" "ls2_alu2_turn_enabled")
+
+;; Reservations for ALU1 (ALU2) instructions.
+;; Instruction goes to ALU1 (ALU2) and makes next ALU1/2 instruction to
+;; be dispatched to ALU2 (ALU1).
+(define_reservation "ls2_alu1"
+ "(ls2_alu1_core+ls2_alu2_turn_enabled)|ls2_alu1_core")
+(define_reservation "ls2_alu2"
+ "(ls2_alu2_core+ls2_alu1_turn_enabled)|ls2_alu2_core")
+
+;; Reservation for ALU1/2 instructions.
+;; Instruction will go to ALU1 iff ls2_alu1_turn_enabled is subscribed and
+;; switch the turn to ALU2 by subscribing ls2_alu2_turn_enabled.
+;; Or to ALU2 otherwise.
+(define_reservation "ls2_alu"
+ "(ls2_alu1_core+ls2_alu1_turn+ls2_alu2_turn_enabled)
+ |(ls2_alu1_core+ls2_alu1_turn)
+ |(ls2_alu2_core+ls2_alu2_turn+ls2_alu1_turn_enabled)
+ |(ls2_alu2_core+ls2_alu2_turn)")
+
+;; Automaton for floating-point instructions.
+(define_automaton "ls2_falu")
+
+;; FALU1 and FALU2.
+;; We need to query these units to adjust round-robin counter.
+(define_query_cpu_unit "ls2_falu1_core,ls2_falu2_core" "ls2_falu")
+
+;; Pseudo units to help modeling of FALU1/2 round-robin dispatch strategy.
+(define_cpu_unit "ls2_falu1_turn,ls2_falu2_turn" "ls2_falu")
+
+;; Pseudo units to enable/disable ls2_falu[12]_turn units.
+;; ls2_falu[12]_turn unit can be subscribed only after
+;; ls2_falu[12]_turn_enabled unit is subscribed.
+(define_cpu_unit "ls2_falu1_turn_enabled,ls2_falu2_turn_enabled" "ls2_falu")
+(presence_set "ls2_falu1_turn" "ls2_falu1_turn_enabled")
+(presence_set "ls2_falu2_turn" "ls2_falu2_turn_enabled")
+
+;; Reservations for FALU1 (FALU2) instructions.
+;; Instruction goes to FALU1 (FALU2) and makes next FALU1/2 instruction to
+;; be dispatched to FALU2 (FALU1).
+(define_reservation "ls2_falu1"
+ "(ls2_falu1_core+ls2_falu2_turn_enabled)|ls2_falu1_core")
+(define_reservation "ls2_falu2"
+ "(ls2_falu2_core+ls2_falu1_turn_enabled)|ls2_falu2_core")
+
+;; Reservation for FALU1/2 instructions.
+;; Instruction will go to FALU1 iff ls2_falu1_turn_enabled is subscribed and
+;; switch the turn to FALU2 by subscribing ls2_falu2_turn_enabled.
+;; Or to FALU2 otherwise.
+(define_reservation "ls2_falu"
+ "(ls2_falu1+ls2_falu1_turn+ls2_falu2_turn_enabled)
+ |(ls2_falu1+ls2_falu1_turn)
+ |(ls2_falu2+ls2_falu2_turn+ls2_falu1_turn_enabled)
+ |(ls2_falu2+ls2_falu2_turn)")
+
+;; The following 4 instructions each subscribe one of
+;; ls2_[f]alu{1,2}_turn_enabled units according to this attribute.
+;; These instructions are used in mips.c: sched_ls2_dfa_post_advance_cycle.
+
+(define_attr "ls2_turn_type" "alu1,alu2,falu1,falu2,unknown"
+ (const_string "unknown"))
+
+;; Subscribe ls2_alu1_turn_enabled.
+(define_insn "ls2_alu1_turn_enabled_insn"
+ [(unspec [(const_int 0)] UNSPEC_LOONGSON_ALU1_TURN_ENABLED_INSN)]
+ "TUNE_LOONGSON_2EF"
+ { gcc_unreachable (); }
+ [(set_attr "ls2_turn_type" "alu1")])
+
+(define_insn_reservation "ls2_alu1_turn_enabled" 0
+ (eq_attr "ls2_turn_type" "alu1")
+ "ls2_alu1_turn_enabled")
+
+;; Subscribe ls2_alu2_turn_enabled.
+(define_insn "ls2_alu2_turn_enabled_insn"
+ [(unspec [(const_int 0)] UNSPEC_LOONGSON_ALU2_TURN_ENABLED_INSN)]
+ "TUNE_LOONGSON_2EF"
+ { gcc_unreachable (); }
+ [(set_attr "ls2_turn_type" "alu2")])
+
+(define_insn_reservation "ls2_alu2_turn_enabled" 0
+ (eq_attr "ls2_turn_type" "alu2")
+ "ls2_alu2_turn_enabled")
+
+;; Subscribe ls2_falu1_turn_enabled.
+(define_insn "ls2_falu1_turn_enabled_insn"
+ [(unspec [(const_int 0)] UNSPEC_LOONGSON_FALU1_TURN_ENABLED_INSN)]
+ "TUNE_LOONGSON_2EF"
+ { gcc_unreachable (); }
+ [(set_attr "ls2_turn_type" "falu1")])
+
+(define_insn_reservation "ls2_falu1_turn_enabled" 0
+ (eq_attr "ls2_turn_type" "falu1")
+ "ls2_falu1_turn_enabled")
+
+;; Subscribe ls2_falu2_turn_enabled.
+(define_insn "ls2_falu2_turn_enabled_insn"
+ [(unspec [(const_int 0)] UNSPEC_LOONGSON_FALU2_TURN_ENABLED_INSN)]
+ "TUNE_LOONGSON_2EF"
+ { gcc_unreachable (); }
+ [(set_attr "ls2_turn_type" "falu2")])
+
+(define_insn_reservation "ls2_falu2_turn_enabled" 0
+ (eq_attr "ls2_turn_type" "falu2")
+ "ls2_falu2_turn_enabled")
+
+;; Automaton for memory operations.
+(define_automaton "ls2_mem")
+
+;; Memory unit.
+(define_query_cpu_unit "ls2_mem" "ls2_mem")
+
+;; Reservation for integer instructions.
+(define_insn_reservation "ls2_alu" 2
+ (and (eq_attr "cpu" "loongson_2e,loongson_2f")
+ (eq_attr "type" "arith,condmove,const,logical,mfhilo,move,
+ mthilo,nop,shift,signext,slt"))
+ "ls2_alu")
+
+;; Reservation for branch instructions.
+(define_insn_reservation "ls2_branch" 2
+ (and (eq_attr "cpu" "loongson_2e,loongson_2f")
+ (eq_attr "type" "branch,jump,call,trap"))
+ "ls2_alu1")
+
+;; Reservation for integer multiplication instructions.
+(define_insn_reservation "ls2_imult" 5
+ (and (eq_attr "cpu" "loongson_2e,loongson_2f")
+ (eq_attr "type" "imul,imul3nc"))
+ "ls2_alu2,ls2_alu2_core")
+
+;; Reservation for integer division / remainder instructions.
+;; These instructions use the SRT algorithm and hence take 2-38 cycles.
+(define_insn_reservation "ls2_idiv" 20
+ (and (eq_attr "cpu" "loongson_2e,loongson_2f")
+ (eq_attr "type" "idiv,idiv3"))
+ "ls2_alu2,ls2_alu2_core*18")
+
+;; Reservation for memory load instructions.
+(define_insn_reservation "ls2_load" 5
+ (and (eq_attr "cpu" "loongson_2e,loongson_2f")
+ (eq_attr "type" "load,fpload,mfc,mtc"))
+ "ls2_mem")
+
+(define_insn_reservation "ls2_prefetch" 0
+ (and (eq_attr "cpu" "loongson_2e,loongson_2f")
+ (eq_attr "type" "prefetch,prefetchx"))
+ "ls2_mem")
+
+;; Reservation for memory store instructions.
+;; With stores we assume they don't alias with dependent loads.
+;; Therefore we set the latency to zero.
+(define_insn_reservation "ls2_store" 0
+ (and (eq_attr "cpu" "loongson_2e,loongson_2f")
+ (eq_attr "type" "store,fpstore"))
+ "ls2_mem")
+
+;; Reservation for floating-point instructions of latency 3.
+(define_insn_reservation "ls2_fp3" 3
+ (and (eq_attr "cpu" "loongson_2e,loongson_2f")
+ (eq_attr "type" "fabs,fneg,fcmp,fmove"))
+ "ls2_falu1")
+
+;; Reservation for floating-point instructions of latency 5.
+(define_insn_reservation "ls2_fp5" 5
+ (and (eq_attr "cpu" "loongson_2e,loongson_2f")
+ (eq_attr "type" "fcvt"))
+ "ls2_falu1")
+
+;; Reservation for floating-point instructions that can go
+;; to either of FALU1/2 units.
+(define_insn_reservation "ls2_falu" 7
+ (and (eq_attr "cpu" "loongson_2e,loongson_2f")
+ (eq_attr "type" "fadd,fmul,fmadd"))
+ "ls2_falu")
+
+;; Reservation for floating-point division / remainder instructions.
+;; These instructions use the SRT algorithm and hence take a variable amount
+;; of cycles:
+;; div.s takes 5-11 cycles
+;; div.d takes 5-18 cycles
+(define_insn_reservation "ls2_fdiv" 9
+ (and (eq_attr "cpu" "loongson_2e,loongson_2f")
+ (eq_attr "type" "fdiv"))
+ "ls2_falu2,ls2_falu2_core*7")
+
+;; Reservation for floating-point sqrt instructions.
+;; These instructions use the SRT algorithm and hence take a variable amount
+;; of cycles:
+;; sqrt.s takes 5-17 cycles
+;; sqrt.d takes 5-32 cycles
+(define_insn_reservation "ls2_fsqrt" 15
+ (and (eq_attr "cpu" "loongson_2e,loongson_2f")
+ (eq_attr "type" "fsqrt"))
+ "ls2_falu2,ls2_falu2_core*13")
+
+;; Two consecutive ALU instructions.
+(define_insn_reservation "ls2_multi" 4
+ (and (eq_attr "cpu" "loongson_2e,loongson_2f")
+ (eq_attr "type" "multi"))
+ "(ls2_alu1,ls2_alu2_core)|(ls2_alu2,ls2_alu1_core)")
+
+;; Reservation for everything else. Normally, this reservation
+;; will only be used to handle cases like compiling for non-loongson
+;; CPUs with -mtune=loongson2?.
+;;
+;; This reservation depends upon the fact that DFA will check
+;; reservations in the same order as they appear in the file.
+(define_insn_reservation "ls2_unknown" 1
+ (eq_attr "cpu" "loongson_2e,loongson_2f")
+ "ls2_alu1_core+ls2_alu2_core+ls2_falu1_core+ls2_falu2_core+ls2_mem")
diff --git a/gcc/config/mips/loongson3a.md b/gcc/config/mips/loongson3a.md
new file mode 100644
index 000000000..c584f42f0
--- /dev/null
+++ b/gcc/config/mips/loongson3a.md
@@ -0,0 +1,137 @@
+;; Pipeline model for Loongson-3A cores.
+
+;; Copyright (C) 2011 Free Software Foundation, 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/>.
+
+;; Uncomment the following line to output automata for debugging.
+;; (automata_option "v")
+
+;; Automaton for integer instructions.
+(define_automaton "ls3a_a_alu")
+
+;; Automaton for floating-point instructions.
+(define_automaton "ls3a_a_falu")
+
+;; Automaton for memory operations.
+(define_automaton "ls3a_a_mem")
+
+;; Describe the resources.
+
+(define_cpu_unit "ls3a_alu1" "ls3a_a_alu")
+(define_cpu_unit "ls3a_alu2" "ls3a_a_alu")
+(define_cpu_unit "ls3a_mem" "ls3a_a_mem")
+(define_cpu_unit "ls3a_falu1" "ls3a_a_falu")
+(define_cpu_unit "ls3a_falu2" "ls3a_a_falu")
+
+;; Describe instruction reservations.
+
+(define_insn_reservation "ls3a_arith" 1
+ (and (eq_attr "cpu" "loongson_3a")
+ (eq_attr "type" "arith,clz,const,logical,
+ move,nop,shift,signext,slt"))
+ "ls3a_alu1 | ls3a_alu2")
+
+(define_insn_reservation "ls3a_branch" 1
+ (and (eq_attr "cpu" "loongson_3a")
+ (eq_attr "type" "branch,jump,call,condmove,trap"))
+ "ls3a_alu1")
+
+(define_insn_reservation "ls3a_mfhilo" 1
+ (and (eq_attr "cpu" "loongson_3a")
+ (eq_attr "type" "mfhilo,mthilo"))
+ "ls3a_alu2")
+
+;; Operation imul3nc is fully pipelined.
+(define_insn_reservation "ls3a_imul3nc" 5
+ (and (eq_attr "cpu" "loongson_3a")
+ (eq_attr "type" "imul3nc"))
+ "ls3a_alu2")
+
+(define_insn_reservation "ls3a_imul" 7
+ (and (eq_attr "cpu" "loongson_3a")
+ (eq_attr "type" "imul,imadd"))
+ "ls3a_alu2 * 7")
+
+(define_insn_reservation "ls3a_idiv_si" 12
+ (and (eq_attr "cpu" "loongson_3a")
+ (and (eq_attr "type" "idiv")
+ (eq_attr "mode" "SI")))
+ "ls3a_alu2 * 12")
+
+(define_insn_reservation "ls3a_idiv_di" 25
+ (and (eq_attr "cpu" "loongson_3a")
+ (and (eq_attr "type" "idiv")
+ (eq_attr "mode" "DI")))
+ "ls3a_alu2 * 25")
+
+(define_insn_reservation "ls3a_load" 3
+ (and (eq_attr "cpu" "loongson_3a")
+ (eq_attr "type" "load"))
+ "ls3a_mem")
+
+(define_insn_reservation "ls3a_fpload" 4
+ (and (eq_attr "cpu" "loongson_3a")
+ (eq_attr "type" "load,mfc,mtc"))
+ "ls3a_mem")
+
+(define_insn_reservation "ls3a_prefetch" 0
+ (and (eq_attr "cpu" "loongson_3a")
+ (eq_attr "type" "prefetch,prefetchx"))
+ "ls3a_mem")
+
+(define_insn_reservation "ls3a_store" 0
+ (and (eq_attr "cpu" "loongson_3a")
+ (eq_attr "type" "store,fpstore,fpidxstore"))
+ "ls3a_mem")
+
+;; All the fp operations can be executed in FALU1. Only fp add,
+;; sub, mul, madd can be executed in FALU2. Try FALU2 firstly.
+(define_insn_reservation "ls3a_fadd" 6
+ (and (eq_attr "cpu" "loongson_3a")
+ (eq_attr "type" "fadd,fmul,fmadd"))
+ "ls3a_falu2 | ls3a_falu1")
+
+(define_insn_reservation "ls3a_fcmp" 2
+ (and (eq_attr "cpu" "loongson_3a")
+ (eq_attr "type" "fabs,fcmp,fmove,fneg"))
+ "ls3a_falu1")
+
+(define_insn_reservation "ls3a_fcvt" 4
+ (and (eq_attr "cpu" "loongson_3a")
+ (eq_attr "type" "fcvt"))
+ "ls3a_falu1")
+
+(define_insn_reservation "ls3a_fdiv_sf" 12
+ (and (eq_attr "cpu" "loongson_3a")
+ (and (eq_attr "type" "fdiv,frdiv,fsqrt,frsqrt")
+ (eq_attr "mode" "SF")))
+ "ls3a_falu1 * 12")
+
+(define_insn_reservation "ls3a_fdiv_df" 19
+ (and (eq_attr "cpu" "loongson_3a")
+ (and (eq_attr "type" "fdiv,frdiv,fsqrt,frsqrt")
+ (eq_attr "mode" "DF")))
+ "ls3a_falu1 * 19")
+
+;; Force single-dispatch for unknown or multi.
+(define_insn_reservation "ls3a_unknown" 1
+ (and (eq_attr "cpu" "loongson_3a")
+ (eq_attr "type" "unknown,multi"))
+ "ls3a_alu1 + ls3a_alu2 + ls3a_falu1 + ls3a_falu2 + ls3a_mem")
+
+;; End of DFA-based pipeline description for loongson_3a
diff --git a/gcc/config/mips/mips-dsp.md b/gcc/config/mips/mips-dsp.md
new file mode 100644
index 000000000..4f518cbc6
--- /dev/null
+++ b/gcc/config/mips/mips-dsp.md
@@ -0,0 +1,1198 @@
+;; Copyright (C) 2005, 2006, 2007, 2008, 2010 Free Software Foundation, 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/>.
+
+;; MIPS DSP ASE Revision 0.98 3/24/2005
+(define_c_enum "unspec" [
+ UNSPEC_ADDQ
+ UNSPEC_ADDQ_S
+ UNSPEC_SUBQ
+ UNSPEC_SUBQ_S
+ UNSPEC_ADDSC
+ UNSPEC_ADDWC
+ UNSPEC_MODSUB
+ UNSPEC_RADDU_W_QB
+ UNSPEC_ABSQ_S
+ UNSPEC_PRECRQ_QB_PH
+ UNSPEC_PRECRQ_PH_W
+ UNSPEC_PRECRQ_RS_PH_W
+ UNSPEC_PRECRQU_S_QB_PH
+ UNSPEC_PRECEQ_W_PHL
+ UNSPEC_PRECEQ_W_PHR
+ UNSPEC_PRECEQU_PH_QBL
+ UNSPEC_PRECEQU_PH_QBR
+ UNSPEC_PRECEQU_PH_QBLA
+ UNSPEC_PRECEQU_PH_QBRA
+ UNSPEC_PRECEU_PH_QBL
+ UNSPEC_PRECEU_PH_QBR
+ UNSPEC_PRECEU_PH_QBLA
+ UNSPEC_PRECEU_PH_QBRA
+ UNSPEC_SHLL
+ UNSPEC_SHLL_S
+ UNSPEC_SHRL_QB
+ UNSPEC_SHRA_PH
+ UNSPEC_SHRA_R
+ UNSPEC_MULEU_S_PH_QBL
+ UNSPEC_MULEU_S_PH_QBR
+ UNSPEC_MULQ_RS_PH
+ UNSPEC_MULEQ_S_W_PHL
+ UNSPEC_MULEQ_S_W_PHR
+ UNSPEC_DPAU_H_QBL
+ UNSPEC_DPAU_H_QBR
+ UNSPEC_DPSU_H_QBL
+ UNSPEC_DPSU_H_QBR
+ UNSPEC_DPAQ_S_W_PH
+ UNSPEC_DPSQ_S_W_PH
+ UNSPEC_MULSAQ_S_W_PH
+ UNSPEC_DPAQ_SA_L_W
+ UNSPEC_DPSQ_SA_L_W
+ UNSPEC_MAQ_S_W_PHL
+ UNSPEC_MAQ_S_W_PHR
+ UNSPEC_MAQ_SA_W_PHL
+ UNSPEC_MAQ_SA_W_PHR
+ UNSPEC_BITREV
+ UNSPEC_INSV
+ UNSPEC_REPL_QB
+ UNSPEC_REPL_PH
+ UNSPEC_CMP_EQ
+ UNSPEC_CMP_LT
+ UNSPEC_CMP_LE
+ UNSPEC_CMPGU_EQ_QB
+ UNSPEC_CMPGU_LT_QB
+ UNSPEC_CMPGU_LE_QB
+ UNSPEC_PICK
+ UNSPEC_PACKRL_PH
+ UNSPEC_EXTR_W
+ UNSPEC_EXTR_R_W
+ UNSPEC_EXTR_RS_W
+ UNSPEC_EXTR_S_H
+ UNSPEC_EXTP
+ UNSPEC_EXTPDP
+ UNSPEC_SHILO
+ UNSPEC_MTHLIP
+ UNSPEC_WRDSP
+ UNSPEC_RDDSP
+])
+
+(define_constants
+ [(CCDSP_PO_REGNUM 182)
+ (CCDSP_SC_REGNUM 183)
+ (CCDSP_CA_REGNUM 184)
+ (CCDSP_OU_REGNUM 185)
+ (CCDSP_CC_REGNUM 186)
+ (CCDSP_EF_REGNUM 187)])
+
+;; This mode iterator allows si, v2hi, v4qi for all possible modes in DSP ASE.
+(define_mode_iterator DSP [(SI "ISA_HAS_DSP")
+ (V2HI "ISA_HAS_DSP")
+ (V4QI "ISA_HAS_DSP")])
+
+;; This mode iterator allows v2hi, v4qi for vector/SIMD data.
+(define_mode_iterator DSPV [(V2HI "ISA_HAS_DSP")
+ (V4QI "ISA_HAS_DSP")])
+
+;; This mode iterator allows si, v2hi for Q31 and V2Q15 fixed-point data.
+(define_mode_iterator DSPQ [(SI "ISA_HAS_DSP")
+ (V2HI "ISA_HAS_DSP")])
+
+;; DSP instructions use q for fixed-point data, and u for integer in the infix.
+(define_mode_attr dspfmt1 [(SI "q") (V2HI "q") (V4QI "u")])
+
+;; DSP instructions use nothing for fixed-point data, and u for integer in
+;; the infix.
+(define_mode_attr dspfmt1_1 [(SI "") (V2HI "") (V4QI "u")])
+
+;; DSP instructions use w, ph, qb in the postfix.
+(define_mode_attr dspfmt2 [(SI "w") (V2HI "ph") (V4QI "qb")])
+
+;; DSP shift masks for SI, V2HI, V4QI.
+(define_mode_attr dspshift_mask [(SI "0x1f") (V2HI "0xf") (V4QI "0x7")])
+
+;; MIPS DSP ASE Revision 0.98 3/24/2005
+;; Table 2-1. MIPS DSP ASE Instructions: Arithmetic
+;; ADDQ*
+(define_insn "add<DSPV:mode>3"
+ [(parallel
+ [(set (match_operand:DSPV 0 "register_operand" "=d")
+ (plus:DSPV (match_operand:DSPV 1 "register_operand" "d")
+ (match_operand:DSPV 2 "register_operand" "d")))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2)] UNSPEC_ADDQ))])]
+ "ISA_HAS_DSP"
+ "add<DSPV:dspfmt1>.<DSPV:dspfmt2>\t%0,%1,%2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_add<DSP:dspfmt1>_s_<DSP:dspfmt2>"
+ [(parallel
+ [(set (match_operand:DSP 0 "register_operand" "=d")
+ (unspec:DSP [(match_operand:DSP 1 "register_operand" "d")
+ (match_operand:DSP 2 "register_operand" "d")]
+ UNSPEC_ADDQ_S))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2)] UNSPEC_ADDQ_S))])]
+ "ISA_HAS_DSP"
+ "add<DSP:dspfmt1>_s.<DSP:dspfmt2>\t%0,%1,%2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+;; SUBQ*
+(define_insn "sub<DSPV:mode>3"
+ [(parallel
+ [(set (match_operand:DSPV 0 "register_operand" "=d")
+ (minus:DSPV (match_operand:DSPV 1 "register_operand" "d")
+ (match_operand:DSPV 2 "register_operand" "d")))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2)] UNSPEC_SUBQ))])]
+ "ISA_HAS_DSP"
+ "sub<DSPV:dspfmt1>.<DSPV:dspfmt2>\t%0,%1,%2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_sub<DSP:dspfmt1>_s_<DSP:dspfmt2>"
+ [(parallel
+ [(set (match_operand:DSP 0 "register_operand" "=d")
+ (unspec:DSP [(match_operand:DSP 1 "register_operand" "d")
+ (match_operand:DSP 2 "register_operand" "d")]
+ UNSPEC_SUBQ_S))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2)] UNSPEC_SUBQ_S))])]
+ "ISA_HAS_DSP"
+ "sub<DSP:dspfmt1>_s.<DSP:dspfmt2>\t%0,%1,%2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+;; ADDSC
+(define_insn "mips_addsc"
+ [(parallel
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (unspec:SI [(match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "register_operand" "d")]
+ UNSPEC_ADDSC))
+ (set (reg:CCDSP CCDSP_CA_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2)] UNSPEC_ADDSC))])]
+ "ISA_HAS_DSP"
+ "addsc\t%0,%1,%2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+;; ADDWC
+(define_insn "mips_addwc"
+ [(parallel
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (unspec:SI [(match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "register_operand" "d")
+ (reg:CCDSP CCDSP_CA_REGNUM)]
+ UNSPEC_ADDWC))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2)] UNSPEC_ADDWC))])]
+ "ISA_HAS_DSP"
+ "addwc\t%0,%1,%2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+;; MODSUB
+(define_insn "mips_modsub"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (unspec:SI [(match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "register_operand" "d")]
+ UNSPEC_MODSUB))]
+ "ISA_HAS_DSP"
+ "modsub\t%0,%1,%2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+;; RADDU*
+(define_insn "mips_raddu_w_qb"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (unspec:SI [(match_operand:V4QI 1 "register_operand" "d")]
+ UNSPEC_RADDU_W_QB))]
+ "ISA_HAS_DSP"
+ "raddu.w.qb\t%0,%1"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+;; ABSQ*
+(define_insn "mips_absq_s_<DSPQ:dspfmt2>"
+ [(parallel
+ [(set (match_operand:DSPQ 0 "register_operand" "=d")
+ (unspec:DSPQ [(match_operand:DSPQ 1 "register_operand" "d")]
+ UNSPEC_ABSQ_S))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1)] UNSPEC_ABSQ_S))])]
+ "ISA_HAS_DSP"
+ "absq_s.<DSPQ:dspfmt2>\t%0,%1"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+;; PRECRQ*
+(define_insn "mips_precrq_qb_ph"
+ [(set (match_operand:V4QI 0 "register_operand" "=d")
+ (unspec:V4QI [(match_operand:V2HI 1 "register_operand" "d")
+ (match_operand:V2HI 2 "register_operand" "d")]
+ UNSPEC_PRECRQ_QB_PH))]
+ "ISA_HAS_DSP"
+ "precrq.qb.ph\t%0,%1,%2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_precrq_ph_w"
+ [(set (match_operand:V2HI 0 "register_operand" "=d")
+ (unspec:V2HI [(match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "register_operand" "d")]
+ UNSPEC_PRECRQ_PH_W))]
+ "ISA_HAS_DSP"
+ "precrq.ph.w\t%0,%1,%2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_precrq_rs_ph_w"
+ [(parallel
+ [(set (match_operand:V2HI 0 "register_operand" "=d")
+ (unspec:V2HI [(match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "register_operand" "d")]
+ UNSPEC_PRECRQ_RS_PH_W))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2)]
+ UNSPEC_PRECRQ_RS_PH_W))])]
+ "ISA_HAS_DSP"
+ "precrq_rs.ph.w\t%0,%1,%2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+;; PRECRQU*
+(define_insn "mips_precrqu_s_qb_ph"
+ [(parallel
+ [(set (match_operand:V4QI 0 "register_operand" "=d")
+ (unspec:V4QI [(match_operand:V2HI 1 "register_operand" "d")
+ (match_operand:V2HI 2 "register_operand" "d")]
+ UNSPEC_PRECRQU_S_QB_PH))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2)]
+ UNSPEC_PRECRQU_S_QB_PH))])]
+ "ISA_HAS_DSP"
+ "precrqu_s.qb.ph\t%0,%1,%2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+;; PRECEQ*
+(define_insn "mips_preceq_w_phl"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (unspec:SI [(match_operand:V2HI 1 "register_operand" "d")]
+ UNSPEC_PRECEQ_W_PHL))]
+ "ISA_HAS_DSP"
+ "preceq.w.phl\t%0,%1"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_preceq_w_phr"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (unspec:SI [(match_operand:V2HI 1 "register_operand" "d")]
+ UNSPEC_PRECEQ_W_PHR))]
+ "ISA_HAS_DSP"
+ "preceq.w.phr\t%0,%1"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+;; PRECEQU*
+(define_insn "mips_precequ_ph_qbl"
+ [(set (match_operand:V2HI 0 "register_operand" "=d")
+ (unspec:V2HI [(match_operand:V4QI 1 "register_operand" "d")]
+ UNSPEC_PRECEQU_PH_QBL))]
+ "ISA_HAS_DSP"
+ "precequ.ph.qbl\t%0,%1"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_precequ_ph_qbr"
+ [(set (match_operand:V2HI 0 "register_operand" "=d")
+ (unspec:V2HI [(match_operand:V4QI 1 "register_operand" "d")]
+ UNSPEC_PRECEQU_PH_QBR))]
+ "ISA_HAS_DSP"
+ "precequ.ph.qbr\t%0,%1"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_precequ_ph_qbla"
+ [(set (match_operand:V2HI 0 "register_operand" "=d")
+ (unspec:V2HI [(match_operand:V4QI 1 "register_operand" "d")]
+ UNSPEC_PRECEQU_PH_QBLA))]
+ "ISA_HAS_DSP"
+ "precequ.ph.qbla\t%0,%1"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_precequ_ph_qbra"
+ [(set (match_operand:V2HI 0 "register_operand" "=d")
+ (unspec:V2HI [(match_operand:V4QI 1 "register_operand" "d")]
+ UNSPEC_PRECEQU_PH_QBRA))]
+ "ISA_HAS_DSP"
+ "precequ.ph.qbra\t%0,%1"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+;; PRECEU*
+(define_insn "mips_preceu_ph_qbl"
+ [(set (match_operand:V2HI 0 "register_operand" "=d")
+ (unspec:V2HI [(match_operand:V4QI 1 "register_operand" "d")]
+ UNSPEC_PRECEU_PH_QBL))]
+ "ISA_HAS_DSP"
+ "preceu.ph.qbl\t%0,%1"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_preceu_ph_qbr"
+ [(set (match_operand:V2HI 0 "register_operand" "=d")
+ (unspec:V2HI [(match_operand:V4QI 1 "register_operand" "d")]
+ UNSPEC_PRECEU_PH_QBR))]
+ "ISA_HAS_DSP"
+ "preceu.ph.qbr\t%0,%1"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_preceu_ph_qbla"
+ [(set (match_operand:V2HI 0 "register_operand" "=d")
+ (unspec:V2HI [(match_operand:V4QI 1 "register_operand" "d")]
+ UNSPEC_PRECEU_PH_QBLA))]
+ "ISA_HAS_DSP"
+ "preceu.ph.qbla\t%0,%1"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_preceu_ph_qbra"
+ [(set (match_operand:V2HI 0 "register_operand" "=d")
+ (unspec:V2HI [(match_operand:V4QI 1 "register_operand" "d")]
+ UNSPEC_PRECEU_PH_QBRA))]
+ "ISA_HAS_DSP"
+ "preceu.ph.qbra\t%0,%1"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+;; Table 2-2. MIPS DSP ASE Instructions: Shift
+;; SHLL*
+(define_insn "mips_shll_<DSPV:dspfmt2>"
+ [(parallel
+ [(set (match_operand:DSPV 0 "register_operand" "=d,d")
+ (unspec:DSPV [(match_operand:DSPV 1 "register_operand" "d,d")
+ (match_operand:SI 2 "arith_operand" "I,d")]
+ UNSPEC_SHLL))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2)] UNSPEC_SHLL))])]
+ "ISA_HAS_DSP"
+{
+ if (which_alternative == 0)
+ {
+ if (INTVAL (operands[2])
+ & ~(unsigned HOST_WIDE_INT) <DSPV:dspshift_mask>)
+ operands[2] = GEN_INT (INTVAL (operands[2]) & <DSPV:dspshift_mask>);
+ return "shll.<DSPV:dspfmt2>\t%0,%1,%2";
+ }
+ return "shllv.<DSPV:dspfmt2>\t%0,%1,%2";
+}
+ [(set_attr "type" "shift")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_shll_s_<DSPQ:dspfmt2>"
+ [(parallel
+ [(set (match_operand:DSPQ 0 "register_operand" "=d,d")
+ (unspec:DSPQ [(match_operand:DSPQ 1 "register_operand" "d,d")
+ (match_operand:SI 2 "arith_operand" "I,d")]
+ UNSPEC_SHLL_S))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2)] UNSPEC_SHLL_S))])]
+ "ISA_HAS_DSP"
+{
+ if (which_alternative == 0)
+ {
+ if (INTVAL (operands[2])
+ & ~(unsigned HOST_WIDE_INT) <DSPQ:dspshift_mask>)
+ operands[2] = GEN_INT (INTVAL (operands[2]) & <DSPQ:dspshift_mask>);
+ return "shll_s.<DSPQ:dspfmt2>\t%0,%1,%2";
+ }
+ return "shllv_s.<DSPQ:dspfmt2>\t%0,%1,%2";
+}
+ [(set_attr "type" "shift")
+ (set_attr "mode" "SI")])
+
+;; SHRL*
+(define_insn "mips_shrl_qb"
+ [(set (match_operand:V4QI 0 "register_operand" "=d,d")
+ (unspec:V4QI [(match_operand:V4QI 1 "register_operand" "d,d")
+ (match_operand:SI 2 "arith_operand" "I,d")]
+ UNSPEC_SHRL_QB))]
+ "ISA_HAS_DSP"
+{
+ if (which_alternative == 0)
+ {
+ if (INTVAL (operands[2]) & ~(unsigned HOST_WIDE_INT) 0x7)
+ operands[2] = GEN_INT (INTVAL (operands[2]) & 0x7);
+ return "shrl.qb\t%0,%1,%2";
+ }
+ return "shrlv.qb\t%0,%1,%2";
+}
+ [(set_attr "type" "shift")
+ (set_attr "mode" "SI")])
+
+;; SHRA*
+(define_insn "mips_shra_ph"
+ [(set (match_operand:V2HI 0 "register_operand" "=d,d")
+ (unspec:V2HI [(match_operand:V2HI 1 "register_operand" "d,d")
+ (match_operand:SI 2 "arith_operand" "I,d")]
+ UNSPEC_SHRA_PH))]
+ "ISA_HAS_DSP"
+{
+ if (which_alternative == 0)
+ {
+ if (INTVAL (operands[2]) & ~(unsigned HOST_WIDE_INT) 0xf)
+ operands[2] = GEN_INT (INTVAL (operands[2]) & 0xf);
+ return "shra.ph\t%0,%1,%2";
+ }
+ return "shrav.ph\t%0,%1,%2";
+}
+ [(set_attr "type" "shift")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_shra_r_<DSPQ:dspfmt2>"
+ [(set (match_operand:DSPQ 0 "register_operand" "=d,d")
+ (unspec:DSPQ [(match_operand:DSPQ 1 "register_operand" "d,d")
+ (match_operand:SI 2 "arith_operand" "I,d")]
+ UNSPEC_SHRA_R))]
+ "ISA_HAS_DSP"
+{
+ if (which_alternative == 0)
+ {
+ if (INTVAL (operands[2])
+ & ~(unsigned HOST_WIDE_INT) <DSPQ:dspshift_mask>)
+ operands[2] = GEN_INT (INTVAL (operands[2]) & <DSPQ:dspshift_mask>);
+ return "shra_r.<DSPQ:dspfmt2>\t%0,%1,%2";
+ }
+ return "shrav_r.<DSPQ:dspfmt2>\t%0,%1,%2";
+}
+ [(set_attr "type" "shift")
+ (set_attr "mode" "SI")])
+
+;; Table 2-3. MIPS DSP ASE Instructions: Multiply
+;; MULEU*
+(define_insn "mips_muleu_s_ph_qbl"
+ [(parallel
+ [(set (match_operand:V2HI 0 "register_operand" "=d")
+ (unspec:V2HI [(match_operand:V4QI 1 "register_operand" "d")
+ (match_operand:V2HI 2 "register_operand" "d")]
+ UNSPEC_MULEU_S_PH_QBL))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2)] UNSPEC_MULEU_S_PH_QBL))
+ (clobber (match_scratch:DI 3 "=x"))])]
+ "ISA_HAS_DSP"
+ "muleu_s.ph.qbl\t%0,%1,%2"
+ [(set_attr "type" "imul3")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_muleu_s_ph_qbr"
+ [(parallel
+ [(set (match_operand:V2HI 0 "register_operand" "=d")
+ (unspec:V2HI [(match_operand:V4QI 1 "register_operand" "d")
+ (match_operand:V2HI 2 "register_operand" "d")]
+ UNSPEC_MULEU_S_PH_QBR))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2)] UNSPEC_MULEU_S_PH_QBR))
+ (clobber (match_scratch:DI 3 "=x"))])]
+ "ISA_HAS_DSP"
+ "muleu_s.ph.qbr\t%0,%1,%2"
+ [(set_attr "type" "imul3")
+ (set_attr "mode" "SI")])
+
+;; MULQ*
+(define_insn "mips_mulq_rs_ph"
+ [(parallel
+ [(set (match_operand:V2HI 0 "register_operand" "=d")
+ (unspec:V2HI [(match_operand:V2HI 1 "register_operand" "d")
+ (match_operand:V2HI 2 "register_operand" "d")]
+ UNSPEC_MULQ_RS_PH))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2)] UNSPEC_MULQ_RS_PH))
+ (clobber (match_scratch:DI 3 "=x"))])]
+ "ISA_HAS_DSP"
+ "mulq_rs.ph\t%0,%1,%2"
+ [(set_attr "type" "imul3")
+ (set_attr "mode" "SI")])
+
+;; MULEQ*
+(define_insn "mips_muleq_s_w_phl"
+ [(parallel
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (unspec:SI [(match_operand:V2HI 1 "register_operand" "d")
+ (match_operand:V2HI 2 "register_operand" "d")]
+ UNSPEC_MULEQ_S_W_PHL))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2)] UNSPEC_MULEQ_S_W_PHL))
+ (clobber (match_scratch:DI 3 "=x"))])]
+ "ISA_HAS_DSP"
+ "muleq_s.w.phl\t%0,%1,%2"
+ [(set_attr "type" "imul3")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_muleq_s_w_phr"
+ [(parallel
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (unspec:SI [(match_operand:V2HI 1 "register_operand" "d")
+ (match_operand:V2HI 2 "register_operand" "d")]
+ UNSPEC_MULEQ_S_W_PHR))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2)] UNSPEC_MULEQ_S_W_PHR))
+ (clobber (match_scratch:DI 3 "=x"))])]
+ "ISA_HAS_DSP"
+ "muleq_s.w.phr\t%0,%1,%2"
+ [(set_attr "type" "imul3")
+ (set_attr "mode" "SI")])
+
+;; DPAU*
+(define_insn "mips_dpau_h_qbl"
+ [(set (match_operand:DI 0 "register_operand" "=a")
+ (unspec:DI [(match_operand:DI 1 "register_operand" "0")
+ (match_operand:V4QI 2 "register_operand" "d")
+ (match_operand:V4QI 3 "register_operand" "d")]
+ UNSPEC_DPAU_H_QBL))]
+ "ISA_HAS_DSP && !TARGET_64BIT"
+ "dpau.h.qbl\t%q0,%2,%3"
+ [(set_attr "type" "imadd")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_dpau_h_qbr"
+ [(set (match_operand:DI 0 "register_operand" "=a")
+ (unspec:DI [(match_operand:DI 1 "register_operand" "0")
+ (match_operand:V4QI 2 "register_operand" "d")
+ (match_operand:V4QI 3 "register_operand" "d")]
+ UNSPEC_DPAU_H_QBR))]
+ "ISA_HAS_DSP && !TARGET_64BIT"
+ "dpau.h.qbr\t%q0,%2,%3"
+ [(set_attr "type" "imadd")
+ (set_attr "mode" "SI")])
+
+;; DPSU*
+(define_insn "mips_dpsu_h_qbl"
+ [(set (match_operand:DI 0 "register_operand" "=a")
+ (unspec:DI [(match_operand:DI 1 "register_operand" "0")
+ (match_operand:V4QI 2 "register_operand" "d")
+ (match_operand:V4QI 3 "register_operand" "d")]
+ UNSPEC_DPSU_H_QBL))]
+ "ISA_HAS_DSP && !TARGET_64BIT"
+ "dpsu.h.qbl\t%q0,%2,%3"
+ [(set_attr "type" "imadd")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_dpsu_h_qbr"
+ [(set (match_operand:DI 0 "register_operand" "=a")
+ (unspec:DI [(match_operand:DI 1 "register_operand" "0")
+ (match_operand:V4QI 2 "register_operand" "d")
+ (match_operand:V4QI 3 "register_operand" "d")]
+ UNSPEC_DPSU_H_QBR))]
+ "ISA_HAS_DSP && !TARGET_64BIT"
+ "dpsu.h.qbr\t%q0,%2,%3"
+ [(set_attr "type" "imadd")
+ (set_attr "mode" "SI")])
+
+;; DPAQ*
+(define_insn "mips_dpaq_s_w_ph"
+ [(parallel
+ [(set (match_operand:DI 0 "register_operand" "=a")
+ (unspec:DI [(match_operand:DI 1 "register_operand" "0")
+ (match_operand:V2HI 2 "register_operand" "d")
+ (match_operand:V2HI 3 "register_operand" "d")]
+ UNSPEC_DPAQ_S_W_PH))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2) (match_dup 3)]
+ UNSPEC_DPAQ_S_W_PH))])]
+ "ISA_HAS_DSP && !TARGET_64BIT"
+ "dpaq_s.w.ph\t%q0,%2,%3"
+ [(set_attr "type" "imadd")
+ (set_attr "mode" "SI")])
+
+;; DPSQ*
+(define_insn "mips_dpsq_s_w_ph"
+ [(parallel
+ [(set (match_operand:DI 0 "register_operand" "=a")
+ (unspec:DI [(match_operand:DI 1 "register_operand" "0")
+ (match_operand:V2HI 2 "register_operand" "d")
+ (match_operand:V2HI 3 "register_operand" "d")]
+ UNSPEC_DPSQ_S_W_PH))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2) (match_dup 3)]
+ UNSPEC_DPSQ_S_W_PH))])]
+ "ISA_HAS_DSP && !TARGET_64BIT"
+ "dpsq_s.w.ph\t%q0,%2,%3"
+ [(set_attr "type" "imadd")
+ (set_attr "mode" "SI")])
+
+;; MULSAQ*
+(define_insn "mips_mulsaq_s_w_ph"
+ [(parallel
+ [(set (match_operand:DI 0 "register_operand" "=a")
+ (unspec:DI [(match_operand:DI 1 "register_operand" "0")
+ (match_operand:V2HI 2 "register_operand" "d")
+ (match_operand:V2HI 3 "register_operand" "d")]
+ UNSPEC_MULSAQ_S_W_PH))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2) (match_dup 3)]
+ UNSPEC_MULSAQ_S_W_PH))])]
+ "ISA_HAS_DSP && !TARGET_64BIT"
+ "mulsaq_s.w.ph\t%q0,%2,%3"
+ [(set_attr "type" "imadd")
+ (set_attr "mode" "SI")])
+
+;; DPAQ*
+(define_insn "mips_dpaq_sa_l_w"
+ [(parallel
+ [(set (match_operand:DI 0 "register_operand" "=a")
+ (unspec:DI [(match_operand:DI 1 "register_operand" "0")
+ (match_operand:SI 2 "register_operand" "d")
+ (match_operand:SI 3 "register_operand" "d")]
+ UNSPEC_DPAQ_SA_L_W))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2) (match_dup 3)]
+ UNSPEC_DPAQ_SA_L_W))])]
+ "ISA_HAS_DSP && !TARGET_64BIT"
+ "dpaq_sa.l.w\t%q0,%2,%3"
+ [(set_attr "type" "imadd")
+ (set_attr "mode" "SI")])
+
+;; DPSQ*
+(define_insn "mips_dpsq_sa_l_w"
+ [(parallel
+ [(set (match_operand:DI 0 "register_operand" "=a")
+ (unspec:DI [(match_operand:DI 1 "register_operand" "0")
+ (match_operand:SI 2 "register_operand" "d")
+ (match_operand:SI 3 "register_operand" "d")]
+ UNSPEC_DPSQ_SA_L_W))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2) (match_dup 3)]
+ UNSPEC_DPSQ_SA_L_W))])]
+ "ISA_HAS_DSP && !TARGET_64BIT"
+ "dpsq_sa.l.w\t%q0,%2,%3"
+ [(set_attr "type" "imadd")
+ (set_attr "mode" "SI")])
+
+;; MAQ*
+(define_insn "mips_maq_s_w_phl"
+ [(parallel
+ [(set (match_operand:DI 0 "register_operand" "=a")
+ (unspec:DI [(match_operand:DI 1 "register_operand" "0")
+ (match_operand:V2HI 2 "register_operand" "d")
+ (match_operand:V2HI 3 "register_operand" "d")]
+ UNSPEC_MAQ_S_W_PHL))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2) (match_dup 3)]
+ UNSPEC_MAQ_S_W_PHL))])]
+ "ISA_HAS_DSP && !TARGET_64BIT"
+ "maq_s.w.phl\t%q0,%2,%3"
+ [(set_attr "type" "imadd")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_maq_s_w_phr"
+ [(parallel
+ [(set (match_operand:DI 0 "register_operand" "=a")
+ (unspec:DI [(match_operand:DI 1 "register_operand" "0")
+ (match_operand:V2HI 2 "register_operand" "d")
+ (match_operand:V2HI 3 "register_operand" "d")]
+ UNSPEC_MAQ_S_W_PHR))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2) (match_dup 3)]
+ UNSPEC_MAQ_S_W_PHR))])]
+ "ISA_HAS_DSP && !TARGET_64BIT"
+ "maq_s.w.phr\t%q0,%2,%3"
+ [(set_attr "type" "imadd")
+ (set_attr "mode" "SI")])
+
+;; MAQ_SA*
+(define_insn "mips_maq_sa_w_phl"
+ [(parallel
+ [(set (match_operand:DI 0 "register_operand" "=a")
+ (unspec:DI [(match_operand:DI 1 "register_operand" "0")
+ (match_operand:V2HI 2 "register_operand" "d")
+ (match_operand:V2HI 3 "register_operand" "d")]
+ UNSPEC_MAQ_SA_W_PHL))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2) (match_dup 3)]
+ UNSPEC_MAQ_SA_W_PHL))])]
+ "ISA_HAS_DSP && !TARGET_64BIT"
+ "maq_sa.w.phl\t%q0,%2,%3"
+ [(set_attr "type" "imadd")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_maq_sa_w_phr"
+ [(parallel
+ [(set (match_operand:DI 0 "register_operand" "=a")
+ (unspec:DI [(match_operand:DI 1 "register_operand" "0")
+ (match_operand:V2HI 2 "register_operand" "d")
+ (match_operand:V2HI 3 "register_operand" "d")]
+ UNSPEC_MAQ_SA_W_PHR))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2) (match_dup 3)]
+ UNSPEC_MAQ_SA_W_PHR))])]
+ "ISA_HAS_DSP && !TARGET_64BIT"
+ "maq_sa.w.phr\t%q0,%2,%3"
+ [(set_attr "type" "imadd")
+ (set_attr "mode" "SI")])
+
+;; Table 2-4. MIPS DSP ASE Instructions: General Bit/Manipulation
+;; BITREV
+(define_insn "mips_bitrev"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (unspec:SI [(match_operand:SI 1 "register_operand" "d")]
+ UNSPEC_BITREV))]
+ "ISA_HAS_DSP"
+ "bitrev\t%0,%1"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+;; INSV
+(define_insn "mips_insv"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (unspec:SI [(match_operand:SI 1 "register_operand" "0")
+ (match_operand:SI 2 "register_operand" "d")
+ (reg:CCDSP CCDSP_SC_REGNUM)
+ (reg:CCDSP CCDSP_PO_REGNUM)]
+ UNSPEC_INSV))]
+ "ISA_HAS_DSP"
+ "insv\t%0,%2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+;; REPL*
+(define_insn "mips_repl_qb"
+ [(set (match_operand:V4QI 0 "register_operand" "=d,d")
+ (unspec:V4QI [(match_operand:SI 1 "arith_operand" "I,d")]
+ UNSPEC_REPL_QB))]
+ "ISA_HAS_DSP"
+{
+ if (which_alternative == 0)
+ {
+ if (INTVAL (operands[1]) & ~(unsigned HOST_WIDE_INT) 0xff)
+ operands[1] = GEN_INT (INTVAL (operands[1]) & 0xff);
+ return "repl.qb\t%0,%1";
+ }
+ return "replv.qb\t%0,%1";
+}
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_repl_ph"
+ [(set (match_operand:V2HI 0 "register_operand" "=d,d")
+ (unspec:V2HI [(match_operand:SI 1 "reg_imm10_operand" "YB,d")]
+ UNSPEC_REPL_PH))]
+ "ISA_HAS_DSP"
+ "@
+ repl.ph\t%0,%1
+ replv.ph\t%0,%1"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+;; Table 2-5. MIPS DSP ASE Instructions: Compare-Pick
+;; CMPU.* CMP.*
+(define_insn "mips_cmp<DSPV:dspfmt1_1>_eq_<DSPV:dspfmt2>"
+ [(set (reg:CCDSP CCDSP_CC_REGNUM)
+ (unspec:CCDSP [(match_operand:DSPV 0 "register_operand" "d")
+ (match_operand:DSPV 1 "register_operand" "d")
+ (reg:CCDSP CCDSP_CC_REGNUM)]
+ UNSPEC_CMP_EQ))]
+ "ISA_HAS_DSP"
+ "cmp<DSPV:dspfmt1_1>.eq.<DSPV:dspfmt2>\t%0,%1"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_cmp<DSPV:dspfmt1_1>_lt_<DSPV:dspfmt2>"
+ [(set (reg:CCDSP CCDSP_CC_REGNUM)
+ (unspec:CCDSP [(match_operand:DSPV 0 "register_operand" "d")
+ (match_operand:DSPV 1 "register_operand" "d")
+ (reg:CCDSP CCDSP_CC_REGNUM)]
+ UNSPEC_CMP_LT))]
+ "ISA_HAS_DSP"
+ "cmp<DSPV:dspfmt1_1>.lt.<DSPV:dspfmt2>\t%0,%1"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_cmp<DSPV:dspfmt1_1>_le_<DSPV:dspfmt2>"
+ [(set (reg:CCDSP CCDSP_CC_REGNUM)
+ (unspec:CCDSP [(match_operand:DSPV 0 "register_operand" "d")
+ (match_operand:DSPV 1 "register_operand" "d")
+ (reg:CCDSP CCDSP_CC_REGNUM)]
+ UNSPEC_CMP_LE))]
+ "ISA_HAS_DSP"
+ "cmp<DSPV:dspfmt1_1>.le.<DSPV:dspfmt2>\t%0,%1"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_cmpgu_eq_qb"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (unspec:SI [(match_operand:V4QI 1 "register_operand" "d")
+ (match_operand:V4QI 2 "register_operand" "d")]
+ UNSPEC_CMPGU_EQ_QB))]
+ "ISA_HAS_DSP"
+ "cmpgu.eq.qb\t%0,%1,%2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_cmpgu_lt_qb"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (unspec:SI [(match_operand:V4QI 1 "register_operand" "d")
+ (match_operand:V4QI 2 "register_operand" "d")]
+ UNSPEC_CMPGU_LT_QB))]
+ "ISA_HAS_DSP"
+ "cmpgu.lt.qb\t%0,%1,%2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_cmpgu_le_qb"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (unspec:SI [(match_operand:V4QI 1 "register_operand" "d")
+ (match_operand:V4QI 2 "register_operand" "d")]
+ UNSPEC_CMPGU_LE_QB))]
+ "ISA_HAS_DSP"
+ "cmpgu.le.qb\t%0,%1,%2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+;; PICK*
+(define_insn "mips_pick_<DSPV:dspfmt2>"
+ [(set (match_operand:DSPV 0 "register_operand" "=d")
+ (unspec:DSPV [(match_operand:DSPV 1 "register_operand" "d")
+ (match_operand:DSPV 2 "register_operand" "d")
+ (reg:CCDSP CCDSP_CC_REGNUM)]
+ UNSPEC_PICK))]
+ "ISA_HAS_DSP"
+ "pick.<DSPV:dspfmt2>\t%0,%1,%2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+;; PACKRL*
+(define_insn "mips_packrl_ph"
+ [(set (match_operand:V2HI 0 "register_operand" "=d")
+ (unspec:V2HI [(match_operand:V2HI 1 "register_operand" "d")
+ (match_operand:V2HI 2 "register_operand" "d")]
+ UNSPEC_PACKRL_PH))]
+ "ISA_HAS_DSP"
+ "packrl.ph\t%0,%1,%2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+;; Table 2-6. MIPS DSP ASE Instructions: Accumulator and DSPControl Access
+;; EXTR*
+(define_insn "mips_extr_w"
+ [(parallel
+ [(set (match_operand:SI 0 "register_operand" "=d,d")
+ (unspec:SI [(match_operand:DI 1 "register_operand" "a,a")
+ (match_operand:SI 2 "arith_operand" "I,d")]
+ UNSPEC_EXTR_W))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2)] UNSPEC_EXTR_W))])]
+ "ISA_HAS_DSP && !TARGET_64BIT"
+{
+ if (which_alternative == 0)
+ {
+ if (INTVAL (operands[2]) & ~(unsigned HOST_WIDE_INT) 0x1f)
+ operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);
+ return "extr.w\t%0,%q1,%2";
+ }
+ return "extrv.w\t%0,%q1,%2";
+}
+ [(set_attr "type" "mfhilo")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_extr_r_w"
+ [(parallel
+ [(set (match_operand:SI 0 "register_operand" "=d,d")
+ (unspec:SI [(match_operand:DI 1 "register_operand" "a,a")
+ (match_operand:SI 2 "arith_operand" "I,d")]
+ UNSPEC_EXTR_R_W))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2)] UNSPEC_EXTR_R_W))])]
+ "ISA_HAS_DSP && !TARGET_64BIT"
+{
+ if (which_alternative == 0)
+ {
+ if (INTVAL (operands[2]) & ~(unsigned HOST_WIDE_INT) 0x1f)
+ operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);
+ return "extr_r.w\t%0,%q1,%2";
+ }
+ return "extrv_r.w\t%0,%q1,%2";
+}
+ [(set_attr "type" "mfhilo")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_extr_rs_w"
+ [(parallel
+ [(set (match_operand:SI 0 "register_operand" "=d,d")
+ (unspec:SI [(match_operand:DI 1 "register_operand" "a,a")
+ (match_operand:SI 2 "arith_operand" "I,d")]
+ UNSPEC_EXTR_RS_W))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2)] UNSPEC_EXTR_RS_W))])]
+ "ISA_HAS_DSP && !TARGET_64BIT"
+{
+ if (which_alternative == 0)
+ {
+ if (INTVAL (operands[2]) & ~(unsigned HOST_WIDE_INT) 0x1f)
+ operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);
+ return "extr_rs.w\t%0,%q1,%2";
+ }
+ return "extrv_rs.w\t%0,%q1,%2";
+}
+ [(set_attr "type" "mfhilo")
+ (set_attr "mode" "SI")])
+
+;; EXTR*_S.H
+(define_insn "mips_extr_s_h"
+ [(parallel
+ [(set (match_operand:SI 0 "register_operand" "=d,d")
+ (unspec:SI [(match_operand:DI 1 "register_operand" "a,a")
+ (match_operand:SI 2 "arith_operand" "I,d")]
+ UNSPEC_EXTR_S_H))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2)] UNSPEC_EXTR_S_H))])]
+ "ISA_HAS_DSP && !TARGET_64BIT"
+{
+ if (which_alternative == 0)
+ {
+ if (INTVAL (operands[2]) & ~(unsigned HOST_WIDE_INT) 0x1f)
+ operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);
+ return "extr_s.h\t%0,%q1,%2";
+ }
+ return "extrv_s.h\t%0,%q1,%2";
+}
+ [(set_attr "type" "mfhilo")
+ (set_attr "mode" "SI")])
+
+;; EXTP*
+(define_insn "mips_extp"
+ [(parallel
+ [(set (match_operand:SI 0 "register_operand" "=d,d")
+ (unspec:SI [(match_operand:DI 1 "register_operand" "a,a")
+ (match_operand:SI 2 "arith_operand" "I,d")
+ (reg:CCDSP CCDSP_PO_REGNUM)]
+ UNSPEC_EXTP))
+ (set (reg:CCDSP CCDSP_EF_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2)] UNSPEC_EXTP))])]
+ "ISA_HAS_DSP && !TARGET_64BIT"
+{
+ if (which_alternative == 0)
+ {
+ if (INTVAL (operands[2]) & ~(unsigned HOST_WIDE_INT) 0x1f)
+ operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);
+ return "extp\t%0,%q1,%2";
+ }
+ return "extpv\t%0,%q1,%2";
+}
+ [(set_attr "type" "mfhilo")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_extpdp"
+ [(parallel
+ [(set (match_operand:SI 0 "register_operand" "=d,d")
+ (unspec:SI [(match_operand:DI 1 "register_operand" "a,a")
+ (match_operand:SI 2 "arith_operand" "I,d")
+ (reg:CCDSP CCDSP_PO_REGNUM)]
+ UNSPEC_EXTPDP))
+ (set (reg:CCDSP CCDSP_PO_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2)
+ (reg:CCDSP CCDSP_PO_REGNUM)] UNSPEC_EXTPDP))
+ (set (reg:CCDSP CCDSP_EF_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2)] UNSPEC_EXTPDP))])]
+ "ISA_HAS_DSP && !TARGET_64BIT"
+{
+ if (which_alternative == 0)
+ {
+ if (INTVAL (operands[2]) & ~(unsigned HOST_WIDE_INT) 0x1f)
+ operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);
+ return "extpdp\t%0,%q1,%2";
+ }
+ return "extpdpv\t%0,%q1,%2";
+}
+ [(set_attr "type" "mfhilo")
+ (set_attr "mode" "SI")])
+
+;; SHILO*
+(define_insn "mips_shilo"
+ [(set (match_operand:DI 0 "register_operand" "=a,a")
+ (unspec:DI [(match_operand:DI 1 "register_operand" "0,0")
+ (match_operand:SI 2 "arith_operand" "I,d")]
+ UNSPEC_SHILO))]
+ "ISA_HAS_DSP && !TARGET_64BIT"
+{
+ if (which_alternative == 0)
+ {
+ if (INTVAL (operands[2]) < -32 || INTVAL (operands[2]) > 31)
+ operands[2] = GEN_INT (INTVAL (operands[2]) & 0x3f);
+ return "shilo\t%q0,%2";
+ }
+ return "shilov\t%q0,%2";
+}
+ [(set_attr "type" "mfhilo")
+ (set_attr "mode" "SI")])
+
+;; MTHLIP*
+(define_insn "mips_mthlip"
+ [(parallel
+ [(set (match_operand:DI 0 "register_operand" "=a")
+ (unspec:DI [(match_operand:DI 1 "register_operand" "0")
+ (match_operand:SI 2 "register_operand" "d")
+ (reg:CCDSP CCDSP_PO_REGNUM)]
+ UNSPEC_MTHLIP))
+ (set (reg:CCDSP CCDSP_PO_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2)
+ (reg:CCDSP CCDSP_PO_REGNUM)] UNSPEC_MTHLIP))])]
+ "ISA_HAS_DSP && !TARGET_64BIT"
+ "mthlip\t%2,%q0"
+ [(set_attr "type" "mfhilo")
+ (set_attr "mode" "SI")])
+
+;; WRDSP
+(define_insn "mips_wrdsp"
+ [(parallel
+ [(set (reg:CCDSP CCDSP_PO_REGNUM)
+ (unspec:CCDSP [(match_operand:SI 0 "register_operand" "d")
+ (match_operand:SI 1 "const_uimm6_operand" "YA")]
+ UNSPEC_WRDSP))
+ (set (reg:CCDSP CCDSP_SC_REGNUM)
+ (unspec:CCDSP [(match_dup 0) (match_dup 1)] UNSPEC_WRDSP))
+ (set (reg:CCDSP CCDSP_CA_REGNUM)
+ (unspec:CCDSP [(match_dup 0) (match_dup 1)] UNSPEC_WRDSP))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 0) (match_dup 1)] UNSPEC_WRDSP))
+ (set (reg:CCDSP CCDSP_CC_REGNUM)
+ (unspec:CCDSP [(match_dup 0) (match_dup 1)] UNSPEC_WRDSP))
+ (set (reg:CCDSP CCDSP_EF_REGNUM)
+ (unspec:CCDSP [(match_dup 0) (match_dup 1)] UNSPEC_WRDSP))])]
+ "ISA_HAS_DSP"
+ "wrdsp\t%0,%1"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+;; RDDSP
+(define_insn "mips_rddsp"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (unspec:SI [(match_operand:SI 1 "const_uimm6_operand" "YA")
+ (reg:CCDSP CCDSP_PO_REGNUM)
+ (reg:CCDSP CCDSP_SC_REGNUM)
+ (reg:CCDSP CCDSP_CA_REGNUM)
+ (reg:CCDSP CCDSP_OU_REGNUM)
+ (reg:CCDSP CCDSP_CC_REGNUM)
+ (reg:CCDSP CCDSP_EF_REGNUM)]
+ UNSPEC_RDDSP))]
+ "ISA_HAS_DSP"
+ "rddsp\t%0,%1"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+;; Table 2-7. MIPS DSP ASE Instructions: Indexed-Load
+;; L*X
+(define_expand "mips_lbux"
+ [(match_operand:SI 0 "register_operand")
+ (match_operand 1 "pmode_register_operand")
+ (match_operand:SI 2 "register_operand")]
+ "ISA_HAS_DSP"
+{
+ operands[2] = convert_to_mode (Pmode, operands[2], false);
+ if (Pmode == SImode)
+ emit_insn (gen_mips_lbux_si (operands[0], operands[1], operands[2]));
+ else
+ emit_insn (gen_mips_lbux_di (operands[0], operands[1], operands[2]));
+ DONE;
+})
+
+(define_insn "mips_lbux_<mode>"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (zero_extend:SI
+ (mem:QI (plus:P (match_operand:P 1 "register_operand" "d")
+ (match_operand:P 2 "register_operand" "d")))))]
+ "ISA_HAS_DSP"
+ "lbux\t%0,%2(%1)"
+ [(set_attr "type" "load")
+ (set_attr "mode" "SI")])
+
+(define_expand "mips_lhx"
+ [(match_operand:SI 0 "register_operand")
+ (match_operand 1 "pmode_register_operand")
+ (match_operand:SI 2 "register_operand")]
+ "ISA_HAS_DSP"
+{
+ operands[2] = convert_to_mode (Pmode, operands[2], false);
+ if (Pmode == SImode)
+ emit_insn (gen_mips_lhx_si (operands[0], operands[1], operands[2]));
+ else
+ emit_insn (gen_mips_lhx_di (operands[0], operands[1], operands[2]));
+ DONE;
+})
+
+(define_insn "mips_lhx_<mode>"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (sign_extend:SI
+ (mem:HI (plus:P (match_operand:P 1 "register_operand" "d")
+ (match_operand:P 2 "register_operand" "d")))))]
+ "ISA_HAS_DSP"
+ "lhx\t%0,%2(%1)"
+ [(set_attr "type" "load")
+ (set_attr "mode" "SI")])
+
+(define_expand "mips_lwx"
+ [(match_operand:SI 0 "register_operand")
+ (match_operand 1 "pmode_register_operand")
+ (match_operand:SI 2 "register_operand")]
+ "ISA_HAS_DSP"
+{
+ operands[2] = convert_to_mode (Pmode, operands[2], false);
+ if (Pmode == SImode)
+ emit_insn (gen_mips_lwx_si (operands[0], operands[1], operands[2]));
+ else
+ emit_insn (gen_mips_lwx_di (operands[0], operands[1], operands[2]));
+ DONE;
+})
+
+(define_insn "mips_lwx_<mode>"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (mem:SI (plus:P (match_operand:P 1 "register_operand" "d")
+ (match_operand:P 2 "register_operand" "d"))))]
+ "ISA_HAS_DSP"
+ "lwx\t%0,%2(%1)"
+ [(set_attr "type" "load")
+ (set_attr "mode" "SI")])
+
+;; Table 2-8. MIPS DSP ASE Instructions: Branch
+;; BPOSGE32
+(define_insn "mips_bposge"
+ [(set (pc)
+ (if_then_else (ge (reg:CCDSP CCDSP_PO_REGNUM)
+ (match_operand:SI 1 "immediate_operand" "I"))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ "ISA_HAS_DSP"
+ "%*bposge%1\t%0%/"
+ [(set_attr "type" "branch")])
+
+(define_expand "mips_madd<u>"
+ [(set (match_operand:DI 0 "register_operand")
+ (plus:DI
+ (mult:DI (any_extend:DI (match_operand:SI 2 "register_operand"))
+ (any_extend:DI (match_operand:SI 3 "register_operand")))
+ (match_operand:DI 1 "register_operand")))]
+ "ISA_HAS_DSP && !TARGET_64BIT")
+
+(define_expand "mips_msub<u>"
+ [(set (match_operand:DI 0 "register_operand")
+ (minus:DI
+ (match_operand:DI 1 "register_operand")
+ (mult:DI (any_extend:DI (match_operand:SI 2 "register_operand"))
+ (any_extend:DI (match_operand:SI 3 "register_operand")))))]
+ "ISA_HAS_DSP && !TARGET_64BIT")
diff --git a/gcc/config/mips/mips-dspr2.md b/gcc/config/mips/mips-dspr2.md
new file mode 100644
index 000000000..5ae902f01
--- /dev/null
+++ b/gcc/config/mips/mips-dspr2.md
@@ -0,0 +1,623 @@
+;; Copyright (C) 2007, 2010 Free Software Foundation, 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/>.
+;;
+; MIPS DSP ASE REV 2 Revision 0.02 11/24/2006
+
+(define_c_enum "unspec" [
+ UNSPEC_ABSQ_S_QB
+ UNSPEC_ADDU_PH
+ UNSPEC_ADDU_S_PH
+ UNSPEC_ADDUH_QB
+ UNSPEC_ADDUH_R_QB
+ UNSPEC_APPEND
+ UNSPEC_BALIGN
+ UNSPEC_CMPGDU_EQ_QB
+ UNSPEC_CMPGDU_LT_QB
+ UNSPEC_CMPGDU_LE_QB
+ UNSPEC_DPA_W_PH
+ UNSPEC_DPS_W_PH
+ UNSPEC_MADD
+ UNSPEC_MADDU
+ UNSPEC_MSUB
+ UNSPEC_MSUBU
+ UNSPEC_MUL_PH
+ UNSPEC_MUL_S_PH
+ UNSPEC_MULQ_RS_W
+ UNSPEC_MULQ_S_PH
+ UNSPEC_MULQ_S_W
+ UNSPEC_MULSA_W_PH
+ UNSPEC_MULT
+ UNSPEC_MULTU
+ UNSPEC_PRECR_QB_PH
+ UNSPEC_PRECR_SRA_PH_W
+ UNSPEC_PRECR_SRA_R_PH_W
+ UNSPEC_PREPEND
+ UNSPEC_SHRA_QB
+ UNSPEC_SHRA_R_QB
+ UNSPEC_SHRL_PH
+ UNSPEC_SUBU_PH
+ UNSPEC_SUBU_S_PH
+ UNSPEC_SUBUH_QB
+ UNSPEC_SUBUH_R_QB
+ UNSPEC_ADDQH_PH
+ UNSPEC_ADDQH_R_PH
+ UNSPEC_ADDQH_W
+ UNSPEC_ADDQH_R_W
+ UNSPEC_SUBQH_PH
+ UNSPEC_SUBQH_R_PH
+ UNSPEC_SUBQH_W
+ UNSPEC_SUBQH_R_W
+ UNSPEC_DPAX_W_PH
+ UNSPEC_DPSX_W_PH
+ UNSPEC_DPAQX_S_W_PH
+ UNSPEC_DPAQX_SA_W_PH
+ UNSPEC_DPSQX_S_W_PH
+ UNSPEC_DPSQX_SA_W_PH
+])
+
+(define_insn "mips_absq_s_qb"
+ [(parallel
+ [(set (match_operand:V4QI 0 "register_operand" "=d")
+ (unspec:V4QI [(match_operand:V4QI 1 "reg_or_0_operand" "dYG")]
+ UNSPEC_ABSQ_S_QB))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1)] UNSPEC_ABSQ_S_QB))])]
+ "ISA_HAS_DSPR2"
+ "absq_s.qb\t%0,%z1"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_addu_ph"
+ [(parallel
+ [(set (match_operand:V2HI 0 "register_operand" "=d")
+ (plus:V2HI (match_operand:V2HI 1 "reg_or_0_operand" "dYG")
+ (match_operand:V2HI 2 "reg_or_0_operand" "dYG")))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2)] UNSPEC_ADDU_PH))])]
+ "ISA_HAS_DSPR2"
+ "addu.ph\t%0,%z1,%z2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_addu_s_ph"
+ [(parallel
+ [(set (match_operand:V2HI 0 "register_operand" "=d")
+ (unspec:V2HI [(match_operand:V2HI 1 "reg_or_0_operand" "dYG")
+ (match_operand:V2HI 2 "reg_or_0_operand" "dYG")]
+ UNSPEC_ADDU_S_PH))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2)] UNSPEC_ADDU_S_PH))])]
+ "ISA_HAS_DSPR2"
+ "addu_s.ph\t%0,%z1,%z2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_adduh_qb"
+ [(set (match_operand:V4QI 0 "register_operand" "=d")
+ (unspec:V4QI [(match_operand:V4QI 1 "reg_or_0_operand" "dYG")
+ (match_operand:V4QI 2 "reg_or_0_operand" "dYG")]
+ UNSPEC_ADDUH_QB))]
+ "ISA_HAS_DSPR2"
+ "adduh.qb\t%0,%z1,%z2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_adduh_r_qb"
+ [(set (match_operand:V4QI 0 "register_operand" "=d")
+ (unspec:V4QI [(match_operand:V4QI 1 "reg_or_0_operand" "dYG")
+ (match_operand:V4QI 2 "reg_or_0_operand" "dYG")]
+ UNSPEC_ADDUH_R_QB))]
+ "ISA_HAS_DSPR2"
+ "adduh_r.qb\t%0,%z1,%z2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_append"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (unspec:SI [(match_operand:SI 1 "register_operand" "0")
+ (match_operand:SI 2 "reg_or_0_operand" "dJ")
+ (match_operand:SI 3 "const_int_operand" "n")]
+ UNSPEC_APPEND))]
+ "ISA_HAS_DSPR2"
+{
+ if (INTVAL (operands[3]) & ~(unsigned HOST_WIDE_INT) 31)
+ operands[2] = GEN_INT (INTVAL (operands[2]) & 31);
+ return "append\t%0,%z2,%3";
+}
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_balign"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (unspec:SI [(match_operand:SI 1 "register_operand" "0")
+ (match_operand:SI 2 "reg_or_0_operand" "dJ")
+ (match_operand:SI 3 "const_int_operand" "n")]
+ UNSPEC_BALIGN))]
+ "ISA_HAS_DSPR2"
+{
+ if (INTVAL (operands[3]) & ~(unsigned HOST_WIDE_INT) 3)
+ operands[2] = GEN_INT (INTVAL (operands[2]) & 3);
+ return "balign\t%0,%z2,%3";
+}
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_cmpgdu_eq_qb"
+ [(parallel
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (unspec:SI [(match_operand:V4QI 1 "reg_or_0_operand" "dYG")
+ (match_operand:V4QI 2 "reg_or_0_operand" "dYG")]
+ UNSPEC_CMPGDU_EQ_QB))
+ (set (reg:CCDSP CCDSP_CC_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2)
+ (reg:CCDSP CCDSP_CC_REGNUM)]
+ UNSPEC_CMPGDU_EQ_QB))])]
+ "ISA_HAS_DSPR2"
+ "cmpgdu.eq.qb\t%0,%z1,%z2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_cmpgdu_lt_qb"
+ [(parallel
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (unspec:SI [(match_operand:V4QI 1 "reg_or_0_operand" "dYG")
+ (match_operand:V4QI 2 "reg_or_0_operand" "dYG")]
+ UNSPEC_CMPGDU_LT_QB))
+ (set (reg:CCDSP CCDSP_CC_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2)
+ (reg:CCDSP CCDSP_CC_REGNUM)]
+ UNSPEC_CMPGDU_LT_QB))])]
+ "ISA_HAS_DSPR2"
+ "cmpgdu.lt.qb\t%0,%z1,%z2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_cmpgdu_le_qb"
+ [(parallel
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (unspec:SI [(match_operand:V4QI 1 "reg_or_0_operand" "dYG")
+ (match_operand:V4QI 2 "reg_or_0_operand" "dYG")]
+ UNSPEC_CMPGDU_LE_QB))
+ (set (reg:CCDSP CCDSP_CC_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2)
+ (reg:CCDSP CCDSP_CC_REGNUM)]
+ UNSPEC_CMPGDU_LE_QB))])]
+ "ISA_HAS_DSPR2"
+ "cmpgdu.le.qb\t%0,%z1,%z2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_dpa_w_ph"
+ [(set (match_operand:DI 0 "register_operand" "=a")
+ (unspec:DI [(match_operand:DI 1 "register_operand" "0")
+ (match_operand:V2HI 2 "reg_or_0_operand" "dYG")
+ (match_operand:V2HI 3 "reg_or_0_operand" "dYG")]
+ UNSPEC_DPA_W_PH))]
+ "ISA_HAS_DSPR2 && !TARGET_64BIT"
+ "dpa.w.ph\t%q0,%z2,%z3"
+ [(set_attr "type" "imadd")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_dps_w_ph"
+ [(set (match_operand:DI 0 "register_operand" "=a")
+ (unspec:DI [(match_operand:DI 1 "register_operand" "0")
+ (match_operand:V2HI 2 "reg_or_0_operand" "dYG")
+ (match_operand:V2HI 3 "reg_or_0_operand" "dYG")]
+ UNSPEC_DPS_W_PH))]
+ "ISA_HAS_DSPR2 && !TARGET_64BIT"
+ "dps.w.ph\t%q0,%z2,%z3"
+ [(set_attr "type" "imadd")
+ (set_attr "mode" "SI")])
+
+(define_insn "mulv2hi3"
+ [(parallel
+ [(set (match_operand:V2HI 0 "register_operand" "=d")
+ (mult:V2HI (match_operand:V2HI 1 "register_operand" "d")
+ (match_operand:V2HI 2 "register_operand" "d")))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2)] UNSPEC_MUL_PH))
+ (clobber (match_scratch:DI 3 "=x"))])]
+ "ISA_HAS_DSPR2"
+ "mul.ph\t%0,%1,%2"
+ [(set_attr "type" "imul3")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_mul_s_ph"
+ [(parallel
+ [(set (match_operand:V2HI 0 "register_operand" "=d")
+ (unspec:V2HI [(match_operand:V2HI 1 "reg_or_0_operand" "dYG")
+ (match_operand:V2HI 2 "reg_or_0_operand" "dYG")]
+ UNSPEC_MUL_S_PH))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2)] UNSPEC_MUL_S_PH))
+ (clobber (match_scratch:DI 3 "=x"))])]
+ "ISA_HAS_DSPR2"
+ "mul_s.ph\t%0,%z1,%z2"
+ [(set_attr "type" "imul3")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_mulq_rs_w"
+ [(parallel
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (unspec:SI [(match_operand:SI 1 "reg_or_0_operand" "dJ")
+ (match_operand:SI 2 "reg_or_0_operand" "dJ")]
+ UNSPEC_MULQ_RS_W))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2)] UNSPEC_MULQ_RS_W))
+ (clobber (match_scratch:DI 3 "=x"))])]
+ "ISA_HAS_DSPR2"
+ "mulq_rs.w\t%0,%z1,%z2"
+ [(set_attr "type" "imul3")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_mulq_s_ph"
+ [(parallel
+ [(set (match_operand:V2HI 0 "register_operand" "=d")
+ (unspec:V2HI [(match_operand:V2HI 1 "reg_or_0_operand" "dYG")
+ (match_operand:V2HI 2 "reg_or_0_operand" "dYG")]
+ UNSPEC_MULQ_S_PH))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2)] UNSPEC_MULQ_S_PH))
+ (clobber (match_scratch:DI 3 "=x"))])]
+ "ISA_HAS_DSPR2"
+ "mulq_s.ph\t%0,%z1,%z2"
+ [(set_attr "type" "imul3")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_mulq_s_w"
+ [(parallel
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (unspec:SI [(match_operand:SI 1 "reg_or_0_operand" "dJ")
+ (match_operand:SI 2 "reg_or_0_operand" "dJ")]
+ UNSPEC_MULQ_S_W))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2)] UNSPEC_MULQ_S_W))
+ (clobber (match_scratch:DI 3 "=x"))])]
+ "ISA_HAS_DSPR2"
+ "mulq_s.w\t%0,%z1,%z2"
+ [(set_attr "type" "imul3")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_mulsa_w_ph"
+ [(set (match_operand:DI 0 "register_operand" "=a")
+ (unspec:DI [(match_operand:DI 1 "register_operand" "0")
+ (match_operand:V2HI 2 "reg_or_0_operand" "dYG")
+ (match_operand:V2HI 3 "reg_or_0_operand" "dYG")]
+ UNSPEC_MULSA_W_PH))]
+ "ISA_HAS_DSPR2 && !TARGET_64BIT"
+ "mulsa.w.ph\t%q0,%z2,%z3"
+ [(set_attr "type" "imadd")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_precr_qb_ph"
+ [(set (match_operand:V4QI 0 "register_operand" "=d")
+ (unspec:V4QI [(match_operand:V2HI 1 "reg_or_0_operand" "dYG")
+ (match_operand:V2HI 2 "reg_or_0_operand" "dYG")]
+ UNSPEC_PRECR_QB_PH))]
+ "ISA_HAS_DSPR2"
+ "precr.qb.ph\t%0,%z1,%z2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_precr_sra_ph_w"
+ [(set (match_operand:V2HI 0 "register_operand" "=d")
+ (unspec:V2HI [(match_operand:SI 1 "register_operand" "0")
+ (match_operand:SI 2 "reg_or_0_operand" "dJ")
+ (match_operand:SI 3 "const_int_operand" "n")]
+ UNSPEC_PRECR_SRA_PH_W))]
+ "ISA_HAS_DSPR2"
+{
+ if (INTVAL (operands[3]) & ~(unsigned HOST_WIDE_INT) 31)
+ operands[2] = GEN_INT (INTVAL (operands[2]) & 31);
+ return "precr_sra.ph.w\t%0,%z2,%3";
+}
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_precr_sra_r_ph_w"
+ [(set (match_operand:V2HI 0 "register_operand" "=d")
+ (unspec:V2HI [(match_operand:SI 1 "register_operand" "0")
+ (match_operand:SI 2 "reg_or_0_operand" "dJ")
+ (match_operand:SI 3 "const_int_operand" "n")]
+ UNSPEC_PRECR_SRA_R_PH_W))]
+ "ISA_HAS_DSPR2"
+{
+ if (INTVAL (operands[3]) & ~(unsigned HOST_WIDE_INT) 31)
+ operands[2] = GEN_INT (INTVAL (operands[2]) & 31);
+ return "precr_sra_r.ph.w\t%0,%z2,%3";
+}
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_prepend"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (unspec:SI [(match_operand:SI 1 "register_operand" "0")
+ (match_operand:SI 2 "reg_or_0_operand" "dJ")
+ (match_operand:SI 3 "const_int_operand" "n")]
+ UNSPEC_PREPEND))]
+ "ISA_HAS_DSPR2"
+{
+ if (INTVAL (operands[3]) & ~(unsigned HOST_WIDE_INT) 31)
+ operands[2] = GEN_INT (INTVAL (operands[2]) & 31);
+ return "prepend\t%0,%z2,%3";
+}
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_shra_qb"
+ [(set (match_operand:V4QI 0 "register_operand" "=d,d")
+ (unspec:V4QI [(match_operand:V4QI 1 "reg_or_0_operand" "dYG,dYG")
+ (match_operand:SI 2 "arith_operand" "I,d")]
+ UNSPEC_SHRA_QB))]
+ "ISA_HAS_DSPR2"
+{
+ if (which_alternative == 0)
+ {
+ if (INTVAL (operands[2]) & ~(unsigned HOST_WIDE_INT) 7)
+ operands[2] = GEN_INT (INTVAL (operands[2]) & 7);
+ return "shra.qb\t%0,%z1,%2";
+ }
+ return "shrav.qb\t%0,%z1,%2";
+}
+ [(set_attr "type" "shift")
+ (set_attr "mode" "SI")])
+
+
+(define_insn "mips_shra_r_qb"
+ [(set (match_operand:V4QI 0 "register_operand" "=d,d")
+ (unspec:V4QI [(match_operand:V4QI 1 "reg_or_0_operand" "dYG,dYG")
+ (match_operand:SI 2 "arith_operand" "I,d")]
+ UNSPEC_SHRA_R_QB))]
+ "ISA_HAS_DSPR2"
+{
+ if (which_alternative == 0)
+ {
+ if (INTVAL (operands[2]) & ~(unsigned HOST_WIDE_INT) 7)
+ operands[2] = GEN_INT (INTVAL (operands[2]) & 7);
+ return "shra_r.qb\t%0,%z1,%2";
+ }
+ return "shrav_r.qb\t%0,%z1,%2";
+}
+ [(set_attr "type" "shift")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_shrl_ph"
+ [(set (match_operand:V2HI 0 "register_operand" "=d,d")
+ (unspec:V2HI [(match_operand:V2HI 1 "reg_or_0_operand" "dYG,dYG")
+ (match_operand:SI 2 "arith_operand" "I,d")]
+ UNSPEC_SHRL_PH))]
+ "ISA_HAS_DSPR2"
+{
+ if (which_alternative == 0)
+ {
+ if (INTVAL (operands[2]) & ~(unsigned HOST_WIDE_INT) 15)
+ operands[2] = GEN_INT (INTVAL (operands[2]) & 15);
+ return "shrl.ph\t%0,%z1,%2";
+ }
+ return "shrlv.ph\t%0,%z1,%2";
+}
+ [(set_attr "type" "shift")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_subu_ph"
+ [(parallel
+ [(set (match_operand:V2HI 0 "register_operand" "=d")
+ (unspec:V2HI [(match_operand:V2HI 1 "reg_or_0_operand" "dYG")
+ (match_operand:V2HI 2 "reg_or_0_operand" "dYG")]
+ UNSPEC_SUBU_PH))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2)] UNSPEC_SUBU_PH))])]
+ "ISA_HAS_DSPR2"
+ "subu.ph\t%0,%z1,%z2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_subu_s_ph"
+ [(parallel
+ [(set (match_operand:V2HI 0 "register_operand" "=d")
+ (unspec:V2HI [(match_operand:V2HI 1 "reg_or_0_operand" "dYG")
+ (match_operand:V2HI 2 "reg_or_0_operand" "dYG")]
+ UNSPEC_SUBU_S_PH))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2)] UNSPEC_SUBU_S_PH))])]
+ "ISA_HAS_DSPR2"
+ "subu_s.ph\t%0,%z1,%z2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_subuh_qb"
+ [(set (match_operand:V4QI 0 "register_operand" "=d")
+ (unspec:V4QI [(match_operand:V4QI 1 "reg_or_0_operand" "dYG")
+ (match_operand:V4QI 2 "reg_or_0_operand" "dYG")]
+ UNSPEC_SUBUH_QB))]
+ "ISA_HAS_DSPR2"
+ "subuh.qb\t%0,%z1,%z2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_subuh_r_qb"
+ [(set (match_operand:V4QI 0 "register_operand" "=d")
+ (unspec:V4QI [(match_operand:V4QI 1 "reg_or_0_operand" "dYG")
+ (match_operand:V4QI 2 "reg_or_0_operand" "dYG")]
+ UNSPEC_SUBUH_R_QB))]
+ "ISA_HAS_DSPR2"
+ "subuh_r.qb\t%0,%z1,%z2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_addqh_ph"
+ [(set (match_operand:V2HI 0 "register_operand" "=d")
+ (unspec:V2HI [(match_operand:V2HI 1 "reg_or_0_operand" "dYG")
+ (match_operand:V2HI 2 "reg_or_0_operand" "dYG")]
+ UNSPEC_ADDQH_PH))]
+ "ISA_HAS_DSPR2"
+ "addqh.ph\t%0,%z1,%z2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_addqh_r_ph"
+ [(set (match_operand:V2HI 0 "register_operand" "=d")
+ (unspec:V2HI [(match_operand:V2HI 1 "reg_or_0_operand" "dYG")
+ (match_operand:V2HI 2 "reg_or_0_operand" "dYG")]
+ UNSPEC_ADDQH_R_PH))]
+ "ISA_HAS_DSPR2"
+ "addqh_r.ph\t%0,%z1,%z2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_addqh_w"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (unspec:SI [(match_operand:SI 1 "reg_or_0_operand" "dJ")
+ (match_operand:SI 2 "reg_or_0_operand" "dJ")]
+ UNSPEC_ADDQH_W))]
+ "ISA_HAS_DSPR2"
+ "addqh.w\t%0,%z1,%z2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_addqh_r_w"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (unspec:SI [(match_operand:SI 1 "reg_or_0_operand" "dJ")
+ (match_operand:SI 2 "reg_or_0_operand" "dJ")]
+ UNSPEC_ADDQH_R_W))]
+ "ISA_HAS_DSPR2"
+ "addqh_r.w\t%0,%z1,%z2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_subqh_ph"
+ [(set (match_operand:V2HI 0 "register_operand" "=d")
+ (unspec:V2HI [(match_operand:V2HI 1 "reg_or_0_operand" "dYG")
+ (match_operand:V2HI 2 "reg_or_0_operand" "dYG")]
+ UNSPEC_SUBQH_PH))]
+ "ISA_HAS_DSPR2"
+ "subqh.ph\t%0,%z1,%z2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_subqh_r_ph"
+ [(set (match_operand:V2HI 0 "register_operand" "=d")
+ (unspec:V2HI [(match_operand:V2HI 1 "reg_or_0_operand" "dYG")
+ (match_operand:V2HI 2 "reg_or_0_operand" "dYG")]
+ UNSPEC_SUBQH_R_PH))]
+ "ISA_HAS_DSPR2"
+ "subqh_r.ph\t%0,%z1,%z2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_subqh_w"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (unspec:SI [(match_operand:SI 1 "reg_or_0_operand" "dJ")
+ (match_operand:SI 2 "reg_or_0_operand" "dJ")]
+ UNSPEC_SUBQH_W))]
+ "ISA_HAS_DSPR2"
+ "subqh.w\t%0,%z1,%z2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_subqh_r_w"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (unspec:SI [(match_operand:SI 1 "reg_or_0_operand" "dJ")
+ (match_operand:SI 2 "reg_or_0_operand" "dJ")]
+ UNSPEC_SUBQH_R_W))]
+ "ISA_HAS_DSPR2"
+ "subqh_r.w\t%0,%z1,%z2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_dpax_w_ph"
+ [(set (match_operand:DI 0 "register_operand" "=a")
+ (unspec:DI [(match_operand:DI 1 "register_operand" "0")
+ (match_operand:V2HI 2 "reg_or_0_operand" "dYG")
+ (match_operand:V2HI 3 "reg_or_0_operand" "dYG")]
+ UNSPEC_DPAX_W_PH))]
+ "ISA_HAS_DSPR2 && !TARGET_64BIT"
+ "dpax.w.ph\t%q0,%z2,%z3"
+ [(set_attr "type" "imadd")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_dpsx_w_ph"
+ [(set (match_operand:DI 0 "register_operand" "=a")
+ (unspec:DI [(match_operand:DI 1 "register_operand" "0")
+ (match_operand:V2HI 2 "reg_or_0_operand" "dYG")
+ (match_operand:V2HI 3 "reg_or_0_operand" "dYG")]
+ UNSPEC_DPSX_W_PH))]
+ "ISA_HAS_DSPR2 && !TARGET_64BIT"
+ "dpsx.w.ph\t%q0,%z2,%z3"
+ [(set_attr "type" "imadd")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_dpaqx_s_w_ph"
+ [(parallel
+ [(set (match_operand:DI 0 "register_operand" "=a")
+ (unspec:DI [(match_operand:DI 1 "register_operand" "0")
+ (match_operand:V2HI 2 "reg_or_0_operand" "dYG")
+ (match_operand:V2HI 3 "reg_or_0_operand" "dYG")]
+ UNSPEC_DPAQX_S_W_PH))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2) (match_dup 3)]
+ UNSPEC_DPAQX_S_W_PH))])]
+ "ISA_HAS_DSPR2 && !TARGET_64BIT"
+ "dpaqx_s.w.ph\t%q0,%z2,%z3"
+ [(set_attr "type" "imadd")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_dpaqx_sa_w_ph"
+ [(parallel
+ [(set (match_operand:DI 0 "register_operand" "=a")
+ (unspec:DI [(match_operand:DI 1 "register_operand" "0")
+ (match_operand:V2HI 2 "reg_or_0_operand" "dYG")
+ (match_operand:V2HI 3 "reg_or_0_operand" "dYG")]
+ UNSPEC_DPAQX_SA_W_PH))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2) (match_dup 3)]
+ UNSPEC_DPAQX_SA_W_PH))])]
+ "ISA_HAS_DSPR2 && !TARGET_64BIT"
+ "dpaqx_sa.w.ph\t%q0,%z2,%z3"
+ [(set_attr "type" "imadd")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_dpsqx_s_w_ph"
+ [(parallel
+ [(set (match_operand:DI 0 "register_operand" "=a")
+ (unspec:DI [(match_operand:DI 1 "register_operand" "0")
+ (match_operand:V2HI 2 "reg_or_0_operand" "dYG")
+ (match_operand:V2HI 3 "reg_or_0_operand" "dYG")]
+ UNSPEC_DPSQX_S_W_PH))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2) (match_dup 3)]
+ UNSPEC_DPSQX_S_W_PH))])]
+ "ISA_HAS_DSPR2 && !TARGET_64BIT"
+ "dpsqx_s.w.ph\t%q0,%z2,%z3"
+ [(set_attr "type" "imadd")
+ (set_attr "mode" "SI")])
+
+(define_insn "mips_dpsqx_sa_w_ph"
+ [(parallel
+ [(set (match_operand:DI 0 "register_operand" "=a")
+ (unspec:DI [(match_operand:DI 1 "register_operand" "0")
+ (match_operand:V2HI 2 "reg_or_0_operand" "dYG")
+ (match_operand:V2HI 3 "reg_or_0_operand" "dYG")]
+ UNSPEC_DPSQX_SA_W_PH))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2) (match_dup 3)]
+ UNSPEC_DPSQX_SA_W_PH))])]
+ "ISA_HAS_DSPR2 && !TARGET_64BIT"
+ "dpsqx_sa.w.ph\t%q0,%z2,%z3"
+ [(set_attr "type" "imadd")
+ (set_attr "mode" "SI")])
diff --git a/gcc/config/mips/mips-fixed.md b/gcc/config/mips/mips-fixed.md
new file mode 100644
index 000000000..d5dd909b1
--- /dev/null
+++ b/gcc/config/mips/mips-fixed.md
@@ -0,0 +1,156 @@
+;; Copyright (C) 2007 Free Software Foundation, 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/>.
+;;
+;; This file contains MIPS instructions that support fixed-point operations.
+
+;; All supported fixed-point modes
+(define_mode_iterator FIXED [(QQ "") (HQ "") (SQ "") (DQ "TARGET_64BIT")
+ (UQQ "") (UHQ "") (USQ "") (UDQ "TARGET_64BIT")
+ (HA "") (SA "") (DA "TARGET_64BIT")
+ (UHA "") (USA "") (UDA "TARGET_64BIT")])
+
+;; For signed add/sub with saturation
+(define_mode_iterator ADDSUB [(HQ "") (SQ "") (HA "") (SA "") (V2HQ "")
+ (V2HA "")])
+(define_mode_attr addsubfmt [(HQ "ph") (SQ "w") (HA "ph") (SA "w")
+ (V2HQ "ph") (V2HA "ph")])
+
+;; For unsigned add/sub with saturation
+(define_mode_iterator UADDSUB [(UQQ "ISA_HAS_DSP") (UHQ "ISA_HAS_DSPR2")
+ (UHA "ISA_HAS_DSPR2") (V4UQQ "ISA_HAS_DSP")
+ (V2UHQ "ISA_HAS_DSPR2") (V2UHA "ISA_HAS_DSPR2")])
+(define_mode_attr uaddsubfmt [(UQQ "qb") (UHQ "ph") (UHA "ph")
+ (V4UQQ "qb") (V2UHQ "ph") (V2UHA "ph")])
+
+;; For signed multiplication with saturation
+(define_mode_iterator MULQ [(V2HQ "ISA_HAS_DSP") (HQ "ISA_HAS_DSP")
+ (SQ "ISA_HAS_DSPR2")])
+(define_mode_attr mulqfmt [(V2HQ "ph") (HQ "ph") (SQ "w")])
+
+(define_insn "add<mode>3"
+ [(set (match_operand:FIXED 0 "register_operand" "=d")
+ (plus:FIXED (match_operand:FIXED 1 "register_operand" "d")
+ (match_operand:FIXED 2 "register_operand" "d")))]
+ ""
+ "<d>addu\t%0,%1,%2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "<IMODE>")])
+
+(define_insn "usadd<mode>3"
+ [(parallel
+ [(set (match_operand:UADDSUB 0 "register_operand" "=d")
+ (us_plus:UADDSUB (match_operand:UADDSUB 1 "register_operand" "d")
+ (match_operand:UADDSUB 2 "register_operand" "d")))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2)] UNSPEC_ADDQ_S))])]
+ ""
+ "addu_s.<uaddsubfmt>\t%0,%1,%2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "<IMODE>")])
+
+(define_insn "ssadd<mode>3"
+ [(parallel
+ [(set (match_operand:ADDSUB 0 "register_operand" "=d")
+ (ss_plus:ADDSUB (match_operand:ADDSUB 1 "register_operand" "d")
+ (match_operand:ADDSUB 2 "register_operand" "d")))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2)] UNSPEC_ADDQ_S))])]
+ "ISA_HAS_DSP"
+ "addq_s.<addsubfmt>\t%0,%1,%2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "<IMODE>")])
+
+(define_insn "sub<mode>3"
+ [(set (match_operand:FIXED 0 "register_operand" "=d")
+ (minus:FIXED (match_operand:FIXED 1 "register_operand" "d")
+ (match_operand:FIXED 2 "register_operand" "d")))]
+ ""
+ "<d>subu\t%0,%1,%2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "<IMODE>")])
+
+(define_insn "ussub<mode>3"
+ [(parallel
+ [(set (match_operand:UADDSUB 0 "register_operand" "=d")
+ (us_minus:UADDSUB (match_operand:UADDSUB 1 "register_operand" "d")
+ (match_operand:UADDSUB 2 "register_operand" "d")))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2)] UNSPEC_SUBQ_S))])]
+ ""
+ "subu_s.<uaddsubfmt>\t%0,%1,%2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "<IMODE>")])
+
+(define_insn "sssub<mode>3"
+ [(parallel
+ [(set (match_operand:ADDSUB 0 "register_operand" "=d")
+ (ss_minus:ADDSUB (match_operand:ADDSUB 1 "register_operand" "d")
+ (match_operand:ADDSUB 2 "register_operand" "d")))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2)] UNSPEC_SUBQ_S))])]
+ "ISA_HAS_DSP"
+ "subq_s.<addsubfmt>\t%0,%1,%2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "<IMODE>")])
+
+(define_insn "ssmul<mode>3"
+ [(parallel
+ [(set (match_operand:MULQ 0 "register_operand" "=d")
+ (ss_mult:MULQ (match_operand:MULQ 1 "register_operand" "d")
+ (match_operand:MULQ 2 "register_operand" "d")))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2)] UNSPEC_MULQ_RS_PH))
+ (clobber (match_scratch:DI 3 "=x"))])]
+ ""
+ "mulq_rs.<mulqfmt>\t%0,%1,%2"
+ [(set_attr "type" "imul3")
+ (set_attr "mode" "<IMODE>")])
+
+(define_insn "ssmaddsqdq4"
+ [(parallel
+ [(set (match_operand:DQ 0 "register_operand" "=a")
+ (ss_plus:DQ
+ (ss_mult:DQ (sat_fract:DQ (match_operand:SQ 1
+ "register_operand" "d"))
+ (sat_fract:DQ (match_operand:SQ 2
+ "register_operand" "d")))
+ (match_operand:DQ 3 "register_operand" "0")))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2) (match_dup 3)]
+ UNSPEC_DPAQ_SA_L_W))])]
+ "ISA_HAS_DSP && !TARGET_64BIT"
+ "dpaq_sa.l.w\t%q0,%1,%2"
+ [(set_attr "type" "imadd")
+ (set_attr "mode" "SI")])
+
+(define_insn "ssmsubsqdq4"
+ [(parallel
+ [(set (match_operand:DQ 0 "register_operand" "=a")
+ (ss_minus:DQ
+ (match_operand:DQ 3 "register_operand" "0")
+ (ss_mult:DQ (sat_fract:DQ (match_operand:SQ 1
+ "register_operand" "d"))
+ (sat_fract:DQ (match_operand:SQ 2
+ "register_operand" "d")))))
+ (set (reg:CCDSP CCDSP_OU_REGNUM)
+ (unspec:CCDSP [(match_dup 1) (match_dup 2) (match_dup 3)]
+ UNSPEC_DPSQ_SA_L_W))])]
+ "ISA_HAS_DSP && !TARGET_64BIT"
+ "dpsq_sa.l.w\t%q0,%1,%2"
+ [(set_attr "type" "imadd")
+ (set_attr "mode" "SI")])
diff --git a/gcc/config/mips/mips-ftypes.def b/gcc/config/mips/mips-ftypes.def
new file mode 100644
index 000000000..0357aa649
--- /dev/null
+++ b/gcc/config/mips/mips-ftypes.def
@@ -0,0 +1,126 @@
+/* Definitions of prototypes for MIPS built-in functions. -*- C -*-
+ Copyright (C) 2007, 2008
+ Free Software Foundation, 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/>. */
+
+/* Invoke DEF_MIPS_FTYPE (NARGS, LIST) for each prototype used by
+ MIPS built-in functions, where:
+
+ NARGS is the number of arguments.
+ LIST contains the return-type code followed by the codes for each
+ argument type.
+
+ Argument- and return-type codes are either modes or one of the following:
+
+ VOID for void_type_node
+ INT for integer_type_node
+ POINTER for ptr_type_node
+
+ (we don't use PTR because that's a ANSI-compatibillity macro).
+
+ Please keep this list lexicographically sorted by the LIST argument. */
+DEF_MIPS_FTYPE (1, (DF, DF))
+DEF_MIPS_FTYPE (2, (DF, DF, DF))
+
+DEF_MIPS_FTYPE (2, (DI, DI, DI))
+DEF_MIPS_FTYPE (2, (DI, DI, SI))
+DEF_MIPS_FTYPE (3, (DI, DI, SI, SI))
+DEF_MIPS_FTYPE (3, (DI, DI, USI, USI))
+DEF_MIPS_FTYPE (3, (DI, DI, V2HI, V2HI))
+DEF_MIPS_FTYPE (3, (DI, DI, V4QI, V4QI))
+DEF_MIPS_FTYPE (2, (DI, SI, SI))
+DEF_MIPS_FTYPE (2, (DI, USI, USI))
+
+DEF_MIPS_FTYPE (2, (INT, DF, DF))
+DEF_MIPS_FTYPE (2, (INT, SF, SF))
+DEF_MIPS_FTYPE (2, (INT, V2SF, V2SF))
+DEF_MIPS_FTYPE (4, (INT, V2SF, V2SF, V2SF, V2SF))
+
+DEF_MIPS_FTYPE (2, (SI, DI, SI))
+DEF_MIPS_FTYPE (2, (SI, POINTER, SI))
+DEF_MIPS_FTYPE (1, (SI, SI))
+DEF_MIPS_FTYPE (2, (SI, SI, SI))
+DEF_MIPS_FTYPE (3, (SI, SI, SI, SI))
+DEF_MIPS_FTYPE (1, (SI, V2HI))
+DEF_MIPS_FTYPE (2, (SI, V2HI, V2HI))
+DEF_MIPS_FTYPE (1, (SI, V4QI))
+DEF_MIPS_FTYPE (2, (SI, V4QI, V4QI))
+DEF_MIPS_FTYPE (1, (SI, VOID))
+
+DEF_MIPS_FTYPE (1, (SF, SF))
+DEF_MIPS_FTYPE (2, (SF, SF, SF))
+DEF_MIPS_FTYPE (1, (SF, V2SF))
+
+DEF_MIPS_FTYPE (2, (UDI, UDI, UDI))
+DEF_MIPS_FTYPE (2, (UDI, UV2SI, UV2SI))
+
+DEF_MIPS_FTYPE (2, (UV2SI, UV2SI, UQI))
+DEF_MIPS_FTYPE (2, (UV2SI, UV2SI, UV2SI))
+
+DEF_MIPS_FTYPE (2, (UV4HI, UV4HI, UQI))
+DEF_MIPS_FTYPE (2, (UV4HI, UV4HI, USI))
+DEF_MIPS_FTYPE (3, (UV4HI, UV4HI, UV4HI, UQI))
+DEF_MIPS_FTYPE (3, (UV4HI, UV4HI, UV4HI, USI))
+DEF_MIPS_FTYPE (2, (UV4HI, UV4HI, UV4HI))
+DEF_MIPS_FTYPE (1, (UV4HI, UV8QI))
+DEF_MIPS_FTYPE (2, (UV4HI, UV8QI, UV8QI))
+
+DEF_MIPS_FTYPE (2, (UV8QI, UV4HI, UV4HI))
+DEF_MIPS_FTYPE (1, (UV8QI, UV8QI))
+DEF_MIPS_FTYPE (2, (UV8QI, UV8QI, UV8QI))
+
+DEF_MIPS_FTYPE (1, (V2HI, SI))
+DEF_MIPS_FTYPE (2, (V2HI, SI, SI))
+DEF_MIPS_FTYPE (3, (V2HI, SI, SI, SI))
+DEF_MIPS_FTYPE (1, (V2HI, V2HI))
+DEF_MIPS_FTYPE (2, (V2HI, V2HI, SI))
+DEF_MIPS_FTYPE (2, (V2HI, V2HI, V2HI))
+DEF_MIPS_FTYPE (1, (V2HI, V4QI))
+DEF_MIPS_FTYPE (2, (V2HI, V4QI, V2HI))
+
+DEF_MIPS_FTYPE (2, (V2SF, SF, SF))
+DEF_MIPS_FTYPE (1, (V2SF, V2SF))
+DEF_MIPS_FTYPE (2, (V2SF, V2SF, V2SF))
+DEF_MIPS_FTYPE (3, (V2SF, V2SF, V2SF, INT))
+DEF_MIPS_FTYPE (4, (V2SF, V2SF, V2SF, V2SF, V2SF))
+
+DEF_MIPS_FTYPE (2, (V2SI, V2SI, UQI))
+DEF_MIPS_FTYPE (2, (V2SI, V2SI, V2SI))
+DEF_MIPS_FTYPE (2, (V2SI, V4HI, V4HI))
+
+DEF_MIPS_FTYPE (2, (V4HI, V2SI, V2SI))
+DEF_MIPS_FTYPE (2, (V4HI, V4HI, UQI))
+DEF_MIPS_FTYPE (2, (V4HI, V4HI, USI))
+DEF_MIPS_FTYPE (2, (V4HI, V4HI, V4HI))
+DEF_MIPS_FTYPE (3, (V4HI, V4HI, V4HI, UQI))
+DEF_MIPS_FTYPE (3, (V4HI, V4HI, V4HI, USI))
+
+DEF_MIPS_FTYPE (1, (V4QI, SI))
+DEF_MIPS_FTYPE (2, (V4QI, V2HI, V2HI))
+DEF_MIPS_FTYPE (1, (V4QI, V4QI))
+DEF_MIPS_FTYPE (2, (V4QI, V4QI, SI))
+DEF_MIPS_FTYPE (2, (V4QI, V4QI, V4QI))
+
+DEF_MIPS_FTYPE (2, (V8QI, V4HI, V4HI))
+DEF_MIPS_FTYPE (1, (V8QI, V8QI))
+DEF_MIPS_FTYPE (2, (V8QI, V8QI, V8QI))
+
+DEF_MIPS_FTYPE (2, (VOID, SI, CVPOINTER))
+DEF_MIPS_FTYPE (2, (VOID, SI, SI))
+DEF_MIPS_FTYPE (2, (VOID, V2HI, V2HI))
+DEF_MIPS_FTYPE (2, (VOID, V4QI, V4QI))
diff --git a/gcc/config/mips/mips-modes.def b/gcc/config/mips/mips-modes.def
new file mode 100644
index 000000000..b9c508b5c
--- /dev/null
+++ b/gcc/config/mips/mips-modes.def
@@ -0,0 +1,48 @@
+/* MIPS extra machine modes.
+ Copyright (C) 2003, 2004, 2007, 2008 Free Software Foundation, 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/>. */
+
+/* MIPS has a quirky almost-IEEE format for all its
+ floating point. */
+RESET_FLOAT_FORMAT (SF, mips_single_format);
+RESET_FLOAT_FORMAT (DF, mips_double_format);
+
+/* Irix6 will override this via MIPS_TFMODE_FORMAT. */
+FLOAT_MODE (TF, 16, mips_quad_format);
+
+/* Vector modes. */
+VECTOR_MODES (INT, 8); /* V8QI V4HI V2SI */
+VECTOR_MODES (FLOAT, 8); /* V4HF V2SF */
+VECTOR_MODES (INT, 4); /* V4QI V2HI */
+
+VECTOR_MODES (FRACT, 4); /* V4QQ V2HQ */
+VECTOR_MODES (UFRACT, 4); /* V4UQQ V2UHQ */
+VECTOR_MODES (ACCUM, 4); /* V2HA */
+VECTOR_MODES (UACCUM, 4); /* V2UHA */
+
+/* Paired single comparison instructions use 2 or 4 CC. */
+CC_MODE (CCV2);
+ADJUST_BYTESIZE (CCV2, 8);
+ADJUST_ALIGNMENT (CCV2, 8);
+
+CC_MODE (CCV4);
+ADJUST_BYTESIZE (CCV4, 16);
+ADJUST_ALIGNMENT (CCV4, 16);
+
+/* For MIPS DSP control registers. */
+CC_MODE (CCDSP);
diff --git a/gcc/config/mips/mips-protos.h b/gcc/config/mips/mips-protos.h
new file mode 100644
index 000000000..611459884
--- /dev/null
+++ b/gcc/config/mips/mips-protos.h
@@ -0,0 +1,341 @@
+/* Prototypes of target machine for GNU compiler. MIPS version.
+ Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
+ 1999, 2001, 2002, 2003, 2004, 2005, 2007, 2008, 2009, 2010, 2011
+ Free Software Foundation, Inc.
+ Contributed by A. Lichnewsky (lich@inria.inria.fr).
+ Changed by Michael Meissner (meissner@osf.org).
+ 64-bit r4000 support by Ian Lance Taylor (ian@cygnus.com) and
+ Brendan Eich (brendan@microunity.com).
+
+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/>. */
+
+#ifndef GCC_MIPS_PROTOS_H
+#define GCC_MIPS_PROTOS_H
+
+/* Describes how a symbol is used.
+
+ SYMBOL_CONTEXT_CALL
+ The symbol is used as the target of a call instruction.
+
+ SYMBOL_CONTEXT_LEA
+ The symbol is used in a load-address operation.
+
+ SYMBOL_CONTEXT_MEM
+ The symbol is used as the address in a MEM. */
+enum mips_symbol_context {
+ SYMBOL_CONTEXT_CALL,
+ SYMBOL_CONTEXT_LEA,
+ SYMBOL_CONTEXT_MEM
+};
+
+/* Classifies a SYMBOL_REF, LABEL_REF or UNSPEC address.
+
+ SYMBOL_ABSOLUTE
+ The symbol's value will be calculated using absolute relocations,
+ such as %hi and %lo.
+
+ SYMBOL_GP_RELATIVE
+ The symbol's value will be calculated by adding a 16-bit offset
+ from $gp.
+
+ SYMBOL_PC_RELATIVE
+ The symbol's value will be calculated using a MIPS16 PC-relative
+ calculation.
+
+ SYMBOL_FORCE_TO_MEM
+ The symbol's value must be forced to memory and loaded from there.
+
+ SYMBOL_GOT_PAGE_OFST
+ The symbol's value will be calculated by loading an address
+ from the GOT and then applying a 16-bit offset.
+
+ SYMBOL_GOT_DISP
+ The symbol's value will be loaded directly from the GOT.
+
+ SYMBOL_GOTOFF_PAGE
+ An UNSPEC wrapper around a SYMBOL_GOT_PAGE_OFST. It represents the
+ offset from _gp of the GOT entry.
+
+ SYMBOL_GOTOFF_DISP
+ An UNSPEC wrapper around a SYMBOL_GOT_DISP. It represents the
+ the offset from _gp of the symbol's GOT entry.
+
+ SYMBOL_GOTOFF_CALL
+ Like SYMBOL_GOTOFF_DISP, but used when calling a global function.
+ The GOT entry is allowed to point to a stub rather than to the
+ function itself.
+
+ SYMBOL_GOTOFF_LOADGP
+ An UNSPEC wrapper around a function's address. It represents the
+ offset of _gp from the start of the function.
+
+ SYMBOL_TLS
+ A thread-local symbol.
+
+ SYMBOL_TLSGD
+ SYMBOL_TLSLDM
+ SYMBOL_DTPREL
+ SYMBOL_GOTTPREL
+ SYMBOL_TPREL
+ UNSPEC wrappers around SYMBOL_TLS, corresponding to the
+ thread-local storage relocation operators.
+
+ SYMBOL_32_HIGH
+ For a 32-bit symbolic address X, this is the value of %hi(X).
+
+ SYMBOL_64_HIGH
+ For a 64-bit symbolic address X, this is the value of
+ (%highest(X) << 16) + %higher(X).
+
+ SYMBOL_64_MID
+ For a 64-bit symbolic address X, this is the value of
+ (%higher(X) << 16) + %hi(X).
+
+ SYMBOL_64_LOW
+ For a 64-bit symbolic address X, this is the value of
+ (%hi(X) << 16) + %lo(X).
+
+ SYMBOL_HALF
+ An UNSPEC wrapper around any kind of address. It represents the
+ low 16 bits of that address. */
+enum mips_symbol_type {
+ SYMBOL_ABSOLUTE,
+ SYMBOL_GP_RELATIVE,
+ SYMBOL_PC_RELATIVE,
+ SYMBOL_FORCE_TO_MEM,
+ SYMBOL_GOT_PAGE_OFST,
+ SYMBOL_GOT_DISP,
+ SYMBOL_GOTOFF_PAGE,
+ SYMBOL_GOTOFF_DISP,
+ SYMBOL_GOTOFF_CALL,
+ SYMBOL_GOTOFF_LOADGP,
+ SYMBOL_TLS,
+ SYMBOL_TLSGD,
+ SYMBOL_TLSLDM,
+ SYMBOL_DTPREL,
+ SYMBOL_GOTTPREL,
+ SYMBOL_TPREL,
+ SYMBOL_32_HIGH,
+ SYMBOL_64_HIGH,
+ SYMBOL_64_MID,
+ SYMBOL_64_LOW,
+ SYMBOL_HALF
+};
+#define NUM_SYMBOL_TYPES (SYMBOL_HALF + 1)
+
+/* Identifiers a style of $gp initialization sequence.
+
+ LOADGP_NONE
+ No initialization sequence is needed.
+
+ LOADGP_OLDABI
+ The o32 and o64 PIC sequence (the kind traditionally generated
+ by .cpload).
+
+ LOADGP_NEWABI
+ The n32 and n64 PIC sequence (the kind traditionally generated
+ by .cpsetup).
+
+ LOADGP_ABSOLUTE
+ The GNU absolute sequence, as generated by loadgp_absolute.
+
+ LOADGP_RTP
+ The VxWorks RTP PIC sequence, as generated by loadgp_rtp. */
+enum mips_loadgp_style {
+ LOADGP_NONE,
+ LOADGP_OLDABI,
+ LOADGP_NEWABI,
+ LOADGP_ABSOLUTE,
+ LOADGP_RTP
+};
+
+struct mips16e_save_restore_info;
+
+/* Classifies a type of call.
+
+ MIPS_CALL_NORMAL
+ A normal call or call_value pattern.
+
+ MIPS_CALL_SIBCALL
+ A sibcall or sibcall_value pattern.
+
+ MIPS_CALL_EPILOGUE
+ A call inserted in the epilogue. */
+enum mips_call_type {
+ MIPS_CALL_NORMAL,
+ MIPS_CALL_SIBCALL,
+ MIPS_CALL_EPILOGUE
+};
+
+extern bool mips_symbolic_constant_p (rtx, enum mips_symbol_context,
+ enum mips_symbol_type *);
+extern int mips_regno_mode_ok_for_base_p (int, enum machine_mode, bool);
+extern bool mips_stack_address_p (rtx, enum machine_mode);
+extern int mips_address_insns (rtx, enum machine_mode, bool);
+extern int mips_const_insns (rtx);
+extern int mips_split_const_insns (rtx);
+extern int mips_load_store_insns (rtx, rtx);
+extern int mips_idiv_insns (void);
+extern rtx mips_emit_move (rtx, rtx);
+extern rtx mips_pic_base_register (rtx);
+extern rtx mips_got_load (rtx, rtx, enum mips_symbol_type);
+extern bool mips_split_symbol (rtx, rtx, enum machine_mode, rtx *);
+extern rtx mips_unspec_address (rtx, enum mips_symbol_type);
+extern void mips_move_integer (rtx, rtx, unsigned HOST_WIDE_INT);
+extern bool mips_legitimize_move (enum machine_mode, rtx, rtx);
+
+extern int m16_uimm3_b (rtx, enum machine_mode);
+extern int m16_simm4_1 (rtx, enum machine_mode);
+extern int m16_nsimm4_1 (rtx, enum machine_mode);
+extern int m16_simm5_1 (rtx, enum machine_mode);
+extern int m16_nsimm5_1 (rtx, enum machine_mode);
+extern int m16_uimm5_4 (rtx, enum machine_mode);
+extern int m16_nuimm5_4 (rtx, enum machine_mode);
+extern int m16_simm8_1 (rtx, enum machine_mode);
+extern int m16_nsimm8_1 (rtx, enum machine_mode);
+extern int m16_uimm8_1 (rtx, enum machine_mode);
+extern int m16_nuimm8_1 (rtx, enum machine_mode);
+extern int m16_uimm8_m1_1 (rtx, enum machine_mode);
+extern int m16_uimm8_4 (rtx, enum machine_mode);
+extern int m16_nuimm8_4 (rtx, enum machine_mode);
+extern int m16_simm8_8 (rtx, enum machine_mode);
+extern int m16_nsimm8_8 (rtx, enum machine_mode);
+
+extern rtx mips_subword (rtx, bool);
+extern bool mips_split_64bit_move_p (rtx, rtx);
+extern void mips_split_doubleword_move (rtx, rtx);
+extern const char *mips_output_move (rtx, rtx);
+extern bool mips_cfun_has_cprestore_slot_p (void);
+extern bool mips_cprestore_address_p (rtx, bool);
+extern void mips_save_gp_to_cprestore_slot (rtx, rtx, rtx, rtx);
+extern void mips_restore_gp_from_cprestore_slot (rtx);
+#ifdef RTX_CODE
+extern void mips_expand_scc (rtx *);
+extern void mips_expand_conditional_branch (rtx *);
+extern void mips_expand_vcondv2sf (rtx, rtx, rtx, enum rtx_code, rtx, rtx);
+extern void mips_expand_conditional_move (rtx *);
+extern void mips_expand_conditional_trap (rtx);
+#endif
+extern bool mips_use_pic_fn_addr_reg_p (const_rtx);
+extern rtx mips_expand_call (enum mips_call_type, rtx, rtx, rtx, rtx, bool);
+extern void mips_split_call (rtx, rtx);
+extern bool mips_get_pic_call_symbol (rtx *, int);
+extern void mips_expand_fcc_reload (rtx, rtx, rtx);
+extern void mips_set_return_address (rtx, rtx);
+extern bool mips_expand_block_move (rtx, rtx, rtx);
+extern void mips_expand_synci_loop (rtx, rtx);
+
+extern void mips_init_cumulative_args (CUMULATIVE_ARGS *, tree);
+extern bool mips_pad_arg_upward (enum machine_mode, const_tree);
+extern bool mips_pad_reg_upward (enum machine_mode, tree);
+
+extern bool mips_expand_ext_as_unaligned_load (rtx, rtx, HOST_WIDE_INT,
+ HOST_WIDE_INT);
+extern bool mips_expand_ins_as_unaligned_store (rtx, rtx, HOST_WIDE_INT,
+ HOST_WIDE_INT);
+extern bool mips_mem_fits_mode_p (enum machine_mode mode, rtx x);
+extern void mips_order_regs_for_local_alloc (void);
+extern HOST_WIDE_INT mips_debugger_offset (rtx, HOST_WIDE_INT);
+
+extern void mips_push_asm_switch (struct mips_asm_switch *);
+extern void mips_pop_asm_switch (struct mips_asm_switch *);
+extern void mips_output_external (FILE *, tree, const char *);
+extern void mips_output_ascii (FILE *, const char *, size_t);
+extern void mips_output_aligned_decl_common (FILE *, tree, const char *,
+ unsigned HOST_WIDE_INT,
+ unsigned int);
+extern void mips_declare_common_object (FILE *, const char *,
+ const char *, unsigned HOST_WIDE_INT,
+ unsigned int, bool);
+extern void mips_declare_object (FILE *, const char *, const char *,
+ const char *, ...) ATTRIBUTE_PRINTF_4;
+extern void mips_declare_object_name (FILE *, const char *, tree);
+extern void mips_finish_declare_object (FILE *, tree, int, int);
+
+extern bool mips_small_data_pattern_p (rtx);
+extern rtx mips_rewrite_small_data (rtx);
+extern HOST_WIDE_INT mips_initial_elimination_offset (int, int);
+extern rtx mips_return_addr (int, rtx);
+extern bool mips_must_initialize_gp_p (void);
+extern enum mips_loadgp_style mips_current_loadgp_style (void);
+extern void mips_emit_save_slot_move (rtx, rtx, rtx);
+extern void mips_expand_prologue (void);
+extern void mips_expand_before_return (void);
+extern void mips_expand_epilogue (bool);
+extern bool mips_can_use_return_insn (void);
+extern rtx mips_function_value (const_tree, const_tree, enum machine_mode);
+
+extern bool mips_cannot_change_mode_class (enum machine_mode,
+ enum machine_mode, enum reg_class);
+extern bool mips_dangerous_for_la25_p (rtx);
+extern bool mips_modes_tieable_p (enum machine_mode, enum machine_mode);
+extern enum reg_class mips_secondary_reload_class (enum reg_class,
+ enum machine_mode,
+ rtx, bool);
+extern int mips_class_max_nregs (enum reg_class, enum machine_mode);
+
+extern int mips_adjust_insn_length (rtx, int);
+extern void mips_output_load_label (rtx);
+extern const char *mips_output_conditional_branch (rtx, rtx *, const char *,
+ const char *);
+extern const char *mips_output_order_conditional_branch (rtx, rtx *, bool);
+extern const char *mips_output_sync (void);
+extern const char *mips_output_sync_loop (rtx, rtx *);
+extern unsigned int mips_sync_loop_insns (rtx, rtx *);
+extern const char *mips_output_division (const char *, rtx *);
+extern unsigned int mips_hard_regno_nregs (int, enum machine_mode);
+extern bool mips_linked_madd_p (rtx, rtx);
+extern bool mips_store_data_bypass_p (rtx, rtx);
+extern rtx mips_prefetch_cookie (rtx, rtx);
+
+extern void irix_asm_output_align (FILE *, unsigned);
+extern const char *current_section_name (void);
+extern unsigned int current_section_flags (void);
+extern bool mips_use_ins_ext_p (rtx, HOST_WIDE_INT, HOST_WIDE_INT);
+
+extern const char *mips16e_output_save_restore (rtx, HOST_WIDE_INT);
+extern bool mips16e_save_restore_pattern_p (rtx, HOST_WIDE_INT,
+ struct mips16e_save_restore_info *);
+
+extern bool mask_low_and_shift_p (enum machine_mode, rtx, rtx, int);
+extern int mask_low_and_shift_len (enum machine_mode, rtx, rtx);
+extern bool and_operands_ok (enum machine_mode, rtx, rtx);
+
+union mips_gen_fn_ptrs
+{
+ rtx (*fn_6) (rtx, rtx, rtx, rtx, rtx, rtx);
+ rtx (*fn_5) (rtx, rtx, rtx, rtx, rtx);
+ rtx (*fn_4) (rtx, rtx, rtx, rtx);
+};
+
+extern void mips_expand_atomic_qihi (union mips_gen_fn_ptrs,
+ rtx, rtx, rtx, rtx);
+
+extern void mips_expand_vector_init (rtx, rtx);
+
+extern bool mips_eh_uses (unsigned int);
+extern bool mips_epilogue_uses (unsigned int);
+extern void mips_final_prescan_insn (rtx, rtx *, int);
+extern int mips_trampoline_code_size (void);
+extern void mips_function_profiler (FILE *);
+
+typedef rtx (*mulsidi3_gen_fn) (rtx, rtx, rtx);
+#ifdef RTX_CODE
+extern mulsidi3_gen_fn mips_mulsidi3_gen_fn (enum rtx_code);
+#endif
+
+#endif /* ! GCC_MIPS_PROTOS_H */
diff --git a/gcc/config/mips/mips-ps-3d.md b/gcc/config/mips/mips-ps-3d.md
new file mode 100644
index 000000000..8e942307c
--- /dev/null
+++ b/gcc/config/mips/mips-ps-3d.md
@@ -0,0 +1,635 @@
+;; MIPS Paired-Single Floating and MIPS-3D Instructions.
+;; Copyright (C) 2004, 2007, 2010 Free Software Foundation, 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/>.
+
+(define_c_enum "unspec" [
+ UNSPEC_MOVE_TF_PS
+ UNSPEC_C
+
+ ;; MIPS64/MIPS32R2 alnv.ps
+ UNSPEC_ALNV_PS
+
+ ;; MIPS-3D instructions
+ UNSPEC_CABS
+
+ UNSPEC_ADDR_PS
+ UNSPEC_CVT_PW_PS
+ UNSPEC_CVT_PS_PW
+ UNSPEC_MULR_PS
+ UNSPEC_ABS_PS
+
+ UNSPEC_RSQRT1
+ UNSPEC_RSQRT2
+ UNSPEC_RECIP1
+ UNSPEC_RECIP2
+ UNSPEC_SINGLE_CC
+ UNSPEC_SCC
+])
+
+(define_insn "*movcc_v2sf_<mode>"
+ [(set (match_operand:V2SF 0 "register_operand" "=f,f")
+ (if_then_else:V2SF
+ (match_operator:GPR 4 "equality_operator"
+ [(match_operand:GPR 1 "register_operand" "d,d")
+ (const_int 0)])
+ (match_operand:V2SF 2 "register_operand" "f,0")
+ (match_operand:V2SF 3 "register_operand" "0,f")))]
+ "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
+ "@
+ mov%T4.ps\t%0,%2,%1
+ mov%t4.ps\t%0,%3,%1"
+ [(set_attr "type" "condmove")
+ (set_attr "mode" "SF")])
+
+(define_insn "mips_cond_move_tf_ps"
+ [(set (match_operand:V2SF 0 "register_operand" "=f,f")
+ (unspec:V2SF [(match_operand:V2SF 1 "register_operand" "f,0")
+ (match_operand:V2SF 2 "register_operand" "0,f")
+ (match_operand:CCV2 3 "register_operand" "z,z")]
+ UNSPEC_MOVE_TF_PS))]
+ "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
+ "@
+ movt.ps\t%0,%1,%3
+ movf.ps\t%0,%2,%3"
+ [(set_attr "type" "condmove")
+ (set_attr "mode" "SF")])
+
+(define_expand "movv2sfcc"
+ [(set (match_dup 4) (match_operand 1 "comparison_operator"))
+ (set (match_operand:V2SF 0 "register_operand")
+ (if_then_else:V2SF (match_dup 5)
+ (match_operand:V2SF 2 "register_operand")
+ (match_operand:V2SF 3 "register_operand")))]
+ "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
+{
+ /* We can only support MOVN.PS and MOVZ.PS.
+ NOTE: MOVT.PS and MOVF.PS have different semantics from MOVN.PS and
+ MOVZ.PS. MOVT.PS and MOVF.PS depend on two CC values and move
+ each item independently. */
+
+ if (GET_MODE_CLASS (GET_MODE (XEXP (operands[1], 0))) != MODE_INT)
+ FAIL;
+
+ mips_expand_conditional_move (operands);
+ DONE;
+})
+
+; pul.ps - Pair Upper Lower
+(define_insn "mips_pul_ps"
+ [(set (match_operand:V2SF 0 "register_operand" "=f")
+ (vec_merge:V2SF
+ (match_operand:V2SF 1 "register_operand" "f")
+ (match_operand:V2SF 2 "register_operand" "f")
+ (const_int 2)))]
+ "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
+ "pul.ps\t%0,%1,%2"
+ [(set_attr "type" "fmove")
+ (set_attr "mode" "SF")])
+
+; puu.ps - Pair upper upper
+(define_insn "mips_puu_ps"
+ [(set (match_operand:V2SF 0 "register_operand" "=f")
+ (vec_merge:V2SF
+ (match_operand:V2SF 1 "register_operand" "f")
+ (vec_select:V2SF (match_operand:V2SF 2 "register_operand" "f")
+ (parallel [(const_int 1)
+ (const_int 0)]))
+ (const_int 2)))]
+ "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
+ "puu.ps\t%0,%1,%2"
+ [(set_attr "type" "fmove")
+ (set_attr "mode" "SF")])
+
+; pll.ps - Pair Lower Lower
+(define_insn "mips_pll_ps"
+ [(set (match_operand:V2SF 0 "register_operand" "=f")
+ (vec_merge:V2SF
+ (vec_select:V2SF (match_operand:V2SF 1 "register_operand" "f")
+ (parallel [(const_int 1)
+ (const_int 0)]))
+ (match_operand:V2SF 2 "register_operand" "f")
+ (const_int 2)))]
+ "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
+ "pll.ps\t%0,%1,%2"
+ [(set_attr "type" "fmove")
+ (set_attr "mode" "SF")])
+
+; plu.ps - Pair Lower Upper
+(define_insn "mips_plu_ps"
+ [(set (match_operand:V2SF 0 "register_operand" "=f")
+ (vec_merge:V2SF
+ (vec_select:V2SF (match_operand:V2SF 1 "register_operand" "f")
+ (parallel [(const_int 1)
+ (const_int 0)]))
+ (vec_select:V2SF (match_operand:V2SF 2 "register_operand" "f")
+ (parallel [(const_int 1)
+ (const_int 0)]))
+ (const_int 2)))]
+ "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
+ "plu.ps\t%0,%1,%2"
+ [(set_attr "type" "fmove")
+ (set_attr "mode" "SF")])
+
+; vec_init
+(define_expand "vec_initv2sf"
+ [(match_operand:V2SF 0 "register_operand")
+ (match_operand:V2SF 1 "")]
+ "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
+{
+ rtx op0 = force_reg (SFmode, XVECEXP (operands[1], 0, 0));
+ rtx op1 = force_reg (SFmode, XVECEXP (operands[1], 0, 1));
+ emit_insn (gen_vec_initv2sf_internal (operands[0], op0, op1));
+ DONE;
+})
+
+(define_insn "vec_initv2sf_internal"
+ [(set (match_operand:V2SF 0 "register_operand" "=f")
+ (vec_concat:V2SF
+ (match_operand:SF 1 "register_operand" "f")
+ (match_operand:SF 2 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
+{
+ if (BYTES_BIG_ENDIAN)
+ return "cvt.ps.s\t%0,%1,%2";
+ else
+ return "cvt.ps.s\t%0,%2,%1";
+}
+ [(set_attr "type" "fcvt")
+ (set_attr "mode" "SF")])
+
+;; ??? This is only generated if we perform a vector operation that has to be
+;; emulated. There is no other way to get a vector mode bitfield extract
+;; currently.
+
+(define_insn "vec_extractv2sf"
+ [(set (match_operand:SF 0 "register_operand" "=f")
+ (vec_select:SF (match_operand:V2SF 1 "register_operand" "f")
+ (parallel
+ [(match_operand 2 "const_0_or_1_operand" "")])))]
+ "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
+{
+ if (INTVAL (operands[2]) == !BYTES_BIG_ENDIAN)
+ return "cvt.s.pu\t%0,%1";
+ else
+ return "cvt.s.pl\t%0,%1";
+}
+ [(set_attr "type" "fcvt")
+ (set_attr "mode" "SF")])
+
+;; ??? This is only generated if we disable the vec_init pattern. There is
+;; no other way to get a vector mode bitfield store currently.
+
+(define_expand "vec_setv2sf"
+ [(match_operand:V2SF 0 "register_operand")
+ (match_operand:SF 1 "register_operand")
+ (match_operand 2 "const_0_or_1_operand")]
+ "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
+{
+ rtx temp;
+
+ /* We don't have an insert instruction, so we duplicate the float, and
+ then use a PUL instruction. */
+ temp = gen_reg_rtx (V2SFmode);
+ emit_insn (gen_mips_cvt_ps_s (temp, operands[1], operands[1]));
+ if (INTVAL (operands[2]) == !BYTES_BIG_ENDIAN)
+ emit_insn (gen_mips_pul_ps (operands[0], temp, operands[0]));
+ else
+ emit_insn (gen_mips_pul_ps (operands[0], operands[0], temp));
+ DONE;
+})
+
+; cvt.ps.s - Floating Point Convert Pair to Paired Single
+(define_expand "mips_cvt_ps_s"
+ [(match_operand:V2SF 0 "register_operand")
+ (match_operand:SF 1 "register_operand")
+ (match_operand:SF 2 "register_operand")]
+ "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
+{
+ if (BYTES_BIG_ENDIAN)
+ emit_insn (gen_vec_initv2sf_internal (operands[0], operands[1],
+ operands[2]));
+ else
+ emit_insn (gen_vec_initv2sf_internal (operands[0], operands[2],
+ operands[1]));
+ DONE;
+})
+
+; cvt.s.pl - Floating Point Convert Pair Lower to Single Floating Point
+(define_expand "mips_cvt_s_pl"
+ [(set (match_operand:SF 0 "register_operand")
+ (vec_select:SF (match_operand:V2SF 1 "register_operand")
+ (parallel [(match_dup 2)])))]
+ "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
+ { operands[2] = GEN_INT (BYTES_BIG_ENDIAN); })
+
+; cvt.s.pu - Floating Point Convert Pair Upper to Single Floating Point
+(define_expand "mips_cvt_s_pu"
+ [(set (match_operand:SF 0 "register_operand")
+ (vec_select:SF (match_operand:V2SF 1 "register_operand")
+ (parallel [(match_dup 2)])))]
+ "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
+ { operands[2] = GEN_INT (!BYTES_BIG_ENDIAN); })
+
+; alnv.ps - Floating Point Align Variable
+(define_insn "mips_alnv_ps"
+ [(set (match_operand:V2SF 0 "register_operand" "=f")
+ (unspec:V2SF [(match_operand:V2SF 1 "register_operand" "f")
+ (match_operand:V2SF 2 "register_operand" "f")
+ (match_operand:SI 3 "register_operand" "d")]
+ UNSPEC_ALNV_PS))]
+ "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
+ "alnv.ps\t%0,%1,%2,%3"
+ [(set_attr "type" "fmove")
+ (set_attr "mode" "SF")])
+
+; addr.ps - Floating Point Reduction Add
+(define_insn "mips_addr_ps"
+ [(set (match_operand:V2SF 0 "register_operand" "=f")
+ (unspec:V2SF [(match_operand:V2SF 1 "register_operand" "f")
+ (match_operand:V2SF 2 "register_operand" "f")]
+ UNSPEC_ADDR_PS))]
+ "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
+ "addr.ps\t%0,%1,%2"
+ [(set_attr "type" "fadd")
+ (set_attr "mode" "SF")])
+
+; cvt.pw.ps - Floating Point Convert Paired Single to Paired Word
+(define_insn "mips_cvt_pw_ps"
+ [(set (match_operand:V2SF 0 "register_operand" "=f")
+ (unspec:V2SF [(match_operand:V2SF 1 "register_operand" "f")]
+ UNSPEC_CVT_PW_PS))]
+ "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
+ "cvt.pw.ps\t%0,%1"
+ [(set_attr "type" "fcvt")
+ (set_attr "mode" "SF")])
+
+; cvt.ps.pw - Floating Point Convert Paired Word to Paired Single
+(define_insn "mips_cvt_ps_pw"
+ [(set (match_operand:V2SF 0 "register_operand" "=f")
+ (unspec:V2SF [(match_operand:V2SF 1 "register_operand" "f")]
+ UNSPEC_CVT_PS_PW))]
+ "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
+ "cvt.ps.pw\t%0,%1"
+ [(set_attr "type" "fcvt")
+ (set_attr "mode" "SF")])
+
+; mulr.ps - Floating Point Reduction Multiply
+(define_insn "mips_mulr_ps"
+ [(set (match_operand:V2SF 0 "register_operand" "=f")
+ (unspec:V2SF [(match_operand:V2SF 1 "register_operand" "f")
+ (match_operand:V2SF 2 "register_operand" "f")]
+ UNSPEC_MULR_PS))]
+ "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
+ "mulr.ps\t%0,%1,%2"
+ [(set_attr "type" "fmul")
+ (set_attr "mode" "SF")])
+
+; abs.ps
+(define_expand "mips_abs_ps"
+ [(set (match_operand:V2SF 0 "register_operand")
+ (unspec:V2SF [(match_operand:V2SF 1 "register_operand")]
+ UNSPEC_ABS_PS))]
+ "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
+{
+ /* If we can ignore NaNs, this operation is equivalent to the
+ rtl ABS code. */
+ if (!HONOR_NANS (V2SFmode))
+ {
+ emit_insn (gen_absv2sf2 (operands[0], operands[1]));
+ DONE;
+ }
+})
+
+(define_insn "*mips_abs_ps"
+ [(set (match_operand:V2SF 0 "register_operand" "=f")
+ (unspec:V2SF [(match_operand:V2SF 1 "register_operand" "f")]
+ UNSPEC_ABS_PS))]
+ "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
+ "abs.ps\t%0,%1"
+ [(set_attr "type" "fabs")
+ (set_attr "mode" "SF")])
+
+;----------------------------------------------------------------------------
+; Floating Point Comparisons for Scalars
+;----------------------------------------------------------------------------
+
+(define_insn "mips_cabs_cond_<fmt>"
+ [(set (match_operand:CC 0 "register_operand" "=z")
+ (unspec:CC [(match_operand:SCALARF 1 "register_operand" "f")
+ (match_operand:SCALARF 2 "register_operand" "f")
+ (match_operand 3 "const_int_operand" "")]
+ UNSPEC_CABS))]
+ "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
+ "cabs.%Y3.<fmt>\t%0,%1,%2"
+ [(set_attr "type" "fcmp")
+ (set_attr "mode" "FPSW")])
+
+
+;----------------------------------------------------------------------------
+; Floating Point Comparisons for Four Singles
+;----------------------------------------------------------------------------
+
+(define_insn_and_split "mips_c_cond_4s"
+ [(set (match_operand:CCV4 0 "register_operand" "=z")
+ (unspec:CCV4 [(match_operand:V2SF 1 "register_operand" "f")
+ (match_operand:V2SF 2 "register_operand" "f")
+ (match_operand:V2SF 3 "register_operand" "f")
+ (match_operand:V2SF 4 "register_operand" "f")
+ (match_operand 5 "const_int_operand" "")]
+ UNSPEC_C))]
+ "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
+ "#"
+ "&& reload_completed"
+ [(set (match_dup 6)
+ (unspec:CCV2 [(match_dup 1)
+ (match_dup 2)
+ (match_dup 5)]
+ UNSPEC_C))
+ (set (match_dup 7)
+ (unspec:CCV2 [(match_dup 3)
+ (match_dup 4)
+ (match_dup 5)]
+ UNSPEC_C))]
+{
+ operands[6] = simplify_gen_subreg (CCV2mode, operands[0], CCV4mode, 0);
+ operands[7] = simplify_gen_subreg (CCV2mode, operands[0], CCV4mode, 8);
+}
+ [(set_attr "type" "fcmp")
+ (set_attr "length" "8")
+ (set_attr "mode" "FPSW")])
+
+(define_insn_and_split "mips_cabs_cond_4s"
+ [(set (match_operand:CCV4 0 "register_operand" "=z")
+ (unspec:CCV4 [(match_operand:V2SF 1 "register_operand" "f")
+ (match_operand:V2SF 2 "register_operand" "f")
+ (match_operand:V2SF 3 "register_operand" "f")
+ (match_operand:V2SF 4 "register_operand" "f")
+ (match_operand 5 "const_int_operand" "")]
+ UNSPEC_CABS))]
+ "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
+ "#"
+ "&& reload_completed"
+ [(set (match_dup 6)
+ (unspec:CCV2 [(match_dup 1)
+ (match_dup 2)
+ (match_dup 5)]
+ UNSPEC_CABS))
+ (set (match_dup 7)
+ (unspec:CCV2 [(match_dup 3)
+ (match_dup 4)
+ (match_dup 5)]
+ UNSPEC_CABS))]
+{
+ operands[6] = simplify_gen_subreg (CCV2mode, operands[0], CCV4mode, 0);
+ operands[7] = simplify_gen_subreg (CCV2mode, operands[0], CCV4mode, 8);
+}
+ [(set_attr "type" "fcmp")
+ (set_attr "length" "8")
+ (set_attr "mode" "FPSW")])
+
+
+;----------------------------------------------------------------------------
+; Floating Point Comparisons for Paired Singles
+;----------------------------------------------------------------------------
+
+(define_insn "mips_c_cond_ps"
+ [(set (match_operand:CCV2 0 "register_operand" "=z")
+ (unspec:CCV2 [(match_operand:V2SF 1 "register_operand" "f")
+ (match_operand:V2SF 2 "register_operand" "f")
+ (match_operand 3 "const_int_operand" "")]
+ UNSPEC_C))]
+ "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
+ "c.%Y3.ps\t%0,%1,%2"
+ [(set_attr "type" "fcmp")
+ (set_attr "mode" "FPSW")])
+
+(define_insn "mips_cabs_cond_ps"
+ [(set (match_operand:CCV2 0 "register_operand" "=z")
+ (unspec:CCV2 [(match_operand:V2SF 1 "register_operand" "f")
+ (match_operand:V2SF 2 "register_operand" "f")
+ (match_operand 3 "const_int_operand" "")]
+ UNSPEC_CABS))]
+ "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
+ "cabs.%Y3.ps\t%0,%1,%2"
+ [(set_attr "type" "fcmp")
+ (set_attr "mode" "FPSW")])
+
+;; An expander for generating an scc operation.
+(define_expand "scc_ps"
+ [(set (match_operand:CCV2 0)
+ (unspec:CCV2 [(match_operand 1)] UNSPEC_SCC))])
+
+(define_insn "s<code>_ps"
+ [(set (match_operand:CCV2 0 "register_operand" "=z")
+ (unspec:CCV2
+ [(fcond (match_operand:V2SF 1 "register_operand" "f")
+ (match_operand:V2SF 2 "register_operand" "f"))]
+ UNSPEC_SCC))]
+ "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
+ "c.<fcond>.ps\t%0,%1,%2"
+ [(set_attr "type" "fcmp")
+ (set_attr "mode" "FPSW")])
+
+(define_insn "s<code>_ps"
+ [(set (match_operand:CCV2 0 "register_operand" "=z")
+ (unspec:CCV2
+ [(swapped_fcond (match_operand:V2SF 1 "register_operand" "f")
+ (match_operand:V2SF 2 "register_operand" "f"))]
+ UNSPEC_SCC))]
+ "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
+ "c.<swapped_fcond>.ps\t%0,%2,%1"
+ [(set_attr "type" "fcmp")
+ (set_attr "mode" "FPSW")])
+
+;----------------------------------------------------------------------------
+; Floating Point Branch Instructions.
+;----------------------------------------------------------------------------
+
+; Branch on Any of Four Floating Point Condition Codes True
+(define_insn "bc1any4t"
+ [(set (pc)
+ (if_then_else (ne (match_operand:CCV4 1 "register_operand" "z")
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
+ "%*bc1any4t\t%1,%0%/"
+ [(set_attr "type" "branch")])
+
+; Branch on Any of Four Floating Point Condition Codes False
+(define_insn "bc1any4f"
+ [(set (pc)
+ (if_then_else (ne (match_operand:CCV4 1 "register_operand" "z")
+ (const_int -1))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
+ "%*bc1any4f\t%1,%0%/"
+ [(set_attr "type" "branch")])
+
+; Branch on Any of Two Floating Point Condition Codes True
+(define_insn "bc1any2t"
+ [(set (pc)
+ (if_then_else (ne (match_operand:CCV2 1 "register_operand" "z")
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
+ "%*bc1any2t\t%1,%0%/"
+ [(set_attr "type" "branch")])
+
+; Branch on Any of Two Floating Point Condition Codes False
+(define_insn "bc1any2f"
+ [(set (pc)
+ (if_then_else (ne (match_operand:CCV2 1 "register_operand" "z")
+ (const_int -1))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
+ "%*bc1any2f\t%1,%0%/"
+ [(set_attr "type" "branch")])
+
+; Used to access one register in a CCV2 pair. Operand 0 is the register
+; pair and operand 1 is the index of the register we want (a CONST_INT).
+(define_expand "single_cc"
+ [(ne (unspec:CC [(match_operand 0) (match_operand 1)] UNSPEC_SINGLE_CC)
+ (const_int 0))])
+
+; This is a normal floating-point branch pattern, but rather than check
+; a single CCmode register, it checks one register in a CCV2 pair.
+; Operand 2 is the register pair and operand 3 is the index of the
+; register we want.
+(define_insn "*branch_upper_lower"
+ [(set (pc)
+ (if_then_else
+ (match_operator 1 "equality_operator"
+ [(unspec:CC [(match_operand:CCV2 2 "register_operand" "z")
+ (match_operand 3 "const_int_operand")]
+ UNSPEC_SINGLE_CC)
+ (const_int 0)])
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ "TARGET_HARD_FLOAT"
+{
+ operands[2]
+ = gen_rtx_REG (CCmode, REGNO (operands[2]) + INTVAL (operands[3]));
+ return mips_output_conditional_branch (insn, operands,
+ MIPS_BRANCH ("b%F1", "%2,%0"),
+ MIPS_BRANCH ("b%W1", "%2,%0"));
+}
+ [(set_attr "type" "branch")])
+
+; As above, but with the sense of the condition reversed.
+(define_insn "*branch_upper_lower_inverted"
+ [(set (pc)
+ (if_then_else
+ (match_operator 1 "equality_operator"
+ [(unspec:CC [(match_operand:CCV2 2 "register_operand" "z")
+ (match_operand 3 "const_int_operand")]
+ UNSPEC_SINGLE_CC)
+ (const_int 0)])
+ (pc)
+ (label_ref (match_operand 0 "" ""))))]
+ "TARGET_HARD_FLOAT"
+{
+ operands[2]
+ = gen_rtx_REG (CCmode, REGNO (operands[2]) + INTVAL (operands[3]));
+ return mips_output_conditional_branch (insn, operands,
+ MIPS_BRANCH ("b%W1", "%2,%0"),
+ MIPS_BRANCH ("b%F1", "%2,%0"));
+}
+ [(set_attr "type" "branch")])
+
+;----------------------------------------------------------------------------
+; Floating Point Reduced Precision Reciprocal Square Root Instructions.
+;----------------------------------------------------------------------------
+
+(define_insn "mips_rsqrt1_<fmt>"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (unspec:ANYF [(match_operand:ANYF 1 "register_operand" "f")]
+ UNSPEC_RSQRT1))]
+ "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
+ "rsqrt1.<fmt>\t%0,%1"
+ [(set_attr "type" "frsqrt1")
+ (set_attr "mode" "<UNITMODE>")])
+
+(define_insn "mips_rsqrt2_<fmt>"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (unspec:ANYF [(match_operand:ANYF 1 "register_operand" "f")
+ (match_operand:ANYF 2 "register_operand" "f")]
+ UNSPEC_RSQRT2))]
+ "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
+ "rsqrt2.<fmt>\t%0,%1,%2"
+ [(set_attr "type" "frsqrt2")
+ (set_attr "mode" "<UNITMODE>")])
+
+(define_insn "mips_recip1_<fmt>"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (unspec:ANYF [(match_operand:ANYF 1 "register_operand" "f")]
+ UNSPEC_RECIP1))]
+ "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
+ "recip1.<fmt>\t%0,%1"
+ [(set_attr "type" "frdiv1")
+ (set_attr "mode" "<UNITMODE>")])
+
+(define_insn "mips_recip2_<fmt>"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (unspec:ANYF [(match_operand:ANYF 1 "register_operand" "f")
+ (match_operand:ANYF 2 "register_operand" "f")]
+ UNSPEC_RECIP2))]
+ "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
+ "recip2.<fmt>\t%0,%1,%2"
+ [(set_attr "type" "frdiv2")
+ (set_attr "mode" "<UNITMODE>")])
+
+(define_expand "vcondv2sf"
+ [(set (match_operand:V2SF 0 "register_operand")
+ (if_then_else:V2SF
+ (match_operator 3 ""
+ [(match_operand:V2SF 4 "register_operand")
+ (match_operand:V2SF 5 "register_operand")])
+ (match_operand:V2SF 1 "register_operand")
+ (match_operand:V2SF 2 "register_operand")))]
+ "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
+{
+ mips_expand_vcondv2sf (operands[0], operands[1], operands[2],
+ GET_CODE (operands[3]), operands[4], operands[5]);
+ DONE;
+})
+
+(define_expand "sminv2sf3"
+ [(set (match_operand:V2SF 0 "register_operand")
+ (smin:V2SF (match_operand:V2SF 1 "register_operand")
+ (match_operand:V2SF 2 "register_operand")))]
+ "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
+{
+ mips_expand_vcondv2sf (operands[0], operands[1], operands[2],
+ LE, operands[1], operands[2]);
+ DONE;
+})
+
+(define_expand "smaxv2sf3"
+ [(set (match_operand:V2SF 0 "register_operand")
+ (smax:V2SF (match_operand:V2SF 1 "register_operand")
+ (match_operand:V2SF 2 "register_operand")))]
+ "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
+{
+ mips_expand_vcondv2sf (operands[0], operands[1], operands[2],
+ LE, operands[2], operands[1]);
+ DONE;
+})
diff --git a/gcc/config/mips/mips.c b/gcc/config/mips/mips.c
new file mode 100644
index 000000000..87bf18e6c
--- /dev/null
+++ b/gcc/config/mips/mips.c
@@ -0,0 +1,16662 @@
+/* Subroutines used for MIPS code generation.
+ Copyright (C) 1989, 1990, 1991, 1993, 1994, 1995, 1996, 1997, 1998,
+ 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+ 2011
+ Free Software Foundation, Inc.
+ Contributed by A. Lichnewsky, lich@inria.inria.fr.
+ Changes by Michael Meissner, meissner@osf.org.
+ 64-bit r4000 support by Ian Lance Taylor, ian@cygnus.com, and
+ Brendan Eich, brendan@microunity.com.
+
+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/>. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "rtl.h"
+#include "regs.h"
+#include "hard-reg-set.h"
+#include "insn-config.h"
+#include "conditions.h"
+#include "insn-attr.h"
+#include "recog.h"
+#include "output.h"
+#include "tree.h"
+#include "function.h"
+#include "expr.h"
+#include "optabs.h"
+#include "libfuncs.h"
+#include "flags.h"
+#include "reload.h"
+#include "tm_p.h"
+#include "ggc.h"
+#include "gstab.h"
+#include "hashtab.h"
+#include "debug.h"
+#include "target.h"
+#include "target-def.h"
+#include "integrate.h"
+#include "langhooks.h"
+#include "cfglayout.h"
+#include "sched-int.h"
+#include "gimple.h"
+#include "bitmap.h"
+#include "diagnostic.h"
+#include "target-globals.h"
+
+/* True if X is an UNSPEC wrapper around a SYMBOL_REF or LABEL_REF. */
+#define UNSPEC_ADDRESS_P(X) \
+ (GET_CODE (X) == UNSPEC \
+ && XINT (X, 1) >= UNSPEC_ADDRESS_FIRST \
+ && XINT (X, 1) < UNSPEC_ADDRESS_FIRST + NUM_SYMBOL_TYPES)
+
+/* Extract the symbol or label from UNSPEC wrapper X. */
+#define UNSPEC_ADDRESS(X) \
+ XVECEXP (X, 0, 0)
+
+/* Extract the symbol type from UNSPEC wrapper X. */
+#define UNSPEC_ADDRESS_TYPE(X) \
+ ((enum mips_symbol_type) (XINT (X, 1) - UNSPEC_ADDRESS_FIRST))
+
+/* The maximum distance between the top of the stack frame and the
+ value $sp has when we save and restore registers.
+
+ The value for normal-mode code must be a SMALL_OPERAND and must
+ preserve the maximum stack alignment. We therefore use a value
+ of 0x7ff0 in this case.
+
+ MIPS16e SAVE and RESTORE instructions can adjust the stack pointer by
+ up to 0x7f8 bytes and can usually save or restore all the registers
+ that we need to save or restore. (Note that we can only use these
+ instructions for o32, for which the stack alignment is 8 bytes.)
+
+ We use a maximum gap of 0x100 or 0x400 for MIPS16 code when SAVE and
+ RESTORE are not available. We can then use unextended instructions
+ to save and restore registers, and to allocate and deallocate the top
+ part of the frame. */
+#define MIPS_MAX_FIRST_STACK_STEP \
+ (!TARGET_MIPS16 ? 0x7ff0 \
+ : GENERATE_MIPS16E_SAVE_RESTORE ? 0x7f8 \
+ : TARGET_64BIT ? 0x100 : 0x400)
+
+/* True if INSN is a mips.md pattern or asm statement. */
+#define USEFUL_INSN_P(INSN) \
+ (NONDEBUG_INSN_P (INSN) \
+ && GET_CODE (PATTERN (INSN)) != USE \
+ && GET_CODE (PATTERN (INSN)) != CLOBBER \
+ && GET_CODE (PATTERN (INSN)) != ADDR_VEC \
+ && GET_CODE (PATTERN (INSN)) != ADDR_DIFF_VEC)
+
+/* If INSN is a delayed branch sequence, return the first instruction
+ in the sequence, otherwise return INSN itself. */
+#define SEQ_BEGIN(INSN) \
+ (INSN_P (INSN) && GET_CODE (PATTERN (INSN)) == SEQUENCE \
+ ? XVECEXP (PATTERN (INSN), 0, 0) \
+ : (INSN))
+
+/* Likewise for the last instruction in a delayed branch sequence. */
+#define SEQ_END(INSN) \
+ (INSN_P (INSN) && GET_CODE (PATTERN (INSN)) == SEQUENCE \
+ ? XVECEXP (PATTERN (INSN), 0, XVECLEN (PATTERN (INSN), 0) - 1) \
+ : (INSN))
+
+/* Execute the following loop body with SUBINSN set to each instruction
+ between SEQ_BEGIN (INSN) and SEQ_END (INSN) inclusive. */
+#define FOR_EACH_SUBINSN(SUBINSN, INSN) \
+ for ((SUBINSN) = SEQ_BEGIN (INSN); \
+ (SUBINSN) != NEXT_INSN (SEQ_END (INSN)); \
+ (SUBINSN) = NEXT_INSN (SUBINSN))
+
+/* True if bit BIT is set in VALUE. */
+#define BITSET_P(VALUE, BIT) (((VALUE) & (1 << (BIT))) != 0)
+
+/* Return the opcode for a ptr_mode load of the form:
+
+ l[wd] DEST, OFFSET(BASE). */
+#define MIPS_LOAD_PTR(DEST, OFFSET, BASE) \
+ (((ptr_mode == DImode ? 0x37 : 0x23) << 26) \
+ | ((BASE) << 21) \
+ | ((DEST) << 16) \
+ | (OFFSET))
+
+/* Return the opcode to move register SRC into register DEST. */
+#define MIPS_MOVE(DEST, SRC) \
+ ((TARGET_64BIT ? 0x2d : 0x21) \
+ | ((DEST) << 11) \
+ | ((SRC) << 21))
+
+/* Return the opcode for:
+
+ lui DEST, VALUE. */
+#define MIPS_LUI(DEST, VALUE) \
+ ((0xf << 26) | ((DEST) << 16) | (VALUE))
+
+/* Return the opcode to jump to register DEST. */
+#define MIPS_JR(DEST) \
+ (((DEST) << 21) | 0x8)
+
+/* Return the opcode for:
+
+ bal . + (1 + OFFSET) * 4. */
+#define MIPS_BAL(OFFSET) \
+ ((0x1 << 26) | (0x11 << 16) | (OFFSET))
+
+/* Return the usual opcode for a nop. */
+#define MIPS_NOP 0
+
+/* Classifies an address.
+
+ ADDRESS_REG
+ A natural register + offset address. The register satisfies
+ mips_valid_base_register_p and the offset is a const_arith_operand.
+
+ ADDRESS_LO_SUM
+ A LO_SUM rtx. The first operand is a valid base register and
+ the second operand is a symbolic address.
+
+ ADDRESS_CONST_INT
+ A signed 16-bit constant address.
+
+ ADDRESS_SYMBOLIC:
+ A constant symbolic address. */
+enum mips_address_type {
+ ADDRESS_REG,
+ ADDRESS_LO_SUM,
+ ADDRESS_CONST_INT,
+ ADDRESS_SYMBOLIC
+};
+
+/* Enumerates the setting of the -mr10k-cache-barrier option. */
+enum mips_r10k_cache_barrier_setting {
+ R10K_CACHE_BARRIER_NONE,
+ R10K_CACHE_BARRIER_STORE,
+ R10K_CACHE_BARRIER_LOAD_STORE
+};
+
+/* Macros to create an enumeration identifier for a function prototype. */
+#define MIPS_FTYPE_NAME1(A, B) MIPS_##A##_FTYPE_##B
+#define MIPS_FTYPE_NAME2(A, B, C) MIPS_##A##_FTYPE_##B##_##C
+#define MIPS_FTYPE_NAME3(A, B, C, D) MIPS_##A##_FTYPE_##B##_##C##_##D
+#define MIPS_FTYPE_NAME4(A, B, C, D, E) MIPS_##A##_FTYPE_##B##_##C##_##D##_##E
+
+/* Classifies the prototype of a built-in function. */
+enum mips_function_type {
+#define DEF_MIPS_FTYPE(NARGS, LIST) MIPS_FTYPE_NAME##NARGS LIST,
+#include "config/mips/mips-ftypes.def"
+#undef DEF_MIPS_FTYPE
+ MIPS_MAX_FTYPE_MAX
+};
+
+/* Specifies how a built-in function should be converted into rtl. */
+enum mips_builtin_type {
+ /* The function corresponds directly to an .md pattern. The return
+ value is mapped to operand 0 and the arguments are mapped to
+ operands 1 and above. */
+ MIPS_BUILTIN_DIRECT,
+
+ /* The function corresponds directly to an .md pattern. There is no return
+ value and the arguments are mapped to operands 0 and above. */
+ MIPS_BUILTIN_DIRECT_NO_TARGET,
+
+ /* The function corresponds to a comparison instruction followed by
+ a mips_cond_move_tf_ps pattern. The first two arguments are the
+ values to compare and the second two arguments are the vector
+ operands for the movt.ps or movf.ps instruction (in assembly order). */
+ MIPS_BUILTIN_MOVF,
+ MIPS_BUILTIN_MOVT,
+
+ /* The function corresponds to a V2SF comparison instruction. Operand 0
+ of this instruction is the result of the comparison, which has mode
+ CCV2 or CCV4. The function arguments are mapped to operands 1 and
+ above. The function's return value is an SImode boolean that is
+ true under the following conditions:
+
+ MIPS_BUILTIN_CMP_ANY: one of the registers is true
+ MIPS_BUILTIN_CMP_ALL: all of the registers are true
+ MIPS_BUILTIN_CMP_LOWER: the first register is true
+ MIPS_BUILTIN_CMP_UPPER: the second register is true. */
+ MIPS_BUILTIN_CMP_ANY,
+ MIPS_BUILTIN_CMP_ALL,
+ MIPS_BUILTIN_CMP_UPPER,
+ MIPS_BUILTIN_CMP_LOWER,
+
+ /* As above, but the instruction only sets a single $fcc register. */
+ MIPS_BUILTIN_CMP_SINGLE,
+
+ /* For generating bposge32 branch instructions in MIPS32 DSP ASE. */
+ MIPS_BUILTIN_BPOSGE32
+};
+
+/* Invoke MACRO (COND) for each C.cond.fmt condition. */
+#define MIPS_FP_CONDITIONS(MACRO) \
+ MACRO (f), \
+ MACRO (un), \
+ MACRO (eq), \
+ MACRO (ueq), \
+ MACRO (olt), \
+ MACRO (ult), \
+ MACRO (ole), \
+ MACRO (ule), \
+ MACRO (sf), \
+ MACRO (ngle), \
+ MACRO (seq), \
+ MACRO (ngl), \
+ MACRO (lt), \
+ MACRO (nge), \
+ MACRO (le), \
+ MACRO (ngt)
+
+/* Enumerates the codes above as MIPS_FP_COND_<X>. */
+#define DECLARE_MIPS_COND(X) MIPS_FP_COND_ ## X
+enum mips_fp_condition {
+ MIPS_FP_CONDITIONS (DECLARE_MIPS_COND)
+};
+
+/* Index X provides the string representation of MIPS_FP_COND_<X>. */
+#define STRINGIFY(X) #X
+static const char *const mips_fp_conditions[] = {
+ MIPS_FP_CONDITIONS (STRINGIFY)
+};
+
+/* Information about a function's frame layout. */
+struct GTY(()) mips_frame_info {
+ /* The size of the frame in bytes. */
+ HOST_WIDE_INT total_size;
+
+ /* The number of bytes allocated to variables. */
+ HOST_WIDE_INT var_size;
+
+ /* The number of bytes allocated to outgoing function arguments. */
+ HOST_WIDE_INT args_size;
+
+ /* The number of bytes allocated to the .cprestore slot, or 0 if there
+ is no such slot. */
+ HOST_WIDE_INT cprestore_size;
+
+ /* Bit X is set if the function saves or restores GPR X. */
+ unsigned int mask;
+
+ /* Likewise FPR X. */
+ unsigned int fmask;
+
+ /* Likewise doubleword accumulator X ($acX). */
+ unsigned int acc_mask;
+
+ /* The number of GPRs, FPRs, doubleword accumulators and COP0
+ registers saved. */
+ unsigned int num_gp;
+ unsigned int num_fp;
+ unsigned int num_acc;
+ unsigned int num_cop0_regs;
+
+ /* The offset of the topmost GPR, FPR, accumulator and COP0-register
+ save slots from the top of the frame, or zero if no such slots are
+ needed. */
+ HOST_WIDE_INT gp_save_offset;
+ HOST_WIDE_INT fp_save_offset;
+ HOST_WIDE_INT acc_save_offset;
+ HOST_WIDE_INT cop0_save_offset;
+
+ /* Likewise, but giving offsets from the bottom of the frame. */
+ HOST_WIDE_INT gp_sp_offset;
+ HOST_WIDE_INT fp_sp_offset;
+ HOST_WIDE_INT acc_sp_offset;
+ HOST_WIDE_INT cop0_sp_offset;
+
+ /* Similar, but the value passed to _mcount. */
+ HOST_WIDE_INT ra_fp_offset;
+
+ /* The offset of arg_pointer_rtx from the bottom of the frame. */
+ HOST_WIDE_INT arg_pointer_offset;
+
+ /* The offset of hard_frame_pointer_rtx from the bottom of the frame. */
+ HOST_WIDE_INT hard_frame_pointer_offset;
+};
+
+struct GTY(()) machine_function {
+ /* The register returned by mips16_gp_pseudo_reg; see there for details. */
+ rtx mips16_gp_pseudo_rtx;
+
+ /* The number of extra stack bytes taken up by register varargs.
+ This area is allocated by the callee at the very top of the frame. */
+ int varargs_size;
+
+ /* The current frame information, calculated by mips_compute_frame_info. */
+ struct mips_frame_info frame;
+
+ /* The register to use as the function's global pointer, or INVALID_REGNUM
+ if the function doesn't need one. */
+ unsigned int global_pointer;
+
+ /* How many instructions it takes to load a label into $AT, or 0 if
+ this property hasn't yet been calculated. */
+ unsigned int load_label_num_insns;
+
+ /* True if mips_adjust_insn_length should ignore an instruction's
+ hazard attribute. */
+ bool ignore_hazard_length_p;
+
+ /* True if the whole function is suitable for .set noreorder and
+ .set nomacro. */
+ bool all_noreorder_p;
+
+ /* True if the function has "inflexible" and "flexible" references
+ to the global pointer. See mips_cfun_has_inflexible_gp_ref_p
+ and mips_cfun_has_flexible_gp_ref_p for details. */
+ bool has_inflexible_gp_insn_p;
+ bool has_flexible_gp_insn_p;
+
+ /* True if the function's prologue must load the global pointer
+ value into pic_offset_table_rtx and store the same value in
+ the function's cprestore slot (if any). Even if this value
+ is currently false, we may decide to set it to true later;
+ see mips_must_initialize_gp_p () for details. */
+ bool must_initialize_gp_p;
+
+ /* True if the current function must restore $gp after any potential
+ clobber. This value is only meaningful during the first post-epilogue
+ split_insns pass; see mips_must_initialize_gp_p () for details. */
+ bool must_restore_gp_when_clobbered_p;
+
+ /* True if this is an interrupt handler. */
+ bool interrupt_handler_p;
+
+ /* True if this is an interrupt handler that uses shadow registers. */
+ bool use_shadow_register_set_p;
+
+ /* True if this is an interrupt handler that should keep interrupts
+ masked. */
+ bool keep_interrupts_masked_p;
+
+ /* True if this is an interrupt handler that should use DERET
+ instead of ERET. */
+ bool use_debug_exception_return_p;
+};
+
+/* Information about a single argument. */
+struct mips_arg_info {
+ /* True if the argument is passed in a floating-point register, or
+ would have been if we hadn't run out of registers. */
+ bool fpr_p;
+
+ /* The number of words passed in registers, rounded up. */
+ unsigned int reg_words;
+
+ /* For EABI, the offset of the first register from GP_ARG_FIRST or
+ FP_ARG_FIRST. For other ABIs, the offset of the first register from
+ the start of the ABI's argument structure (see the CUMULATIVE_ARGS
+ comment for details).
+
+ The value is MAX_ARGS_IN_REGISTERS if the argument is passed entirely
+ on the stack. */
+ unsigned int reg_offset;
+
+ /* The number of words that must be passed on the stack, rounded up. */
+ unsigned int stack_words;
+
+ /* The offset from the start of the stack overflow area of the argument's
+ first stack word. Only meaningful when STACK_WORDS is nonzero. */
+ unsigned int stack_offset;
+};
+
+/* Information about an address described by mips_address_type.
+
+ ADDRESS_CONST_INT
+ No fields are used.
+
+ ADDRESS_REG
+ REG is the base register and OFFSET is the constant offset.
+
+ ADDRESS_LO_SUM
+ REG and OFFSET are the operands to the LO_SUM and SYMBOL_TYPE
+ is the type of symbol it references.
+
+ ADDRESS_SYMBOLIC
+ SYMBOL_TYPE is the type of symbol that the address references. */
+struct mips_address_info {
+ enum mips_address_type type;
+ rtx reg;
+ rtx offset;
+ enum mips_symbol_type symbol_type;
+};
+
+/* One stage in a constant building sequence. These sequences have
+ the form:
+
+ A = VALUE[0]
+ A = A CODE[1] VALUE[1]
+ A = A CODE[2] VALUE[2]
+ ...
+
+ where A is an accumulator, each CODE[i] is a binary rtl operation
+ and each VALUE[i] is a constant integer. CODE[0] is undefined. */
+struct mips_integer_op {
+ enum rtx_code code;
+ unsigned HOST_WIDE_INT value;
+};
+
+/* The largest number of operations needed to load an integer constant.
+ The worst accepted case for 64-bit constants is LUI,ORI,SLL,ORI,SLL,ORI.
+ When the lowest bit is clear, we can try, but reject a sequence with
+ an extra SLL at the end. */
+#define MIPS_MAX_INTEGER_OPS 7
+
+/* Information about a MIPS16e SAVE or RESTORE instruction. */
+struct mips16e_save_restore_info {
+ /* The number of argument registers saved by a SAVE instruction.
+ 0 for RESTORE instructions. */
+ unsigned int nargs;
+
+ /* Bit X is set if the instruction saves or restores GPR X. */
+ unsigned int mask;
+
+ /* The total number of bytes to allocate. */
+ HOST_WIDE_INT size;
+};
+
+/* Costs of various operations on the different architectures. */
+
+struct mips_rtx_cost_data
+{
+ unsigned short fp_add;
+ unsigned short fp_mult_sf;
+ unsigned short fp_mult_df;
+ unsigned short fp_div_sf;
+ unsigned short fp_div_df;
+ unsigned short int_mult_si;
+ unsigned short int_mult_di;
+ unsigned short int_div_si;
+ unsigned short int_div_di;
+ unsigned short branch_cost;
+ unsigned short memory_latency;
+};
+
+/* Global variables for machine-dependent things. */
+
+/* The -G setting, or the configuration's default small-data limit if
+ no -G option is given. */
+static unsigned int mips_small_data_threshold;
+
+/* The number of file directives written by mips_output_filename. */
+int num_source_filenames;
+
+/* The name that appeared in the last .file directive written by
+ mips_output_filename, or "" if mips_output_filename hasn't
+ written anything yet. */
+const char *current_function_file = "";
+
+/* A label counter used by PUT_SDB_BLOCK_START and PUT_SDB_BLOCK_END. */
+int sdb_label_count;
+
+/* Arrays that map GCC register numbers to debugger register numbers. */
+int mips_dbx_regno[FIRST_PSEUDO_REGISTER];
+int mips_dwarf_regno[FIRST_PSEUDO_REGISTER];
+
+/* The nesting depth of the PRINT_OPERAND '%(', '%<' and '%[' constructs. */
+struct mips_asm_switch mips_noreorder = { "reorder", 0 };
+struct mips_asm_switch mips_nomacro = { "macro", 0 };
+struct mips_asm_switch mips_noat = { "at", 0 };
+
+/* True if we're writing out a branch-likely instruction rather than a
+ normal branch. */
+static bool mips_branch_likely;
+
+/* The current instruction-set architecture. */
+enum processor mips_arch;
+const struct mips_cpu_info *mips_arch_info;
+
+/* The processor that we should tune the code for. */
+enum processor mips_tune;
+const struct mips_cpu_info *mips_tune_info;
+
+/* The ISA level associated with mips_arch. */
+int mips_isa;
+
+/* The architecture selected by -mipsN, or null if -mipsN wasn't used. */
+static const struct mips_cpu_info *mips_isa_option_info;
+
+/* Which ABI to use. */
+int mips_abi = MIPS_ABI_DEFAULT;
+
+/* Which cost information to use. */
+static const struct mips_rtx_cost_data *mips_cost;
+
+/* The ambient target flags, excluding MASK_MIPS16. */
+static int mips_base_target_flags;
+
+/* True if MIPS16 is the default mode. */
+bool mips_base_mips16;
+
+/* The ambient values of other global variables. */
+static int mips_base_schedule_insns; /* flag_schedule_insns */
+static int mips_base_reorder_blocks_and_partition; /* flag_reorder... */
+static int mips_base_move_loop_invariants; /* flag_move_loop_invariants */
+static int mips_base_align_loops; /* align_loops */
+static int mips_base_align_jumps; /* align_jumps */
+static int mips_base_align_functions; /* align_functions */
+
+/* The -mcode-readable setting. */
+enum mips_code_readable_setting mips_code_readable = CODE_READABLE_YES;
+
+/* The -mr10k-cache-barrier setting. */
+static enum mips_r10k_cache_barrier_setting mips_r10k_cache_barrier;
+
+/* Index [M][R] is true if register R is allowed to hold a value of mode M. */
+bool mips_hard_regno_mode_ok[(int) MAX_MACHINE_MODE][FIRST_PSEUDO_REGISTER];
+
+/* Index C is true if character C is a valid PRINT_OPERAND punctation
+ character. */
+static bool mips_print_operand_punct[256];
+
+static GTY (()) int mips_output_filename_first_time = 1;
+
+/* mips_split_p[X] is true if symbols of type X can be split by
+ mips_split_symbol. */
+bool mips_split_p[NUM_SYMBOL_TYPES];
+
+/* mips_split_hi_p[X] is true if the high parts of symbols of type X
+ can be split by mips_split_symbol. */
+bool mips_split_hi_p[NUM_SYMBOL_TYPES];
+
+/* mips_lo_relocs[X] is the relocation to use when a symbol of type X
+ appears in a LO_SUM. It can be null if such LO_SUMs aren't valid or
+ if they are matched by a special .md file pattern. */
+static const char *mips_lo_relocs[NUM_SYMBOL_TYPES];
+
+/* Likewise for HIGHs. */
+static const char *mips_hi_relocs[NUM_SYMBOL_TYPES];
+
+/* Target state for MIPS16. */
+struct target_globals *mips16_globals;
+
+/* Cached value of can_issue_more. This is cached in mips_variable_issue hook
+ and returned from mips_sched_reorder2. */
+static int cached_can_issue_more;
+
+/* Index R is the smallest register class that contains register R. */
+const enum reg_class mips_regno_to_class[FIRST_PSEUDO_REGISTER] = {
+ LEA_REGS, LEA_REGS, M16_REGS, V1_REG,
+ M16_REGS, M16_REGS, M16_REGS, M16_REGS,
+ LEA_REGS, LEA_REGS, LEA_REGS, LEA_REGS,
+ LEA_REGS, LEA_REGS, LEA_REGS, LEA_REGS,
+ M16_REGS, M16_REGS, LEA_REGS, LEA_REGS,
+ LEA_REGS, LEA_REGS, LEA_REGS, LEA_REGS,
+ T_REG, PIC_FN_ADDR_REG, LEA_REGS, LEA_REGS,
+ LEA_REGS, LEA_REGS, LEA_REGS, LEA_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ MD0_REG, MD1_REG, NO_REGS, ST_REGS,
+ ST_REGS, ST_REGS, ST_REGS, ST_REGS,
+ ST_REGS, ST_REGS, ST_REGS, NO_REGS,
+ NO_REGS, FRAME_REGS, FRAME_REGS, NO_REGS,
+ COP0_REGS, COP0_REGS, COP0_REGS, COP0_REGS,
+ COP0_REGS, COP0_REGS, COP0_REGS, COP0_REGS,
+ COP0_REGS, COP0_REGS, COP0_REGS, COP0_REGS,
+ COP0_REGS, COP0_REGS, COP0_REGS, COP0_REGS,
+ COP0_REGS, COP0_REGS, COP0_REGS, COP0_REGS,
+ COP0_REGS, COP0_REGS, COP0_REGS, COP0_REGS,
+ COP0_REGS, COP0_REGS, COP0_REGS, COP0_REGS,
+ COP0_REGS, COP0_REGS, COP0_REGS, COP0_REGS,
+ COP2_REGS, COP2_REGS, COP2_REGS, COP2_REGS,
+ COP2_REGS, COP2_REGS, COP2_REGS, COP2_REGS,
+ COP2_REGS, COP2_REGS, COP2_REGS, COP2_REGS,
+ COP2_REGS, COP2_REGS, COP2_REGS, COP2_REGS,
+ COP2_REGS, COP2_REGS, COP2_REGS, COP2_REGS,
+ COP2_REGS, COP2_REGS, COP2_REGS, COP2_REGS,
+ COP2_REGS, COP2_REGS, COP2_REGS, COP2_REGS,
+ COP2_REGS, COP2_REGS, COP2_REGS, COP2_REGS,
+ COP3_REGS, COP3_REGS, COP3_REGS, COP3_REGS,
+ COP3_REGS, COP3_REGS, COP3_REGS, COP3_REGS,
+ COP3_REGS, COP3_REGS, COP3_REGS, COP3_REGS,
+ COP3_REGS, COP3_REGS, COP3_REGS, COP3_REGS,
+ COP3_REGS, COP3_REGS, COP3_REGS, COP3_REGS,
+ COP3_REGS, COP3_REGS, COP3_REGS, COP3_REGS,
+ COP3_REGS, COP3_REGS, COP3_REGS, COP3_REGS,
+ COP3_REGS, COP3_REGS, COP3_REGS, COP3_REGS,
+ DSP_ACC_REGS, DSP_ACC_REGS, DSP_ACC_REGS, DSP_ACC_REGS,
+ DSP_ACC_REGS, DSP_ACC_REGS, ALL_REGS, ALL_REGS,
+ ALL_REGS, ALL_REGS, ALL_REGS, ALL_REGS
+};
+
+/* The value of TARGET_ATTRIBUTE_TABLE. */
+static const struct attribute_spec mips_attribute_table[] = {
+ /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
+ { "long_call", 0, 0, false, true, true, NULL },
+ { "far", 0, 0, false, true, true, NULL },
+ { "near", 0, 0, false, true, true, NULL },
+ /* We would really like to treat "mips16" and "nomips16" as type
+ attributes, but GCC doesn't provide the hooks we need to support
+ the right conversion rules. As declaration attributes, they affect
+ code generation but don't carry other semantics. */
+ { "mips16", 0, 0, true, false, false, NULL },
+ { "nomips16", 0, 0, true, false, false, NULL },
+ /* Allow functions to be specified as interrupt handlers */
+ { "interrupt", 0, 0, false, true, true, NULL },
+ { "use_shadow_register_set", 0, 0, false, true, true, NULL },
+ { "keep_interrupts_masked", 0, 0, false, true, true, NULL },
+ { "use_debug_exception_return", 0, 0, false, true, true, NULL },
+ { NULL, 0, 0, false, false, false, NULL }
+};
+
+/* A table describing all the processors GCC knows about. Names are
+ matched in the order listed. The first mention of an ISA level is
+ taken as the canonical name for that ISA.
+
+ To ease comparison, please keep this table in the same order
+ as GAS's mips_cpu_info_table. Please also make sure that
+ MIPS_ISA_LEVEL_SPEC and MIPS_ARCH_FLOAT_SPEC handle all -march
+ options correctly. */
+static const struct mips_cpu_info mips_cpu_info_table[] = {
+ /* Entries for generic ISAs. */
+ { "mips1", PROCESSOR_R3000, 1, 0 },
+ { "mips2", PROCESSOR_R6000, 2, 0 },
+ { "mips3", PROCESSOR_R4000, 3, 0 },
+ { "mips4", PROCESSOR_R8000, 4, 0 },
+ /* Prefer not to use branch-likely instructions for generic MIPS32rX
+ and MIPS64rX code. The instructions were officially deprecated
+ in revisions 2 and earlier, but revision 3 is likely to downgrade
+ that to a recommendation to avoid the instructions in code that
+ isn't tuned to a specific processor. */
+ { "mips32", PROCESSOR_4KC, 32, PTF_AVOID_BRANCHLIKELY },
+ { "mips32r2", PROCESSOR_M4K, 33, PTF_AVOID_BRANCHLIKELY },
+ { "mips64", PROCESSOR_5KC, 64, PTF_AVOID_BRANCHLIKELY },
+ /* ??? For now just tune the generic MIPS64r2 for 5KC as well. */
+ { "mips64r2", PROCESSOR_5KC, 65, PTF_AVOID_BRANCHLIKELY },
+
+ /* MIPS I processors. */
+ { "r3000", PROCESSOR_R3000, 1, 0 },
+ { "r2000", PROCESSOR_R3000, 1, 0 },
+ { "r3900", PROCESSOR_R3900, 1, 0 },
+
+ /* MIPS II processors. */
+ { "r6000", PROCESSOR_R6000, 2, 0 },
+
+ /* MIPS III processors. */
+ { "r4000", PROCESSOR_R4000, 3, 0 },
+ { "vr4100", PROCESSOR_R4100, 3, 0 },
+ { "vr4111", PROCESSOR_R4111, 3, 0 },
+ { "vr4120", PROCESSOR_R4120, 3, 0 },
+ { "vr4130", PROCESSOR_R4130, 3, 0 },
+ { "vr4300", PROCESSOR_R4300, 3, 0 },
+ { "r4400", PROCESSOR_R4000, 3, 0 },
+ { "r4600", PROCESSOR_R4600, 3, 0 },
+ { "orion", PROCESSOR_R4600, 3, 0 },
+ { "r4650", PROCESSOR_R4650, 3, 0 },
+ /* ST Loongson 2E/2F processors. */
+ { "loongson2e", PROCESSOR_LOONGSON_2E, 3, PTF_AVOID_BRANCHLIKELY },
+ { "loongson2f", PROCESSOR_LOONGSON_2F, 3, PTF_AVOID_BRANCHLIKELY },
+
+ /* MIPS IV processors. */
+ { "r8000", PROCESSOR_R8000, 4, 0 },
+ { "r10000", PROCESSOR_R10000, 4, 0 },
+ { "r12000", PROCESSOR_R10000, 4, 0 },
+ { "r14000", PROCESSOR_R10000, 4, 0 },
+ { "r16000", PROCESSOR_R10000, 4, 0 },
+ { "vr5000", PROCESSOR_R5000, 4, 0 },
+ { "vr5400", PROCESSOR_R5400, 4, 0 },
+ { "vr5500", PROCESSOR_R5500, 4, PTF_AVOID_BRANCHLIKELY },
+ { "rm7000", PROCESSOR_R7000, 4, 0 },
+ { "rm9000", PROCESSOR_R9000, 4, 0 },
+
+ /* MIPS32 processors. */
+ { "4kc", PROCESSOR_4KC, 32, 0 },
+ { "4km", PROCESSOR_4KC, 32, 0 },
+ { "4kp", PROCESSOR_4KP, 32, 0 },
+ { "4ksc", PROCESSOR_4KC, 32, 0 },
+
+ /* MIPS32 Release 2 processors. */
+ { "m4k", PROCESSOR_M4K, 33, 0 },
+ { "4kec", PROCESSOR_4KC, 33, 0 },
+ { "4kem", PROCESSOR_4KC, 33, 0 },
+ { "4kep", PROCESSOR_4KP, 33, 0 },
+ { "4ksd", PROCESSOR_4KC, 33, 0 },
+
+ { "24kc", PROCESSOR_24KC, 33, 0 },
+ { "24kf2_1", PROCESSOR_24KF2_1, 33, 0 },
+ { "24kf", PROCESSOR_24KF2_1, 33, 0 },
+ { "24kf1_1", PROCESSOR_24KF1_1, 33, 0 },
+ { "24kfx", PROCESSOR_24KF1_1, 33, 0 },
+ { "24kx", PROCESSOR_24KF1_1, 33, 0 },
+
+ { "24kec", PROCESSOR_24KC, 33, 0 }, /* 24K with DSP. */
+ { "24kef2_1", PROCESSOR_24KF2_1, 33, 0 },
+ { "24kef", PROCESSOR_24KF2_1, 33, 0 },
+ { "24kef1_1", PROCESSOR_24KF1_1, 33, 0 },
+ { "24kefx", PROCESSOR_24KF1_1, 33, 0 },
+ { "24kex", PROCESSOR_24KF1_1, 33, 0 },
+
+ { "34kc", PROCESSOR_24KC, 33, 0 }, /* 34K with MT/DSP. */
+ { "34kf2_1", PROCESSOR_24KF2_1, 33, 0 },
+ { "34kf", PROCESSOR_24KF2_1, 33, 0 },
+ { "34kf1_1", PROCESSOR_24KF1_1, 33, 0 },
+ { "34kfx", PROCESSOR_24KF1_1, 33, 0 },
+ { "34kx", PROCESSOR_24KF1_1, 33, 0 },
+
+ { "74kc", PROCESSOR_74KC, 33, 0 }, /* 74K with DSPr2. */
+ { "74kf2_1", PROCESSOR_74KF2_1, 33, 0 },
+ { "74kf", PROCESSOR_74KF2_1, 33, 0 },
+ { "74kf1_1", PROCESSOR_74KF1_1, 33, 0 },
+ { "74kfx", PROCESSOR_74KF1_1, 33, 0 },
+ { "74kx", PROCESSOR_74KF1_1, 33, 0 },
+ { "74kf3_2", PROCESSOR_74KF3_2, 33, 0 },
+
+ { "1004kc", PROCESSOR_24KC, 33, 0 }, /* 1004K with MT/DSP. */
+ { "1004kf2_1", PROCESSOR_24KF2_1, 33, 0 },
+ { "1004kf", PROCESSOR_24KF2_1, 33, 0 },
+ { "1004kf1_1", PROCESSOR_24KF1_1, 33, 0 },
+
+ /* MIPS64 processors. */
+ { "5kc", PROCESSOR_5KC, 64, 0 },
+ { "5kf", PROCESSOR_5KF, 64, 0 },
+ { "20kc", PROCESSOR_20KC, 64, PTF_AVOID_BRANCHLIKELY },
+ { "sb1", PROCESSOR_SB1, 64, PTF_AVOID_BRANCHLIKELY },
+ { "sb1a", PROCESSOR_SB1A, 64, PTF_AVOID_BRANCHLIKELY },
+ { "sr71000", PROCESSOR_SR71000, 64, PTF_AVOID_BRANCHLIKELY },
+ { "xlr", PROCESSOR_XLR, 64, 0 },
+ { "loongson3a", PROCESSOR_LOONGSON_3A, 64, PTF_AVOID_BRANCHLIKELY },
+
+ /* MIPS64 Release 2 processors. */
+ { "octeon", PROCESSOR_OCTEON, 65, PTF_AVOID_BRANCHLIKELY }
+};
+
+/* Default costs. If these are used for a processor we should look
+ up the actual costs. */
+#define DEFAULT_COSTS COSTS_N_INSNS (6), /* fp_add */ \
+ COSTS_N_INSNS (7), /* fp_mult_sf */ \
+ COSTS_N_INSNS (8), /* fp_mult_df */ \
+ COSTS_N_INSNS (23), /* fp_div_sf */ \
+ COSTS_N_INSNS (36), /* fp_div_df */ \
+ COSTS_N_INSNS (10), /* int_mult_si */ \
+ COSTS_N_INSNS (10), /* int_mult_di */ \
+ COSTS_N_INSNS (69), /* int_div_si */ \
+ COSTS_N_INSNS (69), /* int_div_di */ \
+ 2, /* branch_cost */ \
+ 4 /* memory_latency */
+
+/* Floating-point costs for processors without an FPU. Just assume that
+ all floating-point libcalls are very expensive. */
+#define SOFT_FP_COSTS COSTS_N_INSNS (256), /* fp_add */ \
+ COSTS_N_INSNS (256), /* fp_mult_sf */ \
+ COSTS_N_INSNS (256), /* fp_mult_df */ \
+ COSTS_N_INSNS (256), /* fp_div_sf */ \
+ COSTS_N_INSNS (256) /* fp_div_df */
+
+/* Costs to use when optimizing for size. */
+static const struct mips_rtx_cost_data mips_rtx_cost_optimize_size = {
+ COSTS_N_INSNS (1), /* fp_add */
+ COSTS_N_INSNS (1), /* fp_mult_sf */
+ COSTS_N_INSNS (1), /* fp_mult_df */
+ COSTS_N_INSNS (1), /* fp_div_sf */
+ COSTS_N_INSNS (1), /* fp_div_df */
+ COSTS_N_INSNS (1), /* int_mult_si */
+ COSTS_N_INSNS (1), /* int_mult_di */
+ COSTS_N_INSNS (1), /* int_div_si */
+ COSTS_N_INSNS (1), /* int_div_di */
+ 2, /* branch_cost */
+ 4 /* memory_latency */
+};
+
+/* Costs to use when optimizing for speed, indexed by processor. */
+static const struct mips_rtx_cost_data
+ mips_rtx_cost_data[NUM_PROCESSOR_VALUES] = {
+ { /* R3000 */
+ COSTS_N_INSNS (2), /* fp_add */
+ COSTS_N_INSNS (4), /* fp_mult_sf */
+ COSTS_N_INSNS (5), /* fp_mult_df */
+ COSTS_N_INSNS (12), /* fp_div_sf */
+ COSTS_N_INSNS (19), /* fp_div_df */
+ COSTS_N_INSNS (12), /* int_mult_si */
+ COSTS_N_INSNS (12), /* int_mult_di */
+ COSTS_N_INSNS (35), /* int_div_si */
+ COSTS_N_INSNS (35), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* 4KC */
+ SOFT_FP_COSTS,
+ COSTS_N_INSNS (6), /* int_mult_si */
+ COSTS_N_INSNS (6), /* int_mult_di */
+ COSTS_N_INSNS (36), /* int_div_si */
+ COSTS_N_INSNS (36), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* 4KP */
+ SOFT_FP_COSTS,
+ COSTS_N_INSNS (36), /* int_mult_si */
+ COSTS_N_INSNS (36), /* int_mult_di */
+ COSTS_N_INSNS (37), /* int_div_si */
+ COSTS_N_INSNS (37), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* 5KC */
+ SOFT_FP_COSTS,
+ COSTS_N_INSNS (4), /* int_mult_si */
+ COSTS_N_INSNS (11), /* int_mult_di */
+ COSTS_N_INSNS (36), /* int_div_si */
+ COSTS_N_INSNS (68), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* 5KF */
+ COSTS_N_INSNS (4), /* fp_add */
+ COSTS_N_INSNS (4), /* fp_mult_sf */
+ COSTS_N_INSNS (5), /* fp_mult_df */
+ COSTS_N_INSNS (17), /* fp_div_sf */
+ COSTS_N_INSNS (32), /* fp_div_df */
+ COSTS_N_INSNS (4), /* int_mult_si */
+ COSTS_N_INSNS (11), /* int_mult_di */
+ COSTS_N_INSNS (36), /* int_div_si */
+ COSTS_N_INSNS (68), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* 20KC */
+ COSTS_N_INSNS (4), /* fp_add */
+ COSTS_N_INSNS (4), /* fp_mult_sf */
+ COSTS_N_INSNS (5), /* fp_mult_df */
+ COSTS_N_INSNS (17), /* fp_div_sf */
+ COSTS_N_INSNS (32), /* fp_div_df */
+ COSTS_N_INSNS (4), /* int_mult_si */
+ COSTS_N_INSNS (7), /* int_mult_di */
+ COSTS_N_INSNS (42), /* int_div_si */
+ COSTS_N_INSNS (72), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* 24KC */
+ SOFT_FP_COSTS,
+ COSTS_N_INSNS (5), /* int_mult_si */
+ COSTS_N_INSNS (5), /* int_mult_di */
+ COSTS_N_INSNS (41), /* int_div_si */
+ COSTS_N_INSNS (41), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* 24KF2_1 */
+ COSTS_N_INSNS (8), /* fp_add */
+ COSTS_N_INSNS (8), /* fp_mult_sf */
+ COSTS_N_INSNS (10), /* fp_mult_df */
+ COSTS_N_INSNS (34), /* fp_div_sf */
+ COSTS_N_INSNS (64), /* fp_div_df */
+ COSTS_N_INSNS (5), /* int_mult_si */
+ COSTS_N_INSNS (5), /* int_mult_di */
+ COSTS_N_INSNS (41), /* int_div_si */
+ COSTS_N_INSNS (41), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* 24KF1_1 */
+ COSTS_N_INSNS (4), /* fp_add */
+ COSTS_N_INSNS (4), /* fp_mult_sf */
+ COSTS_N_INSNS (5), /* fp_mult_df */
+ COSTS_N_INSNS (17), /* fp_div_sf */
+ COSTS_N_INSNS (32), /* fp_div_df */
+ COSTS_N_INSNS (5), /* int_mult_si */
+ COSTS_N_INSNS (5), /* int_mult_di */
+ COSTS_N_INSNS (41), /* int_div_si */
+ COSTS_N_INSNS (41), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* 74KC */
+ SOFT_FP_COSTS,
+ COSTS_N_INSNS (5), /* int_mult_si */
+ COSTS_N_INSNS (5), /* int_mult_di */
+ COSTS_N_INSNS (41), /* int_div_si */
+ COSTS_N_INSNS (41), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* 74KF2_1 */
+ COSTS_N_INSNS (8), /* fp_add */
+ COSTS_N_INSNS (8), /* fp_mult_sf */
+ COSTS_N_INSNS (10), /* fp_mult_df */
+ COSTS_N_INSNS (34), /* fp_div_sf */
+ COSTS_N_INSNS (64), /* fp_div_df */
+ COSTS_N_INSNS (5), /* int_mult_si */
+ COSTS_N_INSNS (5), /* int_mult_di */
+ COSTS_N_INSNS (41), /* int_div_si */
+ COSTS_N_INSNS (41), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* 74KF1_1 */
+ COSTS_N_INSNS (4), /* fp_add */
+ COSTS_N_INSNS (4), /* fp_mult_sf */
+ COSTS_N_INSNS (5), /* fp_mult_df */
+ COSTS_N_INSNS (17), /* fp_div_sf */
+ COSTS_N_INSNS (32), /* fp_div_df */
+ COSTS_N_INSNS (5), /* int_mult_si */
+ COSTS_N_INSNS (5), /* int_mult_di */
+ COSTS_N_INSNS (41), /* int_div_si */
+ COSTS_N_INSNS (41), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* 74KF3_2 */
+ COSTS_N_INSNS (6), /* fp_add */
+ COSTS_N_INSNS (6), /* fp_mult_sf */
+ COSTS_N_INSNS (7), /* fp_mult_df */
+ COSTS_N_INSNS (25), /* fp_div_sf */
+ COSTS_N_INSNS (48), /* fp_div_df */
+ COSTS_N_INSNS (5), /* int_mult_si */
+ COSTS_N_INSNS (5), /* int_mult_di */
+ COSTS_N_INSNS (41), /* int_div_si */
+ COSTS_N_INSNS (41), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* Loongson-2E */
+ DEFAULT_COSTS
+ },
+ { /* Loongson-2F */
+ DEFAULT_COSTS
+ },
+ { /* Loongson-3A */
+ DEFAULT_COSTS
+ },
+ { /* M4k */
+ DEFAULT_COSTS
+ },
+ /* Octeon */
+ {
+ SOFT_FP_COSTS,
+ COSTS_N_INSNS (5), /* int_mult_si */
+ COSTS_N_INSNS (5), /* int_mult_di */
+ COSTS_N_INSNS (72), /* int_div_si */
+ COSTS_N_INSNS (72), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* R3900 */
+ COSTS_N_INSNS (2), /* fp_add */
+ COSTS_N_INSNS (4), /* fp_mult_sf */
+ COSTS_N_INSNS (5), /* fp_mult_df */
+ COSTS_N_INSNS (12), /* fp_div_sf */
+ COSTS_N_INSNS (19), /* fp_div_df */
+ COSTS_N_INSNS (2), /* int_mult_si */
+ COSTS_N_INSNS (2), /* int_mult_di */
+ COSTS_N_INSNS (35), /* int_div_si */
+ COSTS_N_INSNS (35), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* R6000 */
+ COSTS_N_INSNS (3), /* fp_add */
+ COSTS_N_INSNS (5), /* fp_mult_sf */
+ COSTS_N_INSNS (6), /* fp_mult_df */
+ COSTS_N_INSNS (15), /* fp_div_sf */
+ COSTS_N_INSNS (16), /* fp_div_df */
+ COSTS_N_INSNS (17), /* int_mult_si */
+ COSTS_N_INSNS (17), /* int_mult_di */
+ COSTS_N_INSNS (38), /* int_div_si */
+ COSTS_N_INSNS (38), /* int_div_di */
+ 2, /* branch_cost */
+ 6 /* memory_latency */
+ },
+ { /* R4000 */
+ COSTS_N_INSNS (6), /* fp_add */
+ COSTS_N_INSNS (7), /* fp_mult_sf */
+ COSTS_N_INSNS (8), /* fp_mult_df */
+ COSTS_N_INSNS (23), /* fp_div_sf */
+ COSTS_N_INSNS (36), /* fp_div_df */
+ COSTS_N_INSNS (10), /* int_mult_si */
+ COSTS_N_INSNS (10), /* int_mult_di */
+ COSTS_N_INSNS (69), /* int_div_si */
+ COSTS_N_INSNS (69), /* int_div_di */
+ 2, /* branch_cost */
+ 6 /* memory_latency */
+ },
+ { /* R4100 */
+ DEFAULT_COSTS
+ },
+ { /* R4111 */
+ DEFAULT_COSTS
+ },
+ { /* R4120 */
+ DEFAULT_COSTS
+ },
+ { /* R4130 */
+ /* The only costs that appear to be updated here are
+ integer multiplication. */
+ SOFT_FP_COSTS,
+ COSTS_N_INSNS (4), /* int_mult_si */
+ COSTS_N_INSNS (6), /* int_mult_di */
+ COSTS_N_INSNS (69), /* int_div_si */
+ COSTS_N_INSNS (69), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* R4300 */
+ DEFAULT_COSTS
+ },
+ { /* R4600 */
+ DEFAULT_COSTS
+ },
+ { /* R4650 */
+ DEFAULT_COSTS
+ },
+ { /* R5000 */
+ COSTS_N_INSNS (6), /* fp_add */
+ COSTS_N_INSNS (4), /* fp_mult_sf */
+ COSTS_N_INSNS (5), /* fp_mult_df */
+ COSTS_N_INSNS (23), /* fp_div_sf */
+ COSTS_N_INSNS (36), /* fp_div_df */
+ COSTS_N_INSNS (5), /* int_mult_si */
+ COSTS_N_INSNS (5), /* int_mult_di */
+ COSTS_N_INSNS (36), /* int_div_si */
+ COSTS_N_INSNS (36), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* R5400 */
+ COSTS_N_INSNS (6), /* fp_add */
+ COSTS_N_INSNS (5), /* fp_mult_sf */
+ COSTS_N_INSNS (6), /* fp_mult_df */
+ COSTS_N_INSNS (30), /* fp_div_sf */
+ COSTS_N_INSNS (59), /* fp_div_df */
+ COSTS_N_INSNS (3), /* int_mult_si */
+ COSTS_N_INSNS (4), /* int_mult_di */
+ COSTS_N_INSNS (42), /* int_div_si */
+ COSTS_N_INSNS (74), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* R5500 */
+ COSTS_N_INSNS (6), /* fp_add */
+ COSTS_N_INSNS (5), /* fp_mult_sf */
+ COSTS_N_INSNS (6), /* fp_mult_df */
+ COSTS_N_INSNS (30), /* fp_div_sf */
+ COSTS_N_INSNS (59), /* fp_div_df */
+ COSTS_N_INSNS (5), /* int_mult_si */
+ COSTS_N_INSNS (9), /* int_mult_di */
+ COSTS_N_INSNS (42), /* int_div_si */
+ COSTS_N_INSNS (74), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* R7000 */
+ /* The only costs that are changed here are
+ integer multiplication. */
+ COSTS_N_INSNS (6), /* fp_add */
+ COSTS_N_INSNS (7), /* fp_mult_sf */
+ COSTS_N_INSNS (8), /* fp_mult_df */
+ COSTS_N_INSNS (23), /* fp_div_sf */
+ COSTS_N_INSNS (36), /* fp_div_df */
+ COSTS_N_INSNS (5), /* int_mult_si */
+ COSTS_N_INSNS (9), /* int_mult_di */
+ COSTS_N_INSNS (69), /* int_div_si */
+ COSTS_N_INSNS (69), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* R8000 */
+ DEFAULT_COSTS
+ },
+ { /* R9000 */
+ /* The only costs that are changed here are
+ integer multiplication. */
+ COSTS_N_INSNS (6), /* fp_add */
+ COSTS_N_INSNS (7), /* fp_mult_sf */
+ COSTS_N_INSNS (8), /* fp_mult_df */
+ COSTS_N_INSNS (23), /* fp_div_sf */
+ COSTS_N_INSNS (36), /* fp_div_df */
+ COSTS_N_INSNS (3), /* int_mult_si */
+ COSTS_N_INSNS (8), /* int_mult_di */
+ COSTS_N_INSNS (69), /* int_div_si */
+ COSTS_N_INSNS (69), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* R1x000 */
+ COSTS_N_INSNS (2), /* fp_add */
+ COSTS_N_INSNS (2), /* fp_mult_sf */
+ COSTS_N_INSNS (2), /* fp_mult_df */
+ COSTS_N_INSNS (12), /* fp_div_sf */
+ COSTS_N_INSNS (19), /* fp_div_df */
+ COSTS_N_INSNS (5), /* int_mult_si */
+ COSTS_N_INSNS (9), /* int_mult_di */
+ COSTS_N_INSNS (34), /* int_div_si */
+ COSTS_N_INSNS (66), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* SB1 */
+ /* These costs are the same as the SB-1A below. */
+ COSTS_N_INSNS (4), /* fp_add */
+ COSTS_N_INSNS (4), /* fp_mult_sf */
+ COSTS_N_INSNS (4), /* fp_mult_df */
+ COSTS_N_INSNS (24), /* fp_div_sf */
+ COSTS_N_INSNS (32), /* fp_div_df */
+ COSTS_N_INSNS (3), /* int_mult_si */
+ COSTS_N_INSNS (4), /* int_mult_di */
+ COSTS_N_INSNS (36), /* int_div_si */
+ COSTS_N_INSNS (68), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* SB1-A */
+ /* These costs are the same as the SB-1 above. */
+ COSTS_N_INSNS (4), /* fp_add */
+ COSTS_N_INSNS (4), /* fp_mult_sf */
+ COSTS_N_INSNS (4), /* fp_mult_df */
+ COSTS_N_INSNS (24), /* fp_div_sf */
+ COSTS_N_INSNS (32), /* fp_div_df */
+ COSTS_N_INSNS (3), /* int_mult_si */
+ COSTS_N_INSNS (4), /* int_mult_di */
+ COSTS_N_INSNS (36), /* int_div_si */
+ COSTS_N_INSNS (68), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* SR71000 */
+ DEFAULT_COSTS
+ },
+ { /* XLR */
+ SOFT_FP_COSTS,
+ COSTS_N_INSNS (8), /* int_mult_si */
+ COSTS_N_INSNS (8), /* int_mult_di */
+ COSTS_N_INSNS (72), /* int_div_si */
+ COSTS_N_INSNS (72), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ }
+};
+
+static rtx mips_find_pic_call_symbol (rtx, rtx, bool);
+static int mips_register_move_cost (enum machine_mode, reg_class_t,
+ reg_class_t);
+static unsigned int mips_function_arg_boundary (enum machine_mode, const_tree);
+
+/* This hash table keeps track of implicit "mips16" and "nomips16" attributes
+ for -mflip_mips16. It maps decl names onto a boolean mode setting. */
+struct GTY (()) mflip_mips16_entry {
+ const char *name;
+ bool mips16_p;
+};
+static GTY ((param_is (struct mflip_mips16_entry))) htab_t mflip_mips16_htab;
+
+/* Hash table callbacks for mflip_mips16_htab. */
+
+static hashval_t
+mflip_mips16_htab_hash (const void *entry)
+{
+ return htab_hash_string (((const struct mflip_mips16_entry *) entry)->name);
+}
+
+static int
+mflip_mips16_htab_eq (const void *entry, const void *name)
+{
+ return strcmp (((const struct mflip_mips16_entry *) entry)->name,
+ (const char *) name) == 0;
+}
+
+/* True if -mflip-mips16 should next add an attribute for the default MIPS16
+ mode, false if it should next add an attribute for the opposite mode. */
+static GTY(()) bool mips16_flipper;
+
+/* DECL is a function that needs a default "mips16" or "nomips16" attribute
+ for -mflip-mips16. Return true if it should use "mips16" and false if
+ it should use "nomips16". */
+
+static bool
+mflip_mips16_use_mips16_p (tree decl)
+{
+ struct mflip_mips16_entry *entry;
+ const char *name;
+ hashval_t hash;
+ void **slot;
+
+ /* Use the opposite of the command-line setting for anonymous decls. */
+ if (!DECL_NAME (decl))
+ return !mips_base_mips16;
+
+ if (!mflip_mips16_htab)
+ mflip_mips16_htab = htab_create_ggc (37, mflip_mips16_htab_hash,
+ mflip_mips16_htab_eq, NULL);
+
+ name = IDENTIFIER_POINTER (DECL_NAME (decl));
+ hash = htab_hash_string (name);
+ slot = htab_find_slot_with_hash (mflip_mips16_htab, name, hash, INSERT);
+ entry = (struct mflip_mips16_entry *) *slot;
+ if (!entry)
+ {
+ mips16_flipper = !mips16_flipper;
+ entry = ggc_alloc_mflip_mips16_entry ();
+ entry->name = name;
+ entry->mips16_p = mips16_flipper ? !mips_base_mips16 : mips_base_mips16;
+ *slot = entry;
+ }
+ return entry->mips16_p;
+}
+
+/* Predicates to test for presence of "near" and "far"/"long_call"
+ attributes on the given TYPE. */
+
+static bool
+mips_near_type_p (const_tree type)
+{
+ return lookup_attribute ("near", TYPE_ATTRIBUTES (type)) != NULL;
+}
+
+static bool
+mips_far_type_p (const_tree type)
+{
+ return (lookup_attribute ("long_call", TYPE_ATTRIBUTES (type)) != NULL
+ || lookup_attribute ("far", TYPE_ATTRIBUTES (type)) != NULL);
+}
+
+/* Similar predicates for "mips16"/"nomips16" function attributes. */
+
+static bool
+mips_mips16_decl_p (const_tree decl)
+{
+ return lookup_attribute ("mips16", DECL_ATTRIBUTES (decl)) != NULL;
+}
+
+static bool
+mips_nomips16_decl_p (const_tree decl)
+{
+ return lookup_attribute ("nomips16", DECL_ATTRIBUTES (decl)) != NULL;
+}
+
+/* Check if the interrupt attribute is set for a function. */
+
+static bool
+mips_interrupt_type_p (tree type)
+{
+ return lookup_attribute ("interrupt", TYPE_ATTRIBUTES (type)) != NULL;
+}
+
+/* Check if the attribute to use shadow register set is set for a function. */
+
+static bool
+mips_use_shadow_register_set_p (tree type)
+{
+ return lookup_attribute ("use_shadow_register_set",
+ TYPE_ATTRIBUTES (type)) != NULL;
+}
+
+/* Check if the attribute to keep interrupts masked is set for a function. */
+
+static bool
+mips_keep_interrupts_masked_p (tree type)
+{
+ return lookup_attribute ("keep_interrupts_masked",
+ TYPE_ATTRIBUTES (type)) != NULL;
+}
+
+/* Check if the attribute to use debug exception return is set for
+ a function. */
+
+static bool
+mips_use_debug_exception_return_p (tree type)
+{
+ return lookup_attribute ("use_debug_exception_return",
+ TYPE_ATTRIBUTES (type)) != NULL;
+}
+
+/* Return true if function DECL is a MIPS16 function. Return the ambient
+ setting if DECL is null. */
+
+static bool
+mips_use_mips16_mode_p (tree decl)
+{
+ if (decl)
+ {
+ /* Nested functions must use the same frame pointer as their
+ parent and must therefore use the same ISA mode. */
+ tree parent = decl_function_context (decl);
+ if (parent)
+ decl = parent;
+ if (mips_mips16_decl_p (decl))
+ return true;
+ if (mips_nomips16_decl_p (decl))
+ return false;
+ }
+ return mips_base_mips16;
+}
+
+/* Implement TARGET_COMP_TYPE_ATTRIBUTES. */
+
+static int
+mips_comp_type_attributes (const_tree type1, const_tree type2)
+{
+ /* Disallow mixed near/far attributes. */
+ if (mips_far_type_p (type1) && mips_near_type_p (type2))
+ return 0;
+ if (mips_near_type_p (type1) && mips_far_type_p (type2))
+ return 0;
+ return 1;
+}
+
+/* Implement TARGET_INSERT_ATTRIBUTES. */
+
+static void
+mips_insert_attributes (tree decl, tree *attributes)
+{
+ const char *name;
+ bool mips16_p, nomips16_p;
+
+ /* Check for "mips16" and "nomips16" attributes. */
+ mips16_p = lookup_attribute ("mips16", *attributes) != NULL;
+ nomips16_p = lookup_attribute ("nomips16", *attributes) != NULL;
+ if (TREE_CODE (decl) != FUNCTION_DECL)
+ {
+ if (mips16_p)
+ error ("%qs attribute only applies to functions", "mips16");
+ if (nomips16_p)
+ error ("%qs attribute only applies to functions", "nomips16");
+ }
+ else
+ {
+ mips16_p |= mips_mips16_decl_p (decl);
+ nomips16_p |= mips_nomips16_decl_p (decl);
+ if (mips16_p || nomips16_p)
+ {
+ /* DECL cannot be simultaneously "mips16" and "nomips16". */
+ if (mips16_p && nomips16_p)
+ error ("%qE cannot have both %<mips16%> and "
+ "%<nomips16%> attributes",
+ DECL_NAME (decl));
+ }
+ else if (TARGET_FLIP_MIPS16 && !DECL_ARTIFICIAL (decl))
+ {
+ /* Implement -mflip-mips16. If DECL has neither a "nomips16" nor a
+ "mips16" attribute, arbitrarily pick one. We must pick the same
+ setting for duplicate declarations of a function. */
+ name = mflip_mips16_use_mips16_p (decl) ? "mips16" : "nomips16";
+ *attributes = tree_cons (get_identifier (name), NULL, *attributes);
+ }
+ }
+}
+
+/* Implement TARGET_MERGE_DECL_ATTRIBUTES. */
+
+static tree
+mips_merge_decl_attributes (tree olddecl, tree newdecl)
+{
+ /* The decls' "mips16" and "nomips16" attributes must match exactly. */
+ if (mips_mips16_decl_p (olddecl) != mips_mips16_decl_p (newdecl))
+ error ("%qE redeclared with conflicting %qs attributes",
+ DECL_NAME (newdecl), "mips16");
+ if (mips_nomips16_decl_p (olddecl) != mips_nomips16_decl_p (newdecl))
+ error ("%qE redeclared with conflicting %qs attributes",
+ DECL_NAME (newdecl), "nomips16");
+
+ return merge_attributes (DECL_ATTRIBUTES (olddecl),
+ DECL_ATTRIBUTES (newdecl));
+}
+
+/* If X is a PLUS of a CONST_INT, return the two terms in *BASE_PTR
+ and *OFFSET_PTR. Return X in *BASE_PTR and 0 in *OFFSET_PTR otherwise. */
+
+static void
+mips_split_plus (rtx x, rtx *base_ptr, HOST_WIDE_INT *offset_ptr)
+{
+ if (GET_CODE (x) == PLUS && CONST_INT_P (XEXP (x, 1)))
+ {
+ *base_ptr = XEXP (x, 0);
+ *offset_ptr = INTVAL (XEXP (x, 1));
+ }
+ else
+ {
+ *base_ptr = x;
+ *offset_ptr = 0;
+ }
+}
+
+static unsigned int mips_build_integer (struct mips_integer_op *,
+ unsigned HOST_WIDE_INT);
+
+/* A subroutine of mips_build_integer, with the same interface.
+ Assume that the final action in the sequence should be a left shift. */
+
+static unsigned int
+mips_build_shift (struct mips_integer_op *codes, HOST_WIDE_INT value)
+{
+ unsigned int i, shift;
+
+ /* Shift VALUE right until its lowest bit is set. Shift arithmetically
+ since signed numbers are easier to load than unsigned ones. */
+ shift = 0;
+ while ((value & 1) == 0)
+ value /= 2, shift++;
+
+ i = mips_build_integer (codes, value);
+ codes[i].code = ASHIFT;
+ codes[i].value = shift;
+ return i + 1;
+}
+
+/* As for mips_build_shift, but assume that the final action will be
+ an IOR or PLUS operation. */
+
+static unsigned int
+mips_build_lower (struct mips_integer_op *codes, unsigned HOST_WIDE_INT value)
+{
+ unsigned HOST_WIDE_INT high;
+ unsigned int i;
+
+ high = value & ~(unsigned HOST_WIDE_INT) 0xffff;
+ if (!LUI_OPERAND (high) && (value & 0x18000) == 0x18000)
+ {
+ /* The constant is too complex to load with a simple LUI/ORI pair,
+ so we want to give the recursive call as many trailing zeros as
+ possible. In this case, we know bit 16 is set and that the
+ low 16 bits form a negative number. If we subtract that number
+ from VALUE, we will clear at least the lowest 17 bits, maybe more. */
+ i = mips_build_integer (codes, CONST_HIGH_PART (value));
+ codes[i].code = PLUS;
+ codes[i].value = CONST_LOW_PART (value);
+ }
+ else
+ {
+ /* Either this is a simple LUI/ORI pair, or clearing the lowest 16
+ bits gives a value with at least 17 trailing zeros. */
+ i = mips_build_integer (codes, high);
+ codes[i].code = IOR;
+ codes[i].value = value & 0xffff;
+ }
+ return i + 1;
+}
+
+/* Fill CODES with a sequence of rtl operations to load VALUE.
+ Return the number of operations needed. */
+
+static unsigned int
+mips_build_integer (struct mips_integer_op *codes,
+ unsigned HOST_WIDE_INT value)
+{
+ if (SMALL_OPERAND (value)
+ || SMALL_OPERAND_UNSIGNED (value)
+ || LUI_OPERAND (value))
+ {
+ /* The value can be loaded with a single instruction. */
+ codes[0].code = UNKNOWN;
+ codes[0].value = value;
+ return 1;
+ }
+ else if ((value & 1) != 0 || LUI_OPERAND (CONST_HIGH_PART (value)))
+ {
+ /* Either the constant is a simple LUI/ORI combination or its
+ lowest bit is set. We don't want to shift in this case. */
+ return mips_build_lower (codes, value);
+ }
+ else if ((value & 0xffff) == 0)
+ {
+ /* The constant will need at least three actions. The lowest
+ 16 bits are clear, so the final action will be a shift. */
+ return mips_build_shift (codes, value);
+ }
+ else
+ {
+ /* The final action could be a shift, add or inclusive OR.
+ Rather than use a complex condition to select the best
+ approach, try both mips_build_shift and mips_build_lower
+ and pick the one that gives the shortest sequence.
+ Note that this case is only used once per constant. */
+ struct mips_integer_op alt_codes[MIPS_MAX_INTEGER_OPS];
+ unsigned int cost, alt_cost;
+
+ cost = mips_build_shift (codes, value);
+ alt_cost = mips_build_lower (alt_codes, value);
+ if (alt_cost < cost)
+ {
+ memcpy (codes, alt_codes, alt_cost * sizeof (codes[0]));
+ cost = alt_cost;
+ }
+ return cost;
+ }
+}
+
+/* Return true if symbols of type TYPE require a GOT access. */
+
+static bool
+mips_got_symbol_type_p (enum mips_symbol_type type)
+{
+ switch (type)
+ {
+ case SYMBOL_GOT_PAGE_OFST:
+ case SYMBOL_GOT_DISP:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+/* Return true if X is a thread-local symbol. */
+
+static bool
+mips_tls_symbol_p (rtx x)
+{
+ return GET_CODE (x) == SYMBOL_REF && SYMBOL_REF_TLS_MODEL (x) != 0;
+}
+
+/* Return true if SYMBOL_REF X is associated with a global symbol
+ (in the STB_GLOBAL sense). */
+
+static bool
+mips_global_symbol_p (const_rtx x)
+{
+ const_tree decl = SYMBOL_REF_DECL (x);
+
+ if (!decl)
+ return !SYMBOL_REF_LOCAL_P (x) || SYMBOL_REF_EXTERNAL_P (x);
+
+ /* Weakref symbols are not TREE_PUBLIC, but their targets are global
+ or weak symbols. Relocations in the object file will be against
+ the target symbol, so it's that symbol's binding that matters here. */
+ return DECL_P (decl) && (TREE_PUBLIC (decl) || DECL_WEAK (decl));
+}
+
+/* Return true if function X is a libgcc MIPS16 stub function. */
+
+static bool
+mips16_stub_function_p (const_rtx x)
+{
+ return (GET_CODE (x) == SYMBOL_REF
+ && strncmp (XSTR (x, 0), "__mips16_", 9) == 0);
+}
+
+/* Return true if function X is a locally-defined and locally-binding
+ MIPS16 function. */
+
+static bool
+mips16_local_function_p (const_rtx x)
+{
+ return (GET_CODE (x) == SYMBOL_REF
+ && SYMBOL_REF_LOCAL_P (x)
+ && !SYMBOL_REF_EXTERNAL_P (x)
+ && mips_use_mips16_mode_p (SYMBOL_REF_DECL (x)));
+}
+
+/* Return true if SYMBOL_REF X binds locally. */
+
+static bool
+mips_symbol_binds_local_p (const_rtx x)
+{
+ return (SYMBOL_REF_DECL (x)
+ ? targetm.binds_local_p (SYMBOL_REF_DECL (x))
+ : SYMBOL_REF_LOCAL_P (x));
+}
+
+/* Return true if rtx constants of mode MODE should be put into a small
+ data section. */
+
+static bool
+mips_rtx_constant_in_small_data_p (enum machine_mode mode)
+{
+ return (!TARGET_EMBEDDED_DATA
+ && TARGET_LOCAL_SDATA
+ && GET_MODE_SIZE (mode) <= mips_small_data_threshold);
+}
+
+/* Return true if X should not be moved directly into register $25.
+ We need this because many versions of GAS will treat "la $25,foo" as
+ part of a call sequence and so allow a global "foo" to be lazily bound. */
+
+bool
+mips_dangerous_for_la25_p (rtx x)
+{
+ return (!TARGET_EXPLICIT_RELOCS
+ && TARGET_USE_GOT
+ && GET_CODE (x) == SYMBOL_REF
+ && mips_global_symbol_p (x));
+}
+
+/* Return true if calls to X might need $25 to be valid on entry. */
+
+bool
+mips_use_pic_fn_addr_reg_p (const_rtx x)
+{
+ if (!TARGET_USE_PIC_FN_ADDR_REG)
+ return false;
+
+ /* MIPS16 stub functions are guaranteed not to use $25. */
+ if (mips16_stub_function_p (x))
+ return false;
+
+ if (GET_CODE (x) == SYMBOL_REF)
+ {
+ /* If PLTs and copy relocations are available, the static linker
+ will make sure that $25 is valid on entry to the target function. */
+ if (TARGET_ABICALLS_PIC0)
+ return false;
+
+ /* Locally-defined functions use absolute accesses to set up
+ the global pointer. */
+ if (TARGET_ABSOLUTE_ABICALLS
+ && mips_symbol_binds_local_p (x)
+ && !SYMBOL_REF_EXTERNAL_P (x))
+ return false;
+ }
+
+ return true;
+}
+
+/* Return the method that should be used to access SYMBOL_REF or
+ LABEL_REF X in context CONTEXT. */
+
+static enum mips_symbol_type
+mips_classify_symbol (const_rtx x, enum mips_symbol_context context)
+{
+ if (TARGET_RTP_PIC)
+ return SYMBOL_GOT_DISP;
+
+ if (GET_CODE (x) == LABEL_REF)
+ {
+ /* Only return SYMBOL_PC_RELATIVE if we are generating MIPS16
+ code and if we know that the label is in the current function's
+ text section. LABEL_REFs are used for jump tables as well as
+ text labels, so we must check whether jump tables live in the
+ text section. */
+ if (TARGET_MIPS16_SHORT_JUMP_TABLES
+ && !LABEL_REF_NONLOCAL_P (x))
+ return SYMBOL_PC_RELATIVE;
+
+ if (TARGET_ABICALLS && !TARGET_ABSOLUTE_ABICALLS)
+ return SYMBOL_GOT_PAGE_OFST;
+
+ return SYMBOL_ABSOLUTE;
+ }
+
+ gcc_assert (GET_CODE (x) == SYMBOL_REF);
+
+ if (SYMBOL_REF_TLS_MODEL (x))
+ return SYMBOL_TLS;
+
+ if (CONSTANT_POOL_ADDRESS_P (x))
+ {
+ if (TARGET_MIPS16_TEXT_LOADS)
+ return SYMBOL_PC_RELATIVE;
+
+ if (TARGET_MIPS16_PCREL_LOADS && context == SYMBOL_CONTEXT_MEM)
+ return SYMBOL_PC_RELATIVE;
+
+ if (mips_rtx_constant_in_small_data_p (get_pool_mode (x)))
+ return SYMBOL_GP_RELATIVE;
+ }
+
+ /* Do not use small-data accesses for weak symbols; they may end up
+ being zero. */
+ if (TARGET_GPOPT && SYMBOL_REF_SMALL_P (x) && !SYMBOL_REF_WEAK (x))
+ return SYMBOL_GP_RELATIVE;
+
+ /* Don't use GOT accesses for locally-binding symbols when -mno-shared
+ is in effect. */
+ if (TARGET_ABICALLS_PIC2
+ && !(TARGET_ABSOLUTE_ABICALLS && mips_symbol_binds_local_p (x)))
+ {
+ /* There are three cases to consider:
+
+ - o32 PIC (either with or without explicit relocs)
+ - n32/n64 PIC without explicit relocs
+ - n32/n64 PIC with explicit relocs
+
+ In the first case, both local and global accesses will use an
+ R_MIPS_GOT16 relocation. We must correctly predict which of
+ the two semantics (local or global) the assembler and linker
+ will apply. The choice depends on the symbol's binding rather
+ than its visibility.
+
+ In the second case, the assembler will not use R_MIPS_GOT16
+ relocations, but it chooses between local and global accesses
+ in the same way as for o32 PIC.
+
+ In the third case we have more freedom since both forms of
+ access will work for any kind of symbol. However, there seems
+ little point in doing things differently. */
+ if (mips_global_symbol_p (x))
+ return SYMBOL_GOT_DISP;
+
+ return SYMBOL_GOT_PAGE_OFST;
+ }
+
+ if (TARGET_MIPS16_PCREL_LOADS && context != SYMBOL_CONTEXT_CALL)
+ return SYMBOL_FORCE_TO_MEM;
+
+ return SYMBOL_ABSOLUTE;
+}
+
+/* Classify the base of symbolic expression X, given that X appears in
+ context CONTEXT. */
+
+static enum mips_symbol_type
+mips_classify_symbolic_expression (rtx x, enum mips_symbol_context context)
+{
+ rtx offset;
+
+ split_const (x, &x, &offset);
+ if (UNSPEC_ADDRESS_P (x))
+ return UNSPEC_ADDRESS_TYPE (x);
+
+ return mips_classify_symbol (x, context);
+}
+
+/* Return true if OFFSET is within the range [0, ALIGN), where ALIGN
+ is the alignment in bytes of SYMBOL_REF X. */
+
+static bool
+mips_offset_within_alignment_p (rtx x, HOST_WIDE_INT offset)
+{
+ HOST_WIDE_INT align;
+
+ align = SYMBOL_REF_DECL (x) ? DECL_ALIGN_UNIT (SYMBOL_REF_DECL (x)) : 1;
+ return IN_RANGE (offset, 0, align - 1);
+}
+
+/* Return true if X is a symbolic constant that can be used in context
+ CONTEXT. If it is, store the type of the symbol in *SYMBOL_TYPE. */
+
+bool
+mips_symbolic_constant_p (rtx x, enum mips_symbol_context context,
+ enum mips_symbol_type *symbol_type)
+{
+ rtx offset;
+
+ split_const (x, &x, &offset);
+ if (UNSPEC_ADDRESS_P (x))
+ {
+ *symbol_type = UNSPEC_ADDRESS_TYPE (x);
+ x = UNSPEC_ADDRESS (x);
+ }
+ else if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF)
+ {
+ *symbol_type = mips_classify_symbol (x, context);
+ if (*symbol_type == SYMBOL_TLS)
+ return false;
+ }
+ else
+ return false;
+
+ if (offset == const0_rtx)
+ return true;
+
+ /* Check whether a nonzero offset is valid for the underlying
+ relocations. */
+ switch (*symbol_type)
+ {
+ case SYMBOL_ABSOLUTE:
+ case SYMBOL_FORCE_TO_MEM:
+ case SYMBOL_32_HIGH:
+ case SYMBOL_64_HIGH:
+ case SYMBOL_64_MID:
+ case SYMBOL_64_LOW:
+ /* If the target has 64-bit pointers and the object file only
+ supports 32-bit symbols, the values of those symbols will be
+ sign-extended. In this case we can't allow an arbitrary offset
+ in case the 32-bit value X + OFFSET has a different sign from X. */
+ if (Pmode == DImode && !ABI_HAS_64BIT_SYMBOLS)
+ return offset_within_block_p (x, INTVAL (offset));
+
+ /* In other cases the relocations can handle any offset. */
+ return true;
+
+ case SYMBOL_PC_RELATIVE:
+ /* Allow constant pool references to be converted to LABEL+CONSTANT.
+ In this case, we no longer have access to the underlying constant,
+ but the original symbol-based access was known to be valid. */
+ if (GET_CODE (x) == LABEL_REF)
+ return true;
+
+ /* Fall through. */
+
+ case SYMBOL_GP_RELATIVE:
+ /* Make sure that the offset refers to something within the
+ same object block. This should guarantee that the final
+ PC- or GP-relative offset is within the 16-bit limit. */
+ return offset_within_block_p (x, INTVAL (offset));
+
+ case SYMBOL_GOT_PAGE_OFST:
+ case SYMBOL_GOTOFF_PAGE:
+ /* If the symbol is global, the GOT entry will contain the symbol's
+ address, and we will apply a 16-bit offset after loading it.
+ If the symbol is local, the linker should provide enough local
+ GOT entries for a 16-bit offset, but larger offsets may lead
+ to GOT overflow. */
+ return SMALL_INT (offset);
+
+ case SYMBOL_TPREL:
+ case SYMBOL_DTPREL:
+ /* There is no carry between the HI and LO REL relocations, so the
+ offset is only valid if we know it won't lead to such a carry. */
+ return mips_offset_within_alignment_p (x, INTVAL (offset));
+
+ case SYMBOL_GOT_DISP:
+ case SYMBOL_GOTOFF_DISP:
+ case SYMBOL_GOTOFF_CALL:
+ case SYMBOL_GOTOFF_LOADGP:
+ case SYMBOL_TLSGD:
+ case SYMBOL_TLSLDM:
+ case SYMBOL_GOTTPREL:
+ case SYMBOL_TLS:
+ case SYMBOL_HALF:
+ return false;
+ }
+ gcc_unreachable ();
+}
+
+/* Like mips_symbol_insns, but treat extended MIPS16 instructions as a
+ single instruction. We rely on the fact that, in the worst case,
+ all instructions involved in a MIPS16 address calculation are usually
+ extended ones. */
+
+static int
+mips_symbol_insns_1 (enum mips_symbol_type type, enum machine_mode mode)
+{
+ switch (type)
+ {
+ case SYMBOL_ABSOLUTE:
+ /* When using 64-bit symbols, we need 5 preparatory instructions,
+ such as:
+
+ lui $at,%highest(symbol)
+ daddiu $at,$at,%higher(symbol)
+ dsll $at,$at,16
+ daddiu $at,$at,%hi(symbol)
+ dsll $at,$at,16
+
+ The final address is then $at + %lo(symbol). With 32-bit
+ symbols we just need a preparatory LUI for normal mode and
+ a preparatory LI and SLL for MIPS16. */
+ return ABI_HAS_64BIT_SYMBOLS ? 6 : TARGET_MIPS16 ? 3 : 2;
+
+ case SYMBOL_GP_RELATIVE:
+ /* Treat GP-relative accesses as taking a single instruction on
+ MIPS16 too; the copy of $gp can often be shared. */
+ return 1;
+
+ case SYMBOL_PC_RELATIVE:
+ /* PC-relative constants can be only be used with ADDIUPC,
+ DADDIUPC, LWPC and LDPC. */
+ if (mode == MAX_MACHINE_MODE
+ || GET_MODE_SIZE (mode) == 4
+ || GET_MODE_SIZE (mode) == 8)
+ return 1;
+
+ /* The constant must be loaded using ADDIUPC or DADDIUPC first. */
+ return 0;
+
+ case SYMBOL_FORCE_TO_MEM:
+ /* LEAs will be converted into constant-pool references by
+ mips_reorg. */
+ if (mode == MAX_MACHINE_MODE)
+ return 1;
+
+ /* The constant must be loaded and then dereferenced. */
+ return 0;
+
+ case SYMBOL_GOT_DISP:
+ /* The constant will have to be loaded from the GOT before it
+ is used in an address. */
+ if (mode != MAX_MACHINE_MODE)
+ return 0;
+
+ /* Fall through. */
+
+ case SYMBOL_GOT_PAGE_OFST:
+ /* Unless -funit-at-a-time is in effect, we can't be sure whether the
+ local/global classification is accurate. The worst cases are:
+
+ (1) For local symbols when generating o32 or o64 code. The assembler
+ will use:
+
+ lw $at,%got(symbol)
+ nop
+
+ ...and the final address will be $at + %lo(symbol).
+
+ (2) For global symbols when -mxgot. The assembler will use:
+
+ lui $at,%got_hi(symbol)
+ (d)addu $at,$at,$gp
+
+ ...and the final address will be $at + %got_lo(symbol). */
+ return 3;
+
+ case SYMBOL_GOTOFF_PAGE:
+ case SYMBOL_GOTOFF_DISP:
+ case SYMBOL_GOTOFF_CALL:
+ case SYMBOL_GOTOFF_LOADGP:
+ case SYMBOL_32_HIGH:
+ case SYMBOL_64_HIGH:
+ case SYMBOL_64_MID:
+ case SYMBOL_64_LOW:
+ case SYMBOL_TLSGD:
+ case SYMBOL_TLSLDM:
+ case SYMBOL_DTPREL:
+ case SYMBOL_GOTTPREL:
+ case SYMBOL_TPREL:
+ case SYMBOL_HALF:
+ /* A 16-bit constant formed by a single relocation, or a 32-bit
+ constant formed from a high 16-bit relocation and a low 16-bit
+ relocation. Use mips_split_p to determine which. 32-bit
+ constants need an "lui; addiu" sequence for normal mode and
+ an "li; sll; addiu" sequence for MIPS16 mode. */
+ return !mips_split_p[type] ? 1 : TARGET_MIPS16 ? 3 : 2;
+
+ case SYMBOL_TLS:
+ /* We don't treat a bare TLS symbol as a constant. */
+ return 0;
+ }
+ gcc_unreachable ();
+}
+
+/* If MODE is MAX_MACHINE_MODE, return the number of instructions needed
+ to load symbols of type TYPE into a register. Return 0 if the given
+ type of symbol cannot be used as an immediate operand.
+
+ Otherwise, return the number of instructions needed to load or store
+ values of mode MODE to or from addresses of type TYPE. Return 0 if
+ the given type of symbol is not valid in addresses.
+
+ In both cases, treat extended MIPS16 instructions as two instructions. */
+
+static int
+mips_symbol_insns (enum mips_symbol_type type, enum machine_mode mode)
+{
+ return mips_symbol_insns_1 (type, mode) * (TARGET_MIPS16 ? 2 : 1);
+}
+
+/* A for_each_rtx callback. Stop the search if *X references a
+ thread-local symbol. */
+
+static int
+mips_tls_symbol_ref_1 (rtx *x, void *data ATTRIBUTE_UNUSED)
+{
+ return mips_tls_symbol_p (*x);
+}
+
+/* Implement TARGET_CANNOT_FORCE_CONST_MEM. */
+
+static bool
+mips_cannot_force_const_mem (rtx x)
+{
+ enum mips_symbol_type type;
+ rtx base, offset;
+
+ /* There is no assembler syntax for expressing an address-sized
+ high part. */
+ if (GET_CODE (x) == HIGH)
+ return true;
+
+ /* As an optimization, reject constants that mips_legitimize_move
+ can expand inline.
+
+ Suppose we have a multi-instruction sequence that loads constant C
+ into register R. If R does not get allocated a hard register, and
+ R is used in an operand that allows both registers and memory
+ references, reload will consider forcing C into memory and using
+ one of the instruction's memory alternatives. Returning false
+ here will force it to use an input reload instead. */
+ if (CONST_INT_P (x) && LEGITIMATE_CONSTANT_P (x))
+ return true;
+
+ split_const (x, &base, &offset);
+ if (mips_symbolic_constant_p (base, SYMBOL_CONTEXT_LEA, &type)
+ && type != SYMBOL_FORCE_TO_MEM)
+ {
+ /* The same optimization as for CONST_INT. */
+ if (SMALL_INT (offset) && mips_symbol_insns (type, MAX_MACHINE_MODE) > 0)
+ return true;
+
+ /* If MIPS16 constant pools live in the text section, they should
+ not refer to anything that might need run-time relocation. */
+ if (TARGET_MIPS16_PCREL_LOADS && mips_got_symbol_type_p (type))
+ return true;
+ }
+
+ /* TLS symbols must be computed by mips_legitimize_move. */
+ if (for_each_rtx (&x, &mips_tls_symbol_ref_1, NULL))
+ return true;
+
+ return false;
+}
+
+/* Implement TARGET_USE_BLOCKS_FOR_CONSTANT_P. We can't use blocks for
+ constants when we're using a per-function constant pool. */
+
+static bool
+mips_use_blocks_for_constant_p (enum machine_mode mode ATTRIBUTE_UNUSED,
+ const_rtx x ATTRIBUTE_UNUSED)
+{
+ return !TARGET_MIPS16_PCREL_LOADS;
+}
+
+/* Return true if register REGNO is a valid base register for mode MODE.
+ STRICT_P is true if REG_OK_STRICT is in effect. */
+
+int
+mips_regno_mode_ok_for_base_p (int regno, enum machine_mode mode,
+ bool strict_p)
+{
+ if (!HARD_REGISTER_NUM_P (regno))
+ {
+ if (!strict_p)
+ return true;
+ regno = reg_renumber[regno];
+ }
+
+ /* These fake registers will be eliminated to either the stack or
+ hard frame pointer, both of which are usually valid base registers.
+ Reload deals with the cases where the eliminated form isn't valid. */
+ if (regno == ARG_POINTER_REGNUM || regno == FRAME_POINTER_REGNUM)
+ return true;
+
+ /* In MIPS16 mode, the stack pointer can only address word and doubleword
+ values, nothing smaller. There are two problems here:
+
+ (a) Instantiating virtual registers can introduce new uses of the
+ stack pointer. If these virtual registers are valid addresses,
+ the stack pointer should be too.
+
+ (b) Most uses of the stack pointer are not made explicit until
+ FRAME_POINTER_REGNUM and ARG_POINTER_REGNUM have been eliminated.
+ We don't know until that stage whether we'll be eliminating to the
+ stack pointer (which needs the restriction) or the hard frame
+ pointer (which doesn't).
+
+ All in all, it seems more consistent to only enforce this restriction
+ during and after reload. */
+ if (TARGET_MIPS16 && regno == STACK_POINTER_REGNUM)
+ return !strict_p || GET_MODE_SIZE (mode) == 4 || GET_MODE_SIZE (mode) == 8;
+
+ return TARGET_MIPS16 ? M16_REG_P (regno) : GP_REG_P (regno);
+}
+
+/* Return true if X is a valid base register for mode MODE.
+ STRICT_P is true if REG_OK_STRICT is in effect. */
+
+static bool
+mips_valid_base_register_p (rtx x, enum machine_mode mode, bool strict_p)
+{
+ if (!strict_p && GET_CODE (x) == SUBREG)
+ x = SUBREG_REG (x);
+
+ return (REG_P (x)
+ && mips_regno_mode_ok_for_base_p (REGNO (x), mode, strict_p));
+}
+
+/* Return true if, for every base register BASE_REG, (plus BASE_REG X)
+ can address a value of mode MODE. */
+
+static bool
+mips_valid_offset_p (rtx x, enum machine_mode mode)
+{
+ /* Check that X is a signed 16-bit number. */
+ if (!const_arith_operand (x, Pmode))
+ return false;
+
+ /* We may need to split multiword moves, so make sure that every word
+ is accessible. */
+ if (GET_MODE_SIZE (mode) > UNITS_PER_WORD
+ && !SMALL_OPERAND (INTVAL (x) + GET_MODE_SIZE (mode) - UNITS_PER_WORD))
+ return false;
+
+ return true;
+}
+
+/* Return true if a LO_SUM can address a value of mode MODE when the
+ LO_SUM symbol has type SYMBOL_TYPE. */
+
+static bool
+mips_valid_lo_sum_p (enum mips_symbol_type symbol_type, enum machine_mode mode)
+{
+ /* Check that symbols of type SYMBOL_TYPE can be used to access values
+ of mode MODE. */
+ if (mips_symbol_insns (symbol_type, mode) == 0)
+ return false;
+
+ /* Check that there is a known low-part relocation. */
+ if (mips_lo_relocs[symbol_type] == NULL)
+ return false;
+
+ /* We may need to split multiword moves, so make sure that each word
+ can be accessed without inducing a carry. This is mainly needed
+ for o64, which has historically only guaranteed 64-bit alignment
+ for 128-bit types. */
+ if (GET_MODE_SIZE (mode) > UNITS_PER_WORD
+ && GET_MODE_BITSIZE (mode) > GET_MODE_ALIGNMENT (mode))
+ return false;
+
+ return true;
+}
+
+/* Return true if X is a valid address for machine mode MODE. If it is,
+ fill in INFO appropriately. STRICT_P is true if REG_OK_STRICT is in
+ effect. */
+
+static bool
+mips_classify_address (struct mips_address_info *info, rtx x,
+ enum machine_mode mode, bool strict_p)
+{
+ switch (GET_CODE (x))
+ {
+ case REG:
+ case SUBREG:
+ info->type = ADDRESS_REG;
+ info->reg = x;
+ info->offset = const0_rtx;
+ return mips_valid_base_register_p (info->reg, mode, strict_p);
+
+ case PLUS:
+ info->type = ADDRESS_REG;
+ info->reg = XEXP (x, 0);
+ info->offset = XEXP (x, 1);
+ return (mips_valid_base_register_p (info->reg, mode, strict_p)
+ && mips_valid_offset_p (info->offset, mode));
+
+ case LO_SUM:
+ info->type = ADDRESS_LO_SUM;
+ info->reg = XEXP (x, 0);
+ info->offset = XEXP (x, 1);
+ /* We have to trust the creator of the LO_SUM to do something vaguely
+ sane. Target-independent code that creates a LO_SUM should also
+ create and verify the matching HIGH. Target-independent code that
+ adds an offset to a LO_SUM must prove that the offset will not
+ induce a carry. Failure to do either of these things would be
+ a bug, and we are not required to check for it here. The MIPS
+ backend itself should only create LO_SUMs for valid symbolic
+ constants, with the high part being either a HIGH or a copy
+ of _gp. */
+ info->symbol_type
+ = mips_classify_symbolic_expression (info->offset, SYMBOL_CONTEXT_MEM);
+ return (mips_valid_base_register_p (info->reg, mode, strict_p)
+ && mips_valid_lo_sum_p (info->symbol_type, mode));
+
+ case CONST_INT:
+ /* Small-integer addresses don't occur very often, but they
+ are legitimate if $0 is a valid base register. */
+ info->type = ADDRESS_CONST_INT;
+ return !TARGET_MIPS16 && SMALL_INT (x);
+
+ case CONST:
+ case LABEL_REF:
+ case SYMBOL_REF:
+ info->type = ADDRESS_SYMBOLIC;
+ return (mips_symbolic_constant_p (x, SYMBOL_CONTEXT_MEM,
+ &info->symbol_type)
+ && mips_symbol_insns (info->symbol_type, mode) > 0
+ && !mips_split_p[info->symbol_type]);
+
+ default:
+ return false;
+ }
+}
+
+/* Implement TARGET_LEGITIMATE_ADDRESS_P. */
+
+static bool
+mips_legitimate_address_p (enum machine_mode mode, rtx x, bool strict_p)
+{
+ struct mips_address_info addr;
+
+ return mips_classify_address (&addr, x, mode, strict_p);
+}
+
+/* Return true if X is a legitimate $sp-based address for mode MDOE. */
+
+bool
+mips_stack_address_p (rtx x, enum machine_mode mode)
+{
+ struct mips_address_info addr;
+
+ return (mips_classify_address (&addr, x, mode, false)
+ && addr.type == ADDRESS_REG
+ && addr.reg == stack_pointer_rtx);
+}
+
+/* Return true if ADDR matches the pattern for the LWXS load scaled indexed
+ address instruction. Note that such addresses are not considered
+ legitimate in the TARGET_LEGITIMATE_ADDRESS_P sense, because their use
+ is so restricted. */
+
+static bool
+mips_lwxs_address_p (rtx addr)
+{
+ if (ISA_HAS_LWXS
+ && GET_CODE (addr) == PLUS
+ && REG_P (XEXP (addr, 1)))
+ {
+ rtx offset = XEXP (addr, 0);
+ if (GET_CODE (offset) == MULT
+ && REG_P (XEXP (offset, 0))
+ && CONST_INT_P (XEXP (offset, 1))
+ && INTVAL (XEXP (offset, 1)) == 4)
+ return true;
+ }
+ return false;
+}
+
+/* Return true if a value at OFFSET bytes from base register BASE can be
+ accessed using an unextended MIPS16 instruction. MODE is the mode of
+ the value.
+
+ Usually the offset in an unextended instruction is a 5-bit field.
+ The offset is unsigned and shifted left once for LH and SH, twice
+ for LW and SW, and so on. An exception is LWSP and SWSP, which have
+ an 8-bit immediate field that's shifted left twice. */
+
+static bool
+mips16_unextended_reference_p (enum machine_mode mode, rtx base,
+ unsigned HOST_WIDE_INT offset)
+{
+ if (offset % GET_MODE_SIZE (mode) == 0)
+ {
+ if (GET_MODE_SIZE (mode) == 4 && base == stack_pointer_rtx)
+ return offset < 256U * GET_MODE_SIZE (mode);
+ return offset < 32U * GET_MODE_SIZE (mode);
+ }
+ return false;
+}
+
+/* Return the number of instructions needed to load or store a value
+ of mode MODE at address X. Return 0 if X isn't valid for MODE.
+ Assume that multiword moves may need to be split into word moves
+ if MIGHT_SPLIT_P, otherwise assume that a single load or store is
+ enough.
+
+ For MIPS16 code, count extended instructions as two instructions. */
+
+int
+mips_address_insns (rtx x, enum machine_mode mode, bool might_split_p)
+{
+ struct mips_address_info addr;
+ int factor;
+
+ /* BLKmode is used for single unaligned loads and stores and should
+ not count as a multiword mode. (GET_MODE_SIZE (BLKmode) is pretty
+ meaningless, so we have to single it out as a special case one way
+ or the other.) */
+ if (mode != BLKmode && might_split_p)
+ factor = (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+ else
+ factor = 1;
+
+ if (mips_classify_address (&addr, x, mode, false))
+ switch (addr.type)
+ {
+ case ADDRESS_REG:
+ if (TARGET_MIPS16
+ && !mips16_unextended_reference_p (mode, addr.reg,
+ UINTVAL (addr.offset)))
+ return factor * 2;
+ return factor;
+
+ case ADDRESS_LO_SUM:
+ return TARGET_MIPS16 ? factor * 2 : factor;
+
+ case ADDRESS_CONST_INT:
+ return factor;
+
+ case ADDRESS_SYMBOLIC:
+ return factor * mips_symbol_insns (addr.symbol_type, mode);
+ }
+ return 0;
+}
+
+/* Return the number of instructions needed to load constant X.
+ Return 0 if X isn't a valid constant. */
+
+int
+mips_const_insns (rtx x)
+{
+ struct mips_integer_op codes[MIPS_MAX_INTEGER_OPS];
+ enum mips_symbol_type symbol_type;
+ rtx offset;
+
+ switch (GET_CODE (x))
+ {
+ case HIGH:
+ if (!mips_symbolic_constant_p (XEXP (x, 0), SYMBOL_CONTEXT_LEA,
+ &symbol_type)
+ || !mips_split_p[symbol_type])
+ return 0;
+
+ /* This is simply an LUI for normal mode. It is an extended
+ LI followed by an extended SLL for MIPS16. */
+ return TARGET_MIPS16 ? 4 : 1;
+
+ case CONST_INT:
+ if (TARGET_MIPS16)
+ /* Unsigned 8-bit constants can be loaded using an unextended
+ LI instruction. Unsigned 16-bit constants can be loaded
+ using an extended LI. Negative constants must be loaded
+ using LI and then negated. */
+ return (IN_RANGE (INTVAL (x), 0, 255) ? 1
+ : SMALL_OPERAND_UNSIGNED (INTVAL (x)) ? 2
+ : IN_RANGE (-INTVAL (x), 0, 255) ? 2
+ : SMALL_OPERAND_UNSIGNED (-INTVAL (x)) ? 3
+ : 0);
+
+ return mips_build_integer (codes, INTVAL (x));
+
+ case CONST_DOUBLE:
+ case CONST_VECTOR:
+ /* Allow zeros for normal mode, where we can use $0. */
+ return !TARGET_MIPS16 && x == CONST0_RTX (GET_MODE (x)) ? 1 : 0;
+
+ case CONST:
+ if (CONST_GP_P (x))
+ return 1;
+
+ /* See if we can refer to X directly. */
+ if (mips_symbolic_constant_p (x, SYMBOL_CONTEXT_LEA, &symbol_type))
+ return mips_symbol_insns (symbol_type, MAX_MACHINE_MODE);
+
+ /* Otherwise try splitting the constant into a base and offset.
+ If the offset is a 16-bit value, we can load the base address
+ into a register and then use (D)ADDIU to add in the offset.
+ If the offset is larger, we can load the base and offset
+ into separate registers and add them together with (D)ADDU.
+ However, the latter is only possible before reload; during
+ and after reload, we must have the option of forcing the
+ constant into the pool instead. */
+ split_const (x, &x, &offset);
+ if (offset != 0)
+ {
+ int n = mips_const_insns (x);
+ if (n != 0)
+ {
+ if (SMALL_INT (offset))
+ return n + 1;
+ else if (!targetm.cannot_force_const_mem (x))
+ return n + 1 + mips_build_integer (codes, INTVAL (offset));
+ }
+ }
+ return 0;
+
+ case SYMBOL_REF:
+ case LABEL_REF:
+ return mips_symbol_insns (mips_classify_symbol (x, SYMBOL_CONTEXT_LEA),
+ MAX_MACHINE_MODE);
+
+ default:
+ return 0;
+ }
+}
+
+/* X is a doubleword constant that can be handled by splitting it into
+ two words and loading each word separately. Return the number of
+ instructions required to do this. */
+
+int
+mips_split_const_insns (rtx x)
+{
+ unsigned int low, high;
+
+ low = mips_const_insns (mips_subword (x, false));
+ high = mips_const_insns (mips_subword (x, true));
+ gcc_assert (low > 0 && high > 0);
+ return low + high;
+}
+
+/* Return the number of instructions needed to implement INSN,
+ given that it loads from or stores to MEM. Count extended
+ MIPS16 instructions as two instructions. */
+
+int
+mips_load_store_insns (rtx mem, rtx insn)
+{
+ enum machine_mode mode;
+ bool might_split_p;
+ rtx set;
+
+ gcc_assert (MEM_P (mem));
+ mode = GET_MODE (mem);
+
+ /* Try to prove that INSN does not need to be split. */
+ might_split_p = true;
+ if (GET_MODE_BITSIZE (mode) == 64)
+ {
+ set = single_set (insn);
+ if (set && !mips_split_64bit_move_p (SET_DEST (set), SET_SRC (set)))
+ might_split_p = false;
+ }
+
+ return mips_address_insns (XEXP (mem, 0), mode, might_split_p);
+}
+
+/* Return the number of instructions needed for an integer division. */
+
+int
+mips_idiv_insns (void)
+{
+ int count;
+
+ count = 1;
+ if (TARGET_CHECK_ZERO_DIV)
+ {
+ if (GENERATE_DIVIDE_TRAPS)
+ count++;
+ else
+ count += 2;
+ }
+
+ if (TARGET_FIX_R4000 || TARGET_FIX_R4400)
+ count++;
+ return count;
+}
+
+/* Emit a move from SRC to DEST. Assume that the move expanders can
+ handle all moves if !can_create_pseudo_p (). The distinction is
+ important because, unlike emit_move_insn, the move expanders know
+ how to force Pmode objects into the constant pool even when the
+ constant pool address is not itself legitimate. */
+
+rtx
+mips_emit_move (rtx dest, rtx src)
+{
+ return (can_create_pseudo_p ()
+ ? emit_move_insn (dest, src)
+ : emit_move_insn_1 (dest, src));
+}
+
+/* Emit an instruction of the form (set TARGET (CODE OP0)). */
+
+static void
+mips_emit_unary (enum rtx_code code, rtx target, rtx op0)
+{
+ emit_insn (gen_rtx_SET (VOIDmode, target,
+ gen_rtx_fmt_e (code, GET_MODE (op0), op0)));
+}
+
+/* Compute (CODE OP0) and store the result in a new register of mode MODE.
+ Return that new register. */
+
+static rtx
+mips_force_unary (enum machine_mode mode, enum rtx_code code, rtx op0)
+{
+ rtx reg;
+
+ reg = gen_reg_rtx (mode);
+ mips_emit_unary (code, reg, op0);
+ return reg;
+}
+
+/* Emit an instruction of the form (set TARGET (CODE OP0 OP1)). */
+
+static void
+mips_emit_binary (enum rtx_code code, rtx target, rtx op0, rtx op1)
+{
+ emit_insn (gen_rtx_SET (VOIDmode, target,
+ gen_rtx_fmt_ee (code, GET_MODE (target), op0, op1)));
+}
+
+/* Compute (CODE OP0 OP1) and store the result in a new register
+ of mode MODE. Return that new register. */
+
+static rtx
+mips_force_binary (enum machine_mode mode, enum rtx_code code, rtx op0, rtx op1)
+{
+ rtx reg;
+
+ reg = gen_reg_rtx (mode);
+ mips_emit_binary (code, reg, op0, op1);
+ return reg;
+}
+
+/* Copy VALUE to a register and return that register. If new pseudos
+ are allowed, copy it into a new register, otherwise use DEST. */
+
+static rtx
+mips_force_temporary (rtx dest, rtx value)
+{
+ if (can_create_pseudo_p ())
+ return force_reg (Pmode, value);
+ else
+ {
+ mips_emit_move (dest, value);
+ return dest;
+ }
+}
+
+/* Emit a call sequence with call pattern PATTERN and return the call
+ instruction itself (which is not necessarily the last instruction
+ emitted). ORIG_ADDR is the original, unlegitimized address,
+ ADDR is the legitimized form, and LAZY_P is true if the call
+ address is lazily-bound. */
+
+static rtx
+mips_emit_call_insn (rtx pattern, rtx orig_addr, rtx addr, bool lazy_p)
+{
+ rtx insn, reg;
+
+ insn = emit_call_insn (pattern);
+
+ if (TARGET_MIPS16 && mips_use_pic_fn_addr_reg_p (orig_addr))
+ {
+ /* MIPS16 JALRs only take MIPS16 registers. If the target
+ function requires $25 to be valid on entry, we must copy it
+ there separately. The move instruction can be put in the
+ call's delay slot. */
+ reg = gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM);
+ emit_insn_before (gen_move_insn (reg, addr), insn);
+ use_reg (&CALL_INSN_FUNCTION_USAGE (insn), reg);
+ }
+
+ if (lazy_p)
+ /* Lazy-binding stubs require $gp to be valid on entry. */
+ use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx);
+
+ if (TARGET_USE_GOT)
+ {
+ /* See the comment above load_call<mode> for details. */
+ use_reg (&CALL_INSN_FUNCTION_USAGE (insn),
+ gen_rtx_REG (Pmode, GOT_VERSION_REGNUM));
+ emit_insn (gen_update_got_version ());
+ }
+ return insn;
+}
+
+/* Wrap symbol or label BASE in an UNSPEC address of type SYMBOL_TYPE,
+ then add CONST_INT OFFSET to the result. */
+
+static rtx
+mips_unspec_address_offset (rtx base, rtx offset,
+ enum mips_symbol_type symbol_type)
+{
+ base = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, base),
+ UNSPEC_ADDRESS_FIRST + symbol_type);
+ if (offset != const0_rtx)
+ base = gen_rtx_PLUS (Pmode, base, offset);
+ return gen_rtx_CONST (Pmode, base);
+}
+
+/* Return an UNSPEC address with underlying address ADDRESS and symbol
+ type SYMBOL_TYPE. */
+
+rtx
+mips_unspec_address (rtx address, enum mips_symbol_type symbol_type)
+{
+ rtx base, offset;
+
+ split_const (address, &base, &offset);
+ return mips_unspec_address_offset (base, offset, symbol_type);
+}
+
+/* If OP is an UNSPEC address, return the address to which it refers,
+ otherwise return OP itself. */
+
+static rtx
+mips_strip_unspec_address (rtx op)
+{
+ rtx base, offset;
+
+ split_const (op, &base, &offset);
+ if (UNSPEC_ADDRESS_P (base))
+ op = plus_constant (UNSPEC_ADDRESS (base), INTVAL (offset));
+ return op;
+}
+
+/* If mips_unspec_address (ADDR, SYMBOL_TYPE) is a 32-bit value, add the
+ high part to BASE and return the result. Just return BASE otherwise.
+ TEMP is as for mips_force_temporary.
+
+ The returned expression can be used as the first operand to a LO_SUM. */
+
+static rtx
+mips_unspec_offset_high (rtx temp, rtx base, rtx addr,
+ enum mips_symbol_type symbol_type)
+{
+ if (mips_split_p[symbol_type])
+ {
+ addr = gen_rtx_HIGH (Pmode, mips_unspec_address (addr, symbol_type));
+ addr = mips_force_temporary (temp, addr);
+ base = mips_force_temporary (temp, gen_rtx_PLUS (Pmode, addr, base));
+ }
+ return base;
+}
+
+/* Return an instruction that copies $gp into register REG. We want
+ GCC to treat the register's value as constant, so that its value
+ can be rematerialized on demand. */
+
+static rtx
+gen_load_const_gp (rtx reg)
+{
+ return (Pmode == SImode
+ ? gen_load_const_gp_si (reg)
+ : gen_load_const_gp_di (reg));
+}
+
+/* Return a pseudo register that contains the value of $gp throughout
+ the current function. Such registers are needed by MIPS16 functions,
+ for which $gp itself is not a valid base register or addition operand. */
+
+static rtx
+mips16_gp_pseudo_reg (void)
+{
+ if (cfun->machine->mips16_gp_pseudo_rtx == NULL_RTX)
+ {
+ rtx insn, scan;
+
+ cfun->machine->mips16_gp_pseudo_rtx = gen_reg_rtx (Pmode);
+
+ push_topmost_sequence ();
+
+ scan = get_insns ();
+ while (NEXT_INSN (scan) && !INSN_P (NEXT_INSN (scan)))
+ scan = NEXT_INSN (scan);
+
+ insn = gen_load_const_gp (cfun->machine->mips16_gp_pseudo_rtx);
+ emit_insn_after (insn, scan);
+
+ pop_topmost_sequence ();
+ }
+
+ return cfun->machine->mips16_gp_pseudo_rtx;
+}
+
+/* Return a base register that holds pic_offset_table_rtx.
+ TEMP, if nonnull, is a scratch Pmode base register. */
+
+rtx
+mips_pic_base_register (rtx temp)
+{
+ if (!TARGET_MIPS16)
+ return pic_offset_table_rtx;
+
+ if (currently_expanding_to_rtl)
+ return mips16_gp_pseudo_reg ();
+
+ if (can_create_pseudo_p ())
+ temp = gen_reg_rtx (Pmode);
+
+ if (TARGET_USE_GOT)
+ /* The first post-reload split exposes all references to $gp
+ (both uses and definitions). All references must remain
+ explicit after that point.
+
+ It is safe to introduce uses of $gp at any time, so for
+ simplicity, we do that before the split too. */
+ mips_emit_move (temp, pic_offset_table_rtx);
+ else
+ emit_insn (gen_load_const_gp (temp));
+ return temp;
+}
+
+/* Return the RHS of a load_call<mode> insn. */
+
+static rtx
+mips_unspec_call (rtx reg, rtx symbol)
+{
+ rtvec vec;
+
+ vec = gen_rtvec (3, reg, symbol, gen_rtx_REG (SImode, GOT_VERSION_REGNUM));
+ return gen_rtx_UNSPEC (Pmode, vec, UNSPEC_LOAD_CALL);
+}
+
+/* If SRC is the RHS of a load_call<mode> insn, return the underlying symbol
+ reference. Return NULL_RTX otherwise. */
+
+static rtx
+mips_strip_unspec_call (rtx src)
+{
+ if (GET_CODE (src) == UNSPEC && XINT (src, 1) == UNSPEC_LOAD_CALL)
+ return mips_strip_unspec_address (XVECEXP (src, 0, 1));
+ return NULL_RTX;
+}
+
+/* Create and return a GOT reference of type TYPE for address ADDR.
+ TEMP, if nonnull, is a scratch Pmode base register. */
+
+rtx
+mips_got_load (rtx temp, rtx addr, enum mips_symbol_type type)
+{
+ rtx base, high, lo_sum_symbol;
+
+ base = mips_pic_base_register (temp);
+
+ /* If we used the temporary register to load $gp, we can't use
+ it for the high part as well. */
+ if (temp != NULL && reg_overlap_mentioned_p (base, temp))
+ temp = NULL;
+
+ high = mips_unspec_offset_high (temp, base, addr, type);
+ lo_sum_symbol = mips_unspec_address (addr, type);
+
+ if (type == SYMBOL_GOTOFF_CALL)
+ return mips_unspec_call (high, lo_sum_symbol);
+ else
+ return (Pmode == SImode
+ ? gen_unspec_gotsi (high, lo_sum_symbol)
+ : gen_unspec_gotdi (high, lo_sum_symbol));
+}
+
+/* If MODE is MAX_MACHINE_MODE, ADDR appears as a move operand, otherwise
+ it appears in a MEM of that mode. Return true if ADDR is a legitimate
+ constant in that context and can be split into high and low parts.
+ If so, and if LOW_OUT is nonnull, emit the high part and store the
+ low part in *LOW_OUT. Leave *LOW_OUT unchanged otherwise.
+
+ TEMP is as for mips_force_temporary and is used to load the high
+ part into a register.
+
+ When MODE is MAX_MACHINE_MODE, the low part is guaranteed to be
+ a legitimize SET_SRC for an .md pattern, otherwise the low part
+ is guaranteed to be a legitimate address for mode MODE. */
+
+bool
+mips_split_symbol (rtx temp, rtx addr, enum machine_mode mode, rtx *low_out)
+{
+ enum mips_symbol_context context;
+ enum mips_symbol_type symbol_type;
+ rtx high;
+
+ context = (mode == MAX_MACHINE_MODE
+ ? SYMBOL_CONTEXT_LEA
+ : SYMBOL_CONTEXT_MEM);
+ if (GET_CODE (addr) == HIGH && context == SYMBOL_CONTEXT_LEA)
+ {
+ addr = XEXP (addr, 0);
+ if (mips_symbolic_constant_p (addr, context, &symbol_type)
+ && mips_symbol_insns (symbol_type, mode) > 0
+ && mips_split_hi_p[symbol_type])
+ {
+ if (low_out)
+ switch (symbol_type)
+ {
+ case SYMBOL_GOT_PAGE_OFST:
+ /* The high part of a page/ofst pair is loaded from the GOT. */
+ *low_out = mips_got_load (temp, addr, SYMBOL_GOTOFF_PAGE);
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+ return true;
+ }
+ }
+ else
+ {
+ if (mips_symbolic_constant_p (addr, context, &symbol_type)
+ && mips_symbol_insns (symbol_type, mode) > 0
+ && mips_split_p[symbol_type])
+ {
+ if (low_out)
+ switch (symbol_type)
+ {
+ case SYMBOL_GOT_DISP:
+ /* SYMBOL_GOT_DISP symbols are loaded from the GOT. */
+ *low_out = mips_got_load (temp, addr, SYMBOL_GOTOFF_DISP);
+ break;
+
+ case SYMBOL_GP_RELATIVE:
+ high = mips_pic_base_register (temp);
+ *low_out = gen_rtx_LO_SUM (Pmode, high, addr);
+ break;
+
+ default:
+ high = gen_rtx_HIGH (Pmode, copy_rtx (addr));
+ high = mips_force_temporary (temp, high);
+ *low_out = gen_rtx_LO_SUM (Pmode, high, addr);
+ break;
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+/* Return a legitimate address for REG + OFFSET. TEMP is as for
+ mips_force_temporary; it is only needed when OFFSET is not a
+ SMALL_OPERAND. */
+
+static rtx
+mips_add_offset (rtx temp, rtx reg, HOST_WIDE_INT offset)
+{
+ if (!SMALL_OPERAND (offset))
+ {
+ rtx high;
+
+ if (TARGET_MIPS16)
+ {
+ /* Load the full offset into a register so that we can use
+ an unextended instruction for the address itself. */
+ high = GEN_INT (offset);
+ offset = 0;
+ }
+ else
+ {
+ /* Leave OFFSET as a 16-bit offset and put the excess in HIGH.
+ The addition inside the macro CONST_HIGH_PART may cause an
+ overflow, so we need to force a sign-extension check. */
+ high = gen_int_mode (CONST_HIGH_PART (offset), Pmode);
+ offset = CONST_LOW_PART (offset);
+ }
+ high = mips_force_temporary (temp, high);
+ reg = mips_force_temporary (temp, gen_rtx_PLUS (Pmode, high, reg));
+ }
+ return plus_constant (reg, offset);
+}
+
+/* The __tls_get_attr symbol. */
+static GTY(()) rtx mips_tls_symbol;
+
+/* Return an instruction sequence that calls __tls_get_addr. SYM is
+ the TLS symbol we are referencing and TYPE is the symbol type to use
+ (either global dynamic or local dynamic). V0 is an RTX for the
+ return value location. */
+
+static rtx
+mips_call_tls_get_addr (rtx sym, enum mips_symbol_type type, rtx v0)
+{
+ rtx insn, loc, a0;
+
+ a0 = gen_rtx_REG (Pmode, GP_ARG_FIRST);
+
+ if (!mips_tls_symbol)
+ mips_tls_symbol = init_one_libfunc ("__tls_get_addr");
+
+ loc = mips_unspec_address (sym, type);
+
+ start_sequence ();
+
+ emit_insn (gen_rtx_SET (Pmode, a0,
+ gen_rtx_LO_SUM (Pmode, pic_offset_table_rtx, loc)));
+ insn = mips_expand_call (MIPS_CALL_NORMAL, v0, mips_tls_symbol,
+ const0_rtx, NULL_RTX, false);
+ RTL_CONST_CALL_P (insn) = 1;
+ use_reg (&CALL_INSN_FUNCTION_USAGE (insn), a0);
+ insn = get_insns ();
+
+ end_sequence ();
+
+ return insn;
+}
+
+/* Return a pseudo register that contains the current thread pointer. */
+
+static rtx
+mips_get_tp (void)
+{
+ rtx tp;
+
+ tp = gen_reg_rtx (Pmode);
+ if (Pmode == DImode)
+ emit_insn (gen_tls_get_tp_di (tp));
+ else
+ emit_insn (gen_tls_get_tp_si (tp));
+ return tp;
+}
+
+/* Generate the code to access LOC, a thread-local SYMBOL_REF, and return
+ its address. The return value will be both a valid address and a valid
+ SET_SRC (either a REG or a LO_SUM). */
+
+static rtx
+mips_legitimize_tls_address (rtx loc)
+{
+ rtx dest, insn, v0, tp, tmp1, tmp2, eqv;
+ enum tls_model model;
+
+ if (TARGET_MIPS16)
+ {
+ sorry ("MIPS16 TLS");
+ return gen_reg_rtx (Pmode);
+ }
+
+ model = SYMBOL_REF_TLS_MODEL (loc);
+ /* Only TARGET_ABICALLS code can have more than one module; other
+ code must be be static and should not use a GOT. All TLS models
+ reduce to local exec in this situation. */
+ if (!TARGET_ABICALLS)
+ model = TLS_MODEL_LOCAL_EXEC;
+
+ switch (model)
+ {
+ case TLS_MODEL_GLOBAL_DYNAMIC:
+ v0 = gen_rtx_REG (Pmode, GP_RETURN);
+ insn = mips_call_tls_get_addr (loc, SYMBOL_TLSGD, v0);
+ dest = gen_reg_rtx (Pmode);
+ emit_libcall_block (insn, dest, v0, loc);
+ break;
+
+ case TLS_MODEL_LOCAL_DYNAMIC:
+ v0 = gen_rtx_REG (Pmode, GP_RETURN);
+ insn = mips_call_tls_get_addr (loc, SYMBOL_TLSLDM, v0);
+ tmp1 = gen_reg_rtx (Pmode);
+
+ /* Attach a unique REG_EQUIV, to allow the RTL optimizers to
+ share the LDM result with other LD model accesses. */
+ eqv = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const0_rtx),
+ UNSPEC_TLS_LDM);
+ emit_libcall_block (insn, tmp1, v0, eqv);
+
+ tmp2 = mips_unspec_offset_high (NULL, tmp1, loc, SYMBOL_DTPREL);
+ dest = gen_rtx_LO_SUM (Pmode, tmp2,
+ mips_unspec_address (loc, SYMBOL_DTPREL));
+ break;
+
+ case TLS_MODEL_INITIAL_EXEC:
+ tp = mips_get_tp ();
+ tmp1 = gen_reg_rtx (Pmode);
+ tmp2 = mips_unspec_address (loc, SYMBOL_GOTTPREL);
+ if (Pmode == DImode)
+ emit_insn (gen_load_gotdi (tmp1, pic_offset_table_rtx, tmp2));
+ else
+ emit_insn (gen_load_gotsi (tmp1, pic_offset_table_rtx, tmp2));
+ dest = gen_reg_rtx (Pmode);
+ emit_insn (gen_add3_insn (dest, tmp1, tp));
+ break;
+
+ case TLS_MODEL_LOCAL_EXEC:
+ tp = mips_get_tp ();
+ tmp1 = mips_unspec_offset_high (NULL, tp, loc, SYMBOL_TPREL);
+ dest = gen_rtx_LO_SUM (Pmode, tmp1,
+ mips_unspec_address (loc, SYMBOL_TPREL));
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+ return dest;
+}
+
+/* If X is not a valid address for mode MODE, force it into a register. */
+
+static rtx
+mips_force_address (rtx x, enum machine_mode mode)
+{
+ if (!mips_legitimate_address_p (mode, x, false))
+ x = force_reg (Pmode, x);
+ return x;
+}
+
+/* This function is used to implement LEGITIMIZE_ADDRESS. If X can
+ be legitimized in a way that the generic machinery might not expect,
+ return a new address, otherwise return NULL. MODE is the mode of
+ the memory being accessed. */
+
+static rtx
+mips_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
+ enum machine_mode mode)
+{
+ rtx base, addr;
+ HOST_WIDE_INT offset;
+
+ if (mips_tls_symbol_p (x))
+ return mips_legitimize_tls_address (x);
+
+ /* See if the address can split into a high part and a LO_SUM. */
+ if (mips_split_symbol (NULL, x, mode, &addr))
+ return mips_force_address (addr, mode);
+
+ /* Handle BASE + OFFSET using mips_add_offset. */
+ mips_split_plus (x, &base, &offset);
+ if (offset != 0)
+ {
+ if (!mips_valid_base_register_p (base, mode, false))
+ base = copy_to_mode_reg (Pmode, base);
+ addr = mips_add_offset (NULL, base, offset);
+ return mips_force_address (addr, mode);
+ }
+
+ return x;
+}
+
+/* Load VALUE into DEST. TEMP is as for mips_force_temporary. */
+
+void
+mips_move_integer (rtx temp, rtx dest, unsigned HOST_WIDE_INT value)
+{
+ struct mips_integer_op codes[MIPS_MAX_INTEGER_OPS];
+ enum machine_mode mode;
+ unsigned int i, num_ops;
+ rtx x;
+
+ mode = GET_MODE (dest);
+ num_ops = mips_build_integer (codes, value);
+
+ /* Apply each binary operation to X. Invariant: X is a legitimate
+ source operand for a SET pattern. */
+ x = GEN_INT (codes[0].value);
+ for (i = 1; i < num_ops; i++)
+ {
+ if (!can_create_pseudo_p ())
+ {
+ emit_insn (gen_rtx_SET (VOIDmode, temp, x));
+ x = temp;
+ }
+ else
+ x = force_reg (mode, x);
+ x = gen_rtx_fmt_ee (codes[i].code, mode, x, GEN_INT (codes[i].value));
+ }
+
+ emit_insn (gen_rtx_SET (VOIDmode, dest, x));
+}
+
+/* Subroutine of mips_legitimize_move. Move constant SRC into register
+ DEST given that SRC satisfies immediate_operand but doesn't satisfy
+ move_operand. */
+
+static void
+mips_legitimize_const_move (enum machine_mode mode, rtx dest, rtx src)
+{
+ rtx base, offset;
+
+ /* Split moves of big integers into smaller pieces. */
+ if (splittable_const_int_operand (src, mode))
+ {
+ mips_move_integer (dest, dest, INTVAL (src));
+ return;
+ }
+
+ /* Split moves of symbolic constants into high/low pairs. */
+ if (mips_split_symbol (dest, src, MAX_MACHINE_MODE, &src))
+ {
+ emit_insn (gen_rtx_SET (VOIDmode, dest, src));
+ return;
+ }
+
+ /* Generate the appropriate access sequences for TLS symbols. */
+ if (mips_tls_symbol_p (src))
+ {
+ mips_emit_move (dest, mips_legitimize_tls_address (src));
+ return;
+ }
+
+ /* If we have (const (plus symbol offset)), and that expression cannot
+ be forced into memory, load the symbol first and add in the offset.
+ In non-MIPS16 mode, prefer to do this even if the constant _can_ be
+ forced into memory, as it usually produces better code. */
+ split_const (src, &base, &offset);
+ if (offset != const0_rtx
+ && (targetm.cannot_force_const_mem (src)
+ || (!TARGET_MIPS16 && can_create_pseudo_p ())))
+ {
+ base = mips_force_temporary (dest, base);
+ mips_emit_move (dest, mips_add_offset (NULL, base, INTVAL (offset)));
+ return;
+ }
+
+ src = force_const_mem (mode, src);
+
+ /* When using explicit relocs, constant pool references are sometimes
+ not legitimate addresses. */
+ mips_split_symbol (dest, XEXP (src, 0), mode, &XEXP (src, 0));
+ mips_emit_move (dest, src);
+}
+
+/* If (set DEST SRC) is not a valid move instruction, emit an equivalent
+ sequence that is valid. */
+
+bool
+mips_legitimize_move (enum machine_mode mode, rtx dest, rtx src)
+{
+ if (!register_operand (dest, mode) && !reg_or_0_operand (src, mode))
+ {
+ mips_emit_move (dest, force_reg (mode, src));
+ return true;
+ }
+
+ /* We need to deal with constants that would be legitimate
+ immediate_operands but aren't legitimate move_operands. */
+ if (CONSTANT_P (src) && !move_operand (src, mode))
+ {
+ mips_legitimize_const_move (mode, dest, src);
+ set_unique_reg_note (get_last_insn (), REG_EQUAL, copy_rtx (src));
+ return true;
+ }
+ return false;
+}
+
+/* Return true if value X in context CONTEXT is a small-data address
+ that can be rewritten as a LO_SUM. */
+
+static bool
+mips_rewrite_small_data_p (rtx x, enum mips_symbol_context context)
+{
+ enum mips_symbol_type symbol_type;
+
+ return (mips_lo_relocs[SYMBOL_GP_RELATIVE]
+ && !mips_split_p[SYMBOL_GP_RELATIVE]
+ && mips_symbolic_constant_p (x, context, &symbol_type)
+ && symbol_type == SYMBOL_GP_RELATIVE);
+}
+
+/* A for_each_rtx callback for mips_small_data_pattern_p. DATA is the
+ containing MEM, or null if none. */
+
+static int
+mips_small_data_pattern_1 (rtx *loc, void *data)
+{
+ enum mips_symbol_context context;
+
+ if (GET_CODE (*loc) == LO_SUM)
+ return -1;
+
+ if (MEM_P (*loc))
+ {
+ if (for_each_rtx (&XEXP (*loc, 0), mips_small_data_pattern_1, *loc))
+ return 1;
+ return -1;
+ }
+
+ context = data ? SYMBOL_CONTEXT_MEM : SYMBOL_CONTEXT_LEA;
+ return mips_rewrite_small_data_p (*loc, context);
+}
+
+/* Return true if OP refers to small data symbols directly, not through
+ a LO_SUM. */
+
+bool
+mips_small_data_pattern_p (rtx op)
+{
+ return for_each_rtx (&op, mips_small_data_pattern_1, NULL);
+}
+
+/* A for_each_rtx callback, used by mips_rewrite_small_data.
+ DATA is the containing MEM, or null if none. */
+
+static int
+mips_rewrite_small_data_1 (rtx *loc, void *data)
+{
+ enum mips_symbol_context context;
+
+ if (MEM_P (*loc))
+ {
+ for_each_rtx (&XEXP (*loc, 0), mips_rewrite_small_data_1, *loc);
+ return -1;
+ }
+
+ context = data ? SYMBOL_CONTEXT_MEM : SYMBOL_CONTEXT_LEA;
+ if (mips_rewrite_small_data_p (*loc, context))
+ *loc = gen_rtx_LO_SUM (Pmode, pic_offset_table_rtx, *loc);
+
+ if (GET_CODE (*loc) == LO_SUM)
+ return -1;
+
+ return 0;
+}
+
+/* Rewrite instruction pattern PATTERN so that it refers to small data
+ using explicit relocations. */
+
+rtx
+mips_rewrite_small_data (rtx pattern)
+{
+ pattern = copy_insn (pattern);
+ for_each_rtx (&pattern, mips_rewrite_small_data_1, NULL);
+ return pattern;
+}
+
+/* We need a lot of little routines to check the range of MIPS16 immediate
+ operands. */
+
+static int
+m16_check_op (rtx op, int low, int high, int mask)
+{
+ return (CONST_INT_P (op)
+ && IN_RANGE (INTVAL (op), low, high)
+ && (INTVAL (op) & mask) == 0);
+}
+
+int
+m16_uimm3_b (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ return m16_check_op (op, 0x1, 0x8, 0);
+}
+
+int
+m16_simm4_1 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ return m16_check_op (op, -0x8, 0x7, 0);
+}
+
+int
+m16_nsimm4_1 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ return m16_check_op (op, -0x7, 0x8, 0);
+}
+
+int
+m16_simm5_1 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ return m16_check_op (op, -0x10, 0xf, 0);
+}
+
+int
+m16_nsimm5_1 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ return m16_check_op (op, -0xf, 0x10, 0);
+}
+
+int
+m16_uimm5_4 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ return m16_check_op (op, -0x10 << 2, 0xf << 2, 3);
+}
+
+int
+m16_nuimm5_4 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ return m16_check_op (op, -0xf << 2, 0x10 << 2, 3);
+}
+
+int
+m16_simm8_1 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ return m16_check_op (op, -0x80, 0x7f, 0);
+}
+
+int
+m16_nsimm8_1 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ return m16_check_op (op, -0x7f, 0x80, 0);
+}
+
+int
+m16_uimm8_1 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ return m16_check_op (op, 0x0, 0xff, 0);
+}
+
+int
+m16_nuimm8_1 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ return m16_check_op (op, -0xff, 0x0, 0);
+}
+
+int
+m16_uimm8_m1_1 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ return m16_check_op (op, -0x1, 0xfe, 0);
+}
+
+int
+m16_uimm8_4 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ return m16_check_op (op, 0x0, 0xff << 2, 3);
+}
+
+int
+m16_nuimm8_4 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ return m16_check_op (op, -0xff << 2, 0x0, 3);
+}
+
+int
+m16_simm8_8 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ return m16_check_op (op, -0x80 << 3, 0x7f << 3, 7);
+}
+
+int
+m16_nsimm8_8 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ return m16_check_op (op, -0x7f << 3, 0x80 << 3, 7);
+}
+
+/* The cost of loading values from the constant pool. It should be
+ larger than the cost of any constant we want to synthesize inline. */
+#define CONSTANT_POOL_COST COSTS_N_INSNS (TARGET_MIPS16 ? 4 : 8)
+
+/* Return the cost of X when used as an operand to the MIPS16 instruction
+ that implements CODE. Return -1 if there is no such instruction, or if
+ X is not a valid immediate operand for it. */
+
+static int
+mips16_constant_cost (int code, HOST_WIDE_INT x)
+{
+ switch (code)
+ {
+ case ASHIFT:
+ case ASHIFTRT:
+ case LSHIFTRT:
+ /* Shifts by between 1 and 8 bits (inclusive) are unextended,
+ other shifts are extended. The shift patterns truncate the shift
+ count to the right size, so there are no out-of-range values. */
+ if (IN_RANGE (x, 1, 8))
+ return 0;
+ return COSTS_N_INSNS (1);
+
+ case PLUS:
+ if (IN_RANGE (x, -128, 127))
+ return 0;
+ if (SMALL_OPERAND (x))
+ return COSTS_N_INSNS (1);
+ return -1;
+
+ case LEU:
+ /* Like LE, but reject the always-true case. */
+ if (x == -1)
+ return -1;
+ case LE:
+ /* We add 1 to the immediate and use SLT. */
+ x += 1;
+ case XOR:
+ /* We can use CMPI for an xor with an unsigned 16-bit X. */
+ case LT:
+ case LTU:
+ if (IN_RANGE (x, 0, 255))
+ return 0;
+ if (SMALL_OPERAND_UNSIGNED (x))
+ return COSTS_N_INSNS (1);
+ return -1;
+
+ case EQ:
+ case NE:
+ /* Equality comparisons with 0 are cheap. */
+ if (x == 0)
+ return 0;
+ return -1;
+
+ default:
+ return -1;
+ }
+}
+
+/* Return true if there is a non-MIPS16 instruction that implements CODE
+ and if that instruction accepts X as an immediate operand. */
+
+static int
+mips_immediate_operand_p (int code, HOST_WIDE_INT x)
+{
+ switch (code)
+ {
+ case ASHIFT:
+ case ASHIFTRT:
+ case LSHIFTRT:
+ /* All shift counts are truncated to a valid constant. */
+ return true;
+
+ case ROTATE:
+ case ROTATERT:
+ /* Likewise rotates, if the target supports rotates at all. */
+ return ISA_HAS_ROR;
+
+ case AND:
+ case IOR:
+ case XOR:
+ /* These instructions take 16-bit unsigned immediates. */
+ return SMALL_OPERAND_UNSIGNED (x);
+
+ case PLUS:
+ case LT:
+ case LTU:
+ /* These instructions take 16-bit signed immediates. */
+ return SMALL_OPERAND (x);
+
+ case EQ:
+ case NE:
+ case GT:
+ case GTU:
+ /* The "immediate" forms of these instructions are really
+ implemented as comparisons with register 0. */
+ return x == 0;
+
+ case GE:
+ case GEU:
+ /* Likewise, meaning that the only valid immediate operand is 1. */
+ return x == 1;
+
+ case LE:
+ /* We add 1 to the immediate and use SLT. */
+ return SMALL_OPERAND (x + 1);
+
+ case LEU:
+ /* Likewise SLTU, but reject the always-true case. */
+ return SMALL_OPERAND (x + 1) && x + 1 != 0;
+
+ case SIGN_EXTRACT:
+ case ZERO_EXTRACT:
+ /* The bit position and size are immediate operands. */
+ return ISA_HAS_EXT_INS;
+
+ default:
+ /* By default assume that $0 can be used for 0. */
+ return x == 0;
+ }
+}
+
+/* Return the cost of binary operation X, given that the instruction
+ sequence for a word-sized or smaller operation has cost SINGLE_COST
+ and that the sequence of a double-word operation has cost DOUBLE_COST.
+ If SPEED is true, optimize for speed otherwise optimize for size. */
+
+static int
+mips_binary_cost (rtx x, int single_cost, int double_cost, bool speed)
+{
+ int cost;
+
+ if (GET_MODE_SIZE (GET_MODE (x)) == UNITS_PER_WORD * 2)
+ cost = double_cost;
+ else
+ cost = single_cost;
+ return (cost
+ + rtx_cost (XEXP (x, 0), SET, speed)
+ + rtx_cost (XEXP (x, 1), GET_CODE (x), speed));
+}
+
+/* Return the cost of floating-point multiplications of mode MODE. */
+
+static int
+mips_fp_mult_cost (enum machine_mode mode)
+{
+ return mode == DFmode ? mips_cost->fp_mult_df : mips_cost->fp_mult_sf;
+}
+
+/* Return the cost of floating-point divisions of mode MODE. */
+
+static int
+mips_fp_div_cost (enum machine_mode mode)
+{
+ return mode == DFmode ? mips_cost->fp_div_df : mips_cost->fp_div_sf;
+}
+
+/* Return the cost of sign-extending OP to mode MODE, not including the
+ cost of OP itself. */
+
+static int
+mips_sign_extend_cost (enum machine_mode mode, rtx op)
+{
+ if (MEM_P (op))
+ /* Extended loads are as cheap as unextended ones. */
+ return 0;
+
+ if (TARGET_64BIT && mode == DImode && GET_MODE (op) == SImode)
+ /* A sign extension from SImode to DImode in 64-bit mode is free. */
+ return 0;
+
+ if (ISA_HAS_SEB_SEH || GENERATE_MIPS16E)
+ /* We can use SEB or SEH. */
+ return COSTS_N_INSNS (1);
+
+ /* We need to use a shift left and a shift right. */
+ return COSTS_N_INSNS (TARGET_MIPS16 ? 4 : 2);
+}
+
+/* Return the cost of zero-extending OP to mode MODE, not including the
+ cost of OP itself. */
+
+static int
+mips_zero_extend_cost (enum machine_mode mode, rtx op)
+{
+ if (MEM_P (op))
+ /* Extended loads are as cheap as unextended ones. */
+ return 0;
+
+ if (TARGET_64BIT && mode == DImode && GET_MODE (op) == SImode)
+ /* We need a shift left by 32 bits and a shift right by 32 bits. */
+ return COSTS_N_INSNS (TARGET_MIPS16 ? 4 : 2);
+
+ if (GENERATE_MIPS16E)
+ /* We can use ZEB or ZEH. */
+ return COSTS_N_INSNS (1);
+
+ if (TARGET_MIPS16)
+ /* We need to load 0xff or 0xffff into a register and use AND. */
+ return COSTS_N_INSNS (GET_MODE (op) == QImode ? 2 : 3);
+
+ /* We can use ANDI. */
+ return COSTS_N_INSNS (1);
+}
+
+/* Implement TARGET_RTX_COSTS. */
+
+static bool
+mips_rtx_costs (rtx x, int code, int outer_code, int *total, bool speed)
+{
+ enum machine_mode mode = GET_MODE (x);
+ bool float_mode_p = FLOAT_MODE_P (mode);
+ int cost;
+ rtx addr;
+
+ /* The cost of a COMPARE is hard to define for MIPS. COMPAREs don't
+ appear in the instruction stream, and the cost of a comparison is
+ really the cost of the branch or scc condition. At the time of
+ writing, GCC only uses an explicit outer COMPARE code when optabs
+ is testing whether a constant is expensive enough to force into a
+ register. We want optabs to pass such constants through the MIPS
+ expanders instead, so make all constants very cheap here. */
+ if (outer_code == COMPARE)
+ {
+ gcc_assert (CONSTANT_P (x));
+ *total = 0;
+ return true;
+ }
+
+ switch (code)
+ {
+ case CONST_INT:
+ /* Treat *clear_upper32-style ANDs as having zero cost in the
+ second operand. The cost is entirely in the first operand.
+
+ ??? This is needed because we would otherwise try to CSE
+ the constant operand. Although that's the right thing for
+ instructions that continue to be a register operation throughout
+ compilation, it is disastrous for instructions that could
+ later be converted into a memory operation. */
+ if (TARGET_64BIT
+ && outer_code == AND
+ && UINTVAL (x) == 0xffffffff)
+ {
+ *total = 0;
+ return true;
+ }
+
+ if (TARGET_MIPS16)
+ {
+ cost = mips16_constant_cost (outer_code, INTVAL (x));
+ if (cost >= 0)
+ {
+ *total = cost;
+ return true;
+ }
+ }
+ else
+ {
+ /* When not optimizing for size, we care more about the cost
+ of hot code, and hot code is often in a loop. If a constant
+ operand needs to be forced into a register, we will often be
+ able to hoist the constant load out of the loop, so the load
+ should not contribute to the cost. */
+ if (speed || mips_immediate_operand_p (outer_code, INTVAL (x)))
+ {
+ *total = 0;
+ return true;
+ }
+ }
+ /* Fall through. */
+
+ case CONST:
+ case SYMBOL_REF:
+ case LABEL_REF:
+ case CONST_DOUBLE:
+ if (force_to_mem_operand (x, VOIDmode))
+ {
+ *total = COSTS_N_INSNS (1);
+ return true;
+ }
+ cost = mips_const_insns (x);
+ if (cost > 0)
+ {
+ /* If the constant is likely to be stored in a GPR, SETs of
+ single-insn constants are as cheap as register sets; we
+ never want to CSE them.
+
+ Don't reduce the cost of storing a floating-point zero in
+ FPRs. If we have a zero in an FPR for other reasons, we
+ can get better cfg-cleanup and delayed-branch results by
+ using it consistently, rather than using $0 sometimes and
+ an FPR at other times. Also, moves between floating-point
+ registers are sometimes cheaper than (D)MTC1 $0. */
+ if (cost == 1
+ && outer_code == SET
+ && !(float_mode_p && TARGET_HARD_FLOAT))
+ cost = 0;
+ /* When non-MIPS16 code loads a constant N>1 times, we rarely
+ want to CSE the constant itself. It is usually better to
+ have N copies of the last operation in the sequence and one
+ shared copy of the other operations. (Note that this is
+ not true for MIPS16 code, where the final operation in the
+ sequence is often an extended instruction.)
+
+ Also, if we have a CONST_INT, we don't know whether it is
+ for a word or doubleword operation, so we cannot rely on
+ the result of mips_build_integer. */
+ else if (!TARGET_MIPS16
+ && (outer_code == SET || mode == VOIDmode))
+ cost = 1;
+ *total = COSTS_N_INSNS (cost);
+ return true;
+ }
+ /* The value will need to be fetched from the constant pool. */
+ *total = CONSTANT_POOL_COST;
+ return true;
+
+ case MEM:
+ /* If the address is legitimate, return the number of
+ instructions it needs. */
+ addr = XEXP (x, 0);
+ cost = mips_address_insns (addr, mode, true);
+ if (cost > 0)
+ {
+ *total = COSTS_N_INSNS (cost + 1);
+ return true;
+ }
+ /* Check for a scaled indexed address. */
+ if (mips_lwxs_address_p (addr))
+ {
+ *total = COSTS_N_INSNS (2);
+ return true;
+ }
+ /* Otherwise use the default handling. */
+ return false;
+
+ case FFS:
+ *total = COSTS_N_INSNS (6);
+ return false;
+
+ case NOT:
+ *total = COSTS_N_INSNS (GET_MODE_SIZE (mode) > UNITS_PER_WORD ? 2 : 1);
+ return false;
+
+ case AND:
+ /* Check for a *clear_upper32 pattern and treat it like a zero
+ extension. See the pattern's comment for details. */
+ if (TARGET_64BIT
+ && mode == DImode
+ && CONST_INT_P (XEXP (x, 1))
+ && UINTVAL (XEXP (x, 1)) == 0xffffffff)
+ {
+ *total = (mips_zero_extend_cost (mode, XEXP (x, 0))
+ + rtx_cost (XEXP (x, 0), SET, speed));
+ return true;
+ }
+ /* Fall through. */
+
+ case IOR:
+ case XOR:
+ /* Double-word operations use two single-word operations. */
+ *total = mips_binary_cost (x, COSTS_N_INSNS (1), COSTS_N_INSNS (2),
+ speed);
+ return true;
+
+ case ASHIFT:
+ case ASHIFTRT:
+ case LSHIFTRT:
+ case ROTATE:
+ case ROTATERT:
+ if (CONSTANT_P (XEXP (x, 1)))
+ *total = mips_binary_cost (x, COSTS_N_INSNS (1), COSTS_N_INSNS (4),
+ speed);
+ else
+ *total = mips_binary_cost (x, COSTS_N_INSNS (1), COSTS_N_INSNS (12),
+ speed);
+ return true;
+
+ case ABS:
+ if (float_mode_p)
+ *total = mips_cost->fp_add;
+ else
+ *total = COSTS_N_INSNS (4);
+ return false;
+
+ case LO_SUM:
+ /* Low-part immediates need an extended MIPS16 instruction. */
+ *total = (COSTS_N_INSNS (TARGET_MIPS16 ? 2 : 1)
+ + rtx_cost (XEXP (x, 0), SET, speed));
+ return true;
+
+ case LT:
+ case LTU:
+ case LE:
+ case LEU:
+ case GT:
+ case GTU:
+ case GE:
+ case GEU:
+ case EQ:
+ case NE:
+ case UNORDERED:
+ case LTGT:
+ /* Branch comparisons have VOIDmode, so use the first operand's
+ mode instead. */
+ mode = GET_MODE (XEXP (x, 0));
+ if (FLOAT_MODE_P (mode))
+ {
+ *total = mips_cost->fp_add;
+ return false;
+ }
+ *total = mips_binary_cost (x, COSTS_N_INSNS (1), COSTS_N_INSNS (4),
+ speed);
+ return true;
+
+ case MINUS:
+ if (float_mode_p
+ && (ISA_HAS_NMADD4_NMSUB4 (mode) || ISA_HAS_NMADD3_NMSUB3 (mode))
+ && TARGET_FUSED_MADD
+ && !HONOR_NANS (mode)
+ && !HONOR_SIGNED_ZEROS (mode))
+ {
+ /* See if we can use NMADD or NMSUB. See mips.md for the
+ associated patterns. */
+ rtx op0 = XEXP (x, 0);
+ rtx op1 = XEXP (x, 1);
+ if (GET_CODE (op0) == MULT && GET_CODE (XEXP (op0, 0)) == NEG)
+ {
+ *total = (mips_fp_mult_cost (mode)
+ + rtx_cost (XEXP (XEXP (op0, 0), 0), SET, speed)
+ + rtx_cost (XEXP (op0, 1), SET, speed)
+ + rtx_cost (op1, SET, speed));
+ return true;
+ }
+ if (GET_CODE (op1) == MULT)
+ {
+ *total = (mips_fp_mult_cost (mode)
+ + rtx_cost (op0, SET, speed)
+ + rtx_cost (XEXP (op1, 0), SET, speed)
+ + rtx_cost (XEXP (op1, 1), SET, speed));
+ return true;
+ }
+ }
+ /* Fall through. */
+
+ case PLUS:
+ if (float_mode_p)
+ {
+ /* If this is part of a MADD or MSUB, treat the PLUS as
+ being free. */
+ if (ISA_HAS_FP4
+ && TARGET_FUSED_MADD
+ && GET_CODE (XEXP (x, 0)) == MULT)
+ *total = 0;
+ else
+ *total = mips_cost->fp_add;
+ return false;
+ }
+
+ /* Double-word operations require three single-word operations and
+ an SLTU. The MIPS16 version then needs to move the result of
+ the SLTU from $24 to a MIPS16 register. */
+ *total = mips_binary_cost (x, COSTS_N_INSNS (1),
+ COSTS_N_INSNS (TARGET_MIPS16 ? 5 : 4),
+ speed);
+ return true;
+
+ case NEG:
+ if (float_mode_p
+ && (ISA_HAS_NMADD4_NMSUB4 (mode) || ISA_HAS_NMADD3_NMSUB3 (mode))
+ && TARGET_FUSED_MADD
+ && !HONOR_NANS (mode)
+ && HONOR_SIGNED_ZEROS (mode))
+ {
+ /* See if we can use NMADD or NMSUB. See mips.md for the
+ associated patterns. */
+ rtx op = XEXP (x, 0);
+ if ((GET_CODE (op) == PLUS || GET_CODE (op) == MINUS)
+ && GET_CODE (XEXP (op, 0)) == MULT)
+ {
+ *total = (mips_fp_mult_cost (mode)
+ + rtx_cost (XEXP (XEXP (op, 0), 0), SET, speed)
+ + rtx_cost (XEXP (XEXP (op, 0), 1), SET, speed)
+ + rtx_cost (XEXP (op, 1), SET, speed));
+ return true;
+ }
+ }
+
+ if (float_mode_p)
+ *total = mips_cost->fp_add;
+ else
+ *total = COSTS_N_INSNS (GET_MODE_SIZE (mode) > UNITS_PER_WORD ? 4 : 1);
+ return false;
+
+ case MULT:
+ if (float_mode_p)
+ *total = mips_fp_mult_cost (mode);
+ else if (mode == DImode && !TARGET_64BIT)
+ /* Synthesized from 2 mulsi3s, 1 mulsidi3 and two additions,
+ where the mulsidi3 always includes an MFHI and an MFLO. */
+ *total = (speed
+ ? mips_cost->int_mult_si * 3 + 6
+ : COSTS_N_INSNS (ISA_HAS_MUL3 ? 7 : 9));
+ else if (!speed)
+ *total = (ISA_HAS_MUL3 ? 1 : 2);
+ else if (mode == DImode)
+ *total = mips_cost->int_mult_di;
+ else
+ *total = mips_cost->int_mult_si;
+ return false;
+
+ case DIV:
+ /* Check for a reciprocal. */
+ if (float_mode_p
+ && ISA_HAS_FP4
+ && flag_unsafe_math_optimizations
+ && XEXP (x, 0) == CONST1_RTX (mode))
+ {
+ if (outer_code == SQRT || GET_CODE (XEXP (x, 1)) == SQRT)
+ /* An rsqrt<mode>a or rsqrt<mode>b pattern. Count the
+ division as being free. */
+ *total = rtx_cost (XEXP (x, 1), SET, speed);
+ else
+ *total = (mips_fp_div_cost (mode)
+ + rtx_cost (XEXP (x, 1), SET, speed));
+ return true;
+ }
+ /* Fall through. */
+
+ case SQRT:
+ case MOD:
+ if (float_mode_p)
+ {
+ *total = mips_fp_div_cost (mode);
+ return false;
+ }
+ /* Fall through. */
+
+ case UDIV:
+ case UMOD:
+ if (!speed)
+ {
+ /* It is our responsibility to make division by a power of 2
+ as cheap as 2 register additions if we want the division
+ expanders to be used for such operations; see the setting
+ of sdiv_pow2_cheap in optabs.c. Using (D)DIV for MIPS16
+ should always produce shorter code than using
+ expand_sdiv2_pow2. */
+ if (TARGET_MIPS16
+ && CONST_INT_P (XEXP (x, 1))
+ && exact_log2 (INTVAL (XEXP (x, 1))) >= 0)
+ {
+ *total = COSTS_N_INSNS (2) + rtx_cost (XEXP (x, 0), SET, speed);
+ return true;
+ }
+ *total = COSTS_N_INSNS (mips_idiv_insns ());
+ }
+ else if (mode == DImode)
+ *total = mips_cost->int_div_di;
+ else
+ *total = mips_cost->int_div_si;
+ return false;
+
+ case SIGN_EXTEND:
+ *total = mips_sign_extend_cost (mode, XEXP (x, 0));
+ return false;
+
+ case ZERO_EXTEND:
+ *total = mips_zero_extend_cost (mode, XEXP (x, 0));
+ return false;
+
+ case FLOAT:
+ case UNSIGNED_FLOAT:
+ case FIX:
+ case FLOAT_EXTEND:
+ case FLOAT_TRUNCATE:
+ *total = mips_cost->fp_add;
+ return false;
+
+ default:
+ return false;
+ }
+}
+
+/* Implement TARGET_ADDRESS_COST. */
+
+static int
+mips_address_cost (rtx addr, bool speed ATTRIBUTE_UNUSED)
+{
+ return mips_address_insns (addr, SImode, false);
+}
+
+/* Information about a single instruction in a multi-instruction
+ asm sequence. */
+struct mips_multi_member {
+ /* True if this is a label, false if it is code. */
+ bool is_label_p;
+
+ /* The output_asm_insn format of the instruction. */
+ const char *format;
+
+ /* The operands to the instruction. */
+ rtx operands[MAX_RECOG_OPERANDS];
+};
+typedef struct mips_multi_member mips_multi_member;
+
+/* Vector definitions for the above. */
+DEF_VEC_O(mips_multi_member);
+DEF_VEC_ALLOC_O(mips_multi_member, heap);
+
+/* The instructions that make up the current multi-insn sequence. */
+static VEC (mips_multi_member, heap) *mips_multi_members;
+
+/* How many instructions (as opposed to labels) are in the current
+ multi-insn sequence. */
+static unsigned int mips_multi_num_insns;
+
+/* Start a new multi-insn sequence. */
+
+static void
+mips_multi_start (void)
+{
+ VEC_truncate (mips_multi_member, mips_multi_members, 0);
+ mips_multi_num_insns = 0;
+}
+
+/* Add a new, uninitialized member to the current multi-insn sequence. */
+
+static struct mips_multi_member *
+mips_multi_add (void)
+{
+ return VEC_safe_push (mips_multi_member, heap, mips_multi_members, 0);
+}
+
+/* Add a normal insn with the given asm format to the current multi-insn
+ sequence. The other arguments are a null-terminated list of operands. */
+
+static void
+mips_multi_add_insn (const char *format, ...)
+{
+ struct mips_multi_member *member;
+ va_list ap;
+ unsigned int i;
+ rtx op;
+
+ member = mips_multi_add ();
+ member->is_label_p = false;
+ member->format = format;
+ va_start (ap, format);
+ i = 0;
+ while ((op = va_arg (ap, rtx)))
+ member->operands[i++] = op;
+ va_end (ap);
+ mips_multi_num_insns++;
+}
+
+/* Add the given label definition to the current multi-insn sequence.
+ The definition should include the colon. */
+
+static void
+mips_multi_add_label (const char *label)
+{
+ struct mips_multi_member *member;
+
+ member = mips_multi_add ();
+ member->is_label_p = true;
+ member->format = label;
+}
+
+/* Return the index of the last member of the current multi-insn sequence. */
+
+static unsigned int
+mips_multi_last_index (void)
+{
+ return VEC_length (mips_multi_member, mips_multi_members) - 1;
+}
+
+/* Add a copy of an existing instruction to the current multi-insn
+ sequence. I is the index of the instruction that should be copied. */
+
+static void
+mips_multi_copy_insn (unsigned int i)
+{
+ struct mips_multi_member *member;
+
+ member = mips_multi_add ();
+ memcpy (member, VEC_index (mips_multi_member, mips_multi_members, i),
+ sizeof (*member));
+ gcc_assert (!member->is_label_p);
+}
+
+/* Change the operand of an existing instruction in the current
+ multi-insn sequence. I is the index of the instruction,
+ OP is the index of the operand, and X is the new value. */
+
+static void
+mips_multi_set_operand (unsigned int i, unsigned int op, rtx x)
+{
+ VEC_index (mips_multi_member, mips_multi_members, i)->operands[op] = x;
+}
+
+/* Write out the asm code for the current multi-insn sequence. */
+
+static void
+mips_multi_write (void)
+{
+ struct mips_multi_member *member;
+ unsigned int i;
+
+ FOR_EACH_VEC_ELT (mips_multi_member, mips_multi_members, i, member)
+ if (member->is_label_p)
+ fprintf (asm_out_file, "%s\n", member->format);
+ else
+ output_asm_insn (member->format, member->operands);
+}
+
+/* Return one word of double-word value OP, taking into account the fixed
+ endianness of certain registers. HIGH_P is true to select the high part,
+ false to select the low part. */
+
+rtx
+mips_subword (rtx op, bool high_p)
+{
+ unsigned int byte, offset;
+ enum machine_mode mode;
+
+ mode = GET_MODE (op);
+ if (mode == VOIDmode)
+ mode = TARGET_64BIT ? TImode : DImode;
+
+ if (TARGET_BIG_ENDIAN ? !high_p : high_p)
+ byte = UNITS_PER_WORD;
+ else
+ byte = 0;
+
+ if (FP_REG_RTX_P (op))
+ {
+ /* Paired FPRs are always ordered little-endian. */
+ offset = (UNITS_PER_WORD < UNITS_PER_HWFPVALUE ? high_p : byte != 0);
+ return gen_rtx_REG (word_mode, REGNO (op) + offset);
+ }
+
+ if (MEM_P (op))
+ return mips_rewrite_small_data (adjust_address (op, word_mode, byte));
+
+ return simplify_gen_subreg (word_mode, op, mode, byte);
+}
+
+/* Return true if a 64-bit move from SRC to DEST should be split into two. */
+
+bool
+mips_split_64bit_move_p (rtx dest, rtx src)
+{
+ if (TARGET_64BIT)
+ return false;
+
+ /* FPR-to-FPR moves can be done in a single instruction, if they're
+ allowed at all. */
+ if (FP_REG_RTX_P (src) && FP_REG_RTX_P (dest))
+ return false;
+
+ /* Check for floating-point loads and stores. */
+ if (ISA_HAS_LDC1_SDC1)
+ {
+ if (FP_REG_RTX_P (dest) && MEM_P (src))
+ return false;
+ if (FP_REG_RTX_P (src) && MEM_P (dest))
+ return false;
+ }
+ return true;
+}
+
+/* Split a doubleword move from SRC to DEST. On 32-bit targets,
+ this function handles 64-bit moves for which mips_split_64bit_move_p
+ holds. For 64-bit targets, this function handles 128-bit moves. */
+
+void
+mips_split_doubleword_move (rtx dest, rtx src)
+{
+ rtx low_dest;
+
+ if (FP_REG_RTX_P (dest) || FP_REG_RTX_P (src))
+ {
+ if (!TARGET_64BIT && GET_MODE (dest) == DImode)
+ emit_insn (gen_move_doubleword_fprdi (dest, src));
+ else if (!TARGET_64BIT && GET_MODE (dest) == DFmode)
+ emit_insn (gen_move_doubleword_fprdf (dest, src));
+ else if (!TARGET_64BIT && GET_MODE (dest) == V2SFmode)
+ emit_insn (gen_move_doubleword_fprv2sf (dest, src));
+ else if (!TARGET_64BIT && GET_MODE (dest) == V2SImode)
+ emit_insn (gen_move_doubleword_fprv2si (dest, src));
+ else if (!TARGET_64BIT && GET_MODE (dest) == V4HImode)
+ emit_insn (gen_move_doubleword_fprv4hi (dest, src));
+ else if (!TARGET_64BIT && GET_MODE (dest) == V8QImode)
+ emit_insn (gen_move_doubleword_fprv8qi (dest, src));
+ else if (TARGET_64BIT && GET_MODE (dest) == TFmode)
+ emit_insn (gen_move_doubleword_fprtf (dest, src));
+ else
+ gcc_unreachable ();
+ }
+ else if (REG_P (dest) && REGNO (dest) == MD_REG_FIRST)
+ {
+ low_dest = mips_subword (dest, false);
+ mips_emit_move (low_dest, mips_subword (src, false));
+ if (TARGET_64BIT)
+ emit_insn (gen_mthidi_ti (dest, mips_subword (src, true), low_dest));
+ else
+ emit_insn (gen_mthisi_di (dest, mips_subword (src, true), low_dest));
+ }
+ else if (REG_P (src) && REGNO (src) == MD_REG_FIRST)
+ {
+ mips_emit_move (mips_subword (dest, false), mips_subword (src, false));
+ if (TARGET_64BIT)
+ emit_insn (gen_mfhidi_ti (mips_subword (dest, true), src));
+ else
+ emit_insn (gen_mfhisi_di (mips_subword (dest, true), src));
+ }
+ else
+ {
+ /* The operation can be split into two normal moves. Decide in
+ which order to do them. */
+ low_dest = mips_subword (dest, false);
+ if (REG_P (low_dest)
+ && reg_overlap_mentioned_p (low_dest, src))
+ {
+ mips_emit_move (mips_subword (dest, true), mips_subword (src, true));
+ mips_emit_move (low_dest, mips_subword (src, false));
+ }
+ else
+ {
+ mips_emit_move (low_dest, mips_subword (src, false));
+ mips_emit_move (mips_subword (dest, true), mips_subword (src, true));
+ }
+ }
+}
+
+/* Return the appropriate instructions to move SRC into DEST. Assume
+ that SRC is operand 1 and DEST is operand 0. */
+
+const char *
+mips_output_move (rtx dest, rtx src)
+{
+ enum rtx_code dest_code, src_code;
+ enum machine_mode mode;
+ enum mips_symbol_type symbol_type;
+ bool dbl_p;
+
+ dest_code = GET_CODE (dest);
+ src_code = GET_CODE (src);
+ mode = GET_MODE (dest);
+ dbl_p = (GET_MODE_SIZE (mode) == 8);
+
+ if (dbl_p && mips_split_64bit_move_p (dest, src))
+ return "#";
+
+ if ((src_code == REG && GP_REG_P (REGNO (src)))
+ || (!TARGET_MIPS16 && src == CONST0_RTX (mode)))
+ {
+ if (dest_code == REG)
+ {
+ if (GP_REG_P (REGNO (dest)))
+ return "move\t%0,%z1";
+
+ /* Moves to HI are handled by special .md insns. */
+ if (REGNO (dest) == LO_REGNUM)
+ return "mtlo\t%z1";
+
+ if (DSP_ACC_REG_P (REGNO (dest)))
+ {
+ static char retval[] = "mt__\t%z1,%q0";
+
+ retval[2] = reg_names[REGNO (dest)][4];
+ retval[3] = reg_names[REGNO (dest)][5];
+ return retval;
+ }
+
+ if (FP_REG_P (REGNO (dest)))
+ return dbl_p ? "dmtc1\t%z1,%0" : "mtc1\t%z1,%0";
+
+ if (ALL_COP_REG_P (REGNO (dest)))
+ {
+ static char retval[] = "dmtc_\t%z1,%0";
+
+ retval[4] = COPNUM_AS_CHAR_FROM_REGNUM (REGNO (dest));
+ return dbl_p ? retval : retval + 1;
+ }
+ }
+ if (dest_code == MEM)
+ switch (GET_MODE_SIZE (mode))
+ {
+ case 1: return "sb\t%z1,%0";
+ case 2: return "sh\t%z1,%0";
+ case 4: return "sw\t%z1,%0";
+ case 8: return "sd\t%z1,%0";
+ }
+ }
+ if (dest_code == REG && GP_REG_P (REGNO (dest)))
+ {
+ if (src_code == REG)
+ {
+ /* Moves from HI are handled by special .md insns. */
+ if (REGNO (src) == LO_REGNUM)
+ {
+ /* When generating VR4120 or VR4130 code, we use MACC and
+ DMACC instead of MFLO. This avoids both the normal
+ MIPS III HI/LO hazards and the errata related to
+ -mfix-vr4130. */
+ if (ISA_HAS_MACCHI)
+ return dbl_p ? "dmacc\t%0,%.,%." : "macc\t%0,%.,%.";
+ return "mflo\t%0";
+ }
+
+ if (DSP_ACC_REG_P (REGNO (src)))
+ {
+ static char retval[] = "mf__\t%0,%q1";
+
+ retval[2] = reg_names[REGNO (src)][4];
+ retval[3] = reg_names[REGNO (src)][5];
+ return retval;
+ }
+
+ if (FP_REG_P (REGNO (src)))
+ return dbl_p ? "dmfc1\t%0,%1" : "mfc1\t%0,%1";
+
+ if (ALL_COP_REG_P (REGNO (src)))
+ {
+ static char retval[] = "dmfc_\t%0,%1";
+
+ retval[4] = COPNUM_AS_CHAR_FROM_REGNUM (REGNO (src));
+ return dbl_p ? retval : retval + 1;
+ }
+
+ if (ST_REG_P (REGNO (src)) && ISA_HAS_8CC)
+ return "lui\t%0,0x3f80\n\tmovf\t%0,%.,%1";
+ }
+
+ if (src_code == MEM)
+ switch (GET_MODE_SIZE (mode))
+ {
+ case 1: return "lbu\t%0,%1";
+ case 2: return "lhu\t%0,%1";
+ case 4: return "lw\t%0,%1";
+ case 8: return "ld\t%0,%1";
+ }
+
+ if (src_code == CONST_INT)
+ {
+ /* Don't use the X format for the operand itself, because that
+ will give out-of-range numbers for 64-bit hosts and 32-bit
+ targets. */
+ if (!TARGET_MIPS16)
+ return "li\t%0,%1\t\t\t# %X1";
+
+ if (SMALL_OPERAND_UNSIGNED (INTVAL (src)))
+ return "li\t%0,%1";
+
+ if (SMALL_OPERAND_UNSIGNED (-INTVAL (src)))
+ return "#";
+ }
+
+ if (src_code == HIGH)
+ return TARGET_MIPS16 ? "#" : "lui\t%0,%h1";
+
+ if (CONST_GP_P (src))
+ return "move\t%0,%1";
+
+ if (mips_symbolic_constant_p (src, SYMBOL_CONTEXT_LEA, &symbol_type)
+ && mips_lo_relocs[symbol_type] != 0)
+ {
+ /* A signed 16-bit constant formed by applying a relocation
+ operator to a symbolic address. */
+ gcc_assert (!mips_split_p[symbol_type]);
+ return "li\t%0,%R1";
+ }
+
+ if (symbolic_operand (src, VOIDmode))
+ {
+ gcc_assert (TARGET_MIPS16
+ ? TARGET_MIPS16_TEXT_LOADS
+ : !TARGET_EXPLICIT_RELOCS);
+ return dbl_p ? "dla\t%0,%1" : "la\t%0,%1";
+ }
+ }
+ if (src_code == REG && FP_REG_P (REGNO (src)))
+ {
+ if (dest_code == REG && FP_REG_P (REGNO (dest)))
+ {
+ if (GET_MODE (dest) == V2SFmode)
+ return "mov.ps\t%0,%1";
+ else
+ return dbl_p ? "mov.d\t%0,%1" : "mov.s\t%0,%1";
+ }
+
+ if (dest_code == MEM)
+ return dbl_p ? "sdc1\t%1,%0" : "swc1\t%1,%0";
+ }
+ if (dest_code == REG && FP_REG_P (REGNO (dest)))
+ {
+ if (src_code == MEM)
+ return dbl_p ? "ldc1\t%0,%1" : "lwc1\t%0,%1";
+ }
+ if (dest_code == REG && ALL_COP_REG_P (REGNO (dest)) && src_code == MEM)
+ {
+ static char retval[] = "l_c_\t%0,%1";
+
+ retval[1] = (dbl_p ? 'd' : 'w');
+ retval[3] = COPNUM_AS_CHAR_FROM_REGNUM (REGNO (dest));
+ return retval;
+ }
+ if (dest_code == MEM && src_code == REG && ALL_COP_REG_P (REGNO (src)))
+ {
+ static char retval[] = "s_c_\t%1,%0";
+
+ retval[1] = (dbl_p ? 'd' : 'w');
+ retval[3] = COPNUM_AS_CHAR_FROM_REGNUM (REGNO (src));
+ return retval;
+ }
+ gcc_unreachable ();
+}
+
+/* Return true if CMP1 is a suitable second operand for integer ordering
+ test CODE. See also the *sCC patterns in mips.md. */
+
+static bool
+mips_int_order_operand_ok_p (enum rtx_code code, rtx cmp1)
+{
+ switch (code)
+ {
+ case GT:
+ case GTU:
+ return reg_or_0_operand (cmp1, VOIDmode);
+
+ case GE:
+ case GEU:
+ return !TARGET_MIPS16 && cmp1 == const1_rtx;
+
+ case LT:
+ case LTU:
+ return arith_operand (cmp1, VOIDmode);
+
+ case LE:
+ return sle_operand (cmp1, VOIDmode);
+
+ case LEU:
+ return sleu_operand (cmp1, VOIDmode);
+
+ default:
+ gcc_unreachable ();
+ }
+}
+
+/* Return true if *CMP1 (of mode MODE) is a valid second operand for
+ integer ordering test *CODE, or if an equivalent combination can
+ be formed by adjusting *CODE and *CMP1. When returning true, update
+ *CODE and *CMP1 with the chosen code and operand, otherwise leave
+ them alone. */
+
+static bool
+mips_canonicalize_int_order_test (enum rtx_code *code, rtx *cmp1,
+ enum machine_mode mode)
+{
+ HOST_WIDE_INT plus_one;
+
+ if (mips_int_order_operand_ok_p (*code, *cmp1))
+ return true;
+
+ if (CONST_INT_P (*cmp1))
+ switch (*code)
+ {
+ case LE:
+ plus_one = trunc_int_for_mode (UINTVAL (*cmp1) + 1, mode);
+ if (INTVAL (*cmp1) < plus_one)
+ {
+ *code = LT;
+ *cmp1 = force_reg (mode, GEN_INT (plus_one));
+ return true;
+ }
+ break;
+
+ case LEU:
+ plus_one = trunc_int_for_mode (UINTVAL (*cmp1) + 1, mode);
+ if (plus_one != 0)
+ {
+ *code = LTU;
+ *cmp1 = force_reg (mode, GEN_INT (plus_one));
+ return true;
+ }
+ break;
+
+ default:
+ break;
+ }
+ return false;
+}
+
+/* Compare CMP0 and CMP1 using ordering test CODE and store the result
+ in TARGET. CMP0 and TARGET are register_operands. If INVERT_PTR
+ is nonnull, it's OK to set TARGET to the inverse of the result and
+ flip *INVERT_PTR instead. */
+
+static void
+mips_emit_int_order_test (enum rtx_code code, bool *invert_ptr,
+ rtx target, rtx cmp0, rtx cmp1)
+{
+ enum machine_mode mode;
+
+ /* First see if there is a MIPS instruction that can do this operation.
+ If not, try doing the same for the inverse operation. If that also
+ fails, force CMP1 into a register and try again. */
+ mode = GET_MODE (cmp0);
+ if (mips_canonicalize_int_order_test (&code, &cmp1, mode))
+ mips_emit_binary (code, target, cmp0, cmp1);
+ else
+ {
+ enum rtx_code inv_code = reverse_condition (code);
+ if (!mips_canonicalize_int_order_test (&inv_code, &cmp1, mode))
+ {
+ cmp1 = force_reg (mode, cmp1);
+ mips_emit_int_order_test (code, invert_ptr, target, cmp0, cmp1);
+ }
+ else if (invert_ptr == 0)
+ {
+ rtx inv_target;
+
+ inv_target = mips_force_binary (GET_MODE (target),
+ inv_code, cmp0, cmp1);
+ mips_emit_binary (XOR, target, inv_target, const1_rtx);
+ }
+ else
+ {
+ *invert_ptr = !*invert_ptr;
+ mips_emit_binary (inv_code, target, cmp0, cmp1);
+ }
+ }
+}
+
+/* Return a register that is zero iff CMP0 and CMP1 are equal.
+ The register will have the same mode as CMP0. */
+
+static rtx
+mips_zero_if_equal (rtx cmp0, rtx cmp1)
+{
+ if (cmp1 == const0_rtx)
+ return cmp0;
+
+ if (uns_arith_operand (cmp1, VOIDmode))
+ return expand_binop (GET_MODE (cmp0), xor_optab,
+ cmp0, cmp1, 0, 0, OPTAB_DIRECT);
+
+ return expand_binop (GET_MODE (cmp0), sub_optab,
+ cmp0, cmp1, 0, 0, OPTAB_DIRECT);
+}
+
+/* Convert *CODE into a code that can be used in a floating-point
+ scc instruction (C.cond.fmt). Return true if the values of
+ the condition code registers will be inverted, with 0 indicating
+ that the condition holds. */
+
+static bool
+mips_reversed_fp_cond (enum rtx_code *code)
+{
+ switch (*code)
+ {
+ case NE:
+ case LTGT:
+ case ORDERED:
+ *code = reverse_condition_maybe_unordered (*code);
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+/* Convert a comparison into something that can be used in a branch or
+ conditional move. On entry, *OP0 and *OP1 are the values being
+ compared and *CODE is the code used to compare them.
+
+ Update *CODE, *OP0 and *OP1 so that they describe the final comparison.
+ If NEED_EQ_NE_P, then only EQ or NE comparisons against zero are possible,
+ otherwise any standard branch condition can be used. The standard branch
+ conditions are:
+
+ - EQ or NE between two registers.
+ - any comparison between a register and zero. */
+
+static void
+mips_emit_compare (enum rtx_code *code, rtx *op0, rtx *op1, bool need_eq_ne_p)
+{
+ rtx cmp_op0 = *op0;
+ rtx cmp_op1 = *op1;
+
+ if (GET_MODE_CLASS (GET_MODE (*op0)) == MODE_INT)
+ {
+ if (!need_eq_ne_p && *op1 == const0_rtx)
+ ;
+ else if (*code == EQ || *code == NE)
+ {
+ if (need_eq_ne_p)
+ {
+ *op0 = mips_zero_if_equal (cmp_op0, cmp_op1);
+ *op1 = const0_rtx;
+ }
+ else
+ *op1 = force_reg (GET_MODE (cmp_op0), cmp_op1);
+ }
+ else
+ {
+ /* The comparison needs a separate scc instruction. Store the
+ result of the scc in *OP0 and compare it against zero. */
+ bool invert = false;
+ *op0 = gen_reg_rtx (GET_MODE (cmp_op0));
+ mips_emit_int_order_test (*code, &invert, *op0, cmp_op0, cmp_op1);
+ *code = (invert ? EQ : NE);
+ *op1 = const0_rtx;
+ }
+ }
+ else if (ALL_FIXED_POINT_MODE_P (GET_MODE (cmp_op0)))
+ {
+ *op0 = gen_rtx_REG (CCDSPmode, CCDSP_CC_REGNUM);
+ mips_emit_binary (*code, *op0, cmp_op0, cmp_op1);
+ *code = NE;
+ *op1 = const0_rtx;
+ }
+ else
+ {
+ enum rtx_code cmp_code;
+
+ /* Floating-point tests use a separate C.cond.fmt comparison to
+ set a condition code register. The branch or conditional move
+ will then compare that register against zero.
+
+ Set CMP_CODE to the code of the comparison instruction and
+ *CODE to the code that the branch or move should use. */
+ cmp_code = *code;
+ *code = mips_reversed_fp_cond (&cmp_code) ? EQ : NE;
+ *op0 = (ISA_HAS_8CC
+ ? gen_reg_rtx (CCmode)
+ : gen_rtx_REG (CCmode, FPSW_REGNUM));
+ *op1 = const0_rtx;
+ mips_emit_binary (cmp_code, *op0, cmp_op0, cmp_op1);
+ }
+}
+
+/* Try performing the comparison in OPERANDS[1], whose arms are OPERANDS[2]
+ and OPERAND[3]. Store the result in OPERANDS[0].
+
+ On 64-bit targets, the mode of the comparison and target will always be
+ SImode, thus possibly narrower than that of the comparison's operands. */
+
+void
+mips_expand_scc (rtx operands[])
+{
+ rtx target = operands[0];
+ enum rtx_code code = GET_CODE (operands[1]);
+ rtx op0 = operands[2];
+ rtx op1 = operands[3];
+
+ gcc_assert (GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT);
+
+ if (code == EQ || code == NE)
+ {
+ if (ISA_HAS_SEQ_SNE
+ && reg_imm10_operand (op1, GET_MODE (op1)))
+ mips_emit_binary (code, target, op0, op1);
+ else
+ {
+ rtx zie = mips_zero_if_equal (op0, op1);
+ mips_emit_binary (code, target, zie, const0_rtx);
+ }
+ }
+ else
+ mips_emit_int_order_test (code, 0, target, op0, op1);
+}
+
+/* Compare OPERANDS[1] with OPERANDS[2] using comparison code
+ CODE and jump to OPERANDS[3] if the condition holds. */
+
+void
+mips_expand_conditional_branch (rtx *operands)
+{
+ enum rtx_code code = GET_CODE (operands[0]);
+ rtx op0 = operands[1];
+ rtx op1 = operands[2];
+ rtx condition;
+
+ mips_emit_compare (&code, &op0, &op1, TARGET_MIPS16);
+ condition = gen_rtx_fmt_ee (code, VOIDmode, op0, op1);
+ emit_jump_insn (gen_condjump (condition, operands[3]));
+}
+
+/* Implement:
+
+ (set temp (COND:CCV2 CMP_OP0 CMP_OP1))
+ (set DEST (unspec [TRUE_SRC FALSE_SRC temp] UNSPEC_MOVE_TF_PS)) */
+
+void
+mips_expand_vcondv2sf (rtx dest, rtx true_src, rtx false_src,
+ enum rtx_code cond, rtx cmp_op0, rtx cmp_op1)
+{
+ rtx cmp_result;
+ bool reversed_p;
+
+ reversed_p = mips_reversed_fp_cond (&cond);
+ cmp_result = gen_reg_rtx (CCV2mode);
+ emit_insn (gen_scc_ps (cmp_result,
+ gen_rtx_fmt_ee (cond, VOIDmode, cmp_op0, cmp_op1)));
+ if (reversed_p)
+ emit_insn (gen_mips_cond_move_tf_ps (dest, false_src, true_src,
+ cmp_result));
+ else
+ emit_insn (gen_mips_cond_move_tf_ps (dest, true_src, false_src,
+ cmp_result));
+}
+
+/* Perform the comparison in OPERANDS[1]. Move OPERANDS[2] into OPERANDS[0]
+ if the condition holds, otherwise move OPERANDS[3] into OPERANDS[0]. */
+
+void
+mips_expand_conditional_move (rtx *operands)
+{
+ rtx cond;
+ enum rtx_code code = GET_CODE (operands[1]);
+ rtx op0 = XEXP (operands[1], 0);
+ rtx op1 = XEXP (operands[1], 1);
+
+ mips_emit_compare (&code, &op0, &op1, true);
+ cond = gen_rtx_fmt_ee (code, GET_MODE (op0), op0, op1);
+ emit_insn (gen_rtx_SET (VOIDmode, operands[0],
+ gen_rtx_IF_THEN_ELSE (GET_MODE (operands[0]), cond,
+ operands[2], operands[3])));
+}
+
+/* Perform the comparison in COMPARISON, then trap if the condition holds. */
+
+void
+mips_expand_conditional_trap (rtx comparison)
+{
+ rtx op0, op1;
+ enum machine_mode mode;
+ enum rtx_code code;
+
+ /* MIPS conditional trap instructions don't have GT or LE flavors,
+ so we must swap the operands and convert to LT and GE respectively. */
+ code = GET_CODE (comparison);
+ switch (code)
+ {
+ case GT:
+ case LE:
+ case GTU:
+ case LEU:
+ code = swap_condition (code);
+ op0 = XEXP (comparison, 1);
+ op1 = XEXP (comparison, 0);
+ break;
+
+ default:
+ op0 = XEXP (comparison, 0);
+ op1 = XEXP (comparison, 1);
+ break;
+ }
+
+ mode = GET_MODE (XEXP (comparison, 0));
+ op0 = force_reg (mode, op0);
+ if (!arith_operand (op1, mode))
+ op1 = force_reg (mode, op1);
+
+ emit_insn (gen_rtx_TRAP_IF (VOIDmode,
+ gen_rtx_fmt_ee (code, mode, op0, op1),
+ const0_rtx));
+}
+
+/* Initialize *CUM for a call to a function of type FNTYPE. */
+
+void
+mips_init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype)
+{
+ memset (cum, 0, sizeof (*cum));
+ cum->prototype = (fntype && prototype_p (fntype));
+ cum->gp_reg_found = (cum->prototype && stdarg_p (fntype));
+}
+
+/* Fill INFO with information about a single argument. CUM is the
+ cumulative state for earlier arguments. MODE is the mode of this
+ argument and TYPE is its type (if known). NAMED is true if this
+ is a named (fixed) argument rather than a variable one. */
+
+static void
+mips_get_arg_info (struct mips_arg_info *info, const CUMULATIVE_ARGS *cum,
+ enum machine_mode mode, const_tree type, bool named)
+{
+ bool doubleword_aligned_p;
+ unsigned int num_bytes, num_words, max_regs;
+
+ /* Work out the size of the argument. */
+ num_bytes = type ? int_size_in_bytes (type) : GET_MODE_SIZE (mode);
+ num_words = (num_bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+
+ /* Decide whether it should go in a floating-point register, assuming
+ one is free. Later code checks for availability.
+
+ The checks against UNITS_PER_FPVALUE handle the soft-float and
+ single-float cases. */
+ switch (mips_abi)
+ {
+ case ABI_EABI:
+ /* The EABI conventions have traditionally been defined in terms
+ of TYPE_MODE, regardless of the actual type. */
+ info->fpr_p = ((GET_MODE_CLASS (mode) == MODE_FLOAT
+ || GET_MODE_CLASS (mode) == MODE_VECTOR_FLOAT)
+ && GET_MODE_SIZE (mode) <= UNITS_PER_FPVALUE);
+ break;
+
+ case ABI_32:
+ case ABI_O64:
+ /* Only leading floating-point scalars are passed in
+ floating-point registers. We also handle vector floats the same
+ say, which is OK because they are not covered by the standard ABI. */
+ info->fpr_p = (!cum->gp_reg_found
+ && cum->arg_number < 2
+ && (type == 0
+ || SCALAR_FLOAT_TYPE_P (type)
+ || VECTOR_FLOAT_TYPE_P (type))
+ && (GET_MODE_CLASS (mode) == MODE_FLOAT
+ || GET_MODE_CLASS (mode) == MODE_VECTOR_FLOAT)
+ && GET_MODE_SIZE (mode) <= UNITS_PER_FPVALUE);
+ break;
+
+ case ABI_N32:
+ case ABI_64:
+ /* Scalar, complex and vector floating-point types are passed in
+ floating-point registers, as long as this is a named rather
+ than a variable argument. */
+ info->fpr_p = (named
+ && (type == 0 || FLOAT_TYPE_P (type))
+ && (GET_MODE_CLASS (mode) == MODE_FLOAT
+ || GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT
+ || GET_MODE_CLASS (mode) == MODE_VECTOR_FLOAT)
+ && GET_MODE_UNIT_SIZE (mode) <= UNITS_PER_FPVALUE);
+
+ /* ??? According to the ABI documentation, the real and imaginary
+ parts of complex floats should be passed in individual registers.
+ The real and imaginary parts of stack arguments are supposed
+ to be contiguous and there should be an extra word of padding
+ at the end.
+
+ This has two problems. First, it makes it impossible to use a
+ single "void *" va_list type, since register and stack arguments
+ are passed differently. (At the time of writing, MIPSpro cannot
+ handle complex float varargs correctly.) Second, it's unclear
+ what should happen when there is only one register free.
+
+ For now, we assume that named complex floats should go into FPRs
+ if there are two FPRs free, otherwise they should be passed in the
+ same way as a struct containing two floats. */
+ if (info->fpr_p
+ && GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT
+ && GET_MODE_UNIT_SIZE (mode) < UNITS_PER_FPVALUE)
+ {
+ if (cum->num_gprs >= MAX_ARGS_IN_REGISTERS - 1)
+ info->fpr_p = false;
+ else
+ num_words = 2;
+ }
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+
+ /* See whether the argument has doubleword alignment. */
+ doubleword_aligned_p = (mips_function_arg_boundary (mode, type)
+ > BITS_PER_WORD);
+
+ /* Set REG_OFFSET to the register count we're interested in.
+ The EABI allocates the floating-point registers separately,
+ but the other ABIs allocate them like integer registers. */
+ info->reg_offset = (mips_abi == ABI_EABI && info->fpr_p
+ ? cum->num_fprs
+ : cum->num_gprs);
+
+ /* Advance to an even register if the argument is doubleword-aligned. */
+ if (doubleword_aligned_p)
+ info->reg_offset += info->reg_offset & 1;
+
+ /* Work out the offset of a stack argument. */
+ info->stack_offset = cum->stack_words;
+ if (doubleword_aligned_p)
+ info->stack_offset += info->stack_offset & 1;
+
+ max_regs = MAX_ARGS_IN_REGISTERS - info->reg_offset;
+
+ /* Partition the argument between registers and stack. */
+ info->reg_words = MIN (num_words, max_regs);
+ info->stack_words = num_words - info->reg_words;
+}
+
+/* INFO describes a register argument that has the normal format for the
+ argument's mode. Return the register it uses, assuming that FPRs are
+ available if HARD_FLOAT_P. */
+
+static unsigned int
+mips_arg_regno (const struct mips_arg_info *info, bool hard_float_p)
+{
+ if (!info->fpr_p || !hard_float_p)
+ return GP_ARG_FIRST + info->reg_offset;
+ else if (mips_abi == ABI_32 && TARGET_DOUBLE_FLOAT && info->reg_offset > 0)
+ /* In o32, the second argument is always passed in $f14
+ for TARGET_DOUBLE_FLOAT, regardless of whether the
+ first argument was a word or doubleword. */
+ return FP_ARG_FIRST + 2;
+ else
+ return FP_ARG_FIRST + info->reg_offset;
+}
+
+/* Implement TARGET_STRICT_ARGUMENT_NAMING. */
+
+static bool
+mips_strict_argument_naming (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED)
+{
+ return !TARGET_OLDABI;
+}
+
+/* Implement TARGET_FUNCTION_ARG. */
+
+static rtx
+mips_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
+ const_tree type, bool named)
+{
+ struct mips_arg_info info;
+
+ /* We will be called with a mode of VOIDmode after the last argument
+ has been seen. Whatever we return will be passed to the call expander.
+ If we need a MIPS16 fp_code, return a REG with the code stored as
+ the mode. */
+ if (mode == VOIDmode)
+ {
+ if (TARGET_MIPS16 && cum->fp_code != 0)
+ return gen_rtx_REG ((enum machine_mode) cum->fp_code, 0);
+ else
+ return NULL;
+ }
+
+ mips_get_arg_info (&info, cum, mode, type, named);
+
+ /* Return straight away if the whole argument is passed on the stack. */
+ if (info.reg_offset == MAX_ARGS_IN_REGISTERS)
+ return NULL;
+
+ /* The n32 and n64 ABIs say that if any 64-bit chunk of the structure
+ contains a double in its entirety, then that 64-bit chunk is passed
+ in a floating-point register. */
+ if (TARGET_NEWABI
+ && TARGET_HARD_FLOAT
+ && named
+ && type != 0
+ && TREE_CODE (type) == RECORD_TYPE
+ && TYPE_SIZE_UNIT (type)
+ && host_integerp (TYPE_SIZE_UNIT (type), 1))
+ {
+ tree field;
+
+ /* First check to see if there is any such field. */
+ for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
+ if (TREE_CODE (field) == FIELD_DECL
+ && SCALAR_FLOAT_TYPE_P (TREE_TYPE (field))
+ && TYPE_PRECISION (TREE_TYPE (field)) == BITS_PER_WORD
+ && host_integerp (bit_position (field), 0)
+ && int_bit_position (field) % BITS_PER_WORD == 0)
+ break;
+
+ if (field != 0)
+ {
+ /* Now handle the special case by returning a PARALLEL
+ indicating where each 64-bit chunk goes. INFO.REG_WORDS
+ chunks are passed in registers. */
+ unsigned int i;
+ HOST_WIDE_INT bitpos;
+ rtx ret;
+
+ /* assign_parms checks the mode of ENTRY_PARM, so we must
+ use the actual mode here. */
+ ret = gen_rtx_PARALLEL (mode, rtvec_alloc (info.reg_words));
+
+ bitpos = 0;
+ field = TYPE_FIELDS (type);
+ for (i = 0; i < info.reg_words; i++)
+ {
+ rtx reg;
+
+ for (; field; field = DECL_CHAIN (field))
+ if (TREE_CODE (field) == FIELD_DECL
+ && int_bit_position (field) >= bitpos)
+ break;
+
+ if (field
+ && int_bit_position (field) == bitpos
+ && SCALAR_FLOAT_TYPE_P (TREE_TYPE (field))
+ && TYPE_PRECISION (TREE_TYPE (field)) == BITS_PER_WORD)
+ reg = gen_rtx_REG (DFmode, FP_ARG_FIRST + info.reg_offset + i);
+ else
+ reg = gen_rtx_REG (DImode, GP_ARG_FIRST + info.reg_offset + i);
+
+ XVECEXP (ret, 0, i)
+ = gen_rtx_EXPR_LIST (VOIDmode, reg,
+ GEN_INT (bitpos / BITS_PER_UNIT));
+
+ bitpos += BITS_PER_WORD;
+ }
+ return ret;
+ }
+ }
+
+ /* Handle the n32/n64 conventions for passing complex floating-point
+ arguments in FPR pairs. The real part goes in the lower register
+ and the imaginary part goes in the upper register. */
+ if (TARGET_NEWABI
+ && info.fpr_p
+ && GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT)
+ {
+ rtx real, imag;
+ enum machine_mode inner;
+ unsigned int regno;
+
+ inner = GET_MODE_INNER (mode);
+ regno = FP_ARG_FIRST + info.reg_offset;
+ if (info.reg_words * UNITS_PER_WORD == GET_MODE_SIZE (inner))
+ {
+ /* Real part in registers, imaginary part on stack. */
+ gcc_assert (info.stack_words == info.reg_words);
+ return gen_rtx_REG (inner, regno);
+ }
+ else
+ {
+ gcc_assert (info.stack_words == 0);
+ real = gen_rtx_EXPR_LIST (VOIDmode,
+ gen_rtx_REG (inner, regno),
+ const0_rtx);
+ imag = gen_rtx_EXPR_LIST (VOIDmode,
+ gen_rtx_REG (inner,
+ regno + info.reg_words / 2),
+ GEN_INT (GET_MODE_SIZE (inner)));
+ return gen_rtx_PARALLEL (mode, gen_rtvec (2, real, imag));
+ }
+ }
+
+ return gen_rtx_REG (mode, mips_arg_regno (&info, TARGET_HARD_FLOAT));
+}
+
+/* Implement TARGET_FUNCTION_ARG_ADVANCE. */
+
+static void
+mips_function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
+ const_tree type, bool named)
+{
+ struct mips_arg_info info;
+
+ mips_get_arg_info (&info, cum, mode, type, named);
+
+ if (!info.fpr_p)
+ cum->gp_reg_found = true;
+
+ /* See the comment above the CUMULATIVE_ARGS structure in mips.h for
+ an explanation of what this code does. It assumes that we're using
+ either the o32 or the o64 ABI, both of which pass at most 2 arguments
+ in FPRs. */
+ if (cum->arg_number < 2 && info.fpr_p)
+ cum->fp_code += (mode == SFmode ? 1 : 2) << (cum->arg_number * 2);
+
+ /* Advance the register count. This has the effect of setting
+ num_gprs to MAX_ARGS_IN_REGISTERS if a doubleword-aligned
+ argument required us to skip the final GPR and pass the whole
+ argument on the stack. */
+ if (mips_abi != ABI_EABI || !info.fpr_p)
+ cum->num_gprs = info.reg_offset + info.reg_words;
+ else if (info.reg_words > 0)
+ cum->num_fprs += MAX_FPRS_PER_FMT;
+
+ /* Advance the stack word count. */
+ if (info.stack_words > 0)
+ cum->stack_words = info.stack_offset + info.stack_words;
+
+ cum->arg_number++;
+}
+
+/* Implement TARGET_ARG_PARTIAL_BYTES. */
+
+static int
+mips_arg_partial_bytes (CUMULATIVE_ARGS *cum,
+ enum machine_mode mode, tree type, bool named)
+{
+ struct mips_arg_info info;
+
+ mips_get_arg_info (&info, cum, mode, type, named);
+ return info.stack_words > 0 ? info.reg_words * UNITS_PER_WORD : 0;
+}
+
+/* Implement TARGET_FUNCTION_ARG_BOUNDARY. Every parameter gets at
+ least PARM_BOUNDARY bits of alignment, but will be given anything up
+ to STACK_BOUNDARY bits if the type requires it. */
+
+static unsigned int
+mips_function_arg_boundary (enum machine_mode mode, const_tree type)
+{
+ unsigned int alignment;
+
+ alignment = type ? TYPE_ALIGN (type) : GET_MODE_ALIGNMENT (mode);
+ if (alignment < PARM_BOUNDARY)
+ alignment = PARM_BOUNDARY;
+ if (alignment > STACK_BOUNDARY)
+ alignment = STACK_BOUNDARY;
+ return alignment;
+}
+
+/* Return true if FUNCTION_ARG_PADDING (MODE, TYPE) should return
+ upward rather than downward. In other words, return true if the
+ first byte of the stack slot has useful data, false if the last
+ byte does. */
+
+bool
+mips_pad_arg_upward (enum machine_mode mode, const_tree type)
+{
+ /* On little-endian targets, the first byte of every stack argument
+ is passed in the first byte of the stack slot. */
+ if (!BYTES_BIG_ENDIAN)
+ return true;
+
+ /* Otherwise, integral types are padded downward: the last byte of a
+ stack argument is passed in the last byte of the stack slot. */
+ if (type != 0
+ ? (INTEGRAL_TYPE_P (type)
+ || POINTER_TYPE_P (type)
+ || FIXED_POINT_TYPE_P (type))
+ : (SCALAR_INT_MODE_P (mode)
+ || ALL_SCALAR_FIXED_POINT_MODE_P (mode)))
+ return false;
+
+ /* Big-endian o64 pads floating-point arguments downward. */
+ if (mips_abi == ABI_O64)
+ if (type != 0 ? FLOAT_TYPE_P (type) : GET_MODE_CLASS (mode) == MODE_FLOAT)
+ return false;
+
+ /* Other types are padded upward for o32, o64, n32 and n64. */
+ if (mips_abi != ABI_EABI)
+ return true;
+
+ /* Arguments smaller than a stack slot are padded downward. */
+ if (mode != BLKmode)
+ return GET_MODE_BITSIZE (mode) >= PARM_BOUNDARY;
+ else
+ return int_size_in_bytes (type) >= (PARM_BOUNDARY / BITS_PER_UNIT);
+}
+
+/* Likewise BLOCK_REG_PADDING (MODE, TYPE, ...). Return !BYTES_BIG_ENDIAN
+ if the least significant byte of the register has useful data. Return
+ the opposite if the most significant byte does. */
+
+bool
+mips_pad_reg_upward (enum machine_mode mode, tree type)
+{
+ /* No shifting is required for floating-point arguments. */
+ if (type != 0 ? FLOAT_TYPE_P (type) : GET_MODE_CLASS (mode) == MODE_FLOAT)
+ return !BYTES_BIG_ENDIAN;
+
+ /* Otherwise, apply the same padding to register arguments as we do
+ to stack arguments. */
+ return mips_pad_arg_upward (mode, type);
+}
+
+/* Return nonzero when an argument must be passed by reference. */
+
+static bool
+mips_pass_by_reference (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED,
+ enum machine_mode mode, const_tree type,
+ bool named ATTRIBUTE_UNUSED)
+{
+ if (mips_abi == ABI_EABI)
+ {
+ int size;
+
+ /* ??? How should SCmode be handled? */
+ if (mode == DImode || mode == DFmode
+ || mode == DQmode || mode == UDQmode
+ || mode == DAmode || mode == UDAmode)
+ return 0;
+
+ size = type ? int_size_in_bytes (type) : GET_MODE_SIZE (mode);
+ return size == -1 || size > UNITS_PER_WORD;
+ }
+ else
+ {
+ /* If we have a variable-sized parameter, we have no choice. */
+ return targetm.calls.must_pass_in_stack (mode, type);
+ }
+}
+
+/* Implement TARGET_CALLEE_COPIES. */
+
+static bool
+mips_callee_copies (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED,
+ enum machine_mode mode ATTRIBUTE_UNUSED,
+ const_tree type ATTRIBUTE_UNUSED, bool named)
+{
+ return mips_abi == ABI_EABI && named;
+}
+
+/* See whether VALTYPE is a record whose fields should be returned in
+ floating-point registers. If so, return the number of fields and
+ list them in FIELDS (which should have two elements). Return 0
+ otherwise.
+
+ For n32 & n64, a structure with one or two fields is returned in
+ floating-point registers as long as every field has a floating-point
+ type. */
+
+static int
+mips_fpr_return_fields (const_tree valtype, tree *fields)
+{
+ tree field;
+ int i;
+
+ if (!TARGET_NEWABI)
+ return 0;
+
+ if (TREE_CODE (valtype) != RECORD_TYPE)
+ return 0;
+
+ i = 0;
+ for (field = TYPE_FIELDS (valtype); field != 0; field = DECL_CHAIN (field))
+ {
+ if (TREE_CODE (field) != FIELD_DECL)
+ continue;
+
+ if (!SCALAR_FLOAT_TYPE_P (TREE_TYPE (field)))
+ return 0;
+
+ if (i == 2)
+ return 0;
+
+ fields[i++] = field;
+ }
+ return i;
+}
+
+/* Implement TARGET_RETURN_IN_MSB. For n32 & n64, we should return
+ a value in the most significant part of $2/$3 if:
+
+ - the target is big-endian;
+
+ - the value has a structure or union type (we generalize this to
+ cover aggregates from other languages too); and
+
+ - the structure is not returned in floating-point registers. */
+
+static bool
+mips_return_in_msb (const_tree valtype)
+{
+ tree fields[2];
+
+ return (TARGET_NEWABI
+ && TARGET_BIG_ENDIAN
+ && AGGREGATE_TYPE_P (valtype)
+ && mips_fpr_return_fields (valtype, fields) == 0);
+}
+
+/* Return true if the function return value MODE will get returned in a
+ floating-point register. */
+
+static bool
+mips_return_mode_in_fpr_p (enum machine_mode mode)
+{
+ return ((GET_MODE_CLASS (mode) == MODE_FLOAT
+ || GET_MODE_CLASS (mode) == MODE_VECTOR_FLOAT
+ || GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT)
+ && GET_MODE_UNIT_SIZE (mode) <= UNITS_PER_HWFPVALUE);
+}
+
+/* Return the representation of an FPR return register when the
+ value being returned in FP_RETURN has mode VALUE_MODE and the
+ return type itself has mode TYPE_MODE. On NewABI targets,
+ the two modes may be different for structures like:
+
+ struct __attribute__((packed)) foo { float f; }
+
+ where we return the SFmode value of "f" in FP_RETURN, but where
+ the structure itself has mode BLKmode. */
+
+static rtx
+mips_return_fpr_single (enum machine_mode type_mode,
+ enum machine_mode value_mode)
+{
+ rtx x;
+
+ x = gen_rtx_REG (value_mode, FP_RETURN);
+ if (type_mode != value_mode)
+ {
+ x = gen_rtx_EXPR_LIST (VOIDmode, x, const0_rtx);
+ x = gen_rtx_PARALLEL (type_mode, gen_rtvec (1, x));
+ }
+ return x;
+}
+
+/* Return a composite value in a pair of floating-point registers.
+ MODE1 and OFFSET1 are the mode and byte offset for the first value,
+ likewise MODE2 and OFFSET2 for the second. MODE is the mode of the
+ complete value.
+
+ For n32 & n64, $f0 always holds the first value and $f2 the second.
+ Otherwise the values are packed together as closely as possible. */
+
+static rtx
+mips_return_fpr_pair (enum machine_mode mode,
+ enum machine_mode mode1, HOST_WIDE_INT offset1,
+ enum machine_mode mode2, HOST_WIDE_INT offset2)
+{
+ int inc;
+
+ inc = (TARGET_NEWABI ? 2 : MAX_FPRS_PER_FMT);
+ return gen_rtx_PARALLEL
+ (mode,
+ gen_rtvec (2,
+ gen_rtx_EXPR_LIST (VOIDmode,
+ gen_rtx_REG (mode1, FP_RETURN),
+ GEN_INT (offset1)),
+ gen_rtx_EXPR_LIST (VOIDmode,
+ gen_rtx_REG (mode2, FP_RETURN + inc),
+ GEN_INT (offset2))));
+
+}
+
+/* Implement FUNCTION_VALUE and LIBCALL_VALUE. For normal calls,
+ VALTYPE is the return type and MODE is VOIDmode. For libcalls,
+ VALTYPE is null and MODE is the mode of the return value. */
+
+rtx
+mips_function_value (const_tree valtype, const_tree func, enum machine_mode mode)
+{
+ if (valtype)
+ {
+ tree fields[2];
+ int unsigned_p;
+
+ mode = TYPE_MODE (valtype);
+ unsigned_p = TYPE_UNSIGNED (valtype);
+
+ /* Since TARGET_PROMOTE_FUNCTION_MODE unconditionally promotes,
+ return values, promote the mode here too. */
+ mode = promote_function_mode (valtype, mode, &unsigned_p, func, 1);
+
+ /* Handle structures whose fields are returned in $f0/$f2. */
+ switch (mips_fpr_return_fields (valtype, fields))
+ {
+ case 1:
+ return mips_return_fpr_single (mode,
+ TYPE_MODE (TREE_TYPE (fields[0])));
+
+ case 2:
+ return mips_return_fpr_pair (mode,
+ TYPE_MODE (TREE_TYPE (fields[0])),
+ int_byte_position (fields[0]),
+ TYPE_MODE (TREE_TYPE (fields[1])),
+ int_byte_position (fields[1]));
+ }
+
+ /* If a value is passed in the most significant part of a register, see
+ whether we have to round the mode up to a whole number of words. */
+ if (mips_return_in_msb (valtype))
+ {
+ HOST_WIDE_INT size = int_size_in_bytes (valtype);
+ if (size % UNITS_PER_WORD != 0)
+ {
+ size += UNITS_PER_WORD - size % UNITS_PER_WORD;
+ mode = mode_for_size (size * BITS_PER_UNIT, MODE_INT, 0);
+ }
+ }
+
+ /* For EABI, the class of return register depends entirely on MODE.
+ For example, "struct { some_type x; }" and "union { some_type x; }"
+ are returned in the same way as a bare "some_type" would be.
+ Other ABIs only use FPRs for scalar, complex or vector types. */
+ if (mips_abi != ABI_EABI && !FLOAT_TYPE_P (valtype))
+ return gen_rtx_REG (mode, GP_RETURN);
+ }
+
+ if (!TARGET_MIPS16)
+ {
+ /* Handle long doubles for n32 & n64. */
+ if (mode == TFmode)
+ return mips_return_fpr_pair (mode,
+ DImode, 0,
+ DImode, GET_MODE_SIZE (mode) / 2);
+
+ if (mips_return_mode_in_fpr_p (mode))
+ {
+ if (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT)
+ return mips_return_fpr_pair (mode,
+ GET_MODE_INNER (mode), 0,
+ GET_MODE_INNER (mode),
+ GET_MODE_SIZE (mode) / 2);
+ else
+ return gen_rtx_REG (mode, FP_RETURN);
+ }
+ }
+
+ return gen_rtx_REG (mode, GP_RETURN);
+}
+
+/* Implement TARGET_RETURN_IN_MEMORY. Under the o32 and o64 ABIs,
+ all BLKmode objects are returned in memory. Under the n32, n64
+ and embedded ABIs, small structures are returned in a register.
+ Objects with varying size must still be returned in memory, of
+ course. */
+
+static bool
+mips_return_in_memory (const_tree type, const_tree fndecl ATTRIBUTE_UNUSED)
+{
+ return (TARGET_OLDABI
+ ? TYPE_MODE (type) == BLKmode
+ : !IN_RANGE (int_size_in_bytes (type), 0, 2 * UNITS_PER_WORD));
+}
+
+/* Implement TARGET_SETUP_INCOMING_VARARGS. */
+
+static void
+mips_setup_incoming_varargs (CUMULATIVE_ARGS *cum, enum machine_mode mode,
+ tree type, int *pretend_size ATTRIBUTE_UNUSED,
+ int no_rtl)
+{
+ CUMULATIVE_ARGS local_cum;
+ int gp_saved, fp_saved;
+
+ /* The caller has advanced CUM up to, but not beyond, the last named
+ argument. Advance a local copy of CUM past the last "real" named
+ argument, to find out how many registers are left over. */
+ local_cum = *cum;
+ mips_function_arg_advance (&local_cum, mode, type, true);
+
+ /* Found out how many registers we need to save. */
+ gp_saved = MAX_ARGS_IN_REGISTERS - local_cum.num_gprs;
+ fp_saved = (EABI_FLOAT_VARARGS_P
+ ? MAX_ARGS_IN_REGISTERS - local_cum.num_fprs
+ : 0);
+
+ if (!no_rtl)
+ {
+ if (gp_saved > 0)
+ {
+ rtx ptr, mem;
+
+ ptr = plus_constant (virtual_incoming_args_rtx,
+ REG_PARM_STACK_SPACE (cfun->decl)
+ - gp_saved * UNITS_PER_WORD);
+ mem = gen_frame_mem (BLKmode, ptr);
+ set_mem_alias_set (mem, get_varargs_alias_set ());
+
+ move_block_from_reg (local_cum.num_gprs + GP_ARG_FIRST,
+ mem, gp_saved);
+ }
+ if (fp_saved > 0)
+ {
+ /* We can't use move_block_from_reg, because it will use
+ the wrong mode. */
+ enum machine_mode mode;
+ int off, i;
+
+ /* Set OFF to the offset from virtual_incoming_args_rtx of
+ the first float register. The FP save area lies below
+ the integer one, and is aligned to UNITS_PER_FPVALUE bytes. */
+ off = (-gp_saved * UNITS_PER_WORD) & -UNITS_PER_FPVALUE;
+ off -= fp_saved * UNITS_PER_FPREG;
+
+ mode = TARGET_SINGLE_FLOAT ? SFmode : DFmode;
+
+ for (i = local_cum.num_fprs; i < MAX_ARGS_IN_REGISTERS;
+ i += MAX_FPRS_PER_FMT)
+ {
+ rtx ptr, mem;
+
+ ptr = plus_constant (virtual_incoming_args_rtx, off);
+ mem = gen_frame_mem (mode, ptr);
+ set_mem_alias_set (mem, get_varargs_alias_set ());
+ mips_emit_move (mem, gen_rtx_REG (mode, FP_ARG_FIRST + i));
+ off += UNITS_PER_HWFPVALUE;
+ }
+ }
+ }
+ if (REG_PARM_STACK_SPACE (cfun->decl) == 0)
+ cfun->machine->varargs_size = (gp_saved * UNITS_PER_WORD
+ + fp_saved * UNITS_PER_FPREG);
+}
+
+/* Implement TARGET_BUILTIN_VA_LIST. */
+
+static tree
+mips_build_builtin_va_list (void)
+{
+ if (EABI_FLOAT_VARARGS_P)
+ {
+ /* We keep 3 pointers, and two offsets.
+
+ Two pointers are to the overflow area, which starts at the CFA.
+ One of these is constant, for addressing into the GPR save area
+ below it. The other is advanced up the stack through the
+ overflow region.
+
+ The third pointer is to the bottom of the GPR save area.
+ Since the FPR save area is just below it, we can address
+ FPR slots off this pointer.
+
+ We also keep two one-byte offsets, which are to be subtracted
+ from the constant pointers to yield addresses in the GPR and
+ FPR save areas. These are downcounted as float or non-float
+ arguments are used, and when they get to zero, the argument
+ must be obtained from the overflow region. */
+ tree f_ovfl, f_gtop, f_ftop, f_goff, f_foff, f_res, record;
+ tree array, index;
+
+ record = lang_hooks.types.make_type (RECORD_TYPE);
+
+ f_ovfl = build_decl (BUILTINS_LOCATION,
+ FIELD_DECL, get_identifier ("__overflow_argptr"),
+ ptr_type_node);
+ f_gtop = build_decl (BUILTINS_LOCATION,
+ FIELD_DECL, get_identifier ("__gpr_top"),
+ ptr_type_node);
+ f_ftop = build_decl (BUILTINS_LOCATION,
+ FIELD_DECL, get_identifier ("__fpr_top"),
+ ptr_type_node);
+ f_goff = build_decl (BUILTINS_LOCATION,
+ FIELD_DECL, get_identifier ("__gpr_offset"),
+ unsigned_char_type_node);
+ f_foff = build_decl (BUILTINS_LOCATION,
+ FIELD_DECL, get_identifier ("__fpr_offset"),
+ unsigned_char_type_node);
+ /* Explicitly pad to the size of a pointer, so that -Wpadded won't
+ warn on every user file. */
+ index = build_int_cst (NULL_TREE, GET_MODE_SIZE (ptr_mode) - 2 - 1);
+ array = build_array_type (unsigned_char_type_node,
+ build_index_type (index));
+ f_res = build_decl (BUILTINS_LOCATION,
+ FIELD_DECL, get_identifier ("__reserved"), array);
+
+ DECL_FIELD_CONTEXT (f_ovfl) = record;
+ DECL_FIELD_CONTEXT (f_gtop) = record;
+ DECL_FIELD_CONTEXT (f_ftop) = record;
+ DECL_FIELD_CONTEXT (f_goff) = record;
+ DECL_FIELD_CONTEXT (f_foff) = record;
+ DECL_FIELD_CONTEXT (f_res) = record;
+
+ TYPE_FIELDS (record) = f_ovfl;
+ DECL_CHAIN (f_ovfl) = f_gtop;
+ DECL_CHAIN (f_gtop) = f_ftop;
+ DECL_CHAIN (f_ftop) = f_goff;
+ DECL_CHAIN (f_goff) = f_foff;
+ DECL_CHAIN (f_foff) = f_res;
+
+ layout_type (record);
+ return record;
+ }
+ else if (TARGET_IRIX6)
+ /* On IRIX 6, this type is 'char *'. */
+ return build_pointer_type (char_type_node);
+ else
+ /* Otherwise, we use 'void *'. */
+ return ptr_type_node;
+}
+
+/* Implement TARGET_EXPAND_BUILTIN_VA_START. */
+
+static void
+mips_va_start (tree valist, rtx nextarg)
+{
+ if (EABI_FLOAT_VARARGS_P)
+ {
+ const CUMULATIVE_ARGS *cum;
+ tree f_ovfl, f_gtop, f_ftop, f_goff, f_foff;
+ tree ovfl, gtop, ftop, goff, foff;
+ tree t;
+ int gpr_save_area_size;
+ int fpr_save_area_size;
+ int fpr_offset;
+
+ cum = &crtl->args.info;
+ gpr_save_area_size
+ = (MAX_ARGS_IN_REGISTERS - cum->num_gprs) * UNITS_PER_WORD;
+ fpr_save_area_size
+ = (MAX_ARGS_IN_REGISTERS - cum->num_fprs) * UNITS_PER_FPREG;
+
+ f_ovfl = TYPE_FIELDS (va_list_type_node);
+ f_gtop = DECL_CHAIN (f_ovfl);
+ f_ftop = DECL_CHAIN (f_gtop);
+ f_goff = DECL_CHAIN (f_ftop);
+ f_foff = DECL_CHAIN (f_goff);
+
+ ovfl = build3 (COMPONENT_REF, TREE_TYPE (f_ovfl), valist, f_ovfl,
+ NULL_TREE);
+ gtop = build3 (COMPONENT_REF, TREE_TYPE (f_gtop), valist, f_gtop,
+ NULL_TREE);
+ ftop = build3 (COMPONENT_REF, TREE_TYPE (f_ftop), valist, f_ftop,
+ NULL_TREE);
+ goff = build3 (COMPONENT_REF, TREE_TYPE (f_goff), valist, f_goff,
+ NULL_TREE);
+ foff = build3 (COMPONENT_REF, TREE_TYPE (f_foff), valist, f_foff,
+ NULL_TREE);
+
+ /* Emit code to initialize OVFL, which points to the next varargs
+ stack argument. CUM->STACK_WORDS gives the number of stack
+ words used by named arguments. */
+ t = make_tree (TREE_TYPE (ovfl), virtual_incoming_args_rtx);
+ if (cum->stack_words > 0)
+ t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (ovfl), t,
+ size_int (cum->stack_words * UNITS_PER_WORD));
+ t = build2 (MODIFY_EXPR, TREE_TYPE (ovfl), ovfl, t);
+ expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+
+ /* Emit code to initialize GTOP, the top of the GPR save area. */
+ t = make_tree (TREE_TYPE (gtop), virtual_incoming_args_rtx);
+ t = build2 (MODIFY_EXPR, TREE_TYPE (gtop), gtop, t);
+ expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+
+ /* Emit code to initialize FTOP, the top of the FPR save area.
+ This address is gpr_save_area_bytes below GTOP, rounded
+ down to the next fp-aligned boundary. */
+ t = make_tree (TREE_TYPE (ftop), virtual_incoming_args_rtx);
+ fpr_offset = gpr_save_area_size + UNITS_PER_FPVALUE - 1;
+ fpr_offset &= -UNITS_PER_FPVALUE;
+ if (fpr_offset)
+ t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (ftop), t,
+ size_int (-fpr_offset));
+ t = build2 (MODIFY_EXPR, TREE_TYPE (ftop), ftop, t);
+ expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+
+ /* Emit code to initialize GOFF, the offset from GTOP of the
+ next GPR argument. */
+ t = build2 (MODIFY_EXPR, TREE_TYPE (goff), goff,
+ build_int_cst (TREE_TYPE (goff), gpr_save_area_size));
+ expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+
+ /* Likewise emit code to initialize FOFF, the offset from FTOP
+ of the next FPR argument. */
+ t = build2 (MODIFY_EXPR, TREE_TYPE (foff), foff,
+ build_int_cst (TREE_TYPE (foff), fpr_save_area_size));
+ expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+ }
+ else
+ {
+ nextarg = plus_constant (nextarg, -cfun->machine->varargs_size);
+ std_expand_builtin_va_start (valist, nextarg);
+ }
+}
+
+/* Implement TARGET_GIMPLIFY_VA_ARG_EXPR. */
+
+static tree
+mips_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
+ gimple_seq *post_p)
+{
+ tree addr;
+ bool indirect_p;
+
+ indirect_p = pass_by_reference (NULL, TYPE_MODE (type), type, 0);
+ if (indirect_p)
+ type = build_pointer_type (type);
+
+ if (!EABI_FLOAT_VARARGS_P)
+ addr = std_gimplify_va_arg_expr (valist, type, pre_p, post_p);
+ else
+ {
+ tree f_ovfl, f_gtop, f_ftop, f_goff, f_foff;
+ tree ovfl, top, off, align;
+ HOST_WIDE_INT size, rsize, osize;
+ tree t, u;
+
+ f_ovfl = TYPE_FIELDS (va_list_type_node);
+ f_gtop = DECL_CHAIN (f_ovfl);
+ f_ftop = DECL_CHAIN (f_gtop);
+ f_goff = DECL_CHAIN (f_ftop);
+ f_foff = DECL_CHAIN (f_goff);
+
+ /* Let:
+
+ TOP be the top of the GPR or FPR save area;
+ OFF be the offset from TOP of the next register;
+ ADDR_RTX be the address of the argument;
+ SIZE be the number of bytes in the argument type;
+ RSIZE be the number of bytes used to store the argument
+ when it's in the register save area; and
+ OSIZE be the number of bytes used to store it when it's
+ in the stack overflow area.
+
+ The code we want is:
+
+ 1: off &= -rsize; // round down
+ 2: if (off != 0)
+ 3: {
+ 4: addr_rtx = top - off + (BYTES_BIG_ENDIAN ? RSIZE - SIZE : 0);
+ 5: off -= rsize;
+ 6: }
+ 7: else
+ 8: {
+ 9: ovfl = ((intptr_t) ovfl + osize - 1) & -osize;
+ 10: addr_rtx = ovfl + (BYTES_BIG_ENDIAN ? OSIZE - SIZE : 0);
+ 11: ovfl += osize;
+ 14: }
+
+ [1] and [9] can sometimes be optimized away. */
+
+ ovfl = build3 (COMPONENT_REF, TREE_TYPE (f_ovfl), valist, f_ovfl,
+ NULL_TREE);
+ size = int_size_in_bytes (type);
+
+ if (GET_MODE_CLASS (TYPE_MODE (type)) == MODE_FLOAT
+ && GET_MODE_SIZE (TYPE_MODE (type)) <= UNITS_PER_FPVALUE)
+ {
+ top = build3 (COMPONENT_REF, TREE_TYPE (f_ftop),
+ unshare_expr (valist), f_ftop, NULL_TREE);
+ off = build3 (COMPONENT_REF, TREE_TYPE (f_foff),
+ unshare_expr (valist), f_foff, NULL_TREE);
+
+ /* When va_start saves FPR arguments to the stack, each slot
+ takes up UNITS_PER_HWFPVALUE bytes, regardless of the
+ argument's precision. */
+ rsize = UNITS_PER_HWFPVALUE;
+
+ /* Overflow arguments are padded to UNITS_PER_WORD bytes
+ (= PARM_BOUNDARY bits). This can be different from RSIZE
+ in two cases:
+
+ (1) On 32-bit targets when TYPE is a structure such as:
+
+ struct s { float f; };
+
+ Such structures are passed in paired FPRs, so RSIZE
+ will be 8 bytes. However, the structure only takes
+ up 4 bytes of memory, so OSIZE will only be 4.
+
+ (2) In combinations such as -mgp64 -msingle-float
+ -fshort-double. Doubles passed in registers will then take
+ up 4 (UNITS_PER_HWFPVALUE) bytes, but those passed on the
+ stack take up UNITS_PER_WORD bytes. */
+ osize = MAX (GET_MODE_SIZE (TYPE_MODE (type)), UNITS_PER_WORD);
+ }
+ else
+ {
+ top = build3 (COMPONENT_REF, TREE_TYPE (f_gtop),
+ unshare_expr (valist), f_gtop, NULL_TREE);
+ off = build3 (COMPONENT_REF, TREE_TYPE (f_goff),
+ unshare_expr (valist), f_goff, NULL_TREE);
+ rsize = (size + UNITS_PER_WORD - 1) & -UNITS_PER_WORD;
+ if (rsize > UNITS_PER_WORD)
+ {
+ /* [1] Emit code for: off &= -rsize. */
+ t = build2 (BIT_AND_EXPR, TREE_TYPE (off), unshare_expr (off),
+ build_int_cst (TREE_TYPE (off), -rsize));
+ gimplify_assign (unshare_expr (off), t, pre_p);
+ }
+ osize = rsize;
+ }
+
+ /* [2] Emit code to branch if off == 0. */
+ t = build2 (NE_EXPR, boolean_type_node, off,
+ build_int_cst (TREE_TYPE (off), 0));
+ addr = build3 (COND_EXPR, ptr_type_node, t, NULL_TREE, NULL_TREE);
+
+ /* [5] Emit code for: off -= rsize. We do this as a form of
+ post-decrement not available to C. */
+ t = fold_convert (TREE_TYPE (off), build_int_cst (NULL_TREE, rsize));
+ t = build2 (POSTDECREMENT_EXPR, TREE_TYPE (off), off, t);
+
+ /* [4] Emit code for:
+ addr_rtx = top - off + (BYTES_BIG_ENDIAN ? RSIZE - SIZE : 0). */
+ t = fold_convert (sizetype, t);
+ t = fold_build1 (NEGATE_EXPR, sizetype, t);
+ t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (top), top, t);
+ if (BYTES_BIG_ENDIAN && rsize > size)
+ {
+ u = size_int (rsize - size);
+ t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (t), t, u);
+ }
+ COND_EXPR_THEN (addr) = t;
+
+ if (osize > UNITS_PER_WORD)
+ {
+ /* [9] Emit: ovfl = ((intptr_t) ovfl + osize - 1) & -osize. */
+ u = size_int (osize - 1);
+ t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (ovfl),
+ unshare_expr (ovfl), u);
+ t = fold_convert (sizetype, t);
+ u = size_int (-osize);
+ t = build2 (BIT_AND_EXPR, sizetype, t, u);
+ t = fold_convert (TREE_TYPE (ovfl), t);
+ align = build2 (MODIFY_EXPR, TREE_TYPE (ovfl),
+ unshare_expr (ovfl), t);
+ }
+ else
+ align = NULL;
+
+ /* [10, 11] Emit code for:
+ addr_rtx = ovfl + (BYTES_BIG_ENDIAN ? OSIZE - SIZE : 0)
+ ovfl += osize. */
+ u = fold_convert (TREE_TYPE (ovfl), build_int_cst (NULL_TREE, osize));
+ t = build2 (POSTINCREMENT_EXPR, TREE_TYPE (ovfl), ovfl, u);
+ if (BYTES_BIG_ENDIAN && osize > size)
+ {
+ u = size_int (osize - size);
+ t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (t), t, u);
+ }
+
+ /* String [9] and [10, 11] together. */
+ if (align)
+ t = build2 (COMPOUND_EXPR, TREE_TYPE (t), align, t);
+ COND_EXPR_ELSE (addr) = t;
+
+ addr = fold_convert (build_pointer_type (type), addr);
+ addr = build_va_arg_indirect_ref (addr);
+ }
+
+ if (indirect_p)
+ addr = build_va_arg_indirect_ref (addr);
+
+ return addr;
+}
+
+/* Start a definition of function NAME. MIPS16_P indicates whether the
+ function contains MIPS16 code. */
+
+static void
+mips_start_function_definition (const char *name, bool mips16_p)
+{
+ if (mips16_p)
+ fprintf (asm_out_file, "\t.set\tmips16\n");
+ else
+ fprintf (asm_out_file, "\t.set\tnomips16\n");
+
+ if (!flag_inhibit_size_directive)
+ {
+ fputs ("\t.ent\t", asm_out_file);
+ assemble_name (asm_out_file, name);
+ fputs ("\n", asm_out_file);
+ }
+
+ ASM_OUTPUT_TYPE_DIRECTIVE (asm_out_file, name, "function");
+
+ /* Start the definition proper. */
+ assemble_name (asm_out_file, name);
+ fputs (":\n", asm_out_file);
+}
+
+/* End a function definition started by mips_start_function_definition. */
+
+static void
+mips_end_function_definition (const char *name)
+{
+ if (!flag_inhibit_size_directive)
+ {
+ fputs ("\t.end\t", asm_out_file);
+ assemble_name (asm_out_file, name);
+ fputs ("\n", asm_out_file);
+ }
+}
+
+/* Return true if calls to X can use R_MIPS_CALL* relocations. */
+
+static bool
+mips_ok_for_lazy_binding_p (rtx x)
+{
+ return (TARGET_USE_GOT
+ && GET_CODE (x) == SYMBOL_REF
+ && !SYMBOL_REF_BIND_NOW_P (x)
+ && !mips_symbol_binds_local_p (x));
+}
+
+/* Load function address ADDR into register DEST. TYPE is as for
+ mips_expand_call. Return true if we used an explicit lazy-binding
+ sequence. */
+
+static bool
+mips_load_call_address (enum mips_call_type type, rtx dest, rtx addr)
+{
+ /* If we're generating PIC, and this call is to a global function,
+ try to allow its address to be resolved lazily. This isn't
+ possible for sibcalls when $gp is call-saved because the value
+ of $gp on entry to the stub would be our caller's gp, not ours. */
+ if (TARGET_EXPLICIT_RELOCS
+ && !(type == MIPS_CALL_SIBCALL && TARGET_CALL_SAVED_GP)
+ && mips_ok_for_lazy_binding_p (addr))
+ {
+ addr = mips_got_load (dest, addr, SYMBOL_GOTOFF_CALL);
+ emit_insn (gen_rtx_SET (VOIDmode, dest, addr));
+ return true;
+ }
+ else
+ {
+ mips_emit_move (dest, addr);
+ return false;
+ }
+}
+
+/* Each locally-defined hard-float MIPS16 function has a local symbol
+ associated with it. This hash table maps the function symbol (FUNC)
+ to the local symbol (LOCAL). */
+struct GTY(()) mips16_local_alias {
+ rtx func;
+ rtx local;
+};
+static GTY ((param_is (struct mips16_local_alias))) htab_t mips16_local_aliases;
+
+/* Hash table callbacks for mips16_local_aliases. */
+
+static hashval_t
+mips16_local_aliases_hash (const void *entry)
+{
+ const struct mips16_local_alias *alias;
+
+ alias = (const struct mips16_local_alias *) entry;
+ return htab_hash_string (XSTR (alias->func, 0));
+}
+
+static int
+mips16_local_aliases_eq (const void *entry1, const void *entry2)
+{
+ const struct mips16_local_alias *alias1, *alias2;
+
+ alias1 = (const struct mips16_local_alias *) entry1;
+ alias2 = (const struct mips16_local_alias *) entry2;
+ return rtx_equal_p (alias1->func, alias2->func);
+}
+
+/* FUNC is the symbol for a locally-defined hard-float MIPS16 function.
+ Return a local alias for it, creating a new one if necessary. */
+
+static rtx
+mips16_local_alias (rtx func)
+{
+ struct mips16_local_alias *alias, tmp_alias;
+ void **slot;
+
+ /* Create the hash table if this is the first call. */
+ if (mips16_local_aliases == NULL)
+ mips16_local_aliases = htab_create_ggc (37, mips16_local_aliases_hash,
+ mips16_local_aliases_eq, NULL);
+
+ /* Look up the function symbol, creating a new entry if need be. */
+ tmp_alias.func = func;
+ slot = htab_find_slot (mips16_local_aliases, &tmp_alias, INSERT);
+ gcc_assert (slot != NULL);
+
+ alias = (struct mips16_local_alias *) *slot;
+ if (alias == NULL)
+ {
+ const char *func_name, *local_name;
+ rtx local;
+
+ /* Create a new SYMBOL_REF for the local symbol. The choice of
+ __fn_local_* is based on the __fn_stub_* names that we've
+ traditionally used for the non-MIPS16 stub. */
+ func_name = targetm.strip_name_encoding (XSTR (func, 0));
+ local_name = ACONCAT (("__fn_local_", func_name, NULL));
+ local = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (local_name));
+ SYMBOL_REF_FLAGS (local) = SYMBOL_REF_FLAGS (func) | SYMBOL_FLAG_LOCAL;
+
+ /* Create a new structure to represent the mapping. */
+ alias = ggc_alloc_mips16_local_alias ();
+ alias->func = func;
+ alias->local = local;
+ *slot = alias;
+ }
+ return alias->local;
+}
+
+/* A chained list of functions for which mips16_build_call_stub has already
+ generated a stub. NAME is the name of the function and FP_RET_P is true
+ if the function returns a value in floating-point registers. */
+struct mips16_stub {
+ struct mips16_stub *next;
+ char *name;
+ bool fp_ret_p;
+};
+static struct mips16_stub *mips16_stubs;
+
+/* Return a SYMBOL_REF for a MIPS16 function called NAME. */
+
+static rtx
+mips16_stub_function (const char *name)
+{
+ rtx x;
+
+ x = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (name));
+ SYMBOL_REF_FLAGS (x) |= (SYMBOL_FLAG_EXTERNAL | SYMBOL_FLAG_FUNCTION);
+ return x;
+}
+
+/* Return the two-character string that identifies floating-point
+ return mode MODE in the name of a MIPS16 function stub. */
+
+static const char *
+mips16_call_stub_mode_suffix (enum machine_mode mode)
+{
+ if (mode == SFmode)
+ return "sf";
+ else if (mode == DFmode)
+ return "df";
+ else if (mode == SCmode)
+ return "sc";
+ else if (mode == DCmode)
+ return "dc";
+ else if (mode == V2SFmode)
+ return "df";
+ else
+ gcc_unreachable ();
+}
+
+/* Write instructions to move a 32-bit value between general register
+ GPREG and floating-point register FPREG. DIRECTION is 't' to move
+ from GPREG to FPREG and 'f' to move in the opposite direction. */
+
+static void
+mips_output_32bit_xfer (char direction, unsigned int gpreg, unsigned int fpreg)
+{
+ fprintf (asm_out_file, "\tm%cc1\t%s,%s\n", direction,
+ reg_names[gpreg], reg_names[fpreg]);
+}
+
+/* Likewise for 64-bit values. */
+
+static void
+mips_output_64bit_xfer (char direction, unsigned int gpreg, unsigned int fpreg)
+{
+ if (TARGET_64BIT)
+ fprintf (asm_out_file, "\tdm%cc1\t%s,%s\n", direction,
+ reg_names[gpreg], reg_names[fpreg]);
+ else if (TARGET_FLOAT64)
+ {
+ fprintf (asm_out_file, "\tm%cc1\t%s,%s\n", direction,
+ reg_names[gpreg + TARGET_BIG_ENDIAN], reg_names[fpreg]);
+ fprintf (asm_out_file, "\tm%chc1\t%s,%s\n", direction,
+ reg_names[gpreg + TARGET_LITTLE_ENDIAN], reg_names[fpreg]);
+ }
+ else
+ {
+ /* Move the least-significant word. */
+ fprintf (asm_out_file, "\tm%cc1\t%s,%s\n", direction,
+ reg_names[gpreg + TARGET_BIG_ENDIAN], reg_names[fpreg]);
+ /* ...then the most significant word. */
+ fprintf (asm_out_file, "\tm%cc1\t%s,%s\n", direction,
+ reg_names[gpreg + TARGET_LITTLE_ENDIAN], reg_names[fpreg + 1]);
+ }
+}
+
+/* Write out code to move floating-point arguments into or out of
+ general registers. FP_CODE is the code describing which arguments
+ are present (see the comment above the definition of CUMULATIVE_ARGS
+ in mips.h). DIRECTION is as for mips_output_32bit_xfer. */
+
+static void
+mips_output_args_xfer (int fp_code, char direction)
+{
+ unsigned int gparg, fparg, f;
+ CUMULATIVE_ARGS cum;
+
+ /* This code only works for o32 and o64. */
+ gcc_assert (TARGET_OLDABI);
+
+ mips_init_cumulative_args (&cum, NULL);
+
+ for (f = (unsigned int) fp_code; f != 0; f >>= 2)
+ {
+ enum machine_mode mode;
+ struct mips_arg_info info;
+
+ if ((f & 3) == 1)
+ mode = SFmode;
+ else if ((f & 3) == 2)
+ mode = DFmode;
+ else
+ gcc_unreachable ();
+
+ mips_get_arg_info (&info, &cum, mode, NULL, true);
+ gparg = mips_arg_regno (&info, false);
+ fparg = mips_arg_regno (&info, true);
+
+ if (mode == SFmode)
+ mips_output_32bit_xfer (direction, gparg, fparg);
+ else
+ mips_output_64bit_xfer (direction, gparg, fparg);
+
+ mips_function_arg_advance (&cum, mode, NULL, true);
+ }
+}
+
+/* Write a MIPS16 stub for the current function. This stub is used
+ for functions which take arguments in the floating-point registers.
+ It is normal-mode code that moves the floating-point arguments
+ into the general registers and then jumps to the MIPS16 code. */
+
+static void
+mips16_build_function_stub (void)
+{
+ const char *fnname, *alias_name, *separator;
+ char *secname, *stubname;
+ tree stubdecl;
+ unsigned int f;
+ rtx symbol, alias;
+
+ /* Create the name of the stub, and its unique section. */
+ symbol = XEXP (DECL_RTL (current_function_decl), 0);
+ alias = mips16_local_alias (symbol);
+
+ fnname = targetm.strip_name_encoding (XSTR (symbol, 0));
+ alias_name = targetm.strip_name_encoding (XSTR (alias, 0));
+ secname = ACONCAT ((".mips16.fn.", fnname, NULL));
+ stubname = ACONCAT (("__fn_stub_", fnname, NULL));
+
+ /* Build a decl for the stub. */
+ stubdecl = build_decl (BUILTINS_LOCATION,
+ FUNCTION_DECL, get_identifier (stubname),
+ build_function_type (void_type_node, NULL_TREE));
+ DECL_SECTION_NAME (stubdecl) = build_string (strlen (secname), secname);
+ DECL_RESULT (stubdecl) = build_decl (BUILTINS_LOCATION,
+ RESULT_DECL, NULL_TREE, void_type_node);
+
+ /* Output a comment. */
+ fprintf (asm_out_file, "\t# Stub function for %s (",
+ current_function_name ());
+ separator = "";
+ for (f = (unsigned int) crtl->args.info.fp_code; f != 0; f >>= 2)
+ {
+ fprintf (asm_out_file, "%s%s", separator,
+ (f & 3) == 1 ? "float" : "double");
+ separator = ", ";
+ }
+ fprintf (asm_out_file, ")\n");
+
+ /* Start the function definition. */
+ assemble_start_function (stubdecl, stubname);
+ mips_start_function_definition (stubname, false);
+
+ /* If generating pic2 code, either set up the global pointer or
+ switch to pic0. */
+ if (TARGET_ABICALLS_PIC2)
+ {
+ if (TARGET_ABSOLUTE_ABICALLS)
+ fprintf (asm_out_file, "\t.option\tpic0\n");
+ else
+ {
+ output_asm_insn ("%(.cpload\t%^%)", NULL);
+ /* Emit an R_MIPS_NONE relocation to tell the linker what the
+ target function is. Use a local GOT access when loading the
+ symbol, to cut down on the number of unnecessary GOT entries
+ for stubs that aren't needed. */
+ output_asm_insn (".reloc\t0,R_MIPS_NONE,%0", &symbol);
+ symbol = alias;
+ }
+ }
+
+ /* Load the address of the MIPS16 function into $25. Do this first so
+ that targets with coprocessor interlocks can use an MFC1 to fill the
+ delay slot. */
+ output_asm_insn ("la\t%^,%0", &symbol);
+
+ /* Move the arguments from floating-point registers to general registers. */
+ mips_output_args_xfer (crtl->args.info.fp_code, 'f');
+
+ /* Jump to the MIPS16 function. */
+ output_asm_insn ("jr\t%^", NULL);
+
+ if (TARGET_ABICALLS_PIC2 && TARGET_ABSOLUTE_ABICALLS)
+ fprintf (asm_out_file, "\t.option\tpic2\n");
+
+ mips_end_function_definition (stubname);
+
+ /* If the linker needs to create a dynamic symbol for the target
+ function, it will associate the symbol with the stub (which,
+ unlike the target function, follows the proper calling conventions).
+ It is therefore useful to have a local alias for the target function,
+ so that it can still be identified as MIPS16 code. As an optimization,
+ this symbol can also be used for indirect MIPS16 references from
+ within this file. */
+ ASM_OUTPUT_DEF (asm_out_file, alias_name, fnname);
+
+ switch_to_section (function_section (current_function_decl));
+}
+
+/* The current function is a MIPS16 function that returns a value in an FPR.
+ Copy the return value from its soft-float to its hard-float location.
+ libgcc2 has special non-MIPS16 helper functions for each case. */
+
+static void
+mips16_copy_fpr_return_value (void)
+{
+ rtx fn, insn, retval;
+ tree return_type;
+ enum machine_mode return_mode;
+ const char *name;
+
+ return_type = DECL_RESULT (current_function_decl);
+ return_mode = DECL_MODE (return_type);
+
+ name = ACONCAT (("__mips16_ret_",
+ mips16_call_stub_mode_suffix (return_mode),
+ NULL));
+ fn = mips16_stub_function (name);
+
+ /* The function takes arguments in $2 (and possibly $3), so calls
+ to it cannot be lazily bound. */
+ SYMBOL_REF_FLAGS (fn) |= SYMBOL_FLAG_BIND_NOW;
+
+ /* Model the call as something that takes the GPR return value as
+ argument and returns an "updated" value. */
+ retval = gen_rtx_REG (return_mode, GP_RETURN);
+ insn = mips_expand_call (MIPS_CALL_EPILOGUE, retval, fn,
+ const0_rtx, NULL_RTX, false);
+ use_reg (&CALL_INSN_FUNCTION_USAGE (insn), retval);
+}
+
+/* Consider building a stub for a MIPS16 call to function *FN_PTR.
+ RETVAL is the location of the return value, or null if this is
+ a "call" rather than a "call_value". ARGS_SIZE is the size of the
+ arguments and FP_CODE is the code built by mips_function_arg;
+ see the comment before the fp_code field in CUMULATIVE_ARGS for details.
+
+ There are three alternatives:
+
+ - If a stub was needed, emit the call and return the call insn itself.
+
+ - If we can avoid using a stub by redirecting the call, set *FN_PTR
+ to the new target and return null.
+
+ - If *FN_PTR doesn't need a stub, return null and leave *FN_PTR
+ unmodified.
+
+ A stub is needed for calls to functions that, in normal mode,
+ receive arguments in FPRs or return values in FPRs. The stub
+ copies the arguments from their soft-float positions to their
+ hard-float positions, calls the real function, then copies the
+ return value from its hard-float position to its soft-float
+ position.
+
+ We can emit a JAL to *FN_PTR even when *FN_PTR might need a stub.
+ If *FN_PTR turns out to be to a non-MIPS16 function, the linker
+ automatically redirects the JAL to the stub, otherwise the JAL
+ continues to call FN directly. */
+
+static rtx
+mips16_build_call_stub (rtx retval, rtx *fn_ptr, rtx args_size, int fp_code)
+{
+ const char *fnname;
+ bool fp_ret_p;
+ struct mips16_stub *l;
+ rtx insn, fn;
+
+ /* We don't need to do anything if we aren't in MIPS16 mode, or if
+ we were invoked with the -msoft-float option. */
+ if (!TARGET_MIPS16 || TARGET_SOFT_FLOAT_ABI)
+ return NULL_RTX;
+
+ /* Figure out whether the value might come back in a floating-point
+ register. */
+ fp_ret_p = retval && mips_return_mode_in_fpr_p (GET_MODE (retval));
+
+ /* We don't need to do anything if there were no floating-point
+ arguments and the value will not be returned in a floating-point
+ register. */
+ if (fp_code == 0 && !fp_ret_p)
+ return NULL_RTX;
+
+ /* We don't need to do anything if this is a call to a special
+ MIPS16 support function. */
+ fn = *fn_ptr;
+ if (mips16_stub_function_p (fn))
+ return NULL_RTX;
+
+ /* This code will only work for o32 and o64 abis. The other ABI's
+ require more sophisticated support. */
+ gcc_assert (TARGET_OLDABI);
+
+ /* If we're calling via a function pointer, use one of the magic
+ libgcc.a stubs provided for each (FP_CODE, FP_RET_P) combination.
+ Each stub expects the function address to arrive in register $2. */
+ if (GET_CODE (fn) != SYMBOL_REF
+ || !call_insn_operand (fn, VOIDmode))
+ {
+ char buf[30];
+ rtx stub_fn, insn, addr;
+ bool lazy_p;
+
+ /* If this is a locally-defined and locally-binding function,
+ avoid the stub by calling the local alias directly. */
+ if (mips16_local_function_p (fn))
+ {
+ *fn_ptr = mips16_local_alias (fn);
+ return NULL_RTX;
+ }
+
+ /* Create a SYMBOL_REF for the libgcc.a function. */
+ if (fp_ret_p)
+ sprintf (buf, "__mips16_call_stub_%s_%d",
+ mips16_call_stub_mode_suffix (GET_MODE (retval)),
+ fp_code);
+ else
+ sprintf (buf, "__mips16_call_stub_%d", fp_code);
+ stub_fn = mips16_stub_function (buf);
+
+ /* The function uses $2 as an argument, so calls to it
+ cannot be lazily bound. */
+ SYMBOL_REF_FLAGS (stub_fn) |= SYMBOL_FLAG_BIND_NOW;
+
+ /* Load the target function into $2. */
+ addr = gen_rtx_REG (Pmode, GP_REG_FIRST + 2);
+ lazy_p = mips_load_call_address (MIPS_CALL_NORMAL, addr, fn);
+
+ /* Emit the call. */
+ insn = mips_expand_call (MIPS_CALL_NORMAL, retval, stub_fn,
+ args_size, NULL_RTX, lazy_p);
+
+ /* Tell GCC that this call does indeed use the value of $2. */
+ use_reg (&CALL_INSN_FUNCTION_USAGE (insn), addr);
+
+ /* If we are handling a floating-point return value, we need to
+ save $18 in the function prologue. Putting a note on the
+ call will mean that df_regs_ever_live_p ($18) will be true if the
+ call is not eliminated, and we can check that in the prologue
+ code. */
+ if (fp_ret_p)
+ CALL_INSN_FUNCTION_USAGE (insn) =
+ gen_rtx_EXPR_LIST (VOIDmode,
+ gen_rtx_CLOBBER (VOIDmode,
+ gen_rtx_REG (word_mode, 18)),
+ CALL_INSN_FUNCTION_USAGE (insn));
+
+ return insn;
+ }
+
+ /* We know the function we are going to call. If we have already
+ built a stub, we don't need to do anything further. */
+ fnname = targetm.strip_name_encoding (XSTR (fn, 0));
+ for (l = mips16_stubs; l != NULL; l = l->next)
+ if (strcmp (l->name, fnname) == 0)
+ break;
+
+ if (l == NULL)
+ {
+ const char *separator;
+ char *secname, *stubname;
+ tree stubid, stubdecl;
+ unsigned int f;
+
+ /* If the function does not return in FPRs, the special stub
+ section is named
+ .mips16.call.FNNAME
+
+ If the function does return in FPRs, the stub section is named
+ .mips16.call.fp.FNNAME
+
+ Build a decl for the stub. */
+ secname = ACONCAT ((".mips16.call.", fp_ret_p ? "fp." : "",
+ fnname, NULL));
+ stubname = ACONCAT (("__call_stub_", fp_ret_p ? "fp_" : "",
+ fnname, NULL));
+ stubid = get_identifier (stubname);
+ stubdecl = build_decl (BUILTINS_LOCATION,
+ FUNCTION_DECL, stubid,
+ build_function_type (void_type_node, NULL_TREE));
+ DECL_SECTION_NAME (stubdecl) = build_string (strlen (secname), secname);
+ DECL_RESULT (stubdecl) = build_decl (BUILTINS_LOCATION,
+ RESULT_DECL, NULL_TREE,
+ void_type_node);
+
+ /* Output a comment. */
+ fprintf (asm_out_file, "\t# Stub function to call %s%s (",
+ (fp_ret_p
+ ? (GET_MODE (retval) == SFmode ? "float " : "double ")
+ : ""),
+ fnname);
+ separator = "";
+ for (f = (unsigned int) fp_code; f != 0; f >>= 2)
+ {
+ fprintf (asm_out_file, "%s%s", separator,
+ (f & 3) == 1 ? "float" : "double");
+ separator = ", ";
+ }
+ fprintf (asm_out_file, ")\n");
+
+ /* Start the function definition. */
+ assemble_start_function (stubdecl, stubname);
+ mips_start_function_definition (stubname, false);
+
+ if (!fp_ret_p)
+ {
+ /* Load the address of the MIPS16 function into $25. Do this
+ first so that targets with coprocessor interlocks can use
+ an MFC1 to fill the delay slot. */
+ if (TARGET_EXPLICIT_RELOCS)
+ {
+ output_asm_insn ("lui\t%^,%%hi(%0)", &fn);
+ output_asm_insn ("addiu\t%^,%^,%%lo(%0)", &fn);
+ }
+ else
+ output_asm_insn ("la\t%^,%0", &fn);
+ }
+
+ /* Move the arguments from general registers to floating-point
+ registers. */
+ mips_output_args_xfer (fp_code, 't');
+
+ if (!fp_ret_p)
+ {
+ /* Jump to the previously-loaded address. */
+ output_asm_insn ("jr\t%^", NULL);
+ }
+ else
+ {
+ /* Save the return address in $18 and call the non-MIPS16 function.
+ The stub's caller knows that $18 might be clobbered, even though
+ $18 is usually a call-saved register. */
+ fprintf (asm_out_file, "\tmove\t%s,%s\n",
+ reg_names[GP_REG_FIRST + 18], reg_names[RETURN_ADDR_REGNUM]);
+ output_asm_insn (MIPS_CALL ("jal", &fn, 0, -1), &fn);
+
+ /* Move the result from floating-point registers to
+ general registers. */
+ switch (GET_MODE (retval))
+ {
+ case SCmode:
+ mips_output_32bit_xfer ('f', GP_RETURN + TARGET_BIG_ENDIAN,
+ TARGET_BIG_ENDIAN
+ ? FP_REG_FIRST + MAX_FPRS_PER_FMT
+ : FP_REG_FIRST);
+ mips_output_32bit_xfer ('f', GP_RETURN + TARGET_LITTLE_ENDIAN,
+ TARGET_LITTLE_ENDIAN
+ ? FP_REG_FIRST + MAX_FPRS_PER_FMT
+ : FP_REG_FIRST);
+ if (GET_MODE (retval) == SCmode && TARGET_64BIT)
+ {
+ /* On 64-bit targets, complex floats are returned in
+ a single GPR, such that "sd" on a suitably-aligned
+ target would store the value correctly. */
+ fprintf (asm_out_file, "\tdsll\t%s,%s,32\n",
+ reg_names[GP_RETURN + TARGET_BIG_ENDIAN],
+ reg_names[GP_RETURN + TARGET_BIG_ENDIAN]);
+ fprintf (asm_out_file, "\tdsll\t%s,%s,32\n",
+ reg_names[GP_RETURN + TARGET_LITTLE_ENDIAN],
+ reg_names[GP_RETURN + TARGET_LITTLE_ENDIAN]);
+ fprintf (asm_out_file, "\tdsrl\t%s,%s,32\n",
+ reg_names[GP_RETURN + TARGET_BIG_ENDIAN],
+ reg_names[GP_RETURN + TARGET_BIG_ENDIAN]);
+ fprintf (asm_out_file, "\tor\t%s,%s,%s\n",
+ reg_names[GP_RETURN],
+ reg_names[GP_RETURN],
+ reg_names[GP_RETURN + 1]);
+ }
+ break;
+
+ case SFmode:
+ mips_output_32bit_xfer ('f', GP_RETURN, FP_REG_FIRST);
+ break;
+
+ case DCmode:
+ mips_output_64bit_xfer ('f', GP_RETURN + (8 / UNITS_PER_WORD),
+ FP_REG_FIRST + MAX_FPRS_PER_FMT);
+ /* Fall though. */
+ case DFmode:
+ case V2SFmode:
+ mips_output_64bit_xfer ('f', GP_RETURN, FP_REG_FIRST);
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+ fprintf (asm_out_file, "\tjr\t%s\n", reg_names[GP_REG_FIRST + 18]);
+ }
+
+#ifdef ASM_DECLARE_FUNCTION_SIZE
+ ASM_DECLARE_FUNCTION_SIZE (asm_out_file, stubname, stubdecl);
+#endif
+
+ mips_end_function_definition (stubname);
+
+ /* Record this stub. */
+ l = XNEW (struct mips16_stub);
+ l->name = xstrdup (fnname);
+ l->fp_ret_p = fp_ret_p;
+ l->next = mips16_stubs;
+ mips16_stubs = l;
+ }
+
+ /* If we expect a floating-point return value, but we've built a
+ stub which does not expect one, then we're in trouble. We can't
+ use the existing stub, because it won't handle the floating-point
+ value. We can't build a new stub, because the linker won't know
+ which stub to use for the various calls in this object file.
+ Fortunately, this case is illegal, since it means that a function
+ was declared in two different ways in a single compilation. */
+ if (fp_ret_p && !l->fp_ret_p)
+ error ("cannot handle inconsistent calls to %qs", fnname);
+
+ if (retval == NULL_RTX)
+ insn = gen_call_internal_direct (fn, args_size);
+ else
+ insn = gen_call_value_internal_direct (retval, fn, args_size);
+ insn = mips_emit_call_insn (insn, fn, fn, false);
+
+ /* If we are calling a stub which handles a floating-point return
+ value, we need to arrange to save $18 in the prologue. We do this
+ by marking the function call as using the register. The prologue
+ will later see that it is used, and emit code to save it. */
+ if (fp_ret_p)
+ CALL_INSN_FUNCTION_USAGE (insn) =
+ gen_rtx_EXPR_LIST (VOIDmode,
+ gen_rtx_CLOBBER (VOIDmode,
+ gen_rtx_REG (word_mode, 18)),
+ CALL_INSN_FUNCTION_USAGE (insn));
+
+ return insn;
+}
+
+/* Expand a call of type TYPE. RESULT is where the result will go (null
+ for "call"s and "sibcall"s), ADDR is the address of the function,
+ ARGS_SIZE is the size of the arguments and AUX is the value passed
+ to us by mips_function_arg. LAZY_P is true if this call already
+ involves a lazily-bound function address (such as when calling
+ functions through a MIPS16 hard-float stub).
+
+ Return the call itself. */
+
+rtx
+mips_expand_call (enum mips_call_type type, rtx result, rtx addr,
+ rtx args_size, rtx aux, bool lazy_p)
+{
+ rtx orig_addr, pattern, insn;
+ int fp_code;
+
+ fp_code = aux == 0 ? 0 : (int) GET_MODE (aux);
+ insn = mips16_build_call_stub (result, &addr, args_size, fp_code);
+ if (insn)
+ {
+ gcc_assert (!lazy_p && type == MIPS_CALL_NORMAL);
+ return insn;
+ }
+ ;
+ orig_addr = addr;
+ if (!call_insn_operand (addr, VOIDmode))
+ {
+ if (type == MIPS_CALL_EPILOGUE)
+ addr = MIPS_EPILOGUE_TEMP (Pmode);
+ else
+ addr = gen_reg_rtx (Pmode);
+ lazy_p |= mips_load_call_address (type, addr, orig_addr);
+ }
+
+ if (result == 0)
+ {
+ rtx (*fn) (rtx, rtx);
+
+ if (type == MIPS_CALL_SIBCALL)
+ fn = gen_sibcall_internal;
+ else
+ fn = gen_call_internal;
+
+ pattern = fn (addr, args_size);
+ }
+ else if (GET_CODE (result) == PARALLEL && XVECLEN (result, 0) == 2)
+ {
+ /* Handle return values created by mips_return_fpr_pair. */
+ rtx (*fn) (rtx, rtx, rtx, rtx);
+ rtx reg1, reg2;
+
+ if (type == MIPS_CALL_SIBCALL)
+ fn = gen_sibcall_value_multiple_internal;
+ else
+ fn = gen_call_value_multiple_internal;
+
+ reg1 = XEXP (XVECEXP (result, 0, 0), 0);
+ reg2 = XEXP (XVECEXP (result, 0, 1), 0);
+ pattern = fn (reg1, addr, args_size, reg2);
+ }
+ else
+ {
+ rtx (*fn) (rtx, rtx, rtx);
+
+ if (type == MIPS_CALL_SIBCALL)
+ fn = gen_sibcall_value_internal;
+ else
+ fn = gen_call_value_internal;
+
+ /* Handle return values created by mips_return_fpr_single. */
+ if (GET_CODE (result) == PARALLEL && XVECLEN (result, 0) == 1)
+ result = XEXP (XVECEXP (result, 0, 0), 0);
+ pattern = fn (result, addr, args_size);
+ }
+
+ return mips_emit_call_insn (pattern, orig_addr, addr, lazy_p);
+}
+
+/* Split call instruction INSN into a $gp-clobbering call and
+ (where necessary) an instruction to restore $gp from its save slot.
+ CALL_PATTERN is the pattern of the new call. */
+
+void
+mips_split_call (rtx insn, rtx call_pattern)
+{
+ emit_call_insn (call_pattern);
+ if (!find_reg_note (insn, REG_NORETURN, 0))
+ /* Pick a temporary register that is suitable for both MIPS16 and
+ non-MIPS16 code. $4 and $5 are used for returning complex double
+ values in soft-float code, so $6 is the first suitable candidate. */
+ mips_restore_gp_from_cprestore_slot (gen_rtx_REG (Pmode, GP_ARG_FIRST + 2));
+}
+
+/* Implement TARGET_FUNCTION_OK_FOR_SIBCALL. */
+
+static bool
+mips_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED)
+{
+ if (!TARGET_SIBCALLS)
+ return false;
+
+ /* Interrupt handlers need special epilogue code and therefore can't
+ use sibcalls. */
+ if (mips_interrupt_type_p (TREE_TYPE (current_function_decl)))
+ return false;
+
+ /* We can't do a sibcall if the called function is a MIPS16 function
+ because there is no direct "jx" instruction equivalent to "jalx" to
+ switch the ISA mode. We only care about cases where the sibling
+ and normal calls would both be direct. */
+ if (decl
+ && mips_use_mips16_mode_p (decl)
+ && const_call_insn_operand (XEXP (DECL_RTL (decl), 0), VOIDmode))
+ return false;
+
+ /* When -minterlink-mips16 is in effect, assume that non-locally-binding
+ functions could be MIPS16 ones unless an attribute explicitly tells
+ us otherwise. */
+ if (TARGET_INTERLINK_MIPS16
+ && decl
+ && (DECL_EXTERNAL (decl) || !targetm.binds_local_p (decl))
+ && !mips_nomips16_decl_p (decl)
+ && const_call_insn_operand (XEXP (DECL_RTL (decl), 0), VOIDmode))
+ return false;
+
+ /* Otherwise OK. */
+ return true;
+}
+
+/* Emit code to move general operand SRC into condition-code
+ register DEST given that SCRATCH is a scratch TFmode FPR.
+ The sequence is:
+
+ FP1 = SRC
+ FP2 = 0.0f
+ DEST = FP2 < FP1
+
+ where FP1 and FP2 are single-precision FPRs taken from SCRATCH. */
+
+void
+mips_expand_fcc_reload (rtx dest, rtx src, rtx scratch)
+{
+ rtx fp1, fp2;
+
+ /* Change the source to SFmode. */
+ if (MEM_P (src))
+ src = adjust_address (src, SFmode, 0);
+ else if (REG_P (src) || GET_CODE (src) == SUBREG)
+ src = gen_rtx_REG (SFmode, true_regnum (src));
+
+ fp1 = gen_rtx_REG (SFmode, REGNO (scratch));
+ fp2 = gen_rtx_REG (SFmode, REGNO (scratch) + MAX_FPRS_PER_FMT);
+
+ mips_emit_move (copy_rtx (fp1), src);
+ mips_emit_move (copy_rtx (fp2), CONST0_RTX (SFmode));
+ emit_insn (gen_slt_sf (dest, fp2, fp1));
+}
+
+/* Emit straight-line code to move LENGTH bytes from SRC to DEST.
+ Assume that the areas do not overlap. */
+
+static void
+mips_block_move_straight (rtx dest, rtx src, HOST_WIDE_INT length)
+{
+ HOST_WIDE_INT offset, delta;
+ unsigned HOST_WIDE_INT bits;
+ int i;
+ enum machine_mode mode;
+ rtx *regs;
+
+ /* Work out how many bits to move at a time. If both operands have
+ half-word alignment, it is usually better to move in half words.
+ For instance, lh/lh/sh/sh is usually better than lwl/lwr/swl/swr
+ and lw/lw/sw/sw is usually better than ldl/ldr/sdl/sdr.
+ Otherwise move word-sized chunks. */
+ if (MEM_ALIGN (src) == BITS_PER_WORD / 2
+ && MEM_ALIGN (dest) == BITS_PER_WORD / 2)
+ bits = BITS_PER_WORD / 2;
+ else
+ bits = BITS_PER_WORD;
+
+ mode = mode_for_size (bits, MODE_INT, 0);
+ delta = bits / BITS_PER_UNIT;
+
+ /* Allocate a buffer for the temporary registers. */
+ regs = XALLOCAVEC (rtx, length / delta);
+
+ /* Load as many BITS-sized chunks as possible. Use a normal load if
+ the source has enough alignment, otherwise use left/right pairs. */
+ for (offset = 0, i = 0; offset + delta <= length; offset += delta, i++)
+ {
+ regs[i] = gen_reg_rtx (mode);
+ if (MEM_ALIGN (src) >= bits)
+ mips_emit_move (regs[i], adjust_address (src, mode, offset));
+ else
+ {
+ rtx part = adjust_address (src, BLKmode, offset);
+ if (!mips_expand_ext_as_unaligned_load (regs[i], part, bits, 0))
+ gcc_unreachable ();
+ }
+ }
+
+ /* Copy the chunks to the destination. */
+ for (offset = 0, i = 0; offset + delta <= length; offset += delta, i++)
+ if (MEM_ALIGN (dest) >= bits)
+ mips_emit_move (adjust_address (dest, mode, offset), regs[i]);
+ else
+ {
+ rtx part = adjust_address (dest, BLKmode, offset);
+ if (!mips_expand_ins_as_unaligned_store (part, regs[i], bits, 0))
+ gcc_unreachable ();
+ }
+
+ /* Mop up any left-over bytes. */
+ if (offset < length)
+ {
+ src = adjust_address (src, BLKmode, offset);
+ dest = adjust_address (dest, BLKmode, offset);
+ move_by_pieces (dest, src, length - offset,
+ MIN (MEM_ALIGN (src), MEM_ALIGN (dest)), 0);
+ }
+}
+
+/* Helper function for doing a loop-based block operation on memory
+ reference MEM. Each iteration of the loop will operate on LENGTH
+ bytes of MEM.
+
+ Create a new base register for use within the loop and point it to
+ the start of MEM. Create a new memory reference that uses this
+ register. Store them in *LOOP_REG and *LOOP_MEM respectively. */
+
+static void
+mips_adjust_block_mem (rtx mem, HOST_WIDE_INT length,
+ rtx *loop_reg, rtx *loop_mem)
+{
+ *loop_reg = copy_addr_to_reg (XEXP (mem, 0));
+
+ /* Although the new mem does not refer to a known location,
+ it does keep up to LENGTH bytes of alignment. */
+ *loop_mem = change_address (mem, BLKmode, *loop_reg);
+ set_mem_align (*loop_mem, MIN (MEM_ALIGN (mem), length * BITS_PER_UNIT));
+}
+
+/* Move LENGTH bytes from SRC to DEST using a loop that moves BYTES_PER_ITER
+ bytes at a time. LENGTH must be at least BYTES_PER_ITER. Assume that
+ the memory regions do not overlap. */
+
+static void
+mips_block_move_loop (rtx dest, rtx src, HOST_WIDE_INT length,
+ HOST_WIDE_INT bytes_per_iter)
+{
+ rtx label, src_reg, dest_reg, final_src, test;
+ HOST_WIDE_INT leftover;
+
+ leftover = length % bytes_per_iter;
+ length -= leftover;
+
+ /* Create registers and memory references for use within the loop. */
+ mips_adjust_block_mem (src, bytes_per_iter, &src_reg, &src);
+ mips_adjust_block_mem (dest, bytes_per_iter, &dest_reg, &dest);
+
+ /* Calculate the value that SRC_REG should have after the last iteration
+ of the loop. */
+ final_src = expand_simple_binop (Pmode, PLUS, src_reg, GEN_INT (length),
+ 0, 0, OPTAB_WIDEN);
+
+ /* Emit the start of the loop. */
+ label = gen_label_rtx ();
+ emit_label (label);
+
+ /* Emit the loop body. */
+ mips_block_move_straight (dest, src, bytes_per_iter);
+
+ /* Move on to the next block. */
+ mips_emit_move (src_reg, plus_constant (src_reg, bytes_per_iter));
+ mips_emit_move (dest_reg, plus_constant (dest_reg, bytes_per_iter));
+
+ /* Emit the loop condition. */
+ test = gen_rtx_NE (VOIDmode, src_reg, final_src);
+ if (Pmode == DImode)
+ emit_jump_insn (gen_cbranchdi4 (test, src_reg, final_src, label));
+ else
+ emit_jump_insn (gen_cbranchsi4 (test, src_reg, final_src, label));
+
+ /* Mop up any left-over bytes. */
+ if (leftover)
+ mips_block_move_straight (dest, src, leftover);
+}
+
+/* Expand a movmemsi instruction, which copies LENGTH bytes from
+ memory reference SRC to memory reference DEST. */
+
+bool
+mips_expand_block_move (rtx dest, rtx src, rtx length)
+{
+ if (CONST_INT_P (length))
+ {
+ if (INTVAL (length) <= MIPS_MAX_MOVE_BYTES_STRAIGHT)
+ {
+ mips_block_move_straight (dest, src, INTVAL (length));
+ return true;
+ }
+ else if (optimize)
+ {
+ mips_block_move_loop (dest, src, INTVAL (length),
+ MIPS_MAX_MOVE_BYTES_PER_LOOP_ITER);
+ return true;
+ }
+ }
+ return false;
+}
+
+/* Expand a loop of synci insns for the address range [BEGIN, END). */
+
+void
+mips_expand_synci_loop (rtx begin, rtx end)
+{
+ rtx inc, label, end_label, cmp_result, mask, length;
+
+ /* Create end_label. */
+ end_label = gen_label_rtx ();
+
+ /* Check if begin equals end. */
+ cmp_result = gen_rtx_EQ (VOIDmode, begin, end);
+ emit_jump_insn (gen_condjump (cmp_result, end_label));
+
+ /* Load INC with the cache line size (rdhwr INC,$1). */
+ inc = gen_reg_rtx (Pmode);
+ emit_insn (Pmode == SImode
+ ? gen_rdhwr_synci_step_si (inc)
+ : gen_rdhwr_synci_step_di (inc));
+
+ /* Check if inc is 0. */
+ cmp_result = gen_rtx_EQ (VOIDmode, inc, const0_rtx);
+ emit_jump_insn (gen_condjump (cmp_result, end_label));
+
+ /* Calculate mask. */
+ mask = mips_force_unary (Pmode, NEG, inc);
+
+ /* Mask out begin by mask. */
+ begin = mips_force_binary (Pmode, AND, begin, mask);
+
+ /* Calculate length. */
+ length = mips_force_binary (Pmode, MINUS, end, begin);
+
+ /* Loop back to here. */
+ label = gen_label_rtx ();
+ emit_label (label);
+
+ emit_insn (gen_synci (begin));
+
+ /* Update length. */
+ mips_emit_binary (MINUS, length, length, inc);
+
+ /* Update begin. */
+ mips_emit_binary (PLUS, begin, begin, inc);
+
+ /* Check if length is greater than 0. */
+ cmp_result = gen_rtx_GT (VOIDmode, length, const0_rtx);
+ emit_jump_insn (gen_condjump (cmp_result, label));
+
+ emit_label (end_label);
+}
+
+/* Expand a QI or HI mode atomic memory operation.
+
+ GENERATOR contains a pointer to the gen_* function that generates
+ the SI mode underlying atomic operation using masks that we
+ calculate.
+
+ RESULT is the return register for the operation. Its value is NULL
+ if unused.
+
+ MEM is the location of the atomic access.
+
+ OLDVAL is the first operand for the operation.
+
+ NEWVAL is the optional second operand for the operation. Its value
+ is NULL if unused. */
+
+void
+mips_expand_atomic_qihi (union mips_gen_fn_ptrs generator,
+ rtx result, rtx mem, rtx oldval, rtx newval)
+{
+ rtx orig_addr, memsi_addr, memsi, shift, shiftsi, unshifted_mask;
+ rtx unshifted_mask_reg, mask, inverted_mask, si_op;
+ rtx res = NULL;
+ enum machine_mode mode;
+
+ mode = GET_MODE (mem);
+
+ /* Compute the address of the containing SImode value. */
+ orig_addr = force_reg (Pmode, XEXP (mem, 0));
+ memsi_addr = mips_force_binary (Pmode, AND, orig_addr,
+ force_reg (Pmode, GEN_INT (-4)));
+
+ /* Create a memory reference for it. */
+ memsi = gen_rtx_MEM (SImode, memsi_addr);
+ set_mem_alias_set (memsi, ALIAS_SET_MEMORY_BARRIER);
+ MEM_VOLATILE_P (memsi) = MEM_VOLATILE_P (mem);
+
+ /* Work out the byte offset of the QImode or HImode value,
+ counting from the least significant byte. */
+ shift = mips_force_binary (Pmode, AND, orig_addr, GEN_INT (3));
+ if (TARGET_BIG_ENDIAN)
+ mips_emit_binary (XOR, shift, shift, GEN_INT (mode == QImode ? 3 : 2));
+
+ /* Multiply by eight to convert the shift value from bytes to bits. */
+ mips_emit_binary (ASHIFT, shift, shift, GEN_INT (3));
+
+ /* Make the final shift an SImode value, so that it can be used in
+ SImode operations. */
+ shiftsi = force_reg (SImode, gen_lowpart (SImode, shift));
+
+ /* Set MASK to an inclusive mask of the QImode or HImode value. */
+ unshifted_mask = GEN_INT (GET_MODE_MASK (mode));
+ unshifted_mask_reg = force_reg (SImode, unshifted_mask);
+ mask = mips_force_binary (SImode, ASHIFT, unshifted_mask_reg, shiftsi);
+
+ /* Compute the equivalent exclusive mask. */
+ inverted_mask = gen_reg_rtx (SImode);
+ emit_insn (gen_rtx_SET (VOIDmode, inverted_mask,
+ gen_rtx_NOT (SImode, mask)));
+
+ /* Shift the old value into place. */
+ if (oldval != const0_rtx)
+ {
+ oldval = convert_modes (SImode, mode, oldval, true);
+ oldval = force_reg (SImode, oldval);
+ oldval = mips_force_binary (SImode, ASHIFT, oldval, shiftsi);
+ }
+
+ /* Do the same for the new value. */
+ if (newval && newval != const0_rtx)
+ {
+ newval = convert_modes (SImode, mode, newval, true);
+ newval = force_reg (SImode, newval);
+ newval = mips_force_binary (SImode, ASHIFT, newval, shiftsi);
+ }
+
+ /* Do the SImode atomic access. */
+ if (result)
+ res = gen_reg_rtx (SImode);
+ if (newval)
+ si_op = generator.fn_6 (res, memsi, mask, inverted_mask, oldval, newval);
+ else if (result)
+ si_op = generator.fn_5 (res, memsi, mask, inverted_mask, oldval);
+ else
+ si_op = generator.fn_4 (memsi, mask, inverted_mask, oldval);
+
+ emit_insn (si_op);
+
+ if (result)
+ {
+ /* Shift and convert the result. */
+ mips_emit_binary (AND, res, res, mask);
+ mips_emit_binary (LSHIFTRT, res, res, shiftsi);
+ mips_emit_move (result, gen_lowpart (GET_MODE (result), res));
+ }
+}
+
+/* Return true if it is possible to use left/right accesses for a
+ bitfield of WIDTH bits starting BITPOS bits into *OP. When
+ returning true, update *OP, *LEFT and *RIGHT as follows:
+
+ *OP is a BLKmode reference to the whole field.
+
+ *LEFT is a QImode reference to the first byte if big endian or
+ the last byte if little endian. This address can be used in the
+ left-side instructions (LWL, SWL, LDL, SDL).
+
+ *RIGHT is a QImode reference to the opposite end of the field and
+ can be used in the patterning right-side instruction. */
+
+static bool
+mips_get_unaligned_mem (rtx *op, HOST_WIDE_INT width, HOST_WIDE_INT bitpos,
+ rtx *left, rtx *right)
+{
+ rtx first, last;
+
+ /* Check that the operand really is a MEM. Not all the extv and
+ extzv predicates are checked. */
+ if (!MEM_P (*op))
+ return false;
+
+ /* Check that the size is valid. */
+ if (width != 32 && (!TARGET_64BIT || width != 64))
+ return false;
+
+ /* We can only access byte-aligned values. Since we are always passed
+ a reference to the first byte of the field, it is not necessary to
+ do anything with BITPOS after this check. */
+ if (bitpos % BITS_PER_UNIT != 0)
+ return false;
+
+ /* Reject aligned bitfields: we want to use a normal load or store
+ instead of a left/right pair. */
+ if (MEM_ALIGN (*op) >= width)
+ return false;
+
+ /* Adjust *OP to refer to the whole field. This also has the effect
+ of legitimizing *OP's address for BLKmode, possibly simplifying it. */
+ *op = adjust_address (*op, BLKmode, 0);
+ set_mem_size (*op, GEN_INT (width / BITS_PER_UNIT));
+
+ /* Get references to both ends of the field. We deliberately don't
+ use the original QImode *OP for FIRST since the new BLKmode one
+ might have a simpler address. */
+ first = adjust_address (*op, QImode, 0);
+ last = adjust_address (*op, QImode, width / BITS_PER_UNIT - 1);
+
+ /* Allocate to LEFT and RIGHT according to endianness. LEFT should
+ correspond to the MSB and RIGHT to the LSB. */
+ if (TARGET_BIG_ENDIAN)
+ *left = first, *right = last;
+ else
+ *left = last, *right = first;
+
+ return true;
+}
+
+/* Try to use left/right loads to expand an "extv" or "extzv" pattern.
+ DEST, SRC, WIDTH and BITPOS are the operands passed to the expander;
+ the operation is the equivalent of:
+
+ (set DEST (*_extract SRC WIDTH BITPOS))
+
+ Return true on success. */
+
+bool
+mips_expand_ext_as_unaligned_load (rtx dest, rtx src, HOST_WIDE_INT width,
+ HOST_WIDE_INT bitpos)
+{
+ rtx left, right, temp;
+
+ /* If TARGET_64BIT, the destination of a 32-bit "extz" or "extzv" will
+ be a paradoxical word_mode subreg. This is the only case in which
+ we allow the destination to be larger than the source. */
+ if (GET_CODE (dest) == SUBREG
+ && GET_MODE (dest) == DImode
+ && GET_MODE (SUBREG_REG (dest)) == SImode)
+ dest = SUBREG_REG (dest);
+
+ /* After the above adjustment, the destination must be the same
+ width as the source. */
+ if (GET_MODE_BITSIZE (GET_MODE (dest)) != width)
+ return false;
+
+ if (!mips_get_unaligned_mem (&src, width, bitpos, &left, &right))
+ return false;
+
+ temp = gen_reg_rtx (GET_MODE (dest));
+ if (GET_MODE (dest) == DImode)
+ {
+ emit_insn (gen_mov_ldl (temp, src, left));
+ emit_insn (gen_mov_ldr (dest, copy_rtx (src), right, temp));
+ }
+ else
+ {
+ emit_insn (gen_mov_lwl (temp, src, left));
+ emit_insn (gen_mov_lwr (dest, copy_rtx (src), right, temp));
+ }
+ return true;
+}
+
+/* Try to use left/right stores to expand an "ins" pattern. DEST, WIDTH,
+ BITPOS and SRC are the operands passed to the expander; the operation
+ is the equivalent of:
+
+ (set (zero_extract DEST WIDTH BITPOS) SRC)
+
+ Return true on success. */
+
+bool
+mips_expand_ins_as_unaligned_store (rtx dest, rtx src, HOST_WIDE_INT width,
+ HOST_WIDE_INT bitpos)
+{
+ rtx left, right;
+ enum machine_mode mode;
+
+ if (!mips_get_unaligned_mem (&dest, width, bitpos, &left, &right))
+ return false;
+
+ mode = mode_for_size (width, MODE_INT, 0);
+ src = gen_lowpart (mode, src);
+ if (mode == DImode)
+ {
+ emit_insn (gen_mov_sdl (dest, src, left));
+ emit_insn (gen_mov_sdr (copy_rtx (dest), copy_rtx (src), right));
+ }
+ else
+ {
+ emit_insn (gen_mov_swl (dest, src, left));
+ emit_insn (gen_mov_swr (copy_rtx (dest), copy_rtx (src), right));
+ }
+ return true;
+}
+
+/* Return true if X is a MEM with the same size as MODE. */
+
+bool
+mips_mem_fits_mode_p (enum machine_mode mode, rtx x)
+{
+ rtx size;
+
+ if (!MEM_P (x))
+ return false;
+
+ size = MEM_SIZE (x);
+ return size && INTVAL (size) == GET_MODE_SIZE (mode);
+}
+
+/* Return true if (zero_extract OP WIDTH BITPOS) can be used as the
+ source of an "ext" instruction or the destination of an "ins"
+ instruction. OP must be a register operand and the following
+ conditions must hold:
+
+ 0 <= BITPOS < GET_MODE_BITSIZE (GET_MODE (op))
+ 0 < WIDTH <= GET_MODE_BITSIZE (GET_MODE (op))
+ 0 < BITPOS + WIDTH <= GET_MODE_BITSIZE (GET_MODE (op))
+
+ Also reject lengths equal to a word as they are better handled
+ by the move patterns. */
+
+bool
+mips_use_ins_ext_p (rtx op, HOST_WIDE_INT width, HOST_WIDE_INT bitpos)
+{
+ if (!ISA_HAS_EXT_INS
+ || !register_operand (op, VOIDmode)
+ || GET_MODE_BITSIZE (GET_MODE (op)) > BITS_PER_WORD)
+ return false;
+
+ if (!IN_RANGE (width, 1, GET_MODE_BITSIZE (GET_MODE (op)) - 1))
+ return false;
+
+ if (bitpos < 0 || bitpos + width > GET_MODE_BITSIZE (GET_MODE (op)))
+ return false;
+
+ return true;
+}
+
+/* Check if MASK and SHIFT are valid in mask-low-and-shift-left
+ operation if MAXLEN is the maxium length of consecutive bits that
+ can make up MASK. MODE is the mode of the operation. See
+ mask_low_and_shift_len for the actual definition. */
+
+bool
+mask_low_and_shift_p (enum machine_mode mode, rtx mask, rtx shift, int maxlen)
+{
+ return IN_RANGE (mask_low_and_shift_len (mode, mask, shift), 1, maxlen);
+}
+
+/* Return true iff OP1 and OP2 are valid operands together for the
+ *and<MODE>3 and *and<MODE>3_mips16 patterns. For the cases to consider,
+ see the table in the comment before the pattern. */
+
+bool
+and_operands_ok (enum machine_mode mode, rtx op1, rtx op2)
+{
+ return (memory_operand (op1, mode)
+ ? and_load_operand (op2, mode)
+ : and_reg_operand (op2, mode));
+}
+
+/* The canonical form of a mask-low-and-shift-left operation is
+ (and (ashift X SHIFT) MASK) where MASK has the lower SHIFT number of bits
+ cleared. Thus we need to shift MASK to the right before checking if it
+ is a valid mask value. MODE is the mode of the operation. If true
+ return the length of the mask, otherwise return -1. */
+
+int
+mask_low_and_shift_len (enum machine_mode mode, rtx mask, rtx shift)
+{
+ HOST_WIDE_INT shval;
+
+ shval = INTVAL (shift) & (GET_MODE_BITSIZE (mode) - 1);
+ return exact_log2 ((UINTVAL (mask) >> shval) + 1);
+}
+
+/* Return true if -msplit-addresses is selected and should be honored.
+
+ -msplit-addresses is a half-way house between explicit relocations
+ and the traditional assembler macros. It can split absolute 32-bit
+ symbolic constants into a high/lo_sum pair but uses macros for other
+ sorts of access.
+
+ Like explicit relocation support for REL targets, it relies
+ on GNU extensions in the assembler and the linker.
+
+ Although this code should work for -O0, it has traditionally
+ been treated as an optimization. */
+
+static bool
+mips_split_addresses_p (void)
+{
+ return (TARGET_SPLIT_ADDRESSES
+ && optimize
+ && !TARGET_MIPS16
+ && !flag_pic
+ && !ABI_HAS_64BIT_SYMBOLS);
+}
+
+/* (Re-)Initialize mips_split_p, mips_lo_relocs and mips_hi_relocs. */
+
+static void
+mips_init_relocs (void)
+{
+ memset (mips_split_p, '\0', sizeof (mips_split_p));
+ memset (mips_split_hi_p, '\0', sizeof (mips_split_hi_p));
+ memset (mips_hi_relocs, '\0', sizeof (mips_hi_relocs));
+ memset (mips_lo_relocs, '\0', sizeof (mips_lo_relocs));
+
+ if (ABI_HAS_64BIT_SYMBOLS)
+ {
+ if (TARGET_EXPLICIT_RELOCS)
+ {
+ mips_split_p[SYMBOL_64_HIGH] = true;
+ mips_hi_relocs[SYMBOL_64_HIGH] = "%highest(";
+ mips_lo_relocs[SYMBOL_64_HIGH] = "%higher(";
+
+ mips_split_p[SYMBOL_64_MID] = true;
+ mips_hi_relocs[SYMBOL_64_MID] = "%higher(";
+ mips_lo_relocs[SYMBOL_64_MID] = "%hi(";
+
+ mips_split_p[SYMBOL_64_LOW] = true;
+ mips_hi_relocs[SYMBOL_64_LOW] = "%hi(";
+ mips_lo_relocs[SYMBOL_64_LOW] = "%lo(";
+
+ mips_split_p[SYMBOL_ABSOLUTE] = true;
+ mips_lo_relocs[SYMBOL_ABSOLUTE] = "%lo(";
+ }
+ }
+ else
+ {
+ if (TARGET_EXPLICIT_RELOCS || mips_split_addresses_p () || TARGET_MIPS16)
+ {
+ mips_split_p[SYMBOL_ABSOLUTE] = true;
+ mips_hi_relocs[SYMBOL_ABSOLUTE] = "%hi(";
+ mips_lo_relocs[SYMBOL_ABSOLUTE] = "%lo(";
+
+ mips_lo_relocs[SYMBOL_32_HIGH] = "%hi(";
+ }
+ }
+
+ if (TARGET_MIPS16)
+ {
+ /* The high part is provided by a pseudo copy of $gp. */
+ mips_split_p[SYMBOL_GP_RELATIVE] = true;
+ mips_lo_relocs[SYMBOL_GP_RELATIVE] = "%gprel(";
+ }
+ else if (TARGET_EXPLICIT_RELOCS)
+ /* Small data constants are kept whole until after reload,
+ then lowered by mips_rewrite_small_data. */
+ mips_lo_relocs[SYMBOL_GP_RELATIVE] = "%gp_rel(";
+
+ if (TARGET_EXPLICIT_RELOCS)
+ {
+ mips_split_p[SYMBOL_GOT_PAGE_OFST] = true;
+ if (TARGET_NEWABI)
+ {
+ mips_lo_relocs[SYMBOL_GOTOFF_PAGE] = "%got_page(";
+ mips_lo_relocs[SYMBOL_GOT_PAGE_OFST] = "%got_ofst(";
+ }
+ else
+ {
+ mips_lo_relocs[SYMBOL_GOTOFF_PAGE] = "%got(";
+ mips_lo_relocs[SYMBOL_GOT_PAGE_OFST] = "%lo(";
+ }
+ if (TARGET_MIPS16)
+ /* Expose the use of $28 as soon as possible. */
+ mips_split_hi_p[SYMBOL_GOT_PAGE_OFST] = true;
+
+ if (TARGET_XGOT)
+ {
+ /* The HIGH and LO_SUM are matched by special .md patterns. */
+ mips_split_p[SYMBOL_GOT_DISP] = true;
+
+ mips_split_p[SYMBOL_GOTOFF_DISP] = true;
+ mips_hi_relocs[SYMBOL_GOTOFF_DISP] = "%got_hi(";
+ mips_lo_relocs[SYMBOL_GOTOFF_DISP] = "%got_lo(";
+
+ mips_split_p[SYMBOL_GOTOFF_CALL] = true;
+ mips_hi_relocs[SYMBOL_GOTOFF_CALL] = "%call_hi(";
+ mips_lo_relocs[SYMBOL_GOTOFF_CALL] = "%call_lo(";
+ }
+ else
+ {
+ if (TARGET_NEWABI)
+ mips_lo_relocs[SYMBOL_GOTOFF_DISP] = "%got_disp(";
+ else
+ mips_lo_relocs[SYMBOL_GOTOFF_DISP] = "%got(";
+ mips_lo_relocs[SYMBOL_GOTOFF_CALL] = "%call16(";
+ if (TARGET_MIPS16)
+ /* Expose the use of $28 as soon as possible. */
+ mips_split_p[SYMBOL_GOT_DISP] = true;
+ }
+ }
+
+ if (TARGET_NEWABI)
+ {
+ mips_split_p[SYMBOL_GOTOFF_LOADGP] = true;
+ mips_hi_relocs[SYMBOL_GOTOFF_LOADGP] = "%hi(%neg(%gp_rel(";
+ mips_lo_relocs[SYMBOL_GOTOFF_LOADGP] = "%lo(%neg(%gp_rel(";
+ }
+
+ mips_lo_relocs[SYMBOL_TLSGD] = "%tlsgd(";
+ mips_lo_relocs[SYMBOL_TLSLDM] = "%tlsldm(";
+
+ mips_split_p[SYMBOL_DTPREL] = true;
+ mips_hi_relocs[SYMBOL_DTPREL] = "%dtprel_hi(";
+ mips_lo_relocs[SYMBOL_DTPREL] = "%dtprel_lo(";
+
+ mips_lo_relocs[SYMBOL_GOTTPREL] = "%gottprel(";
+
+ mips_split_p[SYMBOL_TPREL] = true;
+ mips_hi_relocs[SYMBOL_TPREL] = "%tprel_hi(";
+ mips_lo_relocs[SYMBOL_TPREL] = "%tprel_lo(";
+
+ mips_lo_relocs[SYMBOL_HALF] = "%half(";
+}
+
+/* Print symbolic operand OP, which is part of a HIGH or LO_SUM
+ in context CONTEXT. RELOCS is the array of relocations to use. */
+
+static void
+mips_print_operand_reloc (FILE *file, rtx op, enum mips_symbol_context context,
+ const char **relocs)
+{
+ enum mips_symbol_type symbol_type;
+ const char *p;
+
+ symbol_type = mips_classify_symbolic_expression (op, context);
+ gcc_assert (relocs[symbol_type]);
+
+ fputs (relocs[symbol_type], file);
+ output_addr_const (file, mips_strip_unspec_address (op));
+ for (p = relocs[symbol_type]; *p != 0; p++)
+ if (*p == '(')
+ fputc (')', file);
+}
+
+/* Start a new block with the given asm switch enabled. If we need
+ to print a directive, emit PREFIX before it and SUFFIX after it. */
+
+static void
+mips_push_asm_switch_1 (struct mips_asm_switch *asm_switch,
+ const char *prefix, const char *suffix)
+{
+ if (asm_switch->nesting_level == 0)
+ fprintf (asm_out_file, "%s.set\tno%s%s", prefix, asm_switch->name, suffix);
+ asm_switch->nesting_level++;
+}
+
+/* Likewise, but end a block. */
+
+static void
+mips_pop_asm_switch_1 (struct mips_asm_switch *asm_switch,
+ const char *prefix, const char *suffix)
+{
+ gcc_assert (asm_switch->nesting_level);
+ asm_switch->nesting_level--;
+ if (asm_switch->nesting_level == 0)
+ fprintf (asm_out_file, "%s.set\t%s%s", prefix, asm_switch->name, suffix);
+}
+
+/* Wrappers around mips_push_asm_switch_1 and mips_pop_asm_switch_1
+ that either print a complete line or print nothing. */
+
+void
+mips_push_asm_switch (struct mips_asm_switch *asm_switch)
+{
+ mips_push_asm_switch_1 (asm_switch, "\t", "\n");
+}
+
+void
+mips_pop_asm_switch (struct mips_asm_switch *asm_switch)
+{
+ mips_pop_asm_switch_1 (asm_switch, "\t", "\n");
+}
+
+/* Print the text for PRINT_OPERAND punctation character CH to FILE.
+ The punctuation characters are:
+
+ '(' Start a nested ".set noreorder" block.
+ ')' End a nested ".set noreorder" block.
+ '[' Start a nested ".set noat" block.
+ ']' End a nested ".set noat" block.
+ '<' Start a nested ".set nomacro" block.
+ '>' End a nested ".set nomacro" block.
+ '*' Behave like %(%< if generating a delayed-branch sequence.
+ '#' Print a nop if in a ".set noreorder" block.
+ '/' Like '#', but do nothing within a delayed-branch sequence.
+ '?' Print "l" if mips_branch_likely is true
+ '~' Print a nop if mips_branch_likely is true
+ '.' Print the name of the register with a hard-wired zero (zero or $0).
+ '@' Print the name of the assembler temporary register (at or $1).
+ '^' Print the name of the pic call-through register (t9 or $25).
+ '+' Print the name of the gp register (usually gp or $28).
+ '$' Print the name of the stack pointer register (sp or $29).
+
+ See also mips_init_print_operand_pucnt. */
+
+static void
+mips_print_operand_punctuation (FILE *file, int ch)
+{
+ switch (ch)
+ {
+ case '(':
+ mips_push_asm_switch_1 (&mips_noreorder, "", "\n\t");
+ break;
+
+ case ')':
+ mips_pop_asm_switch_1 (&mips_noreorder, "\n\t", "");
+ break;
+
+ case '[':
+ mips_push_asm_switch_1 (&mips_noat, "", "\n\t");
+ break;
+
+ case ']':
+ mips_pop_asm_switch_1 (&mips_noat, "\n\t", "");
+ break;
+
+ case '<':
+ mips_push_asm_switch_1 (&mips_nomacro, "", "\n\t");
+ break;
+
+ case '>':
+ mips_pop_asm_switch_1 (&mips_nomacro, "\n\t", "");
+ break;
+
+ case '*':
+ if (final_sequence != 0)
+ {
+ mips_print_operand_punctuation (file, '(');
+ mips_print_operand_punctuation (file, '<');
+ }
+ break;
+
+ case '#':
+ if (mips_noreorder.nesting_level > 0)
+ fputs ("\n\tnop", file);
+ break;
+
+ case '/':
+ /* Print an extra newline so that the delayed insn is separated
+ from the following ones. This looks neater and is consistent
+ with non-nop delayed sequences. */
+ if (mips_noreorder.nesting_level > 0 && final_sequence == 0)
+ fputs ("\n\tnop\n", file);
+ break;
+
+ case '?':
+ if (mips_branch_likely)
+ putc ('l', file);
+ break;
+
+ case '~':
+ if (mips_branch_likely)
+ fputs ("\n\tnop", file);
+ break;
+
+ case '.':
+ fputs (reg_names[GP_REG_FIRST + 0], file);
+ break;
+
+ case '@':
+ fputs (reg_names[AT_REGNUM], file);
+ break;
+
+ case '^':
+ fputs (reg_names[PIC_FUNCTION_ADDR_REGNUM], file);
+ break;
+
+ case '+':
+ fputs (reg_names[PIC_OFFSET_TABLE_REGNUM], file);
+ break;
+
+ case '$':
+ fputs (reg_names[STACK_POINTER_REGNUM], file);
+ break;
+
+ default:
+ gcc_unreachable ();
+ break;
+ }
+}
+
+/* Initialize mips_print_operand_punct. */
+
+static void
+mips_init_print_operand_punct (void)
+{
+ const char *p;
+
+ for (p = "()[]<>*#/?~.@^+$"; *p; p++)
+ mips_print_operand_punct[(unsigned char) *p] = true;
+}
+
+/* PRINT_OPERAND prefix LETTER refers to the integer branch instruction
+ associated with condition CODE. Print the condition part of the
+ opcode to FILE. */
+
+static void
+mips_print_int_branch_condition (FILE *file, enum rtx_code code, int letter)
+{
+ switch (code)
+ {
+ case EQ:
+ case NE:
+ case GT:
+ case GE:
+ case LT:
+ case LE:
+ case GTU:
+ case GEU:
+ case LTU:
+ case LEU:
+ /* Conveniently, the MIPS names for these conditions are the same
+ as their RTL equivalents. */
+ fputs (GET_RTX_NAME (code), file);
+ break;
+
+ default:
+ output_operand_lossage ("'%%%c' is not a valid operand prefix", letter);
+ break;
+ }
+}
+
+/* Likewise floating-point branches. */
+
+static void
+mips_print_float_branch_condition (FILE *file, enum rtx_code code, int letter)
+{
+ switch (code)
+ {
+ case EQ:
+ fputs ("c1f", file);
+ break;
+
+ case NE:
+ fputs ("c1t", file);
+ break;
+
+ default:
+ output_operand_lossage ("'%%%c' is not a valid operand prefix", letter);
+ break;
+ }
+}
+
+/* Implement TARGET_PRINT_OPERAND_PUNCT_VALID_P. */
+
+static bool
+mips_print_operand_punct_valid_p (unsigned char code)
+{
+ return mips_print_operand_punct[code];
+}
+
+/* Implement TARGET_PRINT_OPERAND. The MIPS-specific operand codes are:
+
+ 'X' Print CONST_INT OP in hexadecimal format.
+ 'x' Print the low 16 bits of CONST_INT OP in hexadecimal format.
+ 'd' Print CONST_INT OP in decimal.
+ 'm' Print one less than CONST_INT OP in decimal.
+ 'h' Print the high-part relocation associated with OP, after stripping
+ any outermost HIGH.
+ 'R' Print the low-part relocation associated with OP.
+ 'C' Print the integer branch condition for comparison OP.
+ 'N' Print the inverse of the integer branch condition for comparison OP.
+ 'F' Print the FPU branch condition for comparison OP.
+ 'W' Print the inverse of the FPU branch condition for comparison OP.
+ 'T' Print 'f' for (eq:CC ...), 't' for (ne:CC ...),
+ 'z' for (eq:?I ...), 'n' for (ne:?I ...).
+ 't' Like 'T', but with the EQ/NE cases reversed
+ 'Y' Print mips_fp_conditions[INTVAL (OP)]
+ 'Z' Print OP and a comma for ISA_HAS_8CC, otherwise print nothing.
+ 'q' Print a DSP accumulator register.
+ 'D' Print the second part of a double-word register or memory operand.
+ 'L' Print the low-order register in a double-word register operand.
+ 'M' Print high-order register in a double-word register operand.
+ 'z' Print $0 if OP is zero, otherwise print OP normally. */
+
+static void
+mips_print_operand (FILE *file, rtx op, int letter)
+{
+ enum rtx_code code;
+
+ if (mips_print_operand_punct_valid_p (letter))
+ {
+ mips_print_operand_punctuation (file, letter);
+ return;
+ }
+
+ gcc_assert (op);
+ code = GET_CODE (op);
+
+ switch (letter)
+ {
+ case 'X':
+ if (CONST_INT_P (op))
+ fprintf (file, HOST_WIDE_INT_PRINT_HEX, INTVAL (op));
+ else
+ output_operand_lossage ("invalid use of '%%%c'", letter);
+ break;
+
+ case 'x':
+ if (CONST_INT_P (op))
+ fprintf (file, HOST_WIDE_INT_PRINT_HEX, INTVAL (op) & 0xffff);
+ else
+ output_operand_lossage ("invalid use of '%%%c'", letter);
+ break;
+
+ case 'd':
+ if (CONST_INT_P (op))
+ fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (op));
+ else
+ output_operand_lossage ("invalid use of '%%%c'", letter);
+ break;
+
+ case 'm':
+ if (CONST_INT_P (op))
+ fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (op) - 1);
+ else
+ output_operand_lossage ("invalid use of '%%%c'", letter);
+ break;
+
+ case 'h':
+ if (code == HIGH)
+ op = XEXP (op, 0);
+ mips_print_operand_reloc (file, op, SYMBOL_CONTEXT_LEA, mips_hi_relocs);
+ break;
+
+ case 'R':
+ mips_print_operand_reloc (file, op, SYMBOL_CONTEXT_LEA, mips_lo_relocs);
+ break;
+
+ case 'C':
+ mips_print_int_branch_condition (file, code, letter);
+ break;
+
+ case 'N':
+ mips_print_int_branch_condition (file, reverse_condition (code), letter);
+ break;
+
+ case 'F':
+ mips_print_float_branch_condition (file, code, letter);
+ break;
+
+ case 'W':
+ mips_print_float_branch_condition (file, reverse_condition (code),
+ letter);
+ break;
+
+ case 'T':
+ case 't':
+ {
+ int truth = (code == NE) == (letter == 'T');
+ fputc ("zfnt"[truth * 2 + (GET_MODE (op) == CCmode)], file);
+ }
+ break;
+
+ case 'Y':
+ if (code == CONST_INT && UINTVAL (op) < ARRAY_SIZE (mips_fp_conditions))
+ fputs (mips_fp_conditions[UINTVAL (op)], file);
+ else
+ output_operand_lossage ("'%%%c' is not a valid operand prefix",
+ letter);
+ break;
+
+ case 'Z':
+ if (ISA_HAS_8CC)
+ {
+ mips_print_operand (file, op, 0);
+ fputc (',', file);
+ }
+ break;
+
+ case 'q':
+ if (code == REG && MD_REG_P (REGNO (op)))
+ fprintf (file, "$ac0");
+ else if (code == REG && DSP_ACC_REG_P (REGNO (op)))
+ fprintf (file, "$ac%c", reg_names[REGNO (op)][3]);
+ else
+ output_operand_lossage ("invalid use of '%%%c'", letter);
+ break;
+
+ default:
+ switch (code)
+ {
+ case REG:
+ {
+ unsigned int regno = REGNO (op);
+ if ((letter == 'M' && TARGET_LITTLE_ENDIAN)
+ || (letter == 'L' && TARGET_BIG_ENDIAN)
+ || letter == 'D')
+ regno++;
+ else if (letter && letter != 'z' && letter != 'M' && letter != 'L')
+ output_operand_lossage ("invalid use of '%%%c'", letter);
+ /* We need to print $0 .. $31 for COP0 registers. */
+ if (COP0_REG_P (regno))
+ fprintf (file, "$%s", &reg_names[regno][4]);
+ else
+ fprintf (file, "%s", reg_names[regno]);
+ }
+ break;
+
+ case MEM:
+ if (letter == 'D')
+ output_address (plus_constant (XEXP (op, 0), 4));
+ else if (letter && letter != 'z')
+ output_operand_lossage ("invalid use of '%%%c'", letter);
+ else
+ output_address (XEXP (op, 0));
+ break;
+
+ default:
+ if (letter == 'z' && op == CONST0_RTX (GET_MODE (op)))
+ fputs (reg_names[GP_REG_FIRST], file);
+ else if (letter && letter != 'z')
+ output_operand_lossage ("invalid use of '%%%c'", letter);
+ else if (CONST_GP_P (op))
+ fputs (reg_names[GLOBAL_POINTER_REGNUM], file);
+ else
+ output_addr_const (file, mips_strip_unspec_address (op));
+ break;
+ }
+ }
+}
+
+/* Implement TARGET_PRINT_OPERAND_ADDRESS. */
+
+static void
+mips_print_operand_address (FILE *file, rtx x)
+{
+ struct mips_address_info addr;
+
+ if (mips_classify_address (&addr, x, word_mode, true))
+ switch (addr.type)
+ {
+ case ADDRESS_REG:
+ mips_print_operand (file, addr.offset, 0);
+ fprintf (file, "(%s)", reg_names[REGNO (addr.reg)]);
+ return;
+
+ case ADDRESS_LO_SUM:
+ mips_print_operand_reloc (file, addr.offset, SYMBOL_CONTEXT_MEM,
+ mips_lo_relocs);
+ fprintf (file, "(%s)", reg_names[REGNO (addr.reg)]);
+ return;
+
+ case ADDRESS_CONST_INT:
+ output_addr_const (file, x);
+ fprintf (file, "(%s)", reg_names[GP_REG_FIRST]);
+ return;
+
+ case ADDRESS_SYMBOLIC:
+ output_addr_const (file, mips_strip_unspec_address (x));
+ return;
+ }
+ gcc_unreachable ();
+}
+
+/* Implement TARGET_ENCODE_SECTION_INFO. */
+
+static void
+mips_encode_section_info (tree decl, rtx rtl, int first)
+{
+ default_encode_section_info (decl, rtl, first);
+
+ if (TREE_CODE (decl) == FUNCTION_DECL)
+ {
+ rtx symbol = XEXP (rtl, 0);
+ tree type = TREE_TYPE (decl);
+
+ /* Encode whether the symbol is short or long. */
+ if ((TARGET_LONG_CALLS && !mips_near_type_p (type))
+ || mips_far_type_p (type))
+ SYMBOL_REF_FLAGS (symbol) |= SYMBOL_FLAG_LONG_CALL;
+ }
+}
+
+/* Implement TARGET_SELECT_RTX_SECTION. */
+
+static section *
+mips_select_rtx_section (enum machine_mode mode, rtx x,
+ unsigned HOST_WIDE_INT align)
+{
+ /* ??? Consider using mergeable small data sections. */
+ if (mips_rtx_constant_in_small_data_p (mode))
+ return get_named_section (NULL, ".sdata", 0);
+
+ return default_elf_select_rtx_section (mode, x, align);
+}
+
+/* Implement TARGET_ASM_FUNCTION_RODATA_SECTION.
+
+ The complication here is that, with the combination TARGET_ABICALLS
+ && !TARGET_ABSOLUTE_ABICALLS && !TARGET_GPWORD, jump tables will use
+ absolute addresses, and should therefore not be included in the
+ read-only part of a DSO. Handle such cases by selecting a normal
+ data section instead of a read-only one. The logic apes that in
+ default_function_rodata_section. */
+
+static section *
+mips_function_rodata_section (tree decl)
+{
+ if (!TARGET_ABICALLS || TARGET_ABSOLUTE_ABICALLS || TARGET_GPWORD)
+ return default_function_rodata_section (decl);
+
+ if (decl && DECL_SECTION_NAME (decl))
+ {
+ const char *name = TREE_STRING_POINTER (DECL_SECTION_NAME (decl));
+ if (DECL_ONE_ONLY (decl) && strncmp (name, ".gnu.linkonce.t.", 16) == 0)
+ {
+ char *rname = ASTRDUP (name);
+ rname[14] = 'd';
+ return get_section (rname, SECTION_LINKONCE | SECTION_WRITE, decl);
+ }
+ else if (flag_function_sections
+ && flag_data_sections
+ && strncmp (name, ".text.", 6) == 0)
+ {
+ char *rname = ASTRDUP (name);
+ memcpy (rname + 1, "data", 4);
+ return get_section (rname, SECTION_WRITE, decl);
+ }
+ }
+ return data_section;
+}
+
+/* Implement TARGET_IN_SMALL_DATA_P. */
+
+static bool
+mips_in_small_data_p (const_tree decl)
+{
+ unsigned HOST_WIDE_INT size;
+
+ if (TREE_CODE (decl) == STRING_CST || TREE_CODE (decl) == FUNCTION_DECL)
+ return false;
+
+ /* We don't yet generate small-data references for -mabicalls
+ or VxWorks RTP code. See the related -G handling in
+ mips_option_override. */
+ if (TARGET_ABICALLS || TARGET_VXWORKS_RTP)
+ return false;
+
+ if (TREE_CODE (decl) == VAR_DECL && DECL_SECTION_NAME (decl) != 0)
+ {
+ const char *name;
+
+ /* Reject anything that isn't in a known small-data section. */
+ name = TREE_STRING_POINTER (DECL_SECTION_NAME (decl));
+ if (strcmp (name, ".sdata") != 0 && strcmp (name, ".sbss") != 0)
+ return false;
+
+ /* If a symbol is defined externally, the assembler will use the
+ usual -G rules when deciding how to implement macros. */
+ if (mips_lo_relocs[SYMBOL_GP_RELATIVE] || !DECL_EXTERNAL (decl))
+ return true;
+ }
+ else if (TARGET_EMBEDDED_DATA)
+ {
+ /* Don't put constants into the small data section: we want them
+ to be in ROM rather than RAM. */
+ if (TREE_CODE (decl) != VAR_DECL)
+ return false;
+
+ if (TREE_READONLY (decl)
+ && !TREE_SIDE_EFFECTS (decl)
+ && (!DECL_INITIAL (decl) || TREE_CONSTANT (DECL_INITIAL (decl))))
+ return false;
+ }
+
+ /* Enforce -mlocal-sdata. */
+ if (!TARGET_LOCAL_SDATA && !TREE_PUBLIC (decl))
+ return false;
+
+ /* Enforce -mextern-sdata. */
+ if (!TARGET_EXTERN_SDATA && DECL_P (decl))
+ {
+ if (DECL_EXTERNAL (decl))
+ return false;
+ if (DECL_COMMON (decl) && DECL_INITIAL (decl) == NULL)
+ return false;
+ }
+
+ /* We have traditionally not treated zero-sized objects as small data,
+ so this is now effectively part of the ABI. */
+ size = int_size_in_bytes (TREE_TYPE (decl));
+ return size > 0 && size <= mips_small_data_threshold;
+}
+
+/* Implement TARGET_USE_ANCHORS_FOR_SYMBOL_P. We don't want to use
+ anchors for small data: the GP register acts as an anchor in that
+ case. We also don't want to use them for PC-relative accesses,
+ where the PC acts as an anchor. */
+
+static bool
+mips_use_anchors_for_symbol_p (const_rtx symbol)
+{
+ switch (mips_classify_symbol (symbol, SYMBOL_CONTEXT_MEM))
+ {
+ case SYMBOL_PC_RELATIVE:
+ case SYMBOL_GP_RELATIVE:
+ return false;
+
+ default:
+ return default_use_anchors_for_symbol_p (symbol);
+ }
+}
+
+/* The MIPS debug format wants all automatic variables and arguments
+ to be in terms of the virtual frame pointer (stack pointer before
+ any adjustment in the function), while the MIPS 3.0 linker wants
+ the frame pointer to be the stack pointer after the initial
+ adjustment. So, we do the adjustment here. The arg pointer (which
+ is eliminated) points to the virtual frame pointer, while the frame
+ pointer (which may be eliminated) points to the stack pointer after
+ the initial adjustments. */
+
+HOST_WIDE_INT
+mips_debugger_offset (rtx addr, HOST_WIDE_INT offset)
+{
+ rtx offset2 = const0_rtx;
+ rtx reg = eliminate_constant_term (addr, &offset2);
+
+ if (offset == 0)
+ offset = INTVAL (offset2);
+
+ if (reg == stack_pointer_rtx
+ || reg == frame_pointer_rtx
+ || reg == hard_frame_pointer_rtx)
+ {
+ offset -= cfun->machine->frame.total_size;
+ if (reg == hard_frame_pointer_rtx)
+ offset += cfun->machine->frame.hard_frame_pointer_offset;
+ }
+
+ /* sdbout_parms does not want this to crash for unrecognized cases. */
+#if 0
+ else if (reg != arg_pointer_rtx)
+ fatal_insn ("mips_debugger_offset called with non stack/frame/arg pointer",
+ addr);
+#endif
+
+ return offset;
+}
+
+/* Implement ASM_OUTPUT_EXTERNAL. */
+
+void
+mips_output_external (FILE *file, tree decl, const char *name)
+{
+ default_elf_asm_output_external (file, decl, name);
+
+ /* We output the name if and only if TREE_SYMBOL_REFERENCED is
+ set in order to avoid putting out names that are never really
+ used. */
+ if (TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl)))
+ {
+ if (!TARGET_EXPLICIT_RELOCS && mips_in_small_data_p (decl))
+ {
+ /* When using assembler macros, emit .extern directives for
+ all small-data externs so that the assembler knows how
+ big they are.
+
+ In most cases it would be safe (though pointless) to emit
+ .externs for other symbols too. One exception is when an
+ object is within the -G limit but declared by the user to
+ be in a section other than .sbss or .sdata. */
+ fputs ("\t.extern\t", file);
+ assemble_name (file, name);
+ fprintf (file, ", " HOST_WIDE_INT_PRINT_DEC "\n",
+ int_size_in_bytes (TREE_TYPE (decl)));
+ }
+ }
+}
+
+/* Implement TARGET_ASM_OUTPUT_SOURCE_FILENAME. */
+
+static void
+mips_output_filename (FILE *stream, const char *name)
+{
+ /* If we are emitting DWARF-2, let dwarf2out handle the ".file"
+ directives. */
+ if (write_symbols == DWARF2_DEBUG)
+ return;
+ else if (mips_output_filename_first_time)
+ {
+ mips_output_filename_first_time = 0;
+ num_source_filenames += 1;
+ current_function_file = name;
+ fprintf (stream, "\t.file\t%d ", num_source_filenames);
+ output_quoted_string (stream, name);
+ putc ('\n', stream);
+ }
+ /* If we are emitting stabs, let dbxout.c handle this (except for
+ the mips_output_filename_first_time case). */
+ else if (write_symbols == DBX_DEBUG)
+ return;
+ else if (name != current_function_file
+ && strcmp (name, current_function_file) != 0)
+ {
+ num_source_filenames += 1;
+ current_function_file = name;
+ fprintf (stream, "\t.file\t%d ", num_source_filenames);
+ output_quoted_string (stream, name);
+ putc ('\n', stream);
+ }
+}
+
+/* Implement TARGET_ASM_OUTPUT_DWARF_DTPREL. */
+
+static void ATTRIBUTE_UNUSED
+mips_output_dwarf_dtprel (FILE *file, int size, rtx x)
+{
+ switch (size)
+ {
+ case 4:
+ fputs ("\t.dtprelword\t", file);
+ break;
+
+ case 8:
+ fputs ("\t.dtpreldword\t", file);
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+ output_addr_const (file, x);
+ fputs ("+0x8000", file);
+}
+
+/* Implement TARGET_DWARF_REGISTER_SPAN. */
+
+static rtx
+mips_dwarf_register_span (rtx reg)
+{
+ rtx high, low;
+ enum machine_mode mode;
+
+ /* By default, GCC maps increasing register numbers to increasing
+ memory locations, but paired FPRs are always little-endian,
+ regardless of the prevailing endianness. */
+ mode = GET_MODE (reg);
+ if (FP_REG_P (REGNO (reg))
+ && TARGET_BIG_ENDIAN
+ && MAX_FPRS_PER_FMT > 1
+ && GET_MODE_SIZE (mode) > UNITS_PER_FPREG)
+ {
+ gcc_assert (GET_MODE_SIZE (mode) == UNITS_PER_HWFPVALUE);
+ high = mips_subword (reg, true);
+ low = mips_subword (reg, false);
+ return gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, high, low));
+ }
+
+ return NULL_RTX;
+}
+
+/* Implement ASM_OUTPUT_ASCII. */
+
+void
+mips_output_ascii (FILE *stream, const char *string, size_t len)
+{
+ size_t i;
+ int cur_pos;
+
+ cur_pos = 17;
+ fprintf (stream, "\t.ascii\t\"");
+ for (i = 0; i < len; i++)
+ {
+ int c;
+
+ c = (unsigned char) string[i];
+ if (ISPRINT (c))
+ {
+ if (c == '\\' || c == '\"')
+ {
+ putc ('\\', stream);
+ cur_pos++;
+ }
+ putc (c, stream);
+ cur_pos++;
+ }
+ else
+ {
+ fprintf (stream, "\\%03o", c);
+ cur_pos += 4;
+ }
+
+ if (cur_pos > 72 && i+1 < len)
+ {
+ cur_pos = 17;
+ fprintf (stream, "\"\n\t.ascii\t\"");
+ }
+ }
+ fprintf (stream, "\"\n");
+}
+
+/* Emit either a label, .comm, or .lcomm directive. When using assembler
+ macros, mark the symbol as written so that mips_asm_output_external
+ won't emit an .extern for it. STREAM is the output file, NAME is the
+ name of the symbol, INIT_STRING is the string that should be written
+ before the symbol and FINAL_STRING is the string that should be
+ written after it. FINAL_STRING is a printf format that consumes the
+ remaining arguments. */
+
+void
+mips_declare_object (FILE *stream, const char *name, const char *init_string,
+ const char *final_string, ...)
+{
+ va_list ap;
+
+ fputs (init_string, stream);
+ assemble_name (stream, name);
+ va_start (ap, final_string);
+ vfprintf (stream, final_string, ap);
+ va_end (ap);
+
+ if (!TARGET_EXPLICIT_RELOCS)
+ {
+ tree name_tree = get_identifier (name);
+ TREE_ASM_WRITTEN (name_tree) = 1;
+ }
+}
+
+/* Declare a common object of SIZE bytes using asm directive INIT_STRING.
+ NAME is the name of the object and ALIGN is the required alignment
+ in bytes. TAKES_ALIGNMENT_P is true if the directive takes a third
+ alignment argument. */
+
+void
+mips_declare_common_object (FILE *stream, const char *name,
+ const char *init_string,
+ unsigned HOST_WIDE_INT size,
+ unsigned int align, bool takes_alignment_p)
+{
+ if (!takes_alignment_p)
+ {
+ size += (align / BITS_PER_UNIT) - 1;
+ size -= size % (align / BITS_PER_UNIT);
+ mips_declare_object (stream, name, init_string,
+ "," HOST_WIDE_INT_PRINT_UNSIGNED "\n", size);
+ }
+ else
+ mips_declare_object (stream, name, init_string,
+ "," HOST_WIDE_INT_PRINT_UNSIGNED ",%u\n",
+ size, align / BITS_PER_UNIT);
+}
+
+/* Implement ASM_OUTPUT_ALIGNED_DECL_COMMON. This is usually the same as the
+ elfos.h version, but we also need to handle -muninit-const-in-rodata. */
+
+void
+mips_output_aligned_decl_common (FILE *stream, tree decl, const char *name,
+ unsigned HOST_WIDE_INT size,
+ unsigned int align)
+{
+ /* If the target wants uninitialized const declarations in
+ .rdata then don't put them in .comm. */
+ if (TARGET_EMBEDDED_DATA
+ && TARGET_UNINIT_CONST_IN_RODATA
+ && TREE_CODE (decl) == VAR_DECL
+ && TREE_READONLY (decl)
+ && (DECL_INITIAL (decl) == 0 || DECL_INITIAL (decl) == error_mark_node))
+ {
+ if (TREE_PUBLIC (decl) && DECL_NAME (decl))
+ targetm.asm_out.globalize_label (stream, name);
+
+ switch_to_section (readonly_data_section);
+ ASM_OUTPUT_ALIGN (stream, floor_log2 (align / BITS_PER_UNIT));
+ mips_declare_object (stream, name, "",
+ ":\n\t.space\t" HOST_WIDE_INT_PRINT_UNSIGNED "\n",
+ size);
+ }
+ else
+ mips_declare_common_object (stream, name, "\n\t.comm\t",
+ size, align, true);
+}
+
+#ifdef ASM_OUTPUT_SIZE_DIRECTIVE
+extern int size_directive_output;
+
+/* Implement ASM_DECLARE_OBJECT_NAME. This is like most of the standard ELF
+ definitions except that it uses mips_declare_object to emit the label. */
+
+void
+mips_declare_object_name (FILE *stream, const char *name,
+ tree decl ATTRIBUTE_UNUSED)
+{
+#ifdef ASM_OUTPUT_TYPE_DIRECTIVE
+ ASM_OUTPUT_TYPE_DIRECTIVE (stream, name, "object");
+#endif
+
+ size_directive_output = 0;
+ if (!flag_inhibit_size_directive && DECL_SIZE (decl))
+ {
+ HOST_WIDE_INT size;
+
+ size_directive_output = 1;
+ size = int_size_in_bytes (TREE_TYPE (decl));
+ ASM_OUTPUT_SIZE_DIRECTIVE (stream, name, size);
+ }
+
+ mips_declare_object (stream, name, "", ":\n");
+}
+
+/* Implement ASM_FINISH_DECLARE_OBJECT. This is generic ELF stuff. */
+
+void
+mips_finish_declare_object (FILE *stream, tree decl, int top_level, int at_end)
+{
+ const char *name;
+
+ name = XSTR (XEXP (DECL_RTL (decl), 0), 0);
+ if (!flag_inhibit_size_directive
+ && DECL_SIZE (decl) != 0
+ && !at_end
+ && top_level
+ && DECL_INITIAL (decl) == error_mark_node
+ && !size_directive_output)
+ {
+ HOST_WIDE_INT size;
+
+ size_directive_output = 1;
+ size = int_size_in_bytes (TREE_TYPE (decl));
+ ASM_OUTPUT_SIZE_DIRECTIVE (stream, name, size);
+ }
+}
+#endif
+
+/* Return the FOO in the name of the ".mdebug.FOO" section associated
+ with the current ABI. */
+
+static const char *
+mips_mdebug_abi_name (void)
+{
+ switch (mips_abi)
+ {
+ case ABI_32:
+ return "abi32";
+ case ABI_O64:
+ return "abiO64";
+ case ABI_N32:
+ return "abiN32";
+ case ABI_64:
+ return "abi64";
+ case ABI_EABI:
+ return TARGET_64BIT ? "eabi64" : "eabi32";
+ default:
+ gcc_unreachable ();
+ }
+}
+
+/* Implement TARGET_ASM_FILE_START. */
+
+static void
+mips_file_start (void)
+{
+ default_file_start ();
+
+ /* Generate a special section to describe the ABI switches used to
+ produce the resultant binary. This is unnecessary on IRIX and
+ causes unwanted warnings from the native linker. */
+ if (!TARGET_IRIX6)
+ {
+ /* Record the ABI itself. Modern versions of binutils encode
+ this information in the ELF header flags, but GDB needs the
+ information in order to correctly debug binaries produced by
+ older binutils. See the function mips_gdbarch_init in
+ gdb/mips-tdep.c. */
+ fprintf (asm_out_file, "\t.section .mdebug.%s\n\t.previous\n",
+ mips_mdebug_abi_name ());
+
+ /* There is no ELF header flag to distinguish long32 forms of the
+ EABI from long64 forms. Emit a special section to help tools
+ such as GDB. Do the same for o64, which is sometimes used with
+ -mlong64. */
+ if (mips_abi == ABI_EABI || mips_abi == ABI_O64)
+ fprintf (asm_out_file, "\t.section .gcc_compiled_long%d\n"
+ "\t.previous\n", TARGET_LONG64 ? 64 : 32);
+
+#ifdef HAVE_AS_GNU_ATTRIBUTE
+ {
+ int attr;
+
+ /* No floating-point operations, -mno-float. */
+ if (TARGET_NO_FLOAT)
+ attr = 0;
+ /* Soft-float code, -msoft-float. */
+ else if (!TARGET_HARD_FLOAT_ABI)
+ attr = 3;
+ /* Single-float code, -msingle-float. */
+ else if (!TARGET_DOUBLE_FLOAT)
+ attr = 2;
+ /* 64-bit FP registers on a 32-bit target, -mips32r2 -mfp64. */
+ else if (!TARGET_64BIT && TARGET_FLOAT64)
+ attr = 4;
+ /* Regular FP code, FP regs same size as GP regs, -mdouble-float. */
+ else
+ attr = 1;
+
+ fprintf (asm_out_file, "\t.gnu_attribute 4, %d\n", attr);
+ }
+#endif
+ }
+
+ /* If TARGET_ABICALLS, tell GAS to generate -KPIC code. */
+ if (TARGET_ABICALLS)
+ {
+ fprintf (asm_out_file, "\t.abicalls\n");
+ if (TARGET_ABICALLS_PIC0)
+ fprintf (asm_out_file, "\t.option\tpic0\n");
+ }
+
+ if (flag_verbose_asm)
+ fprintf (asm_out_file, "\n%s -G value = %d, Arch = %s, ISA = %d\n",
+ ASM_COMMENT_START,
+ mips_small_data_threshold, mips_arch_info->name, mips_isa);
+}
+
+/* Make the last instruction frame-related and note that it performs
+ the operation described by FRAME_PATTERN. */
+
+static void
+mips_set_frame_expr (rtx frame_pattern)
+{
+ rtx insn;
+
+ insn = get_last_insn ();
+ RTX_FRAME_RELATED_P (insn) = 1;
+ REG_NOTES (insn) = alloc_EXPR_LIST (REG_FRAME_RELATED_EXPR,
+ frame_pattern,
+ REG_NOTES (insn));
+}
+
+/* Return a frame-related rtx that stores REG at MEM.
+ REG must be a single register. */
+
+static rtx
+mips_frame_set (rtx mem, rtx reg)
+{
+ rtx set;
+
+ /* If we're saving the return address register and the DWARF return
+ address column differs from the hard register number, adjust the
+ note reg to refer to the former. */
+ if (REGNO (reg) == RETURN_ADDR_REGNUM
+ && DWARF_FRAME_RETURN_COLUMN != RETURN_ADDR_REGNUM)
+ reg = gen_rtx_REG (GET_MODE (reg), DWARF_FRAME_RETURN_COLUMN);
+
+ set = gen_rtx_SET (VOIDmode, mem, reg);
+ RTX_FRAME_RELATED_P (set) = 1;
+
+ return set;
+}
+
+/* If a MIPS16e SAVE or RESTORE instruction saves or restores register
+ mips16e_s2_s8_regs[X], it must also save the registers in indexes
+ X + 1 onwards. Likewise mips16e_a0_a3_regs. */
+static const unsigned char mips16e_s2_s8_regs[] = {
+ 30, 23, 22, 21, 20, 19, 18
+};
+static const unsigned char mips16e_a0_a3_regs[] = {
+ 4, 5, 6, 7
+};
+
+/* A list of the registers that can be saved by the MIPS16e SAVE instruction,
+ ordered from the uppermost in memory to the lowest in memory. */
+static const unsigned char mips16e_save_restore_regs[] = {
+ 31, 30, 23, 22, 21, 20, 19, 18, 17, 16, 7, 6, 5, 4
+};
+
+/* Return the index of the lowest X in the range [0, SIZE) for which
+ bit REGS[X] is set in MASK. Return SIZE if there is no such X. */
+
+static unsigned int
+mips16e_find_first_register (unsigned int mask, const unsigned char *regs,
+ unsigned int size)
+{
+ unsigned int i;
+
+ for (i = 0; i < size; i++)
+ if (BITSET_P (mask, regs[i]))
+ break;
+
+ return i;
+}
+
+/* *MASK_PTR is a mask of general-purpose registers and *NUM_REGS_PTR
+ is the number of set bits. If *MASK_PTR contains REGS[X] for some X
+ in [0, SIZE), adjust *MASK_PTR and *NUM_REGS_PTR so that the same
+ is true for all indexes (X, SIZE). */
+
+static void
+mips16e_mask_registers (unsigned int *mask_ptr, const unsigned char *regs,
+ unsigned int size, unsigned int *num_regs_ptr)
+{
+ unsigned int i;
+
+ i = mips16e_find_first_register (*mask_ptr, regs, size);
+ for (i++; i < size; i++)
+ if (!BITSET_P (*mask_ptr, regs[i]))
+ {
+ *num_regs_ptr += 1;
+ *mask_ptr |= 1 << regs[i];
+ }
+}
+
+/* Return a simplified form of X using the register values in REG_VALUES.
+ REG_VALUES[R] is the last value assigned to hard register R, or null
+ if R has not been modified.
+
+ This function is rather limited, but is good enough for our purposes. */
+
+static rtx
+mips16e_collect_propagate_value (rtx x, rtx *reg_values)
+{
+ x = avoid_constant_pool_reference (x);
+
+ if (UNARY_P (x))
+ {
+ rtx x0 = mips16e_collect_propagate_value (XEXP (x, 0), reg_values);
+ return simplify_gen_unary (GET_CODE (x), GET_MODE (x),
+ x0, GET_MODE (XEXP (x, 0)));
+ }
+
+ if (ARITHMETIC_P (x))
+ {
+ rtx x0 = mips16e_collect_propagate_value (XEXP (x, 0), reg_values);
+ rtx x1 = mips16e_collect_propagate_value (XEXP (x, 1), reg_values);
+ return simplify_gen_binary (GET_CODE (x), GET_MODE (x), x0, x1);
+ }
+
+ if (REG_P (x)
+ && reg_values[REGNO (x)]
+ && !rtx_unstable_p (reg_values[REGNO (x)]))
+ return reg_values[REGNO (x)];
+
+ return x;
+}
+
+/* Return true if (set DEST SRC) stores an argument register into its
+ caller-allocated save slot, storing the number of that argument
+ register in *REGNO_PTR if so. REG_VALUES is as for
+ mips16e_collect_propagate_value. */
+
+static bool
+mips16e_collect_argument_save_p (rtx dest, rtx src, rtx *reg_values,
+ unsigned int *regno_ptr)
+{
+ unsigned int argno, regno;
+ HOST_WIDE_INT offset, required_offset;
+ rtx addr, base;
+
+ /* Check that this is a word-mode store. */
+ if (!MEM_P (dest) || !REG_P (src) || GET_MODE (dest) != word_mode)
+ return false;
+
+ /* Check that the register being saved is an unmodified argument
+ register. */
+ regno = REGNO (src);
+ if (!IN_RANGE (regno, GP_ARG_FIRST, GP_ARG_LAST) || reg_values[regno])
+ return false;
+ argno = regno - GP_ARG_FIRST;
+
+ /* Check whether the address is an appropriate stack-pointer or
+ frame-pointer access. */
+ addr = mips16e_collect_propagate_value (XEXP (dest, 0), reg_values);
+ mips_split_plus (addr, &base, &offset);
+ required_offset = cfun->machine->frame.total_size + argno * UNITS_PER_WORD;
+ if (base == hard_frame_pointer_rtx)
+ required_offset -= cfun->machine->frame.hard_frame_pointer_offset;
+ else if (base != stack_pointer_rtx)
+ return false;
+ if (offset != required_offset)
+ return false;
+
+ *regno_ptr = regno;
+ return true;
+}
+
+/* A subroutine of mips_expand_prologue, called only when generating
+ MIPS16e SAVE instructions. Search the start of the function for any
+ instructions that save argument registers into their caller-allocated
+ save slots. Delete such instructions and return a value N such that
+ saving [GP_ARG_FIRST, GP_ARG_FIRST + N) would make all the deleted
+ instructions redundant. */
+
+static unsigned int
+mips16e_collect_argument_saves (void)
+{
+ rtx reg_values[FIRST_PSEUDO_REGISTER];
+ rtx insn, next, set, dest, src;
+ unsigned int nargs, regno;
+
+ push_topmost_sequence ();
+ nargs = 0;
+ memset (reg_values, 0, sizeof (reg_values));
+ for (insn = get_insns (); insn; insn = next)
+ {
+ next = NEXT_INSN (insn);
+ if (NOTE_P (insn) || DEBUG_INSN_P (insn))
+ continue;
+
+ if (!INSN_P (insn))
+ break;
+
+ set = PATTERN (insn);
+ if (GET_CODE (set) != SET)
+ break;
+
+ dest = SET_DEST (set);
+ src = SET_SRC (set);
+ if (mips16e_collect_argument_save_p (dest, src, reg_values, &regno))
+ {
+ if (!BITSET_P (cfun->machine->frame.mask, regno))
+ {
+ delete_insn (insn);
+ nargs = MAX (nargs, (regno - GP_ARG_FIRST) + 1);
+ }
+ }
+ else if (REG_P (dest) && GET_MODE (dest) == word_mode)
+ reg_values[REGNO (dest)]
+ = mips16e_collect_propagate_value (src, reg_values);
+ else
+ break;
+ }
+ pop_topmost_sequence ();
+
+ return nargs;
+}
+
+/* Return a move between register REGNO and memory location SP + OFFSET.
+ Make the move a load if RESTORE_P, otherwise make it a frame-related
+ store. */
+
+static rtx
+mips16e_save_restore_reg (bool restore_p, HOST_WIDE_INT offset,
+ unsigned int regno)
+{
+ rtx reg, mem;
+
+ mem = gen_frame_mem (SImode, plus_constant (stack_pointer_rtx, offset));
+ reg = gen_rtx_REG (SImode, regno);
+ return (restore_p
+ ? gen_rtx_SET (VOIDmode, reg, mem)
+ : mips_frame_set (mem, reg));
+}
+
+/* Return RTL for a MIPS16e SAVE or RESTORE instruction; RESTORE_P says which.
+ The instruction must:
+
+ - Allocate or deallocate SIZE bytes in total; SIZE is known
+ to be nonzero.
+
+ - Save or restore as many registers in *MASK_PTR as possible.
+ The instruction saves the first registers at the top of the
+ allocated area, with the other registers below it.
+
+ - Save NARGS argument registers above the allocated area.
+
+ (NARGS is always zero if RESTORE_P.)
+
+ The SAVE and RESTORE instructions cannot save and restore all general
+ registers, so there may be some registers left over for the caller to
+ handle. Destructively modify *MASK_PTR so that it contains the registers
+ that still need to be saved or restored. The caller can save these
+ registers in the memory immediately below *OFFSET_PTR, which is a
+ byte offset from the bottom of the allocated stack area. */
+
+static rtx
+mips16e_build_save_restore (bool restore_p, unsigned int *mask_ptr,
+ HOST_WIDE_INT *offset_ptr, unsigned int nargs,
+ HOST_WIDE_INT size)
+{
+ rtx pattern, set;
+ HOST_WIDE_INT offset, top_offset;
+ unsigned int i, regno;
+ int n;
+
+ gcc_assert (cfun->machine->frame.num_fp == 0);
+
+ /* Calculate the number of elements in the PARALLEL. We need one element
+ for the stack adjustment, one for each argument register save, and one
+ for each additional register move. */
+ n = 1 + nargs;
+ for (i = 0; i < ARRAY_SIZE (mips16e_save_restore_regs); i++)
+ if (BITSET_P (*mask_ptr, mips16e_save_restore_regs[i]))
+ n++;
+
+ /* Create the final PARALLEL. */
+ pattern = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (n));
+ n = 0;
+
+ /* Add the stack pointer adjustment. */
+ set = gen_rtx_SET (VOIDmode, stack_pointer_rtx,
+ plus_constant (stack_pointer_rtx,
+ restore_p ? size : -size));
+ RTX_FRAME_RELATED_P (set) = 1;
+ XVECEXP (pattern, 0, n++) = set;
+
+ /* Stack offsets in the PARALLEL are relative to the old stack pointer. */
+ top_offset = restore_p ? size : 0;
+
+ /* Save the arguments. */
+ for (i = 0; i < nargs; i++)
+ {
+ offset = top_offset + i * UNITS_PER_WORD;
+ set = mips16e_save_restore_reg (restore_p, offset, GP_ARG_FIRST + i);
+ XVECEXP (pattern, 0, n++) = set;
+ }
+
+ /* Then fill in the other register moves. */
+ offset = top_offset;
+ for (i = 0; i < ARRAY_SIZE (mips16e_save_restore_regs); i++)
+ {
+ regno = mips16e_save_restore_regs[i];
+ if (BITSET_P (*mask_ptr, regno))
+ {
+ offset -= UNITS_PER_WORD;
+ set = mips16e_save_restore_reg (restore_p, offset, regno);
+ XVECEXP (pattern, 0, n++) = set;
+ *mask_ptr &= ~(1 << regno);
+ }
+ }
+
+ /* Tell the caller what offset it should use for the remaining registers. */
+ *offset_ptr = size + (offset - top_offset);
+
+ gcc_assert (n == XVECLEN (pattern, 0));
+
+ return pattern;
+}
+
+/* PATTERN is a PARALLEL whose first element adds ADJUST to the stack
+ pointer. Return true if PATTERN matches the kind of instruction
+ generated by mips16e_build_save_restore. If INFO is nonnull,
+ initialize it when returning true. */
+
+bool
+mips16e_save_restore_pattern_p (rtx pattern, HOST_WIDE_INT adjust,
+ struct mips16e_save_restore_info *info)
+{
+ unsigned int i, nargs, mask, extra;
+ HOST_WIDE_INT top_offset, save_offset, offset;
+ rtx set, reg, mem, base;
+ int n;
+
+ if (!GENERATE_MIPS16E_SAVE_RESTORE)
+ return false;
+
+ /* Stack offsets in the PARALLEL are relative to the old stack pointer. */
+ top_offset = adjust > 0 ? adjust : 0;
+
+ /* Interpret all other members of the PARALLEL. */
+ save_offset = top_offset - UNITS_PER_WORD;
+ mask = 0;
+ nargs = 0;
+ i = 0;
+ for (n = 1; n < XVECLEN (pattern, 0); n++)
+ {
+ /* Check that we have a SET. */
+ set = XVECEXP (pattern, 0, n);
+ if (GET_CODE (set) != SET)
+ return false;
+
+ /* Check that the SET is a load (if restoring) or a store
+ (if saving). */
+ mem = adjust > 0 ? SET_SRC (set) : SET_DEST (set);
+ if (!MEM_P (mem))
+ return false;
+
+ /* Check that the address is the sum of the stack pointer and a
+ possibly-zero constant offset. */
+ mips_split_plus (XEXP (mem, 0), &base, &offset);
+ if (base != stack_pointer_rtx)
+ return false;
+
+ /* Check that SET's other operand is a register. */
+ reg = adjust > 0 ? SET_DEST (set) : SET_SRC (set);
+ if (!REG_P (reg))
+ return false;
+
+ /* Check for argument saves. */
+ if (offset == top_offset + nargs * UNITS_PER_WORD
+ && REGNO (reg) == GP_ARG_FIRST + nargs)
+ nargs++;
+ else if (offset == save_offset)
+ {
+ while (mips16e_save_restore_regs[i++] != REGNO (reg))
+ if (i == ARRAY_SIZE (mips16e_save_restore_regs))
+ return false;
+
+ mask |= 1 << REGNO (reg);
+ save_offset -= UNITS_PER_WORD;
+ }
+ else
+ return false;
+ }
+
+ /* Check that the restrictions on register ranges are met. */
+ extra = 0;
+ mips16e_mask_registers (&mask, mips16e_s2_s8_regs,
+ ARRAY_SIZE (mips16e_s2_s8_regs), &extra);
+ mips16e_mask_registers (&mask, mips16e_a0_a3_regs,
+ ARRAY_SIZE (mips16e_a0_a3_regs), &extra);
+ if (extra != 0)
+ return false;
+
+ /* Make sure that the topmost argument register is not saved twice.
+ The checks above ensure that the same is then true for the other
+ argument registers. */
+ if (nargs > 0 && BITSET_P (mask, GP_ARG_FIRST + nargs - 1))
+ return false;
+
+ /* Pass back information, if requested. */
+ if (info)
+ {
+ info->nargs = nargs;
+ info->mask = mask;
+ info->size = (adjust > 0 ? adjust : -adjust);
+ }
+
+ return true;
+}
+
+/* Add a MIPS16e SAVE or RESTORE register-range argument to string S
+ for the register range [MIN_REG, MAX_REG]. Return a pointer to
+ the null terminator. */
+
+static char *
+mips16e_add_register_range (char *s, unsigned int min_reg,
+ unsigned int max_reg)
+{
+ if (min_reg != max_reg)
+ s += sprintf (s, ",%s-%s", reg_names[min_reg], reg_names[max_reg]);
+ else
+ s += sprintf (s, ",%s", reg_names[min_reg]);
+ return s;
+}
+
+/* Return the assembly instruction for a MIPS16e SAVE or RESTORE instruction.
+ PATTERN and ADJUST are as for mips16e_save_restore_pattern_p. */
+
+const char *
+mips16e_output_save_restore (rtx pattern, HOST_WIDE_INT adjust)
+{
+ static char buffer[300];
+
+ struct mips16e_save_restore_info info;
+ unsigned int i, end;
+ char *s;
+
+ /* Parse the pattern. */
+ if (!mips16e_save_restore_pattern_p (pattern, adjust, &info))
+ gcc_unreachable ();
+
+ /* Add the mnemonic. */
+ s = strcpy (buffer, adjust > 0 ? "restore\t" : "save\t");
+ s += strlen (s);
+
+ /* Save the arguments. */
+ if (info.nargs > 1)
+ s += sprintf (s, "%s-%s,", reg_names[GP_ARG_FIRST],
+ reg_names[GP_ARG_FIRST + info.nargs - 1]);
+ else if (info.nargs == 1)
+ s += sprintf (s, "%s,", reg_names[GP_ARG_FIRST]);
+
+ /* Emit the amount of stack space to allocate or deallocate. */
+ s += sprintf (s, "%d", (int) info.size);
+
+ /* Save or restore $16. */
+ if (BITSET_P (info.mask, 16))
+ s += sprintf (s, ",%s", reg_names[GP_REG_FIRST + 16]);
+
+ /* Save or restore $17. */
+ if (BITSET_P (info.mask, 17))
+ s += sprintf (s, ",%s", reg_names[GP_REG_FIRST + 17]);
+
+ /* Save or restore registers in the range $s2...$s8, which
+ mips16e_s2_s8_regs lists in decreasing order. Note that this
+ is a software register range; the hardware registers are not
+ numbered consecutively. */
+ end = ARRAY_SIZE (mips16e_s2_s8_regs);
+ i = mips16e_find_first_register (info.mask, mips16e_s2_s8_regs, end);
+ if (i < end)
+ s = mips16e_add_register_range (s, mips16e_s2_s8_regs[end - 1],
+ mips16e_s2_s8_regs[i]);
+
+ /* Save or restore registers in the range $a0...$a3. */
+ end = ARRAY_SIZE (mips16e_a0_a3_regs);
+ i = mips16e_find_first_register (info.mask, mips16e_a0_a3_regs, end);
+ if (i < end)
+ s = mips16e_add_register_range (s, mips16e_a0_a3_regs[i],
+ mips16e_a0_a3_regs[end - 1]);
+
+ /* Save or restore $31. */
+ if (BITSET_P (info.mask, RETURN_ADDR_REGNUM))
+ s += sprintf (s, ",%s", reg_names[RETURN_ADDR_REGNUM]);
+
+ return buffer;
+}
+
+/* Return true if the current function returns its value in a floating-point
+ register in MIPS16 mode. */
+
+static bool
+mips16_cfun_returns_in_fpr_p (void)
+{
+ tree return_type = DECL_RESULT (current_function_decl);
+ return (TARGET_MIPS16
+ && TARGET_HARD_FLOAT_ABI
+ && !aggregate_value_p (return_type, current_function_decl)
+ && mips_return_mode_in_fpr_p (DECL_MODE (return_type)));
+}
+
+/* Return true if predicate PRED is true for at least one instruction.
+ Cache the result in *CACHE, and assume that the result is true
+ if *CACHE is already true. */
+
+static bool
+mips_find_gp_ref (bool *cache, bool (*pred) (rtx))
+{
+ rtx insn;
+
+ if (!*cache)
+ {
+ push_topmost_sequence ();
+ for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+ if (USEFUL_INSN_P (insn) && pred (insn))
+ {
+ *cache = true;
+ break;
+ }
+ pop_topmost_sequence ();
+ }
+ return *cache;
+}
+
+/* Return true if INSN refers to the global pointer in an "inflexible" way.
+ See mips_cfun_has_inflexible_gp_ref_p for details. */
+
+static bool
+mips_insn_has_inflexible_gp_ref_p (rtx insn)
+{
+ /* Uses of pic_offset_table_rtx in CALL_INSN_FUNCTION_USAGE
+ indicate that the target could be a traditional MIPS
+ lazily-binding stub. */
+ return find_reg_fusage (insn, USE, pic_offset_table_rtx);
+}
+
+/* Return true if the current function refers to the global pointer
+ in a way that forces $28 to be valid. This means that we can't
+ change the choice of global pointer, even for NewABI code.
+
+ One example of this (and one which needs several checks) is that
+ $28 must be valid when calling traditional MIPS lazy-binding stubs.
+ (This restriction does not apply to PLTs.) */
+
+static bool
+mips_cfun_has_inflexible_gp_ref_p (void)
+{
+ /* If the function has a nonlocal goto, $28 must hold the correct
+ global pointer for the target function. That is, the target
+ of the goto implicitly uses $28. */
+ if (crtl->has_nonlocal_goto)
+ return true;
+
+ if (TARGET_ABICALLS_PIC2)
+ {
+ /* Symbolic accesses implicitly use the global pointer unless
+ -mexplicit-relocs is in effect. JAL macros to symbolic addresses
+ might go to traditional MIPS lazy-binding stubs. */
+ if (!TARGET_EXPLICIT_RELOCS)
+ return true;
+
+ /* FUNCTION_PROFILER includes a JAL to _mcount, which again
+ can be lazily-bound. */
+ if (crtl->profile)
+ return true;
+
+ /* MIPS16 functions that return in FPRs need to call an
+ external libgcc routine. This call is only made explict
+ during mips_expand_epilogue, and it too might be lazily bound. */
+ if (mips16_cfun_returns_in_fpr_p ())
+ return true;
+ }
+
+ return mips_find_gp_ref (&cfun->machine->has_inflexible_gp_insn_p,
+ mips_insn_has_inflexible_gp_ref_p);
+}
+
+/* Return true if INSN refers to the global pointer in a "flexible" way.
+ See mips_cfun_has_flexible_gp_ref_p for details. */
+
+static bool
+mips_insn_has_flexible_gp_ref_p (rtx insn)
+{
+ return (get_attr_got (insn) != GOT_UNSET
+ || mips_small_data_pattern_p (PATTERN (insn))
+ || reg_overlap_mentioned_p (pic_offset_table_rtx, PATTERN (insn)));
+}
+
+/* Return true if the current function references the global pointer,
+ but if those references do not inherently require the global pointer
+ to be $28. Assume !mips_cfun_has_inflexible_gp_ref_p (). */
+
+static bool
+mips_cfun_has_flexible_gp_ref_p (void)
+{
+ /* Reload can sometimes introduce constant pool references
+ into a function that otherwise didn't need them. For example,
+ suppose we have an instruction like:
+
+ (set (reg:DF R1) (float:DF (reg:SI R2)))
+
+ If R2 turns out to be a constant such as 1, the instruction may
+ have a REG_EQUAL note saying that R1 == 1.0. Reload then has
+ the option of using this constant if R2 doesn't get allocated
+ to a register.
+
+ In cases like these, reload will have added the constant to the
+ pool but no instruction will yet refer to it. */
+ if (TARGET_ABICALLS_PIC2 && !reload_completed && crtl->uses_const_pool)
+ return true;
+
+ return mips_find_gp_ref (&cfun->machine->has_flexible_gp_insn_p,
+ mips_insn_has_flexible_gp_ref_p);
+}
+
+/* Return the register that should be used as the global pointer
+ within this function. Return INVALID_REGNUM if the function
+ doesn't need a global pointer. */
+
+static unsigned int
+mips_global_pointer (void)
+{
+ unsigned int regno;
+
+ /* $gp is always available unless we're using a GOT. */
+ if (!TARGET_USE_GOT)
+ return GLOBAL_POINTER_REGNUM;
+
+ /* If there are inflexible references to $gp, we must use the
+ standard register. */
+ if (mips_cfun_has_inflexible_gp_ref_p ())
+ return GLOBAL_POINTER_REGNUM;
+
+ /* If there are no current references to $gp, then the only uses
+ we can introduce later are those involved in long branches. */
+ if (TARGET_ABSOLUTE_JUMPS && !mips_cfun_has_flexible_gp_ref_p ())
+ return INVALID_REGNUM;
+
+ /* If the global pointer is call-saved, try to use a call-clobbered
+ alternative. */
+ if (TARGET_CALL_SAVED_GP && current_function_is_leaf)
+ for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
+ if (!df_regs_ever_live_p (regno)
+ && call_really_used_regs[regno]
+ && !fixed_regs[regno]
+ && regno != PIC_FUNCTION_ADDR_REGNUM)
+ return regno;
+
+ return GLOBAL_POINTER_REGNUM;
+}
+
+/* Return true if the current function's prologue must load the global
+ pointer value into pic_offset_table_rtx and store the same value in
+ the function's cprestore slot (if any).
+
+ One problem we have to deal with is that, when emitting GOT-based
+ position independent code, long-branch sequences will need to load
+ the address of the branch target from the GOT. We don't know until
+ the very end of compilation whether (and where) the function needs
+ long branches, so we must ensure that _any_ branch can access the
+ global pointer in some form. However, we do not want to pessimize
+ the usual case in which all branches are short.
+
+ We handle this as follows:
+
+ (1) During reload, we set cfun->machine->global_pointer to
+ INVALID_REGNUM if we _know_ that the current function
+ doesn't need a global pointer. This is only valid if
+ long branches don't need the GOT.
+
+ Otherwise, we assume that we might need a global pointer
+ and pick an appropriate register.
+
+ (2) If cfun->machine->global_pointer != INVALID_REGNUM,
+ we ensure that the global pointer is available at every
+ block boundary bar entry and exit. We do this in one of two ways:
+
+ - If the function has a cprestore slot, we ensure that this
+ slot is valid at every branch. However, as explained in
+ point (6) below, there is no guarantee that pic_offset_table_rtx
+ itself is valid if new uses of the global pointer are introduced
+ after the first post-epilogue split.
+
+ We guarantee that the cprestore slot is valid by loading it
+ into a fake register, CPRESTORE_SLOT_REGNUM. We then make
+ this register live at every block boundary bar function entry
+ and exit. It is then invalid to move the load (and thus the
+ preceding store) across a block boundary.
+
+ - If the function has no cprestore slot, we guarantee that
+ pic_offset_table_rtx itself is valid at every branch.
+
+ See mips_eh_uses for the handling of the register liveness.
+
+ (3) During prologue and epilogue generation, we emit "ghost"
+ placeholder instructions to manipulate the global pointer.
+
+ (4) During prologue generation, we set cfun->machine->must_initialize_gp_p
+ and cfun->machine->must_restore_gp_when_clobbered_p if we already know
+ that the function needs a global pointer. (There is no need to set
+ them earlier than this, and doing it as late as possible leads to
+ fewer false positives.)
+
+ (5) If cfun->machine->must_initialize_gp_p is true during a
+ split_insns pass, we split the ghost instructions into real
+ instructions. These split instructions can then be optimized in
+ the usual way. Otherwise, we keep the ghost instructions intact,
+ and optimize for the case where they aren't needed. We still
+ have the option of splitting them later, if we need to introduce
+ new uses of the global pointer.
+
+ For example, the scheduler ignores a ghost instruction that
+ stores $28 to the stack, but it handles the split form of
+ the ghost instruction as an ordinary store.
+
+ (6) [OldABI only.] If cfun->machine->must_restore_gp_when_clobbered_p
+ is true during the first post-epilogue split_insns pass, we split
+ calls and restore_gp patterns into instructions that explicitly
+ load pic_offset_table_rtx from the cprestore slot. Otherwise,
+ we split these patterns into instructions that _don't_ load from
+ the cprestore slot.
+
+ If cfun->machine->must_restore_gp_when_clobbered_p is true at the
+ time of the split, then any instructions that exist at that time
+ can make free use of pic_offset_table_rtx. However, if we want
+ to introduce new uses of the global pointer after the split,
+ we must explicitly load the value from the cprestore slot, since
+ pic_offset_table_rtx itself might not be valid at a given point
+ in the function.
+
+ The idea is that we want to be able to delete redundant
+ loads from the cprestore slot in the usual case where no
+ long branches are needed.
+
+ (7) If cfun->machine->must_initialize_gp_p is still false at the end
+ of md_reorg, we decide whether the global pointer is needed for
+ long branches. If so, we set cfun->machine->must_initialize_gp_p
+ to true and split the ghost instructions into real instructions
+ at that stage.
+
+ Note that the ghost instructions must have a zero length for three reasons:
+
+ - Giving the length of the underlying $gp sequence might cause
+ us to use long branches in cases where they aren't really needed.
+
+ - They would perturb things like alignment calculations.
+
+ - More importantly, the hazard detection in md_reorg relies on
+ empty instructions having a zero length.
+
+ If we find a long branch and split the ghost instructions at the
+ end of md_reorg, the split could introduce more long branches.
+ That isn't a problem though, because we still do the split before
+ the final shorten_branches pass.
+
+ This is extremely ugly, but it seems like the best compromise between
+ correctness and efficiency. */
+
+bool
+mips_must_initialize_gp_p (void)
+{
+ return cfun->machine->must_initialize_gp_p;
+}
+
+/* Return true if REGNO is a register that is ordinarily call-clobbered
+ but must nevertheless be preserved by an interrupt handler. */
+
+static bool
+mips_interrupt_extra_call_saved_reg_p (unsigned int regno)
+{
+ if (MD_REG_P (regno))
+ return true;
+
+ if (TARGET_DSP && DSP_ACC_REG_P (regno))
+ return true;
+
+ if (GP_REG_P (regno) && !cfun->machine->use_shadow_register_set_p)
+ {
+ /* $0 is hard-wired. */
+ if (regno == GP_REG_FIRST)
+ return false;
+
+ /* The interrupt handler can treat kernel registers as
+ scratch registers. */
+ if (KERNEL_REG_P (regno))
+ return false;
+
+ /* The function will return the stack pointer to its original value
+ anyway. */
+ if (regno == STACK_POINTER_REGNUM)
+ return false;
+
+ /* Otherwise, return true for registers that aren't ordinarily
+ call-clobbered. */
+ return call_really_used_regs[regno];
+ }
+
+ return false;
+}
+
+/* Return true if the current function should treat register REGNO
+ as call-saved. */
+
+static bool
+mips_cfun_call_saved_reg_p (unsigned int regno)
+{
+ /* If the user makes an ordinarily-call-saved register global,
+ that register is no longer call-saved. */
+ if (global_regs[regno])
+ return false;
+
+ /* Interrupt handlers need to save extra registers. */
+ if (cfun->machine->interrupt_handler_p
+ && mips_interrupt_extra_call_saved_reg_p (regno))
+ return true;
+
+ /* call_insns preserve $28 unless they explicitly say otherwise,
+ so call_really_used_regs[] treats $28 as call-saved. However,
+ we want the ABI property rather than the default call_insn
+ property here. */
+ return (regno == GLOBAL_POINTER_REGNUM
+ ? TARGET_CALL_SAVED_GP
+ : !call_really_used_regs[regno]);
+}
+
+/* Return true if the function body might clobber register REGNO.
+ We know that REGNO is call-saved. */
+
+static bool
+mips_cfun_might_clobber_call_saved_reg_p (unsigned int regno)
+{
+ /* Some functions should be treated as clobbering all call-saved
+ registers. */
+ if (crtl->saves_all_registers)
+ return true;
+
+ /* DF handles cases where a register is explicitly referenced in
+ the rtl. Incoming values are passed in call-clobbered registers,
+ so we can assume that any live call-saved register is set within
+ the function. */
+ if (df_regs_ever_live_p (regno))
+ return true;
+
+ /* Check for registers that are clobbered by FUNCTION_PROFILER.
+ These clobbers are not explicit in the rtl. */
+ if (crtl->profile && MIPS_SAVE_REG_FOR_PROFILING_P (regno))
+ return true;
+
+ /* If we're using a call-saved global pointer, the function's
+ prologue will need to set it up. */
+ if (cfun->machine->global_pointer == regno)
+ return true;
+
+ /* The function's prologue will need to set the frame pointer if
+ frame_pointer_needed. */
+ if (regno == HARD_FRAME_POINTER_REGNUM && frame_pointer_needed)
+ return true;
+
+ /* If a MIPS16 function returns a value in FPRs, its epilogue
+ will need to call an external libgcc routine. This yet-to-be
+ generated call_insn will clobber $31. */
+ if (regno == RETURN_ADDR_REGNUM && mips16_cfun_returns_in_fpr_p ())
+ return true;
+
+ /* If REGNO is ordinarily call-clobbered, we must assume that any
+ called function could modify it. */
+ if (cfun->machine->interrupt_handler_p
+ && !current_function_is_leaf
+ && mips_interrupt_extra_call_saved_reg_p (regno))
+ return true;
+
+ return false;
+}
+
+/* Return true if the current function must save register REGNO. */
+
+static bool
+mips_save_reg_p (unsigned int regno)
+{
+ if (mips_cfun_call_saved_reg_p (regno))
+ {
+ if (mips_cfun_might_clobber_call_saved_reg_p (regno))
+ return true;
+
+ /* Save both registers in an FPR pair if either one is used. This is
+ needed for the case when MIN_FPRS_PER_FMT == 1, which allows the odd
+ register to be used without the even register. */
+ if (FP_REG_P (regno)
+ && MAX_FPRS_PER_FMT == 2
+ && mips_cfun_might_clobber_call_saved_reg_p (regno + 1))
+ return true;
+ }
+
+ /* We need to save the incoming return address if __builtin_eh_return
+ is being used to set a different return address. */
+ if (regno == RETURN_ADDR_REGNUM && crtl->calls_eh_return)
+ return true;
+
+ return false;
+}
+
+/* Populate the current function's mips_frame_info structure.
+
+ MIPS stack frames look like:
+
+ +-------------------------------+
+ | |
+ | incoming stack arguments |
+ | |
+ +-------------------------------+
+ | |
+ | caller-allocated save area |
+ A | for register arguments |
+ | |
+ +-------------------------------+ <-- incoming stack pointer
+ | |
+ | callee-allocated save area |
+ B | for arguments that are |
+ | split between registers and |
+ | the stack |
+ | |
+ +-------------------------------+ <-- arg_pointer_rtx
+ | |
+ C | callee-allocated save area |
+ | for register varargs |
+ | |
+ +-------------------------------+ <-- frame_pointer_rtx
+ | | + cop0_sp_offset
+ | COP0 reg save area | + UNITS_PER_WORD
+ | |
+ +-------------------------------+ <-- frame_pointer_rtx + acc_sp_offset
+ | | + UNITS_PER_WORD
+ | accumulator save area |
+ | |
+ +-------------------------------+ <-- stack_pointer_rtx + fp_sp_offset
+ | | + UNITS_PER_HWFPVALUE
+ | FPR save area |
+ | |
+ +-------------------------------+ <-- stack_pointer_rtx + gp_sp_offset
+ | | + UNITS_PER_WORD
+ | GPR save area |
+ | |
+ +-------------------------------+ <-- frame_pointer_rtx with
+ | | \ -fstack-protector
+ | local variables | | var_size
+ | | /
+ +-------------------------------+
+ | | \
+ | $gp save area | | cprestore_size
+ | | /
+ P +-------------------------------+ <-- hard_frame_pointer_rtx for
+ | | \ MIPS16 code
+ | outgoing stack arguments | |
+ | | |
+ +-------------------------------+ | args_size
+ | | |
+ | caller-allocated save area | |
+ | for register arguments | |
+ | | /
+ +-------------------------------+ <-- stack_pointer_rtx
+ frame_pointer_rtx without
+ -fstack-protector
+ hard_frame_pointer_rtx for
+ non-MIPS16 code.
+
+ At least two of A, B and C will be empty.
+
+ Dynamic stack allocations such as alloca insert data at point P.
+ They decrease stack_pointer_rtx but leave frame_pointer_rtx and
+ hard_frame_pointer_rtx unchanged. */
+
+static void
+mips_compute_frame_info (void)
+{
+ struct mips_frame_info *frame;
+ HOST_WIDE_INT offset, size;
+ unsigned int regno, i;
+
+ /* Set this function's interrupt properties. */
+ if (mips_interrupt_type_p (TREE_TYPE (current_function_decl)))
+ {
+ if (!ISA_MIPS32R2)
+ error ("the %<interrupt%> attribute requires a MIPS32r2 processor");
+ else if (TARGET_HARD_FLOAT)
+ error ("the %<interrupt%> attribute requires %<-msoft-float%>");
+ else if (TARGET_MIPS16)
+ error ("interrupt handlers cannot be MIPS16 functions");
+ else
+ {
+ cfun->machine->interrupt_handler_p = true;
+ cfun->machine->use_shadow_register_set_p =
+ mips_use_shadow_register_set_p (TREE_TYPE (current_function_decl));
+ cfun->machine->keep_interrupts_masked_p =
+ mips_keep_interrupts_masked_p (TREE_TYPE (current_function_decl));
+ cfun->machine->use_debug_exception_return_p =
+ mips_use_debug_exception_return_p (TREE_TYPE
+ (current_function_decl));
+ }
+ }
+
+ frame = &cfun->machine->frame;
+ memset (frame, 0, sizeof (*frame));
+ size = get_frame_size ();
+
+ cfun->machine->global_pointer = mips_global_pointer ();
+
+ /* The first two blocks contain the outgoing argument area and the $gp save
+ slot. This area isn't needed in leaf functions, but if the
+ target-independent frame size is nonzero, we have already committed to
+ allocating these in STARTING_FRAME_OFFSET for !FRAME_GROWS_DOWNWARD. */
+ if ((size == 0 || FRAME_GROWS_DOWNWARD) && current_function_is_leaf)
+ {
+ /* The MIPS 3.0 linker does not like functions that dynamically
+ allocate the stack and have 0 for STACK_DYNAMIC_OFFSET, since it
+ looks like we are trying to create a second frame pointer to the
+ function, so allocate some stack space to make it happy. */
+ if (cfun->calls_alloca)
+ frame->args_size = REG_PARM_STACK_SPACE (cfun->decl);
+ else
+ frame->args_size = 0;
+ frame->cprestore_size = 0;
+ }
+ else
+ {
+ frame->args_size = crtl->outgoing_args_size;
+ frame->cprestore_size = MIPS_GP_SAVE_AREA_SIZE;
+ }
+ offset = frame->args_size + frame->cprestore_size;
+
+ /* Move above the local variables. */
+ frame->var_size = MIPS_STACK_ALIGN (size);
+ offset += frame->var_size;
+
+ /* Find out which GPRs we need to save. */
+ for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
+ if (mips_save_reg_p (regno))
+ {
+ frame->num_gp++;
+ frame->mask |= 1 << (regno - GP_REG_FIRST);
+ }
+
+ /* If this function calls eh_return, we must also save and restore the
+ EH data registers. */
+ if (crtl->calls_eh_return)
+ for (i = 0; EH_RETURN_DATA_REGNO (i) != INVALID_REGNUM; i++)
+ {
+ frame->num_gp++;
+ frame->mask |= 1 << (EH_RETURN_DATA_REGNO (i) - GP_REG_FIRST);
+ }
+
+ /* The MIPS16e SAVE and RESTORE instructions have two ranges of registers:
+ $a3-$a0 and $s2-$s8. If we save one register in the range, we must
+ save all later registers too. */
+ if (GENERATE_MIPS16E_SAVE_RESTORE)
+ {
+ mips16e_mask_registers (&frame->mask, mips16e_s2_s8_regs,
+ ARRAY_SIZE (mips16e_s2_s8_regs), &frame->num_gp);
+ mips16e_mask_registers (&frame->mask, mips16e_a0_a3_regs,
+ ARRAY_SIZE (mips16e_a0_a3_regs), &frame->num_gp);
+ }
+
+ /* Move above the GPR save area. */
+ if (frame->num_gp > 0)
+ {
+ offset += MIPS_STACK_ALIGN (frame->num_gp * UNITS_PER_WORD);
+ frame->gp_sp_offset = offset - UNITS_PER_WORD;
+ }
+
+ /* Find out which FPRs we need to save. This loop must iterate over
+ the same space as its companion in mips_for_each_saved_gpr_and_fpr. */
+ if (TARGET_HARD_FLOAT)
+ for (regno = FP_REG_FIRST; regno <= FP_REG_LAST; regno += MAX_FPRS_PER_FMT)
+ if (mips_save_reg_p (regno))
+ {
+ frame->num_fp += MAX_FPRS_PER_FMT;
+ frame->fmask |= ~(~0 << MAX_FPRS_PER_FMT) << (regno - FP_REG_FIRST);
+ }
+
+ /* Move above the FPR save area. */
+ if (frame->num_fp > 0)
+ {
+ offset += MIPS_STACK_ALIGN (frame->num_fp * UNITS_PER_FPREG);
+ frame->fp_sp_offset = offset - UNITS_PER_HWFPVALUE;
+ }
+
+ /* Add in space for the interrupt context information. */
+ if (cfun->machine->interrupt_handler_p)
+ {
+ /* Check HI/LO. */
+ if (mips_save_reg_p (LO_REGNUM) || mips_save_reg_p (HI_REGNUM))
+ {
+ frame->num_acc++;
+ frame->acc_mask |= (1 << 0);
+ }
+
+ /* Check accumulators 1, 2, 3. */
+ for (i = DSP_ACC_REG_FIRST; i <= DSP_ACC_REG_LAST; i += 2)
+ if (mips_save_reg_p (i) || mips_save_reg_p (i + 1))
+ {
+ frame->num_acc++;
+ frame->acc_mask |= 1 << (((i - DSP_ACC_REG_FIRST) / 2) + 1);
+ }
+
+ /* All interrupt context functions need space to preserve STATUS. */
+ frame->num_cop0_regs++;
+
+ /* If we don't keep interrupts masked, we need to save EPC. */
+ if (!cfun->machine->keep_interrupts_masked_p)
+ frame->num_cop0_regs++;
+ }
+
+ /* Move above the accumulator save area. */
+ if (frame->num_acc > 0)
+ {
+ /* Each accumulator needs 2 words. */
+ offset += frame->num_acc * 2 * UNITS_PER_WORD;
+ frame->acc_sp_offset = offset - UNITS_PER_WORD;
+ }
+
+ /* Move above the COP0 register save area. */
+ if (frame->num_cop0_regs > 0)
+ {
+ offset += frame->num_cop0_regs * UNITS_PER_WORD;
+ frame->cop0_sp_offset = offset - UNITS_PER_WORD;
+ }
+
+ /* Move above the callee-allocated varargs save area. */
+ offset += MIPS_STACK_ALIGN (cfun->machine->varargs_size);
+ frame->arg_pointer_offset = offset;
+
+ /* Move above the callee-allocated area for pretend stack arguments. */
+ offset += crtl->args.pretend_args_size;
+ frame->total_size = offset;
+
+ /* Work out the offsets of the save areas from the top of the frame. */
+ if (frame->gp_sp_offset > 0)
+ frame->gp_save_offset = frame->gp_sp_offset - offset;
+ if (frame->fp_sp_offset > 0)
+ frame->fp_save_offset = frame->fp_sp_offset - offset;
+ if (frame->acc_sp_offset > 0)
+ frame->acc_save_offset = frame->acc_sp_offset - offset;
+ if (frame->num_cop0_regs > 0)
+ frame->cop0_save_offset = frame->cop0_sp_offset - offset;
+
+ /* MIPS16 code offsets the frame pointer by the size of the outgoing
+ arguments. This tends to increase the chances of using unextended
+ instructions for local variables and incoming arguments. */
+ if (TARGET_MIPS16)
+ frame->hard_frame_pointer_offset = frame->args_size;
+}
+
+/* Return the style of GP load sequence that is being used for the
+ current function. */
+
+enum mips_loadgp_style
+mips_current_loadgp_style (void)
+{
+ if (!TARGET_USE_GOT || cfun->machine->global_pointer == INVALID_REGNUM)
+ return LOADGP_NONE;
+
+ if (TARGET_RTP_PIC)
+ return LOADGP_RTP;
+
+ if (TARGET_ABSOLUTE_ABICALLS)
+ return LOADGP_ABSOLUTE;
+
+ return TARGET_NEWABI ? LOADGP_NEWABI : LOADGP_OLDABI;
+}
+
+/* Implement TARGET_FRAME_POINTER_REQUIRED. */
+
+static bool
+mips_frame_pointer_required (void)
+{
+ /* If the function contains dynamic stack allocations, we need to
+ use the frame pointer to access the static parts of the frame. */
+ if (cfun->calls_alloca)
+ return true;
+
+ /* In MIPS16 mode, we need a frame pointer for a large frame; otherwise,
+ reload may be unable to compute the address of a local variable,
+ since there is no way to add a large constant to the stack pointer
+ without using a second temporary register. */
+ if (TARGET_MIPS16)
+ {
+ mips_compute_frame_info ();
+ if (!SMALL_OPERAND (cfun->machine->frame.total_size))
+ return true;
+ }
+
+ return false;
+}
+
+/* Make sure that we're not trying to eliminate to the wrong hard frame
+ pointer. */
+
+static bool
+mips_can_eliminate (const int from ATTRIBUTE_UNUSED, const int to)
+{
+ return (to == HARD_FRAME_POINTER_REGNUM || to == STACK_POINTER_REGNUM);
+}
+
+/* Implement INITIAL_ELIMINATION_OFFSET. FROM is either the frame pointer
+ or argument pointer. TO is either the stack pointer or hard frame
+ pointer. */
+
+HOST_WIDE_INT
+mips_initial_elimination_offset (int from, int to)
+{
+ HOST_WIDE_INT offset;
+
+ mips_compute_frame_info ();
+
+ /* Set OFFSET to the offset from the end-of-prologue stack pointer. */
+ switch (from)
+ {
+ case FRAME_POINTER_REGNUM:
+ if (FRAME_GROWS_DOWNWARD)
+ offset = (cfun->machine->frame.args_size
+ + cfun->machine->frame.cprestore_size
+ + cfun->machine->frame.var_size);
+ else
+ offset = 0;
+ break;
+
+ case ARG_POINTER_REGNUM:
+ offset = cfun->machine->frame.arg_pointer_offset;
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+
+ if (to == HARD_FRAME_POINTER_REGNUM)
+ offset -= cfun->machine->frame.hard_frame_pointer_offset;
+
+ return offset;
+}
+
+/* Implement TARGET_EXTRA_LIVE_ON_ENTRY. */
+
+static void
+mips_extra_live_on_entry (bitmap regs)
+{
+ if (TARGET_USE_GOT)
+ {
+ /* PIC_FUNCTION_ADDR_REGNUM is live if we need it to set up
+ the global pointer. */
+ if (!TARGET_ABSOLUTE_ABICALLS)
+ bitmap_set_bit (regs, PIC_FUNCTION_ADDR_REGNUM);
+
+ /* The prologue may set MIPS16_PIC_TEMP_REGNUM to the value of
+ the global pointer. */
+ if (TARGET_MIPS16)
+ bitmap_set_bit (regs, MIPS16_PIC_TEMP_REGNUM);
+
+ /* See the comment above load_call<mode> for details. */
+ bitmap_set_bit (regs, GOT_VERSION_REGNUM);
+ }
+}
+
+/* Implement RETURN_ADDR_RTX. We do not support moving back to a
+ previous frame. */
+
+rtx
+mips_return_addr (int count, rtx frame ATTRIBUTE_UNUSED)
+{
+ if (count != 0)
+ return const0_rtx;
+
+ return get_hard_reg_initial_val (Pmode, RETURN_ADDR_REGNUM);
+}
+
+/* Emit code to change the current function's return address to
+ ADDRESS. SCRATCH is available as a scratch register, if needed.
+ ADDRESS and SCRATCH are both word-mode GPRs. */
+
+void
+mips_set_return_address (rtx address, rtx scratch)
+{
+ rtx slot_address;
+
+ gcc_assert (BITSET_P (cfun->machine->frame.mask, RETURN_ADDR_REGNUM));
+ slot_address = mips_add_offset (scratch, stack_pointer_rtx,
+ cfun->machine->frame.gp_sp_offset);
+ mips_emit_move (gen_frame_mem (GET_MODE (address), slot_address), address);
+}
+
+/* Return true if the current function has a cprestore slot. */
+
+bool
+mips_cfun_has_cprestore_slot_p (void)
+{
+ return (cfun->machine->global_pointer != INVALID_REGNUM
+ && cfun->machine->frame.cprestore_size > 0);
+}
+
+/* Fill *BASE and *OFFSET such that *BASE + *OFFSET refers to the
+ cprestore slot. LOAD_P is true if the caller wants to load from
+ the cprestore slot; it is false if the caller wants to store to
+ the slot. */
+
+static void
+mips_get_cprestore_base_and_offset (rtx *base, HOST_WIDE_INT *offset,
+ bool load_p)
+{
+ const struct mips_frame_info *frame;
+
+ frame = &cfun->machine->frame;
+ /* .cprestore always uses the stack pointer instead of the frame pointer.
+ We have a free choice for direct stores for non-MIPS16 functions,
+ and for MIPS16 functions whose cprestore slot is in range of the
+ stack pointer. Using the stack pointer would sometimes give more
+ (early) scheduling freedom, but using the frame pointer would
+ sometimes give more (late) scheduling freedom. It's hard to
+ predict which applies to a given function, so let's keep things
+ simple.
+
+ Loads must always use the frame pointer in functions that call
+ alloca, and there's little benefit to using the stack pointer
+ otherwise. */
+ if (frame_pointer_needed && !(TARGET_CPRESTORE_DIRECTIVE && !load_p))
+ {
+ *base = hard_frame_pointer_rtx;
+ *offset = frame->args_size - frame->hard_frame_pointer_offset;
+ }
+ else
+ {
+ *base = stack_pointer_rtx;
+ *offset = frame->args_size;
+ }
+}
+
+/* Return true if X is the load or store address of the cprestore slot;
+ LOAD_P says which. */
+
+bool
+mips_cprestore_address_p (rtx x, bool load_p)
+{
+ rtx given_base, required_base;
+ HOST_WIDE_INT given_offset, required_offset;
+
+ mips_split_plus (x, &given_base, &given_offset);
+ mips_get_cprestore_base_and_offset (&required_base, &required_offset, load_p);
+ return given_base == required_base && given_offset == required_offset;
+}
+
+/* Return a MEM rtx for the cprestore slot. LOAD_P is true if we are
+ going to load from it, false if we are going to store to it.
+ Use TEMP as a temporary register if need be. */
+
+static rtx
+mips_cprestore_slot (rtx temp, bool load_p)
+{
+ rtx base;
+ HOST_WIDE_INT offset;
+
+ mips_get_cprestore_base_and_offset (&base, &offset, load_p);
+ return gen_frame_mem (Pmode, mips_add_offset (temp, base, offset));
+}
+
+/* Emit instructions to save global pointer value GP into cprestore
+ slot MEM. OFFSET is the offset that MEM applies to the base register.
+
+ MEM may not be a legitimate address. If it isn't, TEMP is a
+ temporary register that can be used, otherwise it is a SCRATCH. */
+
+void
+mips_save_gp_to_cprestore_slot (rtx mem, rtx offset, rtx gp, rtx temp)
+{
+ if (TARGET_CPRESTORE_DIRECTIVE)
+ {
+ gcc_assert (gp == pic_offset_table_rtx);
+ emit_insn (gen_cprestore (mem, offset));
+ }
+ else
+ mips_emit_move (mips_cprestore_slot (temp, false), gp);
+}
+
+/* Restore $gp from its save slot, using TEMP as a temporary base register
+ if need be. This function is for o32 and o64 abicalls only.
+
+ See mips_must_initialize_gp_p for details about how we manage the
+ global pointer. */
+
+void
+mips_restore_gp_from_cprestore_slot (rtx temp)
+{
+ gcc_assert (TARGET_ABICALLS && TARGET_OLDABI && epilogue_completed);
+
+ if (!cfun->machine->must_restore_gp_when_clobbered_p)
+ {
+ emit_note (NOTE_INSN_DELETED);
+ return;
+ }
+
+ if (TARGET_MIPS16)
+ {
+ mips_emit_move (temp, mips_cprestore_slot (temp, true));
+ mips_emit_move (pic_offset_table_rtx, temp);
+ }
+ else
+ mips_emit_move (pic_offset_table_rtx, mips_cprestore_slot (temp, true));
+ if (!TARGET_EXPLICIT_RELOCS)
+ emit_insn (gen_blockage ());
+}
+
+/* A function to save or store a register. The first argument is the
+ register and the second is the stack slot. */
+typedef void (*mips_save_restore_fn) (rtx, rtx);
+
+/* Use FN to save or restore register REGNO. MODE is the register's
+ mode and OFFSET is the offset of its save slot from the current
+ stack pointer. */
+
+static void
+mips_save_restore_reg (enum machine_mode mode, int regno,
+ HOST_WIDE_INT offset, mips_save_restore_fn fn)
+{
+ rtx mem;
+
+ mem = gen_frame_mem (mode, plus_constant (stack_pointer_rtx, offset));
+ fn (gen_rtx_REG (mode, regno), mem);
+}
+
+/* Call FN for each accumlator that is saved by the current function.
+ SP_OFFSET is the offset of the current stack pointer from the start
+ of the frame. */
+
+static void
+mips_for_each_saved_acc (HOST_WIDE_INT sp_offset, mips_save_restore_fn fn)
+{
+ HOST_WIDE_INT offset;
+ int regno;
+
+ offset = cfun->machine->frame.acc_sp_offset - sp_offset;
+ if (BITSET_P (cfun->machine->frame.acc_mask, 0))
+ {
+ mips_save_restore_reg (word_mode, LO_REGNUM, offset, fn);
+ offset -= UNITS_PER_WORD;
+ mips_save_restore_reg (word_mode, HI_REGNUM, offset, fn);
+ offset -= UNITS_PER_WORD;
+ }
+
+ for (regno = DSP_ACC_REG_FIRST; regno <= DSP_ACC_REG_LAST; regno++)
+ if (BITSET_P (cfun->machine->frame.acc_mask,
+ ((regno - DSP_ACC_REG_FIRST) / 2) + 1))
+ {
+ mips_save_restore_reg (word_mode, regno, offset, fn);
+ offset -= UNITS_PER_WORD;
+ }
+}
+
+/* Call FN for each register that is saved by the current function.
+ SP_OFFSET is the offset of the current stack pointer from the start
+ of the frame. */
+
+static void
+mips_for_each_saved_gpr_and_fpr (HOST_WIDE_INT sp_offset,
+ mips_save_restore_fn fn)
+{
+ enum machine_mode fpr_mode;
+ HOST_WIDE_INT offset;
+ int regno;
+
+ /* Save registers starting from high to low. The debuggers prefer at least
+ the return register be stored at func+4, and also it allows us not to
+ need a nop in the epilogue if at least one register is reloaded in
+ addition to return address. */
+ offset = cfun->machine->frame.gp_sp_offset - sp_offset;
+ for (regno = GP_REG_LAST; regno >= GP_REG_FIRST; regno--)
+ if (BITSET_P (cfun->machine->frame.mask, regno - GP_REG_FIRST))
+ {
+ /* Record the ra offset for use by mips_function_profiler. */
+ if (regno == RETURN_ADDR_REGNUM)
+ cfun->machine->frame.ra_fp_offset = offset + sp_offset;
+ mips_save_restore_reg (word_mode, regno, offset, fn);
+ offset -= UNITS_PER_WORD;
+ }
+
+ /* This loop must iterate over the same space as its companion in
+ mips_compute_frame_info. */
+ offset = cfun->machine->frame.fp_sp_offset - sp_offset;
+ fpr_mode = (TARGET_SINGLE_FLOAT ? SFmode : DFmode);
+ for (regno = FP_REG_LAST - MAX_FPRS_PER_FMT + 1;
+ regno >= FP_REG_FIRST;
+ regno -= MAX_FPRS_PER_FMT)
+ if (BITSET_P (cfun->machine->frame.fmask, regno - FP_REG_FIRST))
+ {
+ mips_save_restore_reg (fpr_mode, regno, offset, fn);
+ offset -= GET_MODE_SIZE (fpr_mode);
+ }
+}
+
+/* Return true if a move between register REGNO and its save slot (MEM)
+ can be done in a single move. LOAD_P is true if we are loading
+ from the slot, false if we are storing to it. */
+
+static bool
+mips_direct_save_slot_move_p (unsigned int regno, rtx mem, bool load_p)
+{
+ /* There is a specific MIPS16 instruction for saving $31 to the stack. */
+ if (TARGET_MIPS16 && !load_p && regno == RETURN_ADDR_REGNUM)
+ return false;
+
+ return mips_secondary_reload_class (REGNO_REG_CLASS (regno),
+ GET_MODE (mem), mem, load_p) == NO_REGS;
+}
+
+/* Emit a move from SRC to DEST, given that one of them is a register
+ save slot and that the other is a register. TEMP is a temporary
+ GPR of the same mode that is available if need be. */
+
+void
+mips_emit_save_slot_move (rtx dest, rtx src, rtx temp)
+{
+ unsigned int regno;
+ rtx mem;
+
+ if (REG_P (src))
+ {
+ regno = REGNO (src);
+ mem = dest;
+ }
+ else
+ {
+ regno = REGNO (dest);
+ mem = src;
+ }
+
+ if (regno == cfun->machine->global_pointer && !mips_must_initialize_gp_p ())
+ {
+ /* We don't yet know whether we'll need this instruction or not.
+ Postpone the decision by emitting a ghost move. This move
+ is specifically not frame-related; only the split version is. */
+ if (TARGET_64BIT)
+ emit_insn (gen_move_gpdi (dest, src));
+ else
+ emit_insn (gen_move_gpsi (dest, src));
+ return;
+ }
+
+ if (regno == HI_REGNUM)
+ {
+ if (REG_P (dest))
+ {
+ mips_emit_move (temp, src);
+ if (TARGET_64BIT)
+ emit_insn (gen_mthisi_di (gen_rtx_REG (TImode, MD_REG_FIRST),
+ temp, gen_rtx_REG (DImode, LO_REGNUM)));
+ else
+ emit_insn (gen_mthisi_di (gen_rtx_REG (DImode, MD_REG_FIRST),
+ temp, gen_rtx_REG (SImode, LO_REGNUM)));
+ }
+ else
+ {
+ if (TARGET_64BIT)
+ emit_insn (gen_mfhidi_ti (temp,
+ gen_rtx_REG (TImode, MD_REG_FIRST)));
+ else
+ emit_insn (gen_mfhisi_di (temp,
+ gen_rtx_REG (DImode, MD_REG_FIRST)));
+ mips_emit_move (dest, temp);
+ }
+ }
+ else if (mips_direct_save_slot_move_p (regno, mem, mem == src))
+ mips_emit_move (dest, src);
+ else
+ {
+ gcc_assert (!reg_overlap_mentioned_p (dest, temp));
+ mips_emit_move (temp, src);
+ mips_emit_move (dest, temp);
+ }
+ if (MEM_P (dest))
+ mips_set_frame_expr (mips_frame_set (dest, src));
+}
+
+/* If we're generating n32 or n64 abicalls, and the current function
+ does not use $28 as its global pointer, emit a cplocal directive.
+ Use pic_offset_table_rtx as the argument to the directive. */
+
+static void
+mips_output_cplocal (void)
+{
+ if (!TARGET_EXPLICIT_RELOCS
+ && mips_must_initialize_gp_p ()
+ && cfun->machine->global_pointer != GLOBAL_POINTER_REGNUM)
+ output_asm_insn (".cplocal %+", 0);
+}
+
+/* Implement TARGET_OUTPUT_FUNCTION_PROLOGUE. */
+
+static void
+mips_output_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
+{
+ const char *fnname;
+
+#ifdef SDB_DEBUGGING_INFO
+ if (debug_info_level != DINFO_LEVEL_TERSE && write_symbols == SDB_DEBUG)
+ SDB_OUTPUT_SOURCE_LINE (file, DECL_SOURCE_LINE (current_function_decl));
+#endif
+
+ /* In MIPS16 mode, we may need to generate a non-MIPS16 stub to handle
+ floating-point arguments. */
+ if (TARGET_MIPS16
+ && TARGET_HARD_FLOAT_ABI
+ && crtl->args.info.fp_code != 0)
+ mips16_build_function_stub ();
+
+ /* Get the function name the same way that toplev.c does before calling
+ assemble_start_function. This is needed so that the name used here
+ exactly matches the name used in ASM_DECLARE_FUNCTION_NAME. */
+ fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0);
+ mips_start_function_definition (fnname, TARGET_MIPS16);
+
+ /* Output MIPS-specific frame information. */
+ if (!flag_inhibit_size_directive)
+ {
+ const struct mips_frame_info *frame;
+
+ frame = &cfun->machine->frame;
+
+ /* .frame FRAMEREG, FRAMESIZE, RETREG. */
+ fprintf (file,
+ "\t.frame\t%s," HOST_WIDE_INT_PRINT_DEC ",%s\t\t"
+ "# vars= " HOST_WIDE_INT_PRINT_DEC
+ ", regs= %d/%d"
+ ", args= " HOST_WIDE_INT_PRINT_DEC
+ ", gp= " HOST_WIDE_INT_PRINT_DEC "\n",
+ reg_names[frame_pointer_needed
+ ? HARD_FRAME_POINTER_REGNUM
+ : STACK_POINTER_REGNUM],
+ (frame_pointer_needed
+ ? frame->total_size - frame->hard_frame_pointer_offset
+ : frame->total_size),
+ reg_names[RETURN_ADDR_REGNUM],
+ frame->var_size,
+ frame->num_gp, frame->num_fp,
+ frame->args_size,
+ frame->cprestore_size);
+
+ /* .mask MASK, OFFSET. */
+ fprintf (file, "\t.mask\t0x%08x," HOST_WIDE_INT_PRINT_DEC "\n",
+ frame->mask, frame->gp_save_offset);
+
+ /* .fmask MASK, OFFSET. */
+ fprintf (file, "\t.fmask\t0x%08x," HOST_WIDE_INT_PRINT_DEC "\n",
+ frame->fmask, frame->fp_save_offset);
+ }
+
+ /* Handle the initialization of $gp for SVR4 PIC, if applicable.
+ Also emit the ".set noreorder; .set nomacro" sequence for functions
+ that need it. */
+ if (mips_must_initialize_gp_p ()
+ && mips_current_loadgp_style () == LOADGP_OLDABI)
+ {
+ if (TARGET_MIPS16)
+ {
+ /* This is a fixed-form sequence. The position of the
+ first two instructions is important because of the
+ way _gp_disp is defined. */
+ output_asm_insn ("li\t$2,%%hi(_gp_disp)", 0);
+ output_asm_insn ("addiu\t$3,$pc,%%lo(_gp_disp)", 0);
+ output_asm_insn ("sll\t$2,16", 0);
+ output_asm_insn ("addu\t$2,$3", 0);
+ }
+ else
+ {
+ /* .cpload must be in a .set noreorder but not a
+ .set nomacro block. */
+ mips_push_asm_switch (&mips_noreorder);
+ output_asm_insn (".cpload\t%^", 0);
+ if (!cfun->machine->all_noreorder_p)
+ mips_pop_asm_switch (&mips_noreorder);
+ else
+ mips_push_asm_switch (&mips_nomacro);
+ }
+ }
+ else if (cfun->machine->all_noreorder_p)
+ {
+ mips_push_asm_switch (&mips_noreorder);
+ mips_push_asm_switch (&mips_nomacro);
+ }
+
+ /* Tell the assembler which register we're using as the global
+ pointer. This is needed for thunks, since they can use either
+ explicit relocs or assembler macros. */
+ mips_output_cplocal ();
+}
+
+/* Implement TARGET_OUTPUT_FUNCTION_EPILOGUE. */
+
+static void
+mips_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED,
+ HOST_WIDE_INT size ATTRIBUTE_UNUSED)
+{
+ const char *fnname;
+
+ /* Reinstate the normal $gp. */
+ SET_REGNO (pic_offset_table_rtx, GLOBAL_POINTER_REGNUM);
+ mips_output_cplocal ();
+
+ if (cfun->machine->all_noreorder_p)
+ {
+ mips_pop_asm_switch (&mips_nomacro);
+ mips_pop_asm_switch (&mips_noreorder);
+ }
+
+ /* Get the function name the same way that toplev.c does before calling
+ assemble_start_function. This is needed so that the name used here
+ exactly matches the name used in ASM_DECLARE_FUNCTION_NAME. */
+ fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0);
+ mips_end_function_definition (fnname);
+}
+
+/* Save register REG to MEM. Make the instruction frame-related. */
+
+static void
+mips_save_reg (rtx reg, rtx mem)
+{
+ if (GET_MODE (reg) == DFmode && !TARGET_FLOAT64)
+ {
+ rtx x1, x2;
+
+ if (mips_split_64bit_move_p (mem, reg))
+ mips_split_doubleword_move (mem, reg);
+ else
+ mips_emit_move (mem, reg);
+
+ x1 = mips_frame_set (mips_subword (mem, false),
+ mips_subword (reg, false));
+ x2 = mips_frame_set (mips_subword (mem, true),
+ mips_subword (reg, true));
+ mips_set_frame_expr (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, x1, x2)));
+ }
+ else
+ mips_emit_save_slot_move (mem, reg, MIPS_PROLOGUE_TEMP (GET_MODE (reg)));
+}
+
+/* The __gnu_local_gp symbol. */
+
+static GTY(()) rtx mips_gnu_local_gp;
+
+/* If we're generating n32 or n64 abicalls, emit instructions
+ to set up the global pointer. */
+
+static void
+mips_emit_loadgp (void)
+{
+ rtx addr, offset, incoming_address, base, index, pic_reg;
+
+ pic_reg = TARGET_MIPS16 ? MIPS16_PIC_TEMP : pic_offset_table_rtx;
+ switch (mips_current_loadgp_style ())
+ {
+ case LOADGP_ABSOLUTE:
+ if (mips_gnu_local_gp == NULL)
+ {
+ mips_gnu_local_gp = gen_rtx_SYMBOL_REF (Pmode, "__gnu_local_gp");
+ SYMBOL_REF_FLAGS (mips_gnu_local_gp) |= SYMBOL_FLAG_LOCAL;
+ }
+ emit_insn (Pmode == SImode
+ ? gen_loadgp_absolute_si (pic_reg, mips_gnu_local_gp)
+ : gen_loadgp_absolute_di (pic_reg, mips_gnu_local_gp));
+ break;
+
+ case LOADGP_OLDABI:
+ /* Added by mips_output_function_prologue. */
+ break;
+
+ case LOADGP_NEWABI:
+ addr = XEXP (DECL_RTL (current_function_decl), 0);
+ offset = mips_unspec_address (addr, SYMBOL_GOTOFF_LOADGP);
+ incoming_address = gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM);
+ emit_insn (Pmode == SImode
+ ? gen_loadgp_newabi_si (pic_reg, offset, incoming_address)
+ : gen_loadgp_newabi_di (pic_reg, offset, incoming_address));
+ break;
+
+ case LOADGP_RTP:
+ base = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (VXWORKS_GOTT_BASE));
+ index = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (VXWORKS_GOTT_INDEX));
+ emit_insn (Pmode == SImode
+ ? gen_loadgp_rtp_si (pic_reg, base, index)
+ : gen_loadgp_rtp_di (pic_reg, base, index));
+ break;
+
+ default:
+ return;
+ }
+
+ if (TARGET_MIPS16)
+ emit_insn (gen_copygp_mips16 (pic_offset_table_rtx, pic_reg));
+
+ /* Emit a blockage if there are implicit uses of the GP register.
+ This includes profiled functions, because FUNCTION_PROFILE uses
+ a jal macro. */
+ if (!TARGET_EXPLICIT_RELOCS || crtl->profile)
+ emit_insn (gen_loadgp_blockage ());
+}
+
+/* A for_each_rtx callback. Stop the search if *X is a kernel register. */
+
+static int
+mips_kernel_reg_p (rtx *x, void *data ATTRIBUTE_UNUSED)
+{
+ return REG_P (*x) && KERNEL_REG_P (REGNO (*x));
+}
+
+/* Expand the "prologue" pattern. */
+
+void
+mips_expand_prologue (void)
+{
+ const struct mips_frame_info *frame;
+ HOST_WIDE_INT size;
+ unsigned int nargs;
+ rtx insn;
+
+ if (cfun->machine->global_pointer != INVALID_REGNUM)
+ {
+ /* Check whether an insn uses pic_offset_table_rtx, either explicitly
+ or implicitly. If so, we can commit to using a global pointer
+ straight away, otherwise we need to defer the decision. */
+ if (mips_cfun_has_inflexible_gp_ref_p ()
+ || mips_cfun_has_flexible_gp_ref_p ())
+ {
+ cfun->machine->must_initialize_gp_p = true;
+ cfun->machine->must_restore_gp_when_clobbered_p = true;
+ }
+
+ SET_REGNO (pic_offset_table_rtx, cfun->machine->global_pointer);
+ }
+
+ frame = &cfun->machine->frame;
+ size = frame->total_size;
+
+ if (flag_stack_usage)
+ current_function_static_stack_size = size;
+
+ /* Save the registers. Allocate up to MIPS_MAX_FIRST_STACK_STEP
+ bytes beforehand; this is enough to cover the register save area
+ without going out of range. */
+ if (((frame->mask | frame->fmask | frame->acc_mask) != 0)
+ || frame->num_cop0_regs > 0)
+ {
+ HOST_WIDE_INT step1;
+
+ step1 = MIN (size, MIPS_MAX_FIRST_STACK_STEP);
+ if (GENERATE_MIPS16E_SAVE_RESTORE)
+ {
+ HOST_WIDE_INT offset;
+ unsigned int mask, regno;
+
+ /* Try to merge argument stores into the save instruction. */
+ nargs = mips16e_collect_argument_saves ();
+
+ /* Build the save instruction. */
+ mask = frame->mask;
+ insn = mips16e_build_save_restore (false, &mask, &offset,
+ nargs, step1);
+ RTX_FRAME_RELATED_P (emit_insn (insn)) = 1;
+ size -= step1;
+
+ /* Check if we need to save other registers. */
+ for (regno = GP_REG_FIRST; regno < GP_REG_LAST; regno++)
+ if (BITSET_P (mask, regno - GP_REG_FIRST))
+ {
+ offset -= UNITS_PER_WORD;
+ mips_save_restore_reg (word_mode, regno,
+ offset, mips_save_reg);
+ }
+ }
+ else
+ {
+ if (cfun->machine->interrupt_handler_p)
+ {
+ HOST_WIDE_INT offset;
+ rtx mem;
+
+ /* If this interrupt is using a shadow register set, we need to
+ get the stack pointer from the previous register set. */
+ if (cfun->machine->use_shadow_register_set_p)
+ emit_insn (gen_mips_rdpgpr (stack_pointer_rtx,
+ stack_pointer_rtx));
+
+ if (!cfun->machine->keep_interrupts_masked_p)
+ {
+ /* Move from COP0 Cause to K0. */
+ emit_insn (gen_cop0_move (gen_rtx_REG (SImode, K0_REG_NUM),
+ gen_rtx_REG (SImode,
+ COP0_CAUSE_REG_NUM)));
+ /* Move from COP0 EPC to K1. */
+ emit_insn (gen_cop0_move (gen_rtx_REG (SImode, K1_REG_NUM),
+ gen_rtx_REG (SImode,
+ COP0_EPC_REG_NUM)));
+ }
+
+ /* Allocate the first part of the frame. */
+ insn = gen_add3_insn (stack_pointer_rtx, stack_pointer_rtx,
+ GEN_INT (-step1));
+ RTX_FRAME_RELATED_P (emit_insn (insn)) = 1;
+ size -= step1;
+
+ /* Start at the uppermost location for saving. */
+ offset = frame->cop0_sp_offset - size;
+ if (!cfun->machine->keep_interrupts_masked_p)
+ {
+ /* Push EPC into its stack slot. */
+ mem = gen_frame_mem (word_mode,
+ plus_constant (stack_pointer_rtx,
+ offset));
+ mips_emit_move (mem, gen_rtx_REG (word_mode, K1_REG_NUM));
+ offset -= UNITS_PER_WORD;
+ }
+
+ /* Move from COP0 Status to K1. */
+ emit_insn (gen_cop0_move (gen_rtx_REG (SImode, K1_REG_NUM),
+ gen_rtx_REG (SImode,
+ COP0_STATUS_REG_NUM)));
+
+ /* Right justify the RIPL in k0. */
+ if (!cfun->machine->keep_interrupts_masked_p)
+ emit_insn (gen_lshrsi3 (gen_rtx_REG (SImode, K0_REG_NUM),
+ gen_rtx_REG (SImode, K0_REG_NUM),
+ GEN_INT (CAUSE_IPL)));
+
+ /* Push Status into its stack slot. */
+ mem = gen_frame_mem (word_mode,
+ plus_constant (stack_pointer_rtx, offset));
+ mips_emit_move (mem, gen_rtx_REG (word_mode, K1_REG_NUM));
+ offset -= UNITS_PER_WORD;
+
+ /* Insert the RIPL into our copy of SR (k1) as the new IPL. */
+ if (!cfun->machine->keep_interrupts_masked_p)
+ emit_insn (gen_insvsi (gen_rtx_REG (SImode, K1_REG_NUM),
+ GEN_INT (6),
+ GEN_INT (SR_IPL),
+ gen_rtx_REG (SImode, K0_REG_NUM)));
+
+ if (!cfun->machine->keep_interrupts_masked_p)
+ /* Enable interrupts by clearing the KSU ERL and EXL bits.
+ IE is already the correct value, so we don't have to do
+ anything explicit. */
+ emit_insn (gen_insvsi (gen_rtx_REG (SImode, K1_REG_NUM),
+ GEN_INT (4),
+ GEN_INT (SR_EXL),
+ gen_rtx_REG (SImode, GP_REG_FIRST)));
+ else
+ /* Disable interrupts by clearing the KSU, ERL, EXL,
+ and IE bits. */
+ emit_insn (gen_insvsi (gen_rtx_REG (SImode, K1_REG_NUM),
+ GEN_INT (5),
+ GEN_INT (SR_IE),
+ gen_rtx_REG (SImode, GP_REG_FIRST)));
+ }
+ else
+ {
+ insn = gen_add3_insn (stack_pointer_rtx,
+ stack_pointer_rtx,
+ GEN_INT (-step1));
+ RTX_FRAME_RELATED_P (emit_insn (insn)) = 1;
+ size -= step1;
+ }
+ mips_for_each_saved_acc (size, mips_save_reg);
+ mips_for_each_saved_gpr_and_fpr (size, mips_save_reg);
+ }
+ }
+
+ /* Allocate the rest of the frame. */
+ if (size > 0)
+ {
+ if (SMALL_OPERAND (-size))
+ RTX_FRAME_RELATED_P (emit_insn (gen_add3_insn (stack_pointer_rtx,
+ stack_pointer_rtx,
+ GEN_INT (-size)))) = 1;
+ else
+ {
+ mips_emit_move (MIPS_PROLOGUE_TEMP (Pmode), GEN_INT (size));
+ if (TARGET_MIPS16)
+ {
+ /* There are no instructions to add or subtract registers
+ from the stack pointer, so use the frame pointer as a
+ temporary. We should always be using a frame pointer
+ in this case anyway. */
+ gcc_assert (frame_pointer_needed);
+ mips_emit_move (hard_frame_pointer_rtx, stack_pointer_rtx);
+ emit_insn (gen_sub3_insn (hard_frame_pointer_rtx,
+ hard_frame_pointer_rtx,
+ MIPS_PROLOGUE_TEMP (Pmode)));
+ mips_emit_move (stack_pointer_rtx, hard_frame_pointer_rtx);
+ }
+ else
+ emit_insn (gen_sub3_insn (stack_pointer_rtx,
+ stack_pointer_rtx,
+ MIPS_PROLOGUE_TEMP (Pmode)));
+
+ /* Describe the combined effect of the previous instructions. */
+ mips_set_frame_expr
+ (gen_rtx_SET (VOIDmode, stack_pointer_rtx,
+ plus_constant (stack_pointer_rtx, -size)));
+ }
+ }
+
+ /* Set up the frame pointer, if we're using one. */
+ if (frame_pointer_needed)
+ {
+ HOST_WIDE_INT offset;
+
+ offset = frame->hard_frame_pointer_offset;
+ if (offset == 0)
+ {
+ insn = mips_emit_move (hard_frame_pointer_rtx, stack_pointer_rtx);
+ RTX_FRAME_RELATED_P (insn) = 1;
+ }
+ else if (SMALL_OPERAND (offset))
+ {
+ insn = gen_add3_insn (hard_frame_pointer_rtx,
+ stack_pointer_rtx, GEN_INT (offset));
+ RTX_FRAME_RELATED_P (emit_insn (insn)) = 1;
+ }
+ else
+ {
+ mips_emit_move (MIPS_PROLOGUE_TEMP (Pmode), GEN_INT (offset));
+ mips_emit_move (hard_frame_pointer_rtx, stack_pointer_rtx);
+ emit_insn (gen_add3_insn (hard_frame_pointer_rtx,
+ hard_frame_pointer_rtx,
+ MIPS_PROLOGUE_TEMP (Pmode)));
+ mips_set_frame_expr
+ (gen_rtx_SET (VOIDmode, hard_frame_pointer_rtx,
+ plus_constant (stack_pointer_rtx, offset)));
+ }
+ }
+
+ mips_emit_loadgp ();
+
+ /* Initialize the $gp save slot. */
+ if (mips_cfun_has_cprestore_slot_p ())
+ {
+ rtx base, mem, gp, temp;
+ HOST_WIDE_INT offset;
+
+ mips_get_cprestore_base_and_offset (&base, &offset, false);
+ mem = gen_frame_mem (Pmode, plus_constant (base, offset));
+ gp = TARGET_MIPS16 ? MIPS16_PIC_TEMP : pic_offset_table_rtx;
+ temp = (SMALL_OPERAND (offset)
+ ? gen_rtx_SCRATCH (Pmode)
+ : MIPS_PROLOGUE_TEMP (Pmode));
+ emit_insn (gen_potential_cprestore (mem, GEN_INT (offset), gp, temp));
+
+ mips_get_cprestore_base_and_offset (&base, &offset, true);
+ mem = gen_frame_mem (Pmode, plus_constant (base, offset));
+ emit_insn (gen_use_cprestore (mem));
+ }
+
+ /* We need to search back to the last use of K0 or K1. */
+ if (cfun->machine->interrupt_handler_p)
+ {
+ for (insn = get_last_insn (); insn != NULL_RTX; insn = PREV_INSN (insn))
+ if (INSN_P (insn)
+ && for_each_rtx (&PATTERN (insn), mips_kernel_reg_p, NULL))
+ break;
+ /* Emit a move from K1 to COP0 Status after insn. */
+ gcc_assert (insn != NULL_RTX);
+ emit_insn_after (gen_cop0_move (gen_rtx_REG (SImode, COP0_STATUS_REG_NUM),
+ gen_rtx_REG (SImode, K1_REG_NUM)),
+ insn);
+ }
+
+ /* If we are profiling, make sure no instructions are scheduled before
+ the call to mcount. */
+ if (crtl->profile)
+ emit_insn (gen_blockage ());
+}
+
+/* Emit instructions to restore register REG from slot MEM. */
+
+static void
+mips_restore_reg (rtx reg, rtx mem)
+{
+ /* There's no MIPS16 instruction to load $31 directly. Load into
+ $7 instead and adjust the return insn appropriately. */
+ if (TARGET_MIPS16 && REGNO (reg) == RETURN_ADDR_REGNUM)
+ reg = gen_rtx_REG (GET_MODE (reg), GP_REG_FIRST + 7);
+
+ mips_emit_save_slot_move (reg, mem, MIPS_EPILOGUE_TEMP (GET_MODE (reg)));
+}
+
+/* Emit any instructions needed before a return. */
+
+void
+mips_expand_before_return (void)
+{
+ /* When using a call-clobbered gp, we start out with unified call
+ insns that include instructions to restore the gp. We then split
+ these unified calls after reload. These split calls explicitly
+ clobber gp, so there is no need to define
+ PIC_OFFSET_TABLE_REG_CALL_CLOBBERED.
+
+ For consistency, we should also insert an explicit clobber of $28
+ before return insns, so that the post-reload optimizers know that
+ the register is not live on exit. */
+ if (TARGET_CALL_CLOBBERED_GP)
+ emit_clobber (pic_offset_table_rtx);
+}
+
+/* Expand an "epilogue" or "sibcall_epilogue" pattern; SIBCALL_P
+ says which. */
+
+void
+mips_expand_epilogue (bool sibcall_p)
+{
+ const struct mips_frame_info *frame;
+ HOST_WIDE_INT step1, step2;
+ rtx base, target, insn;
+
+ if (!sibcall_p && mips_can_use_return_insn ())
+ {
+ emit_jump_insn (gen_return ());
+ return;
+ }
+
+ /* In MIPS16 mode, if the return value should go into a floating-point
+ register, we need to call a helper routine to copy it over. */
+ if (mips16_cfun_returns_in_fpr_p ())
+ mips16_copy_fpr_return_value ();
+
+ /* Split the frame into two. STEP1 is the amount of stack we should
+ deallocate before restoring the registers. STEP2 is the amount we
+ should deallocate afterwards.
+
+ Start off by assuming that no registers need to be restored. */
+ frame = &cfun->machine->frame;
+ step1 = frame->total_size;
+ step2 = 0;
+
+ /* Work out which register holds the frame address. */
+ if (!frame_pointer_needed)
+ base = stack_pointer_rtx;
+ else
+ {
+ base = hard_frame_pointer_rtx;
+ step1 -= frame->hard_frame_pointer_offset;
+ }
+
+ /* If we need to restore registers, deallocate as much stack as
+ possible in the second step without going out of range. */
+ if ((frame->mask | frame->fmask | frame->acc_mask) != 0
+ || frame->num_cop0_regs > 0)
+ {
+ step2 = MIN (step1, MIPS_MAX_FIRST_STACK_STEP);
+ step1 -= step2;
+ }
+
+ /* Set TARGET to BASE + STEP1. */
+ target = base;
+ if (step1 > 0)
+ {
+ rtx adjust;
+
+ /* Get an rtx for STEP1 that we can add to BASE. */
+ adjust = GEN_INT (step1);
+ if (!SMALL_OPERAND (step1))
+ {
+ mips_emit_move (MIPS_EPILOGUE_TEMP (Pmode), adjust);
+ adjust = MIPS_EPILOGUE_TEMP (Pmode);
+ }
+
+ /* Normal mode code can copy the result straight into $sp. */
+ if (!TARGET_MIPS16)
+ target = stack_pointer_rtx;
+
+ emit_insn (gen_add3_insn (target, base, adjust));
+ }
+
+ /* Copy TARGET into the stack pointer. */
+ if (target != stack_pointer_rtx)
+ mips_emit_move (stack_pointer_rtx, target);
+
+ /* If we're using addressing macros, $gp is implicitly used by all
+ SYMBOL_REFs. We must emit a blockage insn before restoring $gp
+ from the stack. */
+ if (TARGET_CALL_SAVED_GP && !TARGET_EXPLICIT_RELOCS)
+ emit_insn (gen_blockage ());
+
+ if (GENERATE_MIPS16E_SAVE_RESTORE && frame->mask != 0)
+ {
+ unsigned int regno, mask;
+ HOST_WIDE_INT offset;
+ rtx restore;
+
+ /* Generate the restore instruction. */
+ mask = frame->mask;
+ restore = mips16e_build_save_restore (true, &mask, &offset, 0, step2);
+
+ /* Restore any other registers manually. */
+ for (regno = GP_REG_FIRST; regno < GP_REG_LAST; regno++)
+ if (BITSET_P (mask, regno - GP_REG_FIRST))
+ {
+ offset -= UNITS_PER_WORD;
+ mips_save_restore_reg (word_mode, regno, offset, mips_restore_reg);
+ }
+
+ /* Restore the remaining registers and deallocate the final bit
+ of the frame. */
+ emit_insn (restore);
+ }
+ else
+ {
+ /* Restore the registers. */
+ mips_for_each_saved_acc (frame->total_size - step2, mips_restore_reg);
+ mips_for_each_saved_gpr_and_fpr (frame->total_size - step2,
+ mips_restore_reg);
+
+ if (cfun->machine->interrupt_handler_p)
+ {
+ HOST_WIDE_INT offset;
+ rtx mem;
+
+ offset = frame->cop0_sp_offset - (frame->total_size - step2);
+ if (!cfun->machine->keep_interrupts_masked_p)
+ {
+ /* Restore the original EPC. */
+ mem = gen_frame_mem (word_mode,
+ plus_constant (stack_pointer_rtx, offset));
+ mips_emit_move (gen_rtx_REG (word_mode, K0_REG_NUM), mem);
+ offset -= UNITS_PER_WORD;
+
+ /* Move to COP0 EPC. */
+ emit_insn (gen_cop0_move (gen_rtx_REG (SImode, COP0_EPC_REG_NUM),
+ gen_rtx_REG (SImode, K0_REG_NUM)));
+ }
+
+ /* Restore the original Status. */
+ mem = gen_frame_mem (word_mode,
+ plus_constant (stack_pointer_rtx, offset));
+ mips_emit_move (gen_rtx_REG (word_mode, K0_REG_NUM), mem);
+ offset -= UNITS_PER_WORD;
+
+ /* If we don't use shoadow register set, we need to update SP. */
+ if (!cfun->machine->use_shadow_register_set_p && step2 > 0)
+ emit_insn (gen_add3_insn (stack_pointer_rtx,
+ stack_pointer_rtx,
+ GEN_INT (step2)));
+
+ /* Move to COP0 Status. */
+ emit_insn (gen_cop0_move (gen_rtx_REG (SImode, COP0_STATUS_REG_NUM),
+ gen_rtx_REG (SImode, K0_REG_NUM)));
+ }
+ else
+ {
+ /* Deallocate the final bit of the frame. */
+ if (step2 > 0)
+ emit_insn (gen_add3_insn (stack_pointer_rtx,
+ stack_pointer_rtx,
+ GEN_INT (step2)));
+ }
+ }
+
+ /* Add in the __builtin_eh_return stack adjustment. We need to
+ use a temporary in MIPS16 code. */
+ if (crtl->calls_eh_return)
+ {
+ if (TARGET_MIPS16)
+ {
+ mips_emit_move (MIPS_EPILOGUE_TEMP (Pmode), stack_pointer_rtx);
+ emit_insn (gen_add3_insn (MIPS_EPILOGUE_TEMP (Pmode),
+ MIPS_EPILOGUE_TEMP (Pmode),
+ EH_RETURN_STACKADJ_RTX));
+ mips_emit_move (stack_pointer_rtx, MIPS_EPILOGUE_TEMP (Pmode));
+ }
+ else
+ emit_insn (gen_add3_insn (stack_pointer_rtx,
+ stack_pointer_rtx,
+ EH_RETURN_STACKADJ_RTX));
+ }
+
+ if (!sibcall_p)
+ {
+ mips_expand_before_return ();
+ if (cfun->machine->interrupt_handler_p)
+ {
+ /* Interrupt handlers generate eret or deret. */
+ if (cfun->machine->use_debug_exception_return_p)
+ emit_jump_insn (gen_mips_deret ());
+ else
+ emit_jump_insn (gen_mips_eret ());
+ }
+ else
+ {
+ unsigned int regno;
+
+ /* When generating MIPS16 code, the normal
+ mips_for_each_saved_gpr_and_fpr path will restore the return
+ address into $7 rather than $31. */
+ if (TARGET_MIPS16
+ && !GENERATE_MIPS16E_SAVE_RESTORE
+ && BITSET_P (frame->mask, RETURN_ADDR_REGNUM))
+ regno = GP_REG_FIRST + 7;
+ else
+ regno = RETURN_ADDR_REGNUM;
+ emit_jump_insn (gen_return_internal (gen_rtx_REG (Pmode, regno)));
+ }
+ }
+
+ /* Search from the beginning to the first use of K0 or K1. */
+ if (cfun->machine->interrupt_handler_p
+ && !cfun->machine->keep_interrupts_masked_p)
+ {
+ for (insn = get_insns (); insn != NULL_RTX; insn = NEXT_INSN (insn))
+ if (INSN_P (insn)
+ && for_each_rtx (&PATTERN(insn), mips_kernel_reg_p, NULL))
+ break;
+ gcc_assert (insn != NULL_RTX);
+ /* Insert disable interrupts before the first use of K0 or K1. */
+ emit_insn_before (gen_mips_di (), insn);
+ emit_insn_before (gen_mips_ehb (), insn);
+ }
+}
+
+/* Return nonzero if this function is known to have a null epilogue.
+ This allows the optimizer to omit jumps to jumps if no stack
+ was created. */
+
+bool
+mips_can_use_return_insn (void)
+{
+ /* Interrupt handlers need to go through the epilogue. */
+ if (cfun->machine->interrupt_handler_p)
+ return false;
+
+ if (!reload_completed)
+ return false;
+
+ if (crtl->profile)
+ return false;
+
+ /* In MIPS16 mode, a function that returns a floating-point value
+ needs to arrange to copy the return value into the floating-point
+ registers. */
+ if (mips16_cfun_returns_in_fpr_p ())
+ return false;
+
+ return cfun->machine->frame.total_size == 0;
+}
+
+/* Return true if register REGNO can store a value of mode MODE.
+ The result of this function is cached in mips_hard_regno_mode_ok. */
+
+static bool
+mips_hard_regno_mode_ok_p (unsigned int regno, enum machine_mode mode)
+{
+ unsigned int size;
+ enum mode_class mclass;
+
+ if (mode == CCV2mode)
+ return (ISA_HAS_8CC
+ && ST_REG_P (regno)
+ && (regno - ST_REG_FIRST) % 2 == 0);
+
+ if (mode == CCV4mode)
+ return (ISA_HAS_8CC
+ && ST_REG_P (regno)
+ && (regno - ST_REG_FIRST) % 4 == 0);
+
+ if (mode == CCmode)
+ {
+ if (!ISA_HAS_8CC)
+ return regno == FPSW_REGNUM;
+
+ return (ST_REG_P (regno)
+ || GP_REG_P (regno)
+ || FP_REG_P (regno));
+ }
+
+ size = GET_MODE_SIZE (mode);
+ mclass = GET_MODE_CLASS (mode);
+
+ if (GP_REG_P (regno))
+ return ((regno - GP_REG_FIRST) & 1) == 0 || size <= UNITS_PER_WORD;
+
+ if (FP_REG_P (regno)
+ && (((regno - FP_REG_FIRST) % MAX_FPRS_PER_FMT) == 0
+ || (MIN_FPRS_PER_FMT == 1 && size <= UNITS_PER_FPREG)))
+ {
+ /* Allow TFmode for CCmode reloads. */
+ if (mode == TFmode && ISA_HAS_8CC)
+ return true;
+
+ /* Allow 64-bit vector modes for Loongson-2E/2F. */
+ if (TARGET_LOONGSON_VECTORS
+ && (mode == V2SImode
+ || mode == V4HImode
+ || mode == V8QImode
+ || mode == DImode))
+ return true;
+
+ if (mclass == MODE_FLOAT
+ || mclass == MODE_COMPLEX_FLOAT
+ || mclass == MODE_VECTOR_FLOAT)
+ return size <= UNITS_PER_FPVALUE;
+
+ /* Allow integer modes that fit into a single register. We need
+ to put integers into FPRs when using instructions like CVT
+ and TRUNC. There's no point allowing sizes smaller than a word,
+ because the FPU has no appropriate load/store instructions. */
+ if (mclass == MODE_INT)
+ return size >= MIN_UNITS_PER_WORD && size <= UNITS_PER_FPREG;
+ }
+
+ if (ACC_REG_P (regno)
+ && (INTEGRAL_MODE_P (mode) || ALL_FIXED_POINT_MODE_P (mode)))
+ {
+ if (MD_REG_P (regno))
+ {
+ /* After a multiplication or division, clobbering HI makes
+ the value of LO unpredictable, and vice versa. This means
+ that, for all interesting cases, HI and LO are effectively
+ a single register.
+
+ We model this by requiring that any value that uses HI
+ also uses LO. */
+ if (size <= UNITS_PER_WORD * 2)
+ return regno == (size <= UNITS_PER_WORD ? LO_REGNUM : MD_REG_FIRST);
+ }
+ else
+ {
+ /* DSP accumulators do not have the same restrictions as
+ HI and LO, so we can treat them as normal doubleword
+ registers. */
+ if (size <= UNITS_PER_WORD)
+ return true;
+
+ if (size <= UNITS_PER_WORD * 2
+ && ((regno - DSP_ACC_REG_FIRST) & 1) == 0)
+ return true;
+ }
+ }
+
+ if (ALL_COP_REG_P (regno))
+ return mclass == MODE_INT && size <= UNITS_PER_WORD;
+
+ if (regno == GOT_VERSION_REGNUM)
+ return mode == SImode;
+
+ return false;
+}
+
+/* Implement HARD_REGNO_NREGS. */
+
+unsigned int
+mips_hard_regno_nregs (int regno, enum machine_mode mode)
+{
+ if (ST_REG_P (regno))
+ /* The size of FP status registers is always 4, because they only hold
+ CCmode values, and CCmode is always considered to be 4 bytes wide. */
+ return (GET_MODE_SIZE (mode) + 3) / 4;
+
+ if (FP_REG_P (regno))
+ return (GET_MODE_SIZE (mode) + UNITS_PER_FPREG - 1) / UNITS_PER_FPREG;
+
+ /* All other registers are word-sized. */
+ return (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+}
+
+/* Implement CLASS_MAX_NREGS, taking the maximum of the cases
+ in mips_hard_regno_nregs. */
+
+int
+mips_class_max_nregs (enum reg_class rclass, enum machine_mode mode)
+{
+ int size;
+ HARD_REG_SET left;
+
+ size = 0x8000;
+ COPY_HARD_REG_SET (left, reg_class_contents[(int) rclass]);
+ if (hard_reg_set_intersect_p (left, reg_class_contents[(int) ST_REGS]))
+ {
+ size = MIN (size, 4);
+ AND_COMPL_HARD_REG_SET (left, reg_class_contents[(int) ST_REGS]);
+ }
+ if (hard_reg_set_intersect_p (left, reg_class_contents[(int) FP_REGS]))
+ {
+ size = MIN (size, UNITS_PER_FPREG);
+ AND_COMPL_HARD_REG_SET (left, reg_class_contents[(int) FP_REGS]);
+ }
+ if (!hard_reg_set_empty_p (left))
+ size = MIN (size, UNITS_PER_WORD);
+ return (GET_MODE_SIZE (mode) + size - 1) / size;
+}
+
+/* Implement CANNOT_CHANGE_MODE_CLASS. */
+
+bool
+mips_cannot_change_mode_class (enum machine_mode from ATTRIBUTE_UNUSED,
+ enum machine_mode to ATTRIBUTE_UNUSED,
+ enum reg_class rclass)
+{
+ /* There are several problems with changing the modes of values
+ in floating-point registers:
+
+ - When a multi-word value is stored in paired floating-point
+ registers, the first register always holds the low word.
+ We therefore can't allow FPRs to change between single-word
+ and multi-word modes on big-endian targets.
+
+ - GCC assumes that each word of a multiword register can be accessed
+ individually using SUBREGs. This is not true for floating-point
+ registers if they are bigger than a word.
+
+ - Loading a 32-bit value into a 64-bit floating-point register
+ will not sign-extend the value, despite what LOAD_EXTEND_OP says.
+ We can't allow FPRs to change from SImode to to a wider mode on
+ 64-bit targets.
+
+ - If the FPU has already interpreted a value in one format, we must
+ not ask it to treat the value as having a different format.
+
+ We therefore disallow all mode changes involving FPRs. */
+ return reg_classes_intersect_p (FP_REGS, rclass);
+}
+
+/* Implement target hook small_register_classes_for_mode_p. */
+
+static bool
+mips_small_register_classes_for_mode_p (enum machine_mode mode
+ ATTRIBUTE_UNUSED)
+{
+ return TARGET_MIPS16;
+}
+
+/* Return true if moves in mode MODE can use the FPU's mov.fmt instruction. */
+
+static bool
+mips_mode_ok_for_mov_fmt_p (enum machine_mode mode)
+{
+ switch (mode)
+ {
+ case SFmode:
+ return TARGET_HARD_FLOAT;
+
+ case DFmode:
+ return TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT;
+
+ case V2SFmode:
+ return TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT;
+
+ default:
+ return false;
+ }
+}
+
+/* Implement MODES_TIEABLE_P. */
+
+bool
+mips_modes_tieable_p (enum machine_mode mode1, enum machine_mode mode2)
+{
+ /* FPRs allow no mode punning, so it's not worth tying modes if we'd
+ prefer to put one of them in FPRs. */
+ return (mode1 == mode2
+ || (!mips_mode_ok_for_mov_fmt_p (mode1)
+ && !mips_mode_ok_for_mov_fmt_p (mode2)));
+}
+
+/* Implement TARGET_PREFERRED_RELOAD_CLASS. */
+
+static reg_class_t
+mips_preferred_reload_class (rtx x, reg_class_t rclass)
+{
+ if (mips_dangerous_for_la25_p (x) && reg_class_subset_p (LEA_REGS, rclass))
+ return LEA_REGS;
+
+ if (reg_class_subset_p (FP_REGS, rclass)
+ && mips_mode_ok_for_mov_fmt_p (GET_MODE (x)))
+ return FP_REGS;
+
+ if (reg_class_subset_p (GR_REGS, rclass))
+ rclass = GR_REGS;
+
+ if (TARGET_MIPS16 && reg_class_subset_p (M16_REGS, rclass))
+ rclass = M16_REGS;
+
+ return rclass;
+}
+
+/* RCLASS is a class involved in a REGISTER_MOVE_COST calculation.
+ Return a "canonical" class to represent it in later calculations. */
+
+static reg_class_t
+mips_canonicalize_move_class (reg_class_t rclass)
+{
+ /* All moves involving accumulator registers have the same cost. */
+ if (reg_class_subset_p (rclass, ACC_REGS))
+ rclass = ACC_REGS;
+
+ /* Likewise promote subclasses of general registers to the most
+ interesting containing class. */
+ if (TARGET_MIPS16 && reg_class_subset_p (rclass, M16_REGS))
+ rclass = M16_REGS;
+ else if (reg_class_subset_p (rclass, GENERAL_REGS))
+ rclass = GENERAL_REGS;
+
+ return rclass;
+}
+
+/* Return the cost of moving a value of mode MODE from a register of
+ class FROM to a GPR. Return 0 for classes that are unions of other
+ classes handled by this function. */
+
+static int
+mips_move_to_gpr_cost (enum machine_mode mode ATTRIBUTE_UNUSED,
+ reg_class_t from)
+{
+ switch (from)
+ {
+ case GENERAL_REGS:
+ /* A MIPS16 MOVE instruction, or a non-MIPS16 MOVE macro. */
+ return 2;
+
+ case ACC_REGS:
+ /* MFLO and MFHI. */
+ return 6;
+
+ case FP_REGS:
+ /* MFC1, etc. */
+ return 4;
+
+ case ST_REGS:
+ /* LUI followed by MOVF. */
+ return 4;
+
+ case COP0_REGS:
+ case COP2_REGS:
+ case COP3_REGS:
+ /* This choice of value is historical. */
+ return 5;
+
+ default:
+ return 0;
+ }
+}
+
+/* Return the cost of moving a value of mode MODE from a GPR to a
+ register of class TO. Return 0 for classes that are unions of
+ other classes handled by this function. */
+
+static int
+mips_move_from_gpr_cost (enum machine_mode mode, reg_class_t to)
+{
+ switch (to)
+ {
+ case GENERAL_REGS:
+ /* A MIPS16 MOVE instruction, or a non-MIPS16 MOVE macro. */
+ return 2;
+
+ case ACC_REGS:
+ /* MTLO and MTHI. */
+ return 6;
+
+ case FP_REGS:
+ /* MTC1, etc. */
+ return 4;
+
+ case ST_REGS:
+ /* A secondary reload through an FPR scratch. */
+ return (mips_register_move_cost (mode, GENERAL_REGS, FP_REGS)
+ + mips_register_move_cost (mode, FP_REGS, ST_REGS));
+
+ case COP0_REGS:
+ case COP2_REGS:
+ case COP3_REGS:
+ /* This choice of value is historical. */
+ return 5;
+
+ default:
+ return 0;
+ }
+}
+
+/* Implement TARGET_REGISTER_MOVE_COST. Return 0 for classes that are the
+ maximum of the move costs for subclasses; regclass will work out
+ the maximum for us. */
+
+static int
+mips_register_move_cost (enum machine_mode mode,
+ reg_class_t from, reg_class_t to)
+{
+ reg_class_t dregs;
+ int cost1, cost2;
+
+ from = mips_canonicalize_move_class (from);
+ to = mips_canonicalize_move_class (to);
+
+ /* Handle moves that can be done without using general-purpose registers. */
+ if (from == FP_REGS)
+ {
+ if (to == FP_REGS && mips_mode_ok_for_mov_fmt_p (mode))
+ /* MOV.FMT. */
+ return 4;
+ if (to == ST_REGS)
+ /* The sequence generated by mips_expand_fcc_reload. */
+ return 8;
+ }
+
+ /* Handle cases in which only one class deviates from the ideal. */
+ dregs = TARGET_MIPS16 ? M16_REGS : GENERAL_REGS;
+ if (from == dregs)
+ return mips_move_from_gpr_cost (mode, to);
+ if (to == dregs)
+ return mips_move_to_gpr_cost (mode, from);
+
+ /* Handles cases that require a GPR temporary. */
+ cost1 = mips_move_to_gpr_cost (mode, from);
+ if (cost1 != 0)
+ {
+ cost2 = mips_move_from_gpr_cost (mode, to);
+ if (cost2 != 0)
+ return cost1 + cost2;
+ }
+
+ return 0;
+}
+
+/* Implement TARGET_MEMORY_MOVE_COST. */
+
+static int
+mips_memory_move_cost (enum machine_mode mode, reg_class_t rclass, bool in)
+{
+ return (mips_cost->memory_latency
+ + memory_move_secondary_cost (mode, rclass, in));
+}
+
+/* Implement TARGET_IRA_COVER_CLASSES. */
+
+static const reg_class_t *
+mips_ira_cover_classes (void)
+{
+ static const reg_class_t acc_classes[] = {
+ GR_AND_ACC_REGS, FP_REGS, COP0_REGS, COP2_REGS, COP3_REGS,
+ ST_REGS, LIM_REG_CLASSES
+ };
+ static const reg_class_t no_acc_classes[] = {
+ GR_REGS, FP_REGS, COP0_REGS, COP2_REGS, COP3_REGS,
+ ST_REGS, LIM_REG_CLASSES
+ };
+
+ /* Don't allow the register allocators to use LO and HI in MIPS16 mode,
+ which has no MTLO or MTHI instructions. Also, using GR_AND_ACC_REGS
+ as a cover class only works well when we keep per-register costs.
+ Using it when not optimizing can cause us to think accumulators
+ have the same cost as GPRs in cases where GPRs are actually much
+ cheaper. */
+ return TARGET_MIPS16 || !optimize ? no_acc_classes : acc_classes;
+}
+
+/* Return the register class required for a secondary register when
+ copying between one of the registers in RCLASS and value X, which
+ has mode MODE. X is the source of the move if IN_P, otherwise it
+ is the destination. Return NO_REGS if no secondary register is
+ needed. */
+
+enum reg_class
+mips_secondary_reload_class (enum reg_class rclass,
+ enum machine_mode mode, rtx x, bool in_p)
+{
+ int regno;
+
+ /* If X is a constant that cannot be loaded into $25, it must be loaded
+ into some other GPR. No other register class allows a direct move. */
+ if (mips_dangerous_for_la25_p (x))
+ return reg_class_subset_p (rclass, LEA_REGS) ? NO_REGS : LEA_REGS;
+
+ regno = true_regnum (x);
+ if (TARGET_MIPS16)
+ {
+ /* In MIPS16 mode, every move must involve a member of M16_REGS. */
+ if (!reg_class_subset_p (rclass, M16_REGS) && !M16_REG_P (regno))
+ return M16_REGS;
+
+ return NO_REGS;
+ }
+
+ /* Copying from accumulator registers to anywhere other than a general
+ register requires a temporary general register. */
+ if (reg_class_subset_p (rclass, ACC_REGS))
+ return GP_REG_P (regno) ? NO_REGS : GR_REGS;
+ if (ACC_REG_P (regno))
+ return reg_class_subset_p (rclass, GR_REGS) ? NO_REGS : GR_REGS;
+
+ /* We can only copy a value to a condition code register from a
+ floating-point register, and even then we require a scratch
+ floating-point register. We can only copy a value out of a
+ condition-code register into a general register. */
+ if (reg_class_subset_p (rclass, ST_REGS))
+ {
+ if (in_p)
+ return FP_REGS;
+ return GP_REG_P (regno) ? NO_REGS : GR_REGS;
+ }
+ if (ST_REG_P (regno))
+ {
+ if (!in_p)
+ return FP_REGS;
+ return reg_class_subset_p (rclass, GR_REGS) ? NO_REGS : GR_REGS;
+ }
+
+ if (reg_class_subset_p (rclass, FP_REGS))
+ {
+ if (MEM_P (x)
+ && (GET_MODE_SIZE (mode) == 4 || GET_MODE_SIZE (mode) == 8))
+ /* In this case we can use lwc1, swc1, ldc1 or sdc1. We'll use
+ pairs of lwc1s and swc1s if ldc1 and sdc1 are not supported. */
+ return NO_REGS;
+
+ if (GP_REG_P (regno) || x == CONST0_RTX (mode))
+ /* In this case we can use mtc1, mfc1, dmtc1 or dmfc1. */
+ return NO_REGS;
+
+ if (CONSTANT_P (x) && !targetm.cannot_force_const_mem (x))
+ /* We can force the constant to memory and use lwc1
+ and ldc1. As above, we will use pairs of lwc1s if
+ ldc1 is not supported. */
+ return NO_REGS;
+
+ if (FP_REG_P (regno) && mips_mode_ok_for_mov_fmt_p (mode))
+ /* In this case we can use mov.fmt. */
+ return NO_REGS;
+
+ /* Otherwise, we need to reload through an integer register. */
+ return GR_REGS;
+ }
+ if (FP_REG_P (regno))
+ return reg_class_subset_p (rclass, GR_REGS) ? NO_REGS : GR_REGS;
+
+ return NO_REGS;
+}
+
+/* Implement TARGET_MODE_REP_EXTENDED. */
+
+static int
+mips_mode_rep_extended (enum machine_mode mode, enum machine_mode mode_rep)
+{
+ /* On 64-bit targets, SImode register values are sign-extended to DImode. */
+ if (TARGET_64BIT && mode == SImode && mode_rep == DImode)
+ return SIGN_EXTEND;
+
+ return UNKNOWN;
+}
+
+/* Implement TARGET_VALID_POINTER_MODE. */
+
+static bool
+mips_valid_pointer_mode (enum machine_mode mode)
+{
+ return mode == SImode || (TARGET_64BIT && mode == DImode);
+}
+
+/* Implement TARGET_VECTOR_MODE_SUPPORTED_P. */
+
+static bool
+mips_vector_mode_supported_p (enum machine_mode mode)
+{
+ switch (mode)
+ {
+ case V2SFmode:
+ return TARGET_PAIRED_SINGLE_FLOAT;
+
+ case V2HImode:
+ case V4QImode:
+ case V2HQmode:
+ case V2UHQmode:
+ case V2HAmode:
+ case V2UHAmode:
+ case V4QQmode:
+ case V4UQQmode:
+ return TARGET_DSP;
+
+ case V2SImode:
+ case V4HImode:
+ case V8QImode:
+ return TARGET_LOONGSON_VECTORS;
+
+ default:
+ return false;
+ }
+}
+
+/* Implement TARGET_SCALAR_MODE_SUPPORTED_P. */
+
+static bool
+mips_scalar_mode_supported_p (enum machine_mode mode)
+{
+ if (ALL_FIXED_POINT_MODE_P (mode)
+ && GET_MODE_PRECISION (mode) <= 2 * BITS_PER_WORD)
+ return true;
+
+ return default_scalar_mode_supported_p (mode);
+}
+
+/* Implement TARGET_VECTORIZE_PREFERRED_SIMD_MODE. */
+
+static enum machine_mode
+mips_preferred_simd_mode (enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ if (TARGET_PAIRED_SINGLE_FLOAT
+ && mode == SFmode)
+ return V2SFmode;
+ return word_mode;
+}
+
+/* Implement TARGET_INIT_LIBFUNCS. */
+
+static void
+mips_init_libfuncs (void)
+{
+ if (TARGET_FIX_VR4120)
+ {
+ /* Register the special divsi3 and modsi3 functions needed to work
+ around VR4120 division errata. */
+ set_optab_libfunc (sdiv_optab, SImode, "__vr4120_divsi3");
+ set_optab_libfunc (smod_optab, SImode, "__vr4120_modsi3");
+ }
+
+ if (TARGET_MIPS16 && TARGET_HARD_FLOAT_ABI)
+ {
+ /* Register the MIPS16 -mhard-float stubs. */
+ set_optab_libfunc (add_optab, SFmode, "__mips16_addsf3");
+ set_optab_libfunc (sub_optab, SFmode, "__mips16_subsf3");
+ set_optab_libfunc (smul_optab, SFmode, "__mips16_mulsf3");
+ set_optab_libfunc (sdiv_optab, SFmode, "__mips16_divsf3");
+
+ set_optab_libfunc (eq_optab, SFmode, "__mips16_eqsf2");
+ set_optab_libfunc (ne_optab, SFmode, "__mips16_nesf2");
+ set_optab_libfunc (gt_optab, SFmode, "__mips16_gtsf2");
+ set_optab_libfunc (ge_optab, SFmode, "__mips16_gesf2");
+ set_optab_libfunc (lt_optab, SFmode, "__mips16_ltsf2");
+ set_optab_libfunc (le_optab, SFmode, "__mips16_lesf2");
+ set_optab_libfunc (unord_optab, SFmode, "__mips16_unordsf2");
+
+ set_conv_libfunc (sfix_optab, SImode, SFmode, "__mips16_fix_truncsfsi");
+ set_conv_libfunc (sfloat_optab, SFmode, SImode, "__mips16_floatsisf");
+ set_conv_libfunc (ufloat_optab, SFmode, SImode, "__mips16_floatunsisf");
+
+ if (TARGET_DOUBLE_FLOAT)
+ {
+ set_optab_libfunc (add_optab, DFmode, "__mips16_adddf3");
+ set_optab_libfunc (sub_optab, DFmode, "__mips16_subdf3");
+ set_optab_libfunc (smul_optab, DFmode, "__mips16_muldf3");
+ set_optab_libfunc (sdiv_optab, DFmode, "__mips16_divdf3");
+
+ set_optab_libfunc (eq_optab, DFmode, "__mips16_eqdf2");
+ set_optab_libfunc (ne_optab, DFmode, "__mips16_nedf2");
+ set_optab_libfunc (gt_optab, DFmode, "__mips16_gtdf2");
+ set_optab_libfunc (ge_optab, DFmode, "__mips16_gedf2");
+ set_optab_libfunc (lt_optab, DFmode, "__mips16_ltdf2");
+ set_optab_libfunc (le_optab, DFmode, "__mips16_ledf2");
+ set_optab_libfunc (unord_optab, DFmode, "__mips16_unorddf2");
+
+ set_conv_libfunc (sext_optab, DFmode, SFmode,
+ "__mips16_extendsfdf2");
+ set_conv_libfunc (trunc_optab, SFmode, DFmode,
+ "__mips16_truncdfsf2");
+ set_conv_libfunc (sfix_optab, SImode, DFmode,
+ "__mips16_fix_truncdfsi");
+ set_conv_libfunc (sfloat_optab, DFmode, SImode,
+ "__mips16_floatsidf");
+ set_conv_libfunc (ufloat_optab, DFmode, SImode,
+ "__mips16_floatunsidf");
+ }
+ }
+
+ /* The MIPS16 ISA does not have an encoding for "sync", so we rely
+ on an external non-MIPS16 routine to implement __sync_synchronize. */
+ if (TARGET_MIPS16)
+ synchronize_libfunc = init_one_libfunc ("__sync_synchronize");
+}
+
+/* Build up a multi-insn sequence that loads label TARGET into $AT. */
+
+static void
+mips_process_load_label (rtx target)
+{
+ rtx base, gp, intop;
+ HOST_WIDE_INT offset;
+
+ mips_multi_start ();
+ switch (mips_abi)
+ {
+ case ABI_N32:
+ mips_multi_add_insn ("lw\t%@,%%got_page(%0)(%+)", target, 0);
+ mips_multi_add_insn ("addiu\t%@,%@,%%got_ofst(%0)", target, 0);
+ break;
+
+ case ABI_64:
+ mips_multi_add_insn ("ld\t%@,%%got_page(%0)(%+)", target, 0);
+ mips_multi_add_insn ("daddiu\t%@,%@,%%got_ofst(%0)", target, 0);
+ break;
+
+ default:
+ gp = pic_offset_table_rtx;
+ if (mips_cfun_has_cprestore_slot_p ())
+ {
+ gp = gen_rtx_REG (Pmode, AT_REGNUM);
+ mips_get_cprestore_base_and_offset (&base, &offset, true);
+ if (!SMALL_OPERAND (offset))
+ {
+ intop = GEN_INT (CONST_HIGH_PART (offset));
+ mips_multi_add_insn ("lui\t%0,%1", gp, intop, 0);
+ mips_multi_add_insn ("addu\t%0,%0,%1", gp, base, 0);
+
+ base = gp;
+ offset = CONST_LOW_PART (offset);
+ }
+ intop = GEN_INT (offset);
+ if (ISA_HAS_LOAD_DELAY)
+ mips_multi_add_insn ("lw\t%0,%1(%2)%#", gp, intop, base, 0);
+ else
+ mips_multi_add_insn ("lw\t%0,%1(%2)", gp, intop, base, 0);
+ }
+ if (ISA_HAS_LOAD_DELAY)
+ mips_multi_add_insn ("lw\t%@,%%got(%0)(%1)%#", target, gp, 0);
+ else
+ mips_multi_add_insn ("lw\t%@,%%got(%0)(%1)", target, gp, 0);
+ mips_multi_add_insn ("addiu\t%@,%@,%%lo(%0)", target, 0);
+ break;
+ }
+}
+
+/* Return the number of instructions needed to load a label into $AT. */
+
+static unsigned int
+mips_load_label_num_insns (void)
+{
+ if (cfun->machine->load_label_num_insns == 0)
+ {
+ mips_process_load_label (pc_rtx);
+ cfun->machine->load_label_num_insns = mips_multi_num_insns;
+ }
+ return cfun->machine->load_label_num_insns;
+}
+
+/* Emit an asm sequence to start a noat block and load the address
+ of a label into $1. */
+
+void
+mips_output_load_label (rtx target)
+{
+ mips_push_asm_switch (&mips_noat);
+ if (TARGET_EXPLICIT_RELOCS)
+ {
+ mips_process_load_label (target);
+ mips_multi_write ();
+ }
+ else
+ {
+ if (Pmode == DImode)
+ output_asm_insn ("dla\t%@,%0", &target);
+ else
+ output_asm_insn ("la\t%@,%0", &target);
+ }
+}
+
+/* Return the length of INSN. LENGTH is the initial length computed by
+ attributes in the machine-description file. */
+
+int
+mips_adjust_insn_length (rtx insn, int length)
+{
+ /* mips.md uses MAX_PIC_BRANCH_LENGTH as a placeholder for the length
+ of a PIC long-branch sequence. Substitute the correct value. */
+ if (length == MAX_PIC_BRANCH_LENGTH
+ && INSN_CODE (insn) >= 0
+ && get_attr_type (insn) == TYPE_BRANCH)
+ {
+ /* Add the branch-over instruction and its delay slot, if this
+ is a conditional branch. */
+ length = simplejump_p (insn) ? 0 : 8;
+
+ /* Load the label into $AT and jump to it. Ignore the delay
+ slot of the jump. */
+ length += 4 * mips_load_label_num_insns() + 4;
+ }
+
+ /* A unconditional jump has an unfilled delay slot if it is not part
+ of a sequence. A conditional jump normally has a delay slot, but
+ does not on MIPS16. */
+ if (CALL_P (insn) || (TARGET_MIPS16 ? simplejump_p (insn) : JUMP_P (insn)))
+ length += 4;
+
+ /* See how many nops might be needed to avoid hardware hazards. */
+ if (!cfun->machine->ignore_hazard_length_p && INSN_CODE (insn) >= 0)
+ switch (get_attr_hazard (insn))
+ {
+ case HAZARD_NONE:
+ break;
+
+ case HAZARD_DELAY:
+ length += 4;
+ break;
+
+ case HAZARD_HILO:
+ length += 8;
+ break;
+ }
+
+ /* In order to make it easier to share MIPS16 and non-MIPS16 patterns,
+ the .md file length attributes are 4-based for both modes.
+ Adjust the MIPS16 ones here. */
+ if (TARGET_MIPS16)
+ length /= 2;
+
+ return length;
+}
+
+/* Return the assembly code for INSN, which has the operands given by
+ OPERANDS, and which branches to OPERANDS[0] if some condition is true.
+ BRANCH_IF_TRUE is the asm template that should be used if OPERANDS[0]
+ is in range of a direct branch. BRANCH_IF_FALSE is an inverted
+ version of BRANCH_IF_TRUE. */
+
+const char *
+mips_output_conditional_branch (rtx insn, rtx *operands,
+ const char *branch_if_true,
+ const char *branch_if_false)
+{
+ unsigned int length;
+ rtx taken, not_taken;
+
+ gcc_assert (LABEL_P (operands[0]));
+
+ length = get_attr_length (insn);
+ if (length <= 8)
+ {
+ /* Just a simple conditional branch. */
+ mips_branch_likely = (final_sequence && INSN_ANNULLED_BRANCH_P (insn));
+ return branch_if_true;
+ }
+
+ /* Generate a reversed branch around a direct jump. This fallback does
+ not use branch-likely instructions. */
+ mips_branch_likely = false;
+ not_taken = gen_label_rtx ();
+ taken = operands[0];
+
+ /* Generate the reversed branch to NOT_TAKEN. */
+ operands[0] = not_taken;
+ output_asm_insn (branch_if_false, operands);
+
+ /* If INSN has a delay slot, we must provide delay slots for both the
+ branch to NOT_TAKEN and the conditional jump. We must also ensure
+ that INSN's delay slot is executed in the appropriate cases. */
+ if (final_sequence)
+ {
+ /* This first delay slot will always be executed, so use INSN's
+ delay slot if is not annulled. */
+ if (!INSN_ANNULLED_BRANCH_P (insn))
+ {
+ final_scan_insn (XVECEXP (final_sequence, 0, 1),
+ asm_out_file, optimize, 1, NULL);
+ INSN_DELETED_P (XVECEXP (final_sequence, 0, 1)) = 1;
+ }
+ else
+ output_asm_insn ("nop", 0);
+ fprintf (asm_out_file, "\n");
+ }
+
+ /* Output the unconditional branch to TAKEN. */
+ if (TARGET_ABSOLUTE_JUMPS)
+ output_asm_insn (MIPS_ABSOLUTE_JUMP ("j\t%0%/"), &taken);
+ else
+ {
+ mips_output_load_label (taken);
+ output_asm_insn ("jr\t%@%]%/", 0);
+ }
+
+ /* Now deal with its delay slot; see above. */
+ if (final_sequence)
+ {
+ /* This delay slot will only be executed if the branch is taken.
+ Use INSN's delay slot if is annulled. */
+ if (INSN_ANNULLED_BRANCH_P (insn))
+ {
+ final_scan_insn (XVECEXP (final_sequence, 0, 1),
+ asm_out_file, optimize, 1, NULL);
+ INSN_DELETED_P (XVECEXP (final_sequence, 0, 1)) = 1;
+ }
+ else
+ output_asm_insn ("nop", 0);
+ fprintf (asm_out_file, "\n");
+ }
+
+ /* Output NOT_TAKEN. */
+ targetm.asm_out.internal_label (asm_out_file, "L",
+ CODE_LABEL_NUMBER (not_taken));
+ return "";
+}
+
+/* Return the assembly code for INSN, which branches to OPERANDS[0]
+ if some ordering condition is true. The condition is given by
+ OPERANDS[1] if !INVERTED_P, otherwise it is the inverse of
+ OPERANDS[1]. OPERANDS[2] is the comparison's first operand;
+ its second is always zero. */
+
+const char *
+mips_output_order_conditional_branch (rtx insn, rtx *operands, bool inverted_p)
+{
+ const char *branch[2];
+
+ /* Make BRANCH[1] branch to OPERANDS[0] when the condition is true.
+ Make BRANCH[0] branch on the inverse condition. */
+ switch (GET_CODE (operands[1]))
+ {
+ /* These cases are equivalent to comparisons against zero. */
+ case LEU:
+ inverted_p = !inverted_p;
+ /* Fall through. */
+ case GTU:
+ branch[!inverted_p] = MIPS_BRANCH ("bne", "%2,%.,%0");
+ branch[inverted_p] = MIPS_BRANCH ("beq", "%2,%.,%0");
+ break;
+
+ /* These cases are always true or always false. */
+ case LTU:
+ inverted_p = !inverted_p;
+ /* Fall through. */
+ case GEU:
+ branch[!inverted_p] = MIPS_BRANCH ("beq", "%.,%.,%0");
+ branch[inverted_p] = MIPS_BRANCH ("bne", "%.,%.,%0");
+ break;
+
+ default:
+ branch[!inverted_p] = MIPS_BRANCH ("b%C1z", "%2,%0");
+ branch[inverted_p] = MIPS_BRANCH ("b%N1z", "%2,%0");
+ break;
+ }
+ return mips_output_conditional_branch (insn, operands, branch[1], branch[0]);
+}
+
+/* Start a block of code that needs access to the LL, SC and SYNC
+ instructions. */
+
+static void
+mips_start_ll_sc_sync_block (void)
+{
+ if (!ISA_HAS_LL_SC)
+ {
+ output_asm_insn (".set\tpush", 0);
+ output_asm_insn (".set\tmips2", 0);
+ }
+}
+
+/* End a block started by mips_start_ll_sc_sync_block. */
+
+static void
+mips_end_ll_sc_sync_block (void)
+{
+ if (!ISA_HAS_LL_SC)
+ output_asm_insn (".set\tpop", 0);
+}
+
+/* Output and/or return the asm template for a sync instruction. */
+
+const char *
+mips_output_sync (void)
+{
+ mips_start_ll_sc_sync_block ();
+ output_asm_insn ("sync", 0);
+ mips_end_ll_sc_sync_block ();
+ return "";
+}
+
+/* Return the asm template associated with sync_insn1 value TYPE.
+ IS_64BIT_P is true if we want a 64-bit rather than 32-bit operation. */
+
+static const char *
+mips_sync_insn1_template (enum attr_sync_insn1 type, bool is_64bit_p)
+{
+ switch (type)
+ {
+ case SYNC_INSN1_MOVE:
+ return "move\t%0,%z2";
+ case SYNC_INSN1_LI:
+ return "li\t%0,%2";
+ case SYNC_INSN1_ADDU:
+ return is_64bit_p ? "daddu\t%0,%1,%z2" : "addu\t%0,%1,%z2";
+ case SYNC_INSN1_ADDIU:
+ return is_64bit_p ? "daddiu\t%0,%1,%2" : "addiu\t%0,%1,%2";
+ case SYNC_INSN1_SUBU:
+ return is_64bit_p ? "dsubu\t%0,%1,%z2" : "subu\t%0,%1,%z2";
+ case SYNC_INSN1_AND:
+ return "and\t%0,%1,%z2";
+ case SYNC_INSN1_ANDI:
+ return "andi\t%0,%1,%2";
+ case SYNC_INSN1_OR:
+ return "or\t%0,%1,%z2";
+ case SYNC_INSN1_ORI:
+ return "ori\t%0,%1,%2";
+ case SYNC_INSN1_XOR:
+ return "xor\t%0,%1,%z2";
+ case SYNC_INSN1_XORI:
+ return "xori\t%0,%1,%2";
+ }
+ gcc_unreachable ();
+}
+
+/* Return the asm template associated with sync_insn2 value TYPE. */
+
+static const char *
+mips_sync_insn2_template (enum attr_sync_insn2 type)
+{
+ switch (type)
+ {
+ case SYNC_INSN2_NOP:
+ gcc_unreachable ();
+ case SYNC_INSN2_AND:
+ return "and\t%0,%1,%z2";
+ case SYNC_INSN2_XOR:
+ return "xor\t%0,%1,%z2";
+ case SYNC_INSN2_NOT:
+ return "nor\t%0,%1,%.";
+ }
+ gcc_unreachable ();
+}
+
+/* OPERANDS are the operands to a sync loop instruction and INDEX is
+ the value of the one of the sync_* attributes. Return the operand
+ referred to by the attribute, or DEFAULT_VALUE if the insn doesn't
+ have the associated attribute. */
+
+static rtx
+mips_get_sync_operand (rtx *operands, int index, rtx default_value)
+{
+ if (index > 0)
+ default_value = operands[index - 1];
+ return default_value;
+}
+
+/* INSN is a sync loop with operands OPERANDS. Build up a multi-insn
+ sequence for it. */
+
+static void
+mips_process_sync_loop (rtx insn, rtx *operands)
+{
+ rtx at, mem, oldval, newval, inclusive_mask, exclusive_mask;
+ rtx required_oldval, insn1_op2, tmp1, tmp2, tmp3;
+ unsigned int tmp3_insn;
+ enum attr_sync_insn1 insn1;
+ enum attr_sync_insn2 insn2;
+ bool is_64bit_p;
+
+ /* Read an operand from the sync_WHAT attribute and store it in
+ variable WHAT. DEFAULT is the default value if no attribute
+ is specified. */
+#define READ_OPERAND(WHAT, DEFAULT) \
+ WHAT = mips_get_sync_operand (operands, (int) get_attr_sync_##WHAT (insn), \
+ DEFAULT)
+
+ /* Read the memory. */
+ READ_OPERAND (mem, 0);
+ gcc_assert (mem);
+ is_64bit_p = (GET_MODE_BITSIZE (GET_MODE (mem)) == 64);
+
+ /* Read the other attributes. */
+ at = gen_rtx_REG (GET_MODE (mem), AT_REGNUM);
+ READ_OPERAND (oldval, at);
+ READ_OPERAND (newval, at);
+ READ_OPERAND (inclusive_mask, 0);
+ READ_OPERAND (exclusive_mask, 0);
+ READ_OPERAND (required_oldval, 0);
+ READ_OPERAND (insn1_op2, 0);
+ insn1 = get_attr_sync_insn1 (insn);
+ insn2 = get_attr_sync_insn2 (insn);
+
+ mips_multi_start ();
+
+ /* Output the release side of the memory barrier. */
+ if (get_attr_sync_release_barrier (insn) == SYNC_RELEASE_BARRIER_YES)
+ {
+ if (required_oldval == 0 && TARGET_OCTEON)
+ {
+ /* Octeon doesn't reorder reads, so a full barrier can be
+ created by using SYNCW to order writes combined with the
+ write from the following SC. When the SC successfully
+ completes, we know that all preceding writes are also
+ committed to the coherent memory system. It is possible
+ for a single SYNCW to fail, but a pair of them will never
+ fail, so we use two. */
+ mips_multi_add_insn ("syncw", NULL);
+ mips_multi_add_insn ("syncw", NULL);
+ }
+ else
+ mips_multi_add_insn ("sync", NULL);
+ }
+
+ /* Output the branch-back label. */
+ mips_multi_add_label ("1:");
+
+ /* OLDVAL = *MEM. */
+ mips_multi_add_insn (is_64bit_p ? "lld\t%0,%1" : "ll\t%0,%1",
+ oldval, mem, NULL);
+
+ /* if ((OLDVAL & INCLUSIVE_MASK) != REQUIRED_OLDVAL) goto 2. */
+ if (required_oldval)
+ {
+ if (inclusive_mask == 0)
+ tmp1 = oldval;
+ else
+ {
+ gcc_assert (oldval != at);
+ mips_multi_add_insn ("and\t%0,%1,%2",
+ at, oldval, inclusive_mask, NULL);
+ tmp1 = at;
+ }
+ mips_multi_add_insn ("bne\t%0,%z1,2f", tmp1, required_oldval, NULL);
+ }
+
+ /* $TMP1 = OLDVAL & EXCLUSIVE_MASK. */
+ if (exclusive_mask == 0)
+ tmp1 = const0_rtx;
+ else
+ {
+ gcc_assert (oldval != at);
+ mips_multi_add_insn ("and\t%0,%1,%z2",
+ at, oldval, exclusive_mask, NULL);
+ tmp1 = at;
+ }
+
+ /* $TMP2 = INSN1 (OLDVAL, INSN1_OP2).
+
+ We can ignore moves if $TMP4 != INSN1_OP2, since we'll still emit
+ at least one instruction in that case. */
+ if (insn1 == SYNC_INSN1_MOVE
+ && (tmp1 != const0_rtx || insn2 != SYNC_INSN2_NOP))
+ tmp2 = insn1_op2;
+ else
+ {
+ mips_multi_add_insn (mips_sync_insn1_template (insn1, is_64bit_p),
+ newval, oldval, insn1_op2, NULL);
+ tmp2 = newval;
+ }
+
+ /* $TMP3 = INSN2 ($TMP2, INCLUSIVE_MASK). */
+ if (insn2 == SYNC_INSN2_NOP)
+ tmp3 = tmp2;
+ else
+ {
+ mips_multi_add_insn (mips_sync_insn2_template (insn2),
+ newval, tmp2, inclusive_mask, NULL);
+ tmp3 = newval;
+ }
+ tmp3_insn = mips_multi_last_index ();
+
+ /* $AT = $TMP1 | $TMP3. */
+ if (tmp1 == const0_rtx || tmp3 == const0_rtx)
+ {
+ mips_multi_set_operand (tmp3_insn, 0, at);
+ tmp3 = at;
+ }
+ else
+ {
+ gcc_assert (tmp1 != tmp3);
+ mips_multi_add_insn ("or\t%0,%1,%2", at, tmp1, tmp3, NULL);
+ }
+
+ /* if (!commit (*MEM = $AT)) goto 1.
+
+ This will sometimes be a delayed branch; see the write code below
+ for details. */
+ mips_multi_add_insn (is_64bit_p ? "scd\t%0,%1" : "sc\t%0,%1", at, mem, NULL);
+ mips_multi_add_insn ("beq%?\t%0,%.,1b", at, NULL);
+
+ /* if (INSN1 != MOVE && INSN1 != LI) NEWVAL = $TMP3 [delay slot]. */
+ if (insn1 != SYNC_INSN1_MOVE && insn1 != SYNC_INSN1_LI && tmp3 != newval)
+ {
+ mips_multi_copy_insn (tmp3_insn);
+ mips_multi_set_operand (mips_multi_last_index (), 0, newval);
+ }
+ else
+ mips_multi_add_insn ("nop", NULL);
+
+ /* Output the acquire side of the memory barrier. */
+ if (TARGET_SYNC_AFTER_SC)
+ mips_multi_add_insn ("sync", NULL);
+
+ /* Output the exit label, if needed. */
+ if (required_oldval)
+ mips_multi_add_label ("2:");
+
+#undef READ_OPERAND
+}
+
+/* Output and/or return the asm template for sync loop INSN, which has
+ the operands given by OPERANDS. */
+
+const char *
+mips_output_sync_loop (rtx insn, rtx *operands)
+{
+ mips_process_sync_loop (insn, operands);
+
+ /* Use branch-likely instructions to work around the LL/SC R10000
+ errata. */
+ mips_branch_likely = TARGET_FIX_R10000;
+
+ mips_push_asm_switch (&mips_noreorder);
+ mips_push_asm_switch (&mips_nomacro);
+ mips_push_asm_switch (&mips_noat);
+ mips_start_ll_sc_sync_block ();
+
+ mips_multi_write ();
+
+ mips_end_ll_sc_sync_block ();
+ mips_pop_asm_switch (&mips_noat);
+ mips_pop_asm_switch (&mips_nomacro);
+ mips_pop_asm_switch (&mips_noreorder);
+
+ return "";
+}
+
+/* Return the number of individual instructions in sync loop INSN,
+ which has the operands given by OPERANDS. */
+
+unsigned int
+mips_sync_loop_insns (rtx insn, rtx *operands)
+{
+ mips_process_sync_loop (insn, operands);
+ return mips_multi_num_insns;
+}
+
+/* Return the assembly code for DIV or DDIV instruction DIVISION, which has
+ the operands given by OPERANDS. Add in a divide-by-zero check if needed.
+
+ When working around R4000 and R4400 errata, we need to make sure that
+ the division is not immediately followed by a shift[1][2]. We also
+ need to stop the division from being put into a branch delay slot[3].
+ The easiest way to avoid both problems is to add a nop after the
+ division. When a divide-by-zero check is needed, this nop can be
+ used to fill the branch delay slot.
+
+ [1] If a double-word or a variable shift executes immediately
+ after starting an integer division, the shift may give an
+ incorrect result. See quotations of errata #16 and #28 from
+ "MIPS R4000PC/SC Errata, Processor Revision 2.2 and 3.0"
+ in mips.md for details.
+
+ [2] A similar bug to [1] exists for all revisions of the
+ R4000 and the R4400 when run in an MC configuration.
+ From "MIPS R4000MC Errata, Processor Revision 2.2 and 3.0":
+
+ "19. In this following sequence:
+
+ ddiv (or ddivu or div or divu)
+ dsll32 (or dsrl32, dsra32)
+
+ if an MPT stall occurs, while the divide is slipping the cpu
+ pipeline, then the following double shift would end up with an
+ incorrect result.
+
+ Workaround: The compiler needs to avoid generating any
+ sequence with divide followed by extended double shift."
+
+ This erratum is also present in "MIPS R4400MC Errata, Processor
+ Revision 1.0" and "MIPS R4400MC Errata, Processor Revision 2.0
+ & 3.0" as errata #10 and #4, respectively.
+
+ [3] From "MIPS R4000PC/SC Errata, Processor Revision 2.2 and 3.0"
+ (also valid for MIPS R4000MC processors):
+
+ "52. R4000SC: This bug does not apply for the R4000PC.
+
+ There are two flavors of this bug:
+
+ 1) If the instruction just after divide takes an RF exception
+ (tlb-refill, tlb-invalid) and gets an instruction cache
+ miss (both primary and secondary) and the line which is
+ currently in secondary cache at this index had the first
+ data word, where the bits 5..2 are set, then R4000 would
+ get a wrong result for the div.
+
+ ##1
+ nop
+ div r8, r9
+ ------------------- # end-of page. -tlb-refill
+ nop
+ ##2
+ nop
+ div r8, r9
+ ------------------- # end-of page. -tlb-invalid
+ nop
+
+ 2) If the divide is in the taken branch delay slot, where the
+ target takes RF exception and gets an I-cache miss for the
+ exception vector or where I-cache miss occurs for the
+ target address, under the above mentioned scenarios, the
+ div would get wrong results.
+
+ ##1
+ j r2 # to next page mapped or unmapped
+ div r8,r9 # this bug would be there as long
+ # as there is an ICache miss and
+ nop # the "data pattern" is present
+
+ ##2
+ beq r0, r0, NextPage # to Next page
+ div r8,r9
+ nop
+
+ This bug is present for div, divu, ddiv, and ddivu
+ instructions.
+
+ Workaround: For item 1), OS could make sure that the next page
+ after the divide instruction is also mapped. For item 2), the
+ compiler could make sure that the divide instruction is not in
+ the branch delay slot."
+
+ These processors have PRId values of 0x00004220 and 0x00004300 for
+ the R4000 and 0x00004400, 0x00004500 and 0x00004600 for the R4400. */
+
+const char *
+mips_output_division (const char *division, rtx *operands)
+{
+ const char *s;
+
+ s = division;
+ if (TARGET_FIX_R4000 || TARGET_FIX_R4400)
+ {
+ output_asm_insn (s, operands);
+ s = "nop";
+ }
+ if (TARGET_CHECK_ZERO_DIV)
+ {
+ if (TARGET_MIPS16)
+ {
+ output_asm_insn (s, operands);
+ s = "bnez\t%2,1f\n\tbreak\t7\n1:";
+ }
+ else if (GENERATE_DIVIDE_TRAPS)
+ {
+ /* Avoid long replay penalty on load miss by putting the trap before
+ the divide. */
+ if (TUNE_74K)
+ output_asm_insn ("teq\t%2,%.,7", operands);
+ else
+ {
+ output_asm_insn (s, operands);
+ s = "teq\t%2,%.,7";
+ }
+ }
+ else
+ {
+ output_asm_insn ("%(bne\t%2,%.,1f", operands);
+ output_asm_insn (s, operands);
+ s = "break\t7%)\n1:";
+ }
+ }
+ return s;
+}
+
+/* Return true if IN_INSN is a multiply-add or multiply-subtract
+ instruction and if OUT_INSN assigns to the accumulator operand. */
+
+bool
+mips_linked_madd_p (rtx out_insn, rtx in_insn)
+{
+ rtx x;
+
+ x = single_set (in_insn);
+ if (x == 0)
+ return false;
+
+ x = SET_SRC (x);
+
+ if (GET_CODE (x) == PLUS
+ && GET_CODE (XEXP (x, 0)) == MULT
+ && reg_set_p (XEXP (x, 1), out_insn))
+ return true;
+
+ if (GET_CODE (x) == MINUS
+ && GET_CODE (XEXP (x, 1)) == MULT
+ && reg_set_p (XEXP (x, 0), out_insn))
+ return true;
+
+ return false;
+}
+
+/* True if the dependency between OUT_INSN and IN_INSN is on the store
+ data rather than the address. We need this because the cprestore
+ pattern is type "store", but is defined using an UNSPEC_VOLATILE,
+ which causes the default routine to abort. We just return false
+ for that case. */
+
+bool
+mips_store_data_bypass_p (rtx out_insn, rtx in_insn)
+{
+ if (GET_CODE (PATTERN (in_insn)) == UNSPEC_VOLATILE)
+ return false;
+
+ return !store_data_bypass_p (out_insn, in_insn);
+}
+
+
+/* Variables and flags used in scheduler hooks when tuning for
+ Loongson 2E/2F. */
+static struct
+{
+ /* Variables to support Loongson 2E/2F round-robin [F]ALU1/2 dispatch
+ strategy. */
+
+ /* If true, then next ALU1/2 instruction will go to ALU1. */
+ bool alu1_turn_p;
+
+ /* If true, then next FALU1/2 unstruction will go to FALU1. */
+ bool falu1_turn_p;
+
+ /* Codes to query if [f]alu{1,2}_core units are subscribed or not. */
+ int alu1_core_unit_code;
+ int alu2_core_unit_code;
+ int falu1_core_unit_code;
+ int falu2_core_unit_code;
+
+ /* True if current cycle has a multi instruction.
+ This flag is used in mips_ls2_dfa_post_advance_cycle. */
+ bool cycle_has_multi_p;
+
+ /* Instructions to subscribe ls2_[f]alu{1,2}_turn_enabled units.
+ These are used in mips_ls2_dfa_post_advance_cycle to initialize
+ DFA state.
+ E.g., when alu1_turn_enabled_insn is issued it makes next ALU1/2
+ instruction to go ALU1. */
+ rtx alu1_turn_enabled_insn;
+ rtx alu2_turn_enabled_insn;
+ rtx falu1_turn_enabled_insn;
+ rtx falu2_turn_enabled_insn;
+} mips_ls2;
+
+/* Implement TARGET_SCHED_ADJUST_COST. We assume that anti and output
+ dependencies have no cost, except on the 20Kc where output-dependence
+ is treated like input-dependence. */
+
+static int
+mips_adjust_cost (rtx insn ATTRIBUTE_UNUSED, rtx link,
+ rtx dep ATTRIBUTE_UNUSED, int cost)
+{
+ if (REG_NOTE_KIND (link) == REG_DEP_OUTPUT
+ && TUNE_20KC)
+ return cost;
+ if (REG_NOTE_KIND (link) != 0)
+ return 0;
+ return cost;
+}
+
+/* Return the number of instructions that can be issued per cycle. */
+
+static int
+mips_issue_rate (void)
+{
+ switch (mips_tune)
+ {
+ case PROCESSOR_74KC:
+ case PROCESSOR_74KF2_1:
+ case PROCESSOR_74KF1_1:
+ case PROCESSOR_74KF3_2:
+ /* The 74k is not strictly quad-issue cpu, but can be seen as one
+ by the scheduler. It can issue 1 ALU, 1 AGEN and 2 FPU insns,
+ but in reality only a maximum of 3 insns can be issued as
+ floating-point loads and stores also require a slot in the
+ AGEN pipe. */
+ case PROCESSOR_R10000:
+ /* All R10K Processors are quad-issue (being the first MIPS
+ processors to support this feature). */
+ return 4;
+
+ case PROCESSOR_20KC:
+ case PROCESSOR_R4130:
+ case PROCESSOR_R5400:
+ case PROCESSOR_R5500:
+ case PROCESSOR_R7000:
+ case PROCESSOR_R9000:
+ case PROCESSOR_OCTEON:
+ return 2;
+
+ case PROCESSOR_SB1:
+ case PROCESSOR_SB1A:
+ /* This is actually 4, but we get better performance if we claim 3.
+ This is partly because of unwanted speculative code motion with the
+ larger number, and partly because in most common cases we can't
+ reach the theoretical max of 4. */
+ return 3;
+
+ case PROCESSOR_LOONGSON_2E:
+ case PROCESSOR_LOONGSON_2F:
+ case PROCESSOR_LOONGSON_3A:
+ return 4;
+
+ default:
+ return 1;
+ }
+}
+
+/* Implement TARGET_SCHED_INIT_DFA_POST_CYCLE_INSN hook for Loongson2. */
+
+static void
+mips_ls2_init_dfa_post_cycle_insn (void)
+{
+ start_sequence ();
+ emit_insn (gen_ls2_alu1_turn_enabled_insn ());
+ mips_ls2.alu1_turn_enabled_insn = get_insns ();
+ end_sequence ();
+
+ start_sequence ();
+ emit_insn (gen_ls2_alu2_turn_enabled_insn ());
+ mips_ls2.alu2_turn_enabled_insn = get_insns ();
+ end_sequence ();
+
+ start_sequence ();
+ emit_insn (gen_ls2_falu1_turn_enabled_insn ());
+ mips_ls2.falu1_turn_enabled_insn = get_insns ();
+ end_sequence ();
+
+ start_sequence ();
+ emit_insn (gen_ls2_falu2_turn_enabled_insn ());
+ mips_ls2.falu2_turn_enabled_insn = get_insns ();
+ end_sequence ();
+
+ mips_ls2.alu1_core_unit_code = get_cpu_unit_code ("ls2_alu1_core");
+ mips_ls2.alu2_core_unit_code = get_cpu_unit_code ("ls2_alu2_core");
+ mips_ls2.falu1_core_unit_code = get_cpu_unit_code ("ls2_falu1_core");
+ mips_ls2.falu2_core_unit_code = get_cpu_unit_code ("ls2_falu2_core");
+}
+
+/* Implement TARGET_SCHED_INIT_DFA_POST_CYCLE_INSN hook.
+ Init data used in mips_dfa_post_advance_cycle. */
+
+static void
+mips_init_dfa_post_cycle_insn (void)
+{
+ if (TUNE_LOONGSON_2EF)
+ mips_ls2_init_dfa_post_cycle_insn ();
+}
+
+/* Initialize STATE when scheduling for Loongson 2E/2F.
+ Support round-robin dispatch scheme by enabling only one of
+ ALU1/ALU2 and one of FALU1/FALU2 units for ALU1/2 and FALU1/2 instructions
+ respectively. */
+
+static void
+mips_ls2_dfa_post_advance_cycle (state_t state)
+{
+ if (cpu_unit_reservation_p (state, mips_ls2.alu1_core_unit_code))
+ {
+ /* Though there are no non-pipelined ALU1 insns,
+ we can get an instruction of type 'multi' before reload. */
+ gcc_assert (mips_ls2.cycle_has_multi_p);
+ mips_ls2.alu1_turn_p = false;
+ }
+
+ mips_ls2.cycle_has_multi_p = false;
+
+ if (cpu_unit_reservation_p (state, mips_ls2.alu2_core_unit_code))
+ /* We have a non-pipelined alu instruction in the core,
+ adjust round-robin counter. */
+ mips_ls2.alu1_turn_p = true;
+
+ if (mips_ls2.alu1_turn_p)
+ {
+ if (state_transition (state, mips_ls2.alu1_turn_enabled_insn) >= 0)
+ gcc_unreachable ();
+ }
+ else
+ {
+ if (state_transition (state, mips_ls2.alu2_turn_enabled_insn) >= 0)
+ gcc_unreachable ();
+ }
+
+ if (cpu_unit_reservation_p (state, mips_ls2.falu1_core_unit_code))
+ {
+ /* There are no non-pipelined FALU1 insns. */
+ gcc_unreachable ();
+ mips_ls2.falu1_turn_p = false;
+ }
+
+ if (cpu_unit_reservation_p (state, mips_ls2.falu2_core_unit_code))
+ /* We have a non-pipelined falu instruction in the core,
+ adjust round-robin counter. */
+ mips_ls2.falu1_turn_p = true;
+
+ if (mips_ls2.falu1_turn_p)
+ {
+ if (state_transition (state, mips_ls2.falu1_turn_enabled_insn) >= 0)
+ gcc_unreachable ();
+ }
+ else
+ {
+ if (state_transition (state, mips_ls2.falu2_turn_enabled_insn) >= 0)
+ gcc_unreachable ();
+ }
+}
+
+/* Implement TARGET_SCHED_DFA_POST_ADVANCE_CYCLE.
+ This hook is being called at the start of each cycle. */
+
+static void
+mips_dfa_post_advance_cycle (void)
+{
+ if (TUNE_LOONGSON_2EF)
+ mips_ls2_dfa_post_advance_cycle (curr_state);
+}
+
+/* Implement TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD. This should
+ be as wide as the scheduling freedom in the DFA. */
+
+static int
+mips_multipass_dfa_lookahead (void)
+{
+ /* Can schedule up to 4 of the 6 function units in any one cycle. */
+ if (TUNE_SB1)
+ return 4;
+
+ if (TUNE_LOONGSON_2EF || TUNE_LOONGSON_3A)
+ return 4;
+
+ if (TUNE_OCTEON)
+ return 2;
+
+ return 0;
+}
+
+/* Remove the instruction at index LOWER from ready queue READY and
+ reinsert it in front of the instruction at index HIGHER. LOWER must
+ be <= HIGHER. */
+
+static void
+mips_promote_ready (rtx *ready, int lower, int higher)
+{
+ rtx new_head;
+ int i;
+
+ new_head = ready[lower];
+ for (i = lower; i < higher; i++)
+ ready[i] = ready[i + 1];
+ ready[i] = new_head;
+}
+
+/* If the priority of the instruction at POS2 in the ready queue READY
+ is within LIMIT units of that of the instruction at POS1, swap the
+ instructions if POS2 is not already less than POS1. */
+
+static void
+mips_maybe_swap_ready (rtx *ready, int pos1, int pos2, int limit)
+{
+ if (pos1 < pos2
+ && INSN_PRIORITY (ready[pos1]) + limit >= INSN_PRIORITY (ready[pos2]))
+ {
+ rtx temp;
+
+ temp = ready[pos1];
+ ready[pos1] = ready[pos2];
+ ready[pos2] = temp;
+ }
+}
+
+/* Used by TUNE_MACC_CHAINS to record the last scheduled instruction
+ that may clobber hi or lo. */
+static rtx mips_macc_chains_last_hilo;
+
+/* A TUNE_MACC_CHAINS helper function. Record that instruction INSN has
+ been scheduled, updating mips_macc_chains_last_hilo appropriately. */
+
+static void
+mips_macc_chains_record (rtx insn)
+{
+ if (get_attr_may_clobber_hilo (insn))
+ mips_macc_chains_last_hilo = insn;
+}
+
+/* A TUNE_MACC_CHAINS helper function. Search ready queue READY, which
+ has NREADY elements, looking for a multiply-add or multiply-subtract
+ instruction that is cumulative with mips_macc_chains_last_hilo.
+ If there is one, promote it ahead of anything else that might
+ clobber hi or lo. */
+
+static void
+mips_macc_chains_reorder (rtx *ready, int nready)
+{
+ int i, j;
+
+ if (mips_macc_chains_last_hilo != 0)
+ for (i = nready - 1; i >= 0; i--)
+ if (mips_linked_madd_p (mips_macc_chains_last_hilo, ready[i]))
+ {
+ for (j = nready - 1; j > i; j--)
+ if (recog_memoized (ready[j]) >= 0
+ && get_attr_may_clobber_hilo (ready[j]))
+ {
+ mips_promote_ready (ready, i, j);
+ break;
+ }
+ break;
+ }
+}
+
+/* The last instruction to be scheduled. */
+static rtx vr4130_last_insn;
+
+/* A note_stores callback used by vr4130_true_reg_dependence_p. DATA
+ points to an rtx that is initially an instruction. Nullify the rtx
+ if the instruction uses the value of register X. */
+
+static void
+vr4130_true_reg_dependence_p_1 (rtx x, const_rtx pat ATTRIBUTE_UNUSED,
+ void *data)
+{
+ rtx *insn_ptr;
+
+ insn_ptr = (rtx *) data;
+ if (REG_P (x)
+ && *insn_ptr != 0
+ && reg_referenced_p (x, PATTERN (*insn_ptr)))
+ *insn_ptr = 0;
+}
+
+/* Return true if there is true register dependence between vr4130_last_insn
+ and INSN. */
+
+static bool
+vr4130_true_reg_dependence_p (rtx insn)
+{
+ note_stores (PATTERN (vr4130_last_insn),
+ vr4130_true_reg_dependence_p_1, &insn);
+ return insn == 0;
+}
+
+/* A TUNE_MIPS4130 helper function. Given that INSN1 is at the head of
+ the ready queue and that INSN2 is the instruction after it, return
+ true if it is worth promoting INSN2 ahead of INSN1. Look for cases
+ in which INSN1 and INSN2 can probably issue in parallel, but for
+ which (INSN2, INSN1) should be less sensitive to instruction
+ alignment than (INSN1, INSN2). See 4130.md for more details. */
+
+static bool
+vr4130_swap_insns_p (rtx insn1, rtx insn2)
+{
+ sd_iterator_def sd_it;
+ dep_t dep;
+
+ /* Check for the following case:
+
+ 1) there is some other instruction X with an anti dependence on INSN1;
+ 2) X has a higher priority than INSN2; and
+ 3) X is an arithmetic instruction (and thus has no unit restrictions).
+
+ If INSN1 is the last instruction blocking X, it would better to
+ choose (INSN1, X) over (INSN2, INSN1). */
+ FOR_EACH_DEP (insn1, SD_LIST_FORW, sd_it, dep)
+ if (DEP_TYPE (dep) == REG_DEP_ANTI
+ && INSN_PRIORITY (DEP_CON (dep)) > INSN_PRIORITY (insn2)
+ && recog_memoized (DEP_CON (dep)) >= 0
+ && get_attr_vr4130_class (DEP_CON (dep)) == VR4130_CLASS_ALU)
+ return false;
+
+ if (vr4130_last_insn != 0
+ && recog_memoized (insn1) >= 0
+ && recog_memoized (insn2) >= 0)
+ {
+ /* See whether INSN1 and INSN2 use different execution units,
+ or if they are both ALU-type instructions. If so, they can
+ probably execute in parallel. */
+ enum attr_vr4130_class class1 = get_attr_vr4130_class (insn1);
+ enum attr_vr4130_class class2 = get_attr_vr4130_class (insn2);
+ if (class1 != class2 || class1 == VR4130_CLASS_ALU)
+ {
+ /* If only one of the instructions has a dependence on
+ vr4130_last_insn, prefer to schedule the other one first. */
+ bool dep1_p = vr4130_true_reg_dependence_p (insn1);
+ bool dep2_p = vr4130_true_reg_dependence_p (insn2);
+ if (dep1_p != dep2_p)
+ return dep1_p;
+
+ /* Prefer to schedule INSN2 ahead of INSN1 if vr4130_last_insn
+ is not an ALU-type instruction and if INSN1 uses the same
+ execution unit. (Note that if this condition holds, we already
+ know that INSN2 uses a different execution unit.) */
+ if (class1 != VR4130_CLASS_ALU
+ && recog_memoized (vr4130_last_insn) >= 0
+ && class1 == get_attr_vr4130_class (vr4130_last_insn))
+ return true;
+ }
+ }
+ return false;
+}
+
+/* A TUNE_MIPS4130 helper function. (READY, NREADY) describes a ready
+ queue with at least two instructions. Swap the first two if
+ vr4130_swap_insns_p says that it could be worthwhile. */
+
+static void
+vr4130_reorder (rtx *ready, int nready)
+{
+ if (vr4130_swap_insns_p (ready[nready - 1], ready[nready - 2]))
+ mips_promote_ready (ready, nready - 2, nready - 1);
+}
+
+/* Record whether last 74k AGEN instruction was a load or store. */
+static enum attr_type mips_last_74k_agen_insn = TYPE_UNKNOWN;
+
+/* Initialize mips_last_74k_agen_insn from INSN. A null argument
+ resets to TYPE_UNKNOWN state. */
+
+static void
+mips_74k_agen_init (rtx insn)
+{
+ if (!insn || CALL_P (insn) || JUMP_P (insn))
+ mips_last_74k_agen_insn = TYPE_UNKNOWN;
+ else
+ {
+ enum attr_type type = get_attr_type (insn);
+ if (type == TYPE_LOAD || type == TYPE_STORE)
+ mips_last_74k_agen_insn = type;
+ }
+}
+
+/* A TUNE_74K helper function. The 74K AGEN pipeline likes multiple
+ loads to be grouped together, and multiple stores to be grouped
+ together. Swap things around in the ready queue to make this happen. */
+
+static void
+mips_74k_agen_reorder (rtx *ready, int nready)
+{
+ int i;
+ int store_pos, load_pos;
+
+ store_pos = -1;
+ load_pos = -1;
+
+ for (i = nready - 1; i >= 0; i--)
+ {
+ rtx insn = ready[i];
+ if (USEFUL_INSN_P (insn))
+ switch (get_attr_type (insn))
+ {
+ case TYPE_STORE:
+ if (store_pos == -1)
+ store_pos = i;
+ break;
+
+ case TYPE_LOAD:
+ if (load_pos == -1)
+ load_pos = i;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (load_pos == -1 || store_pos == -1)
+ return;
+
+ switch (mips_last_74k_agen_insn)
+ {
+ case TYPE_UNKNOWN:
+ /* Prefer to schedule loads since they have a higher latency. */
+ case TYPE_LOAD:
+ /* Swap loads to the front of the queue. */
+ mips_maybe_swap_ready (ready, load_pos, store_pos, 4);
+ break;
+ case TYPE_STORE:
+ /* Swap stores to the front of the queue. */
+ mips_maybe_swap_ready (ready, store_pos, load_pos, 4);
+ break;
+ default:
+ break;
+ }
+}
+
+/* Implement TARGET_SCHED_INIT. */
+
+static void
+mips_sched_init (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED,
+ int max_ready ATTRIBUTE_UNUSED)
+{
+ mips_macc_chains_last_hilo = 0;
+ vr4130_last_insn = 0;
+ mips_74k_agen_init (NULL_RTX);
+
+ /* When scheduling for Loongson2, branch instructions go to ALU1,
+ therefore basic block is most likely to start with round-robin counter
+ pointed to ALU2. */
+ mips_ls2.alu1_turn_p = false;
+ mips_ls2.falu1_turn_p = true;
+}
+
+/* Subroutine used by TARGET_SCHED_REORDER and TARGET_SCHED_REORDER2. */
+
+static void
+mips_sched_reorder_1 (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED,
+ rtx *ready, int *nreadyp, int cycle ATTRIBUTE_UNUSED)
+{
+ if (!reload_completed
+ && TUNE_MACC_CHAINS
+ && *nreadyp > 0)
+ mips_macc_chains_reorder (ready, *nreadyp);
+
+ if (reload_completed
+ && TUNE_MIPS4130
+ && !TARGET_VR4130_ALIGN
+ && *nreadyp > 1)
+ vr4130_reorder (ready, *nreadyp);
+
+ if (TUNE_74K)
+ mips_74k_agen_reorder (ready, *nreadyp);
+}
+
+/* Implement TARGET_SCHED_REORDER. */
+
+static int
+mips_sched_reorder (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED,
+ rtx *ready, int *nreadyp, int cycle ATTRIBUTE_UNUSED)
+{
+ mips_sched_reorder_1 (file, verbose, ready, nreadyp, cycle);
+ return mips_issue_rate ();
+}
+
+/* Implement TARGET_SCHED_REORDER2. */
+
+static int
+mips_sched_reorder2 (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED,
+ rtx *ready, int *nreadyp, int cycle ATTRIBUTE_UNUSED)
+{
+ mips_sched_reorder_1 (file, verbose, ready, nreadyp, cycle);
+ return cached_can_issue_more;
+}
+
+/* Update round-robin counters for ALU1/2 and FALU1/2. */
+
+static void
+mips_ls2_variable_issue (rtx insn)
+{
+ if (mips_ls2.alu1_turn_p)
+ {
+ if (cpu_unit_reservation_p (curr_state, mips_ls2.alu1_core_unit_code))
+ mips_ls2.alu1_turn_p = false;
+ }
+ else
+ {
+ if (cpu_unit_reservation_p (curr_state, mips_ls2.alu2_core_unit_code))
+ mips_ls2.alu1_turn_p = true;
+ }
+
+ if (mips_ls2.falu1_turn_p)
+ {
+ if (cpu_unit_reservation_p (curr_state, mips_ls2.falu1_core_unit_code))
+ mips_ls2.falu1_turn_p = false;
+ }
+ else
+ {
+ if (cpu_unit_reservation_p (curr_state, mips_ls2.falu2_core_unit_code))
+ mips_ls2.falu1_turn_p = true;
+ }
+
+ if (recog_memoized (insn) >= 0)
+ mips_ls2.cycle_has_multi_p |= (get_attr_type (insn) == TYPE_MULTI);
+}
+
+/* Implement TARGET_SCHED_VARIABLE_ISSUE. */
+
+static int
+mips_variable_issue (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED,
+ rtx insn, int more)
+{
+ /* Ignore USEs and CLOBBERs; don't count them against the issue rate. */
+ if (USEFUL_INSN_P (insn))
+ {
+ if (get_attr_type (insn) != TYPE_GHOST)
+ more--;
+ if (!reload_completed && TUNE_MACC_CHAINS)
+ mips_macc_chains_record (insn);
+ vr4130_last_insn = insn;
+ if (TUNE_74K)
+ mips_74k_agen_init (insn);
+ else if (TUNE_LOONGSON_2EF)
+ mips_ls2_variable_issue (insn);
+ }
+
+ /* Instructions of type 'multi' should all be split before
+ the second scheduling pass. */
+ gcc_assert (!reload_completed
+ || recog_memoized (insn) < 0
+ || get_attr_type (insn) != TYPE_MULTI);
+
+ cached_can_issue_more = more;
+ return more;
+}
+
+/* Given that we have an rtx of the form (prefetch ... WRITE LOCALITY),
+ return the first operand of the associated PREF or PREFX insn. */
+
+rtx
+mips_prefetch_cookie (rtx write, rtx locality)
+{
+ /* store_streamed / load_streamed. */
+ if (INTVAL (locality) <= 0)
+ return GEN_INT (INTVAL (write) + 4);
+
+ /* store / load. */
+ if (INTVAL (locality) <= 2)
+ return write;
+
+ /* store_retained / load_retained. */
+ return GEN_INT (INTVAL (write) + 6);
+}
+
+/* Flags that indicate when a built-in function is available.
+
+ BUILTIN_AVAIL_NON_MIPS16
+ The function is available on the current target, but only
+ in non-MIPS16 mode. */
+#define BUILTIN_AVAIL_NON_MIPS16 1
+
+/* Declare an availability predicate for built-in functions that
+ require non-MIPS16 mode and also require COND to be true.
+ NAME is the main part of the predicate's name. */
+#define AVAIL_NON_MIPS16(NAME, COND) \
+ static unsigned int \
+ mips_builtin_avail_##NAME (void) \
+ { \
+ return (COND) ? BUILTIN_AVAIL_NON_MIPS16 : 0; \
+ }
+
+/* This structure describes a single built-in function. */
+struct mips_builtin_description {
+ /* The code of the main .md file instruction. See mips_builtin_type
+ for more information. */
+ enum insn_code icode;
+
+ /* The floating-point comparison code to use with ICODE, if any. */
+ enum mips_fp_condition cond;
+
+ /* The name of the built-in function. */
+ const char *name;
+
+ /* Specifies how the function should be expanded. */
+ enum mips_builtin_type builtin_type;
+
+ /* The function's prototype. */
+ enum mips_function_type function_type;
+
+ /* Whether the function is available. */
+ unsigned int (*avail) (void);
+};
+
+AVAIL_NON_MIPS16 (paired_single, TARGET_PAIRED_SINGLE_FLOAT)
+AVAIL_NON_MIPS16 (sb1_paired_single, TARGET_SB1 && TARGET_PAIRED_SINGLE_FLOAT)
+AVAIL_NON_MIPS16 (mips3d, TARGET_MIPS3D)
+AVAIL_NON_MIPS16 (dsp, TARGET_DSP)
+AVAIL_NON_MIPS16 (dspr2, TARGET_DSPR2)
+AVAIL_NON_MIPS16 (dsp_32, !TARGET_64BIT && TARGET_DSP)
+AVAIL_NON_MIPS16 (dspr2_32, !TARGET_64BIT && TARGET_DSPR2)
+AVAIL_NON_MIPS16 (loongson, TARGET_LOONGSON_VECTORS)
+AVAIL_NON_MIPS16 (cache, TARGET_CACHE_BUILTIN)
+
+/* Construct a mips_builtin_description from the given arguments.
+
+ INSN is the name of the associated instruction pattern, without the
+ leading CODE_FOR_mips_.
+
+ CODE is the floating-point condition code associated with the
+ function. It can be 'f' if the field is not applicable.
+
+ NAME is the name of the function itself, without the leading
+ "__builtin_mips_".
+
+ BUILTIN_TYPE and FUNCTION_TYPE are mips_builtin_description fields.
+
+ AVAIL is the name of the availability predicate, without the leading
+ mips_builtin_avail_. */
+#define MIPS_BUILTIN(INSN, COND, NAME, BUILTIN_TYPE, \
+ FUNCTION_TYPE, AVAIL) \
+ { CODE_FOR_mips_ ## INSN, MIPS_FP_COND_ ## COND, \
+ "__builtin_mips_" NAME, BUILTIN_TYPE, FUNCTION_TYPE, \
+ mips_builtin_avail_ ## AVAIL }
+
+/* Define __builtin_mips_<INSN>, which is a MIPS_BUILTIN_DIRECT function
+ mapped to instruction CODE_FOR_mips_<INSN>, FUNCTION_TYPE and AVAIL
+ are as for MIPS_BUILTIN. */
+#define DIRECT_BUILTIN(INSN, FUNCTION_TYPE, AVAIL) \
+ MIPS_BUILTIN (INSN, f, #INSN, MIPS_BUILTIN_DIRECT, FUNCTION_TYPE, AVAIL)
+
+/* Define __builtin_mips_<INSN>_<COND>_{s,d} functions, both of which
+ are subject to mips_builtin_avail_<AVAIL>. */
+#define CMP_SCALAR_BUILTINS(INSN, COND, AVAIL) \
+ MIPS_BUILTIN (INSN ## _cond_s, COND, #INSN "_" #COND "_s", \
+ MIPS_BUILTIN_CMP_SINGLE, MIPS_INT_FTYPE_SF_SF, AVAIL), \
+ MIPS_BUILTIN (INSN ## _cond_d, COND, #INSN "_" #COND "_d", \
+ MIPS_BUILTIN_CMP_SINGLE, MIPS_INT_FTYPE_DF_DF, AVAIL)
+
+/* Define __builtin_mips_{any,all,upper,lower}_<INSN>_<COND>_ps.
+ The lower and upper forms are subject to mips_builtin_avail_<AVAIL>
+ while the any and all forms are subject to mips_builtin_avail_mips3d. */
+#define CMP_PS_BUILTINS(INSN, COND, AVAIL) \
+ MIPS_BUILTIN (INSN ## _cond_ps, COND, "any_" #INSN "_" #COND "_ps", \
+ MIPS_BUILTIN_CMP_ANY, MIPS_INT_FTYPE_V2SF_V2SF, \
+ mips3d), \
+ MIPS_BUILTIN (INSN ## _cond_ps, COND, "all_" #INSN "_" #COND "_ps", \
+ MIPS_BUILTIN_CMP_ALL, MIPS_INT_FTYPE_V2SF_V2SF, \
+ mips3d), \
+ MIPS_BUILTIN (INSN ## _cond_ps, COND, "lower_" #INSN "_" #COND "_ps", \
+ MIPS_BUILTIN_CMP_LOWER, MIPS_INT_FTYPE_V2SF_V2SF, \
+ AVAIL), \
+ MIPS_BUILTIN (INSN ## _cond_ps, COND, "upper_" #INSN "_" #COND "_ps", \
+ MIPS_BUILTIN_CMP_UPPER, MIPS_INT_FTYPE_V2SF_V2SF, \
+ AVAIL)
+
+/* Define __builtin_mips_{any,all}_<INSN>_<COND>_4s. The functions
+ are subject to mips_builtin_avail_mips3d. */
+#define CMP_4S_BUILTINS(INSN, COND) \
+ MIPS_BUILTIN (INSN ## _cond_4s, COND, "any_" #INSN "_" #COND "_4s", \
+ MIPS_BUILTIN_CMP_ANY, \
+ MIPS_INT_FTYPE_V2SF_V2SF_V2SF_V2SF, mips3d), \
+ MIPS_BUILTIN (INSN ## _cond_4s, COND, "all_" #INSN "_" #COND "_4s", \
+ MIPS_BUILTIN_CMP_ALL, \
+ MIPS_INT_FTYPE_V2SF_V2SF_V2SF_V2SF, mips3d)
+
+/* Define __builtin_mips_mov{t,f}_<INSN>_<COND>_ps. The comparison
+ instruction requires mips_builtin_avail_<AVAIL>. */
+#define MOVTF_BUILTINS(INSN, COND, AVAIL) \
+ MIPS_BUILTIN (INSN ## _cond_ps, COND, "movt_" #INSN "_" #COND "_ps", \
+ MIPS_BUILTIN_MOVT, MIPS_V2SF_FTYPE_V2SF_V2SF_V2SF_V2SF, \
+ AVAIL), \
+ MIPS_BUILTIN (INSN ## _cond_ps, COND, "movf_" #INSN "_" #COND "_ps", \
+ MIPS_BUILTIN_MOVF, MIPS_V2SF_FTYPE_V2SF_V2SF_V2SF_V2SF, \
+ AVAIL)
+
+/* Define all the built-in functions related to C.cond.fmt condition COND. */
+#define CMP_BUILTINS(COND) \
+ MOVTF_BUILTINS (c, COND, paired_single), \
+ MOVTF_BUILTINS (cabs, COND, mips3d), \
+ CMP_SCALAR_BUILTINS (cabs, COND, mips3d), \
+ CMP_PS_BUILTINS (c, COND, paired_single), \
+ CMP_PS_BUILTINS (cabs, COND, mips3d), \
+ CMP_4S_BUILTINS (c, COND), \
+ CMP_4S_BUILTINS (cabs, COND)
+
+/* Define __builtin_mips_<INSN>, which is a MIPS_BUILTIN_DIRECT_NO_TARGET
+ function mapped to instruction CODE_FOR_mips_<INSN>, FUNCTION_TYPE
+ and AVAIL are as for MIPS_BUILTIN. */
+#define DIRECT_NO_TARGET_BUILTIN(INSN, FUNCTION_TYPE, AVAIL) \
+ MIPS_BUILTIN (INSN, f, #INSN, MIPS_BUILTIN_DIRECT_NO_TARGET, \
+ FUNCTION_TYPE, AVAIL)
+
+/* Define __builtin_mips_bposge<VALUE>. <VALUE> is 32 for the MIPS32 DSP
+ branch instruction. AVAIL is as for MIPS_BUILTIN. */
+#define BPOSGE_BUILTIN(VALUE, AVAIL) \
+ MIPS_BUILTIN (bposge, f, "bposge" #VALUE, \
+ MIPS_BUILTIN_BPOSGE ## VALUE, MIPS_SI_FTYPE_VOID, AVAIL)
+
+/* Define a Loongson MIPS_BUILTIN_DIRECT function __builtin_loongson_<FN_NAME>
+ for instruction CODE_FOR_loongson_<INSN>. FUNCTION_TYPE is a
+ builtin_description field. */
+#define LOONGSON_BUILTIN_ALIAS(INSN, FN_NAME, FUNCTION_TYPE) \
+ { CODE_FOR_loongson_ ## INSN, MIPS_FP_COND_f, \
+ "__builtin_loongson_" #FN_NAME, MIPS_BUILTIN_DIRECT, \
+ FUNCTION_TYPE, mips_builtin_avail_loongson }
+
+/* Define a Loongson MIPS_BUILTIN_DIRECT function __builtin_loongson_<INSN>
+ for instruction CODE_FOR_loongson_<INSN>. FUNCTION_TYPE is a
+ builtin_description field. */
+#define LOONGSON_BUILTIN(INSN, FUNCTION_TYPE) \
+ LOONGSON_BUILTIN_ALIAS (INSN, INSN, FUNCTION_TYPE)
+
+/* Like LOONGSON_BUILTIN, but add _<SUFFIX> to the end of the function name.
+ We use functions of this form when the same insn can be usefully applied
+ to more than one datatype. */
+#define LOONGSON_BUILTIN_SUFFIX(INSN, SUFFIX, FUNCTION_TYPE) \
+ LOONGSON_BUILTIN_ALIAS (INSN, INSN ## _ ## SUFFIX, FUNCTION_TYPE)
+
+#define CODE_FOR_mips_sqrt_ps CODE_FOR_sqrtv2sf2
+#define CODE_FOR_mips_addq_ph CODE_FOR_addv2hi3
+#define CODE_FOR_mips_addu_qb CODE_FOR_addv4qi3
+#define CODE_FOR_mips_subq_ph CODE_FOR_subv2hi3
+#define CODE_FOR_mips_subu_qb CODE_FOR_subv4qi3
+#define CODE_FOR_mips_mul_ph CODE_FOR_mulv2hi3
+#define CODE_FOR_mips_mult CODE_FOR_mulsidi3_32bit
+#define CODE_FOR_mips_multu CODE_FOR_umulsidi3_32bit
+
+#define CODE_FOR_loongson_packsswh CODE_FOR_vec_pack_ssat_v2si
+#define CODE_FOR_loongson_packsshb CODE_FOR_vec_pack_ssat_v4hi
+#define CODE_FOR_loongson_packushb CODE_FOR_vec_pack_usat_v4hi
+#define CODE_FOR_loongson_paddw CODE_FOR_addv2si3
+#define CODE_FOR_loongson_paddh CODE_FOR_addv4hi3
+#define CODE_FOR_loongson_paddb CODE_FOR_addv8qi3
+#define CODE_FOR_loongson_paddsh CODE_FOR_ssaddv4hi3
+#define CODE_FOR_loongson_paddsb CODE_FOR_ssaddv8qi3
+#define CODE_FOR_loongson_paddush CODE_FOR_usaddv4hi3
+#define CODE_FOR_loongson_paddusb CODE_FOR_usaddv8qi3
+#define CODE_FOR_loongson_pmaxsh CODE_FOR_smaxv4hi3
+#define CODE_FOR_loongson_pmaxub CODE_FOR_umaxv8qi3
+#define CODE_FOR_loongson_pminsh CODE_FOR_sminv4hi3
+#define CODE_FOR_loongson_pminub CODE_FOR_uminv8qi3
+#define CODE_FOR_loongson_pmulhuh CODE_FOR_umulv4hi3_highpart
+#define CODE_FOR_loongson_pmulhh CODE_FOR_smulv4hi3_highpart
+#define CODE_FOR_loongson_pmullh CODE_FOR_mulv4hi3
+#define CODE_FOR_loongson_psllh CODE_FOR_ashlv4hi3
+#define CODE_FOR_loongson_psllw CODE_FOR_ashlv2si3
+#define CODE_FOR_loongson_psrlh CODE_FOR_lshrv4hi3
+#define CODE_FOR_loongson_psrlw CODE_FOR_lshrv2si3
+#define CODE_FOR_loongson_psrah CODE_FOR_ashrv4hi3
+#define CODE_FOR_loongson_psraw CODE_FOR_ashrv2si3
+#define CODE_FOR_loongson_psubw CODE_FOR_subv2si3
+#define CODE_FOR_loongson_psubh CODE_FOR_subv4hi3
+#define CODE_FOR_loongson_psubb CODE_FOR_subv8qi3
+#define CODE_FOR_loongson_psubsh CODE_FOR_sssubv4hi3
+#define CODE_FOR_loongson_psubsb CODE_FOR_sssubv8qi3
+#define CODE_FOR_loongson_psubush CODE_FOR_ussubv4hi3
+#define CODE_FOR_loongson_psubusb CODE_FOR_ussubv8qi3
+#define CODE_FOR_loongson_punpckhbh CODE_FOR_vec_interleave_highv8qi
+#define CODE_FOR_loongson_punpckhhw CODE_FOR_vec_interleave_highv4hi
+#define CODE_FOR_loongson_punpckhwd CODE_FOR_vec_interleave_highv2si
+#define CODE_FOR_loongson_punpcklbh CODE_FOR_vec_interleave_lowv8qi
+#define CODE_FOR_loongson_punpcklhw CODE_FOR_vec_interleave_lowv4hi
+#define CODE_FOR_loongson_punpcklwd CODE_FOR_vec_interleave_lowv2si
+
+static const struct mips_builtin_description mips_builtins[] = {
+ DIRECT_BUILTIN (pll_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, paired_single),
+ DIRECT_BUILTIN (pul_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, paired_single),
+ DIRECT_BUILTIN (plu_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, paired_single),
+ DIRECT_BUILTIN (puu_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, paired_single),
+ DIRECT_BUILTIN (cvt_ps_s, MIPS_V2SF_FTYPE_SF_SF, paired_single),
+ DIRECT_BUILTIN (cvt_s_pl, MIPS_SF_FTYPE_V2SF, paired_single),
+ DIRECT_BUILTIN (cvt_s_pu, MIPS_SF_FTYPE_V2SF, paired_single),
+ DIRECT_BUILTIN (abs_ps, MIPS_V2SF_FTYPE_V2SF, paired_single),
+
+ DIRECT_BUILTIN (alnv_ps, MIPS_V2SF_FTYPE_V2SF_V2SF_INT, paired_single),
+ DIRECT_BUILTIN (addr_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, mips3d),
+ DIRECT_BUILTIN (mulr_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, mips3d),
+ DIRECT_BUILTIN (cvt_pw_ps, MIPS_V2SF_FTYPE_V2SF, mips3d),
+ DIRECT_BUILTIN (cvt_ps_pw, MIPS_V2SF_FTYPE_V2SF, mips3d),
+
+ DIRECT_BUILTIN (recip1_s, MIPS_SF_FTYPE_SF, mips3d),
+ DIRECT_BUILTIN (recip1_d, MIPS_DF_FTYPE_DF, mips3d),
+ DIRECT_BUILTIN (recip1_ps, MIPS_V2SF_FTYPE_V2SF, mips3d),
+ DIRECT_BUILTIN (recip2_s, MIPS_SF_FTYPE_SF_SF, mips3d),
+ DIRECT_BUILTIN (recip2_d, MIPS_DF_FTYPE_DF_DF, mips3d),
+ DIRECT_BUILTIN (recip2_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, mips3d),
+
+ DIRECT_BUILTIN (rsqrt1_s, MIPS_SF_FTYPE_SF, mips3d),
+ DIRECT_BUILTIN (rsqrt1_d, MIPS_DF_FTYPE_DF, mips3d),
+ DIRECT_BUILTIN (rsqrt1_ps, MIPS_V2SF_FTYPE_V2SF, mips3d),
+ DIRECT_BUILTIN (rsqrt2_s, MIPS_SF_FTYPE_SF_SF, mips3d),
+ DIRECT_BUILTIN (rsqrt2_d, MIPS_DF_FTYPE_DF_DF, mips3d),
+ DIRECT_BUILTIN (rsqrt2_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, mips3d),
+
+ MIPS_FP_CONDITIONS (CMP_BUILTINS),
+
+ /* Built-in functions for the SB-1 processor. */
+ DIRECT_BUILTIN (sqrt_ps, MIPS_V2SF_FTYPE_V2SF, sb1_paired_single),
+
+ /* Built-in functions for the DSP ASE (32-bit and 64-bit). */
+ DIRECT_BUILTIN (addq_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dsp),
+ DIRECT_BUILTIN (addq_s_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dsp),
+ DIRECT_BUILTIN (addq_s_w, MIPS_SI_FTYPE_SI_SI, dsp),
+ DIRECT_BUILTIN (addu_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, dsp),
+ DIRECT_BUILTIN (addu_s_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, dsp),
+ DIRECT_BUILTIN (subq_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dsp),
+ DIRECT_BUILTIN (subq_s_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dsp),
+ DIRECT_BUILTIN (subq_s_w, MIPS_SI_FTYPE_SI_SI, dsp),
+ DIRECT_BUILTIN (subu_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, dsp),
+ DIRECT_BUILTIN (subu_s_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, dsp),
+ DIRECT_BUILTIN (addsc, MIPS_SI_FTYPE_SI_SI, dsp),
+ DIRECT_BUILTIN (addwc, MIPS_SI_FTYPE_SI_SI, dsp),
+ DIRECT_BUILTIN (modsub, MIPS_SI_FTYPE_SI_SI, dsp),
+ DIRECT_BUILTIN (raddu_w_qb, MIPS_SI_FTYPE_V4QI, dsp),
+ DIRECT_BUILTIN (absq_s_ph, MIPS_V2HI_FTYPE_V2HI, dsp),
+ DIRECT_BUILTIN (absq_s_w, MIPS_SI_FTYPE_SI, dsp),
+ DIRECT_BUILTIN (precrq_qb_ph, MIPS_V4QI_FTYPE_V2HI_V2HI, dsp),
+ DIRECT_BUILTIN (precrq_ph_w, MIPS_V2HI_FTYPE_SI_SI, dsp),
+ DIRECT_BUILTIN (precrq_rs_ph_w, MIPS_V2HI_FTYPE_SI_SI, dsp),
+ DIRECT_BUILTIN (precrqu_s_qb_ph, MIPS_V4QI_FTYPE_V2HI_V2HI, dsp),
+ DIRECT_BUILTIN (preceq_w_phl, MIPS_SI_FTYPE_V2HI, dsp),
+ DIRECT_BUILTIN (preceq_w_phr, MIPS_SI_FTYPE_V2HI, dsp),
+ DIRECT_BUILTIN (precequ_ph_qbl, MIPS_V2HI_FTYPE_V4QI, dsp),
+ DIRECT_BUILTIN (precequ_ph_qbr, MIPS_V2HI_FTYPE_V4QI, dsp),
+ DIRECT_BUILTIN (precequ_ph_qbla, MIPS_V2HI_FTYPE_V4QI, dsp),
+ DIRECT_BUILTIN (precequ_ph_qbra, MIPS_V2HI_FTYPE_V4QI, dsp),
+ DIRECT_BUILTIN (preceu_ph_qbl, MIPS_V2HI_FTYPE_V4QI, dsp),
+ DIRECT_BUILTIN (preceu_ph_qbr, MIPS_V2HI_FTYPE_V4QI, dsp),
+ DIRECT_BUILTIN (preceu_ph_qbla, MIPS_V2HI_FTYPE_V4QI, dsp),
+ DIRECT_BUILTIN (preceu_ph_qbra, MIPS_V2HI_FTYPE_V4QI, dsp),
+ DIRECT_BUILTIN (shll_qb, MIPS_V4QI_FTYPE_V4QI_SI, dsp),
+ DIRECT_BUILTIN (shll_ph, MIPS_V2HI_FTYPE_V2HI_SI, dsp),
+ DIRECT_BUILTIN (shll_s_ph, MIPS_V2HI_FTYPE_V2HI_SI, dsp),
+ DIRECT_BUILTIN (shll_s_w, MIPS_SI_FTYPE_SI_SI, dsp),
+ DIRECT_BUILTIN (shrl_qb, MIPS_V4QI_FTYPE_V4QI_SI, dsp),
+ DIRECT_BUILTIN (shra_ph, MIPS_V2HI_FTYPE_V2HI_SI, dsp),
+ DIRECT_BUILTIN (shra_r_ph, MIPS_V2HI_FTYPE_V2HI_SI, dsp),
+ DIRECT_BUILTIN (shra_r_w, MIPS_SI_FTYPE_SI_SI, dsp),
+ DIRECT_BUILTIN (muleu_s_ph_qbl, MIPS_V2HI_FTYPE_V4QI_V2HI, dsp),
+ DIRECT_BUILTIN (muleu_s_ph_qbr, MIPS_V2HI_FTYPE_V4QI_V2HI, dsp),
+ DIRECT_BUILTIN (mulq_rs_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dsp),
+ DIRECT_BUILTIN (muleq_s_w_phl, MIPS_SI_FTYPE_V2HI_V2HI, dsp),
+ DIRECT_BUILTIN (muleq_s_w_phr, MIPS_SI_FTYPE_V2HI_V2HI, dsp),
+ DIRECT_BUILTIN (bitrev, MIPS_SI_FTYPE_SI, dsp),
+ DIRECT_BUILTIN (insv, MIPS_SI_FTYPE_SI_SI, dsp),
+ DIRECT_BUILTIN (repl_qb, MIPS_V4QI_FTYPE_SI, dsp),
+ DIRECT_BUILTIN (repl_ph, MIPS_V2HI_FTYPE_SI, dsp),
+ DIRECT_NO_TARGET_BUILTIN (cmpu_eq_qb, MIPS_VOID_FTYPE_V4QI_V4QI, dsp),
+ DIRECT_NO_TARGET_BUILTIN (cmpu_lt_qb, MIPS_VOID_FTYPE_V4QI_V4QI, dsp),
+ DIRECT_NO_TARGET_BUILTIN (cmpu_le_qb, MIPS_VOID_FTYPE_V4QI_V4QI, dsp),
+ DIRECT_BUILTIN (cmpgu_eq_qb, MIPS_SI_FTYPE_V4QI_V4QI, dsp),
+ DIRECT_BUILTIN (cmpgu_lt_qb, MIPS_SI_FTYPE_V4QI_V4QI, dsp),
+ DIRECT_BUILTIN (cmpgu_le_qb, MIPS_SI_FTYPE_V4QI_V4QI, dsp),
+ DIRECT_NO_TARGET_BUILTIN (cmp_eq_ph, MIPS_VOID_FTYPE_V2HI_V2HI, dsp),
+ DIRECT_NO_TARGET_BUILTIN (cmp_lt_ph, MIPS_VOID_FTYPE_V2HI_V2HI, dsp),
+ DIRECT_NO_TARGET_BUILTIN (cmp_le_ph, MIPS_VOID_FTYPE_V2HI_V2HI, dsp),
+ DIRECT_BUILTIN (pick_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, dsp),
+ DIRECT_BUILTIN (pick_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dsp),
+ DIRECT_BUILTIN (packrl_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dsp),
+ DIRECT_NO_TARGET_BUILTIN (wrdsp, MIPS_VOID_FTYPE_SI_SI, dsp),
+ DIRECT_BUILTIN (rddsp, MIPS_SI_FTYPE_SI, dsp),
+ DIRECT_BUILTIN (lbux, MIPS_SI_FTYPE_POINTER_SI, dsp),
+ DIRECT_BUILTIN (lhx, MIPS_SI_FTYPE_POINTER_SI, dsp),
+ DIRECT_BUILTIN (lwx, MIPS_SI_FTYPE_POINTER_SI, dsp),
+ BPOSGE_BUILTIN (32, dsp),
+
+ /* The following are for the MIPS DSP ASE REV 2 (32-bit and 64-bit). */
+ DIRECT_BUILTIN (absq_s_qb, MIPS_V4QI_FTYPE_V4QI, dspr2),
+ DIRECT_BUILTIN (addu_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dspr2),
+ DIRECT_BUILTIN (addu_s_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dspr2),
+ DIRECT_BUILTIN (adduh_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, dspr2),
+ DIRECT_BUILTIN (adduh_r_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, dspr2),
+ DIRECT_BUILTIN (append, MIPS_SI_FTYPE_SI_SI_SI, dspr2),
+ DIRECT_BUILTIN (balign, MIPS_SI_FTYPE_SI_SI_SI, dspr2),
+ DIRECT_BUILTIN (cmpgdu_eq_qb, MIPS_SI_FTYPE_V4QI_V4QI, dspr2),
+ DIRECT_BUILTIN (cmpgdu_lt_qb, MIPS_SI_FTYPE_V4QI_V4QI, dspr2),
+ DIRECT_BUILTIN (cmpgdu_le_qb, MIPS_SI_FTYPE_V4QI_V4QI, dspr2),
+ DIRECT_BUILTIN (mul_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dspr2),
+ DIRECT_BUILTIN (mul_s_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dspr2),
+ DIRECT_BUILTIN (mulq_rs_w, MIPS_SI_FTYPE_SI_SI, dspr2),
+ DIRECT_BUILTIN (mulq_s_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dspr2),
+ DIRECT_BUILTIN (mulq_s_w, MIPS_SI_FTYPE_SI_SI, dspr2),
+ DIRECT_BUILTIN (precr_qb_ph, MIPS_V4QI_FTYPE_V2HI_V2HI, dspr2),
+ DIRECT_BUILTIN (precr_sra_ph_w, MIPS_V2HI_FTYPE_SI_SI_SI, dspr2),
+ DIRECT_BUILTIN (precr_sra_r_ph_w, MIPS_V2HI_FTYPE_SI_SI_SI, dspr2),
+ DIRECT_BUILTIN (prepend, MIPS_SI_FTYPE_SI_SI_SI, dspr2),
+ DIRECT_BUILTIN (shra_qb, MIPS_V4QI_FTYPE_V4QI_SI, dspr2),
+ DIRECT_BUILTIN (shra_r_qb, MIPS_V4QI_FTYPE_V4QI_SI, dspr2),
+ DIRECT_BUILTIN (shrl_ph, MIPS_V2HI_FTYPE_V2HI_SI, dspr2),
+ DIRECT_BUILTIN (subu_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dspr2),
+ DIRECT_BUILTIN (subu_s_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dspr2),
+ DIRECT_BUILTIN (subuh_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, dspr2),
+ DIRECT_BUILTIN (subuh_r_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, dspr2),
+ DIRECT_BUILTIN (addqh_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dspr2),
+ DIRECT_BUILTIN (addqh_r_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dspr2),
+ DIRECT_BUILTIN (addqh_w, MIPS_SI_FTYPE_SI_SI, dspr2),
+ DIRECT_BUILTIN (addqh_r_w, MIPS_SI_FTYPE_SI_SI, dspr2),
+ DIRECT_BUILTIN (subqh_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dspr2),
+ DIRECT_BUILTIN (subqh_r_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dspr2),
+ DIRECT_BUILTIN (subqh_w, MIPS_SI_FTYPE_SI_SI, dspr2),
+ DIRECT_BUILTIN (subqh_r_w, MIPS_SI_FTYPE_SI_SI, dspr2),
+
+ /* Built-in functions for the DSP ASE (32-bit only). */
+ DIRECT_BUILTIN (dpau_h_qbl, MIPS_DI_FTYPE_DI_V4QI_V4QI, dsp_32),
+ DIRECT_BUILTIN (dpau_h_qbr, MIPS_DI_FTYPE_DI_V4QI_V4QI, dsp_32),
+ DIRECT_BUILTIN (dpsu_h_qbl, MIPS_DI_FTYPE_DI_V4QI_V4QI, dsp_32),
+ DIRECT_BUILTIN (dpsu_h_qbr, MIPS_DI_FTYPE_DI_V4QI_V4QI, dsp_32),
+ DIRECT_BUILTIN (dpaq_s_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dsp_32),
+ DIRECT_BUILTIN (dpsq_s_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dsp_32),
+ DIRECT_BUILTIN (mulsaq_s_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dsp_32),
+ DIRECT_BUILTIN (dpaq_sa_l_w, MIPS_DI_FTYPE_DI_SI_SI, dsp_32),
+ DIRECT_BUILTIN (dpsq_sa_l_w, MIPS_DI_FTYPE_DI_SI_SI, dsp_32),
+ DIRECT_BUILTIN (maq_s_w_phl, MIPS_DI_FTYPE_DI_V2HI_V2HI, dsp_32),
+ DIRECT_BUILTIN (maq_s_w_phr, MIPS_DI_FTYPE_DI_V2HI_V2HI, dsp_32),
+ DIRECT_BUILTIN (maq_sa_w_phl, MIPS_DI_FTYPE_DI_V2HI_V2HI, dsp_32),
+ DIRECT_BUILTIN (maq_sa_w_phr, MIPS_DI_FTYPE_DI_V2HI_V2HI, dsp_32),
+ DIRECT_BUILTIN (extr_w, MIPS_SI_FTYPE_DI_SI, dsp_32),
+ DIRECT_BUILTIN (extr_r_w, MIPS_SI_FTYPE_DI_SI, dsp_32),
+ DIRECT_BUILTIN (extr_rs_w, MIPS_SI_FTYPE_DI_SI, dsp_32),
+ DIRECT_BUILTIN (extr_s_h, MIPS_SI_FTYPE_DI_SI, dsp_32),
+ DIRECT_BUILTIN (extp, MIPS_SI_FTYPE_DI_SI, dsp_32),
+ DIRECT_BUILTIN (extpdp, MIPS_SI_FTYPE_DI_SI, dsp_32),
+ DIRECT_BUILTIN (shilo, MIPS_DI_FTYPE_DI_SI, dsp_32),
+ DIRECT_BUILTIN (mthlip, MIPS_DI_FTYPE_DI_SI, dsp_32),
+ DIRECT_BUILTIN (madd, MIPS_DI_FTYPE_DI_SI_SI, dsp_32),
+ DIRECT_BUILTIN (maddu, MIPS_DI_FTYPE_DI_USI_USI, dsp_32),
+ DIRECT_BUILTIN (msub, MIPS_DI_FTYPE_DI_SI_SI, dsp_32),
+ DIRECT_BUILTIN (msubu, MIPS_DI_FTYPE_DI_USI_USI, dsp_32),
+ DIRECT_BUILTIN (mult, MIPS_DI_FTYPE_SI_SI, dsp_32),
+ DIRECT_BUILTIN (multu, MIPS_DI_FTYPE_USI_USI, dsp_32),
+
+ /* The following are for the MIPS DSP ASE REV 2 (32-bit only). */
+ DIRECT_BUILTIN (dpa_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dspr2_32),
+ DIRECT_BUILTIN (dps_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dspr2_32),
+ DIRECT_BUILTIN (mulsa_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dspr2_32),
+ DIRECT_BUILTIN (dpax_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dspr2_32),
+ DIRECT_BUILTIN (dpsx_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dspr2_32),
+ DIRECT_BUILTIN (dpaqx_s_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dspr2_32),
+ DIRECT_BUILTIN (dpaqx_sa_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dspr2_32),
+ DIRECT_BUILTIN (dpsqx_s_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dspr2_32),
+ DIRECT_BUILTIN (dpsqx_sa_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dspr2_32),
+
+ /* Builtin functions for ST Microelectronics Loongson-2E/2F cores. */
+ LOONGSON_BUILTIN (packsswh, MIPS_V4HI_FTYPE_V2SI_V2SI),
+ LOONGSON_BUILTIN (packsshb, MIPS_V8QI_FTYPE_V4HI_V4HI),
+ LOONGSON_BUILTIN (packushb, MIPS_UV8QI_FTYPE_UV4HI_UV4HI),
+ LOONGSON_BUILTIN_SUFFIX (paddw, u, MIPS_UV2SI_FTYPE_UV2SI_UV2SI),
+ LOONGSON_BUILTIN_SUFFIX (paddh, u, MIPS_UV4HI_FTYPE_UV4HI_UV4HI),
+ LOONGSON_BUILTIN_SUFFIX (paddb, u, MIPS_UV8QI_FTYPE_UV8QI_UV8QI),
+ LOONGSON_BUILTIN_SUFFIX (paddw, s, MIPS_V2SI_FTYPE_V2SI_V2SI),
+ LOONGSON_BUILTIN_SUFFIX (paddh, s, MIPS_V4HI_FTYPE_V4HI_V4HI),
+ LOONGSON_BUILTIN_SUFFIX (paddb, s, MIPS_V8QI_FTYPE_V8QI_V8QI),
+ LOONGSON_BUILTIN_SUFFIX (paddd, u, MIPS_UDI_FTYPE_UDI_UDI),
+ LOONGSON_BUILTIN_SUFFIX (paddd, s, MIPS_DI_FTYPE_DI_DI),
+ LOONGSON_BUILTIN (paddsh, MIPS_V4HI_FTYPE_V4HI_V4HI),
+ LOONGSON_BUILTIN (paddsb, MIPS_V8QI_FTYPE_V8QI_V8QI),
+ LOONGSON_BUILTIN (paddush, MIPS_UV4HI_FTYPE_UV4HI_UV4HI),
+ LOONGSON_BUILTIN (paddusb, MIPS_UV8QI_FTYPE_UV8QI_UV8QI),
+ LOONGSON_BUILTIN_ALIAS (pandn_d, pandn_ud, MIPS_UDI_FTYPE_UDI_UDI),
+ LOONGSON_BUILTIN_ALIAS (pandn_w, pandn_uw, MIPS_UV2SI_FTYPE_UV2SI_UV2SI),
+ LOONGSON_BUILTIN_ALIAS (pandn_h, pandn_uh, MIPS_UV4HI_FTYPE_UV4HI_UV4HI),
+ LOONGSON_BUILTIN_ALIAS (pandn_b, pandn_ub, MIPS_UV8QI_FTYPE_UV8QI_UV8QI),
+ LOONGSON_BUILTIN_ALIAS (pandn_d, pandn_sd, MIPS_DI_FTYPE_DI_DI),
+ LOONGSON_BUILTIN_ALIAS (pandn_w, pandn_sw, MIPS_V2SI_FTYPE_V2SI_V2SI),
+ LOONGSON_BUILTIN_ALIAS (pandn_h, pandn_sh, MIPS_V4HI_FTYPE_V4HI_V4HI),
+ LOONGSON_BUILTIN_ALIAS (pandn_b, pandn_sb, MIPS_V8QI_FTYPE_V8QI_V8QI),
+ LOONGSON_BUILTIN (pavgh, MIPS_UV4HI_FTYPE_UV4HI_UV4HI),
+ LOONGSON_BUILTIN (pavgb, MIPS_UV8QI_FTYPE_UV8QI_UV8QI),
+ LOONGSON_BUILTIN_SUFFIX (pcmpeqw, u, MIPS_UV2SI_FTYPE_UV2SI_UV2SI),
+ LOONGSON_BUILTIN_SUFFIX (pcmpeqh, u, MIPS_UV4HI_FTYPE_UV4HI_UV4HI),
+ LOONGSON_BUILTIN_SUFFIX (pcmpeqb, u, MIPS_UV8QI_FTYPE_UV8QI_UV8QI),
+ LOONGSON_BUILTIN_SUFFIX (pcmpeqw, s, MIPS_V2SI_FTYPE_V2SI_V2SI),
+ LOONGSON_BUILTIN_SUFFIX (pcmpeqh, s, MIPS_V4HI_FTYPE_V4HI_V4HI),
+ LOONGSON_BUILTIN_SUFFIX (pcmpeqb, s, MIPS_V8QI_FTYPE_V8QI_V8QI),
+ LOONGSON_BUILTIN_SUFFIX (pcmpgtw, u, MIPS_UV2SI_FTYPE_UV2SI_UV2SI),
+ LOONGSON_BUILTIN_SUFFIX (pcmpgth, u, MIPS_UV4HI_FTYPE_UV4HI_UV4HI),
+ LOONGSON_BUILTIN_SUFFIX (pcmpgtb, u, MIPS_UV8QI_FTYPE_UV8QI_UV8QI),
+ LOONGSON_BUILTIN_SUFFIX (pcmpgtw, s, MIPS_V2SI_FTYPE_V2SI_V2SI),
+ LOONGSON_BUILTIN_SUFFIX (pcmpgth, s, MIPS_V4HI_FTYPE_V4HI_V4HI),
+ LOONGSON_BUILTIN_SUFFIX (pcmpgtb, s, MIPS_V8QI_FTYPE_V8QI_V8QI),
+ LOONGSON_BUILTIN_SUFFIX (pextrh, u, MIPS_UV4HI_FTYPE_UV4HI_USI),
+ LOONGSON_BUILTIN_SUFFIX (pextrh, s, MIPS_V4HI_FTYPE_V4HI_USI),
+ LOONGSON_BUILTIN_SUFFIX (pinsrh_0, u, MIPS_UV4HI_FTYPE_UV4HI_UV4HI),
+ LOONGSON_BUILTIN_SUFFIX (pinsrh_1, u, MIPS_UV4HI_FTYPE_UV4HI_UV4HI),
+ LOONGSON_BUILTIN_SUFFIX (pinsrh_2, u, MIPS_UV4HI_FTYPE_UV4HI_UV4HI),
+ LOONGSON_BUILTIN_SUFFIX (pinsrh_3, u, MIPS_UV4HI_FTYPE_UV4HI_UV4HI),
+ LOONGSON_BUILTIN_SUFFIX (pinsrh_0, s, MIPS_V4HI_FTYPE_V4HI_V4HI),
+ LOONGSON_BUILTIN_SUFFIX (pinsrh_1, s, MIPS_V4HI_FTYPE_V4HI_V4HI),
+ LOONGSON_BUILTIN_SUFFIX (pinsrh_2, s, MIPS_V4HI_FTYPE_V4HI_V4HI),
+ LOONGSON_BUILTIN_SUFFIX (pinsrh_3, s, MIPS_V4HI_FTYPE_V4HI_V4HI),
+ LOONGSON_BUILTIN (pmaddhw, MIPS_V2SI_FTYPE_V4HI_V4HI),
+ LOONGSON_BUILTIN (pmaxsh, MIPS_V4HI_FTYPE_V4HI_V4HI),
+ LOONGSON_BUILTIN (pmaxub, MIPS_UV8QI_FTYPE_UV8QI_UV8QI),
+ LOONGSON_BUILTIN (pminsh, MIPS_V4HI_FTYPE_V4HI_V4HI),
+ LOONGSON_BUILTIN (pminub, MIPS_UV8QI_FTYPE_UV8QI_UV8QI),
+ LOONGSON_BUILTIN_SUFFIX (pmovmskb, u, MIPS_UV8QI_FTYPE_UV8QI),
+ LOONGSON_BUILTIN_SUFFIX (pmovmskb, s, MIPS_V8QI_FTYPE_V8QI),
+ LOONGSON_BUILTIN (pmulhuh, MIPS_UV4HI_FTYPE_UV4HI_UV4HI),
+ LOONGSON_BUILTIN (pmulhh, MIPS_V4HI_FTYPE_V4HI_V4HI),
+ LOONGSON_BUILTIN (pmullh, MIPS_V4HI_FTYPE_V4HI_V4HI),
+ LOONGSON_BUILTIN (pmuluw, MIPS_UDI_FTYPE_UV2SI_UV2SI),
+ LOONGSON_BUILTIN (pasubub, MIPS_UV8QI_FTYPE_UV8QI_UV8QI),
+ LOONGSON_BUILTIN (biadd, MIPS_UV4HI_FTYPE_UV8QI),
+ LOONGSON_BUILTIN (psadbh, MIPS_UV4HI_FTYPE_UV8QI_UV8QI),
+ LOONGSON_BUILTIN_SUFFIX (pshufh, u, MIPS_UV4HI_FTYPE_UV4HI_UV4HI_UQI),
+ LOONGSON_BUILTIN_SUFFIX (pshufh, s, MIPS_V4HI_FTYPE_V4HI_V4HI_UQI),
+ LOONGSON_BUILTIN_SUFFIX (psllh, u, MIPS_UV4HI_FTYPE_UV4HI_UQI),
+ LOONGSON_BUILTIN_SUFFIX (psllh, s, MIPS_V4HI_FTYPE_V4HI_UQI),
+ LOONGSON_BUILTIN_SUFFIX (psllw, u, MIPS_UV2SI_FTYPE_UV2SI_UQI),
+ LOONGSON_BUILTIN_SUFFIX (psllw, s, MIPS_V2SI_FTYPE_V2SI_UQI),
+ LOONGSON_BUILTIN_SUFFIX (psrah, u, MIPS_UV4HI_FTYPE_UV4HI_UQI),
+ LOONGSON_BUILTIN_SUFFIX (psrah, s, MIPS_V4HI_FTYPE_V4HI_UQI),
+ LOONGSON_BUILTIN_SUFFIX (psraw, u, MIPS_UV2SI_FTYPE_UV2SI_UQI),
+ LOONGSON_BUILTIN_SUFFIX (psraw, s, MIPS_V2SI_FTYPE_V2SI_UQI),
+ LOONGSON_BUILTIN_SUFFIX (psrlh, u, MIPS_UV4HI_FTYPE_UV4HI_UQI),
+ LOONGSON_BUILTIN_SUFFIX (psrlh, s, MIPS_V4HI_FTYPE_V4HI_UQI),
+ LOONGSON_BUILTIN_SUFFIX (psrlw, u, MIPS_UV2SI_FTYPE_UV2SI_UQI),
+ LOONGSON_BUILTIN_SUFFIX (psrlw, s, MIPS_V2SI_FTYPE_V2SI_UQI),
+ LOONGSON_BUILTIN_SUFFIX (psubw, u, MIPS_UV2SI_FTYPE_UV2SI_UV2SI),
+ LOONGSON_BUILTIN_SUFFIX (psubh, u, MIPS_UV4HI_FTYPE_UV4HI_UV4HI),
+ LOONGSON_BUILTIN_SUFFIX (psubb, u, MIPS_UV8QI_FTYPE_UV8QI_UV8QI),
+ LOONGSON_BUILTIN_SUFFIX (psubw, s, MIPS_V2SI_FTYPE_V2SI_V2SI),
+ LOONGSON_BUILTIN_SUFFIX (psubh, s, MIPS_V4HI_FTYPE_V4HI_V4HI),
+ LOONGSON_BUILTIN_SUFFIX (psubb, s, MIPS_V8QI_FTYPE_V8QI_V8QI),
+ LOONGSON_BUILTIN_SUFFIX (psubd, u, MIPS_UDI_FTYPE_UDI_UDI),
+ LOONGSON_BUILTIN_SUFFIX (psubd, s, MIPS_DI_FTYPE_DI_DI),
+ LOONGSON_BUILTIN (psubsh, MIPS_V4HI_FTYPE_V4HI_V4HI),
+ LOONGSON_BUILTIN (psubsb, MIPS_V8QI_FTYPE_V8QI_V8QI),
+ LOONGSON_BUILTIN (psubush, MIPS_UV4HI_FTYPE_UV4HI_UV4HI),
+ LOONGSON_BUILTIN (psubusb, MIPS_UV8QI_FTYPE_UV8QI_UV8QI),
+ LOONGSON_BUILTIN_SUFFIX (punpckhbh, u, MIPS_UV8QI_FTYPE_UV8QI_UV8QI),
+ LOONGSON_BUILTIN_SUFFIX (punpckhhw, u, MIPS_UV4HI_FTYPE_UV4HI_UV4HI),
+ LOONGSON_BUILTIN_SUFFIX (punpckhwd, u, MIPS_UV2SI_FTYPE_UV2SI_UV2SI),
+ LOONGSON_BUILTIN_SUFFIX (punpckhbh, s, MIPS_V8QI_FTYPE_V8QI_V8QI),
+ LOONGSON_BUILTIN_SUFFIX (punpckhhw, s, MIPS_V4HI_FTYPE_V4HI_V4HI),
+ LOONGSON_BUILTIN_SUFFIX (punpckhwd, s, MIPS_V2SI_FTYPE_V2SI_V2SI),
+ LOONGSON_BUILTIN_SUFFIX (punpcklbh, u, MIPS_UV8QI_FTYPE_UV8QI_UV8QI),
+ LOONGSON_BUILTIN_SUFFIX (punpcklhw, u, MIPS_UV4HI_FTYPE_UV4HI_UV4HI),
+ LOONGSON_BUILTIN_SUFFIX (punpcklwd, u, MIPS_UV2SI_FTYPE_UV2SI_UV2SI),
+ LOONGSON_BUILTIN_SUFFIX (punpcklbh, s, MIPS_V8QI_FTYPE_V8QI_V8QI),
+ LOONGSON_BUILTIN_SUFFIX (punpcklhw, s, MIPS_V4HI_FTYPE_V4HI_V4HI),
+ LOONGSON_BUILTIN_SUFFIX (punpcklwd, s, MIPS_V2SI_FTYPE_V2SI_V2SI),
+
+ /* Sundry other built-in functions. */
+ DIRECT_NO_TARGET_BUILTIN (cache, MIPS_VOID_FTYPE_SI_CVPOINTER, cache)
+};
+
+/* Index I is the function declaration for mips_builtins[I], or null if the
+ function isn't defined on this target. */
+static GTY(()) tree mips_builtin_decls[ARRAY_SIZE (mips_builtins)];
+
+/* MODE is a vector mode whose elements have type TYPE. Return the type
+ of the vector itself. */
+
+static tree
+mips_builtin_vector_type (tree type, enum machine_mode mode)
+{
+ static tree types[2 * (int) MAX_MACHINE_MODE];
+ int mode_index;
+
+ mode_index = (int) mode;
+
+ if (TREE_CODE (type) == INTEGER_TYPE && TYPE_UNSIGNED (type))
+ mode_index += MAX_MACHINE_MODE;
+
+ if (types[mode_index] == NULL_TREE)
+ types[mode_index] = build_vector_type_for_mode (type, mode);
+ return types[mode_index];
+}
+
+/* Return a type for 'const volatile void *'. */
+
+static tree
+mips_build_cvpointer_type (void)
+{
+ static tree cache;
+
+ if (cache == NULL_TREE)
+ cache = build_pointer_type (build_qualified_type
+ (void_type_node,
+ TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE));
+ return cache;
+}
+
+/* Source-level argument types. */
+#define MIPS_ATYPE_VOID void_type_node
+#define MIPS_ATYPE_INT integer_type_node
+#define MIPS_ATYPE_POINTER ptr_type_node
+#define MIPS_ATYPE_CVPOINTER mips_build_cvpointer_type ()
+
+/* Standard mode-based argument types. */
+#define MIPS_ATYPE_UQI unsigned_intQI_type_node
+#define MIPS_ATYPE_SI intSI_type_node
+#define MIPS_ATYPE_USI unsigned_intSI_type_node
+#define MIPS_ATYPE_DI intDI_type_node
+#define MIPS_ATYPE_UDI unsigned_intDI_type_node
+#define MIPS_ATYPE_SF float_type_node
+#define MIPS_ATYPE_DF double_type_node
+
+/* Vector argument types. */
+#define MIPS_ATYPE_V2SF mips_builtin_vector_type (float_type_node, V2SFmode)
+#define MIPS_ATYPE_V2HI mips_builtin_vector_type (intHI_type_node, V2HImode)
+#define MIPS_ATYPE_V2SI mips_builtin_vector_type (intSI_type_node, V2SImode)
+#define MIPS_ATYPE_V4QI mips_builtin_vector_type (intQI_type_node, V4QImode)
+#define MIPS_ATYPE_V4HI mips_builtin_vector_type (intHI_type_node, V4HImode)
+#define MIPS_ATYPE_V8QI mips_builtin_vector_type (intQI_type_node, V8QImode)
+#define MIPS_ATYPE_UV2SI \
+ mips_builtin_vector_type (unsigned_intSI_type_node, V2SImode)
+#define MIPS_ATYPE_UV4HI \
+ mips_builtin_vector_type (unsigned_intHI_type_node, V4HImode)
+#define MIPS_ATYPE_UV8QI \
+ mips_builtin_vector_type (unsigned_intQI_type_node, V8QImode)
+
+/* MIPS_FTYPE_ATYPESN takes N MIPS_FTYPES-like type codes and lists
+ their associated MIPS_ATYPEs. */
+#define MIPS_FTYPE_ATYPES1(A, B) \
+ MIPS_ATYPE_##A, MIPS_ATYPE_##B
+
+#define MIPS_FTYPE_ATYPES2(A, B, C) \
+ MIPS_ATYPE_##A, MIPS_ATYPE_##B, MIPS_ATYPE_##C
+
+#define MIPS_FTYPE_ATYPES3(A, B, C, D) \
+ MIPS_ATYPE_##A, MIPS_ATYPE_##B, MIPS_ATYPE_##C, MIPS_ATYPE_##D
+
+#define MIPS_FTYPE_ATYPES4(A, B, C, D, E) \
+ MIPS_ATYPE_##A, MIPS_ATYPE_##B, MIPS_ATYPE_##C, MIPS_ATYPE_##D, \
+ MIPS_ATYPE_##E
+
+/* Return the function type associated with function prototype TYPE. */
+
+static tree
+mips_build_function_type (enum mips_function_type type)
+{
+ static tree types[(int) MIPS_MAX_FTYPE_MAX];
+
+ if (types[(int) type] == NULL_TREE)
+ switch (type)
+ {
+#define DEF_MIPS_FTYPE(NUM, ARGS) \
+ case MIPS_FTYPE_NAME##NUM ARGS: \
+ types[(int) type] \
+ = build_function_type_list (MIPS_FTYPE_ATYPES##NUM ARGS, \
+ NULL_TREE); \
+ break;
+#include "config/mips/mips-ftypes.def"
+#undef DEF_MIPS_FTYPE
+ default:
+ gcc_unreachable ();
+ }
+
+ return types[(int) type];
+}
+
+/* Implement TARGET_INIT_BUILTINS. */
+
+static void
+mips_init_builtins (void)
+{
+ const struct mips_builtin_description *d;
+ unsigned int i;
+
+ /* Iterate through all of the bdesc arrays, initializing all of the
+ builtin functions. */
+ for (i = 0; i < ARRAY_SIZE (mips_builtins); i++)
+ {
+ d = &mips_builtins[i];
+ if (d->avail ())
+ mips_builtin_decls[i]
+ = add_builtin_function (d->name,
+ mips_build_function_type (d->function_type),
+ i, BUILT_IN_MD, NULL, NULL);
+ }
+}
+
+/* Implement TARGET_BUILTIN_DECL. */
+
+static tree
+mips_builtin_decl (unsigned int code, bool initialize_p ATTRIBUTE_UNUSED)
+{
+ if (code >= ARRAY_SIZE (mips_builtins))
+ return error_mark_node;
+ return mips_builtin_decls[code];
+}
+
+/* Take argument ARGNO from EXP's argument list and convert it into a
+ form suitable for input operand OPNO of instruction ICODE. Return the
+ value. */
+
+static rtx
+mips_prepare_builtin_arg (enum insn_code icode,
+ unsigned int opno, tree exp, unsigned int argno)
+{
+ tree arg;
+ rtx value;
+ enum machine_mode mode;
+
+ arg = CALL_EXPR_ARG (exp, argno);
+ value = expand_normal (arg);
+ mode = insn_data[icode].operand[opno].mode;
+ if (!insn_data[icode].operand[opno].predicate (value, mode))
+ {
+ /* We need to get the mode from ARG for two reasons:
+
+ - to cope with address operands, where MODE is the mode of the
+ memory, rather than of VALUE itself.
+
+ - to cope with special predicates like pmode_register_operand,
+ where MODE is VOIDmode. */
+ value = copy_to_mode_reg (TYPE_MODE (TREE_TYPE (arg)), value);
+
+ /* Check the predicate again. */
+ if (!insn_data[icode].operand[opno].predicate (value, mode))
+ {
+ error ("invalid argument to built-in function");
+ return const0_rtx;
+ }
+ }
+
+ return value;
+}
+
+/* Return an rtx suitable for output operand OP of instruction ICODE.
+ If TARGET is non-null, try to use it where possible. */
+
+static rtx
+mips_prepare_builtin_target (enum insn_code icode, unsigned int op, rtx target)
+{
+ enum machine_mode mode;
+
+ mode = insn_data[icode].operand[op].mode;
+ if (target == 0 || !insn_data[icode].operand[op].predicate (target, mode))
+ target = gen_reg_rtx (mode);
+
+ return target;
+}
+
+/* Expand a MIPS_BUILTIN_DIRECT or MIPS_BUILTIN_DIRECT_NO_TARGET function;
+ HAS_TARGET_P says which. EXP is the CALL_EXPR that calls the function
+ and ICODE is the code of the associated .md pattern. TARGET, if nonnull,
+ suggests a good place to put the result. */
+
+static rtx
+mips_expand_builtin_direct (enum insn_code icode, rtx target, tree exp,
+ bool has_target_p)
+{
+ rtx ops[MAX_RECOG_OPERANDS];
+ int opno, argno;
+
+ /* Map any target to operand 0. */
+ opno = 0;
+ if (has_target_p)
+ {
+ target = mips_prepare_builtin_target (icode, opno, target);
+ ops[opno] = target;
+ opno++;
+ }
+
+ /* Map the arguments to the other operands. The n_operands value
+ for an expander includes match_dups and match_scratches as well as
+ match_operands, so n_operands is only an upper bound on the number
+ of arguments to the expander function. */
+ gcc_assert (opno + call_expr_nargs (exp) <= insn_data[icode].n_operands);
+ for (argno = 0; argno < call_expr_nargs (exp); argno++, opno++)
+ ops[opno] = mips_prepare_builtin_arg (icode, opno, exp, argno);
+
+ switch (opno)
+ {
+ case 2:
+ emit_insn (GEN_FCN (icode) (ops[0], ops[1]));
+ break;
+
+ case 3:
+ emit_insn (GEN_FCN (icode) (ops[0], ops[1], ops[2]));
+ break;
+
+ case 4:
+ emit_insn (GEN_FCN (icode) (ops[0], ops[1], ops[2], ops[3]));
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+ return target;
+}
+
+/* Expand a __builtin_mips_movt_*_ps or __builtin_mips_movf_*_ps
+ function; TYPE says which. EXP is the CALL_EXPR that calls the
+ function, ICODE is the instruction that should be used to compare
+ the first two arguments, and COND is the condition it should test.
+ TARGET, if nonnull, suggests a good place to put the result. */
+
+static rtx
+mips_expand_builtin_movtf (enum mips_builtin_type type,
+ enum insn_code icode, enum mips_fp_condition cond,
+ rtx target, tree exp)
+{
+ rtx cmp_result, op0, op1;
+
+ cmp_result = mips_prepare_builtin_target (icode, 0, 0);
+ op0 = mips_prepare_builtin_arg (icode, 1, exp, 0);
+ op1 = mips_prepare_builtin_arg (icode, 2, exp, 1);
+ emit_insn (GEN_FCN (icode) (cmp_result, op0, op1, GEN_INT (cond)));
+
+ icode = CODE_FOR_mips_cond_move_tf_ps;
+ target = mips_prepare_builtin_target (icode, 0, target);
+ if (type == MIPS_BUILTIN_MOVT)
+ {
+ op1 = mips_prepare_builtin_arg (icode, 2, exp, 2);
+ op0 = mips_prepare_builtin_arg (icode, 1, exp, 3);
+ }
+ else
+ {
+ op0 = mips_prepare_builtin_arg (icode, 1, exp, 2);
+ op1 = mips_prepare_builtin_arg (icode, 2, exp, 3);
+ }
+ emit_insn (gen_mips_cond_move_tf_ps (target, op0, op1, cmp_result));
+ return target;
+}
+
+/* Move VALUE_IF_TRUE into TARGET if CONDITION is true; move VALUE_IF_FALSE
+ into TARGET otherwise. Return TARGET. */
+
+static rtx
+mips_builtin_branch_and_move (rtx condition, rtx target,
+ rtx value_if_true, rtx value_if_false)
+{
+ rtx true_label, done_label;
+
+ true_label = gen_label_rtx ();
+ done_label = gen_label_rtx ();
+
+ /* First assume that CONDITION is false. */
+ mips_emit_move (target, value_if_false);
+
+ /* Branch to TRUE_LABEL if CONDITION is true and DONE_LABEL otherwise. */
+ emit_jump_insn (gen_condjump (condition, true_label));
+ emit_jump_insn (gen_jump (done_label));
+ emit_barrier ();
+
+ /* Fix TARGET if CONDITION is true. */
+ emit_label (true_label);
+ mips_emit_move (target, value_if_true);
+
+ emit_label (done_label);
+ return target;
+}
+
+/* Expand a comparison built-in function of type BUILTIN_TYPE. EXP is
+ the CALL_EXPR that calls the function, ICODE is the code of the
+ comparison instruction, and COND is the condition it should test.
+ TARGET, if nonnull, suggests a good place to put the boolean result. */
+
+static rtx
+mips_expand_builtin_compare (enum mips_builtin_type builtin_type,
+ enum insn_code icode, enum mips_fp_condition cond,
+ rtx target, tree exp)
+{
+ rtx offset, condition, cmp_result, args[MAX_RECOG_OPERANDS];
+ int argno;
+
+ if (target == 0 || GET_MODE (target) != SImode)
+ target = gen_reg_rtx (SImode);
+
+ /* The instruction should have a target operand, an operand for each
+ argument, and an operand for COND. */
+ gcc_assert (call_expr_nargs (exp) + 2 == insn_data[icode].n_operands);
+
+ /* Prepare the operands to the comparison. */
+ cmp_result = mips_prepare_builtin_target (icode, 0, 0);
+ for (argno = 0; argno < call_expr_nargs (exp); argno++)
+ args[argno] = mips_prepare_builtin_arg (icode, argno + 1, exp, argno);
+
+ switch (insn_data[icode].n_operands)
+ {
+ case 4:
+ emit_insn (GEN_FCN (icode) (cmp_result, args[0], args[1],
+ GEN_INT (cond)));
+ break;
+
+ case 6:
+ emit_insn (GEN_FCN (icode) (cmp_result, args[0], args[1],
+ args[2], args[3], GEN_INT (cond)));
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+
+ /* If the comparison sets more than one register, we define the result
+ to be 0 if all registers are false and -1 if all registers are true.
+ The value of the complete result is indeterminate otherwise. */
+ switch (builtin_type)
+ {
+ case MIPS_BUILTIN_CMP_ALL:
+ condition = gen_rtx_NE (VOIDmode, cmp_result, constm1_rtx);
+ return mips_builtin_branch_and_move (condition, target,
+ const0_rtx, const1_rtx);
+
+ case MIPS_BUILTIN_CMP_UPPER:
+ case MIPS_BUILTIN_CMP_LOWER:
+ offset = GEN_INT (builtin_type == MIPS_BUILTIN_CMP_UPPER);
+ condition = gen_single_cc (cmp_result, offset);
+ return mips_builtin_branch_and_move (condition, target,
+ const1_rtx, const0_rtx);
+
+ default:
+ condition = gen_rtx_NE (VOIDmode, cmp_result, const0_rtx);
+ return mips_builtin_branch_and_move (condition, target,
+ const1_rtx, const0_rtx);
+ }
+}
+
+/* Expand a bposge built-in function of type BUILTIN_TYPE. TARGET,
+ if nonnull, suggests a good place to put the boolean result. */
+
+static rtx
+mips_expand_builtin_bposge (enum mips_builtin_type builtin_type, rtx target)
+{
+ rtx condition, cmp_result;
+ int cmp_value;
+
+ if (target == 0 || GET_MODE (target) != SImode)
+ target = gen_reg_rtx (SImode);
+
+ cmp_result = gen_rtx_REG (CCDSPmode, CCDSP_PO_REGNUM);
+
+ if (builtin_type == MIPS_BUILTIN_BPOSGE32)
+ cmp_value = 32;
+ else
+ gcc_assert (0);
+
+ condition = gen_rtx_GE (VOIDmode, cmp_result, GEN_INT (cmp_value));
+ return mips_builtin_branch_and_move (condition, target,
+ const1_rtx, const0_rtx);
+}
+
+/* Implement TARGET_EXPAND_BUILTIN. */
+
+static rtx
+mips_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
+ enum machine_mode mode, int ignore)
+{
+ tree fndecl;
+ unsigned int fcode, avail;
+ const struct mips_builtin_description *d;
+
+ fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
+ fcode = DECL_FUNCTION_CODE (fndecl);
+ gcc_assert (fcode < ARRAY_SIZE (mips_builtins));
+ d = &mips_builtins[fcode];
+ avail = d->avail ();
+ gcc_assert (avail != 0);
+ if (TARGET_MIPS16)
+ {
+ error ("built-in function %qE not supported for MIPS16",
+ DECL_NAME (fndecl));
+ return ignore ? const0_rtx : CONST0_RTX (mode);
+ }
+ switch (d->builtin_type)
+ {
+ case MIPS_BUILTIN_DIRECT:
+ return mips_expand_builtin_direct (d->icode, target, exp, true);
+
+ case MIPS_BUILTIN_DIRECT_NO_TARGET:
+ return mips_expand_builtin_direct (d->icode, target, exp, false);
+
+ case MIPS_BUILTIN_MOVT:
+ case MIPS_BUILTIN_MOVF:
+ return mips_expand_builtin_movtf (d->builtin_type, d->icode,
+ d->cond, target, exp);
+
+ case MIPS_BUILTIN_CMP_ANY:
+ case MIPS_BUILTIN_CMP_ALL:
+ case MIPS_BUILTIN_CMP_UPPER:
+ case MIPS_BUILTIN_CMP_LOWER:
+ case MIPS_BUILTIN_CMP_SINGLE:
+ return mips_expand_builtin_compare (d->builtin_type, d->icode,
+ d->cond, target, exp);
+
+ case MIPS_BUILTIN_BPOSGE32:
+ return mips_expand_builtin_bposge (d->builtin_type, target);
+ }
+ gcc_unreachable ();
+}
+
+/* An entry in the MIPS16 constant pool. VALUE is the pool constant,
+ MODE is its mode, and LABEL is the CODE_LABEL associated with it. */
+struct mips16_constant {
+ struct mips16_constant *next;
+ rtx value;
+ rtx label;
+ enum machine_mode mode;
+};
+
+/* Information about an incomplete MIPS16 constant pool. FIRST is the
+ first constant, HIGHEST_ADDRESS is the highest address that the first
+ byte of the pool can have, and INSN_ADDRESS is the current instruction
+ address. */
+struct mips16_constant_pool {
+ struct mips16_constant *first;
+ int highest_address;
+ int insn_address;
+};
+
+/* Add constant VALUE to POOL and return its label. MODE is the
+ value's mode (used for CONST_INTs, etc.). */
+
+static rtx
+mips16_add_constant (struct mips16_constant_pool *pool,
+ rtx value, enum machine_mode mode)
+{
+ struct mips16_constant **p, *c;
+ bool first_of_size_p;
+
+ /* See whether the constant is already in the pool. If so, return the
+ existing label, otherwise leave P pointing to the place where the
+ constant should be added.
+
+ Keep the pool sorted in increasing order of mode size so that we can
+ reduce the number of alignments needed. */
+ first_of_size_p = true;
+ for (p = &pool->first; *p != 0; p = &(*p)->next)
+ {
+ if (mode == (*p)->mode && rtx_equal_p (value, (*p)->value))
+ return (*p)->label;
+ if (GET_MODE_SIZE (mode) < GET_MODE_SIZE ((*p)->mode))
+ break;
+ if (GET_MODE_SIZE (mode) == GET_MODE_SIZE ((*p)->mode))
+ first_of_size_p = false;
+ }
+
+ /* In the worst case, the constant needed by the earliest instruction
+ will end up at the end of the pool. The entire pool must then be
+ accessible from that instruction.
+
+ When adding the first constant, set the pool's highest address to
+ the address of the first out-of-range byte. Adjust this address
+ downwards each time a new constant is added. */
+ if (pool->first == 0)
+ /* For LWPC, ADDIUPC and DADDIUPC, the base PC value is the address
+ of the instruction with the lowest two bits clear. The base PC
+ value for LDPC has the lowest three bits clear. Assume the worst
+ case here; namely that the PC-relative instruction occupies the
+ last 2 bytes in an aligned word. */
+ pool->highest_address = pool->insn_address - (UNITS_PER_WORD - 2) + 0x8000;
+ pool->highest_address -= GET_MODE_SIZE (mode);
+ if (first_of_size_p)
+ /* Take into account the worst possible padding due to alignment. */
+ pool->highest_address -= GET_MODE_SIZE (mode) - 1;
+
+ /* Create a new entry. */
+ c = XNEW (struct mips16_constant);
+ c->value = value;
+ c->mode = mode;
+ c->label = gen_label_rtx ();
+ c->next = *p;
+ *p = c;
+
+ return c->label;
+}
+
+/* Output constant VALUE after instruction INSN and return the last
+ instruction emitted. MODE is the mode of the constant. */
+
+static rtx
+mips16_emit_constants_1 (enum machine_mode mode, rtx value, rtx insn)
+{
+ if (SCALAR_INT_MODE_P (mode) || ALL_SCALAR_FIXED_POINT_MODE_P (mode))
+ {
+ rtx size = GEN_INT (GET_MODE_SIZE (mode));
+ return emit_insn_after (gen_consttable_int (value, size), insn);
+ }
+
+ if (SCALAR_FLOAT_MODE_P (mode))
+ return emit_insn_after (gen_consttable_float (value), insn);
+
+ if (VECTOR_MODE_P (mode))
+ {
+ int i;
+
+ for (i = 0; i < CONST_VECTOR_NUNITS (value); i++)
+ insn = mips16_emit_constants_1 (GET_MODE_INNER (mode),
+ CONST_VECTOR_ELT (value, i), insn);
+ return insn;
+ }
+
+ gcc_unreachable ();
+}
+
+/* Dump out the constants in CONSTANTS after INSN. */
+
+static void
+mips16_emit_constants (struct mips16_constant *constants, rtx insn)
+{
+ struct mips16_constant *c, *next;
+ int align;
+
+ align = 0;
+ for (c = constants; c != NULL; c = next)
+ {
+ /* If necessary, increase the alignment of PC. */
+ if (align < GET_MODE_SIZE (c->mode))
+ {
+ int align_log = floor_log2 (GET_MODE_SIZE (c->mode));
+ insn = emit_insn_after (gen_align (GEN_INT (align_log)), insn);
+ }
+ align = GET_MODE_SIZE (c->mode);
+
+ insn = emit_label_after (c->label, insn);
+ insn = mips16_emit_constants_1 (c->mode, c->value, insn);
+
+ next = c->next;
+ free (c);
+ }
+
+ emit_barrier_after (insn);
+}
+
+/* Return the length of instruction INSN. */
+
+static int
+mips16_insn_length (rtx insn)
+{
+ if (JUMP_P (insn))
+ {
+ rtx body = PATTERN (insn);
+ if (GET_CODE (body) == ADDR_VEC)
+ return GET_MODE_SIZE (GET_MODE (body)) * XVECLEN (body, 0);
+ if (GET_CODE (body) == ADDR_DIFF_VEC)
+ return GET_MODE_SIZE (GET_MODE (body)) * XVECLEN (body, 1);
+ }
+ return get_attr_length (insn);
+}
+
+/* If *X is a symbolic constant that refers to the constant pool, add
+ the constant to POOL and rewrite *X to use the constant's label. */
+
+static void
+mips16_rewrite_pool_constant (struct mips16_constant_pool *pool, rtx *x)
+{
+ rtx base, offset, label;
+
+ split_const (*x, &base, &offset);
+ if (GET_CODE (base) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (base))
+ {
+ label = mips16_add_constant (pool, get_pool_constant (base),
+ get_pool_mode (base));
+ base = gen_rtx_LABEL_REF (Pmode, label);
+ *x = mips_unspec_address_offset (base, offset, SYMBOL_PC_RELATIVE);
+ }
+}
+
+/* This structure is used to communicate with mips16_rewrite_pool_refs.
+ INSN is the instruction we're rewriting and POOL points to the current
+ constant pool. */
+struct mips16_rewrite_pool_refs_info {
+ rtx insn;
+ struct mips16_constant_pool *pool;
+};
+
+/* Rewrite *X so that constant pool references refer to the constant's
+ label instead. DATA points to a mips16_rewrite_pool_refs_info
+ structure. */
+
+static int
+mips16_rewrite_pool_refs (rtx *x, void *data)
+{
+ struct mips16_rewrite_pool_refs_info *info =
+ (struct mips16_rewrite_pool_refs_info *) data;
+
+ if (force_to_mem_operand (*x, Pmode))
+ {
+ rtx mem = force_const_mem (GET_MODE (*x), *x);
+ validate_change (info->insn, x, mem, false);
+ }
+
+ if (MEM_P (*x))
+ {
+ mips16_rewrite_pool_constant (info->pool, &XEXP (*x, 0));
+ return -1;
+ }
+
+ if (TARGET_MIPS16_TEXT_LOADS)
+ mips16_rewrite_pool_constant (info->pool, x);
+
+ return GET_CODE (*x) == CONST ? -1 : 0;
+}
+
+/* Return whether CFG is used in mips_reorg. */
+
+static bool
+mips_cfg_in_reorg (void)
+{
+ return (mips_r10k_cache_barrier != R10K_CACHE_BARRIER_NONE
+ || TARGET_RELAX_PIC_CALLS);
+}
+
+/* Build MIPS16 constant pools. */
+
+static void
+mips16_lay_out_constants (void)
+{
+ struct mips16_constant_pool pool;
+ struct mips16_rewrite_pool_refs_info info;
+ rtx insn, barrier;
+
+ if (!TARGET_MIPS16_PCREL_LOADS)
+ return;
+
+ if (mips_cfg_in_reorg ())
+ split_all_insns ();
+ else
+ split_all_insns_noflow ();
+ barrier = 0;
+ memset (&pool, 0, sizeof (pool));
+ for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+ {
+ /* Rewrite constant pool references in INSN. */
+ if (USEFUL_INSN_P (insn))
+ {
+ info.insn = insn;
+ info.pool = &pool;
+ for_each_rtx (&PATTERN (insn), mips16_rewrite_pool_refs, &info);
+ }
+
+ pool.insn_address += mips16_insn_length (insn);
+
+ if (pool.first != NULL)
+ {
+ /* If there are no natural barriers between the first user of
+ the pool and the highest acceptable address, we'll need to
+ create a new instruction to jump around the constant pool.
+ In the worst case, this instruction will be 4 bytes long.
+
+ If it's too late to do this transformation after INSN,
+ do it immediately before INSN. */
+ if (barrier == 0 && pool.insn_address + 4 > pool.highest_address)
+ {
+ rtx label, jump;
+
+ label = gen_label_rtx ();
+
+ jump = emit_jump_insn_before (gen_jump (label), insn);
+ JUMP_LABEL (jump) = label;
+ LABEL_NUSES (label) = 1;
+ barrier = emit_barrier_after (jump);
+
+ emit_label_after (label, barrier);
+ pool.insn_address += 4;
+ }
+
+ /* See whether the constant pool is now out of range of the first
+ user. If so, output the constants after the previous barrier.
+ Note that any instructions between BARRIER and INSN (inclusive)
+ will use negative offsets to refer to the pool. */
+ if (pool.insn_address > pool.highest_address)
+ {
+ mips16_emit_constants (pool.first, barrier);
+ pool.first = NULL;
+ barrier = 0;
+ }
+ else if (BARRIER_P (insn))
+ barrier = insn;
+ }
+ }
+ mips16_emit_constants (pool.first, get_last_insn ());
+}
+
+/* Return true if it is worth r10k_simplify_address's while replacing
+ an address with X. We are looking for constants, and for addresses
+ at a known offset from the incoming stack pointer. */
+
+static bool
+r10k_simplified_address_p (rtx x)
+{
+ if (GET_CODE (x) == PLUS && CONST_INT_P (XEXP (x, 1)))
+ x = XEXP (x, 0);
+ return x == virtual_incoming_args_rtx || CONSTANT_P (x);
+}
+
+/* X is an expression that appears in INSN. Try to use the UD chains
+ to simplify it, returning the simplified form on success and the
+ original form otherwise. Replace the incoming value of $sp with
+ virtual_incoming_args_rtx (which should never occur in X otherwise). */
+
+static rtx
+r10k_simplify_address (rtx x, rtx insn)
+{
+ rtx newx, op0, op1, set, def_insn, note;
+ df_ref use, def;
+ struct df_link *defs;
+
+ newx = NULL_RTX;
+ if (UNARY_P (x))
+ {
+ op0 = r10k_simplify_address (XEXP (x, 0), insn);
+ if (op0 != XEXP (x, 0))
+ newx = simplify_gen_unary (GET_CODE (x), GET_MODE (x),
+ op0, GET_MODE (XEXP (x, 0)));
+ }
+ else if (BINARY_P (x))
+ {
+ op0 = r10k_simplify_address (XEXP (x, 0), insn);
+ op1 = r10k_simplify_address (XEXP (x, 1), insn);
+ if (op0 != XEXP (x, 0) || op1 != XEXP (x, 1))
+ newx = simplify_gen_binary (GET_CODE (x), GET_MODE (x), op0, op1);
+ }
+ else if (GET_CODE (x) == LO_SUM)
+ {
+ /* LO_SUMs can be offset from HIGHs, if we know they won't
+ overflow. See mips_classify_address for the rationale behind
+ the lax check. */
+ op0 = r10k_simplify_address (XEXP (x, 0), insn);
+ if (GET_CODE (op0) == HIGH)
+ newx = XEXP (x, 1);
+ }
+ else if (REG_P (x))
+ {
+ /* Uses are recorded by regno_reg_rtx, not X itself. */
+ use = df_find_use (insn, regno_reg_rtx[REGNO (x)]);
+ gcc_assert (use);
+ defs = DF_REF_CHAIN (use);
+
+ /* Require a single definition. */
+ if (defs && defs->next == NULL)
+ {
+ def = defs->ref;
+ if (DF_REF_IS_ARTIFICIAL (def))
+ {
+ /* Replace the incoming value of $sp with
+ virtual_incoming_args_rtx. */
+ if (x == stack_pointer_rtx
+ && DF_REF_BB (def) == ENTRY_BLOCK_PTR)
+ newx = virtual_incoming_args_rtx;
+ }
+ else if (dominated_by_p (CDI_DOMINATORS, DF_REF_BB (use),
+ DF_REF_BB (def)))
+ {
+ /* Make sure that DEF_INSN is a single set of REG. */
+ def_insn = DF_REF_INSN (def);
+ if (NONJUMP_INSN_P (def_insn))
+ {
+ set = single_set (def_insn);
+ if (set && rtx_equal_p (SET_DEST (set), x))
+ {
+ /* Prefer to use notes, since the def-use chains
+ are often shorter. */
+ note = find_reg_equal_equiv_note (def_insn);
+ if (note)
+ newx = XEXP (note, 0);
+ else
+ newx = SET_SRC (set);
+ newx = r10k_simplify_address (newx, def_insn);
+ }
+ }
+ }
+ }
+ }
+ if (newx && r10k_simplified_address_p (newx))
+ return newx;
+ return x;
+}
+
+/* Return true if ADDRESS is known to be an uncached address
+ on R10K systems. */
+
+static bool
+r10k_uncached_address_p (unsigned HOST_WIDE_INT address)
+{
+ unsigned HOST_WIDE_INT upper;
+
+ /* Check for KSEG1. */
+ if (address + 0x60000000 < 0x20000000)
+ return true;
+
+ /* Check for uncached XKPHYS addresses. */
+ if (Pmode == DImode)
+ {
+ upper = (address >> 40) & 0xf9ffff;
+ if (upper == 0x900000 || upper == 0xb80000)
+ return true;
+ }
+ return false;
+}
+
+/* Return true if we can prove that an access to address X in instruction
+ INSN would be safe from R10K speculation. This X is a general
+ expression; it might not be a legitimate address. */
+
+static bool
+r10k_safe_address_p (rtx x, rtx insn)
+{
+ rtx base, offset;
+ HOST_WIDE_INT offset_val;
+
+ x = r10k_simplify_address (x, insn);
+
+ /* Check for references to the stack frame. It doesn't really matter
+ how much of the frame has been allocated at INSN; -mr10k-cache-barrier
+ allows us to assume that accesses to any part of the eventual frame
+ is safe from speculation at any point in the function. */
+ mips_split_plus (x, &base, &offset_val);
+ if (base == virtual_incoming_args_rtx
+ && offset_val >= -cfun->machine->frame.total_size
+ && offset_val < cfun->machine->frame.args_size)
+ return true;
+
+ /* Check for uncached addresses. */
+ if (CONST_INT_P (x))
+ return r10k_uncached_address_p (INTVAL (x));
+
+ /* Check for accesses to a static object. */
+ split_const (x, &base, &offset);
+ return offset_within_block_p (base, INTVAL (offset));
+}
+
+/* Return true if a MEM with MEM_EXPR EXPR and MEM_OFFSET OFFSET is
+ an in-range access to an automatic variable, or to an object with
+ a link-time-constant address. */
+
+static bool
+r10k_safe_mem_expr_p (tree expr, rtx offset)
+{
+ if (expr == NULL_TREE
+ || offset == NULL_RTX
+ || !CONST_INT_P (offset)
+ || INTVAL (offset) < 0
+ || INTVAL (offset) >= int_size_in_bytes (TREE_TYPE (expr)))
+ return false;
+
+ while (TREE_CODE (expr) == COMPONENT_REF)
+ {
+ expr = TREE_OPERAND (expr, 0);
+ if (expr == NULL_TREE)
+ return false;
+ }
+
+ return DECL_P (expr);
+}
+
+/* A for_each_rtx callback for which DATA points to the instruction
+ containing *X. Stop the search if we find a MEM that is not safe
+ from R10K speculation. */
+
+static int
+r10k_needs_protection_p_1 (rtx *loc, void *data)
+{
+ rtx mem;
+
+ mem = *loc;
+ if (!MEM_P (mem))
+ return 0;
+
+ if (r10k_safe_mem_expr_p (MEM_EXPR (mem), MEM_OFFSET (mem)))
+ return -1;
+
+ if (r10k_safe_address_p (XEXP (mem, 0), (rtx) data))
+ return -1;
+
+ return 1;
+}
+
+/* A note_stores callback for which DATA points to an instruction pointer.
+ If *DATA is nonnull, make it null if it X contains a MEM that is not
+ safe from R10K speculation. */
+
+static void
+r10k_needs_protection_p_store (rtx x, const_rtx pat ATTRIBUTE_UNUSED,
+ void *data)
+{
+ rtx *insn_ptr;
+
+ insn_ptr = (rtx *) data;
+ if (*insn_ptr && for_each_rtx (&x, r10k_needs_protection_p_1, *insn_ptr))
+ *insn_ptr = NULL_RTX;
+}
+
+/* A for_each_rtx callback that iterates over the pattern of a CALL_INSN.
+ Return nonzero if the call is not to a declared function. */
+
+static int
+r10k_needs_protection_p_call (rtx *loc, void *data ATTRIBUTE_UNUSED)
+{
+ rtx x;
+
+ x = *loc;
+ if (!MEM_P (x))
+ return 0;
+
+ x = XEXP (x, 0);
+ if (GET_CODE (x) == SYMBOL_REF && SYMBOL_REF_DECL (x))
+ return -1;
+
+ return 1;
+}
+
+/* Return true if instruction INSN needs to be protected by an R10K
+ cache barrier. */
+
+static bool
+r10k_needs_protection_p (rtx insn)
+{
+ if (CALL_P (insn))
+ return for_each_rtx (&PATTERN (insn), r10k_needs_protection_p_call, NULL);
+
+ if (mips_r10k_cache_barrier == R10K_CACHE_BARRIER_STORE)
+ {
+ note_stores (PATTERN (insn), r10k_needs_protection_p_store, &insn);
+ return insn == NULL_RTX;
+ }
+
+ return for_each_rtx (&PATTERN (insn), r10k_needs_protection_p_1, insn);
+}
+
+/* Return true if BB is only reached by blocks in PROTECTED_BBS and if every
+ edge is unconditional. */
+
+static bool
+r10k_protected_bb_p (basic_block bb, sbitmap protected_bbs)
+{
+ edge_iterator ei;
+ edge e;
+
+ FOR_EACH_EDGE (e, ei, bb->preds)
+ if (!single_succ_p (e->src)
+ || !TEST_BIT (protected_bbs, e->src->index)
+ || (e->flags & EDGE_COMPLEX) != 0)
+ return false;
+ return true;
+}
+
+/* Implement -mr10k-cache-barrier= for the current function. */
+
+static void
+r10k_insert_cache_barriers (void)
+{
+ int *rev_post_order;
+ unsigned int i, n;
+ basic_block bb;
+ sbitmap protected_bbs;
+ rtx insn, end, unprotected_region;
+
+ if (TARGET_MIPS16)
+ {
+ sorry ("%qs does not support MIPS16 code", "-mr10k-cache-barrier");
+ return;
+ }
+
+ /* Calculate dominators. */
+ calculate_dominance_info (CDI_DOMINATORS);
+
+ /* Bit X of PROTECTED_BBS is set if the last operation in basic block
+ X is protected by a cache barrier. */
+ protected_bbs = sbitmap_alloc (last_basic_block);
+ sbitmap_zero (protected_bbs);
+
+ /* Iterate over the basic blocks in reverse post-order. */
+ rev_post_order = XNEWVEC (int, last_basic_block);
+ n = pre_and_rev_post_order_compute (NULL, rev_post_order, false);
+ for (i = 0; i < n; i++)
+ {
+ bb = BASIC_BLOCK (rev_post_order[i]);
+
+ /* If this block is only reached by unconditional edges, and if the
+ source of every edge is protected, the beginning of the block is
+ also protected. */
+ if (r10k_protected_bb_p (bb, protected_bbs))
+ unprotected_region = NULL_RTX;
+ else
+ unprotected_region = pc_rtx;
+ end = NEXT_INSN (BB_END (bb));
+
+ /* UNPROTECTED_REGION is:
+
+ - null if we are processing a protected region,
+ - pc_rtx if we are processing an unprotected region but have
+ not yet found the first instruction in it
+ - the first instruction in an unprotected region otherwise. */
+ for (insn = BB_HEAD (bb); insn != end; insn = NEXT_INSN (insn))
+ {
+ if (unprotected_region && USEFUL_INSN_P (insn))
+ {
+ if (recog_memoized (insn) == CODE_FOR_mips_cache)
+ /* This CACHE instruction protects the following code. */
+ unprotected_region = NULL_RTX;
+ else
+ {
+ /* See if INSN is the first instruction in this
+ unprotected region. */
+ if (unprotected_region == pc_rtx)
+ unprotected_region = insn;
+
+ /* See if INSN needs to be protected. If so,
+ we must insert a cache barrier somewhere between
+ PREV_INSN (UNPROTECTED_REGION) and INSN. It isn't
+ clear which position is better performance-wise,
+ but as a tie-breaker, we assume that it is better
+ to allow delay slots to be back-filled where
+ possible, and that it is better not to insert
+ barriers in the middle of already-scheduled code.
+ We therefore insert the barrier at the beginning
+ of the region. */
+ if (r10k_needs_protection_p (insn))
+ {
+ emit_insn_before (gen_r10k_cache_barrier (),
+ unprotected_region);
+ unprotected_region = NULL_RTX;
+ }
+ }
+ }
+
+ if (CALL_P (insn))
+ /* The called function is not required to protect the exit path.
+ The code that follows a call is therefore unprotected. */
+ unprotected_region = pc_rtx;
+ }
+
+ /* Record whether the end of this block is protected. */
+ if (unprotected_region == NULL_RTX)
+ SET_BIT (protected_bbs, bb->index);
+ }
+ XDELETEVEC (rev_post_order);
+
+ sbitmap_free (protected_bbs);
+
+ free_dominance_info (CDI_DOMINATORS);
+}
+
+/* If INSN is a call, return the underlying CALL expr. Return NULL_RTX
+ otherwise. If INSN has two call rtx, then store the second one in
+ SECOND_CALL. */
+
+static rtx
+mips_call_expr_from_insn (rtx insn, rtx *second_call)
+{
+ rtx x;
+ rtx x2;
+
+ if (!CALL_P (insn))
+ return NULL_RTX;
+
+ x = PATTERN (insn);
+ if (GET_CODE (x) == PARALLEL)
+ {
+ /* Calls returning complex values have two CALL rtx. Look for the second
+ one here, and return it via the SECOND_CALL arg. */
+ x2 = XVECEXP (x, 0, 1);
+ if (GET_CODE (x2) == SET)
+ x2 = XEXP (x2, 1);
+ if (GET_CODE (x2) == CALL)
+ *second_call = x2;
+
+ x = XVECEXP (x, 0, 0);
+ }
+ if (GET_CODE (x) == SET)
+ x = XEXP (x, 1);
+ gcc_assert (GET_CODE (x) == CALL);
+
+ return x;
+}
+
+/* REG is set in DEF. See if the definition is one of the ways we load a
+ register with a symbol address for a mips_use_pic_fn_addr_reg_p call.
+ If it is, return the symbol reference of the function, otherwise return
+ NULL_RTX.
+
+ If RECURSE_P is true, use mips_find_pic_call_symbol to interpret
+ the values of source registers, otherwise treat such registers as
+ having an unknown value. */
+
+static rtx
+mips_pic_call_symbol_from_set (df_ref def, rtx reg, bool recurse_p)
+{
+ rtx def_insn, set;
+
+ if (DF_REF_IS_ARTIFICIAL (def))
+ return NULL_RTX;
+
+ def_insn = DF_REF_INSN (def);
+ set = single_set (def_insn);
+ if (set && rtx_equal_p (SET_DEST (set), reg))
+ {
+ rtx note, src, symbol;
+
+ /* First, look at REG_EQUAL/EQUIV notes. */
+ note = find_reg_equal_equiv_note (def_insn);
+ if (note && GET_CODE (XEXP (note, 0)) == SYMBOL_REF)
+ return XEXP (note, 0);
+
+ /* For %call16 references we don't have REG_EQUAL. */
+ src = SET_SRC (set);
+ symbol = mips_strip_unspec_call (src);
+ if (symbol)
+ {
+ gcc_assert (GET_CODE (symbol) == SYMBOL_REF);
+ return symbol;
+ }
+
+ /* Follow at most one simple register copy. Such copies are
+ interesting in cases like:
+
+ for (...)
+ {
+ locally_binding_fn (...);
+ }
+
+ and:
+
+ locally_binding_fn (...);
+ ...
+ locally_binding_fn (...);
+
+ where the load of locally_binding_fn can legitimately be
+ hoisted or shared. However, we do not expect to see complex
+ chains of copies, so a full worklist solution to the problem
+ would probably be overkill. */
+ if (recurse_p && REG_P (src))
+ return mips_find_pic_call_symbol (def_insn, src, false);
+ }
+
+ return NULL_RTX;
+}
+
+/* Find the definition of the use of REG in INSN. See if the definition
+ is one of the ways we load a register with a symbol address for a
+ mips_use_pic_fn_addr_reg_p call. If it is return the symbol reference
+ of the function, otherwise return NULL_RTX. RECURSE_P is as for
+ mips_pic_call_symbol_from_set. */
+
+static rtx
+mips_find_pic_call_symbol (rtx insn, rtx reg, bool recurse_p)
+{
+ df_ref use;
+ struct df_link *defs;
+ rtx symbol;
+
+ use = df_find_use (insn, regno_reg_rtx[REGNO (reg)]);
+ if (!use)
+ return NULL_RTX;
+ defs = DF_REF_CHAIN (use);
+ if (!defs)
+ return NULL_RTX;
+ symbol = mips_pic_call_symbol_from_set (defs->ref, reg, recurse_p);
+ if (!symbol)
+ return NULL_RTX;
+
+ /* If we have more than one definition, they need to be identical. */
+ for (defs = defs->next; defs; defs = defs->next)
+ {
+ rtx other;
+
+ other = mips_pic_call_symbol_from_set (defs->ref, reg, recurse_p);
+ if (!rtx_equal_p (symbol, other))
+ return NULL_RTX;
+ }
+
+ return symbol;
+}
+
+/* Replace the args_size operand of the call expression CALL with the
+ call-attribute UNSPEC and fill in SYMBOL as the function symbol. */
+
+static void
+mips_annotate_pic_call_expr (rtx call, rtx symbol)
+{
+ rtx args_size;
+
+ args_size = XEXP (call, 1);
+ XEXP (call, 1) = gen_rtx_UNSPEC (GET_MODE (args_size),
+ gen_rtvec (2, args_size, symbol),
+ UNSPEC_CALL_ATTR);
+}
+
+/* OPERANDS[ARGS_SIZE_OPNO] is the arg_size operand of a CALL expression. See
+ if instead of the arg_size argument it contains the call attributes. If
+ yes return true along with setting OPERANDS[ARGS_SIZE_OPNO] to the function
+ symbol from the call attributes. Also return false if ARGS_SIZE_OPNO is
+ -1. */
+
+bool
+mips_get_pic_call_symbol (rtx *operands, int args_size_opno)
+{
+ rtx args_size, symbol;
+
+ if (!TARGET_RELAX_PIC_CALLS || args_size_opno == -1)
+ return false;
+
+ args_size = operands[args_size_opno];
+ if (GET_CODE (args_size) != UNSPEC)
+ return false;
+ gcc_assert (XINT (args_size, 1) == UNSPEC_CALL_ATTR);
+
+ symbol = XVECEXP (args_size, 0, 1);
+ gcc_assert (GET_CODE (symbol) == SYMBOL_REF);
+
+ operands[args_size_opno] = symbol;
+ return true;
+}
+
+/* Use DF to annotate PIC indirect calls with the function symbol they
+ dispatch to. */
+
+static void
+mips_annotate_pic_calls (void)
+{
+ basic_block bb;
+ rtx insn;
+
+ FOR_EACH_BB (bb)
+ FOR_BB_INSNS (bb, insn)
+ {
+ rtx call, reg, symbol, second_call;
+
+ second_call = 0;
+ call = mips_call_expr_from_insn (insn, &second_call);
+ if (!call)
+ continue;
+ gcc_assert (MEM_P (XEXP (call, 0)));
+ reg = XEXP (XEXP (call, 0), 0);
+ if (!REG_P (reg))
+ continue;
+
+ symbol = mips_find_pic_call_symbol (insn, reg, true);
+ if (symbol)
+ {
+ mips_annotate_pic_call_expr (call, symbol);
+ if (second_call)
+ mips_annotate_pic_call_expr (second_call, symbol);
+ }
+ }
+}
+
+/* A temporary variable used by for_each_rtx callbacks, etc. */
+static rtx mips_sim_insn;
+
+/* A structure representing the state of the processor pipeline.
+ Used by the mips_sim_* family of functions. */
+struct mips_sim {
+ /* The maximum number of instructions that can be issued in a cycle.
+ (Caches mips_issue_rate.) */
+ unsigned int issue_rate;
+
+ /* The current simulation time. */
+ unsigned int time;
+
+ /* How many more instructions can be issued in the current cycle. */
+ unsigned int insns_left;
+
+ /* LAST_SET[X].INSN is the last instruction to set register X.
+ LAST_SET[X].TIME is the time at which that instruction was issued.
+ INSN is null if no instruction has yet set register X. */
+ struct {
+ rtx insn;
+ unsigned int time;
+ } last_set[FIRST_PSEUDO_REGISTER];
+
+ /* The pipeline's current DFA state. */
+ state_t dfa_state;
+};
+
+/* Reset STATE to the initial simulation state. */
+
+static void
+mips_sim_reset (struct mips_sim *state)
+{
+ state->time = 0;
+ state->insns_left = state->issue_rate;
+ memset (&state->last_set, 0, sizeof (state->last_set));
+ state_reset (state->dfa_state);
+}
+
+/* Initialize STATE before its first use. DFA_STATE points to an
+ allocated but uninitialized DFA state. */
+
+static void
+mips_sim_init (struct mips_sim *state, state_t dfa_state)
+{
+ state->issue_rate = mips_issue_rate ();
+ state->dfa_state = dfa_state;
+ mips_sim_reset (state);
+}
+
+/* Advance STATE by one clock cycle. */
+
+static void
+mips_sim_next_cycle (struct mips_sim *state)
+{
+ state->time++;
+ state->insns_left = state->issue_rate;
+ state_transition (state->dfa_state, 0);
+}
+
+/* Advance simulation state STATE until instruction INSN can read
+ register REG. */
+
+static void
+mips_sim_wait_reg (struct mips_sim *state, rtx insn, rtx reg)
+{
+ unsigned int regno, end_regno;
+
+ end_regno = END_REGNO (reg);
+ for (regno = REGNO (reg); regno < end_regno; regno++)
+ if (state->last_set[regno].insn != 0)
+ {
+ unsigned int t;
+
+ t = (state->last_set[regno].time
+ + insn_latency (state->last_set[regno].insn, insn));
+ while (state->time < t)
+ mips_sim_next_cycle (state);
+ }
+}
+
+/* A for_each_rtx callback. If *X is a register, advance simulation state
+ DATA until mips_sim_insn can read the register's value. */
+
+static int
+mips_sim_wait_regs_2 (rtx *x, void *data)
+{
+ if (REG_P (*x))
+ mips_sim_wait_reg ((struct mips_sim *) data, mips_sim_insn, *x);
+ return 0;
+}
+
+/* Call mips_sim_wait_regs_2 (R, DATA) for each register R mentioned in *X. */
+
+static void
+mips_sim_wait_regs_1 (rtx *x, void *data)
+{
+ for_each_rtx (x, mips_sim_wait_regs_2, data);
+}
+
+/* Advance simulation state STATE until all of INSN's register
+ dependencies are satisfied. */
+
+static void
+mips_sim_wait_regs (struct mips_sim *state, rtx insn)
+{
+ mips_sim_insn = insn;
+ note_uses (&PATTERN (insn), mips_sim_wait_regs_1, state);
+}
+
+/* Advance simulation state STATE until the units required by
+ instruction INSN are available. */
+
+static void
+mips_sim_wait_units (struct mips_sim *state, rtx insn)
+{
+ state_t tmp_state;
+
+ tmp_state = alloca (state_size ());
+ while (state->insns_left == 0
+ || (memcpy (tmp_state, state->dfa_state, state_size ()),
+ state_transition (tmp_state, insn) >= 0))
+ mips_sim_next_cycle (state);
+}
+
+/* Advance simulation state STATE until INSN is ready to issue. */
+
+static void
+mips_sim_wait_insn (struct mips_sim *state, rtx insn)
+{
+ mips_sim_wait_regs (state, insn);
+ mips_sim_wait_units (state, insn);
+}
+
+/* mips_sim_insn has just set X. Update the LAST_SET array
+ in simulation state DATA. */
+
+static void
+mips_sim_record_set (rtx x, const_rtx pat ATTRIBUTE_UNUSED, void *data)
+{
+ struct mips_sim *state;
+
+ state = (struct mips_sim *) data;
+ if (REG_P (x))
+ {
+ unsigned int regno, end_regno;
+
+ end_regno = END_REGNO (x);
+ for (regno = REGNO (x); regno < end_regno; regno++)
+ {
+ state->last_set[regno].insn = mips_sim_insn;
+ state->last_set[regno].time = state->time;
+ }
+ }
+}
+
+/* Issue instruction INSN in scheduler state STATE. Assume that INSN
+ can issue immediately (i.e., that mips_sim_wait_insn has already
+ been called). */
+
+static void
+mips_sim_issue_insn (struct mips_sim *state, rtx insn)
+{
+ state_transition (state->dfa_state, insn);
+ state->insns_left--;
+
+ mips_sim_insn = insn;
+ note_stores (PATTERN (insn), mips_sim_record_set, state);
+}
+
+/* Simulate issuing a NOP in state STATE. */
+
+static void
+mips_sim_issue_nop (struct mips_sim *state)
+{
+ if (state->insns_left == 0)
+ mips_sim_next_cycle (state);
+ state->insns_left--;
+}
+
+/* Update simulation state STATE so that it's ready to accept the instruction
+ after INSN. INSN should be part of the main rtl chain, not a member of a
+ SEQUENCE. */
+
+static void
+mips_sim_finish_insn (struct mips_sim *state, rtx insn)
+{
+ /* If INSN is a jump with an implicit delay slot, simulate a nop. */
+ if (JUMP_P (insn))
+ mips_sim_issue_nop (state);
+
+ switch (GET_CODE (SEQ_BEGIN (insn)))
+ {
+ case CODE_LABEL:
+ case CALL_INSN:
+ /* We can't predict the processor state after a call or label. */
+ mips_sim_reset (state);
+ break;
+
+ case JUMP_INSN:
+ /* The delay slots of branch likely instructions are only executed
+ when the branch is taken. Therefore, if the caller has simulated
+ the delay slot instruction, STATE does not really reflect the state
+ of the pipeline for the instruction after the delay slot. Also,
+ branch likely instructions tend to incur a penalty when not taken,
+ so there will probably be an extra delay between the branch and
+ the instruction after the delay slot. */
+ if (INSN_ANNULLED_BRANCH_P (SEQ_BEGIN (insn)))
+ mips_sim_reset (state);
+ break;
+
+ default:
+ break;
+ }
+}
+
+/* The VR4130 pipeline issues aligned pairs of instructions together,
+ but it stalls the second instruction if it depends on the first.
+ In order to cut down the amount of logic required, this dependence
+ check is not based on a full instruction decode. Instead, any non-SPECIAL
+ instruction is assumed to modify the register specified by bits 20-16
+ (which is usually the "rt" field).
+
+ In BEQ, BEQL, BNE and BNEL instructions, the rt field is actually an
+ input, so we can end up with a false dependence between the branch
+ and its delay slot. If this situation occurs in instruction INSN,
+ try to avoid it by swapping rs and rt. */
+
+static void
+vr4130_avoid_branch_rt_conflict (rtx insn)
+{
+ rtx first, second;
+
+ first = SEQ_BEGIN (insn);
+ second = SEQ_END (insn);
+ if (JUMP_P (first)
+ && NONJUMP_INSN_P (second)
+ && GET_CODE (PATTERN (first)) == SET
+ && GET_CODE (SET_DEST (PATTERN (first))) == PC
+ && GET_CODE (SET_SRC (PATTERN (first))) == IF_THEN_ELSE)
+ {
+ /* Check for the right kind of condition. */
+ rtx cond = XEXP (SET_SRC (PATTERN (first)), 0);
+ if ((GET_CODE (cond) == EQ || GET_CODE (cond) == NE)
+ && REG_P (XEXP (cond, 0))
+ && REG_P (XEXP (cond, 1))
+ && reg_referenced_p (XEXP (cond, 1), PATTERN (second))
+ && !reg_referenced_p (XEXP (cond, 0), PATTERN (second)))
+ {
+ /* SECOND mentions the rt register but not the rs register. */
+ rtx tmp = XEXP (cond, 0);
+ XEXP (cond, 0) = XEXP (cond, 1);
+ XEXP (cond, 1) = tmp;
+ }
+ }
+}
+
+/* Implement -mvr4130-align. Go through each basic block and simulate the
+ processor pipeline. If we find that a pair of instructions could execute
+ in parallel, and the first of those instructions is not 8-byte aligned,
+ insert a nop to make it aligned. */
+
+static void
+vr4130_align_insns (void)
+{
+ struct mips_sim state;
+ rtx insn, subinsn, last, last2, next;
+ bool aligned_p;
+
+ dfa_start ();
+
+ /* LAST is the last instruction before INSN to have a nonzero length.
+ LAST2 is the last such instruction before LAST. */
+ last = 0;
+ last2 = 0;
+
+ /* ALIGNED_P is true if INSN is known to be at an aligned address. */
+ aligned_p = true;
+
+ mips_sim_init (&state, alloca (state_size ()));
+ for (insn = get_insns (); insn != 0; insn = next)
+ {
+ unsigned int length;
+
+ next = NEXT_INSN (insn);
+
+ /* See the comment above vr4130_avoid_branch_rt_conflict for details.
+ This isn't really related to the alignment pass, but we do it on
+ the fly to avoid a separate instruction walk. */
+ vr4130_avoid_branch_rt_conflict (insn);
+
+ if (USEFUL_INSN_P (insn))
+ FOR_EACH_SUBINSN (subinsn, insn)
+ {
+ mips_sim_wait_insn (&state, subinsn);
+
+ /* If we want this instruction to issue in parallel with the
+ previous one, make sure that the previous instruction is
+ aligned. There are several reasons why this isn't worthwhile
+ when the second instruction is a call:
+
+ - Calls are less likely to be performance critical,
+ - There's a good chance that the delay slot can execute
+ in parallel with the call.
+ - The return address would then be unaligned.
+
+ In general, if we're going to insert a nop between instructions
+ X and Y, it's better to insert it immediately after X. That
+ way, if the nop makes Y aligned, it will also align any labels
+ between X and Y. */
+ if (state.insns_left != state.issue_rate
+ && !CALL_P (subinsn))
+ {
+ if (subinsn == SEQ_BEGIN (insn) && aligned_p)
+ {
+ /* SUBINSN is the first instruction in INSN and INSN is
+ aligned. We want to align the previous instruction
+ instead, so insert a nop between LAST2 and LAST.
+
+ Note that LAST could be either a single instruction
+ or a branch with a delay slot. In the latter case,
+ LAST, like INSN, is already aligned, but the delay
+ slot must have some extra delay that stops it from
+ issuing at the same time as the branch. We therefore
+ insert a nop before the branch in order to align its
+ delay slot. */
+ emit_insn_after (gen_nop (), last2);
+ aligned_p = false;
+ }
+ else if (subinsn != SEQ_BEGIN (insn) && !aligned_p)
+ {
+ /* SUBINSN is the delay slot of INSN, but INSN is
+ currently unaligned. Insert a nop between
+ LAST and INSN to align it. */
+ emit_insn_after (gen_nop (), last);
+ aligned_p = true;
+ }
+ }
+ mips_sim_issue_insn (&state, subinsn);
+ }
+ mips_sim_finish_insn (&state, insn);
+
+ /* Update LAST, LAST2 and ALIGNED_P for the next instruction. */
+ length = get_attr_length (insn);
+ if (length > 0)
+ {
+ /* If the instruction is an asm statement or multi-instruction
+ mips.md patern, the length is only an estimate. Insert an
+ 8 byte alignment after it so that the following instructions
+ can be handled correctly. */
+ if (NONJUMP_INSN_P (SEQ_BEGIN (insn))
+ && (recog_memoized (insn) < 0 || length >= 8))
+ {
+ next = emit_insn_after (gen_align (GEN_INT (3)), insn);
+ next = NEXT_INSN (next);
+ mips_sim_next_cycle (&state);
+ aligned_p = true;
+ }
+ else if (length & 4)
+ aligned_p = !aligned_p;
+ last2 = last;
+ last = insn;
+ }
+
+ /* See whether INSN is an aligned label. */
+ if (LABEL_P (insn) && label_to_alignment (insn) >= 3)
+ aligned_p = true;
+ }
+ dfa_finish ();
+}
+
+/* This structure records that the current function has a LO_SUM
+ involving SYMBOL_REF or LABEL_REF BASE and that MAX_OFFSET is
+ the largest offset applied to BASE by all such LO_SUMs. */
+struct mips_lo_sum_offset {
+ rtx base;
+ HOST_WIDE_INT offset;
+};
+
+/* Return a hash value for SYMBOL_REF or LABEL_REF BASE. */
+
+static hashval_t
+mips_hash_base (rtx base)
+{
+ int do_not_record_p;
+
+ return hash_rtx (base, GET_MODE (base), &do_not_record_p, NULL, false);
+}
+
+/* Hash-table callbacks for mips_lo_sum_offsets. */
+
+static hashval_t
+mips_lo_sum_offset_hash (const void *entry)
+{
+ return mips_hash_base (((const struct mips_lo_sum_offset *) entry)->base);
+}
+
+static int
+mips_lo_sum_offset_eq (const void *entry, const void *value)
+{
+ return rtx_equal_p (((const struct mips_lo_sum_offset *) entry)->base,
+ (const_rtx) value);
+}
+
+/* Look up symbolic constant X in HTAB, which is a hash table of
+ mips_lo_sum_offsets. If OPTION is NO_INSERT, return true if X can be
+ paired with a recorded LO_SUM, otherwise record X in the table. */
+
+static bool
+mips_lo_sum_offset_lookup (htab_t htab, rtx x, enum insert_option option)
+{
+ rtx base, offset;
+ void **slot;
+ struct mips_lo_sum_offset *entry;
+
+ /* Split X into a base and offset. */
+ split_const (x, &base, &offset);
+ if (UNSPEC_ADDRESS_P (base))
+ base = UNSPEC_ADDRESS (base);
+
+ /* Look up the base in the hash table. */
+ slot = htab_find_slot_with_hash (htab, base, mips_hash_base (base), option);
+ if (slot == NULL)
+ return false;
+
+ entry = (struct mips_lo_sum_offset *) *slot;
+ if (option == INSERT)
+ {
+ if (entry == NULL)
+ {
+ entry = XNEW (struct mips_lo_sum_offset);
+ entry->base = base;
+ entry->offset = INTVAL (offset);
+ *slot = entry;
+ }
+ else
+ {
+ if (INTVAL (offset) > entry->offset)
+ entry->offset = INTVAL (offset);
+ }
+ }
+ return INTVAL (offset) <= entry->offset;
+}
+
+/* A for_each_rtx callback for which DATA is a mips_lo_sum_offset hash table.
+ Record every LO_SUM in *LOC. */
+
+static int
+mips_record_lo_sum (rtx *loc, void *data)
+{
+ if (GET_CODE (*loc) == LO_SUM)
+ mips_lo_sum_offset_lookup ((htab_t) data, XEXP (*loc, 1), INSERT);
+ return 0;
+}
+
+/* Return true if INSN is a SET of an orphaned high-part relocation.
+ HTAB is a hash table of mips_lo_sum_offsets that describes all the
+ LO_SUMs in the current function. */
+
+static bool
+mips_orphaned_high_part_p (htab_t htab, rtx insn)
+{
+ enum mips_symbol_type type;
+ rtx x, set;
+
+ set = single_set (insn);
+ if (set)
+ {
+ /* Check for %his. */
+ x = SET_SRC (set);
+ if (GET_CODE (x) == HIGH
+ && absolute_symbolic_operand (XEXP (x, 0), VOIDmode))
+ return !mips_lo_sum_offset_lookup (htab, XEXP (x, 0), NO_INSERT);
+
+ /* Check for local %gots (and %got_pages, which is redundant but OK). */
+ if (GET_CODE (x) == UNSPEC
+ && XINT (x, 1) == UNSPEC_LOAD_GOT
+ && mips_symbolic_constant_p (XVECEXP (x, 0, 1),
+ SYMBOL_CONTEXT_LEA, &type)
+ && type == SYMBOL_GOTOFF_PAGE)
+ return !mips_lo_sum_offset_lookup (htab, XVECEXP (x, 0, 1), NO_INSERT);
+ }
+ return false;
+}
+
+/* Subroutine of mips_reorg_process_insns. If there is a hazard between
+ INSN and a previous instruction, avoid it by inserting nops after
+ instruction AFTER.
+
+ *DELAYED_REG and *HILO_DELAY describe the hazards that apply at
+ this point. If *DELAYED_REG is non-null, INSN must wait a cycle
+ before using the value of that register. *HILO_DELAY counts the
+ number of instructions since the last hilo hazard (that is,
+ the number of instructions since the last MFLO or MFHI).
+
+ After inserting nops for INSN, update *DELAYED_REG and *HILO_DELAY
+ for the next instruction.
+
+ LO_REG is an rtx for the LO register, used in dependence checking. */
+
+static void
+mips_avoid_hazard (rtx after, rtx insn, int *hilo_delay,
+ rtx *delayed_reg, rtx lo_reg)
+{
+ rtx pattern, set;
+ int nops, ninsns;
+
+ pattern = PATTERN (insn);
+
+ /* Do not put the whole function in .set noreorder if it contains
+ an asm statement. We don't know whether there will be hazards
+ between the asm statement and the gcc-generated code. */
+ if (GET_CODE (pattern) == ASM_INPUT || asm_noperands (pattern) >= 0)
+ cfun->machine->all_noreorder_p = false;
+
+ /* Ignore zero-length instructions (barriers and the like). */
+ ninsns = get_attr_length (insn) / 4;
+ if (ninsns == 0)
+ return;
+
+ /* Work out how many nops are needed. Note that we only care about
+ registers that are explicitly mentioned in the instruction's pattern.
+ It doesn't matter that calls use the argument registers or that they
+ clobber hi and lo. */
+ if (*hilo_delay < 2 && reg_set_p (lo_reg, pattern))
+ nops = 2 - *hilo_delay;
+ else if (*delayed_reg != 0 && reg_referenced_p (*delayed_reg, pattern))
+ nops = 1;
+ else
+ nops = 0;
+
+ /* Insert the nops between this instruction and the previous one.
+ Each new nop takes us further from the last hilo hazard. */
+ *hilo_delay += nops;
+ while (nops-- > 0)
+ emit_insn_after (gen_hazard_nop (), after);
+
+ /* Set up the state for the next instruction. */
+ *hilo_delay += ninsns;
+ *delayed_reg = 0;
+ if (INSN_CODE (insn) >= 0)
+ switch (get_attr_hazard (insn))
+ {
+ case HAZARD_NONE:
+ break;
+
+ case HAZARD_HILO:
+ *hilo_delay = 0;
+ break;
+
+ case HAZARD_DELAY:
+ set = single_set (insn);
+ gcc_assert (set);
+ *delayed_reg = SET_DEST (set);
+ break;
+ }
+}
+
+/* Go through the instruction stream and insert nops where necessary.
+ Also delete any high-part relocations whose partnering low parts
+ are now all dead. See if the whole function can then be put into
+ .set noreorder and .set nomacro. */
+
+static void
+mips_reorg_process_insns (void)
+{
+ rtx insn, last_insn, subinsn, next_insn, lo_reg, delayed_reg;
+ int hilo_delay;
+ htab_t htab;
+
+ /* Force all instructions to be split into their final form. */
+ split_all_insns_noflow ();
+
+ /* Recalculate instruction lengths without taking nops into account. */
+ cfun->machine->ignore_hazard_length_p = true;
+ shorten_branches (get_insns ());
+
+ cfun->machine->all_noreorder_p = true;
+
+ /* We don't track MIPS16 PC-relative offsets closely enough to make
+ a good job of "set .noreorder" code in MIPS16 mode. */
+ if (TARGET_MIPS16)
+ cfun->machine->all_noreorder_p = false;
+
+ /* Code that doesn't use explicit relocs can't be ".set nomacro". */
+ if (!TARGET_EXPLICIT_RELOCS)
+ cfun->machine->all_noreorder_p = false;
+
+ /* Profiled functions can't be all noreorder because the profiler
+ support uses assembler macros. */
+ if (crtl->profile)
+ cfun->machine->all_noreorder_p = false;
+
+ /* Code compiled with -mfix-vr4120 can't be all noreorder because
+ we rely on the assembler to work around some errata. */
+ if (TARGET_FIX_VR4120)
+ cfun->machine->all_noreorder_p = false;
+
+ /* The same is true for -mfix-vr4130 if we might generate MFLO or
+ MFHI instructions. Note that we avoid using MFLO and MFHI if
+ the VR4130 MACC and DMACC instructions are available instead;
+ see the *mfhilo_{si,di}_macc patterns. */
+ if (TARGET_FIX_VR4130 && !ISA_HAS_MACCHI)
+ cfun->machine->all_noreorder_p = false;
+
+ htab = htab_create (37, mips_lo_sum_offset_hash,
+ mips_lo_sum_offset_eq, free);
+
+ /* Make a first pass over the instructions, recording all the LO_SUMs. */
+ for (insn = get_insns (); insn != 0; insn = NEXT_INSN (insn))
+ FOR_EACH_SUBINSN (subinsn, insn)
+ if (USEFUL_INSN_P (subinsn))
+ for_each_rtx (&PATTERN (subinsn), mips_record_lo_sum, htab);
+
+ last_insn = 0;
+ hilo_delay = 2;
+ delayed_reg = 0;
+ lo_reg = gen_rtx_REG (SImode, LO_REGNUM);
+
+ /* Make a second pass over the instructions. Delete orphaned
+ high-part relocations or turn them into NOPs. Avoid hazards
+ by inserting NOPs. */
+ for (insn = get_insns (); insn != 0; insn = next_insn)
+ {
+ next_insn = NEXT_INSN (insn);
+ if (USEFUL_INSN_P (insn))
+ {
+ if (GET_CODE (PATTERN (insn)) == SEQUENCE)
+ {
+ /* If we find an orphaned high-part relocation in a delay
+ slot, it's easier to turn that instruction into a NOP than
+ to delete it. The delay slot will be a NOP either way. */
+ FOR_EACH_SUBINSN (subinsn, insn)
+ if (INSN_P (subinsn))
+ {
+ if (mips_orphaned_high_part_p (htab, subinsn))
+ {
+ PATTERN (subinsn) = gen_nop ();
+ INSN_CODE (subinsn) = CODE_FOR_nop;
+ }
+ mips_avoid_hazard (last_insn, subinsn, &hilo_delay,
+ &delayed_reg, lo_reg);
+ }
+ last_insn = insn;
+ }
+ else
+ {
+ /* INSN is a single instruction. Delete it if it's an
+ orphaned high-part relocation. */
+ if (mips_orphaned_high_part_p (htab, insn))
+ delete_insn (insn);
+ /* Also delete cache barriers if the last instruction
+ was an annulled branch. INSN will not be speculatively
+ executed. */
+ else if (recog_memoized (insn) == CODE_FOR_r10k_cache_barrier
+ && last_insn
+ && INSN_ANNULLED_BRANCH_P (SEQ_BEGIN (last_insn)))
+ delete_insn (insn);
+ else
+ {
+ mips_avoid_hazard (last_insn, insn, &hilo_delay,
+ &delayed_reg, lo_reg);
+ last_insn = insn;
+ }
+ }
+ }
+ }
+
+ htab_delete (htab);
+}
+
+/* If we are using a GOT, but have not decided to use a global pointer yet,
+ see whether we need one to implement long branches. Convert the ghost
+ global-pointer instructions into real ones if so. */
+
+static bool
+mips_expand_ghost_gp_insns (void)
+{
+ rtx insn;
+ int normal_length;
+
+ /* Quick exit if we already know that we will or won't need a
+ global pointer. */
+ if (!TARGET_USE_GOT
+ || cfun->machine->global_pointer == INVALID_REGNUM
+ || mips_must_initialize_gp_p ())
+ return false;
+
+ shorten_branches (get_insns ());
+
+ /* Look for a branch that is longer than normal. The normal length for
+ non-MIPS16 branches is 8, because the length includes the delay slot.
+ It is 4 for MIPS16, because MIPS16 branches are extended instructions,
+ but they have no delay slot. */
+ normal_length = (TARGET_MIPS16 ? 4 : 8);
+ for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+ if (JUMP_P (insn)
+ && USEFUL_INSN_P (insn)
+ && get_attr_length (insn) > normal_length)
+ break;
+
+ if (insn == NULL_RTX)
+ return false;
+
+ /* We've now established that we need $gp. */
+ cfun->machine->must_initialize_gp_p = true;
+ split_all_insns_noflow ();
+
+ return true;
+}
+
+/* Subroutine of mips_reorg to manage passes that require DF. */
+
+static void
+mips_df_reorg (void)
+{
+ /* Create def-use chains. */
+ df_set_flags (DF_EQ_NOTES);
+ df_chain_add_problem (DF_UD_CHAIN);
+ df_analyze ();
+
+ if (TARGET_RELAX_PIC_CALLS)
+ mips_annotate_pic_calls ();
+
+ if (mips_r10k_cache_barrier != R10K_CACHE_BARRIER_NONE)
+ r10k_insert_cache_barriers ();
+
+ df_finish_pass (false);
+}
+
+/* Implement TARGET_MACHINE_DEPENDENT_REORG. */
+
+static void
+mips_reorg (void)
+{
+ /* Restore the BLOCK_FOR_INSN pointers, which are needed by DF. Also during
+ insn splitting in mips16_lay_out_constants, DF insn info is only kept up
+ to date if the CFG is available. */
+ if (mips_cfg_in_reorg ())
+ compute_bb_for_insn ();
+ mips16_lay_out_constants ();
+ if (mips_cfg_in_reorg ())
+ {
+ mips_df_reorg ();
+ free_bb_for_insn ();
+ }
+
+ if (optimize > 0 && flag_delayed_branch)
+ {
+ cleanup_barriers ();
+ dbr_schedule (get_insns ());
+ }
+ mips_reorg_process_insns ();
+ if (!TARGET_MIPS16
+ && TARGET_EXPLICIT_RELOCS
+ && TUNE_MIPS4130
+ && TARGET_VR4130_ALIGN)
+ vr4130_align_insns ();
+ if (mips_expand_ghost_gp_insns ())
+ /* The expansion could invalidate some of the VR4130 alignment
+ optimizations, but this should be an extremely rare case anyhow. */
+ mips_reorg_process_insns ();
+}
+
+/* Implement TARGET_ASM_OUTPUT_MI_THUNK. Generate rtl rather than asm text
+ in order to avoid duplicating too much logic from elsewhere. */
+
+static void
+mips_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
+ HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset,
+ tree function)
+{
+ rtx this_rtx, temp1, temp2, insn, fnaddr;
+ bool use_sibcall_p;
+
+ /* Pretend to be a post-reload pass while generating rtl. */
+ reload_completed = 1;
+
+ /* Mark the end of the (empty) prologue. */
+ emit_note (NOTE_INSN_PROLOGUE_END);
+
+ /* Determine if we can use a sibcall to call FUNCTION directly. */
+ fnaddr = XEXP (DECL_RTL (function), 0);
+ use_sibcall_p = (mips_function_ok_for_sibcall (function, NULL)
+ && const_call_insn_operand (fnaddr, Pmode));
+
+ /* Determine if we need to load FNADDR from the GOT. */
+ if (!use_sibcall_p
+ && (mips_got_symbol_type_p
+ (mips_classify_symbol (fnaddr, SYMBOL_CONTEXT_LEA))))
+ {
+ /* Pick a global pointer. Use a call-clobbered register if
+ TARGET_CALL_SAVED_GP. */
+ cfun->machine->global_pointer
+ = TARGET_CALL_SAVED_GP ? 15 : GLOBAL_POINTER_REGNUM;
+ cfun->machine->must_initialize_gp_p = true;
+ SET_REGNO (pic_offset_table_rtx, cfun->machine->global_pointer);
+
+ /* Set up the global pointer for n32 or n64 abicalls. */
+ mips_emit_loadgp ();
+ }
+
+ /* We need two temporary registers in some cases. */
+ temp1 = gen_rtx_REG (Pmode, 2);
+ temp2 = gen_rtx_REG (Pmode, 3);
+
+ /* Find out which register contains the "this" pointer. */
+ if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function))
+ this_rtx = gen_rtx_REG (Pmode, GP_ARG_FIRST + 1);
+ else
+ this_rtx = gen_rtx_REG (Pmode, GP_ARG_FIRST);
+
+ /* Add DELTA to THIS_RTX. */
+ if (delta != 0)
+ {
+ rtx offset = GEN_INT (delta);
+ if (!SMALL_OPERAND (delta))
+ {
+ mips_emit_move (temp1, offset);
+ offset = temp1;
+ }
+ emit_insn (gen_add3_insn (this_rtx, this_rtx, offset));
+ }
+
+ /* If needed, add *(*THIS_RTX + VCALL_OFFSET) to THIS_RTX. */
+ if (vcall_offset != 0)
+ {
+ rtx addr;
+
+ /* Set TEMP1 to *THIS_RTX. */
+ mips_emit_move (temp1, gen_rtx_MEM (Pmode, this_rtx));
+
+ /* Set ADDR to a legitimate address for *THIS_RTX + VCALL_OFFSET. */
+ addr = mips_add_offset (temp2, temp1, vcall_offset);
+
+ /* Load the offset and add it to THIS_RTX. */
+ mips_emit_move (temp1, gen_rtx_MEM (Pmode, addr));
+ emit_insn (gen_add3_insn (this_rtx, this_rtx, temp1));
+ }
+
+ /* Jump to the target function. Use a sibcall if direct jumps are
+ allowed, otherwise load the address into a register first. */
+ if (use_sibcall_p)
+ {
+ insn = emit_call_insn (gen_sibcall_internal (fnaddr, const0_rtx));
+ SIBLING_CALL_P (insn) = 1;
+ }
+ else
+ {
+ /* This is messy. GAS treats "la $25,foo" as part of a call
+ sequence and may allow a global "foo" to be lazily bound.
+ The general move patterns therefore reject this combination.
+
+ In this context, lazy binding would actually be OK
+ for TARGET_CALL_CLOBBERED_GP, but it's still wrong for
+ TARGET_CALL_SAVED_GP; see mips_load_call_address.
+ We must therefore load the address via a temporary
+ register if mips_dangerous_for_la25_p.
+
+ If we jump to the temporary register rather than $25,
+ the assembler can use the move insn to fill the jump's
+ delay slot.
+
+ We can use the same technique for MIPS16 code, where $25
+ is not a valid JR register. */
+ if (TARGET_USE_PIC_FN_ADDR_REG
+ && !TARGET_MIPS16
+ && !mips_dangerous_for_la25_p (fnaddr))
+ temp1 = gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM);
+ mips_load_call_address (MIPS_CALL_SIBCALL, temp1, fnaddr);
+
+ if (TARGET_USE_PIC_FN_ADDR_REG
+ && REGNO (temp1) != PIC_FUNCTION_ADDR_REGNUM)
+ mips_emit_move (gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM), temp1);
+ emit_jump_insn (gen_indirect_jump (temp1));
+ }
+
+ /* Run just enough of rest_of_compilation. This sequence was
+ "borrowed" from alpha.c. */
+ insn = get_insns ();
+ insn_locators_alloc ();
+ split_all_insns_noflow ();
+ mips16_lay_out_constants ();
+ shorten_branches (insn);
+ final_start_function (insn, file, 1);
+ final (insn, file, 1);
+ final_end_function ();
+
+ /* Clean up the vars set above. Note that final_end_function resets
+ the global pointer for us. */
+ reload_completed = 0;
+}
+
+/* The last argument passed to mips_set_mips16_mode, or negative if the
+ function hasn't been called yet.
+
+ There are two copies of this information. One is saved and restored
+ by the PCH process while the other is specific to this compiler
+ invocation. The information calculated by mips_set_mips16_mode
+ is invalid unless the two variables are the same. */
+static int was_mips16_p = -1;
+static GTY(()) int was_mips16_pch_p = -1;
+
+/* Set up the target-dependent global state so that it matches the
+ current function's ISA mode. */
+
+static void
+mips_set_mips16_mode (int mips16_p)
+{
+ if (mips16_p == was_mips16_p
+ && mips16_p == was_mips16_pch_p)
+ return;
+
+ /* Restore base settings of various flags. */
+ target_flags = mips_base_target_flags;
+ flag_schedule_insns = mips_base_schedule_insns;
+ flag_reorder_blocks_and_partition = mips_base_reorder_blocks_and_partition;
+ flag_move_loop_invariants = mips_base_move_loop_invariants;
+ align_loops = mips_base_align_loops;
+ align_jumps = mips_base_align_jumps;
+ align_functions = mips_base_align_functions;
+
+ if (mips16_p)
+ {
+ /* Switch to MIPS16 mode. */
+ target_flags |= MASK_MIPS16;
+
+ /* Don't run the scheduler before reload, since it tends to
+ increase register pressure. */
+ flag_schedule_insns = 0;
+
+ /* Don't do hot/cold partitioning. mips16_lay_out_constants expects
+ the whole function to be in a single section. */
+ flag_reorder_blocks_and_partition = 0;
+
+ /* Don't move loop invariants, because it tends to increase
+ register pressure. It also introduces an extra move in cases
+ where the constant is the first operand in a two-operand binary
+ instruction, or when it forms a register argument to a functon
+ call. */
+ flag_move_loop_invariants = 0;
+
+ target_flags |= MASK_EXPLICIT_RELOCS;
+
+ /* Experiments suggest we get the best overall section-anchor
+ results from using the range of an unextended LW or SW. Code
+ that makes heavy use of byte or short accesses can do better
+ with ranges of 0...31 and 0...63 respectively, but most code is
+ sensitive to the range of LW and SW instead. */
+ targetm.min_anchor_offset = 0;
+ targetm.max_anchor_offset = 127;
+
+ targetm.const_anchor = 0;
+
+ /* MIPS16 has no BAL instruction. */
+ target_flags &= ~MASK_RELAX_PIC_CALLS;
+
+ if (flag_pic && !TARGET_OLDABI)
+ sorry ("MIPS16 PIC for ABIs other than o32 and o64");
+
+ if (TARGET_XGOT)
+ sorry ("MIPS16 -mxgot code");
+
+ if (TARGET_HARD_FLOAT_ABI && !TARGET_OLDABI)
+ sorry ("hard-float MIPS16 code for ABIs other than o32 and o64");
+ }
+ else
+ {
+ /* Switch to normal (non-MIPS16) mode. */
+ target_flags &= ~MASK_MIPS16;
+
+ /* Provide default values for align_* for 64-bit targets. */
+ if (TARGET_64BIT)
+ {
+ if (align_loops == 0)
+ align_loops = 8;
+ if (align_jumps == 0)
+ align_jumps = 8;
+ if (align_functions == 0)
+ align_functions = 8;
+ }
+
+ targetm.min_anchor_offset = -32768;
+ targetm.max_anchor_offset = 32767;
+
+ targetm.const_anchor = 0x8000;
+ }
+
+ /* (Re)initialize MIPS target internals for new ISA. */
+ mips_init_relocs ();
+
+ if (mips16_p)
+ {
+ if (!mips16_globals)
+ mips16_globals = save_target_globals ();
+ else
+ restore_target_globals (mips16_globals);
+ }
+ else
+ restore_target_globals (&default_target_globals);
+
+ was_mips16_p = mips16_p;
+ was_mips16_pch_p = mips16_p;
+}
+
+/* Implement TARGET_SET_CURRENT_FUNCTION. Decide whether the current
+ function should use the MIPS16 ISA and switch modes accordingly. */
+
+static void
+mips_set_current_function (tree fndecl)
+{
+ mips_set_mips16_mode (mips_use_mips16_mode_p (fndecl));
+}
+
+/* Allocate a chunk of memory for per-function machine-dependent data. */
+
+static struct machine_function *
+mips_init_machine_status (void)
+{
+ return ggc_alloc_cleared_machine_function ();
+}
+
+/* Return the processor associated with the given ISA level, or null
+ if the ISA isn't valid. */
+
+static const struct mips_cpu_info *
+mips_cpu_info_from_isa (int isa)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE (mips_cpu_info_table); i++)
+ if (mips_cpu_info_table[i].isa == isa)
+ return mips_cpu_info_table + i;
+
+ return NULL;
+}
+
+/* Return true if GIVEN is the same as CANONICAL, or if it is CANONICAL
+ with a final "000" replaced by "k". Ignore case.
+
+ Note: this function is shared between GCC and GAS. */
+
+static bool
+mips_strict_matching_cpu_name_p (const char *canonical, const char *given)
+{
+ while (*given != 0 && TOLOWER (*given) == TOLOWER (*canonical))
+ given++, canonical++;
+
+ return ((*given == 0 && *canonical == 0)
+ || (strcmp (canonical, "000") == 0 && strcasecmp (given, "k") == 0));
+}
+
+/* Return true if GIVEN matches CANONICAL, where GIVEN is a user-supplied
+ CPU name. We've traditionally allowed a lot of variation here.
+
+ Note: this function is shared between GCC and GAS. */
+
+static bool
+mips_matching_cpu_name_p (const char *canonical, const char *given)
+{
+ /* First see if the name matches exactly, or with a final "000"
+ turned into "k". */
+ if (mips_strict_matching_cpu_name_p (canonical, given))
+ return true;
+
+ /* If not, try comparing based on numerical designation alone.
+ See if GIVEN is an unadorned number, or 'r' followed by a number. */
+ if (TOLOWER (*given) == 'r')
+ given++;
+ if (!ISDIGIT (*given))
+ return false;
+
+ /* Skip over some well-known prefixes in the canonical name,
+ hoping to find a number there too. */
+ if (TOLOWER (canonical[0]) == 'v' && TOLOWER (canonical[1]) == 'r')
+ canonical += 2;
+ else if (TOLOWER (canonical[0]) == 'r' && TOLOWER (canonical[1]) == 'm')
+ canonical += 2;
+ else if (TOLOWER (canonical[0]) == 'r')
+ canonical += 1;
+
+ return mips_strict_matching_cpu_name_p (canonical, given);
+}
+
+/* Return the mips_cpu_info entry for the processor or ISA given
+ by CPU_STRING. Return null if the string isn't recognized.
+
+ A similar function exists in GAS. */
+
+static const struct mips_cpu_info *
+mips_parse_cpu (const char *cpu_string)
+{
+ unsigned int i;
+ const char *s;
+
+ /* In the past, we allowed upper-case CPU names, but it doesn't
+ work well with the multilib machinery. */
+ for (s = cpu_string; *s != 0; s++)
+ if (ISUPPER (*s))
+ {
+ warning (0, "CPU names must be lower case");
+ break;
+ }
+
+ /* 'from-abi' selects the most compatible architecture for the given
+ ABI: MIPS I for 32-bit ABIs and MIPS III for 64-bit ABIs. For the
+ EABIs, we have to decide whether we're using the 32-bit or 64-bit
+ version. */
+ if (strcasecmp (cpu_string, "from-abi") == 0)
+ return mips_cpu_info_from_isa (ABI_NEEDS_32BIT_REGS ? 1
+ : ABI_NEEDS_64BIT_REGS ? 3
+ : (TARGET_64BIT ? 3 : 1));
+
+ /* 'default' has traditionally been a no-op. Probably not very useful. */
+ if (strcasecmp (cpu_string, "default") == 0)
+ return NULL;
+
+ for (i = 0; i < ARRAY_SIZE (mips_cpu_info_table); i++)
+ if (mips_matching_cpu_name_p (mips_cpu_info_table[i].name, cpu_string))
+ return mips_cpu_info_table + i;
+
+ return NULL;
+}
+
+/* Set up globals to generate code for the ISA or processor
+ described by INFO. */
+
+static void
+mips_set_architecture (const struct mips_cpu_info *info)
+{
+ if (info != 0)
+ {
+ mips_arch_info = info;
+ mips_arch = info->cpu;
+ mips_isa = info->isa;
+ }
+}
+
+/* Likewise for tuning. */
+
+static void
+mips_set_tune (const struct mips_cpu_info *info)
+{
+ if (info != 0)
+ {
+ mips_tune_info = info;
+ mips_tune = info->cpu;
+ }
+}
+
+/* Implement TARGET_HANDLE_OPTION. */
+
+static bool
+mips_handle_option (size_t code, const char *arg, int value ATTRIBUTE_UNUSED)
+{
+ switch (code)
+ {
+ case OPT_mabi_:
+ if (strcmp (arg, "32") == 0)
+ mips_abi = ABI_32;
+ else if (strcmp (arg, "o64") == 0)
+ mips_abi = ABI_O64;
+ else if (strcmp (arg, "n32") == 0)
+ mips_abi = ABI_N32;
+ else if (strcmp (arg, "64") == 0)
+ mips_abi = ABI_64;
+ else if (strcmp (arg, "eabi") == 0)
+ mips_abi = ABI_EABI;
+ else
+ return false;
+ return true;
+
+ case OPT_march_:
+ case OPT_mtune_:
+ return mips_parse_cpu (arg) != 0;
+
+ case OPT_mips:
+ mips_isa_option_info = mips_parse_cpu (ACONCAT (("mips", arg, NULL)));
+ return mips_isa_option_info != 0;
+
+ case OPT_mno_flush_func:
+ mips_cache_flush_func = NULL;
+ return true;
+
+ case OPT_mcode_readable_:
+ if (strcmp (arg, "yes") == 0)
+ mips_code_readable = CODE_READABLE_YES;
+ else if (strcmp (arg, "pcrel") == 0)
+ mips_code_readable = CODE_READABLE_PCREL;
+ else if (strcmp (arg, "no") == 0)
+ mips_code_readable = CODE_READABLE_NO;
+ else
+ return false;
+ return true;
+
+ case OPT_mr10k_cache_barrier_:
+ if (strcmp (arg, "load-store") == 0)
+ mips_r10k_cache_barrier = R10K_CACHE_BARRIER_LOAD_STORE;
+ else if (strcmp (arg, "store") == 0)
+ mips_r10k_cache_barrier = R10K_CACHE_BARRIER_STORE;
+ else if (strcmp (arg, "none") == 0)
+ mips_r10k_cache_barrier = R10K_CACHE_BARRIER_NONE;
+ else
+ return false;
+ return true;
+
+ default:
+ return true;
+ }
+}
+
+/* Implement TARGET_OPTION_OVERRIDE. */
+
+static void
+mips_option_override (void)
+{
+ int i, start, regno, mode;
+
+ /* Process flags as though we were generating non-MIPS16 code. */
+ mips_base_mips16 = TARGET_MIPS16;
+ target_flags &= ~MASK_MIPS16;
+
+#ifdef SUBTARGET_OVERRIDE_OPTIONS
+ SUBTARGET_OVERRIDE_OPTIONS;
+#endif
+
+ /* -mno-float overrides -mhard-float and -msoft-float. */
+ if (TARGET_NO_FLOAT)
+ {
+ target_flags |= MASK_SOFT_FLOAT_ABI;
+ target_flags_explicit |= MASK_SOFT_FLOAT_ABI;
+ }
+
+ if (TARGET_FLIP_MIPS16)
+ TARGET_INTERLINK_MIPS16 = 1;
+
+ /* Set the small data limit. */
+ mips_small_data_threshold = (global_options_set.x_g_switch_value
+ ? g_switch_value
+ : MIPS_DEFAULT_GVALUE);
+
+ /* The following code determines the architecture and register size.
+ Similar code was added to GAS 2.14 (see tc-mips.c:md_after_parse_args()).
+ The GAS and GCC code should be kept in sync as much as possible. */
+
+ if (mips_arch_string != 0)
+ mips_set_architecture (mips_parse_cpu (mips_arch_string));
+
+ if (mips_isa_option_info != 0)
+ {
+ if (mips_arch_info == 0)
+ mips_set_architecture (mips_isa_option_info);
+ else if (mips_arch_info->isa != mips_isa_option_info->isa)
+ error ("%<-%s%> conflicts with the other architecture options, "
+ "which specify a %s processor",
+ mips_isa_option_info->name,
+ mips_cpu_info_from_isa (mips_arch_info->isa)->name);
+ }
+
+ if (mips_arch_info == 0)
+ {
+#ifdef MIPS_CPU_STRING_DEFAULT
+ mips_set_architecture (mips_parse_cpu (MIPS_CPU_STRING_DEFAULT));
+#else
+ mips_set_architecture (mips_cpu_info_from_isa (MIPS_ISA_DEFAULT));
+#endif
+ }
+
+ if (ABI_NEEDS_64BIT_REGS && !ISA_HAS_64BIT_REGS)
+ error ("%<-march=%s%> is not compatible with the selected ABI",
+ mips_arch_info->name);
+
+ /* Optimize for mips_arch, unless -mtune selects a different processor. */
+ if (mips_tune_string != 0)
+ mips_set_tune (mips_parse_cpu (mips_tune_string));
+
+ if (mips_tune_info == 0)
+ mips_set_tune (mips_arch_info);
+
+ if ((target_flags_explicit & MASK_64BIT) != 0)
+ {
+ /* The user specified the size of the integer registers. Make sure
+ it agrees with the ABI and ISA. */
+ if (TARGET_64BIT && !ISA_HAS_64BIT_REGS)
+ error ("%<-mgp64%> used with a 32-bit processor");
+ else if (!TARGET_64BIT && ABI_NEEDS_64BIT_REGS)
+ error ("%<-mgp32%> used with a 64-bit ABI");
+ else if (TARGET_64BIT && ABI_NEEDS_32BIT_REGS)
+ error ("%<-mgp64%> used with a 32-bit ABI");
+ }
+ else
+ {
+ /* Infer the integer register size from the ABI and processor.
+ Restrict ourselves to 32-bit registers if that's all the
+ processor has, or if the ABI cannot handle 64-bit registers. */
+ if (ABI_NEEDS_32BIT_REGS || !ISA_HAS_64BIT_REGS)
+ target_flags &= ~MASK_64BIT;
+ else
+ target_flags |= MASK_64BIT;
+ }
+
+ if ((target_flags_explicit & MASK_FLOAT64) != 0)
+ {
+ if (TARGET_SINGLE_FLOAT && TARGET_FLOAT64)
+ error ("unsupported combination: %s", "-mfp64 -msingle-float");
+ else if (TARGET_64BIT && TARGET_DOUBLE_FLOAT && !TARGET_FLOAT64)
+ error ("unsupported combination: %s", "-mgp64 -mfp32 -mdouble-float");
+ else if (!TARGET_64BIT && TARGET_FLOAT64)
+ {
+ if (!ISA_HAS_MXHC1)
+ error ("%<-mgp32%> and %<-mfp64%> can only be combined if"
+ " the target supports the mfhc1 and mthc1 instructions");
+ else if (mips_abi != ABI_32)
+ error ("%<-mgp32%> and %<-mfp64%> can only be combined when using"
+ " the o32 ABI");
+ }
+ }
+ else
+ {
+ /* -msingle-float selects 32-bit float registers. Otherwise the
+ float registers should be the same size as the integer ones. */
+ if (TARGET_64BIT && TARGET_DOUBLE_FLOAT)
+ target_flags |= MASK_FLOAT64;
+ else
+ target_flags &= ~MASK_FLOAT64;
+ }
+
+ /* End of code shared with GAS. */
+
+ /* If no -mlong* option was given, infer it from the other options. */
+ if ((target_flags_explicit & MASK_LONG64) == 0)
+ {
+ if ((mips_abi == ABI_EABI && TARGET_64BIT) || mips_abi == ABI_64)
+ target_flags |= MASK_LONG64;
+ else
+ target_flags &= ~MASK_LONG64;
+ }
+
+ if (!TARGET_OLDABI)
+ flag_pcc_struct_return = 0;
+
+ /* Decide which rtx_costs structure to use. */
+ if (optimize_size)
+ mips_cost = &mips_rtx_cost_optimize_size;
+ else
+ mips_cost = &mips_rtx_cost_data[mips_tune];
+
+ /* If the user hasn't specified a branch cost, use the processor's
+ default. */
+ if (mips_branch_cost == 0)
+ mips_branch_cost = mips_cost->branch_cost;
+
+ /* If neither -mbranch-likely nor -mno-branch-likely was given
+ on the command line, set MASK_BRANCHLIKELY based on the target
+ architecture and tuning flags. Annulled delay slots are a
+ size win, so we only consider the processor-specific tuning
+ for !optimize_size. */
+ if ((target_flags_explicit & MASK_BRANCHLIKELY) == 0)
+ {
+ if (ISA_HAS_BRANCHLIKELY
+ && (optimize_size
+ || (mips_tune_info->tune_flags & PTF_AVOID_BRANCHLIKELY) == 0))
+ target_flags |= MASK_BRANCHLIKELY;
+ else
+ target_flags &= ~MASK_BRANCHLIKELY;
+ }
+ else if (TARGET_BRANCHLIKELY && !ISA_HAS_BRANCHLIKELY)
+ warning (0, "the %qs architecture does not support branch-likely"
+ " instructions", mips_arch_info->name);
+
+ /* The effect of -mabicalls isn't defined for the EABI. */
+ if (mips_abi == ABI_EABI && TARGET_ABICALLS)
+ {
+ error ("unsupported combination: %s", "-mabicalls -mabi=eabi");
+ target_flags &= ~MASK_ABICALLS;
+ }
+
+ if (TARGET_ABICALLS_PIC2)
+ /* We need to set flag_pic for executables as well as DSOs
+ because we may reference symbols that are not defined in
+ the final executable. (MIPS does not use things like
+ copy relocs, for example.)
+
+ There is a body of code that uses __PIC__ to distinguish
+ between -mabicalls and -mno-abicalls code. The non-__PIC__
+ variant is usually appropriate for TARGET_ABICALLS_PIC0, as
+ long as any indirect jumps use $25. */
+ flag_pic = 1;
+
+ /* -mvr4130-align is a "speed over size" optimization: it usually produces
+ faster code, but at the expense of more nops. Enable it at -O3 and
+ above. */
+ if (optimize > 2 && (target_flags_explicit & MASK_VR4130_ALIGN) == 0)
+ target_flags |= MASK_VR4130_ALIGN;
+
+ /* Prefer a call to memcpy over inline code when optimizing for size,
+ though see MOVE_RATIO in mips.h. */
+ if (optimize_size && (target_flags_explicit & MASK_MEMCPY) == 0)
+ target_flags |= MASK_MEMCPY;
+
+ /* If we have a nonzero small-data limit, check that the -mgpopt
+ setting is consistent with the other target flags. */
+ if (mips_small_data_threshold > 0)
+ {
+ if (!TARGET_GPOPT)
+ {
+ if (!TARGET_EXPLICIT_RELOCS)
+ error ("%<-mno-gpopt%> needs %<-mexplicit-relocs%>");
+
+ TARGET_LOCAL_SDATA = false;
+ TARGET_EXTERN_SDATA = false;
+ }
+ else
+ {
+ if (TARGET_VXWORKS_RTP)
+ warning (0, "cannot use small-data accesses for %qs", "-mrtp");
+
+ if (TARGET_ABICALLS)
+ warning (0, "cannot use small-data accesses for %qs",
+ "-mabicalls");
+ }
+ }
+
+#ifdef MIPS_TFMODE_FORMAT
+ REAL_MODE_FORMAT (TFmode) = &MIPS_TFMODE_FORMAT;
+#endif
+
+ /* Make sure that the user didn't turn off paired single support when
+ MIPS-3D support is requested. */
+ if (TARGET_MIPS3D
+ && (target_flags_explicit & MASK_PAIRED_SINGLE_FLOAT)
+ && !TARGET_PAIRED_SINGLE_FLOAT)
+ error ("%<-mips3d%> requires %<-mpaired-single%>");
+
+ /* If TARGET_MIPS3D, enable MASK_PAIRED_SINGLE_FLOAT. */
+ if (TARGET_MIPS3D)
+ target_flags |= MASK_PAIRED_SINGLE_FLOAT;
+
+ /* Make sure that when TARGET_PAIRED_SINGLE_FLOAT is true, TARGET_FLOAT64
+ and TARGET_HARD_FLOAT_ABI are both true. */
+ if (TARGET_PAIRED_SINGLE_FLOAT && !(TARGET_FLOAT64 && TARGET_HARD_FLOAT_ABI))
+ error ("%qs must be used with %qs",
+ TARGET_MIPS3D ? "-mips3d" : "-mpaired-single",
+ TARGET_HARD_FLOAT_ABI ? "-mfp64" : "-mhard-float");
+
+ /* Make sure that the ISA supports TARGET_PAIRED_SINGLE_FLOAT when it is
+ enabled. */
+ if (TARGET_PAIRED_SINGLE_FLOAT && !ISA_HAS_PAIRED_SINGLE)
+ warning (0, "the %qs architecture does not support paired-single"
+ " instructions", mips_arch_info->name);
+
+ if (mips_r10k_cache_barrier != R10K_CACHE_BARRIER_NONE
+ && !TARGET_CACHE_BUILTIN)
+ {
+ error ("%qs requires a target that provides the %qs instruction",
+ "-mr10k-cache-barrier", "cache");
+ mips_r10k_cache_barrier = R10K_CACHE_BARRIER_NONE;
+ }
+
+ /* If TARGET_DSPR2, enable MASK_DSP. */
+ if (TARGET_DSPR2)
+ target_flags |= MASK_DSP;
+
+ /* .eh_frame addresses should be the same width as a C pointer.
+ Most MIPS ABIs support only one pointer size, so the assembler
+ will usually know exactly how big an .eh_frame address is.
+
+ Unfortunately, this is not true of the 64-bit EABI. The ABI was
+ originally defined to use 64-bit pointers (i.e. it is LP64), and
+ this is still the default mode. However, we also support an n32-like
+ ILP32 mode, which is selected by -mlong32. The problem is that the
+ assembler has traditionally not had an -mlong option, so it has
+ traditionally not known whether we're using the ILP32 or LP64 form.
+
+ As it happens, gas versions up to and including 2.19 use _32-bit_
+ addresses for EABI64 .cfi_* directives. This is wrong for the
+ default LP64 mode, so we can't use the directives by default.
+ Moreover, since gas's current behavior is at odds with gcc's
+ default behavior, it seems unwise to rely on future versions
+ of gas behaving the same way. We therefore avoid using .cfi
+ directives for -mlong32 as well. */
+ if (mips_abi == ABI_EABI && TARGET_64BIT)
+ flag_dwarf2_cfi_asm = 0;
+
+ /* .cfi_* directives generate a read-only section, so fall back on
+ manual .eh_frame creation if we need the section to be writable. */
+ if (TARGET_WRITABLE_EH_FRAME)
+ flag_dwarf2_cfi_asm = 0;
+
+ mips_init_print_operand_punct ();
+
+ /* Set up array to map GCC register number to debug register number.
+ Ignore the special purpose register numbers. */
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ {
+ mips_dbx_regno[i] = INVALID_REGNUM;
+ if (GP_REG_P (i) || FP_REG_P (i) || ALL_COP_REG_P (i))
+ mips_dwarf_regno[i] = i;
+ else
+ mips_dwarf_regno[i] = INVALID_REGNUM;
+ }
+
+ start = GP_DBX_FIRST - GP_REG_FIRST;
+ for (i = GP_REG_FIRST; i <= GP_REG_LAST; i++)
+ mips_dbx_regno[i] = i + start;
+
+ start = FP_DBX_FIRST - FP_REG_FIRST;
+ for (i = FP_REG_FIRST; i <= FP_REG_LAST; i++)
+ mips_dbx_regno[i] = i + start;
+
+ /* Accumulator debug registers use big-endian ordering. */
+ mips_dbx_regno[HI_REGNUM] = MD_DBX_FIRST + 0;
+ mips_dbx_regno[LO_REGNUM] = MD_DBX_FIRST + 1;
+ mips_dwarf_regno[HI_REGNUM] = MD_REG_FIRST + 0;
+ mips_dwarf_regno[LO_REGNUM] = MD_REG_FIRST + 1;
+ for (i = DSP_ACC_REG_FIRST; i <= DSP_ACC_REG_LAST; i += 2)
+ {
+ mips_dwarf_regno[i + TARGET_LITTLE_ENDIAN] = i;
+ mips_dwarf_regno[i + TARGET_BIG_ENDIAN] = i + 1;
+ }
+
+ /* Set up mips_hard_regno_mode_ok. */
+ for (mode = 0; mode < MAX_MACHINE_MODE; mode++)
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+ mips_hard_regno_mode_ok[mode][regno]
+ = mips_hard_regno_mode_ok_p (regno, (enum machine_mode) mode);
+
+ /* Function to allocate machine-dependent function status. */
+ init_machine_status = &mips_init_machine_status;
+
+ /* Default to working around R4000 errata only if the processor
+ was selected explicitly. */
+ if ((target_flags_explicit & MASK_FIX_R4000) == 0
+ && mips_matching_cpu_name_p (mips_arch_info->name, "r4000"))
+ target_flags |= MASK_FIX_R4000;
+
+ /* Default to working around R4400 errata only if the processor
+ was selected explicitly. */
+ if ((target_flags_explicit & MASK_FIX_R4400) == 0
+ && mips_matching_cpu_name_p (mips_arch_info->name, "r4400"))
+ target_flags |= MASK_FIX_R4400;
+
+ /* Default to working around R10000 errata only if the processor
+ was selected explicitly. */
+ if ((target_flags_explicit & MASK_FIX_R10000) == 0
+ && mips_matching_cpu_name_p (mips_arch_info->name, "r10000"))
+ target_flags |= MASK_FIX_R10000;
+
+ /* Make sure that branch-likely instructions available when using
+ -mfix-r10000. The instructions are not available if either:
+
+ 1. -mno-branch-likely was passed.
+ 2. The selected ISA does not support branch-likely and
+ the command line does not include -mbranch-likely. */
+ if (TARGET_FIX_R10000
+ && ((target_flags_explicit & MASK_BRANCHLIKELY) == 0
+ ? !ISA_HAS_BRANCHLIKELY
+ : !TARGET_BRANCHLIKELY))
+ sorry ("%qs requires branch-likely instructions", "-mfix-r10000");
+
+ if (TARGET_SYNCI && !ISA_HAS_SYNCI)
+ {
+ warning (0, "the %qs architecture does not support the synci "
+ "instruction", mips_arch_info->name);
+ target_flags &= ~MASK_SYNCI;
+ }
+
+ /* Only optimize PIC indirect calls if they are actually required. */
+ if (!TARGET_USE_GOT || !TARGET_EXPLICIT_RELOCS)
+ target_flags &= ~MASK_RELAX_PIC_CALLS;
+
+ /* Save base state of options. */
+ mips_base_target_flags = target_flags;
+ mips_base_schedule_insns = flag_schedule_insns;
+ mips_base_reorder_blocks_and_partition = flag_reorder_blocks_and_partition;
+ mips_base_move_loop_invariants = flag_move_loop_invariants;
+ mips_base_align_loops = align_loops;
+ mips_base_align_jumps = align_jumps;
+ mips_base_align_functions = align_functions;
+
+ /* Now select the ISA mode.
+
+ Do all CPP-sensitive stuff in non-MIPS16 mode; we'll switch to
+ MIPS16 mode afterwards if need be. */
+ mips_set_mips16_mode (false);
+}
+
+/* Implement TARGET_OPTION_OPTIMIZATION_TABLE. */
+static const struct default_options mips_option_optimization_table[] =
+ {
+ { OPT_LEVELS_1_PLUS, OPT_fomit_frame_pointer, NULL, 1 },
+ { OPT_LEVELS_NONE, 0, NULL, 0 }
+ };
+
+/* Swap the register information for registers I and I + 1, which
+ currently have the wrong endianness. Note that the registers'
+ fixedness and call-clobberedness might have been set on the
+ command line. */
+
+static void
+mips_swap_registers (unsigned int i)
+{
+ int tmpi;
+ const char *tmps;
+
+#define SWAP_INT(X, Y) (tmpi = (X), (X) = (Y), (Y) = tmpi)
+#define SWAP_STRING(X, Y) (tmps = (X), (X) = (Y), (Y) = tmps)
+
+ SWAP_INT (fixed_regs[i], fixed_regs[i + 1]);
+ SWAP_INT (call_used_regs[i], call_used_regs[i + 1]);
+ SWAP_INT (call_really_used_regs[i], call_really_used_regs[i + 1]);
+ SWAP_STRING (reg_names[i], reg_names[i + 1]);
+
+#undef SWAP_STRING
+#undef SWAP_INT
+}
+
+/* Implement TARGET_CONDITIONAL_REGISTER_USAGE. */
+
+static void
+mips_conditional_register_usage (void)
+{
+
+ if (ISA_HAS_DSP)
+ {
+ /* These DSP control register fields are global. */
+ global_regs[CCDSP_PO_REGNUM] = 1;
+ global_regs[CCDSP_SC_REGNUM] = 1;
+ }
+ else
+ {
+ int regno;
+
+ for (regno = DSP_ACC_REG_FIRST; regno <= DSP_ACC_REG_LAST; regno++)
+ fixed_regs[regno] = call_used_regs[regno] = 1;
+ }
+ if (!TARGET_HARD_FLOAT)
+ {
+ int regno;
+
+ for (regno = FP_REG_FIRST; regno <= FP_REG_LAST; regno++)
+ fixed_regs[regno] = call_used_regs[regno] = 1;
+ for (regno = ST_REG_FIRST; regno <= ST_REG_LAST; regno++)
+ fixed_regs[regno] = call_used_regs[regno] = 1;
+ }
+ else if (! ISA_HAS_8CC)
+ {
+ int regno;
+
+ /* We only have a single condition-code register. We implement
+ this by fixing all the condition-code registers and generating
+ RTL that refers directly to ST_REG_FIRST. */
+ for (regno = ST_REG_FIRST; regno <= ST_REG_LAST; regno++)
+ fixed_regs[regno] = call_used_regs[regno] = 1;
+ }
+ /* In MIPS16 mode, we permit the $t temporary registers to be used
+ for reload. We prohibit the unused $s registers, since they
+ are call-saved, and saving them via a MIPS16 register would
+ probably waste more time than just reloading the value. */
+ if (TARGET_MIPS16)
+ {
+ fixed_regs[18] = call_used_regs[18] = 1;
+ fixed_regs[19] = call_used_regs[19] = 1;
+ fixed_regs[20] = call_used_regs[20] = 1;
+ fixed_regs[21] = call_used_regs[21] = 1;
+ fixed_regs[22] = call_used_regs[22] = 1;
+ fixed_regs[23] = call_used_regs[23] = 1;
+ fixed_regs[26] = call_used_regs[26] = 1;
+ fixed_regs[27] = call_used_regs[27] = 1;
+ fixed_regs[30] = call_used_regs[30] = 1;
+ }
+ /* $f20-$f23 are call-clobbered for n64. */
+ if (mips_abi == ABI_64)
+ {
+ int regno;
+ for (regno = FP_REG_FIRST + 20; regno < FP_REG_FIRST + 24; regno++)
+ call_really_used_regs[regno] = call_used_regs[regno] = 1;
+ }
+ /* Odd registers in the range $f21-$f31 (inclusive) are call-clobbered
+ for n32. */
+ if (mips_abi == ABI_N32)
+ {
+ int regno;
+ for (regno = FP_REG_FIRST + 21; regno <= FP_REG_FIRST + 31; regno+=2)
+ call_really_used_regs[regno] = call_used_regs[regno] = 1;
+ }
+ /* Make sure that double-register accumulator values are correctly
+ ordered for the current endianness. */
+ if (TARGET_LITTLE_ENDIAN)
+ {
+ unsigned int regno;
+
+ mips_swap_registers (MD_REG_FIRST);
+ for (regno = DSP_ACC_REG_FIRST; regno <= DSP_ACC_REG_LAST; regno += 2)
+ mips_swap_registers (regno);
+ }
+}
+
+/* Initialize vector TARGET to VALS. */
+
+void
+mips_expand_vector_init (rtx target, rtx vals)
+{
+ enum machine_mode mode;
+ enum machine_mode inner;
+ unsigned int i, n_elts;
+ rtx mem;
+
+ mode = GET_MODE (target);
+ inner = GET_MODE_INNER (mode);
+ n_elts = GET_MODE_NUNITS (mode);
+
+ gcc_assert (VECTOR_MODE_P (mode));
+
+ mem = assign_stack_temp (mode, GET_MODE_SIZE (mode), 0);
+ for (i = 0; i < n_elts; i++)
+ emit_move_insn (adjust_address_nv (mem, inner, i * GET_MODE_SIZE (inner)),
+ XVECEXP (vals, 0, i));
+
+ emit_move_insn (target, mem);
+}
+
+/* When generating MIPS16 code, we want to allocate $24 (T_REG) before
+ other registers for instructions for which it is possible. This
+ encourages the compiler to use CMP in cases where an XOR would
+ require some register shuffling. */
+
+void
+mips_order_regs_for_local_alloc (void)
+{
+ int i;
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ reg_alloc_order[i] = i;
+
+ if (TARGET_MIPS16)
+ {
+ /* It really doesn't matter where we put register 0, since it is
+ a fixed register anyhow. */
+ reg_alloc_order[0] = 24;
+ reg_alloc_order[24] = 0;
+ }
+}
+
+/* Implement EH_USES. */
+
+bool
+mips_eh_uses (unsigned int regno)
+{
+ if (reload_completed && !TARGET_ABSOLUTE_JUMPS)
+ {
+ /* We need to force certain registers to be live in order to handle
+ PIC long branches correctly. See mips_must_initialize_gp_p for
+ details. */
+ if (mips_cfun_has_cprestore_slot_p ())
+ {
+ if (regno == CPRESTORE_SLOT_REGNUM)
+ return true;
+ }
+ else
+ {
+ if (cfun->machine->global_pointer == regno)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/* Implement EPILOGUE_USES. */
+
+bool
+mips_epilogue_uses (unsigned int regno)
+{
+ /* Say that the epilogue uses the return address register. Note that
+ in the case of sibcalls, the values "used by the epilogue" are
+ considered live at the start of the called function. */
+ if (regno == RETURN_ADDR_REGNUM)
+ return true;
+
+ /* If using a GOT, say that the epilogue also uses GOT_VERSION_REGNUM.
+ See the comment above load_call<mode> for details. */
+ if (TARGET_USE_GOT && (regno) == GOT_VERSION_REGNUM)
+ return true;
+
+ /* An interrupt handler must preserve some registers that are
+ ordinarily call-clobbered. */
+ if (cfun->machine->interrupt_handler_p
+ && mips_interrupt_extra_call_saved_reg_p (regno))
+ return true;
+
+ return false;
+}
+
+/* A for_each_rtx callback. Stop the search if *X is an AT register. */
+
+static int
+mips_at_reg_p (rtx *x, void *data ATTRIBUTE_UNUSED)
+{
+ return REG_P (*x) && REGNO (*x) == AT_REGNUM;
+}
+
+/* Return true if INSN needs to be wrapped in ".set noat".
+ INSN has NOPERANDS operands, stored in OPVEC. */
+
+static bool
+mips_need_noat_wrapper_p (rtx insn, rtx *opvec, int noperands)
+{
+ int i;
+
+ if (recog_memoized (insn) >= 0)
+ for (i = 0; i < noperands; i++)
+ if (for_each_rtx (&opvec[i], mips_at_reg_p, NULL))
+ return true;
+ return false;
+}
+
+/* Implement FINAL_PRESCAN_INSN. */
+
+void
+mips_final_prescan_insn (rtx insn, rtx *opvec, int noperands)
+{
+ if (mips_need_noat_wrapper_p (insn, opvec, noperands))
+ mips_push_asm_switch (&mips_noat);
+}
+
+/* Implement TARGET_ASM_FINAL_POSTSCAN_INSN. */
+
+static void
+mips_final_postscan_insn (FILE *file ATTRIBUTE_UNUSED, rtx insn,
+ rtx *opvec, int noperands)
+{
+ if (mips_need_noat_wrapper_p (insn, opvec, noperands))
+ mips_pop_asm_switch (&mips_noat);
+}
+
+/* Return the function that is used to expand the <u>mulsidi3 pattern.
+ EXT_CODE is the code of the extension used. Return NULL if widening
+ multiplication shouldn't be used. */
+
+mulsidi3_gen_fn
+mips_mulsidi3_gen_fn (enum rtx_code ext_code)
+{
+ bool signed_p;
+
+ signed_p = ext_code == SIGN_EXTEND;
+ if (TARGET_64BIT)
+ {
+ /* Don't use widening multiplication with MULT when we have DMUL. Even
+ with the extension of its input operands DMUL is faster. Note that
+ the extension is not needed for signed multiplication. In order to
+ ensure that we always remove the redundant sign-extension in this
+ case we still expand mulsidi3 for DMUL. */
+ if (ISA_HAS_DMUL3)
+ return signed_p ? gen_mulsidi3_64bit_dmul : NULL;
+ if (TARGET_FIX_R4000)
+ return NULL;
+ return signed_p ? gen_mulsidi3_64bit : gen_umulsidi3_64bit;
+ }
+ else
+ {
+ if (TARGET_FIX_R4000 && !ISA_HAS_DSP)
+ return signed_p ? gen_mulsidi3_32bit_r4000 : gen_umulsidi3_32bit_r4000;
+ return signed_p ? gen_mulsidi3_32bit : gen_umulsidi3_32bit;
+ }
+}
+
+/* Return the size in bytes of the trampoline code, padded to
+ TRAMPOLINE_ALIGNMENT bits. The static chain pointer and target
+ function address immediately follow. */
+
+int
+mips_trampoline_code_size (void)
+{
+ if (TARGET_USE_PIC_FN_ADDR_REG)
+ return 4 * 4;
+ else if (ptr_mode == DImode)
+ return 8 * 4;
+ else if (ISA_HAS_LOAD_DELAY)
+ return 6 * 4;
+ else
+ return 4 * 4;
+}
+
+/* Implement TARGET_TRAMPOLINE_INIT. */
+
+static void
+mips_trampoline_init (rtx m_tramp, tree fndecl, rtx chain_value)
+{
+ rtx addr, end_addr, high, low, opcode, mem;
+ rtx trampoline[8];
+ unsigned int i, j;
+ HOST_WIDE_INT end_addr_offset, static_chain_offset, target_function_offset;
+
+ /* Work out the offsets of the pointers from the start of the
+ trampoline code. */
+ end_addr_offset = mips_trampoline_code_size ();
+ static_chain_offset = end_addr_offset;
+ target_function_offset = static_chain_offset + GET_MODE_SIZE (ptr_mode);
+
+ /* Get pointers to the beginning and end of the code block. */
+ addr = force_reg (Pmode, XEXP (m_tramp, 0));
+ end_addr = mips_force_binary (Pmode, PLUS, addr, GEN_INT (end_addr_offset));
+
+#define OP(X) gen_int_mode (X, SImode)
+
+ /* Build up the code in TRAMPOLINE. */
+ i = 0;
+ if (TARGET_USE_PIC_FN_ADDR_REG)
+ {
+ /* $25 contains the address of the trampoline. Emit code of the form:
+
+ l[wd] $1, target_function_offset($25)
+ l[wd] $static_chain, static_chain_offset($25)
+ jr $1
+ move $25,$1. */
+ trampoline[i++] = OP (MIPS_LOAD_PTR (AT_REGNUM,
+ target_function_offset,
+ PIC_FUNCTION_ADDR_REGNUM));
+ trampoline[i++] = OP (MIPS_LOAD_PTR (STATIC_CHAIN_REGNUM,
+ static_chain_offset,
+ PIC_FUNCTION_ADDR_REGNUM));
+ trampoline[i++] = OP (MIPS_JR (AT_REGNUM));
+ trampoline[i++] = OP (MIPS_MOVE (PIC_FUNCTION_ADDR_REGNUM, AT_REGNUM));
+ }
+ else if (ptr_mode == DImode)
+ {
+ /* It's too cumbersome to create the full 64-bit address, so let's
+ instead use:
+
+ move $1, $31
+ bal 1f
+ nop
+ 1: l[wd] $25, target_function_offset - 12($31)
+ l[wd] $static_chain, static_chain_offset - 12($31)
+ jr $25
+ move $31, $1
+
+ where 12 is the offset of "1:" from the start of the code block. */
+ trampoline[i++] = OP (MIPS_MOVE (AT_REGNUM, RETURN_ADDR_REGNUM));
+ trampoline[i++] = OP (MIPS_BAL (1));
+ trampoline[i++] = OP (MIPS_NOP);
+ trampoline[i++] = OP (MIPS_LOAD_PTR (PIC_FUNCTION_ADDR_REGNUM,
+ target_function_offset - 12,
+ RETURN_ADDR_REGNUM));
+ trampoline[i++] = OP (MIPS_LOAD_PTR (STATIC_CHAIN_REGNUM,
+ static_chain_offset - 12,
+ RETURN_ADDR_REGNUM));
+ trampoline[i++] = OP (MIPS_JR (PIC_FUNCTION_ADDR_REGNUM));
+ trampoline[i++] = OP (MIPS_MOVE (RETURN_ADDR_REGNUM, AT_REGNUM));
+ }
+ else
+ {
+ /* If the target has load delays, emit:
+
+ lui $1, %hi(end_addr)
+ lw $25, %lo(end_addr + ...)($1)
+ lw $static_chain, %lo(end_addr + ...)($1)
+ jr $25
+ nop
+
+ Otherwise emit:
+
+ lui $1, %hi(end_addr)
+ lw $25, %lo(end_addr + ...)($1)
+ jr $25
+ lw $static_chain, %lo(end_addr + ...)($1). */
+
+ /* Split END_ADDR into %hi and %lo values. Trampolines are aligned
+ to 64 bits, so the %lo value will have the bottom 3 bits clear. */
+ high = expand_simple_binop (SImode, PLUS, end_addr, GEN_INT (0x8000),
+ NULL, false, OPTAB_WIDEN);
+ high = expand_simple_binop (SImode, LSHIFTRT, high, GEN_INT (16),
+ NULL, false, OPTAB_WIDEN);
+ low = convert_to_mode (SImode, gen_lowpart (HImode, end_addr), true);
+
+ /* Emit the LUI. */
+ opcode = OP (MIPS_LUI (AT_REGNUM, 0));
+ trampoline[i++] = expand_simple_binop (SImode, IOR, opcode, high,
+ NULL, false, OPTAB_WIDEN);
+
+ /* Emit the load of the target function. */
+ opcode = OP (MIPS_LOAD_PTR (PIC_FUNCTION_ADDR_REGNUM,
+ target_function_offset - end_addr_offset,
+ AT_REGNUM));
+ trampoline[i++] = expand_simple_binop (SImode, IOR, opcode, low,
+ NULL, false, OPTAB_WIDEN);
+
+ /* Emit the JR here, if we can. */
+ if (!ISA_HAS_LOAD_DELAY)
+ trampoline[i++] = OP (MIPS_JR (PIC_FUNCTION_ADDR_REGNUM));
+
+ /* Emit the load of the static chain register. */
+ opcode = OP (MIPS_LOAD_PTR (STATIC_CHAIN_REGNUM,
+ static_chain_offset - end_addr_offset,
+ AT_REGNUM));
+ trampoline[i++] = expand_simple_binop (SImode, IOR, opcode, low,
+ NULL, false, OPTAB_WIDEN);
+
+ /* Emit the JR, if we couldn't above. */
+ if (ISA_HAS_LOAD_DELAY)
+ {
+ trampoline[i++] = OP (MIPS_JR (PIC_FUNCTION_ADDR_REGNUM));
+ trampoline[i++] = OP (MIPS_NOP);
+ }
+ }
+
+#undef OP
+
+ /* Copy the trampoline code. Leave any padding uninitialized. */
+ for (j = 0; j < i; j++)
+ {
+ mem = adjust_address (m_tramp, SImode, j * GET_MODE_SIZE (SImode));
+ mips_emit_move (mem, trampoline[j]);
+ }
+
+ /* Set up the static chain pointer field. */
+ mem = adjust_address (m_tramp, ptr_mode, static_chain_offset);
+ mips_emit_move (mem, chain_value);
+
+ /* Set up the target function field. */
+ mem = adjust_address (m_tramp, ptr_mode, target_function_offset);
+ mips_emit_move (mem, XEXP (DECL_RTL (fndecl), 0));
+
+ /* Flush the code part of the trampoline. */
+ emit_insn (gen_add3_insn (end_addr, addr, GEN_INT (TRAMPOLINE_SIZE)));
+ emit_insn (gen_clear_cache (addr, end_addr));
+}
+
+/* Implement FUNCTION_PROFILER. */
+
+void mips_function_profiler (FILE *file)
+{
+ if (TARGET_MIPS16)
+ sorry ("mips16 function profiling");
+ if (TARGET_LONG_CALLS)
+ {
+ /* For TARGET_LONG_CALLS use $3 for the address of _mcount. */
+ if (Pmode == DImode)
+ fprintf (file, "\tdla\t%s,_mcount\n", reg_names[3]);
+ else
+ fprintf (file, "\tla\t%s,_mcount\n", reg_names[3]);
+ }
+ mips_push_asm_switch (&mips_noat);
+ fprintf (file, "\tmove\t%s,%s\t\t# save current return address\n",
+ reg_names[AT_REGNUM], reg_names[RETURN_ADDR_REGNUM]);
+ /* _mcount treats $2 as the static chain register. */
+ if (cfun->static_chain_decl != NULL)
+ fprintf (file, "\tmove\t%s,%s\n", reg_names[2],
+ reg_names[STATIC_CHAIN_REGNUM]);
+ if (TARGET_MCOUNT_RA_ADDRESS)
+ {
+ /* If TARGET_MCOUNT_RA_ADDRESS load $12 with the address of the
+ ra save location. */
+ if (cfun->machine->frame.ra_fp_offset == 0)
+ /* ra not saved, pass zero. */
+ fprintf (file, "\tmove\t%s,%s\n", reg_names[12], reg_names[0]);
+ else
+ fprintf (file, "\t%s\t%s," HOST_WIDE_INT_PRINT_DEC "(%s)\n",
+ Pmode == DImode ? "dla" : "la", reg_names[12],
+ cfun->machine->frame.ra_fp_offset,
+ reg_names[STACK_POINTER_REGNUM]);
+ }
+ if (!TARGET_NEWABI)
+ fprintf (file,
+ "\t%s\t%s,%s,%d\t\t# _mcount pops 2 words from stack\n",
+ TARGET_64BIT ? "dsubu" : "subu",
+ reg_names[STACK_POINTER_REGNUM],
+ reg_names[STACK_POINTER_REGNUM],
+ Pmode == DImode ? 16 : 8);
+
+ if (TARGET_LONG_CALLS)
+ fprintf (file, "\tjalr\t%s\n", reg_names[3]);
+ else
+ fprintf (file, "\tjal\t_mcount\n");
+ mips_pop_asm_switch (&mips_noat);
+ /* _mcount treats $2 as the static chain register. */
+ if (cfun->static_chain_decl != NULL)
+ fprintf (file, "\tmove\t%s,%s\n", reg_names[STATIC_CHAIN_REGNUM],
+ reg_names[2]);
+}
+
+/* Implement TARGET_SHIFT_TRUNCATION_MASK. We want to keep the default
+ behaviour of TARGET_SHIFT_TRUNCATION_MASK for non-vector modes even
+ when TARGET_LOONGSON_VECTORS is true. */
+
+static unsigned HOST_WIDE_INT
+mips_shift_truncation_mask (enum machine_mode mode)
+{
+ if (TARGET_LOONGSON_VECTORS && VECTOR_MODE_P (mode))
+ return 0;
+
+ return GET_MODE_BITSIZE (mode) - 1;
+}
+
+
+/* Initialize the GCC target structure. */
+#undef TARGET_ASM_ALIGNED_HI_OP
+#define TARGET_ASM_ALIGNED_HI_OP "\t.half\t"
+#undef TARGET_ASM_ALIGNED_SI_OP
+#define TARGET_ASM_ALIGNED_SI_OP "\t.word\t"
+#undef TARGET_ASM_ALIGNED_DI_OP
+#define TARGET_ASM_ALIGNED_DI_OP "\t.dword\t"
+
+#undef TARGET_OPTION_OVERRIDE
+#define TARGET_OPTION_OVERRIDE mips_option_override
+#undef TARGET_OPTION_OPTIMIZATION_TABLE
+#define TARGET_OPTION_OPTIMIZATION_TABLE mips_option_optimization_table
+
+#undef TARGET_LEGITIMIZE_ADDRESS
+#define TARGET_LEGITIMIZE_ADDRESS mips_legitimize_address
+
+#undef TARGET_ASM_FUNCTION_PROLOGUE
+#define TARGET_ASM_FUNCTION_PROLOGUE mips_output_function_prologue
+#undef TARGET_ASM_FUNCTION_EPILOGUE
+#define TARGET_ASM_FUNCTION_EPILOGUE mips_output_function_epilogue
+#undef TARGET_ASM_SELECT_RTX_SECTION
+#define TARGET_ASM_SELECT_RTX_SECTION mips_select_rtx_section
+#undef TARGET_ASM_FUNCTION_RODATA_SECTION
+#define TARGET_ASM_FUNCTION_RODATA_SECTION mips_function_rodata_section
+
+#undef TARGET_SCHED_INIT
+#define TARGET_SCHED_INIT mips_sched_init
+#undef TARGET_SCHED_REORDER
+#define TARGET_SCHED_REORDER mips_sched_reorder
+#undef TARGET_SCHED_REORDER2
+#define TARGET_SCHED_REORDER2 mips_sched_reorder2
+#undef TARGET_SCHED_VARIABLE_ISSUE
+#define TARGET_SCHED_VARIABLE_ISSUE mips_variable_issue
+#undef TARGET_SCHED_ADJUST_COST
+#define TARGET_SCHED_ADJUST_COST mips_adjust_cost
+#undef TARGET_SCHED_ISSUE_RATE
+#define TARGET_SCHED_ISSUE_RATE mips_issue_rate
+#undef TARGET_SCHED_INIT_DFA_POST_CYCLE_INSN
+#define TARGET_SCHED_INIT_DFA_POST_CYCLE_INSN mips_init_dfa_post_cycle_insn
+#undef TARGET_SCHED_DFA_POST_ADVANCE_CYCLE
+#define TARGET_SCHED_DFA_POST_ADVANCE_CYCLE mips_dfa_post_advance_cycle
+#undef TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD
+#define TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD \
+ mips_multipass_dfa_lookahead
+#undef TARGET_SMALL_REGISTER_CLASSES_FOR_MODE_P
+#define TARGET_SMALL_REGISTER_CLASSES_FOR_MODE_P \
+ mips_small_register_classes_for_mode_p
+
+#undef TARGET_DEFAULT_TARGET_FLAGS
+#define TARGET_DEFAULT_TARGET_FLAGS \
+ (TARGET_DEFAULT \
+ | TARGET_CPU_DEFAULT \
+ | TARGET_ENDIAN_DEFAULT \
+ | TARGET_FP_EXCEPTIONS_DEFAULT \
+ | MASK_CHECK_ZERO_DIV \
+ | MASK_FUSED_MADD)
+#undef TARGET_HANDLE_OPTION
+#define TARGET_HANDLE_OPTION mips_handle_option
+
+#undef TARGET_FUNCTION_OK_FOR_SIBCALL
+#define TARGET_FUNCTION_OK_FOR_SIBCALL mips_function_ok_for_sibcall
+
+#undef TARGET_INSERT_ATTRIBUTES
+#define TARGET_INSERT_ATTRIBUTES mips_insert_attributes
+#undef TARGET_MERGE_DECL_ATTRIBUTES
+#define TARGET_MERGE_DECL_ATTRIBUTES mips_merge_decl_attributes
+#undef TARGET_SET_CURRENT_FUNCTION
+#define TARGET_SET_CURRENT_FUNCTION mips_set_current_function
+
+#undef TARGET_VALID_POINTER_MODE
+#define TARGET_VALID_POINTER_MODE mips_valid_pointer_mode
+#undef TARGET_REGISTER_MOVE_COST
+#define TARGET_REGISTER_MOVE_COST mips_register_move_cost
+#undef TARGET_MEMORY_MOVE_COST
+#define TARGET_MEMORY_MOVE_COST mips_memory_move_cost
+#undef TARGET_RTX_COSTS
+#define TARGET_RTX_COSTS mips_rtx_costs
+#undef TARGET_ADDRESS_COST
+#define TARGET_ADDRESS_COST mips_address_cost
+
+#undef TARGET_IN_SMALL_DATA_P
+#define TARGET_IN_SMALL_DATA_P mips_in_small_data_p
+
+#undef TARGET_MACHINE_DEPENDENT_REORG
+#define TARGET_MACHINE_DEPENDENT_REORG mips_reorg
+
+#undef TARGET_PREFERRED_RELOAD_CLASS
+#define TARGET_PREFERRED_RELOAD_CLASS mips_preferred_reload_class
+
+#undef TARGET_ASM_FILE_START
+#define TARGET_ASM_FILE_START mips_file_start
+#undef TARGET_ASM_FILE_START_FILE_DIRECTIVE
+#define TARGET_ASM_FILE_START_FILE_DIRECTIVE true
+
+#undef TARGET_INIT_LIBFUNCS
+#define TARGET_INIT_LIBFUNCS mips_init_libfuncs
+
+#undef TARGET_BUILD_BUILTIN_VA_LIST
+#define TARGET_BUILD_BUILTIN_VA_LIST mips_build_builtin_va_list
+#undef TARGET_EXPAND_BUILTIN_VA_START
+#define TARGET_EXPAND_BUILTIN_VA_START mips_va_start
+#undef TARGET_GIMPLIFY_VA_ARG_EXPR
+#define TARGET_GIMPLIFY_VA_ARG_EXPR mips_gimplify_va_arg_expr
+
+#undef TARGET_PROMOTE_FUNCTION_MODE
+#define TARGET_PROMOTE_FUNCTION_MODE default_promote_function_mode_always_promote
+#undef TARGET_PROMOTE_PROTOTYPES
+#define TARGET_PROMOTE_PROTOTYPES hook_bool_const_tree_true
+
+#undef TARGET_RETURN_IN_MEMORY
+#define TARGET_RETURN_IN_MEMORY mips_return_in_memory
+#undef TARGET_RETURN_IN_MSB
+#define TARGET_RETURN_IN_MSB mips_return_in_msb
+
+#undef TARGET_ASM_OUTPUT_MI_THUNK
+#define TARGET_ASM_OUTPUT_MI_THUNK mips_output_mi_thunk
+#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
+#define TARGET_ASM_CAN_OUTPUT_MI_THUNK hook_bool_const_tree_hwi_hwi_const_tree_true
+
+#undef TARGET_PRINT_OPERAND
+#define TARGET_PRINT_OPERAND mips_print_operand
+#undef TARGET_PRINT_OPERAND_ADDRESS
+#define TARGET_PRINT_OPERAND_ADDRESS mips_print_operand_address
+#undef TARGET_PRINT_OPERAND_PUNCT_VALID_P
+#define TARGET_PRINT_OPERAND_PUNCT_VALID_P mips_print_operand_punct_valid_p
+
+#undef TARGET_SETUP_INCOMING_VARARGS
+#define TARGET_SETUP_INCOMING_VARARGS mips_setup_incoming_varargs
+#undef TARGET_STRICT_ARGUMENT_NAMING
+#define TARGET_STRICT_ARGUMENT_NAMING mips_strict_argument_naming
+#undef TARGET_MUST_PASS_IN_STACK
+#define TARGET_MUST_PASS_IN_STACK must_pass_in_stack_var_size
+#undef TARGET_PASS_BY_REFERENCE
+#define TARGET_PASS_BY_REFERENCE mips_pass_by_reference
+#undef TARGET_CALLEE_COPIES
+#define TARGET_CALLEE_COPIES mips_callee_copies
+#undef TARGET_ARG_PARTIAL_BYTES
+#define TARGET_ARG_PARTIAL_BYTES mips_arg_partial_bytes
+#undef TARGET_FUNCTION_ARG
+#define TARGET_FUNCTION_ARG mips_function_arg
+#undef TARGET_FUNCTION_ARG_ADVANCE
+#define TARGET_FUNCTION_ARG_ADVANCE mips_function_arg_advance
+#undef TARGET_FUNCTION_ARG_BOUNDARY
+#define TARGET_FUNCTION_ARG_BOUNDARY mips_function_arg_boundary
+
+#undef TARGET_MODE_REP_EXTENDED
+#define TARGET_MODE_REP_EXTENDED mips_mode_rep_extended
+
+#undef TARGET_VECTOR_MODE_SUPPORTED_P
+#define TARGET_VECTOR_MODE_SUPPORTED_P mips_vector_mode_supported_p
+
+#undef TARGET_SCALAR_MODE_SUPPORTED_P
+#define TARGET_SCALAR_MODE_SUPPORTED_P mips_scalar_mode_supported_p
+
+#undef TARGET_VECTORIZE_PREFERRED_SIMD_MODE
+#define TARGET_VECTORIZE_PREFERRED_SIMD_MODE mips_preferred_simd_mode
+
+#undef TARGET_INIT_BUILTINS
+#define TARGET_INIT_BUILTINS mips_init_builtins
+#undef TARGET_BUILTIN_DECL
+#define TARGET_BUILTIN_DECL mips_builtin_decl
+#undef TARGET_EXPAND_BUILTIN
+#define TARGET_EXPAND_BUILTIN mips_expand_builtin
+
+#undef TARGET_HAVE_TLS
+#define TARGET_HAVE_TLS HAVE_AS_TLS
+
+#undef TARGET_CANNOT_FORCE_CONST_MEM
+#define TARGET_CANNOT_FORCE_CONST_MEM mips_cannot_force_const_mem
+
+#undef TARGET_ENCODE_SECTION_INFO
+#define TARGET_ENCODE_SECTION_INFO mips_encode_section_info
+
+#undef TARGET_ATTRIBUTE_TABLE
+#define TARGET_ATTRIBUTE_TABLE mips_attribute_table
+/* All our function attributes are related to how out-of-line copies should
+ be compiled or called. They don't in themselves prevent inlining. */
+#undef TARGET_FUNCTION_ATTRIBUTE_INLINABLE_P
+#define TARGET_FUNCTION_ATTRIBUTE_INLINABLE_P hook_bool_const_tree_true
+
+#undef TARGET_EXTRA_LIVE_ON_ENTRY
+#define TARGET_EXTRA_LIVE_ON_ENTRY mips_extra_live_on_entry
+
+#undef TARGET_USE_BLOCKS_FOR_CONSTANT_P
+#define TARGET_USE_BLOCKS_FOR_CONSTANT_P mips_use_blocks_for_constant_p
+#undef TARGET_USE_ANCHORS_FOR_SYMBOL_P
+#define TARGET_USE_ANCHORS_FOR_SYMBOL_P mips_use_anchors_for_symbol_p
+
+#undef TARGET_COMP_TYPE_ATTRIBUTES
+#define TARGET_COMP_TYPE_ATTRIBUTES mips_comp_type_attributes
+
+#ifdef HAVE_AS_DTPRELWORD
+#undef TARGET_ASM_OUTPUT_DWARF_DTPREL
+#define TARGET_ASM_OUTPUT_DWARF_DTPREL mips_output_dwarf_dtprel
+#endif
+#undef TARGET_DWARF_REGISTER_SPAN
+#define TARGET_DWARF_REGISTER_SPAN mips_dwarf_register_span
+
+#undef TARGET_IRA_COVER_CLASSES
+#define TARGET_IRA_COVER_CLASSES mips_ira_cover_classes
+
+#undef TARGET_ASM_FINAL_POSTSCAN_INSN
+#define TARGET_ASM_FINAL_POSTSCAN_INSN mips_final_postscan_insn
+
+#undef TARGET_LEGITIMATE_ADDRESS_P
+#define TARGET_LEGITIMATE_ADDRESS_P mips_legitimate_address_p
+
+#undef TARGET_FRAME_POINTER_REQUIRED
+#define TARGET_FRAME_POINTER_REQUIRED mips_frame_pointer_required
+
+#undef TARGET_CAN_ELIMINATE
+#define TARGET_CAN_ELIMINATE mips_can_eliminate
+
+#undef TARGET_CONDITIONAL_REGISTER_USAGE
+#define TARGET_CONDITIONAL_REGISTER_USAGE mips_conditional_register_usage
+
+#undef TARGET_TRAMPOLINE_INIT
+#define TARGET_TRAMPOLINE_INIT mips_trampoline_init
+
+#undef TARGET_ASM_OUTPUT_SOURCE_FILENAME
+#define TARGET_ASM_OUTPUT_SOURCE_FILENAME mips_output_filename
+
+#undef TARGET_SHIFT_TRUNCATION_MASK
+#define TARGET_SHIFT_TRUNCATION_MASK mips_shift_truncation_mask
+
+struct gcc_target targetm = TARGET_INITIALIZER;
+
+#include "gt-mips.h"
diff --git a/gcc/config/mips/mips.h b/gcc/config/mips/mips.h
new file mode 100644
index 000000000..9600dcb28
--- /dev/null
+++ b/gcc/config/mips/mips.h
@@ -0,0 +1,2984 @@
+/* Definitions of target machine for GNU compiler. MIPS version.
+ Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998
+ 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007, 2008, 2009, 2010, 2011
+ Free Software Foundation, Inc.
+ Contributed by A. Lichnewsky (lich@inria.inria.fr).
+ Changed by Michael Meissner (meissner@osf.org).
+ 64-bit r4000 support by Ian Lance Taylor (ian@cygnus.com) and
+ Brendan Eich (brendan@microunity.com).
+
+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/>. */
+
+
+#include "config/vxworks-dummy.h"
+
+#ifdef GENERATOR_FILE
+/* This is used in some insn conditions, so needs to be declared, but
+ does not need to be defined. */
+extern int target_flags_explicit;
+#endif
+
+/* MIPS external variables defined in mips.c. */
+
+/* Which ABI to use. ABI_32 (original 32, or o32), ABI_N32 (n32),
+ ABI_64 (n64) are all defined by SGI. ABI_O64 is o32 extended
+ to work on a 64-bit machine. */
+
+#define ABI_32 0
+#define ABI_N32 1
+#define ABI_64 2
+#define ABI_EABI 3
+#define ABI_O64 4
+
+/* Masks that affect tuning.
+
+ PTF_AVOID_BRANCHLIKELY
+ Set if it is usually not profitable to use branch-likely instructions
+ for this target, typically because the branches are always predicted
+ taken and so incur a large overhead when not taken. */
+#define PTF_AVOID_BRANCHLIKELY 0x1
+
+/* Information about one recognized processor. Defined here for the
+ benefit of TARGET_CPU_CPP_BUILTINS. */
+struct mips_cpu_info {
+ /* The 'canonical' name of the processor as far as GCC is concerned.
+ It's typically a manufacturer's prefix followed by a numerical
+ designation. It should be lowercase. */
+ const char *name;
+
+ /* The internal processor number that most closely matches this
+ entry. Several processors can have the same value, if there's no
+ difference between them from GCC's point of view. */
+ enum processor cpu;
+
+ /* The ISA level that the processor implements. */
+ int isa;
+
+ /* A mask of PTF_* values. */
+ unsigned int tune_flags;
+};
+
+/* Enumerates the setting of the -mcode-readable option. */
+enum mips_code_readable_setting {
+ CODE_READABLE_NO,
+ CODE_READABLE_PCREL,
+ CODE_READABLE_YES
+};
+
+/* Macros to silence warnings about numbers being signed in traditional
+ C and unsigned in ISO C when compiled on 32-bit hosts. */
+
+#define BITMASK_HIGH (((unsigned long)1) << 31) /* 0x80000000 */
+#define BITMASK_UPPER16 ((unsigned long)0xffff << 16) /* 0xffff0000 */
+#define BITMASK_LOWER16 ((unsigned long)0xffff) /* 0x0000ffff */
+
+
+/* Run-time compilation parameters selecting different hardware subsets. */
+
+/* True if we are generating position-independent VxWorks RTP code. */
+#define TARGET_RTP_PIC (TARGET_VXWORKS_RTP && flag_pic)
+
+/* True if the output file is marked as ".abicalls; .option pic0"
+ (-call_nonpic). */
+#define TARGET_ABICALLS_PIC0 \
+ (TARGET_ABSOLUTE_ABICALLS && TARGET_PLT)
+
+/* True if the output file is marked as ".abicalls; .option pic2" (-KPIC). */
+#define TARGET_ABICALLS_PIC2 \
+ (TARGET_ABICALLS && !TARGET_ABICALLS_PIC0)
+
+/* True if the call patterns should be split into a jalr followed by
+ an instruction to restore $gp. It is only safe to split the load
+ from the call when every use of $gp is explicit.
+
+ See mips_must_initialize_gp_p for details about how we manage the
+ global pointer. */
+
+#define TARGET_SPLIT_CALLS \
+ (TARGET_EXPLICIT_RELOCS && TARGET_CALL_CLOBBERED_GP && epilogue_completed)
+
+/* True if we're generating a form of -mabicalls in which we can use
+ operators like %hi and %lo to refer to locally-binding symbols.
+ We can only do this for -mno-shared, and only then if we can use
+ relocation operations instead of assembly macros. It isn't really
+ worth using absolute sequences for 64-bit symbols because GOT
+ accesses are so much shorter. */
+
+#define TARGET_ABSOLUTE_ABICALLS \
+ (TARGET_ABICALLS \
+ && !TARGET_SHARED \
+ && TARGET_EXPLICIT_RELOCS \
+ && !ABI_HAS_64BIT_SYMBOLS)
+
+/* True if we can optimize sibling calls. For simplicity, we only
+ handle cases in which call_insn_operand will reject invalid
+ sibcall addresses. There are two cases in which this isn't true:
+
+ - TARGET_MIPS16. call_insn_operand accepts constant addresses
+ but there is no direct jump instruction. It isn't worth
+ using sibling calls in this case anyway; they would usually
+ be longer than normal calls.
+
+ - TARGET_USE_GOT && !TARGET_EXPLICIT_RELOCS. call_insn_operand
+ accepts global constants, but all sibcalls must be indirect. */
+#define TARGET_SIBCALLS \
+ (!TARGET_MIPS16 && (!TARGET_USE_GOT || TARGET_EXPLICIT_RELOCS))
+
+/* True if we need to use a global offset table to access some symbols. */
+#define TARGET_USE_GOT (TARGET_ABICALLS || TARGET_RTP_PIC)
+
+/* True if TARGET_USE_GOT and if $gp is a call-clobbered register. */
+#define TARGET_CALL_CLOBBERED_GP (TARGET_ABICALLS && TARGET_OLDABI)
+
+/* True if TARGET_USE_GOT and if $gp is a call-saved register. */
+#define TARGET_CALL_SAVED_GP (TARGET_USE_GOT && !TARGET_CALL_CLOBBERED_GP)
+
+/* True if we should use .cprestore to store to the cprestore slot.
+
+ We continue to use .cprestore for explicit-reloc code so that JALs
+ inside inline asms will work correctly. */
+#define TARGET_CPRESTORE_DIRECTIVE \
+ (TARGET_ABICALLS_PIC2 && !TARGET_MIPS16)
+
+/* True if we can use the J and JAL instructions. */
+#define TARGET_ABSOLUTE_JUMPS \
+ (!flag_pic || TARGET_ABSOLUTE_ABICALLS)
+
+/* True if indirect calls must use register class PIC_FN_ADDR_REG.
+ This is true for both the PIC and non-PIC VxWorks RTP modes. */
+#define TARGET_USE_PIC_FN_ADDR_REG (TARGET_ABICALLS || TARGET_VXWORKS_RTP)
+
+/* True if .gpword or .gpdword should be used for switch tables.
+
+ Although GAS does understand .gpdword, the SGI linker mishandles
+ the relocations GAS generates (R_MIPS_GPREL32 followed by R_MIPS_64).
+ We therefore disable GP-relative switch tables for n64 on IRIX targets. */
+#define TARGET_GPWORD \
+ (TARGET_ABICALLS \
+ && !TARGET_ABSOLUTE_ABICALLS \
+ && !(mips_abi == ABI_64 && TARGET_IRIX6))
+
+/* True if the output must have a writable .eh_frame.
+ See ASM_PREFERRED_EH_DATA_FORMAT for details. */
+#ifdef HAVE_LD_PERSONALITY_RELAXATION
+#define TARGET_WRITABLE_EH_FRAME 0
+#else
+#define TARGET_WRITABLE_EH_FRAME (flag_pic && TARGET_SHARED)
+#endif
+
+/* Test the assembler to set ISA_HAS_DSP_MULT to DSP Rev 1 or 2. */
+#ifdef HAVE_AS_DSPR1_MULT
+#define ISA_HAS_DSP_MULT ISA_HAS_DSP
+#else
+#define ISA_HAS_DSP_MULT ISA_HAS_DSPR2
+#endif
+
+/* Generate mips16 code */
+#define TARGET_MIPS16 ((target_flags & MASK_MIPS16) != 0)
+/* Generate mips16e code. Default 16bit ASE for mips32* and mips64* */
+#define GENERATE_MIPS16E (TARGET_MIPS16 && mips_isa >= 32)
+/* Generate mips16e register save/restore sequences. */
+#define GENERATE_MIPS16E_SAVE_RESTORE (GENERATE_MIPS16E && mips_abi == ABI_32)
+
+/* True if we're generating a form of MIPS16 code in which general
+ text loads are allowed. */
+#define TARGET_MIPS16_TEXT_LOADS \
+ (TARGET_MIPS16 && mips_code_readable == CODE_READABLE_YES)
+
+/* True if we're generating a form of MIPS16 code in which PC-relative
+ loads are allowed. */
+#define TARGET_MIPS16_PCREL_LOADS \
+ (TARGET_MIPS16 && mips_code_readable >= CODE_READABLE_PCREL)
+
+/* Generic ISA defines. */
+#define ISA_MIPS1 (mips_isa == 1)
+#define ISA_MIPS2 (mips_isa == 2)
+#define ISA_MIPS3 (mips_isa == 3)
+#define ISA_MIPS4 (mips_isa == 4)
+#define ISA_MIPS32 (mips_isa == 32)
+#define ISA_MIPS32R2 (mips_isa == 33)
+#define ISA_MIPS64 (mips_isa == 64)
+#define ISA_MIPS64R2 (mips_isa == 65)
+
+/* Architecture target defines. */
+#define TARGET_LOONGSON_2E (mips_arch == PROCESSOR_LOONGSON_2E)
+#define TARGET_LOONGSON_2F (mips_arch == PROCESSOR_LOONGSON_2F)
+#define TARGET_LOONGSON_2EF (TARGET_LOONGSON_2E || TARGET_LOONGSON_2F)
+#define TARGET_LOONGSON_3A (mips_arch == PROCESSOR_LOONGSON_3A)
+#define TARGET_MIPS3900 (mips_arch == PROCESSOR_R3900)
+#define TARGET_MIPS4000 (mips_arch == PROCESSOR_R4000)
+#define TARGET_MIPS4120 (mips_arch == PROCESSOR_R4120)
+#define TARGET_MIPS4130 (mips_arch == PROCESSOR_R4130)
+#define TARGET_MIPS5400 (mips_arch == PROCESSOR_R5400)
+#define TARGET_MIPS5500 (mips_arch == PROCESSOR_R5500)
+#define TARGET_MIPS7000 (mips_arch == PROCESSOR_R7000)
+#define TARGET_MIPS9000 (mips_arch == PROCESSOR_R9000)
+#define TARGET_OCTEON (mips_arch == PROCESSOR_OCTEON)
+#define TARGET_SB1 (mips_arch == PROCESSOR_SB1 \
+ || mips_arch == PROCESSOR_SB1A)
+#define TARGET_SR71K (mips_arch == PROCESSOR_SR71000)
+
+/* Scheduling target defines. */
+#define TUNE_20KC (mips_tune == PROCESSOR_20KC)
+#define TUNE_24K (mips_tune == PROCESSOR_24KC \
+ || mips_tune == PROCESSOR_24KF2_1 \
+ || mips_tune == PROCESSOR_24KF1_1)
+#define TUNE_74K (mips_tune == PROCESSOR_74KC \
+ || mips_tune == PROCESSOR_74KF2_1 \
+ || mips_tune == PROCESSOR_74KF1_1 \
+ || mips_tune == PROCESSOR_74KF3_2)
+#define TUNE_LOONGSON_2EF (mips_tune == PROCESSOR_LOONGSON_2E \
+ || mips_tune == PROCESSOR_LOONGSON_2F)
+#define TUNE_LOONGSON_3A (mips_tune == PROCESSOR_LOONGSON_3A)
+#define TUNE_MIPS3000 (mips_tune == PROCESSOR_R3000)
+#define TUNE_MIPS3900 (mips_tune == PROCESSOR_R3900)
+#define TUNE_MIPS4000 (mips_tune == PROCESSOR_R4000)
+#define TUNE_MIPS4120 (mips_tune == PROCESSOR_R4120)
+#define TUNE_MIPS4130 (mips_tune == PROCESSOR_R4130)
+#define TUNE_MIPS5000 (mips_tune == PROCESSOR_R5000)
+#define TUNE_MIPS5400 (mips_tune == PROCESSOR_R5400)
+#define TUNE_MIPS5500 (mips_tune == PROCESSOR_R5500)
+#define TUNE_MIPS6000 (mips_tune == PROCESSOR_R6000)
+#define TUNE_MIPS7000 (mips_tune == PROCESSOR_R7000)
+#define TUNE_MIPS9000 (mips_tune == PROCESSOR_R9000)
+#define TUNE_OCTEON (mips_tune == PROCESSOR_OCTEON)
+#define TUNE_SB1 (mips_tune == PROCESSOR_SB1 \
+ || mips_tune == PROCESSOR_SB1A)
+
+/* Whether vector modes and intrinsics for ST Microelectronics
+ Loongson-2E/2F processors should be enabled. In o32 pairs of
+ floating-point registers provide 64-bit values. */
+#define TARGET_LOONGSON_VECTORS (TARGET_HARD_FLOAT_ABI \
+ && (TARGET_LOONGSON_2EF \
+ || TARGET_LOONGSON_3A))
+
+/* True if the pre-reload scheduler should try to create chains of
+ multiply-add or multiply-subtract instructions. For example,
+ suppose we have:
+
+ t1 = a * b
+ t2 = t1 + c * d
+ t3 = e * f
+ t4 = t3 - g * h
+
+ t1 will have a higher priority than t2 and t3 will have a higher
+ priority than t4. However, before reload, there is no dependence
+ between t1 and t3, and they can often have similar priorities.
+ The scheduler will then tend to prefer:
+
+ t1 = a * b
+ t3 = e * f
+ t2 = t1 + c * d
+ t4 = t3 - g * h
+
+ which stops us from making full use of macc/madd-style instructions.
+ This sort of situation occurs frequently in Fourier transforms and
+ in unrolled loops.
+
+ To counter this, the TUNE_MACC_CHAINS code will reorder the ready
+ queue so that chained multiply-add and multiply-subtract instructions
+ appear ahead of any other instruction that is likely to clobber lo.
+ In the example above, if t2 and t3 become ready at the same time,
+ the code ensures that t2 is scheduled first.
+
+ Multiply-accumulate instructions are a bigger win for some targets
+ than others, so this macro is defined on an opt-in basis. */
+#define TUNE_MACC_CHAINS (TUNE_MIPS5500 \
+ || TUNE_MIPS4120 \
+ || TUNE_MIPS4130 \
+ || TUNE_24K)
+
+#define TARGET_OLDABI (mips_abi == ABI_32 || mips_abi == ABI_O64)
+#define TARGET_NEWABI (mips_abi == ABI_N32 || mips_abi == ABI_64)
+
+/* TARGET_HARD_FLOAT and TARGET_SOFT_FLOAT reflect whether the FPU is
+ directly accessible, while the command-line options select
+ TARGET_HARD_FLOAT_ABI and TARGET_SOFT_FLOAT_ABI to reflect the ABI
+ in use. */
+#define TARGET_HARD_FLOAT (TARGET_HARD_FLOAT_ABI && !TARGET_MIPS16)
+#define TARGET_SOFT_FLOAT (TARGET_SOFT_FLOAT_ABI || TARGET_MIPS16)
+
+/* False if SC acts as a memory barrier with respect to itself,
+ otherwise a SYNC will be emitted after SC for atomic operations
+ that require ordering between the SC and following loads and
+ stores. It does not tell anything about ordering of loads and
+ stores prior to and following the SC, only about the SC itself and
+ those loads and stores follow it. */
+#define TARGET_SYNC_AFTER_SC (!TARGET_OCTEON)
+
+/* IRIX specific stuff. */
+#define TARGET_IRIX6 0
+
+/* Define preprocessor macros for the -march and -mtune options.
+ PREFIX is either _MIPS_ARCH or _MIPS_TUNE, INFO is the selected
+ processor. If INFO's canonical name is "foo", define PREFIX to
+ be "foo", and define an additional macro PREFIX_FOO. */
+#define MIPS_CPP_SET_PROCESSOR(PREFIX, INFO) \
+ do \
+ { \
+ char *macro, *p; \
+ \
+ macro = concat ((PREFIX), "_", (INFO)->name, NULL); \
+ for (p = macro; *p != 0; p++) \
+ *p = TOUPPER (*p); \
+ \
+ builtin_define (macro); \
+ builtin_define_with_value ((PREFIX), (INFO)->name, 1); \
+ free (macro); \
+ } \
+ while (0)
+
+/* Target CPU builtins. */
+#define TARGET_CPU_CPP_BUILTINS() \
+ do \
+ { \
+ /* Everyone but IRIX defines this to mips. */ \
+ if (!TARGET_IRIX6) \
+ builtin_assert ("machine=mips"); \
+ \
+ builtin_assert ("cpu=mips"); \
+ builtin_define ("__mips__"); \
+ builtin_define ("_mips"); \
+ \
+ /* We do this here because __mips is defined below and so we \
+ can't use builtin_define_std. We don't ever want to define \
+ "mips" for VxWorks because some of the VxWorks headers \
+ construct include filenames from a root directory macro, \
+ an architecture macro and a filename, where the architecture \
+ macro expands to 'mips'. If we define 'mips' to 1, the \
+ architecture macro expands to 1 as well. */ \
+ if (!flag_iso && !TARGET_VXWORKS) \
+ builtin_define ("mips"); \
+ \
+ if (TARGET_64BIT) \
+ builtin_define ("__mips64"); \
+ \
+ if (!TARGET_IRIX6) \
+ { \
+ /* Treat _R3000 and _R4000 like register-size \
+ defines, which is how they've historically \
+ been used. */ \
+ if (TARGET_64BIT) \
+ { \
+ builtin_define_std ("R4000"); \
+ builtin_define ("_R4000"); \
+ } \
+ else \
+ { \
+ builtin_define_std ("R3000"); \
+ builtin_define ("_R3000"); \
+ } \
+ } \
+ if (TARGET_FLOAT64) \
+ builtin_define ("__mips_fpr=64"); \
+ else \
+ builtin_define ("__mips_fpr=32"); \
+ \
+ if (mips_base_mips16) \
+ builtin_define ("__mips16"); \
+ \
+ if (TARGET_MIPS3D) \
+ builtin_define ("__mips3d"); \
+ \
+ if (TARGET_SMARTMIPS) \
+ builtin_define ("__mips_smartmips"); \
+ \
+ if (TARGET_DSP) \
+ { \
+ builtin_define ("__mips_dsp"); \
+ if (TARGET_DSPR2) \
+ { \
+ builtin_define ("__mips_dspr2"); \
+ builtin_define ("__mips_dsp_rev=2"); \
+ } \
+ else \
+ builtin_define ("__mips_dsp_rev=1"); \
+ } \
+ \
+ MIPS_CPP_SET_PROCESSOR ("_MIPS_ARCH", mips_arch_info); \
+ MIPS_CPP_SET_PROCESSOR ("_MIPS_TUNE", mips_tune_info); \
+ \
+ if (ISA_MIPS1) \
+ { \
+ builtin_define ("__mips=1"); \
+ builtin_define ("_MIPS_ISA=_MIPS_ISA_MIPS1"); \
+ } \
+ else if (ISA_MIPS2) \
+ { \
+ builtin_define ("__mips=2"); \
+ builtin_define ("_MIPS_ISA=_MIPS_ISA_MIPS2"); \
+ } \
+ else if (ISA_MIPS3) \
+ { \
+ builtin_define ("__mips=3"); \
+ builtin_define ("_MIPS_ISA=_MIPS_ISA_MIPS3"); \
+ } \
+ else if (ISA_MIPS4) \
+ { \
+ builtin_define ("__mips=4"); \
+ builtin_define ("_MIPS_ISA=_MIPS_ISA_MIPS4"); \
+ } \
+ else if (ISA_MIPS32) \
+ { \
+ builtin_define ("__mips=32"); \
+ builtin_define ("__mips_isa_rev=1"); \
+ builtin_define ("_MIPS_ISA=_MIPS_ISA_MIPS32"); \
+ } \
+ else if (ISA_MIPS32R2) \
+ { \
+ builtin_define ("__mips=32"); \
+ builtin_define ("__mips_isa_rev=2"); \
+ builtin_define ("_MIPS_ISA=_MIPS_ISA_MIPS32"); \
+ } \
+ else if (ISA_MIPS64) \
+ { \
+ builtin_define ("__mips=64"); \
+ builtin_define ("__mips_isa_rev=1"); \
+ builtin_define ("_MIPS_ISA=_MIPS_ISA_MIPS64"); \
+ } \
+ else if (ISA_MIPS64R2) \
+ { \
+ builtin_define ("__mips=64"); \
+ builtin_define ("__mips_isa_rev=2"); \
+ builtin_define ("_MIPS_ISA=_MIPS_ISA_MIPS64"); \
+ } \
+ \
+ switch (mips_abi) \
+ { \
+ case ABI_32: \
+ builtin_define ("_ABIO32=1"); \
+ builtin_define ("_MIPS_SIM=_ABIO32"); \
+ break; \
+ \
+ case ABI_N32: \
+ builtin_define ("_ABIN32=2"); \
+ builtin_define ("_MIPS_SIM=_ABIN32"); \
+ break; \
+ \
+ case ABI_64: \
+ builtin_define ("_ABI64=3"); \
+ builtin_define ("_MIPS_SIM=_ABI64"); \
+ break; \
+ \
+ case ABI_O64: \
+ builtin_define ("_ABIO64=4"); \
+ builtin_define ("_MIPS_SIM=_ABIO64"); \
+ break; \
+ } \
+ \
+ builtin_define_with_int_value ("_MIPS_SZINT", INT_TYPE_SIZE); \
+ builtin_define_with_int_value ("_MIPS_SZLONG", LONG_TYPE_SIZE); \
+ builtin_define_with_int_value ("_MIPS_SZPTR", POINTER_SIZE); \
+ builtin_define_with_int_value ("_MIPS_FPSET", \
+ 32 / MAX_FPRS_PER_FMT); \
+ \
+ /* These defines reflect the ABI in use, not whether the \
+ FPU is directly accessible. */ \
+ if (TARGET_NO_FLOAT) \
+ builtin_define ("__mips_no_float"); \
+ else if (TARGET_HARD_FLOAT_ABI) \
+ builtin_define ("__mips_hard_float"); \
+ else \
+ builtin_define ("__mips_soft_float"); \
+ \
+ if (TARGET_SINGLE_FLOAT) \
+ builtin_define ("__mips_single_float"); \
+ \
+ if (TARGET_PAIRED_SINGLE_FLOAT) \
+ builtin_define ("__mips_paired_single_float"); \
+ \
+ if (TARGET_BIG_ENDIAN) \
+ { \
+ builtin_define_std ("MIPSEB"); \
+ builtin_define ("_MIPSEB"); \
+ } \
+ else \
+ { \
+ builtin_define_std ("MIPSEL"); \
+ builtin_define ("_MIPSEL"); \
+ } \
+ \
+ /* Whether calls should go through $25. The separate __PIC__ \
+ macro indicates whether abicalls code might use a GOT. */ \
+ if (TARGET_ABICALLS) \
+ builtin_define ("__mips_abicalls"); \
+ \
+ /* Whether Loongson vector modes are enabled. */ \
+ if (TARGET_LOONGSON_VECTORS) \
+ builtin_define ("__mips_loongson_vector_rev"); \
+ \
+ /* Historical Octeon macro. */ \
+ if (TARGET_OCTEON) \
+ builtin_define ("__OCTEON__"); \
+ \
+ /* Macros dependent on the C dialect. */ \
+ if (preprocessing_asm_p ()) \
+ { \
+ builtin_define_std ("LANGUAGE_ASSEMBLY"); \
+ builtin_define ("_LANGUAGE_ASSEMBLY"); \
+ } \
+ else if (c_dialect_cxx ()) \
+ { \
+ builtin_define ("_LANGUAGE_C_PLUS_PLUS"); \
+ builtin_define ("__LANGUAGE_C_PLUS_PLUS"); \
+ builtin_define ("__LANGUAGE_C_PLUS_PLUS__"); \
+ } \
+ else \
+ { \
+ builtin_define_std ("LANGUAGE_C"); \
+ builtin_define ("_LANGUAGE_C"); \
+ } \
+ if (c_dialect_objc ()) \
+ { \
+ builtin_define ("_LANGUAGE_OBJECTIVE_C"); \
+ builtin_define ("__LANGUAGE_OBJECTIVE_C"); \
+ /* Bizarre, but needed at least for Irix. */ \
+ builtin_define_std ("LANGUAGE_C"); \
+ builtin_define ("_LANGUAGE_C"); \
+ } \
+ \
+ if (mips_abi == ABI_EABI) \
+ builtin_define ("__mips_eabi"); \
+ \
+ if (TARGET_CACHE_BUILTIN) \
+ builtin_define ("__GCC_HAVE_BUILTIN_MIPS_CACHE"); \
+ } \
+ while (0)
+
+/* Default target_flags if no switches are specified */
+
+#ifndef TARGET_DEFAULT
+#define TARGET_DEFAULT 0
+#endif
+
+#ifndef TARGET_CPU_DEFAULT
+#define TARGET_CPU_DEFAULT 0
+#endif
+
+#ifndef TARGET_ENDIAN_DEFAULT
+#define TARGET_ENDIAN_DEFAULT MASK_BIG_ENDIAN
+#endif
+
+#ifndef TARGET_FP_EXCEPTIONS_DEFAULT
+#define TARGET_FP_EXCEPTIONS_DEFAULT MASK_FP_EXCEPTIONS
+#endif
+
+/* 'from-abi' makes a good default: you get whatever the ABI requires. */
+#ifndef MIPS_ISA_DEFAULT
+#ifndef MIPS_CPU_STRING_DEFAULT
+#define MIPS_CPU_STRING_DEFAULT "from-abi"
+#endif
+#endif
+
+#ifdef IN_LIBGCC2
+#undef TARGET_64BIT
+/* Make this compile time constant for libgcc2 */
+#ifdef __mips64
+#define TARGET_64BIT 1
+#else
+#define TARGET_64BIT 0
+#endif
+#endif /* IN_LIBGCC2 */
+
+/* Force the call stack unwinders in unwind.inc not to be MIPS16 code
+ when compiled with hardware floating point. This is because MIPS16
+ code cannot save and restore the floating-point registers, which is
+ important if in a mixed MIPS16/non-MIPS16 environment. */
+
+#ifdef IN_LIBGCC2
+#if __mips_hard_float
+#define LIBGCC2_UNWIND_ATTRIBUTE __attribute__((__nomips16__))
+#endif
+#endif /* IN_LIBGCC2 */
+
+#define TARGET_LIBGCC_SDATA_SECTION ".sdata"
+
+#ifndef MULTILIB_ENDIAN_DEFAULT
+#if TARGET_ENDIAN_DEFAULT == 0
+#define MULTILIB_ENDIAN_DEFAULT "EL"
+#else
+#define MULTILIB_ENDIAN_DEFAULT "EB"
+#endif
+#endif
+
+#ifndef MULTILIB_ISA_DEFAULT
+# if MIPS_ISA_DEFAULT == 1
+# define MULTILIB_ISA_DEFAULT "mips1"
+# else
+# if MIPS_ISA_DEFAULT == 2
+# define MULTILIB_ISA_DEFAULT "mips2"
+# else
+# if MIPS_ISA_DEFAULT == 3
+# define MULTILIB_ISA_DEFAULT "mips3"
+# else
+# if MIPS_ISA_DEFAULT == 4
+# define MULTILIB_ISA_DEFAULT "mips4"
+# else
+# if MIPS_ISA_DEFAULT == 32
+# define MULTILIB_ISA_DEFAULT "mips32"
+# else
+# if MIPS_ISA_DEFAULT == 33
+# define MULTILIB_ISA_DEFAULT "mips32r2"
+# else
+# if MIPS_ISA_DEFAULT == 64
+# define MULTILIB_ISA_DEFAULT "mips64"
+# else
+# if MIPS_ISA_DEFAULT == 65
+# define MULTILIB_ISA_DEFAULT "mips64r2"
+# else
+# define MULTILIB_ISA_DEFAULT "mips1"
+# endif
+# endif
+# endif
+# endif
+# endif
+# endif
+# endif
+# endif
+#endif
+
+#ifndef MIPS_ABI_DEFAULT
+#define MIPS_ABI_DEFAULT ABI_32
+#endif
+
+/* Use the most portable ABI flag for the ASM specs. */
+
+#if MIPS_ABI_DEFAULT == ABI_32
+#define MULTILIB_ABI_DEFAULT "mabi=32"
+#endif
+
+#if MIPS_ABI_DEFAULT == ABI_O64
+#define MULTILIB_ABI_DEFAULT "mabi=o64"
+#endif
+
+#if MIPS_ABI_DEFAULT == ABI_N32
+#define MULTILIB_ABI_DEFAULT "mabi=n32"
+#endif
+
+#if MIPS_ABI_DEFAULT == ABI_64
+#define MULTILIB_ABI_DEFAULT "mabi=64"
+#endif
+
+#if MIPS_ABI_DEFAULT == ABI_EABI
+#define MULTILIB_ABI_DEFAULT "mabi=eabi"
+#endif
+
+#ifndef MULTILIB_DEFAULTS
+#define MULTILIB_DEFAULTS \
+ { MULTILIB_ENDIAN_DEFAULT, MULTILIB_ISA_DEFAULT, MULTILIB_ABI_DEFAULT }
+#endif
+
+/* We must pass -EL to the linker by default for little endian embedded
+ targets using linker scripts with a OUTPUT_FORMAT line. Otherwise, the
+ linker will default to using big-endian output files. The OUTPUT_FORMAT
+ line must be in the linker script, otherwise -EB/-EL will not work. */
+
+#ifndef ENDIAN_SPEC
+#if TARGET_ENDIAN_DEFAULT == 0
+#define ENDIAN_SPEC "%{!EB:%{!meb:-EL}} %{EB|meb:-EB}"
+#else
+#define ENDIAN_SPEC "%{!EL:%{!mel:-EB}} %{EL|mel:-EL}"
+#endif
+#endif
+
+/* A spec condition that matches all non-mips16 -mips arguments. */
+
+#define MIPS_ISA_LEVEL_OPTION_SPEC \
+ "mips1|mips2|mips3|mips4|mips32*|mips64*"
+
+/* A spec condition that matches all non-mips16 architecture arguments. */
+
+#define MIPS_ARCH_OPTION_SPEC \
+ MIPS_ISA_LEVEL_OPTION_SPEC "|march=*"
+
+/* A spec that infers a -mips argument from an -march argument,
+ or injects the default if no architecture is specified. */
+
+#define MIPS_ISA_LEVEL_SPEC \
+ "%{" MIPS_ISA_LEVEL_OPTION_SPEC ":;: \
+ %{march=mips1|march=r2000|march=r3000|march=r3900:-mips1} \
+ %{march=mips2|march=r6000:-mips2} \
+ %{march=mips3|march=r4*|march=vr4*|march=orion|march=loongson2*:-mips3} \
+ %{march=mips4|march=r8000|march=vr5*|march=rm7000|march=rm9000 \
+ |march=r10000|march=r12000|march=r14000|march=r16000:-mips4} \
+ %{march=mips32|march=4kc|march=4km|march=4kp|march=4ksc:-mips32} \
+ %{march=mips32r2|march=m4k|march=4ke*|march=4ksd|march=24k* \
+ |march=34k*|march=74k*|march=1004k*: -mips32r2} \
+ %{march=mips64|march=5k*|march=20k*|march=sb1*|march=sr71000 \
+ |march=xlr|march=loongson3a: -mips64} \
+ %{march=mips64r2|march=octeon: -mips64r2} \
+ %{!march=*: -" MULTILIB_ISA_DEFAULT "}}"
+
+/* A spec that infers a -mhard-float or -msoft-float setting from an
+ -march argument. Note that soft-float and hard-float code are not
+ link-compatible. */
+
+#define MIPS_ARCH_FLOAT_SPEC \
+ "%{mhard-float|msoft-float|march=mips*:; \
+ march=vr41*|march=m4k|march=4k*|march=24kc|march=24kec \
+ |march=34kc|march=74kc|march=1004kc|march=5kc \
+ |march=octeon|march=xlr: -msoft-float; \
+ march=*: -mhard-float}"
+
+/* A spec condition that matches 32-bit options. It only works if
+ MIPS_ISA_LEVEL_SPEC has been applied. */
+
+#define MIPS_32BIT_OPTION_SPEC \
+ "mips1|mips2|mips32*|mgp32"
+
+#if MIPS_ABI_DEFAULT == ABI_O64 \
+ || MIPS_ABI_DEFAULT == ABI_N32 \
+ || MIPS_ABI_DEFAULT == ABI_64
+#define OPT_ARCH64 "mabi=32|mgp32:;"
+#define OPT_ARCH32 "mabi=32|mgp32"
+#else
+#define OPT_ARCH64 "mabi=o64|mabi=n32|mabi=64|mgp64"
+#define OPT_ARCH32 "mabi=o64|mabi=n32|mabi=64|mgp64:;"
+#endif
+
+/* Support for a compile-time default CPU, et cetera. The rules are:
+ --with-arch is ignored if -march is specified or a -mips is specified
+ (other than -mips16); likewise --with-arch-32 and --with-arch-64.
+ --with-tune is ignored if -mtune is specified; likewise
+ --with-tune-32 and --with-tune-64.
+ --with-abi is ignored if -mabi is specified.
+ --with-float is ignored if -mhard-float or -msoft-float are
+ specified.
+ --with-divide is ignored if -mdivide-traps or -mdivide-breaks are
+ specified. */
+#define OPTION_DEFAULT_SPECS \
+ {"arch", "%{" MIPS_ARCH_OPTION_SPEC ":;: -march=%(VALUE)}" }, \
+ {"arch_32", "%{" OPT_ARCH32 ":%{" MIPS_ARCH_OPTION_SPEC ":;: -march=%(VALUE)}}" }, \
+ {"arch_64", "%{" OPT_ARCH64 ":%{" MIPS_ARCH_OPTION_SPEC ":;: -march=%(VALUE)}}" }, \
+ {"tune", "%{!mtune=*:-mtune=%(VALUE)}" }, \
+ {"tune_32", "%{" OPT_ARCH32 ":%{!mtune=*:-mtune=%(VALUE)}}" }, \
+ {"tune_64", "%{" OPT_ARCH64 ":%{!mtune=*:-mtune=%(VALUE)}}" }, \
+ {"abi", "%{!mabi=*:-mabi=%(VALUE)}" }, \
+ {"float", "%{!msoft-float:%{!mhard-float:-m%(VALUE)-float}}" }, \
+ {"divide", "%{!mdivide-traps:%{!mdivide-breaks:-mdivide-%(VALUE)}}" }, \
+ {"llsc", "%{!mllsc:%{!mno-llsc:-m%(VALUE)}}" }, \
+ {"mips-plt", "%{!mplt:%{!mno-plt:-m%(VALUE)}}" }, \
+ {"synci", "%{!msynci:%{!mno-synci:-m%(VALUE)}}" }
+
+
+/* A spec that infers the -mdsp setting from an -march argument. */
+#define BASE_DRIVER_SELF_SPECS \
+ "%{!mno-dsp: \
+ %{march=24ke*|march=34k*|march=1004k*: -mdsp} \
+ %{march=74k*:%{!mno-dspr2: -mdspr2 -mdsp}}}"
+
+#define DRIVER_SELF_SPECS BASE_DRIVER_SELF_SPECS
+
+#define GENERATE_DIVIDE_TRAPS (TARGET_DIVIDE_TRAPS \
+ && ISA_HAS_COND_TRAP)
+
+#define GENERATE_BRANCHLIKELY (TARGET_BRANCHLIKELY && !TARGET_MIPS16)
+
+/* True if the ABI can only work with 64-bit integer registers. We
+ generally allow ad-hoc variations for TARGET_SINGLE_FLOAT, but
+ otherwise floating-point registers must also be 64-bit. */
+#define ABI_NEEDS_64BIT_REGS (TARGET_NEWABI || mips_abi == ABI_O64)
+
+/* Likewise for 32-bit regs. */
+#define ABI_NEEDS_32BIT_REGS (mips_abi == ABI_32)
+
+/* True if the file format uses 64-bit symbols. At present, this is
+ only true for n64, which uses 64-bit ELF. */
+#define FILE_HAS_64BIT_SYMBOLS (mips_abi == ABI_64)
+
+/* True if symbols are 64 bits wide. This is usually determined by
+ the ABI's file format, but it can be overridden by -msym32. Note that
+ overriding the size with -msym32 changes the ABI of relocatable objects,
+ although it doesn't change the ABI of a fully-linked object. */
+#define ABI_HAS_64BIT_SYMBOLS (FILE_HAS_64BIT_SYMBOLS && !TARGET_SYM32)
+
+/* ISA has instructions for managing 64-bit fp and gp regs (e.g. mips3). */
+#define ISA_HAS_64BIT_REGS (ISA_MIPS3 \
+ || ISA_MIPS4 \
+ || ISA_MIPS64 \
+ || ISA_MIPS64R2)
+
+/* ISA has branch likely instructions (e.g. mips2). */
+/* Disable branchlikely for tx39 until compare rewrite. They haven't
+ been generated up to this point. */
+#define ISA_HAS_BRANCHLIKELY (!ISA_MIPS1)
+
+/* ISA has a three-operand multiplication instruction (usually spelt "mul"). */
+#define ISA_HAS_MUL3 ((TARGET_MIPS3900 \
+ || TARGET_MIPS5400 \
+ || TARGET_MIPS5500 \
+ || TARGET_MIPS7000 \
+ || TARGET_MIPS9000 \
+ || TARGET_MAD \
+ || ISA_MIPS32 \
+ || ISA_MIPS32R2 \
+ || ISA_MIPS64 \
+ || ISA_MIPS64R2) \
+ && !TARGET_MIPS16)
+
+/* ISA has a three-operand multiplication instruction. */
+#define ISA_HAS_DMUL3 (TARGET_64BIT \
+ && TARGET_OCTEON \
+ && !TARGET_MIPS16)
+
+/* ISA has the floating-point conditional move instructions introduced
+ in mips4. */
+#define ISA_HAS_FP_CONDMOVE ((ISA_MIPS4 \
+ || ISA_MIPS32 \
+ || ISA_MIPS32R2 \
+ || ISA_MIPS64 \
+ || ISA_MIPS64R2) \
+ && !TARGET_MIPS5500 \
+ && !TARGET_MIPS16)
+
+/* ISA has the integer conditional move instructions introduced in mips4 and
+ ST Loongson 2E/2F. */
+#define ISA_HAS_CONDMOVE (ISA_HAS_FP_CONDMOVE || TARGET_LOONGSON_2EF)
+
+/* ISA has LDC1 and SDC1. */
+#define ISA_HAS_LDC1_SDC1 (!ISA_MIPS1 && !TARGET_MIPS16)
+
+/* ISA has the mips4 FP condition code instructions: FP-compare to CC,
+ branch on CC, and move (both FP and non-FP) on CC. */
+#define ISA_HAS_8CC (ISA_MIPS4 \
+ || ISA_MIPS32 \
+ || ISA_MIPS32R2 \
+ || ISA_MIPS64 \
+ || ISA_MIPS64R2)
+
+/* This is a catch all for other mips4 instructions: indexed load, the
+ FP madd and msub instructions, and the FP recip and recip sqrt
+ instructions. */
+#define ISA_HAS_FP4 ((ISA_MIPS4 \
+ || (ISA_MIPS32R2 && TARGET_FLOAT64) \
+ || ISA_MIPS64 \
+ || ISA_MIPS64R2) \
+ && !TARGET_MIPS16)
+
+/* ISA has paired-single instructions. */
+#define ISA_HAS_PAIRED_SINGLE (ISA_MIPS32R2 || ISA_MIPS64 || ISA_MIPS64R2)
+
+/* ISA has conditional trap instructions. */
+#define ISA_HAS_COND_TRAP (!ISA_MIPS1 \
+ && !TARGET_MIPS16)
+
+/* ISA has integer multiply-accumulate instructions, madd and msub. */
+#define ISA_HAS_MADD_MSUB ((ISA_MIPS32 \
+ || ISA_MIPS32R2 \
+ || ISA_MIPS64 \
+ || ISA_MIPS64R2) \
+ && !TARGET_MIPS16)
+
+/* Integer multiply-accumulate instructions should be generated. */
+#define GENERATE_MADD_MSUB (ISA_HAS_MADD_MSUB && !TUNE_74K)
+
+/* ISA has floating-point madd and msub instructions 'd = a * b [+-] c'. */
+#define ISA_HAS_FP_MADD4_MSUB4 ISA_HAS_FP4
+
+/* ISA has floating-point madd and msub instructions 'c = a * b [+-] c'. */
+#define ISA_HAS_FP_MADD3_MSUB3 TARGET_LOONGSON_2EF
+
+/* ISA has floating-point nmadd and nmsub instructions
+ 'd = -((a * b) [+-] c)'. */
+#define ISA_HAS_NMADD4_NMSUB4(MODE) \
+ ((ISA_MIPS4 \
+ || (ISA_MIPS32R2 && (MODE) == V2SFmode) \
+ || ISA_MIPS64 \
+ || ISA_MIPS64R2) \
+ && (!TARGET_MIPS5400 || TARGET_MAD) \
+ && !TARGET_MIPS16)
+
+/* ISA has floating-point nmadd and nmsub instructions
+ 'c = -((a * b) [+-] c)'. */
+#define ISA_HAS_NMADD3_NMSUB3(MODE) \
+ TARGET_LOONGSON_2EF
+
+/* ISA has count leading zeroes/ones instruction (not implemented). */
+#define ISA_HAS_CLZ_CLO ((ISA_MIPS32 \
+ || ISA_MIPS32R2 \
+ || ISA_MIPS64 \
+ || ISA_MIPS64R2) \
+ && !TARGET_MIPS16)
+
+/* ISA has three operand multiply instructions that put
+ the high part in an accumulator: mulhi or mulhiu. */
+#define ISA_HAS_MULHI ((TARGET_MIPS5400 \
+ || TARGET_MIPS5500 \
+ || TARGET_SR71K) \
+ && !TARGET_MIPS16)
+
+/* ISA has three operand multiply instructions that
+ negates the result and puts the result in an accumulator. */
+#define ISA_HAS_MULS ((TARGET_MIPS5400 \
+ || TARGET_MIPS5500 \
+ || TARGET_SR71K) \
+ && !TARGET_MIPS16)
+
+/* ISA has three operand multiply instructions that subtracts the
+ result from a 4th operand and puts the result in an accumulator. */
+#define ISA_HAS_MSAC ((TARGET_MIPS5400 \
+ || TARGET_MIPS5500 \
+ || TARGET_SR71K) \
+ && !TARGET_MIPS16)
+
+/* ISA has three operand multiply instructions that the result
+ from a 4th operand and puts the result in an accumulator. */
+#define ISA_HAS_MACC ((TARGET_MIPS4120 \
+ || TARGET_MIPS4130 \
+ || TARGET_MIPS5400 \
+ || TARGET_MIPS5500 \
+ || TARGET_SR71K) \
+ && !TARGET_MIPS16)
+
+/* ISA has NEC VR-style MACC, MACCHI, DMACC and DMACCHI instructions. */
+#define ISA_HAS_MACCHI ((TARGET_MIPS4120 \
+ || TARGET_MIPS4130) \
+ && !TARGET_MIPS16)
+
+/* ISA has the "ror" (rotate right) instructions. */
+#define ISA_HAS_ROR ((ISA_MIPS32R2 \
+ || ISA_MIPS64R2 \
+ || TARGET_MIPS5400 \
+ || TARGET_MIPS5500 \
+ || TARGET_SR71K \
+ || TARGET_SMARTMIPS) \
+ && !TARGET_MIPS16)
+
+/* ISA has data prefetch instructions. This controls use of 'pref'. */
+#define ISA_HAS_PREFETCH ((ISA_MIPS4 \
+ || TARGET_LOONGSON_2EF \
+ || ISA_MIPS32 \
+ || ISA_MIPS32R2 \
+ || ISA_MIPS64 \
+ || ISA_MIPS64R2) \
+ && !TARGET_MIPS16)
+
+/* ISA has data indexed prefetch instructions. This controls use of
+ 'prefx', along with TARGET_HARD_FLOAT and TARGET_DOUBLE_FLOAT.
+ (prefx is a cop1x instruction, so can only be used if FP is
+ enabled.) */
+#define ISA_HAS_PREFETCHX ((ISA_MIPS4 \
+ || ISA_MIPS32R2 \
+ || ISA_MIPS64 \
+ || ISA_MIPS64R2) \
+ && !TARGET_MIPS16)
+
+/* True if trunc.w.s and trunc.w.d are real (not synthetic)
+ instructions. Both require TARGET_HARD_FLOAT, and trunc.w.d
+ also requires TARGET_DOUBLE_FLOAT. */
+#define ISA_HAS_TRUNC_W (!ISA_MIPS1)
+
+/* ISA includes the MIPS32r2 seb and seh instructions. */
+#define ISA_HAS_SEB_SEH ((ISA_MIPS32R2 \
+ || ISA_MIPS64R2) \
+ && !TARGET_MIPS16)
+
+/* ISA includes the MIPS32/64 rev 2 ext and ins instructions. */
+#define ISA_HAS_EXT_INS ((ISA_MIPS32R2 \
+ || ISA_MIPS64R2) \
+ && !TARGET_MIPS16)
+
+/* ISA has instructions for accessing top part of 64-bit fp regs. */
+#define ISA_HAS_MXHC1 (TARGET_FLOAT64 \
+ && (ISA_MIPS32R2 \
+ || ISA_MIPS64R2))
+
+/* ISA has lwxs instruction (load w/scaled index address. */
+#define ISA_HAS_LWXS (TARGET_SMARTMIPS && !TARGET_MIPS16)
+
+/* The DSP ASE is available. */
+#define ISA_HAS_DSP (TARGET_DSP && !TARGET_MIPS16)
+
+/* Revision 2 of the DSP ASE is available. */
+#define ISA_HAS_DSPR2 (TARGET_DSPR2 && !TARGET_MIPS16)
+
+/* True if the result of a load is not available to the next instruction.
+ A nop will then be needed between instructions like "lw $4,..."
+ and "addiu $4,$4,1". */
+#define ISA_HAS_LOAD_DELAY (ISA_MIPS1 \
+ && !TARGET_MIPS3900 \
+ && !TARGET_MIPS16)
+
+/* Likewise mtc1 and mfc1. */
+#define ISA_HAS_XFER_DELAY (mips_isa <= 3 \
+ && !TARGET_LOONGSON_2EF)
+
+/* Likewise floating-point comparisons. */
+#define ISA_HAS_FCMP_DELAY (mips_isa <= 3 \
+ && !TARGET_LOONGSON_2EF)
+
+/* True if mflo and mfhi can be immediately followed by instructions
+ which write to the HI and LO registers.
+
+ According to MIPS specifications, MIPS ISAs I, II, and III need
+ (at least) two instructions between the reads of HI/LO and
+ instructions which write them, and later ISAs do not. Contradicting
+ the MIPS specifications, some MIPS IV processor user manuals (e.g.
+ the UM for the NEC Vr5000) document needing the instructions between
+ HI/LO reads and writes, as well. Therefore, we declare only MIPS32,
+ MIPS64 and later ISAs to have the interlocks, plus any specific
+ earlier-ISA CPUs for which CPU documentation declares that the
+ instructions are really interlocked. */
+#define ISA_HAS_HILO_INTERLOCKS (ISA_MIPS32 \
+ || ISA_MIPS32R2 \
+ || ISA_MIPS64 \
+ || ISA_MIPS64R2 \
+ || TARGET_MIPS5500 \
+ || TARGET_LOONGSON_2EF)
+
+/* ISA includes synci, jr.hb and jalr.hb. */
+#define ISA_HAS_SYNCI ((ISA_MIPS32R2 \
+ || ISA_MIPS64R2) \
+ && !TARGET_MIPS16)
+
+/* ISA includes sync. */
+#define ISA_HAS_SYNC ((mips_isa >= 2 || TARGET_MIPS3900) && !TARGET_MIPS16)
+#define GENERATE_SYNC \
+ (target_flags_explicit & MASK_LLSC \
+ ? TARGET_LLSC && !TARGET_MIPS16 \
+ : ISA_HAS_SYNC)
+
+/* ISA includes ll and sc. Note that this implies ISA_HAS_SYNC
+ because the expanders use both ISA_HAS_SYNC and ISA_HAS_LL_SC
+ instructions. */
+#define ISA_HAS_LL_SC (mips_isa >= 2 && !TARGET_MIPS16)
+#define GENERATE_LL_SC \
+ (target_flags_explicit & MASK_LLSC \
+ ? TARGET_LLSC && !TARGET_MIPS16 \
+ : ISA_HAS_LL_SC)
+
+/* ISA includes the baddu instruction. */
+#define ISA_HAS_BADDU (TARGET_OCTEON && !TARGET_MIPS16)
+
+/* ISA includes the bbit* instructions. */
+#define ISA_HAS_BBIT (TARGET_OCTEON && !TARGET_MIPS16)
+
+/* ISA includes the cins instruction. */
+#define ISA_HAS_CINS (TARGET_OCTEON && !TARGET_MIPS16)
+
+/* ISA includes the exts instruction. */
+#define ISA_HAS_EXTS (TARGET_OCTEON && !TARGET_MIPS16)
+
+/* ISA includes the seq and sne instructions. */
+#define ISA_HAS_SEQ_SNE (TARGET_OCTEON && !TARGET_MIPS16)
+
+/* ISA includes the pop instruction. */
+#define ISA_HAS_POP (TARGET_OCTEON && !TARGET_MIPS16)
+
+/* The CACHE instruction is available in non-MIPS16 code. */
+#define TARGET_CACHE_BUILTIN (mips_isa >= 3)
+
+/* The CACHE instruction is available. */
+#define ISA_HAS_CACHE (TARGET_CACHE_BUILTIN && !TARGET_MIPS16)
+
+/* Tell collect what flags to pass to nm. */
+#ifndef NM_FLAGS
+#define NM_FLAGS "-Bn"
+#endif
+
+
+/* SUBTARGET_ASM_OPTIMIZING_SPEC handles passing optimization options
+ to the assembler. It may be overridden by subtargets. */
+#ifndef SUBTARGET_ASM_OPTIMIZING_SPEC
+#define SUBTARGET_ASM_OPTIMIZING_SPEC "\
+%{noasmopt:-O0} \
+%{!noasmopt:%{O:-O2} %{O1:-O2} %{O2:-O2} %{O3:-O3}}"
+#endif
+
+/* SUBTARGET_ASM_DEBUGGING_SPEC handles passing debugging options to
+ the assembler. It may be overridden by subtargets.
+
+ Beginning with gas 2.13, -mdebug must be passed to correctly handle
+ COFF debugging info. */
+
+#ifndef SUBTARGET_ASM_DEBUGGING_SPEC
+#define SUBTARGET_ASM_DEBUGGING_SPEC "\
+%{g} %{g0} %{g1} %{g2} %{g3} \
+%{ggdb:-g} %{ggdb0:-g0} %{ggdb1:-g1} %{ggdb2:-g2} %{ggdb3:-g3} \
+%{gstabs:-g} %{gstabs0:-g0} %{gstabs1:-g1} %{gstabs2:-g2} %{gstabs3:-g3} \
+%{gstabs+:-g} %{gstabs+0:-g0} %{gstabs+1:-g1} %{gstabs+2:-g2} %{gstabs+3:-g3} \
+%{gcoff:-g} %{gcoff0:-g0} %{gcoff1:-g1} %{gcoff2:-g2} %{gcoff3:-g3} \
+%{gcoff*:-mdebug} %{!gcoff*:-no-mdebug}"
+#endif
+
+/* SUBTARGET_ASM_SPEC is always passed to the assembler. It may be
+ overridden by subtargets. */
+
+#ifndef SUBTARGET_ASM_SPEC
+#define SUBTARGET_ASM_SPEC ""
+#endif
+
+#undef ASM_SPEC
+#define ASM_SPEC "\
+%{G*} %(endian_spec) %{mips1} %{mips2} %{mips3} %{mips4} \
+%{mips32*} %{mips64*} \
+%{mips16} %{mno-mips16:-no-mips16} \
+%{mips3d} %{mno-mips3d:-no-mips3d} \
+%{mdmx} %{mno-mdmx:-no-mdmx} \
+%{mdsp} %{mno-dsp} \
+%{mdspr2} %{mno-dspr2} \
+%{msmartmips} %{mno-smartmips} \
+%{mmt} %{mno-mt} \
+%{mfix-vr4120} %{mfix-vr4130} \
+%(subtarget_asm_optimizing_spec) \
+%(subtarget_asm_debugging_spec) \
+%{mabi=*} %{!mabi=*: %(asm_abi_default_spec)} \
+%{mgp32} %{mgp64} %{march=*} %{mxgot:-xgot} \
+%{mfp32} %{mfp64} \
+%{mshared} %{mno-shared} \
+%{msym32} %{mno-sym32} \
+%{mtune=*} \
+%(subtarget_asm_spec)"
+
+/* Extra switches sometimes passed to the linker. */
+
+#ifndef LINK_SPEC
+#define LINK_SPEC "\
+%(endian_spec) \
+%{G*} %{mips1} %{mips2} %{mips3} %{mips4} %{mips32*} %{mips64*} \
+%{shared}"
+#endif /* LINK_SPEC defined */
+
+
+/* Specs for the compiler proper */
+
+/* SUBTARGET_CC1_SPEC is passed to the compiler proper. It may be
+ overridden by subtargets. */
+#ifndef SUBTARGET_CC1_SPEC
+#define SUBTARGET_CC1_SPEC ""
+#endif
+
+/* CC1_SPEC is the set of arguments to pass to the compiler proper. */
+
+#undef CC1_SPEC
+#define CC1_SPEC "\
+%{G*} %{EB:-meb} %{EL:-mel} %{EB:%{EL:%emay not use both -EB and -EL}} \
+%(subtarget_cc1_spec)"
+
+/* Preprocessor specs. */
+
+/* SUBTARGET_CPP_SPEC is passed to the preprocessor. It may be
+ overridden by subtargets. */
+#ifndef SUBTARGET_CPP_SPEC
+#define SUBTARGET_CPP_SPEC ""
+#endif
+
+#define CPP_SPEC "%(subtarget_cpp_spec)"
+
+/* This macro defines names of additional specifications to put in the specs
+ that can be used in various specifications like CC1_SPEC. Its definition
+ is an initializer with a subgrouping for each command option.
+
+ Each subgrouping contains a string constant, that defines the
+ specification name, and a string constant that used by the GCC driver
+ program.
+
+ Do not define this macro if it does not need to do anything. */
+
+#define EXTRA_SPECS \
+ { "subtarget_cc1_spec", SUBTARGET_CC1_SPEC }, \
+ { "subtarget_cpp_spec", SUBTARGET_CPP_SPEC }, \
+ { "subtarget_asm_optimizing_spec", SUBTARGET_ASM_OPTIMIZING_SPEC }, \
+ { "subtarget_asm_debugging_spec", SUBTARGET_ASM_DEBUGGING_SPEC }, \
+ { "subtarget_asm_spec", SUBTARGET_ASM_SPEC }, \
+ { "asm_abi_default_spec", "-" MULTILIB_ABI_DEFAULT }, \
+ { "endian_spec", ENDIAN_SPEC }, \
+ SUBTARGET_EXTRA_SPECS
+
+#ifndef SUBTARGET_EXTRA_SPECS
+#define SUBTARGET_EXTRA_SPECS
+#endif
+
+#define DBX_DEBUGGING_INFO 1 /* generate stabs (OSF/rose) */
+#define DWARF2_DEBUGGING_INFO 1 /* dwarf2 debugging info */
+
+#ifndef PREFERRED_DEBUGGING_TYPE
+#define PREFERRED_DEBUGGING_TYPE DWARF2_DEBUG
+#endif
+
+/* The size of DWARF addresses should be the same as the size of symbols
+ in the target file format. They shouldn't depend on things like -msym32,
+ because many DWARF consumers do not allow the mixture of address sizes
+ that one would then get from linking -msym32 code with -msym64 code.
+
+ Note that the default POINTER_SIZE test is not appropriate for MIPS.
+ EABI64 has 64-bit pointers but uses 32-bit ELF. */
+#define DWARF2_ADDR_SIZE (FILE_HAS_64BIT_SYMBOLS ? 8 : 4)
+
+/* By default, turn on GDB extensions. */
+#define DEFAULT_GDB_EXTENSIONS 1
+
+/* Local compiler-generated symbols must have a prefix that the assembler
+ understands. By default, this is $, although some targets (e.g.,
+ NetBSD-ELF) need to override this. */
+
+#ifndef LOCAL_LABEL_PREFIX
+#define LOCAL_LABEL_PREFIX "$"
+#endif
+
+/* By default on the mips, external symbols do not have an underscore
+ prepended, but some targets (e.g., NetBSD) require this. */
+
+#ifndef USER_LABEL_PREFIX
+#define USER_LABEL_PREFIX ""
+#endif
+
+/* On Sun 4, this limit is 2048. We use 1500 to be safe,
+ since the length can run past this up to a continuation point. */
+#undef DBX_CONTIN_LENGTH
+#define DBX_CONTIN_LENGTH 1500
+
+/* How to renumber registers for dbx and gdb. */
+#define DBX_REGISTER_NUMBER(REGNO) mips_dbx_regno[REGNO]
+
+/* The mapping from gcc register number to DWARF 2 CFA column number. */
+#define DWARF_FRAME_REGNUM(REGNO) mips_dwarf_regno[REGNO]
+
+/* The DWARF 2 CFA column which tracks the return address. */
+#define DWARF_FRAME_RETURN_COLUMN RETURN_ADDR_REGNUM
+
+/* Before the prologue, RA lives in r31. */
+#define INCOMING_RETURN_ADDR_RTX gen_rtx_REG (VOIDmode, RETURN_ADDR_REGNUM)
+
+/* Describe how we implement __builtin_eh_return. */
+#define EH_RETURN_DATA_REGNO(N) \
+ ((N) < (TARGET_MIPS16 ? 2 : 4) ? (N) + GP_ARG_FIRST : INVALID_REGNUM)
+
+#define EH_RETURN_STACKADJ_RTX gen_rtx_REG (Pmode, GP_REG_FIRST + 3)
+
+#define EH_USES(N) mips_eh_uses (N)
+
+/* Offsets recorded in opcodes are a multiple of this alignment factor.
+ The default for this in 64-bit mode is 8, which causes problems with
+ SFmode register saves. */
+#define DWARF_CIE_DATA_ALIGNMENT -4
+
+/* Correct the offset of automatic variables and arguments. Note that
+ the MIPS debug format wants all automatic variables and arguments
+ to be in terms of the virtual frame pointer (stack pointer before
+ any adjustment in the function), while the MIPS 3.0 linker wants
+ the frame pointer to be the stack pointer after the initial
+ adjustment. */
+
+#define DEBUGGER_AUTO_OFFSET(X) \
+ mips_debugger_offset (X, (HOST_WIDE_INT) 0)
+#define DEBUGGER_ARG_OFFSET(OFFSET, X) \
+ mips_debugger_offset (X, (HOST_WIDE_INT) OFFSET)
+
+/* Target machine storage layout */
+
+#define BITS_BIG_ENDIAN 0
+#define BYTES_BIG_ENDIAN (TARGET_BIG_ENDIAN != 0)
+#define WORDS_BIG_ENDIAN (TARGET_BIG_ENDIAN != 0)
+
+#define MAX_BITS_PER_WORD 64
+
+/* Width of a word, in units (bytes). */
+#define UNITS_PER_WORD (TARGET_64BIT ? 8 : 4)
+#ifndef IN_LIBGCC2
+#define MIN_UNITS_PER_WORD 4
+#endif
+
+/* For MIPS, width of a floating point register. */
+#define UNITS_PER_FPREG (TARGET_FLOAT64 ? 8 : 4)
+
+/* The number of consecutive floating-point registers needed to store the
+ largest format supported by the FPU. */
+#define MAX_FPRS_PER_FMT (TARGET_FLOAT64 || TARGET_SINGLE_FLOAT ? 1 : 2)
+
+/* The number of consecutive floating-point registers needed to store the
+ smallest format supported by the FPU. */
+#define MIN_FPRS_PER_FMT \
+ (ISA_MIPS32 || ISA_MIPS32R2 || ISA_MIPS64 || ISA_MIPS64R2 \
+ ? 1 : MAX_FPRS_PER_FMT)
+
+/* The largest size of value that can be held in floating-point
+ registers and moved with a single instruction. */
+#define UNITS_PER_HWFPVALUE \
+ (TARGET_SOFT_FLOAT_ABI ? 0 : MAX_FPRS_PER_FMT * UNITS_PER_FPREG)
+
+/* The largest size of value that can be held in floating-point
+ registers. */
+#define UNITS_PER_FPVALUE \
+ (TARGET_SOFT_FLOAT_ABI ? 0 \
+ : TARGET_SINGLE_FLOAT ? UNITS_PER_FPREG \
+ : LONG_DOUBLE_TYPE_SIZE / BITS_PER_UNIT)
+
+/* The number of bytes in a double. */
+#define UNITS_PER_DOUBLE (TYPE_PRECISION (double_type_node) / BITS_PER_UNIT)
+
+/* Set the sizes of the core types. */
+#define SHORT_TYPE_SIZE 16
+#define INT_TYPE_SIZE 32
+#define LONG_TYPE_SIZE (TARGET_LONG64 ? 64 : 32)
+#define LONG_LONG_TYPE_SIZE 64
+
+#define FLOAT_TYPE_SIZE 32
+#define DOUBLE_TYPE_SIZE 64
+#define LONG_DOUBLE_TYPE_SIZE (TARGET_NEWABI ? 128 : 64)
+
+/* Define the sizes of fixed-point types. */
+#define SHORT_FRACT_TYPE_SIZE 8
+#define FRACT_TYPE_SIZE 16
+#define LONG_FRACT_TYPE_SIZE 32
+#define LONG_LONG_FRACT_TYPE_SIZE 64
+
+#define SHORT_ACCUM_TYPE_SIZE 16
+#define ACCUM_TYPE_SIZE 32
+#define LONG_ACCUM_TYPE_SIZE 64
+/* FIXME. LONG_LONG_ACCUM_TYPE_SIZE should be 128 bits, but GCC
+ doesn't support 128-bit integers for MIPS32 currently. */
+#define LONG_LONG_ACCUM_TYPE_SIZE (TARGET_64BIT ? 128 : 64)
+
+/* long double is not a fixed mode, but the idea is that, if we
+ support long double, we also want a 128-bit integer type. */
+#define MAX_FIXED_MODE_SIZE LONG_DOUBLE_TYPE_SIZE
+
+#ifdef IN_LIBGCC2
+#if (defined _ABIN32 && _MIPS_SIM == _ABIN32) \
+ || (defined _ABI64 && _MIPS_SIM == _ABI64)
+# define LIBGCC2_LONG_DOUBLE_TYPE_SIZE 128
+# else
+# define LIBGCC2_LONG_DOUBLE_TYPE_SIZE 64
+# endif
+#endif
+
+/* Width in bits of a pointer. */
+#ifndef POINTER_SIZE
+#define POINTER_SIZE ((TARGET_LONG64 && TARGET_64BIT) ? 64 : 32)
+#endif
+
+/* Allocation boundary (in *bits*) for storing arguments in argument list. */
+#define PARM_BOUNDARY BITS_PER_WORD
+
+/* Allocation boundary (in *bits*) for the code of a function. */
+#define FUNCTION_BOUNDARY 32
+
+/* Alignment of field after `int : 0' in a structure. */
+#define EMPTY_FIELD_BOUNDARY 32
+
+/* Every structure's size must be a multiple of this. */
+/* 8 is observed right on a DECstation and on riscos 4.02. */
+#define STRUCTURE_SIZE_BOUNDARY 8
+
+/* There is no point aligning anything to a rounder boundary than this. */
+#define BIGGEST_ALIGNMENT LONG_DOUBLE_TYPE_SIZE
+
+/* All accesses must be aligned. */
+#define STRICT_ALIGNMENT 1
+
+/* Define this if you wish to imitate the way many other C compilers
+ handle alignment of bitfields and the structures that contain
+ them.
+
+ The behavior is that the type written for a bit-field (`int',
+ `short', or other integer type) imposes an alignment for the
+ entire structure, as if the structure really did contain an
+ ordinary field of that type. In addition, the bit-field is placed
+ within the structure so that it would fit within such a field,
+ not crossing a boundary for it.
+
+ Thus, on most machines, a bit-field whose type is written as `int'
+ would not cross a four-byte boundary, and would force four-byte
+ alignment for the whole structure. (The alignment used may not
+ be four bytes; it is controlled by the other alignment
+ parameters.)
+
+ If the macro is defined, its definition should be a C expression;
+ a nonzero value for the expression enables this behavior. */
+
+#define PCC_BITFIELD_TYPE_MATTERS 1
+
+/* If defined, a C expression to compute the alignment given to a
+ constant that is being placed in memory. CONSTANT is the constant
+ and ALIGN is the alignment that the object would ordinarily have.
+ The value of this macro is used instead of that alignment to align
+ the object.
+
+ If this macro is not defined, then ALIGN is used.
+
+ The typical use of this macro is to increase alignment for string
+ constants to be word aligned so that `strcpy' calls that copy
+ constants can be done inline. */
+
+#define CONSTANT_ALIGNMENT(EXP, ALIGN) \
+ ((TREE_CODE (EXP) == STRING_CST || TREE_CODE (EXP) == CONSTRUCTOR) \
+ && (ALIGN) < BITS_PER_WORD ? BITS_PER_WORD : (ALIGN))
+
+/* If defined, a C expression to compute the alignment for a static
+ variable. TYPE is the data type, and ALIGN is the alignment that
+ the object would ordinarily have. The value of this macro is used
+ instead of that alignment to align the object.
+
+ If this macro is not defined, then ALIGN is used.
+
+ One use of this macro is to increase alignment of medium-size
+ data to make it all fit in fewer cache lines. Another is to
+ cause character arrays to be word-aligned so that `strcpy' calls
+ that copy constants to character arrays can be done inline. */
+
+#undef DATA_ALIGNMENT
+#define DATA_ALIGNMENT(TYPE, ALIGN) \
+ ((((ALIGN) < BITS_PER_WORD) \
+ && (TREE_CODE (TYPE) == ARRAY_TYPE \
+ || TREE_CODE (TYPE) == UNION_TYPE \
+ || TREE_CODE (TYPE) == RECORD_TYPE)) ? BITS_PER_WORD : (ALIGN))
+
+/* We need this for the same reason as DATA_ALIGNMENT, namely to cause
+ character arrays to be word-aligned so that `strcpy' calls that copy
+ constants to character arrays can be done inline, and 'strcmp' can be
+ optimised to use word loads. */
+#define LOCAL_ALIGNMENT(TYPE, ALIGN) \
+ DATA_ALIGNMENT (TYPE, ALIGN)
+
+#define PAD_VARARGS_DOWN \
+ (FUNCTION_ARG_PADDING (TYPE_MODE (type), type) == downward)
+
+/* Define if operations between registers always perform the operation
+ on the full register even if a narrower mode is specified. */
+#define WORD_REGISTER_OPERATIONS
+
+/* When in 64-bit mode, move insns will sign extend SImode and CCmode
+ moves. All other references are zero extended. */
+#define LOAD_EXTEND_OP(MODE) \
+ (TARGET_64BIT && ((MODE) == SImode || (MODE) == CCmode) \
+ ? SIGN_EXTEND : ZERO_EXTEND)
+
+/* Define this macro if it is advisable to hold scalars in registers
+ in a wider mode than that declared by the program. In such cases,
+ the value is constrained to be within the bounds of the declared
+ type, but kept valid in the wider mode. The signedness of the
+ extension may differ from that of the type. */
+
+#define PROMOTE_MODE(MODE, UNSIGNEDP, TYPE) \
+ if (GET_MODE_CLASS (MODE) == MODE_INT \
+ && GET_MODE_SIZE (MODE) < UNITS_PER_WORD) \
+ { \
+ if ((MODE) == SImode) \
+ (UNSIGNEDP) = 0; \
+ (MODE) = Pmode; \
+ }
+
+/* Pmode is always the same as ptr_mode, but not always the same as word_mode.
+ Extensions of pointers to word_mode must be signed. */
+#define POINTERS_EXTEND_UNSIGNED false
+
+/* Define if loading short immediate values into registers sign extends. */
+#define SHORT_IMMEDIATES_SIGN_EXTEND
+
+/* The [d]clz instructions have the natural values at 0. */
+
+#define CLZ_DEFINED_VALUE_AT_ZERO(MODE, VALUE) \
+ ((VALUE) = GET_MODE_BITSIZE (MODE), 2)
+
+/* Standard register usage. */
+
+/* Number of hardware registers. We have:
+
+ - 32 integer registers
+ - 32 floating point registers
+ - 8 condition code registers
+ - 2 accumulator registers (hi and lo)
+ - 32 registers each for coprocessors 0, 2 and 3
+ - 4 fake registers:
+ - ARG_POINTER_REGNUM
+ - FRAME_POINTER_REGNUM
+ - GOT_VERSION_REGNUM (see the comment above load_call<mode> for details)
+ - CPRESTORE_SLOT_REGNUM
+ - 2 dummy entries that were used at various times in the past.
+ - 6 DSP accumulator registers (3 hi-lo pairs) for MIPS DSP ASE
+ - 6 DSP control registers */
+
+#define FIRST_PSEUDO_REGISTER 188
+
+/* By default, fix the kernel registers ($26 and $27), the global
+ pointer ($28) and the stack pointer ($29). This can change
+ depending on the command-line options.
+
+ Regarding coprocessor registers: without evidence to the contrary,
+ it's best to assume that each coprocessor register has a unique
+ use. This can be overridden, in, e.g., mips_option_override or
+ TARGET_CONDITIONAL_REGISTER_USAGE should the assumption be
+ inappropriate for a particular target. */
+
+#define FIXED_REGISTERS \
+{ \
+ 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, \
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
+ 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, \
+ /* COP0 registers */ \
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
+ /* COP2 registers */ \
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
+ /* COP3 registers */ \
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
+ /* 6 DSP accumulator registers & 6 control registers */ \
+ 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1 \
+}
+
+
+/* Set up this array for o32 by default.
+
+ Note that we don't mark $31 as a call-clobbered register. The idea is
+ that it's really the call instructions themselves which clobber $31.
+ We don't care what the called function does with it afterwards.
+
+ This approach makes it easier to implement sibcalls. Unlike normal
+ calls, sibcalls don't clobber $31, so the register reaches the
+ called function in tact. EPILOGUE_USES says that $31 is useful
+ to the called function. */
+
+#define CALL_USED_REGISTERS \
+{ \
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, \
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
+ 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
+ /* COP0 registers */ \
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
+ /* COP2 registers */ \
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
+ /* COP3 registers */ \
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
+ /* 6 DSP accumulator registers & 6 control registers */ \
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 \
+}
+
+
+/* Define this since $28, though fixed, is call-saved in many ABIs. */
+
+#define CALL_REALLY_USED_REGISTERS \
+{ /* General registers. */ \
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, \
+ /* Floating-point registers. */ \
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
+ 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
+ /* Others. */ \
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, \
+ /* COP0 registers */ \
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
+ /* COP2 registers */ \
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
+ /* COP3 registers */ \
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
+ /* 6 DSP accumulator registers & 6 control registers */ \
+ 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0 \
+}
+
+/* Internal macros to classify a register number as to whether it's a
+ general purpose register, a floating point register, a
+ multiply/divide register, or a status register. */
+
+#define GP_REG_FIRST 0
+#define GP_REG_LAST 31
+#define GP_REG_NUM (GP_REG_LAST - GP_REG_FIRST + 1)
+#define GP_DBX_FIRST 0
+#define K0_REG_NUM (GP_REG_FIRST + 26)
+#define K1_REG_NUM (GP_REG_FIRST + 27)
+#define KERNEL_REG_P(REGNO) (IN_RANGE (REGNO, K0_REG_NUM, K1_REG_NUM))
+
+#define FP_REG_FIRST 32
+#define FP_REG_LAST 63
+#define FP_REG_NUM (FP_REG_LAST - FP_REG_FIRST + 1)
+#define FP_DBX_FIRST ((write_symbols == DBX_DEBUG) ? 38 : 32)
+
+#define MD_REG_FIRST 64
+#define MD_REG_LAST 65
+#define MD_REG_NUM (MD_REG_LAST - MD_REG_FIRST + 1)
+#define MD_DBX_FIRST (FP_DBX_FIRST + FP_REG_NUM)
+
+/* The DWARF 2 CFA column which tracks the return address from a
+ signal handler context. This means that to maintain backwards
+ compatibility, no hard register can be assigned this column if it
+ would need to be handled by the DWARF unwinder. */
+#define DWARF_ALT_FRAME_RETURN_COLUMN 66
+
+#define ST_REG_FIRST 67
+#define ST_REG_LAST 74
+#define ST_REG_NUM (ST_REG_LAST - ST_REG_FIRST + 1)
+
+
+/* FIXME: renumber. */
+#define COP0_REG_FIRST 80
+#define COP0_REG_LAST 111
+#define COP0_REG_NUM (COP0_REG_LAST - COP0_REG_FIRST + 1)
+
+#define COP0_STATUS_REG_NUM (COP0_REG_FIRST + 12)
+#define COP0_CAUSE_REG_NUM (COP0_REG_FIRST + 13)
+#define COP0_EPC_REG_NUM (COP0_REG_FIRST + 14)
+
+#define COP2_REG_FIRST 112
+#define COP2_REG_LAST 143
+#define COP2_REG_NUM (COP2_REG_LAST - COP2_REG_FIRST + 1)
+
+#define COP3_REG_FIRST 144
+#define COP3_REG_LAST 175
+#define COP3_REG_NUM (COP3_REG_LAST - COP3_REG_FIRST + 1)
+/* ALL_COP_REG_NUM assumes that COP0,2,and 3 are numbered consecutively. */
+#define ALL_COP_REG_NUM (COP3_REG_LAST - COP0_REG_FIRST + 1)
+
+#define DSP_ACC_REG_FIRST 176
+#define DSP_ACC_REG_LAST 181
+#define DSP_ACC_REG_NUM (DSP_ACC_REG_LAST - DSP_ACC_REG_FIRST + 1)
+
+#define AT_REGNUM (GP_REG_FIRST + 1)
+#define HI_REGNUM (TARGET_BIG_ENDIAN ? MD_REG_FIRST : MD_REG_FIRST + 1)
+#define LO_REGNUM (TARGET_BIG_ENDIAN ? MD_REG_FIRST + 1 : MD_REG_FIRST)
+
+/* A few bitfield locations for the coprocessor registers. */
+/* Request Interrupt Priority Level is from bit 10 to bit 15 of
+ the cause register for the EIC interrupt mode. */
+#define CAUSE_IPL 10
+/* Interrupt Priority Level is from bit 10 to bit 15 of the status register. */
+#define SR_IPL 10
+/* Exception Level is at bit 1 of the status register. */
+#define SR_EXL 1
+/* Interrupt Enable is at bit 0 of the status register. */
+#define SR_IE 0
+
+/* FPSW_REGNUM is the single condition code used if !ISA_HAS_8CC.
+ If ISA_HAS_8CC, it should not be used, and an arbitrary ST_REG
+ should be used instead. */
+#define FPSW_REGNUM ST_REG_FIRST
+
+#define GP_REG_P(REGNO) \
+ ((unsigned int) ((int) (REGNO) - GP_REG_FIRST) < GP_REG_NUM)
+#define M16_REG_P(REGNO) \
+ (((REGNO) >= 2 && (REGNO) <= 7) || (REGNO) == 16 || (REGNO) == 17)
+#define FP_REG_P(REGNO) \
+ ((unsigned int) ((int) (REGNO) - FP_REG_FIRST) < FP_REG_NUM)
+#define MD_REG_P(REGNO) \
+ ((unsigned int) ((int) (REGNO) - MD_REG_FIRST) < MD_REG_NUM)
+#define ST_REG_P(REGNO) \
+ ((unsigned int) ((int) (REGNO) - ST_REG_FIRST) < ST_REG_NUM)
+#define COP0_REG_P(REGNO) \
+ ((unsigned int) ((int) (REGNO) - COP0_REG_FIRST) < COP0_REG_NUM)
+#define COP2_REG_P(REGNO) \
+ ((unsigned int) ((int) (REGNO) - COP2_REG_FIRST) < COP2_REG_NUM)
+#define COP3_REG_P(REGNO) \
+ ((unsigned int) ((int) (REGNO) - COP3_REG_FIRST) < COP3_REG_NUM)
+#define ALL_COP_REG_P(REGNO) \
+ ((unsigned int) ((int) (REGNO) - COP0_REG_FIRST) < ALL_COP_REG_NUM)
+/* Test if REGNO is one of the 6 new DSP accumulators. */
+#define DSP_ACC_REG_P(REGNO) \
+ ((unsigned int) ((int) (REGNO) - DSP_ACC_REG_FIRST) < DSP_ACC_REG_NUM)
+/* Test if REGNO is hi, lo, or one of the 6 new DSP accumulators. */
+#define ACC_REG_P(REGNO) \
+ (MD_REG_P (REGNO) || DSP_ACC_REG_P (REGNO))
+
+#define FP_REG_RTX_P(X) (REG_P (X) && FP_REG_P (REGNO (X)))
+
+/* True if X is (const (unspec [(const_int 0)] UNSPEC_GP)). This is used
+ to initialize the mips16 gp pseudo register. */
+#define CONST_GP_P(X) \
+ (GET_CODE (X) == CONST \
+ && GET_CODE (XEXP (X, 0)) == UNSPEC \
+ && XINT (XEXP (X, 0), 1) == UNSPEC_GP)
+
+/* Return coprocessor number from register number. */
+
+#define COPNUM_AS_CHAR_FROM_REGNUM(REGNO) \
+ (COP0_REG_P (REGNO) ? '0' : COP2_REG_P (REGNO) ? '2' \
+ : COP3_REG_P (REGNO) ? '3' : '?')
+
+
+#define HARD_REGNO_NREGS(REGNO, MODE) mips_hard_regno_nregs (REGNO, MODE)
+
+#define HARD_REGNO_MODE_OK(REGNO, MODE) \
+ mips_hard_regno_mode_ok[ (int)(MODE) ][ (REGNO) ]
+
+#define MODES_TIEABLE_P mips_modes_tieable_p
+
+/* Register to use for pushing function arguments. */
+#define STACK_POINTER_REGNUM (GP_REG_FIRST + 29)
+
+/* These two registers don't really exist: they get eliminated to either
+ the stack or hard frame pointer. */
+#define ARG_POINTER_REGNUM 77
+#define FRAME_POINTER_REGNUM 78
+
+/* $30 is not available on the mips16, so we use $17 as the frame
+ pointer. */
+#define HARD_FRAME_POINTER_REGNUM \
+ (TARGET_MIPS16 ? GP_REG_FIRST + 17 : GP_REG_FIRST + 30)
+
+#define HARD_FRAME_POINTER_IS_FRAME_POINTER 0
+#define HARD_FRAME_POINTER_IS_ARG_POINTER 0
+
+/* Register in which static-chain is passed to a function. */
+#define STATIC_CHAIN_REGNUM (GP_REG_FIRST + 15)
+
+/* Registers used as temporaries in prologue/epilogue code:
+
+ - If a MIPS16 PIC function needs access to _gp, it first loads
+ the value into MIPS16_PIC_TEMP and then copies it to $gp.
+
+ - The prologue can use MIPS_PROLOGUE_TEMP as a general temporary
+ register. The register must not conflict with MIPS16_PIC_TEMP.
+
+ - The epilogue can use MIPS_EPILOGUE_TEMP as a general temporary
+ register.
+
+ If we're generating MIPS16 code, these registers must come from the
+ core set of 8. The prologue registers mustn't conflict with any
+ incoming arguments, the static chain pointer, or the frame pointer.
+ The epilogue temporary mustn't conflict with the return registers,
+ the PIC call register ($25), the frame pointer, the EH stack adjustment,
+ or the EH data registers.
+
+ If we're generating interrupt handlers, we use K0 as a temporary register
+ in prologue/epilogue code. */
+
+#define MIPS16_PIC_TEMP_REGNUM (GP_REG_FIRST + 2)
+#define MIPS_PROLOGUE_TEMP_REGNUM \
+ (cfun->machine->interrupt_handler_p ? K0_REG_NUM : GP_REG_FIRST + 3)
+#define MIPS_EPILOGUE_TEMP_REGNUM \
+ (cfun->machine->interrupt_handler_p \
+ ? K0_REG_NUM \
+ : GP_REG_FIRST + (TARGET_MIPS16 ? 6 : 8))
+
+#define MIPS16_PIC_TEMP gen_rtx_REG (Pmode, MIPS16_PIC_TEMP_REGNUM)
+#define MIPS_PROLOGUE_TEMP(MODE) gen_rtx_REG (MODE, MIPS_PROLOGUE_TEMP_REGNUM)
+#define MIPS_EPILOGUE_TEMP(MODE) gen_rtx_REG (MODE, MIPS_EPILOGUE_TEMP_REGNUM)
+
+/* Define this macro if it is as good or better to call a constant
+ function address than to call an address kept in a register. */
+#define NO_FUNCTION_CSE 1
+
+/* The ABI-defined global pointer. Sometimes we use a different
+ register in leaf functions: see PIC_OFFSET_TABLE_REGNUM. */
+#define GLOBAL_POINTER_REGNUM (GP_REG_FIRST + 28)
+
+/* We normally use $28 as the global pointer. However, when generating
+ n32/64 PIC, it is better for leaf functions to use a call-clobbered
+ register instead. They can then avoid saving and restoring $28
+ and perhaps avoid using a frame at all.
+
+ When a leaf function uses something other than $28, mips_expand_prologue
+ will modify pic_offset_table_rtx in place. Take the register number
+ from there after reload. */
+#define PIC_OFFSET_TABLE_REGNUM \
+ (reload_completed ? REGNO (pic_offset_table_rtx) : GLOBAL_POINTER_REGNUM)
+
+#define PIC_FUNCTION_ADDR_REGNUM (GP_REG_FIRST + 25)
+
+/* Define the classes of registers for register constraints in the
+ machine description. Also define ranges of constants.
+
+ One of the classes must always be named ALL_REGS and include all hard regs.
+ If there is more than one class, another class must be named NO_REGS
+ and contain no registers.
+
+ The name GENERAL_REGS must be the name of a class (or an alias for
+ another name such as ALL_REGS). This is the class of registers
+ that is allowed by "g" or "r" in a register constraint.
+ Also, registers outside this class are allocated only when
+ instructions express preferences for them.
+
+ The classes must be numbered in nondecreasing order; that is,
+ a larger-numbered class must never be contained completely
+ in a smaller-numbered class.
+
+ For any two classes, it is very desirable that there be another
+ class that represents their union. */
+
+enum reg_class
+{
+ NO_REGS, /* no registers in set */
+ M16_REGS, /* mips16 directly accessible registers */
+ T_REG, /* mips16 T register ($24) */
+ M16_T_REGS, /* mips16 registers plus T register */
+ PIC_FN_ADDR_REG, /* SVR4 PIC function address register */
+ V1_REG, /* Register $v1 ($3) used for TLS access. */
+ LEA_REGS, /* Every GPR except $25 */
+ GR_REGS, /* integer registers */
+ FP_REGS, /* floating point registers */
+ MD0_REG, /* first multiply/divide register */
+ MD1_REG, /* second multiply/divide register */
+ MD_REGS, /* multiply/divide registers (hi/lo) */
+ COP0_REGS, /* generic coprocessor classes */
+ COP2_REGS,
+ COP3_REGS,
+ ST_REGS, /* status registers (fp status) */
+ DSP_ACC_REGS, /* DSP accumulator registers */
+ ACC_REGS, /* Hi/Lo and DSP accumulator registers */
+ FRAME_REGS, /* $arg and $frame */
+ GR_AND_MD0_REGS, /* union classes */
+ GR_AND_MD1_REGS,
+ GR_AND_MD_REGS,
+ GR_AND_ACC_REGS,
+ ALL_REGS, /* all registers */
+ LIM_REG_CLASSES /* max value + 1 */
+};
+
+#define N_REG_CLASSES (int) LIM_REG_CLASSES
+
+#define GENERAL_REGS GR_REGS
+
+/* An initializer containing the names of the register classes as C
+ string constants. These names are used in writing some of the
+ debugging dumps. */
+
+#define REG_CLASS_NAMES \
+{ \
+ "NO_REGS", \
+ "M16_REGS", \
+ "T_REG", \
+ "M16_T_REGS", \
+ "PIC_FN_ADDR_REG", \
+ "V1_REG", \
+ "LEA_REGS", \
+ "GR_REGS", \
+ "FP_REGS", \
+ "MD0_REG", \
+ "MD1_REG", \
+ "MD_REGS", \
+ /* coprocessor registers */ \
+ "COP0_REGS", \
+ "COP2_REGS", \
+ "COP3_REGS", \
+ "ST_REGS", \
+ "DSP_ACC_REGS", \
+ "ACC_REGS", \
+ "FRAME_REGS", \
+ "GR_AND_MD0_REGS", \
+ "GR_AND_MD1_REGS", \
+ "GR_AND_MD_REGS", \
+ "GR_AND_ACC_REGS", \
+ "ALL_REGS" \
+}
+
+/* An initializer containing the contents of the register classes,
+ as integers which are bit masks. The Nth integer specifies the
+ contents of class N. The way the integer MASK is interpreted is
+ that register R is in the class if `MASK & (1 << R)' is 1.
+
+ When the machine has more than 32 registers, an integer does not
+ suffice. Then the integers are replaced by sub-initializers,
+ braced groupings containing several integers. Each
+ sub-initializer must be suitable as an initializer for the type
+ `HARD_REG_SET' which is defined in `hard-reg-set.h'. */
+
+#define REG_CLASS_CONTENTS \
+{ \
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, /* NO_REGS */ \
+ { 0x000300fc, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, /* M16_REGS */ \
+ { 0x01000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, /* T_REG */ \
+ { 0x010300fc, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, /* M16_T_REGS */ \
+ { 0x02000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, /* PIC_FN_ADDR_REG */ \
+ { 0x00000008, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, /* V1_REG */ \
+ { 0xfdffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, /* LEA_REGS */ \
+ { 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, /* GR_REGS */ \
+ { 0x00000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, /* FP_REGS */ \
+ { 0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000000 }, /* MD0_REG */ \
+ { 0x00000000, 0x00000000, 0x00000002, 0x00000000, 0x00000000, 0x00000000 }, /* MD1_REG */ \
+ { 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000000, 0x00000000 }, /* MD_REGS */ \
+ { 0x00000000, 0x00000000, 0xffff0000, 0x0000ffff, 0x00000000, 0x00000000 }, /* COP0_REGS */ \
+ { 0x00000000, 0x00000000, 0x00000000, 0xffff0000, 0x0000ffff, 0x00000000 }, /* COP2_REGS */ \
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffff0000, 0x0000ffff }, /* COP3_REGS */ \
+ { 0x00000000, 0x00000000, 0x000007f8, 0x00000000, 0x00000000, 0x00000000 }, /* ST_REGS */ \
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x003f0000 }, /* DSP_ACC_REGS */ \
+ { 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000000, 0x003f0000 }, /* ACC_REGS */ \
+ { 0x00000000, 0x00000000, 0x00006000, 0x00000000, 0x00000000, 0x00000000 }, /* FRAME_REGS */ \
+ { 0xffffffff, 0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000000 }, /* GR_AND_MD0_REGS */ \
+ { 0xffffffff, 0x00000000, 0x00000002, 0x00000000, 0x00000000, 0x00000000 }, /* GR_AND_MD1_REGS */ \
+ { 0xffffffff, 0x00000000, 0x00000003, 0x00000000, 0x00000000, 0x00000000 }, /* GR_AND_MD_REGS */ \
+ { 0xffffffff, 0x00000000, 0x00000003, 0x00000000, 0x00000000, 0x003f0000 }, /* GR_AND_ACC_REGS */ \
+ { 0xffffffff, 0xffffffff, 0xffff67ff, 0xffffffff, 0xffffffff, 0x0fffffff } /* ALL_REGS */ \
+}
+
+
+/* A C expression whose value is a register class containing hard
+ register REGNO. In general there is more that one such class;
+ choose a class which is "minimal", meaning that no smaller class
+ also contains the register. */
+
+#define REGNO_REG_CLASS(REGNO) mips_regno_to_class[ (REGNO) ]
+
+/* A macro whose definition is the name of the class to which a
+ valid base register must belong. A base register is one used in
+ an address which is the register value plus a displacement. */
+
+#define BASE_REG_CLASS (TARGET_MIPS16 ? M16_REGS : GR_REGS)
+
+/* A macro whose definition is the name of the class to which a
+ valid index register must belong. An index register is one used
+ in an address where its value is either multiplied by a scale
+ factor or added to another register (as well as added to a
+ displacement). */
+
+#define INDEX_REG_CLASS NO_REGS
+
+/* We generally want to put call-clobbered registers ahead of
+ call-saved ones. (IRA expects this.) */
+
+#define REG_ALLOC_ORDER \
+{ /* Accumulator registers. When GPRs and accumulators have equal \
+ cost, we generally prefer to use accumulators. For example, \
+ a division of multiplication result is better allocated to LO, \
+ so that we put the MFLO at the point of use instead of at the \
+ point of definition. It's also needed if we're to take advantage \
+ of the extra accumulators available with -mdspr2. In some cases, \
+ it can also help to reduce register pressure. */ \
+ 64, 65,176,177,178,179,180,181, \
+ /* Call-clobbered GPRs. */ \
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, \
+ 24, 25, 31, \
+ /* The global pointer. This is call-clobbered for o32 and o64 \
+ abicalls, call-saved for n32 and n64 abicalls, and a program \
+ invariant otherwise. Putting it between the call-clobbered \
+ and call-saved registers should cope with all eventualities. */ \
+ 28, \
+ /* Call-saved GPRs. */ \
+ 16, 17, 18, 19, 20, 21, 22, 23, 30, \
+ /* GPRs that can never be exposed to the register allocator. */ \
+ 0, 26, 27, 29, \
+ /* Call-clobbered FPRs. */ \
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, \
+ 48, 49, 50, 51, \
+ /* FPRs that are usually call-saved. The odd ones are actually \
+ call-clobbered for n32, but listing them ahead of the even \
+ registers might encourage the register allocator to fragment \
+ the available FPR pairs. We need paired FPRs to store long \
+ doubles, so it isn't clear that using a different order \
+ for n32 would be a win. */ \
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, \
+ /* None of the remaining classes have defined call-saved \
+ registers. */ \
+ 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, \
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, \
+ 96, 97, 98, 99, 100,101,102,103,104,105,106,107,108,109,110,111, \
+ 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, \
+ 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, \
+ 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, \
+ 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, \
+ 182,183,184,185,186,187 \
+}
+
+/* ADJUST_REG_ALLOC_ORDER is a macro which permits reg_alloc_order
+ to be rearranged based on a particular function. On the mips16, we
+ want to allocate $24 (T_REG) before other registers for
+ instructions for which it is possible. */
+
+#define ADJUST_REG_ALLOC_ORDER mips_order_regs_for_local_alloc ()
+
+/* True if VALUE is an unsigned 6-bit number. */
+
+#define UIMM6_OPERAND(VALUE) \
+ (((VALUE) & ~(unsigned HOST_WIDE_INT) 0x3f) == 0)
+
+/* True if VALUE is a signed 10-bit number. */
+
+#define IMM10_OPERAND(VALUE) \
+ ((unsigned HOST_WIDE_INT) (VALUE) + 0x200 < 0x400)
+
+/* True if VALUE is a signed 16-bit number. */
+
+#define SMALL_OPERAND(VALUE) \
+ ((unsigned HOST_WIDE_INT) (VALUE) + 0x8000 < 0x10000)
+
+/* True if VALUE is an unsigned 16-bit number. */
+
+#define SMALL_OPERAND_UNSIGNED(VALUE) \
+ (((VALUE) & ~(unsigned HOST_WIDE_INT) 0xffff) == 0)
+
+/* True if VALUE can be loaded into a register using LUI. */
+
+#define LUI_OPERAND(VALUE) \
+ (((VALUE) | 0x7fff0000) == 0x7fff0000 \
+ || ((VALUE) | 0x7fff0000) + 0x10000 == 0)
+
+/* Return a value X with the low 16 bits clear, and such that
+ VALUE - X is a signed 16-bit value. */
+
+#define CONST_HIGH_PART(VALUE) \
+ (((VALUE) + 0x8000) & ~(unsigned HOST_WIDE_INT) 0xffff)
+
+#define CONST_LOW_PART(VALUE) \
+ ((VALUE) - CONST_HIGH_PART (VALUE))
+
+#define SMALL_INT(X) SMALL_OPERAND (INTVAL (X))
+#define SMALL_INT_UNSIGNED(X) SMALL_OPERAND_UNSIGNED (INTVAL (X))
+#define LUI_INT(X) LUI_OPERAND (INTVAL (X))
+
+/* The HI and LO registers can only be reloaded via the general
+ registers. Condition code registers can only be loaded to the
+ general registers, and from the floating point registers. */
+
+#define SECONDARY_INPUT_RELOAD_CLASS(CLASS, MODE, X) \
+ mips_secondary_reload_class (CLASS, MODE, X, true)
+#define SECONDARY_OUTPUT_RELOAD_CLASS(CLASS, MODE, X) \
+ mips_secondary_reload_class (CLASS, MODE, X, false)
+
+/* Return the maximum number of consecutive registers
+ needed to represent mode MODE in a register of class CLASS. */
+
+#define CLASS_MAX_NREGS(CLASS, MODE) mips_class_max_nregs (CLASS, MODE)
+
+#define CANNOT_CHANGE_MODE_CLASS(FROM, TO, CLASS) \
+ mips_cannot_change_mode_class (FROM, TO, CLASS)
+
+/* Stack layout; function entry, exit and calling. */
+
+#define STACK_GROWS_DOWNWARD
+
+#define FRAME_GROWS_DOWNWARD flag_stack_protect
+
+/* Size of the area allocated in the frame to save the GP. */
+
+#define MIPS_GP_SAVE_AREA_SIZE \
+ (TARGET_CALL_CLOBBERED_GP ? MIPS_STACK_ALIGN (UNITS_PER_WORD) : 0)
+
+/* The offset of the first local variable from the frame pointer. See
+ mips_compute_frame_info for details about the frame layout. */
+
+#define STARTING_FRAME_OFFSET \
+ (FRAME_GROWS_DOWNWARD \
+ ? 0 \
+ : crtl->outgoing_args_size + MIPS_GP_SAVE_AREA_SIZE)
+
+#define RETURN_ADDR_RTX mips_return_addr
+
+/* Mask off the MIPS16 ISA bit in unwind addresses.
+
+ The reason for this is a little subtle. When unwinding a call,
+ we are given the call's return address, which on most targets
+ is the address of the following instruction. However, what we
+ actually want to find is the EH region for the call itself.
+ The target-independent unwind code therefore searches for "RA - 1".
+
+ In the MIPS16 case, RA is always an odd-valued (ISA-encoded) address.
+ RA - 1 is therefore the real (even-valued) start of the return
+ instruction. EH region labels are usually odd-valued MIPS16 symbols
+ too, so a search for an even address within a MIPS16 region would
+ usually work.
+
+ However, there is an exception. If the end of an EH region is also
+ the end of a function, the end label is allowed to be even. This is
+ necessary because a following non-MIPS16 function may also need EH
+ information for its first instruction.
+
+ Thus a MIPS16 region may be terminated by an ISA-encoded or a
+ non-ISA-encoded address. This probably isn't ideal, but it is
+ the traditional (legacy) behavior. It is therefore only safe
+ to search MIPS EH regions for an _odd-valued_ address.
+
+ Masking off the ISA bit means that the target-independent code
+ will search for "(RA & -2) - 1", which is guaranteed to be odd. */
+#define MASK_RETURN_ADDR GEN_INT (-2)
+
+
+/* Similarly, don't use the least-significant bit to tell pointers to
+ code from vtable index. */
+
+#define TARGET_PTRMEMFUNC_VBIT_LOCATION ptrmemfunc_vbit_in_delta
+
+/* The eliminations to $17 are only used for mips16 code. See the
+ definition of HARD_FRAME_POINTER_REGNUM. */
+
+#define ELIMINABLE_REGS \
+{{ ARG_POINTER_REGNUM, STACK_POINTER_REGNUM}, \
+ { ARG_POINTER_REGNUM, GP_REG_FIRST + 30}, \
+ { ARG_POINTER_REGNUM, GP_REG_FIRST + 17}, \
+ { FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM}, \
+ { FRAME_POINTER_REGNUM, GP_REG_FIRST + 30}, \
+ { FRAME_POINTER_REGNUM, GP_REG_FIRST + 17}}
+
+#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \
+ (OFFSET) = mips_initial_elimination_offset ((FROM), (TO))
+
+/* Allocate stack space for arguments at the beginning of each function. */
+#define ACCUMULATE_OUTGOING_ARGS 1
+
+/* The argument pointer always points to the first argument. */
+#define FIRST_PARM_OFFSET(FNDECL) 0
+
+/* o32 and o64 reserve stack space for all argument registers. */
+#define REG_PARM_STACK_SPACE(FNDECL) \
+ (TARGET_OLDABI \
+ ? (MAX_ARGS_IN_REGISTERS * UNITS_PER_WORD) \
+ : 0)
+
+/* Define this if it is the responsibility of the caller to
+ allocate the area reserved for arguments passed in registers.
+ If `ACCUMULATE_OUTGOING_ARGS' is also defined, the only effect
+ of this macro is to determine whether the space is included in
+ `crtl->outgoing_args_size'. */
+#define OUTGOING_REG_PARM_STACK_SPACE(FNTYPE) 1
+
+#define STACK_BOUNDARY (TARGET_NEWABI ? 128 : 64)
+
+/* Symbolic macros for the registers used to return integer and floating
+ point values. */
+
+#define GP_RETURN (GP_REG_FIRST + 2)
+#define FP_RETURN ((TARGET_SOFT_FLOAT) ? GP_RETURN : (FP_REG_FIRST + 0))
+
+#define MAX_ARGS_IN_REGISTERS (TARGET_OLDABI ? 4 : 8)
+
+/* Symbolic macros for the first/last argument registers. */
+
+#define GP_ARG_FIRST (GP_REG_FIRST + 4)
+#define GP_ARG_LAST (GP_ARG_FIRST + MAX_ARGS_IN_REGISTERS - 1)
+#define FP_ARG_FIRST (FP_REG_FIRST + 12)
+#define FP_ARG_LAST (FP_ARG_FIRST + MAX_ARGS_IN_REGISTERS - 1)
+
+#define LIBCALL_VALUE(MODE) \
+ mips_function_value (NULL_TREE, NULL_TREE, MODE)
+
+#define FUNCTION_VALUE(VALTYPE, FUNC) \
+ mips_function_value (VALTYPE, FUNC, VOIDmode)
+
+/* 1 if N is a possible register number for a function value.
+ On the MIPS, R2 R3 and F0 F2 are the only register thus used.
+ Currently, R2 and F0 are only implemented here (C has no complex type) */
+
+#define FUNCTION_VALUE_REGNO_P(N) ((N) == GP_RETURN || (N) == FP_RETURN \
+ || (LONG_DOUBLE_TYPE_SIZE == 128 && FP_RETURN != GP_RETURN \
+ && (N) == FP_RETURN + 2))
+
+/* 1 if N is a possible register number for function argument passing.
+ We have no FP argument registers when soft-float. When FP registers
+ are 32 bits, we can't directly reference the odd numbered ones. */
+
+#define FUNCTION_ARG_REGNO_P(N) \
+ ((IN_RANGE((N), GP_ARG_FIRST, GP_ARG_LAST) \
+ || (IN_RANGE((N), FP_ARG_FIRST, FP_ARG_LAST))) \
+ && !fixed_regs[N])
+
+/* This structure has to cope with two different argument allocation
+ schemes. Most MIPS ABIs view the arguments as a structure, of which
+ the first N words go in registers and the rest go on the stack. If I
+ < N, the Ith word might go in Ith integer argument register or in a
+ floating-point register. For these ABIs, we only need to remember
+ the offset of the current argument into the structure.
+
+ The EABI instead allocates the integer and floating-point arguments
+ separately. The first N words of FP arguments go in FP registers,
+ the rest go on the stack. Likewise, the first N words of the other
+ arguments go in integer registers, and the rest go on the stack. We
+ need to maintain three counts: the number of integer registers used,
+ the number of floating-point registers used, and the number of words
+ passed on the stack.
+
+ We could keep separate information for the two ABIs (a word count for
+ the standard ABIs, and three separate counts for the EABI). But it
+ seems simpler to view the standard ABIs as forms of EABI that do not
+ allocate floating-point registers.
+
+ So for the standard ABIs, the first N words are allocated to integer
+ registers, and mips_function_arg decides on an argument-by-argument
+ basis whether that argument should really go in an integer register,
+ or in a floating-point one. */
+
+typedef struct mips_args {
+ /* Always true for varargs functions. Otherwise true if at least
+ one argument has been passed in an integer register. */
+ int gp_reg_found;
+
+ /* The number of arguments seen so far. */
+ unsigned int arg_number;
+
+ /* The number of integer registers used so far. For all ABIs except
+ EABI, this is the number of words that have been added to the
+ argument structure, limited to MAX_ARGS_IN_REGISTERS. */
+ unsigned int num_gprs;
+
+ /* For EABI, the number of floating-point registers used so far. */
+ unsigned int num_fprs;
+
+ /* The number of words passed on the stack. */
+ unsigned int stack_words;
+
+ /* On the mips16, we need to keep track of which floating point
+ arguments were passed in general registers, but would have been
+ passed in the FP regs if this were a 32-bit function, so that we
+ can move them to the FP regs if we wind up calling a 32-bit
+ function. We record this information in fp_code, encoded in base
+ four. A zero digit means no floating point argument, a one digit
+ means an SFmode argument, and a two digit means a DFmode argument,
+ and a three digit is not used. The low order digit is the first
+ argument. Thus 6 == 1 * 4 + 2 means a DFmode argument followed by
+ an SFmode argument. ??? A more sophisticated approach will be
+ needed if MIPS_ABI != ABI_32. */
+ int fp_code;
+
+ /* True if the function has a prototype. */
+ int prototype;
+} CUMULATIVE_ARGS;
+
+/* Initialize a variable CUM of type CUMULATIVE_ARGS
+ for a call to a function whose data type is FNTYPE.
+ For a library call, FNTYPE is 0. */
+
+#define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME, INDIRECT, N_NAMED_ARGS) \
+ mips_init_cumulative_args (&CUM, FNTYPE)
+
+#define FUNCTION_ARG_PADDING(MODE, TYPE) \
+ (mips_pad_arg_upward (MODE, TYPE) ? upward : downward)
+
+#define BLOCK_REG_PADDING(MODE, TYPE, FIRST) \
+ (mips_pad_reg_upward (MODE, TYPE) ? upward : downward)
+
+/* True if using EABI and varargs can be passed in floating-point
+ registers. Under these conditions, we need a more complex form
+ of va_list, which tracks GPR, FPR and stack arguments separately. */
+#define EABI_FLOAT_VARARGS_P \
+ (mips_abi == ABI_EABI && UNITS_PER_FPVALUE >= UNITS_PER_DOUBLE)
+
+
+#define EPILOGUE_USES(REGNO) mips_epilogue_uses (REGNO)
+
+/* Treat LOC as a byte offset from the stack pointer and round it up
+ to the next fully-aligned offset. */
+#define MIPS_STACK_ALIGN(LOC) \
+ (TARGET_NEWABI ? ((LOC) + 15) & -16 : ((LOC) + 7) & -8)
+
+
+/* Output assembler code to FILE to increment profiler label # LABELNO
+ for profiling a function entry. */
+
+#define FUNCTION_PROFILER(FILE, LABELNO) mips_function_profiler ((FILE))
+
+/* The profiler preserves all interesting registers, including $31. */
+#define MIPS_SAVE_REG_FOR_PROFILING_P(REGNO) false
+
+/* No mips port has ever used the profiler counter word, so don't emit it
+ or the label for it. */
+
+#define NO_PROFILE_COUNTERS 1
+
+/* Define this macro if the code for function profiling should come
+ before the function prologue. Normally, the profiling code comes
+ after. */
+
+/* #define PROFILE_BEFORE_PROLOGUE */
+
+/* EXIT_IGNORE_STACK should be nonzero if, when returning from a function,
+ the stack pointer does not matter. The value is tested only in
+ functions that have frame pointers.
+ No definition is equivalent to always zero. */
+
+#define EXIT_IGNORE_STACK 1
+
+
+/* Trampolines are a block of code followed by two pointers. */
+
+#define TRAMPOLINE_SIZE \
+ (mips_trampoline_code_size () + GET_MODE_SIZE (ptr_mode) * 2)
+
+/* Forcing a 64-bit alignment for 32-bit targets allows us to load two
+ pointers from a single LUI base. */
+
+#define TRAMPOLINE_ALIGNMENT 64
+
+/* mips_trampoline_init calls this library function to flush
+ program and data caches. */
+
+#ifndef CACHE_FLUSH_FUNC
+#define CACHE_FLUSH_FUNC "_flush_cache"
+#endif
+
+#define MIPS_ICACHE_SYNC(ADDR, SIZE) \
+ /* Flush both caches. We need to flush the data cache in case \
+ the system has a write-back cache. */ \
+ emit_library_call (gen_rtx_SYMBOL_REF (Pmode, mips_cache_flush_func), \
+ LCT_NORMAL, VOIDmode, 3, ADDR, Pmode, SIZE, Pmode, \
+ GEN_INT (3), TYPE_MODE (integer_type_node))
+
+
+/* Addressing modes, and classification of registers for them. */
+
+#define REGNO_OK_FOR_INDEX_P(REGNO) 0
+#define REGNO_MODE_OK_FOR_BASE_P(REGNO, MODE) \
+ mips_regno_mode_ok_for_base_p (REGNO, MODE, 1)
+
+/* The macros REG_OK_FOR..._P assume that the arg is a REG rtx
+ and check its validity for a certain class.
+ We have two alternate definitions for each of them.
+ The usual definition accepts all pseudo regs; the other rejects them all.
+ The symbol REG_OK_STRICT causes the latter definition to be used.
+
+ Most source files want to accept pseudo regs in the hope that
+ they will get allocated to the class that the insn wants them to be in.
+ Some source files that are used after register allocation
+ need to be strict. */
+
+#ifndef REG_OK_STRICT
+#define REG_MODE_OK_FOR_BASE_P(X, MODE) \
+ mips_regno_mode_ok_for_base_p (REGNO (X), MODE, 0)
+#else
+#define REG_MODE_OK_FOR_BASE_P(X, MODE) \
+ mips_regno_mode_ok_for_base_p (REGNO (X), MODE, 1)
+#endif
+
+#define REG_OK_FOR_INDEX_P(X) 0
+
+
+/* Maximum number of registers that can appear in a valid memory address. */
+
+#define MAX_REGS_PER_ADDRESS 1
+
+/* Check for constness inline but use mips_legitimate_address_p
+ to check whether a constant really is an address. */
+
+#define CONSTANT_ADDRESS_P(X) \
+ (CONSTANT_P (X) && memory_address_p (SImode, X))
+
+#define LEGITIMATE_CONSTANT_P(X) (mips_const_insns (X) > 0)
+
+/* This handles the magic '..CURRENT_FUNCTION' symbol, which means
+ 'the start of the function that this code is output in'. */
+
+#define ASM_OUTPUT_LABELREF(FILE,NAME) \
+ if (strcmp (NAME, "..CURRENT_FUNCTION") == 0) \
+ asm_fprintf ((FILE), "%U%s", \
+ XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0)); \
+ else \
+ asm_fprintf ((FILE), "%U%s", (NAME))
+
+/* Flag to mark a function decl symbol that requires a long call. */
+#define SYMBOL_FLAG_LONG_CALL (SYMBOL_FLAG_MACH_DEP << 0)
+#define SYMBOL_REF_LONG_CALL_P(X) \
+ ((SYMBOL_REF_FLAGS (X) & SYMBOL_FLAG_LONG_CALL) != 0)
+
+/* This flag marks functions that cannot be lazily bound. */
+#define SYMBOL_FLAG_BIND_NOW (SYMBOL_FLAG_MACH_DEP << 1)
+#define SYMBOL_REF_BIND_NOW_P(RTX) \
+ ((SYMBOL_REF_FLAGS (RTX) & SYMBOL_FLAG_BIND_NOW) != 0)
+
+/* True if we're generating a form of MIPS16 code in which jump tables
+ are stored in the text section and encoded as 16-bit PC-relative
+ offsets. This is only possible when general text loads are allowed,
+ since the table access itself will be an "lh" instruction. */
+/* ??? 16-bit offsets can overflow in large functions. */
+#define TARGET_MIPS16_SHORT_JUMP_TABLES TARGET_MIPS16_TEXT_LOADS
+
+#define JUMP_TABLES_IN_TEXT_SECTION TARGET_MIPS16_SHORT_JUMP_TABLES
+
+#define CASE_VECTOR_MODE (TARGET_MIPS16_SHORT_JUMP_TABLES ? HImode : ptr_mode)
+
+#define CASE_VECTOR_PC_RELATIVE TARGET_MIPS16_SHORT_JUMP_TABLES
+
+/* Define this as 1 if `char' should by default be signed; else as 0. */
+#ifndef DEFAULT_SIGNED_CHAR
+#define DEFAULT_SIGNED_CHAR 1
+#endif
+
+/* Although LDC1 and SDC1 provide 64-bit moves on 32-bit targets,
+ we generally don't want to use them for copying arbitrary data.
+ A single N-word move is usually the same cost as N single-word moves. */
+#define MOVE_MAX UNITS_PER_WORD
+#define MAX_MOVE_MAX 8
+
+/* Define this macro as a C expression which is nonzero if
+ accessing less than a word of memory (i.e. a `char' or a
+ `short') is no faster than accessing a word of memory, i.e., if
+ such access require more than one instruction or if there is no
+ difference in cost between byte and (aligned) word loads.
+
+ On RISC machines, it tends to generate better code to define
+ this as 1, since it avoids making a QI or HI mode register.
+
+ But, generating word accesses for -mips16 is generally bad as shifts
+ (often extended) would be needed for byte accesses. */
+#define SLOW_BYTE_ACCESS (!TARGET_MIPS16)
+
+/* Standard MIPS integer shifts truncate the shift amount to the
+ width of the shifted operand. However, Loongson vector shifts
+ do not truncate the shift amount at all. */
+#define SHIFT_COUNT_TRUNCATED (!TARGET_LOONGSON_VECTORS)
+
+/* Value is 1 if truncating an integer of INPREC bits to OUTPREC bits
+ is done just by pretending it is already truncated. */
+#define TRULY_NOOP_TRUNCATION(OUTPREC, INPREC) \
+ (TARGET_64BIT ? ((INPREC) <= 32 || (OUTPREC) > 32) : 1)
+
+
+/* Specify the machine mode that pointers have.
+ After generation of rtl, the compiler makes no further distinction
+ between pointers and any other objects of this machine mode. */
+
+#ifndef Pmode
+#define Pmode (TARGET_64BIT && TARGET_LONG64 ? DImode : SImode)
+#endif
+
+/* Give call MEMs SImode since it is the "most permissive" mode
+ for both 32-bit and 64-bit targets. */
+
+#define FUNCTION_MODE SImode
+
+
+
+/* Define if copies to/from condition code registers should be avoided.
+
+ This is needed for the MIPS because reload_outcc is not complete;
+ it needs to handle cases where the source is a general or another
+ condition code register. */
+#define AVOID_CCMODE_COPIES
+
+/* A C expression for the cost of a branch instruction. A value of
+ 1 is the default; other values are interpreted relative to that. */
+
+#define BRANCH_COST(speed_p, predictable_p) mips_branch_cost
+#define LOGICAL_OP_NON_SHORT_CIRCUIT 0
+
+/* If defined, modifies the length assigned to instruction INSN as a
+ function of the context in which it is used. LENGTH is an lvalue
+ that contains the initially computed length of the insn and should
+ be updated with the correct length of the insn. */
+#define ADJUST_INSN_LENGTH(INSN, LENGTH) \
+ ((LENGTH) = mips_adjust_insn_length ((INSN), (LENGTH)))
+
+/* Return the asm template for a non-MIPS16 conditional branch instruction.
+ OPCODE is the opcode's mnemonic and OPERANDS is the asm template for
+ its operands. */
+#define MIPS_BRANCH(OPCODE, OPERANDS) \
+ "%*" OPCODE "%?\t" OPERANDS "%/"
+
+/* Return an asm string that forces INSN to be treated as an absolute
+ J or JAL instruction instead of an assembler macro. */
+#define MIPS_ABSOLUTE_JUMP(INSN) \
+ (TARGET_ABICALLS_PIC2 \
+ ? ".option\tpic0\n\t" INSN "\n\t.option\tpic2" \
+ : INSN)
+
+/* Return the asm template for a call. INSN is the instruction's mnemonic
+ ("j" or "jal"), OPERANDS are its operands, TARGET_OPNO is the operand
+ number of the target. SIZE_OPNO is the operand number of the argument size
+ operand that can optionally hold the call attributes. If SIZE_OPNO is not
+ -1 and the call is indirect, use the function symbol from the call
+ attributes to attach a R_MIPS_JALR relocation to the call.
+
+ When generating GOT code without explicit relocation operators,
+ all calls should use assembly macros. Otherwise, all indirect
+ calls should use "jr" or "jalr"; we will arrange to restore $gp
+ afterwards if necessary. Finally, we can only generate direct
+ calls for -mabicalls by temporarily switching to non-PIC mode. */
+#define MIPS_CALL(INSN, OPERANDS, TARGET_OPNO, SIZE_OPNO) \
+ (TARGET_USE_GOT && !TARGET_EXPLICIT_RELOCS \
+ ? "%*" INSN "\t%" #TARGET_OPNO "%/" \
+ : (REG_P (OPERANDS[TARGET_OPNO]) \
+ && mips_get_pic_call_symbol (OPERANDS, SIZE_OPNO)) \
+ ? ("%*.reloc\t1f,R_MIPS_JALR,%" #SIZE_OPNO "\n" \
+ "1:\t" INSN "r\t%" #TARGET_OPNO "%/") \
+ : REG_P (OPERANDS[TARGET_OPNO]) \
+ ? "%*" INSN "r\t%" #TARGET_OPNO "%/" \
+ : MIPS_ABSOLUTE_JUMP ("%*" INSN "\t%" #TARGET_OPNO "%/"))
+
+/* Control the assembler format that we output. */
+
+/* Output to assembler file text saying following lines
+ may contain character constants, extra white space, comments, etc. */
+
+#ifndef ASM_APP_ON
+#define ASM_APP_ON " #APP\n"
+#endif
+
+/* Output to assembler file text saying following lines
+ no longer contain unusual constructs. */
+
+#ifndef ASM_APP_OFF
+#define ASM_APP_OFF " #NO_APP\n"
+#endif
+
+#define REGISTER_NAMES \
+{ "$0", "$1", "$2", "$3", "$4", "$5", "$6", "$7", \
+ "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", \
+ "$16", "$17", "$18", "$19", "$20", "$21", "$22", "$23", \
+ "$24", "$25", "$26", "$27", "$28", "$sp", "$fp", "$31", \
+ "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7", \
+ "$f8", "$f9", "$f10", "$f11", "$f12", "$f13", "$f14", "$f15", \
+ "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23", \
+ "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31", \
+ "hi", "lo", "", "$fcc0","$fcc1","$fcc2","$fcc3","$fcc4", \
+ "$fcc5","$fcc6","$fcc7","", "$cprestore", "$arg", "$frame", "$fakec", \
+ "$c0r0", "$c0r1", "$c0r2", "$c0r3", "$c0r4", "$c0r5", "$c0r6", "$c0r7", \
+ "$c0r8", "$c0r9", "$c0r10","$c0r11","$c0r12","$c0r13","$c0r14","$c0r15", \
+ "$c0r16","$c0r17","$c0r18","$c0r19","$c0r20","$c0r21","$c0r22","$c0r23", \
+ "$c0r24","$c0r25","$c0r26","$c0r27","$c0r28","$c0r29","$c0r30","$c0r31", \
+ "$c2r0", "$c2r1", "$c2r2", "$c2r3", "$c2r4", "$c2r5", "$c2r6", "$c2r7", \
+ "$c2r8", "$c2r9", "$c2r10","$c2r11","$c2r12","$c2r13","$c2r14","$c2r15", \
+ "$c2r16","$c2r17","$c2r18","$c2r19","$c2r20","$c2r21","$c2r22","$c2r23", \
+ "$c2r24","$c2r25","$c2r26","$c2r27","$c2r28","$c2r29","$c2r30","$c2r31", \
+ "$c3r0", "$c3r1", "$c3r2", "$c3r3", "$c3r4", "$c3r5", "$c3r6", "$c3r7", \
+ "$c3r8", "$c3r9", "$c3r10","$c3r11","$c3r12","$c3r13","$c3r14","$c3r15", \
+ "$c3r16","$c3r17","$c3r18","$c3r19","$c3r20","$c3r21","$c3r22","$c3r23", \
+ "$c3r24","$c3r25","$c3r26","$c3r27","$c3r28","$c3r29","$c3r30","$c3r31", \
+ "$ac1hi","$ac1lo","$ac2hi","$ac2lo","$ac3hi","$ac3lo","$dsp_po","$dsp_sc", \
+ "$dsp_ca","$dsp_ou","$dsp_cc","$dsp_ef" }
+
+/* List the "software" names for each register. Also list the numerical
+ names for $fp and $sp. */
+
+#define ADDITIONAL_REGISTER_NAMES \
+{ \
+ { "$29", 29 + GP_REG_FIRST }, \
+ { "$30", 30 + GP_REG_FIRST }, \
+ { "at", 1 + GP_REG_FIRST }, \
+ { "v0", 2 + GP_REG_FIRST }, \
+ { "v1", 3 + GP_REG_FIRST }, \
+ { "a0", 4 + GP_REG_FIRST }, \
+ { "a1", 5 + GP_REG_FIRST }, \
+ { "a2", 6 + GP_REG_FIRST }, \
+ { "a3", 7 + GP_REG_FIRST }, \
+ { "t0", 8 + GP_REG_FIRST }, \
+ { "t1", 9 + GP_REG_FIRST }, \
+ { "t2", 10 + GP_REG_FIRST }, \
+ { "t3", 11 + GP_REG_FIRST }, \
+ { "t4", 12 + GP_REG_FIRST }, \
+ { "t5", 13 + GP_REG_FIRST }, \
+ { "t6", 14 + GP_REG_FIRST }, \
+ { "t7", 15 + GP_REG_FIRST }, \
+ { "s0", 16 + GP_REG_FIRST }, \
+ { "s1", 17 + GP_REG_FIRST }, \
+ { "s2", 18 + GP_REG_FIRST }, \
+ { "s3", 19 + GP_REG_FIRST }, \
+ { "s4", 20 + GP_REG_FIRST }, \
+ { "s5", 21 + GP_REG_FIRST }, \
+ { "s6", 22 + GP_REG_FIRST }, \
+ { "s7", 23 + GP_REG_FIRST }, \
+ { "t8", 24 + GP_REG_FIRST }, \
+ { "t9", 25 + GP_REG_FIRST }, \
+ { "k0", 26 + GP_REG_FIRST }, \
+ { "k1", 27 + GP_REG_FIRST }, \
+ { "gp", 28 + GP_REG_FIRST }, \
+ { "sp", 29 + GP_REG_FIRST }, \
+ { "fp", 30 + GP_REG_FIRST }, \
+ { "ra", 31 + GP_REG_FIRST }, \
+ ALL_COP_ADDITIONAL_REGISTER_NAMES \
+}
+
+/* This is meant to be redefined in the host dependent files. It is a
+ set of alternative names and regnums for mips coprocessors. */
+
+#define ALL_COP_ADDITIONAL_REGISTER_NAMES
+
+#define DBR_OUTPUT_SEQEND(STREAM) \
+do \
+ { \
+ /* Undo the effect of '%*'. */ \
+ mips_pop_asm_switch (&mips_nomacro); \
+ mips_pop_asm_switch (&mips_noreorder); \
+ /* Emit a blank line after the delay slot for emphasis. */ \
+ fputs ("\n", STREAM); \
+ } \
+while (0)
+
+/* mips-tfile does not understand .stabd directives. */
+#define DBX_OUTPUT_SOURCE_LINE(STREAM, LINE, COUNTER) do { \
+ dbxout_begin_stabn_sline (LINE); \
+ dbxout_stab_value_internal_label ("LM", &COUNTER); \
+} while (0)
+
+/* Use .loc directives for SDB line numbers. */
+#define SDB_OUTPUT_SOURCE_LINE(STREAM, LINE) \
+ fprintf (STREAM, "\t.loc\t%d %d\n", num_source_filenames, LINE)
+
+/* The MIPS implementation uses some labels for its own purpose. The
+ following lists what labels are created, and are all formed by the
+ pattern $L[a-z].*. The machine independent portion of GCC creates
+ labels matching: $L[A-Z][0-9]+ and $L[0-9]+.
+
+ LM[0-9]+ Silicon Graphics/ECOFF stabs label before each stmt.
+ $Lb[0-9]+ Begin blocks for MIPS debug support
+ $Lc[0-9]+ Label for use in s<xx> operation.
+ $Le[0-9]+ End blocks for MIPS debug support */
+
+#undef ASM_DECLARE_OBJECT_NAME
+#define ASM_DECLARE_OBJECT_NAME(STREAM, NAME, DECL) \
+ mips_declare_object (STREAM, NAME, "", ":\n")
+
+/* Globalizing directive for a label. */
+#define GLOBAL_ASM_OP "\t.globl\t"
+
+/* This says how to define a global common symbol. */
+
+#define ASM_OUTPUT_ALIGNED_DECL_COMMON mips_output_aligned_decl_common
+
+/* This says how to define a local common symbol (i.e., not visible to
+ linker). */
+
+#ifndef ASM_OUTPUT_ALIGNED_LOCAL
+#define ASM_OUTPUT_ALIGNED_LOCAL(STREAM, NAME, SIZE, ALIGN) \
+ mips_declare_common_object (STREAM, NAME, "\n\t.lcomm\t", SIZE, ALIGN, false)
+#endif
+
+/* This says how to output an external. It would be possible not to
+ output anything and let undefined symbol become external. However
+ the assembler uses length information on externals to allocate in
+ data/sdata bss/sbss, thereby saving exec time. */
+
+#undef ASM_OUTPUT_EXTERNAL
+#define ASM_OUTPUT_EXTERNAL(STREAM,DECL,NAME) \
+ mips_output_external(STREAM,DECL,NAME)
+
+/* This is how to declare a function name. The actual work of
+ emitting the label is moved to function_prologue, so that we can
+ get the line number correctly emitted before the .ent directive,
+ and after any .file directives. Define as empty so that the function
+ is not declared before the .ent directive elsewhere. */
+
+#undef ASM_DECLARE_FUNCTION_NAME
+#define ASM_DECLARE_FUNCTION_NAME(STREAM,NAME,DECL)
+
+/* This is how to store into the string LABEL
+ the symbol_ref name of an internal numbered label where
+ PREFIX is the class of label and NUM is the number within the class.
+ This is suitable for output with `assemble_name'. */
+
+#undef ASM_GENERATE_INTERNAL_LABEL
+#define ASM_GENERATE_INTERNAL_LABEL(LABEL,PREFIX,NUM) \
+ sprintf ((LABEL), "*%s%s%ld", (LOCAL_LABEL_PREFIX), (PREFIX), (long)(NUM))
+
+/* Print debug labels as "foo = ." rather than "foo:" because they should
+ represent a byte pointer rather than an ISA-encoded address. This is
+ particularly important for code like:
+
+ $LFBxxx = .
+ .cfi_startproc
+ ...
+ .section .gcc_except_table,...
+ ...
+ .uleb128 foo-$LFBxxx
+
+ The .uleb128 requies $LFBxxx to match the FDE start address, which is
+ likewise a byte pointer rather than an ISA-encoded address.
+
+ At the time of writing, this hook is not used for the function end
+ label:
+
+ $LFExxx:
+ .end foo
+
+ But this doesn't matter, because GAS doesn't treat a pre-.end label
+ as a MIPS16 one anyway. */
+
+#define ASM_OUTPUT_DEBUG_LABEL(FILE, PREFIX, NUM) \
+ fprintf (FILE, "%s%s%d = .\n", LOCAL_LABEL_PREFIX, PREFIX, NUM)
+
+/* This is how to output an element of a case-vector that is absolute. */
+
+#define ASM_OUTPUT_ADDR_VEC_ELT(STREAM, VALUE) \
+ fprintf (STREAM, "\t%s\t%sL%d\n", \
+ ptr_mode == DImode ? ".dword" : ".word", \
+ LOCAL_LABEL_PREFIX, \
+ VALUE)
+
+/* This is how to output an element of a case-vector. We can make the
+ entries PC-relative in MIPS16 code and GP-relative when .gp(d)word
+ is supported. */
+
+#define ASM_OUTPUT_ADDR_DIFF_ELT(STREAM, BODY, VALUE, REL) \
+do { \
+ if (TARGET_MIPS16_SHORT_JUMP_TABLES) \
+ fprintf (STREAM, "\t.half\t%sL%d-%sL%d\n", \
+ LOCAL_LABEL_PREFIX, VALUE, LOCAL_LABEL_PREFIX, REL); \
+ else if (TARGET_GPWORD) \
+ fprintf (STREAM, "\t%s\t%sL%d\n", \
+ ptr_mode == DImode ? ".gpdword" : ".gpword", \
+ LOCAL_LABEL_PREFIX, VALUE); \
+ else if (TARGET_RTP_PIC) \
+ { \
+ /* Make the entry relative to the start of the function. */ \
+ rtx fnsym = XEXP (DECL_RTL (current_function_decl), 0); \
+ fprintf (STREAM, "\t%s\t%sL%d-", \
+ Pmode == DImode ? ".dword" : ".word", \
+ LOCAL_LABEL_PREFIX, VALUE); \
+ assemble_name (STREAM, XSTR (fnsym, 0)); \
+ fprintf (STREAM, "\n"); \
+ } \
+ else \
+ fprintf (STREAM, "\t%s\t%sL%d\n", \
+ ptr_mode == DImode ? ".dword" : ".word", \
+ LOCAL_LABEL_PREFIX, VALUE); \
+} while (0)
+
+/* This is how to output an assembler line
+ that says to advance the location counter
+ to a multiple of 2**LOG bytes. */
+
+#define ASM_OUTPUT_ALIGN(STREAM,LOG) \
+ fprintf (STREAM, "\t.align\t%d\n", (LOG))
+
+/* This is how to output an assembler line to advance the location
+ counter by SIZE bytes. */
+
+#undef ASM_OUTPUT_SKIP
+#define ASM_OUTPUT_SKIP(STREAM,SIZE) \
+ fprintf (STREAM, "\t.space\t"HOST_WIDE_INT_PRINT_UNSIGNED"\n", (SIZE))
+
+/* This is how to output a string. */
+#undef ASM_OUTPUT_ASCII
+#define ASM_OUTPUT_ASCII mips_output_ascii
+
+/* Output #ident as a in the read-only data section. */
+#undef ASM_OUTPUT_IDENT
+#define ASM_OUTPUT_IDENT(FILE, STRING) \
+{ \
+ const char *p = STRING; \
+ int size = strlen (p) + 1; \
+ switch_to_section (readonly_data_section); \
+ assemble_string (p, size); \
+}
+
+/* Default to -G 8 */
+#ifndef MIPS_DEFAULT_GVALUE
+#define MIPS_DEFAULT_GVALUE 8
+#endif
+
+/* Define the strings to put out for each section in the object file. */
+#define TEXT_SECTION_ASM_OP "\t.text" /* instructions */
+#define DATA_SECTION_ASM_OP "\t.data" /* large data */
+
+#undef READONLY_DATA_SECTION_ASM_OP
+#define READONLY_DATA_SECTION_ASM_OP "\t.rdata" /* read-only data */
+
+#define ASM_OUTPUT_REG_PUSH(STREAM,REGNO) \
+do \
+ { \
+ fprintf (STREAM, "\t%s\t%s,%s,-8\n\t%s\t%s,0(%s)\n", \
+ TARGET_64BIT ? "daddiu" : "addiu", \
+ reg_names[STACK_POINTER_REGNUM], \
+ reg_names[STACK_POINTER_REGNUM], \
+ TARGET_64BIT ? "sd" : "sw", \
+ reg_names[REGNO], \
+ reg_names[STACK_POINTER_REGNUM]); \
+ } \
+while (0)
+
+#define ASM_OUTPUT_REG_POP(STREAM,REGNO) \
+do \
+ { \
+ mips_push_asm_switch (&mips_noreorder); \
+ fprintf (STREAM, "\t%s\t%s,0(%s)\n\t%s\t%s,%s,8\n", \
+ TARGET_64BIT ? "ld" : "lw", \
+ reg_names[REGNO], \
+ reg_names[STACK_POINTER_REGNUM], \
+ TARGET_64BIT ? "daddu" : "addu", \
+ reg_names[STACK_POINTER_REGNUM], \
+ reg_names[STACK_POINTER_REGNUM]); \
+ mips_pop_asm_switch (&mips_noreorder); \
+ } \
+while (0)
+
+/* How to start an assembler comment.
+ The leading space is important (the mips native assembler requires it). */
+#ifndef ASM_COMMENT_START
+#define ASM_COMMENT_START " #"
+#endif
+
+#undef SIZE_TYPE
+#define SIZE_TYPE (POINTER_SIZE == 64 ? "long unsigned int" : "unsigned int")
+
+#undef PTRDIFF_TYPE
+#define PTRDIFF_TYPE (POINTER_SIZE == 64 ? "long int" : "int")
+
+/* The maximum number of bytes that can be copied by one iteration of
+ a movmemsi loop; see mips_block_move_loop. */
+#define MIPS_MAX_MOVE_BYTES_PER_LOOP_ITER \
+ (UNITS_PER_WORD * 4)
+
+/* The maximum number of bytes that can be copied by a straight-line
+ implementation of movmemsi; see mips_block_move_straight. We want
+ to make sure that any loop-based implementation will iterate at
+ least twice. */
+#define MIPS_MAX_MOVE_BYTES_STRAIGHT \
+ (MIPS_MAX_MOVE_BYTES_PER_LOOP_ITER * 2)
+
+/* The base cost of a memcpy call, for MOVE_RATIO and friends. These
+ values were determined experimentally by benchmarking with CSiBE.
+ In theory, the call overhead is higher for TARGET_ABICALLS (especially
+ for o32 where we have to restore $gp afterwards as well as make an
+ indirect call), but in practice, bumping this up higher for
+ TARGET_ABICALLS doesn't make much difference to code size. */
+
+#define MIPS_CALL_RATIO 8
+
+/* Any loop-based implementation of movmemsi will have at least
+ MIPS_MAX_MOVE_BYTES_STRAIGHT / UNITS_PER_WORD memory-to-memory
+ moves, so allow individual copies of fewer elements.
+
+ When movmemsi is not available, use a value approximating
+ the length of a memcpy call sequence, so that move_by_pieces
+ will generate inline code if it is shorter than a function call.
+ Since move_by_pieces_ninsns counts memory-to-memory moves, but
+ we'll have to generate a load/store pair for each, halve the
+ value of MIPS_CALL_RATIO to take that into account. */
+
+#define MOVE_RATIO(speed) \
+ (HAVE_movmemsi \
+ ? MIPS_MAX_MOVE_BYTES_STRAIGHT / MOVE_MAX \
+ : MIPS_CALL_RATIO / 2)
+
+/* movmemsi is meant to generate code that is at least as good as
+ move_by_pieces. However, movmemsi effectively uses a by-pieces
+ implementation both for moves smaller than a word and for word-aligned
+ moves of no more than MIPS_MAX_MOVE_BYTES_STRAIGHT bytes. We should
+ allow the tree-level optimisers to do such moves by pieces, as it
+ often exposes other optimization opportunities. We might as well
+ continue to use movmemsi at the rtl level though, as it produces
+ better code when scheduling is disabled (such as at -O). */
+
+#define MOVE_BY_PIECES_P(SIZE, ALIGN) \
+ (HAVE_movmemsi \
+ ? (!currently_expanding_to_rtl \
+ && ((ALIGN) < BITS_PER_WORD \
+ ? (SIZE) < UNITS_PER_WORD \
+ : (SIZE) <= MIPS_MAX_MOVE_BYTES_STRAIGHT)) \
+ : (move_by_pieces_ninsns (SIZE, ALIGN, MOVE_MAX_PIECES + 1) \
+ < (unsigned int) MOVE_RATIO (false)))
+
+/* For CLEAR_RATIO, when optimizing for size, give a better estimate
+ of the length of a memset call, but use the default otherwise. */
+
+#define CLEAR_RATIO(speed)\
+ ((speed) ? 15 : MIPS_CALL_RATIO)
+
+/* This is similar to CLEAR_RATIO, but for a non-zero constant, so when
+ optimizing for size adjust the ratio to account for the overhead of
+ loading the constant and replicating it across the word. */
+
+#define SET_RATIO(speed) \
+ ((speed) ? 15 : MIPS_CALL_RATIO - 2)
+
+/* STORE_BY_PIECES_P can be used when copying a constant string, but
+ in that case each word takes 3 insns (lui, ori, sw), or more in
+ 64-bit mode, instead of 2 (lw, sw). For now we always fail this
+ and let the move_by_pieces code copy the string from read-only
+ memory. In the future, this could be tuned further for multi-issue
+ CPUs that can issue stores down one pipe and arithmetic instructions
+ down another; in that case, the lui/ori/sw combination would be a
+ win for long enough strings. */
+
+#define STORE_BY_PIECES_P(SIZE, ALIGN) 0
+
+#ifndef __mips16
+/* Since the bits of the _init and _fini function is spread across
+ many object files, each potentially with its own GP, we must assume
+ we need to load our GP. We don't preserve $gp or $ra, since each
+ init/fini chunk is supposed to initialize $gp, and crti/crtn
+ already take care of preserving $ra and, when appropriate, $gp. */
+#if (defined _ABIO32 && _MIPS_SIM == _ABIO32)
+#define CRT_CALL_STATIC_FUNCTION(SECTION_OP, FUNC) \
+ asm (SECTION_OP "\n\
+ .set noreorder\n\
+ bal 1f\n\
+ nop\n\
+1: .cpload $31\n\
+ .set reorder\n\
+ jal " USER_LABEL_PREFIX #FUNC "\n\
+ " TEXT_SECTION_ASM_OP);
+#endif /* Switch to #elif when we're no longer limited by K&R C. */
+#if (defined _ABIN32 && _MIPS_SIM == _ABIN32) \
+ || (defined _ABI64 && _MIPS_SIM == _ABI64)
+#define CRT_CALL_STATIC_FUNCTION(SECTION_OP, FUNC) \
+ asm (SECTION_OP "\n\
+ .set noreorder\n\
+ bal 1f\n\
+ nop\n\
+1: .set reorder\n\
+ .cpsetup $31, $2, 1b\n\
+ jal " USER_LABEL_PREFIX #FUNC "\n\
+ " TEXT_SECTION_ASM_OP);
+#endif
+#endif
+
+#ifndef HAVE_AS_TLS
+#define HAVE_AS_TLS 0
+#endif
+
+#ifndef USED_FOR_TARGET
+/* Information about ".set noFOO; ...; .set FOO" blocks. */
+struct mips_asm_switch {
+ /* The FOO in the description above. */
+ const char *name;
+
+ /* The current block nesting level, or 0 if we aren't in a block. */
+ int nesting_level;
+};
+
+extern const enum reg_class mips_regno_to_class[];
+extern bool mips_hard_regno_mode_ok[][FIRST_PSEUDO_REGISTER];
+extern const char *current_function_file; /* filename current function is in */
+extern int num_source_filenames; /* current .file # */
+extern struct mips_asm_switch mips_noreorder;
+extern struct mips_asm_switch mips_nomacro;
+extern struct mips_asm_switch mips_noat;
+extern int mips_dbx_regno[];
+extern int mips_dwarf_regno[];
+extern bool mips_split_p[];
+extern bool mips_split_hi_p[];
+extern enum processor mips_arch; /* which cpu to codegen for */
+extern enum processor mips_tune; /* which cpu to schedule for */
+extern int mips_isa; /* architectural level */
+extern int mips_abi; /* which ABI to use */
+extern const struct mips_cpu_info *mips_arch_info;
+extern const struct mips_cpu_info *mips_tune_info;
+extern bool mips_base_mips16;
+extern enum mips_code_readable_setting mips_code_readable;
+extern GTY(()) struct target_globals *mips16_globals;
+#endif
+
+/* Enable querying of DFA units. */
+#define CPU_UNITS_QUERY 1
+
+#define FINAL_PRESCAN_INSN(INSN, OPVEC, NOPERANDS) \
+ mips_final_prescan_insn (INSN, OPVEC, NOPERANDS)
+
+/* As on most targets, we want the .eh_frame section to be read-only where
+ possible. And as on most targets, this means two things:
+
+ (a) Non-locally-binding pointers must have an indirect encoding,
+ so that the addresses in the .eh_frame section itself become
+ locally-binding.
+
+ (b) A shared library's .eh_frame section must encode locally-binding
+ pointers in a relative (relocation-free) form.
+
+ However, MIPS has traditionally not allowed directives like:
+
+ .long x-.
+
+ in cases where "x" is in a different section, or is not defined in the
+ same assembly file. We are therefore unable to emit the PC-relative
+ form required by (b) at assembly time.
+
+ Fortunately, the linker is able to convert absolute addresses into
+ PC-relative addresses on our behalf. Unfortunately, only certain
+ versions of the linker know how to do this for indirect pointers,
+ and for personality data. We must fall back on using writable
+ .eh_frame sections for shared libraries if the linker does not
+ support this feature. */
+#define ASM_PREFERRED_EH_DATA_FORMAT(CODE,GLOBAL) \
+ (((GLOBAL) ? DW_EH_PE_indirect : 0) | DW_EH_PE_absptr)
+
+/* For switching between MIPS16 and non-MIPS16 modes. */
+#define SWITCHABLE_TARGET 1
diff --git a/gcc/config/mips/mips.md b/gcc/config/mips/mips.md
new file mode 100644
index 000000000..e629db7a7
--- /dev/null
+++ b/gcc/config/mips/mips.md
@@ -0,0 +1,6486 @@
+;; Mips.md Machine Description for MIPS based processors
+;; Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
+;; 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
+;; Free Software Foundation, Inc.
+;; Contributed by A. Lichnewsky, lich@inria.inria.fr
+;; Changes by Michael Meissner, meissner@osf.org
+;; 64-bit r4000 support by Ian Lance Taylor, ian@cygnus.com, and
+;; Brendan Eich, brendan@microunity.com.
+
+;; 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/>.
+
+(define_enum "processor" [
+ r3000
+ 4kc
+ 4kp
+ 5kc
+ 5kf
+ 20kc
+ 24kc
+ 24kf2_1
+ 24kf1_1
+ 74kc
+ 74kf2_1
+ 74kf1_1
+ 74kf3_2
+ loongson_2e
+ loongson_2f
+ loongson_3a
+ m4k
+ octeon
+ r3900
+ r6000
+ r4000
+ r4100
+ r4111
+ r4120
+ r4130
+ r4300
+ r4600
+ r4650
+ r5000
+ r5400
+ r5500
+ r7000
+ r8000
+ r9000
+ r10000
+ sb1
+ sb1a
+ sr71000
+ xlr
+])
+
+(define_c_enum "unspec" [
+ ;; Unaligned accesses.
+ UNSPEC_LOAD_LEFT
+ UNSPEC_LOAD_RIGHT
+ UNSPEC_STORE_LEFT
+ UNSPEC_STORE_RIGHT
+
+ ;; Floating-point moves.
+ UNSPEC_LOAD_LOW
+ UNSPEC_LOAD_HIGH
+ UNSPEC_STORE_WORD
+ UNSPEC_MFHC1
+ UNSPEC_MTHC1
+
+ ;; HI/LO moves.
+ UNSPEC_MFHI
+ UNSPEC_MTHI
+ UNSPEC_SET_HILO
+
+ ;; GP manipulation.
+ UNSPEC_LOADGP
+ UNSPEC_COPYGP
+ UNSPEC_MOVE_GP
+ UNSPEC_POTENTIAL_CPRESTORE
+ UNSPEC_CPRESTORE
+ UNSPEC_RESTORE_GP
+ UNSPEC_EH_RETURN
+ UNSPEC_GP
+ UNSPEC_SET_GOT_VERSION
+ UNSPEC_UPDATE_GOT_VERSION
+
+ ;; Symbolic accesses.
+ UNSPEC_LOAD_CALL
+ UNSPEC_LOAD_GOT
+ UNSPEC_TLS_LDM
+ UNSPEC_TLS_GET_TP
+
+ ;; MIPS16 constant pools.
+ UNSPEC_ALIGN
+ UNSPEC_CONSTTABLE_INT
+ UNSPEC_CONSTTABLE_FLOAT
+
+ ;; Blockage and synchronisation.
+ UNSPEC_BLOCKAGE
+ UNSPEC_CLEAR_HAZARD
+ UNSPEC_RDHWR
+ UNSPEC_SYNCI
+ UNSPEC_SYNC
+
+ ;; Cache manipulation.
+ UNSPEC_MIPS_CACHE
+ UNSPEC_R10K_CACHE_BARRIER
+
+ ;; Interrupt handling.
+ UNSPEC_ERET
+ UNSPEC_DERET
+ UNSPEC_DI
+ UNSPEC_EHB
+ UNSPEC_RDPGPR
+ UNSPEC_COP0
+
+ ;; Used in a call expression in place of args_size. It's present for PIC
+ ;; indirect calls where it contains args_size and the function symbol.
+ UNSPEC_CALL_ATTR
+])
+
+(define_constants
+ [(TLS_GET_TP_REGNUM 3)
+ (RETURN_ADDR_REGNUM 31)
+ (CPRESTORE_SLOT_REGNUM 76)
+ (GOT_VERSION_REGNUM 79)
+
+ ;; PIC long branch sequences are never longer than 100 bytes.
+ (MAX_PIC_BRANCH_LENGTH 100)
+ ]
+)
+
+(include "predicates.md")
+(include "constraints.md")
+
+;; ....................
+;;
+;; Attributes
+;;
+;; ....................
+
+(define_attr "got" "unset,xgot_high,load"
+ (const_string "unset"))
+
+;; For jal instructions, this attribute is DIRECT when the target address
+;; is symbolic and INDIRECT when it is a register.
+(define_attr "jal" "unset,direct,indirect"
+ (const_string "unset"))
+
+;; This attribute is YES if the instruction is a jal macro (not a
+;; real jal instruction).
+;;
+;; jal is always a macro for TARGET_CALL_CLOBBERED_GP because it includes
+;; an instruction to restore $gp. Direct jals are also macros for
+;; !TARGET_ABSOLUTE_JUMPS because they first load the target address
+;; into a register.
+(define_attr "jal_macro" "no,yes"
+ (cond [(eq_attr "jal" "direct")
+ (symbol_ref "(TARGET_CALL_CLOBBERED_GP || !TARGET_ABSOLUTE_JUMPS
+ ? JAL_MACRO_YES : JAL_MACRO_NO)")
+ (eq_attr "jal" "indirect")
+ (symbol_ref "(TARGET_CALL_CLOBBERED_GP
+ ? JAL_MACRO_YES : JAL_MACRO_NO)")]
+ (const_string "no")))
+
+;; Classification of moves, extensions and truncations. Most values
+;; are as for "type" (see below) but there are also the following
+;; move-specific values:
+;;
+;; constN move an N-constraint integer into a MIPS16 register
+;; sll0 "sll DEST,SRC,0", which on 64-bit targets is guaranteed
+;; to produce a sign-extended DEST, even if SRC is not
+;; properly sign-extended
+;; ext_ins EXT, DEXT, INS or DINS instruction
+;; andi a single ANDI instruction
+;; loadpool move a constant into a MIPS16 register by loading it
+;; from the pool
+;; shift_shift a shift left followed by a shift right
+;; lui_movf an LUI followed by a MOVF (for d<-z CC moves)
+;;
+;; This attribute is used to determine the instruction's length and
+;; scheduling type. For doubleword moves, the attribute always describes
+;; the split instructions; in some cases, it is more appropriate for the
+;; scheduling type to be "multi" instead.
+(define_attr "move_type"
+ "unknown,load,fpload,store,fpstore,mtc,mfc,mthilo,mfhilo,move,fmove,
+ const,constN,signext,ext_ins,logical,arith,sll0,andi,loadpool,
+ shift_shift,lui_movf"
+ (const_string "unknown"))
+
+(define_attr "alu_type" "unknown,add,sub,not,nor,and,or,xor"
+ (const_string "unknown"))
+
+;; Main data type used by the insn
+(define_attr "mode" "unknown,none,QI,HI,SI,DI,TI,SF,DF,TF,FPSW"
+ (const_string "unknown"))
+
+;; True if the main data type is twice the size of a word.
+(define_attr "dword_mode" "no,yes"
+ (cond [(and (eq_attr "mode" "DI,DF")
+ (eq (symbol_ref "TARGET_64BIT") (const_int 0)))
+ (const_string "yes")
+
+ (and (eq_attr "mode" "TI,TF")
+ (ne (symbol_ref "TARGET_64BIT") (const_int 0)))
+ (const_string "yes")]
+ (const_string "no")))
+
+;; Classification of each insn.
+;; branch conditional branch
+;; jump unconditional jump
+;; call unconditional call
+;; load load instruction(s)
+;; fpload floating point load
+;; fpidxload floating point indexed load
+;; store store instruction(s)
+;; fpstore floating point store
+;; fpidxstore floating point indexed store
+;; prefetch memory prefetch (register + offset)
+;; prefetchx memory indexed prefetch (register + register)
+;; condmove conditional moves
+;; mtc transfer to coprocessor
+;; mfc transfer from coprocessor
+;; mthilo transfer to hi/lo registers
+;; mfhilo transfer from hi/lo registers
+;; const load constant
+;; arith integer arithmetic instructions
+;; logical integer logical instructions
+;; shift integer shift instructions
+;; slt set less than instructions
+;; signext sign extend instructions
+;; clz the clz and clo instructions
+;; pop the pop instruction
+;; trap trap if instructions
+;; imul integer multiply 2 operands
+;; imul3 integer multiply 3 operands
+;; imul3nc integer multiply 3 operands without clobbering HI/LO
+;; imadd integer multiply-add
+;; idiv integer divide 2 operands
+;; idiv3 integer divide 3 operands
+;; move integer register move ({,D}ADD{,U} with rt = 0)
+;; fmove floating point register move
+;; fadd floating point add/subtract
+;; fmul floating point multiply
+;; fmadd floating point multiply-add
+;; fdiv floating point divide
+;; frdiv floating point reciprocal divide
+;; frdiv1 floating point reciprocal divide step 1
+;; frdiv2 floating point reciprocal divide step 2
+;; fabs floating point absolute value
+;; fneg floating point negation
+;; fcmp floating point compare
+;; fcvt floating point convert
+;; fsqrt floating point square root
+;; frsqrt floating point reciprocal square root
+;; frsqrt1 floating point reciprocal square root step1
+;; frsqrt2 floating point reciprocal square root step2
+;; multi multiword sequence (or user asm statements)
+;; nop no operation
+;; ghost an instruction that produces no real code
+(define_attr "type"
+ "unknown,branch,jump,call,load,fpload,fpidxload,store,fpstore,fpidxstore,
+ prefetch,prefetchx,condmove,mtc,mfc,mthilo,mfhilo,const,arith,logical,
+ shift,slt,signext,clz,pop,trap,imul,imul3,imul3nc,imadd,idiv,idiv3,move,
+ fmove,fadd,fmul,fmadd,fdiv,frdiv,frdiv1,frdiv2,fabs,fneg,fcmp,fcvt,fsqrt,
+ frsqrt,frsqrt1,frsqrt2,multi,nop,ghost"
+ (cond [(eq_attr "jal" "!unset") (const_string "call")
+ (eq_attr "got" "load") (const_string "load")
+
+ (eq_attr "alu_type" "add,sub") (const_string "arith")
+
+ (eq_attr "alu_type" "not,nor,and,or,xor") (const_string "logical")
+
+ ;; If a doubleword move uses these expensive instructions,
+ ;; it is usually better to schedule them in the same way
+ ;; as the singleword form, rather than as "multi".
+ (eq_attr "move_type" "load") (const_string "load")
+ (eq_attr "move_type" "fpload") (const_string "fpload")
+ (eq_attr "move_type" "store") (const_string "store")
+ (eq_attr "move_type" "fpstore") (const_string "fpstore")
+ (eq_attr "move_type" "mtc") (const_string "mtc")
+ (eq_attr "move_type" "mfc") (const_string "mfc")
+ (eq_attr "move_type" "mthilo") (const_string "mthilo")
+ (eq_attr "move_type" "mfhilo") (const_string "mfhilo")
+
+ ;; These types of move are always single insns.
+ (eq_attr "move_type" "fmove") (const_string "fmove")
+ (eq_attr "move_type" "loadpool") (const_string "load")
+ (eq_attr "move_type" "signext") (const_string "signext")
+ (eq_attr "move_type" "ext_ins") (const_string "arith")
+ (eq_attr "move_type" "arith") (const_string "arith")
+ (eq_attr "move_type" "logical") (const_string "logical")
+ (eq_attr "move_type" "sll0") (const_string "shift")
+ (eq_attr "move_type" "andi") (const_string "logical")
+
+ ;; These types of move are always split.
+ (eq_attr "move_type" "constN,shift_shift")
+ (const_string "multi")
+
+ ;; These types of move are split for doubleword modes only.
+ (and (eq_attr "move_type" "move,const")
+ (eq_attr "dword_mode" "yes"))
+ (const_string "multi")
+ (eq_attr "move_type" "move") (const_string "move")
+ (eq_attr "move_type" "const") (const_string "const")]
+ ;; We classify "lui_movf" as "unknown" rather than "multi"
+ ;; because we don't split it. FIXME: we should split instead.
+ (const_string "unknown")))
+
+;; Mode for conversion types (fcvt)
+;; I2S integer to float single (SI/DI to SF)
+;; I2D integer to float double (SI/DI to DF)
+;; S2I float to integer (SF to SI/DI)
+;; D2I float to integer (DF to SI/DI)
+;; D2S double to float single
+;; S2D float single to double
+
+(define_attr "cnv_mode" "unknown,I2S,I2D,S2I,D2I,D2S,S2D"
+ (const_string "unknown"))
+
+;; Is this an extended instruction in mips16 mode?
+(define_attr "extended_mips16" "no,yes"
+ (if_then_else (ior (eq_attr "move_type" "sll0")
+ (eq_attr "type" "branch")
+ (eq_attr "jal" "direct"))
+ (const_string "yes")
+ (const_string "no")))
+
+;; Attributes describing a sync loop. These loops have the form:
+;;
+;; if (RELEASE_BARRIER == YES) sync
+;; 1: OLDVAL = *MEM
+;; if ((OLDVAL & INCLUSIVE_MASK) != REQUIRED_OLDVAL) goto 2
+;; $TMP1 = OLDVAL & EXCLUSIVE_MASK
+;; $TMP2 = INSN1 (OLDVAL, INSN1_OP2)
+;; $TMP3 = INSN2 ($TMP2, INCLUSIVE_MASK)
+;; $AT |= $TMP1 | $TMP3
+;; if (!commit (*MEM = $AT)) goto 1.
+;; if (INSN1 != MOVE && INSN1 != LI) NEWVAL = $TMP3 [delay slot]
+;; sync
+;; 2:
+;;
+;; where "$" values are temporaries and where the other values are
+;; specified by the attributes below. Values are specified as operand
+;; numbers and insns are specified as enums. If no operand number is
+;; specified, the following values are used instead:
+;;
+;; - OLDVAL: $AT
+;; - NEWVAL: $AT
+;; - INCLUSIVE_MASK: -1
+;; - REQUIRED_OLDVAL: OLDVAL & INCLUSIVE_MASK
+;; - EXCLUSIVE_MASK: 0
+;;
+;; MEM and INSN1_OP2 are required.
+;;
+;; Ideally, the operand attributes would be integers, with -1 meaning "none",
+;; but the gen* programs don't yet support that.
+(define_attr "sync_mem" "none,0,1,2,3,4,5" (const_string "none"))
+(define_attr "sync_oldval" "none,0,1,2,3,4,5" (const_string "none"))
+(define_attr "sync_newval" "none,0,1,2,3,4,5" (const_string "none"))
+(define_attr "sync_inclusive_mask" "none,0,1,2,3,4,5" (const_string "none"))
+(define_attr "sync_exclusive_mask" "none,0,1,2,3,4,5" (const_string "none"))
+(define_attr "sync_required_oldval" "none,0,1,2,3,4,5" (const_string "none"))
+(define_attr "sync_insn1_op2" "none,0,1,2,3,4,5" (const_string "none"))
+(define_attr "sync_insn1" "move,li,addu,addiu,subu,and,andi,or,ori,xor,xori"
+ (const_string "move"))
+(define_attr "sync_insn2" "nop,and,xor,not"
+ (const_string "nop"))
+(define_attr "sync_release_barrier" "yes,no"
+ (const_string "yes"))
+
+;; Length of instruction in bytes.
+(define_attr "length" ""
+ (cond [(and (eq_attr "extended_mips16" "yes")
+ (ne (symbol_ref "TARGET_MIPS16") (const_int 0)))
+ (const_int 8)
+
+ ;; Direct branch instructions have a range of [-0x20000,0x1fffc],
+ ;; relative to the address of the delay slot. If a branch is
+ ;; outside this range, we have a choice of two sequences.
+ ;; For PIC, an out-of-range branch like:
+ ;;
+ ;; bne r1,r2,target
+ ;; dslot
+ ;;
+ ;; becomes the equivalent of:
+ ;;
+ ;; beq r1,r2,1f
+ ;; dslot
+ ;; la $at,target
+ ;; jr $at
+ ;; nop
+ ;; 1:
+ ;;
+ ;; The non-PIC case is similar except that we use a direct
+ ;; jump instead of an la/jr pair. Since the target of this
+ ;; jump is an absolute 28-bit bit address (the other bits
+ ;; coming from the address of the delay slot) this form cannot
+ ;; cross a 256MB boundary. We could provide the option of
+ ;; using la/jr in this case too, but we do not do so at
+ ;; present.
+ ;;
+ ;; Note that this value does not account for the delay slot
+ ;; instruction, whose length is added separately. If the RTL
+ ;; pattern has no explicit delay slot, mips_adjust_insn_length
+ ;; will add the length of the implicit nop. The values for
+ ;; forward and backward branches will be different as well.
+ (eq_attr "type" "branch")
+ (cond [(and (le (minus (match_dup 0) (pc)) (const_int 131064))
+ (le (minus (pc) (match_dup 0)) (const_int 131068)))
+ (const_int 4)
+
+ ;; The non-PIC case: branch, first delay slot, and J.
+ (ne (symbol_ref "TARGET_ABSOLUTE_JUMPS") (const_int 0))
+ (const_int 12)]
+
+ ;; Use MAX_PIC_BRANCH_LENGTH as a (gross) overestimate.
+ ;; mips_adjust_insn_length substitutes the correct length.
+ ;;
+ ;; Note that we can't simply use (symbol_ref ...) here
+ ;; because genattrtab needs to know the maximum length
+ ;; of an insn.
+ (const_int MAX_PIC_BRANCH_LENGTH))
+
+ ;; "Ghost" instructions occupy no space.
+ (eq_attr "type" "ghost")
+ (const_int 0)
+
+ (eq_attr "got" "load")
+ (if_then_else (ne (symbol_ref "TARGET_MIPS16") (const_int 0))
+ (const_int 8)
+ (const_int 4))
+ (eq_attr "got" "xgot_high")
+ (const_int 8)
+
+ ;; In general, constant-pool loads are extended instructions.
+ (eq_attr "move_type" "loadpool")
+ (const_int 8)
+
+ ;; LUI_MOVFs are decomposed into two separate instructions.
+ (eq_attr "move_type" "lui_movf")
+ (const_int 8)
+
+ ;; SHIFT_SHIFTs are decomposed into two separate instructions.
+ ;; They are extended instructions on MIPS16 targets.
+ (eq_attr "move_type" "shift_shift")
+ (if_then_else (ne (symbol_ref "TARGET_MIPS16") (const_int 0))
+ (const_int 16)
+ (const_int 8))
+
+ ;; Check for doubleword moves that are decomposed into two
+ ;; instructions.
+ (and (eq_attr "move_type" "mtc,mfc,mthilo,mfhilo,move")
+ (eq_attr "dword_mode" "yes"))
+ (const_int 8)
+
+ ;; Doubleword CONST{,N} moves are split into two word
+ ;; CONST{,N} moves.
+ (and (eq_attr "move_type" "const,constN")
+ (eq_attr "dword_mode" "yes"))
+ (symbol_ref "mips_split_const_insns (operands[1]) * 4")
+
+ ;; Otherwise, constants, loads and stores are handled by external
+ ;; routines.
+ (eq_attr "move_type" "const,constN")
+ (symbol_ref "mips_const_insns (operands[1]) * 4")
+ (eq_attr "move_type" "load,fpload")
+ (symbol_ref "mips_load_store_insns (operands[1], insn) * 4")
+ (eq_attr "move_type" "store,fpstore")
+ (symbol_ref "mips_load_store_insns (operands[0], insn) * 4")
+
+ ;; In the worst case, a call macro will take 8 instructions:
+ ;;
+ ;; lui $25,%call_hi(FOO)
+ ;; addu $25,$25,$28
+ ;; lw $25,%call_lo(FOO)($25)
+ ;; nop
+ ;; jalr $25
+ ;; nop
+ ;; lw $gp,X($sp)
+ ;; nop
+ (eq_attr "jal_macro" "yes")
+ (const_int 32)
+
+ ;; Various VR4120 errata require a nop to be inserted after a macc
+ ;; instruction. The assembler does this for us, so account for
+ ;; the worst-case length here.
+ (and (eq_attr "type" "imadd")
+ (ne (symbol_ref "TARGET_FIX_VR4120") (const_int 0)))
+ (const_int 8)
+
+ ;; VR4120 errata MD(4): if there are consecutive dmult instructions,
+ ;; the result of the second one is missed. The assembler should work
+ ;; around this by inserting a nop after the first dmult.
+ (and (eq_attr "type" "imul,imul3")
+ (and (eq_attr "mode" "DI")
+ (ne (symbol_ref "TARGET_FIX_VR4120") (const_int 0))))
+ (const_int 8)
+
+ (eq_attr "type" "idiv,idiv3")
+ (symbol_ref "mips_idiv_insns () * 4")
+
+ (not (eq_attr "sync_mem" "none"))
+ (symbol_ref "mips_sync_loop_insns (insn, operands) * 4")
+ ] (const_int 4)))
+
+;; Attribute describing the processor.
+(define_enum_attr "cpu" "processor"
+ (const (symbol_ref "mips_tune")))
+
+;; The type of hardware hazard associated with this instruction.
+;; DELAY means that the next instruction cannot read the result
+;; of this one. HILO means that the next two instructions cannot
+;; write to HI or LO.
+(define_attr "hazard" "none,delay,hilo"
+ (cond [(and (eq_attr "type" "load,fpload,fpidxload")
+ (ne (symbol_ref "ISA_HAS_LOAD_DELAY") (const_int 0)))
+ (const_string "delay")
+
+ (and (eq_attr "type" "mfc,mtc")
+ (ne (symbol_ref "ISA_HAS_XFER_DELAY") (const_int 0)))
+ (const_string "delay")
+
+ (and (eq_attr "type" "fcmp")
+ (ne (symbol_ref "ISA_HAS_FCMP_DELAY") (const_int 0)))
+ (const_string "delay")
+
+ ;; The r4000 multiplication patterns include an mflo instruction.
+ (and (eq_attr "type" "imul")
+ (ne (symbol_ref "TARGET_FIX_R4000") (const_int 0)))
+ (const_string "hilo")
+
+ (and (eq_attr "type" "mfhilo")
+ (eq (symbol_ref "ISA_HAS_HILO_INTERLOCKS") (const_int 0)))
+ (const_string "hilo")]
+ (const_string "none")))
+
+;; Is it a single instruction?
+(define_attr "single_insn" "no,yes"
+ (symbol_ref "(get_attr_length (insn) == (TARGET_MIPS16 ? 2 : 4)
+ ? SINGLE_INSN_YES : SINGLE_INSN_NO)"))
+
+;; Can the instruction be put into a delay slot?
+(define_attr "can_delay" "no,yes"
+ (if_then_else (and (eq_attr "type" "!branch,call,jump")
+ (and (eq_attr "hazard" "none")
+ (eq_attr "single_insn" "yes")))
+ (const_string "yes")
+ (const_string "no")))
+
+;; Attribute defining whether or not we can use the branch-likely
+;; instructions.
+(define_attr "branch_likely" "no,yes"
+ (if_then_else (ne (symbol_ref "GENERATE_BRANCHLIKELY") (const_int 0))
+ (const_string "yes")
+ (const_string "no")))
+
+;; True if an instruction might assign to hi or lo when reloaded.
+;; This is used by the TUNE_MACC_CHAINS code.
+(define_attr "may_clobber_hilo" "no,yes"
+ (if_then_else (eq_attr "type" "imul,imul3,imadd,idiv,mthilo")
+ (const_string "yes")
+ (const_string "no")))
+
+;; Describe a user's asm statement.
+(define_asm_attributes
+ [(set_attr "type" "multi")
+ (set_attr "can_delay" "no")])
+
+;; This mode iterator allows 32-bit and 64-bit GPR patterns to be generated
+;; from the same template.
+(define_mode_iterator GPR [SI (DI "TARGET_64BIT")])
+
+;; A copy of GPR that can be used when a pattern has two independent
+;; modes.
+(define_mode_iterator GPR2 [SI (DI "TARGET_64BIT")])
+
+;; This mode iterator allows :HILO to be used as the mode of the
+;; concatenated HI and LO registers.
+(define_mode_iterator HILO [(DI "!TARGET_64BIT") (TI "TARGET_64BIT")])
+
+;; This mode iterator allows :P to be used for patterns that operate on
+;; pointer-sized quantities. Exactly one of the two alternatives will match.
+(define_mode_iterator P [(SI "Pmode == SImode") (DI "Pmode == DImode")])
+
+;; This mode iterator allows :MOVECC to be used anywhere that a
+;; conditional-move-type condition is needed.
+(define_mode_iterator MOVECC [SI (DI "TARGET_64BIT")
+ (CC "TARGET_HARD_FLOAT && !TARGET_LOONGSON_2EF")])
+
+;; 32-bit integer moves for which we provide move patterns.
+(define_mode_iterator IMOVE32
+ [SI
+ (V2HI "TARGET_DSP")
+ (V4QI "TARGET_DSP")
+ (V2HQ "TARGET_DSP")
+ (V2UHQ "TARGET_DSP")
+ (V2HA "TARGET_DSP")
+ (V2UHA "TARGET_DSP")
+ (V4QQ "TARGET_DSP")
+ (V4UQQ "TARGET_DSP")])
+
+;; 64-bit modes for which we provide move patterns.
+(define_mode_iterator MOVE64
+ [DI DF
+ (V2SF "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT")
+ (V2SI "TARGET_HARD_FLOAT && TARGET_LOONGSON_VECTORS")
+ (V4HI "TARGET_HARD_FLOAT && TARGET_LOONGSON_VECTORS")
+ (V8QI "TARGET_HARD_FLOAT && TARGET_LOONGSON_VECTORS")])
+
+;; 128-bit modes for which we provide move patterns on 64-bit targets.
+(define_mode_iterator MOVE128 [TI TF])
+
+;; This mode iterator allows the QI and HI extension patterns to be
+;; defined from the same template.
+(define_mode_iterator SHORT [QI HI])
+
+;; Likewise the 64-bit truncate-and-shift patterns.
+(define_mode_iterator SUBDI [QI HI SI])
+
+;; This mode iterator allows :ANYF to be used wherever a scalar or vector
+;; floating-point mode is allowed.
+(define_mode_iterator ANYF [(SF "TARGET_HARD_FLOAT")
+ (DF "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT")
+ (V2SF "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT")])
+
+;; Like ANYF, but only applies to scalar modes.
+(define_mode_iterator SCALARF [(SF "TARGET_HARD_FLOAT")
+ (DF "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT")])
+
+;; A floating-point mode for which moves involving FPRs may need to be split.
+(define_mode_iterator SPLITF
+ [(DF "!TARGET_64BIT && TARGET_DOUBLE_FLOAT")
+ (DI "!TARGET_64BIT && TARGET_DOUBLE_FLOAT")
+ (V2SF "!TARGET_64BIT && TARGET_PAIRED_SINGLE_FLOAT")
+ (V2SI "!TARGET_64BIT && TARGET_LOONGSON_VECTORS")
+ (V4HI "!TARGET_64BIT && TARGET_LOONGSON_VECTORS")
+ (V8QI "!TARGET_64BIT && TARGET_LOONGSON_VECTORS")
+ (TF "TARGET_64BIT && TARGET_FLOAT64")])
+
+;; In GPR templates, a string like "<d>subu" will expand to "subu" in the
+;; 32-bit version and "dsubu" in the 64-bit version.
+(define_mode_attr d [(SI "") (DI "d")
+ (QQ "") (HQ "") (SQ "") (DQ "d")
+ (UQQ "") (UHQ "") (USQ "") (UDQ "d")
+ (HA "") (SA "") (DA "d")
+ (UHA "") (USA "") (UDA "d")])
+
+;; Same as d but upper-case.
+(define_mode_attr D [(SI "") (DI "D")
+ (QQ "") (HQ "") (SQ "") (DQ "D")
+ (UQQ "") (UHQ "") (USQ "") (UDQ "D")
+ (HA "") (SA "") (DA "D")
+ (UHA "") (USA "") (UDA "D")])
+
+;; This attribute gives the length suffix for a sign- or zero-extension
+;; instruction.
+(define_mode_attr size [(QI "b") (HI "h")])
+
+;; This attributes gives the mode mask of a SHORT.
+(define_mode_attr mask [(QI "0x00ff") (HI "0xffff")])
+
+;; Mode attributes for GPR loads.
+(define_mode_attr load [(SI "lw") (DI "ld")])
+;; Instruction names for stores.
+(define_mode_attr store [(QI "sb") (HI "sh") (SI "sw") (DI "sd")])
+
+;; Similarly for MIPS IV indexed FPR loads and stores.
+(define_mode_attr loadx [(SF "lwxc1") (DF "ldxc1") (V2SF "ldxc1")])
+(define_mode_attr storex [(SF "swxc1") (DF "sdxc1") (V2SF "sdxc1")])
+
+;; The unextended ranges of the MIPS16 addiu and daddiu instructions
+;; are different. Some forms of unextended addiu have an 8-bit immediate
+;; field but the equivalent daddiu has only a 5-bit field.
+(define_mode_attr si8_di5 [(SI "8") (DI "5")])
+
+;; This attribute gives the best constraint to use for registers of
+;; a given mode.
+(define_mode_attr reg [(SI "d") (DI "d") (CC "z")])
+
+;; This attribute gives the format suffix for floating-point operations.
+(define_mode_attr fmt [(SF "s") (DF "d") (V2SF "ps")])
+
+;; This attribute gives the upper-case mode name for one unit of a
+;; floating-point mode.
+(define_mode_attr UNITMODE [(SF "SF") (DF "DF") (V2SF "SF")])
+
+;; This attribute gives the integer mode that has the same size as a
+;; fixed-point mode.
+(define_mode_attr IMODE [(QQ "QI") (HQ "HI") (SQ "SI") (DQ "DI")
+ (UQQ "QI") (UHQ "HI") (USQ "SI") (UDQ "DI")
+ (HA "HI") (SA "SI") (DA "DI")
+ (UHA "HI") (USA "SI") (UDA "DI")
+ (V4UQQ "SI") (V2UHQ "SI") (V2UHA "SI")
+ (V2HQ "SI") (V2HA "SI")])
+
+;; This attribute gives the integer mode that has half the size of
+;; the controlling mode.
+(define_mode_attr HALFMODE [(DF "SI") (DI "SI") (V2SF "SI")
+ (V2SI "SI") (V4HI "SI") (V8QI "SI")
+ (TF "DI")])
+
+;; This attribute works around the early SB-1 rev2 core "F2" erratum:
+;;
+;; In certain cases, div.s and div.ps may have a rounding error
+;; and/or wrong inexact flag.
+;;
+;; Therefore, we only allow div.s if not working around SB-1 rev2
+;; errata or if a slight loss of precision is OK.
+(define_mode_attr divide_condition
+ [DF (SF "!TARGET_FIX_SB1 || flag_unsafe_math_optimizations")
+ (V2SF "TARGET_SB1 && (!TARGET_FIX_SB1 || flag_unsafe_math_optimizations)")])
+
+;; This attribute gives the conditions under which SQRT.fmt instructions
+;; can be used.
+(define_mode_attr sqrt_condition
+ [(SF "!ISA_MIPS1") (DF "!ISA_MIPS1") (V2SF "TARGET_SB1")])
+
+;; This attribute gives the conditions under which RECIP.fmt and RSQRT.fmt
+;; instructions can be used. The MIPS32 and MIPS64 ISAs say that RECIP.D
+;; and RSQRT.D are unpredictable when doubles are stored in pairs of FPRs,
+;; so for safety's sake, we apply this restriction to all targets.
+(define_mode_attr recip_condition
+ [(SF "ISA_HAS_FP4")
+ (DF "ISA_HAS_FP4 && TARGET_FLOAT64")
+ (V2SF "TARGET_SB1")])
+
+;; This code iterator allows signed and unsigned widening multiplications
+;; to use the same template.
+(define_code_iterator any_extend [sign_extend zero_extend])
+
+;; This code iterator allows the two right shift instructions to be
+;; generated from the same template.
+(define_code_iterator any_shiftrt [ashiftrt lshiftrt])
+
+;; This code iterator allows the three shift instructions to be generated
+;; from the same template.
+(define_code_iterator any_shift [ashift ashiftrt lshiftrt])
+
+;; This code iterator allows unsigned and signed division to be generated
+;; from the same template.
+(define_code_iterator any_div [div udiv])
+
+;; This code iterator allows unsigned and signed modulus to be generated
+;; from the same template.
+(define_code_iterator any_mod [mod umod])
+
+;; This code iterator allows all native floating-point comparisons to be
+;; generated from the same template.
+(define_code_iterator fcond [unordered uneq unlt unle eq lt le])
+
+;; This code iterator is used for comparisons that can be implemented
+;; by swapping the operands.
+(define_code_iterator swapped_fcond [ge gt unge ungt])
+
+;; Equality operators.
+(define_code_iterator equality_op [eq ne])
+
+;; These code iterators allow the signed and unsigned scc operations to use
+;; the same template.
+(define_code_iterator any_gt [gt gtu])
+(define_code_iterator any_ge [ge geu])
+(define_code_iterator any_lt [lt ltu])
+(define_code_iterator any_le [le leu])
+
+;; <u> expands to an empty string when doing a signed operation and
+;; "u" when doing an unsigned operation.
+(define_code_attr u [(sign_extend "") (zero_extend "u")
+ (div "") (udiv "u")
+ (mod "") (umod "u")
+ (gt "") (gtu "u")
+ (ge "") (geu "u")
+ (lt "") (ltu "u")
+ (le "") (leu "u")])
+
+;; <su> is like <u>, but the signed form expands to "s" rather than "".
+(define_code_attr su [(sign_extend "s") (zero_extend "u")])
+
+;; <optab> expands to the name of the optab for a particular code.
+(define_code_attr optab [(ashift "ashl")
+ (ashiftrt "ashr")
+ (lshiftrt "lshr")
+ (ior "ior")
+ (xor "xor")
+ (and "and")
+ (plus "add")
+ (minus "sub")])
+
+;; <insn> expands to the name of the insn that implements a particular code.
+(define_code_attr insn [(ashift "sll")
+ (ashiftrt "sra")
+ (lshiftrt "srl")
+ (ior "or")
+ (xor "xor")
+ (and "and")
+ (plus "addu")
+ (minus "subu")])
+
+;; <immediate_insn> expands to the name of the insn that implements
+;; a particular code to operate on immediate values.
+(define_code_attr immediate_insn [(ior "ori")
+ (xor "xori")
+ (and "andi")])
+
+;; <fcond> is the c.cond.fmt condition associated with a particular code.
+(define_code_attr fcond [(unordered "un")
+ (uneq "ueq")
+ (unlt "ult")
+ (unle "ule")
+ (eq "eq")
+ (lt "lt")
+ (le "le")])
+
+;; Similar, but for swapped conditions.
+(define_code_attr swapped_fcond [(ge "le")
+ (gt "lt")
+ (unge "ule")
+ (ungt "ult")])
+
+;; The value of the bit when the branch is taken for branch_bit patterns.
+;; Comparison is always against zero so this depends on the operator.
+(define_code_attr bbv [(eq "0") (ne "1")])
+
+;; This is the inverse value of bbv.
+(define_code_attr bbinv [(eq "1") (ne "0")])
+
+;; .........................
+;;
+;; Branch, call and jump delay slots
+;;
+;; .........................
+
+(define_delay (and (eq_attr "type" "branch")
+ (eq (symbol_ref "TARGET_MIPS16") (const_int 0))
+ (eq_attr "branch_likely" "yes"))
+ [(eq_attr "can_delay" "yes")
+ (nil)
+ (eq_attr "can_delay" "yes")])
+
+;; Branches that don't have likely variants do not annul on false.
+(define_delay (and (eq_attr "type" "branch")
+ (eq (symbol_ref "TARGET_MIPS16") (const_int 0))
+ (eq_attr "branch_likely" "no"))
+ [(eq_attr "can_delay" "yes")
+ (nil)
+ (nil)])
+
+(define_delay (eq_attr "type" "jump")
+ [(eq_attr "can_delay" "yes")
+ (nil)
+ (nil)])
+
+(define_delay (and (eq_attr "type" "call")
+ (eq_attr "jal_macro" "no"))
+ [(eq_attr "can_delay" "yes")
+ (nil)
+ (nil)])
+
+;; Pipeline descriptions.
+;;
+;; generic.md provides a fallback for processors without a specific
+;; pipeline description. It is derived from the old define_function_unit
+;; version and uses the "alu" and "imuldiv" units declared below.
+;;
+;; Some of the processor-specific files are also derived from old
+;; define_function_unit descriptions and simply override the parts of
+;; generic.md that don't apply. The other processor-specific files
+;; are self-contained.
+(define_automaton "alu,imuldiv")
+
+(define_cpu_unit "alu" "alu")
+(define_cpu_unit "imuldiv" "imuldiv")
+
+;; Ghost instructions produce no real code and introduce no hazards.
+;; They exist purely to express an effect on dataflow.
+(define_insn_reservation "ghost" 0
+ (eq_attr "type" "ghost")
+ "nothing")
+
+(include "4k.md")
+(include "5k.md")
+(include "20kc.md")
+(include "24k.md")
+(include "74k.md")
+(include "3000.md")
+(include "4000.md")
+(include "4100.md")
+(include "4130.md")
+(include "4300.md")
+(include "4600.md")
+(include "5000.md")
+(include "5400.md")
+(include "5500.md")
+(include "6000.md")
+(include "7000.md")
+(include "9000.md")
+(include "10000.md")
+(include "loongson2ef.md")
+(include "loongson3a.md")
+(include "octeon.md")
+(include "sb1.md")
+(include "sr71k.md")
+(include "xlr.md")
+(include "generic.md")
+
+;;
+;; ....................
+;;
+;; CONDITIONAL TRAPS
+;;
+;; ....................
+;;
+
+(define_insn "trap"
+ [(trap_if (const_int 1) (const_int 0))]
+ ""
+{
+ if (ISA_HAS_COND_TRAP)
+ return "teq\t$0,$0";
+ else if (TARGET_MIPS16)
+ return "break 0";
+ else
+ return "break";
+}
+ [(set_attr "type" "trap")])
+
+(define_expand "ctrap<mode>4"
+ [(trap_if (match_operator 0 "comparison_operator"
+ [(match_operand:GPR 1 "reg_or_0_operand")
+ (match_operand:GPR 2 "arith_operand")])
+ (match_operand 3 "const_0_operand"))]
+ "ISA_HAS_COND_TRAP"
+{
+ mips_expand_conditional_trap (operands[0]);
+ DONE;
+})
+
+(define_insn "*conditional_trap<mode>"
+ [(trap_if (match_operator:GPR 0 "trap_comparison_operator"
+ [(match_operand:GPR 1 "reg_or_0_operand" "dJ")
+ (match_operand:GPR 2 "arith_operand" "dI")])
+ (const_int 0))]
+ "ISA_HAS_COND_TRAP"
+ "t%C0\t%z1,%2"
+ [(set_attr "type" "trap")])
+
+;;
+;; ....................
+;;
+;; ADDITION
+;;
+;; ....................
+;;
+
+(define_insn "add<mode>3"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (plus:ANYF (match_operand:ANYF 1 "register_operand" "f")
+ (match_operand:ANYF 2 "register_operand" "f")))]
+ ""
+ "add.<fmt>\t%0,%1,%2"
+ [(set_attr "type" "fadd")
+ (set_attr "mode" "<UNITMODE>")])
+
+(define_expand "add<mode>3"
+ [(set (match_operand:GPR 0 "register_operand")
+ (plus:GPR (match_operand:GPR 1 "register_operand")
+ (match_operand:GPR 2 "arith_operand")))]
+ "")
+
+(define_insn "*add<mode>3"
+ [(set (match_operand:GPR 0 "register_operand" "=d,d")
+ (plus:GPR (match_operand:GPR 1 "register_operand" "d,d")
+ (match_operand:GPR 2 "arith_operand" "d,Q")))]
+ "!TARGET_MIPS16"
+ "@
+ <d>addu\t%0,%1,%2
+ <d>addiu\t%0,%1,%2"
+ [(set_attr "alu_type" "add")
+ (set_attr "mode" "<MODE>")])
+
+(define_insn "*add<mode>3_mips16"
+ [(set (match_operand:GPR 0 "register_operand" "=ks,d,d,d,d")
+ (plus:GPR (match_operand:GPR 1 "register_operand" "ks,ks,0,d,d")
+ (match_operand:GPR 2 "arith_operand" "Q,Q,Q,O,d")))]
+ "TARGET_MIPS16"
+ "@
+ <d>addiu\t%0,%2
+ <d>addiu\t%0,%1,%2
+ <d>addiu\t%0,%2
+ <d>addiu\t%0,%1,%2
+ <d>addu\t%0,%1,%2"
+ [(set_attr "alu_type" "add")
+ (set_attr "mode" "<MODE>")
+ (set_attr_alternative "length"
+ [(if_then_else (match_operand 2 "m16_simm8_8")
+ (const_int 4)
+ (const_int 8))
+ (if_then_else (match_operand 2 "m16_uimm<si8_di5>_4")
+ (const_int 4)
+ (const_int 8))
+ (if_then_else (match_operand 2 "m16_simm<si8_di5>_1")
+ (const_int 4)
+ (const_int 8))
+ (if_then_else (match_operand 2 "m16_simm4_1")
+ (const_int 4)
+ (const_int 8))
+ (const_int 4)])])
+
+;; On the mips16, we can sometimes split an add of a constant which is
+;; a 4 byte instruction into two adds which are both 2 byte
+;; instructions. There are two cases: one where we are adding a
+;; constant plus a register to another register, and one where we are
+;; simply adding a constant to a register.
+
+(define_split
+ [(set (match_operand:SI 0 "d_operand")
+ (plus:SI (match_dup 0)
+ (match_operand:SI 1 "const_int_operand")))]
+ "TARGET_MIPS16 && reload_completed && !TARGET_DEBUG_D_MODE
+ && ((INTVAL (operands[1]) > 0x7f
+ && INTVAL (operands[1]) <= 0x7f + 0x7f)
+ || (INTVAL (operands[1]) < - 0x80
+ && INTVAL (operands[1]) >= - 0x80 - 0x80))"
+ [(set (match_dup 0) (plus:SI (match_dup 0) (match_dup 1)))
+ (set (match_dup 0) (plus:SI (match_dup 0) (match_dup 2)))]
+{
+ HOST_WIDE_INT val = INTVAL (operands[1]);
+
+ if (val >= 0)
+ {
+ operands[1] = GEN_INT (0x7f);
+ operands[2] = GEN_INT (val - 0x7f);
+ }
+ else
+ {
+ operands[1] = GEN_INT (- 0x80);
+ operands[2] = GEN_INT (val + 0x80);
+ }
+})
+
+(define_split
+ [(set (match_operand:SI 0 "d_operand")
+ (plus:SI (match_operand:SI 1 "d_operand")
+ (match_operand:SI 2 "const_int_operand")))]
+ "TARGET_MIPS16 && reload_completed && !TARGET_DEBUG_D_MODE
+ && REGNO (operands[0]) != REGNO (operands[1])
+ && ((INTVAL (operands[2]) > 0x7
+ && INTVAL (operands[2]) <= 0x7 + 0x7f)
+ || (INTVAL (operands[2]) < - 0x8
+ && INTVAL (operands[2]) >= - 0x8 - 0x80))"
+ [(set (match_dup 0) (plus:SI (match_dup 1) (match_dup 2)))
+ (set (match_dup 0) (plus:SI (match_dup 0) (match_dup 3)))]
+{
+ HOST_WIDE_INT val = INTVAL (operands[2]);
+
+ if (val >= 0)
+ {
+ operands[2] = GEN_INT (0x7);
+ operands[3] = GEN_INT (val - 0x7);
+ }
+ else
+ {
+ operands[2] = GEN_INT (- 0x8);
+ operands[3] = GEN_INT (val + 0x8);
+ }
+})
+
+(define_split
+ [(set (match_operand:DI 0 "d_operand")
+ (plus:DI (match_dup 0)
+ (match_operand:DI 1 "const_int_operand")))]
+ "TARGET_MIPS16 && TARGET_64BIT && reload_completed && !TARGET_DEBUG_D_MODE
+ && ((INTVAL (operands[1]) > 0xf
+ && INTVAL (operands[1]) <= 0xf + 0xf)
+ || (INTVAL (operands[1]) < - 0x10
+ && INTVAL (operands[1]) >= - 0x10 - 0x10))"
+ [(set (match_dup 0) (plus:DI (match_dup 0) (match_dup 1)))
+ (set (match_dup 0) (plus:DI (match_dup 0) (match_dup 2)))]
+{
+ HOST_WIDE_INT val = INTVAL (operands[1]);
+
+ if (val >= 0)
+ {
+ operands[1] = GEN_INT (0xf);
+ operands[2] = GEN_INT (val - 0xf);
+ }
+ else
+ {
+ operands[1] = GEN_INT (- 0x10);
+ operands[2] = GEN_INT (val + 0x10);
+ }
+})
+
+(define_split
+ [(set (match_operand:DI 0 "d_operand")
+ (plus:DI (match_operand:DI 1 "d_operand")
+ (match_operand:DI 2 "const_int_operand")))]
+ "TARGET_MIPS16 && TARGET_64BIT && reload_completed && !TARGET_DEBUG_D_MODE
+ && REGNO (operands[0]) != REGNO (operands[1])
+ && ((INTVAL (operands[2]) > 0x7
+ && INTVAL (operands[2]) <= 0x7 + 0xf)
+ || (INTVAL (operands[2]) < - 0x8
+ && INTVAL (operands[2]) >= - 0x8 - 0x10))"
+ [(set (match_dup 0) (plus:DI (match_dup 1) (match_dup 2)))
+ (set (match_dup 0) (plus:DI (match_dup 0) (match_dup 3)))]
+{
+ HOST_WIDE_INT val = INTVAL (operands[2]);
+
+ if (val >= 0)
+ {
+ operands[2] = GEN_INT (0x7);
+ operands[3] = GEN_INT (val - 0x7);
+ }
+ else
+ {
+ operands[2] = GEN_INT (- 0x8);
+ operands[3] = GEN_INT (val + 0x8);
+ }
+})
+
+(define_insn "*addsi3_extended"
+ [(set (match_operand:DI 0 "register_operand" "=d,d")
+ (sign_extend:DI
+ (plus:SI (match_operand:SI 1 "register_operand" "d,d")
+ (match_operand:SI 2 "arith_operand" "d,Q"))))]
+ "TARGET_64BIT && !TARGET_MIPS16"
+ "@
+ addu\t%0,%1,%2
+ addiu\t%0,%1,%2"
+ [(set_attr "alu_type" "add")
+ (set_attr "mode" "SI")])
+
+;; Split this insn so that the addiu splitters can have a crack at it.
+;; Use a conservative length estimate until the split.
+(define_insn_and_split "*addsi3_extended_mips16"
+ [(set (match_operand:DI 0 "register_operand" "=d,d,d")
+ (sign_extend:DI
+ (plus:SI (match_operand:SI 1 "register_operand" "0,d,d")
+ (match_operand:SI 2 "arith_operand" "Q,O,d"))))]
+ "TARGET_64BIT && TARGET_MIPS16"
+ "#"
+ "&& reload_completed"
+ [(set (match_dup 3) (plus:SI (match_dup 1) (match_dup 2)))]
+ { operands[3] = gen_lowpart (SImode, operands[0]); }
+ [(set_attr "alu_type" "add")
+ (set_attr "mode" "SI")
+ (set_attr "extended_mips16" "yes")])
+
+;; Combiner patterns for unsigned byte-add.
+
+(define_insn "*baddu_si_eb"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (zero_extend:SI
+ (subreg:QI
+ (plus:SI (match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "register_operand" "d")) 3)))]
+ "ISA_HAS_BADDU && BYTES_BIG_ENDIAN"
+ "baddu\\t%0,%1,%2"
+ [(set_attr "alu_type" "add")])
+
+(define_insn "*baddu_si_el"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (zero_extend:SI
+ (subreg:QI
+ (plus:SI (match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "register_operand" "d")) 0)))]
+ "ISA_HAS_BADDU && !BYTES_BIG_ENDIAN"
+ "baddu\\t%0,%1,%2"
+ [(set_attr "alu_type" "add")])
+
+(define_insn "*baddu_di<mode>"
+ [(set (match_operand:GPR 0 "register_operand" "=d")
+ (zero_extend:GPR
+ (truncate:QI
+ (plus:DI (match_operand:DI 1 "register_operand" "d")
+ (match_operand:DI 2 "register_operand" "d")))))]
+ "ISA_HAS_BADDU && TARGET_64BIT"
+ "baddu\\t%0,%1,%2"
+ [(set_attr "alu_type" "add")])
+
+;;
+;; ....................
+;;
+;; SUBTRACTION
+;;
+;; ....................
+;;
+
+(define_insn "sub<mode>3"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (minus:ANYF (match_operand:ANYF 1 "register_operand" "f")
+ (match_operand:ANYF 2 "register_operand" "f")))]
+ ""
+ "sub.<fmt>\t%0,%1,%2"
+ [(set_attr "type" "fadd")
+ (set_attr "mode" "<UNITMODE>")])
+
+(define_insn "sub<mode>3"
+ [(set (match_operand:GPR 0 "register_operand" "=d")
+ (minus:GPR (match_operand:GPR 1 "register_operand" "d")
+ (match_operand:GPR 2 "register_operand" "d")))]
+ ""
+ "<d>subu\t%0,%1,%2"
+ [(set_attr "alu_type" "sub")
+ (set_attr "mode" "<MODE>")])
+
+(define_insn "*subsi3_extended"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (sign_extend:DI
+ (minus:SI (match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "register_operand" "d"))))]
+ "TARGET_64BIT"
+ "subu\t%0,%1,%2"
+ [(set_attr "alu_type" "sub")
+ (set_attr "mode" "DI")])
+
+;;
+;; ....................
+;;
+;; MULTIPLICATION
+;;
+;; ....................
+;;
+
+(define_expand "mul<mode>3"
+ [(set (match_operand:SCALARF 0 "register_operand")
+ (mult:SCALARF (match_operand:SCALARF 1 "register_operand")
+ (match_operand:SCALARF 2 "register_operand")))]
+ ""
+ "")
+
+(define_insn "*mul<mode>3"
+ [(set (match_operand:SCALARF 0 "register_operand" "=f")
+ (mult:SCALARF (match_operand:SCALARF 1 "register_operand" "f")
+ (match_operand:SCALARF 2 "register_operand" "f")))]
+ "!TARGET_4300_MUL_FIX"
+ "mul.<fmt>\t%0,%1,%2"
+ [(set_attr "type" "fmul")
+ (set_attr "mode" "<MODE>")])
+
+;; Early VR4300 silicon has a CPU bug where multiplies with certain
+;; operands may corrupt immediately following multiplies. This is a
+;; simple fix to insert NOPs.
+
+(define_insn "*mul<mode>3_r4300"
+ [(set (match_operand:SCALARF 0 "register_operand" "=f")
+ (mult:SCALARF (match_operand:SCALARF 1 "register_operand" "f")
+ (match_operand:SCALARF 2 "register_operand" "f")))]
+ "TARGET_4300_MUL_FIX"
+ "mul.<fmt>\t%0,%1,%2\;nop"
+ [(set_attr "type" "fmul")
+ (set_attr "mode" "<MODE>")
+ (set_attr "length" "8")])
+
+(define_insn "mulv2sf3"
+ [(set (match_operand:V2SF 0 "register_operand" "=f")
+ (mult:V2SF (match_operand:V2SF 1 "register_operand" "f")
+ (match_operand:V2SF 2 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
+ "mul.ps\t%0,%1,%2"
+ [(set_attr "type" "fmul")
+ (set_attr "mode" "SF")])
+
+;; The original R4000 has a cpu bug. If a double-word or a variable
+;; shift executes while an integer multiplication is in progress, the
+;; shift may give an incorrect result. Avoid this by keeping the mflo
+;; with the mult on the R4000.
+;;
+;; From "MIPS R4000PC/SC Errata, Processor Revision 2.2 and 3.0"
+;; (also valid for MIPS R4000MC processors):
+;;
+;; "16. R4000PC, R4000SC: Please refer to errata 28 for an update to
+;; this errata description.
+;; The following code sequence causes the R4000 to incorrectly
+;; execute the Double Shift Right Arithmetic 32 (dsra32)
+;; instruction. If the dsra32 instruction is executed during an
+;; integer multiply, the dsra32 will only shift by the amount in
+;; specified in the instruction rather than the amount plus 32
+;; bits.
+;; instruction 1: mult rs,rt integer multiply
+;; instruction 2-12: dsra32 rd,rt,rs doubleword shift
+;; right arithmetic + 32
+;; Workaround: A dsra32 instruction placed after an integer
+;; multiply should not be one of the 11 instructions after the
+;; multiply instruction."
+;;
+;; and:
+;;
+;; "28. R4000PC, R4000SC: The text from errata 16 should be replaced by
+;; the following description.
+;; All extended shifts (shift by n+32) and variable shifts (32 and
+;; 64-bit versions) may produce incorrect results under the
+;; following conditions:
+;; 1) An integer multiply is currently executing
+;; 2) These types of shift instructions are executed immediately
+;; following an integer divide instruction.
+;; Workaround:
+;; 1) Make sure no integer multiply is running wihen these
+;; instruction are executed. If this cannot be predicted at
+;; compile time, then insert a "mfhi" to R0 instruction
+;; immediately after the integer multiply instruction. This
+;; will cause the integer multiply to complete before the shift
+;; is executed.
+;; 2) Separate integer divide and these two classes of shift
+;; instructions by another instruction or a noop."
+;;
+;; These processors have PRId values of 0x00004220 and 0x00004300,
+;; respectively.
+
+(define_expand "mul<mode>3"
+ [(set (match_operand:GPR 0 "register_operand")
+ (mult:GPR (match_operand:GPR 1 "register_operand")
+ (match_operand:GPR 2 "register_operand")))]
+ ""
+{
+ if (TARGET_LOONGSON_2EF || TARGET_LOONGSON_3A)
+ emit_insn (gen_mul<mode>3_mul3_loongson (operands[0], operands[1],
+ operands[2]));
+ else if (ISA_HAS_<D>MUL3)
+ emit_insn (gen_mul<mode>3_mul3 (operands[0], operands[1], operands[2]));
+ else if (TARGET_FIX_R4000)
+ emit_insn (gen_mul<mode>3_r4000 (operands[0], operands[1], operands[2]));
+ else
+ emit_insn
+ (gen_mul<mode>3_internal (operands[0], operands[1], operands[2]));
+ DONE;
+})
+
+(define_insn "mul<mode>3_mul3_loongson"
+ [(set (match_operand:GPR 0 "register_operand" "=d")
+ (mult:GPR (match_operand:GPR 1 "register_operand" "d")
+ (match_operand:GPR 2 "register_operand" "d")))]
+ "TARGET_LOONGSON_2EF || TARGET_LOONGSON_3A"
+{
+ if (TARGET_LOONGSON_2EF)
+ return "<d>multu.g\t%0,%1,%2";
+ else
+ return "gs<d>multu\t%0,%1,%2";
+}
+ [(set_attr "type" "imul3nc")
+ (set_attr "mode" "<MODE>")])
+
+(define_insn "mul<mode>3_mul3"
+ [(set (match_operand:GPR 0 "register_operand" "=d,l")
+ (mult:GPR (match_operand:GPR 1 "register_operand" "d,d")
+ (match_operand:GPR 2 "register_operand" "d,d")))
+ (clobber (match_scratch:GPR 3 "=l,X"))]
+ "ISA_HAS_<D>MUL3"
+{
+ if (which_alternative == 1)
+ return "<d>mult\t%1,%2";
+ if (<MODE>mode == SImode && TARGET_MIPS3900)
+ return "mult\t%0,%1,%2";
+ return "<d>mul\t%0,%1,%2";
+}
+ [(set_attr "type" "imul3,imul")
+ (set_attr "mode" "<MODE>")])
+
+;; If a register gets allocated to LO, and we spill to memory, the reload
+;; will include a move from LO to a GPR. Merge it into the multiplication
+;; if it can set the GPR directly.
+;;
+;; Operand 0: LO
+;; Operand 1: GPR (1st multiplication operand)
+;; Operand 2: GPR (2nd multiplication operand)
+;; Operand 3: GPR (destination)
+(define_peephole2
+ [(parallel
+ [(set (match_operand:SI 0 "lo_operand")
+ (mult:SI (match_operand:SI 1 "d_operand")
+ (match_operand:SI 2 "d_operand")))
+ (clobber (scratch:SI))])
+ (set (match_operand:SI 3 "d_operand")
+ (match_dup 0))]
+ "ISA_HAS_MUL3 && peep2_reg_dead_p (2, operands[0])"
+ [(parallel
+ [(set (match_dup 3)
+ (mult:SI (match_dup 1)
+ (match_dup 2)))
+ (clobber (match_dup 0))])])
+
+(define_insn "mul<mode>3_internal"
+ [(set (match_operand:GPR 0 "register_operand" "=l")
+ (mult:GPR (match_operand:GPR 1 "register_operand" "d")
+ (match_operand:GPR 2 "register_operand" "d")))]
+ "!TARGET_FIX_R4000"
+ "<d>mult\t%1,%2"
+ [(set_attr "type" "imul")
+ (set_attr "mode" "<MODE>")])
+
+(define_insn "mul<mode>3_r4000"
+ [(set (match_operand:GPR 0 "register_operand" "=d")
+ (mult:GPR (match_operand:GPR 1 "register_operand" "d")
+ (match_operand:GPR 2 "register_operand" "d")))
+ (clobber (match_scratch:GPR 3 "=l"))]
+ "TARGET_FIX_R4000"
+ "<d>mult\t%1,%2\;mflo\t%0"
+ [(set_attr "type" "imul")
+ (set_attr "mode" "<MODE>")
+ (set_attr "length" "8")])
+
+;; On the VR4120 and VR4130, it is better to use "mtlo $0; macc" instead
+;; of "mult; mflo". They have the same latency, but the first form gives
+;; us an extra cycle to compute the operands.
+
+;; Operand 0: LO
+;; Operand 1: GPR (1st multiplication operand)
+;; Operand 2: GPR (2nd multiplication operand)
+;; Operand 3: GPR (destination)
+(define_peephole2
+ [(set (match_operand:SI 0 "lo_operand")
+ (mult:SI (match_operand:SI 1 "d_operand")
+ (match_operand:SI 2 "d_operand")))
+ (set (match_operand:SI 3 "d_operand")
+ (match_dup 0))]
+ "ISA_HAS_MACC && !ISA_HAS_MUL3"
+ [(set (match_dup 0)
+ (const_int 0))
+ (parallel
+ [(set (match_dup 0)
+ (plus:SI (mult:SI (match_dup 1)
+ (match_dup 2))
+ (match_dup 0)))
+ (set (match_dup 3)
+ (plus:SI (mult:SI (match_dup 1)
+ (match_dup 2))
+ (match_dup 0)))])])
+
+;; Multiply-accumulate patterns
+
+;; This pattern is first matched by combine, which tries to use the
+;; pattern wherever it can. We don't know until later whether it
+;; is actually profitable to use MADD over a "MUL; ADDIU" sequence,
+;; so we need to keep both options open.
+;;
+;; The second alternative has a "?" marker because it is generally
+;; one instruction more costly than the first alternative. This "?"
+;; marker is enough to convey the relative costs to the register
+;; allocator.
+;;
+;; However, reload counts reloads of operands 4 and 5 in the same way as
+;; reloads of the other operands, even though operands 4 and 5 need no
+;; copy instructions. Reload therefore thinks that the second alternative
+;; is two reloads more costly than the first. We add "*?*?" to the first
+;; alternative as a counterweight.
+(define_insn "*mul_acc_si"
+ [(set (match_operand:SI 0 "register_operand" "=l*?*?,d?")
+ (plus:SI (mult:SI (match_operand:SI 1 "register_operand" "d,d")
+ (match_operand:SI 2 "register_operand" "d,d"))
+ (match_operand:SI 3 "register_operand" "0,d")))
+ (clobber (match_scratch:SI 4 "=X,l"))
+ (clobber (match_scratch:SI 5 "=X,&d"))]
+ "GENERATE_MADD_MSUB && !TARGET_MIPS16"
+ "@
+ madd\t%1,%2
+ #"
+ [(set_attr "type" "imadd")
+ (set_attr "mode" "SI")
+ (set_attr "length" "4,8")])
+
+;; The same idea applies here. The middle alternative needs one less
+;; clobber than the final alternative, so we add "*?" as a counterweight.
+(define_insn "*mul_acc_si_r3900"
+ [(set (match_operand:SI 0 "register_operand" "=l*?*?,d*?,d?")
+ (plus:SI (mult:SI (match_operand:SI 1 "register_operand" "d,d,d")
+ (match_operand:SI 2 "register_operand" "d,d,d"))
+ (match_operand:SI 3 "register_operand" "0,l,d")))
+ (clobber (match_scratch:SI 4 "=X,3,l"))
+ (clobber (match_scratch:SI 5 "=X,X,&d"))]
+ "TARGET_MIPS3900 && !TARGET_MIPS16"
+ "@
+ madd\t%1,%2
+ madd\t%0,%1,%2
+ #"
+ [(set_attr "type" "imadd")
+ (set_attr "mode" "SI")
+ (set_attr "length" "4,4,8")])
+
+;; Split *mul_acc_si if both the source and destination accumulator
+;; values are GPRs.
+(define_split
+ [(set (match_operand:SI 0 "d_operand")
+ (plus:SI (mult:SI (match_operand:SI 1 "d_operand")
+ (match_operand:SI 2 "d_operand"))
+ (match_operand:SI 3 "d_operand")))
+ (clobber (match_operand:SI 4 "lo_operand"))
+ (clobber (match_operand:SI 5 "d_operand"))]
+ "reload_completed"
+ [(parallel [(set (match_dup 5)
+ (mult:SI (match_dup 1) (match_dup 2)))
+ (clobber (match_dup 4))])
+ (set (match_dup 0) (plus:SI (match_dup 5) (match_dup 3)))]
+ "")
+
+(define_insn "*macc"
+ [(set (match_operand:SI 0 "register_operand" "=l,d")
+ (plus:SI (mult:SI (match_operand:SI 1 "register_operand" "d,d")
+ (match_operand:SI 2 "register_operand" "d,d"))
+ (match_operand:SI 3 "register_operand" "0,l")))
+ (clobber (match_scratch:SI 4 "=X,3"))]
+ "ISA_HAS_MACC"
+{
+ if (which_alternative == 1)
+ return "macc\t%0,%1,%2";
+ else if (TARGET_MIPS5500)
+ return "madd\t%1,%2";
+ else
+ /* The VR4130 assumes that there is a two-cycle latency between a macc
+ that "writes" to $0 and an instruction that reads from it. We avoid
+ this by assigning to $1 instead. */
+ return "%[macc\t%@,%1,%2%]";
+}
+ [(set_attr "type" "imadd")
+ (set_attr "mode" "SI")])
+
+(define_insn "*msac"
+ [(set (match_operand:SI 0 "register_operand" "=l,d")
+ (minus:SI (match_operand:SI 1 "register_operand" "0,l")
+ (mult:SI (match_operand:SI 2 "register_operand" "d,d")
+ (match_operand:SI 3 "register_operand" "d,d"))))
+ (clobber (match_scratch:SI 4 "=X,1"))]
+ "ISA_HAS_MSAC"
+{
+ if (which_alternative == 1)
+ return "msac\t%0,%2,%3";
+ else if (TARGET_MIPS5500)
+ return "msub\t%2,%3";
+ else
+ return "msac\t$0,%2,%3";
+}
+ [(set_attr "type" "imadd")
+ (set_attr "mode" "SI")])
+
+;; An msac-like instruction implemented using negation and a macc.
+(define_insn_and_split "*msac_using_macc"
+ [(set (match_operand:SI 0 "register_operand" "=l,d")
+ (minus:SI (match_operand:SI 1 "register_operand" "0,l")
+ (mult:SI (match_operand:SI 2 "register_operand" "d,d")
+ (match_operand:SI 3 "register_operand" "d,d"))))
+ (clobber (match_scratch:SI 4 "=X,1"))
+ (clobber (match_scratch:SI 5 "=d,d"))]
+ "ISA_HAS_MACC && !ISA_HAS_MSAC"
+ "#"
+ "&& reload_completed"
+ [(set (match_dup 5)
+ (neg:SI (match_dup 3)))
+ (parallel
+ [(set (match_dup 0)
+ (plus:SI (mult:SI (match_dup 2)
+ (match_dup 5))
+ (match_dup 1)))
+ (clobber (match_dup 4))])]
+ ""
+ [(set_attr "type" "imadd")
+ (set_attr "length" "8")])
+
+;; Patterns generated by the define_peephole2 below.
+
+(define_insn "*macc2"
+ [(set (match_operand:SI 0 "register_operand" "=l")
+ (plus:SI (mult:SI (match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "register_operand" "d"))
+ (match_dup 0)))
+ (set (match_operand:SI 3 "register_operand" "=d")
+ (plus:SI (mult:SI (match_dup 1)
+ (match_dup 2))
+ (match_dup 0)))]
+ "ISA_HAS_MACC && reload_completed"
+ "macc\t%3,%1,%2"
+ [(set_attr "type" "imadd")
+ (set_attr "mode" "SI")])
+
+(define_insn "*msac2"
+ [(set (match_operand:SI 0 "register_operand" "=l")
+ (minus:SI (match_dup 0)
+ (mult:SI (match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "register_operand" "d"))))
+ (set (match_operand:SI 3 "register_operand" "=d")
+ (minus:SI (match_dup 0)
+ (mult:SI (match_dup 1)
+ (match_dup 2))))]
+ "ISA_HAS_MSAC && reload_completed"
+ "msac\t%3,%1,%2"
+ [(set_attr "type" "imadd")
+ (set_attr "mode" "SI")])
+
+;; Convert macc $0,<r1>,<r2> & mflo <r3> into macc <r3>,<r1>,<r2>
+;; Similarly msac.
+;;
+;; Operand 0: LO
+;; Operand 1: macc/msac
+;; Operand 2: GPR (destination)
+(define_peephole2
+ [(parallel
+ [(set (match_operand:SI 0 "lo_operand")
+ (match_operand:SI 1 "macc_msac_operand"))
+ (clobber (scratch:SI))])
+ (set (match_operand:SI 2 "d_operand")
+ (match_dup 0))]
+ ""
+ [(parallel [(set (match_dup 0)
+ (match_dup 1))
+ (set (match_dup 2)
+ (match_dup 1))])])
+
+;; When we have a three-address multiplication instruction, it should
+;; be faster to do a separate multiply and add, rather than moving
+;; something into LO in order to use a macc instruction.
+;;
+;; This peephole needs a scratch register to cater for the case when one
+;; of the multiplication operands is the same as the destination.
+;;
+;; Operand 0: GPR (scratch)
+;; Operand 1: LO
+;; Operand 2: GPR (addend)
+;; Operand 3: GPR (destination)
+;; Operand 4: macc/msac
+;; Operand 5: new multiplication
+;; Operand 6: new addition/subtraction
+(define_peephole2
+ [(match_scratch:SI 0 "d")
+ (set (match_operand:SI 1 "lo_operand")
+ (match_operand:SI 2 "d_operand"))
+ (match_dup 0)
+ (parallel
+ [(set (match_operand:SI 3 "d_operand")
+ (match_operand:SI 4 "macc_msac_operand"))
+ (clobber (match_dup 1))])]
+ "ISA_HAS_MUL3 && peep2_reg_dead_p (2, operands[1])"
+ [(parallel [(set (match_dup 0)
+ (match_dup 5))
+ (clobber (match_dup 1))])
+ (set (match_dup 3)
+ (match_dup 6))]
+{
+ operands[5] = XEXP (operands[4], GET_CODE (operands[4]) == PLUS ? 0 : 1);
+ operands[6] = gen_rtx_fmt_ee (GET_CODE (operands[4]), SImode,
+ operands[2], operands[0]);
+})
+
+;; Same as above, except LO is the initial target of the macc.
+;;
+;; Operand 0: GPR (scratch)
+;; Operand 1: LO
+;; Operand 2: GPR (addend)
+;; Operand 3: macc/msac
+;; Operand 4: GPR (destination)
+;; Operand 5: new multiplication
+;; Operand 6: new addition/subtraction
+(define_peephole2
+ [(match_scratch:SI 0 "d")
+ (set (match_operand:SI 1 "lo_operand")
+ (match_operand:SI 2 "d_operand"))
+ (match_dup 0)
+ (parallel
+ [(set (match_dup 1)
+ (match_operand:SI 3 "macc_msac_operand"))
+ (clobber (scratch:SI))])
+ (match_dup 0)
+ (set (match_operand:SI 4 "d_operand")
+ (match_dup 1))]
+ "ISA_HAS_MUL3 && peep2_reg_dead_p (3, operands[1])"
+ [(parallel [(set (match_dup 0)
+ (match_dup 5))
+ (clobber (match_dup 1))])
+ (set (match_dup 4)
+ (match_dup 6))]
+{
+ operands[5] = XEXP (operands[3], GET_CODE (operands[3]) == PLUS ? 0 : 1);
+ operands[6] = gen_rtx_fmt_ee (GET_CODE (operands[3]), SImode,
+ operands[2], operands[0]);
+})
+
+;; See the comment above *mul_add_si for details.
+(define_insn "*mul_sub_si"
+ [(set (match_operand:SI 0 "register_operand" "=l*?*?,d?")
+ (minus:SI (match_operand:SI 1 "register_operand" "0,d")
+ (mult:SI (match_operand:SI 2 "register_operand" "d,d")
+ (match_operand:SI 3 "register_operand" "d,d"))))
+ (clobber (match_scratch:SI 4 "=X,l"))
+ (clobber (match_scratch:SI 5 "=X,&d"))]
+ "GENERATE_MADD_MSUB"
+ "@
+ msub\t%2,%3
+ #"
+ [(set_attr "type" "imadd")
+ (set_attr "mode" "SI")
+ (set_attr "length" "4,8")])
+
+;; Split *mul_sub_si if both the source and destination accumulator
+;; values are GPRs.
+(define_split
+ [(set (match_operand:SI 0 "d_operand")
+ (minus:SI (match_operand:SI 1 "d_operand")
+ (mult:SI (match_operand:SI 2 "d_operand")
+ (match_operand:SI 3 "d_operand"))))
+ (clobber (match_operand:SI 4 "lo_operand"))
+ (clobber (match_operand:SI 5 "d_operand"))]
+ "reload_completed"
+ [(parallel [(set (match_dup 5)
+ (mult:SI (match_dup 2) (match_dup 3)))
+ (clobber (match_dup 4))])
+ (set (match_dup 0) (minus:SI (match_dup 1) (match_dup 5)))]
+ "")
+
+(define_insn "*muls"
+ [(set (match_operand:SI 0 "register_operand" "=l,d")
+ (neg:SI (mult:SI (match_operand:SI 1 "register_operand" "d,d")
+ (match_operand:SI 2 "register_operand" "d,d"))))
+ (clobber (match_scratch:SI 3 "=X,l"))]
+ "ISA_HAS_MULS"
+ "@
+ muls\t$0,%1,%2
+ muls\t%0,%1,%2"
+ [(set_attr "type" "imul,imul3")
+ (set_attr "mode" "SI")])
+
+(define_expand "<u>mulsidi3"
+ [(set (match_operand:DI 0 "register_operand")
+ (mult:DI (any_extend:DI (match_operand:SI 1 "register_operand"))
+ (any_extend:DI (match_operand:SI 2 "register_operand"))))]
+ "mips_mulsidi3_gen_fn (<CODE>) != NULL"
+{
+ mulsidi3_gen_fn fn = mips_mulsidi3_gen_fn (<CODE>);
+ emit_insn (fn (operands[0], operands[1], operands[2]));
+ DONE;
+})
+
+;; As well as being named patterns, these instructions are used by the
+;; __builtin_mips_mult<u>() functions. We must always make those functions
+;; available if !TARGET_64BIT && ISA_HAS_DSP.
+(define_insn "<u>mulsidi3_32bit"
+ [(set (match_operand:DI 0 "register_operand" "=ka")
+ (mult:DI (any_extend:DI (match_operand:SI 1 "register_operand" "d"))
+ (any_extend:DI (match_operand:SI 2 "register_operand" "d"))))]
+ "!TARGET_64BIT && (!TARGET_FIX_R4000 || ISA_HAS_DSP)"
+{
+ if (ISA_HAS_DSP_MULT)
+ return "mult<u>\t%q0,%1,%2";
+ else
+ return "mult<u>\t%1,%2";
+}
+ [(set_attr "type" "imul")
+ (set_attr "mode" "SI")])
+
+(define_insn "<u>mulsidi3_32bit_r4000"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (mult:DI (any_extend:DI (match_operand:SI 1 "register_operand" "d"))
+ (any_extend:DI (match_operand:SI 2 "register_operand" "d"))))
+ (clobber (match_scratch:DI 3 "=x"))]
+ "!TARGET_64BIT && TARGET_FIX_R4000"
+ "mult<u>\t%1,%2\;mflo\t%L0\;mfhi\t%M0"
+ [(set_attr "type" "imul")
+ (set_attr "mode" "SI")
+ (set_attr "length" "12")])
+
+(define_insn "<u>mulsidi3_64bit"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (mult:DI (any_extend:DI (match_operand:SI 1 "register_operand" "d"))
+ (any_extend:DI (match_operand:SI 2 "register_operand" "d"))))
+ (clobber (match_scratch:TI 3 "=x"))
+ (clobber (match_scratch:DI 4 "=d"))]
+ "TARGET_64BIT && !TARGET_FIX_R4000 && !ISA_HAS_DMUL3"
+ "#"
+ [(set_attr "type" "imul")
+ (set_attr "mode" "SI")
+ (set (attr "length")
+ (if_then_else (ne (symbol_ref "ISA_HAS_EXT_INS") (const_int 0))
+ (const_int 16)
+ (const_int 28)))])
+
+(define_split
+ [(set (match_operand:DI 0 "d_operand")
+ (mult:DI (any_extend:DI (match_operand:SI 1 "d_operand"))
+ (any_extend:DI (match_operand:SI 2 "d_operand"))))
+ (clobber (match_operand:TI 3 "hilo_operand"))
+ (clobber (match_operand:DI 4 "d_operand"))]
+ "TARGET_64BIT && !TARGET_FIX_R4000 && ISA_HAS_EXT_INS && reload_completed"
+ [(set (match_dup 3)
+ (unspec:TI [(mult:DI (any_extend:DI (match_dup 1))
+ (any_extend:DI (match_dup 2)))]
+ UNSPEC_SET_HILO))
+
+ ;; OP0 <- LO, OP4 <- HI
+ (set (match_dup 0) (match_dup 5))
+ (set (match_dup 4) (unspec:DI [(match_dup 3)] UNSPEC_MFHI))
+
+ (set (zero_extract:DI (match_dup 0) (const_int 32) (const_int 32))
+ (match_dup 4))]
+ { operands[5] = gen_rtx_REG (DImode, LO_REGNUM); })
+
+(define_split
+ [(set (match_operand:DI 0 "d_operand")
+ (mult:DI (any_extend:DI (match_operand:SI 1 "d_operand"))
+ (any_extend:DI (match_operand:SI 2 "d_operand"))))
+ (clobber (match_operand:TI 3 "hilo_operand"))
+ (clobber (match_operand:DI 4 "d_operand"))]
+ "TARGET_64BIT && !TARGET_FIX_R4000 && !ISA_HAS_EXT_INS && reload_completed"
+ [(set (match_dup 3)
+ (unspec:TI [(mult:DI (any_extend:DI (match_dup 1))
+ (any_extend:DI (match_dup 2)))]
+ UNSPEC_SET_HILO))
+
+ ;; OP0 <- LO, OP4 <- HI
+ (set (match_dup 0) (match_dup 5))
+ (set (match_dup 4) (unspec:DI [(match_dup 3)] UNSPEC_MFHI))
+
+ ;; Zero-extend OP0.
+ (set (match_dup 0)
+ (ashift:DI (match_dup 0)
+ (const_int 32)))
+ (set (match_dup 0)
+ (lshiftrt:DI (match_dup 0)
+ (const_int 32)))
+
+ ;; Shift OP4 into place.
+ (set (match_dup 4)
+ (ashift:DI (match_dup 4)
+ (const_int 32)))
+
+ ;; OR the two halves together
+ (set (match_dup 0)
+ (ior:DI (match_dup 0)
+ (match_dup 4)))]
+ { operands[5] = gen_rtx_REG (DImode, LO_REGNUM); })
+
+(define_insn "<u>mulsidi3_64bit_hilo"
+ [(set (match_operand:TI 0 "register_operand" "=x")
+ (unspec:TI
+ [(mult:DI
+ (any_extend:DI (match_operand:SI 1 "register_operand" "d"))
+ (any_extend:DI (match_operand:SI 2 "register_operand" "d")))]
+ UNSPEC_SET_HILO))]
+ "TARGET_64BIT && !TARGET_FIX_R4000"
+ "mult<u>\t%1,%2"
+ [(set_attr "type" "imul")
+ (set_attr "mode" "SI")])
+
+;; See comment before the ISA_HAS_DMUL3 case in mips_mulsidi3_gen_fn.
+(define_insn "mulsidi3_64bit_dmul"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "d"))
+ (sign_extend:DI (match_operand:SI 2 "register_operand" "d"))))
+ (clobber (match_scratch:DI 3 "=l"))]
+ "TARGET_64BIT && ISA_HAS_DMUL3"
+ "dmul\t%0,%1,%2"
+ [(set_attr "type" "imul3")
+ (set_attr "mode" "DI")])
+
+;; Widening multiply with negation.
+(define_insn "*muls<u>_di"
+ [(set (match_operand:DI 0 "register_operand" "=x")
+ (neg:DI
+ (mult:DI
+ (any_extend:DI (match_operand:SI 1 "register_operand" "d"))
+ (any_extend:DI (match_operand:SI 2 "register_operand" "d")))))]
+ "!TARGET_64BIT && ISA_HAS_MULS"
+ "muls<u>\t$0,%1,%2"
+ [(set_attr "type" "imul")
+ (set_attr "mode" "SI")])
+
+;; As well as being named patterns, these instructions are used by the
+;; __builtin_mips_msub<u>() functions. We must always make those functions
+;; available if !TARGET_64BIT && ISA_HAS_DSP.
+;;
+;; This leads to a slight inconsistency. We honor any tuning overrides
+;; in GENERATE_MADD_MSUB for -mno-dsp, but always ignore them for -mdsp,
+;; even if !ISA_HAS_DSP_MULT.
+(define_insn "<u>msubsidi4"
+ [(set (match_operand:DI 0 "register_operand" "=ka")
+ (minus:DI
+ (match_operand:DI 3 "register_operand" "0")
+ (mult:DI
+ (any_extend:DI (match_operand:SI 1 "register_operand" "d"))
+ (any_extend:DI (match_operand:SI 2 "register_operand" "d")))))]
+ "!TARGET_64BIT && (ISA_HAS_MSAC || GENERATE_MADD_MSUB || ISA_HAS_DSP)"
+{
+ if (ISA_HAS_DSP_MULT)
+ return "msub<u>\t%q0,%1,%2";
+ else if (TARGET_MIPS5500 || GENERATE_MADD_MSUB)
+ return "msub<u>\t%1,%2";
+ else
+ return "msac<u>\t$0,%1,%2";
+}
+ [(set_attr "type" "imadd")
+ (set_attr "mode" "SI")])
+
+;; _highpart patterns
+
+(define_expand "<su>mulsi3_highpart"
+ [(set (match_operand:SI 0 "register_operand")
+ (truncate:SI
+ (lshiftrt:DI
+ (mult:DI (any_extend:DI (match_operand:SI 1 "register_operand"))
+ (any_extend:DI (match_operand:SI 2 "register_operand")))
+ (const_int 32))))]
+ ""
+{
+ if (ISA_HAS_MULHI)
+ emit_insn (gen_<su>mulsi3_highpart_mulhi_internal (operands[0],
+ operands[1],
+ operands[2]));
+ else
+ emit_insn (gen_<su>mulsi3_highpart_internal (operands[0], operands[1],
+ operands[2]));
+ DONE;
+})
+
+(define_insn_and_split "<su>mulsi3_highpart_internal"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (truncate:SI
+ (lshiftrt:DI
+ (mult:DI (any_extend:DI (match_operand:SI 1 "register_operand" "d"))
+ (any_extend:DI (match_operand:SI 2 "register_operand" "d")))
+ (const_int 32))))
+ (clobber (match_scratch:SI 3 "=l"))]
+ "!ISA_HAS_MULHI"
+ { return TARGET_FIX_R4000 ? "mult<u>\t%1,%2\n\tmfhi\t%0" : "#"; }
+ "&& reload_completed && !TARGET_FIX_R4000"
+ [(const_int 0)]
+{
+ rtx hilo;
+
+ if (TARGET_64BIT)
+ {
+ hilo = gen_rtx_REG (TImode, MD_REG_FIRST);
+ emit_insn (gen_<u>mulsidi3_64bit_hilo (hilo, operands[1], operands[2]));
+ emit_insn (gen_mfhisi_ti (operands[0], hilo));
+ }
+ else
+ {
+ hilo = gen_rtx_REG (DImode, MD_REG_FIRST);
+ emit_insn (gen_<u>mulsidi3_32bit (hilo, operands[1], operands[2]));
+ emit_insn (gen_mfhisi_di (operands[0], hilo));
+ }
+ DONE;
+}
+ [(set_attr "type" "imul")
+ (set_attr "mode" "SI")
+ (set_attr "length" "8")])
+
+(define_insn "<su>mulsi3_highpart_mulhi_internal"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (truncate:SI
+ (lshiftrt:DI
+ (mult:DI
+ (any_extend:DI (match_operand:SI 1 "register_operand" "d"))
+ (any_extend:DI (match_operand:SI 2 "register_operand" "d")))
+ (const_int 32))))
+ (clobber (match_scratch:SI 3 "=l"))]
+ "ISA_HAS_MULHI"
+ "mulhi<u>\t%0,%1,%2"
+ [(set_attr "type" "imul3")
+ (set_attr "mode" "SI")])
+
+(define_insn "*<su>mulsi3_highpart_neg_mulhi_internal"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (truncate:SI
+ (lshiftrt:DI
+ (neg:DI
+ (mult:DI
+ (any_extend:DI (match_operand:SI 1 "register_operand" "d"))
+ (any_extend:DI (match_operand:SI 2 "register_operand" "d"))))
+ (const_int 32))))
+ (clobber (match_scratch:SI 3 "=l"))]
+ "ISA_HAS_MULHI"
+ "mulshi<u>\t%0,%1,%2"
+ [(set_attr "type" "imul3")
+ (set_attr "mode" "SI")])
+
+;; Disable unsigned multiplication for -mfix-vr4120. This is for VR4120
+;; errata MD(0), which says that dmultu does not always produce the
+;; correct result.
+(define_insn_and_split "<su>muldi3_highpart"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (truncate:DI
+ (lshiftrt:TI
+ (mult:TI (any_extend:TI (match_operand:DI 1 "register_operand" "d"))
+ (any_extend:TI (match_operand:DI 2 "register_operand" "d")))
+ (const_int 64))))
+ (clobber (match_scratch:DI 3 "=l"))]
+ "TARGET_64BIT && !(<CODE> == ZERO_EXTEND && TARGET_FIX_VR4120)"
+ { return TARGET_FIX_R4000 ? "dmult<u>\t%1,%2\n\tmfhi\t%0" : "#"; }
+ "&& reload_completed && !TARGET_FIX_R4000"
+ [(const_int 0)]
+{
+ rtx hilo;
+
+ hilo = gen_rtx_REG (TImode, MD_REG_FIRST);
+ emit_insn (gen_<u>mulditi3_internal (hilo, operands[1], operands[2]));
+ emit_insn (gen_mfhidi_ti (operands[0], hilo));
+ DONE;
+}
+ [(set_attr "type" "imul")
+ (set_attr "mode" "DI")
+ (set_attr "length" "8")])
+
+(define_expand "<u>mulditi3"
+ [(set (match_operand:TI 0 "register_operand")
+ (mult:TI (any_extend:TI (match_operand:DI 1 "register_operand"))
+ (any_extend:TI (match_operand:DI 2 "register_operand"))))]
+ "TARGET_64BIT && !(<CODE> == ZERO_EXTEND && TARGET_FIX_VR4120)"
+{
+ if (TARGET_FIX_R4000)
+ emit_insn (gen_<u>mulditi3_r4000 (operands[0], operands[1], operands[2]));
+ else
+ emit_insn (gen_<u>mulditi3_internal (operands[0], operands[1],
+ operands[2]));
+ DONE;
+})
+
+(define_insn "<u>mulditi3_internal"
+ [(set (match_operand:TI 0 "register_operand" "=x")
+ (mult:TI (any_extend:TI (match_operand:DI 1 "register_operand" "d"))
+ (any_extend:TI (match_operand:DI 2 "register_operand" "d"))))]
+ "TARGET_64BIT
+ && !TARGET_FIX_R4000
+ && !(<CODE> == ZERO_EXTEND && TARGET_FIX_VR4120)"
+ "dmult<u>\t%1,%2"
+ [(set_attr "type" "imul")
+ (set_attr "mode" "DI")])
+
+(define_insn "<u>mulditi3_r4000"
+ [(set (match_operand:TI 0 "register_operand" "=d")
+ (mult:TI (any_extend:TI (match_operand:DI 1 "register_operand" "d"))
+ (any_extend:TI (match_operand:DI 2 "register_operand" "d"))))
+ (clobber (match_scratch:TI 3 "=x"))]
+ "TARGET_64BIT
+ && TARGET_FIX_R4000
+ && !(<CODE> == ZERO_EXTEND && TARGET_FIX_VR4120)"
+ "dmult<u>\t%1,%2\;mflo\t%L0\;mfhi\t%M0"
+ [(set_attr "type" "imul")
+ (set_attr "mode" "DI")
+ (set_attr "length" "12")])
+
+;; The R4650 supports a 32-bit multiply/ 64-bit accumulate
+;; instruction. The HI/LO registers are used as a 64-bit accumulator.
+
+(define_insn "madsi"
+ [(set (match_operand:SI 0 "register_operand" "+l")
+ (plus:SI (mult:SI (match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "register_operand" "d"))
+ (match_dup 0)))]
+ "TARGET_MAD"
+ "mad\t%1,%2"
+ [(set_attr "type" "imadd")
+ (set_attr "mode" "SI")])
+
+;; See the comment above <u>msubsidi4 for the relationship between
+;; ISA_HAS_DSP and ISA_HAS_DSP_MULT.
+(define_insn "<u>maddsidi4"
+ [(set (match_operand:DI 0 "register_operand" "=ka")
+ (plus:DI
+ (mult:DI (any_extend:DI (match_operand:SI 1 "register_operand" "d"))
+ (any_extend:DI (match_operand:SI 2 "register_operand" "d")))
+ (match_operand:DI 3 "register_operand" "0")))]
+ "(TARGET_MAD || ISA_HAS_MACC || GENERATE_MADD_MSUB || ISA_HAS_DSP)
+ && !TARGET_64BIT"
+{
+ if (TARGET_MAD)
+ return "mad<u>\t%1,%2";
+ else if (ISA_HAS_DSP_MULT)
+ return "madd<u>\t%q0,%1,%2";
+ else if (GENERATE_MADD_MSUB || TARGET_MIPS5500)
+ return "madd<u>\t%1,%2";
+ else
+ /* See comment in *macc. */
+ return "%[macc<u>\t%@,%1,%2%]";
+}
+ [(set_attr "type" "imadd")
+ (set_attr "mode" "SI")])
+
+;; Floating point multiply accumulate instructions.
+
+(define_insn "*madd4<mode>"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (plus:ANYF (mult:ANYF (match_operand:ANYF 1 "register_operand" "f")
+ (match_operand:ANYF 2 "register_operand" "f"))
+ (match_operand:ANYF 3 "register_operand" "f")))]
+ "ISA_HAS_FP_MADD4_MSUB4 && TARGET_FUSED_MADD"
+ "madd.<fmt>\t%0,%3,%1,%2"
+ [(set_attr "type" "fmadd")
+ (set_attr "mode" "<UNITMODE>")])
+
+(define_insn "*madd3<mode>"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (plus:ANYF (mult:ANYF (match_operand:ANYF 1 "register_operand" "f")
+ (match_operand:ANYF 2 "register_operand" "f"))
+ (match_operand:ANYF 3 "register_operand" "0")))]
+ "ISA_HAS_FP_MADD3_MSUB3 && TARGET_FUSED_MADD"
+ "madd.<fmt>\t%0,%1,%2"
+ [(set_attr "type" "fmadd")
+ (set_attr "mode" "<UNITMODE>")])
+
+(define_insn "*msub4<mode>"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (minus:ANYF (mult:ANYF (match_operand:ANYF 1 "register_operand" "f")
+ (match_operand:ANYF 2 "register_operand" "f"))
+ (match_operand:ANYF 3 "register_operand" "f")))]
+ "ISA_HAS_FP_MADD4_MSUB4 && TARGET_FUSED_MADD"
+ "msub.<fmt>\t%0,%3,%1,%2"
+ [(set_attr "type" "fmadd")
+ (set_attr "mode" "<UNITMODE>")])
+
+(define_insn "*msub3<mode>"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (minus:ANYF (mult:ANYF (match_operand:ANYF 1 "register_operand" "f")
+ (match_operand:ANYF 2 "register_operand" "f"))
+ (match_operand:ANYF 3 "register_operand" "0")))]
+ "ISA_HAS_FP_MADD3_MSUB3 && TARGET_FUSED_MADD"
+ "msub.<fmt>\t%0,%1,%2"
+ [(set_attr "type" "fmadd")
+ (set_attr "mode" "<UNITMODE>")])
+
+(define_insn "*nmadd4<mode>"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (neg:ANYF (plus:ANYF
+ (mult:ANYF (match_operand:ANYF 1 "register_operand" "f")
+ (match_operand:ANYF 2 "register_operand" "f"))
+ (match_operand:ANYF 3 "register_operand" "f"))))]
+ "ISA_HAS_NMADD4_NMSUB4 (<MODE>mode)
+ && TARGET_FUSED_MADD
+ && HONOR_SIGNED_ZEROS (<MODE>mode)
+ && !HONOR_NANS (<MODE>mode)"
+ "nmadd.<fmt>\t%0,%3,%1,%2"
+ [(set_attr "type" "fmadd")
+ (set_attr "mode" "<UNITMODE>")])
+
+(define_insn "*nmadd3<mode>"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (neg:ANYF (plus:ANYF
+ (mult:ANYF (match_operand:ANYF 1 "register_operand" "f")
+ (match_operand:ANYF 2 "register_operand" "f"))
+ (match_operand:ANYF 3 "register_operand" "0"))))]
+ "ISA_HAS_NMADD3_NMSUB3 (<MODE>mode)
+ && TARGET_FUSED_MADD
+ && HONOR_SIGNED_ZEROS (<MODE>mode)
+ && !HONOR_NANS (<MODE>mode)"
+ "nmadd.<fmt>\t%0,%1,%2"
+ [(set_attr "type" "fmadd")
+ (set_attr "mode" "<UNITMODE>")])
+
+(define_insn "*nmadd4<mode>_fastmath"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (minus:ANYF
+ (mult:ANYF (neg:ANYF (match_operand:ANYF 1 "register_operand" "f"))
+ (match_operand:ANYF 2 "register_operand" "f"))
+ (match_operand:ANYF 3 "register_operand" "f")))]
+ "ISA_HAS_NMADD4_NMSUB4 (<MODE>mode)
+ && TARGET_FUSED_MADD
+ && !HONOR_SIGNED_ZEROS (<MODE>mode)
+ && !HONOR_NANS (<MODE>mode)"
+ "nmadd.<fmt>\t%0,%3,%1,%2"
+ [(set_attr "type" "fmadd")
+ (set_attr "mode" "<UNITMODE>")])
+
+(define_insn "*nmadd3<mode>_fastmath"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (minus:ANYF
+ (mult:ANYF (neg:ANYF (match_operand:ANYF 1 "register_operand" "f"))
+ (match_operand:ANYF 2 "register_operand" "f"))
+ (match_operand:ANYF 3 "register_operand" "0")))]
+ "ISA_HAS_NMADD3_NMSUB3 (<MODE>mode)
+ && TARGET_FUSED_MADD
+ && !HONOR_SIGNED_ZEROS (<MODE>mode)
+ && !HONOR_NANS (<MODE>mode)"
+ "nmadd.<fmt>\t%0,%1,%2"
+ [(set_attr "type" "fmadd")
+ (set_attr "mode" "<UNITMODE>")])
+
+(define_insn "*nmsub4<mode>"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (neg:ANYF (minus:ANYF
+ (mult:ANYF (match_operand:ANYF 2 "register_operand" "f")
+ (match_operand:ANYF 3 "register_operand" "f"))
+ (match_operand:ANYF 1 "register_operand" "f"))))]
+ "ISA_HAS_NMADD4_NMSUB4 (<MODE>mode)
+ && TARGET_FUSED_MADD
+ && HONOR_SIGNED_ZEROS (<MODE>mode)
+ && !HONOR_NANS (<MODE>mode)"
+ "nmsub.<fmt>\t%0,%1,%2,%3"
+ [(set_attr "type" "fmadd")
+ (set_attr "mode" "<UNITMODE>")])
+
+(define_insn "*nmsub3<mode>"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (neg:ANYF (minus:ANYF
+ (mult:ANYF (match_operand:ANYF 2 "register_operand" "f")
+ (match_operand:ANYF 3 "register_operand" "f"))
+ (match_operand:ANYF 1 "register_operand" "0"))))]
+ "ISA_HAS_NMADD3_NMSUB3 (<MODE>mode)
+ && TARGET_FUSED_MADD
+ && HONOR_SIGNED_ZEROS (<MODE>mode)
+ && !HONOR_NANS (<MODE>mode)"
+ "nmsub.<fmt>\t%0,%1,%2"
+ [(set_attr "type" "fmadd")
+ (set_attr "mode" "<UNITMODE>")])
+
+(define_insn "*nmsub4<mode>_fastmath"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (minus:ANYF
+ (match_operand:ANYF 1 "register_operand" "f")
+ (mult:ANYF (match_operand:ANYF 2 "register_operand" "f")
+ (match_operand:ANYF 3 "register_operand" "f"))))]
+ "ISA_HAS_NMADD4_NMSUB4 (<MODE>mode)
+ && TARGET_FUSED_MADD
+ && !HONOR_SIGNED_ZEROS (<MODE>mode)
+ && !HONOR_NANS (<MODE>mode)"
+ "nmsub.<fmt>\t%0,%1,%2,%3"
+ [(set_attr "type" "fmadd")
+ (set_attr "mode" "<UNITMODE>")])
+
+(define_insn "*nmsub3<mode>_fastmath"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (minus:ANYF
+ (match_operand:ANYF 1 "register_operand" "f")
+ (mult:ANYF (match_operand:ANYF 2 "register_operand" "f")
+ (match_operand:ANYF 3 "register_operand" "0"))))]
+ "ISA_HAS_NMADD3_NMSUB3 (<MODE>mode)
+ && TARGET_FUSED_MADD
+ && !HONOR_SIGNED_ZEROS (<MODE>mode)
+ && !HONOR_NANS (<MODE>mode)"
+ "nmsub.<fmt>\t%0,%1,%2"
+ [(set_attr "type" "fmadd")
+ (set_attr "mode" "<UNITMODE>")])
+
+;;
+;; ....................
+;;
+;; DIVISION and REMAINDER
+;;
+;; ....................
+;;
+
+(define_expand "div<mode>3"
+ [(set (match_operand:ANYF 0 "register_operand")
+ (div:ANYF (match_operand:ANYF 1 "reg_or_1_operand")
+ (match_operand:ANYF 2 "register_operand")))]
+ "<divide_condition>"
+{
+ if (const_1_operand (operands[1], <MODE>mode))
+ if (!(<recip_condition> && flag_unsafe_math_optimizations))
+ operands[1] = force_reg (<MODE>mode, operands[1]);
+})
+
+;; These patterns work around the early SB-1 rev2 core "F1" erratum:
+;;
+;; If an mfc1 or dmfc1 happens to access the floating point register
+;; file at the same time a long latency operation (div, sqrt, recip,
+;; sqrt) iterates an intermediate result back through the floating
+;; point register file bypass, then instead returning the correct
+;; register value the mfc1 or dmfc1 operation returns the intermediate
+;; result of the long latency operation.
+;;
+;; The workaround is to insert an unconditional 'mov' from/to the
+;; long latency op destination register.
+
+(define_insn "*div<mode>3"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (div:ANYF (match_operand:ANYF 1 "register_operand" "f")
+ (match_operand:ANYF 2 "register_operand" "f")))]
+ "<divide_condition>"
+{
+ if (TARGET_FIX_SB1)
+ return "div.<fmt>\t%0,%1,%2\;mov.<fmt>\t%0,%0";
+ else
+ return "div.<fmt>\t%0,%1,%2";
+}
+ [(set_attr "type" "fdiv")
+ (set_attr "mode" "<UNITMODE>")
+ (set (attr "length")
+ (if_then_else (ne (symbol_ref "TARGET_FIX_SB1") (const_int 0))
+ (const_int 8)
+ (const_int 4)))])
+
+(define_insn "*recip<mode>3"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (div:ANYF (match_operand:ANYF 1 "const_1_operand" "")
+ (match_operand:ANYF 2 "register_operand" "f")))]
+ "<recip_condition> && flag_unsafe_math_optimizations"
+{
+ if (TARGET_FIX_SB1)
+ return "recip.<fmt>\t%0,%2\;mov.<fmt>\t%0,%0";
+ else
+ return "recip.<fmt>\t%0,%2";
+}
+ [(set_attr "type" "frdiv")
+ (set_attr "mode" "<UNITMODE>")
+ (set (attr "length")
+ (if_then_else (ne (symbol_ref "TARGET_FIX_SB1") (const_int 0))
+ (const_int 8)
+ (const_int 4)))])
+
+;; VR4120 errata MD(A1): signed division instructions do not work correctly
+;; with negative operands. We use special libgcc functions instead.
+(define_insn_and_split "divmod<mode>4"
+ [(set (match_operand:GPR 0 "register_operand" "=l")
+ (div:GPR (match_operand:GPR 1 "register_operand" "d")
+ (match_operand:GPR 2 "register_operand" "d")))
+ (set (match_operand:GPR 3 "register_operand" "=d")
+ (mod:GPR (match_dup 1)
+ (match_dup 2)))]
+ "!TARGET_FIX_VR4120"
+ "#"
+ "&& reload_completed"
+ [(const_int 0)]
+{
+ rtx hilo;
+
+ if (TARGET_64BIT)
+ {
+ hilo = gen_rtx_REG (TImode, MD_REG_FIRST);
+ emit_insn (gen_divmod<mode>4_hilo_ti (hilo, operands[1], operands[2]));
+ emit_insn (gen_mfhi<mode>_ti (operands[3], hilo));
+ }
+ else
+ {
+ hilo = gen_rtx_REG (DImode, MD_REG_FIRST);
+ emit_insn (gen_divmod<mode>4_hilo_di (hilo, operands[1], operands[2]));
+ emit_insn (gen_mfhi<mode>_di (operands[3], hilo));
+ }
+ DONE;
+}
+ [(set_attr "type" "idiv")
+ (set_attr "mode" "<MODE>")
+ (set_attr "length" "8")])
+
+(define_insn_and_split "udivmod<mode>4"
+ [(set (match_operand:GPR 0 "register_operand" "=l")
+ (udiv:GPR (match_operand:GPR 1 "register_operand" "d")
+ (match_operand:GPR 2 "register_operand" "d")))
+ (set (match_operand:GPR 3 "register_operand" "=d")
+ (umod:GPR (match_dup 1)
+ (match_dup 2)))]
+ ""
+ "#"
+ "reload_completed"
+ [(const_int 0)]
+{
+ rtx hilo;
+
+ if (TARGET_64BIT)
+ {
+ hilo = gen_rtx_REG (TImode, MD_REG_FIRST);
+ emit_insn (gen_udivmod<mode>4_hilo_ti (hilo, operands[1], operands[2]));
+ emit_insn (gen_mfhi<mode>_ti (operands[3], hilo));
+ }
+ else
+ {
+ hilo = gen_rtx_REG (DImode, MD_REG_FIRST);
+ emit_insn (gen_udivmod<mode>4_hilo_di (hilo, operands[1], operands[2]));
+ emit_insn (gen_mfhi<mode>_di (operands[3], hilo));
+ }
+ DONE;
+}
+ [(set_attr "type" "idiv")
+ (set_attr "mode" "<MODE>")
+ (set_attr "length" "8")])
+
+(define_insn "<u>divmod<GPR:mode>4_hilo_<HILO:mode>"
+ [(set (match_operand:HILO 0 "register_operand" "=x")
+ (unspec:HILO
+ [(any_div:GPR (match_operand:GPR 1 "register_operand" "d")
+ (match_operand:GPR 2 "register_operand" "d"))]
+ UNSPEC_SET_HILO))]
+ ""
+ { return mips_output_division ("<GPR:d>div<u>\t%.,%1,%2", operands); }
+ [(set_attr "type" "idiv")
+ (set_attr "mode" "<GPR:MODE>")])
+
+;;
+;; ....................
+;;
+;; SQUARE ROOT
+;;
+;; ....................
+
+;; These patterns work around the early SB-1 rev2 core "F1" erratum (see
+;; "*div[sd]f3" comment for details).
+
+(define_insn "sqrt<mode>2"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (sqrt:ANYF (match_operand:ANYF 1 "register_operand" "f")))]
+ "<sqrt_condition>"
+{
+ if (TARGET_FIX_SB1)
+ return "sqrt.<fmt>\t%0,%1\;mov.<fmt>\t%0,%0";
+ else
+ return "sqrt.<fmt>\t%0,%1";
+}
+ [(set_attr "type" "fsqrt")
+ (set_attr "mode" "<UNITMODE>")
+ (set (attr "length")
+ (if_then_else (ne (symbol_ref "TARGET_FIX_SB1") (const_int 0))
+ (const_int 8)
+ (const_int 4)))])
+
+(define_insn "*rsqrt<mode>a"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (div:ANYF (match_operand:ANYF 1 "const_1_operand" "")
+ (sqrt:ANYF (match_operand:ANYF 2 "register_operand" "f"))))]
+ "<recip_condition> && flag_unsafe_math_optimizations"
+{
+ if (TARGET_FIX_SB1)
+ return "rsqrt.<fmt>\t%0,%2\;mov.<fmt>\t%0,%0";
+ else
+ return "rsqrt.<fmt>\t%0,%2";
+}
+ [(set_attr "type" "frsqrt")
+ (set_attr "mode" "<UNITMODE>")
+ (set (attr "length")
+ (if_then_else (ne (symbol_ref "TARGET_FIX_SB1") (const_int 0))
+ (const_int 8)
+ (const_int 4)))])
+
+(define_insn "*rsqrt<mode>b"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (sqrt:ANYF (div:ANYF (match_operand:ANYF 1 "const_1_operand" "")
+ (match_operand:ANYF 2 "register_operand" "f"))))]
+ "<recip_condition> && flag_unsafe_math_optimizations"
+{
+ if (TARGET_FIX_SB1)
+ return "rsqrt.<fmt>\t%0,%2\;mov.<fmt>\t%0,%0";
+ else
+ return "rsqrt.<fmt>\t%0,%2";
+}
+ [(set_attr "type" "frsqrt")
+ (set_attr "mode" "<UNITMODE>")
+ (set (attr "length")
+ (if_then_else (ne (symbol_ref "TARGET_FIX_SB1") (const_int 0))
+ (const_int 8)
+ (const_int 4)))])
+
+;;
+;; ....................
+;;
+;; ABSOLUTE VALUE
+;;
+;; ....................
+
+;; Do not use the integer abs macro instruction, since that signals an
+;; exception on -2147483648 (sigh).
+
+;; abs.fmt is an arithmetic instruction and treats all NaN inputs as
+;; invalid; it does not clear their sign bits. We therefore can't use
+;; abs.fmt if the signs of NaNs matter.
+
+(define_insn "abs<mode>2"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (abs:ANYF (match_operand:ANYF 1 "register_operand" "f")))]
+ "!HONOR_NANS (<MODE>mode)"
+ "abs.<fmt>\t%0,%1"
+ [(set_attr "type" "fabs")
+ (set_attr "mode" "<UNITMODE>")])
+
+;;
+;; ...................
+;;
+;; Count leading zeroes.
+;;
+;; ...................
+;;
+
+(define_insn "clz<mode>2"
+ [(set (match_operand:GPR 0 "register_operand" "=d")
+ (clz:GPR (match_operand:GPR 1 "register_operand" "d")))]
+ "ISA_HAS_CLZ_CLO"
+ "<d>clz\t%0,%1"
+ [(set_attr "type" "clz")
+ (set_attr "mode" "<MODE>")])
+
+;;
+;; ...................
+;;
+;; Count number of set bits.
+;;
+;; ...................
+;;
+
+(define_insn "popcount<mode>2"
+ [(set (match_operand:GPR 0 "register_operand" "=d")
+ (popcount:GPR (match_operand:GPR 1 "register_operand" "d")))]
+ "ISA_HAS_POP"
+ "<d>pop\t%0,%1"
+ [(set_attr "type" "pop")
+ (set_attr "mode" "<MODE>")])
+
+;;
+;; ....................
+;;
+;; NEGATION and ONE'S COMPLEMENT
+;;
+;; ....................
+
+(define_insn "negsi2"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (neg:SI (match_operand:SI 1 "register_operand" "d")))]
+ ""
+{
+ if (TARGET_MIPS16)
+ return "neg\t%0,%1";
+ else
+ return "subu\t%0,%.,%1";
+}
+ [(set_attr "alu_type" "sub")
+ (set_attr "mode" "SI")])
+
+(define_insn "negdi2"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (neg:DI (match_operand:DI 1 "register_operand" "d")))]
+ "TARGET_64BIT && !TARGET_MIPS16"
+ "dsubu\t%0,%.,%1"
+ [(set_attr "alu_type" "sub")
+ (set_attr "mode" "DI")])
+
+;; neg.fmt is an arithmetic instruction and treats all NaN inputs as
+;; invalid; it does not flip their sign bit. We therefore can't use
+;; neg.fmt if the signs of NaNs matter.
+
+(define_insn "neg<mode>2"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (neg:ANYF (match_operand:ANYF 1 "register_operand" "f")))]
+ "!HONOR_NANS (<MODE>mode)"
+ "neg.<fmt>\t%0,%1"
+ [(set_attr "type" "fneg")
+ (set_attr "mode" "<UNITMODE>")])
+
+(define_insn "one_cmpl<mode>2"
+ [(set (match_operand:GPR 0 "register_operand" "=d")
+ (not:GPR (match_operand:GPR 1 "register_operand" "d")))]
+ ""
+{
+ if (TARGET_MIPS16)
+ return "not\t%0,%1";
+ else
+ return "nor\t%0,%.,%1";
+}
+ [(set_attr "alu_type" "not")
+ (set_attr "mode" "<MODE>")])
+
+;;
+;; ....................
+;;
+;; LOGICAL
+;;
+;; ....................
+;;
+
+;; Many of these instructions use trivial define_expands, because we
+;; want to use a different set of constraints when TARGET_MIPS16.
+
+(define_expand "and<mode>3"
+ [(set (match_operand:GPR 0 "register_operand")
+ (and:GPR (match_operand:GPR 1 "register_operand")
+ (match_operand:GPR 2 "and_reg_operand")))])
+
+;; The middle-end is not allowed to convert ANDing with 0xffff_ffff into a
+;; zero_extendsidi2 because of TRULY_NOOP_TRUNCATION, so handle these here.
+;; Note that this variant does not trigger for SI mode because we require
+;; a 64-bit HOST_WIDE_INT and 0xffff_ffff wouldn't be a canonical
+;; sign-extended SImode value.
+;;
+;; These are possible combinations for operand 1 and 2. The table
+;; includes both MIPS and MIPS16 cases. (r=register, mem=memory,
+;; 16=MIPS16, x=match, S=split):
+;;
+;; \ op1 r/EXT r/!EXT mem r/16 mem/16
+;; op2
+;;
+;; andi x x
+;; 0xff x x x x
+;; 0xffff x x x x
+;; 0xffff_ffff x S x S x
+;; low-bitmask x
+;; register x x
+;; register =op1 x
+
+(define_insn "*and<mode>3"
+ [(set (match_operand:GPR 0 "register_operand" "=d,d,d,d,d,d,d")
+ (and:GPR (match_operand:GPR 1 "nonimmediate_operand" "o,o,W,d,d,d,d")
+ (match_operand:GPR 2 "and_operand" "Yb,Yh,Yw,K,Yx,Yw,d")))]
+ "!TARGET_MIPS16 && and_operands_ok (<MODE>mode, operands[1], operands[2])"
+{
+ int len;
+
+ switch (which_alternative)
+ {
+ case 0:
+ operands[1] = gen_lowpart (QImode, operands[1]);
+ return "lbu\t%0,%1";
+ case 1:
+ operands[1] = gen_lowpart (HImode, operands[1]);
+ return "lhu\t%0,%1";
+ case 2:
+ operands[1] = gen_lowpart (SImode, operands[1]);
+ return "lwu\t%0,%1";
+ case 3:
+ return "andi\t%0,%1,%x2";
+ case 4:
+ len = low_bitmask_len (<MODE>mode, INTVAL (operands[2]));
+ operands[2] = GEN_INT (len);
+ return "<d>ext\t%0,%1,0,%2";
+ case 5:
+ return "#";
+ case 6:
+ return "and\t%0,%1,%2";
+ default:
+ gcc_unreachable ();
+ }
+}
+ [(set_attr "move_type" "load,load,load,andi,ext_ins,shift_shift,logical")
+ (set_attr "mode" "<MODE>")])
+
+(define_insn "*and<mode>3_mips16"
+ [(set (match_operand:GPR 0 "register_operand" "=d,d,d,d,d")
+ (and:GPR (match_operand:GPR 1 "nonimmediate_operand" "%o,o,W,d,0")
+ (match_operand:GPR 2 "and_operand" "Yb,Yh,Yw,Yw,d")))]
+ "TARGET_MIPS16 && and_operands_ok (<MODE>mode, operands[1], operands[2])"
+{
+ switch (which_alternative)
+ {
+ case 0:
+ operands[1] = gen_lowpart (QImode, operands[1]);
+ return "lbu\t%0,%1";
+ case 1:
+ operands[1] = gen_lowpart (HImode, operands[1]);
+ return "lhu\t%0,%1";
+ case 2:
+ operands[1] = gen_lowpart (SImode, operands[1]);
+ return "lwu\t%0,%1";
+ case 3:
+ return "#";
+ case 4:
+ return "and\t%0,%2";
+ default:
+ gcc_unreachable ();
+ }
+}
+ [(set_attr "move_type" "load,load,load,shift_shift,logical")
+ (set_attr "mode" "<MODE>")])
+
+(define_expand "ior<mode>3"
+ [(set (match_operand:GPR 0 "register_operand")
+ (ior:GPR (match_operand:GPR 1 "register_operand")
+ (match_operand:GPR 2 "uns_arith_operand")))]
+ ""
+{
+ if (TARGET_MIPS16)
+ operands[2] = force_reg (<MODE>mode, operands[2]);
+})
+
+(define_insn "*ior<mode>3"
+ [(set (match_operand:GPR 0 "register_operand" "=d,d")
+ (ior:GPR (match_operand:GPR 1 "register_operand" "%d,d")
+ (match_operand:GPR 2 "uns_arith_operand" "d,K")))]
+ "!TARGET_MIPS16"
+ "@
+ or\t%0,%1,%2
+ ori\t%0,%1,%x2"
+ [(set_attr "alu_type" "or")
+ (set_attr "mode" "<MODE>")])
+
+(define_insn "*ior<mode>3_mips16"
+ [(set (match_operand:GPR 0 "register_operand" "=d")
+ (ior:GPR (match_operand:GPR 1 "register_operand" "%0")
+ (match_operand:GPR 2 "register_operand" "d")))]
+ "TARGET_MIPS16"
+ "or\t%0,%2"
+ [(set_attr "alu_type" "or")
+ (set_attr "mode" "<MODE>")])
+
+(define_expand "xor<mode>3"
+ [(set (match_operand:GPR 0 "register_operand")
+ (xor:GPR (match_operand:GPR 1 "register_operand")
+ (match_operand:GPR 2 "uns_arith_operand")))]
+ ""
+ "")
+
+(define_insn ""
+ [(set (match_operand:GPR 0 "register_operand" "=d,d")
+ (xor:GPR (match_operand:GPR 1 "register_operand" "%d,d")
+ (match_operand:GPR 2 "uns_arith_operand" "d,K")))]
+ "!TARGET_MIPS16"
+ "@
+ xor\t%0,%1,%2
+ xori\t%0,%1,%x2"
+ [(set_attr "alu_type" "xor")
+ (set_attr "mode" "<MODE>")])
+
+(define_insn ""
+ [(set (match_operand:GPR 0 "register_operand" "=d,t,t")
+ (xor:GPR (match_operand:GPR 1 "register_operand" "%0,d,d")
+ (match_operand:GPR 2 "uns_arith_operand" "d,K,d")))]
+ "TARGET_MIPS16"
+ "@
+ xor\t%0,%2
+ cmpi\t%1,%2
+ cmp\t%1,%2"
+ [(set_attr "alu_type" "xor")
+ (set_attr "mode" "<MODE>")
+ (set_attr_alternative "length"
+ [(const_int 4)
+ (if_then_else (match_operand:VOID 2 "m16_uimm8_1")
+ (const_int 4)
+ (const_int 8))
+ (const_int 4)])])
+
+(define_insn "*nor<mode>3"
+ [(set (match_operand:GPR 0 "register_operand" "=d")
+ (and:GPR (not:GPR (match_operand:GPR 1 "register_operand" "d"))
+ (not:GPR (match_operand:GPR 2 "register_operand" "d"))))]
+ "!TARGET_MIPS16"
+ "nor\t%0,%1,%2"
+ [(set_attr "alu_type" "nor")
+ (set_attr "mode" "<MODE>")])
+
+;;
+;; ....................
+;;
+;; TRUNCATION
+;;
+;; ....................
+
+
+
+(define_insn "truncdfsf2"
+ [(set (match_operand:SF 0 "register_operand" "=f")
+ (float_truncate:SF (match_operand:DF 1 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
+ "cvt.s.d\t%0,%1"
+ [(set_attr "type" "fcvt")
+ (set_attr "cnv_mode" "D2S")
+ (set_attr "mode" "SF")])
+
+;; Integer truncation patterns. Truncating SImode values to smaller
+;; modes is a no-op, as it is for most other GCC ports. Truncating
+;; DImode values to SImode is not a no-op for TARGET_64BIT since we
+;; need to make sure that the lower 32 bits are properly sign-extended
+;; (see TRULY_NOOP_TRUNCATION). Truncating DImode values into modes
+;; smaller than SImode is equivalent to two separate truncations:
+;;
+;; A B
+;; DI ---> HI == DI ---> SI ---> HI
+;; DI ---> QI == DI ---> SI ---> QI
+;;
+;; Step A needs a real instruction but step B does not.
+
+(define_insn "truncdi<mode>2"
+ [(set (match_operand:SUBDI 0 "nonimmediate_operand" "=d,m")
+ (truncate:SUBDI (match_operand:DI 1 "register_operand" "d,d")))]
+ "TARGET_64BIT"
+ "@
+ sll\t%0,%1,0
+ <store>\t%1,%0"
+ [(set_attr "move_type" "sll0,store")
+ (set_attr "mode" "SI")])
+
+;; Combiner patterns to optimize shift/truncate combinations.
+
+(define_insn "*ashr_trunc<mode>"
+ [(set (match_operand:SUBDI 0 "register_operand" "=d")
+ (truncate:SUBDI
+ (ashiftrt:DI (match_operand:DI 1 "register_operand" "d")
+ (match_operand:DI 2 "const_arith_operand" ""))))]
+ "TARGET_64BIT && !TARGET_MIPS16 && IN_RANGE (INTVAL (operands[2]), 32, 63)"
+ "dsra\t%0,%1,%2"
+ [(set_attr "type" "shift")
+ (set_attr "mode" "<MODE>")])
+
+(define_insn "*lshr32_trunc<mode>"
+ [(set (match_operand:SUBDI 0 "register_operand" "=d")
+ (truncate:SUBDI
+ (lshiftrt:DI (match_operand:DI 1 "register_operand" "d")
+ (const_int 32))))]
+ "TARGET_64BIT && !TARGET_MIPS16"
+ "dsra\t%0,%1,32"
+ [(set_attr "type" "shift")
+ (set_attr "mode" "<MODE>")])
+
+;; Logical shift by more than 32 results in proper SI values so truncation is
+;; removed by the middle end. Note that a logical shift by 32 is handled by
+;; the previous pattern.
+(define_insn "*<optab>_trunc<mode>_exts"
+ [(set (match_operand:SUBDI 0 "register_operand" "=d")
+ (truncate:SUBDI
+ (any_shiftrt:DI (match_operand:DI 1 "register_operand" "d")
+ (match_operand:DI 2 "const_arith_operand" ""))))]
+ "ISA_HAS_EXTS && TARGET_64BIT && UINTVAL (operands[2]) < 32"
+ "exts\t%0,%1,%2,31"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "<MODE>")])
+
+;;
+;; ....................
+;;
+;; ZERO EXTENSION
+;;
+;; ....................
+
+;; Extension insns.
+
+(define_expand "zero_extendsidi2"
+ [(set (match_operand:DI 0 "register_operand")
+ (zero_extend:DI (match_operand:SI 1 "nonimmediate_operand")))]
+ "TARGET_64BIT")
+
+(define_insn_and_split "*zero_extendsidi2"
+ [(set (match_operand:DI 0 "register_operand" "=d,d")
+ (zero_extend:DI (match_operand:SI 1 "nonimmediate_operand" "d,W")))]
+ "TARGET_64BIT && !ISA_HAS_EXT_INS"
+ "@
+ #
+ lwu\t%0,%1"
+ "&& reload_completed && REG_P (operands[1])"
+ [(set (match_dup 0)
+ (ashift:DI (match_dup 1) (const_int 32)))
+ (set (match_dup 0)
+ (lshiftrt:DI (match_dup 0) (const_int 32)))]
+ { operands[1] = gen_lowpart (DImode, operands[1]); }
+ [(set_attr "move_type" "shift_shift,load")
+ (set_attr "mode" "DI")])
+
+(define_insn "*zero_extendsidi2_dext"
+ [(set (match_operand:DI 0 "register_operand" "=d,d")
+ (zero_extend:DI (match_operand:SI 1 "nonimmediate_operand" "d,W")))]
+ "TARGET_64BIT && ISA_HAS_EXT_INS"
+ "@
+ dext\t%0,%1,0,32
+ lwu\t%0,%1"
+ [(set_attr "move_type" "arith,load")
+ (set_attr "mode" "DI")])
+
+;; See the comment before the *and<mode>3 pattern why this is generated by
+;; combine.
+
+(define_split
+ [(set (match_operand:DI 0 "register_operand")
+ (and:DI (match_operand:DI 1 "register_operand")
+ (const_int 4294967295)))]
+ "TARGET_64BIT && !ISA_HAS_EXT_INS && reload_completed"
+ [(set (match_dup 0)
+ (ashift:DI (match_dup 1) (const_int 32)))
+ (set (match_dup 0)
+ (lshiftrt:DI (match_dup 0) (const_int 32)))])
+
+(define_expand "zero_extend<SHORT:mode><GPR:mode>2"
+ [(set (match_operand:GPR 0 "register_operand")
+ (zero_extend:GPR (match_operand:SHORT 1 "nonimmediate_operand")))]
+ ""
+{
+ if (TARGET_MIPS16 && !GENERATE_MIPS16E
+ && !memory_operand (operands[1], <SHORT:MODE>mode))
+ {
+ emit_insn (gen_and<GPR:mode>3 (operands[0],
+ gen_lowpart (<GPR:MODE>mode, operands[1]),
+ force_reg (<GPR:MODE>mode,
+ GEN_INT (<SHORT:mask>))));
+ DONE;
+ }
+})
+
+(define_insn "*zero_extend<SHORT:mode><GPR:mode>2"
+ [(set (match_operand:GPR 0 "register_operand" "=d,d")
+ (zero_extend:GPR
+ (match_operand:SHORT 1 "nonimmediate_operand" "d,m")))]
+ "!TARGET_MIPS16"
+ "@
+ andi\t%0,%1,<SHORT:mask>
+ l<SHORT:size>u\t%0,%1"
+ [(set_attr "move_type" "andi,load")
+ (set_attr "mode" "<GPR:MODE>")])
+
+(define_insn "*zero_extend<SHORT:mode><GPR:mode>2_mips16e"
+ [(set (match_operand:GPR 0 "register_operand" "=d")
+ (zero_extend:GPR (match_operand:SHORT 1 "register_operand" "0")))]
+ "GENERATE_MIPS16E"
+ "ze<SHORT:size>\t%0"
+ ;; This instruction is effectively a special encoding of ANDI.
+ [(set_attr "move_type" "andi")
+ (set_attr "mode" "<GPR:MODE>")])
+
+(define_insn "*zero_extend<SHORT:mode><GPR:mode>2_mips16"
+ [(set (match_operand:GPR 0 "register_operand" "=d")
+ (zero_extend:GPR (match_operand:SHORT 1 "memory_operand" "m")))]
+ "TARGET_MIPS16"
+ "l<SHORT:size>u\t%0,%1"
+ [(set_attr "move_type" "load")
+ (set_attr "mode" "<GPR:MODE>")])
+
+(define_expand "zero_extendqihi2"
+ [(set (match_operand:HI 0 "register_operand")
+ (zero_extend:HI (match_operand:QI 1 "nonimmediate_operand")))]
+ ""
+{
+ if (TARGET_MIPS16 && !memory_operand (operands[1], QImode))
+ {
+ emit_insn (gen_zero_extendqisi2 (gen_lowpart (SImode, operands[0]),
+ operands[1]));
+ DONE;
+ }
+})
+
+(define_insn "*zero_extendqihi2"
+ [(set (match_operand:HI 0 "register_operand" "=d,d")
+ (zero_extend:HI (match_operand:QI 1 "nonimmediate_operand" "d,m")))]
+ "!TARGET_MIPS16"
+ "@
+ andi\t%0,%1,0x00ff
+ lbu\t%0,%1"
+ [(set_attr "move_type" "andi,load")
+ (set_attr "mode" "HI")])
+
+(define_insn "*zero_extendqihi2_mips16"
+ [(set (match_operand:HI 0 "register_operand" "=d")
+ (zero_extend:HI (match_operand:QI 1 "memory_operand" "m")))]
+ "TARGET_MIPS16"
+ "lbu\t%0,%1"
+ [(set_attr "move_type" "load")
+ (set_attr "mode" "HI")])
+
+;; Combiner patterns to optimize truncate/zero_extend combinations.
+
+(define_insn "*zero_extend<GPR:mode>_trunc<SHORT:mode>"
+ [(set (match_operand:GPR 0 "register_operand" "=d")
+ (zero_extend:GPR
+ (truncate:SHORT (match_operand:DI 1 "register_operand" "d"))))]
+ "TARGET_64BIT && !TARGET_MIPS16"
+{
+ operands[2] = GEN_INT (GET_MODE_MASK (<SHORT:MODE>mode));
+ return "andi\t%0,%1,%x2";
+}
+ [(set_attr "alu_type" "and")
+ (set_attr "mode" "<GPR:MODE>")])
+
+(define_insn "*zero_extendhi_truncqi"
+ [(set (match_operand:HI 0 "register_operand" "=d")
+ (zero_extend:HI
+ (truncate:QI (match_operand:DI 1 "register_operand" "d"))))]
+ "TARGET_64BIT && !TARGET_MIPS16"
+ "andi\t%0,%1,0xff"
+ [(set_attr "alu_type" "and")
+ (set_attr "mode" "HI")])
+
+;;
+;; ....................
+;;
+;; SIGN EXTENSION
+;;
+;; ....................
+
+;; Extension insns.
+;; Those for integer source operand are ordered widest source type first.
+
+;; When TARGET_64BIT, all SImode integer registers should already be in
+;; sign-extended form (see TRULY_NOOP_TRUNCATION and truncdisi2). We can
+;; therefore get rid of register->register instructions if we constrain
+;; the source to be in the same register as the destination.
+;;
+;; The register alternative has type "arith" so that the pre-reload
+;; scheduler will treat it as a move. This reflects what happens if
+;; the register alternative needs a reload.
+(define_insn_and_split "extendsidi2"
+ [(set (match_operand:DI 0 "register_operand" "=d,d")
+ (sign_extend:DI (match_operand:SI 1 "nonimmediate_operand" "0,m")))]
+ "TARGET_64BIT"
+ "@
+ #
+ lw\t%0,%1"
+ "&& reload_completed && register_operand (operands[1], VOIDmode)"
+ [(const_int 0)]
+{
+ emit_note (NOTE_INSN_DELETED);
+ DONE;
+}
+ [(set_attr "move_type" "move,load")
+ (set_attr "mode" "DI")])
+
+(define_expand "extend<SHORT:mode><GPR:mode>2"
+ [(set (match_operand:GPR 0 "register_operand")
+ (sign_extend:GPR (match_operand:SHORT 1 "nonimmediate_operand")))]
+ "")
+
+(define_insn "*extend<SHORT:mode><GPR:mode>2_mips16e"
+ [(set (match_operand:GPR 0 "register_operand" "=d,d")
+ (sign_extend:GPR (match_operand:SHORT 1 "nonimmediate_operand" "0,m")))]
+ "GENERATE_MIPS16E"
+ "@
+ se<SHORT:size>\t%0
+ l<SHORT:size>\t%0,%1"
+ [(set_attr "move_type" "signext,load")
+ (set_attr "mode" "<GPR:MODE>")])
+
+(define_insn_and_split "*extend<SHORT:mode><GPR:mode>2"
+ [(set (match_operand:GPR 0 "register_operand" "=d,d")
+ (sign_extend:GPR
+ (match_operand:SHORT 1 "nonimmediate_operand" "d,m")))]
+ "!ISA_HAS_SEB_SEH && !GENERATE_MIPS16E"
+ "@
+ #
+ l<SHORT:size>\t%0,%1"
+ "&& reload_completed && REG_P (operands[1])"
+ [(set (match_dup 0) (ashift:GPR (match_dup 1) (match_dup 2)))
+ (set (match_dup 0) (ashiftrt:GPR (match_dup 0) (match_dup 2)))]
+{
+ operands[1] = gen_lowpart (<GPR:MODE>mode, operands[1]);
+ operands[2] = GEN_INT (GET_MODE_BITSIZE (<GPR:MODE>mode)
+ - GET_MODE_BITSIZE (<SHORT:MODE>mode));
+}
+ [(set_attr "move_type" "shift_shift,load")
+ (set_attr "mode" "<GPR:MODE>")])
+
+(define_insn "*extend<SHORT:mode><GPR:mode>2_se<SHORT:size>"
+ [(set (match_operand:GPR 0 "register_operand" "=d,d")
+ (sign_extend:GPR
+ (match_operand:SHORT 1 "nonimmediate_operand" "d,m")))]
+ "ISA_HAS_SEB_SEH"
+ "@
+ se<SHORT:size>\t%0,%1
+ l<SHORT:size>\t%0,%1"
+ [(set_attr "move_type" "signext,load")
+ (set_attr "mode" "<GPR:MODE>")])
+
+(define_expand "extendqihi2"
+ [(set (match_operand:HI 0 "register_operand")
+ (sign_extend:HI (match_operand:QI 1 "nonimmediate_operand")))]
+ "")
+
+(define_insn "*extendqihi2_mips16e"
+ [(set (match_operand:HI 0 "register_operand" "=d,d")
+ (sign_extend:HI (match_operand:QI 1 "nonimmediate_operand" "0,m")))]
+ "GENERATE_MIPS16E"
+ "@
+ seb\t%0
+ lb\t%0,%1"
+ [(set_attr "move_type" "signext,load")
+ (set_attr "mode" "SI")])
+
+(define_insn_and_split "*extendqihi2"
+ [(set (match_operand:HI 0 "register_operand" "=d,d")
+ (sign_extend:HI
+ (match_operand:QI 1 "nonimmediate_operand" "d,m")))]
+ "!ISA_HAS_SEB_SEH && !GENERATE_MIPS16E"
+ "@
+ #
+ lb\t%0,%1"
+ "&& reload_completed && REG_P (operands[1])"
+ [(set (match_dup 0) (ashift:SI (match_dup 1) (match_dup 2)))
+ (set (match_dup 0) (ashiftrt:SI (match_dup 0) (match_dup 2)))]
+{
+ operands[0] = gen_lowpart (SImode, operands[0]);
+ operands[1] = gen_lowpart (SImode, operands[1]);
+ operands[2] = GEN_INT (GET_MODE_BITSIZE (SImode)
+ - GET_MODE_BITSIZE (QImode));
+}
+ [(set_attr "move_type" "shift_shift,load")
+ (set_attr "mode" "SI")])
+
+(define_insn "*extendqihi2_seb"
+ [(set (match_operand:HI 0 "register_operand" "=d,d")
+ (sign_extend:HI
+ (match_operand:QI 1 "nonimmediate_operand" "d,m")))]
+ "ISA_HAS_SEB_SEH"
+ "@
+ seb\t%0,%1
+ lb\t%0,%1"
+ [(set_attr "move_type" "signext,load")
+ (set_attr "mode" "SI")])
+
+;; Combiner patterns for truncate/sign_extend combinations. The SI versions
+;; use the shift/truncate patterns.
+
+(define_insn_and_split "*extenddi_truncate<mode>"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (sign_extend:DI
+ (truncate:SHORT (match_operand:DI 1 "register_operand" "d"))))]
+ "TARGET_64BIT && !TARGET_MIPS16 && !ISA_HAS_EXTS"
+ "#"
+ "&& reload_completed"
+ [(set (match_dup 2)
+ (ashift:DI (match_dup 1)
+ (match_dup 3)))
+ (set (match_dup 0)
+ (ashiftrt:DI (match_dup 2)
+ (match_dup 3)))]
+{
+ operands[2] = gen_lowpart (DImode, operands[0]);
+ operands[3] = GEN_INT (BITS_PER_WORD - GET_MODE_BITSIZE (<MODE>mode));
+}
+ [(set_attr "move_type" "shift_shift")
+ (set_attr "mode" "DI")])
+
+(define_insn_and_split "*extendsi_truncate<mode>"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (sign_extend:SI
+ (truncate:SHORT (match_operand:DI 1 "register_operand" "d"))))]
+ "TARGET_64BIT && !TARGET_MIPS16 && !ISA_HAS_EXTS"
+ "#"
+ "&& reload_completed"
+ [(set (match_dup 2)
+ (ashift:DI (match_dup 1)
+ (match_dup 3)))
+ (set (match_dup 0)
+ (truncate:SI (ashiftrt:DI (match_dup 2)
+ (match_dup 3))))]
+{
+ operands[2] = gen_lowpart (DImode, operands[0]);
+ operands[3] = GEN_INT (BITS_PER_WORD - GET_MODE_BITSIZE (<MODE>mode));
+}
+ [(set_attr "move_type" "shift_shift")
+ (set_attr "mode" "SI")])
+
+(define_insn_and_split "*extendhi_truncateqi"
+ [(set (match_operand:HI 0 "register_operand" "=d")
+ (sign_extend:HI
+ (truncate:QI (match_operand:DI 1 "register_operand" "d"))))]
+ "TARGET_64BIT && !TARGET_MIPS16 && !ISA_HAS_EXTS"
+ "#"
+ "&& reload_completed"
+ [(set (match_dup 2)
+ (ashift:DI (match_dup 1)
+ (const_int 56)))
+ (set (match_dup 0)
+ (truncate:HI (ashiftrt:DI (match_dup 2)
+ (const_int 56))))]
+{
+ operands[2] = gen_lowpart (DImode, operands[0]);
+}
+ [(set_attr "move_type" "shift_shift")
+ (set_attr "mode" "SI")])
+
+(define_insn "*extend<GPR:mode>_truncate<SHORT:mode>_exts"
+ [(set (match_operand:GPR 0 "register_operand" "=d")
+ (sign_extend:GPR
+ (truncate:SHORT (match_operand:DI 1 "register_operand" "d"))))]
+ "TARGET_64BIT && !TARGET_MIPS16 && ISA_HAS_EXTS"
+{
+ operands[2] = GEN_INT (GET_MODE_BITSIZE (<SHORT:MODE>mode));
+ return "exts\t%0,%1,0,%m2";
+}
+ [(set_attr "type" "arith")
+ (set_attr "mode" "<GPR:MODE>")])
+
+(define_insn "*extendhi_truncateqi_exts"
+ [(set (match_operand:HI 0 "register_operand" "=d")
+ (sign_extend:HI
+ (truncate:QI (match_operand:DI 1 "register_operand" "d"))))]
+ "TARGET_64BIT && !TARGET_MIPS16 && ISA_HAS_EXTS"
+ "exts\t%0,%1,0,7"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+(define_insn "extendsfdf2"
+ [(set (match_operand:DF 0 "register_operand" "=f")
+ (float_extend:DF (match_operand:SF 1 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
+ "cvt.d.s\t%0,%1"
+ [(set_attr "type" "fcvt")
+ (set_attr "cnv_mode" "S2D")
+ (set_attr "mode" "DF")])
+
+;;
+;; ....................
+;;
+;; CONVERSIONS
+;;
+;; ....................
+
+(define_expand "fix_truncdfsi2"
+ [(set (match_operand:SI 0 "register_operand")
+ (fix:SI (match_operand:DF 1 "register_operand")))]
+ "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
+{
+ if (!ISA_HAS_TRUNC_W)
+ {
+ emit_insn (gen_fix_truncdfsi2_macro (operands[0], operands[1]));
+ DONE;
+ }
+})
+
+(define_insn "fix_truncdfsi2_insn"
+ [(set (match_operand:SI 0 "register_operand" "=f")
+ (fix:SI (match_operand:DF 1 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT && ISA_HAS_TRUNC_W"
+ "trunc.w.d %0,%1"
+ [(set_attr "type" "fcvt")
+ (set_attr "mode" "DF")
+ (set_attr "cnv_mode" "D2I")])
+
+(define_insn "fix_truncdfsi2_macro"
+ [(set (match_operand:SI 0 "register_operand" "=f")
+ (fix:SI (match_operand:DF 1 "register_operand" "f")))
+ (clobber (match_scratch:DF 2 "=d"))]
+ "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT && !ISA_HAS_TRUNC_W"
+{
+ if (mips_nomacro.nesting_level > 0)
+ return ".set\tmacro\;trunc.w.d %0,%1,%2\;.set\tnomacro";
+ else
+ return "trunc.w.d %0,%1,%2";
+}
+ [(set_attr "type" "fcvt")
+ (set_attr "mode" "DF")
+ (set_attr "cnv_mode" "D2I")
+ (set_attr "length" "36")])
+
+(define_expand "fix_truncsfsi2"
+ [(set (match_operand:SI 0 "register_operand")
+ (fix:SI (match_operand:SF 1 "register_operand")))]
+ "TARGET_HARD_FLOAT"
+{
+ if (!ISA_HAS_TRUNC_W)
+ {
+ emit_insn (gen_fix_truncsfsi2_macro (operands[0], operands[1]));
+ DONE;
+ }
+})
+
+(define_insn "fix_truncsfsi2_insn"
+ [(set (match_operand:SI 0 "register_operand" "=f")
+ (fix:SI (match_operand:SF 1 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT && ISA_HAS_TRUNC_W"
+ "trunc.w.s %0,%1"
+ [(set_attr "type" "fcvt")
+ (set_attr "mode" "SF")
+ (set_attr "cnv_mode" "S2I")])
+
+(define_insn "fix_truncsfsi2_macro"
+ [(set (match_operand:SI 0 "register_operand" "=f")
+ (fix:SI (match_operand:SF 1 "register_operand" "f")))
+ (clobber (match_scratch:SF 2 "=d"))]
+ "TARGET_HARD_FLOAT && !ISA_HAS_TRUNC_W"
+{
+ if (mips_nomacro.nesting_level > 0)
+ return ".set\tmacro\;trunc.w.s %0,%1,%2\;.set\tnomacro";
+ else
+ return "trunc.w.s %0,%1,%2";
+}
+ [(set_attr "type" "fcvt")
+ (set_attr "mode" "SF")
+ (set_attr "cnv_mode" "S2I")
+ (set_attr "length" "36")])
+
+
+(define_insn "fix_truncdfdi2"
+ [(set (match_operand:DI 0 "register_operand" "=f")
+ (fix:DI (match_operand:DF 1 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT && TARGET_FLOAT64 && TARGET_DOUBLE_FLOAT"
+ "trunc.l.d %0,%1"
+ [(set_attr "type" "fcvt")
+ (set_attr "mode" "DF")
+ (set_attr "cnv_mode" "D2I")])
+
+
+(define_insn "fix_truncsfdi2"
+ [(set (match_operand:DI 0 "register_operand" "=f")
+ (fix:DI (match_operand:SF 1 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT && TARGET_FLOAT64 && TARGET_DOUBLE_FLOAT"
+ "trunc.l.s %0,%1"
+ [(set_attr "type" "fcvt")
+ (set_attr "mode" "SF")
+ (set_attr "cnv_mode" "S2I")])
+
+
+(define_insn "floatsidf2"
+ [(set (match_operand:DF 0 "register_operand" "=f")
+ (float:DF (match_operand:SI 1 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
+ "cvt.d.w\t%0,%1"
+ [(set_attr "type" "fcvt")
+ (set_attr "mode" "DF")
+ (set_attr "cnv_mode" "I2D")])
+
+
+(define_insn "floatdidf2"
+ [(set (match_operand:DF 0 "register_operand" "=f")
+ (float:DF (match_operand:DI 1 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT && TARGET_FLOAT64 && TARGET_DOUBLE_FLOAT"
+ "cvt.d.l\t%0,%1"
+ [(set_attr "type" "fcvt")
+ (set_attr "mode" "DF")
+ (set_attr "cnv_mode" "I2D")])
+
+
+(define_insn "floatsisf2"
+ [(set (match_operand:SF 0 "register_operand" "=f")
+ (float:SF (match_operand:SI 1 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT"
+ "cvt.s.w\t%0,%1"
+ [(set_attr "type" "fcvt")
+ (set_attr "mode" "SF")
+ (set_attr "cnv_mode" "I2S")])
+
+
+(define_insn "floatdisf2"
+ [(set (match_operand:SF 0 "register_operand" "=f")
+ (float:SF (match_operand:DI 1 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT && TARGET_FLOAT64 && TARGET_DOUBLE_FLOAT"
+ "cvt.s.l\t%0,%1"
+ [(set_attr "type" "fcvt")
+ (set_attr "mode" "SF")
+ (set_attr "cnv_mode" "I2S")])
+
+
+(define_expand "fixuns_truncdfsi2"
+ [(set (match_operand:SI 0 "register_operand")
+ (unsigned_fix:SI (match_operand:DF 1 "register_operand")))]
+ "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
+{
+ rtx reg1 = gen_reg_rtx (DFmode);
+ rtx reg2 = gen_reg_rtx (DFmode);
+ rtx reg3 = gen_reg_rtx (SImode);
+ rtx label1 = gen_label_rtx ();
+ rtx label2 = gen_label_rtx ();
+ rtx test;
+ REAL_VALUE_TYPE offset;
+
+ real_2expN (&offset, 31, DFmode);
+
+ if (reg1) /* Turn off complaints about unreached code. */
+ {
+ mips_emit_move (reg1, CONST_DOUBLE_FROM_REAL_VALUE (offset, DFmode));
+ do_pending_stack_adjust ();
+
+ test = gen_rtx_GE (VOIDmode, operands[1], reg1);
+ emit_jump_insn (gen_cbranchdf4 (test, operands[1], reg1, label1));
+
+ emit_insn (gen_fix_truncdfsi2 (operands[0], operands[1]));
+ emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx,
+ gen_rtx_LABEL_REF (VOIDmode, label2)));
+ emit_barrier ();
+
+ emit_label (label1);
+ mips_emit_move (reg2, gen_rtx_MINUS (DFmode, operands[1], reg1));
+ mips_emit_move (reg3, GEN_INT (trunc_int_for_mode
+ (BITMASK_HIGH, SImode)));
+
+ emit_insn (gen_fix_truncdfsi2 (operands[0], reg2));
+ emit_insn (gen_iorsi3 (operands[0], operands[0], reg3));
+
+ emit_label (label2);
+
+ /* Allow REG_NOTES to be set on last insn (labels don't have enough
+ fields, and can't be used for REG_NOTES anyway). */
+ emit_use (stack_pointer_rtx);
+ DONE;
+ }
+})
+
+
+(define_expand "fixuns_truncdfdi2"
+ [(set (match_operand:DI 0 "register_operand")
+ (unsigned_fix:DI (match_operand:DF 1 "register_operand")))]
+ "TARGET_HARD_FLOAT && TARGET_64BIT && TARGET_DOUBLE_FLOAT"
+{
+ rtx reg1 = gen_reg_rtx (DFmode);
+ rtx reg2 = gen_reg_rtx (DFmode);
+ rtx reg3 = gen_reg_rtx (DImode);
+ rtx label1 = gen_label_rtx ();
+ rtx label2 = gen_label_rtx ();
+ rtx test;
+ REAL_VALUE_TYPE offset;
+
+ real_2expN (&offset, 63, DFmode);
+
+ mips_emit_move (reg1, CONST_DOUBLE_FROM_REAL_VALUE (offset, DFmode));
+ do_pending_stack_adjust ();
+
+ test = gen_rtx_GE (VOIDmode, operands[1], reg1);
+ emit_jump_insn (gen_cbranchdf4 (test, operands[1], reg1, label1));
+
+ emit_insn (gen_fix_truncdfdi2 (operands[0], operands[1]));
+ emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx,
+ gen_rtx_LABEL_REF (VOIDmode, label2)));
+ emit_barrier ();
+
+ emit_label (label1);
+ mips_emit_move (reg2, gen_rtx_MINUS (DFmode, operands[1], reg1));
+ mips_emit_move (reg3, GEN_INT (BITMASK_HIGH));
+ emit_insn (gen_ashldi3 (reg3, reg3, GEN_INT (32)));
+
+ emit_insn (gen_fix_truncdfdi2 (operands[0], reg2));
+ emit_insn (gen_iordi3 (operands[0], operands[0], reg3));
+
+ emit_label (label2);
+
+ /* Allow REG_NOTES to be set on last insn (labels don't have enough
+ fields, and can't be used for REG_NOTES anyway). */
+ emit_use (stack_pointer_rtx);
+ DONE;
+})
+
+
+(define_expand "fixuns_truncsfsi2"
+ [(set (match_operand:SI 0 "register_operand")
+ (unsigned_fix:SI (match_operand:SF 1 "register_operand")))]
+ "TARGET_HARD_FLOAT"
+{
+ rtx reg1 = gen_reg_rtx (SFmode);
+ rtx reg2 = gen_reg_rtx (SFmode);
+ rtx reg3 = gen_reg_rtx (SImode);
+ rtx label1 = gen_label_rtx ();
+ rtx label2 = gen_label_rtx ();
+ rtx test;
+ REAL_VALUE_TYPE offset;
+
+ real_2expN (&offset, 31, SFmode);
+
+ mips_emit_move (reg1, CONST_DOUBLE_FROM_REAL_VALUE (offset, SFmode));
+ do_pending_stack_adjust ();
+
+ test = gen_rtx_GE (VOIDmode, operands[1], reg1);
+ emit_jump_insn (gen_cbranchsf4 (test, operands[1], reg1, label1));
+
+ emit_insn (gen_fix_truncsfsi2 (operands[0], operands[1]));
+ emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx,
+ gen_rtx_LABEL_REF (VOIDmode, label2)));
+ emit_barrier ();
+
+ emit_label (label1);
+ mips_emit_move (reg2, gen_rtx_MINUS (SFmode, operands[1], reg1));
+ mips_emit_move (reg3, GEN_INT (trunc_int_for_mode
+ (BITMASK_HIGH, SImode)));
+
+ emit_insn (gen_fix_truncsfsi2 (operands[0], reg2));
+ emit_insn (gen_iorsi3 (operands[0], operands[0], reg3));
+
+ emit_label (label2);
+
+ /* Allow REG_NOTES to be set on last insn (labels don't have enough
+ fields, and can't be used for REG_NOTES anyway). */
+ emit_use (stack_pointer_rtx);
+ DONE;
+})
+
+
+(define_expand "fixuns_truncsfdi2"
+ [(set (match_operand:DI 0 "register_operand")
+ (unsigned_fix:DI (match_operand:SF 1 "register_operand")))]
+ "TARGET_HARD_FLOAT && TARGET_64BIT && TARGET_DOUBLE_FLOAT"
+{
+ rtx reg1 = gen_reg_rtx (SFmode);
+ rtx reg2 = gen_reg_rtx (SFmode);
+ rtx reg3 = gen_reg_rtx (DImode);
+ rtx label1 = gen_label_rtx ();
+ rtx label2 = gen_label_rtx ();
+ rtx test;
+ REAL_VALUE_TYPE offset;
+
+ real_2expN (&offset, 63, SFmode);
+
+ mips_emit_move (reg1, CONST_DOUBLE_FROM_REAL_VALUE (offset, SFmode));
+ do_pending_stack_adjust ();
+
+ test = gen_rtx_GE (VOIDmode, operands[1], reg1);
+ emit_jump_insn (gen_cbranchsf4 (test, operands[1], reg1, label1));
+
+ emit_insn (gen_fix_truncsfdi2 (operands[0], operands[1]));
+ emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx,
+ gen_rtx_LABEL_REF (VOIDmode, label2)));
+ emit_barrier ();
+
+ emit_label (label1);
+ mips_emit_move (reg2, gen_rtx_MINUS (SFmode, operands[1], reg1));
+ mips_emit_move (reg3, GEN_INT (BITMASK_HIGH));
+ emit_insn (gen_ashldi3 (reg3, reg3, GEN_INT (32)));
+
+ emit_insn (gen_fix_truncsfdi2 (operands[0], reg2));
+ emit_insn (gen_iordi3 (operands[0], operands[0], reg3));
+
+ emit_label (label2);
+
+ /* Allow REG_NOTES to be set on last insn (labels don't have enough
+ fields, and can't be used for REG_NOTES anyway). */
+ emit_use (stack_pointer_rtx);
+ DONE;
+})
+
+;;
+;; ....................
+;;
+;; DATA MOVEMENT
+;;
+;; ....................
+
+;; Bit field extract patterns which use lwl/lwr or ldl/ldr.
+
+(define_expand "extv"
+ [(set (match_operand 0 "register_operand")
+ (sign_extract (match_operand 1 "nonimmediate_operand")
+ (match_operand 2 "const_int_operand")
+ (match_operand 3 "const_int_operand")))]
+ "!TARGET_MIPS16"
+{
+ if (mips_expand_ext_as_unaligned_load (operands[0], operands[1],
+ INTVAL (operands[2]),
+ INTVAL (operands[3])))
+ DONE;
+ else if (register_operand (operands[1], GET_MODE (operands[0]))
+ && ISA_HAS_EXTS && UINTVAL (operands[2]) <= 32)
+ {
+ if (GET_MODE (operands[0]) == DImode)
+ emit_insn (gen_extvdi (operands[0], operands[1], operands[2],
+ operands[3]));
+ else
+ emit_insn (gen_extvsi (operands[0], operands[1], operands[2],
+ operands[3]));
+ DONE;
+ }
+ else
+ FAIL;
+})
+
+(define_insn "extv<mode>"
+ [(set (match_operand:GPR 0 "register_operand" "=d")
+ (sign_extract:GPR (match_operand:GPR 1 "register_operand" "d")
+ (match_operand 2 "const_int_operand" "")
+ (match_operand 3 "const_int_operand" "")))]
+ "ISA_HAS_EXTS && UINTVAL (operands[2]) <= 32"
+ "exts\t%0,%1,%3,%m2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "<MODE>")])
+
+
+(define_expand "extzv"
+ [(set (match_operand 0 "register_operand")
+ (zero_extract (match_operand 1 "nonimmediate_operand")
+ (match_operand 2 "const_int_operand")
+ (match_operand 3 "const_int_operand")))]
+ "!TARGET_MIPS16"
+{
+ if (mips_expand_ext_as_unaligned_load (operands[0], operands[1],
+ INTVAL (operands[2]),
+ INTVAL (operands[3])))
+ DONE;
+ else if (mips_use_ins_ext_p (operands[1], INTVAL (operands[2]),
+ INTVAL (operands[3])))
+ {
+ if (GET_MODE (operands[0]) == DImode)
+ emit_insn (gen_extzvdi (operands[0], operands[1], operands[2],
+ operands[3]));
+ else
+ emit_insn (gen_extzvsi (operands[0], operands[1], operands[2],
+ operands[3]));
+ DONE;
+ }
+ else
+ FAIL;
+})
+
+(define_insn "extzv<mode>"
+ [(set (match_operand:GPR 0 "register_operand" "=d")
+ (zero_extract:GPR (match_operand:GPR 1 "register_operand" "d")
+ (match_operand 2 "const_int_operand" "")
+ (match_operand 3 "const_int_operand" "")))]
+ "mips_use_ins_ext_p (operands[1], INTVAL (operands[2]),
+ INTVAL (operands[3]))"
+ "<d>ext\t%0,%1,%3,%2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "<MODE>")])
+
+(define_insn "*extzv_truncsi_exts"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (truncate:SI
+ (zero_extract:DI (match_operand:DI 1 "register_operand" "d")
+ (match_operand 2 "const_int_operand" "")
+ (match_operand 3 "const_int_operand" ""))))]
+ "ISA_HAS_EXTS && TARGET_64BIT && IN_RANGE (INTVAL (operands[2]), 32, 63)"
+ "exts\t%0,%1,%3,31"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+
+(define_expand "insv"
+ [(set (zero_extract (match_operand 0 "nonimmediate_operand")
+ (match_operand 1 "immediate_operand")
+ (match_operand 2 "immediate_operand"))
+ (match_operand 3 "reg_or_0_operand"))]
+ "!TARGET_MIPS16"
+{
+ if (mips_expand_ins_as_unaligned_store (operands[0], operands[3],
+ INTVAL (operands[1]),
+ INTVAL (operands[2])))
+ DONE;
+ else if (mips_use_ins_ext_p (operands[0], INTVAL (operands[1]),
+ INTVAL (operands[2])))
+ {
+ if (GET_MODE (operands[0]) == DImode)
+ emit_insn (gen_insvdi (operands[0], operands[1], operands[2],
+ operands[3]));
+ else
+ emit_insn (gen_insvsi (operands[0], operands[1], operands[2],
+ operands[3]));
+ DONE;
+ }
+ else
+ FAIL;
+})
+
+(define_insn "insv<mode>"
+ [(set (zero_extract:GPR (match_operand:GPR 0 "register_operand" "+d")
+ (match_operand:SI 1 "immediate_operand" "I")
+ (match_operand:SI 2 "immediate_operand" "I"))
+ (match_operand:GPR 3 "reg_or_0_operand" "dJ"))]
+ "mips_use_ins_ext_p (operands[0], INTVAL (operands[1]),
+ INTVAL (operands[2]))"
+ "<d>ins\t%0,%z3,%2,%1"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "<MODE>")])
+
+;; Combiner pattern for cins (clear and insert bit field). We can
+;; implement mask-and-shift-left operation with this. Note that if
+;; the upper bit of the mask is set in an SImode operation, the mask
+;; itself will be sign-extended. mask_low_and_shift_len will
+;; therefore be greater than our threshold of 32.
+
+(define_insn "*cins<mode>"
+ [(set (match_operand:GPR 0 "register_operand" "=d")
+ (and:GPR
+ (ashift:GPR (match_operand:GPR 1 "register_operand" "d")
+ (match_operand:GPR 2 "const_int_operand" ""))
+ (match_operand:GPR 3 "const_int_operand" "")))]
+ "ISA_HAS_CINS
+ && mask_low_and_shift_p (<MODE>mode, operands[3], operands[2], 32)"
+{
+ operands[3] =
+ GEN_INT (mask_low_and_shift_len (<MODE>mode, operands[3], operands[2]));
+ return "cins\t%0,%1,%2,%m3";
+}
+ [(set_attr "type" "shift")
+ (set_attr "mode" "<MODE>")])
+
+;; Unaligned word moves generated by the bit field patterns.
+;;
+;; As far as the rtl is concerned, both the left-part and right-part
+;; instructions can access the whole field. However, the real operand
+;; refers to just the first or the last byte (depending on endianness).
+;; We therefore use two memory operands to each instruction, one to
+;; describe the rtl effect and one to use in the assembly output.
+;;
+;; Operands 0 and 1 are the rtl-level target and source respectively.
+;; This allows us to use the standard length calculations for the "load"
+;; and "store" type attributes.
+
+(define_insn "mov_<load>l"
+ [(set (match_operand:GPR 0 "register_operand" "=d")
+ (unspec:GPR [(match_operand:BLK 1 "memory_operand" "m")
+ (match_operand:QI 2 "memory_operand" "m")]
+ UNSPEC_LOAD_LEFT))]
+ "!TARGET_MIPS16 && mips_mem_fits_mode_p (<MODE>mode, operands[1])"
+ "<load>l\t%0,%2"
+ [(set_attr "move_type" "load")
+ (set_attr "mode" "<MODE>")])
+
+(define_insn "mov_<load>r"
+ [(set (match_operand:GPR 0 "register_operand" "=d")
+ (unspec:GPR [(match_operand:BLK 1 "memory_operand" "m")
+ (match_operand:QI 2 "memory_operand" "m")
+ (match_operand:GPR 3 "register_operand" "0")]
+ UNSPEC_LOAD_RIGHT))]
+ "!TARGET_MIPS16 && mips_mem_fits_mode_p (<MODE>mode, operands[1])"
+ "<load>r\t%0,%2"
+ [(set_attr "move_type" "load")
+ (set_attr "mode" "<MODE>")])
+
+(define_insn "mov_<store>l"
+ [(set (match_operand:BLK 0 "memory_operand" "=m")
+ (unspec:BLK [(match_operand:GPR 1 "reg_or_0_operand" "dJ")
+ (match_operand:QI 2 "memory_operand" "m")]
+ UNSPEC_STORE_LEFT))]
+ "!TARGET_MIPS16 && mips_mem_fits_mode_p (<MODE>mode, operands[0])"
+ "<store>l\t%z1,%2"
+ [(set_attr "move_type" "store")
+ (set_attr "mode" "<MODE>")])
+
+(define_insn "mov_<store>r"
+ [(set (match_operand:BLK 0 "memory_operand" "+m")
+ (unspec:BLK [(match_operand:GPR 1 "reg_or_0_operand" "dJ")
+ (match_operand:QI 2 "memory_operand" "m")
+ (match_dup 0)]
+ UNSPEC_STORE_RIGHT))]
+ "!TARGET_MIPS16 && mips_mem_fits_mode_p (<MODE>mode, operands[0])"
+ "<store>r\t%z1,%2"
+ [(set_attr "move_type" "store")
+ (set_attr "mode" "<MODE>")])
+
+;; An instruction to calculate the high part of a 64-bit SYMBOL_ABSOLUTE.
+;; The required value is:
+;;
+;; (%highest(op1) << 48) + (%higher(op1) << 32) + (%hi(op1) << 16)
+;;
+;; which translates to:
+;;
+;; lui op0,%highest(op1)
+;; daddiu op0,op0,%higher(op1)
+;; dsll op0,op0,16
+;; daddiu op0,op0,%hi(op1)
+;; dsll op0,op0,16
+;;
+;; The split is deferred until after flow2 to allow the peephole2 below
+;; to take effect.
+(define_insn_and_split "*lea_high64"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (high:DI (match_operand:DI 1 "absolute_symbolic_operand" "")))]
+ "TARGET_EXPLICIT_RELOCS && ABI_HAS_64BIT_SYMBOLS"
+ "#"
+ "&& epilogue_completed"
+ [(set (match_dup 0) (high:DI (match_dup 2)))
+ (set (match_dup 0) (lo_sum:DI (match_dup 0) (match_dup 2)))
+ (set (match_dup 0) (ashift:DI (match_dup 0) (const_int 16)))
+ (set (match_dup 0) (lo_sum:DI (match_dup 0) (match_dup 3)))
+ (set (match_dup 0) (ashift:DI (match_dup 0) (const_int 16)))]
+{
+ operands[2] = mips_unspec_address (operands[1], SYMBOL_64_HIGH);
+ operands[3] = mips_unspec_address (operands[1], SYMBOL_64_MID);
+}
+ [(set_attr "length" "20")])
+
+;; Use a scratch register to reduce the latency of the above pattern
+;; on superscalar machines. The optimized sequence is:
+;;
+;; lui op1,%highest(op2)
+;; lui op0,%hi(op2)
+;; daddiu op1,op1,%higher(op2)
+;; dsll32 op1,op1,0
+;; daddu op1,op1,op0
+(define_peephole2
+ [(set (match_operand:DI 1 "d_operand")
+ (high:DI (match_operand:DI 2 "absolute_symbolic_operand")))
+ (match_scratch:DI 0 "d")]
+ "TARGET_EXPLICIT_RELOCS && ABI_HAS_64BIT_SYMBOLS"
+ [(set (match_dup 1) (high:DI (match_dup 3)))
+ (set (match_dup 0) (high:DI (match_dup 4)))
+ (set (match_dup 1) (lo_sum:DI (match_dup 1) (match_dup 3)))
+ (set (match_dup 1) (ashift:DI (match_dup 1) (const_int 32)))
+ (set (match_dup 1) (plus:DI (match_dup 1) (match_dup 0)))]
+{
+ operands[3] = mips_unspec_address (operands[2], SYMBOL_64_HIGH);
+ operands[4] = mips_unspec_address (operands[2], SYMBOL_64_LOW);
+})
+
+;; On most targets, the expansion of (lo_sum (high X) X) for a 64-bit
+;; SYMBOL_ABSOLUTE X will take 6 cycles. This next pattern allows combine
+;; to merge the HIGH and LO_SUM parts of a move if the HIGH part is only
+;; used once. We can then use the sequence:
+;;
+;; lui op0,%highest(op1)
+;; lui op2,%hi(op1)
+;; daddiu op0,op0,%higher(op1)
+;; daddiu op2,op2,%lo(op1)
+;; dsll32 op0,op0,0
+;; daddu op0,op0,op2
+;;
+;; which takes 4 cycles on most superscalar targets.
+(define_insn_and_split "*lea64"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (match_operand:DI 1 "absolute_symbolic_operand" ""))
+ (clobber (match_scratch:DI 2 "=&d"))]
+ "TARGET_EXPLICIT_RELOCS && ABI_HAS_64BIT_SYMBOLS && cse_not_expected"
+ "#"
+ "&& reload_completed"
+ [(set (match_dup 0) (high:DI (match_dup 3)))
+ (set (match_dup 2) (high:DI (match_dup 4)))
+ (set (match_dup 0) (lo_sum:DI (match_dup 0) (match_dup 3)))
+ (set (match_dup 2) (lo_sum:DI (match_dup 2) (match_dup 4)))
+ (set (match_dup 0) (ashift:DI (match_dup 0) (const_int 32)))
+ (set (match_dup 0) (plus:DI (match_dup 0) (match_dup 2)))]
+{
+ operands[3] = mips_unspec_address (operands[1], SYMBOL_64_HIGH);
+ operands[4] = mips_unspec_address (operands[1], SYMBOL_64_LOW);
+}
+ [(set_attr "length" "24")])
+
+;; Split HIGHs into:
+;;
+;; li op0,%hi(sym)
+;; sll op0,16
+;;
+;; on MIPS16 targets.
+(define_split
+ [(set (match_operand:SI 0 "d_operand")
+ (high:SI (match_operand:SI 1 "absolute_symbolic_operand")))]
+ "TARGET_MIPS16 && reload_completed"
+ [(set (match_dup 0) (match_dup 2))
+ (set (match_dup 0) (ashift:SI (match_dup 0) (const_int 16)))]
+{
+ operands[2] = mips_unspec_address (operands[1], SYMBOL_32_HIGH);
+})
+
+;; Insns to fetch a symbol from a big GOT.
+
+(define_insn_and_split "*xgot_hi<mode>"
+ [(set (match_operand:P 0 "register_operand" "=d")
+ (high:P (match_operand:P 1 "got_disp_operand" "")))]
+ "TARGET_EXPLICIT_RELOCS && TARGET_XGOT"
+ "#"
+ "&& reload_completed"
+ [(set (match_dup 0) (high:P (match_dup 2)))
+ (set (match_dup 0) (plus:P (match_dup 0) (match_dup 3)))]
+{
+ operands[2] = mips_unspec_address (operands[1], SYMBOL_GOTOFF_DISP);
+ operands[3] = pic_offset_table_rtx;
+}
+ [(set_attr "got" "xgot_high")
+ (set_attr "mode" "<MODE>")])
+
+(define_insn_and_split "*xgot_lo<mode>"
+ [(set (match_operand:P 0 "register_operand" "=d")
+ (lo_sum:P (match_operand:P 1 "register_operand" "d")
+ (match_operand:P 2 "got_disp_operand" "")))]
+ "TARGET_EXPLICIT_RELOCS && TARGET_XGOT"
+ "#"
+ "&& reload_completed"
+ [(set (match_dup 0)
+ (unspec:P [(match_dup 1) (match_dup 3)] UNSPEC_LOAD_GOT))]
+ { operands[3] = mips_unspec_address (operands[2], SYMBOL_GOTOFF_DISP); }
+ [(set_attr "got" "load")
+ (set_attr "mode" "<MODE>")])
+
+;; Insns to fetch a symbol from a normal GOT.
+
+(define_insn_and_split "*got_disp<mode>"
+ [(set (match_operand:P 0 "register_operand" "=d")
+ (match_operand:P 1 "got_disp_operand" ""))]
+ "TARGET_EXPLICIT_RELOCS && !mips_split_p[SYMBOL_GOT_DISP]"
+ "#"
+ "&& reload_completed"
+ [(set (match_dup 0) (match_dup 2))]
+ { operands[2] = mips_got_load (NULL, operands[1], SYMBOL_GOTOFF_DISP); }
+ [(set_attr "got" "load")
+ (set_attr "mode" "<MODE>")])
+
+;; Insns for loading the "page" part of a page/ofst address from the GOT.
+
+(define_insn_and_split "*got_page<mode>"
+ [(set (match_operand:P 0 "register_operand" "=d")
+ (high:P (match_operand:P 1 "got_page_ofst_operand" "")))]
+ "TARGET_EXPLICIT_RELOCS && !mips_split_hi_p[SYMBOL_GOT_PAGE_OFST]"
+ "#"
+ "&& reload_completed"
+ [(set (match_dup 0) (match_dup 2))]
+ { operands[2] = mips_got_load (NULL, operands[1], SYMBOL_GOTOFF_PAGE); }
+ [(set_attr "got" "load")
+ (set_attr "mode" "<MODE>")])
+
+;; Convenience expander that generates the rhs of a load_got<mode> insn.
+(define_expand "unspec_got<mode>"
+ [(unspec:P [(match_operand:P 0)
+ (match_operand:P 1)] UNSPEC_LOAD_GOT)])
+
+;; Lower-level instructions for loading an address from the GOT.
+;; We could use MEMs, but an unspec gives more optimization
+;; opportunities.
+
+(define_insn "load_got<mode>"
+ [(set (match_operand:P 0 "register_operand" "=d")
+ (unspec:P [(match_operand:P 1 "register_operand" "d")
+ (match_operand:P 2 "immediate_operand" "")]
+ UNSPEC_LOAD_GOT))]
+ ""
+ "<load>\t%0,%R2(%1)"
+ [(set_attr "got" "load")
+ (set_attr "mode" "<MODE>")])
+
+;; Instructions for adding the low 16 bits of an address to a register.
+;; Operand 2 is the address: mips_print_operand works out which relocation
+;; should be applied.
+
+(define_insn "*low<mode>"
+ [(set (match_operand:P 0 "register_operand" "=d")
+ (lo_sum:P (match_operand:P 1 "register_operand" "d")
+ (match_operand:P 2 "immediate_operand" "")))]
+ "!TARGET_MIPS16"
+ "<d>addiu\t%0,%1,%R2"
+ [(set_attr "alu_type" "add")
+ (set_attr "mode" "<MODE>")])
+
+(define_insn "*low<mode>_mips16"
+ [(set (match_operand:P 0 "register_operand" "=d")
+ (lo_sum:P (match_operand:P 1 "register_operand" "0")
+ (match_operand:P 2 "immediate_operand" "")))]
+ "TARGET_MIPS16"
+ "<d>addiu\t%0,%R2"
+ [(set_attr "alu_type" "add")
+ (set_attr "mode" "<MODE>")
+ (set_attr "extended_mips16" "yes")])
+
+;; Expose MIPS16 uses of the global pointer after reload if the function
+;; is responsible for setting up the register itself.
+(define_split
+ [(set (match_operand:GPR 0 "d_operand")
+ (const:GPR (unspec:GPR [(const_int 0)] UNSPEC_GP)))]
+ "TARGET_MIPS16 && TARGET_USE_GOT && reload_completed"
+ [(set (match_dup 0) (match_dup 1))]
+ { operands[1] = pic_offset_table_rtx; })
+
+;; Allow combine to split complex const_int load sequences, using operand 2
+;; to store the intermediate results. See move_operand for details.
+(define_split
+ [(set (match_operand:GPR 0 "register_operand")
+ (match_operand:GPR 1 "splittable_const_int_operand"))
+ (clobber (match_operand:GPR 2 "register_operand"))]
+ ""
+ [(const_int 0)]
+{
+ mips_move_integer (operands[2], operands[0], INTVAL (operands[1]));
+ DONE;
+})
+
+;; Likewise, for symbolic operands.
+(define_split
+ [(set (match_operand:P 0 "register_operand")
+ (match_operand:P 1))
+ (clobber (match_operand:P 2 "register_operand"))]
+ "mips_split_symbol (operands[2], operands[1], MAX_MACHINE_MODE, NULL)"
+ [(set (match_dup 0) (match_dup 3))]
+{
+ mips_split_symbol (operands[2], operands[1],
+ MAX_MACHINE_MODE, &operands[3]);
+})
+
+;; 64-bit integer moves
+
+;; Unlike most other insns, the move insns can't be split with
+;; different predicates, because register spilling and other parts of
+;; the compiler, have memoized the insn number already.
+
+(define_expand "movdi"
+ [(set (match_operand:DI 0 "")
+ (match_operand:DI 1 ""))]
+ ""
+{
+ if (mips_legitimize_move (DImode, operands[0], operands[1]))
+ DONE;
+})
+
+;; For mips16, we need a special case to handle storing $31 into
+;; memory, since we don't have a constraint to match $31. This
+;; instruction can be generated by save_restore_insns.
+
+(define_insn "*mov<mode>_ra"
+ [(set (match_operand:GPR 0 "stack_operand" "=m")
+ (reg:GPR RETURN_ADDR_REGNUM))]
+ "TARGET_MIPS16"
+ "<store>\t$31,%0"
+ [(set_attr "move_type" "store")
+ (set_attr "mode" "<MODE>")])
+
+(define_insn "*movdi_32bit"
+ [(set (match_operand:DI 0 "nonimmediate_operand" "=d,d,d,m,*a,*d,*f,*f,*d,*m,*B*C*D,*B*C*D,*d,*m")
+ (match_operand:DI 1 "move_operand" "d,i,m,d,*J*d,*a,*J*d,*m,*f,*f,*d,*m,*B*C*D,*B*C*D"))]
+ "!TARGET_64BIT && !TARGET_MIPS16
+ && (register_operand (operands[0], DImode)
+ || reg_or_0_operand (operands[1], DImode))"
+ { return mips_output_move (operands[0], operands[1]); }
+ [(set_attr "move_type" "move,const,load,store,mthilo,mfhilo,mtc,fpload,mfc,fpstore,mtc,fpload,mfc,fpstore")
+ (set_attr "mode" "DI")])
+
+(define_insn "*movdi_32bit_mips16"
+ [(set (match_operand:DI 0 "nonimmediate_operand" "=d,y,d,d,d,d,m,*d")
+ (match_operand:DI 1 "move_operand" "d,d,y,K,N,m,d,*x"))]
+ "!TARGET_64BIT && TARGET_MIPS16
+ && (register_operand (operands[0], DImode)
+ || register_operand (operands[1], DImode))"
+ { return mips_output_move (operands[0], operands[1]); }
+ [(set_attr "move_type" "move,move,move,const,constN,load,store,mfhilo")
+ (set_attr "mode" "DI")])
+
+(define_insn "*movdi_64bit"
+ [(set (match_operand:DI 0 "nonimmediate_operand" "=d,d,e,d,m,*f,*f,*d,*m,*a,*d,*B*C*D,*B*C*D,*d,*m")
+ (match_operand:DI 1 "move_operand" "d,U,T,m,dJ,*d*J,*m,*f,*f,*J*d,*a,*d,*m,*B*C*D,*B*C*D"))]
+ "TARGET_64BIT && !TARGET_MIPS16
+ && (register_operand (operands[0], DImode)
+ || reg_or_0_operand (operands[1], DImode))"
+ { return mips_output_move (operands[0], operands[1]); }
+ [(set_attr "move_type" "move,const,const,load,store,mtc,fpload,mfc,fpstore,mthilo,mfhilo,mtc,fpload,mfc,fpstore")
+ (set_attr "mode" "DI")])
+
+(define_insn "*movdi_64bit_mips16"
+ [(set (match_operand:DI 0 "nonimmediate_operand" "=d,y,d,d,d,d,d,d,m,*d")
+ (match_operand:DI 1 "move_operand" "d,d,y,K,N,U,kf,m,d,*a"))]
+ "TARGET_64BIT && TARGET_MIPS16
+ && (register_operand (operands[0], DImode)
+ || register_operand (operands[1], DImode))"
+ { return mips_output_move (operands[0], operands[1]); }
+ [(set_attr "move_type" "move,move,move,const,constN,const,loadpool,load,store,mfhilo")
+ (set_attr "mode" "DI")])
+
+;; On the mips16, we can split ld $r,N($r) into an add and a load,
+;; when the original load is a 4 byte instruction but the add and the
+;; load are 2 2 byte instructions.
+
+(define_split
+ [(set (match_operand:DI 0 "d_operand")
+ (mem:DI (plus:DI (match_dup 0)
+ (match_operand:DI 1 "const_int_operand"))))]
+ "TARGET_64BIT && TARGET_MIPS16 && reload_completed
+ && !TARGET_DEBUG_D_MODE
+ && ((INTVAL (operands[1]) < 0
+ && INTVAL (operands[1]) >= -0x10)
+ || (INTVAL (operands[1]) >= 32 * 8
+ && INTVAL (operands[1]) <= 31 * 8 + 0x8)
+ || (INTVAL (operands[1]) >= 0
+ && INTVAL (operands[1]) < 32 * 8
+ && (INTVAL (operands[1]) & 7) != 0))"
+ [(set (match_dup 0) (plus:DI (match_dup 0) (match_dup 1)))
+ (set (match_dup 0) (mem:DI (plus:DI (match_dup 0) (match_dup 2))))]
+{
+ HOST_WIDE_INT val = INTVAL (operands[1]);
+
+ if (val < 0)
+ operands[2] = const0_rtx;
+ else if (val >= 32 * 8)
+ {
+ int off = val & 7;
+
+ operands[1] = GEN_INT (0x8 + off);
+ operands[2] = GEN_INT (val - off - 0x8);
+ }
+ else
+ {
+ int off = val & 7;
+
+ operands[1] = GEN_INT (off);
+ operands[2] = GEN_INT (val - off);
+ }
+})
+
+;; 32-bit Integer moves
+
+;; Unlike most other insns, the move insns can't be split with
+;; different predicates, because register spilling and other parts of
+;; the compiler, have memoized the insn number already.
+
+(define_expand "mov<mode>"
+ [(set (match_operand:IMOVE32 0 "")
+ (match_operand:IMOVE32 1 ""))]
+ ""
+{
+ if (mips_legitimize_move (<MODE>mode, operands[0], operands[1]))
+ DONE;
+})
+
+;; The difference between these two is whether or not ints are allowed
+;; in FP registers (off by default, use -mdebugh to enable).
+
+(define_insn "*mov<mode>_internal"
+ [(set (match_operand:IMOVE32 0 "nonimmediate_operand" "=d,d,e,d,m,*f,*f,*d,*m,*d,*z,*a,*d,*B*C*D,*B*C*D,*d,*m")
+ (match_operand:IMOVE32 1 "move_operand" "d,U,T,m,dJ,*d*J,*m,*f,*f,*z,*d,*J*d,*a,*d,*m,*B*C*D,*B*C*D"))]
+ "!TARGET_MIPS16
+ && (register_operand (operands[0], <MODE>mode)
+ || reg_or_0_operand (operands[1], <MODE>mode))"
+ { return mips_output_move (operands[0], operands[1]); }
+ [(set_attr "move_type" "move,const,const,load,store,mtc,fpload,mfc,fpstore,mfc,mtc,mthilo,mfhilo,mtc,fpload,mfc,fpstore")
+ (set_attr "mode" "SI")])
+
+(define_insn "*mov<mode>_mips16"
+ [(set (match_operand:IMOVE32 0 "nonimmediate_operand" "=d,y,d,d,d,d,d,d,m,*d")
+ (match_operand:IMOVE32 1 "move_operand" "d,d,y,K,N,U,kf,m,d,*a"))]
+ "TARGET_MIPS16
+ && (register_operand (operands[0], <MODE>mode)
+ || register_operand (operands[1], <MODE>mode))"
+ { return mips_output_move (operands[0], operands[1]); }
+ [(set_attr "move_type" "move,move,move,const,constN,const,loadpool,load,store,mfhilo")
+ (set_attr "mode" "SI")])
+
+;; On the mips16, we can split lw $r,N($r) into an add and a load,
+;; when the original load is a 4 byte instruction but the add and the
+;; load are 2 2 byte instructions.
+
+(define_split
+ [(set (match_operand:SI 0 "d_operand")
+ (mem:SI (plus:SI (match_dup 0)
+ (match_operand:SI 1 "const_int_operand"))))]
+ "TARGET_MIPS16 && reload_completed && !TARGET_DEBUG_D_MODE
+ && ((INTVAL (operands[1]) < 0
+ && INTVAL (operands[1]) >= -0x80)
+ || (INTVAL (operands[1]) >= 32 * 4
+ && INTVAL (operands[1]) <= 31 * 4 + 0x7c)
+ || (INTVAL (operands[1]) >= 0
+ && INTVAL (operands[1]) < 32 * 4
+ && (INTVAL (operands[1]) & 3) != 0))"
+ [(set (match_dup 0) (plus:SI (match_dup 0) (match_dup 1)))
+ (set (match_dup 0) (mem:SI (plus:SI (match_dup 0) (match_dup 2))))]
+{
+ HOST_WIDE_INT val = INTVAL (operands[1]);
+
+ if (val < 0)
+ operands[2] = const0_rtx;
+ else if (val >= 32 * 4)
+ {
+ int off = val & 3;
+
+ operands[1] = GEN_INT (0x7c + off);
+ operands[2] = GEN_INT (val - off - 0x7c);
+ }
+ else
+ {
+ int off = val & 3;
+
+ operands[1] = GEN_INT (off);
+ operands[2] = GEN_INT (val - off);
+ }
+})
+
+;; On the mips16, we can split a load of certain constants into a load
+;; and an add. This turns a 4 byte instruction into 2 2 byte
+;; instructions.
+
+(define_split
+ [(set (match_operand:SI 0 "d_operand")
+ (match_operand:SI 1 "const_int_operand"))]
+ "TARGET_MIPS16 && reload_completed && !TARGET_DEBUG_D_MODE
+ && INTVAL (operands[1]) >= 0x100
+ && INTVAL (operands[1]) <= 0xff + 0x7f"
+ [(set (match_dup 0) (match_dup 1))
+ (set (match_dup 0) (plus:SI (match_dup 0) (match_dup 2)))]
+{
+ int val = INTVAL (operands[1]);
+
+ operands[1] = GEN_INT (0xff);
+ operands[2] = GEN_INT (val - 0xff);
+})
+
+;; This insn handles moving CCmode values. It's really just a
+;; slightly simplified copy of movsi_internal2, with additional cases
+;; to move a condition register to a general register and to move
+;; between the general registers and the floating point registers.
+
+(define_insn "movcc"
+ [(set (match_operand:CC 0 "nonimmediate_operand" "=d,*d,*d,*m,*d,*f,*f,*f,*m")
+ (match_operand:CC 1 "general_operand" "z,*d,*m,*d,*f,*d,*f,*m,*f"))]
+ "ISA_HAS_8CC && TARGET_HARD_FLOAT"
+ { return mips_output_move (operands[0], operands[1]); }
+ [(set_attr "move_type" "lui_movf,move,load,store,mfc,mtc,fmove,fpload,fpstore")
+ (set_attr "mode" "SI")])
+
+;; Reload condition code registers. reload_incc and reload_outcc
+;; both handle moves from arbitrary operands into condition code
+;; registers. reload_incc handles the more common case in which
+;; a source operand is constrained to be in a condition-code
+;; register, but has not been allocated to one.
+;;
+;; Sometimes, such as in movcc, we have a CCmode destination whose
+;; constraints do not include 'z'. reload_outcc handles the case
+;; when such an operand is allocated to a condition-code register.
+;;
+;; Note that reloads from a condition code register to some
+;; other location can be done using ordinary moves. Moving
+;; into a GPR takes a single movcc, moving elsewhere takes
+;; two. We can leave these cases to the generic reload code.
+(define_expand "reload_incc"
+ [(set (match_operand:CC 0 "fcc_reload_operand" "=z")
+ (match_operand:CC 1 "general_operand" ""))
+ (clobber (match_operand:TF 2 "register_operand" "=&f"))]
+ "ISA_HAS_8CC && TARGET_HARD_FLOAT"
+{
+ mips_expand_fcc_reload (operands[0], operands[1], operands[2]);
+ DONE;
+})
+
+(define_expand "reload_outcc"
+ [(set (match_operand:CC 0 "fcc_reload_operand" "=z")
+ (match_operand:CC 1 "register_operand" ""))
+ (clobber (match_operand:TF 2 "register_operand" "=&f"))]
+ "ISA_HAS_8CC && TARGET_HARD_FLOAT"
+{
+ mips_expand_fcc_reload (operands[0], operands[1], operands[2]);
+ DONE;
+})
+
+;; MIPS4 supports loading and storing a floating point register from
+;; the sum of two general registers. We use two versions for each of
+;; these four instructions: one where the two general registers are
+;; SImode, and one where they are DImode. This is because general
+;; registers will be in SImode when they hold 32-bit values, but,
+;; since the 32-bit values are always sign extended, the [ls][wd]xc1
+;; instructions will still work correctly.
+
+;; ??? Perhaps it would be better to support these instructions by
+;; modifying TARGET_LEGITIMATE_ADDRESS_P and friends. However, since
+;; these instructions can only be used to load and store floating
+;; point registers, that would probably cause trouble in reload.
+
+(define_insn "*<ANYF:loadx>_<P:mode>"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (mem:ANYF (plus:P (match_operand:P 1 "register_operand" "d")
+ (match_operand:P 2 "register_operand" "d"))))]
+ "ISA_HAS_FP4"
+ "<ANYF:loadx>\t%0,%1(%2)"
+ [(set_attr "type" "fpidxload")
+ (set_attr "mode" "<ANYF:UNITMODE>")])
+
+(define_insn "*<ANYF:storex>_<P:mode>"
+ [(set (mem:ANYF (plus:P (match_operand:P 1 "register_operand" "d")
+ (match_operand:P 2 "register_operand" "d")))
+ (match_operand:ANYF 0 "register_operand" "f"))]
+ "ISA_HAS_FP4"
+ "<ANYF:storex>\t%0,%1(%2)"
+ [(set_attr "type" "fpidxstore")
+ (set_attr "mode" "<ANYF:UNITMODE>")])
+
+;; Scaled indexed address load.
+;; Per md.texi, we only need to look for a pattern with multiply in the
+;; address expression, not shift.
+
+(define_insn "*lwxs"
+ [(set (match_operand:IMOVE32 0 "register_operand" "=d")
+ (mem:IMOVE32
+ (plus:P (mult:P (match_operand:P 1 "register_operand" "d")
+ (const_int 4))
+ (match_operand:P 2 "register_operand" "d"))))]
+ "ISA_HAS_LWXS"
+ "lwxs\t%0,%1(%2)"
+ [(set_attr "type" "load")
+ (set_attr "mode" "SI")])
+
+;; 16-bit Integer moves
+
+;; Unlike most other insns, the move insns can't be split with
+;; different predicates, because register spilling and other parts of
+;; the compiler, have memoized the insn number already.
+;; Unsigned loads are used because LOAD_EXTEND_OP returns ZERO_EXTEND.
+
+(define_expand "movhi"
+ [(set (match_operand:HI 0 "")
+ (match_operand:HI 1 ""))]
+ ""
+{
+ if (mips_legitimize_move (HImode, operands[0], operands[1]))
+ DONE;
+})
+
+(define_insn "*movhi_internal"
+ [(set (match_operand:HI 0 "nonimmediate_operand" "=d,d,d,m,*a,*d")
+ (match_operand:HI 1 "move_operand" "d,I,m,dJ,*d*J,*a"))]
+ "!TARGET_MIPS16
+ && (register_operand (operands[0], HImode)
+ || reg_or_0_operand (operands[1], HImode))"
+ { return mips_output_move (operands[0], operands[1]); }
+ [(set_attr "move_type" "move,const,load,store,mthilo,mfhilo")
+ (set_attr "mode" "HI")])
+
+(define_insn "*movhi_mips16"
+ [(set (match_operand:HI 0 "nonimmediate_operand" "=d,y,d,d,d,d,m,*d")
+ (match_operand:HI 1 "move_operand" "d,d,y,K,N,m,d,*a"))]
+ "TARGET_MIPS16
+ && (register_operand (operands[0], HImode)
+ || register_operand (operands[1], HImode))"
+ { return mips_output_move (operands[0], operands[1]); }
+ [(set_attr "move_type" "move,move,move,const,constN,load,store,mfhilo")
+ (set_attr "mode" "HI")])
+
+;; On the mips16, we can split lh $r,N($r) into an add and a load,
+;; when the original load is a 4 byte instruction but the add and the
+;; load are 2 2 byte instructions.
+
+(define_split
+ [(set (match_operand:HI 0 "d_operand")
+ (mem:HI (plus:SI (match_dup 0)
+ (match_operand:SI 1 "const_int_operand"))))]
+ "TARGET_MIPS16 && reload_completed && !TARGET_DEBUG_D_MODE
+ && ((INTVAL (operands[1]) < 0
+ && INTVAL (operands[1]) >= -0x80)
+ || (INTVAL (operands[1]) >= 32 * 2
+ && INTVAL (operands[1]) <= 31 * 2 + 0x7e)
+ || (INTVAL (operands[1]) >= 0
+ && INTVAL (operands[1]) < 32 * 2
+ && (INTVAL (operands[1]) & 1) != 0))"
+ [(set (match_dup 0) (plus:SI (match_dup 0) (match_dup 1)))
+ (set (match_dup 0) (mem:HI (plus:SI (match_dup 0) (match_dup 2))))]
+{
+ HOST_WIDE_INT val = INTVAL (operands[1]);
+
+ if (val < 0)
+ operands[2] = const0_rtx;
+ else if (val >= 32 * 2)
+ {
+ int off = val & 1;
+
+ operands[1] = GEN_INT (0x7e + off);
+ operands[2] = GEN_INT (val - off - 0x7e);
+ }
+ else
+ {
+ int off = val & 1;
+
+ operands[1] = GEN_INT (off);
+ operands[2] = GEN_INT (val - off);
+ }
+})
+
+;; 8-bit Integer moves
+
+;; Unlike most other insns, the move insns can't be split with
+;; different predicates, because register spilling and other parts of
+;; the compiler, have memoized the insn number already.
+;; Unsigned loads are used because LOAD_EXTEND_OP returns ZERO_EXTEND.
+
+(define_expand "movqi"
+ [(set (match_operand:QI 0 "")
+ (match_operand:QI 1 ""))]
+ ""
+{
+ if (mips_legitimize_move (QImode, operands[0], operands[1]))
+ DONE;
+})
+
+(define_insn "*movqi_internal"
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=d,d,d,m,*a,*d")
+ (match_operand:QI 1 "move_operand" "d,I,m,dJ,*d*J,*a"))]
+ "!TARGET_MIPS16
+ && (register_operand (operands[0], QImode)
+ || reg_or_0_operand (operands[1], QImode))"
+ { return mips_output_move (operands[0], operands[1]); }
+ [(set_attr "move_type" "move,const,load,store,mthilo,mfhilo")
+ (set_attr "mode" "QI")])
+
+(define_insn "*movqi_mips16"
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=d,y,d,d,d,d,m,*d")
+ (match_operand:QI 1 "move_operand" "d,d,y,K,N,m,d,*a"))]
+ "TARGET_MIPS16
+ && (register_operand (operands[0], QImode)
+ || register_operand (operands[1], QImode))"
+ { return mips_output_move (operands[0], operands[1]); }
+ [(set_attr "move_type" "move,move,move,const,constN,load,store,mfhilo")
+ (set_attr "mode" "QI")])
+
+;; On the mips16, we can split lb $r,N($r) into an add and a load,
+;; when the original load is a 4 byte instruction but the add and the
+;; load are 2 2 byte instructions.
+
+(define_split
+ [(set (match_operand:QI 0 "d_operand")
+ (mem:QI (plus:SI (match_dup 0)
+ (match_operand:SI 1 "const_int_operand"))))]
+ "TARGET_MIPS16 && reload_completed && !TARGET_DEBUG_D_MODE
+ && ((INTVAL (operands[1]) < 0
+ && INTVAL (operands[1]) >= -0x80)
+ || (INTVAL (operands[1]) >= 32
+ && INTVAL (operands[1]) <= 31 + 0x7f))"
+ [(set (match_dup 0) (plus:SI (match_dup 0) (match_dup 1)))
+ (set (match_dup 0) (mem:QI (plus:SI (match_dup 0) (match_dup 2))))]
+{
+ HOST_WIDE_INT val = INTVAL (operands[1]);
+
+ if (val < 0)
+ operands[2] = const0_rtx;
+ else
+ {
+ operands[1] = GEN_INT (0x7f);
+ operands[2] = GEN_INT (val - 0x7f);
+ }
+})
+
+;; 32-bit floating point moves
+
+(define_expand "movsf"
+ [(set (match_operand:SF 0 "")
+ (match_operand:SF 1 ""))]
+ ""
+{
+ if (mips_legitimize_move (SFmode, operands[0], operands[1]))
+ DONE;
+})
+
+(define_insn "*movsf_hardfloat"
+ [(set (match_operand:SF 0 "nonimmediate_operand" "=f,f,f,m,m,*f,*d,*d,*d,*m")
+ (match_operand:SF 1 "move_operand" "f,G,m,f,G,*d,*f,*G*d,*m,*d"))]
+ "TARGET_HARD_FLOAT
+ && (register_operand (operands[0], SFmode)
+ || reg_or_0_operand (operands[1], SFmode))"
+ { return mips_output_move (operands[0], operands[1]); }
+ [(set_attr "move_type" "fmove,mtc,fpload,fpstore,store,mtc,mfc,move,load,store")
+ (set_attr "mode" "SF")])
+
+(define_insn "*movsf_softfloat"
+ [(set (match_operand:SF 0 "nonimmediate_operand" "=d,d,m")
+ (match_operand:SF 1 "move_operand" "Gd,m,d"))]
+ "TARGET_SOFT_FLOAT && !TARGET_MIPS16
+ && (register_operand (operands[0], SFmode)
+ || reg_or_0_operand (operands[1], SFmode))"
+ { return mips_output_move (operands[0], operands[1]); }
+ [(set_attr "move_type" "move,load,store")
+ (set_attr "mode" "SF")])
+
+(define_insn "*movsf_mips16"
+ [(set (match_operand:SF 0 "nonimmediate_operand" "=d,y,d,d,m")
+ (match_operand:SF 1 "move_operand" "d,d,y,m,d"))]
+ "TARGET_MIPS16
+ && (register_operand (operands[0], SFmode)
+ || register_operand (operands[1], SFmode))"
+ { return mips_output_move (operands[0], operands[1]); }
+ [(set_attr "move_type" "move,move,move,load,store")
+ (set_attr "mode" "SF")])
+
+;; 64-bit floating point moves
+
+(define_expand "movdf"
+ [(set (match_operand:DF 0 "")
+ (match_operand:DF 1 ""))]
+ ""
+{
+ if (mips_legitimize_move (DFmode, operands[0], operands[1]))
+ DONE;
+})
+
+(define_insn "*movdf_hardfloat"
+ [(set (match_operand:DF 0 "nonimmediate_operand" "=f,f,f,m,m,*f,*d,*d,*d,*m")
+ (match_operand:DF 1 "move_operand" "f,G,m,f,G,*d,*f,*d*G,*m,*d"))]
+ "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT
+ && (register_operand (operands[0], DFmode)
+ || reg_or_0_operand (operands[1], DFmode))"
+ { return mips_output_move (operands[0], operands[1]); }
+ [(set_attr "move_type" "fmove,mtc,fpload,fpstore,store,mtc,mfc,move,load,store")
+ (set_attr "mode" "DF")])
+
+(define_insn "*movdf_softfloat"
+ [(set (match_operand:DF 0 "nonimmediate_operand" "=d,d,m")
+ (match_operand:DF 1 "move_operand" "dG,m,dG"))]
+ "(TARGET_SOFT_FLOAT || TARGET_SINGLE_FLOAT) && !TARGET_MIPS16
+ && (register_operand (operands[0], DFmode)
+ || reg_or_0_operand (operands[1], DFmode))"
+ { return mips_output_move (operands[0], operands[1]); }
+ [(set_attr "move_type" "move,load,store")
+ (set_attr "mode" "DF")])
+
+(define_insn "*movdf_mips16"
+ [(set (match_operand:DF 0 "nonimmediate_operand" "=d,y,d,d,m")
+ (match_operand:DF 1 "move_operand" "d,d,y,m,d"))]
+ "TARGET_MIPS16
+ && (register_operand (operands[0], DFmode)
+ || register_operand (operands[1], DFmode))"
+ { return mips_output_move (operands[0], operands[1]); }
+ [(set_attr "move_type" "move,move,move,load,store")
+ (set_attr "mode" "DF")])
+
+;; 128-bit integer moves
+
+(define_expand "movti"
+ [(set (match_operand:TI 0)
+ (match_operand:TI 1))]
+ "TARGET_64BIT"
+{
+ if (mips_legitimize_move (TImode, operands[0], operands[1]))
+ DONE;
+})
+
+(define_insn "*movti"
+ [(set (match_operand:TI 0 "nonimmediate_operand" "=d,d,d,m,*a,*d")
+ (match_operand:TI 1 "move_operand" "d,i,m,dJ,*d*J,*a"))]
+ "TARGET_64BIT
+ && !TARGET_MIPS16
+ && (register_operand (operands[0], TImode)
+ || reg_or_0_operand (operands[1], TImode))"
+ "#"
+ [(set_attr "move_type" "move,const,load,store,mthilo,mfhilo")
+ (set_attr "mode" "TI")])
+
+(define_insn "*movti_mips16"
+ [(set (match_operand:TI 0 "nonimmediate_operand" "=d,y,d,d,d,d,m,*d")
+ (match_operand:TI 1 "move_operand" "d,d,y,K,N,m,d,*a"))]
+ "TARGET_64BIT
+ && TARGET_MIPS16
+ && (register_operand (operands[0], TImode)
+ || register_operand (operands[1], TImode))"
+ "#"
+ [(set_attr "move_type" "move,move,move,const,constN,load,store,mfhilo")
+ (set_attr "mode" "TI")])
+
+;; 128-bit floating point moves
+
+(define_expand "movtf"
+ [(set (match_operand:TF 0)
+ (match_operand:TF 1))]
+ "TARGET_64BIT"
+{
+ if (mips_legitimize_move (TFmode, operands[0], operands[1]))
+ DONE;
+})
+
+;; This pattern handles both hard- and soft-float cases.
+(define_insn "*movtf"
+ [(set (match_operand:TF 0 "nonimmediate_operand" "=d,d,m,f,d,f,m")
+ (match_operand:TF 1 "move_operand" "dG,m,dG,dG,f,m,f"))]
+ "TARGET_64BIT
+ && !TARGET_MIPS16
+ && (register_operand (operands[0], TFmode)
+ || reg_or_0_operand (operands[1], TFmode))"
+ "#"
+ [(set_attr "move_type" "move,load,store,mtc,mfc,fpload,fpstore")
+ (set_attr "mode" "TF")])
+
+(define_insn "*movtf_mips16"
+ [(set (match_operand:TF 0 "nonimmediate_operand" "=d,y,d,d,m")
+ (match_operand:TF 1 "move_operand" "d,d,y,m,d"))]
+ "TARGET_64BIT
+ && TARGET_MIPS16
+ && (register_operand (operands[0], TFmode)
+ || register_operand (operands[1], TFmode))"
+ "#"
+ [(set_attr "move_type" "move,move,move,load,store")
+ (set_attr "mode" "TF")])
+
+(define_split
+ [(set (match_operand:MOVE64 0 "nonimmediate_operand")
+ (match_operand:MOVE64 1 "move_operand"))]
+ "reload_completed && !TARGET_64BIT
+ && mips_split_64bit_move_p (operands[0], operands[1])"
+ [(const_int 0)]
+{
+ mips_split_doubleword_move (operands[0], operands[1]);
+ DONE;
+})
+
+(define_split
+ [(set (match_operand:MOVE128 0 "nonimmediate_operand")
+ (match_operand:MOVE128 1 "move_operand"))]
+ "TARGET_64BIT && reload_completed"
+ [(const_int 0)]
+{
+ mips_split_doubleword_move (operands[0], operands[1]);
+ DONE;
+})
+
+;; When generating mips16 code, split moves of negative constants into
+;; a positive "li" followed by a negation.
+(define_split
+ [(set (match_operand 0 "d_operand")
+ (match_operand 1 "const_int_operand"))]
+ "TARGET_MIPS16 && reload_completed && INTVAL (operands[1]) < 0"
+ [(set (match_dup 2)
+ (match_dup 3))
+ (set (match_dup 2)
+ (neg:SI (match_dup 2)))]
+{
+ operands[2] = gen_lowpart (SImode, operands[0]);
+ operands[3] = GEN_INT (-INTVAL (operands[1]));
+})
+
+;; 64-bit paired-single floating point moves
+
+(define_expand "movv2sf"
+ [(set (match_operand:V2SF 0)
+ (match_operand:V2SF 1))]
+ "TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT"
+{
+ if (mips_legitimize_move (V2SFmode, operands[0], operands[1]))
+ DONE;
+})
+
+(define_insn "*movv2sf"
+ [(set (match_operand:V2SF 0 "nonimmediate_operand" "=f,f,f,m,m,*f,*d,*d,*d,*m")
+ (match_operand:V2SF 1 "move_operand" "f,YG,m,f,YG,*d,*f,*d*YG,*m,*d"))]
+ "TARGET_HARD_FLOAT
+ && TARGET_PAIRED_SINGLE_FLOAT
+ && (register_operand (operands[0], V2SFmode)
+ || reg_or_0_operand (operands[1], V2SFmode))"
+ { return mips_output_move (operands[0], operands[1]); }
+ [(set_attr "move_type" "fmove,mtc,fpload,fpstore,store,mtc,mfc,move,load,store")
+ (set_attr "mode" "DF")])
+
+;; Extract the high part of a HI/LO value. See mips_hard_regno_mode_ok_p
+;; for the reason why we can't just use (reg:GPR HI_REGNUM).
+;;
+;; When generating VR4120 or VR4130 code, we use MACCHI and DMACCHI
+;; instead of MFHI. This avoids both the normal MIPS III hi/lo hazards
+;; and the errata related to -mfix-vr4130.
+(define_insn "mfhi<GPR:mode>_<HILO:mode>"
+ [(set (match_operand:GPR 0 "register_operand" "=d")
+ (unspec:GPR [(match_operand:HILO 1 "register_operand" "x")]
+ UNSPEC_MFHI))]
+ ""
+ { return ISA_HAS_MACCHI ? "<GPR:d>macchi\t%0,%.,%." : "mfhi\t%0"; }
+ [(set_attr "move_type" "mfhilo")
+ (set_attr "mode" "<GPR:MODE>")])
+
+;; Set the high part of a HI/LO value, given that the low part has
+;; already been set. See mips_hard_regno_mode_ok_p for the reason
+;; why we can't just use (reg:GPR HI_REGNUM).
+(define_insn "mthi<GPR:mode>_<HILO:mode>"
+ [(set (match_operand:HILO 0 "register_operand" "=x")
+ (unspec:HILO [(match_operand:GPR 1 "reg_or_0_operand" "dJ")
+ (match_operand:GPR 2 "register_operand" "l")]
+ UNSPEC_MTHI))]
+ ""
+ "mthi\t%z1"
+ [(set_attr "move_type" "mthilo")
+ (set_attr "mode" "SI")])
+
+;; Emit a doubleword move in which exactly one of the operands is
+;; a floating-point register. We can't just emit two normal moves
+;; because of the constraints imposed by the FPU register model;
+;; see mips_cannot_change_mode_class for details. Instead, we keep
+;; the FPR whole and use special patterns to refer to each word of
+;; the other operand.
+
+(define_expand "move_doubleword_fpr<mode>"
+ [(set (match_operand:SPLITF 0)
+ (match_operand:SPLITF 1))]
+ ""
+{
+ if (FP_REG_RTX_P (operands[0]))
+ {
+ rtx low = mips_subword (operands[1], 0);
+ rtx high = mips_subword (operands[1], 1);
+ emit_insn (gen_load_low<mode> (operands[0], low));
+ if (TARGET_FLOAT64 && !TARGET_64BIT)
+ emit_insn (gen_mthc1<mode> (operands[0], high, operands[0]));
+ else
+ emit_insn (gen_load_high<mode> (operands[0], high, operands[0]));
+ }
+ else
+ {
+ rtx low = mips_subword (operands[0], 0);
+ rtx high = mips_subword (operands[0], 1);
+ emit_insn (gen_store_word<mode> (low, operands[1], const0_rtx));
+ if (TARGET_FLOAT64 && !TARGET_64BIT)
+ emit_insn (gen_mfhc1<mode> (high, operands[1]));
+ else
+ emit_insn (gen_store_word<mode> (high, operands[1], const1_rtx));
+ }
+ DONE;
+})
+
+;; Load the low word of operand 0 with operand 1.
+(define_insn "load_low<mode>"
+ [(set (match_operand:SPLITF 0 "register_operand" "=f,f")
+ (unspec:SPLITF [(match_operand:<HALFMODE> 1 "general_operand" "dJ,m")]
+ UNSPEC_LOAD_LOW))]
+ "TARGET_HARD_FLOAT"
+{
+ operands[0] = mips_subword (operands[0], 0);
+ return mips_output_move (operands[0], operands[1]);
+}
+ [(set_attr "move_type" "mtc,fpload")
+ (set_attr "mode" "<HALFMODE>")])
+
+;; Load the high word of operand 0 from operand 1, preserving the value
+;; in the low word.
+(define_insn "load_high<mode>"
+ [(set (match_operand:SPLITF 0 "register_operand" "=f,f")
+ (unspec:SPLITF [(match_operand:<HALFMODE> 1 "general_operand" "dJ,m")
+ (match_operand:SPLITF 2 "register_operand" "0,0")]
+ UNSPEC_LOAD_HIGH))]
+ "TARGET_HARD_FLOAT"
+{
+ operands[0] = mips_subword (operands[0], 1);
+ return mips_output_move (operands[0], operands[1]);
+}
+ [(set_attr "move_type" "mtc,fpload")
+ (set_attr "mode" "<HALFMODE>")])
+
+;; Store one word of operand 1 in operand 0. Operand 2 is 1 to store the
+;; high word and 0 to store the low word.
+(define_insn "store_word<mode>"
+ [(set (match_operand:<HALFMODE> 0 "nonimmediate_operand" "=d,m")
+ (unspec:<HALFMODE> [(match_operand:SPLITF 1 "register_operand" "f,f")
+ (match_operand 2 "const_int_operand")]
+ UNSPEC_STORE_WORD))]
+ "TARGET_HARD_FLOAT"
+{
+ operands[1] = mips_subword (operands[1], INTVAL (operands[2]));
+ return mips_output_move (operands[0], operands[1]);
+}
+ [(set_attr "move_type" "mfc,fpstore")
+ (set_attr "mode" "<HALFMODE>")])
+
+;; Move operand 1 to the high word of operand 0 using mthc1, preserving the
+;; value in the low word.
+(define_insn "mthc1<mode>"
+ [(set (match_operand:SPLITF 0 "register_operand" "=f")
+ (unspec:SPLITF [(match_operand:<HALFMODE> 1 "reg_or_0_operand" "dJ")
+ (match_operand:SPLITF 2 "register_operand" "0")]
+ UNSPEC_MTHC1))]
+ "TARGET_HARD_FLOAT && ISA_HAS_MXHC1"
+ "mthc1\t%z1,%0"
+ [(set_attr "move_type" "mtc")
+ (set_attr "mode" "<HALFMODE>")])
+
+;; Move high word of operand 1 to operand 0 using mfhc1.
+(define_insn "mfhc1<mode>"
+ [(set (match_operand:<HALFMODE> 0 "register_operand" "=d")
+ (unspec:<HALFMODE> [(match_operand:SPLITF 1 "register_operand" "f")]
+ UNSPEC_MFHC1))]
+ "TARGET_HARD_FLOAT && ISA_HAS_MXHC1"
+ "mfhc1\t%0,%1"
+ [(set_attr "move_type" "mfc")
+ (set_attr "mode" "<HALFMODE>")])
+
+;; Move a constant that satisfies CONST_GP_P into operand 0.
+(define_expand "load_const_gp_<mode>"
+ [(set (match_operand:P 0 "register_operand" "=d")
+ (const:P (unspec:P [(const_int 0)] UNSPEC_GP)))])
+
+;; Insn to initialize $gp for n32/n64 abicalls. Operand 0 is the offset
+;; of _gp from the start of this function. Operand 1 is the incoming
+;; function address.
+(define_insn_and_split "loadgp_newabi_<mode>"
+ [(set (match_operand:P 0 "register_operand" "=&d")
+ (unspec:P [(match_operand:P 1)
+ (match_operand:P 2 "register_operand" "d")]
+ UNSPEC_LOADGP))]
+ "mips_current_loadgp_style () == LOADGP_NEWABI"
+ { return mips_must_initialize_gp_p () ? "#" : ""; }
+ "&& mips_must_initialize_gp_p ()"
+ [(set (match_dup 0) (match_dup 3))
+ (set (match_dup 0) (match_dup 4))
+ (set (match_dup 0) (match_dup 5))]
+{
+ operands[3] = gen_rtx_HIGH (Pmode, operands[1]);
+ operands[4] = gen_rtx_PLUS (Pmode, operands[0], operands[2]);
+ operands[5] = gen_rtx_LO_SUM (Pmode, operands[0], operands[1]);
+}
+ [(set_attr "type" "ghost")])
+
+;; Likewise, for -mno-shared code. Operand 0 is the __gnu_local_gp symbol.
+(define_insn_and_split "loadgp_absolute_<mode>"
+ [(set (match_operand:P 0 "register_operand" "=d")
+ (unspec:P [(match_operand:P 1)] UNSPEC_LOADGP))]
+ "mips_current_loadgp_style () == LOADGP_ABSOLUTE"
+ { return mips_must_initialize_gp_p () ? "#" : ""; }
+ "&& mips_must_initialize_gp_p ()"
+ [(const_int 0)]
+{
+ mips_emit_move (operands[0], operands[1]);
+ DONE;
+}
+ [(set_attr "type" "ghost")])
+
+;; This blockage instruction prevents the gp load from being
+;; scheduled after an implicit use of gp. It also prevents
+;; the load from being deleted as dead.
+(define_insn "loadgp_blockage"
+ [(unspec_volatile [(reg:SI 28)] UNSPEC_BLOCKAGE)]
+ ""
+ ""
+ [(set_attr "type" "ghost")])
+
+;; Initialize $gp for RTP PIC. Operand 0 is the __GOTT_BASE__ symbol
+;; and operand 1 is the __GOTT_INDEX__ symbol.
+(define_insn_and_split "loadgp_rtp_<mode>"
+ [(set (match_operand:P 0 "register_operand" "=d")
+ (unspec:P [(match_operand:P 1 "symbol_ref_operand")
+ (match_operand:P 2 "symbol_ref_operand")]
+ UNSPEC_LOADGP))]
+ "mips_current_loadgp_style () == LOADGP_RTP"
+ { return mips_must_initialize_gp_p () ? "#" : ""; }
+ "&& mips_must_initialize_gp_p ()"
+ [(set (match_dup 0) (high:P (match_dup 3)))
+ (set (match_dup 0) (unspec:P [(match_dup 0)
+ (match_dup 3)] UNSPEC_LOAD_GOT))
+ (set (match_dup 0) (unspec:P [(match_dup 0)
+ (match_dup 4)] UNSPEC_LOAD_GOT))]
+{
+ operands[3] = mips_unspec_address (operands[1], SYMBOL_ABSOLUTE);
+ operands[4] = mips_unspec_address (operands[2], SYMBOL_HALF);
+}
+ [(set_attr "type" "ghost")])
+
+;; Initialize the global pointer for MIPS16 code. Operand 0 is the
+;; global pointer and operand 1 is the MIPS16 register that holds
+;; the required value.
+(define_insn_and_split "copygp_mips16"
+ [(set (match_operand:SI 0 "register_operand" "=y")
+ (unspec:SI [(match_operand:SI 1 "register_operand" "d")]
+ UNSPEC_COPYGP))]
+ "TARGET_MIPS16"
+ { return mips_must_initialize_gp_p () ? "#" : ""; }
+ "&& mips_must_initialize_gp_p ()"
+ [(set (match_dup 0) (match_dup 1))]
+ ""
+ [(set_attr "type" "ghost")])
+
+;; A placeholder for where the cprestore instruction should go,
+;; if we decide we need one. Operand 0 and operand 1 are as for
+;; "cprestore". Operand 2 is a register that holds the gp value.
+;;
+;; The "cprestore" pattern requires operand 2 to be pic_offset_table_rtx,
+;; otherwise any register that holds the correct value will do.
+(define_insn_and_split "potential_cprestore"
+ [(set (match_operand:SI 0 "cprestore_save_slot_operand" "=X,X")
+ (unspec:SI [(match_operand:SI 1 "const_int_operand" "I,i")
+ (match_operand:SI 2 "register_operand" "d,d")]
+ UNSPEC_POTENTIAL_CPRESTORE))
+ (clobber (match_operand:SI 3 "scratch_operand" "=X,&d"))]
+ "!TARGET_CPRESTORE_DIRECTIVE || operands[2] == pic_offset_table_rtx"
+ { return mips_must_initialize_gp_p () ? "#" : ""; }
+ "mips_must_initialize_gp_p ()"
+ [(const_int 0)]
+{
+ mips_save_gp_to_cprestore_slot (operands[0], operands[1],
+ operands[2], operands[3]);
+ DONE;
+}
+ [(set_attr "type" "ghost")])
+
+;; Emit a .cprestore directive, which normally expands to a single store
+;; instruction. Operand 0 is a (possibly illegitimate) sp-based MEM
+;; for the cprestore slot. Operand 1 is the offset of the slot from
+;; the stack pointer. (This is redundant with operand 0, but it makes
+;; things a little simpler.)
+(define_insn "cprestore"
+ [(set (match_operand:SI 0 "cprestore_save_slot_operand" "=X,X")
+ (unspec:SI [(match_operand:SI 1 "const_int_operand" "I,i")
+ (reg:SI 28)]
+ UNSPEC_CPRESTORE))]
+ "TARGET_CPRESTORE_DIRECTIVE"
+{
+ if (mips_nomacro.nesting_level > 0 && which_alternative == 1)
+ return ".set\tmacro\;.cprestore\t%1\;.set\tnomacro";
+ else
+ return ".cprestore\t%1";
+}
+ [(set_attr "type" "store")
+ (set_attr "length" "4,12")])
+
+(define_insn "use_cprestore"
+ [(set (reg:SI CPRESTORE_SLOT_REGNUM)
+ (match_operand:SI 0 "cprestore_load_slot_operand"))]
+ ""
+ ""
+ [(set_attr "type" "ghost")])
+
+;; Expand in-line code to clear the instruction cache between operand[0] and
+;; operand[1].
+(define_expand "clear_cache"
+ [(match_operand 0 "pmode_register_operand")
+ (match_operand 1 "pmode_register_operand")]
+ ""
+ "
+{
+ if (TARGET_SYNCI)
+ {
+ mips_expand_synci_loop (operands[0], operands[1]);
+ emit_insn (gen_sync ());
+ emit_insn (Pmode == SImode
+ ? gen_clear_hazard_si ()
+ : gen_clear_hazard_di ());
+ }
+ else if (mips_cache_flush_func && mips_cache_flush_func[0])
+ {
+ rtx len = gen_reg_rtx (Pmode);
+ emit_insn (gen_sub3_insn (len, operands[1], operands[0]));
+ MIPS_ICACHE_SYNC (operands[0], len);
+ }
+ DONE;
+}")
+
+(define_insn "sync"
+ [(unspec_volatile [(const_int 0)] UNSPEC_SYNC)]
+ "GENERATE_SYNC"
+ { return mips_output_sync (); })
+
+(define_insn "synci"
+ [(unspec_volatile [(match_operand 0 "pmode_register_operand" "d")]
+ UNSPEC_SYNCI)]
+ "TARGET_SYNCI"
+ "synci\t0(%0)")
+
+(define_insn "rdhwr_synci_step_<mode>"
+ [(set (match_operand:P 0 "register_operand" "=d")
+ (unspec_volatile [(const_int 1)]
+ UNSPEC_RDHWR))]
+ "ISA_HAS_SYNCI"
+ "rdhwr\t%0,$1")
+
+(define_insn "clear_hazard_<mode>"
+ [(unspec_volatile [(const_int 0)] UNSPEC_CLEAR_HAZARD)
+ (clobber (reg:P RETURN_ADDR_REGNUM))]
+ "ISA_HAS_SYNCI"
+{
+ return "%(%<bal\t1f\n"
+ "\tnop\n"
+ "1:\t<d>addiu\t$31,$31,12\n"
+ "\tjr.hb\t$31\n"
+ "\tnop%>%)";
+}
+ [(set_attr "length" "20")])
+
+;; Cache operations for R4000-style caches.
+(define_insn "mips_cache"
+ [(set (mem:BLK (scratch))
+ (unspec:BLK [(match_operand:SI 0 "const_int_operand")
+ (match_operand:QI 1 "address_operand" "p")]
+ UNSPEC_MIPS_CACHE))]
+ "ISA_HAS_CACHE"
+ "cache\t%X0,%a1")
+
+;; Similar, but with the operands hard-coded to an R10K cache barrier
+;; operation. We keep the pattern distinct so that we can identify
+;; cache operations inserted by -mr10k-cache-barrier=, and so that
+;; the operation is never inserted into a delay slot.
+(define_insn "r10k_cache_barrier"
+ [(set (mem:BLK (scratch))
+ (unspec:BLK [(const_int 0)] UNSPEC_R10K_CACHE_BARRIER))]
+ "ISA_HAS_CACHE"
+ "cache\t0x14,0(%$)"
+ [(set_attr "can_delay" "no")])
+
+;; Block moves, see mips.c for more details.
+;; Argument 0 is the destination
+;; Argument 1 is the source
+;; Argument 2 is the length
+;; Argument 3 is the alignment
+
+(define_expand "movmemsi"
+ [(parallel [(set (match_operand:BLK 0 "general_operand")
+ (match_operand:BLK 1 "general_operand"))
+ (use (match_operand:SI 2 ""))
+ (use (match_operand:SI 3 "const_int_operand"))])]
+ "!TARGET_MIPS16 && !TARGET_MEMCPY"
+{
+ if (mips_expand_block_move (operands[0], operands[1], operands[2]))
+ DONE;
+ else
+ FAIL;
+})
+
+;;
+;; ....................
+;;
+;; SHIFTS
+;;
+;; ....................
+
+(define_expand "<optab><mode>3"
+ [(set (match_operand:GPR 0 "register_operand")
+ (any_shift:GPR (match_operand:GPR 1 "register_operand")
+ (match_operand:SI 2 "arith_operand")))]
+ ""
+{
+ /* On the mips16, a shift of more than 8 is a four byte instruction,
+ so, for a shift between 8 and 16, it is just as fast to do two
+ shifts of 8 or less. If there is a lot of shifting going on, we
+ may win in CSE. Otherwise combine will put the shifts back
+ together again. This can be called by mips_function_arg, so we must
+ be careful not to allocate a new register if we've reached the
+ reload pass. */
+ if (TARGET_MIPS16
+ && optimize
+ && CONST_INT_P (operands[2])
+ && INTVAL (operands[2]) > 8
+ && INTVAL (operands[2]) <= 16
+ && !reload_in_progress
+ && !reload_completed)
+ {
+ rtx temp = gen_reg_rtx (<MODE>mode);
+
+ emit_insn (gen_<optab><mode>3 (temp, operands[1], GEN_INT (8)));
+ emit_insn (gen_<optab><mode>3 (operands[0], temp,
+ GEN_INT (INTVAL (operands[2]) - 8)));
+ DONE;
+ }
+})
+
+(define_insn "*<optab><mode>3"
+ [(set (match_operand:GPR 0 "register_operand" "=d")
+ (any_shift:GPR (match_operand:GPR 1 "register_operand" "d")
+ (match_operand:SI 2 "arith_operand" "dI")))]
+ "!TARGET_MIPS16"
+{
+ if (CONST_INT_P (operands[2]))
+ operands[2] = GEN_INT (INTVAL (operands[2])
+ & (GET_MODE_BITSIZE (<MODE>mode) - 1));
+
+ return "<d><insn>\t%0,%1,%2";
+}
+ [(set_attr "type" "shift")
+ (set_attr "mode" "<MODE>")])
+
+(define_insn "*<optab>si3_extend"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (sign_extend:DI
+ (any_shift:SI (match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "arith_operand" "dI"))))]
+ "TARGET_64BIT && !TARGET_MIPS16"
+{
+ if (CONST_INT_P (operands[2]))
+ operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);
+
+ return "<insn>\t%0,%1,%2";
+}
+ [(set_attr "type" "shift")
+ (set_attr "mode" "SI")])
+
+(define_insn "*<optab>si3_mips16"
+ [(set (match_operand:SI 0 "register_operand" "=d,d")
+ (any_shift:SI (match_operand:SI 1 "register_operand" "0,d")
+ (match_operand:SI 2 "arith_operand" "d,I")))]
+ "TARGET_MIPS16"
+{
+ if (which_alternative == 0)
+ return "<insn>\t%0,%2";
+
+ operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);
+ return "<insn>\t%0,%1,%2";
+}
+ [(set_attr "type" "shift")
+ (set_attr "mode" "SI")
+ (set_attr_alternative "length"
+ [(const_int 4)
+ (if_then_else (match_operand 2 "m16_uimm3_b")
+ (const_int 4)
+ (const_int 8))])])
+
+;; We need separate DImode MIPS16 patterns because of the irregularity
+;; of right shifts.
+(define_insn "*ashldi3_mips16"
+ [(set (match_operand:DI 0 "register_operand" "=d,d")
+ (ashift:DI (match_operand:DI 1 "register_operand" "0,d")
+ (match_operand:SI 2 "arith_operand" "d,I")))]
+ "TARGET_64BIT && TARGET_MIPS16"
+{
+ if (which_alternative == 0)
+ return "dsll\t%0,%2";
+
+ operands[2] = GEN_INT (INTVAL (operands[2]) & 0x3f);
+ return "dsll\t%0,%1,%2";
+}
+ [(set_attr "type" "shift")
+ (set_attr "mode" "DI")
+ (set_attr_alternative "length"
+ [(const_int 4)
+ (if_then_else (match_operand 2 "m16_uimm3_b")
+ (const_int 4)
+ (const_int 8))])])
+
+(define_insn "*ashrdi3_mips16"
+ [(set (match_operand:DI 0 "register_operand" "=d,d")
+ (ashiftrt:DI (match_operand:DI 1 "register_operand" "0,0")
+ (match_operand:SI 2 "arith_operand" "d,I")))]
+ "TARGET_64BIT && TARGET_MIPS16"
+{
+ if (CONST_INT_P (operands[2]))
+ operands[2] = GEN_INT (INTVAL (operands[2]) & 0x3f);
+
+ return "dsra\t%0,%2";
+}
+ [(set_attr "type" "shift")
+ (set_attr "mode" "DI")
+ (set_attr_alternative "length"
+ [(const_int 4)
+ (if_then_else (match_operand 2 "m16_uimm3_b")
+ (const_int 4)
+ (const_int 8))])])
+
+(define_insn "*lshrdi3_mips16"
+ [(set (match_operand:DI 0 "register_operand" "=d,d")
+ (lshiftrt:DI (match_operand:DI 1 "register_operand" "0,0")
+ (match_operand:SI 2 "arith_operand" "d,I")))]
+ "TARGET_64BIT && TARGET_MIPS16"
+{
+ if (CONST_INT_P (operands[2]))
+ operands[2] = GEN_INT (INTVAL (operands[2]) & 0x3f);
+
+ return "dsrl\t%0,%2";
+}
+ [(set_attr "type" "shift")
+ (set_attr "mode" "DI")
+ (set_attr_alternative "length"
+ [(const_int 4)
+ (if_then_else (match_operand 2 "m16_uimm3_b")
+ (const_int 4)
+ (const_int 8))])])
+
+;; On the mips16, we can split a 4 byte shift into 2 2 byte shifts.
+
+(define_split
+ [(set (match_operand:GPR 0 "d_operand")
+ (any_shift:GPR (match_operand:GPR 1 "d_operand")
+ (match_operand:GPR 2 "const_int_operand")))]
+ "TARGET_MIPS16 && reload_completed && !TARGET_DEBUG_D_MODE
+ && INTVAL (operands[2]) > 8
+ && INTVAL (operands[2]) <= 16"
+ [(set (match_dup 0) (any_shift:GPR (match_dup 1) (const_int 8)))
+ (set (match_dup 0) (any_shift:GPR (match_dup 0) (match_dup 2)))]
+ { operands[2] = GEN_INT (INTVAL (operands[2]) - 8); })
+
+;; If we load a byte on the mips16 as a bitfield, the resulting
+;; sequence of instructions is too complicated for combine, because it
+;; involves four instructions: a load, a shift, a constant load into a
+;; register, and an and (the key problem here is that the mips16 does
+;; not have and immediate). We recognize a shift of a load in order
+;; to make it simple enough for combine to understand.
+;;
+;; The length here is the worst case: the length of the split version
+;; will be more accurate.
+(define_insn_and_split ""
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (lshiftrt:SI (match_operand:SI 1 "memory_operand" "m")
+ (match_operand:SI 2 "immediate_operand" "I")))]
+ "TARGET_MIPS16"
+ "#"
+ ""
+ [(set (match_dup 0) (match_dup 1))
+ (set (match_dup 0) (lshiftrt:SI (match_dup 0) (match_dup 2)))]
+ ""
+ [(set_attr "type" "load")
+ (set_attr "mode" "SI")
+ (set_attr "length" "16")])
+
+(define_insn "rotr<mode>3"
+ [(set (match_operand:GPR 0 "register_operand" "=d")
+ (rotatert:GPR (match_operand:GPR 1 "register_operand" "d")
+ (match_operand:SI 2 "arith_operand" "dI")))]
+ "ISA_HAS_ROR"
+{
+ if (CONST_INT_P (operands[2]))
+ gcc_assert (INTVAL (operands[2]) >= 0
+ && INTVAL (operands[2]) < GET_MODE_BITSIZE (<MODE>mode));
+
+ return "<d>ror\t%0,%1,%2";
+}
+ [(set_attr "type" "shift")
+ (set_attr "mode" "<MODE>")])
+
+;;
+;; ....................
+;;
+;; CONDITIONAL BRANCHES
+;;
+;; ....................
+
+;; Conditional branches on floating-point equality tests.
+
+(define_insn "*branch_fp"
+ [(set (pc)
+ (if_then_else
+ (match_operator 1 "equality_operator"
+ [(match_operand:CC 2 "register_operand" "z")
+ (const_int 0)])
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ "TARGET_HARD_FLOAT"
+{
+ return mips_output_conditional_branch (insn, operands,
+ MIPS_BRANCH ("b%F1", "%Z2%0"),
+ MIPS_BRANCH ("b%W1", "%Z2%0"));
+}
+ [(set_attr "type" "branch")])
+
+(define_insn "*branch_fp_inverted"
+ [(set (pc)
+ (if_then_else
+ (match_operator 1 "equality_operator"
+ [(match_operand:CC 2 "register_operand" "z")
+ (const_int 0)])
+ (pc)
+ (label_ref (match_operand 0 "" ""))))]
+ "TARGET_HARD_FLOAT"
+{
+ return mips_output_conditional_branch (insn, operands,
+ MIPS_BRANCH ("b%W1", "%Z2%0"),
+ MIPS_BRANCH ("b%F1", "%Z2%0"));
+}
+ [(set_attr "type" "branch")])
+
+;; Conditional branches on ordered comparisons with zero.
+
+(define_insn "*branch_order<mode>"
+ [(set (pc)
+ (if_then_else
+ (match_operator 1 "order_operator"
+ [(match_operand:GPR 2 "register_operand" "d")
+ (const_int 0)])
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ "!TARGET_MIPS16"
+ { return mips_output_order_conditional_branch (insn, operands, false); }
+ [(set_attr "type" "branch")])
+
+(define_insn "*branch_order<mode>_inverted"
+ [(set (pc)
+ (if_then_else
+ (match_operator 1 "order_operator"
+ [(match_operand:GPR 2 "register_operand" "d")
+ (const_int 0)])
+ (pc)
+ (label_ref (match_operand 0 "" ""))))]
+ "!TARGET_MIPS16"
+ { return mips_output_order_conditional_branch (insn, operands, true); }
+ [(set_attr "type" "branch")])
+
+;; Conditional branch on equality comparison.
+
+(define_insn "*branch_equality<mode>"
+ [(set (pc)
+ (if_then_else
+ (match_operator 1 "equality_operator"
+ [(match_operand:GPR 2 "register_operand" "d")
+ (match_operand:GPR 3 "reg_or_0_operand" "dJ")])
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ "!TARGET_MIPS16"
+{
+ return mips_output_conditional_branch (insn, operands,
+ MIPS_BRANCH ("b%C1", "%2,%z3,%0"),
+ MIPS_BRANCH ("b%N1", "%2,%z3,%0"));
+}
+ [(set_attr "type" "branch")])
+
+(define_insn "*branch_equality<mode>_inverted"
+ [(set (pc)
+ (if_then_else
+ (match_operator 1 "equality_operator"
+ [(match_operand:GPR 2 "register_operand" "d")
+ (match_operand:GPR 3 "reg_or_0_operand" "dJ")])
+ (pc)
+ (label_ref (match_operand 0 "" ""))))]
+ "!TARGET_MIPS16"
+{
+ return mips_output_conditional_branch (insn, operands,
+ MIPS_BRANCH ("b%N1", "%2,%z3,%0"),
+ MIPS_BRANCH ("b%C1", "%2,%z3,%0"));
+}
+ [(set_attr "type" "branch")])
+
+;; MIPS16 branches
+
+(define_insn "*branch_equality<mode>_mips16"
+ [(set (pc)
+ (if_then_else
+ (match_operator 0 "equality_operator"
+ [(match_operand:GPR 1 "register_operand" "d,t")
+ (const_int 0)])
+ (match_operand 2 "pc_or_label_operand" "")
+ (match_operand 3 "pc_or_label_operand" "")))]
+ "TARGET_MIPS16"
+{
+ if (operands[2] != pc_rtx)
+ {
+ if (which_alternative == 0)
+ return "b%C0z\t%1,%2";
+ else
+ return "bt%C0z\t%2";
+ }
+ else
+ {
+ if (which_alternative == 0)
+ return "b%N0z\t%1,%3";
+ else
+ return "bt%N0z\t%3";
+ }
+}
+ [(set_attr "type" "branch")])
+
+(define_expand "cbranch<mode>4"
+ [(set (pc)
+ (if_then_else (match_operator 0 "comparison_operator"
+ [(match_operand:GPR 1 "register_operand")
+ (match_operand:GPR 2 "nonmemory_operand")])
+ (label_ref (match_operand 3 ""))
+ (pc)))]
+ ""
+{
+ mips_expand_conditional_branch (operands);
+ DONE;
+})
+
+(define_expand "cbranch<mode>4"
+ [(set (pc)
+ (if_then_else (match_operator 0 "comparison_operator"
+ [(match_operand:SCALARF 1 "register_operand")
+ (match_operand:SCALARF 2 "register_operand")])
+ (label_ref (match_operand 3 ""))
+ (pc)))]
+ ""
+{
+ mips_expand_conditional_branch (operands);
+ DONE;
+})
+
+;; Used to implement built-in functions.
+(define_expand "condjump"
+ [(set (pc)
+ (if_then_else (match_operand 0)
+ (label_ref (match_operand 1))
+ (pc)))])
+
+;; Branch if bit is set/clear.
+
+(define_insn "*branch_bit<bbv><mode>"
+ [(set (pc)
+ (if_then_else
+ (equality_op (zero_extract:GPR
+ (match_operand:GPR 1 "register_operand" "d")
+ (const_int 1)
+ (match_operand 2 "const_int_operand" ""))
+ (const_int 0))
+ (label_ref (match_operand 0 ""))
+ (pc)))]
+ "ISA_HAS_BBIT && UINTVAL (operands[2]) < GET_MODE_BITSIZE (<MODE>mode)"
+{
+ return
+ mips_output_conditional_branch (insn, operands,
+ MIPS_BRANCH ("bbit<bbv>", "%1,%2,%0"),
+ MIPS_BRANCH ("bbit<bbinv>", "%1,%2,%0"));
+}
+ [(set_attr "type" "branch")
+ (set_attr "branch_likely" "no")])
+
+(define_insn "*branch_bit<bbv><mode>_inverted"
+ [(set (pc)
+ (if_then_else
+ (equality_op (zero_extract:GPR
+ (match_operand:GPR 1 "register_operand" "d")
+ (const_int 1)
+ (match_operand 2 "const_int_operand" ""))
+ (const_int 0))
+ (pc)
+ (label_ref (match_operand 0 ""))))]
+ "ISA_HAS_BBIT && UINTVAL (operands[2]) < GET_MODE_BITSIZE (<MODE>mode)"
+{
+ return
+ mips_output_conditional_branch (insn, operands,
+ MIPS_BRANCH ("bbit<bbinv>", "%1,%2,%0"),
+ MIPS_BRANCH ("bbit<bbv>", "%1,%2,%0"));
+}
+ [(set_attr "type" "branch")
+ (set_attr "branch_likely" "no")])
+
+;;
+;; ....................
+;;
+;; SETTING A REGISTER FROM A COMPARISON
+;;
+;; ....................
+
+;; Destination is always set in SI mode.
+
+(define_expand "cstore<mode>4"
+ [(set (match_operand:SI 0 "register_operand")
+ (match_operator:SI 1 "mips_cstore_operator"
+ [(match_operand:GPR 2 "register_operand")
+ (match_operand:GPR 3 "nonmemory_operand")]))]
+ ""
+{
+ mips_expand_scc (operands);
+ DONE;
+})
+
+(define_insn "*seq_zero_<GPR:mode><GPR2:mode>"
+ [(set (match_operand:GPR2 0 "register_operand" "=d")
+ (eq:GPR2 (match_operand:GPR 1 "register_operand" "d")
+ (const_int 0)))]
+ "!TARGET_MIPS16 && !ISA_HAS_SEQ_SNE"
+ "sltu\t%0,%1,1"
+ [(set_attr "type" "slt")
+ (set_attr "mode" "<GPR:MODE>")])
+
+(define_insn "*seq_zero_<GPR:mode><GPR2:mode>_mips16"
+ [(set (match_operand:GPR2 0 "register_operand" "=t")
+ (eq:GPR2 (match_operand:GPR 1 "register_operand" "d")
+ (const_int 0)))]
+ "TARGET_MIPS16 && !ISA_HAS_SEQ_SNE"
+ "sltu\t%1,1"
+ [(set_attr "type" "slt")
+ (set_attr "mode" "<GPR:MODE>")])
+
+;; Generate sltiu unless using seq results in better code.
+(define_insn "*seq_<GPR:mode><GPR2:mode>_seq"
+ [(set (match_operand:GPR2 0 "register_operand" "=d,d,d")
+ (eq:GPR2 (match_operand:GPR 1 "register_operand" "%d,d,d")
+ (match_operand:GPR 2 "reg_imm10_operand" "d,J,YB")))]
+ "ISA_HAS_SEQ_SNE"
+ "@
+ seq\t%0,%1,%2
+ sltiu\t%0,%1,1
+ seqi\t%0,%1,%2"
+ [(set_attr "type" "slt")
+ (set_attr "mode" "<GPR:MODE>")])
+
+(define_insn "*sne_zero_<GPR:mode><GPR2:mode>"
+ [(set (match_operand:GPR2 0 "register_operand" "=d")
+ (ne:GPR2 (match_operand:GPR 1 "register_operand" "d")
+ (const_int 0)))]
+ "!TARGET_MIPS16 && !ISA_HAS_SEQ_SNE"
+ "sltu\t%0,%.,%1"
+ [(set_attr "type" "slt")
+ (set_attr "mode" "<GPR:MODE>")])
+
+;; Generate sltu unless using sne results in better code.
+(define_insn "*sne_<GPR:mode><GPR2:mode>_sne"
+ [(set (match_operand:GPR2 0 "register_operand" "=d,d,d")
+ (ne:GPR2 (match_operand:GPR 1 "register_operand" "%d,d,d")
+ (match_operand:GPR 2 "reg_imm10_operand" "d,J,YB")))]
+ "ISA_HAS_SEQ_SNE"
+ "@
+ sne\t%0,%1,%2
+ sltu\t%0,%.,%1
+ snei\t%0,%1,%2"
+ [(set_attr "type" "slt")
+ (set_attr "mode" "<GPR:MODE>")])
+
+(define_insn "*sgt<u>_<GPR:mode><GPR2:mode>"
+ [(set (match_operand:GPR2 0 "register_operand" "=d")
+ (any_gt:GPR2 (match_operand:GPR 1 "register_operand" "d")
+ (match_operand:GPR 2 "reg_or_0_operand" "dJ")))]
+ "!TARGET_MIPS16"
+ "slt<u>\t%0,%z2,%1"
+ [(set_attr "type" "slt")
+ (set_attr "mode" "<GPR:MODE>")])
+
+(define_insn "*sgt<u>_<GPR:mode><GPR2:mode>_mips16"
+ [(set (match_operand:GPR2 0 "register_operand" "=t")
+ (any_gt:GPR2 (match_operand:GPR 1 "register_operand" "d")
+ (match_operand:GPR 2 "register_operand" "d")))]
+ "TARGET_MIPS16"
+ "slt<u>\t%2,%1"
+ [(set_attr "type" "slt")
+ (set_attr "mode" "<GPR:MODE>")])
+
+(define_insn "*sge<u>_<GPR:mode><GPR2:mode>"
+ [(set (match_operand:GPR2 0 "register_operand" "=d")
+ (any_ge:GPR2 (match_operand:GPR 1 "register_operand" "d")
+ (const_int 1)))]
+ "!TARGET_MIPS16"
+ "slt<u>\t%0,%.,%1"
+ [(set_attr "type" "slt")
+ (set_attr "mode" "<GPR:MODE>")])
+
+(define_insn "*slt<u>_<GPR:mode><GPR2:mode>"
+ [(set (match_operand:GPR2 0 "register_operand" "=d")
+ (any_lt:GPR2 (match_operand:GPR 1 "register_operand" "d")
+ (match_operand:GPR 2 "arith_operand" "dI")))]
+ "!TARGET_MIPS16"
+ "slt<u>\t%0,%1,%2"
+ [(set_attr "type" "slt")
+ (set_attr "mode" "<GPR:MODE>")])
+
+(define_insn "*slt<u>_<GPR:mode><GPR2:mode>_mips16"
+ [(set (match_operand:GPR2 0 "register_operand" "=t,t")
+ (any_lt:GPR2 (match_operand:GPR 1 "register_operand" "d,d")
+ (match_operand:GPR 2 "arith_operand" "d,I")))]
+ "TARGET_MIPS16"
+ "slt<u>\t%1,%2"
+ [(set_attr "type" "slt")
+ (set_attr "mode" "<GPR:MODE>")
+ (set_attr_alternative "length"
+ [(const_int 4)
+ (if_then_else (match_operand 2 "m16_uimm8_1")
+ (const_int 4)
+ (const_int 8))])])
+
+(define_insn "*sle<u>_<GPR:mode><GPR2:mode>"
+ [(set (match_operand:GPR2 0 "register_operand" "=d")
+ (any_le:GPR2 (match_operand:GPR 1 "register_operand" "d")
+ (match_operand:GPR 2 "sle_operand" "")))]
+ "!TARGET_MIPS16"
+{
+ operands[2] = GEN_INT (INTVAL (operands[2]) + 1);
+ return "slt<u>\t%0,%1,%2";
+}
+ [(set_attr "type" "slt")
+ (set_attr "mode" "<GPR:MODE>")])
+
+(define_insn "*sle<u>_<GPR:mode><GPR2:mode>_mips16"
+ [(set (match_operand:GPR2 0 "register_operand" "=t")
+ (any_le:GPR2 (match_operand:GPR 1 "register_operand" "d")
+ (match_operand:GPR 2 "sle_operand" "")))]
+ "TARGET_MIPS16"
+{
+ operands[2] = GEN_INT (INTVAL (operands[2]) + 1);
+ return "slt<u>\t%1,%2";
+}
+ [(set_attr "type" "slt")
+ (set_attr "mode" "<GPR:MODE>")
+ (set (attr "length") (if_then_else (match_operand 2 "m16_uimm8_m1_1")
+ (const_int 4)
+ (const_int 8)))])
+
+;;
+;; ....................
+;;
+;; FLOATING POINT COMPARISONS
+;;
+;; ....................
+
+(define_insn "s<code>_<mode>"
+ [(set (match_operand:CC 0 "register_operand" "=z")
+ (fcond:CC (match_operand:SCALARF 1 "register_operand" "f")
+ (match_operand:SCALARF 2 "register_operand" "f")))]
+ ""
+ "c.<fcond>.<fmt>\t%Z0%1,%2"
+ [(set_attr "type" "fcmp")
+ (set_attr "mode" "FPSW")])
+
+(define_insn "s<code>_<mode>"
+ [(set (match_operand:CC 0 "register_operand" "=z")
+ (swapped_fcond:CC (match_operand:SCALARF 1 "register_operand" "f")
+ (match_operand:SCALARF 2 "register_operand" "f")))]
+ ""
+ "c.<swapped_fcond>.<fmt>\t%Z0%2,%1"
+ [(set_attr "type" "fcmp")
+ (set_attr "mode" "FPSW")])
+
+;;
+;; ....................
+;;
+;; UNCONDITIONAL BRANCHES
+;;
+;; ....................
+
+;; Unconditional branches.
+
+(define_expand "jump"
+ [(set (pc)
+ (label_ref (match_operand 0)))])
+
+(define_insn "*jump_absolute"
+ [(set (pc)
+ (label_ref (match_operand 0)))]
+ "!TARGET_MIPS16 && TARGET_ABSOLUTE_JUMPS"
+ { return MIPS_ABSOLUTE_JUMP ("%*j\t%l0%/"); }
+ [(set_attr "type" "jump")])
+
+(define_insn "*jump_pic"
+ [(set (pc)
+ (label_ref (match_operand 0)))]
+ "!TARGET_MIPS16 && !TARGET_ABSOLUTE_JUMPS"
+{
+ if (get_attr_length (insn) <= 8)
+ return "%*b\t%l0%/";
+ else
+ {
+ mips_output_load_label (operands[0]);
+ return "%*jr\t%@%/%]";
+ }
+}
+ [(set_attr "type" "branch")])
+
+;; We need a different insn for the mips16, because a mips16 branch
+;; does not have a delay slot.
+
+(define_insn "*jump_mips16"
+ [(set (pc)
+ (label_ref (match_operand 0 "" "")))]
+ "TARGET_MIPS16"
+ "b\t%l0"
+ [(set_attr "type" "branch")])
+
+(define_expand "indirect_jump"
+ [(set (pc) (match_operand 0 "register_operand"))]
+ ""
+{
+ operands[0] = force_reg (Pmode, operands[0]);
+ if (Pmode == SImode)
+ emit_jump_insn (gen_indirect_jumpsi (operands[0]));
+ else
+ emit_jump_insn (gen_indirect_jumpdi (operands[0]));
+ DONE;
+})
+
+(define_insn "indirect_jump<mode>"
+ [(set (pc) (match_operand:P 0 "register_operand" "d"))]
+ ""
+ "%*j\t%0%/"
+ [(set_attr "type" "jump")
+ (set_attr "mode" "none")])
+
+(define_expand "tablejump"
+ [(set (pc)
+ (match_operand 0 "register_operand"))
+ (use (label_ref (match_operand 1 "")))]
+ ""
+{
+ if (TARGET_MIPS16_SHORT_JUMP_TABLES)
+ operands[0] = expand_binop (Pmode, add_optab,
+ convert_to_mode (Pmode, operands[0], false),
+ gen_rtx_LABEL_REF (Pmode, operands[1]),
+ 0, 0, OPTAB_WIDEN);
+ else if (TARGET_GPWORD)
+ operands[0] = expand_binop (Pmode, add_optab, operands[0],
+ pic_offset_table_rtx, 0, 0, OPTAB_WIDEN);
+ else if (TARGET_RTP_PIC)
+ {
+ /* When generating RTP PIC, we use case table entries that are relative
+ to the start of the function. Add the function's address to the
+ value we loaded. */
+ rtx start = get_hard_reg_initial_val (Pmode, PIC_FUNCTION_ADDR_REGNUM);
+ operands[0] = expand_binop (ptr_mode, add_optab, operands[0],
+ start, 0, 0, OPTAB_WIDEN);
+ }
+
+ if (Pmode == SImode)
+ emit_jump_insn (gen_tablejumpsi (operands[0], operands[1]));
+ else
+ emit_jump_insn (gen_tablejumpdi (operands[0], operands[1]));
+ DONE;
+})
+
+(define_insn "tablejump<mode>"
+ [(set (pc)
+ (match_operand:P 0 "register_operand" "d"))
+ (use (label_ref (match_operand 1 "" "")))]
+ ""
+ "%*j\t%0%/"
+ [(set_attr "type" "jump")
+ (set_attr "mode" "none")])
+
+;; For TARGET_USE_GOT, we save the gp in the jmp_buf as well.
+;; While it is possible to either pull it off the stack (in the
+;; o32 case) or recalculate it given t9 and our target label,
+;; it takes 3 or 4 insns to do so.
+
+(define_expand "builtin_setjmp_setup"
+ [(use (match_operand 0 "register_operand"))]
+ "TARGET_USE_GOT"
+{
+ rtx addr;
+
+ addr = plus_constant (operands[0], GET_MODE_SIZE (Pmode) * 3);
+ mips_emit_move (gen_rtx_MEM (Pmode, addr), pic_offset_table_rtx);
+ DONE;
+})
+
+;; Restore the gp that we saved above. Despite the earlier comment, it seems
+;; that older code did recalculate the gp from $25. Continue to jump through
+;; $25 for compatibility (we lose nothing by doing so).
+
+(define_expand "builtin_longjmp"
+ [(use (match_operand 0 "register_operand"))]
+ "TARGET_USE_GOT"
+{
+ /* The elements of the buffer are, in order: */
+ int W = GET_MODE_SIZE (Pmode);
+ rtx fp = gen_rtx_MEM (Pmode, operands[0]);
+ rtx lab = gen_rtx_MEM (Pmode, plus_constant (operands[0], 1*W));
+ rtx stack = gen_rtx_MEM (Pmode, plus_constant (operands[0], 2*W));
+ rtx gpv = gen_rtx_MEM (Pmode, plus_constant (operands[0], 3*W));
+ rtx pv = gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM);
+ /* Use gen_raw_REG to avoid being given pic_offset_table_rtx.
+ The target is bound to be using $28 as the global pointer
+ but the current function might not be. */
+ rtx gp = gen_raw_REG (Pmode, GLOBAL_POINTER_REGNUM);
+
+ /* This bit is similar to expand_builtin_longjmp except that it
+ restores $gp as well. */
+ mips_emit_move (hard_frame_pointer_rtx, fp);
+ mips_emit_move (pv, lab);
+ emit_stack_restore (SAVE_NONLOCAL, stack);
+ mips_emit_move (gp, gpv);
+ emit_use (hard_frame_pointer_rtx);
+ emit_use (stack_pointer_rtx);
+ emit_use (gp);
+ emit_indirect_jump (pv);
+ DONE;
+})
+
+;;
+;; ....................
+;;
+;; Function prologue/epilogue
+;;
+;; ....................
+;;
+
+(define_expand "prologue"
+ [(const_int 1)]
+ ""
+{
+ mips_expand_prologue ();
+ DONE;
+})
+
+;; Block any insns from being moved before this point, since the
+;; profiling call to mcount can use various registers that aren't
+;; saved or used to pass arguments.
+
+(define_insn "blockage"
+ [(unspec_volatile [(const_int 0)] UNSPEC_BLOCKAGE)]
+ ""
+ ""
+ [(set_attr "type" "ghost")
+ (set_attr "mode" "none")])
+
+(define_expand "epilogue"
+ [(const_int 2)]
+ ""
+{
+ mips_expand_epilogue (false);
+ DONE;
+})
+
+(define_expand "sibcall_epilogue"
+ [(const_int 2)]
+ ""
+{
+ mips_expand_epilogue (true);
+ DONE;
+})
+
+;; Trivial return. Make it look like a normal return insn as that
+;; allows jump optimizations to work better.
+
+(define_expand "return"
+ [(return)]
+ "mips_can_use_return_insn ()"
+ { mips_expand_before_return (); })
+
+(define_insn "*return"
+ [(return)]
+ "mips_can_use_return_insn ()"
+ "%*j\t$31%/"
+ [(set_attr "type" "jump")
+ (set_attr "mode" "none")])
+
+;; Normal return.
+
+(define_insn "return_internal"
+ [(return)
+ (use (match_operand 0 "pmode_register_operand" ""))]
+ ""
+ "%*j\t%0%/"
+ [(set_attr "type" "jump")
+ (set_attr "mode" "none")])
+
+;; Exception return.
+(define_insn "mips_eret"
+ [(return)
+ (unspec_volatile [(const_int 0)] UNSPEC_ERET)]
+ ""
+ "eret"
+ [(set_attr "type" "trap")
+ (set_attr "mode" "none")])
+
+;; Debug exception return.
+(define_insn "mips_deret"
+ [(return)
+ (unspec_volatile [(const_int 0)] UNSPEC_DERET)]
+ ""
+ "deret"
+ [(set_attr "type" "trap")
+ (set_attr "mode" "none")])
+
+;; Disable interrupts.
+(define_insn "mips_di"
+ [(unspec_volatile [(const_int 0)] UNSPEC_DI)]
+ ""
+ "di"
+ [(set_attr "type" "trap")
+ (set_attr "mode" "none")])
+
+;; Execution hazard barrier.
+(define_insn "mips_ehb"
+ [(unspec_volatile [(const_int 0)] UNSPEC_EHB)]
+ ""
+ "ehb"
+ [(set_attr "type" "trap")
+ (set_attr "mode" "none")])
+
+;; Read GPR from previous shadow register set.
+(define_insn "mips_rdpgpr"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (unspec_volatile:SI [(match_operand:SI 1 "register_operand" "d")]
+ UNSPEC_RDPGPR))]
+ ""
+ "rdpgpr\t%0,%1"
+ [(set_attr "type" "move")
+ (set_attr "mode" "SI")])
+
+;; Move involving COP0 registers.
+(define_insn "cop0_move"
+ [(set (match_operand:SI 0 "register_operand" "=B,d")
+ (unspec_volatile:SI [(match_operand:SI 1 "register_operand" "d,B")]
+ UNSPEC_COP0))]
+ ""
+{ return mips_output_move (operands[0], operands[1]); }
+ [(set_attr "type" "mtc,mfc")
+ (set_attr "mode" "SI")])
+
+;; This is used in compiling the unwind routines.
+(define_expand "eh_return"
+ [(use (match_operand 0 "general_operand"))]
+ ""
+{
+ if (GET_MODE (operands[0]) != word_mode)
+ operands[0] = convert_to_mode (word_mode, operands[0], 0);
+ if (TARGET_64BIT)
+ emit_insn (gen_eh_set_lr_di (operands[0]));
+ else
+ emit_insn (gen_eh_set_lr_si (operands[0]));
+ DONE;
+})
+
+;; Clobber the return address on the stack. We can't expand this
+;; until we know where it will be put in the stack frame.
+
+(define_insn "eh_set_lr_si"
+ [(unspec [(match_operand:SI 0 "register_operand" "d")] UNSPEC_EH_RETURN)
+ (clobber (match_scratch:SI 1 "=&d"))]
+ "! TARGET_64BIT"
+ "#")
+
+(define_insn "eh_set_lr_di"
+ [(unspec [(match_operand:DI 0 "register_operand" "d")] UNSPEC_EH_RETURN)
+ (clobber (match_scratch:DI 1 "=&d"))]
+ "TARGET_64BIT"
+ "#")
+
+(define_split
+ [(unspec [(match_operand 0 "register_operand")] UNSPEC_EH_RETURN)
+ (clobber (match_scratch 1))]
+ "reload_completed"
+ [(const_int 0)]
+{
+ mips_set_return_address (operands[0], operands[1]);
+ DONE;
+})
+
+(define_expand "exception_receiver"
+ [(const_int 0)]
+ "TARGET_USE_GOT"
+{
+ /* See the comment above load_call<mode> for details. */
+ emit_insn (gen_set_got_version ());
+
+ /* If we have a call-clobbered $gp, restore it from its save slot. */
+ if (HAVE_restore_gp)
+ emit_insn (gen_restore_gp ());
+ DONE;
+})
+
+(define_expand "nonlocal_goto_receiver"
+ [(const_int 0)]
+ "TARGET_USE_GOT"
+{
+ /* See the comment above load_call<mode> for details. */
+ emit_insn (gen_set_got_version ());
+ DONE;
+})
+
+;; Restore $gp from its .cprestore stack slot. The instruction remains
+;; volatile until all uses of $28 are exposed.
+(define_insn_and_split "restore_gp"
+ [(set (reg:SI 28)
+ (unspec_volatile:SI [(const_int 0)] UNSPEC_RESTORE_GP))
+ (clobber (match_scratch:SI 0 "=&d"))]
+ "TARGET_CALL_CLOBBERED_GP"
+ "#"
+ "&& epilogue_completed"
+ [(const_int 0)]
+{
+ mips_restore_gp_from_cprestore_slot (operands[0]);
+ DONE;
+}
+ [(set_attr "type" "ghost")])
+
+;; Move between $gp and its register save slot.
+(define_insn_and_split "move_gp<mode>"
+ [(set (match_operand:GPR 0 "nonimmediate_operand" "=d,m")
+ (unspec:GPR [(match_operand:GPR 1 "move_operand" "m,d")]
+ UNSPEC_MOVE_GP))]
+ ""
+ { return mips_must_initialize_gp_p () ? "#" : ""; }
+ "mips_must_initialize_gp_p ()"
+ [(const_int 0)]
+{
+ mips_emit_move (operands[0], operands[1]);
+ DONE;
+}
+ [(set_attr "type" "ghost")])
+
+;;
+;; ....................
+;;
+;; FUNCTION CALLS
+;;
+;; ....................
+
+;; Instructions to load a call address from the GOT. The address might
+;; point to a function or to a lazy binding stub. In the latter case,
+;; the stub will use the dynamic linker to resolve the function, which
+;; in turn will change the GOT entry to point to the function's real
+;; address.
+;;
+;; This means that every call, even pure and constant ones, can
+;; potentially modify the GOT entry. And once a stub has been called,
+;; we must not call it again.
+;;
+;; We represent this restriction using an imaginary, fixed, call-saved
+;; register called GOT_VERSION_REGNUM. The idea is to make the register
+;; live throughout the function and to change its value after every
+;; potential call site. This stops any rtx value that uses the register
+;; from being computed before an earlier call. To do this, we:
+;;
+;; - Ensure that the register is live on entry to the function,
+;; so that it is never thought to be used uninitalized.
+;;
+;; - Ensure that the register is live on exit from the function,
+;; so that it is live throughout.
+;;
+;; - Make each call (lazily-bound or not) use the current value
+;; of GOT_VERSION_REGNUM, so that updates of the register are
+;; not moved across call boundaries.
+;;
+;; - Add "ghost" definitions of the register to the beginning of
+;; blocks reached by EH and ABNORMAL_CALL edges, because those
+;; edges may involve calls that normal paths don't. (E.g. the
+;; unwinding code that handles a non-call exception may change
+;; lazily-bound GOT entries.) We do this by making the
+;; exception_receiver and nonlocal_goto_receiver expanders emit
+;; a set_got_version instruction.
+;;
+;; - After each call (lazily-bound or not), use a "ghost"
+;; update_got_version instruction to change the register's value.
+;; This instruction mimics the _possible_ effect of the dynamic
+;; resolver during the call and it remains live even if the call
+;; itself becomes dead.
+;;
+;; - Leave GOT_VERSION_REGNUM out of all register classes.
+;; The register is therefore not a valid register_operand
+;; and cannot be moved to or from other registers.
+
+(define_insn "load_call<mode>"
+ [(set (match_operand:P 0 "register_operand" "=d")
+ (unspec:P [(match_operand:P 1 "register_operand" "d")
+ (match_operand:P 2 "immediate_operand" "")
+ (reg:SI GOT_VERSION_REGNUM)] UNSPEC_LOAD_CALL))]
+ "TARGET_USE_GOT"
+ "<load>\t%0,%R2(%1)"
+ [(set_attr "got" "load")
+ (set_attr "mode" "<MODE>")])
+
+(define_insn "set_got_version"
+ [(set (reg:SI GOT_VERSION_REGNUM)
+ (unspec_volatile:SI [(const_int 0)] UNSPEC_SET_GOT_VERSION))]
+ "TARGET_USE_GOT"
+ ""
+ [(set_attr "type" "ghost")])
+
+(define_insn "update_got_version"
+ [(set (reg:SI GOT_VERSION_REGNUM)
+ (unspec:SI [(reg:SI GOT_VERSION_REGNUM)] UNSPEC_UPDATE_GOT_VERSION))]
+ "TARGET_USE_GOT"
+ ""
+ [(set_attr "type" "ghost")])
+
+;; Sibling calls. All these patterns use jump instructions.
+
+;; If TARGET_SIBCALLS, call_insn_operand will only accept constant
+;; addresses if a direct jump is acceptable. Since the 'S' constraint
+;; is defined in terms of call_insn_operand, the same is true of the
+;; constraints.
+
+;; When we use an indirect jump, we need a register that will be
+;; preserved by the epilogue. Since TARGET_USE_PIC_FN_ADDR_REG forces
+;; us to use $25 for this purpose -- and $25 is never clobbered by the
+;; epilogue -- we might as well use it for !TARGET_USE_PIC_FN_ADDR_REG
+;; as well.
+
+(define_expand "sibcall"
+ [(parallel [(call (match_operand 0 "")
+ (match_operand 1 ""))
+ (use (match_operand 2 "")) ;; next_arg_reg
+ (use (match_operand 3 ""))])] ;; struct_value_size_rtx
+ "TARGET_SIBCALLS"
+{
+ mips_expand_call (MIPS_CALL_SIBCALL, NULL_RTX, XEXP (operands[0], 0),
+ operands[1], operands[2], false);
+ DONE;
+})
+
+(define_insn "sibcall_internal"
+ [(call (mem:SI (match_operand 0 "call_insn_operand" "j,S"))
+ (match_operand 1 "" ""))]
+ "TARGET_SIBCALLS && SIBLING_CALL_P (insn)"
+ { return MIPS_CALL ("j", operands, 0, 1); }
+ [(set_attr "type" "call")])
+
+(define_expand "sibcall_value"
+ [(parallel [(set (match_operand 0 "")
+ (call (match_operand 1 "")
+ (match_operand 2 "")))
+ (use (match_operand 3 ""))])] ;; next_arg_reg
+ "TARGET_SIBCALLS"
+{
+ mips_expand_call (MIPS_CALL_SIBCALL, operands[0], XEXP (operands[1], 0),
+ operands[2], operands[3], false);
+ DONE;
+})
+
+(define_insn "sibcall_value_internal"
+ [(set (match_operand 0 "register_operand" "")
+ (call (mem:SI (match_operand 1 "call_insn_operand" "j,S"))
+ (match_operand 2 "" "")))]
+ "TARGET_SIBCALLS && SIBLING_CALL_P (insn)"
+ { return MIPS_CALL ("j", operands, 1, 2); }
+ [(set_attr "type" "call")])
+
+(define_insn "sibcall_value_multiple_internal"
+ [(set (match_operand 0 "register_operand" "")
+ (call (mem:SI (match_operand 1 "call_insn_operand" "j,S"))
+ (match_operand 2 "" "")))
+ (set (match_operand 3 "register_operand" "")
+ (call (mem:SI (match_dup 1))
+ (match_dup 2)))]
+ "TARGET_SIBCALLS && SIBLING_CALL_P (insn)"
+ { return MIPS_CALL ("j", operands, 1, 2); }
+ [(set_attr "type" "call")])
+
+(define_expand "call"
+ [(parallel [(call (match_operand 0 "")
+ (match_operand 1 ""))
+ (use (match_operand 2 "")) ;; next_arg_reg
+ (use (match_operand 3 ""))])] ;; struct_value_size_rtx
+ ""
+{
+ mips_expand_call (MIPS_CALL_NORMAL, NULL_RTX, XEXP (operands[0], 0),
+ operands[1], operands[2], false);
+ DONE;
+})
+
+;; This instruction directly corresponds to an assembly-language "jal".
+;; There are four cases:
+;;
+;; - -mno-abicalls:
+;; Both symbolic and register destinations are OK. The pattern
+;; always expands to a single mips instruction.
+;;
+;; - -mabicalls/-mno-explicit-relocs:
+;; Again, both symbolic and register destinations are OK.
+;; The call is treated as a multi-instruction black box.
+;;
+;; - -mabicalls/-mexplicit-relocs with n32 or n64:
+;; Only "jal $25" is allowed. This expands to a single "jalr $25"
+;; instruction.
+;;
+;; - -mabicalls/-mexplicit-relocs with o32 or o64:
+;; Only "jal $25" is allowed. The call is actually two instructions:
+;; "jalr $25" followed by an insn to reload $gp.
+;;
+;; In the last case, we can generate the individual instructions with
+;; a define_split. There are several things to be wary of:
+;;
+;; - We can't expose the load of $gp before reload. If we did,
+;; it might get removed as dead, but reload can introduce new
+;; uses of $gp by rematerializing constants.
+;;
+;; - We shouldn't restore $gp after calls that never return.
+;; It isn't valid to insert instructions between a noreturn
+;; call and the following barrier.
+;;
+;; - The splitter deliberately changes the liveness of $gp. The unsplit
+;; instruction preserves $gp and so have no effect on its liveness.
+;; But once we generate the separate insns, it becomes obvious that
+;; $gp is not live on entry to the call.
+;;
+(define_insn_and_split "call_internal"
+ [(call (mem:SI (match_operand 0 "call_insn_operand" "c,S"))
+ (match_operand 1 "" ""))
+ (clobber (reg:SI RETURN_ADDR_REGNUM))]
+ ""
+ { return TARGET_SPLIT_CALLS ? "#" : MIPS_CALL ("jal", operands, 0, 1); }
+ "reload_completed && TARGET_SPLIT_CALLS"
+ [(const_int 0)]
+{
+ mips_split_call (curr_insn, gen_call_split (operands[0], operands[1]));
+ DONE;
+}
+ [(set_attr "jal" "indirect,direct")])
+
+(define_insn "call_split"
+ [(call (mem:SI (match_operand 0 "call_insn_operand" "cS"))
+ (match_operand 1 "" ""))
+ (clobber (reg:SI RETURN_ADDR_REGNUM))
+ (clobber (reg:SI 28))]
+ "TARGET_SPLIT_CALLS"
+ { return MIPS_CALL ("jal", operands, 0, 1); }
+ [(set_attr "type" "call")])
+
+;; A pattern for calls that must be made directly. It is used for
+;; MIPS16 calls that the linker may need to redirect to a hard-float
+;; stub; the linker relies on the call relocation type to detect when
+;; such redirection is needed.
+(define_insn_and_split "call_internal_direct"
+ [(call (mem:SI (match_operand 0 "const_call_insn_operand"))
+ (match_operand 1))
+ (const_int 1)
+ (clobber (reg:SI RETURN_ADDR_REGNUM))]
+ ""
+ { return TARGET_SPLIT_CALLS ? "#" : MIPS_CALL ("jal", operands, 0, -1); }
+ "reload_completed && TARGET_SPLIT_CALLS"
+ [(const_int 0)]
+{
+ mips_split_call (curr_insn,
+ gen_call_direct_split (operands[0], operands[1]));
+ DONE;
+}
+ [(set_attr "type" "call")])
+
+(define_insn "call_direct_split"
+ [(call (mem:SI (match_operand 0 "const_call_insn_operand"))
+ (match_operand 1))
+ (const_int 1)
+ (clobber (reg:SI RETURN_ADDR_REGNUM))
+ (clobber (reg:SI 28))]
+ "TARGET_SPLIT_CALLS"
+ { return MIPS_CALL ("jal", operands, 0, -1); }
+ [(set_attr "type" "call")])
+
+(define_expand "call_value"
+ [(parallel [(set (match_operand 0 "")
+ (call (match_operand 1 "")
+ (match_operand 2 "")))
+ (use (match_operand 3 ""))])] ;; next_arg_reg
+ ""
+{
+ mips_expand_call (MIPS_CALL_NORMAL, operands[0], XEXP (operands[1], 0),
+ operands[2], operands[3], false);
+ DONE;
+})
+
+;; See comment for call_internal.
+(define_insn_and_split "call_value_internal"
+ [(set (match_operand 0 "register_operand" "")
+ (call (mem:SI (match_operand 1 "call_insn_operand" "c,S"))
+ (match_operand 2 "" "")))
+ (clobber (reg:SI RETURN_ADDR_REGNUM))]
+ ""
+ { return TARGET_SPLIT_CALLS ? "#" : MIPS_CALL ("jal", operands, 1, 2); }
+ "reload_completed && TARGET_SPLIT_CALLS"
+ [(const_int 0)]
+{
+ mips_split_call (curr_insn,
+ gen_call_value_split (operands[0], operands[1],
+ operands[2]));
+ DONE;
+}
+ [(set_attr "jal" "indirect,direct")])
+
+(define_insn "call_value_split"
+ [(set (match_operand 0 "register_operand" "")
+ (call (mem:SI (match_operand 1 "call_insn_operand" "cS"))
+ (match_operand 2 "" "")))
+ (clobber (reg:SI RETURN_ADDR_REGNUM))
+ (clobber (reg:SI 28))]
+ "TARGET_SPLIT_CALLS"
+ { return MIPS_CALL ("jal", operands, 1, 2); }
+ [(set_attr "type" "call")])
+
+;; See call_internal_direct.
+(define_insn_and_split "call_value_internal_direct"
+ [(set (match_operand 0 "register_operand")
+ (call (mem:SI (match_operand 1 "const_call_insn_operand"))
+ (match_operand 2)))
+ (const_int 1)
+ (clobber (reg:SI RETURN_ADDR_REGNUM))]
+ ""
+ { return TARGET_SPLIT_CALLS ? "#" : MIPS_CALL ("jal", operands, 1, -1); }
+ "reload_completed && TARGET_SPLIT_CALLS"
+ [(const_int 0)]
+{
+ mips_split_call (curr_insn,
+ gen_call_value_direct_split (operands[0], operands[1],
+ operands[2]));
+ DONE;
+}
+ [(set_attr "type" "call")])
+
+(define_insn "call_value_direct_split"
+ [(set (match_operand 0 "register_operand")
+ (call (mem:SI (match_operand 1 "const_call_insn_operand"))
+ (match_operand 2)))
+ (const_int 1)
+ (clobber (reg:SI RETURN_ADDR_REGNUM))
+ (clobber (reg:SI 28))]
+ "TARGET_SPLIT_CALLS"
+ { return MIPS_CALL ("jal", operands, 1, -1); }
+ [(set_attr "type" "call")])
+
+;; See comment for call_internal.
+(define_insn_and_split "call_value_multiple_internal"
+ [(set (match_operand 0 "register_operand" "")
+ (call (mem:SI (match_operand 1 "call_insn_operand" "c,S"))
+ (match_operand 2 "" "")))
+ (set (match_operand 3 "register_operand" "")
+ (call (mem:SI (match_dup 1))
+ (match_dup 2)))
+ (clobber (reg:SI RETURN_ADDR_REGNUM))]
+ ""
+ { return TARGET_SPLIT_CALLS ? "#" : MIPS_CALL ("jal", operands, 1, 2); }
+ "reload_completed && TARGET_SPLIT_CALLS"
+ [(const_int 0)]
+{
+ mips_split_call (curr_insn,
+ gen_call_value_multiple_split (operands[0], operands[1],
+ operands[2], operands[3]));
+ DONE;
+}
+ [(set_attr "jal" "indirect,direct")])
+
+(define_insn "call_value_multiple_split"
+ [(set (match_operand 0 "register_operand" "")
+ (call (mem:SI (match_operand 1 "call_insn_operand" "cS"))
+ (match_operand 2 "" "")))
+ (set (match_operand 3 "register_operand" "")
+ (call (mem:SI (match_dup 1))
+ (match_dup 2)))
+ (clobber (reg:SI RETURN_ADDR_REGNUM))
+ (clobber (reg:SI 28))]
+ "TARGET_SPLIT_CALLS"
+ { return MIPS_CALL ("jal", operands, 1, 2); }
+ [(set_attr "type" "call")])
+
+;; Call subroutine returning any type.
+
+(define_expand "untyped_call"
+ [(parallel [(call (match_operand 0 "")
+ (const_int 0))
+ (match_operand 1 "")
+ (match_operand 2 "")])]
+ ""
+{
+ int i;
+
+ emit_call_insn (GEN_CALL (operands[0], const0_rtx, NULL, const0_rtx));
+
+ for (i = 0; i < XVECLEN (operands[2], 0); i++)
+ {
+ rtx set = XVECEXP (operands[2], 0, i);
+ mips_emit_move (SET_DEST (set), SET_SRC (set));
+ }
+
+ emit_insn (gen_blockage ());
+ DONE;
+})
+
+;;
+;; ....................
+;;
+;; MISC.
+;;
+;; ....................
+;;
+
+
+(define_insn "prefetch"
+ [(prefetch (match_operand:QI 0 "address_operand" "p")
+ (match_operand 1 "const_int_operand" "n")
+ (match_operand 2 "const_int_operand" "n"))]
+ "ISA_HAS_PREFETCH && TARGET_EXPLICIT_RELOCS"
+{
+ if (TARGET_LOONGSON_2EF || TARGET_LOONGSON_3A)
+ /* Loongson 2[ef] and Loongson 3a use load to $0 to perform prefetching. */
+ return "ld\t$0,%a0";
+ operands[1] = mips_prefetch_cookie (operands[1], operands[2]);
+ return "pref\t%1,%a0";
+}
+ [(set_attr "type" "prefetch")])
+
+(define_insn "*prefetch_indexed_<mode>"
+ [(prefetch (plus:P (match_operand:P 0 "register_operand" "d")
+ (match_operand:P 1 "register_operand" "d"))
+ (match_operand 2 "const_int_operand" "n")
+ (match_operand 3 "const_int_operand" "n"))]
+ "ISA_HAS_PREFETCHX && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
+{
+ operands[2] = mips_prefetch_cookie (operands[2], operands[3]);
+ return "prefx\t%2,%1(%0)";
+}
+ [(set_attr "type" "prefetchx")])
+
+(define_insn "nop"
+ [(const_int 0)]
+ ""
+ "%(nop%)"
+ [(set_attr "type" "nop")
+ (set_attr "mode" "none")])
+
+;; Like nop, but commented out when outside a .set noreorder block.
+(define_insn "hazard_nop"
+ [(const_int 1)]
+ ""
+ {
+ if (mips_noreorder.nesting_level > 0)
+ return "nop";
+ else
+ return "#nop";
+ }
+ [(set_attr "type" "nop")])
+
+;; MIPS4 Conditional move instructions.
+
+(define_insn "*mov<GPR:mode>_on_<MOVECC:mode>"
+ [(set (match_operand:GPR 0 "register_operand" "=d,d")
+ (if_then_else:GPR
+ (match_operator:MOVECC 4 "equality_operator"
+ [(match_operand:MOVECC 1 "register_operand" "<MOVECC:reg>,<MOVECC:reg>")
+ (const_int 0)])
+ (match_operand:GPR 2 "reg_or_0_operand" "dJ,0")
+ (match_operand:GPR 3 "reg_or_0_operand" "0,dJ")))]
+ "ISA_HAS_CONDMOVE"
+ "@
+ mov%T4\t%0,%z2,%1
+ mov%t4\t%0,%z3,%1"
+ [(set_attr "type" "condmove")
+ (set_attr "mode" "<GPR:MODE>")])
+
+(define_insn "*mov<SCALARF:mode>_on_<MOVECC:mode>"
+ [(set (match_operand:SCALARF 0 "register_operand" "=f,f")
+ (if_then_else:SCALARF
+ (match_operator:MOVECC 4 "equality_operator"
+ [(match_operand:MOVECC 1 "register_operand" "<MOVECC:reg>,<MOVECC:reg>")
+ (const_int 0)])
+ (match_operand:SCALARF 2 "register_operand" "f,0")
+ (match_operand:SCALARF 3 "register_operand" "0,f")))]
+ "ISA_HAS_FP_CONDMOVE"
+ "@
+ mov%T4.<fmt>\t%0,%2,%1
+ mov%t4.<fmt>\t%0,%3,%1"
+ [(set_attr "type" "condmove")
+ (set_attr "mode" "<SCALARF:MODE>")])
+
+;; These are the main define_expand's used to make conditional moves.
+
+(define_expand "mov<mode>cc"
+ [(set (match_dup 4) (match_operand 1 "comparison_operator"))
+ (set (match_operand:GPR 0 "register_operand")
+ (if_then_else:GPR (match_dup 5)
+ (match_operand:GPR 2 "reg_or_0_operand")
+ (match_operand:GPR 3 "reg_or_0_operand")))]
+ "ISA_HAS_CONDMOVE"
+{
+ mips_expand_conditional_move (operands);
+ DONE;
+})
+
+(define_expand "mov<mode>cc"
+ [(set (match_dup 4) (match_operand 1 "comparison_operator"))
+ (set (match_operand:SCALARF 0 "register_operand")
+ (if_then_else:SCALARF (match_dup 5)
+ (match_operand:SCALARF 2 "register_operand")
+ (match_operand:SCALARF 3 "register_operand")))]
+ "ISA_HAS_FP_CONDMOVE"
+{
+ mips_expand_conditional_move (operands);
+ DONE;
+})
+
+;;
+;; ....................
+;;
+;; mips16 inline constant tables
+;;
+;; ....................
+;;
+
+(define_insn "consttable_int"
+ [(unspec_volatile [(match_operand 0 "consttable_operand" "")
+ (match_operand 1 "const_int_operand" "")]
+ UNSPEC_CONSTTABLE_INT)]
+ "TARGET_MIPS16"
+{
+ assemble_integer (operands[0], INTVAL (operands[1]),
+ BITS_PER_UNIT * INTVAL (operands[1]), 1);
+ return "";
+}
+ [(set (attr "length") (symbol_ref "INTVAL (operands[1])"))])
+
+(define_insn "consttable_float"
+ [(unspec_volatile [(match_operand 0 "consttable_operand" "")]
+ UNSPEC_CONSTTABLE_FLOAT)]
+ "TARGET_MIPS16"
+{
+ REAL_VALUE_TYPE d;
+
+ gcc_assert (GET_CODE (operands[0]) == CONST_DOUBLE);
+ REAL_VALUE_FROM_CONST_DOUBLE (d, operands[0]);
+ assemble_real (d, GET_MODE (operands[0]),
+ GET_MODE_BITSIZE (GET_MODE (operands[0])));
+ return "";
+}
+ [(set (attr "length")
+ (symbol_ref "GET_MODE_SIZE (GET_MODE (operands[0]))"))])
+
+(define_insn "align"
+ [(unspec_volatile [(match_operand 0 "const_int_operand" "")] UNSPEC_ALIGN)]
+ ""
+ ".align\t%0"
+ [(set (attr "length") (symbol_ref "(1 << INTVAL (operands[0])) - 1"))])
+
+(define_split
+ [(match_operand 0 "small_data_pattern")]
+ "reload_completed"
+ [(match_dup 0)]
+ { operands[0] = mips_rewrite_small_data (operands[0]); })
+
+;;
+;; ....................
+;;
+;; MIPS16e Save/Restore
+;;
+;; ....................
+;;
+
+(define_insn "*mips16e_save_restore"
+ [(match_parallel 0 ""
+ [(set (match_operand:SI 1 "register_operand")
+ (plus:SI (match_dup 1)
+ (match_operand:SI 2 "const_int_operand")))])]
+ "operands[1] == stack_pointer_rtx
+ && mips16e_save_restore_pattern_p (operands[0], INTVAL (operands[2]), NULL)"
+ { return mips16e_output_save_restore (operands[0], INTVAL (operands[2])); }
+ [(set_attr "type" "arith")
+ (set_attr "extended_mips16" "yes")])
+
+;; Thread-Local Storage
+
+;; The TLS base pointer is accessed via "rdhwr $3, $29". No current
+;; MIPS architecture defines this register, and no current
+;; implementation provides it; instead, any OS which supports TLS is
+;; expected to trap and emulate this instruction. rdhwr is part of the
+;; MIPS 32r2 specification, but we use it on any architecture because
+;; we expect it to be emulated. Use .set to force the assembler to
+;; accept it.
+;;
+;; We do not use a constraint to force the destination to be $3
+;; because $3 can appear explicitly as a function return value.
+;; If we leave the use of $3 implicit in the constraints until
+;; reload, we may end up making a $3 return value live across
+;; the instruction, leading to a spill failure when reloading it.
+(define_insn_and_split "tls_get_tp_<mode>"
+ [(set (match_operand:P 0 "register_operand" "=d")
+ (unspec:P [(const_int 0)] UNSPEC_TLS_GET_TP))
+ (clobber (reg:P TLS_GET_TP_REGNUM))]
+ "HAVE_AS_TLS && !TARGET_MIPS16"
+ "#"
+ "&& reload_completed"
+ [(set (reg:P TLS_GET_TP_REGNUM)
+ (unspec:P [(const_int 0)] UNSPEC_TLS_GET_TP))
+ (set (match_dup 0) (reg:P TLS_GET_TP_REGNUM))]
+ ""
+ [(set_attr "type" "unknown")
+ ; Since rdhwr always generates a trap for now, putting it in a delay
+ ; slot would make the kernel's emulation of it much slower.
+ (set_attr "can_delay" "no")
+ (set_attr "mode" "<MODE>")
+ (set_attr "length" "8")])
+
+(define_insn "*tls_get_tp_<mode>_split"
+ [(set (reg:P TLS_GET_TP_REGNUM)
+ (unspec:P [(const_int 0)] UNSPEC_TLS_GET_TP))]
+ "HAVE_AS_TLS && !TARGET_MIPS16"
+ ".set\tpush\;.set\tmips32r2\t\;rdhwr\t$3,$29\;.set\tpop"
+ [(set_attr "type" "unknown")
+ ; See tls_get_tp_<mode>
+ (set_attr "can_delay" "no")
+ (set_attr "mode" "<MODE>")])
+
+;; Synchronization instructions.
+
+(include "sync.md")
+
+; The MIPS Paired-Single Floating Point and MIPS-3D Instructions.
+
+(include "mips-ps-3d.md")
+
+; The MIPS DSP Instructions.
+
+(include "mips-dsp.md")
+
+; The MIPS DSP REV 2 Instructions.
+
+(include "mips-dspr2.md")
+
+; MIPS fixed-point instructions.
+(include "mips-fixed.md")
+
+; ST-Microelectronics Loongson-2E/2F-specific patterns.
+(include "loongson.md")
+
+(define_c_enum "unspec" [
+ UNSPEC_ADDRESS_FIRST
+])
diff --git a/gcc/config/mips/mips.opt b/gcc/config/mips/mips.opt
new file mode 100644
index 000000000..20b0b6cde
--- /dev/null
+++ b/gcc/config/mips/mips.opt
@@ -0,0 +1,310 @@
+; Options for the MIPS port of the compiler
+;
+; Copyright (C) 2005, 2007, 2008, 2010, 2011 Free Software Foundation, 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/>.
+
+EB
+Driver
+
+EL
+Driver
+
+mabi=
+Target RejectNegative Joined
+-mabi=ABI Generate code that conforms to the given ABI
+
+mabicalls
+Target Report Mask(ABICALLS)
+Generate code that can be used in SVR4-style dynamic objects
+
+mad
+Target Report Var(TARGET_MAD)
+Use PMC-style 'mad' instructions
+
+march=
+Target RejectNegative Joined Var(mips_arch_string)
+-march=ISA Generate code for the given ISA
+
+mbranch-cost=
+Target RejectNegative Joined UInteger Var(mips_branch_cost)
+-mbranch-cost=COST Set the cost of branches to roughly COST instructions
+
+mbranch-likely
+Target Report Mask(BRANCHLIKELY)
+Use Branch Likely instructions, overriding the architecture default
+
+mflip-mips16
+Target Report Var(TARGET_FLIP_MIPS16)
+Switch on/off MIPS16 ASE on alternating functions for compiler testing
+
+mcheck-zero-division
+Target Report Mask(CHECK_ZERO_DIV)
+Trap on integer divide by zero
+
+mcode-readable=
+Target RejectNegative Joined
+-mcode-readable=SETTING Specify when instructions are allowed to access code
+
+mdivide-breaks
+Target Report RejectNegative Mask(DIVIDE_BREAKS)
+Use branch-and-break sequences to check for integer divide by zero
+
+mdivide-traps
+Target Report RejectNegative InverseMask(DIVIDE_BREAKS, DIVIDE_TRAPS)
+Use trap instructions to check for integer divide by zero
+
+mdmx
+Target Report RejectNegative Var(TARGET_MDMX)
+Allow the use of MDMX instructions
+
+mdouble-float
+Target Report RejectNegative InverseMask(SINGLE_FLOAT, DOUBLE_FLOAT)
+Allow hardware floating-point instructions to cover both 32-bit and 64-bit operations
+
+mdsp
+Target Report Mask(DSP)
+Use MIPS-DSP instructions
+
+mdspr2
+Target Report Mask(DSPR2)
+Use MIPS-DSP REV 2 instructions
+
+mdebug
+Target Var(TARGET_DEBUG_MODE) Undocumented
+
+mdebugd
+Target Var(TARGET_DEBUG_D_MODE) Undocumented
+
+meb
+Target Report RejectNegative Mask(BIG_ENDIAN)
+Use big-endian byte order
+
+mel
+Target Report RejectNegative InverseMask(BIG_ENDIAN, LITTLE_ENDIAN)
+Use little-endian byte order
+
+membedded-data
+Target Report Var(TARGET_EMBEDDED_DATA)
+Use ROM instead of RAM
+
+mexplicit-relocs
+Target Report Mask(EXPLICIT_RELOCS)
+Use NewABI-style %reloc() assembly operators
+
+mextern-sdata
+Target Report Var(TARGET_EXTERN_SDATA) Init(1)
+Use -G for data that is not defined by the current object
+
+mfix-r4000
+Target Report Mask(FIX_R4000)
+Work around certain R4000 errata
+
+mfix-r4400
+Target Report Mask(FIX_R4400)
+Work around certain R4400 errata
+
+mfix-r10000
+Target Report Mask(FIX_R10000)
+Work around certain R10000 errata
+
+mfix-sb1
+Target Report Var(TARGET_FIX_SB1)
+Work around errata for early SB-1 revision 2 cores
+
+mfix-vr4120
+Target Report Var(TARGET_FIX_VR4120)
+Work around certain VR4120 errata
+
+mfix-vr4130
+Target Report Var(TARGET_FIX_VR4130)
+Work around VR4130 mflo/mfhi errata
+
+mfix4300
+Target Report Var(TARGET_4300_MUL_FIX)
+Work around an early 4300 hardware bug
+
+mfp-exceptions
+Target Report Mask(FP_EXCEPTIONS)
+FP exceptions are enabled
+
+mfp32
+Target Report RejectNegative InverseMask(FLOAT64)
+Use 32-bit floating-point registers
+
+mfp64
+Target Report RejectNegative Mask(FLOAT64)
+Use 64-bit floating-point registers
+
+mflush-func=
+Target RejectNegative Joined Var(mips_cache_flush_func) Init(CACHE_FLUSH_FUNC)
+-mflush-func=FUNC Use FUNC to flush the cache before calling stack trampolines
+
+mfused-madd
+Target Report Mask(FUSED_MADD)
+Generate floating-point multiply-add instructions
+
+mgp32
+Target Report RejectNegative InverseMask(64BIT)
+Use 32-bit general registers
+
+mgp64
+Target Report RejectNegative Mask(64BIT)
+Use 64-bit general registers
+
+mgpopt
+Target Report Var(TARGET_GPOPT) Init(1)
+Use GP-relative addressing to access small data
+
+mplt
+Target Report Var(TARGET_PLT)
+When generating -mabicalls code, allow executables to use PLTs and copy relocations
+
+mhard-float
+Target Report RejectNegative InverseMask(SOFT_FLOAT_ABI, HARD_FLOAT_ABI)
+Allow the use of hardware floating-point ABI and instructions
+
+minterlink-mips16
+Target Report Var(TARGET_INTERLINK_MIPS16) Init(0)
+Generate code that can be safely linked with MIPS16 code.
+
+mips
+Target RejectNegative Joined
+-mipsN Generate code for ISA level N
+
+mips16
+Target Report RejectNegative Mask(MIPS16)
+Generate MIPS16 code
+
+mips3d
+Target Report RejectNegative Mask(MIPS3D)
+Use MIPS-3D instructions
+
+mllsc
+Target Report Mask(LLSC)
+Use ll, sc and sync instructions
+
+mlocal-sdata
+Target Report Var(TARGET_LOCAL_SDATA) Init(1)
+Use -G for object-local data
+
+mlong-calls
+Target Report Var(TARGET_LONG_CALLS)
+Use indirect calls
+
+mlong32
+Target Report RejectNegative InverseMask(LONG64, LONG32)
+Use a 32-bit long type
+
+mlong64
+Target Report RejectNegative Mask(LONG64)
+Use a 64-bit long type
+
+mmcount-ra-address
+Target Report Var(TARGET_MCOUNT_RA_ADDRESS)
+Pass the address of the ra save location to _mcount in $12
+
+mmemcpy
+Target Report Mask(MEMCPY)
+Don't optimize block moves
+
+mmips-tfile
+Target
+Use the mips-tfile postpass
+
+mmt
+Target Report Var(TARGET_MT)
+Allow the use of MT instructions
+
+mno-float
+Target Report RejectNegative Var(TARGET_NO_FLOAT) Condition(TARGET_SUPPORTS_NO_FLOAT)
+Prevent the use of all floating-point operations
+
+mno-flush-func
+Target RejectNegative
+Do not use a cache-flushing function before calling stack trampolines
+
+mno-mdmx
+Target Report RejectNegative Var(TARGET_MDMX, 0)
+Do not use MDMX instructions
+
+mno-mips16
+Target Report RejectNegative InverseMask(MIPS16)
+Generate normal-mode code
+
+mno-mips3d
+Target Report RejectNegative InverseMask(MIPS3D)
+Do not use MIPS-3D instructions
+
+mpaired-single
+Target Report Mask(PAIRED_SINGLE_FLOAT)
+Use paired-single floating-point instructions
+
+mr10k-cache-barrier=
+Target Joined RejectNegative
+-mr10k-cache-barrier=SETTING Specify when r10k cache barriers should be inserted
+
+mrelax-pic-calls
+Target Report Mask(RELAX_PIC_CALLS)
+Try to allow the linker to turn PIC calls into direct calls
+
+mshared
+Target Report Var(TARGET_SHARED) Init(1)
+When generating -mabicalls code, make the code suitable for use in shared libraries
+
+msingle-float
+Target Report RejectNegative Mask(SINGLE_FLOAT)
+Restrict the use of hardware floating-point instructions to 32-bit operations
+
+msmartmips
+Target Report Mask(SMARTMIPS)
+Use SmartMIPS instructions
+
+msoft-float
+Target Report RejectNegative Mask(SOFT_FLOAT_ABI)
+Prevent the use of all hardware floating-point instructions
+
+msplit-addresses
+Target Report Mask(SPLIT_ADDRESSES)
+Optimize lui/addiu address loads
+
+msym32
+Target Report Var(TARGET_SYM32)
+Assume all symbols have 32-bit values
+
+msynci
+Target Report Mask(SYNCI)
+Use synci instruction to invalidate i-cache
+
+mtune=
+Target RejectNegative Joined Var(mips_tune_string)
+-mtune=PROCESSOR Optimize the output for PROCESSOR
+
+muninit-const-in-rodata
+Target Report Var(TARGET_UNINIT_CONST_IN_RODATA)
+Put uninitialized constants in ROM (needs -membedded-data)
+
+mvr4130-align
+Target Report Mask(VR4130_ALIGN)
+Perform VR4130-specific alignment optimizations
+
+mxgot
+Target Report Var(TARGET_XGOT)
+Lift restrictions on GOT size
+
+noasmopt
+Driver
diff --git a/gcc/config/mips/mips16.S b/gcc/config/mips/mips16.S
new file mode 100644
index 000000000..ec331b5f6
--- /dev/null
+++ b/gcc/config/mips/mips16.S
@@ -0,0 +1,712 @@
+/* mips16 floating point support code
+ Copyright (C) 1996, 1997, 1998, 2008, 2009, 2010
+ Free Software Foundation, Inc.
+ Contributed by Cygnus Support
+
+This file 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.
+
+This file 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.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+<http://www.gnu.org/licenses/>. */
+
+/* This file contains mips16 floating point support functions. These
+ functions are called by mips16 code to handle floating point when
+ -msoft-float is not used. They accept the arguments and return
+ values using the soft-float calling convention, but do the actual
+ operation using the hard floating point instructions. */
+
+#if defined _MIPS_SIM && (_MIPS_SIM == _ABIO32 || _MIPS_SIM == _ABIO64)
+
+/* This file contains 32-bit assembly code. */
+ .set nomips16
+
+/* Start a function. */
+
+#define STARTFN(NAME) .globl NAME; .ent NAME; NAME:
+
+/* Finish a function. */
+
+#define ENDFN(NAME) .end NAME
+
+/* ARG1
+ The FPR that holds the first floating-point argument.
+
+ ARG2
+ The FPR that holds the second floating-point argument.
+
+ RET
+ The FPR that holds a floating-point return value. */
+
+#define RET $f0
+#define ARG1 $f12
+#ifdef __mips64
+#define ARG2 $f13
+#else
+#define ARG2 $f14
+#endif
+
+/* Set 64-bit register GPR so that its high 32 bits contain HIGH_FPR
+ and so that its low 32 bits contain LOW_FPR. */
+#define MERGE_GPRf(GPR, HIGH_FPR, LOW_FPR) \
+ .set noat; \
+ mfc1 $1, LOW_FPR; \
+ mfc1 GPR, HIGH_FPR; \
+ dsll $1, $1, 32; \
+ dsll GPR, GPR, 32; \
+ dsrl $1, $1, 32; \
+ or GPR, GPR, $1; \
+ .set at
+
+/* Move the high 32 bits of GPR to HIGH_FPR and the low 32 bits of
+ GPR to LOW_FPR. */
+#define MERGE_GPRt(GPR, HIGH_FPR, LOW_FPR) \
+ .set noat; \
+ dsrl $1, GPR, 32; \
+ mtc1 GPR, LOW_FPR; \
+ mtc1 $1, HIGH_FPR; \
+ .set at
+
+/* Jump to T, and use "OPCODE, OP2" to implement a delayed move. */
+#define DELAYt(T, OPCODE, OP2) \
+ .set noreorder; \
+ jr T; \
+ OPCODE, OP2; \
+ .set reorder
+
+/* Use "OPCODE. OP2" and jump to T. */
+#define DELAYf(T, OPCODE, OP2) OPCODE, OP2; jr T
+
+/* MOVE_SF_BYTE0(D)
+ Move the first single-precision floating-point argument between
+ GPRs and FPRs.
+
+ MOVE_SI_BYTE0(D)
+ Likewise the first single-precision integer argument.
+
+ MOVE_SF_BYTE4(D)
+ Move the second single-precision floating-point argument between
+ GPRs and FPRs, given that the first argument occupies 4 bytes.
+
+ MOVE_SF_BYTE8(D)
+ Move the second single-precision floating-point argument between
+ GPRs and FPRs, given that the first argument occupies 8 bytes.
+
+ MOVE_DF_BYTE0(D)
+ Move the first double-precision floating-point argument between
+ GPRs and FPRs.
+
+ MOVE_DF_BYTE8(D)
+ Likewise the second double-precision floating-point argument.
+
+ MOVE_SF_RET(D, T)
+ Likewise a single-precision floating-point return value,
+ then jump to T.
+
+ MOVE_SC_RET(D, T)
+ Likewise a complex single-precision floating-point return value.
+
+ MOVE_DF_RET(D, T)
+ Likewise a double-precision floating-point return value.
+
+ MOVE_DC_RET(D, T)
+ Likewise a complex double-precision floating-point return value.
+
+ MOVE_SI_RET(D, T)
+ Likewise a single-precision integer return value.
+
+ The D argument is "t" to move to FPRs and "f" to move from FPRs.
+ The return macros may assume that the target of the jump does not
+ use a floating-point register. */
+
+#define MOVE_SF_RET(D, T) DELAY##D (T, m##D##c1 $2,$f0)
+#define MOVE_SI_RET(D, T) DELAY##D (T, m##D##c1 $2,$f0)
+
+#if defined(__mips64) && defined(__MIPSEB__)
+#define MOVE_SC_RET(D, T) MERGE_GPR##D ($2, $f0, $f1); jr T
+#elif defined(__mips64)
+/* The high 32 bits of $2 correspond to the second word in memory;
+ i.e. the imaginary part. */
+#define MOVE_SC_RET(D, T) MERGE_GPR##D ($2, $f1, $f0); jr T
+#elif __mips_fpr == 64
+#define MOVE_SC_RET(D, T) m##D##c1 $2,$f0; DELAY##D (T, m##D##c1 $3,$f1)
+#else
+#define MOVE_SC_RET(D, T) m##D##c1 $2,$f0; DELAY##D (T, m##D##c1 $3,$f2)
+#endif
+
+#if defined(__mips64)
+#define MOVE_SF_BYTE0(D) m##D##c1 $4,$f12
+#define MOVE_SF_BYTE4(D) m##D##c1 $5,$f13
+#define MOVE_SF_BYTE8(D) m##D##c1 $5,$f13
+#else
+#define MOVE_SF_BYTE0(D) m##D##c1 $4,$f12
+#define MOVE_SF_BYTE4(D) m##D##c1 $5,$f14
+#define MOVE_SF_BYTE8(D) m##D##c1 $6,$f14
+#endif
+#define MOVE_SI_BYTE0(D) MOVE_SF_BYTE0(D)
+
+#if defined(__mips64)
+#define MOVE_DF_BYTE0(D) dm##D##c1 $4,$f12
+#define MOVE_DF_BYTE8(D) dm##D##c1 $5,$f13
+#define MOVE_DF_RET(D, T) DELAY##D (T, dm##D##c1 $2,$f0)
+#define MOVE_DC_RET(D, T) dm##D##c1 $3,$f1; MOVE_DF_RET (D, T)
+#elif __mips_fpr == 64 && defined(__MIPSEB__)
+#define MOVE_DF_BYTE0(D) m##D##c1 $5,$f12; m##D##hc1 $4,$f12
+#define MOVE_DF_BYTE8(D) m##D##c1 $7,$f14; m##D##hc1 $6,$f14
+#define MOVE_DF_RET(D, T) m##D##c1 $3,$f0; DELAY##D (T, m##D##hc1 $2,$f0)
+#define MOVE_DC_RET(D, T) m##D##c1 $5,$f1; m##D##hc1 $4,$f1; MOVE_DF_RET (D, T)
+#elif __mips_fpr == 64
+#define MOVE_DF_BYTE0(D) m##D##c1 $4,$f12; m##D##hc1 $5,$f12
+#define MOVE_DF_BYTE8(D) m##D##c1 $6,$f14; m##D##hc1 $7,$f14
+#define MOVE_DF_RET(D, T) m##D##c1 $2,$f0; DELAY##D (T, m##D##hc1 $3,$f0)
+#define MOVE_DC_RET(D, T) m##D##c1 $4,$f1; m##D##hc1 $5,$f1; MOVE_DF_RET (D, T)
+#elif defined(__MIPSEB__)
+/* FPRs are little-endian. */
+#define MOVE_DF_BYTE0(D) m##D##c1 $4,$f13; m##D##c1 $5,$f12
+#define MOVE_DF_BYTE8(D) m##D##c1 $6,$f15; m##D##c1 $7,$f14
+#define MOVE_DF_RET(D, T) m##D##c1 $2,$f1; DELAY##D (T, m##D##c1 $3,$f0)
+#define MOVE_DC_RET(D, T) m##D##c1 $4,$f3; m##D##c1 $5,$f2; MOVE_DF_RET (D, T)
+#else
+#define MOVE_DF_BYTE0(D) m##D##c1 $4,$f12; m##D##c1 $5,$f13
+#define MOVE_DF_BYTE8(D) m##D##c1 $6,$f14; m##D##c1 $7,$f15
+#define MOVE_DF_RET(D, T) m##D##c1 $2,$f0; DELAY##D (T, m##D##c1 $3,$f1)
+#define MOVE_DC_RET(D, T) m##D##c1 $4,$f2; m##D##c1 $5,$f3; MOVE_DF_RET (D, T)
+#endif
+
+/* Single-precision math. */
+
+/* Define a function NAME that loads two single-precision values,
+ performs FPU operation OPCODE on them, and returns the single-
+ precision result. */
+
+#define OPSF3(NAME, OPCODE) \
+STARTFN (NAME); \
+ MOVE_SF_BYTE0 (t); \
+ MOVE_SF_BYTE4 (t); \
+ OPCODE RET,ARG1,ARG2; \
+ MOVE_SF_RET (f, $31); \
+ ENDFN (NAME)
+
+#ifdef L_m16addsf3
+OPSF3 (__mips16_addsf3, add.s)
+#endif
+#ifdef L_m16subsf3
+OPSF3 (__mips16_subsf3, sub.s)
+#endif
+#ifdef L_m16mulsf3
+OPSF3 (__mips16_mulsf3, mul.s)
+#endif
+#ifdef L_m16divsf3
+OPSF3 (__mips16_divsf3, div.s)
+#endif
+
+/* Define a function NAME that loads a single-precision value,
+ performs FPU operation OPCODE on it, and returns the single-
+ precision result. */
+
+#define OPSF2(NAME, OPCODE) \
+STARTFN (NAME); \
+ MOVE_SF_BYTE0 (t); \
+ OPCODE RET,ARG1; \
+ MOVE_SF_RET (f, $31); \
+ ENDFN (NAME)
+
+#ifdef L_m16negsf2
+OPSF2 (__mips16_negsf2, neg.s)
+#endif
+#ifdef L_m16abssf2
+OPSF2 (__mips16_abssf2, abs.s)
+#endif
+
+/* Single-precision comparisons. */
+
+/* Define a function NAME that loads two single-precision values,
+ performs floating point comparison OPCODE, and returns TRUE or
+ FALSE depending on the result. */
+
+#define CMPSF(NAME, OPCODE, TRUE, FALSE) \
+STARTFN (NAME); \
+ MOVE_SF_BYTE0 (t); \
+ MOVE_SF_BYTE4 (t); \
+ OPCODE ARG1,ARG2; \
+ li $2,TRUE; \
+ bc1t 1f; \
+ li $2,FALSE; \
+1:; \
+ j $31; \
+ ENDFN (NAME)
+
+/* Like CMPSF, but reverse the comparison operands. */
+
+#define REVCMPSF(NAME, OPCODE, TRUE, FALSE) \
+STARTFN (NAME); \
+ MOVE_SF_BYTE0 (t); \
+ MOVE_SF_BYTE4 (t); \
+ OPCODE ARG2,ARG1; \
+ li $2,TRUE; \
+ bc1t 1f; \
+ li $2,FALSE; \
+1:; \
+ j $31; \
+ ENDFN (NAME)
+
+#ifdef L_m16eqsf2
+CMPSF (__mips16_eqsf2, c.eq.s, 0, 1)
+#endif
+#ifdef L_m16nesf2
+CMPSF (__mips16_nesf2, c.eq.s, 0, 1)
+#endif
+#ifdef L_m16gtsf2
+REVCMPSF (__mips16_gtsf2, c.lt.s, 1, 0)
+#endif
+#ifdef L_m16gesf2
+REVCMPSF (__mips16_gesf2, c.le.s, 0, -1)
+#endif
+#ifdef L_m16lesf2
+CMPSF (__mips16_lesf2, c.le.s, 0, 1)
+#endif
+#ifdef L_m16ltsf2
+CMPSF (__mips16_ltsf2, c.lt.s, -1, 0)
+#endif
+#ifdef L_m16unordsf2
+CMPSF(__mips16_unordsf2, c.un.s, 1, 0)
+#endif
+
+
+/* Single-precision conversions. */
+
+#ifdef L_m16fltsisf
+STARTFN (__mips16_floatsisf)
+ MOVE_SF_BYTE0 (t)
+ cvt.s.w RET,ARG1
+ MOVE_SF_RET (f, $31)
+ ENDFN (__mips16_floatsisf)
+#endif
+
+#ifdef L_m16fltunsisf
+STARTFN (__mips16_floatunsisf)
+ .set noreorder
+ bltz $4,1f
+ MOVE_SF_BYTE0 (t)
+ .set reorder
+ cvt.s.w RET,ARG1
+ MOVE_SF_RET (f, $31)
+1:
+ and $2,$4,1
+ srl $3,$4,1
+ or $2,$2,$3
+ mtc1 $2,RET
+ cvt.s.w RET,RET
+ add.s RET,RET,RET
+ MOVE_SF_RET (f, $31)
+ ENDFN (__mips16_floatunsisf)
+#endif
+
+#ifdef L_m16fix_truncsfsi
+STARTFN (__mips16_fix_truncsfsi)
+ MOVE_SF_BYTE0 (t)
+ trunc.w.s RET,ARG1,$4
+ MOVE_SI_RET (f, $31)
+ ENDFN (__mips16_fix_truncsfsi)
+#endif
+
+#if !defined(__mips_single_float) && !defined(__SINGLE_FLOAT)
+
+/* Double-precision math. */
+
+/* Define a function NAME that loads two double-precision values,
+ performs FPU operation OPCODE on them, and returns the double-
+ precision result. */
+
+#define OPDF3(NAME, OPCODE) \
+STARTFN (NAME); \
+ MOVE_DF_BYTE0 (t); \
+ MOVE_DF_BYTE8 (t); \
+ OPCODE RET,ARG1,ARG2; \
+ MOVE_DF_RET (f, $31); \
+ ENDFN (NAME)
+
+#ifdef L_m16adddf3
+OPDF3 (__mips16_adddf3, add.d)
+#endif
+#ifdef L_m16subdf3
+OPDF3 (__mips16_subdf3, sub.d)
+#endif
+#ifdef L_m16muldf3
+OPDF3 (__mips16_muldf3, mul.d)
+#endif
+#ifdef L_m16divdf3
+OPDF3 (__mips16_divdf3, div.d)
+#endif
+
+/* Define a function NAME that loads a double-precision value,
+ performs FPU operation OPCODE on it, and returns the double-
+ precision result. */
+
+#define OPDF2(NAME, OPCODE) \
+STARTFN (NAME); \
+ MOVE_DF_BYTE0 (t); \
+ OPCODE RET,ARG1; \
+ MOVE_DF_RET (f, $31); \
+ ENDFN (NAME)
+
+#ifdef L_m16negdf2
+OPDF2 (__mips16_negdf2, neg.d)
+#endif
+#ifdef L_m16absdf2
+OPDF2 (__mips16_absdf2, abs.d)
+#endif
+
+/* Conversions between single and double precision. */
+
+#ifdef L_m16extsfdf2
+STARTFN (__mips16_extendsfdf2)
+ MOVE_SF_BYTE0 (t)
+ cvt.d.s RET,ARG1
+ MOVE_DF_RET (f, $31)
+ ENDFN (__mips16_extendsfdf2)
+#endif
+
+#ifdef L_m16trdfsf2
+STARTFN (__mips16_truncdfsf2)
+ MOVE_DF_BYTE0 (t)
+ cvt.s.d RET,ARG1
+ MOVE_SF_RET (f, $31)
+ ENDFN (__mips16_truncdfsf2)
+#endif
+
+/* Double-precision comparisons. */
+
+/* Define a function NAME that loads two double-precision values,
+ performs floating point comparison OPCODE, and returns TRUE or
+ FALSE depending on the result. */
+
+#define CMPDF(NAME, OPCODE, TRUE, FALSE) \
+STARTFN (NAME); \
+ MOVE_DF_BYTE0 (t); \
+ MOVE_DF_BYTE8 (t); \
+ OPCODE ARG1,ARG2; \
+ li $2,TRUE; \
+ bc1t 1f; \
+ li $2,FALSE; \
+1:; \
+ j $31; \
+ ENDFN (NAME)
+
+/* Like CMPDF, but reverse the comparison operands. */
+
+#define REVCMPDF(NAME, OPCODE, TRUE, FALSE) \
+STARTFN (NAME); \
+ MOVE_DF_BYTE0 (t); \
+ MOVE_DF_BYTE8 (t); \
+ OPCODE ARG2,ARG1; \
+ li $2,TRUE; \
+ bc1t 1f; \
+ li $2,FALSE; \
+1:; \
+ j $31; \
+ ENDFN (NAME)
+
+#ifdef L_m16eqdf2
+CMPDF (__mips16_eqdf2, c.eq.d, 0, 1)
+#endif
+#ifdef L_m16nedf2
+CMPDF (__mips16_nedf2, c.eq.d, 0, 1)
+#endif
+#ifdef L_m16gtdf2
+REVCMPDF (__mips16_gtdf2, c.lt.d, 1, 0)
+#endif
+#ifdef L_m16gedf2
+REVCMPDF (__mips16_gedf2, c.le.d, 0, -1)
+#endif
+#ifdef L_m16ledf2
+CMPDF (__mips16_ledf2, c.le.d, 0, 1)
+#endif
+#ifdef L_m16ltdf2
+CMPDF (__mips16_ltdf2, c.lt.d, -1, 0)
+#endif
+#ifdef L_m16unorddf2
+CMPDF(__mips16_unorddf2, c.un.d, 1, 0)
+#endif
+
+/* Double-precision conversions. */
+
+#ifdef L_m16fltsidf
+STARTFN (__mips16_floatsidf)
+ MOVE_SI_BYTE0 (t)
+ cvt.d.w RET,ARG1
+ MOVE_DF_RET (f, $31)
+ ENDFN (__mips16_floatsidf)
+#endif
+
+#ifdef L_m16fltunsidf
+STARTFN (__mips16_floatunsidf)
+ MOVE_SI_BYTE0 (t)
+ cvt.d.w RET,ARG1
+ bgez $4,1f
+ li.d ARG1, 4.294967296e+9
+ add.d RET, RET, ARG1
+1: MOVE_DF_RET (f, $31)
+ ENDFN (__mips16_floatunsidf)
+#endif
+
+#ifdef L_m16fix_truncdfsi
+STARTFN (__mips16_fix_truncdfsi)
+ MOVE_DF_BYTE0 (t)
+ trunc.w.d RET,ARG1,$4
+ MOVE_SI_RET (f, $31)
+ ENDFN (__mips16_fix_truncdfsi)
+#endif
+#endif /* !__mips_single_float */
+
+/* Define a function NAME that moves a return value of mode MODE from
+ FPRs to GPRs. */
+
+#define RET_FUNCTION(NAME, MODE) \
+STARTFN (NAME); \
+ MOVE_##MODE##_RET (t, $31); \
+ ENDFN (NAME)
+
+#ifdef L_m16retsf
+RET_FUNCTION (__mips16_ret_sf, SF)
+#endif
+
+#ifdef L_m16retsc
+RET_FUNCTION (__mips16_ret_sc, SC)
+#endif
+
+#if !defined(__mips_single_float) && !defined(__SINGLE_FLOAT)
+#ifdef L_m16retdf
+RET_FUNCTION (__mips16_ret_df, DF)
+#endif
+
+#ifdef L_m16retdc
+RET_FUNCTION (__mips16_ret_dc, DC)
+#endif
+#endif /* !__mips_single_float */
+
+/* STUB_ARGS_X copies the arguments from GPRs to FPRs for argument
+ code X. X is calculated as ARG1 + ARG2 * 4, where ARG1 and ARG2
+ classify the first and second arguments as follows:
+
+ 1: a single-precision argument
+ 2: a double-precision argument
+ 0: no argument, or not one of the above. */
+
+#define STUB_ARGS_0 /* () */
+#define STUB_ARGS_1 MOVE_SF_BYTE0 (t) /* (sf) */
+#define STUB_ARGS_5 MOVE_SF_BYTE0 (t); MOVE_SF_BYTE4 (t) /* (sf, sf) */
+#define STUB_ARGS_9 MOVE_SF_BYTE0 (t); MOVE_DF_BYTE8 (t) /* (sf, df) */
+#define STUB_ARGS_2 MOVE_DF_BYTE0 (t) /* (df) */
+#define STUB_ARGS_6 MOVE_DF_BYTE0 (t); MOVE_SF_BYTE8 (t) /* (df, sf) */
+#define STUB_ARGS_10 MOVE_DF_BYTE0 (t); MOVE_DF_BYTE8 (t) /* (df, df) */
+
+/* These functions are used by 16-bit code when calling via a function
+ pointer. They must copy the floating point arguments from the GPRs
+ to FPRs and then call function $2. */
+
+#define CALL_STUB_NO_RET(NAME, CODE) \
+STARTFN (NAME); \
+ STUB_ARGS_##CODE; \
+ .set noreorder; \
+ jr $2; \
+ move $25,$2; \
+ .set reorder; \
+ ENDFN (NAME)
+
+#ifdef L_m16stub1
+CALL_STUB_NO_RET (__mips16_call_stub_1, 1)
+#endif
+
+#ifdef L_m16stub5
+CALL_STUB_NO_RET (__mips16_call_stub_5, 5)
+#endif
+
+#if !defined(__mips_single_float) && !defined(__SINGLE_FLOAT)
+
+#ifdef L_m16stub2
+CALL_STUB_NO_RET (__mips16_call_stub_2, 2)
+#endif
+
+#ifdef L_m16stub6
+CALL_STUB_NO_RET (__mips16_call_stub_6, 6)
+#endif
+
+#ifdef L_m16stub9
+CALL_STUB_NO_RET (__mips16_call_stub_9, 9)
+#endif
+
+#ifdef L_m16stub10
+CALL_STUB_NO_RET (__mips16_call_stub_10, 10)
+#endif
+#endif /* !__mips_single_float */
+
+/* Now we have the same set of functions, except that this time the
+ function being called returns an SFmode, SCmode, DFmode or DCmode
+ value; we need to instantiate a set for each case. The calling
+ function will arrange to preserve $18, so these functions are free
+ to use it to hold the return address.
+
+ Note that we do not know whether the function we are calling is 16
+ bit or 32 bit. However, it does not matter, because 16-bit
+ functions always return floating point values in both the gp and
+ the fp regs. It would be possible to check whether the function
+ being called is 16 bits, in which case the copy is unnecessary;
+ however, it's faster to always do the copy. */
+
+#define CALL_STUB_RET(NAME, CODE, MODE) \
+STARTFN (NAME); \
+ move $18,$31; \
+ STUB_ARGS_##CODE; \
+ .set noreorder; \
+ jalr $2; \
+ move $25,$2; \
+ .set reorder; \
+ MOVE_##MODE##_RET (f, $18); \
+ ENDFN (NAME)
+
+/* First, instantiate the single-float set. */
+
+#ifdef L_m16stubsf0
+CALL_STUB_RET (__mips16_call_stub_sf_0, 0, SF)
+#endif
+
+#ifdef L_m16stubsf1
+CALL_STUB_RET (__mips16_call_stub_sf_1, 1, SF)
+#endif
+
+#ifdef L_m16stubsf5
+CALL_STUB_RET (__mips16_call_stub_sf_5, 5, SF)
+#endif
+
+#if !defined(__mips_single_float) && !defined(__SINGLE_FLOAT)
+#ifdef L_m16stubsf2
+CALL_STUB_RET (__mips16_call_stub_sf_2, 2, SF)
+#endif
+
+#ifdef L_m16stubsf6
+CALL_STUB_RET (__mips16_call_stub_sf_6, 6, SF)
+#endif
+
+#ifdef L_m16stubsf9
+CALL_STUB_RET (__mips16_call_stub_sf_9, 9, SF)
+#endif
+
+#ifdef L_m16stubsf10
+CALL_STUB_RET (__mips16_call_stub_sf_10, 10, SF)
+#endif
+#endif /* !__mips_single_float */
+
+
+/* Now we have the same set of functions again, except that this time
+ the function being called returns an DFmode value. */
+
+#if !defined(__mips_single_float) && !defined(__SINGLE_FLOAT)
+#ifdef L_m16stubdf0
+CALL_STUB_RET (__mips16_call_stub_df_0, 0, DF)
+#endif
+
+#ifdef L_m16stubdf1
+CALL_STUB_RET (__mips16_call_stub_df_1, 1, DF)
+#endif
+
+#ifdef L_m16stubdf5
+CALL_STUB_RET (__mips16_call_stub_df_5, 5, DF)
+#endif
+
+#ifdef L_m16stubdf2
+CALL_STUB_RET (__mips16_call_stub_df_2, 2, DF)
+#endif
+
+#ifdef L_m16stubdf6
+CALL_STUB_RET (__mips16_call_stub_df_6, 6, DF)
+#endif
+
+#ifdef L_m16stubdf9
+CALL_STUB_RET (__mips16_call_stub_df_9, 9, DF)
+#endif
+
+#ifdef L_m16stubdf10
+CALL_STUB_RET (__mips16_call_stub_df_10, 10, DF)
+#endif
+#endif /* !__mips_single_float */
+
+
+/* Ho hum. Here we have the same set of functions again, this time
+ for when the function being called returns an SCmode value. */
+
+#ifdef L_m16stubsc0
+CALL_STUB_RET (__mips16_call_stub_sc_0, 0, SC)
+#endif
+
+#ifdef L_m16stubsc1
+CALL_STUB_RET (__mips16_call_stub_sc_1, 1, SC)
+#endif
+
+#ifdef L_m16stubsc5
+CALL_STUB_RET (__mips16_call_stub_sc_5, 5, SC)
+#endif
+
+#if !defined(__mips_single_float) && !defined(__SINGLE_FLOAT)
+#ifdef L_m16stubsc2
+CALL_STUB_RET (__mips16_call_stub_sc_2, 2, SC)
+#endif
+
+#ifdef L_m16stubsc6
+CALL_STUB_RET (__mips16_call_stub_sc_6, 6, SC)
+#endif
+
+#ifdef L_m16stubsc9
+CALL_STUB_RET (__mips16_call_stub_sc_9, 9, SC)
+#endif
+
+#ifdef L_m16stubsc10
+CALL_STUB_RET (__mips16_call_stub_sc_10, 10, SC)
+#endif
+#endif /* !__mips_single_float */
+
+
+/* Finally, another set of functions for DCmode. */
+
+#if !defined(__mips_single_float) && !defined(__SINGLE_FLOAT)
+#ifdef L_m16stubdc0
+CALL_STUB_RET (__mips16_call_stub_dc_0, 0, DC)
+#endif
+
+#ifdef L_m16stubdc1
+CALL_STUB_RET (__mips16_call_stub_dc_1, 1, DC)
+#endif
+
+#ifdef L_m16stubdc5
+CALL_STUB_RET (__mips16_call_stub_dc_5, 5, DC)
+#endif
+
+#ifdef L_m16stubdc2
+CALL_STUB_RET (__mips16_call_stub_dc_2, 2, DC)
+#endif
+
+#ifdef L_m16stubdc6
+CALL_STUB_RET (__mips16_call_stub_dc_6, 6, DC)
+#endif
+
+#ifdef L_m16stubdc9
+CALL_STUB_RET (__mips16_call_stub_dc_9, 9, DC)
+#endif
+
+#ifdef L_m16stubdc10
+CALL_STUB_RET (__mips16_call_stub_dc_10, 10, DC)
+#endif
+#endif /* !__mips_single_float */
+#endif
diff --git a/gcc/config/mips/netbsd.h b/gcc/config/mips/netbsd.h
new file mode 100644
index 000000000..82a0921ed
--- /dev/null
+++ b/gcc/config/mips/netbsd.h
@@ -0,0 +1,187 @@
+/* Definitions of target machine for GNU compiler, for MIPS NetBSD systems.
+ Copyright (C) 1993, 1995, 1996, 1997, 1999, 2000, 2001, 2002, 2003, 2004,
+ 2007, 2010, 2011 Free Software Foundation, 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/>. */
+
+
+/* Define default target values. */
+
+#undef MACHINE_TYPE
+#if TARGET_ENDIAN_DEFAULT != 0
+#define MACHINE_TYPE "NetBSD/mipseb ELF"
+#else
+#define MACHINE_TYPE "NetBSD/mipsel ELF"
+#endif
+
+#define TARGET_OS_CPP_BUILTINS() \
+ do \
+ { \
+ NETBSD_OS_CPP_BUILTINS_ELF(); \
+ builtin_define ("__NO_LEADING_UNDERSCORES__"); \
+ builtin_define ("__GP_SUPPORT__"); \
+ if (TARGET_LONG64) \
+ builtin_define ("__LONG64"); \
+ \
+ if (TARGET_ABICALLS) \
+ builtin_define ("__ABICALLS__"); \
+ \
+ if (mips_abi == ABI_EABI) \
+ builtin_define ("__mips_eabi"); \
+ else if (mips_abi == ABI_N32) \
+ builtin_define ("__mips_n32"); \
+ else if (mips_abi == ABI_64) \
+ builtin_define ("__mips_n64"); \
+ else if (mips_abi == ABI_O64) \
+ builtin_define ("__mips_o64"); \
+ } \
+ while (0)
+
+/* The generic MIPS TARGET_CPU_CPP_BUILTINS are incorrect for NetBSD.
+ Specifically, they define too many namespace-invasive macros. Override
+ them here. Note this is structured for easy comparison to the version
+ in mips.h.
+
+ FIXME: This probably isn't the best solution. But in the absence
+ of something better, it will have to do, for now. */
+
+#undef TARGET_CPU_CPP_BUILTINS
+#define TARGET_CPU_CPP_BUILTINS() \
+ do \
+ { \
+ builtin_assert ("cpu=mips"); \
+ builtin_define ("__mips__"); \
+ builtin_define ("_mips"); \
+ \
+ /* No _R3000 or _R4000. */ \
+ if (TARGET_64BIT) \
+ builtin_define ("__mips64"); \
+ \
+ if (TARGET_FLOAT64) \
+ builtin_define ("__mips_fpr=64"); \
+ else \
+ builtin_define ("__mips_fpr=32"); \
+ \
+ if (TARGET_MIPS16) \
+ builtin_define ("__mips16"); \
+ \
+ MIPS_CPP_SET_PROCESSOR ("_MIPS_ARCH", mips_arch_info); \
+ MIPS_CPP_SET_PROCESSOR ("_MIPS_TUNE", mips_tune_info); \
+ \
+ if (ISA_MIPS1) \
+ builtin_define ("__mips=1"); \
+ else if (ISA_MIPS2) \
+ builtin_define ("__mips=2"); \
+ else if (ISA_MIPS3) \
+ builtin_define ("__mips=3"); \
+ else if (ISA_MIPS4) \
+ builtin_define ("__mips=4"); \
+ else if (ISA_MIPS32) \
+ { \
+ builtin_define ("__mips=32"); \
+ builtin_define ("__mips_isa_rev=1"); \
+ } \
+ else if (ISA_MIPS32R2) \
+ { \
+ builtin_define ("__mips=32"); \
+ builtin_define ("__mips_isa_rev=2"); \
+ } \
+ else if (ISA_MIPS64) \
+ { \
+ builtin_define ("__mips=64"); \
+ builtin_define ("__mips_isa_rev=1"); \
+ } \
+ \
+ if (TARGET_HARD_FLOAT) \
+ builtin_define ("__mips_hard_float"); \
+ else if (TARGET_SOFT_FLOAT) \
+ builtin_define ("__mips_soft_float"); \
+ \
+ if (TARGET_SINGLE_FLOAT) \
+ builtin_define ("__mips_single_float"); \
+ \
+ if (TARGET_BIG_ENDIAN) \
+ builtin_define ("__MIPSEB__"); \
+ else \
+ builtin_define ("__MIPSEL__"); \
+ \
+ /* No language dialect defines. */ \
+ \
+ /* ABIs handled in TARGET_OS_CPP_BUILTINS. */ \
+ } \
+ while (0)
+
+
+/* Extra specs we need. */
+#undef SUBTARGET_EXTRA_SPECS
+#define SUBTARGET_EXTRA_SPECS \
+ { "netbsd_cpp_spec", NETBSD_CPP_SPEC }, \
+ { "netbsd_link_spec", NETBSD_LINK_SPEC_ELF }, \
+ { "netbsd_entry_point", NETBSD_ENTRY_POINT },
+
+/* Provide a SUBTARGET_CPP_SPEC appropriate for NetBSD. */
+
+#undef SUBTARGET_CPP_SPEC
+#define SUBTARGET_CPP_SPEC "%(netbsd_cpp_spec)"
+
+/* Provide a LINK_SPEC appropriate for a NetBSD/mips target.
+ This is a copy of LINK_SPEC from <netbsd-elf.h> tweaked for
+ the MIPS target. */
+
+#undef LINK_SPEC
+#define LINK_SPEC \
+ "%{EL:-m elf32lmip} \
+ %{EB:-m elf32bmip} \
+ %(endian_spec) \
+ %{G*} %{mips1} %{mips2} %{mips3} %{mips4} %{mips32} %{mips32r2} %{mips64} \
+ %(netbsd_link_spec)"
+
+#define NETBSD_ENTRY_POINT "__start"
+
+#undef SUBTARGET_ASM_SPEC
+#define SUBTARGET_ASM_SPEC \
+ "%{!mno-abicalls: \
+ %{!fno-PIC:%{!fno-pic:-KPIC}}}"
+
+
+/* -G is incompatible with -KPIC which is the default, so only allow objects
+ in the small data section if the user explicitly asks for it. */
+
+#undef MIPS_DEFAULT_GVALUE
+#define MIPS_DEFAULT_GVALUE 0
+
+
+#undef ASM_FINAL_SPEC
+#undef SET_ASM_OP
+
+
+/* NetBSD hasn't historically provided _flush_cache(), but rather
+ _cacheflush(), which takes the same arguments as the former. */
+#undef CACHE_FLUSH_FUNC
+#define CACHE_FLUSH_FUNC "_cacheflush"
+
+
+/* Make gcc agree with <machine/ansi.h> */
+
+#undef WCHAR_TYPE
+#define WCHAR_TYPE "int"
+
+#undef WCHAR_TYPE_SIZE
+#define WCHAR_TYPE_SIZE 32
+
+#undef WINT_TYPE
+#define WINT_TYPE "int"
diff --git a/gcc/config/mips/octeon.md b/gcc/config/mips/octeon.md
new file mode 100644
index 000000000..0d94e6eec
--- /dev/null
+++ b/gcc/config/mips/octeon.md
@@ -0,0 +1,88 @@
+;; Octeon pipeline description.
+;; Copyright (C) 2008
+;; Free Software Foundation, 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/>.
+;; Copyright (C) 2004, 2005, 2006 Cavium Networks.
+
+
+;; Octeon is a dual-issue processor that can issue all instructions on
+;; pipe0 and a subset on pipe1.
+
+(define_automaton "octeon_main, octeon_mult")
+
+(define_cpu_unit "octeon_pipe0" "octeon_main")
+(define_cpu_unit "octeon_pipe1" "octeon_main")
+(define_cpu_unit "octeon_mult" "octeon_mult")
+
+(define_insn_reservation "octeon_arith" 1
+ (and (eq_attr "cpu" "octeon")
+ (eq_attr "type" "arith,const,logical,move,shift,signext,slt,nop"))
+ "octeon_pipe0 | octeon_pipe1")
+
+(define_insn_reservation "octeon_condmove" 2
+ (and (eq_attr "cpu" "octeon")
+ (eq_attr "type" "condmove"))
+ "octeon_pipe0 | octeon_pipe1")
+
+(define_insn_reservation "octeon_load" 2
+ (and (eq_attr "cpu" "octeon")
+ (eq_attr "type" "load,prefetch,mtc,mfc"))
+ "octeon_pipe0")
+
+(define_insn_reservation "octeon_store" 1
+ (and (eq_attr "cpu" "octeon")
+ (eq_attr "type" "store"))
+ "octeon_pipe0")
+
+(define_insn_reservation "octeon_brj" 1
+ (and (eq_attr "cpu" "octeon")
+ (eq_attr "type" "branch,jump,call,trap"))
+ "octeon_pipe0")
+
+(define_insn_reservation "octeon_imul3" 5
+ (and (eq_attr "cpu" "octeon")
+ (eq_attr "type" "imul3,pop,clz"))
+ "(octeon_pipe0 | octeon_pipe1) + octeon_mult")
+
+(define_insn_reservation "octeon_imul" 2
+ (and (eq_attr "cpu" "octeon")
+ (eq_attr "type" "imul,mthilo"))
+ "(octeon_pipe0 | octeon_pipe1) + octeon_mult, octeon_mult")
+
+(define_insn_reservation "octeon_mfhilo" 5
+ (and (eq_attr "cpu" "octeon")
+ (eq_attr "type" "mfhilo"))
+ "(octeon_pipe0 | octeon_pipe1) + octeon_mult")
+
+(define_insn_reservation "octeon_imadd" 4
+ (and (eq_attr "cpu" "octeon")
+ (eq_attr "type" "imadd"))
+ "(octeon_pipe0 | octeon_pipe1) + octeon_mult, octeon_mult*3")
+
+(define_insn_reservation "octeon_idiv" 72
+ (and (eq_attr "cpu" "octeon")
+ (eq_attr "type" "idiv"))
+ "(octeon_pipe0 | octeon_pipe1) + octeon_mult, octeon_mult*71")
+
+;; Assume both pipes are needed for unknown and multiple-instruction
+;; patterns.
+
+(define_insn_reservation "octeon_unknown" 1
+ (and (eq_attr "cpu" "octeon")
+ (eq_attr "type" "unknown,multi"))
+ "octeon_pipe0 + octeon_pipe1")
diff --git a/gcc/config/mips/openbsd.h b/gcc/config/mips/openbsd.h
new file mode 100644
index 000000000..a392ca470
--- /dev/null
+++ b/gcc/config/mips/openbsd.h
@@ -0,0 +1,101 @@
+/* Configuration for a MIPS ABI32 OpenBSD target.
+ Copyright (C) 1999, 2003, 2004, 2007, 2008, 2009, 2010, 2011
+ Free Software Foundation, 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/>. */
+
+/* Definitions needed for OpenBSD, to avoid picking mips 'defaults'. */
+
+/* GAS must know this. */
+#undef SUBTARGET_ASM_SPEC
+#define SUBTARGET_ASM_SPEC "%{fPIC|fPIE:-KPIC}"
+
+/* CPP specific OpenBSD specs. */
+#undef SUBTARGET_CPP_SPEC
+#define SUBTARGET_CPP_SPEC OBSD_CPP_SPEC
+
+/* Needed for ELF (inspired by netbsd-elf). */
+#undef LOCAL_LABEL_PREFIX
+#define LOCAL_LABEL_PREFIX "."
+
+/* The profiling lib spec here is not really correct but we leave
+ it as it is until we have some kind of profiling working. */
+#define LIB_SPEC OBSD_LIB_SPEC
+
+/* mips assembler uses .set for arcane purposes. __attribute__((alias))
+ and friends won't work until we get recent binutils with .weakext
+ support. */
+#undef SET_ASM_OP
+
+#define TARGET_OS_CPP_BUILTINS() \
+ do { \
+ builtin_define ("__unix__"); \
+ builtin_define ("__SYSTYPE_BSD__"); \
+ builtin_define ("__NO_LEADING_UNDERSCORES__"); \
+ builtin_define ("__GP_SUPPORT__"); \
+ builtin_define ("__OpenBSD__"); \
+ builtin_assert ("system=unix"); \
+ builtin_assert ("system=OpenBSD"); \
+} while (0)
+
+/* Layout of source language data types. */
+
+/* This must agree with <machine/ansi.h>. */
+#undef SIZE_TYPE
+#define SIZE_TYPE "long unsigned int"
+
+#undef PTRDIFF_TYPE
+#define PTRDIFF_TYPE "long int"
+
+#undef WCHAR_TYPE
+#define WCHAR_TYPE "int"
+
+#undef WCHAR_TYPE_SIZE
+#define WCHAR_TYPE_SIZE 32
+
+#undef WINT_TYPE
+#define WINT_TYPE "int"
+
+/* Controlling the compilation driver. */
+
+/* LINK_SPEC appropriate for OpenBSD: support for GCC options
+ -static, -assert, and -nostdlib. Dynamic loader control. */
+#undef LINK_SPEC
+#define LINK_SPEC \
+ "%{G*} %{EB} %{EL} %{mips1} %{mips2} %{mips3} \
+ %{shared} \
+ %{!shared: -non_shared} \
+ -dynamic-linker /usr/libexec/ld.so \
+ %{!nostdlib:%{!r:%{!e*:-e __start}}} -dc -dp \
+ %{static:-Bstatic} %{!static:-Bdynamic} %{assert*}"
+
+/* -G is incompatible with -KPIC which is the default, so only allow objects
+ in the small data section if the user explicitly asks for it. */
+#undef MIPS_DEFAULT_GVALUE
+#define MIPS_DEFAULT_GVALUE 0
+
+
+/* Since gas and gld are standard on OpenBSD, we don't need these. */
+#undef ASM_FINAL_SPEC
+#undef STARTFILE_SPEC
+
+/* Switch into a generic section. */
+#undef TARGET_ASM_NAMED_SECTION
+#define TARGET_ASM_NAMED_SECTION default_elf_asm_named_section
+
+/* MIPS specific debugging info */
+#define MIPS_DEBUGGING_INFO 1
diff --git a/gcc/config/mips/predicates.md b/gcc/config/mips/predicates.md
new file mode 100644
index 000000000..7430dd32b
--- /dev/null
+++ b/gcc/config/mips/predicates.md
@@ -0,0 +1,342 @@
+;; Predicate definitions for MIPS.
+;; Copyright (C) 2004, 2007, 2008 Free Software Foundation, 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/>.
+
+(define_predicate "const_uns_arith_operand"
+ (and (match_code "const_int")
+ (match_test "SMALL_OPERAND_UNSIGNED (INTVAL (op))")))
+
+(define_predicate "uns_arith_operand"
+ (ior (match_operand 0 "const_uns_arith_operand")
+ (match_operand 0 "register_operand")))
+
+(define_predicate "const_arith_operand"
+ (and (match_code "const_int")
+ (match_test "SMALL_OPERAND (INTVAL (op))")))
+
+(define_predicate "arith_operand"
+ (ior (match_operand 0 "const_arith_operand")
+ (match_operand 0 "register_operand")))
+
+(define_predicate "const_uimm6_operand"
+ (and (match_code "const_int")
+ (match_test "UIMM6_OPERAND (INTVAL (op))")))
+
+(define_predicate "const_imm10_operand"
+ (and (match_code "const_int")
+ (match_test "IMM10_OPERAND (INTVAL (op))")))
+
+(define_predicate "reg_imm10_operand"
+ (ior (match_operand 0 "const_imm10_operand")
+ (match_operand 0 "register_operand")))
+
+(define_predicate "sle_operand"
+ (and (match_code "const_int")
+ (match_test "SMALL_OPERAND (INTVAL (op) + 1)")))
+
+(define_predicate "sleu_operand"
+ (and (match_operand 0 "sle_operand")
+ (match_test "INTVAL (op) + 1 != 0")))
+
+(define_predicate "const_0_operand"
+ (and (match_code "const_int,const_double,const_vector")
+ (match_test "op == CONST0_RTX (GET_MODE (op))")))
+
+(define_predicate "reg_or_0_operand"
+ (ior (and (match_operand 0 "const_0_operand")
+ (match_test "!TARGET_MIPS16"))
+ (match_operand 0 "register_operand")))
+
+(define_predicate "const_1_operand"
+ (and (match_code "const_int,const_double,const_vector")
+ (match_test "op == CONST1_RTX (GET_MODE (op))")))
+
+(define_predicate "reg_or_1_operand"
+ (ior (match_operand 0 "const_1_operand")
+ (match_operand 0 "register_operand")))
+
+;; This is used for indexing into vectors, and hence only accepts const_int.
+(define_predicate "const_0_or_1_operand"
+ (and (match_code "const_int")
+ (ior (match_test "op == CONST0_RTX (GET_MODE (op))")
+ (match_test "op == CONST1_RTX (GET_MODE (op))"))))
+
+(define_predicate "qi_mask_operand"
+ (and (match_code "const_int")
+ (match_test "UINTVAL (op) == 0xff")))
+
+(define_predicate "hi_mask_operand"
+ (and (match_code "const_int")
+ (match_test "UINTVAL (op) == 0xffff")))
+
+(define_predicate "si_mask_operand"
+ (and (match_code "const_int")
+ (match_test "UINTVAL (op) == 0xffffffff")))
+
+(define_predicate "and_load_operand"
+ (ior (match_operand 0 "qi_mask_operand")
+ (match_operand 0 "hi_mask_operand")
+ (match_operand 0 "si_mask_operand")))
+
+(define_predicate "low_bitmask_operand"
+ (and (match_test "ISA_HAS_EXT_INS")
+ (match_code "const_int")
+ (match_test "low_bitmask_len (mode, INTVAL (op)) > 16")))
+
+(define_predicate "and_reg_operand"
+ (ior (match_operand 0 "register_operand")
+ (and (match_test "!TARGET_MIPS16")
+ (match_operand 0 "const_uns_arith_operand"))
+ (match_operand 0 "low_bitmask_operand")
+ (match_operand 0 "si_mask_operand")))
+
+(define_predicate "and_operand"
+ (ior (match_operand 0 "and_load_operand")
+ (match_operand 0 "and_reg_operand")))
+
+(define_predicate "d_operand"
+ (and (match_code "reg")
+ (match_test "TARGET_MIPS16
+ ? M16_REG_P (REGNO (op))
+ : GP_REG_P (REGNO (op))")))
+
+(define_predicate "lo_operand"
+ (and (match_code "reg")
+ (match_test "REGNO (op) == LO_REGNUM")))
+
+(define_predicate "hilo_operand"
+ (and (match_code "reg")
+ (match_test "MD_REG_P (REGNO (op))")))
+
+(define_predicate "fcc_reload_operand"
+ (and (match_code "reg,subreg")
+ (match_test "ST_REG_P (true_regnum (op))")))
+
+(define_special_predicate "pc_or_label_operand"
+ (match_code "pc,label_ref"))
+
+(define_predicate "const_call_insn_operand"
+ (match_code "const,symbol_ref,label_ref")
+{
+ enum mips_symbol_type symbol_type;
+
+ if (!mips_symbolic_constant_p (op, SYMBOL_CONTEXT_CALL, &symbol_type))
+ return false;
+
+ switch (symbol_type)
+ {
+ case SYMBOL_ABSOLUTE:
+ /* We can only use direct calls if we're sure that the target
+ function does not need $25 to be valid on entry. */
+ if (mips_use_pic_fn_addr_reg_p (op))
+ return false;
+
+ /* If -mlong-calls or if this function has an explicit long_call
+ attribute, we must use register addressing. The
+ SYMBOL_FLAG_LONG_CALL bit is set by mips_encode_section_info. */
+ return !(GET_CODE (op) == SYMBOL_REF && SYMBOL_REF_LONG_CALL_P (op));
+
+ case SYMBOL_GOT_DISP:
+ /* Without explicit relocs, there is no special syntax for
+ loading the address of a call destination into a register.
+ Using "la $25,foo; jal $25" would prevent the lazy binding
+ of "foo", so keep the address of global symbols with the
+ jal macro. */
+ return !TARGET_EXPLICIT_RELOCS;
+
+ default:
+ return false;
+ }
+})
+
+(define_predicate "call_insn_operand"
+ (ior (match_operand 0 "const_call_insn_operand")
+ (match_operand 0 "register_operand")))
+
+;; A legitimate CONST_INT operand that takes more than one instruction
+;; to load.
+(define_predicate "splittable_const_int_operand"
+ (match_code "const_int")
+{
+ /* When generating mips16 code, LEGITIMATE_CONSTANT_P rejects
+ CONST_INTs that can't be loaded using simple insns. */
+ if (TARGET_MIPS16)
+ return false;
+
+ /* Don't handle multi-word moves this way; we don't want to introduce
+ the individual word-mode moves until after reload. */
+ if (GET_MODE_SIZE (mode) > UNITS_PER_WORD)
+ return false;
+
+ /* Otherwise check whether the constant can be loaded in a single
+ instruction. */
+ return !LUI_INT (op) && !SMALL_INT (op) && !SMALL_INT_UNSIGNED (op);
+})
+
+(define_predicate "move_operand"
+ (match_operand 0 "general_operand")
+{
+ enum mips_symbol_type symbol_type;
+
+ /* The thinking here is as follows:
+
+ (1) The move expanders should split complex load sequences into
+ individual instructions. Those individual instructions can
+ then be optimized by all rtl passes.
+
+ (2) The target of pre-reload load sequences should not be used
+ to store temporary results. If the target register is only
+ assigned one value, reload can rematerialize that value
+ on demand, rather than spill it to the stack.
+
+ (3) If we allowed pre-reload passes like combine and cse to recreate
+ complex load sequences, we would want to be able to split the
+ sequences before reload as well, so that the pre-reload scheduler
+ can see the individual instructions. This falls foul of (2);
+ the splitter would be forced to reuse the target register for
+ intermediate results.
+
+ (4) We want to define complex load splitters for combine. These
+ splitters can request a temporary scratch register, which avoids
+ the problem in (2). They allow things like:
+
+ (set (reg T1) (high SYM))
+ (set (reg T2) (low (reg T1) SYM))
+ (set (reg X) (plus (reg T2) (const_int OFFSET)))
+
+ to be combined into:
+
+ (set (reg T3) (high SYM+OFFSET))
+ (set (reg X) (lo_sum (reg T3) SYM+OFFSET))
+
+ if T2 is only used this once. */
+ switch (GET_CODE (op))
+ {
+ case CONST_INT:
+ return !splittable_const_int_operand (op, mode);
+
+ case CONST:
+ case SYMBOL_REF:
+ case LABEL_REF:
+ if (CONST_GP_P (op))
+ return true;
+ return (mips_symbolic_constant_p (op, SYMBOL_CONTEXT_LEA, &symbol_type)
+ && !mips_split_p[symbol_type]);
+
+ case HIGH:
+ op = XEXP (op, 0);
+ return (mips_symbolic_constant_p (op, SYMBOL_CONTEXT_LEA, &symbol_type)
+ && !mips_split_hi_p[symbol_type]);
+
+ default:
+ return true;
+ }
+})
+
+(define_predicate "cprestore_save_slot_operand"
+ (and (match_code "mem")
+ (match_test "mips_cprestore_address_p (XEXP (op, 0), false)")))
+
+(define_predicate "cprestore_load_slot_operand"
+ (and (match_code "mem")
+ (match_test "mips_cprestore_address_p (XEXP (op, 0), true)")))
+
+(define_predicate "consttable_operand"
+ (match_test "CONSTANT_P (op)"))
+
+(define_predicate "symbolic_operand"
+ (match_code "const,symbol_ref,label_ref")
+{
+ enum mips_symbol_type type;
+ return mips_symbolic_constant_p (op, SYMBOL_CONTEXT_LEA, &type);
+})
+
+(define_predicate "absolute_symbolic_operand"
+ (match_code "const,symbol_ref,label_ref")
+{
+ enum mips_symbol_type type;
+ return (mips_symbolic_constant_p (op, SYMBOL_CONTEXT_LEA, &type)
+ && type == SYMBOL_ABSOLUTE);
+})
+
+(define_predicate "force_to_mem_operand"
+ (match_code "const,symbol_ref,label_ref")
+{
+ enum mips_symbol_type symbol_type;
+ return (mips_symbolic_constant_p (op, SYMBOL_CONTEXT_LEA, &symbol_type)
+ && symbol_type == SYMBOL_FORCE_TO_MEM);
+})
+
+(define_predicate "got_disp_operand"
+ (match_code "const,symbol_ref,label_ref")
+{
+ enum mips_symbol_type type;
+ return (mips_symbolic_constant_p (op, SYMBOL_CONTEXT_LEA, &type)
+ && type == SYMBOL_GOT_DISP);
+})
+
+(define_predicate "got_page_ofst_operand"
+ (match_code "const,symbol_ref,label_ref")
+{
+ enum mips_symbol_type type;
+ return (mips_symbolic_constant_p (op, SYMBOL_CONTEXT_LEA, &type)
+ && type == SYMBOL_GOT_PAGE_OFST);
+})
+
+(define_predicate "symbol_ref_operand"
+ (match_code "symbol_ref"))
+
+(define_predicate "stack_operand"
+ (and (match_code "mem")
+ (match_test "mips_stack_address_p (XEXP (op, 0), GET_MODE (op))")))
+
+(define_predicate "macc_msac_operand"
+ (ior (and (match_code "plus") (match_test "ISA_HAS_MACC"))
+ (and (match_code "minus") (match_test "ISA_HAS_MSAC")))
+{
+ rtx mult = XEXP (op, GET_CODE (op) == PLUS ? 0 : 1);
+ rtx accum = XEXP (op, GET_CODE (op) == PLUS ? 1 : 0);
+ return (GET_CODE (mult) == MULT
+ && REG_P (XEXP (mult, 0))
+ && REG_P (XEXP (mult, 1))
+ && REG_P (accum));
+})
+
+
+(define_predicate "equality_operator"
+ (match_code "eq,ne"))
+
+(define_predicate "extend_operator"
+ (match_code "zero_extend,sign_extend"))
+
+(define_predicate "trap_comparison_operator"
+ (match_code "eq,ne,lt,ltu,ge,geu"))
+
+(define_predicate "order_operator"
+ (match_code "lt,ltu,le,leu,ge,geu,gt,gtu"))
+
+;; For NE, cstore uses sltu instructions in which the first operand is $0.
+;; This isn't possible in mips16 code.
+
+(define_predicate "mips_cstore_operator"
+ (ior (match_code "eq,gt,gtu,ge,geu,lt,ltu,le,leu")
+ (and (match_code "ne") (match_test "!TARGET_MIPS16"))))
+
+(define_predicate "small_data_pattern"
+ (and (match_code "set,parallel,unspec,unspec_volatile,prefetch")
+ (match_test "mips_small_data_pattern_p (op)")))
diff --git a/gcc/config/mips/r3900.h b/gcc/config/mips/r3900.h
new file mode 100644
index 000000000..557ca3e47
--- /dev/null
+++ b/gcc/config/mips/r3900.h
@@ -0,0 +1,40 @@
+/* Definitions of MIPS sub target machine for GNU compiler.
+ Toshiba r3900. You should include mips.h after this.
+
+ Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 2004,
+ 2007, 2010 Free Software Foundation, Inc.
+ Contributed by Gavin Koch (gavin@cygnus.com).
+
+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/>. */
+
+#undef MIPS_CPU_STRING_DEFAULT
+#define MIPS_CPU_STRING_DEFAULT "r3900"
+#define MIPS_ISA_DEFAULT 1
+
+#undef MULTILIB_DEFAULTS
+#define MULTILIB_DEFAULTS { MULTILIB_ENDIAN_DEFAULT, "msoft-float" }
+
+/* We use the MIPS EABI by default. */
+#undef MIPS_ABI_DEFAULT
+#define MIPS_ABI_DEFAULT ABI_EABI
+
+/* By default (if not mips-something-else) produce code for the r3900 */
+#undef SUBTARGET_CC1_SPEC
+#define SUBTARGET_CC1_SPEC "\
+%{mhard-float:%e-mhard-float not supported} \
+%{msingle-float:%{msoft-float: \
+ %e-msingle-float and -msoft-float cannot both be specified}}"
diff --git a/gcc/config/mips/rtems.h b/gcc/config/mips/rtems.h
new file mode 100644
index 000000000..74da4f706
--- /dev/null
+++ b/gcc/config/mips/rtems.h
@@ -0,0 +1,35 @@
+/* Definitions for rtems targeting a MIPS using ELF.
+ Copyright (C) 1996, 1997, 1998, 1999, 2000, 2002, 2003, 2005, 2007
+ Free Software Foundation, Inc.
+ Contributed by Joel Sherrill (joel@OARcorp.com).
+
+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/>. */
+
+/* Specify predefined symbols in preprocessor. */
+
+#define TARGET_OS_CPP_BUILTINS() \
+do { \
+ builtin_define ("__rtems__"); \
+ builtin_define ("__USE_INIT_FINI__"); \
+ builtin_assert ("system=rtems"); \
+} while (0)
+
+/* No sdata.
+ * The RTEMS BSPs expect -G0
+ */
+#undef MIPS_DEFAULT_GVALUE
+#define MIPS_DEFAULT_GVALUE 0
diff --git a/gcc/config/mips/sb1.md b/gcc/config/mips/sb1.md
new file mode 100644
index 000000000..8c0b7608e
--- /dev/null
+++ b/gcc/config/mips/sb1.md
@@ -0,0 +1,579 @@
+;; Copyright (C) 2004, 2005, 2006, 2007 Free Software Foundation, 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/>.
+;;
+;; DFA-based pipeline description for Broadcom SB-1
+;;
+
+;; The Broadcom SB-1 core is 4-way superscalar, in-order. It has 2 load/store
+;; pipes (one of which can support some ALU operations), 2 alu pipes, 2 FP
+;; pipes, and 1 MDMX pipes. It can issue 2 ls insns and 2 exe/fpu/mdmx insns
+;; each cycle.
+
+;; We model the 4-way issue by ordering unit choices. The possible choices are
+;; {ex1,fp1}|{ex0,fp0}|ls1|ls0. Instructions issue to the first eligible unit
+;; in the list in most cases. Non-indexed load/stores issue to ls0 first.
+;; simple alu operations issue to ls1 if it is still available, and their
+;; operands are ready (no co-issue with loads), otherwise to the first
+;; available ex unit.
+
+;; When exceptions are enabled, can only issue FP insns to fp1. This is
+;; to ensure that instructions complete in order. The -mfp-exceptions option
+;; can be used to specify whether the system has FP exceptions enabled or not.
+
+;; In 32-bit mode, dependent FP can't co-issue with load, and only one FP exe
+;; insn can issue per cycle (fp1).
+
+;; The A1 MDMX pipe is separate from the FP pipes, but uses the same register
+;; file. As a result, once an MDMX insn is issued, no FP insns can be issued
+;; for 3 cycles. When an FP insn is issued, no MDMX insn can be issued for
+;; 5 cycles. This is currently not handled because there is no MDMX insn
+;; support as yet.
+
+;;
+;; We use two automata. sb1_cpu_div is for the integer divides, which are
+;; not pipelined. sb1_cpu is for everything else.
+;;
+(define_automaton "sb1_cpu, sb1_cpu_div")
+
+;; Load/store function units.
+(define_cpu_unit "sb1_ls0" "sb1_cpu")
+(define_cpu_unit "sb1_ls1" "sb1_cpu")
+
+;; CPU function units.
+(define_cpu_unit "sb1_ex0" "sb1_cpu")
+(define_cpu_unit "sb1_ex1" "sb1_cpu")
+
+;; The divide unit is not pipelined, and blocks hi/lo reads and writes.
+(define_cpu_unit "sb1_div" "sb1_cpu_div")
+;; DMULT block any multiply from issuing in the next cycle.
+(define_cpu_unit "sb1_mul" "sb1_cpu")
+
+;; Floating-point units.
+(define_cpu_unit "sb1_fp0" "sb1_cpu")
+(define_cpu_unit "sb1_fp1" "sb1_cpu")
+
+;; Can only issue to one of the ex and fp pipes at a time.
+(exclusion_set "sb1_ex0" "sb1_fp0")
+(exclusion_set "sb1_ex1" "sb1_fp1")
+
+;; Define an SB-1 specific attribute to simplify some FP descriptions.
+;; We can use 2 FP pipes only if we have 64-bit FP code, and exceptions are
+;; disabled.
+
+(define_attr "sb1_fp_pipes" "one,two"
+ (cond [(and (ne (symbol_ref "TARGET_FLOAT64") (const_int 0))
+ (eq (symbol_ref "TARGET_FP_EXCEPTIONS") (const_int 0)))
+ (const_string "two")]
+ (const_string "one")))
+
+;; Define reservations for common combinations.
+
+;; For long cycle operations, the FPU has a 4 cycle pipeline that repeats,
+;; effectively re-issuing the operation every 4 cycles. This means that we
+;; can have at most 4 long-cycle operations per pipe.
+
+;; ??? The fdiv operations should be e.g.
+;; sb1_fp1_4cycles*7" | "sb1_fp0_4cycle*7
+;; but the DFA is too large when we do that. Perhaps have to use scheduler
+;; hooks here.
+
+;; ??? Try limiting scheduler to 2 long latency operations, and see if this
+;; results in a usable DFA, and whether it helps code performance.
+
+;;(define_reservation "sb1_fp0_4cycles" "sb1_fp0, nothing*3")
+;;(define_reservation "sb1_fp1_4cycles" "sb1_fp1, nothing*3")
+
+;;
+;; The ordering of the instruction-execution-path/resource-usage
+;; descriptions (also known as reservation RTL) is roughly ordered
+;; based on the define attribute RTL for the "type" classification.
+;; When modifying, remember that the first test that matches is the
+;; reservation used!
+;;
+
+(define_insn_reservation "ir_sb1_unknown" 1
+ (and (eq_attr "cpu" "sb1,sb1a")
+ (eq_attr "type" "unknown,multi"))
+ "sb1_ls0+sb1_ls1+sb1_ex0+sb1_ex1+sb1_fp0+sb1_fp1")
+
+;; predicted taken branch causes 2 cycle ifetch bubble. predicted not
+;; taken branch causes 0 cycle ifetch bubble. mispredicted branch causes 8
+;; cycle ifetch bubble. We assume all branches predicted not taken.
+
+;; ??? This assumption that branches are predicated not taken should be
+;; investigated. Maybe using 2 here will give better results.
+
+(define_insn_reservation "ir_sb1_branch" 0
+ (and (eq_attr "cpu" "sb1,sb1a")
+ (eq_attr "type" "branch,jump,call"))
+ "sb1_ex0")
+
+;; ??? This is 1 cycle for ldl/ldr to ldl/ldr when they use the same data
+;; register as destination.
+
+;; ??? SB-1 can co-issue a load with a dependent arith insn if it executes on
+;; an EX unit. Can not co-issue if the dependent insn executes on an LS unit.
+;; SB-1A can always co-issue here.
+
+;; A load normally has a latency of zero cycles. In some cases, dependent
+;; insns can be issued in the same cycle. However, a value of 1 gives
+;; better performance in empirical testing.
+
+(define_insn_reservation "ir_sb1_load" 1
+ (and (eq_attr "cpu" "sb1")
+ (eq_attr "type" "load,prefetch"))
+ "sb1_ls0 | sb1_ls1")
+
+(define_insn_reservation "ir_sb1a_load" 0
+ (and (eq_attr "cpu" "sb1a")
+ (eq_attr "type" "load,prefetch"))
+ "sb1_ls0 | sb1_ls1")
+
+;; Can not co-issue fpload with fp exe when in 32-bit mode.
+
+(define_insn_reservation "ir_sb1_fpload" 0
+ (and (eq_attr "cpu" "sb1,sb1a")
+ (and (eq_attr "type" "fpload")
+ (ne (symbol_ref "TARGET_FLOAT64")
+ (const_int 0))))
+ "sb1_ls0 | sb1_ls1")
+
+(define_insn_reservation "ir_sb1_fpload_32bitfp" 1
+ (and (eq_attr "cpu" "sb1,sb1a")
+ (and (eq_attr "type" "fpload")
+ (eq (symbol_ref "TARGET_FLOAT64")
+ (const_int 0))))
+ "sb1_ls0 | sb1_ls1")
+
+;; Indexed loads can only execute on LS1 pipe.
+
+(define_insn_reservation "ir_sb1_fpidxload" 0
+ (and (eq_attr "cpu" "sb1,sb1a")
+ (and (eq_attr "type" "fpidxload")
+ (ne (symbol_ref "TARGET_FLOAT64")
+ (const_int 0))))
+ "sb1_ls1")
+
+(define_insn_reservation "ir_sb1_fpidxload_32bitfp" 1
+ (and (eq_attr "cpu" "sb1,sb1a")
+ (and (eq_attr "type" "fpidxload")
+ (eq (symbol_ref "TARGET_FLOAT64")
+ (const_int 0))))
+ "sb1_ls1")
+
+;; prefx can only execute on the ls1 pipe.
+
+(define_insn_reservation "ir_sb1_prefetchx" 0
+ (and (eq_attr "cpu" "sb1,sb1a")
+ (eq_attr "type" "prefetchx"))
+ "sb1_ls1")
+
+;; ??? There is a 4.5 cycle latency if a store is followed by a load, and
+;; there is a RAW dependency.
+
+(define_insn_reservation "ir_sb1_store" 1
+ (and (eq_attr "cpu" "sb1,sb1a")
+ (eq_attr "type" "store"))
+ "sb1_ls0+sb1_ex1 | sb1_ls0+sb1_ex0 | sb1_ls1+sb1_ex1 | sb1_ls1+sb1_ex0")
+
+(define_insn_reservation "ir_sb1_fpstore" 1
+ (and (eq_attr "cpu" "sb1,sb1a")
+ (eq_attr "type" "fpstore"))
+ "sb1_ls0+sb1_fp1 | sb1_ls0+sb1_fp0 | sb1_ls1+sb1_fp1 | sb1_ls1+sb1_fp0")
+
+;; Indexed stores can only execute on LS1 pipe.
+
+(define_insn_reservation "ir_sb1_fpidxstore" 1
+ (and (eq_attr "cpu" "sb1,sb1a")
+ (eq_attr "type" "fpidxstore"))
+ "sb1_ls1+sb1_fp1 | sb1_ls1+sb1_fp0")
+
+;; Load latencies are 3 cycles for one load to another load or store (address
+;; only). This is 0 cycles for one load to a store using it as the data
+;; written.
+
+;; This assumes that if a load is dependent on a previous insn, then it must
+;; be an address dependence.
+
+(define_bypass 3
+ "ir_sb1_load,ir_sb1a_load,ir_sb1_fpload,ir_sb1_fpload_32bitfp,
+ ir_sb1_fpidxload,ir_sb1_fpidxload_32bitfp"
+ "ir_sb1_load,ir_sb1a_load,ir_sb1_fpload,ir_sb1_fpload_32bitfp,
+ ir_sb1_fpidxload,ir_sb1_fpidxload_32bitfp,ir_sb1_prefetchx")
+
+(define_bypass 3
+ "ir_sb1_load,ir_sb1a_load,ir_sb1_fpload,ir_sb1_fpload_32bitfp,
+ ir_sb1_fpidxload,ir_sb1_fpidxload_32bitfp"
+ "ir_sb1_store,ir_sb1_fpstore,ir_sb1_fpidxstore"
+ "mips_store_data_bypass_p")
+
+;; On SB-1, simple alu instructions can execute on the LS1 unit.
+
+;; ??? A simple alu insn issued on an LS unit has 0 cycle latency to an EX
+;; insn, to a store (for data), and to an xfer insn. It has 1 cycle latency to
+;; another LS insn (excluding store data). A simple alu insn issued on an EX
+;; unit has a latency of 5 cycles when the results goes to a LS unit (excluding
+;; store data), otherwise a latency of 1 cycle.
+
+;; ??? We cannot handle latencies properly for simple alu instructions
+;; within the DFA pipeline model. Latencies can be defined only from one
+;; insn reservation to another. We can't make them depend on which function
+;; unit was used. This isn't a DFA flaw. There is a conflict here, as we
+;; need to know the latency before we can determine which unit will be
+;; available, but we need to know which unit it is issued to before we can
+;; compute the latency. Perhaps this can be handled via scheduler hooks.
+;; This needs to be investigated.
+
+;; ??? Optimal scheduling taking the LS units into account seems to require
+;; a pre-scheduling pass. We need to determine which instructions feed results
+;; into store/load addresses, and thus benefit most from being issued to the
+;; LS unit. Also, we need to prune the list to ensure we don't overschedule
+;; insns to the LS unit, and that we don't conflict with insns that need LS1
+;; such as indexed loads. We then need to emit nops to ensure that simple
+;; alu instructions that are not supposed to be scheduled to LS1 don't
+;; accidentally end up there because LS1 is free when they are issued. This
+;; will be a lot of work, and it isn't clear how useful it will be.
+
+;; Empirical testing shows that 2 gives the best result.
+
+(define_insn_reservation "ir_sb1_simple_alu" 2
+ (and (eq_attr "cpu" "sb1")
+ (eq_attr "type" "const,arith,logical,move,signext"))
+ "sb1_ls1 | sb1_ex1 | sb1_ex0")
+
+;; On SB-1A, simple alu instructions can not execute on the LS1 unit, and we
+;; have none of the above problems.
+
+(define_insn_reservation "ir_sb1a_simple_alu" 1
+ (and (eq_attr "cpu" "sb1a")
+ (eq_attr "type" "const,arith,logical,move,signext"))
+ "sb1_ex1 | sb1_ex0")
+
+;; ??? condmove also includes some FP instructions that execute on the FP
+;; units. This needs to be clarified.
+
+(define_insn_reservation "ir_sb1_alu" 1
+ (and (eq_attr "cpu" "sb1,sb1a")
+ (eq_attr "type" "condmove,nop,shift"))
+ "sb1_ex1 | sb1_ex0")
+
+;; These are type arith/darith that only execute on the EX0 unit.
+
+(define_insn_reservation "ir_sb1_alu_0" 1
+ (and (eq_attr "cpu" "sb1,sb1a")
+ (eq_attr "type" "slt,clz,trap"))
+ "sb1_ex0")
+
+;; An alu insn issued on an EX unit has a latency of 5 cycles when the
+;; result goes to a LS unit (excluding store data).
+
+;; This assumes that if a load is dependent on a previous insn, then it must
+;; be an address dependence.
+
+(define_bypass 5
+ "ir_sb1a_simple_alu,ir_sb1_alu,ir_sb1_alu_0,ir_sb1_mfhi,ir_sb1_mflo"
+ "ir_sb1_load,ir_sb1a_load,ir_sb1_fpload,ir_sb1_fpload_32bitfp,
+ ir_sb1_fpidxload,ir_sb1_fpidxload_32bitfp,ir_sb1_prefetchx")
+
+(define_bypass 5
+ "ir_sb1a_simple_alu,ir_sb1_alu,ir_sb1_alu_0,ir_sb1_mfhi,ir_sb1_mflo"
+ "ir_sb1_store,ir_sb1_fpstore,ir_sb1_fpidxstore"
+ "mips_store_data_bypass_p")
+
+;; mf{hi,lo} is 1 cycle.
+
+(define_insn_reservation "ir_sb1_mfhi" 1
+ (and (eq_attr "cpu" "sb1,sb1a")
+ (and (eq_attr "type" "mfhilo")
+ (not (match_operand 1 "lo_operand"))))
+ "sb1_ex1")
+
+(define_insn_reservation "ir_sb1_mflo" 1
+ (and (eq_attr "cpu" "sb1,sb1a")
+ (and (eq_attr "type" "mfhilo")
+ (match_operand 1 "lo_operand")))
+ "sb1_ex1")
+
+;; mt{hi,lo} to mul/div is 4 cycles.
+
+(define_insn_reservation "ir_sb1_mthilo" 4
+ (and (eq_attr "cpu" "sb1,sb1a")
+ (eq_attr "type" "mthilo"))
+ "sb1_ex1")
+
+;; mt{hi,lo} to mf{hi,lo} is 3 cycles.
+
+(define_bypass 3 "ir_sb1_mthilo" "ir_sb1_mfhi,ir_sb1_mflo")
+
+;; multiply latency to an EX operation is 3 cycles.
+
+;; ??? Should check whether we need to make multiply conflict with moves
+;; to/from hilo registers.
+
+(define_insn_reservation "ir_sb1_mulsi" 3
+ (and (eq_attr "cpu" "sb1,sb1a")
+ (and (eq_attr "type" "imul,imul3,imadd")
+ (eq_attr "mode" "SI")))
+ "sb1_ex1+sb1_mul")
+
+;; muldi to mfhi is 4 cycles.
+;; Blocks any other multiply insn issue for 1 cycle.
+
+(define_insn_reservation "ir_sb1_muldi" 4
+ (and (eq_attr "cpu" "sb1,sb1a")
+ (and (eq_attr "type" "imul,imul3")
+ (eq_attr "mode" "DI")))
+ "sb1_ex1+sb1_mul, sb1_mul")
+
+;; muldi to mflo is 3 cycles.
+
+(define_bypass 3 "ir_sb1_muldi" "ir_sb1_mflo")
+
+;; mul latency is 7 cycles if the result is used by any LS insn.
+
+;; This assumes that if a load is dependent on a previous insn, then it must
+;; be an address dependence.
+
+(define_bypass 7
+ "ir_sb1_mulsi,ir_sb1_muldi"
+ "ir_sb1_load,ir_sb1a_load,ir_sb1_fpload,ir_sb1_fpload_32bitfp,
+ ir_sb1_fpidxload,ir_sb1_fpidxload_32bitfp,ir_sb1_prefetchx")
+
+(define_bypass 7
+ "ir_sb1_mulsi,ir_sb1_muldi"
+ "ir_sb1_store,ir_sb1_fpstore,ir_sb1_fpidxstore"
+ "mips_store_data_bypass_p")
+
+;; The divide unit is not pipelined. Divide busy is asserted in the 4th
+;; cycle, and then deasserted on the latency cycle. So only one divide at
+;; a time, but the first/last 4 cycles can overlap.
+
+;; ??? All divides block writes to hi/lo regs. hi/lo regs are written 4 cycles
+;; after the latency cycle for divides (e.g. 40/72). dmult writes lo in
+;; cycle 7, and hi in cycle 8. All other insns write hi/lo regs in cycle 7.
+;; Default for output dependencies is the difference in latencies, which is
+;; only 1 cycle off here, e.g. div to mtlo stalls for 32 cycles, but should
+;; stall for 33 cycles. This does not seem significant enough to worry about.
+
+(define_insn_reservation "ir_sb1_divsi" 36
+ (and (eq_attr "cpu" "sb1,sb1a")
+ (and (eq_attr "type" "idiv")
+ (eq_attr "mode" "SI")))
+ "sb1_ex1, nothing*3, sb1_div*32")
+
+(define_insn_reservation "ir_sb1_divdi" 68
+ (and (eq_attr "cpu" "sb1,sb1a")
+ (and (eq_attr "type" "idiv")
+ (eq_attr "mode" "DI")))
+ "sb1_ex1, nothing*3, sb1_div*64")
+
+(define_insn_reservation "ir_sb1_fpu_2pipes" 4
+ (and (eq_attr "cpu" "sb1,sb1a")
+ (and (eq_attr "type" "fmove,fadd,fmul,fabs,fneg,fcvt,frdiv1,frsqrt1")
+ (eq_attr "sb1_fp_pipes" "two")))
+ "sb1_fp1 | sb1_fp0")
+
+(define_insn_reservation "ir_sb1_fpu_1pipe" 4
+ (and (eq_attr "cpu" "sb1,sb1a")
+ (and (eq_attr "type" "fmove,fadd,fmul,fabs,fneg,fcvt,frdiv1,frsqrt1")
+ (eq_attr "sb1_fp_pipes" "one")))
+ "sb1_fp1")
+
+(define_insn_reservation "ir_sb1_fpu_step2_2pipes" 8
+ (and (eq_attr "cpu" "sb1,sb1a")
+ (and (eq_attr "type" "frdiv2,frsqrt2")
+ (eq_attr "sb1_fp_pipes" "two")))
+ "sb1_fp1 | sb1_fp0")
+
+(define_insn_reservation "ir_sb1_fpu_step2_1pipe" 8
+ (and (eq_attr "cpu" "sb1,sb1a")
+ (and (eq_attr "type" "frdiv2,frsqrt2")
+ (eq_attr "sb1_fp_pipes" "one")))
+ "sb1_fp1")
+
+;; ??? madd/msub 4-cycle latency to itself (same fr?), but 8 cycle latency
+;; otherwise.
+
+;; ??? Blocks issue of another non-madd/msub after 4 cycles.
+
+(define_insn_reservation "ir_sb1_fmadd_2pipes" 8
+ (and (eq_attr "cpu" "sb1,sb1a")
+ (and (eq_attr "type" "fmadd")
+ (eq_attr "sb1_fp_pipes" "two")))
+ "sb1_fp1 | sb1_fp0")
+
+(define_insn_reservation "ir_sb1_fmadd_1pipe" 8
+ (and (eq_attr "cpu" "sb1,sb1a")
+ (and (eq_attr "type" "fmadd")
+ (eq_attr "sb1_fp_pipes" "one")))
+ "sb1_fp1")
+
+(define_insn_reservation "ir_sb1_fcmp" 4
+ (and (eq_attr "cpu" "sb1,sb1a")
+ (eq_attr "type" "fcmp"))
+ "sb1_fp1")
+
+;; mtc1 latency 5 cycles.
+
+(define_insn_reservation "ir_sb1_mtxfer" 5
+ (and (eq_attr "cpu" "sb1,sb1a")
+ (eq_attr "type" "mtc"))
+ "sb1_fp0")
+
+;; mfc1 latency 1 cycle.
+
+(define_insn_reservation "ir_sb1_mfxfer" 1
+ (and (eq_attr "cpu" "sb1,sb1a")
+ (eq_attr "type" "mfc"))
+ "sb1_fp0")
+
+;; ??? Can deliver at most 1 result per every 6 cycles because of issue
+;; restrictions.
+
+(define_insn_reservation "ir_sb1_divsf_2pipes" 24
+ (and (eq_attr "cpu" "sb1,sb1a")
+ (and (eq_attr "type" "fdiv")
+ (and (eq_attr "mode" "SF")
+ (eq_attr "sb1_fp_pipes" "two"))))
+ "sb1_fp1 | sb1_fp0")
+
+(define_insn_reservation "ir_sb1_divsf_1pipe" 24
+ (and (eq_attr "cpu" "sb1,sb1a")
+ (and (eq_attr "type" "fdiv")
+ (and (eq_attr "mode" "SF")
+ (eq_attr "sb1_fp_pipes" "one"))))
+ "sb1_fp1")
+
+;; ??? Can deliver at most 1 result per every 8 cycles because of issue
+;; restrictions.
+
+(define_insn_reservation "ir_sb1_divdf_2pipes" 32
+ (and (eq_attr "cpu" "sb1,sb1a")
+ (and (eq_attr "type" "fdiv")
+ (and (eq_attr "mode" "DF")
+ (eq_attr "sb1_fp_pipes" "two"))))
+ "sb1_fp1 | sb1_fp0")
+
+(define_insn_reservation "ir_sb1_divdf_1pipe" 32
+ (and (eq_attr "cpu" "sb1,sb1a")
+ (and (eq_attr "type" "fdiv")
+ (and (eq_attr "mode" "DF")
+ (eq_attr "sb1_fp_pipes" "one"))))
+ "sb1_fp1")
+
+;; ??? Can deliver at most 1 result per every 3 cycles because of issue
+;; restrictions.
+
+(define_insn_reservation "ir_sb1_recipsf_2pipes" 12
+ (and (eq_attr "cpu" "sb1,sb1a")
+ (and (eq_attr "type" "frdiv")
+ (and (eq_attr "mode" "SF")
+ (eq_attr "sb1_fp_pipes" "two"))))
+ "sb1_fp1 | sb1_fp0")
+
+(define_insn_reservation "ir_sb1_recipsf_1pipe" 12
+ (and (eq_attr "cpu" "sb1,sb1a")
+ (and (eq_attr "type" "frdiv")
+ (and (eq_attr "mode" "SF")
+ (eq_attr "sb1_fp_pipes" "one"))))
+ "sb1_fp1")
+
+;; ??? Can deliver at most 1 result per every 5 cycles because of issue
+;; restrictions.
+
+(define_insn_reservation "ir_sb1_recipdf_2pipes" 20
+ (and (eq_attr "cpu" "sb1,sb1a")
+ (and (eq_attr "type" "frdiv")
+ (and (eq_attr "mode" "DF")
+ (eq_attr "sb1_fp_pipes" "two"))))
+ "sb1_fp1 | sb1_fp0")
+
+(define_insn_reservation "ir_sb1_recipdf_1pipe" 20
+ (and (eq_attr "cpu" "sb1,sb1a")
+ (and (eq_attr "type" "frdiv")
+ (and (eq_attr "mode" "DF")
+ (eq_attr "sb1_fp_pipes" "one"))))
+ "sb1_fp1")
+
+;; ??? Can deliver at most 1 result per every 7 cycles because of issue
+;; restrictions.
+
+(define_insn_reservation "ir_sb1_sqrtsf_2pipes" 28
+ (and (eq_attr "cpu" "sb1,sb1a")
+ (and (eq_attr "type" "fsqrt")
+ (and (eq_attr "mode" "SF")
+ (eq_attr "sb1_fp_pipes" "two"))))
+ "sb1_fp1 | sb1_fp0")
+
+(define_insn_reservation "ir_sb1_sqrtsf_1pipe" 28
+ (and (eq_attr "cpu" "sb1,sb1a")
+ (and (eq_attr "type" "fsqrt")
+ (and (eq_attr "mode" "SF")
+ (eq_attr "sb1_fp_pipes" "one"))))
+ "sb1_fp1")
+
+;; ??? Can deliver at most 1 result per every 10 cycles because of issue
+;; restrictions.
+
+(define_insn_reservation "ir_sb1_sqrtdf_2pipes" 40
+ (and (eq_attr "cpu" "sb1,sb1a")
+ (and (eq_attr "type" "fsqrt")
+ (and (eq_attr "mode" "DF")
+ (eq_attr "sb1_fp_pipes" "two"))))
+ "sb1_fp1 | sb1_fp0")
+
+(define_insn_reservation "ir_sb1_sqrtdf_1pipe" 40
+ (and (eq_attr "cpu" "sb1,sb1a")
+ (and (eq_attr "type" "fsqrt")
+ (and (eq_attr "mode" "DF")
+ (eq_attr "sb1_fp_pipes" "one"))))
+ "sb1_fp1")
+
+;; ??? Can deliver at most 1 result per every 4 cycles because of issue
+;; restrictions.
+
+(define_insn_reservation "ir_sb1_rsqrtsf_2pipes" 16
+ (and (eq_attr "cpu" "sb1,sb1a")
+ (and (eq_attr "type" "frsqrt")
+ (and (eq_attr "mode" "SF")
+ (eq_attr "sb1_fp_pipes" "two"))))
+ "sb1_fp1 | sb1_fp0")
+
+(define_insn_reservation "ir_sb1_rsqrtsf_1pipe" 16
+ (and (eq_attr "cpu" "sb1,sb1a")
+ (and (eq_attr "type" "frsqrt")
+ (and (eq_attr "mode" "SF")
+ (eq_attr "sb1_fp_pipes" "one"))))
+ "sb1_fp1")
+
+;; ??? Can deliver at most 1 result per every 7 cycles because of issue
+;; restrictions.
+
+(define_insn_reservation "ir_sb1_rsqrtdf_2pipes" 28
+ (and (eq_attr "cpu" "sb1,sb1a")
+ (and (eq_attr "type" "frsqrt")
+ (and (eq_attr "mode" "DF")
+ (eq_attr "sb1_fp_pipes" "two"))))
+ "sb1_fp1 | sb1_fp0")
+
+(define_insn_reservation "ir_sb1_rsqrtdf_1pipe" 28
+ (and (eq_attr "cpu" "sb1,sb1a")
+ (and (eq_attr "type" "frsqrt")
+ (and (eq_attr "mode" "DF")
+ (eq_attr "sb1_fp_pipes" "one"))))
+ "sb1_fp1")
diff --git a/gcc/config/mips/sdb.h b/gcc/config/mips/sdb.h
new file mode 100644
index 000000000..27a42df7a
--- /dev/null
+++ b/gcc/config/mips/sdb.h
@@ -0,0 +1,87 @@
+/* Generate SDB debugging info.
+ Copyright (C) 2003, 2004, 2007 Free Software Foundation, 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/>. */
+
+/* Note that no configuration uses sdb as its preferred format. */
+
+#define SDB_DEBUGGING_INFO 1
+
+/* Forward references to tags are allowed. */
+#define SDB_ALLOW_FORWARD_REFERENCES
+
+/* Unknown tags are also allowed. */
+#define SDB_ALLOW_UNKNOWN_REFERENCES
+
+/* Block start/end next label #. */
+extern int sdb_label_count;
+
+/* Starting line of current function. */
+extern int sdb_begin_function_line;
+
+/* For block start and end, we create labels, so that
+ later we can figure out where the correct offset is.
+ The normal .ent/.end serve well enough for functions,
+ so those are just commented out. */
+
+#define PUT_SDB_BLOCK_START(LINE) \
+do { \
+ fprintf (asm_out_file, \
+ "%sLb%d:\n\t.begin\t%sLb%d\t%d\n", \
+ LOCAL_LABEL_PREFIX, \
+ sdb_label_count, \
+ LOCAL_LABEL_PREFIX, \
+ sdb_label_count, \
+ (LINE)); \
+ sdb_label_count++; \
+} while (0)
+
+#define PUT_SDB_BLOCK_END(LINE) \
+do { \
+ fprintf (asm_out_file, \
+ "%sLe%d:\n\t.bend\t%sLe%d\t%d\n", \
+ LOCAL_LABEL_PREFIX, \
+ sdb_label_count, \
+ LOCAL_LABEL_PREFIX, \
+ sdb_label_count, \
+ (LINE)); \
+ sdb_label_count++; \
+} while (0)
+
+#define PUT_SDB_FUNCTION_START(LINE)
+
+#define PUT_SDB_FUNCTION_END(LINE) \
+do { \
+ SDB_OUTPUT_SOURCE_LINE (asm_out_file, LINE + sdb_begin_function_line); \
+} while (0)
+
+#define PUT_SDB_EPILOGUE_END(NAME)
+
+/* We need to use .esize and .etype instead of .size and .type to
+ avoid conflicting with ELF directives. */
+#undef PUT_SDB_SIZE
+#define PUT_SDB_SIZE(a) \
+do { \
+ fprintf (asm_out_file, "\t.esize\t" HOST_WIDE_INT_PRINT_DEC ";", \
+ (HOST_WIDE_INT) (a)); \
+} while (0)
+
+#undef PUT_SDB_TYPE
+#define PUT_SDB_TYPE(a) \
+do { \
+ fprintf (asm_out_file, "\t.etype\t0x%x;", (a)); \
+} while (0)
diff --git a/gcc/config/mips/sde.h b/gcc/config/mips/sde.h
new file mode 100644
index 000000000..7194c1e54
--- /dev/null
+++ b/gcc/config/mips/sde.h
@@ -0,0 +1,135 @@
+/* Definitions of target machine for GNU compiler.
+ MIPS SDE version.
+ Copyright (C) 2003, 2004, 2007, 2008, 2009, 2010, 2011
+ Free Software Foundation, 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/>. */
+
+#undef DRIVER_SELF_SPECS
+#define DRIVER_SELF_SPECS \
+ /* Make sure a -mips option is present. This helps us to pick \
+ the right multilib, and also makes the later specs easier \
+ to write. */ \
+ MIPS_ISA_LEVEL_SPEC, \
+ \
+ /* Infer the default float setting from -march. */ \
+ MIPS_ARCH_FLOAT_SPEC, \
+ \
+ /* If no ABI option is specified, infer one from the ISA level \
+ or -mgp setting. */ \
+ "%{!mabi=*: %{" MIPS_32BIT_OPTION_SPEC ": -mabi=32;: -mabi=n32}}", \
+ \
+ /* Remove a redundant -mfp64 for -mabi=n32; we want the !mfp64 \
+ multilibs. There's no need to check whether the architecture \
+ is 64-bit; cc1 will complain if it isn't. */ \
+ "%{mabi=n32: %<mfp64}", \
+ \
+ /* Make sure that an endian option is always present. This makes \
+ things like LINK_SPEC easier to write. */ \
+ "%{!EB:%{!EL:%(endian_spec)}}", \
+ \
+ /* Configuration-independent MIPS rules. */ \
+ BASE_DRIVER_SELF_SPECS
+
+/* Use trap rather than break for all but MIPS I ISA. Force -no-mips16,
+ so that MIPS16 assembler code requires an explicit ".set mips16".
+ Very little hand-written MIPS16 assembler exists, and some build
+ systems expect code to be assembled as non-MIPS16 even if the
+ prevailing compiler flags select -mips16. */
+#undef SUBTARGET_ASM_SPEC
+#define SUBTARGET_ASM_SPEC "\
+%{!mips1:--trap} \
+%{mips16:-no-mips16}"
+
+#undef LINK_SPEC
+#define LINK_SPEC "\
+%(endian_spec) \
+%{G*} %{mips1} %{mips2} %{mips3} %{mips4} %{mips32*} %{mips64*} \
+%{shared} \
+%{mabi=n32:-melf32%{EB:b}%{EL:l}tsmipn32} \
+%{mabi=64:-melf64%{EB:b}%{EL:l}tsmip} \
+%{mabi=32:-melf32%{EB:b}%{EL:l}tsmip}"
+
+#undef DEFAULT_SIGNED_CHAR
+#define DEFAULT_SIGNED_CHAR 0
+
+/* SDE-MIPS won't ever support SDB debugging info. */
+#undef SDB_DEBUGGING_INFO
+
+/* Describe how we implement __builtin_eh_return. */
+
+/* At the moment, nothing appears to use more than 2 EH data registers.
+ The chosen registers must not clash with the return register ($2),
+ EH_RETURN_STACKADJ ($3), or MIPS_EPILOGUE_TEMP ($5), and they must
+ be general MIPS16 registers. Pick $6 and $7. */
+#undef EH_RETURN_DATA_REGNO
+#define EH_RETURN_DATA_REGNO(N) \
+ ((N) < 2 ? 7 - (N) : INVALID_REGNUM)
+
+/* Use $5 as a temporary for both MIPS16 and non-MIPS16. */
+#undef MIPS_EPILOGUE_TEMP_REGNUM
+#define MIPS_EPILOGUE_TEMP_REGNUM \
+ (cfun->machine->interrupt_handler_p ? K0_REG_NUM : GP_REG_FIRST + 5)
+
+/* Using long will always be right for size_t and ptrdiff_t, since
+ sizeof(long) must equal sizeof(void *), following from the setting
+ of the -mlong64 option. */
+#undef SIZE_TYPE
+#define SIZE_TYPE "long unsigned int"
+#undef PTRDIFF_TYPE
+#define PTRDIFF_TYPE "long int"
+
+/* Use standard ELF-style local labels (not '$' as on early Irix). */
+#undef LOCAL_LABEL_PREFIX
+#define LOCAL_LABEL_PREFIX "."
+
+/* Use periods rather than dollar signs in special g++ assembler names. */
+#define NO_DOLLAR_IN_LABEL
+
+/* Attach a special .ident directive to the end of the file to identify
+ the version of GCC which compiled this code. */
+#undef IDENT_ASM_OP
+#define IDENT_ASM_OP "\t.ident\t"
+
+/* Output #ident string into the ELF .comment section, so it doesn't
+ form part of the load image, and so that it can be stripped. */
+#undef ASM_OUTPUT_IDENT
+#define ASM_OUTPUT_IDENT(STREAM, STRING) \
+ fprintf (STREAM, "%s\"%s\"\n", IDENT_ASM_OP, STRING);
+
+/* Currently we don't support 128bit long doubles, so for now we force
+ n32 to be 64bit. */
+#undef LONG_DOUBLE_TYPE_SIZE
+#define LONG_DOUBLE_TYPE_SIZE 64
+
+#ifdef IN_LIBGCC2
+#undef LIBGCC2_LONG_DOUBLE_TYPE_SIZE
+#define LIBGCC2_LONG_DOUBLE_TYPE_SIZE 64
+#endif
+
+/* Force all .init and .fini entries to be 32-bit, not mips16, so that
+ in a mixed environment they are all the same mode. The crti.asm and
+ crtn.asm files will also be compiled as 32-bit due to the
+ -no-mips16 flag in SUBTARGET_ASM_SPEC above. */
+#undef CRT_CALL_STATIC_FUNCTION
+#define CRT_CALL_STATIC_FUNCTION(SECTION_OP, FUNC) \
+ asm (SECTION_OP "\n\
+ .set push\n\
+ .set nomips16\n\
+ jal " USER_LABEL_PREFIX #FUNC "\n\
+ .set pop\n\
+ " TEXT_SECTION_ASM_OP);
diff --git a/gcc/config/mips/sde.opt b/gcc/config/mips/sde.opt
new file mode 100644
index 000000000..61b0ebedd
--- /dev/null
+++ b/gcc/config/mips/sde.opt
@@ -0,0 +1,28 @@
+; MIPS SDE options.
+;
+; Copyright (C) 2010 Free Software Foundation, 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/>.
+
+; -mcode-xonly is a traditional alias for -mcode-readable=pcrel and
+; -mno-data-in-code is a traditional alias for -mcode-readable=no.
+
+mno-data-in-code
+Target RejectNegative Alias(mcode-readable=, no)
+
+mcode-xonly
+Target RejectNegative Alias(mcode-readable=, pcrel)
diff --git a/gcc/config/mips/sdemtk.h b/gcc/config/mips/sdemtk.h
new file mode 100644
index 000000000..16c3a6cba
--- /dev/null
+++ b/gcc/config/mips/sdemtk.h
@@ -0,0 +1,103 @@
+/* Definitions of target machine for GNU compiler.
+ MIPS SDE version, for use with the SDE C library rather than newlib.
+ Copyright (C) 2007, 2008, 2009, 2010
+ Free Software Foundation, 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/>. */
+
+#define TARGET_OS_CPP_BUILTINS() \
+ do \
+ { \
+ builtin_assert ("system=sde"); \
+ builtin_assert ("system=posix"); \
+ builtin_define ("__SDE_MIPS__"); \
+ \
+ /* Deprecated: use __mips_isa_rev >= 2. */ \
+ if (ISA_MIPS32R2) \
+ builtin_define ("__mipsr2"); \
+ \
+ /* Deprecated: use __mips_fpr == 64. */ \
+ if (TARGET_FLOAT64) \
+ builtin_define ("__mipsfp64"); \
+ \
+ if (TARGET_NO_FLOAT) \
+ builtin_define ("__NO_FLOAT"); \
+ else if (TARGET_SOFT_FLOAT_ABI) \
+ builtin_define ("__SOFT_FLOAT"); \
+ else if (TARGET_SINGLE_FLOAT) \
+ builtin_define ("__SINGLE_FLOAT"); \
+ \
+ if (TARGET_BIG_ENDIAN) \
+ { \
+ builtin_assert ("endian=big"); \
+ builtin_assert ("cpu=mipseb"); \
+ } \
+ else \
+ { \
+ builtin_assert ("endian=little"); \
+ builtin_assert ("cpu=mipsel"); \
+ } \
+ } \
+ while (0)
+
+/* For __clear_cache in libgcc2.c. */
+#ifdef IN_LIBGCC2
+extern void mips_sync_icache (void *beg, unsigned long len);
+#undef CLEAR_INSN_CACHE
+#define CLEAR_INSN_CACHE(beg, end) \
+ mips_sync_icache (beg, end - beg)
+#endif
+
+/* For mips_cache_flush_func in mips.opt. */
+#undef CACHE_FLUSH_FUNC
+#define CACHE_FLUSH_FUNC "mips_sync_icache"
+
+/* For inline code which needs to sync the icache and dcache,
+ noting that the SDE library takes arguments (address, size). */
+#undef MIPS_ICACHE_SYNC
+#define MIPS_ICACHE_SYNC(ADDR, SIZE) \
+ emit_library_call (gen_rtx_SYMBOL_REF (Pmode, mips_cache_flush_func), \
+ LCT_NORMAL, VOIDmode, 2, ADDR, Pmode, \
+ SIZE, TYPE_MODE (sizetype))
+
+/* This version of _mcount does not pop 2 words from the stack. */
+#undef FUNCTION_PROFILER
+#define FUNCTION_PROFILER(FILE, LABELNO) \
+ { \
+ mips_push_asm_switch (&mips_noat); \
+ /* _mcount treats $2 as the static chain register. */ \
+ if (cfun->static_chain_decl != NULL) \
+ fprintf (FILE, "\tmove\t%s,%s\n", reg_names[2], \
+ reg_names[STATIC_CHAIN_REGNUM]); \
+ /* MIPS16 code passes saved $ra in $v1 instead of $at. */ \
+ fprintf (FILE, "\tmove\t%s,%s\n", \
+ reg_names[GP_REG_FIRST + (TARGET_MIPS16 ? 3 : 1)], \
+ reg_names[RETURN_ADDR_REGNUM]); \
+ fprintf (FILE, "\tjal\t_mcount\n"); \
+ mips_pop_asm_switch (&mips_noat); \
+ /* _mcount treats $2 as the static chain register. */ \
+ if (cfun->static_chain_decl != NULL) \
+ fprintf (FILE, "\tmove\t%s,%s\n", reg_names[STATIC_CHAIN_REGNUM], \
+ reg_names[2]); \
+ }
+
+/* ...nor does the call sequence preserve $31. */
+#undef MIPS_SAVE_REG_FOR_PROFILING_P
+#define MIPS_SAVE_REG_FOR_PROFILING_P(REGNO) ((REGNO) == RETURN_ADDR_REGNUM)
+
+/* Compile in support for the -mno-float option. */
+#define TARGET_SUPPORTS_NO_FLOAT 1
diff --git a/gcc/config/mips/sr71k.md b/gcc/config/mips/sr71k.md
new file mode 100644
index 000000000..9b2a784b1
--- /dev/null
+++ b/gcc/config/mips/sr71k.md
@@ -0,0 +1,337 @@
+;; Copyright (C) 2002, 2003, 2004, 2005, 2007 Free Software Foundation, 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/>.
+;;
+;; .........................
+;;
+;; DFA-based pipeline description for Sandcraft SR3 (MIPS64 based)
+;;
+;; The SR3 is described as:
+;; - nine-stage pipeline, insn buffering with out-of-order issue to
+;; multiple function units, with an average dispatch rate of 2
+;; insn.s per cycle (max 6 insns: 2 fpu, 4 cpu).
+;;
+;; The details on this are scant except for a diagram in
+;; Chap. 6 of Rev. 1.0 SR3 Spec.
+;;
+;; The model employed below is designed to closely approximate the
+;; published latencies. Emulation of out-of-order issue and the insn
+;; buffering is done via a VLIW dispatch style (with a packing of 6 insns);
+;; the function unit reservations restrictions (define_*_set) are
+;; contrived to support published timings.
+;;
+;; Reference:
+;; "SR3 Microprocessor Specification, System development information,"
+;; Revision 1.0, 13 December 2000.
+;;
+;;
+;; Reservation model is based on:
+;; 1) Figure 6-1, from the 1.0 specification.
+;; 2) Chapter 19, from the 1.0 specification.
+;; 3) following questions(Red Hat)/answers(Sandcraft):
+;; RH> From Section 19.1
+;; RH> 1) In terms of figure 6-1, are all the instructions in
+;; RH> table 19-1 restricted
+;; RH> to ALUx? When ALUx is not in use for an instruction in table;; RH> 19-1 is
+;; RH> it fully compatible with all insns that issue to ALUy?
+;;
+;; Yes, all the instructions in Table 19-1 only go to ALUX, and all the
+;; instructions that can be issued to ALUY can also be issued to ALUX.
+;;
+;;
+;; RH> From Section 19.2
+;; RH> 2) Explain conditional moves execution path (in terms of
+;; RH> figure 6-1)
+;;
+;; Conditional move of integer registers (based on floating point condition
+;; codes or integer register value) go to ALUX or ALUY.
+;;
+;; RH> 3) Explain floating point store execution path (in terms of
+;; RH> figure 6-1)
+;;
+;; Floating point stores go to Ld/St and go to MOV in the floating point
+;; pipeline.
+;;
+;; Floating point loads go to Ld/St and go to LOAD in the floating point
+;; pipeline.
+;;
+;; RH> 4) Explain branch on floating condition (in terms of figure 6-1);;
+;; Branch on floating condition go to BRU.
+;;
+;; RH> 5) Is the column for single RECIP instruction latency correct?
+;; RH> What about for RSQRT single and double?
+;;
+;; The latency/repeat for RECIP and RSQRT are correct.
+;;
+
+;;
+;; Use four automata to isolate long latency operations, and to
+;; reduce the complexity of cpu+fpu, reducing space.
+;;
+(define_automaton "sr71_cpu, sr71_cpu1, sr71_cp1, sr71_cp2, sr71_fextra, sr71_imacc")
+
+;; feeders for CPU function units and feeders for fpu (CP1 interface)
+(define_cpu_unit "sr_iss0,sr_iss1,sr_iss2,sr_iss3,sr_iss4,sr_iss5" "sr71_cpu")
+
+;; CPU function units
+(define_cpu_unit "ipu_bru" "sr71_cpu1")
+(define_cpu_unit "ipu_alux" "sr71_cpu1")
+(define_cpu_unit "ipu_aluy" "sr71_cpu1")
+(define_cpu_unit "ipu_ldst" "sr71_cpu1")
+(define_cpu_unit "ipu_macc_iter" "sr71_imacc")
+
+
+;; Floating-point unit (Co-processor interface 1).
+(define_cpu_unit "fpu_mov" "sr71_cp1")
+(define_cpu_unit "fpu_load" "sr71_cp1")
+(define_cpu_unit "fpu_fpu" "sr71_cp2")
+
+;; fictitous unit to track long float insns with separate automaton
+(define_cpu_unit "fpu_iter" "sr71_fextra")
+
+
+;;
+;; Define common execution path (reservation) combinations
+;;
+
+;;
+(define_reservation "cpu_iss" "sr_iss0|sr_iss1|sr_iss2|sr_iss3")
+
+;; two cycles are used for instruction using the fpu as it runs
+;; at half the clock speed of the cpu. By adding an extra cycle
+;; to the issue units, the default/minimum "repeat" dispatch delay is
+;; accounted for all insn.s
+(define_reservation "cp1_iss" "(sr_iss4*2)|(sr_iss5*2)")
+
+(define_reservation "serial_dispatch" "sr_iss0+sr_iss1+sr_iss2+sr_iss3+sr_iss4+sr_iss5")
+
+;; Simulate a 6 insn VLIW dispatch, 1 cycle in dispatch followed by
+;; reservation of function unit.
+(define_reservation "ri_insns" "cpu_iss,(ipu_alux|ipu_aluy)")
+(define_reservation "ri_mem" "cpu_iss,ipu_ldst")
+(define_reservation "ri_alux" "cpu_iss,ipu_alux")
+(define_reservation "ri_branch" "cpu_iss,ipu_bru")
+
+(define_reservation "rf_insn" "cp1_iss,fpu_fpu")
+(define_reservation "rf_ldmem" "cp1_iss,fpu_load")
+
+; simultaneous reservation of pseudo-unit keeps cp1 fpu tied
+; up until long cycle insn is finished...
+(define_reservation "rf_multi1" "rf_insn+fpu_iter")
+
+;;
+;; The ordering of the instruction-execution-path/resource-usage
+;; descriptions (also known as reservation RTL) is roughly ordered
+;; based on the define attribute RTL for the "type" classification.
+;; When modifying, remember that the first test that matches is the
+;; reservation used!
+;;
+
+
+(define_insn_reservation "ir_sr70_unknown" 1
+ (and (eq_attr "cpu" "sr71000")
+ (eq_attr "type" "unknown"))
+ "serial_dispatch")
+
+
+;; Assume prediction fails.
+(define_insn_reservation "ir_sr70_branch" 6
+ (and (eq_attr "cpu" "sr71000")
+ (eq_attr "type" "branch,jump,call"))
+ "ri_branch")
+
+(define_insn_reservation "ir_sr70_load" 2
+ (and (eq_attr "cpu" "sr71000")
+ (eq_attr "type" "load"))
+ "ri_mem")
+
+(define_insn_reservation "ir_sr70_store" 1
+ (and (eq_attr "cpu" "sr71000")
+ (eq_attr "type" "store"))
+ "ri_mem")
+
+
+;;
+;; float loads/stores flow through both cpu and cp1...
+;;
+(define_insn_reservation "ir_sr70_fload" 9
+ (and (eq_attr "cpu" "sr71000")
+ (eq_attr "type" "fpload,fpidxload"))
+ "(cpu_iss+cp1_iss),(ri_mem+rf_ldmem)")
+
+(define_insn_reservation "ir_sr70_fstore" 1
+ (and (eq_attr "cpu" "sr71000")
+ (eq_attr "type" "fpstore,fpidxstore"))
+ "(cpu_iss+cp1_iss),(fpu_mov+ri_mem)")
+
+
+;; This reservation is for conditional move based on integer
+;; or floating point CC.
+(define_insn_reservation "ir_sr70_condmove" 4
+ (and (eq_attr "cpu" "sr71000")
+ (eq_attr "type" "condmove"))
+ "ri_insns")
+
+;; Try to discriminate move-from-cp1 versus move-to-cp1 as latencies
+;; are different. Like float load/store, these insns use multiple
+;; resources simultaneously
+(define_insn_reservation "ir_sr70_xfer_from" 6
+ (and (eq_attr "cpu" "sr71000")
+ (eq_attr "type" "mfc"))
+ "(cpu_iss+cp1_iss),(fpu_mov+ri_mem)")
+
+(define_insn_reservation "ir_sr70_xfer_to" 9
+ (and (eq_attr "cpu" "sr71000")
+ (eq_attr "type" "mtc"))
+ "(cpu_iss+cp1_iss),(ri_mem+rf_ldmem)")
+
+(define_insn_reservation "ir_sr70_hilo" 1
+ (and (eq_attr "cpu" "sr71000")
+ (eq_attr "type" "mthilo,mfhilo"))
+ "ri_insns")
+
+(define_insn_reservation "ir_sr70_arith" 1
+ (and (eq_attr "cpu" "sr71000")
+ (eq_attr "type" "arith,shift,signext,slt,clz,const,logical,move,trap"))
+ "ri_insns")
+
+;; emulate repeat (dispatch stall) by spending extra cycle(s) in
+;; in iter unit
+(define_insn_reservation "ir_sr70_imul_si" 4
+ (and (eq_attr "cpu" "sr71000")
+ (and (eq_attr "type" "imul,imul3,imadd")
+ (eq_attr "mode" "SI")))
+ "ri_alux,ipu_alux,ipu_macc_iter")
+
+(define_insn_reservation "ir_sr70_imul_di" 6
+ (and (eq_attr "cpu" "sr71000")
+ (and (eq_attr "type" "imul,imul3,imadd")
+ (eq_attr "mode" "DI")))
+ "ri_alux,ipu_alux,(ipu_macc_iter*3)")
+
+;; Divide algorithm is early out with best latency of 7 pcycles.
+;; Use worst case for scheduling purposes.
+(define_insn_reservation "ir_sr70_idiv_si" 41
+ (and (eq_attr "cpu" "sr71000")
+ (and (eq_attr "type" "idiv")
+ (eq_attr "mode" "SI")))
+ "ri_alux,ipu_alux,(ipu_macc_iter*38)")
+
+(define_insn_reservation "ir_sr70_idiv_di" 73
+ (and (eq_attr "cpu" "sr71000")
+ (and (eq_attr "type" "idiv")
+ (eq_attr "mode" "DI")))
+ "ri_alux,ipu_alux,(ipu_macc_iter*70)")
+
+;; extra reservations of fpu_fpu are for repeat latency
+(define_insn_reservation "ir_sr70_fadd_sf" 8
+ (and (eq_attr "cpu" "sr71000")
+ (and (eq_attr "type" "fadd")
+ (eq_attr "mode" "SF")))
+ "rf_insn,fpu_fpu")
+
+(define_insn_reservation "ir_sr70_fadd_df" 10
+ (and (eq_attr "cpu" "sr71000")
+ (and (eq_attr "type" "fadd")
+ (eq_attr "mode" "DF")))
+ "rf_insn,fpu_fpu")
+
+;; Latencies for MADD,MSUB, NMADD, NMSUB assume the Multiply is fused
+;; with the sub or add.
+(define_insn_reservation "ir_sr70_fmul_sf" 8
+ (and (eq_attr "cpu" "sr71000")
+ (and (eq_attr "type" "fmul,fmadd")
+ (eq_attr "mode" "SF")))
+ "rf_insn,fpu_fpu")
+
+;; tie up the fpu unit to emulate the balance for the "repeat
+;; rate" of 8 (2 are spent in the iss unit)
+(define_insn_reservation "ir_sr70_fmul_df" 16
+ (and (eq_attr "cpu" "sr71000")
+ (and (eq_attr "type" "fmul,fmadd")
+ (eq_attr "mode" "DF")))
+ "rf_insn,fpu_fpu*6")
+
+
+;; RECIP insn uses same type attr as div, and for SR3, has same
+;; timings for double. However, single RECIP has a latency of
+;; 28 -- only way to fix this is to introduce new insn attrs.
+;; cycles spent in iter unit are designed to satisfy balance
+;; of "repeat" latency after insn uses up rf_multi1 reservation
+(define_insn_reservation "ir_sr70_fdiv_sf" 60
+ (and (eq_attr "cpu" "sr71000")
+ (and (eq_attr "type" "fdiv,frdiv")
+ (eq_attr "mode" "SF")))
+ "rf_multi1+(fpu_iter*51)")
+
+(define_insn_reservation "ir_sr70_fdiv_df" 120
+ (and (eq_attr "cpu" "sr71000")
+ (and (eq_attr "type" "fdiv,frdiv")
+ (eq_attr "mode" "DF")))
+ "rf_multi1+(fpu_iter*109)")
+
+(define_insn_reservation "ir_sr70_fabs" 4
+ (and (eq_attr "cpu" "sr71000")
+ (eq_attr "type" "fabs,fneg,fmove"))
+ "rf_insn,fpu_fpu")
+
+(define_insn_reservation "ir_sr70_fcmp" 10
+ (and (eq_attr "cpu" "sr71000")
+ (eq_attr "type" "fcmp"))
+ "rf_insn,fpu_fpu")
+
+;; "fcvt" type attribute covers a number of diff insns, most have the same
+;; latency descriptions, a few vary. We use the
+;; most common timing (which is also worst case).
+(define_insn_reservation "ir_sr70_fcvt" 12
+ (and (eq_attr "cpu" "sr71000")
+ (eq_attr "type" "fcvt"))
+ "rf_insn,fpu_fpu*4")
+
+(define_insn_reservation "ir_sr70_fsqrt_sf" 62
+ (and (eq_attr "cpu" "sr71000")
+ (and (eq_attr "type" "fsqrt")
+ (eq_attr "mode" "SF")))
+ "rf_multi1+(fpu_iter*53)")
+
+(define_insn_reservation "ir_sr70_fsqrt_df" 122
+ (and (eq_attr "cpu" "sr71000")
+ (and (eq_attr "type" "fsqrt")
+ (eq_attr "mode" "DF")))
+ "rf_multi1+(fpu_iter*111)")
+
+(define_insn_reservation "ir_sr70_frsqrt_sf" 48
+ (and (eq_attr "cpu" "sr71000")
+ (and (eq_attr "type" "frsqrt")
+ (eq_attr "mode" "SF")))
+ "rf_multi1+(fpu_iter*39)")
+
+(define_insn_reservation "ir_sr70_frsqrt_df" 240
+ (and (eq_attr "cpu" "sr71000")
+ (and (eq_attr "type" "frsqrt")
+ (eq_attr "mode" "DF")))
+ "rf_multi1+(fpu_iter*229)")
+
+(define_insn_reservation "ir_sr70_multi" 1
+ (and (eq_attr "cpu" "sr71000")
+ (eq_attr "type" "multi"))
+ "serial_dispatch")
+
+(define_insn_reservation "ir_sr70_nop" 1
+ (and (eq_attr "cpu" "sr71000")
+ (eq_attr "type" "nop"))
+ "ri_insns")
diff --git a/gcc/config/mips/st.h b/gcc/config/mips/st.h
new file mode 100644
index 000000000..363e797a9
--- /dev/null
+++ b/gcc/config/mips/st.h
@@ -0,0 +1,31 @@
+/* ST 2e / 2f GNU/Linux Configuration.
+ Copyright (C) 2008
+ Free Software Foundation, 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/>. */
+
+/* The various C libraries each have their own subdirectory. */
+#undef SYSROOT_SUFFIX_SPEC
+#define SYSROOT_SUFFIX_SPEC \
+ "%{march=loongson2e:/2e ; \
+ march=loongson2f:/2f}"
+
+#undef STARTFILE_PREFIX_SPEC
+#define STARTFILE_PREFIX_SPEC \
+ "%{mabi=32: /usr/local/lib/ /lib/ /usr/lib/} \
+ %{mabi=n32: /usr/local/lib32/ /lib32/ /usr/lib32/} \
+ %{mabi=64: /usr/local/lib64/ /lib64/ /usr/lib64/}"
diff --git a/gcc/config/mips/sync.md b/gcc/config/mips/sync.md
new file mode 100644
index 000000000..b3098b434
--- /dev/null
+++ b/gcc/config/mips/sync.md
@@ -0,0 +1,560 @@
+;; Machine Description for MIPS based processor synchronization
+;; instructions.
+;; Copyright (C) 2007, 2008, 2009, 2010
+;; Free Software Foundation, 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/>.
+
+(define_c_enum "unspec" [
+ UNSPEC_COMPARE_AND_SWAP
+ UNSPEC_COMPARE_AND_SWAP_12
+ UNSPEC_SYNC_OLD_OP
+ UNSPEC_SYNC_NEW_OP
+ UNSPEC_SYNC_NEW_OP_12
+ UNSPEC_SYNC_OLD_OP_12
+ UNSPEC_SYNC_EXCHANGE
+ UNSPEC_SYNC_EXCHANGE_12
+ UNSPEC_MEMORY_BARRIER
+])
+
+;; Atomic fetch bitwise operations.
+(define_code_iterator fetchop_bit [ior xor and])
+
+;; Atomic HI and QI operations
+(define_code_iterator atomic_hiqi_op [plus minus ior xor and])
+
+;; Atomic memory operations.
+
+(define_expand "memory_barrier"
+ [(set (match_dup 0)
+ (unspec:BLK [(match_dup 0)] UNSPEC_MEMORY_BARRIER))]
+ "GENERATE_SYNC"
+{
+ operands[0] = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode));
+ MEM_VOLATILE_P (operands[0]) = 1;
+})
+
+(define_insn "*memory_barrier"
+ [(set (match_operand:BLK 0 "" "")
+ (unspec:BLK [(match_dup 0)] UNSPEC_MEMORY_BARRIER))]
+ "GENERATE_SYNC"
+ { return mips_output_sync (); })
+
+(define_insn "sync_compare_and_swap<mode>"
+ [(set (match_operand:GPR 0 "register_operand" "=&d,&d")
+ (match_operand:GPR 1 "memory_operand" "+R,R"))
+ (set (match_dup 1)
+ (unspec_volatile:GPR [(match_operand:GPR 2 "reg_or_0_operand" "dJ,dJ")
+ (match_operand:GPR 3 "arith_operand" "I,d")]
+ UNSPEC_COMPARE_AND_SWAP))]
+ "GENERATE_LL_SC"
+ { return mips_output_sync_loop (insn, operands); }
+ [(set_attr "sync_insn1" "li,move")
+ (set_attr "sync_oldval" "0")
+ (set_attr "sync_mem" "1")
+ (set_attr "sync_required_oldval" "2")
+ (set_attr "sync_insn1_op2" "3")])
+
+(define_expand "sync_compare_and_swap<mode>"
+ [(match_operand:SHORT 0 "register_operand")
+ (match_operand:SHORT 1 "memory_operand")
+ (match_operand:SHORT 2 "general_operand")
+ (match_operand:SHORT 3 "general_operand")]
+ "GENERATE_LL_SC"
+{
+ union mips_gen_fn_ptrs generator;
+ generator.fn_6 = gen_compare_and_swap_12;
+ mips_expand_atomic_qihi (generator,
+ operands[0], operands[1], operands[2], operands[3]);
+ DONE;
+})
+
+;; Helper insn for mips_expand_atomic_qihi.
+(define_insn "compare_and_swap_12"
+ [(set (match_operand:SI 0 "register_operand" "=&d,&d")
+ (match_operand:SI 1 "memory_operand" "+R,R"))
+ (set (match_dup 1)
+ (unspec_volatile:SI [(match_operand:SI 2 "register_operand" "d,d")
+ (match_operand:SI 3 "register_operand" "d,d")
+ (match_operand:SI 4 "reg_or_0_operand" "dJ,dJ")
+ (match_operand:SI 5 "reg_or_0_operand" "d,J")]
+ UNSPEC_COMPARE_AND_SWAP_12))]
+ "GENERATE_LL_SC"
+ { return mips_output_sync_loop (insn, operands); }
+ [(set_attr "sync_oldval" "0")
+ (set_attr "sync_mem" "1")
+ (set_attr "sync_inclusive_mask" "2")
+ (set_attr "sync_exclusive_mask" "3")
+ (set_attr "sync_required_oldval" "4")
+ (set_attr "sync_insn1_op2" "5")])
+
+(define_insn "sync_add<mode>"
+ [(set (match_operand:GPR 0 "memory_operand" "+R,R")
+ (unspec_volatile:GPR
+ [(plus:GPR (match_dup 0)
+ (match_operand:GPR 1 "arith_operand" "I,d"))]
+ UNSPEC_SYNC_OLD_OP))]
+ "GENERATE_LL_SC"
+ { return mips_output_sync_loop (insn, operands); }
+ [(set_attr "sync_insn1" "addiu,addu")
+ (set_attr "sync_mem" "0")
+ (set_attr "sync_insn1_op2" "1")])
+
+(define_expand "sync_<optab><mode>"
+ [(set (match_operand:SHORT 0 "memory_operand")
+ (unspec_volatile:SHORT
+ [(atomic_hiqi_op:SHORT (match_dup 0)
+ (match_operand:SHORT 1 "general_operand"))]
+ UNSPEC_SYNC_OLD_OP))]
+ "GENERATE_LL_SC"
+{
+ union mips_gen_fn_ptrs generator;
+ generator.fn_4 = gen_sync_<optab>_12;
+ mips_expand_atomic_qihi (generator,
+ NULL, operands[0], operands[1], NULL);
+ DONE;
+})
+
+;; Helper insn for sync_<optab><mode>
+(define_insn "sync_<optab>_12"
+ [(set (match_operand:SI 0 "memory_operand" "+R")
+ (unspec_volatile:SI
+ [(match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "register_operand" "d")
+ (atomic_hiqi_op:SI (match_dup 0)
+ (match_operand:SI 3 "register_operand" "dJ"))]
+ UNSPEC_SYNC_OLD_OP_12))
+ (clobber (match_scratch:SI 4 "=&d"))]
+ "GENERATE_LL_SC"
+ { return mips_output_sync_loop (insn, operands); }
+ [(set_attr "sync_insn1" "<insn>")
+ (set_attr "sync_insn2" "and")
+ (set_attr "sync_mem" "0")
+ (set_attr "sync_inclusive_mask" "1")
+ (set_attr "sync_exclusive_mask" "2")
+ (set_attr "sync_insn1_op2" "3")
+ (set_attr "sync_oldval" "4")
+ (set_attr "sync_newval" "4")])
+
+(define_expand "sync_old_<optab><mode>"
+ [(parallel [
+ (set (match_operand:SHORT 0 "register_operand")
+ (match_operand:SHORT 1 "memory_operand"))
+ (set (match_dup 1)
+ (unspec_volatile:SHORT [(atomic_hiqi_op:SHORT
+ (match_dup 1)
+ (match_operand:SHORT 2 "general_operand"))]
+ UNSPEC_SYNC_OLD_OP))])]
+ "GENERATE_LL_SC"
+{
+ union mips_gen_fn_ptrs generator;
+ generator.fn_5 = gen_sync_old_<optab>_12;
+ mips_expand_atomic_qihi (generator,
+ operands[0], operands[1], operands[2], NULL);
+ DONE;
+})
+
+;; Helper insn for sync_old_<optab><mode>
+(define_insn "sync_old_<optab>_12"
+ [(set (match_operand:SI 0 "register_operand" "=&d")
+ (match_operand:SI 1 "memory_operand" "+R"))
+ (set (match_dup 1)
+ (unspec_volatile:SI
+ [(match_operand:SI 2 "register_operand" "d")
+ (match_operand:SI 3 "register_operand" "d")
+ (atomic_hiqi_op:SI (match_dup 0)
+ (match_operand:SI 4 "register_operand" "dJ"))]
+ UNSPEC_SYNC_OLD_OP_12))
+ (clobber (match_scratch:SI 5 "=&d"))]
+ "GENERATE_LL_SC"
+ { return mips_output_sync_loop (insn, operands); }
+ [(set_attr "sync_insn1" "<insn>")
+ (set_attr "sync_insn2" "and")
+ (set_attr "sync_oldval" "0")
+ (set_attr "sync_mem" "1")
+ (set_attr "sync_inclusive_mask" "2")
+ (set_attr "sync_exclusive_mask" "3")
+ (set_attr "sync_insn1_op2" "4")
+ (set_attr "sync_newval" "5")])
+
+(define_expand "sync_new_<optab><mode>"
+ [(parallel [
+ (set (match_operand:SHORT 0 "register_operand")
+ (unspec_volatile:SHORT [(atomic_hiqi_op:SHORT
+ (match_operand:SHORT 1 "memory_operand")
+ (match_operand:SHORT 2 "general_operand"))]
+ UNSPEC_SYNC_NEW_OP))
+ (set (match_dup 1)
+ (unspec_volatile:SHORT [(match_dup 1) (match_dup 2)]
+ UNSPEC_SYNC_NEW_OP))])]
+ "GENERATE_LL_SC"
+{
+ union mips_gen_fn_ptrs generator;
+ generator.fn_5 = gen_sync_new_<optab>_12;
+ mips_expand_atomic_qihi (generator,
+ operands[0], operands[1], operands[2], NULL);
+ DONE;
+})
+
+;; Helper insn for sync_new_<optab><mode>
+(define_insn "sync_new_<optab>_12"
+ [(set (match_operand:SI 0 "register_operand" "=&d")
+ (unspec_volatile:SI
+ [(match_operand:SI 1 "memory_operand" "+R")
+ (match_operand:SI 2 "register_operand" "d")
+ (match_operand:SI 3 "register_operand" "d")
+ (atomic_hiqi_op:SI (match_dup 0)
+ (match_operand:SI 4 "register_operand" "dJ"))]
+ UNSPEC_SYNC_NEW_OP_12))
+ (set (match_dup 1)
+ (unspec_volatile:SI
+ [(match_dup 1)
+ (match_dup 2)
+ (match_dup 3)
+ (match_dup 4)] UNSPEC_SYNC_NEW_OP_12))]
+ "GENERATE_LL_SC"
+ { return mips_output_sync_loop (insn, operands); }
+ [(set_attr "sync_insn1" "<insn>")
+ (set_attr "sync_insn2" "and")
+ (set_attr "sync_oldval" "0")
+ (set_attr "sync_newval" "0")
+ (set_attr "sync_mem" "1")
+ (set_attr "sync_inclusive_mask" "2")
+ (set_attr "sync_exclusive_mask" "3")
+ (set_attr "sync_insn1_op2" "4")])
+
+(define_expand "sync_nand<mode>"
+ [(set (match_operand:SHORT 0 "memory_operand")
+ (unspec_volatile:SHORT
+ [(match_dup 0)
+ (match_operand:SHORT 1 "general_operand")]
+ UNSPEC_SYNC_OLD_OP))]
+ "GENERATE_LL_SC"
+{
+ union mips_gen_fn_ptrs generator;
+ generator.fn_4 = gen_sync_nand_12;
+ mips_expand_atomic_qihi (generator,
+ NULL, operands[0], operands[1], NULL);
+ DONE;
+})
+
+;; Helper insn for sync_nand<mode>
+(define_insn "sync_nand_12"
+ [(set (match_operand:SI 0 "memory_operand" "+R")
+ (unspec_volatile:SI
+ [(match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "register_operand" "d")
+ (match_dup 0)
+ (match_operand:SI 3 "register_operand" "dJ")]
+ UNSPEC_SYNC_OLD_OP_12))
+ (clobber (match_scratch:SI 4 "=&d"))]
+ "GENERATE_LL_SC"
+ { return mips_output_sync_loop (insn, operands); }
+ [(set_attr "sync_insn1" "and")
+ (set_attr "sync_insn2" "xor")
+ (set_attr "sync_mem" "0")
+ (set_attr "sync_inclusive_mask" "1")
+ (set_attr "sync_exclusive_mask" "2")
+ (set_attr "sync_insn1_op2" "3")
+ (set_attr "sync_oldval" "4")
+ (set_attr "sync_newval" "4")])
+
+(define_expand "sync_old_nand<mode>"
+ [(parallel [
+ (set (match_operand:SHORT 0 "register_operand")
+ (match_operand:SHORT 1 "memory_operand"))
+ (set (match_dup 1)
+ (unspec_volatile:SHORT [(match_dup 1)
+ (match_operand:SHORT 2 "general_operand")]
+ UNSPEC_SYNC_OLD_OP))])]
+ "GENERATE_LL_SC"
+{
+ union mips_gen_fn_ptrs generator;
+ generator.fn_5 = gen_sync_old_nand_12;
+ mips_expand_atomic_qihi (generator,
+ operands[0], operands[1], operands[2], NULL);
+ DONE;
+})
+
+;; Helper insn for sync_old_nand<mode>
+(define_insn "sync_old_nand_12"
+ [(set (match_operand:SI 0 "register_operand" "=&d")
+ (match_operand:SI 1 "memory_operand" "+R"))
+ (set (match_dup 1)
+ (unspec_volatile:SI
+ [(match_operand:SI 2 "register_operand" "d")
+ (match_operand:SI 3 "register_operand" "d")
+ (match_operand:SI 4 "register_operand" "dJ")]
+ UNSPEC_SYNC_OLD_OP_12))
+ (clobber (match_scratch:SI 5 "=&d"))]
+ "GENERATE_LL_SC"
+ { return mips_output_sync_loop (insn, operands); }
+ [(set_attr "sync_insn1" "and")
+ (set_attr "sync_insn2" "xor")
+ (set_attr "sync_oldval" "0")
+ (set_attr "sync_mem" "1")
+ (set_attr "sync_inclusive_mask" "2")
+ (set_attr "sync_exclusive_mask" "3")
+ (set_attr "sync_insn1_op2" "4")
+ (set_attr "sync_newval" "5")])
+
+(define_expand "sync_new_nand<mode>"
+ [(parallel [
+ (set (match_operand:SHORT 0 "register_operand")
+ (unspec_volatile:SHORT [(match_operand:SHORT 1 "memory_operand")
+ (match_operand:SHORT 2 "general_operand")]
+ UNSPEC_SYNC_NEW_OP))
+ (set (match_dup 1)
+ (unspec_volatile:SHORT [(match_dup 1) (match_dup 2)]
+ UNSPEC_SYNC_NEW_OP))])]
+ "GENERATE_LL_SC"
+{
+ union mips_gen_fn_ptrs generator;
+ generator.fn_5 = gen_sync_new_nand_12;
+ mips_expand_atomic_qihi (generator,
+ operands[0], operands[1], operands[2], NULL);
+ DONE;
+})
+
+;; Helper insn for sync_new_nand<mode>
+(define_insn "sync_new_nand_12"
+ [(set (match_operand:SI 0 "register_operand" "=&d")
+ (unspec_volatile:SI
+ [(match_operand:SI 1 "memory_operand" "+R")
+ (match_operand:SI 2 "register_operand" "d")
+ (match_operand:SI 3 "register_operand" "d")
+ (match_operand:SI 4 "register_operand" "dJ")]
+ UNSPEC_SYNC_NEW_OP_12))
+ (set (match_dup 1)
+ (unspec_volatile:SI
+ [(match_dup 1)
+ (match_dup 2)
+ (match_dup 3)
+ (match_dup 4)] UNSPEC_SYNC_NEW_OP_12))]
+ "GENERATE_LL_SC"
+ { return mips_output_sync_loop (insn, operands); }
+ [(set_attr "sync_insn1" "and")
+ (set_attr "sync_insn2" "xor")
+ (set_attr "sync_oldval" "0")
+ (set_attr "sync_newval" "0")
+ (set_attr "sync_mem" "1")
+ (set_attr "sync_inclusive_mask" "2")
+ (set_attr "sync_exclusive_mask" "3")
+ (set_attr "sync_insn1_op2" "4")])
+
+(define_insn "sync_sub<mode>"
+ [(set (match_operand:GPR 0 "memory_operand" "+R")
+ (unspec_volatile:GPR
+ [(minus:GPR (match_dup 0)
+ (match_operand:GPR 1 "register_operand" "d"))]
+ UNSPEC_SYNC_OLD_OP))]
+ "GENERATE_LL_SC"
+ { return mips_output_sync_loop (insn, operands); }
+ [(set_attr "sync_insn1" "subu")
+ (set_attr "sync_mem" "0")
+ (set_attr "sync_insn1_op2" "1")])
+
+(define_insn "sync_old_add<mode>"
+ [(set (match_operand:GPR 0 "register_operand" "=&d,&d")
+ (match_operand:GPR 1 "memory_operand" "+R,R"))
+ (set (match_dup 1)
+ (unspec_volatile:GPR
+ [(plus:GPR (match_dup 1)
+ (match_operand:GPR 2 "arith_operand" "I,d"))]
+ UNSPEC_SYNC_OLD_OP))]
+ "GENERATE_LL_SC"
+ { return mips_output_sync_loop (insn, operands); }
+ [(set_attr "sync_insn1" "addiu,addu")
+ (set_attr "sync_oldval" "0")
+ (set_attr "sync_mem" "1")
+ (set_attr "sync_insn1_op2" "2")])
+
+(define_insn "sync_old_sub<mode>"
+ [(set (match_operand:GPR 0 "register_operand" "=&d")
+ (match_operand:GPR 1 "memory_operand" "+R"))
+ (set (match_dup 1)
+ (unspec_volatile:GPR
+ [(minus:GPR (match_dup 1)
+ (match_operand:GPR 2 "register_operand" "d"))]
+ UNSPEC_SYNC_OLD_OP))]
+ "GENERATE_LL_SC"
+ { return mips_output_sync_loop (insn, operands); }
+ [(set_attr "sync_insn1" "subu")
+ (set_attr "sync_oldval" "0")
+ (set_attr "sync_mem" "1")
+ (set_attr "sync_insn1_op2" "2")])
+
+(define_insn "sync_new_add<mode>"
+ [(set (match_operand:GPR 0 "register_operand" "=&d,&d")
+ (plus:GPR (match_operand:GPR 1 "memory_operand" "+R,R")
+ (match_operand:GPR 2 "arith_operand" "I,d")))
+ (set (match_dup 1)
+ (unspec_volatile:GPR
+ [(plus:GPR (match_dup 1) (match_dup 2))]
+ UNSPEC_SYNC_NEW_OP))]
+ "GENERATE_LL_SC"
+ { return mips_output_sync_loop (insn, operands); }
+ [(set_attr "sync_insn1" "addiu,addu")
+ (set_attr "sync_oldval" "0")
+ (set_attr "sync_newval" "0")
+ (set_attr "sync_mem" "1")
+ (set_attr "sync_insn1_op2" "2")])
+
+(define_insn "sync_new_sub<mode>"
+ [(set (match_operand:GPR 0 "register_operand" "=&d")
+ (minus:GPR (match_operand:GPR 1 "memory_operand" "+R")
+ (match_operand:GPR 2 "register_operand" "d")))
+ (set (match_dup 1)
+ (unspec_volatile:GPR
+ [(minus:GPR (match_dup 1) (match_dup 2))]
+ UNSPEC_SYNC_NEW_OP))]
+ "GENERATE_LL_SC"
+ { return mips_output_sync_loop (insn, operands); }
+ [(set_attr "sync_insn1" "subu")
+ (set_attr "sync_oldval" "0")
+ (set_attr "sync_newval" "0")
+ (set_attr "sync_mem" "1")
+ (set_attr "sync_insn1_op2" "2")])
+
+(define_insn "sync_<optab><mode>"
+ [(set (match_operand:GPR 0 "memory_operand" "+R,R")
+ (unspec_volatile:GPR
+ [(fetchop_bit:GPR (match_operand:GPR 1 "uns_arith_operand" "K,d")
+ (match_dup 0))]
+ UNSPEC_SYNC_OLD_OP))]
+ "GENERATE_LL_SC"
+ { return mips_output_sync_loop (insn, operands); }
+ [(set_attr "sync_insn1" "<immediate_insn>,<insn>")
+ (set_attr "sync_mem" "0")
+ (set_attr "sync_insn1_op2" "1")])
+
+(define_insn "sync_old_<optab><mode>"
+ [(set (match_operand:GPR 0 "register_operand" "=&d,&d")
+ (match_operand:GPR 1 "memory_operand" "+R,R"))
+ (set (match_dup 1)
+ (unspec_volatile:GPR
+ [(fetchop_bit:GPR (match_operand:GPR 2 "uns_arith_operand" "K,d")
+ (match_dup 1))]
+ UNSPEC_SYNC_OLD_OP))]
+ "GENERATE_LL_SC"
+ { return mips_output_sync_loop (insn, operands); }
+ [(set_attr "sync_insn1" "<immediate_insn>,<insn>")
+ (set_attr "sync_oldval" "0")
+ (set_attr "sync_mem" "1")
+ (set_attr "sync_insn1_op2" "2")])
+
+(define_insn "sync_new_<optab><mode>"
+ [(set (match_operand:GPR 0 "register_operand" "=&d,&d")
+ (match_operand:GPR 1 "memory_operand" "+R,R"))
+ (set (match_dup 1)
+ (unspec_volatile:GPR
+ [(fetchop_bit:GPR (match_operand:GPR 2 "uns_arith_operand" "K,d")
+ (match_dup 1))]
+ UNSPEC_SYNC_NEW_OP))]
+ "GENERATE_LL_SC"
+ { return mips_output_sync_loop (insn, operands); }
+ [(set_attr "sync_insn1" "<immediate_insn>,<insn>")
+ (set_attr "sync_oldval" "0")
+ (set_attr "sync_newval" "0")
+ (set_attr "sync_mem" "1")
+ (set_attr "sync_insn1_op2" "2")])
+
+(define_insn "sync_nand<mode>"
+ [(set (match_operand:GPR 0 "memory_operand" "+R,R")
+ (unspec_volatile:GPR [(match_operand:GPR 1 "uns_arith_operand" "K,d")]
+ UNSPEC_SYNC_OLD_OP))]
+ "GENERATE_LL_SC"
+ { return mips_output_sync_loop (insn, operands); }
+ [(set_attr "sync_insn1" "andi,and")
+ (set_attr "sync_insn2" "not")
+ (set_attr "sync_mem" "0")
+ (set_attr "sync_insn1_op2" "1")])
+
+(define_insn "sync_old_nand<mode>"
+ [(set (match_operand:GPR 0 "register_operand" "=&d,&d")
+ (match_operand:GPR 1 "memory_operand" "+R,R"))
+ (set (match_dup 1)
+ (unspec_volatile:GPR [(match_operand:GPR 2 "uns_arith_operand" "K,d")]
+ UNSPEC_SYNC_OLD_OP))]
+ "GENERATE_LL_SC"
+ { return mips_output_sync_loop (insn, operands); }
+ [(set_attr "sync_insn1" "andi,and")
+ (set_attr "sync_insn2" "not")
+ (set_attr "sync_oldval" "0")
+ (set_attr "sync_mem" "1")
+ (set_attr "sync_insn1_op2" "2")])
+
+(define_insn "sync_new_nand<mode>"
+ [(set (match_operand:GPR 0 "register_operand" "=&d,&d")
+ (match_operand:GPR 1 "memory_operand" "+R,R"))
+ (set (match_dup 1)
+ (unspec_volatile:GPR [(match_operand:GPR 2 "uns_arith_operand" "K,d")]
+ UNSPEC_SYNC_NEW_OP))]
+ "GENERATE_LL_SC"
+ { return mips_output_sync_loop (insn, operands); }
+ [(set_attr "sync_insn1" "andi,and")
+ (set_attr "sync_insn2" "not")
+ (set_attr "sync_oldval" "0")
+ (set_attr "sync_newval" "0")
+ (set_attr "sync_mem" "1")
+ (set_attr "sync_insn1_op2" "2")])
+
+(define_insn "sync_lock_test_and_set<mode>"
+ [(set (match_operand:GPR 0 "register_operand" "=&d,&d")
+ (match_operand:GPR 1 "memory_operand" "+R,R"))
+ (set (match_dup 1)
+ (unspec_volatile:GPR [(match_operand:GPR 2 "arith_operand" "I,d")]
+ UNSPEC_SYNC_EXCHANGE))]
+ "GENERATE_LL_SC"
+ { return mips_output_sync_loop (insn, operands); }
+ [(set_attr "sync_release_barrier" "no")
+ (set_attr "sync_insn1" "li,move")
+ (set_attr "sync_oldval" "0")
+ (set_attr "sync_mem" "1")
+ (set_attr "sync_insn1_op2" "2")])
+
+(define_expand "sync_lock_test_and_set<mode>"
+ [(match_operand:SHORT 0 "register_operand")
+ (match_operand:SHORT 1 "memory_operand")
+ (match_operand:SHORT 2 "general_operand")]
+ "GENERATE_LL_SC"
+{
+ union mips_gen_fn_ptrs generator;
+ generator.fn_5 = gen_test_and_set_12;
+ mips_expand_atomic_qihi (generator,
+ operands[0], operands[1], operands[2], NULL);
+ DONE;
+})
+
+(define_insn "test_and_set_12"
+ [(set (match_operand:SI 0 "register_operand" "=&d")
+ (match_operand:SI 1 "memory_operand" "+R"))
+ (set (match_dup 1)
+ (unspec_volatile:SI [(match_operand:SI 2 "register_operand" "d")
+ (match_operand:SI 3 "register_operand" "d")
+ (match_operand:SI 4 "arith_operand" "dJ")]
+ UNSPEC_SYNC_EXCHANGE_12))]
+ "GENERATE_LL_SC"
+ { return mips_output_sync_loop (insn, operands); }
+ [(set_attr "sync_release_barrier" "no")
+ (set_attr "sync_oldval" "0")
+ (set_attr "sync_mem" "1")
+ ;; Unused, but needed to give the number of operands expected by
+ ;; the expander.
+ (set_attr "sync_inclusive_mask" "2")
+ (set_attr "sync_exclusive_mask" "3")
+ (set_attr "sync_insn1_op2" "4")])
diff --git a/gcc/config/mips/t-elf b/gcc/config/mips/t-elf
new file mode 100644
index 000000000..4ed36da65
--- /dev/null
+++ b/gcc/config/mips/t-elf
@@ -0,0 +1,44 @@
+# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2006,
+# 2007 Free Software Foundation, 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/>.
+
+# Don't let CTOR_LIST end up in sdata section.
+CRTSTUFF_T_CFLAGS = -G 0
+
+# Assemble startup files.
+$(T)crti.o: $(srcdir)/config/mips/crti.asm $(GCC_PASSES)
+ $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(MULTILIB_CFLAGS) $(INCLUDES) \
+ -c -o $(T)crti.o -x assembler-with-cpp $(srcdir)/config/mips/crti.asm
+
+$(T)crtn.o: $(srcdir)/config/mips/crtn.asm $(GCC_PASSES)
+ $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(MULTILIB_CFLAGS) $(INCLUDES) \
+ -c -o $(T)crtn.o -x assembler-with-cpp $(srcdir)/config/mips/crtn.asm
+
+# We must build libgcc2.a with -G 0, in case the user wants to link
+# without the $gp register.
+TARGET_LIBGCC2_CFLAGS = -G 0
+
+# Build the libraries for both hard and soft floating point
+
+MULTILIB_OPTIONS = msoft-float EL/EB
+MULTILIB_DIRNAMES = soft-float el eb
+MULTILIB_MATCHES = EL=mel EB=meb msingle-float=m4650
+EXTRA_MULTILIB_PARTS = crtbegin.o crtend.o crti.o crtn.o
+
+LIBGCC = stmp-multilib
+INSTALL_LIBGCC = install-multilib
diff --git a/gcc/config/mips/t-iris b/gcc/config/mips/t-iris
new file mode 100644
index 000000000..a200cf8c1
--- /dev/null
+++ b/gcc/config/mips/t-iris
@@ -0,0 +1,9 @@
+$(T)irix-crti.o: $(srcdir)/config/mips/irix-crti.asm $(GCC_PASSES)
+ $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(MULTILIB_CFLAGS) $(INCLUDES) \
+ -c -o $@ -x assembler-with-cpp $<
+
+$(T)irix-crtn.o: $(srcdir)/config/mips/irix-crtn.asm $(GCC_PASSES)
+ $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(MULTILIB_CFLAGS) $(INCLUDES) \
+ -c -o $@ -x assembler-with-cpp $<
+
+EXTRA_MULTILIB_PARTS = crtbegin.o crtend.o irix-crti.o irix-crtn.o
diff --git a/gcc/config/mips/t-iris6 b/gcc/config/mips/t-iris6
new file mode 100644
index 000000000..49f16d7fc
--- /dev/null
+++ b/gcc/config/mips/t-iris6
@@ -0,0 +1,38 @@
+# Copyright (C) 1995, 1996, 1997, 1998, 2000, 2001, 2002, 2003, 2004, 2005,
+# 2006, 2010 Free Software Foundation, 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/>.
+
+MULTILIB_OPTIONS=mabi=n32/mabi=64
+MULTILIB_DIRNAMES=n32 64
+MULTILIB_MATCHES=
+MULTILIB_OSDIRNAMES=../lib32 ../lib64
+
+LIBGCC = stmp-multilib
+INSTALL_LIBGCC = install-multilib
+
+TPBIT = tp-bit.c
+
+tp-bit.c: $(srcdir)/config/fp-bit.c
+ echo '#ifdef __MIPSEL__' > tp-bit.c
+ echo '# define FLOAT_BIT_ORDER_MISMATCH' >> tp-bit.c
+ echo '#endif' >> tp-bit.c
+ echo '#define QUIET_NAN_NEGATED' >> tp-bit.c
+ echo '#if __LDBL_MANT_DIG__ == 106' >> tp-bit.c
+ echo '# define TFLOAT' >> tp-bit.c
+ cat $(srcdir)/config/fp-bit.c >> tp-bit.c
+ echo '#endif' >> tp-bit.c
diff --git a/gcc/config/mips/t-isa3264 b/gcc/config/mips/t-isa3264
new file mode 100644
index 000000000..f6dce3255
--- /dev/null
+++ b/gcc/config/mips/t-isa3264
@@ -0,0 +1,54 @@
+# Copyright (C) 2001, 2002, 2003, 2004, 2007,
+# 2008 Free Software Foundation, 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/>.
+
+# Don't let CTOR_LIST end up in sdata section.
+CRTSTUFF_T_CFLAGS = -G 0
+
+# Assemble startup files.
+$(T)crti.o: $(srcdir)/config/mips/crti.asm $(GCC_PASSES)
+ $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(MULTILIB_CFLAGS) $(INCLUDES) \
+ -c -o $(T)crti.o -x assembler-with-cpp $(srcdir)/config/mips/crti.asm
+
+$(T)crtn.o: $(srcdir)/config/mips/crtn.asm $(GCC_PASSES)
+ $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(MULTILIB_CFLAGS) $(INCLUDES) \
+ -c -o $(T)crtn.o -x assembler-with-cpp $(srcdir)/config/mips/crtn.asm
+
+# We must build libgcc2.a with -G 0, in case the user wants to link
+# without the $gp register.
+TARGET_LIBGCC2_CFLAGS = -G 0
+
+# Build the libraries for both hard and soft floating point
+
+ifneq ($(filter MIPS_ABI_DEFAULT=ABI_EABI,$(tm_defines)),)
+MULTILIB_OPTIONS = msoft-float EL/EB mips32/mips32r2/mips64/mips64r2
+MULTILIB_DIRNAMES = soft-float el eb mips32 mips32r2 mips64 mips64r2
+else
+MULTILIB_OPTIONS = msoft-float/mfp64 EL/EB mips32/mips32r2/mips64/mips64r2
+MULTILIB_DIRNAMES = soft-float fp64 el eb mips32 mips32r2 mips64 mips64r2
+ifneq ($(filter MIPS_ISA_DEFAULT=33,$(tm_defines)),)
+MULTILIB_EXCLUSIONS = mips32/mfp64 mips64/mfp64 mips64r2/mfp64
+else
+MULTILIB_EXCLUSIONS = !mips32r2/mfp64
+endif
+endif
+MULTILIB_MATCHES = EL=mel EB=meb
+EXTRA_MULTILIB_PARTS = crtbegin.o crtend.o crti.o crtn.o
+
+LIBGCC = stmp-multilib
+INSTALL_LIBGCC = install-multilib
diff --git a/gcc/config/mips/t-libgcc-mips16 b/gcc/config/mips/t-libgcc-mips16
new file mode 100644
index 000000000..fa3949a20
--- /dev/null
+++ b/gcc/config/mips/t-libgcc-mips16
@@ -0,0 +1,45 @@
+# Copyright (C) 2007, 2008 Free Software Foundation, 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/>.
+
+LIB1ASMSRC = mips/mips16.S
+LIB1ASMFUNCS = _m16addsf3 _m16subsf3 _m16mulsf3 _m16divsf3 \
+ _m16eqsf2 _m16nesf2 _m16gtsf2 _m16gesf2 _m16lesf2 _m16ltsf2 \
+ _m16unordsf2 \
+ _m16fltsisf _m16fix_truncsfsi _m16fltunsisf \
+ _m16adddf3 _m16subdf3 _m16muldf3 _m16divdf3 \
+ _m16extsfdf2 _m16trdfsf2 \
+ _m16eqdf2 _m16nedf2 _m16gtdf2 _m16gedf2 _m16ledf2 _m16ltdf2 \
+ _m16unorddf2 \
+ _m16fltsidf _m16fix_truncdfsi _m16fltunsidf \
+ _m16retsf _m16retdf \
+ _m16retsc _m16retdc \
+ _m16stub1 _m16stub2 _m16stub5 _m16stub6 _m16stub9 _m16stub10 \
+ _m16stubsf0 _m16stubsf1 _m16stubsf2 _m16stubsf5 _m16stubsf6 \
+ _m16stubsf9 _m16stubsf10 \
+ _m16stubdf0 _m16stubdf1 _m16stubdf2 _m16stubdf5 _m16stubdf6 \
+ _m16stubdf9 _m16stubdf10 \
+ _m16stubsc0 _m16stubsc1 _m16stubsc2 _m16stubsc5 _m16stubsc6 \
+ _m16stubsc9 _m16stubsc10 \
+ _m16stubdc0 _m16stubdc1 _m16stubdc2 _m16stubdc5 _m16stubdc6 \
+ _m16stubdc9 _m16stubdc10
+
+LIBGCC_SYNC = yes
+LIBGCC_SYNC_CFLAGS = -mno-mips16
+
+# Version these symbols if building libgcc.so.
+SHLIB_MAPFILES += $(srcdir)/config/mips/libgcc-mips16.ver
diff --git a/gcc/config/mips/t-linux64 b/gcc/config/mips/t-linux64
new file mode 100644
index 000000000..7915f4d7d
--- /dev/null
+++ b/gcc/config/mips/t-linux64
@@ -0,0 +1,40 @@
+# Copyright (C) 2003, 2004, 2005, 2006, 2012 Free Software Foundation, 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/>.
+
+MULTILIB_OPTIONS = mabi=n32/mabi=32/mabi=64
+MULTILIB_DIRNAMES = n32 32 64
+MIPS_EL = $(if $(filter %el, $(firstword $(subst -, ,$(target)))),el)
+MIPS_SOFT = $(if $(strip $(filter MASK_SOFT_FLOAT_ABI, $(target_cpu_default)) $(filter soft, $(with_float))),soft)
+MULTILIB_OSDIRNAMES = \
+ ../lib32$(call if_multiarch,:mips64$(MIPS_EL)-linux-gnuabin32$(MIPS_SOFT)) \
+ ../lib$(call if_multiarch,:mips$(MIPS_EL)-linux-gnu$(MIPS_SOFT)) \
+ ../lib64$(call if_multiarch,:mips64$(MIPS_EL)-linux-gnuabi64$(MIPS_SOFT))
+
+EXTRA_MULTILIB_PARTS=crtbegin.o crtend.o crtbeginS.o crtendS.o crtbeginT.o
+
+TPBIT = tp-bit.c
+
+tp-bit.c: $(srcdir)/config/fp-bit.c
+ echo '#ifdef __MIPSEL__' > tp-bit.c
+ echo '# define FLOAT_BIT_ORDER_MISMATCH' >> tp-bit.c
+ echo '#endif' >> tp-bit.c
+ echo '#if __LDBL_MANT_DIG__ == 113' >> tp-bit.c
+ echo '#define QUIET_NAN_NEGATED' >> tp-bit.c
+ echo '# define TFLOAT' >> tp-bit.c
+ cat $(srcdir)/config/fp-bit.c >> tp-bit.c
+ echo '#endif' >> tp-bit.c
diff --git a/gcc/config/mips/t-mips b/gcc/config/mips/t-mips
new file mode 100644
index 000000000..d412da571
--- /dev/null
+++ b/gcc/config/mips/t-mips
@@ -0,0 +1,41 @@
+# Copyright (C) 2002, 2003, 2006 Free Software Foundation, 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/>.
+
+# fp-bit and dp-bit are really part of libgcc1, but this will cause
+# them to be built correctly, so... [taken from t-sparclite]
+# We want fine grained libraries, so use the new code to build the
+# floating point emulation libraries.
+FPBIT = fp-bit.c
+DPBIT = dp-bit.c
+
+dp-bit.c: $(srcdir)/config/fp-bit.c
+ echo '#ifdef __MIPSEL__' > dp-bit.c
+ echo '#define FLOAT_BIT_ORDER_MISMATCH' >> dp-bit.c
+ echo '#endif' >> dp-bit.c
+ echo '#define QUIET_NAN_NEGATED' >> dp-bit.c
+ cat $(srcdir)/config/fp-bit.c >> dp-bit.c
+
+fp-bit.c: $(srcdir)/config/fp-bit.c
+ echo '#define FLOAT' > fp-bit.c
+ echo '#ifdef __MIPSEL__' >> fp-bit.c
+ echo '#define FLOAT_BIT_ORDER_MISMATCH' >> fp-bit.c
+ echo '#endif' >> fp-bit.c
+ echo '#define QUIET_NAN_NEGATED' >> fp-bit.c
+ cat $(srcdir)/config/fp-bit.c >> fp-bit.c
+
+LIB2_SIDITI_CONV_FUNCS=yes
diff --git a/gcc/config/mips/t-r3900 b/gcc/config/mips/t-r3900
new file mode 100644
index 000000000..2c4216399
--- /dev/null
+++ b/gcc/config/mips/t-r3900
@@ -0,0 +1,35 @@
+# Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+# 2007 Free Software Foundation, 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/>.
+
+# We must build libgcc2.a with -G 0, in case the user wants to link
+# without the $gp register.
+TARGET_LIBGCC2_CFLAGS = -G 0
+
+EXTRA_MULTILIB_PARTS = crtbegin.o crtend.o
+# Don't let CTOR_LIST end up in sdata section.
+CRTSTUFF_T_CFLAGS = -G 0
+
+# Build the libraries for both hard and soft floating point
+
+MULTILIB_OPTIONS = msoft-float EL/EB
+MULTILIB_DIRNAMES = soft-float el eb
+MULTILIB_MATCHES = EL=mel EB=meb
+
+LIBGCC = stmp-multilib
+INSTALL_LIBGCC = install-multilib
diff --git a/gcc/config/mips/t-rtems b/gcc/config/mips/t-rtems
new file mode 100644
index 000000000..a2a185725
--- /dev/null
+++ b/gcc/config/mips/t-rtems
@@ -0,0 +1,34 @@
+# Custom multilibs for RTEMS
+#
+# Copyright (C) 2003, 2005, 2006 Free Software Foundation, 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/>.
+
+# default is mips1 EB hard-float
+MULTILIB_OPTIONS = mips1/mips3/mips32 EB/EL msoft-float
+MULTILIB_DIRNAMES = mips1 mips3 mips32 eb el soft-float
+MULTILIB_MATCHES = EL=mel EB=meb
+
+MULTILIB_EXCEPTIONS =
+
+# Big endian only
+MULTILIB_EXCEPTIONS += EL*
+MULTILIB_EXCEPTIONS += mips32/EL*
+
+# Little endian only
+MULTILIB_EXCEPTIONS += mips3
+MULTILIB_EXCEPTIONS += mips3/msoft-float
diff --git a/gcc/config/mips/t-sb1 b/gcc/config/mips/t-sb1
new file mode 100644
index 000000000..6c4acb320
--- /dev/null
+++ b/gcc/config/mips/t-sb1
@@ -0,0 +1,62 @@
+# Copyright (C) 2006 Free Software Foundation, 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/>.
+
+# GP-rel: G0 only
+#
+# Endianness: EB or EL
+#
+# ABIs: mabi=32
+# mabi=o64
+# mabi=o64/mlong64
+#
+# FPU: (default mhard-float)
+# msoft-float (only for mabi=32)
+#
+
+MULTILIB_EXTRA_OPTS = G0
+
+MULTILIB_OPTIONS = \
+ EB/EL \
+ mabi=32/mabi=o64 \
+ mips32/mips64 \
+ mlong64/msoft-float \
+
+MULTILIB_DIRNAMES = \
+ eb el \
+ o32 o64 \
+ mips32 mips64 \
+ long64 soft-float \
+
+MULTILIB_MATCHES = \
+ EB=meb EL=mel \
+
+MULTILIB_EXCEPTIONS = \
+ *mabi=32/*mlong64* \
+
+MULTILIB_EXCLUSIONS = \
+ mips32/!mabi=32 \
+ mabi=32/!mips32 \
+ msoft-float/!mabi=32 \
+
+# Small multilib list for quick builds and tests.
+# Must either comment out everything above these lines, or everything below
+# these lines.
+
+#MULTILIB_OPTIONS = EB/EL msoft-float
+#MULTILIB_DIRNAMES = eb el soft-float
+#MULTILIB_MATCHES = EB=meb EL=mel
diff --git a/gcc/config/mips/t-sde b/gcc/config/mips/t-sde
new file mode 100644
index 000000000..0fa2277d5
--- /dev/null
+++ b/gcc/config/mips/t-sde
@@ -0,0 +1,53 @@
+# Copyright (C) 2007, 2008 Free Software Foundation, 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/>.
+
+# Don't let CTOR_LIST end up in sdata section.
+CRTSTUFF_T_CFLAGS = -G 0
+
+# Assemble startup files.
+$(T)crti.o: $(srcdir)/config/mips/crti.asm $(GCC_PASSES)
+ $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(MULTILIB_CFLAGS) $(INCLUDES) \
+ -c -o $(T)crti.o -x assembler-with-cpp $(srcdir)/config/mips/crti.asm
+
+$(T)crtn.o: $(srcdir)/config/mips/crtn.asm $(GCC_PASSES)
+ $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(MULTILIB_CFLAGS) $(INCLUDES) \
+ -c -o $(T)crtn.o -x assembler-with-cpp $(srcdir)/config/mips/crtn.asm
+
+MULTILIB_OPTIONS = EL/EB mips32/mips32r2/mips64/mips64r2 mips16 msoft-float/mfp64 mcode-readable=no
+MULTILIB_DIRNAMES = el eb mips32 mips32r2 mips64 mips64r2 mips16 sof f64 spram
+MULTILIB_MATCHES = EL=mel EB=meb
+
+# The -mfp64 option is only valid in conjunction with -mips32r2.
+ifneq ($(filter MIPS_ISA_DEFAULT=33,$(tm_defines)),)
+MULTILIB_EXCLUSIONS := mips32/mfp64 mips64/mfp64 mips64r2/mfp64
+else
+MULTILIB_EXCLUSIONS := !mips32r2/mfp64
+endif
+
+# Don't build 64-bit MIPS16 multilibs.
+ifneq ($(filter MIPS_ISA_DEFAULT=6%,$(tm_defines)),)
+MULTILIB_EXCLUSIONS += !mips32/!mips32r2/mips16
+else
+MULTILIB_EXCLUSIONS += mips64/mips16 mips64r2/mips16
+endif
+
+EXTRA_MULTILIB_PARTS = crtbegin.o crtend.o crti.o crtn.o
+
+# Build the multilibs.
+LIBGCC = stmp-multilib
+INSTALL_LIBGCC = install-multilib
diff --git a/gcc/config/mips/t-sdemtk b/gcc/config/mips/t-sdemtk
new file mode 100644
index 000000000..40a8294fd
--- /dev/null
+++ b/gcc/config/mips/t-sdemtk
@@ -0,0 +1,44 @@
+# Copyright (C) 2007, 2008 Free Software Foundation, 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/>.
+
+# Override newlib settings in t-sde and set up for building
+# against SDE header files and libraries.
+
+MULTILIB_OPTIONS = EL/EB mips32/mips32r2/mips64/mips64r2 mips16 msoft-float/mno-float/mfp64
+MULTILIB_DIRNAMES = el eb mips32 mips32r2 mips64 mips64r2 mips16 sof nof f64
+
+# Remove stdarg.h and stddef.h from USER_H.
+USER_H = $(srcdir)/ginclude/float.h \
+ $(srcdir)/ginclude/iso646.h \
+ $(srcdir)/ginclude/stdbool.h \
+ $(srcdir)/ginclude/varargs.h \
+ $(EXTRA_HEADERS)
+
+# Don't run fixinclude
+STMP_FIXINC = stmp-sdefixinc
+stmp-sdefixinc: gsyslimits.h
+ rm -rf include; mkdir include
+ chmod a+rx include
+ rm -f include/syslimits.h
+ cp $(srcdir)/gsyslimits.h include/syslimits.h
+ chmod a+r include/syslimits.h
+ $(STAMP) stmp-sdefixinc
+
+# Don't build FPBIT and DPBIT; we'll be using the SDE soft-float library.
+FPBIT =
+DPBIT =
diff --git a/gcc/config/mips/t-slibgcc-irix b/gcc/config/mips/t-slibgcc-irix
new file mode 100644
index 000000000..6227ccef3
--- /dev/null
+++ b/gcc/config/mips/t-slibgcc-irix
@@ -0,0 +1,52 @@
+# Copyright (C) 2004, 2005 Free Software Foundation, 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/>.
+
+# Build a shared libgcc library.
+
+SHLIB_EXT = .so
+SHLIB_SOLINK = @shlib_base_name@.so
+SHLIB_SOVERSION = 1
+SHLIB_SONAME = @shlib_base_name@.so.$(SHLIB_SOVERSION)
+SHLIB_MAP = @shlib_map_file@
+SHLIB_OBJS = @shlib_objs@
+SHLIB_DIR = @multilib_dir@
+SHLIB_SLIBDIR_QUAL = @shlib_slibdir_qual@
+SHLIB_LC = -lc
+
+SHLIB_LINK = $(GCC_FOR_TARGET) $(LIBGCC2_CFLAGS) -shared -nodefaultlibs \
+ -Wl,-soname,$(SHLIB_SONAME) \
+ -o $(SHLIB_DIR)/$(SHLIB_SONAME).tmp @multilib_flags@ \
+ $(SHLIB_OBJS) $(SHLIB_LC) && \
+ rm -f $(SHLIB_DIR)/$(SHLIB_SOLINK) && \
+ if [ -f $(SHLIB_DIR)/$(SHLIB_SONAME) ]; then \
+ mv -f $(SHLIB_DIR)/$(SHLIB_SONAME) \
+ $(SHLIB_DIR)/$(SHLIB_SONAME).backup; \
+ else true; fi && \
+ mv $(SHLIB_DIR)/$(SHLIB_SONAME).tmp $(SHLIB_DIR)/$(SHLIB_SONAME) && \
+ $(LN_S) $(SHLIB_SONAME) $(SHLIB_DIR)/$(SHLIB_SOLINK)
+# $(slibdir) double quoted to protect it from expansion while building
+# libgcc.mk. We want this delayed until actual install time.
+SHLIB_INSTALL = \
+ $$(mkinstalldirs) $$(DESTDIR)$$(slibdir)$(SHLIB_SLIBDIR_QUAL); \
+ $(INSTALL_DATA) $(SHLIB_DIR)/$(SHLIB_SONAME) \
+ $$(DESTDIR)$$(slibdir)$(SHLIB_SLIBDIR_QUAL)/$(SHLIB_SONAME); \
+ rm -f $$(DESTDIR)$$(slibdir)$(SHLIB_SLIBDIR_QUAL)/$(SHLIB_SOLINK); \
+ $(LN_S) $(SHLIB_SONAME) \
+ $$(DESTDIR)$$(slibdir)$(SHLIB_SLIBDIR_QUAL)/$(SHLIB_SOLINK)
+SHLIB_MKMAP = $(srcdir)/mkmap-flat.awk
+SHLIB_MAPFILES = $(srcdir)/libgcc-std.ver
diff --git a/gcc/config/mips/t-sr71k b/gcc/config/mips/t-sr71k
new file mode 100644
index 000000000..5eb96a12e
--- /dev/null
+++ b/gcc/config/mips/t-sr71k
@@ -0,0 +1,67 @@
+# Copyright (C) 2002, 2003, 2010 Free Software Foundation, 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/>.
+
+# Suppress building libgcc1.a, since the MIPS compiler port is complete
+# and does not need anything from libgcc1.a.
+LIBGCC1 =
+CROSS_LIBGCC1 =
+
+EXTRA_MULTILIB_PARTS = crtbegin.o crtend.o crti.o crtn.o
+# Don't let CTOR_LIST end up in sdata section.
+CRTSTUFF_T_CFLAGS = -G 0
+
+# Assemble startup files.
+$(T)crti.o: $(srcdir)/config/mips/crti.asm $(GCC_PASSES)
+ $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(MULTILIB_CFLAGS) $(INCLUDES) \
+ -c -o $(T)crti.o -x assembler-with-cpp $(srcdir)/config/mips/crti.asm
+
+$(T)crtn.o: $(srcdir)/config/mips/crtn.asm $(GCC_PASSES)
+ $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(MULTILIB_CFLAGS) $(INCLUDES) \
+ -c -o $(T)crtn.o -x assembler-with-cpp $(srcdir)/config/mips/crtn.asm
+
+# We must build libgcc2.a with -G 0, in case the user wants to link
+# without the $gp register.
+TARGET_LIBGCC2_CFLAGS = -G 0
+
+# fp-bit and dp-bit are really part of libgcc1, but this will cause
+# them to be built correctly, so... [taken from t-sparclite]
+# We want fine grained libraries, so use the new code to build the
+# floating point emulation libraries.
+FPBIT = fp-bit.c
+DPBIT = dp-bit.c
+
+dp-bit.c: $(srcdir)/config/fp-bit.c
+ echo '#ifdef __MIPSEL__' > dp-bit.c
+ echo '#define FLOAT_BIT_ORDER_MISMATCH' >> dp-bit.c
+ echo '#endif' >> dp-bit.c
+ cat $(srcdir)/config/fp-bit.c >> dp-bit.c
+
+fp-bit.c: $(srcdir)/config/fp-bit.c
+ echo '#define FLOAT' > fp-bit.c
+ echo '#ifdef __MIPSEL__' >> fp-bit.c
+ echo '#define FLOAT_BIT_ORDER_MISMATCH' >> fp-bit.c
+ echo '#endif' >> fp-bit.c
+ cat $(srcdir)/config/fp-bit.c >> fp-bit.c
+
+# Build the libraries for both hard and soft floating point
+
+MULTILIB_OPTIONS = EL/EB msoft-float mips2
+MULTILIB_DIRNAMES = el eb soft-float mips2
+
+LIBGCC = stmp-multilib
+INSTALL_LIBGCC = install-multilib
diff --git a/gcc/config/mips/t-st b/gcc/config/mips/t-st
new file mode 100644
index 000000000..83115f6fd
--- /dev/null
+++ b/gcc/config/mips/t-st
@@ -0,0 +1,32 @@
+# Copyright (C) 2008 Free Software Foundation, 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/>.
+
+MULTILIB_OPTIONS = march=loongson2e/march=loongson2f mabi=n32/mabi=32/mabi=64
+MULTILIB_DIRNAMES = 2e 2f lib32 lib lib64
+
+MULTILIB_OSDIRNAMES = march.loongson2e/mabi.n32=../lib32/2e
+MULTILIB_OSDIRNAMES += march.loongson2e/mabi.32=../lib/2e
+MULTILIB_OSDIRNAMES += march.loongson2e/mabi.64=../lib64/2e
+MULTILIB_OSDIRNAMES += march.loongson2f/mabi.n32=../lib32/2f
+MULTILIB_OSDIRNAMES += march.loongson2f/mabi.32=../lib/2f
+MULTILIB_OSDIRNAMES += march.loongson2f/mabi.64=../lib64/2f
+MULTILIB_OSDIRNAMES += mabi.n32=../lib32
+MULTILIB_OSDIRNAMES += mabi.32=../lib
+MULTILIB_OSDIRNAMES += mabi.64=../lib64
+
+EXTRA_MULTILIB_PARTS=crtbegin.o crtend.o crtbeginS.o crtendS.o crtbeginT.o
diff --git a/gcc/config/mips/t-vr b/gcc/config/mips/t-vr
new file mode 100644
index 000000000..81efef9b8
--- /dev/null
+++ b/gcc/config/mips/t-vr
@@ -0,0 +1,130 @@
+# Copyright (C) 2002, 2004, 2005 Free Software Foundation, 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/>.
+
+# BEGIN boiler-plate MIPS stuff
+
+# Don't let CTOR_LIST end up in sdata section.
+CRTSTUFF_T_CFLAGS = -G 0
+
+# We must build libgcc2.a with -G 0, in case the user wants to link
+# without the $gp register.
+TARGET_LIBGCC2_CFLAGS = -G 0
+
+LIB2FUNCS_STATIC_EXTRA = $(srcdir)/config/mips/mips16.S \
+ $(srcdir)/config/mips/vr4120-div.S
+EXTRA_MULTILIB_PARTS = crtbegin.o crtend.o crti.o crtn.o
+
+# Assemble startup files.
+$(T)crti.o: $(srcdir)/config/mips/crti.asm $(GCC_PASSES)
+ $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(MULTILIB_CFLAGS) $(INCLUDES) \
+ -c -o $(T)crti.o -x assembler-with-cpp $(srcdir)/config/mips/crti.asm
+
+$(T)crtn.o: $(srcdir)/config/mips/crtn.asm $(GCC_PASSES)
+ $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(MULTILIB_CFLAGS) $(INCLUDES) \
+ -c -o $(T)crtn.o -x assembler-with-cpp $(srcdir)/config/mips/crtn.asm
+
+# END boiler-plate
+
+# Main multilibs
+# --------------
+#
+# Endianness: EB or EL
+#
+# ABIs: mabi=32
+# mabi=o64
+# mabi=eabi
+# mabi=eabi/mlong32
+# mabi=eabi/mgp32
+# mabi=eabi/mgp32/mlong64
+#
+# Architecture: march=vr4120 with -mfix-vr4120
+# march=vr4130 with -mfix-vr4130 (default)
+# march=vr5000
+# march=vr5400
+# march=vr5500
+#
+# Total: 2 * 6 * 5 = 60 multilibs.
+#
+#
+# Extra vr4300 multilibs
+# ----------------------
+#
+# Endianness: EB or EL
+#
+# ABI: o64
+#
+# Architecture: vr4300.
+#
+# Total: 2 * 1 * 2 = 2 multilibs.
+#
+#
+# Extra MIPS16 multilibs
+# ----------------------
+#
+# Endianness: EB or EL
+#
+# ABIs: mabi=o64
+# mabi=eabi/mlong32
+# mabi=eabi/mgp32
+#
+# Architecture: march=vr4120 with -mfix-vr4120
+# march=vr4130 with -mfix-vr4130 (default)
+#
+# Total: 2 * 3 * 2 = 12 multilibs.
+MULTILIB_OPTIONS = \
+ EL/EB \
+ mabi=32/mabi=o64/mabi=eabi \
+ mgp32 \
+ mlong64 \
+ mips16 \
+ mfix-vr4120/mfix-vr4130/march=vr4300/march=vr5000/march=vr5400/march=vr5500
+
+MULTILIB_DIRNAMES = \
+ el eb \
+ o32 o64 eabi \
+ gp32 \
+ long64 \
+ mips16 \
+ vr4120 vr4130 vr4300 vr5000 vr5400 vr5500
+
+MULTILIB_MATCHES = EL=mel EB=meb mfix-vr4120=march?vr4120 \
+ mfix-vr4130=march?vr4130
+
+# Assume a 41xx-series is the default: we'd need a *mips16 entry if
+# the default processor didn't support mips16. Also assume the
+# default ABI is EABI64 -mlong32.
+MULTILIB_EXCEPTIONS = \
+ *mabi=32/mlong64* \
+ *mabi=32/mgp32* \
+ *mabi=o64/mgp32* \
+ *mabi=o64/mlong64* \
+ *mips16/march=vr5* \
+ *mips16/march=vr4300 \
+ $(MIPS16_EXCEPTIONS) \
+ $(VR4300_EXCEPTIONS)
+
+MIPS16_EXCEPTIONS = \
+ *mabi=32*mips16* \
+ *mlong64*mips16*
+
+VR4300_EXCEPTIONS = \
+ *mabi=32*march=vr4300 \
+ *mgp32*march=vr4300 \
+ *mlong64*march=vr4300 \
+ march=vr4300 \
+ E[LB]/march=vr4300
diff --git a/gcc/config/mips/t-vxworks b/gcc/config/mips/t-vxworks
new file mode 100644
index 000000000..ac2fa9d9f
--- /dev/null
+++ b/gcc/config/mips/t-vxworks
@@ -0,0 +1,35 @@
+# Copyright (C) 2003, 2007 Free Software Foundation, 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/>.
+
+# Multilibs for VxWorks. We want these 8 architecture combinations:
+#
+# {-mips2,-mips3} x {-EB,-EL} x {-mhard-float,-msoft-float}
+#
+# where the first option in each group is the default. The -mips2
+# multilibs use o32 and the -mips3 multilibs use o64.
+#
+# We want three multilibs for each architecture combination:
+# default (kernel mode), -mrtp and -mrtp/-fPIC.
+MULTILIB_OPTIONS = mabi=o64 mips3 EL msoft-float mrtp fPIC
+MULTILIB_DIRNAMES = o64 mips3 EL msoft-float mrtp pic
+MULTILIB_MATCHES = EL=mel fPIC=fpic
+MULTILIB_EXCEPTIONS = mips3* mabi=o64 fPIC \
+ $(addprefix mabi=o64/, EL* msoft-float* mrtp* fPIC*) \
+ $(addsuffix /fPIC, *mabi=o64 *mips3 *EL *msoft-float)
+
+MUTLILIB_EXTRA_OPTS = -G 0 -mno-branch-likely
diff --git a/gcc/config/mips/vr.h b/gcc/config/mips/vr.h
new file mode 100644
index 000000000..3a89e69ce
--- /dev/null
+++ b/gcc/config/mips/vr.h
@@ -0,0 +1,58 @@
+/* Definitions of target machine for GNU compiler.
+ NEC VR Series Processors
+ Copyright (c) 2002, 2004, 2005, 2007, 2008 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/>. */
+
+#define DEFAULT_VR_ARCH "mfix-vr4130"
+#undef MULTILIB_DEFAULTS
+#define MULTILIB_DEFAULTS \
+ { MULTILIB_ENDIAN_DEFAULT, \
+ MULTILIB_ABI_DEFAULT, \
+ DEFAULT_VR_ARCH }
+
+#undef DRIVER_SELF_SPECS
+#define DRIVER_SELF_SPECS \
+ /* Enforce the default architecture. This is mostly for \
+ the assembler's benefit. */ \
+ "%{!march=*:%{!mfix-vr4120:%{!mfix-vr4130:" \
+ "-" DEFAULT_VR_ARCH "}}}", \
+ \
+ /* Make -mfix-vr4120 imply -march=vr4120. This cuts down \
+ on command-line tautology and makes it easier for t-vr to \
+ provide a -mfix-vr4120 multilib. */ \
+ "%{mfix-vr4120:%{!march=*:-march=vr4120}}", \
+ \
+ /* Same idea for -mfix-vr4130. */ \
+ "%{mfix-vr4130:%{!march=*:-march=vr4130}}", \
+ \
+ /* Infer the default float setting from -march. */ \
+ MIPS_ARCH_FLOAT_SPEC, \
+ \
+ /* Make -mabi=eabi -mlong32 the default. */ \
+ "%{!mabi=*:-mabi=eabi %{!mlong*:-mlong32}}", \
+ \
+ /* Make sure -mlong64 multilibs are chosen when 64-bit longs \
+ are needed. */ \
+ "%{mabi=eabi:%{!mlong*:%{!mgp32:-mlong64}}}", \
+ \
+ /* Remove -mgp32 if it is redundant. */ \
+ "%{mabi=32:%<mgp32}", \
+ \
+ /* Configuration-independent MIPS rules. */ \
+ BASE_DRIVER_SELF_SPECS
diff --git a/gcc/config/mips/vr4120-div.S b/gcc/config/mips/vr4120-div.S
new file mode 100644
index 000000000..79ede3de9
--- /dev/null
+++ b/gcc/config/mips/vr4120-div.S
@@ -0,0 +1,74 @@
+/* Support file for -mfix-vr4120.
+ Copyright (C) 2002, 2004, 2007 Free Software Foundation, 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/>. */
+
+/* This file contains functions which implement divsi3 and modsi3 for
+ -mfix-vr4120. div and ddiv do not give the correct result when one
+ of the operands is negative. */
+
+ .set nomips16
+
+#define DIV \
+ xor $3,$4,$5 /* t = x ^ y */ ; \
+ li $2,0x80000000; \
+ .set noreorder; \
+ bgez $4,1f /* x >= 0 */; \
+ and $3,$3,$2 /* t = (x ^ y) & 0x80000000 in delay slot */ ;\
+ .set reorder; \
+ subu $4,$0,$4 /* x = -x */ ; \
+1:; \
+ .set noreorder; \
+ bgez $5,2f /* y >= 0 */ ; \
+ nop; \
+ subu $5,$0,$5 /* y = -y */ ; \
+ .set reorder; \
+2:; \
+ divu $0,$4,$5; /* we use divu because of INT_MIN */ \
+ .set noreorder; \
+ bne $5,$0,3f; \
+ nop; \
+ break 7 /* division on zero y */ ; \
+3:; \
+ .set reorder; \
+ mflo $2 /* r = x / y */ ; \
+ .set noreorder; \
+ beq $3,$0,4f /* t == 0 */ ; \
+ nop; \
+ subu $2,$0,$2 /* r = -r */ ; \
+ .set reorder; \
+4:
+
+ .globl __vr4120_divsi3
+ .ent __vr4120_divsi3
+__vr4120_divsi3:
+ DIV
+ j $31
+ .end __vr4120_divsi3
+
+ .globl __vr4120_modsi3
+ .ent __vr4120_modsi3
+__vr4120_modsi3:
+ move $6,$4 # x1 = x
+ move $7,$5 # y1 = y
+ DIV
+ mult $2,$7 # r = r * y1
+ mflo $2
+ .set noreorder
+ j $31
+ subu $2,$6,$2 # r = x1 - r in delay slot
+ .end __vr4120_modsi3
diff --git a/gcc/config/mips/vxworks.h b/gcc/config/mips/vxworks.h
new file mode 100644
index 000000000..f6c84b9a6
--- /dev/null
+++ b/gcc/config/mips/vxworks.h
@@ -0,0 +1,82 @@
+/* Copyright (C) 1999, 2003, 2004, 2007, 2008, 2010, 2011
+ Free Software Foundation, 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/>. */
+
+#undef TARGET_VERSION
+#define TARGET_VERSION fprintf (stderr, " (MIPS, VxWorks syntax)");
+
+#undef ASM_SPEC
+#define ASM_SPEC "\
+%{!G:-G 0} %{G*} %(endian_spec) %{mips1} %{mips2} %{mips3} %{mips4} \
+%{mips32} %{mips32r2} %{mips64} \
+%{mips16:%{!mno-mips16:-mips16}} %{mno-mips16:-no-mips16} \
+%(subtarget_asm_optimizing_spec) \
+%(subtarget_asm_debugging_spec) \
+%{mabi=*} %{!mabi*: %(asm_abi_default_spec)} \
+%{mgp32} %{mgp64} %{march=*} %{mxgot:-xgot} \
+%{mtune=*} \
+%(subtarget_asm_spec)"
+
+#undef LINK_SPEC
+#define LINK_SPEC "\
+%(endian_spec) \
+%{!G:-G 0} %{G*} %{mips1} %{mips2} %{mips3} %{mips4} %{mips32} %{mips64} " \
+VXWORKS_LINK_SPEC
+
+#undef LIB_SPEC
+#define LIB_SPEC VXWORKS_LIB_SPEC
+#undef STARTFILE_SPEC
+#define STARTFILE_SPEC VXWORKS_STARTFILE_SPEC
+#undef ENDFILE_SPEC
+#define ENDFILE_SPEC VXWORKS_ENDFILE_SPEC
+
+#define TARGET_OS_CPP_BUILTINS() \
+ do \
+ { \
+ if (TARGET_64BIT) \
+ builtin_define ("CPU=MIPS64"); \
+ else \
+ builtin_define ("CPU=MIPS32"); \
+ if (TARGET_BIG_ENDIAN) \
+ builtin_define ("MIPSEB"); \
+ else \
+ builtin_define ("MIPSEL"); \
+ if (TARGET_SOFT_FLOAT) \
+ builtin_define ("SOFT_FLOAT"); \
+ VXWORKS_OS_CPP_BUILTINS (); \
+ } \
+ while (0)
+
+#undef SUBTARGET_CPP_SPEC
+#define SUBTARGET_CPP_SPEC VXWORKS_ADDITIONAL_CPP_SPEC
+
+/* No sdata. */
+#undef MIPS_DEFAULT_GVALUE
+#define MIPS_DEFAULT_GVALUE 0
+
+/* No _mcount profiling on VxWorks. */
+#undef FUNCTION_PROFILER
+#define FUNCTION_PROFILER VXWORKS_FUNCTION_PROFILER
+
+#undef SUBTARGET_ASM_SPEC
+#define SUBTARGET_ASM_SPEC "%{mrtp:%{fPIC|fpic:-mvxworks-pic}}"
+
+#undef SUBTARGET_OVERRIDE_OPTIONS
+#define SUBTARGET_OVERRIDE_OPTIONS VXWORKS_OVERRIDE_OPTIONS
+
+#undef DBX_REGISTER_NUMBER
diff --git a/gcc/config/mips/x-native b/gcc/config/mips/x-native
new file mode 100644
index 000000000..5e31121ed
--- /dev/null
+++ b/gcc/config/mips/x-native
@@ -0,0 +1,3 @@
+driver-native.o : $(srcdir)/config/mips/driver-native.c \
+ $(CONFIG_H) $(SYSTEM_H)
+ $(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) $<
diff --git a/gcc/config/mips/xlr.md b/gcc/config/mips/xlr.md
new file mode 100644
index 000000000..69913b7b2
--- /dev/null
+++ b/gcc/config/mips/xlr.md
@@ -0,0 +1,89 @@
+;; DFA-based pipeline description for the XLR.
+;; Copyright (C) 2008, 2009 Free Software Foundation, Inc.
+;;
+;; xlr.md Machine Description for the RMI XLR Microprocessor
+;; 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/>.
+
+(define_automaton "xlr_main,xlr_muldiv")
+
+;; Definitions for xlr_main automaton.
+(define_cpu_unit "xlr_main_pipe" "xlr_main")
+
+(define_insn_reservation "ir_xlr_alu_slt" 2
+ (and (eq_attr "cpu" "xlr")
+ (eq_attr "type" "slt"))
+ "xlr_main_pipe")
+
+;; Integer arithmetic instructions.
+(define_insn_reservation "ir_xlr_alu" 1
+ (and (eq_attr "cpu" "xlr")
+ (eq_attr "type" "move,arith,shift,clz,logical,signext,const,unknown,multi,nop,trap"))
+ "xlr_main_pipe")
+
+;; Integer arithmetic instructions.
+(define_insn_reservation "ir_xlr_condmove" 2
+ (and (eq_attr "cpu" "xlr")
+ (eq_attr "type" "condmove"))
+ "xlr_main_pipe")
+
+;; Load/store instructions.
+(define_insn_reservation "ir_xlr_load" 4
+ (and (eq_attr "cpu" "xlr")
+ (eq_attr "type" "load"))
+ "xlr_main_pipe")
+
+(define_insn_reservation "ir_xlr_store" 1
+ (and (eq_attr "cpu" "xlr")
+ (eq_attr "type" "store"))
+ "xlr_main_pipe")
+
+(define_insn_reservation "ir_xlr_prefetch_x" 1
+ (and (eq_attr "cpu" "xlr")
+ (eq_attr "type" "prefetch,prefetchx"))
+ "xlr_main_pipe")
+
+;; Branch instructions - use branch misprediction latency.
+(define_insn_reservation "ir_xlr_branch" 1
+ (and (eq_attr "cpu" "xlr")
+ (eq_attr "type" "branch,jump,call"))
+ "xlr_main_pipe")
+
+;; Coprocessor move instructions.
+(define_insn_reservation "ir_xlr_xfer" 2
+ (and (eq_attr "cpu" "xlr")
+ (eq_attr "type" "mtc,mfc"))
+ "xlr_main_pipe")
+
+(define_bypass 5 "ir_xlr_xfer" "ir_xlr_xfer")
+
+;; Definitions for the xlr_muldiv automaton.
+(define_cpu_unit "xlr_imuldiv_nopipe" "xlr_muldiv")
+
+(define_insn_reservation "ir_xlr_imul" 8
+ (and (eq_attr "cpu" "xlr")
+ (eq_attr "type" "imul,imul3,imadd"))
+ "xlr_main_pipe,xlr_imuldiv_nopipe*6")
+
+(define_insn_reservation "ir_xlr_div" 68
+ (and (eq_attr "cpu" "xlr")
+ (eq_attr "type" "idiv"))
+ "xlr_main_pipe,xlr_imuldiv_nopipe*67")
+
+(define_insn_reservation "xlr_hilo" 2
+ (and (eq_attr "cpu" "xlr")
+ (eq_attr "type" "mfhilo,mthilo"))
+ "xlr_imuldiv_nopipe")