diff options
Diffstat (limited to 'contrib/gcc/config/mips')
65 files changed, 27826 insertions, 0 deletions
diff --git a/contrib/gcc/config/mips/24k.md b/contrib/gcc/config/mips/24k.md new file mode 100644 index 0000000..0dbb9f0 --- /dev/null +++ b/contrib/gcc/config/mips/24k.md @@ -0,0 +1,455 @@ +;; 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 24K is a single-issue processor with a half-clocked fpu. +;; The 24Kx is 24k with 1:1 clocked fpu. +;; +;; References: +;; "MIPS32 24K Processor Core Family Software User's Manual, Rev 3.04." +;; +;; Copyright (C) 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 2, 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 COPYING. If not, write to the +;; Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, +;; MA 02110-1301, USA. + +(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" "24k,24kx") + (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" "24k,24kx") + (eq_attr "type" "arith,const,nop,shift,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" "24k,24kx") + (eq_attr "type" "call,jump")) + "r24k_iss+r24k_ixu_arith") + +;; 3b. branch consumer +(define_insn_reservation "r24k_int_branch" 1 + (and (eq_attr "cpu" "24k,24kx") + (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" "24k,24kx") + (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" "24k,24kx") + (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" "24k,24kx") + (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" "24k,24kx") + (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" "24k,24kx") + (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" "24k,24kx") + (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" "24k,24kx") +;; (eq_attr "type" "cop0")) +;; "r24k_iss+r24k_ixu_arith") + + +;; 6. Store +(define_insn_reservation "r24k_int_store" 1 + (and (eq_attr "cpu" "24k,24kx") + (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" "24k,24kx") + (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" "24k,24kx") + (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" "24k,24kx") + (eq_attr "type" "unknown")) + "r24k_iss") + + +;; 9. Prefetch +(define_insn_reservation "r24k_int_prefetch" 1 + (and (eq_attr "cpu" "24k,24kx") + (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") + +;; 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 24kx is a 24k configured with 1:1 cpu and fpu, so use +;; the unscaled timings + +(define_reservation "r24k_fpu_iss" "r24k_iss+(r24k_fpu_arith*2)") + +;; fadd, fabs, fneg +(define_insn_reservation "r24k_fadd" 8 + (and (eq_attr "cpu" "24k") + (eq_attr "type" "fadd,fabs,fneg")) + "r24k_fpu_iss") + +;; fmove, fcmove +(define_insn_reservation "r24k_fmove" 8 + (and (eq_attr "cpu" "24k") + (eq_attr "type" "fmove,condmove")) + "r24k_fpu_iss") + +;; fload +(define_insn_reservation "r24k_fload" 6 + (and (eq_attr "cpu" "24k") + (eq_attr "type" "fpload,fpidxload")) + "r24k_fpu_iss") + +;; fstore +(define_insn_reservation "r24k_fstore" 2 + (and (eq_attr "cpu" "24k") + (eq_attr "type" "fpstore")) + "r24k_fpu_iss") + +;; fmul, fmadd +(define_insn_reservation "r24k_fmul_sf" 8 + (and (eq_attr "cpu" "24k") + (and (eq_attr "type" "fmul,fmadd") + (eq_attr "mode" "SF"))) + "r24k_fpu_iss") + +(define_insn_reservation "r24k_fmul_df" 10 + (and (eq_attr "cpu" "24k") + (and (eq_attr "type" "fmul,fmadd") + (eq_attr "mode" "DF"))) + "r24k_fpu_iss,(r24k_fpu_arith*2)") + + +;; fdiv, fsqrt, frsqrt +(define_insn_reservation "r24k_fdiv_sf" 34 + (and (eq_attr "cpu" "24k") + (and (eq_attr "type" "fdiv,fsqrt,frsqrt") + (eq_attr "mode" "SF"))) + "r24k_fpu_iss,(r24k_fpu_arith*26)") + +(define_insn_reservation "r24k_fdiv_df" 64 + (and (eq_attr "cpu" "24k") + (and (eq_attr "type" "fdiv,fsqrt") + (eq_attr "mode" "DF"))) + "r24k_fpu_iss,(r24k_fpu_arith*56)") + +;; frsqrt +(define_insn_reservation "r24k_frsqrt_df" 70 + (and (eq_attr "cpu" "24k") + (and (eq_attr "type" "frsqrt") + (eq_attr "mode" "DF"))) + "r24k_fpu_iss,(r24k_fpu_arith*60)") + +;; fcmp +(define_insn_reservation "r24k_fcmp" 4 + (and (eq_attr "cpu" "24k") + (eq_attr "type" "fcmp")) + "r24k_fpu_iss") + +;; fcmp -> movf.fmt & movt.fmt bypass (dependency must be on the condition) +(define_bypass 2 "r24k_fcmp" "r24k_fmove") + +;; fcvt (cvt.d.s, cvt.[sd].[wl]) +(define_insn_reservation "r24k_fcvt_i2f_s2d" 8 + (and (eq_attr "cpu" "24k") + (and (eq_attr "type" "fcvt") + (eq_attr "cnv_mode" "I2S,I2D,S2D"))) + "r24k_fpu_iss") + +;; fcvt (cvt.s.d) +(define_insn_reservation "r24k_fcvt_s2d" 12 + (and (eq_attr "cpu" "24k") + (and (eq_attr "type" "fcvt") + (eq_attr "cnv_mode" "D2S"))) + "r24k_fpu_iss") + +;; fcvt (cvt.[wl].[sd], etc) +(define_insn_reservation "r24k_fcvt_f2i" 10 + (and (eq_attr "cpu" "24k") + (and (eq_attr "type" "fcvt") + (eq_attr "cnv_mode" "S2I,D2I"))) + "r24k_fpu_iss") + +;; fxfer (mfc1, mfhc1, mtc1, mthc1) +(define_insn_reservation "r24k_fxfer" 4 + (and (eq_attr "cpu" "24k") + (eq_attr "type" "xfer")) + "r24k_fpu_iss") + +;; -------------------------------------------------------------- +;; Bypass to Consumer +;; -------------------------------------------------------------- +;; r24k_fcvt_f2i->l/s base : 11 cycles +;; r24k_fcvt_f2i->prefetch : 11 cycles +(define_bypass 11 "r24k_fcvt_f2i" "r24k_int_load") +(define_bypass 11 "r24k_fcvt_f2i" "r24k_int_store" "!store_data_bypass_p") +(define_bypass 11 "r24k_fcvt_f2i" "r24k_int_prefetch") + +;; r24k_fxfer->l/s base : 5 cycles +;; r24k_fxfer->prefetch : 5 cycles +(define_bypass 5 "r24k_fxfer" "r24k_int_load") +(define_bypass 5 "r24k_fxfer" "r24k_int_store" "!store_data_bypass_p") +(define_bypass 5 "r24k_fxfer" "r24k_int_prefetch") + +;; -------------------------------------------------------------- +;; The 24kx is a 24k configured with 1:1 cpu and fpu, so use +;; the unscaled timings +;; -------------------------------------------------------------- + +(define_reservation "r24kx_fpu_iss" "r24k_iss+r24k_fpu_arith") + +;; fadd, fabs, fneg +(define_insn_reservation "r24kx_fadd" 4 + (and (eq_attr "cpu" "24kx") + (eq_attr "type" "fadd,fabs,fneg")) + "r24kx_fpu_iss") + +;; fmove, fcmove +(define_insn_reservation "r24kx_fmove" 4 + (and (eq_attr "cpu" "24kx") + (eq_attr "type" "fmove,condmove")) + "r24kx_fpu_iss") + +;; fload +(define_insn_reservation "r24kx_fload" 3 + (and (eq_attr "cpu" "24kx") + (eq_attr "type" "fpload,fpidxload")) + "r24kx_fpu_iss") + +;; fstore +(define_insn_reservation "r24kx_fstore" 1 + (and (eq_attr "cpu" "24kx") + (eq_attr "type" "fpstore")) + "r24kx_fpu_iss") + +;; fmul, fmadd +(define_insn_reservation "r24kx_fmul_sf" 4 + (and (eq_attr "cpu" "24kx") + (and (eq_attr "type" "fmul,fmadd") + (eq_attr "mode" "SF"))) + "r24kx_fpu_iss") + +(define_insn_reservation "r24kx_fmul_df" 5 + (and (eq_attr "cpu" "24kx") + (and (eq_attr "type" "fmul,fmadd") + (eq_attr "mode" "DF"))) + "r24kx_fpu_iss,r24k_fpu_arith") + + +;; fdiv, fsqrt, frsqrt +(define_insn_reservation "r24kx_fdiv_sf" 17 + (and (eq_attr "cpu" "24kx") + (and (eq_attr "type" "fdiv,fsqrt,frsqrt") + (eq_attr "mode" "SF"))) + "r24kx_fpu_iss,(r24k_fpu_arith*13)") + +(define_insn_reservation "r24kx_fdiv_df" 32 + (and (eq_attr "cpu" "24kx") + (and (eq_attr "type" "fdiv,fsqrt") + (eq_attr "mode" "DF"))) + "r24kx_fpu_iss,(r24k_fpu_arith*28)") + +;; frsqrt +(define_insn_reservation "r24kx_frsqrt_df" 35 + (and (eq_attr "cpu" "24kx") + (and (eq_attr "type" "frsqrt") + (eq_attr "mode" "DF"))) + "r24kx_fpu_iss,(r24k_fpu_arith*30)") + +;; fcmp +(define_insn_reservation "r24kx_fcmp" 2 + (and (eq_attr "cpu" "24kx") + (eq_attr "type" "fcmp")) + "r24kx_fpu_iss") + +;; fcmp -> movf.fmt & movt.fmt bypass (dependency must be on the condition) +(define_bypass 1 "r24kx_fcmp" "r24kx_fmove") + +;; fcvt (cvt.d.s, cvt.[sd].[wl]) +(define_insn_reservation "r24kx_fcvt_i2f_s2d" 4 + (and (eq_attr "cpu" "24kx") + (and (eq_attr "type" "fcvt") + (eq_attr "cnv_mode" "I2S,I2D,S2D"))) + "r24kx_fpu_iss") + +;; fcvt (cvt.s.d) +(define_insn_reservation "r24kx_fcvt_s2d" 6 + (and (eq_attr "cpu" "24kx") + (and (eq_attr "type" "fcvt") + (eq_attr "cnv_mode" "D2S"))) + "r24kx_fpu_iss") + +;; fcvt (cvt.[wl].[sd], etc) +(define_insn_reservation "r24kx_fcvt_f2i" 5 + (and (eq_attr "cpu" "24kx") + (and (eq_attr "type" "fcvt") + (eq_attr "cnv_mode" "S2I,D2I"))) + "r24kx_fpu_iss") + +;; fxfer (mfc1, mfhc1, mtc1, mthc1) +(define_insn_reservation "r24kx_fxfer" 2 + (and (eq_attr "cpu" "24kx") + (eq_attr "type" "xfer")) + "r24kx_fpu_iss") + +;; -------------------------------------------------------------- +;; Bypass to Consumer +;; -------------------------------------------------------------- +;; r24kx_fcvt_f2i->l/s base : 6 cycles +;; r24kx_fcvt_f2i->prefetch : 6 cycles +(define_bypass 6 "r24kx_fcvt_f2i" "r24k_int_load") +(define_bypass 6 "r24kx_fcvt_f2i" "r24k_int_store" "!store_data_bypass_p") +(define_bypass 6 "r24kx_fcvt_f2i" "r24k_int_prefetch") + +;; r24kx_fxfer->l/s base : 3 cycles +;; r24kx_fxfer->prefetch : 3 cycles +(define_bypass 3 "r24kx_fxfer" "r24k_int_load") +(define_bypass 3 "r24kx_fxfer" "r24k_int_store" "!store_data_bypass_p") +(define_bypass 3 "r24kx_fxfer" "r24k_int_prefetch") + diff --git a/contrib/gcc/config/mips/3000.md b/contrib/gcc/config/mips/3000.md new file mode 100644 index 0000000..67c7ac0 --- /dev/null +++ b/contrib/gcc/config/mips/3000.md @@ -0,0 +1,72 @@ +;; R3000 and TX39 pipeline description. +;; 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 2, 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 COPYING. If not, write to the +;; Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, +;; MA 02110-1301, USA. + + +;; 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/contrib/gcc/config/mips/4000.md b/contrib/gcc/config/mips/4000.md new file mode 100644 index 0000000..1e2875f --- /dev/null +++ b/contrib/gcc/config/mips/4000.md @@ -0,0 +1,33 @@ +;; R4000 pipeline description. +;; 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 2, 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 COPYING. If not, write to the +;; Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, +;; MA 02110-1301, USA. + + +;; 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/contrib/gcc/config/mips/4100.md b/contrib/gcc/config/mips/4100.md new file mode 100644 index 0000000..c7314d8 --- /dev/null +++ b/contrib/gcc/config/mips/4100.md @@ -0,0 +1,52 @@ +;; VR4100 and VR4120 pipeline description. +;; 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 2, 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 COPYING. If not, write to the +;; Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, +;; MA 02110-1301, USA. + + +;; 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,xfer")) + "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/contrib/gcc/config/mips/4130.md b/contrib/gcc/config/mips/4130.md new file mode 100644 index 0000000..6feeab3 --- /dev/null +++ b/contrib/gcc/config/mips/4130.md @@ -0,0 +1,136 @@ +;; +;; 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" "const,arith,shift,slt,nop")) + "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/contrib/gcc/config/mips/4300.md b/contrib/gcc/config/mips/4300.md new file mode 100644 index 0000000..7453f4c --- /dev/null +++ b/contrib/gcc/config/mips/4300.md @@ -0,0 +1,86 @@ +;; VR4300 pipeline description. +;; 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 2, 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 COPYING. If not, write to the +;; Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, +;; MA 02110-1301, USA. + + +;; 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,xfer")) + "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/contrib/gcc/config/mips/4600.md b/contrib/gcc/config/mips/4600.md new file mode 100644 index 0000000..df3f72d --- /dev/null +++ b/contrib/gcc/config/mips/4600.md @@ -0,0 +1,88 @@ +;; R4600 and R4650 pipeline description. +;; 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 2, 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 COPYING. If not, write to the +;; Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, +;; MA 02110-1301, USA. + + +;; 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/contrib/gcc/config/mips/4k.md b/contrib/gcc/config/mips/4k.md new file mode 100644 index 0000000..6f7d6d1 --- /dev/null +++ b/contrib/gcc/config/mips/4k.md @@ -0,0 +1,154 @@ +;; 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 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 2, 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 COPYING. If not, write to the +;; Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, +;; MA 02110-1301, USA. + +(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,shift,const,nop,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/contrib/gcc/config/mips/5000.md b/contrib/gcc/config/mips/5000.md new file mode 100644 index 0000000..4d9c98c --- /dev/null +++ b/contrib/gcc/config/mips/5000.md @@ -0,0 +1,81 @@ +;; VR5000 pipeline description. +;; 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 2, 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 COPYING. If not, write to the +;; Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, +;; MA 02110-1301, USA. + + +;; 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,xfer")) + "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/contrib/gcc/config/mips/5400.md b/contrib/gcc/config/mips/5400.md new file mode 100644 index 0000000..8571308 --- /dev/null +++ b/contrib/gcc/config/mips/5400.md @@ -0,0 +1,166 @@ +;; 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" "xfer")) + "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,slt,clz,const,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/contrib/gcc/config/mips/5500.md b/contrib/gcc/config/mips/5500.md new file mode 100644 index 0000000..881cbcb --- /dev/null +++ b/contrib/gcc/config/mips/5500.md @@ -0,0 +1,209 @@ +;; 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" "xfer")) + "vr55_dp0|vr55_dp1") + +(define_insn_reservation "ir_vr55_arith" 1 + (and (eq_attr "cpu" "r5500") + (eq_attr "type" "arith,shift,slt,clz,const,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/contrib/gcc/config/mips/5k.md b/contrib/gcc/config/mips/5k.md new file mode 100644 index 0000000..0fa588a --- /dev/null +++ b/contrib/gcc/config/mips/5k.md @@ -0,0 +1,230 @@ +;; 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 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 2, 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 COPYING. If not, write to the +;; Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, +;; MA 02110-1301, USA. + +(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,shift,const,nop,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" "xfer")) + "r5k_ixu_arith+r5kf_fpu_arith") diff --git a/contrib/gcc/config/mips/6000.md b/contrib/gcc/config/mips/6000.md new file mode 100644 index 0000000..ac06e7a --- /dev/null +++ b/contrib/gcc/config/mips/6000.md @@ -0,0 +1,57 @@ +;; R6000 pipeline description. +;; 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 2, 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 COPYING. If not, write to the +;; Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, +;; MA 02110-1301, USA. + + +;; 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/contrib/gcc/config/mips/7000.md b/contrib/gcc/config/mips/7000.md new file mode 100644 index 0000000..a67851c --- /dev/null +++ b/contrib/gcc/config/mips/7000.md @@ -0,0 +1,215 @@ +;; DFA-based pipeline description for the RM7000. +;; Copyright (C) 2003, 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 2, 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 COPYING. If not, write to the +;; Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, +;; MA 02110-1301, USA. + +;; ......................... +;; +;; 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,slt,clz,const,condmove,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" "xfer")) + "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/contrib/gcc/config/mips/9000.md b/contrib/gcc/config/mips/9000.md new file mode 100644 index 0000000..04ddc8c --- /dev/null +++ b/contrib/gcc/config/mips/9000.md @@ -0,0 +1,152 @@ +;; DFA-based pipeline description for the RM9000. +;; Copyright (C) 2003, 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 2, 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 COPYING. If not, write to the +;; Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, +;; MA 02110-1301, USA. + +(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,slt,clz,const,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" "xfer")) + "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/contrib/gcc/config/mips/constraints.md b/contrib/gcc/config/mips/constraints.md new file mode 100644 index 0000000..711aa15 --- /dev/null +++ b/contrib/gcc/config/mips/constraints.md @@ -0,0 +1,195 @@ +;; Constraint definitions for MIPS. +;; 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 2, 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 COPYING. If not, write to +;; the Free Software Foundation, 51 Franklin Street, Fifth Floor, +;; Boston, MA 02110-1301, USA. + +;; 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" "HI_REG" + "The @code{hi} register.") + +(define_register_constraint "l" "LO_REG" + "The @code{lo} register.") + +(define_register_constraint "x" "MD_REGS" + "The @code{hi} and @code{lo} registers.") + +(define_register_constraint "b" "ALL_REGS" + "@internal") + +(define_register_constraint "c" "TARGET_ABICALLS ? PIC_FN_ADDR_REG + : TARGET_MIPS16 ? M16_NA_REGS + : 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") + +(define_register_constraint "v" "V1_REG" + "@internal") + +(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") + +;; 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_fetch_insns (op) == 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)"))) diff --git a/contrib/gcc/config/mips/crti.asm b/contrib/gcc/config/mips/crti.asm new file mode 100644 index 0000000..f4bef6f --- /dev/null +++ b/contrib/gcc/config/mips/crti.asm @@ -0,0 +1,26 @@ +/* 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/contrib/gcc/config/mips/crtn.asm b/contrib/gcc/config/mips/crtn.asm new file mode 100644 index 0000000..5d41d3d --- /dev/null +++ b/contrib/gcc/config/mips/crtn.asm @@ -0,0 +1,29 @@ +/* 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/contrib/gcc/config/mips/dbxmdebug.h b/contrib/gcc/config/mips/dbxmdebug.h new file mode 100644 index 0000000..230e2ea --- /dev/null +++ b/contrib/gcc/config/mips/dbxmdebug.h @@ -0,0 +1,6 @@ +/* Definitions of target machine for GNU compiler, for MIPS running IRIX 5 + or IRIX 6 (O32 ABI) using the GNU assembler with stabs-in-mdebug. */ + +/* Override iris5gas.h version again to retain mips.h default. */ +#undef MDEBUG_ASM_SPEC +#define MDEBUG_ASM_SPEC "%{!gdwarf*:-mdebug} %{gdwarf*:-no-mdebug}" diff --git a/contrib/gcc/config/mips/elf.h b/contrib/gcc/config/mips/elf.h new file mode 100644 index 0000000..9d7259e --- /dev/null +++ b/contrib/gcc/config/mips/elf.h @@ -0,0 +1,54 @@ +/* Target macros for mips*-elf targets. + Copyright (C) 1994, 1997, 1999, 2000, 2002, 2003, 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 2, 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 COPYING. If not, write to +the Free Software Foundation, 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. */ + +/* 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 + +#define HANDLE_PRAGMA_PACK_PUSH_POP 1 diff --git a/contrib/gcc/config/mips/elforion.h b/contrib/gcc/config/mips/elforion.h new file mode 100644 index 0000000..9ff3a4f --- /dev/null +++ b/contrib/gcc/config/mips/elforion.h @@ -0,0 +1,22 @@ +/* Definitions of target machine for GNU compiler. MIPS ORION version with + GOFAST floating point library. + Copyright (C) 1994, 1998 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 2, 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 COPYING. If not, write to +the Free Software Foundation, 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. */ + +#define MIPS_CPU_STRING_DEFAULT "orion" diff --git a/contrib/gcc/config/mips/generic.md b/contrib/gcc/config/mips/generic.md new file mode 100644 index 0000000..f5641a9 --- /dev/null +++ b/contrib/gcc/config/mips/generic.md @@ -0,0 +1,106 @@ +;; Generic DFA-based pipeline description for MIPS targets +;; 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 2, 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 COPYING. If not, write to the +;; Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, +;; MA 02110-1301, USA. + + +;; 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") + "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" "xfer") + "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/contrib/gcc/config/mips/iris.h b/contrib/gcc/config/mips/iris.h new file mode 100644 index 0000000..dbde9d1 --- /dev/null +++ b/contrib/gcc/config/mips/iris.h @@ -0,0 +1,222 @@ +/* Definitions of target machine for GNU compiler. Generic IRIX version. + Copyright (C) 1993, 1995, 1996, 1998, 2000, + 2001, 2002, 2003, 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 2, 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 COPYING. If not, write to +the Free Software Foundation, 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. */ + +/* We are compiling for IRIX now. */ +#undef TARGET_IRIX +#define TARGET_IRIX 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) + +/* The MIPSpro o32 linker warns about not linking .comment sections. */ +#undef IDENT_ASM_OP + +#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 + +/* Also do this for libcalls. */ +#undef TARGET_ASM_EXTERNAL_LIBCALL +#define TARGET_ASM_EXTERNAL_LIBCALL irix_output_external_libcall + +/* The linker needs a space after "-o". */ +#define SWITCHES_NEED_SPACES "o" + +/* 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 + +/* Plain char is unsigned in the SGI compiler. */ +#undef DEFAULT_SIGNED_CHAR +#define DEFAULT_SIGNED_CHAR 0 + +#define WORD_SWITCH_TAKES_ARG(STR) \ + (DEFAULT_WORD_SWITCH_TAKES_ARG (STR) \ + || strcmp (STR, "rpath") == 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 (mips_abi == ABI_32) \ + { \ + builtin_define ("_ABIO32=1"); \ + builtin_define ("_MIPS_SIM=_ABIO32"); \ + builtin_define ("_MIPS_SZINT=32"); \ + builtin_define ("_MIPS_SZLONG=32"); \ + builtin_define ("_MIPS_SZPTR=32"); \ + } \ + else if (mips_abi == ABI_64) \ + { \ + builtin_define ("_ABI64=3"); \ + builtin_define ("_MIPS_SIM=_ABI64"); \ + builtin_define ("_MIPS_SZINT=32"); \ + builtin_define ("_MIPS_SZLONG=64"); \ + builtin_define ("_MIPS_SZPTR=64"); \ + } \ + else \ + { \ + builtin_define ("_ABIN32=2"); \ + builtin_define ("_MIPS_SIM=_ABIN32"); \ + builtin_define ("_MIPS_SZINT=32"); \ + builtin_define ("_MIPS_SZLONG=32"); \ + builtin_define ("_MIPS_SZPTR=32"); \ + } \ + \ + if (!ISA_MIPS1 && !ISA_MIPS2) \ + builtin_define ("_COMPILER_VERSION=601"); \ + \ + if (!TARGET_FLOAT64) \ + builtin_define ("_MIPS_FPSET=16"); \ + else \ + builtin_define ("_MIPS_FPSET=32"); \ + \ + /* 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 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 + +/* 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} -init __gcc_init -fini __gcc_fini " IRIX_SUBTARGET_LINK_SPEC + +/* A linker error can empirically be avoided by removing duplicate + library search directories. */ +#define LINK_ELIMINATE_DUPLICATE_LDIRECTORIES 1 + +/* 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 + +/* The native o32 IRIX 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 diff --git a/contrib/gcc/config/mips/iris5.h b/contrib/gcc/config/mips/iris5.h new file mode 100644 index 0000000..10c2830 --- /dev/null +++ b/contrib/gcc/config/mips/iris5.h @@ -0,0 +1,45 @@ +/* Definitions of target machine for GNU compiler. IRIX version 5. + Copyright (C) 1993, 1995, 1996, 1998, 2000, + 2001, 2002, 2003, 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 2, 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 COPYING. If not, write to +the Free Software Foundation, 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. */ + +#ifdef IRIX_USING_GNU_LD +#define IRIX_SUBTARGET_LINK_SPEC "-melf32bsmip" +#else +#define IRIX_SUBTARGET_LINK_SPEC "-_SYSTYPE_SVR4" +#endif + +#undef STARTFILE_SPEC +#define STARTFILE_SPEC "\ +%{!static: \ + %{!shared:%{pg:gcrt1.o%s}%{!pg:%{p:mcrt1.o%s libprof1.a%s}%{!p:crt1.o%s}}}} \ +%{static: \ + %{pg:gcrt1.o%s} \ + %{!pg:%{p:/usr/lib/nonshared/mcrt1.o%s libprof1.a%s} \ + %{!p:/usr/lib/nonshared/crt1.o%s}}} \ +irix-crti.o%s crtbegin.o%s" + +#undef LIB_SPEC +#define LIB_SPEC "%{!shared:%{p:-lprof1} %{pg:-lprof1} -lc}" + +#undef ENDFILE_SPEC +#define ENDFILE_SPEC "crtend.o%s irix-crtn.o%s %{!shared:crtn.o%s}" + +#undef MACHINE_TYPE +#define MACHINE_TYPE "SGI running IRIX 5.x" diff --git a/contrib/gcc/config/mips/iris6.h b/contrib/gcc/config/mips/iris6.h new file mode 100644 index 0000000..8686b14 --- /dev/null +++ b/contrib/gcc/config/mips/iris6.h @@ -0,0 +1,118 @@ +/* Definitions of target machine for GNU compiler. IRIX version 6. + Copyright (C) 1994, 1995, 1996, 1997, 1998, 2000, 2001, 2002, 2003, 2004, + 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 2, 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 COPYING. If not, write to +the Free Software Foundation, 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. */ + +/* Allow some special handling for IRIX 6. */ +#undef TARGET_IRIX6 +#define TARGET_IRIX6 1 + +/* 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. Default to the mips2 ISA for the O32 ABI. */ +#define DRIVER_SELF_SPECS \ + "%{!mabi=*: -mabi=n32}", \ + "%{mabi=32: %{!mips*: %{!march*: -mips2}}}" + +/* 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) + +#undef MACHINE_TYPE +#define MACHINE_TYPE "SGI running IRIX 6.x" + +#ifdef IRIX_USING_GNU_LD +#define IRIX_SUBTARGET_LINK_SPEC \ + "%{mabi=32: -melf32bsmip}%{mabi=n32: -melf32bmipn32}%{mabi=64: -melf64bmip}" +#else +#define IRIX_SUBTARGET_LINK_SPEC \ + "%{w} -_SYSTYPE_SVR4 -woff 131 \ + %{mabi=32: -32}%{mabi=n32: -n32}%{mabi=64: -64}%{!mabi*: -n32}" +#endif + +/* Profiling is supported via libprof1.a not -lc_p as in IRIX 3. */ +#undef STARTFILE_SPEC +#define STARTFILE_SPEC \ + "%{!shared: \ + %{mabi=32:%{pg:gcrt1.o%s} \ + %{!pg:%{p:mcrt1.o%s libprof1.a%s}%{!p:crt1.o%s}}} \ + %{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" + +#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 + +#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. */ +#undef LIBGCC_SPEC +#define LIBGCC_SPEC \ + SUBTARGET_DONT_WARN_UNUSED_SPEC " -lgcc " SUBTARGET_WARN_UNUSED_SPEC + +#undef ENDFILE_SPEC +#define ENDFILE_SPEC \ + "crtend.o%s irix-crtn.o%s \ + %{!shared: \ + %{mabi=32:crtn.o%s}\ + %{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}}}" + +#define MIPS_TFMODE_FORMAT mips_extended_format + +#undef SUBTARGET_CPP_SPEC +#define SUBTARGET_CPP_SPEC "%{pthread:-D_REENTRANT}" + diff --git a/contrib/gcc/config/mips/irix-crti.asm b/contrib/gcc/config/mips/irix-crti.asm new file mode 100644 index 0000000..d6888bb --- /dev/null +++ b/contrib/gcc/config/mips/irix-crti.asm @@ -0,0 +1,51 @@ + .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 diff --git a/contrib/gcc/config/mips/irix-crtn.asm b/contrib/gcc/config/mips/irix-crtn.asm new file mode 100644 index 0000000..600576c --- /dev/null +++ b/contrib/gcc/config/mips/irix-crtn.asm @@ -0,0 +1,27 @@ + .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/contrib/gcc/config/mips/linux-unwind.h b/contrib/gcc/config/mips/linux-unwind.h new file mode 100644 index 0000000..4f96e95 --- /dev/null +++ b/contrib/gcc/config/mips/linux-unwind.h @@ -0,0 +1,112 @@ +/* DWARF2 EH unwinding support for MIPS Linux. + 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 2, or (at your option) +any later version. + +In addition to the permissions in the GNU General Public License, the +Free Software Foundation gives you unlimited permission to link the +compiled version of this file with other programs, and to distribute +those programs without any restriction coming from the use of this +file. (The General Public License restrictions do apply in other +respects; for example, they cover modification of the file, and +distribution when not linked into another program.) + +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 COPYING. If not, write to +the Free Software Foundation, 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. */ + +#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; + 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 trampoline[2]; + struct sigcontext sigctx; + } *rt_ = context->ra; + sc = &rt_->sigctx; + } + else +#endif + if (pc[0] == (0x24020000 | __NR_rt_sigreturn)) + { + struct rt_sigframe { + u_int32_t trampoline[2]; + struct siginfo info; + _sig_ucontext_t uc; + } *rt_ = context->ra; + sc = &rt_->uc.uc_mcontext; + } + else + return _URC_END_OF_STACK; + + new_cfa = (_Unwind_Ptr)sc; + fs->cfa_how = CFA_REG_OFFSET; + fs->cfa_reg = STACK_POINTER_REGNUM; + fs->cfa_offset = new_cfa - (_Unwind_Ptr) context->cfa; + +#if _MIPS_SIM == _ABIO32 && defined __MIPSEB__ + /* 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. */ + new_cfa -= 4; +#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]) - new_cfa; + } + fs->regs.reg[SIGNAL_UNWIND_RETURN_COLUMN].how = REG_SAVED_OFFSET; + fs->regs.reg[SIGNAL_UNWIND_RETURN_COLUMN].loc.offset + = (_Unwind_Ptr)&(sc->sc_pc) - new_cfa; + fs->retaddr_column = SIGNAL_UNWIND_RETURN_COLUMN; + + return _URC_NO_REASON; +} +#endif diff --git a/contrib/gcc/config/mips/linux.h b/contrib/gcc/config/mips/linux.h new file mode 100644 index 0000000..ff268d4 --- /dev/null +++ b/contrib/gcc/config/mips/linux.h @@ -0,0 +1,181 @@ +/* Definitions for MIPS running Linux-based GNU systems with ELF format. + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 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 2, 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 COPYING. If not, write to +the Free Software Foundation, 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. */ + +#undef WCHAR_TYPE +#define WCHAR_TYPE "int" + +#undef WCHAR_TYPE_SIZE +#define WCHAR_TYPE_SIZE 32 + +/* If defined, a C expression whose value is a string containing the + assembler operation to identify the following data as + uninitialized global data. If not defined, and neither + `ASM_OUTPUT_BSS' nor `ASM_OUTPUT_ALIGNED_BSS' are defined, + uninitialized global data will be output in the data section if + `-fno-common' is passed, otherwise `ASM_OUTPUT_COMMON' will be + used. */ +#define BSS_SECTION_ASM_OP "\t.section\t.bss" + +#define ASM_OUTPUT_ALIGNED_BSS mips_output_aligned_bss + +#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 + +#undef MD_EXEC_PREFIX +#undef MD_STARTFILE_PREFIX + +/* 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"); \ + \ + if (mips_abi == ABI_N32) \ + { \ + builtin_define ("_ABIN32=2"); \ + builtin_define ("_MIPS_SIM=_ABIN32"); \ + builtin_define ("_MIPS_SZLONG=32"); \ + builtin_define ("_MIPS_SZPTR=32"); \ + } \ + else if (mips_abi == ABI_64) \ + { \ + builtin_define ("_ABI64=3"); \ + builtin_define ("_MIPS_SIM=_ABI64"); \ + builtin_define ("_MIPS_SZLONG=64"); \ + builtin_define ("_MIPS_SZPTR=64"); \ + } \ + else \ + { \ + builtin_define ("_ABIO32=1"); \ + builtin_define ("_MIPS_SIM=_ABIO32"); \ + builtin_define ("_MIPS_SZLONG=32"); \ + builtin_define ("_MIPS_SZPTR=32"); \ + } \ + if (TARGET_FLOAT64) \ + builtin_define ("_MIPS_FPSET=32"); \ + else \ + builtin_define ("_MIPS_FPSET=16"); \ + \ + builtin_define ("_MIPS_SZINT=32"); \ + } 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: \ + %{!ibcs: \ + %{!static: \ + %{rdynamic:-export-dynamic} \ + %{!dynamic-linker:-dynamic-linker " LINUX_DYNAMIC_LINKER "}} \ + %{static:-static}}}" + +#undef SUBTARGET_ASM_SPEC +#define SUBTARGET_ASM_SPEC "%{mabi=64: -64} %{!mno-abicalls:-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) + +#undef ASM_DECLARE_FUNCTION_NAME +#define ASM_DECLARE_FUNCTION_NAME(STREAM, NAME, DECL) \ + do { \ + if (!flag_inhibit_size_directive) \ + { \ + fputs ("\t.ent\t", STREAM); \ + assemble_name (STREAM, NAME); \ + putc ('\n', STREAM); \ + } \ + ASM_OUTPUT_TYPE_DIRECTIVE (STREAM, NAME, "function"); \ + assemble_name (STREAM, NAME); \ + fputs (":\n", STREAM); \ + } while (0) + +#undef ASM_DECLARE_FUNCTION_SIZE +#define ASM_DECLARE_FUNCTION_SIZE(STREAM, NAME, DECL) \ + do { \ + if (!flag_inhibit_size_directive) \ + { \ + fputs ("\t.end\t", STREAM); \ + assemble_name (STREAM, NAME); \ + putc ('\n', STREAM); \ + } \ + } while (0) + +/* Tell function_prologue in mips.c that we have already output the .ent/.end + pseudo-ops. */ +#undef FUNCTION_NAME_ALREADY_DECLARED +#define FUNCTION_NAME_ALREADY_DECLARED 1 + +/* 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 "\ +%{shared: -lc} \ +%{!shared: %{pthread:-lpthread} \ + %{profile:-lc_p} %{!profile: -lc}}" + +#define MD_UNWIND_SUPPORT "config/mips/linux-unwind.h" diff --git a/contrib/gcc/config/mips/linux64.h b/contrib/gcc/config/mips/linux64.h new file mode 100644 index 0000000..4ccf938 --- /dev/null +++ b/contrib/gcc/config/mips/linux64.h @@ -0,0 +1,72 @@ +/* Definitions for MIPS running Linux-based GNU systems with ELF format + using n32/64 abi. + Copyright 2002, 2003, 2004, 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 2, 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 COPYING. If not, write to +the Free Software Foundation, 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. */ + +/* Force the default endianness and ABI flags onto the command line + in order to make the other specs easier to write. */ +#define DRIVER_SELF_SPECS \ +"%{!EB:%{!EL:%(endian_spec)}}", \ +"%{!mabi=*: -mabi=n32}" + +#undef SUBTARGET_ASM_SPEC +#define SUBTARGET_ASM_SPEC "\ +%{!fno-PIC:%{!fno-pic:-KPIC}} \ +%{fno-PIC:-non_shared} %{fno-pic:-non_shared}" + +#undef LIB_SPEC +#define LIB_SPEC "\ +%{shared: -lc} \ +%{!shared: %{pthread:-lpthread} \ + %{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 LINUX_DYNAMIC_LINKERN32 \ + CHOOSE_DYNAMIC_LINKER (GLIBC_DYNAMIC_LINKERN32, UCLIBC_DYNAMIC_LINKERN32) + +#undef LINK_SPEC +#define LINK_SPEC "\ +%{G*} %{EB} %{EL} %{mips1} %{mips2} %{mips3} %{mips4} \ +%{bestGnum} %{shared} %{non_shared} \ +%{call_shared} %{no_archive} %{exact_version} \ + %(endian_spec) \ + %{!shared: \ + %{!ibcs: \ + %{!static: \ + %{rdynamic:-export-dynamic} \ + %{!dynamic-linker: \ + %{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/contrib/gcc/config/mips/mips-dsp.md b/contrib/gcc/config/mips/mips-dsp.md new file mode 100644 index 0000000..3fdcc59 --- /dev/null +++ b/contrib/gcc/config/mips/mips-dsp.md @@ -0,0 +1,1057 @@ +(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 macro allows si, v2hi, v4qi for all possible modes in DSP ASE. +(define_mode_macro DSP [(SI "TARGET_DSP") + (V2HI "TARGET_DSP") + (V4QI "TARGET_DSP")]) + +;; This mode macro allows v2hi, v4qi for vector/SIMD data. +(define_mode_macro DSPV [(V2HI "TARGET_DSP") + (V4QI "TARGET_DSP")]) + +;; This mode macro allows si, v2hi for Q31 and V2Q15 fixed-point data. +(define_mode_macro DSPQ [(SI "TARGET_DSP") + (V2HI "TARGET_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))])] + "" + "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))])] + "" + "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))])] + "TARGET_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))])] + "TARGET_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))])] + "TARGET_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))])] + "TARGET_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))] + "TARGET_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))] + "TARGET_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))])] + "TARGET_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))] + "TARGET_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))] + "TARGET_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))])] + "TARGET_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))])] + "TARGET_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))] + "TARGET_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))] + "TARGET_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))] + "TARGET_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))] + "TARGET_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))] + "TARGET_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))] + "TARGET_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))] + "TARGET_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))] + "TARGET_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))] + "TARGET_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))] + "TARGET_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))])] + "TARGET_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))])] + "TARGET_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))] + "TARGET_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))] + "TARGET_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))] + "TARGET_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"))])] + "TARGET_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"))])] + "TARGET_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"))])] + "TARGET_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"))])] + "TARGET_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"))])] + "TARGET_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))] + "TARGET_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))] + "TARGET_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))] + "TARGET_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))] + "TARGET_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))])] + "TARGET_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))])] + "TARGET_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))])] + "TARGET_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))])] + "TARGET_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))])] + "TARGET_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))])] + "TARGET_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))])] + "TARGET_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))])] + "TARGET_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))])] + "TARGET_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))] + "TARGET_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))] + "TARGET_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))] + "TARGET_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))] + "TARGET_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))] + "TARGET_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))] + "TARGET_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))] + "TARGET_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))] + "TARGET_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))] + "TARGET_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))] + "TARGET_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))] + "TARGET_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))] + "TARGET_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))])] + "TARGET_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))])] + "TARGET_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))])] + "TARGET_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))])] + "TARGET_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))])] + "TARGET_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))])] + "TARGET_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))] + "TARGET_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))])] + "TARGET_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))])] + "TARGET_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))] + "TARGET_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_insn "mips_lbux" + [(set (match_operand:SI 0 "register_operand" "=d") + (zero_extend:SI (mem:QI (plus:SI (match_operand:SI 1 + "register_operand" "d") + (match_operand:SI 2 + "register_operand" "d")))))] + "TARGET_DSP" + "lbux\t%0,%2(%1)" + [(set_attr "type" "load") + (set_attr "mode" "SI") + (set_attr "length" "4")]) + +(define_insn "mips_lhx" + [(set (match_operand:SI 0 "register_operand" "=d") + (sign_extend:SI (mem:HI (plus:SI (match_operand:SI 1 + "register_operand" "d") + (match_operand:SI 2 + "register_operand" "d")))))] + "TARGET_DSP" + "lhx\t%0,%2(%1)" + [(set_attr "type" "load") + (set_attr "mode" "SI") + (set_attr "length" "4")]) + +(define_insn "mips_lwx" + [(set (match_operand:SI 0 "register_operand" "=d") + (mem:SI (plus:SI (match_operand:SI 1 "register_operand" "d") + (match_operand:SI 2 "register_operand" "d"))))] + "TARGET_DSP" + "lwx\t%0,%2(%1)" + [(set_attr "type" "load") + (set_attr "mode" "SI") + (set_attr "length" "4")]) + +;; 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 0 "immediate_operand" "I")) + (label_ref (match_operand 1 "" "")) + (pc)))] + "TARGET_DSP" + "%*bposge%0\t%1%/" + [(set_attr "type" "branch") + (set_attr "mode" "none")]) + diff --git a/contrib/gcc/config/mips/mips-modes.def b/contrib/gcc/config/mips/mips-modes.def new file mode 100644 index 0000000..39c2f16 --- /dev/null +++ b/contrib/gcc/config/mips/mips-modes.def @@ -0,0 +1,43 @@ +/* MIPS extra machine modes. + Copyright (C) 2003, 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 2, 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 COPYING. If not, write to +the Free Software Foundation, 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. */ + +/* 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 (FLOAT, 8); /* V4HF V2SF */ +VECTOR_MODES (INT, 4); /* V4QI V2HI */ + +/* 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/contrib/gcc/config/mips/mips-protos.h b/contrib/gcc/config/mips/mips-protos.h new file mode 100644 index 0000000..da1ccea --- /dev/null +++ b/contrib/gcc/config/mips/mips-protos.h @@ -0,0 +1,255 @@ +/* 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 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 2, 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 COPYING. If not, write to +the Free Software Foundation, 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. */ + +#ifndef GCC_MIPS_PROTOS_H +#define GCC_MIPS_PROTOS_H + +/* Classifies a SYMBOL_REF, LABEL_REF or UNSPEC address. + + SYMBOL_GENERAL + Used when none of the below apply. + + SYMBOL_SMALL_DATA + The symbol refers to something in a small data section. + + SYMBOL_CONSTANT_POOL + The symbol refers to something in the mips16 constant pool. + + SYMBOL_GOT_LOCAL + The symbol refers to local data that will be found using + the global offset table. + + SYMBOL_GOT_GLOBAL + Likewise non-local data. + + SYMBOL_GOTOFF_PAGE + An UNSPEC wrapper around a SYMBOL_GOT_LOCAL. It represents the + offset from _gp of a GOT page entry. + + SYMBOL_GOTOFF_GLOBAL + An UNSPEC wrapper around a SYMBOL_GOT_GLOBAL. It represents the + the offset from _gp of the symbol's GOT entry. + + SYMBOL_GOTOFF_CALL + Like SYMBOL_GOTOFF_GLOBAL, 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_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). */ +enum mips_symbol_type { + SYMBOL_GENERAL, + SYMBOL_SMALL_DATA, + SYMBOL_CONSTANT_POOL, + SYMBOL_GOT_LOCAL, + SYMBOL_GOT_GLOBAL, + SYMBOL_GOTOFF_PAGE, + SYMBOL_GOTOFF_GLOBAL, + SYMBOL_GOTOFF_CALL, + SYMBOL_GOTOFF_LOADGP, + SYMBOL_TLS, + SYMBOL_TLSGD, + SYMBOL_TLSLDM, + SYMBOL_DTPREL, + SYMBOL_GOTTPREL, + SYMBOL_TPREL, + SYMBOL_64_HIGH, + SYMBOL_64_MID, + SYMBOL_64_LOW +}; +#define NUM_SYMBOL_TYPES (SYMBOL_64_LOW + 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_noshared. */ +enum mips_loadgp_style { + LOADGP_NONE, + LOADGP_OLDABI, + LOADGP_NEWABI, + LOADGP_ABSOLUTE +}; + +extern bool mips_symbolic_constant_p (rtx, enum mips_symbol_type *); +extern int mips_regno_mode_ok_for_base_p (int, enum machine_mode, int); +extern bool mips_stack_address_p (rtx, enum machine_mode); +extern int mips_address_insns (rtx, enum machine_mode); +extern int mips_const_insns (rtx); +extern int mips_fetch_insns (rtx); +extern int mips_idiv_insns (void); +extern int fp_register_operand (rtx, enum machine_mode); +extern int lo_operand (rtx, enum machine_mode); +extern bool mips_legitimate_address_p (enum machine_mode, rtx, int); +extern rtx mips_split_symbol (rtx, rtx); +extern rtx mips_unspec_address (rtx, enum mips_symbol_type); +extern bool mips_legitimize_address (rtx *, enum machine_mode); +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, int); +extern bool mips_split_64bit_move_p (rtx, rtx); +extern void mips_split_64bit_move (rtx, rtx); +extern const char *mips_output_move (rtx, rtx); +extern void mips_restore_gp (void); +#ifdef RTX_CODE +extern bool mips_emit_scc (enum rtx_code, rtx); +extern void gen_conditional_branch (rtx *, enum rtx_code); +extern void mips_expand_vcondv2sf (rtx, rtx, rtx, enum rtx_code, rtx, rtx); +#endif +extern void gen_conditional_move (rtx *); +extern void mips_gen_conditional_trap (rtx *); +extern void mips_expand_call (rtx, rtx, rtx, rtx, int); +extern void mips_emit_fcc_reload (rtx, rtx, rtx); +extern void mips_set_return_address (rtx, rtx); +extern bool mips_expand_block_move (rtx, rtx, rtx); + +extern void init_cumulative_args (CUMULATIVE_ARGS *, tree, rtx); +extern void function_arg_advance (CUMULATIVE_ARGS *, enum machine_mode, + tree, int); +extern struct rtx_def *function_arg (const CUMULATIVE_ARGS *, + enum machine_mode, tree, int); +extern int function_arg_boundary (enum machine_mode, tree); +extern bool mips_pad_arg_upward (enum machine_mode, tree); +extern bool mips_pad_reg_upward (enum machine_mode, tree); +extern void mips_va_start (tree, rtx); + +extern bool mips_expand_unaligned_load (rtx, rtx, unsigned int, int); +extern bool mips_expand_unaligned_store (rtx, rtx, unsigned int, int); +extern bool mips_mem_fits_mode_p (enum machine_mode mode, rtx x); +extern void override_options (void); +extern void mips_conditional_register_usage (void); +extern void mips_order_regs_for_local_alloc (void); +extern HOST_WIDE_INT mips_debugger_offset (rtx, HOST_WIDE_INT); + +extern void print_operand (FILE *, rtx, int); +extern void print_operand_address (FILE *, rtx); +extern int mips_output_external (FILE *, tree, const char *); +extern void mips_output_filename (FILE *, const char *); +extern void mips_output_ascii (FILE *, const char *, size_t, const char *); +extern void mips_output_aligned_bss (FILE *, tree, const char *, + unsigned HOST_WIDE_INT, int); +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 compute_frame_size (HOST_WIDE_INT); +extern HOST_WIDE_INT mips_initial_elimination_offset (int, int); +extern rtx mips_return_addr (int, rtx); +extern enum mips_loadgp_style mips_current_loadgp_style (void); +extern void mips_expand_prologue (void); +extern void mips_expand_epilogue (int); +extern int mips_can_use_return_insn (void); +extern struct rtx_def *mips_function_value (tree, 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 enum reg_class mips_preferred_reload_class (rtx, enum reg_class); +extern enum reg_class mips_secondary_reload_class (enum reg_class, + enum machine_mode, + rtx, int); +extern int mips_class_max_nregs (enum reg_class, enum machine_mode); +extern int build_mips16_call_stub (rtx, rtx, rtx, int); +extern int mips_register_move_cost (enum machine_mode, enum reg_class, + enum reg_class); + +extern int mips_adjust_insn_length (rtx, int); +extern const char *mips_output_load_label (void); +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_division (const char *, rtx *); +extern unsigned int mips_hard_regno_nregs (int, enum machine_mode); +extern bool mips_linked_madd_p (rtx, rtx); +extern int 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, rtx, rtx); + +#endif /* ! GCC_MIPS_PROTOS_H */ diff --git a/contrib/gcc/config/mips/mips-ps-3d.md b/contrib/gcc/config/mips/mips-ps-3d.md new file mode 100644 index 0000000..c817f4d --- /dev/null +++ b/contrib/gcc/config/mips/mips-ps-3d.md @@ -0,0 +1,618 @@ +;; MIPS Paired-Single Floating and MIPS-3D Instructions. +;; 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 2, 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 COPYING. If not, write to +;; the Free Software Foundation, 51 Franklin Street, Fifth Floor, +;; Boston, MA 02110-1301, USA. + +(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_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_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_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 (cmp_operands[0])) != MODE_INT) + FAIL; + + gen_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_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_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_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_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_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_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_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_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_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_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_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_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_MIPS3D" + "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_MIPS3D" + "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_MIPS3D" + "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_MIPS3D" + "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_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_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_MIPS3D" + "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_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_MIPS3D" + "#" + "&& 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_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_MIPS3D" + "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_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_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 0 "register_operand" "z") + (const_int 0)) + (label_ref (match_operand 1 "" "")) + (pc)))] + "TARGET_MIPS3D" + "%*bc1any4t\t%0,%1%/" + [(set_attr "type" "branch") + (set_attr "mode" "none")]) + +; Branch on Any of Four Floating Point Condition Codes False +(define_insn "bc1any4f" + [(set (pc) + (if_then_else (ne (match_operand:CCV4 0 "register_operand" "z") + (const_int -1)) + (label_ref (match_operand 1 "" "")) + (pc)))] + "TARGET_MIPS3D" + "%*bc1any4f\t%0,%1%/" + [(set_attr "type" "branch") + (set_attr "mode" "none")]) + +; Branch on Any of Two Floating Point Condition Codes True +(define_insn "bc1any2t" + [(set (pc) + (if_then_else (ne (match_operand:CCV2 0 "register_operand" "z") + (const_int 0)) + (label_ref (match_operand 1 "" "")) + (pc)))] + "TARGET_MIPS3D" + "%*bc1any2t\t%0,%1%/" + [(set_attr "type" "branch") + (set_attr "mode" "none")]) + +; Branch on Any of Two Floating Point Condition Codes False +(define_insn "bc1any2f" + [(set (pc) + (if_then_else (ne (match_operand:CCV2 0 "register_operand" "z") + (const_int -1)) + (label_ref (match_operand 1 "" "")) + (pc)))] + "TARGET_MIPS3D" + "%*bc1any2f\t%0,%1%/" + [(set_attr "type" "branch") + (set_attr "mode" "none")]) + +; 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 0 "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 1 "" "")) + (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%F0", "%2,%1"), + MIPS_BRANCH ("b%W0", "%2,%1")); +} + [(set_attr "type" "branch") + (set_attr "mode" "none")]) + +; As above, but with the sense of the condition reversed. +(define_insn "*branch_upper_lower_inverted" + [(set (pc) + (if_then_else + (match_operator 0 "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 1 "" ""))))] + "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%W0", "%2,%1"), + MIPS_BRANCH ("b%F0", "%2,%1")); +} + [(set_attr "type" "branch") + (set_attr "mode" "none")]) + +;---------------------------------------------------------------------------- +; 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_MIPS3D" + "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_MIPS3D" + "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_MIPS3D" + "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_MIPS3D" + "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_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_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_PAIRED_SINGLE_FLOAT" +{ + mips_expand_vcondv2sf (operands[0], operands[1], operands[2], + LE, operands[2], operands[1]); + DONE; +}) diff --git a/contrib/gcc/config/mips/mips.c b/contrib/gcc/config/mips/mips.c new file mode 100644 index 0000000..05498c2 --- /dev/null +++ b/contrib/gcc/config/mips/mips.c @@ -0,0 +1,10840 @@ +/* Subroutines used for MIPS code generation. + Copyright (C) 1989, 1990, 1991, 1993, 1994, 1995, 1996, 1997, 1998, + 1999, 2000, 2001, 2002, 2003, 2004, 2005 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 2, 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 COPYING. If not, write to +the Free Software Foundation, 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include <signal.h> +#include "rtl.h" +#include "regs.h" +#include "hard-reg-set.h" +#include "real.h" +#include "insn-config.h" +#include "conditions.h" +#include "insn-attr.h" +#include "recog.h" +#include "toplev.h" +#include "output.h" +#include "tree.h" +#include "function.h" +#include "expr.h" +#include "optabs.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 "tree-gimple.h" +#include "bitmap.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 & restore registers. + + Use a maximum gap of 0x100 in the mips16 case. We can then use + unextended instructions to save and restore registers, and to + allocate and deallocate the top part of the frame. + + The value in the !mips16 case must be a SMALL_OPERAND and must + preserve the maximum stack alignment. */ +#define MIPS_MAX_FIRST_STACK_STEP (TARGET_MIPS16 ? 0x100 : 0x7ff0) + +/* True if INSN is a mips.md pattern or asm statement. */ +#define USEFUL_INSN_P(INSN) \ + (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)) + +/* 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 (equivalent to CONSTANT_SYMBOLIC). */ +enum mips_address_type { + ADDRESS_REG, + ADDRESS_LO_SUM, + ADDRESS_CONST_INT, + ADDRESS_SYMBOLIC +}; + +/* Classifies the prototype of a builtin function. */ +enum mips_function_type +{ + MIPS_V2SF_FTYPE_V2SF, + MIPS_V2SF_FTYPE_V2SF_V2SF, + MIPS_V2SF_FTYPE_V2SF_V2SF_INT, + MIPS_V2SF_FTYPE_V2SF_V2SF_V2SF_V2SF, + MIPS_V2SF_FTYPE_SF_SF, + MIPS_INT_FTYPE_V2SF_V2SF, + MIPS_INT_FTYPE_V2SF_V2SF_V2SF_V2SF, + MIPS_INT_FTYPE_SF_SF, + MIPS_INT_FTYPE_DF_DF, + MIPS_SF_FTYPE_V2SF, + MIPS_SF_FTYPE_SF, + MIPS_SF_FTYPE_SF_SF, + MIPS_DF_FTYPE_DF, + MIPS_DF_FTYPE_DF_DF, + + /* For MIPS DSP ASE */ + MIPS_DI_FTYPE_DI_SI, + MIPS_DI_FTYPE_DI_SI_SI, + MIPS_DI_FTYPE_DI_V2HI_V2HI, + MIPS_DI_FTYPE_DI_V4QI_V4QI, + MIPS_SI_FTYPE_DI_SI, + MIPS_SI_FTYPE_PTR_SI, + MIPS_SI_FTYPE_SI, + MIPS_SI_FTYPE_SI_SI, + MIPS_SI_FTYPE_V2HI, + MIPS_SI_FTYPE_V2HI_V2HI, + MIPS_SI_FTYPE_V4QI, + MIPS_SI_FTYPE_V4QI_V4QI, + MIPS_SI_FTYPE_VOID, + MIPS_V2HI_FTYPE_SI, + MIPS_V2HI_FTYPE_SI_SI, + MIPS_V2HI_FTYPE_V2HI, + MIPS_V2HI_FTYPE_V2HI_SI, + MIPS_V2HI_FTYPE_V2HI_V2HI, + MIPS_V2HI_FTYPE_V4QI, + MIPS_V2HI_FTYPE_V4QI_V2HI, + MIPS_V4QI_FTYPE_SI, + MIPS_V4QI_FTYPE_V2HI_V2HI, + MIPS_V4QI_FTYPE_V4QI_SI, + MIPS_V4QI_FTYPE_V4QI_V4QI, + MIPS_VOID_FTYPE_SI_SI, + MIPS_VOID_FTYPE_V2HI_V2HI, + MIPS_VOID_FTYPE_V4QI_V4QI, + + /* The last type. */ + MIPS_MAX_FTYPE_MAX +}; + +/* Specifies how a builtin function should be converted into rtl. */ +enum mips_builtin_type +{ + /* The builtin 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 builtin 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 builtin 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 builtin 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 +}; + +/* Invokes 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) +}; + +/* 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); + +struct mips16_constant; +struct mips_arg_info; +struct mips_address_info; +struct mips_integer_op; +struct mips_sim; + +static enum mips_symbol_type mips_classify_symbol (rtx); +static void mips_split_const (rtx, rtx *, HOST_WIDE_INT *); +static bool mips_offset_within_object_p (rtx, HOST_WIDE_INT); +static bool mips_valid_base_register_p (rtx, enum machine_mode, int); +static bool mips_symbolic_address_p (enum mips_symbol_type, enum machine_mode); +static bool mips_classify_address (struct mips_address_info *, rtx, + enum machine_mode, int); +static bool mips_cannot_force_const_mem (rtx); +static bool mips_use_blocks_for_constant_p (enum machine_mode, rtx); +static int mips_symbol_insns (enum mips_symbol_type); +static bool mips16_unextended_reference_p (enum machine_mode mode, rtx, rtx); +static rtx mips_force_temporary (rtx, rtx); +static rtx mips_unspec_offset_high (rtx, rtx, rtx, enum mips_symbol_type); +static rtx mips_add_offset (rtx, rtx, HOST_WIDE_INT); +static unsigned int mips_build_shift (struct mips_integer_op *, HOST_WIDE_INT); +static unsigned int mips_build_lower (struct mips_integer_op *, + unsigned HOST_WIDE_INT); +static unsigned int mips_build_integer (struct mips_integer_op *, + unsigned HOST_WIDE_INT); +static void mips_legitimize_const_move (enum machine_mode, rtx, rtx); +static int m16_check_op (rtx, int, int, int); +static bool mips_rtx_costs (rtx, int, int, int *); +static int mips_address_cost (rtx); +static void mips_emit_compare (enum rtx_code *, rtx *, rtx *, bool); +static void mips_load_call_address (rtx, rtx, int); +static bool mips_function_ok_for_sibcall (tree, tree); +static void mips_block_move_straight (rtx, rtx, HOST_WIDE_INT); +static void mips_adjust_block_mem (rtx, HOST_WIDE_INT, rtx *, rtx *); +static void mips_block_move_loop (rtx, rtx, HOST_WIDE_INT); +static void mips_arg_info (const CUMULATIVE_ARGS *, enum machine_mode, + tree, int, struct mips_arg_info *); +static bool mips_get_unaligned_mem (rtx *, unsigned int, int, rtx *, rtx *); +static void mips_set_architecture (const struct mips_cpu_info *); +static void mips_set_tune (const struct mips_cpu_info *); +static bool mips_handle_option (size_t, const char *, int); +static struct machine_function *mips_init_machine_status (void); +static void print_operand_reloc (FILE *, rtx, const char **); +#if TARGET_IRIX +static void irix_output_external_libcall (rtx); +#endif +static void mips_file_start (void); +static void mips_file_end (void); +static bool mips_rewrite_small_data_p (rtx); +static int mips_small_data_pattern_1 (rtx *, void *); +static int mips_rewrite_small_data_1 (rtx *, void *); +static bool mips_function_has_gp_insn (void); +static unsigned int mips_global_pointer (void); +static bool mips_save_reg_p (unsigned int); +static void mips_save_restore_reg (enum machine_mode, int, HOST_WIDE_INT, + mips_save_restore_fn); +static void mips_for_each_saved_reg (HOST_WIDE_INT, mips_save_restore_fn); +static void mips_output_cplocal (void); +static void mips_emit_loadgp (void); +static void mips_output_function_prologue (FILE *, HOST_WIDE_INT); +static void mips_set_frame_expr (rtx); +static rtx mips_frame_set (rtx, rtx); +static void mips_save_reg (rtx, rtx); +static void mips_output_function_epilogue (FILE *, HOST_WIDE_INT); +static void mips_restore_reg (rtx, rtx); +static void mips_output_mi_thunk (FILE *, tree, HOST_WIDE_INT, + HOST_WIDE_INT, tree); +static int symbolic_expression_p (rtx); +static section *mips_select_rtx_section (enum machine_mode, rtx, + unsigned HOST_WIDE_INT); +static section *mips_function_rodata_section (tree); +static bool mips_in_small_data_p (tree); +static bool mips_use_anchors_for_symbol_p (rtx); +static int mips_fpr_return_fields (tree, tree *); +static bool mips_return_in_msb (tree); +static rtx mips_return_fpr_pair (enum machine_mode mode, + enum machine_mode mode1, HOST_WIDE_INT, + enum machine_mode mode2, HOST_WIDE_INT); +static rtx mips16_gp_pseudo_reg (void); +static void mips16_fp_args (FILE *, int, int); +static void build_mips16_function_stub (FILE *); +static rtx dump_constants_1 (enum machine_mode, rtx, rtx); +static void dump_constants (struct mips16_constant *, rtx); +static int mips16_insn_length (rtx); +static int mips16_rewrite_pool_refs (rtx *, void *); +static void mips16_lay_out_constants (void); +static void mips_sim_reset (struct mips_sim *); +static void mips_sim_init (struct mips_sim *, state_t); +static void mips_sim_next_cycle (struct mips_sim *); +static void mips_sim_wait_reg (struct mips_sim *, rtx, rtx); +static int mips_sim_wait_regs_2 (rtx *, void *); +static void mips_sim_wait_regs_1 (rtx *, void *); +static void mips_sim_wait_regs (struct mips_sim *, rtx); +static void mips_sim_wait_units (struct mips_sim *, rtx); +static void mips_sim_wait_insn (struct mips_sim *, rtx); +static void mips_sim_record_set (rtx, rtx, void *); +static void mips_sim_issue_insn (struct mips_sim *, rtx); +static void mips_sim_issue_nop (struct mips_sim *); +static void mips_sim_finish_insn (struct mips_sim *, rtx); +static void vr4130_avoid_branch_rt_conflict (rtx); +static void vr4130_align_insns (void); +static void mips_avoid_hazard (rtx, rtx, int *, rtx *, rtx); +static void mips_avoid_hazards (void); +static void mips_reorg (void); +static bool mips_strict_matching_cpu_name_p (const char *, const char *); +static bool mips_matching_cpu_name_p (const char *, const char *); +static const struct mips_cpu_info *mips_parse_cpu (const char *); +static const struct mips_cpu_info *mips_cpu_info_from_isa (int); +static bool mips_return_in_memory (tree, tree); +static bool mips_strict_argument_naming (CUMULATIVE_ARGS *); +static void mips_macc_chains_record (rtx); +static void mips_macc_chains_reorder (rtx *, int); +static void vr4130_true_reg_dependence_p_1 (rtx, rtx, void *); +static bool vr4130_true_reg_dependence_p (rtx); +static bool vr4130_swap_insns_p (rtx, rtx); +static void vr4130_reorder (rtx *, int); +static void mips_promote_ready (rtx *, int, int); +static int mips_sched_reorder (FILE *, int, rtx *, int *, int); +static int mips_variable_issue (FILE *, int, rtx, int); +static int mips_adjust_cost (rtx, rtx, rtx, int); +static int mips_issue_rate (void); +static int mips_multipass_dfa_lookahead (void); +static void mips_init_libfuncs (void); +static void mips_setup_incoming_varargs (CUMULATIVE_ARGS *, enum machine_mode, + tree, int *, int); +static tree mips_build_builtin_va_list (void); +static tree mips_gimplify_va_arg_expr (tree, tree, tree *, tree *); +static bool mips_pass_by_reference (CUMULATIVE_ARGS *, enum machine_mode mode, + tree, bool); +static bool mips_callee_copies (CUMULATIVE_ARGS *, enum machine_mode mode, + tree, bool); +static int mips_arg_partial_bytes (CUMULATIVE_ARGS *, enum machine_mode mode, + tree, bool); +static bool mips_valid_pointer_mode (enum machine_mode); +static bool mips_vector_mode_supported_p (enum machine_mode); +static rtx mips_prepare_builtin_arg (enum insn_code, unsigned int, tree *); +static rtx mips_prepare_builtin_target (enum insn_code, unsigned int, rtx); +static rtx mips_expand_builtin (tree, rtx, rtx, enum machine_mode, int); +static void mips_init_builtins (void); +static rtx mips_expand_builtin_direct (enum insn_code, rtx, tree, bool); +static rtx mips_expand_builtin_movtf (enum mips_builtin_type, + enum insn_code, enum mips_fp_condition, + rtx, tree); +static rtx mips_expand_builtin_compare (enum mips_builtin_type, + enum insn_code, enum mips_fp_condition, + rtx, tree); +static rtx mips_expand_builtin_bposge (enum mips_builtin_type, rtx); +static void mips_encode_section_info (tree, rtx, int); +static void mips_extra_live_on_entry (bitmap); +static int mips_mode_rep_extended (enum machine_mode, enum machine_mode); + +/* Structure to be filled in by compute_frame_size with register + save masks, and offsets for the current function. */ + +struct mips_frame_info GTY(()) +{ + HOST_WIDE_INT total_size; /* # bytes that the entire frame takes up */ + HOST_WIDE_INT var_size; /* # bytes that variables take up */ + HOST_WIDE_INT args_size; /* # bytes that outgoing arguments take up */ + HOST_WIDE_INT cprestore_size; /* # bytes that the .cprestore slot takes up */ + HOST_WIDE_INT gp_reg_size; /* # bytes needed to store gp regs */ + HOST_WIDE_INT fp_reg_size; /* # bytes needed to store fp regs */ + unsigned int mask; /* mask of saved gp registers */ + unsigned int fmask; /* mask of saved fp registers */ + HOST_WIDE_INT gp_save_offset; /* offset from vfp to store gp registers */ + HOST_WIDE_INT fp_save_offset; /* offset from vfp to store fp registers */ + HOST_WIDE_INT gp_sp_offset; /* offset from new sp to store gp registers */ + HOST_WIDE_INT fp_sp_offset; /* offset from new sp to store fp registers */ + bool initialized; /* true if frame size already calculated */ + int num_gp; /* number of gp registers saved */ + int num_fp; /* number of fp registers saved */ +}; + +struct machine_function GTY(()) { + /* Pseudo-reg holding the value of $28 in a mips16 function which + refers to GP relative global variables. */ + 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; + + /* Current frame information, calculated by compute_frame_size. */ + struct mips_frame_info frame; + + /* The register to use as the global pointer within this function. */ + unsigned int global_pointer; + + /* 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 is known to have an instruction that needs $gp. */ + bool has_gp_insn_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 is the register that contains the high part of the address, + OFFSET is the symbolic address being referenced and SYMBOL_TYPE + is the type of OFFSET's symbol. + + ADDRESS_SYMBOLIC + SYMBOL_TYPE is the type of symbol being referenced. */ + +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. */ +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 + + +/* Global variables for machine-dependent things. */ + +/* Threshold for data being put into the small data/bss area, instead + of the normal data area. */ +int mips_section_threshold = -1; + +/* Count the number of .file directives, so that .loc is up to date. */ +int num_source_filenames = 0; + +/* Count the number of sdb related labels are generated (to find block + start and end boundaries). */ +int sdb_label_count = 0; + +/* Next label # for each statement for Silicon Graphics IRIS systems. */ +int sym_lineno = 0; + +/* Linked list of all externals that are to be emitted when optimizing + for the global pointer if they haven't been declared by the end of + the program with an appropriate .comm or initialization. */ + +struct extern_list GTY (()) +{ + struct extern_list *next; /* next external */ + const char *name; /* name of the external */ + int size; /* size in bytes */ +}; + +static GTY (()) struct extern_list *extern_head = 0; + +/* Name of the file containing the current function. */ +const char *current_function_file = ""; + +/* Number of nested .set noreorder, noat, nomacro, and volatile requests. */ +int set_noreorder; +int set_noat; +int set_nomacro; +int set_volatile; + +/* The next branch instruction is a branch likely, not branch normal. */ +int mips_branch_likely; + +/* The operands passed to the last cmpMM expander. */ +rtx cmp_operands[2]; + +/* The target cpu for code generation. */ +enum processor_type mips_arch; +const struct mips_cpu_info *mips_arch_info; + +/* The target cpu for optimization and scheduling. */ +enum processor_type mips_tune; +const struct mips_cpu_info *mips_tune_info; + +/* Which instruction set architecture to use. */ +int mips_isa; + +/* Which ABI to use. */ +int mips_abi = MIPS_ABI_DEFAULT; + +/* Cost information to use. */ +const struct mips_rtx_cost_data *mips_cost; + +/* Whether we are generating mips16 hard float code. In mips16 mode + we always set TARGET_SOFT_FLOAT; this variable is nonzero if + -msoft-float was not specified by the user, which means that we + should arrange to call mips32 hard floating point code. */ +int mips16_hard_float; + +/* The architecture selected by -mipsN. */ +static const struct mips_cpu_info *mips_isa_info; + +/* If TRUE, we split addresses into their high and low parts in the RTL. */ +int mips_split_addresses; + +/* Mode used for saving/restoring general purpose registers. */ +static enum machine_mode gpr_mode; + +/* Array giving truth value on whether or not a given hard register + can support a given mode. */ +char mips_hard_regno_mode_ok[(int)MAX_MACHINE_MODE][FIRST_PSEUDO_REGISTER]; + +/* List of all MIPS punctuation characters used by print_operand. */ +char mips_print_operand_punct[256]; + +/* Map GCC register number to debugger register number. */ +int mips_dbx_regno[FIRST_PSEUDO_REGISTER]; + +/* A copy of the original flag_delayed_branch: see override_options. */ +static int mips_flag_delayed_branch; + +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_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]; + +/* Map hard register number to register class */ +const enum reg_class mips_regno_to_class[] = +{ + LEA_REGS, LEA_REGS, M16_NA_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_NA_REGS, M16_NA_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, + HI_REG, LO_REG, NO_REGS, ST_REGS, + ST_REGS, ST_REGS, ST_REGS, ST_REGS, + ST_REGS, ST_REGS, ST_REGS, NO_REGS, + NO_REGS, ALL_REGS, ALL_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 +}; + +/* Table of machine dependent attributes. */ +const struct attribute_spec mips_attribute_table[] = +{ + { "long_call", 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[]. */ +const struct mips_cpu_info mips_cpu_info_table[] = { + /* Entries for generic ISAs */ + { "mips1", PROCESSOR_R3000, 1 }, + { "mips2", PROCESSOR_R6000, 2 }, + { "mips3", PROCESSOR_R4000, 3 }, + { "mips4", PROCESSOR_R8000, 4 }, + { "mips32", PROCESSOR_4KC, 32 }, + { "mips32r2", PROCESSOR_M4K, 33 }, + { "mips64", PROCESSOR_5KC, 64 }, + + /* MIPS I */ + { "r3000", PROCESSOR_R3000, 1 }, + { "r2000", PROCESSOR_R3000, 1 }, /* = r3000 */ + { "r3900", PROCESSOR_R3900, 1 }, + + /* MIPS II */ + { "r6000", PROCESSOR_R6000, 2 }, + + /* MIPS III */ + { "r4000", PROCESSOR_R4000, 3 }, + { "vr4100", PROCESSOR_R4100, 3 }, + { "vr4111", PROCESSOR_R4111, 3 }, + { "vr4120", PROCESSOR_R4120, 3 }, + { "vr4130", PROCESSOR_R4130, 3 }, + { "vr4300", PROCESSOR_R4300, 3 }, + { "r4400", PROCESSOR_R4000, 3 }, /* = r4000 */ + { "r4600", PROCESSOR_R4600, 3 }, + { "orion", PROCESSOR_R4600, 3 }, /* = r4600 */ + { "r4650", PROCESSOR_R4650, 3 }, + + /* MIPS IV */ + { "r8000", PROCESSOR_R8000, 4 }, + { "vr5000", PROCESSOR_R5000, 4 }, + { "vr5400", PROCESSOR_R5400, 4 }, + { "vr5500", PROCESSOR_R5500, 4 }, + { "rm7000", PROCESSOR_R7000, 4 }, + { "rm9000", PROCESSOR_R9000, 4 }, + + /* MIPS32 */ + { "4kc", PROCESSOR_4KC, 32 }, + { "4km", PROCESSOR_4KC, 32 }, /* = 4kc */ + { "4kp", PROCESSOR_4KP, 32 }, + + /* MIPS32 Release 2 */ + { "m4k", PROCESSOR_M4K, 33 }, + { "24k", PROCESSOR_24K, 33 }, + { "24kc", PROCESSOR_24K, 33 }, /* 24K no FPU */ + { "24kf", PROCESSOR_24K, 33 }, /* 24K 1:2 FPU */ + { "24kx", PROCESSOR_24KX, 33 }, /* 24K 1:1 FPU */ + + /* MIPS64 */ + { "5kc", PROCESSOR_5KC, 64 }, + { "5kf", PROCESSOR_5KF, 64 }, + { "20kc", PROCESSOR_20KC, 64 }, + { "sb1", PROCESSOR_SB1, 64 }, + { "sb1a", PROCESSOR_SB1A, 64 }, + { "sr71000", PROCESSOR_SR71000, 64 }, + + /* End marker */ + { 0, 0, 0 } +}; + +/* 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 */ + +/* Need to replace these with the costs of calling the appropriate + libgcc routine. */ +#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 */ + +static struct mips_rtx_cost_data const mips_rtx_cost_data[PROCESSOR_MAX] = + { + { /* 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 */ + DEFAULT_COSTS + }, + { /* 24k */ + 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 */ + }, + { /* 24kx */ + 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 */ + }, + { /* M4k */ + DEFAULT_COSTS + }, + { /* 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 */ + }, + { /* 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 + }, + }; + + +/* Nonzero if -march should decide the default value of MASK_SOFT_FLOAT. */ +#ifndef MIPS_MARCH_CONTROLS_SOFT_FLOAT +#define MIPS_MARCH_CONTROLS_SOFT_FLOAT 0 +#endif + +/* 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_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_REORDER +#define TARGET_SCHED_REORDER mips_sched_reorder +#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_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD +#define TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD \ + mips_multipass_dfa_lookahead + +#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_VALID_POINTER_MODE +#define TARGET_VALID_POINTER_MODE mips_valid_pointer_mode +#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_ASM_FILE_START +#undef TARGET_ASM_FILE_END +#define TARGET_ASM_FILE_START mips_file_start +#define TARGET_ASM_FILE_END mips_file_end +#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_GIMPLIFY_VA_ARG_EXPR +#define TARGET_GIMPLIFY_VA_ARG_EXPR mips_gimplify_va_arg_expr + +#undef TARGET_PROMOTE_FUNCTION_ARGS +#define TARGET_PROMOTE_FUNCTION_ARGS hook_bool_tree_true +#undef TARGET_PROMOTE_FUNCTION_RETURN +#define TARGET_PROMOTE_FUNCTION_RETURN hook_bool_tree_true +#undef TARGET_PROMOTE_PROTOTYPES +#define TARGET_PROMOTE_PROTOTYPES hook_bool_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_tree_hwi_hwi_tree_true + +#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_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_INIT_BUILTINS +#define TARGET_INIT_BUILTINS mips_init_builtins +#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 + +#undef TARGET_EXTRA_LIVE_ON_ENTRY +#define TARGET_EXTRA_LIVE_ON_ENTRY mips_extra_live_on_entry + +#undef TARGET_MIN_ANCHOR_OFFSET +#define TARGET_MIN_ANCHOR_OFFSET -32768 +#undef TARGET_MAX_ANCHOR_OFFSET +#define TARGET_MAX_ANCHOR_OFFSET 32767 +#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 + +struct gcc_target targetm = TARGET_INITIALIZER; + +/* Classify symbol X, which must be a SYMBOL_REF or a LABEL_REF. */ + +static enum mips_symbol_type +mips_classify_symbol (rtx x) +{ + if (GET_CODE (x) == LABEL_REF) + { + if (TARGET_MIPS16) + return SYMBOL_CONSTANT_POOL; + if (TARGET_ABICALLS && !TARGET_ABSOLUTE_ABICALLS) + return SYMBOL_GOT_LOCAL; + return SYMBOL_GENERAL; + } + + 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) + return SYMBOL_CONSTANT_POOL; + + if (GET_MODE_SIZE (get_pool_mode (x)) <= mips_section_threshold) + return SYMBOL_SMALL_DATA; + } + + /* Do not use small-data accesses for weak symbols; they may end up + being zero. */ + if (SYMBOL_REF_SMALL_P (x) + && !SYMBOL_REF_WEAK (x)) + return SYMBOL_SMALL_DATA; + + if (TARGET_ABICALLS) + { + if (SYMBOL_REF_DECL (x) == 0) + { + if (!SYMBOL_REF_LOCAL_P (x)) + return SYMBOL_GOT_GLOBAL; + } + else + { + /* Don't use GOT accesses for locally-binding symbols if + TARGET_ABSOLUTE_ABICALLS. Otherwise, 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 doesn't depend on the symbol's + visibility, so we deliberately ignore decl_visibility and + binds_local_p here. + + 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 (DECL_P (SYMBOL_REF_DECL (x)) + && TREE_PUBLIC (SYMBOL_REF_DECL (x)) + && !(TARGET_ABSOLUTE_ABICALLS + && targetm.binds_local_p (SYMBOL_REF_DECL (x)))) + return SYMBOL_GOT_GLOBAL; + } + + if (!TARGET_ABSOLUTE_ABICALLS) + return SYMBOL_GOT_LOCAL; + } + + return SYMBOL_GENERAL; +} + + +/* Split X into a base and a constant offset, storing them in *BASE + and *OFFSET respectively. */ + +static void +mips_split_const (rtx x, rtx *base, HOST_WIDE_INT *offset) +{ + *offset = 0; + + if (GET_CODE (x) == CONST) + { + x = XEXP (x, 0); + if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == CONST_INT) + { + *offset += INTVAL (XEXP (x, 1)); + x = XEXP (x, 0); + } + } + *base = x; +} + + +/* Return true if SYMBOL is a SYMBOL_REF and OFFSET + SYMBOL points + to the same object as SYMBOL, or to the same object_block. */ + +static bool +mips_offset_within_object_p (rtx symbol, HOST_WIDE_INT offset) +{ + if (GET_CODE (symbol) != SYMBOL_REF) + return false; + + if (CONSTANT_POOL_ADDRESS_P (symbol) + && offset >= 0 + && offset < (int) GET_MODE_SIZE (get_pool_mode (symbol))) + return true; + + if (SYMBOL_REF_DECL (symbol) != 0 + && offset >= 0 + && offset < int_size_in_bytes (TREE_TYPE (SYMBOL_REF_DECL (symbol)))) + return true; + + if (SYMBOL_REF_HAS_BLOCK_INFO_P (symbol) + && SYMBOL_REF_BLOCK (symbol) + && SYMBOL_REF_BLOCK_OFFSET (symbol) >= 0 + && ((unsigned HOST_WIDE_INT) offset + SYMBOL_REF_BLOCK_OFFSET (symbol) + < (unsigned HOST_WIDE_INT) SYMBOL_REF_BLOCK (symbol)->size)) + return true; + + return false; +} + + +/* Return true if X is a symbolic constant that can be calculated in + the same way as a bare symbol. If it is, store the type of the + symbol in *SYMBOL_TYPE. */ + +bool +mips_symbolic_constant_p (rtx x, enum mips_symbol_type *symbol_type) +{ + HOST_WIDE_INT offset; + + mips_split_const (x, &x, &offset); + if (UNSPEC_ADDRESS_P (x)) + *symbol_type = UNSPEC_ADDRESS_TYPE (x); + else if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF) + { + *symbol_type = mips_classify_symbol (x); + if (*symbol_type == SYMBOL_TLS) + return false; + } + else + return false; + + if (offset == 0) + return true; + + /* Check whether a nonzero offset is valid for the underlying + relocations. */ + switch (*symbol_type) + { + case SYMBOL_GENERAL: + 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 mips_offset_within_object_p (x, offset); + + /* In other cases the relocations can handle any offset. */ + return true; + + case SYMBOL_CONSTANT_POOL: + /* 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_SMALL_DATA: + /* Make sure that the offset refers to something within the + underlying object. This should guarantee that the final + PC- or GP-relative offset is within the 16-bit limit. */ + return mips_offset_within_object_p (x, offset); + + case SYMBOL_GOT_LOCAL: + case SYMBOL_GOTOFF_PAGE: + /* The linker should provide enough local GOT entries for a + 16-bit offset. Larger offsets may lead to GOT overflow. */ + return SMALL_OPERAND (offset); + + case SYMBOL_GOT_GLOBAL: + case SYMBOL_GOTOFF_GLOBAL: + case SYMBOL_GOTOFF_CALL: + case SYMBOL_GOTOFF_LOADGP: + case SYMBOL_TLSGD: + case SYMBOL_TLSLDM: + case SYMBOL_DTPREL: + case SYMBOL_TPREL: + case SYMBOL_GOTTPREL: + case SYMBOL_TLS: + return false; + } + gcc_unreachable (); +} + + +/* This function is used to implement REG_MODE_OK_FOR_BASE_P. */ + +int +mips_regno_mode_ok_for_base_p (int regno, enum machine_mode mode, int strict) +{ + if (regno >= FIRST_PSEUDO_REGISTER) + { + if (!strict) + 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 || 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 the given mode. + Allow only hard registers if STRICT. */ + +static bool +mips_valid_base_register_p (rtx x, enum machine_mode mode, int strict) +{ + if (!strict && GET_CODE (x) == SUBREG) + x = SUBREG_REG (x); + + return (REG_P (x) + && mips_regno_mode_ok_for_base_p (REGNO (x), mode, strict)); +} + + +/* Return true if symbols of type SYMBOL_TYPE can directly address a value + with mode MODE. This is used for both symbolic and LO_SUM addresses. */ + +static bool +mips_symbolic_address_p (enum mips_symbol_type symbol_type, + enum machine_mode mode) +{ + switch (symbol_type) + { + case SYMBOL_GENERAL: + return !TARGET_MIPS16; + + case SYMBOL_SMALL_DATA: + return true; + + case SYMBOL_CONSTANT_POOL: + /* PC-relative addressing is only available for lw and ld. */ + return GET_MODE_SIZE (mode) == 4 || GET_MODE_SIZE (mode) == 8; + + case SYMBOL_GOT_LOCAL: + return true; + + case SYMBOL_GOT_GLOBAL: + /* The address will have to be loaded from the GOT first. */ + return false; + + case SYMBOL_GOTOFF_PAGE: + case SYMBOL_GOTOFF_GLOBAL: + case SYMBOL_GOTOFF_CALL: + case SYMBOL_GOTOFF_LOADGP: + case SYMBOL_TLS: + case SYMBOL_TLSGD: + case SYMBOL_TLSLDM: + case SYMBOL_DTPREL: + case SYMBOL_GOTTPREL: + case SYMBOL_TPREL: + case SYMBOL_64_HIGH: + case SYMBOL_64_MID: + case SYMBOL_64_LOW: + return true; + } + gcc_unreachable (); +} + + +/* Return true if X is a valid address for machine mode MODE. If it is, + fill in INFO appropriately. STRICT is true if we should only accept + hard base registers. */ + +static bool +mips_classify_address (struct mips_address_info *info, rtx x, + enum machine_mode mode, int strict) +{ + 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); + + 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) + && const_arith_operand (info->offset, VOIDmode)); + + case LO_SUM: + info->type = ADDRESS_LO_SUM; + info->reg = XEXP (x, 0); + info->offset = XEXP (x, 1); + return (mips_valid_base_register_p (info->reg, mode, strict) + && mips_symbolic_constant_p (info->offset, &info->symbol_type) + && mips_symbolic_address_p (info->symbol_type, mode) + && mips_lo_relocs[info->symbol_type] != 0); + + 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, &info->symbol_type) + && mips_symbolic_address_p (info->symbol_type, mode) + && !mips_split_p[info->symbol_type]); + + default: + return false; + } +} + +/* Return true if X is a thread-local symbol. */ + +static bool +mips_tls_operand_p (rtx x) +{ + return GET_CODE (x) == SYMBOL_REF && SYMBOL_REF_TLS_MODEL (x) != 0; +} + +/* Return true if X can not be forced into a constant pool. */ + +static int +mips_tls_symbol_ref_1 (rtx *x, void *data ATTRIBUTE_UNUSED) +{ + return mips_tls_operand_p (*x); +} + +/* Return true if X can not be forced into a constant pool. */ + +static bool +mips_cannot_force_const_mem (rtx x) +{ + rtx base; + HOST_WIDE_INT offset; + + if (!TARGET_MIPS16) + { + /* 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 (GET_CODE (x) == CONST_INT) + return true; + + mips_split_const (x, &base, &offset); + if (symbolic_operand (base, VOIDmode) && SMALL_OPERAND (offset)) + return true; + } + + if (TARGET_HAVE_TLS && for_each_rtx (&x, &mips_tls_symbol_ref_1, 0)) + return true; + + return false; +} + +/* Implement TARGET_USE_BLOCKS_FOR_CONSTANT_P. MIPS16 uses per-function + constant pools, but normal-mode code doesn't need to. */ + +static bool +mips_use_blocks_for_constant_p (enum machine_mode mode ATTRIBUTE_UNUSED, + rtx x ATTRIBUTE_UNUSED) +{ + return !TARGET_MIPS16; +} + +/* Return the number of instructions needed to load a symbol of the + given type into a register. If valid in an address, the same number + of instructions are needed for loads and stores. Treat extended + mips16 instructions as two instructions. */ + +static int +mips_symbol_insns (enum mips_symbol_type type) +{ + switch (type) + { + case SYMBOL_GENERAL: + /* In mips16 code, general symbols must be fetched from the + constant pool. */ + if (TARGET_MIPS16) + return 0; + + /* 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. */ + return (ABI_HAS_64BIT_SYMBOLS ? 6 : 2); + + case SYMBOL_SMALL_DATA: + return 1; + + case SYMBOL_CONSTANT_POOL: + /* This case is for mips16 only. Assume we'll need an + extended instruction. */ + return 2; + + case SYMBOL_GOT_LOCAL: + case SYMBOL_GOT_GLOBAL: + /* Unless -funit-at-a-time is in effect, we can't be sure whether + the local/global classification is accurate. See override_options + for details. + + 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_GLOBAL: + case SYMBOL_GOTOFF_CALL: + case SYMBOL_GOTOFF_LOADGP: + 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: + /* Check whether the offset is a 16- or 32-bit value. */ + return mips_split_p[type] ? 2 : 1; + + case SYMBOL_TLS: + /* We don't treat a bare TLS symbol as a constant. */ + return 0; + } + gcc_unreachable (); +} + +/* 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 a value at OFFSET bytes from 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 HIs, twice + for SIs, and so on. An exception is SImode accesses off the + stack pointer, which have an 8-bit immediate field. */ + +static bool +mips16_unextended_reference_p (enum machine_mode mode, rtx base, rtx offset) +{ + if (TARGET_MIPS16 + && GET_CODE (offset) == CONST_INT + && INTVAL (offset) >= 0 + && (INTVAL (offset) & (GET_MODE_SIZE (mode) - 1)) == 0) + { + if (GET_MODE_SIZE (mode) == 4 && base == stack_pointer_rtx) + return INTVAL (offset) < 256 * GET_MODE_SIZE (mode); + return INTVAL (offset) < 32 * GET_MODE_SIZE (mode); + } + return false; +} + + +/* Return the number of instructions needed to load or store a value + of mode MODE at X. Return 0 if X isn't valid for MODE. + + For mips16 code, count extended instructions as two instructions. */ + +int +mips_address_insns (rtx x, enum machine_mode mode) +{ + struct mips_address_info addr; + int factor; + + if (mode == BLKmode) + /* BLKmode is used for single unaligned loads and stores. */ + factor = 1; + else + /* Each word of a multi-word value will be accessed individually. */ + factor = (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD; + + if (mips_classify_address (&addr, x, mode, false)) + switch (addr.type) + { + case ADDRESS_REG: + if (TARGET_MIPS16 + && !mips16_unextended_reference_p (mode, addr.reg, 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); + } + return 0; +} + + +/* Likewise for constant X. */ + +int +mips_const_insns (rtx x) +{ + struct mips_integer_op codes[MIPS_MAX_INTEGER_OPS]; + enum mips_symbol_type symbol_type; + HOST_WIDE_INT offset; + + switch (GET_CODE (x)) + { + case HIGH: + if (TARGET_MIPS16 + || !mips_symbolic_constant_p (XEXP (x, 0), &symbol_type) + || !mips_split_p[symbol_type]) + return 0; + + return 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 (INTVAL (x) >= 0 && INTVAL (x) < 256 ? 1 + : SMALL_OPERAND_UNSIGNED (INTVAL (x)) ? 2 + : INTVAL (x) > -256 && INTVAL (x) < 0 ? 2 + : SMALL_OPERAND_UNSIGNED (-INTVAL (x)) ? 3 + : 0); + + return mips_build_integer (codes, INTVAL (x)); + + case CONST_DOUBLE: + case CONST_VECTOR: + 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_type)) + return mips_symbol_insns (symbol_type); + + /* Otherwise try splitting the constant into a base and offset. + 16-bit offsets can be added using an extra addiu. Larger offsets + must be calculated separately and then added to the base. */ + mips_split_const (x, &x, &offset); + if (offset != 0) + { + int n = mips_const_insns (x); + if (n != 0) + { + if (SMALL_OPERAND (offset)) + return n + 1; + else + return n + 1 + mips_build_integer (codes, offset); + } + } + return 0; + + case SYMBOL_REF: + case LABEL_REF: + return mips_symbol_insns (mips_classify_symbol (x)); + + default: + return 0; + } +} + + +/* Return the number of instructions needed for memory reference X. + Count extended mips16 instructions as two instructions. */ + +int +mips_fetch_insns (rtx x) +{ + gcc_assert (MEM_P (x)); + return mips_address_insns (XEXP (x, 0), GET_MODE (x)); +} + + +/* 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; +} + +/* This function is used to implement GO_IF_LEGITIMATE_ADDRESS. It + returns a nonzero value if X is a legitimate address for a memory + operand of the indicated MODE. STRICT is nonzero if this function + is called during reload. */ + +bool +mips_legitimate_address_p (enum machine_mode mode, rtx x, int strict) +{ + struct mips_address_info addr; + + return mips_classify_address (&addr, x, mode, strict); +} + + +/* Copy VALUE to a register and return that register. If new psuedos + are allowed, copy it into a new register, otherwise use DEST. */ + +static rtx +mips_force_temporary (rtx dest, rtx value) +{ + if (!no_new_pseudos) + return force_reg (Pmode, value); + else + { + emit_move_insn (copy_rtx (dest), value); + return dest; + } +} + + +/* Return a LO_SUM expression for ADDR. TEMP is as for mips_force_temporary + and is used to load the high part into a register. */ + +rtx +mips_split_symbol (rtx temp, rtx addr) +{ + rtx high; + + if (TARGET_MIPS16) + high = mips16_gp_pseudo_reg (); + else + high = mips_force_temporary (temp, gen_rtx_HIGH (Pmode, copy_rtx (addr))); + return gen_rtx_LO_SUM (Pmode, high, addr); +} + + +/* 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; + HOST_WIDE_INT offset; + + mips_split_const (address, &base, &offset); + base = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, base), + UNSPEC_ADDRESS_FIRST + symbol_type); + return plus_constant (gen_rtx_CONST (Pmode, base), offset); +} + + +/* 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 available as a temporary register if needed. + + 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); + return mips_force_temporary (temp, gen_rtx_PLUS (Pmode, addr, base)); + } + return base; +} + + +/* 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. */ + high = GEN_INT (CONST_HIGH_PART (offset)); + 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); +} + +/* Emit a call to __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. The entire insn sequence is returned. */ + +static GTY(()) rtx mips_tls_symbol; + +static rtx +mips_call_tls_get_addr (rtx sym, enum mips_symbol_type type, rtx v0) +{ + rtx insn, loc, tga, 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))); + tga = gen_rtx_MEM (Pmode, mips_tls_symbol); + insn = emit_call_insn (gen_call_value (v0, tga, const0_rtx, const0_rtx)); + CONST_OR_PURE_CALL_P (insn) = 1; + use_reg (&CALL_INSN_FUNCTION_USAGE (insn), v0); + use_reg (&CALL_INSN_FUNCTION_USAGE (insn), a0); + insn = get_insns (); + + end_sequence (); + + return insn; +} + +/* Generate the code to access LOC, a thread local SYMBOL_REF. The + return value will be a valid address and move_operand (either a REG + or a LO_SUM). */ + +static rtx +mips_legitimize_tls_address (rtx loc) +{ + rtx dest, insn, v0, v1, tmp1, tmp2, eqv; + enum tls_model model; + + v0 = gen_rtx_REG (Pmode, GP_RETURN); + v1 = gen_rtx_REG (Pmode, GP_RETURN + 1); + + 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: + 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: + 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: + tmp1 = gen_reg_rtx (Pmode); + tmp2 = mips_unspec_address (loc, SYMBOL_GOTTPREL); + if (Pmode == DImode) + { + emit_insn (gen_tls_get_tp_di (v1)); + emit_insn (gen_load_gotdi (tmp1, pic_offset_table_rtx, tmp2)); + } + else + { + emit_insn (gen_tls_get_tp_si (v1)); + emit_insn (gen_load_gotsi (tmp1, pic_offset_table_rtx, tmp2)); + } + dest = gen_reg_rtx (Pmode); + emit_insn (gen_add3_insn (dest, tmp1, v1)); + break; + + case TLS_MODEL_LOCAL_EXEC: + if (Pmode == DImode) + emit_insn (gen_tls_get_tp_di (v1)); + else + emit_insn (gen_tls_get_tp_si (v1)); + + tmp1 = mips_unspec_offset_high (NULL, v1, loc, SYMBOL_TPREL); + dest = gen_rtx_LO_SUM (Pmode, tmp1, + mips_unspec_address (loc, SYMBOL_TPREL)); + break; + + default: + gcc_unreachable (); + } + + return dest; +} + +/* This function is used to implement LEGITIMIZE_ADDRESS. If *XLOC can + be legitimized in a way that the generic machinery might not expect, + put the new address in *XLOC and return true. MODE is the mode of + the memory being accessed. */ + +bool +mips_legitimize_address (rtx *xloc, enum machine_mode mode) +{ + enum mips_symbol_type symbol_type; + + if (mips_tls_operand_p (*xloc)) + { + *xloc = mips_legitimize_tls_address (*xloc); + return true; + } + + /* See if the address can split into a high part and a LO_SUM. */ + if (mips_symbolic_constant_p (*xloc, &symbol_type) + && mips_symbolic_address_p (symbol_type, mode) + && mips_split_p[symbol_type]) + { + *xloc = mips_split_symbol (0, *xloc); + return true; + } + + if (GET_CODE (*xloc) == PLUS && GET_CODE (XEXP (*xloc, 1)) == CONST_INT) + { + /* Handle REG + CONSTANT using mips_add_offset. */ + rtx reg; + + reg = XEXP (*xloc, 0); + if (!mips_valid_base_register_p (reg, mode, 0)) + reg = copy_to_mode_reg (Pmode, reg); + *xloc = mips_add_offset (0, reg, INTVAL (XEXP (*xloc, 1))); + return true; + } + + return false; +} + + +/* 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 our goal is to clear 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 + { + 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; + } +} + + +/* Load VALUE into DEST, using TEMP as a temporary register if need be. */ + +void +mips_move_integer (rtx dest, rtx temp, unsigned HOST_WIDE_INT value) +{ + struct mips_integer_op codes[MIPS_MAX_INTEGER_OPS]; + enum machine_mode mode; + unsigned int i, cost; + rtx x; + + mode = GET_MODE (dest); + cost = 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 < cost; i++) + { + if (no_new_pseudos) + { + 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; + HOST_WIDE_INT 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 (splittable_symbolic_operand (src, mode)) + { + emit_insn (gen_rtx_SET (VOIDmode, dest, mips_split_symbol (dest, src))); + return; + } + + if (mips_tls_operand_p (src)) + { + emit_move_insn (dest, mips_legitimize_tls_address (src)); + return; + } + + /* If we have (const (plus symbol offset)), load the symbol first + and then add in the offset. This is usually better than forcing + the constant into memory, at least in non-mips16 code. */ + mips_split_const (src, &base, &offset); + if (!TARGET_MIPS16 + && offset != 0 + && (!no_new_pseudos || SMALL_OPERAND (offset))) + { + base = mips_force_temporary (dest, base); + emit_move_insn (dest, mips_add_offset (0, base, offset)); + return; + } + + src = force_const_mem (mode, src); + + /* When using explicit relocs, constant pool references are sometimes + not legitimate addresses. */ + if (!memory_operand (src, VOIDmode)) + src = replace_equiv_address (src, mips_split_symbol (dest, XEXP (src, 0))); + emit_move_insn (dest, src); +} + + +/* If (set DEST SRC) is not a valid 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)) + { + emit_move_insn (dest, force_reg (mode, src)); + return true; + } + + /* Check for individual, fully-reloaded mflo and mfhi instructions. */ + if (GET_MODE_SIZE (mode) <= UNITS_PER_WORD + && REG_P (src) && MD_REG_P (REGNO (src)) + && REG_P (dest) && GP_REG_P (REGNO (dest))) + { + int other_regno = REGNO (src) == HI_REGNUM ? LO_REGNUM : HI_REGNUM; + if (GET_MODE_SIZE (mode) <= 4) + emit_insn (gen_mfhilo_si (gen_rtx_REG (SImode, REGNO (dest)), + gen_rtx_REG (SImode, REGNO (src)), + gen_rtx_REG (SImode, other_regno))); + else + emit_insn (gen_mfhilo_di (gen_rtx_REG (DImode, REGNO (dest)), + gen_rtx_REG (DImode, REGNO (src)), + gen_rtx_REG (DImode, other_regno))); + return true; + } + + /* We need to deal with constants that would be legitimate + immediate_operands but not 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; +} + +/* We need a lot of little routines to check constant values on the + mips16. These are used to figure out how long the instruction will + be. It would be much better to do this using constraints, but + there aren't nearly enough letters available. */ + +static int +m16_check_op (rtx op, int low, int high, int mask) +{ + return (GET_CODE (op) == CONST_INT + && INTVAL (op) >= low + && INTVAL (op) <= 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); +} + +static bool +mips_rtx_costs (rtx x, int code, int outer_code, int *total) +{ + enum machine_mode mode = GET_MODE (x); + bool float_mode_p = FLOAT_MODE_P (mode); + + switch (code) + { + case CONST_INT: + if (TARGET_MIPS16) + { + /* A number between 1 and 8 inclusive is efficient for a shift. + Otherwise, we will need an extended instruction. */ + if ((outer_code) == ASHIFT || (outer_code) == ASHIFTRT + || (outer_code) == LSHIFTRT) + { + if (INTVAL (x) >= 1 && INTVAL (x) <= 8) + *total = 0; + else + *total = COSTS_N_INSNS (1); + return true; + } + + /* We can use cmpi for an xor with an unsigned 16 bit value. */ + if ((outer_code) == XOR + && INTVAL (x) >= 0 && INTVAL (x) < 0x10000) + { + *total = 0; + return true; + } + + /* We may be able to use slt or sltu for a comparison with a + signed 16 bit value. (The boundary conditions aren't quite + right, but this is just a heuristic anyhow.) */ + if (((outer_code) == LT || (outer_code) == LE + || (outer_code) == GE || (outer_code) == GT + || (outer_code) == LTU || (outer_code) == LEU + || (outer_code) == GEU || (outer_code) == GTU) + && INTVAL (x) >= -0x8000 && INTVAL (x) < 0x8000) + { + *total = 0; + return true; + } + + /* Equality comparisons with 0 are cheap. */ + if (((outer_code) == EQ || (outer_code) == NE) + && INTVAL (x) == 0) + { + *total = 0; + return true; + } + + /* Constants in the range 0...255 can be loaded with an unextended + instruction. They are therefore as cheap as a register move. + + Given the choice between "li R1,0...255" and "move R1,R2" + (where R2 is a known constant), it is usually better to use "li", + since we do not want to unnecessarily extend the lifetime + of R2. */ + if (outer_code == SET + && INTVAL (x) >= 0 + && INTVAL (x) < 256) + { + *total = 0; + return true; + } + } + else + { + /* These can be used anywhere. */ + *total = 0; + return true; + } + + /* Otherwise fall through to the handling below because + we'll need to construct the constant. */ + + case CONST: + case SYMBOL_REF: + case LABEL_REF: + case CONST_DOUBLE: + if (LEGITIMATE_CONSTANT_P (x)) + { + *total = COSTS_N_INSNS (1); + return true; + } + else + { + /* 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, otherwise use the default handling. */ + int n = mips_address_insns (XEXP (x, 0), GET_MODE (x)); + if (n > 0) + { + *total = COSTS_N_INSNS (n + 1); + return true; + } + return false; + } + + case FFS: + *total = COSTS_N_INSNS (6); + return true; + + case NOT: + *total = COSTS_N_INSNS ((mode == DImode && !TARGET_64BIT) ? 2 : 1); + return true; + + case AND: + case IOR: + case XOR: + if (mode == DImode && !TARGET_64BIT) + { + *total = COSTS_N_INSNS (2); + return true; + } + return false; + + case ASHIFT: + case ASHIFTRT: + case LSHIFTRT: + if (mode == DImode && !TARGET_64BIT) + { + *total = COSTS_N_INSNS ((GET_CODE (XEXP (x, 1)) == CONST_INT) + ? 4 : 12); + return true; + } + return false; + + case ABS: + if (float_mode_p) + *total = COSTS_N_INSNS (1); + else + *total = COSTS_N_INSNS (4); + return true; + + case LO_SUM: + *total = COSTS_N_INSNS (1); + return true; + + case PLUS: + case MINUS: + if (float_mode_p) + { + *total = mips_cost->fp_add; + return true; + } + + else if (mode == DImode && !TARGET_64BIT) + { + *total = COSTS_N_INSNS (4); + return true; + } + return false; + + case NEG: + if (mode == DImode && !TARGET_64BIT) + { + *total = COSTS_N_INSNS (4); + return true; + } + return false; + + case MULT: + if (mode == SFmode) + *total = mips_cost->fp_mult_sf; + + else if (mode == DFmode) + *total = mips_cost->fp_mult_df; + + else if (mode == SImode) + *total = mips_cost->int_mult_si; + + else + *total = mips_cost->int_mult_di; + + return true; + + case DIV: + case MOD: + if (float_mode_p) + { + if (mode == SFmode) + *total = mips_cost->fp_div_sf; + else + *total = mips_cost->fp_div_df; + + return true; + } + /* Fall through. */ + + case UDIV: + case UMOD: + if (mode == DImode) + *total = mips_cost->int_div_di; + else + *total = mips_cost->int_div_si; + + return true; + + case SIGN_EXTEND: + /* A sign extend from SImode to DImode in 64 bit mode is often + zero instructions, because the result can often be used + directly by another instruction; we'll call it one. */ + if (TARGET_64BIT && mode == DImode + && GET_MODE (XEXP (x, 0)) == SImode) + *total = COSTS_N_INSNS (1); + else + *total = COSTS_N_INSNS (2); + return true; + + case ZERO_EXTEND: + if (TARGET_64BIT && mode == DImode + && GET_MODE (XEXP (x, 0)) == SImode) + *total = COSTS_N_INSNS (2); + else + *total = COSTS_N_INSNS (1); + return true; + + case FLOAT: + case UNSIGNED_FLOAT: + case FIX: + case FLOAT_EXTEND: + case FLOAT_TRUNCATE: + case SQRT: + *total = mips_cost->fp_add; + return true; + + default: + return false; + } +} + +/* Provide the costs of an addressing mode that contains ADDR. + If ADDR is not a valid address, its cost is irrelevant. */ + +static int +mips_address_cost (rtx addr) +{ + return mips_address_insns (addr, SImode); +} + +/* 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, int high_p) +{ + unsigned int byte; + enum machine_mode mode; + + mode = GET_MODE (op); + if (mode == VOIDmode) + mode = DImode; + + if (TARGET_BIG_ENDIAN ? !high_p : high_p) + byte = UNITS_PER_WORD; + else + byte = 0; + + if (REG_P (op)) + { + if (FP_REG_P (REGNO (op))) + return gen_rtx_REG (word_mode, high_p ? REGNO (op) + 1 : REGNO (op)); + if (ACC_HI_REG_P (REGNO (op))) + return gen_rtx_REG (word_mode, high_p ? REGNO (op) : REGNO (op) + 1); + } + + 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; + + /* FP->FP moves can be done in a single instruction. */ + if (FP_REG_RTX_P (src) && FP_REG_RTX_P (dest)) + return false; + + /* Check for floating-point loads and stores. They can be done using + ldc1 and sdc1 on MIPS II and above. */ + if (mips_isa > 1) + { + 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 64-bit move from SRC to DEST assuming that + mips_split_64bit_move_p holds. + + Moves into and out of FPRs cause some difficulty here. Such moves + will always be DFmode, since paired FPRs are not allowed to store + DImode values. The most natural representation would be two separate + 32-bit moves, such as: + + (set (reg:SI $f0) (mem:SI ...)) + (set (reg:SI $f1) (mem:SI ...)) + + However, the second insn is invalid because odd-numbered FPRs are + not allowed to store independent values. Use the patterns load_df_low, + load_df_high and store_df_high instead. */ + +void +mips_split_64bit_move (rtx dest, rtx src) +{ + if (FP_REG_RTX_P (dest)) + { + /* Loading an FPR from memory or from GPRs. */ + emit_insn (gen_load_df_low (copy_rtx (dest), mips_subword (src, 0))); + emit_insn (gen_load_df_high (dest, mips_subword (src, 1), + copy_rtx (dest))); + } + else if (FP_REG_RTX_P (src)) + { + /* Storing an FPR into memory or GPRs. */ + emit_move_insn (mips_subword (dest, 0), mips_subword (src, 0)); + emit_insn (gen_store_df_high (mips_subword (dest, 1), src)); + } + else + { + /* The operation can be split into two normal moves. Decide in + which order to do them. */ + rtx low_dest; + + low_dest = mips_subword (dest, 0); + if (REG_P (low_dest) + && reg_overlap_mentioned_p (low_dest, src)) + { + emit_move_insn (mips_subword (dest, 1), mips_subword (src, 1)); + emit_move_insn (low_dest, mips_subword (src, 0)); + } + else + { + emit_move_insn (low_dest, mips_subword (src, 0)); + emit_move_insn (mips_subword (dest, 1), mips_subword (src, 1)); + } + } +} + +/* 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; + bool dbl_p; + + dest_code = GET_CODE (dest); + src_code = GET_CODE (src); + dbl_p = (GET_MODE_SIZE (GET_MODE (dest)) == 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 (GET_MODE (dest)))) + { + if (dest_code == REG) + { + if (GP_REG_P (REGNO (dest))) + return "move\t%0,%z1"; + + if (MD_REG_P (REGNO (dest))) + return "mt%0\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) + return (dbl_p ? "sd\t%z1,%0" : "sw\t%z1,%0"); + } + if (dest_code == REG && GP_REG_P (REGNO (dest))) + { + if (src_code == REG) + { + 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 (ST_REG_P (REGNO (src)) && ISA_HAS_8CC) + return "lui\t%0,0x3f80\n\tmovf\t%0,%.,%1"; + + 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 (src_code == MEM) + return (dbl_p ? "ld\t%0,%1" : "lw\t%0,%1"); + + if (src_code == CONST_INT) + { + /* Don't use the X format, 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 (INTVAL (src) >= 0 && INTVAL (src) <= 0xffff) + return "li\t%0,%1"; + + if (INTVAL (src) < 0 && INTVAL (src) >= -0xffff) + return "#"; + } + + if (src_code == HIGH) + return "lui\t%0,%h1"; + + if (CONST_GP_P (src)) + return "move\t%0,%1"; + + if (symbolic_operand (src, VOIDmode)) + 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 (); +} + +/* Restore $gp from its save slot. Valid only when using o32 or + o64 abicalls. */ + +void +mips_restore_gp (void) +{ + rtx address, slot; + + gcc_assert (TARGET_ABICALLS && TARGET_OLDABI); + + address = mips_add_offset (pic_offset_table_rtx, + frame_pointer_needed + ? hard_frame_pointer_rtx + : stack_pointer_rtx, + current_function_outgoing_args_size); + slot = gen_rtx_MEM (Pmode, address); + + emit_move_insn (pic_offset_table_rtx, slot); + if (!TARGET_EXPLICIT_RELOCS) + emit_insn (gen_blockage ()); +} + +/* 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))); +} + +/* Return true if CMP1 is a suitable second operand for relational + operator CODE. See also the *sCC patterns in mips.md. */ + +static bool +mips_relational_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 (); + } +} + +/* Canonicalize LE or LEU comparisons into LT comparisons when + possible to avoid extra instructions or inverting the + comparison. */ + +static bool +mips_canonicalize_comparison (enum rtx_code *code, rtx *cmp1, + enum machine_mode mode) +{ + HOST_WIDE_INT original, plus_one; + + if (GET_CODE (*cmp1) != CONST_INT) + return false; + + original = INTVAL (*cmp1); + plus_one = trunc_int_for_mode ((unsigned HOST_WIDE_INT) original + 1, mode); + + switch (*code) + { + case LE: + if (original < plus_one) + { + *code = LT; + *cmp1 = force_reg (mode, GEN_INT (plus_one)); + return true; + } + break; + + case LEU: + if (plus_one != 0) + { + *code = LTU; + *cmp1 = force_reg (mode, GEN_INT (plus_one)); + return true; + } + break; + + default: + return false; + } + + return false; + +} + +/* Compare CMP0 and CMP1 using relational operator CODE and store the + result in TARGET. CMP0 and TARGET are register_operands that have + the same integer mode. 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_relational (enum rtx_code code, bool *invert_ptr, + rtx target, rtx cmp0, rtx cmp1) +{ + /* First see if there is a MIPS instruction that can do this operation + with CMP1 in its current form. If not, try to canonicalize the + comparison to LT. If that fails, try doing the same for the + inverse operation. If that also fails, force CMP1 into a register + and try again. */ + if (mips_relational_operand_ok_p (code, cmp1)) + mips_emit_binary (code, target, cmp0, cmp1); + else if (mips_canonicalize_comparison (&code, &cmp1, GET_MODE (target))) + mips_emit_binary (code, target, cmp0, cmp1); + else + { + enum rtx_code inv_code = reverse_condition (code); + if (!mips_relational_operand_ok_p (inv_code, cmp1)) + { + cmp1 = force_reg (GET_MODE (cmp0), cmp1); + mips_emit_int_relational (code, invert_ptr, target, cmp0, cmp1); + } + else if (invert_ptr == 0) + { + rtx inv_target = gen_reg_rtx (GET_MODE (target)); + mips_emit_binary (inv_code, inv_target, 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_reverse_fp_cond_p (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. cmp_operands[0] and cmp_operands[1] 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/NE comparisons against zero are possible, + otherwise any standard branch condition can be used. The standard branch + conditions are: + + - EQ/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) +{ + if (GET_MODE_CLASS (GET_MODE (cmp_operands[0])) == MODE_INT) + { + if (!need_eq_ne_p && cmp_operands[1] == const0_rtx) + { + *op0 = cmp_operands[0]; + *op1 = cmp_operands[1]; + } + else if (*code == EQ || *code == NE) + { + if (need_eq_ne_p) + { + *op0 = mips_zero_if_equal (cmp_operands[0], cmp_operands[1]); + *op1 = const0_rtx; + } + else + { + *op0 = cmp_operands[0]; + *op1 = force_reg (GET_MODE (*op0), cmp_operands[1]); + } + } + 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_operands[0])); + *op1 = const0_rtx; + mips_emit_int_relational (*code, &invert, *op0, + cmp_operands[0], cmp_operands[1]); + *code = (invert ? EQ : NE); + } + } + 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_reverse_fp_cond_p (&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_operands[0], cmp_operands[1]); + } +} + +/* Try comparing cmp_operands[0] and cmp_operands[1] using rtl code CODE. + Store the result in TARGET and return true if successful. + + On 64-bit targets, TARGET may be wider than cmp_operands[0]. */ + +bool +mips_emit_scc (enum rtx_code code, rtx target) +{ + if (GET_MODE_CLASS (GET_MODE (cmp_operands[0])) != MODE_INT) + return false; + + target = gen_lowpart (GET_MODE (cmp_operands[0]), target); + if (code == EQ || code == NE) + { + rtx zie = mips_zero_if_equal (cmp_operands[0], cmp_operands[1]); + mips_emit_binary (code, target, zie, const0_rtx); + } + else + mips_emit_int_relational (code, 0, target, + cmp_operands[0], cmp_operands[1]); + return true; +} + +/* Emit the common code for doing conditional branches. + operand[0] is the label to jump to. + The comparison operands are saved away by cmp{si,di,sf,df}. */ + +void +gen_conditional_branch (rtx *operands, enum rtx_code code) +{ + rtx op0, op1, 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[0])); +} + +/* 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_reverse_fp_cond_p (&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)); +} + +/* Emit the common code for conditional moves. OPERANDS is the array + of operands passed to the conditional move define_expand. */ + +void +gen_conditional_move (rtx *operands) +{ + enum rtx_code code; + rtx op0, op1; + + code = GET_CODE (operands[1]); + mips_emit_compare (&code, &op0, &op1, true); + emit_insn (gen_rtx_SET (VOIDmode, operands[0], + gen_rtx_IF_THEN_ELSE (GET_MODE (operands[0]), + gen_rtx_fmt_ee (code, + GET_MODE (op0), + op0, op1), + operands[2], operands[3]))); +} + +/* Emit a conditional trap. OPERANDS is the array of operands passed to + the conditional_trap expander. */ + +void +mips_gen_conditional_trap (rtx *operands) +{ + rtx op0, op1; + enum rtx_code cmp_code = GET_CODE (operands[0]); + enum machine_mode mode = GET_MODE (cmp_operands[0]); + + /* MIPS conditional trap machine instructions don't have GT or LE + flavors, so we must invert the comparison and convert to LT and + GE, respectively. */ + switch (cmp_code) + { + case GT: cmp_code = LT; break; + case LE: cmp_code = GE; break; + case GTU: cmp_code = LTU; break; + case LEU: cmp_code = GEU; break; + default: break; + } + if (cmp_code == GET_CODE (operands[0])) + { + op0 = cmp_operands[0]; + op1 = cmp_operands[1]; + } + else + { + op0 = cmp_operands[1]; + op1 = cmp_operands[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 (cmp_code, mode, op0, op1), + operands[1])); +} + +/* Load function address ADDR into register DEST. SIBCALL_P is true + if the address is needed for a sibling call. */ + +static void +mips_load_call_address (rtx dest, rtx addr, int sibcall_p) +{ + /* 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 NewABI sibcalls since the value of $gp on entry + to the stub would be our caller's gp, not ours. */ + if (TARGET_EXPLICIT_RELOCS + && !(sibcall_p && TARGET_NEWABI) + && global_got_operand (addr, VOIDmode)) + { + rtx high, lo_sum_symbol; + + high = mips_unspec_offset_high (dest, pic_offset_table_rtx, + addr, SYMBOL_GOTOFF_CALL); + lo_sum_symbol = mips_unspec_address (addr, SYMBOL_GOTOFF_CALL); + if (Pmode == SImode) + emit_insn (gen_load_callsi (dest, high, lo_sum_symbol)); + else + emit_insn (gen_load_calldi (dest, high, lo_sum_symbol)); + } + else + emit_move_insn (dest, addr); +} + + +/* Expand a call or call_value instruction. RESULT is where the + result will go (null for calls), 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. SIBCALL_P is true + if we are expanding a sibling call, false if we're expanding + a normal call. */ + +void +mips_expand_call (rtx result, rtx addr, rtx args_size, rtx aux, int sibcall_p) +{ + rtx orig_addr, pattern, insn; + + orig_addr = addr; + if (!call_insn_operand (addr, VOIDmode)) + { + addr = gen_reg_rtx (Pmode); + mips_load_call_address (addr, orig_addr, sibcall_p); + } + + if (TARGET_MIPS16 + && mips16_hard_float + && build_mips16_call_stub (result, addr, args_size, + aux == 0 ? 0 : (int) GET_MODE (aux))) + return; + + if (result == 0) + pattern = (sibcall_p + ? gen_sibcall_internal (addr, args_size) + : gen_call_internal (addr, args_size)); + else if (GET_CODE (result) == PARALLEL && XVECLEN (result, 0) == 2) + { + rtx reg1, reg2; + + reg1 = XEXP (XVECEXP (result, 0, 0), 0); + reg2 = XEXP (XVECEXP (result, 0, 1), 0); + pattern = + (sibcall_p + ? gen_sibcall_value_multiple_internal (reg1, addr, args_size, reg2) + : gen_call_value_multiple_internal (reg1, addr, args_size, reg2)); + } + else + pattern = (sibcall_p + ? gen_sibcall_value_internal (result, addr, args_size) + : gen_call_value_internal (result, addr, args_size)); + + insn = emit_call_insn (pattern); + + /* Lazy-binding stubs require $gp to be valid on entry. */ + if (global_got_operand (orig_addr, VOIDmode)) + use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx); +} + + +/* We can handle any sibcall when TARGET_SIBCALLS is true. */ + +static bool +mips_function_ok_for_sibcall (tree decl ATTRIBUTE_UNUSED, + tree exp ATTRIBUTE_UNUSED) +{ + return TARGET_SIBCALLS; +} + +/* Emit code to move general operand SRC into condition-code + register DEST. SCRATCH is a scratch TFmode float register. + The sequence is: + + FP1 = SRC + FP2 = 0.0f + DEST = FP2 < FP1 + + where FP1 and FP2 are single-precision float registers + taken from SCRATCH. */ + +void +mips_emit_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) + FP_INC); + + emit_move_insn (copy_rtx (fp1), src); + emit_move_insn (copy_rtx (fp2), CONST0_RTX (SFmode)); + emit_insn (gen_slt_sf (dest, fp2, fp1)); +} + +/* 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; + + compute_frame_size (get_frame_size ()); + gcc_assert ((cfun->machine->frame.mask >> 31) & 1); + slot_address = mips_add_offset (scratch, stack_pointer_rtx, + cfun->machine->frame.gp_sp_offset); + + emit_move_insn (gen_rtx_MEM (GET_MODE (address), slot_address), address); +} + +/* 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 = alloca (sizeof (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) + emit_move_insn (regs[i], adjust_address (src, mode, offset)); + else + { + rtx part = adjust_address (src, BLKmode, offset); + if (!mips_expand_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) + emit_move_insn (adjust_address (dest, mode, offset), regs[i]); + else + { + rtx part = adjust_address (dest, BLKmode, offset); + if (!mips_expand_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); + } +} + +#define MAX_MOVE_REGS 4 +#define MAX_MOVE_BYTES (MAX_MOVE_REGS * UNITS_PER_WORD) + + +/* 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 MAX_MOVE_BYTES + per iteration. LENGTH must be at least MAX_MOVE_BYTES. Assume that the + memory regions do not overlap. */ + +static void +mips_block_move_loop (rtx dest, rtx src, HOST_WIDE_INT length) +{ + rtx label, src_reg, dest_reg, final_src; + HOST_WIDE_INT leftover; + + leftover = length % MAX_MOVE_BYTES; + length -= leftover; + + /* Create registers and memory references for use within the loop. */ + mips_adjust_block_mem (src, MAX_MOVE_BYTES, &src_reg, &src); + mips_adjust_block_mem (dest, MAX_MOVE_BYTES, &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, MAX_MOVE_BYTES); + + /* Move on to the next block. */ + emit_move_insn (src_reg, plus_constant (src_reg, MAX_MOVE_BYTES)); + emit_move_insn (dest_reg, plus_constant (dest_reg, MAX_MOVE_BYTES)); + + /* Emit the loop condition. */ + if (Pmode == DImode) + emit_insn (gen_cmpdi (src_reg, final_src)); + else + emit_insn (gen_cmpsi (src_reg, final_src)); + emit_jump_insn (gen_bne (label)); + + /* Mop up any left-over bytes. */ + if (leftover) + mips_block_move_straight (dest, src, leftover); +} + +/* Expand a movmemsi instruction. */ + +bool +mips_expand_block_move (rtx dest, rtx src, rtx length) +{ + if (GET_CODE (length) == CONST_INT) + { + if (INTVAL (length) <= 2 * MAX_MOVE_BYTES) + { + mips_block_move_straight (dest, src, INTVAL (length)); + return true; + } + else if (optimize) + { + mips_block_move_loop (dest, src, INTVAL (length)); + return true; + } + } + return false; +} + +/* Argument support functions. */ + +/* Initialize CUMULATIVE_ARGS for a function. */ + +void +init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype, + rtx libname ATTRIBUTE_UNUSED) +{ + static CUMULATIVE_ARGS zero_cum; + tree param, next_param; + + *cum = zero_cum; + cum->prototype = (fntype && TYPE_ARG_TYPES (fntype)); + + /* Determine if this function has variable arguments. This is + indicated by the last argument being 'void_type_mode' if there + are no variable arguments. The standard MIPS calling sequence + passes all arguments in the general purpose registers in this case. */ + + for (param = fntype ? TYPE_ARG_TYPES (fntype) : 0; + param != 0; param = next_param) + { + next_param = TREE_CHAIN (param); + if (next_param == 0 && TREE_VALUE (param) != void_type_node) + cum->gp_reg_found = 1; + } +} + + +/* 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_arg_info (const CUMULATIVE_ARGS *cum, enum machine_mode mode, + tree type, int named, struct mips_arg_info *info) +{ + 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 and complex floating-point types are passed in + floating-point registers. */ + 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 = 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; +} + + +/* Implement FUNCTION_ARG_ADVANCE. */ + +void +function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode, + tree type, int named) +{ + struct mips_arg_info info; + + mips_arg_info (cum, mode, type, named, &info); + + 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 the O32 + ABI, which passes at most 2 arguments in float registers. */ + if (cum->arg_number < 2 && info.fpr_p) + cum->fp_code += (mode == SFmode ? 1 : 2) << ((cum->arg_number - 1) * 2); + + 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 += FP_INC; + + if (info.stack_words > 0) + cum->stack_words = info.stack_offset + info.stack_words; + + cum->arg_number++; +} + +/* Implement FUNCTION_ARG. */ + +struct rtx_def * +function_arg (const CUMULATIVE_ARGS *cum, enum machine_mode mode, + tree type, int 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 + insn. 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 0; + } + + mips_arg_info (cum, mode, type, named, &info); + + /* Return straight away if the whole argument is passed on the stack. */ + if (info.reg_offset == MAX_ARGS_IN_REGISTERS) + return 0; + + if (type != 0 + && TREE_CODE (type) == RECORD_TYPE + && TARGET_NEWABI + && TYPE_SIZE_UNIT (type) + && host_integerp (TYPE_SIZE_UNIT (type), 1) + && named) + { + /* The Irix 6 n32/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. */ + tree field; + + /* First check to see if there is any such field. */ + for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field)) + if (TREE_CODE (field) == FIELD_DECL + && TREE_CODE (TREE_TYPE (field)) == REAL_TYPE + && 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 = TREE_CHAIN (field)) + if (TREE_CODE (field) == FIELD_DECL + && int_bit_position (field) >= bitpos) + break; + + if (field + && int_bit_position (field) == bitpos + && TREE_CODE (TREE_TYPE (field)) == REAL_TYPE + && !TARGET_SOFT_FLOAT + && 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; + int reg; + + inner = GET_MODE_INNER (mode); + reg = 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, reg); + } + else + { + gcc_assert (info.stack_words == 0); + real = gen_rtx_EXPR_LIST (VOIDmode, + gen_rtx_REG (inner, reg), + const0_rtx); + imag = gen_rtx_EXPR_LIST (VOIDmode, + gen_rtx_REG (inner, + reg + info.reg_words / 2), + GEN_INT (GET_MODE_SIZE (inner))); + return gen_rtx_PARALLEL (mode, gen_rtvec (2, real, imag)); + } + } + + if (!info.fpr_p) + return gen_rtx_REG (mode, GP_ARG_FIRST + info.reg_offset); + else if (info.reg_offset == 1) + /* This code handles the special o32 case in which the second word + of the argument structure is passed in floating-point registers. */ + return gen_rtx_REG (mode, FP_ARG_FIRST + FP_INC); + else + return gen_rtx_REG (mode, FP_ARG_FIRST + info.reg_offset); +} + + +/* 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_arg_info (cum, mode, type, named, &info); + return info.stack_words > 0 ? info.reg_words * UNITS_PER_WORD : 0; +} + + +/* Implement 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. */ + +int +function_arg_boundary (enum machine_mode mode, 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, 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) + : GET_MODE_CLASS (mode) == MODE_INT) + 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); +} + +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; + FUNCTION_ARG_ADVANCE (local_cum, mode, type, 1); + + /* 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_rtx_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; + off &= ~(UNITS_PER_FPVALUE - 1); + off -= fp_saved * UNITS_PER_FPREG; + + mode = TARGET_SINGLE_FLOAT ? SFmode : DFmode; + + for (i = local_cum.num_fprs; i < MAX_ARGS_IN_REGISTERS; i += FP_INC) + { + rtx ptr, mem; + + ptr = plus_constant (virtual_incoming_args_rtx, off); + mem = gen_rtx_MEM (mode, ptr); + set_mem_alias_set (mem, get_varargs_alias_set ()); + emit_move_insn (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); +} + +/* Create the va_list data type. + 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 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. + If !EABI_FLOAT_VARARGS_P, then no FPR save area exists, and a single + pointer is enough. It's started at the GPR save area, and is + advanced, period. + Note that the GPR save area is not constant size, due to optimization + in the prologue. Hence, we can't use a design with two pointers + and two offsets, although we could have designed this with two pointers + and three offsets. */ + +static tree +mips_build_builtin_va_list (void) +{ + if (EABI_FLOAT_VARARGS_P) + { + 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 (FIELD_DECL, get_identifier ("__overflow_argptr"), + ptr_type_node); + f_gtop = build_decl (FIELD_DECL, get_identifier ("__gpr_top"), + ptr_type_node); + f_ftop = build_decl (FIELD_DECL, get_identifier ("__fpr_top"), + ptr_type_node); + f_goff = build_decl (FIELD_DECL, get_identifier ("__gpr_offset"), + unsigned_char_type_node); + f_foff = build_decl (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 (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; + TREE_CHAIN (f_ovfl) = f_gtop; + TREE_CHAIN (f_gtop) = f_ftop; + TREE_CHAIN (f_ftop) = f_goff; + TREE_CHAIN (f_goff) = f_foff; + TREE_CHAIN (f_foff) = f_res; + + layout_type (record); + return record; + } + else if (TARGET_IRIX && 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 va_start. */ + +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 = ¤t_function_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 = TREE_CHAIN (f_ovfl); + f_ftop = TREE_CHAIN (f_gtop); + f_goff = TREE_CHAIN (f_ftop); + f_foff = TREE_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 (PLUS_EXPR, TREE_TYPE (ovfl), t, + build_int_cst (NULL_TREE, + 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 - 1); + if (fpr_offset) + t = build2 (PLUS_EXPR, TREE_TYPE (ftop), t, + build_int_cst (NULL_TREE, -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 (NULL_TREE, 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 (NULL_TREE, 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 va_arg. */ + +static tree +mips_gimplify_va_arg_expr (tree valist, tree type, tree *pre_p, tree *post_p) +{ + HOST_WIDE_INT size, rsize; + tree addr; + bool indirect; + + indirect = pass_by_reference (NULL, TYPE_MODE (type), type, 0); + + if (indirect) + type = build_pointer_type (type); + + size = int_size_in_bytes (type); + rsize = (size + UNITS_PER_WORD - 1) & -UNITS_PER_WORD; + + if (mips_abi != ABI_EABI || !EABI_FLOAT_VARARGS_P) + addr = std_gimplify_va_arg_expr (valist, type, pre_p, post_p); + else + { + /* Not a simple merged stack. */ + + tree f_ovfl, f_gtop, f_ftop, f_goff, f_foff; + tree ovfl, top, off, align; + HOST_WIDE_INT osize; + tree t, u; + + f_ovfl = TYPE_FIELDS (va_list_type_node); + f_gtop = TREE_CHAIN (f_ovfl); + f_ftop = TREE_CHAIN (f_gtop); + f_goff = TREE_CHAIN (f_ftop); + f_foff = TREE_CHAIN (f_goff); + + /* We maintain separate pointers and offsets for floating-point + and integer arguments, but we need similar code in both cases. + Let: + + TOP be the top of the register save area; + OFF be the offset from TOP of the next register; + ADDR_RTX be the address of the argument; + RSIZE be the number of bytes used to store the argument + when it's in the register save area; + OSIZE be the number of bytes used to store it when it's + in the stack overflow area; and + PADDING be (BYTES_BIG_ENDIAN ? OSIZE - RSIZE : 0) + + The code we want is: + + 1: off &= -rsize; // round down + 2: if (off != 0) + 3: { + 4: addr_rtx = top - off; + 5: off -= rsize; + 6: } + 7: else + 8: { + 9: ovfl += ((intptr_t) ovfl + osize - 1) & -osize; + 10: addr_rtx = ovfl + PADDING; + 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); + + 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), valist, f_ftop, + NULL_TREE); + off = build3 (COMPONENT_REF, TREE_TYPE (f_foff), valist, f_foff, + NULL_TREE); + + /* When floating-point registers are saved to the stack, + each one will take up UNITS_PER_HWFPVALUE bytes, regardless + of the float'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), valist, f_gtop, + NULL_TREE); + off = build3 (COMPONENT_REF, TREE_TYPE (f_goff), valist, f_goff, + NULL_TREE); + if (rsize > UNITS_PER_WORD) + { + /* [1] Emit code for: off &= -rsize. */ + t = build2 (BIT_AND_EXPR, TREE_TYPE (off), off, + build_int_cst (NULL_TREE, -rsize)); + t = build2 (MODIFY_EXPR, TREE_TYPE (off), off, t); + gimplify_and_add (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-increment not available to C. Also widen for the + coming pointer arithmetic. */ + t = fold_convert (TREE_TYPE (off), build_int_cst (NULL_TREE, rsize)); + t = build2 (POSTDECREMENT_EXPR, TREE_TYPE (off), off, t); + t = fold_convert (sizetype, t); + t = fold_convert (TREE_TYPE (top), t); + + /* [4] Emit code for: addr_rtx = top - off. On big endian machines, + the argument has RSIZE - SIZE bytes of leading padding. */ + t = build2 (MINUS_EXPR, TREE_TYPE (top), top, t); + if (BYTES_BIG_ENDIAN && rsize > size) + { + u = fold_convert (TREE_TYPE (t), build_int_cst (NULL_TREE, + rsize - size)); + t = build2 (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 = fold_convert (TREE_TYPE (ovfl), + build_int_cst (NULL_TREE, osize - 1)); + t = build2 (PLUS_EXPR, TREE_TYPE (ovfl), ovfl, u); + u = fold_convert (TREE_TYPE (ovfl), + build_int_cst (NULL_TREE, -osize)); + t = build2 (BIT_AND_EXPR, TREE_TYPE (ovfl), t, u); + align = build2 (MODIFY_EXPR, TREE_TYPE (ovfl), ovfl, t); + } + else + align = NULL; + + /* [10, 11]. Emit code to store ovfl in addr_rtx, then + post-increment ovfl by osize. On big-endian machines, + the argument has OSIZE - SIZE bytes of leading padding. */ + 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 = fold_convert (TREE_TYPE (t), + build_int_cst (NULL_TREE, osize - size)); + t = build2 (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) + addr = build_va_arg_indirect_ref (addr); + + return addr; +} + +/* 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, unsigned int width, 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 + be the upper word and RIGHT the lower word. */ + if (TARGET_BIG_ENDIAN) + *left = first, *right = last; + else + *left = last, *right = first; + + return true; +} + + +/* Try to emit the equivalent of (set DEST (zero_extract SRC WIDTH BITPOS)). + Return true on success. We only handle cases where zero_extract is + equivalent to sign_extract. */ + +bool +mips_expand_unaligned_load (rtx dest, rtx src, unsigned int width, int bitpos) +{ + rtx left, right, temp; + + /* If TARGET_64BIT, the destination of a 32-bit load 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 + && SUBREG_BYTE (dest) == 0 + && 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 expand (set (zero_extract DEST WIDTH BITPOS) SRC). Return + true on success. */ + +bool +mips_expand_unaligned_store (rtx dest, rtx src, unsigned int width, 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 SIZE POSITION) 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 <= POSITION < GET_MODE_BITSIZE (GET_MODE (op)) + 0 < SIZE <= GET_MODE_BITSIZE (GET_MODE (op)) + 0 < POSITION + SIZE <= 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, rtx size, rtx position) +{ + HOST_WIDE_INT len, pos; + + if (!ISA_HAS_EXT_INS + || !register_operand (op, VOIDmode) + || GET_MODE_BITSIZE (GET_MODE (op)) > BITS_PER_WORD) + return false; + + len = INTVAL (size); + pos = INTVAL (position); + + if (len <= 0 || len >= GET_MODE_BITSIZE (GET_MODE (op)) + || pos < 0 || pos + len > GET_MODE_BITSIZE (GET_MODE (op))) + return false; + + return true; +} + +/* 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_info = mips_parse_cpu (ACONCAT (("mips", arg, NULL))); + return mips_isa_info != 0; + + case OPT_mno_flush_func: + mips_cache_flush_func = NULL; + return true; + + default: + return true; + } +} + +/* Set up the threshold for data to go into the small data area, instead + of the normal data area, and detect any conflicts in the switches. */ + +void +override_options (void) +{ + int i, start, regno; + enum machine_mode mode; + + mips_section_threshold = g_switch_set ? 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_info != 0) + { + if (mips_arch_info == 0) + mips_set_architecture (mips_isa_info); + else if (mips_arch_info->isa != mips_isa_info->isa) + error ("-%s conflicts with the other architecture options, " + "which specify a %s processor", + mips_isa_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); + + /* Set cost structure for the processor. */ + mips_cost = &mips_rtx_cost_data[mips_tune]; + + 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) + { + /* Really, -mfp32 and -mfp64 are ornamental options. There's + only one right answer here. */ + if (TARGET_64BIT && TARGET_DOUBLE_FLOAT && !TARGET_FLOAT64) + error ("unsupported combination: %s", "-mgp64 -mfp32 -mdouble-float"); + else if (!TARGET_64BIT && TARGET_FLOAT64) + error ("unsupported combination: %s", "-mgp32 -mfp64"); + else if (TARGET_SINGLE_FLOAT && TARGET_FLOAT64) + error ("unsupported combination: %s", "-mfp64 -msingle-float"); + } + 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 ((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 (MIPS_MARCH_CONTROLS_SOFT_FLOAT + && (target_flags_explicit & MASK_SOFT_FLOAT) == 0) + { + /* For some configurations, it is useful to have -march control + the default setting of MASK_SOFT_FLOAT. */ + switch ((int) mips_arch) + { + case PROCESSOR_R4100: + case PROCESSOR_R4111: + case PROCESSOR_R4120: + case PROCESSOR_R4130: + target_flags |= MASK_SOFT_FLOAT; + break; + + default: + target_flags &= ~MASK_SOFT_FLOAT; + break; + } + } + + if (!TARGET_OLDABI) + flag_pcc_struct_return = 0; + + if ((target_flags_explicit & MASK_BRANCHLIKELY) == 0) + { + /* If neither -mbranch-likely nor -mno-branch-likely was given + on the command line, set MASK_BRANCHLIKELY based on the target + architecture. + + By default, we enable use of Branch Likely instructions on + all architectures which support them with the following + exceptions: when creating MIPS32 or MIPS64 code, and when + tuning for architectures where their use tends to hurt + performance. + + The MIPS32 and MIPS64 architecture specifications say "Software + is strongly encouraged to avoid use of Branch Likely + instructions, as they will be removed from a future revision + of the [MIPS32 and MIPS64] architecture." Therefore, we do not + issue those instructions unless instructed to do so by + -mbranch-likely. */ + if (ISA_HAS_BRANCHLIKELY + && !(ISA_MIPS32 || ISA_MIPS32R2 || ISA_MIPS64) + && !(TUNE_MIPS5500 || TUNE_SB1)) + target_flags |= MASK_BRANCHLIKELY; + else + target_flags &= ~MASK_BRANCHLIKELY; + } + if (TARGET_BRANCHLIKELY && !ISA_HAS_BRANCHLIKELY) + warning (0, "generation of Branch Likely instructions enabled, but not supported by architecture"); + + /* 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) + { + /* 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.) + + Also, there is a body of code that uses __PIC__ to distinguish + between -mabicalls and -mno-abicalls code. */ + flag_pic = 1; + if (mips_section_threshold > 0) + warning (0, "%<-G%> is incompatible with %<-mabicalls%>"); + } + + /* mips_split_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. */ + if (!TARGET_MIPS16 && TARGET_SPLIT_ADDRESSES + && optimize && !flag_pic + && !ABI_HAS_64BIT_SYMBOLS) + mips_split_addresses = 1; + else + mips_split_addresses = 0; + + /* -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; + + /* When compiling for the mips16, we cannot use floating point. We + record the original hard float value in mips16_hard_float. */ + if (TARGET_MIPS16) + { + if (TARGET_SOFT_FLOAT) + mips16_hard_float = 0; + else + mips16_hard_float = 1; + target_flags |= MASK_SOFT_FLOAT; + + /* Don't run the scheduler before reload, since it tends to + increase register pressure. */ + flag_schedule_insns = 0; + + /* Don't do hot/cold partitioning. The constant layout code expects + the whole function to be in a single section. */ + flag_reorder_blocks_and_partition = 0; + + /* Silently disable -mexplicit-relocs since it doesn't apply + to mips16 code. Even so, it would overly pedantic to warn + about "-mips16 -mexplicit-relocs", especially given that + we use a %gprel() operator. */ + target_flags &= ~MASK_EXPLICIT_RELOCS; + } + + /* When using explicit relocs, we call dbr_schedule from within + mips_reorg. */ + if (TARGET_EXPLICIT_RELOCS) + { + mips_flag_delayed_branch = flag_delayed_branch; + flag_delayed_branch = 0; + } + +#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 are both true. */ + if (TARGET_PAIRED_SINGLE_FLOAT && !(TARGET_FLOAT64 && TARGET_HARD_FLOAT)) + error ("-mips3d/-mpaired-single must be used with -mfp64 -mhard-float"); + + /* Make sure that the ISA supports TARGET_PAIRED_SINGLE_FLOAT when it is + enabled. */ + if (TARGET_PAIRED_SINGLE_FLOAT && !ISA_MIPS64) + error ("-mips3d/-mpaired-single must be used with -mips64"); + + if (TARGET_MIPS16 && TARGET_DSP) + error ("-mips16 and -mdsp cannot be used together"); + + mips_print_operand_punct['?'] = 1; + mips_print_operand_punct['#'] = 1; + mips_print_operand_punct['/'] = 1; + mips_print_operand_punct['&'] = 1; + mips_print_operand_punct['!'] = 1; + mips_print_operand_punct['*'] = 1; + mips_print_operand_punct['@'] = 1; + mips_print_operand_punct['.'] = 1; + mips_print_operand_punct['('] = 1; + mips_print_operand_punct[')'] = 1; + mips_print_operand_punct['['] = 1; + mips_print_operand_punct[']'] = 1; + mips_print_operand_punct['<'] = 1; + mips_print_operand_punct['>'] = 1; + mips_print_operand_punct['{'] = 1; + mips_print_operand_punct['}'] = 1; + mips_print_operand_punct['^'] = 1; + mips_print_operand_punct['$'] = 1; + mips_print_operand_punct['+'] = 1; + mips_print_operand_punct['~'] = 1; + + /* 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] = -1; + + 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; + + mips_dbx_regno[HI_REGNUM] = MD_DBX_FIRST + 0; + mips_dbx_regno[LO_REGNUM] = MD_DBX_FIRST + 1; + + /* Set up array giving whether a given register can hold a given mode. */ + + for (mode = VOIDmode; + mode != MAX_MACHINE_MODE; + mode = (enum machine_mode) ((int)mode + 1)) + { + register int size = GET_MODE_SIZE (mode); + register enum mode_class class = GET_MODE_CLASS (mode); + + for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + { + register int temp; + + if (mode == CCV2mode) + temp = (ISA_HAS_8CC + && ST_REG_P (regno) + && (regno - ST_REG_FIRST) % 2 == 0); + + else if (mode == CCV4mode) + temp = (ISA_HAS_8CC + && ST_REG_P (regno) + && (regno - ST_REG_FIRST) % 4 == 0); + + else if (mode == CCmode) + { + if (! ISA_HAS_8CC) + temp = (regno == FPSW_REGNUM); + else + temp = (ST_REG_P (regno) || GP_REG_P (regno) + || FP_REG_P (regno)); + } + + else if (GP_REG_P (regno)) + temp = ((regno & 1) == 0 || size <= UNITS_PER_WORD); + + else if (FP_REG_P (regno)) + temp = ((regno % FP_INC) == 0) + && (((class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT + || class == MODE_VECTOR_FLOAT) + && 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. + We can't allow sizes smaller than a word, + the FPU has no appropriate load/store + instructions for those. */ + || (class == MODE_INT + && size >= MIN_UNITS_PER_WORD + && size <= UNITS_PER_FPREG) + /* Allow TFmode for CCmode reloads. */ + || (ISA_HAS_8CC && mode == TFmode)); + + else if (ACC_REG_P (regno)) + temp = (INTEGRAL_MODE_P (mode) + && (size <= UNITS_PER_WORD + || (ACC_HI_REG_P (regno) + && size == 2 * UNITS_PER_WORD))); + + else if (ALL_COP_REG_P (regno)) + temp = (class == MODE_INT && size <= UNITS_PER_WORD); + else + temp = 0; + + mips_hard_regno_mode_ok[(int)mode][regno] = temp; + } + } + + /* Save GPR registers in word_mode sized hunks. word_mode hasn't been + initialized yet, so we can't use that here. */ + gpr_mode = TARGET_64BIT ? DImode : SImode; + + /* Provide default values for align_* for 64-bit targets. */ + if (TARGET_64BIT && !TARGET_MIPS16) + { + if (align_loops == 0) + align_loops = 8; + if (align_jumps == 0) + align_jumps = 8; + if (align_functions == 0) + align_functions = 8; + } + + /* Function to allocate machine-dependent function status. */ + init_machine_status = &mips_init_machine_status; + + 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_GENERAL] = true; + mips_lo_relocs[SYMBOL_GENERAL] = "%lo("; + } + } + else + { + if (TARGET_EXPLICIT_RELOCS || mips_split_addresses) + { + mips_split_p[SYMBOL_GENERAL] = true; + mips_hi_relocs[SYMBOL_GENERAL] = "%hi("; + mips_lo_relocs[SYMBOL_GENERAL] = "%lo("; + } + } + + if (TARGET_MIPS16) + { + /* The high part is provided by a pseudo copy of $gp. */ + mips_split_p[SYMBOL_SMALL_DATA] = true; + mips_lo_relocs[SYMBOL_SMALL_DATA] = "%gprel("; + } + + if (TARGET_EXPLICIT_RELOCS) + { + /* Small data constants are kept whole until after reload, + then lowered by mips_rewrite_small_data. */ + mips_lo_relocs[SYMBOL_SMALL_DATA] = "%gp_rel("; + + mips_split_p[SYMBOL_GOT_LOCAL] = true; + if (TARGET_NEWABI) + { + mips_lo_relocs[SYMBOL_GOTOFF_PAGE] = "%got_page("; + mips_lo_relocs[SYMBOL_GOT_LOCAL] = "%got_ofst("; + } + else + { + mips_lo_relocs[SYMBOL_GOTOFF_PAGE] = "%got("; + mips_lo_relocs[SYMBOL_GOT_LOCAL] = "%lo("; + } + + if (TARGET_XGOT) + { + /* The HIGH and LO_SUM are matched by special .md patterns. */ + mips_split_p[SYMBOL_GOT_GLOBAL] = true; + + mips_split_p[SYMBOL_GOTOFF_GLOBAL] = true; + mips_hi_relocs[SYMBOL_GOTOFF_GLOBAL] = "%got_hi("; + mips_lo_relocs[SYMBOL_GOTOFF_GLOBAL] = "%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_GLOBAL] = "%got_disp("; + else + mips_lo_relocs[SYMBOL_GOTOFF_GLOBAL] = "%got("; + mips_lo_relocs[SYMBOL_GOTOFF_CALL] = "%call16("; + } + } + + 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("; + } + + /* Thread-local relocation operators. */ + mips_lo_relocs[SYMBOL_TLSGD] = "%tlsgd("; + mips_lo_relocs[SYMBOL_TLSLDM] = "%tlsldm("; + mips_split_p[SYMBOL_DTPREL] = 1; + 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] = 1; + mips_hi_relocs[SYMBOL_TPREL] = "%tprel_hi("; + mips_lo_relocs[SYMBOL_TPREL] = "%tprel_lo("; + + /* We don't have a thread pointer access instruction on MIPS16, or + appropriate TLS relocations. */ + if (TARGET_MIPS16) + targetm.have_tls = false; + + /* 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; +} + +/* Implement CONDITIONAL_REGISTER_USAGE. */ + +void +mips_conditional_register_usage (void) +{ + if (!TARGET_DSP) + { + 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 hiding 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 caller 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; + } + /* fp20-23 are now caller saved. */ + 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 from fp21 to fp31 are now caller saved. */ + 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; + } +} + +/* Allocate a chunk of memory for per-function machine-dependent data. */ +static struct machine_function * +mips_init_machine_status (void) +{ + return ((struct machine_function *) + ggc_alloc_cleared (sizeof (struct machine_function))); +} + +/* On the mips16, we want to allocate $24 (T_REG) before other + registers for instructions for which it is possible. This helps + avoid shuffling registers around in order to set up for an xor, + encouraging the compiler to use a cmp instead. */ + +void +mips_order_regs_for_local_alloc (void) +{ + register 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; + } +} + + +/* 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) + { + HOST_WIDE_INT frame_size = (!cfun->machine->frame.initialized) + ? compute_frame_size (get_frame_size ()) + : cfun->machine->frame.total_size; + + /* MIPS16 frame is smaller */ + if (frame_pointer_needed && TARGET_MIPS16) + frame_size -= cfun->machine->frame.args_size; + + offset = offset - frame_size; + } + + /* 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 the PRINT_OPERAND macro. The MIPS-specific operand codes are: + + 'X' OP is CONST_INT, prints 32 bits in hexadecimal format = "0x%08x", + 'x' OP is CONST_INT, prints 16 bits in hexadecimal format = "0x%04x", + 'h' OP is HIGH, prints %hi(X), + 'd' output integer constant in decimal, + 'z' if the operand is 0, use $0 instead of normal operand. + 'D' print second part of double-word register or memory operand. + 'L' print low-order register of double-word register operand. + 'M' print high-order register of double-word register operand. + 'C' print part of opcode for a branch condition. + 'F' print part of opcode for a floating-point branch condition. + 'N' print part of opcode for a branch condition, inverted. + 'W' print part of opcode for a floating-point branch condition, inverted. + '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' for a CONST_INT X, print mips_fp_conditions[X] + 'Z' print the operand and a comma for ISA_HAS_8CC, otherwise print nothing + 'R' print the reloc associated with LO_SUM + 'q' print DSP accumulator registers + + The punctuation characters are: + + '(' Turn on .set noreorder + ')' Turn on .set reorder + '[' Turn on .set noat + ']' Turn on .set at + '<' Turn on .set nomacro + '>' Turn on .set macro + '{' Turn on .set volatile (not GAS) + '}' Turn on .set novolatile (not GAS) + '&' Turn on .set noreorder if filling delay slots + '*' Turn on both .set noreorder and .set nomacro if filling delay slots + '!' Turn on .set nomacro if filling delay slots + '#' Print nop if in a .set noreorder section. + '/' Like '#', but does nothing within a delayed branch sequence + '?' Print 'l' if we are to use a branch likely instead of normal branch. + '@' Print the name of the assembler temporary register (at or $1). + '.' Print the name of the register with a hard-wired zero (zero or $0). + '^' Print the name of the pic call-through register (t9 or $25). + '$' Print the name of the stack pointer register (sp or $29). + '+' Print the name of the gp register (usually gp or $28). + '~' Output a branch alignment to LABEL_ALIGN(NULL). */ + +void +print_operand (FILE *file, rtx op, int letter) +{ + register enum rtx_code code; + + if (PRINT_OPERAND_PUNCT_VALID_P (letter)) + { + switch (letter) + { + case '?': + if (mips_branch_likely) + putc ('l', file); + break; + + case '@': + fputs (reg_names [GP_REG_FIRST + 1], file); + break; + + case '^': + fputs (reg_names [PIC_FUNCTION_ADDR_REGNUM], file); + break; + + case '.': + fputs (reg_names [GP_REG_FIRST + 0], file); + break; + + case '$': + fputs (reg_names[STACK_POINTER_REGNUM], file); + break; + + case '+': + fputs (reg_names[PIC_OFFSET_TABLE_REGNUM], file); + break; + + case '&': + if (final_sequence != 0 && set_noreorder++ == 0) + fputs (".set\tnoreorder\n\t", file); + break; + + case '*': + if (final_sequence != 0) + { + if (set_noreorder++ == 0) + fputs (".set\tnoreorder\n\t", file); + + if (set_nomacro++ == 0) + fputs (".set\tnomacro\n\t", file); + } + break; + + case '!': + if (final_sequence != 0 && set_nomacro++ == 0) + fputs ("\n\t.set\tnomacro", file); + break; + + case '#': + if (set_noreorder != 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 (set_noreorder != 0 && final_sequence == 0) + fputs ("\n\tnop\n", file); + break; + + case '(': + if (set_noreorder++ == 0) + fputs (".set\tnoreorder\n\t", file); + break; + + case ')': + if (set_noreorder == 0) + error ("internal error: %%) found without a %%( in assembler pattern"); + + else if (--set_noreorder == 0) + fputs ("\n\t.set\treorder", file); + + break; + + case '[': + if (set_noat++ == 0) + fputs (".set\tnoat\n\t", file); + break; + + case ']': + if (set_noat == 0) + error ("internal error: %%] found without a %%[ in assembler pattern"); + else if (--set_noat == 0) + fputs ("\n\t.set\tat", file); + + break; + + case '<': + if (set_nomacro++ == 0) + fputs (".set\tnomacro\n\t", file); + break; + + case '>': + if (set_nomacro == 0) + error ("internal error: %%> found without a %%< in assembler pattern"); + else if (--set_nomacro == 0) + fputs ("\n\t.set\tmacro", file); + + break; + + case '{': + if (set_volatile++ == 0) + fputs ("#.set\tvolatile\n\t", file); + break; + + case '}': + if (set_volatile == 0) + error ("internal error: %%} found without a %%{ in assembler pattern"); + else if (--set_volatile == 0) + fputs ("\n\t#.set\tnovolatile", file); + + break; + + case '~': + { + if (align_labels_log > 0) + ASM_OUTPUT_ALIGN (file, align_labels_log); + } + break; + + default: + error ("PRINT_OPERAND: unknown punctuation '%c'", letter); + break; + } + + return; + } + + if (! op) + { + error ("PRINT_OPERAND null pointer"); + return; + } + + code = GET_CODE (op); + + if (letter == 'C') + switch (code) + { + case EQ: fputs ("eq", file); break; + case NE: fputs ("ne", file); break; + case GT: fputs ("gt", file); break; + case GE: fputs ("ge", file); break; + case LT: fputs ("lt", file); break; + case LE: fputs ("le", file); break; + case GTU: fputs ("gtu", file); break; + case GEU: fputs ("geu", file); break; + case LTU: fputs ("ltu", file); break; + case LEU: fputs ("leu", file); break; + default: + fatal_insn ("PRINT_OPERAND, invalid insn for %%C", op); + } + + else if (letter == 'N') + switch (code) + { + case EQ: fputs ("ne", file); break; + case NE: fputs ("eq", file); break; + case GT: fputs ("le", file); break; + case GE: fputs ("lt", file); break; + case LT: fputs ("ge", file); break; + case LE: fputs ("gt", file); break; + case GTU: fputs ("leu", file); break; + case GEU: fputs ("ltu", file); break; + case LTU: fputs ("geu", file); break; + case LEU: fputs ("gtu", file); break; + default: + fatal_insn ("PRINT_OPERAND, invalid insn for %%N", op); + } + + else if (letter == 'F') + switch (code) + { + case EQ: fputs ("c1f", file); break; + case NE: fputs ("c1t", file); break; + default: + fatal_insn ("PRINT_OPERAND, invalid insn for %%F", op); + } + + else if (letter == 'W') + switch (code) + { + case EQ: fputs ("c1t", file); break; + case NE: fputs ("c1f", file); break; + default: + fatal_insn ("PRINT_OPERAND, invalid insn for %%W", op); + } + + else if (letter == 'h') + { + if (GET_CODE (op) == HIGH) + op = XEXP (op, 0); + + print_operand_reloc (file, op, mips_hi_relocs); + } + + else if (letter == 'R') + print_operand_reloc (file, op, mips_lo_relocs); + + else if (letter == 'Y') + { + if (GET_CODE (op) == CONST_INT + && ((unsigned HOST_WIDE_INT) INTVAL (op) + < ARRAY_SIZE (mips_fp_conditions))) + fputs (mips_fp_conditions[INTVAL (op)], file); + else + output_operand_lossage ("invalid %%Y value"); + } + + else if (letter == 'Z') + { + if (ISA_HAS_8CC) + { + print_operand (file, op, 0); + fputc (',', file); + } + } + + else if (letter == 'q') + { + int regnum; + + if (code != REG) + fatal_insn ("PRINT_OPERAND, invalid insn for %%q", op); + + regnum = REGNO (op); + if (MD_REG_P (regnum)) + fprintf (file, "$ac0"); + else if (DSP_ACC_REG_P (regnum)) + fprintf (file, "$ac%c", reg_names[regnum][3]); + else + fatal_insn ("PRINT_OPERAND, invalid insn for %%q", op); + } + + else if (code == REG || code == SUBREG) + { + register int regnum; + + if (code == REG) + regnum = REGNO (op); + else + regnum = true_regnum (op); + + if ((letter == 'M' && ! WORDS_BIG_ENDIAN) + || (letter == 'L' && WORDS_BIG_ENDIAN) + || letter == 'D') + regnum++; + + fprintf (file, "%s", reg_names[regnum]); + } + + else if (code == MEM) + { + if (letter == 'D') + output_address (plus_constant (XEXP (op, 0), 4)); + else + output_address (XEXP (op, 0)); + } + + else if (letter == 'x' && GET_CODE (op) == CONST_INT) + fprintf (file, HOST_WIDE_INT_PRINT_HEX, 0xffff & INTVAL(op)); + + else if (letter == 'X' && GET_CODE(op) == CONST_INT) + fprintf (file, HOST_WIDE_INT_PRINT_HEX, INTVAL (op)); + + else if (letter == 'd' && GET_CODE(op) == CONST_INT) + fprintf (file, HOST_WIDE_INT_PRINT_DEC, (INTVAL(op))); + + else if (letter == 'z' && op == CONST0_RTX (GET_MODE (op))) + fputs (reg_names[GP_REG_FIRST], file); + + else if (letter == 'd' || letter == 'x' || letter == 'X') + output_operand_lossage ("invalid use of %%d, %%x, or %%X"); + + else if (letter == 'T' || letter == 't') + { + int truth = (code == NE) == (letter == 'T'); + fputc ("zfnt"[truth * 2 + (GET_MODE (op) == CCmode)], file); + } + + else if (CONST_GP_P (op)) + fputs (reg_names[GLOBAL_POINTER_REGNUM], file); + + else + output_addr_const (file, op); +} + + +/* Print symbolic operand OP, which is part of a HIGH or LO_SUM. + RELOCS is the array of relocations to use. */ + +static void +print_operand_reloc (FILE *file, rtx op, const char **relocs) +{ + enum mips_symbol_type symbol_type; + const char *p; + rtx base; + HOST_WIDE_INT offset; + + if (!mips_symbolic_constant_p (op, &symbol_type) || relocs[symbol_type] == 0) + fatal_insn ("PRINT_OPERAND, invalid operand for relocation", op); + + /* If OP uses an UNSPEC address, we want to print the inner symbol. */ + mips_split_const (op, &base, &offset); + if (UNSPEC_ADDRESS_P (base)) + op = plus_constant (UNSPEC_ADDRESS (base), offset); + + fputs (relocs[symbol_type], file); + output_addr_const (file, op); + for (p = relocs[symbol_type]; *p != 0; p++) + if (*p == '(') + fputc (')', file); +} + +/* Output address operand X to FILE. */ + +void +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: + print_operand (file, addr.offset, 0); + fprintf (file, "(%s)", reg_names[REGNO (addr.reg)]); + return; + + case ADDRESS_LO_SUM: + print_operand (file, addr.offset, 'R'); + fprintf (file, "(%s)", reg_names[REGNO (addr.reg)]); + return; + + case ADDRESS_CONST_INT: + output_addr_const (file, x); + fprintf (file, "(%s)", reg_names[0]); + return; + + case ADDRESS_SYMBOLIC: + output_addr_const (file, x); + return; + } + gcc_unreachable (); +} + +/* When using assembler macros, keep track of all of small-data externs + so that mips_file_end can emit the appropriate declarations for them. + + 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. */ + +int +mips_output_external (FILE *file ATTRIBUTE_UNUSED, tree decl, const char *name) +{ + register struct extern_list *p; + + if (!TARGET_EXPLICIT_RELOCS && mips_in_small_data_p (decl)) + { + p = (struct extern_list *) ggc_alloc (sizeof (struct extern_list)); + p->next = extern_head; + p->name = name; + p->size = int_size_in_bytes (TREE_TYPE (decl)); + extern_head = p; + } + + if (TARGET_IRIX && mips_abi == ABI_32 && TREE_CODE (decl) == FUNCTION_DECL) + { + p = (struct extern_list *) ggc_alloc (sizeof (struct extern_list)); + p->next = extern_head; + p->name = name; + p->size = -1; + extern_head = p; + } + + return 0; +} + +#if TARGET_IRIX +static void +irix_output_external_libcall (rtx fun) +{ + register struct extern_list *p; + + if (mips_abi == ABI_32) + { + p = (struct extern_list *) ggc_alloc (sizeof (struct extern_list)); + p->next = extern_head; + p->name = XSTR (fun, 0); + p->size = -1; + extern_head = p; + } +} +#endif + +/* Emit a new filename to a stream. If we are smuggling stabs, try to + put out a MIPS ECOFF file and a stab. */ + +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); + } +} + +/* Output an ASCII string, in a space-saving way. PREFIX is the string + that should be written before the opening quote, such as "\t.ascii\t" + for real string data or "\t# " for a comment. */ + +void +mips_output_ascii (FILE *stream, const char *string_param, size_t len, + const char *prefix) +{ + size_t i; + int cur_pos = 17; + register const unsigned char *string = + (const unsigned char *)string_param; + + fprintf (stream, "%s\"", prefix); + for (i = 0; i < len; i++) + { + register int c = 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%s\"", prefix); + } + } + fprintf (stream, "\"\n"); +} + +/* Implement TARGET_ASM_FILE_START. */ + +static void +mips_file_start (void) +{ + default_file_start (); + + if (!TARGET_IRIX) + { + /* Generate a special section to describe the ABI switches used to + produce the resultant binary. This used to be done by the assembler + setting bits in the ELF header's flags field, but we have run out of + bits. GDB needs this information in order to be able to correctly + debug these binaries. See the function mips_gdbarch_init() in + gdb/mips-tdep.c. This is unnecessary for the IRIX 5/6 ABIs and + causes unnecessary IRIX 6 ld warnings. */ + const char * abi_string = NULL; + + switch (mips_abi) + { + case ABI_32: abi_string = "abi32"; break; + case ABI_N32: abi_string = "abiN32"; break; + case ABI_64: abi_string = "abi64"; break; + case ABI_O64: abi_string = "abiO64"; break; + case ABI_EABI: abi_string = TARGET_64BIT ? "eabi64" : "eabi32"; break; + default: + gcc_unreachable (); + } + /* Note - we use fprintf directly rather than calling switch_to_section + because in this way we can avoid creating an allocated section. We + do not want this section to take up any space in the running + executable. */ + fprintf (asm_out_file, "\t.section .mdebug.%s\n", abi_string); + + /* 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", + TARGET_LONG64 ? 64 : 32); + + /* Restore the default section. */ + fprintf (asm_out_file, "\t.previous\n"); + } + + /* Generate the pseudo ops that System V.4 wants. */ + if (TARGET_ABICALLS) + fprintf (asm_out_file, "\t.abicalls\n"); + + if (TARGET_MIPS16) + fprintf (asm_out_file, "\t.set\tmips16\n"); + + if (flag_verbose_asm) + fprintf (asm_out_file, "\n%s -G value = %d, Arch = %s, ISA = %d\n", + ASM_COMMENT_START, + mips_section_threshold, mips_arch_info->name, mips_isa); +} + +#ifdef BSS_SECTION_ASM_OP +/* Implement ASM_OUTPUT_ALIGNED_BSS. This differs from the default only + in the use of sbss. */ + +void +mips_output_aligned_bss (FILE *stream, tree decl, const char *name, + unsigned HOST_WIDE_INT size, int align) +{ + extern tree last_assemble_variable_decl; + + if (mips_in_small_data_p (decl)) + switch_to_section (get_named_section (NULL, ".sbss", 0)); + else + switch_to_section (bss_section); + ASM_OUTPUT_ALIGN (stream, floor_log2 (align / BITS_PER_UNIT)); + last_assemble_variable_decl = decl; + ASM_DECLARE_OBJECT_NAME (stream, name, decl); + ASM_OUTPUT_SKIP (stream, size != 0 ? size : 1); +} +#endif + +/* Implement TARGET_ASM_FILE_END. When using assembler macros, emit + .externs for any small-data variables that turned out to be external. */ + +static void +mips_file_end (void) +{ + tree name_tree; + struct extern_list *p; + + if (extern_head) + { + fputs ("\n", asm_out_file); + + for (p = extern_head; p != 0; p = p->next) + { + name_tree = get_identifier (p->name); + + /* Positively ensure only one .extern for any given symbol. */ + if (!TREE_ASM_WRITTEN (name_tree) + && TREE_SYMBOL_REFERENCED (name_tree)) + { + TREE_ASM_WRITTEN (name_tree) = 1; + /* In IRIX 5 or IRIX 6 for the O32 ABI, we must output a + `.global name .text' directive for every used but + undefined function. If we don't, the linker may perform + an optimization (skipping over the insns that set $gp) + when it is unsafe. */ + if (TARGET_IRIX && mips_abi == ABI_32 && p->size == -1) + { + fputs ("\t.globl ", asm_out_file); + assemble_name (asm_out_file, p->name); + fputs (" .text\n", asm_out_file); + } + else + { + fputs ("\t.extern\t", asm_out_file); + assemble_name (asm_out_file, p->name); + fprintf (asm_out_file, ", %d\n", p->size); + } + } + } + } +} + +/* 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); +} + +/* 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); +} + +/* Emit either a label, .comm, or .lcomm directive. When using assembler + macros, mark the symbol as written so that mips_file_end 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; + } +} + +#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 true if X 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_type symbol_type; + + return (TARGET_EXPLICIT_RELOCS + && mips_symbolic_constant_p (x, &symbol_type) + && symbol_type == SYMBOL_SMALL_DATA); +} + + +/* A for_each_rtx callback for mips_small_data_pattern_p. */ + +static int +mips_small_data_pattern_1 (rtx *loc, void *data ATTRIBUTE_UNUSED) +{ + if (GET_CODE (*loc) == LO_SUM) + return -1; + + return mips_rewrite_small_data_p (*loc); +} + +/* 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, 0); +} + +/* A for_each_rtx callback, used by mips_rewrite_small_data. */ + +static int +mips_rewrite_small_data_1 (rtx *loc, void *data ATTRIBUTE_UNUSED) +{ + if (mips_rewrite_small_data_p (*loc)) + *loc = gen_rtx_LO_SUM (Pmode, pic_offset_table_rtx, *loc); + + if (GET_CODE (*loc) == LO_SUM) + return -1; + + return 0; +} + +/* If possible, rewrite OP so that it refers to small data using + explicit relocations. */ + +rtx +mips_rewrite_small_data (rtx op) +{ + op = copy_insn (op); + for_each_rtx (&op, mips_rewrite_small_data_1, 0); + return op; +} + +/* Return true if the current function has an insn that implicitly + refers to $gp. */ + +static bool +mips_function_has_gp_insn (void) +{ + /* Don't bother rechecking if we found one last time. */ + if (!cfun->machine->has_gp_insn_p) + { + rtx insn; + + push_topmost_sequence (); + for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) + if (INSN_P (insn) + && GET_CODE (PATTERN (insn)) != USE + && GET_CODE (PATTERN (insn)) != CLOBBER + && (get_attr_got (insn) != GOT_UNSET + || small_data_pattern (PATTERN (insn), VOIDmode))) + break; + pop_topmost_sequence (); + + cfun->machine->has_gp_insn_p = (insn != 0); + } + return cfun->machine->has_gp_insn_p; +} + + +/* Return the register that should be used as the global pointer + within this function. Return 0 if the function doesn't need + a global pointer. */ + +static unsigned int +mips_global_pointer (void) +{ + unsigned int regno; + + /* $gp is always available in non-abicalls code. */ + if (!TARGET_ABICALLS) + return GLOBAL_POINTER_REGNUM; + + /* We must always provide $gp when it is used implicitly. */ + if (!TARGET_EXPLICIT_RELOCS) + return GLOBAL_POINTER_REGNUM; + + /* FUNCTION_PROFILER includes a jal macro, so we need to give it + a valid gp. */ + if (current_function_profile) + return GLOBAL_POINTER_REGNUM; + + /* If the function has a nonlocal goto, $gp must hold the correct + global pointer for the target function. */ + if (current_function_has_nonlocal_goto) + return GLOBAL_POINTER_REGNUM; + + /* If the gp is never referenced, there's no need to initialize it. + Note that 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 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 (!regs_ever_live[GLOBAL_POINTER_REGNUM] + && !current_function_uses_const_pool + && !mips_function_has_gp_insn ()) + return 0; + + /* We need a global pointer, but perhaps we can use a call-clobbered + register instead of $gp. */ + if (TARGET_NEWABI && current_function_is_leaf) + for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++) + if (!regs_ever_live[regno] + && call_used_regs[regno] + && !fixed_regs[regno] + && regno != PIC_FUNCTION_ADDR_REGNUM) + return regno; + + return GLOBAL_POINTER_REGNUM; +} + + +/* Return true if the current function must save REGNO. */ + +static bool +mips_save_reg_p (unsigned int regno) +{ + /* We only need to save $gp for NewABI PIC. */ + if (regno == GLOBAL_POINTER_REGNUM) + return (TARGET_ABICALLS && TARGET_NEWABI + && cfun->machine->global_pointer == regno); + + /* Check call-saved registers. */ + if (regs_ever_live[regno] && !call_used_regs[regno]) + return true; + + /* We need to save the old frame pointer before setting up a new one. */ + if (regno == HARD_FRAME_POINTER_REGNUM && frame_pointer_needed) + return true; + + /* We need to save the incoming return address if it is ever clobbered + within the function. */ + if (regno == GP_REG_FIRST + 31 && regs_ever_live[regno]) + return true; + + if (TARGET_MIPS16) + { + tree return_type; + + return_type = DECL_RESULT (current_function_decl); + + /* $18 is a special case in mips16 code. It may be used to call + a function which returns a floating point value, but it is + marked in call_used_regs. */ + if (regno == GP_REG_FIRST + 18 && regs_ever_live[regno]) + return true; + + /* $31 is also a special case. It will be used to copy a return + value into the floating point registers if the return value is + floating point. */ + if (regno == GP_REG_FIRST + 31 + && mips16_hard_float + && !aggregate_value_p (return_type, current_function_decl) + && GET_MODE_CLASS (DECL_MODE (return_type)) == MODE_FLOAT + && GET_MODE_SIZE (DECL_MODE (return_type)) <= UNITS_PER_FPVALUE) + return true; + } + + return false; +} + + +/* Return the bytes needed to compute the frame pointer from the current + stack pointer. SIZE is the size (in bytes) of the local variables. + + MIPS stack frames look like: + + Before call After call + +-----------------------+ +-----------------------+ + high | | | | + mem. | | | | + | caller's temps. | | caller's temps. | + | | | | + +-----------------------+ +-----------------------+ + | | | | + | arguments on stack. | | arguments on stack. | + | | | | + +-----------------------+ +-----------------------+ + | 4 words to save | | 4 words to save | + | arguments passed | | arguments passed | + | in registers, even | | in registers, even | + SP->| if not passed. | VFP->| if not passed. | + +-----------------------+ +-----------------------+ + | | + | fp register save | + | | + +-----------------------+ + | | + | gp register save | + | | + +-----------------------+ + | | + | local variables | + | | + +-----------------------+ + | | + | alloca allocations | + | | + +-----------------------+ + | | + | GP save for V.4 abi | + | | + +-----------------------+ + | | + | arguments on stack | + | | + +-----------------------+ + | 4 words to save | + | arguments passed | + | in registers, even | + low SP->| if not passed. | + memory +-----------------------+ + +*/ + +HOST_WIDE_INT +compute_frame_size (HOST_WIDE_INT size) +{ + unsigned int regno; + HOST_WIDE_INT total_size; /* # bytes that the entire frame takes up */ + HOST_WIDE_INT var_size; /* # bytes that variables take up */ + HOST_WIDE_INT args_size; /* # bytes that outgoing arguments take up */ + HOST_WIDE_INT cprestore_size; /* # bytes that the cprestore slot takes up */ + HOST_WIDE_INT gp_reg_rounded; /* # bytes needed to store gp after rounding */ + HOST_WIDE_INT gp_reg_size; /* # bytes needed to store gp regs */ + HOST_WIDE_INT fp_reg_size; /* # bytes needed to store fp regs */ + unsigned int mask; /* mask of saved gp registers */ + unsigned int fmask; /* mask of saved fp registers */ + + cfun->machine->global_pointer = mips_global_pointer (); + + gp_reg_size = 0; + fp_reg_size = 0; + mask = 0; + fmask = 0; + var_size = MIPS_STACK_ALIGN (size); + args_size = current_function_outgoing_args_size; + cprestore_size = MIPS_STACK_ALIGN (STARTING_FRAME_OFFSET) - args_size; + + /* The space set aside by STARTING_FRAME_OFFSET isn't needed in leaf + functions. If the function has local variables, we're committed + to allocating it anyway. Otherwise reclaim it here. */ + if (var_size == 0 && current_function_is_leaf) + cprestore_size = args_size = 0; + + /* 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 (args_size == 0 && current_function_calls_alloca) + args_size = 4 * UNITS_PER_WORD; + + total_size = var_size + args_size + cprestore_size; + + /* Calculate space needed for gp registers. */ + for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++) + if (mips_save_reg_p (regno)) + { + gp_reg_size += GET_MODE_SIZE (gpr_mode); + mask |= 1 << (regno - GP_REG_FIRST); + } + + /* We need to restore these for the handler. */ + if (current_function_calls_eh_return) + { + unsigned int i; + for (i = 0; ; ++i) + { + regno = EH_RETURN_DATA_REGNO (i); + if (regno == INVALID_REGNUM) + break; + gp_reg_size += GET_MODE_SIZE (gpr_mode); + mask |= 1 << (regno - GP_REG_FIRST); + } + } + + /* This loop must iterate over the same space as its companion in + save_restore_insns. */ + for (regno = (FP_REG_LAST - FP_INC + 1); + regno >= FP_REG_FIRST; + regno -= FP_INC) + { + if (mips_save_reg_p (regno)) + { + fp_reg_size += FP_INC * UNITS_PER_FPREG; + fmask |= ((1 << FP_INC) - 1) << (regno - FP_REG_FIRST); + } + } + + gp_reg_rounded = MIPS_STACK_ALIGN (gp_reg_size); + total_size += gp_reg_rounded + MIPS_STACK_ALIGN (fp_reg_size); + + /* Add in the space required for saving incoming register arguments. */ + total_size += current_function_pretend_args_size; + total_size += MIPS_STACK_ALIGN (cfun->machine->varargs_size); + + /* Save other computed information. */ + cfun->machine->frame.total_size = total_size; + cfun->machine->frame.var_size = var_size; + cfun->machine->frame.args_size = args_size; + cfun->machine->frame.cprestore_size = cprestore_size; + cfun->machine->frame.gp_reg_size = gp_reg_size; + cfun->machine->frame.fp_reg_size = fp_reg_size; + cfun->machine->frame.mask = mask; + cfun->machine->frame.fmask = fmask; + cfun->machine->frame.initialized = reload_completed; + cfun->machine->frame.num_gp = gp_reg_size / UNITS_PER_WORD; + cfun->machine->frame.num_fp = fp_reg_size / (FP_INC * UNITS_PER_FPREG); + + if (mask) + { + HOST_WIDE_INT offset; + + offset = (args_size + cprestore_size + var_size + + gp_reg_size - GET_MODE_SIZE (gpr_mode)); + cfun->machine->frame.gp_sp_offset = offset; + cfun->machine->frame.gp_save_offset = offset - total_size; + } + else + { + cfun->machine->frame.gp_sp_offset = 0; + cfun->machine->frame.gp_save_offset = 0; + } + + if (fmask) + { + HOST_WIDE_INT offset; + + offset = (args_size + cprestore_size + var_size + + gp_reg_rounded + fp_reg_size + - FP_INC * UNITS_PER_FPREG); + cfun->machine->frame.fp_sp_offset = offset; + cfun->machine->frame.fp_save_offset = offset - total_size; + } + else + { + cfun->machine->frame.fp_sp_offset = 0; + cfun->machine->frame.fp_save_offset = 0; + } + + /* Ok, we're done. */ + return total_size; +} + +/* 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; + + compute_frame_size (get_frame_size ()); + + /* Set OFFSET to the offset from the stack pointer. */ + switch (from) + { + case FRAME_POINTER_REGNUM: + offset = 0; + break; + + case ARG_POINTER_REGNUM: + offset = (cfun->machine->frame.total_size + - current_function_pretend_args_size); + break; + + default: + gcc_unreachable (); + } + + if (TARGET_MIPS16 && to == HARD_FRAME_POINTER_REGNUM) + offset -= cfun->machine->frame.args_size; + + return offset; +} + +/* Implement RETURN_ADDR_RTX. Note, 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, GP_REG_FIRST + 31); +} + +/* 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 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_reg (HOST_WIDE_INT sp_offset, mips_save_restore_fn fn) +{ +#define BITSET_P(VALUE, BIT) (((VALUE) & (1L << (BIT))) != 0) + + 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 epilog 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)) + { + mips_save_restore_reg (gpr_mode, regno, offset, fn); + offset -= GET_MODE_SIZE (gpr_mode); + } + + /* This loop must iterate over the same space as its companion in + compute_frame_size. */ + offset = cfun->machine->frame.fp_sp_offset - sp_offset; + fpr_mode = (TARGET_SINGLE_FLOAT ? SFmode : DFmode); + for (regno = (FP_REG_LAST - FP_INC + 1); + regno >= FP_REG_FIRST; + regno -= FP_INC) + 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); + } +#undef BITSET_P +} + +/* 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 + && cfun->machine->global_pointer > 0 + && cfun->machine->global_pointer != GLOBAL_POINTER_REGNUM) + output_asm_insn (".cplocal %+", 0); +} + +/* 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_ABICALLS || cfun->machine->global_pointer == 0) + return LOADGP_NONE; + + if (TARGET_ABSOLUTE_ABICALLS) + return LOADGP_ABSOLUTE; + + return TARGET_NEWABI ? LOADGP_NEWABI : LOADGP_OLDABI; +} + +/* 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; + + 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 (gen_loadgp_noshared (mips_gnu_local_gp)); + 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 (gen_loadgp (offset, incoming_address)); + if (!TARGET_EXPLICIT_RELOCS) + emit_insn (gen_loadgp_blockage ()); + break; + + default: + break; + } +} + +/* Set up the stack and frame (if desired) for the function. */ + +static void +mips_output_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED) +{ + const char *fnname; + HOST_WIDE_INT tsize = cfun->machine->frame.total_size; + +#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 32 bit to handle + floating point arguments. The linker will arrange for any 32 bit + functions to call this stub, which will then jump to the 16 bit + function proper. */ + if (TARGET_MIPS16 && !TARGET_SOFT_FLOAT + && current_function_args_info.fp_code != 0) + build_mips16_function_stub (file); + + if (!FUNCTION_NAME_ALREADY_DECLARED) + { + /* 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); + + if (!flag_inhibit_size_directive) + { + fputs ("\t.ent\t", file); + assemble_name (file, fnname); + fputs ("\n", file); + } + + assemble_name (file, fnname); + fputs (":\n", file); + } + + /* Stop mips_file_end from treating this function as external. */ + if (TARGET_IRIX && mips_abi == ABI_32) + TREE_ASM_WRITTEN (DECL_NAME (cfun->decl)) = 1; + + if (!flag_inhibit_size_directive) + { + /* .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 && TARGET_MIPS16) + ? tsize - cfun->machine->frame.args_size + : tsize), + reg_names[GP_REG_FIRST + 31], + cfun->machine->frame.var_size, + cfun->machine->frame.num_gp, + cfun->machine->frame.num_fp, + cfun->machine->frame.args_size, + cfun->machine->frame.cprestore_size); + + /* .mask MASK, GPOFFSET; .fmask FPOFFSET */ + fprintf (file, "\t.mask\t0x%08x," HOST_WIDE_INT_PRINT_DEC "\n", + cfun->machine->frame.mask, + cfun->machine->frame.gp_save_offset); + fprintf (file, "\t.fmask\t0x%08x," HOST_WIDE_INT_PRINT_DEC "\n", + cfun->machine->frame.fmask, + cfun->machine->frame.fp_save_offset); + + /* Require: + OLD_SP == *FRAMEREG + FRAMESIZE => can find old_sp from nominated FP reg. + HIGHEST_GP_SAVED == *FRAMEREG + FRAMESIZE + GPOFFSET => can find saved regs. */ + } + + if (mips_current_loadgp_style () == LOADGP_OLDABI) + { + /* Handle the initialization of $gp for SVR4 PIC. */ + if (!cfun->machine->all_noreorder_p) + output_asm_insn ("%(.cpload\t%^%)", 0); + else + output_asm_insn ("%(.cpload\t%^\n\t%<", 0); + } + else if (cfun->machine->all_noreorder_p) + output_asm_insn ("%(%<", 0); + + /* 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 (); +} + +/* 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) == GP_REG_FIRST + 31 + && DWARF_FRAME_RETURN_COLUMN != GP_REG_FIRST + 31) + 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; +} + + +/* 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_64bit_move (mem, reg); + else + emit_move_insn (mem, reg); + + x1 = mips_frame_set (mips_subword (mem, 0), mips_subword (reg, 0)); + x2 = mips_frame_set (mips_subword (mem, 1), mips_subword (reg, 1)); + mips_set_frame_expr (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, x1, x2))); + } + else + { + if (TARGET_MIPS16 + && REGNO (reg) != GP_REG_FIRST + 31 + && !M16_REG_P (REGNO (reg))) + { + /* Save a non-mips16 register by moving it through a temporary. + We don't need to do this for $31 since there's a special + instruction for it. */ + emit_move_insn (MIPS_PROLOGUE_TEMP (GET_MODE (reg)), reg); + emit_move_insn (mem, MIPS_PROLOGUE_TEMP (GET_MODE (reg))); + } + else + emit_move_insn (mem, reg); + + mips_set_frame_expr (mips_frame_set (mem, reg)); + } +} + + +/* Expand the prologue into a bunch of separate insns. */ + +void +mips_expand_prologue (void) +{ + HOST_WIDE_INT size; + + if (cfun->machine->global_pointer > 0) + REGNO (pic_offset_table_rtx) = cfun->machine->global_pointer; + + size = compute_frame_size (get_frame_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 ((cfun->machine->frame.mask | cfun->machine->frame.fmask) != 0) + { + HOST_WIDE_INT step1; + + step1 = MIN (size, MIPS_MAX_FIRST_STACK_STEP); + RTX_FRAME_RELATED_P (emit_insn (gen_add3_insn (stack_pointer_rtx, + stack_pointer_rtx, + GEN_INT (-step1)))) = 1; + size -= step1; + mips_for_each_saved_reg (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 + { + emit_move_insn (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); + emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx); + emit_insn (gen_sub3_insn (hard_frame_pointer_rtx, + hard_frame_pointer_rtx, + MIPS_PROLOGUE_TEMP (Pmode))); + emit_move_insn (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. In mips16 code, + we point the frame pointer ahead of the outgoing argument area. + This should allow more variables & incoming arguments to be + accessed with unextended instructions. */ + if (frame_pointer_needed) + { + if (TARGET_MIPS16 && cfun->machine->frame.args_size != 0) + { + rtx offset = GEN_INT (cfun->machine->frame.args_size); + if (SMALL_OPERAND (cfun->machine->frame.args_size)) + RTX_FRAME_RELATED_P + (emit_insn (gen_add3_insn (hard_frame_pointer_rtx, + stack_pointer_rtx, + offset))) = 1; + else + { + emit_move_insn (MIPS_PROLOGUE_TEMP (Pmode), offset); + emit_move_insn (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, + cfun->machine->frame.args_size))); + } + } + else + RTX_FRAME_RELATED_P (emit_move_insn (hard_frame_pointer_rtx, + stack_pointer_rtx)) = 1; + } + + mips_emit_loadgp (); + + /* If generating o32/o64 abicalls, save $gp on the stack. */ + if (TARGET_ABICALLS && !TARGET_NEWABI && !current_function_is_leaf) + emit_insn (gen_cprestore (GEN_INT (current_function_outgoing_args_size))); + + /* If we are profiling, make sure no instructions are scheduled before + the call to mcount. */ + + if (current_function_profile) + emit_insn (gen_blockage ()); +} + +/* Do any necessary cleanup after a function to restore stack, frame, + and regs. */ + +#define RA_MASK BITMASK_HIGH /* 1 << 31 */ + +static void +mips_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED, + HOST_WIDE_INT size ATTRIBUTE_UNUSED) +{ + /* Reinstate the normal $gp. */ + REGNO (pic_offset_table_rtx) = GLOBAL_POINTER_REGNUM; + mips_output_cplocal (); + + if (cfun->machine->all_noreorder_p) + { + /* Avoid using %>%) since it adds excess whitespace. */ + output_asm_insn (".set\tmacro", 0); + output_asm_insn (".set\treorder", 0); + set_noreorder = set_nomacro = 0; + } + + if (!FUNCTION_NAME_ALREADY_DECLARED && !flag_inhibit_size_directive) + { + const char *fnname; + + /* 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); + fputs ("\t.end\t", file); + assemble_name (file, fnname); + fputs ("\n", file); + } +} + +/* 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) == GP_REG_FIRST + 31) + reg = gen_rtx_REG (GET_MODE (reg), 7); + + if (TARGET_MIPS16 && !M16_REG_P (REGNO (reg))) + { + /* Can't restore directly; move through a temporary. */ + emit_move_insn (MIPS_EPILOGUE_TEMP (GET_MODE (reg)), mem); + emit_move_insn (reg, MIPS_EPILOGUE_TEMP (GET_MODE (reg))); + } + else + emit_move_insn (reg, mem); +} + + +/* Expand the epilogue into a bunch of separate insns. SIBCALL_P is true + if this epilogue precedes a sibling call, false if it is for a normal + "epilogue" pattern. */ + +void +mips_expand_epilogue (int sibcall_p) +{ + HOST_WIDE_INT step1, step2; + rtx base, target; + + if (!sibcall_p && mips_can_use_return_insn ()) + { + emit_jump_insn (gen_return ()); + return; + } + + /* 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. */ + step1 = cfun->machine->frame.total_size; + step2 = 0; + + /* Work out which register holds the frame address. Account for the + frame pointer offset used by mips16 code. */ + if (!frame_pointer_needed) + base = stack_pointer_rtx; + else + { + base = hard_frame_pointer_rtx; + if (TARGET_MIPS16) + step1 -= cfun->machine->frame.args_size; + } + + /* If we need to restore registers, deallocate as much stack as + possible in the second step without going out of range. */ + if ((cfun->machine->frame.mask | cfun->machine->frame.fmask) != 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)) + { + emit_move_insn (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) + emit_move_insn (stack_pointer_rtx, target); + + /* If we're using addressing macros for n32/n64 abicalls, $gp is + implicitly used by all SYMBOL_REFs. We must emit a blockage + insn before restoring it. */ + if (TARGET_ABICALLS && TARGET_NEWABI && !TARGET_EXPLICIT_RELOCS) + emit_insn (gen_blockage ()); + + /* Restore the registers. */ + mips_for_each_saved_reg (cfun->machine->frame.total_size - step2, + mips_restore_reg); + + /* 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 (current_function_calls_eh_return) + { + if (TARGET_MIPS16) + { + emit_move_insn (MIPS_EPILOGUE_TEMP (Pmode), stack_pointer_rtx); + emit_insn (gen_add3_insn (MIPS_EPILOGUE_TEMP (Pmode), + MIPS_EPILOGUE_TEMP (Pmode), + EH_RETURN_STACKADJ_RTX)); + emit_move_insn (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) + { + /* The mips16 loads the return address into $7, not $31. */ + if (TARGET_MIPS16 && (cfun->machine->frame.mask & RA_MASK) != 0) + emit_jump_insn (gen_return_internal (gen_rtx_REG (Pmode, + GP_REG_FIRST + 7))); + else + emit_jump_insn (gen_return_internal (gen_rtx_REG (Pmode, + GP_REG_FIRST + 31))); + } +} + +/* 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. */ + +int +mips_can_use_return_insn (void) +{ + tree return_type; + + if (! reload_completed) + return 0; + + if (regs_ever_live[31] || current_function_profile) + return 0; + + return_type = DECL_RESULT (current_function_decl); + + /* In mips16 mode, a function which returns a floating point value + needs to arrange to copy the return value into the floating point + registers. */ + if (TARGET_MIPS16 + && mips16_hard_float + && ! aggregate_value_p (return_type, current_function_decl) + && GET_MODE_CLASS (DECL_MODE (return_type)) == MODE_FLOAT + && GET_MODE_SIZE (DECL_MODE (return_type)) <= UNITS_PER_FPVALUE) + return 0; + + if (cfun->machine->frame.initialized) + return cfun->machine->frame.total_size == 0; + + return compute_frame_size (get_frame_size ()) == 0; +} + +/* 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, temp1, temp2, insn, fnaddr; + + /* Pretend to be a post-reload pass while generating rtl. */ + no_new_pseudos = 1; + reload_completed = 1; + reset_block_changes (); + + /* Pick a global pointer for -mabicalls. Use $15 rather than $28 + for TARGET_NEWABI since the latter is a call-saved register. */ + if (TARGET_ABICALLS) + cfun->machine->global_pointer + = REGNO (pic_offset_table_rtx) + = TARGET_NEWABI ? 15 : GLOBAL_POINTER_REGNUM; + + /* 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 = gen_rtx_REG (Pmode, GP_ARG_FIRST + 1); + else + this = gen_rtx_REG (Pmode, GP_ARG_FIRST); + + /* Add DELTA to THIS. */ + if (delta != 0) + { + rtx offset = GEN_INT (delta); + if (!SMALL_OPERAND (delta)) + { + emit_move_insn (temp1, offset); + offset = temp1; + } + emit_insn (gen_add3_insn (this, this, offset)); + } + + /* If needed, add *(*THIS + VCALL_OFFSET) to THIS. */ + if (vcall_offset != 0) + { + rtx addr; + + /* Set TEMP1 to *THIS. */ + emit_move_insn (temp1, gen_rtx_MEM (Pmode, this)); + + /* Set ADDR to a legitimate address for *THIS + VCALL_OFFSET. */ + addr = mips_add_offset (temp2, temp1, vcall_offset); + + /* Load the offset and add it to THIS. */ + emit_move_insn (temp1, gen_rtx_MEM (Pmode, addr)); + emit_insn (gen_add3_insn (this, this, temp1)); + } + + /* Jump to the target function. Use a sibcall if direct jumps are + allowed, otherwise load the address into a register first. */ + fnaddr = XEXP (DECL_RTL (function), 0); + if (TARGET_MIPS16 || TARGET_ABICALLS || TARGET_LONG_CALLS) + { + /* 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 o32 and o64, + but it's still wrong for n32 and n64; 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. */ + if (TARGET_ABICALLS && !mips_dangerous_for_la25_p (fnaddr)) + temp1 = gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM); + mips_load_call_address (temp1, fnaddr, true); + + if (TARGET_ABICALLS && REGNO (temp1) != PIC_FUNCTION_ADDR_REGNUM) + emit_move_insn (gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM), temp1); + emit_jump_insn (gen_indirect_jump (temp1)); + } + else + { + insn = emit_call_insn (gen_sibcall_internal (fnaddr, const0_rtx)); + SIBLING_CALL_P (insn) = 1; + } + + /* Run just enough of rest_of_compilation. This sequence was + "borrowed" from alpha.c. */ + insn = get_insns (); + insn_locators_initialize (); + split_all_insns_noflow (); + if (TARGET_MIPS16) + 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; + no_new_pseudos = 0; +} + +/* Returns nonzero if X contains a SYMBOL_REF. */ + +static int +symbolic_expression_p (rtx x) +{ + if (GET_CODE (x) == SYMBOL_REF) + return 1; + + if (GET_CODE (x) == CONST) + return symbolic_expression_p (XEXP (x, 0)); + + if (UNARY_P (x)) + return symbolic_expression_p (XEXP (x, 0)); + + if (ARITHMETIC_P (x)) + return (symbolic_expression_p (XEXP (x, 0)) + || symbolic_expression_p (XEXP (x, 1))); + + return 0; +} + +/* Choose the section to use for the constant rtx expression X that has + mode MODE. */ + +static section * +mips_select_rtx_section (enum machine_mode mode, rtx x, + unsigned HOST_WIDE_INT align) +{ + if (TARGET_MIPS16) + { + /* In mips16 mode, the constant table always goes in the same section + as the function, so that constants can be loaded using PC relative + addressing. */ + return function_section (current_function_decl); + } + else if (TARGET_EMBEDDED_DATA) + { + /* For embedded applications, always put constants in read-only data, + in order to reduce RAM usage. */ + return mergeable_constant_section (mode, align, 0); + } + else + { + /* For hosted applications, always put constants in small data if + possible, as this gives the best performance. */ + /* ??? Consider using mergeable small data sections. */ + + if (GET_MODE_SIZE (mode) <= (unsigned) mips_section_threshold + && mips_section_threshold > 0) + return get_named_section (NULL, ".sdata", 0); + else if (flag_pic && symbolic_expression_p (x)) + return get_named_section (NULL, ".data.rel.ro", 3); + else + return mergeable_constant_section (mode, align, 0); + } +} + +/* Implement TARGET_ASM_FUNCTION_RODATA_SECTION. + + The complication here is that, with the combination TARGET_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_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. This function controls whether + locally-defined objects go in a small data section. It also controls + the setting of the SYMBOL_REF_SMALL_P flag, which in turn helps + mips_classify_symbol decide when to use %gp_rel(...)($gp) accesses. */ + +static bool +mips_in_small_data_p (tree decl) +{ + 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. See related + -G handling in override_options. */ + if (TARGET_ABICALLS) + 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 (TARGET_EXPLICIT_RELOCS || !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; + } + + size = int_size_in_bytes (TREE_TYPE (decl)); + return (size > 0 && size <= mips_section_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 (rtx symbol) +{ + switch (mips_classify_symbol (symbol)) + { + case SYMBOL_CONSTANT_POOL: + case SYMBOL_SMALL_DATA: + return false; + + default: + return true; + } +} + +/* 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 (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 = TREE_CHAIN (field)) + { + if (TREE_CODE (field) != FIELD_DECL) + continue; + + if (TREE_CODE (TREE_TYPE (field)) != REAL_TYPE) + 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 (tree valtype) +{ + tree fields[2]; + + return (TARGET_NEWABI + && TARGET_BIG_ENDIAN + && AGGREGATE_TYPE_P (valtype) + && mips_fpr_return_fields (valtype, fields) == 0); +} + + +/* 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 : FP_INC); + 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 (tree valtype, tree func ATTRIBUTE_UNUSED, + enum machine_mode mode) +{ + if (valtype) + { + tree fields[2]; + int unsignedp; + + mode = TYPE_MODE (valtype); + unsignedp = TYPE_UNSIGNED (valtype); + + /* Since we define TARGET_PROMOTE_FUNCTION_RETURN that returns + true, we must promote the mode just as PROMOTE_MODE does. */ + mode = promote_mode (valtype, mode, &unsignedp, 1); + + /* Handle structures whose fields are returned in $f0/$f2. */ + switch (mips_fpr_return_fields (valtype, fields)) + { + case 1: + return gen_rtx_REG (mode, FP_RETURN); + + 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 ((GET_MODE_CLASS (mode) == MODE_FLOAT + || GET_MODE_CLASS (mode) == MODE_VECTOR_FLOAT) + && GET_MODE_SIZE (mode) <= UNITS_PER_HWFPVALUE) + return gen_rtx_REG (mode, FP_RETURN); + + /* Handle long doubles for n32 & n64. */ + if (mode == TFmode) + return mips_return_fpr_pair (mode, + DImode, 0, + DImode, GET_MODE_SIZE (mode) / 2); + + if (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT + && GET_MODE_SIZE (mode) <= UNITS_PER_HWFPVALUE * 2) + return mips_return_fpr_pair (mode, + GET_MODE_INNER (mode), 0, + GET_MODE_INNER (mode), + GET_MODE_SIZE (mode) / 2); + + return gen_rtx_REG (mode, GP_RETURN); +} + +/* 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, tree type, + bool named ATTRIBUTE_UNUSED) +{ + if (mips_abi == ABI_EABI) + { + int size; + + /* ??? How should SCmode be handled? */ + if (mode == DImode || mode == DFmode) + 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); + } +} + +static bool +mips_callee_copies (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED, + enum machine_mode mode ATTRIBUTE_UNUSED, + tree type ATTRIBUTE_UNUSED, bool named) +{ + return mips_abi == ABI_EABI && named; +} + +/* Return true if registers of class CLASS cannot change from mode FROM + to mode TO. */ + +bool +mips_cannot_change_mode_class (enum machine_mode from, + enum machine_mode to, enum reg_class class) +{ + if (MIN (GET_MODE_SIZE (from), GET_MODE_SIZE (to)) <= UNITS_PER_WORD + && MAX (GET_MODE_SIZE (from), GET_MODE_SIZE (to)) > UNITS_PER_WORD) + { + if (TARGET_BIG_ENDIAN) + { + /* 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. */ + if (FP_INC > 1 && reg_classes_intersect_p (FP_REGS, class)) + return true; + } + else + { + /* LO_REGNO == HI_REGNO + 1, so if a multi-word value is stored + in LO and HI, the high word always comes first. We therefore + can't allow values stored in HI to change between single-word + and multi-word modes. + This rule applies to both the original HI/LO pair and the new + DSP accumulators. */ + if (reg_classes_intersect_p (ACC_REGS, class)) + return true; + } + } + /* 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 64-bit float registers to change from SImode to + to a wider mode. */ + if (TARGET_FLOAT64 + && from == SImode + && GET_MODE_SIZE (to) >= UNITS_PER_WORD + && reg_classes_intersect_p (FP_REGS, class)) + return true; + return false; +} + +/* 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) +{ + HOST_WIDE_INT offset; + + if (TARGET_EXPLICIT_RELOCS) + return false; + + mips_split_const (x, &x, &offset); + return global_got_operand (x, VOIDmode); +} + +/* Implement PREFERRED_RELOAD_CLASS. */ + +enum reg_class +mips_preferred_reload_class (rtx x, enum reg_class class) +{ + if (mips_dangerous_for_la25_p (x) && reg_class_subset_p (LEA_REGS, class)) + return LEA_REGS; + + if (TARGET_HARD_FLOAT + && FLOAT_MODE_P (GET_MODE (x)) + && reg_class_subset_p (FP_REGS, class)) + return FP_REGS; + + if (reg_class_subset_p (GR_REGS, class)) + class = GR_REGS; + + if (TARGET_MIPS16 && reg_class_subset_p (M16_REGS, class)) + class = M16_REGS; + + return class; +} + +/* This function returns the register class required for a secondary + register when copying between one of the registers in CLASS, and X, + using MODE. If IN_P is nonzero, the copy is going from X to the + register, otherwise the register is the source. A return value of + NO_REGS means that no secondary register is required. */ + +enum reg_class +mips_secondary_reload_class (enum reg_class class, + enum machine_mode mode, rtx x, int in_p) +{ + enum reg_class gr_regs = TARGET_MIPS16 ? M16_REGS : GR_REGS; + int regno = -1; + int gp_reg_p; + + if (REG_P (x)|| GET_CODE (x) == SUBREG) + regno = true_regnum (x); + + gp_reg_p = TARGET_MIPS16 ? M16_REG_P (regno) : GP_REG_P (regno); + + if (mips_dangerous_for_la25_p (x)) + { + gr_regs = LEA_REGS; + if (TEST_HARD_REG_BIT (reg_class_contents[(int) class], 25)) + return gr_regs; + } + + /* Copying from HI or LO to anywhere other than a general register + requires a general register. + This rule applies to both the original HI/LO pair and the new + DSP accumulators. */ + if (reg_class_subset_p (class, ACC_REGS)) + { + if (TARGET_MIPS16 && in_p) + { + /* We can't really copy to HI or LO at all in mips16 mode. */ + return M16_REGS; + } + return gp_reg_p ? NO_REGS : gr_regs; + } + if (ACC_REG_P (regno)) + { + if (TARGET_MIPS16 && ! in_p) + { + /* We can't really copy to HI or LO at all in mips16 mode. */ + return M16_REGS; + } + return class == 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 (class == ST_REGS) + { + if (in_p) + return FP_REGS; + return gp_reg_p ? NO_REGS : gr_regs; + } + if (ST_REG_P (regno)) + { + if (! in_p) + return FP_REGS; + return class == gr_regs ? NO_REGS : gr_regs; + } + + if (class == FP_REGS) + { + if (MEM_P (x)) + { + /* In this case we can use lwc1, swc1, ldc1 or sdc1. */ + return NO_REGS; + } + else if (CONSTANT_P (x) && GET_MODE_CLASS (mode) == MODE_FLOAT) + { + /* We can use the l.s and l.d macros to load floating-point + constants. ??? For l.s, we could probably get better + code by returning GR_REGS here. */ + return NO_REGS; + } + else if (gp_reg_p || x == CONST0_RTX (mode)) + { + /* In this case we can use mtc1, mfc1, dmtc1 or dmfc1. */ + return NO_REGS; + } + else if (FP_REG_P (regno)) + { + /* In this case we can use mov.s or mov.d. */ + return NO_REGS; + } + else + { + /* Otherwise, we need to reload through an integer register. */ + return gr_regs; + } + } + + /* In mips16 mode, going between memory and anything but M16_REGS + requires an M16_REG. */ + if (TARGET_MIPS16) + { + if (class != M16_REGS && class != M16_NA_REGS) + { + if (gp_reg_p) + return NO_REGS; + return M16_REGS; + } + if (! gp_reg_p) + { + if (class == M16_REGS || class == M16_NA_REGS) + return NO_REGS; + return M16_REGS; + } + } + + return NO_REGS; +} + +/* Implement CLASS_MAX_NREGS. + + Usually all registers are word-sized. The only supported exception + is -mgp64 -msingle-float, which has 64-bit words but 32-bit float + registers. A word-based calculation is correct even in that case, + since -msingle-float disallows multi-FPR values. + + The FP status registers are an exception to this rule. They are always + 4 bytes wide as they only hold condition code modes, and CCmode is always + considered to be 4 bytes wide. */ + +int +mips_class_max_nregs (enum reg_class class ATTRIBUTE_UNUSED, + enum machine_mode mode) +{ + if (class == ST_REGS) + return (GET_MODE_SIZE (mode) + 3) / 4; + else + return (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD; +} + +static bool +mips_valid_pointer_mode (enum machine_mode mode) +{ + return (mode == SImode || (TARGET_64BIT && mode == DImode)); +} + +/* Target hook for 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: + return TARGET_DSP; + + default: + return false; + } +} + +/* If we can access small data directly (using gp-relative relocation + operators) return the small data pointer, otherwise return null. + + For each mips16 function which refers to GP relative symbols, we + use a pseudo register, initialized at the start of the function, to + hold the $gp value. */ + +static rtx +mips16_gp_pseudo_reg (void) +{ + if (cfun->machine->mips16_gp_pseudo_rtx == NULL_RTX) + { + rtx unspec; + rtx insn, scan; + + cfun->machine->mips16_gp_pseudo_rtx = gen_reg_rtx (Pmode); + + /* We want to initialize this to a value which gcc will believe + is constant. */ + start_sequence (); + unspec = gen_rtx_UNSPEC (VOIDmode, gen_rtvec (1, const0_rtx), UNSPEC_GP); + emit_move_insn (cfun->machine->mips16_gp_pseudo_rtx, + gen_rtx_CONST (Pmode, unspec)); + insn = get_insns (); + end_sequence (); + + push_topmost_sequence (); + /* We need to emit the initialization after the FUNCTION_BEG + note, so that it will be integrated. */ + for (scan = get_insns (); scan != NULL_RTX; scan = NEXT_INSN (scan)) + if (NOTE_P (scan) + && NOTE_LINE_NUMBER (scan) == NOTE_INSN_FUNCTION_BEG) + break; + if (scan == NULL_RTX) + scan = get_insns (); + insn = emit_insn_after (insn, scan); + pop_topmost_sequence (); + } + + return cfun->machine->mips16_gp_pseudo_rtx; +} + +/* Write out code to move floating point arguments in or out of + general registers. Output the instructions to FILE. FP_CODE is + the code describing which arguments are present (see the comment at + the definition of CUMULATIVE_ARGS in mips.h). FROM_FP_P is nonzero if + we are copying from the floating point registers. */ + +static void +mips16_fp_args (FILE *file, int fp_code, int from_fp_p) +{ + const char *s; + int gparg, fparg; + unsigned int f; + + /* This code only works for the original 32 bit ABI and the O64 ABI. */ + gcc_assert (TARGET_OLDABI); + + if (from_fp_p) + s = "mfc1"; + else + s = "mtc1"; + gparg = GP_ARG_FIRST; + fparg = FP_ARG_FIRST; + for (f = (unsigned int) fp_code; f != 0; f >>= 2) + { + if ((f & 3) == 1) + { + if ((fparg & 1) != 0) + ++fparg; + fprintf (file, "\t%s\t%s,%s\n", s, + reg_names[gparg], reg_names[fparg]); + } + else if ((f & 3) == 2) + { + if (TARGET_64BIT) + fprintf (file, "\td%s\t%s,%s\n", s, + reg_names[gparg], reg_names[fparg]); + else + { + if ((fparg & 1) != 0) + ++fparg; + if (TARGET_BIG_ENDIAN) + fprintf (file, "\t%s\t%s,%s\n\t%s\t%s,%s\n", s, + reg_names[gparg], reg_names[fparg + 1], s, + reg_names[gparg + 1], reg_names[fparg]); + else + fprintf (file, "\t%s\t%s,%s\n\t%s\t%s,%s\n", s, + reg_names[gparg], reg_names[fparg], s, + reg_names[gparg + 1], reg_names[fparg + 1]); + ++gparg; + ++fparg; + } + } + else + gcc_unreachable (); + + ++gparg; + ++fparg; + } +} + +/* Build a mips16 function stub. This is used for functions which + take arguments in the floating point registers. It is 32 bit code + that moves the floating point args into the general registers, and + then jumps to the 16 bit code. */ + +static void +build_mips16_function_stub (FILE *file) +{ + const char *fnname; + char *secname, *stubname; + tree stubid, stubdecl; + int need_comma; + unsigned int f; + + fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0); + secname = (char *) alloca (strlen (fnname) + 20); + sprintf (secname, ".mips16.fn.%s", fnname); + stubname = (char *) alloca (strlen (fnname) + 20); + sprintf (stubname, "__fn_stub_%s", fnname); + stubid = get_identifier (stubname); + stubdecl = build_decl (FUNCTION_DECL, stubid, + build_function_type (void_type_node, NULL_TREE)); + DECL_SECTION_NAME (stubdecl) = build_string (strlen (secname), secname); + + fprintf (file, "\t# Stub function for %s (", current_function_name ()); + need_comma = 0; + for (f = (unsigned int) current_function_args_info.fp_code; f != 0; f >>= 2) + { + fprintf (file, "%s%s", + need_comma ? ", " : "", + (f & 3) == 1 ? "float" : "double"); + need_comma = 1; + } + fprintf (file, ")\n"); + + fprintf (file, "\t.set\tnomips16\n"); + switch_to_section (function_section (stubdecl)); + ASM_OUTPUT_ALIGN (file, floor_log2 (FUNCTION_BOUNDARY / BITS_PER_UNIT)); + + /* ??? If FUNCTION_NAME_ALREADY_DECLARED is defined, then we are + within a .ent, and we cannot emit another .ent. */ + if (!FUNCTION_NAME_ALREADY_DECLARED) + { + fputs ("\t.ent\t", file); + assemble_name (file, stubname); + fputs ("\n", file); + } + + assemble_name (file, stubname); + fputs (":\n", file); + + /* We don't want the assembler to insert any nops here. */ + fprintf (file, "\t.set\tnoreorder\n"); + + mips16_fp_args (file, current_function_args_info.fp_code, 1); + + fprintf (asm_out_file, "\t.set\tnoat\n"); + fprintf (asm_out_file, "\tla\t%s,", reg_names[GP_REG_FIRST + 1]); + assemble_name (file, fnname); + fprintf (file, "\n"); + fprintf (asm_out_file, "\tjr\t%s\n", reg_names[GP_REG_FIRST + 1]); + fprintf (asm_out_file, "\t.set\tat\n"); + + /* Unfortunately, we can't fill the jump delay slot. We can't fill + with one of the mfc1 instructions, because the result is not + available for one instruction, so if the very first instruction + in the function refers to the register, it will see the wrong + value. */ + fprintf (file, "\tnop\n"); + + fprintf (file, "\t.set\treorder\n"); + + if (!FUNCTION_NAME_ALREADY_DECLARED) + { + fputs ("\t.end\t", file); + assemble_name (file, stubname); + fputs ("\n", file); + } + + fprintf (file, "\t.set\tmips16\n"); + + switch_to_section (function_section (current_function_decl)); +} + +/* We keep a list of functions for which we have already built stubs + in build_mips16_call_stub. */ + +struct mips16_stub +{ + struct mips16_stub *next; + char *name; + int fpret; +}; + +static struct mips16_stub *mips16_stubs; + +/* Build a call stub for a mips16 call. A stub is needed if we are + passing any floating point values which should go into the floating + point registers. If we are, and the call turns out to be to a 32 + bit function, the stub will be used to move the values into the + floating point registers before calling the 32 bit function. The + linker will magically adjust the function call to either the 16 bit + function or the 32 bit stub, depending upon where the function call + is actually defined. + + Similarly, we need a stub if the return value might come back in a + floating point register. + + RETVAL is the location of the return value, or null if this is + a call rather than a call_value. FN is the address of the + function and ARG_SIZE is the size of the arguments. FP_CODE + is the code built by function_arg. This function returns a nonzero + value if it builds the call instruction itself. */ + +int +build_mips16_call_stub (rtx retval, rtx fn, rtx arg_size, int fp_code) +{ + int fpret; + const char *fnname; + char *secname, *stubname; + struct mips16_stub *l; + tree stubid, stubdecl; + int need_comma; + unsigned int f; + + /* 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 || ! mips16_hard_float) + return 0; + + /* Figure out whether the value might come back in a floating point + register. */ + fpret = (retval != 0 + && GET_MODE_CLASS (GET_MODE (retval)) == MODE_FLOAT + && GET_MODE_SIZE (GET_MODE (retval)) <= UNITS_PER_FPVALUE); + + /* 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 && ! fpret) + return 0; + + /* We don't need to do anything if this is a call to a special + mips16 support function. */ + if (GET_CODE (fn) == SYMBOL_REF + && strncmp (XSTR (fn, 0), "__mips16_", 9) == 0) + return 0; + + /* This code will only work for o32 and o64 abis. The other ABI's + require more sophisticated support. */ + gcc_assert (TARGET_OLDABI); + + /* We can only handle SFmode and DFmode floating point return + values. */ + if (fpret) + gcc_assert (GET_MODE (retval) == SFmode || GET_MODE (retval) == DFmode); + + /* If we're calling via a function pointer, then we must always call + via a stub. There are magic stubs provided in libgcc.a for each + of the required cases. Each of them expects the function address + to arrive in register $2. */ + + if (GET_CODE (fn) != SYMBOL_REF) + { + char buf[30]; + tree id; + rtx stub_fn, insn; + + /* ??? If this code is modified to support other ABI's, we need + to handle PARALLEL return values here. */ + + sprintf (buf, "__mips16_call_stub_%s%d", + (fpret + ? (GET_MODE (retval) == SFmode ? "sf_" : "df_") + : ""), + fp_code); + id = get_identifier (buf); + stub_fn = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (id)); + + emit_move_insn (gen_rtx_REG (Pmode, 2), fn); + + if (retval == NULL_RTX) + insn = gen_call_internal (stub_fn, arg_size); + else + insn = gen_call_value_internal (retval, stub_fn, arg_size); + insn = emit_call_insn (insn); + + /* Put the register usage information on the CALL. */ + CALL_INSN_FUNCTION_USAGE (insn) = + gen_rtx_EXPR_LIST (VOIDmode, + gen_rtx_USE (VOIDmode, gen_rtx_REG (Pmode, 2)), + CALL_INSN_FUNCTION_USAGE (insn)); + + /* 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 regs_ever_live[$18] will be true if the + call is not eliminated, and we can check that in the prologue + code. */ + if (fpret) + CALL_INSN_FUNCTION_USAGE (insn) = + gen_rtx_EXPR_LIST (VOIDmode, + gen_rtx_USE (VOIDmode, + gen_rtx_REG (word_mode, 18)), + CALL_INSN_FUNCTION_USAGE (insn)); + + /* Return 1 to tell the caller that we've generated the call + insn. */ + return 1; + } + + /* 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 = XSTR (fn, 0); + for (l = mips16_stubs; l != NULL; l = l->next) + if (strcmp (l->name, fnname) == 0) + break; + + if (l == NULL) + { + /* Build a special purpose stub. When the linker sees a + function call in mips16 code, it will check where the target + is defined. If the target is a 32 bit call, the linker will + search for the section defined here. It can tell which + symbol this section is associated with by looking at the + relocation information (the name is unreliable, since this + might be a static function). If such a section is found, the + linker will redirect the call to the start of the magic + section. + + If the function does not return a floating point value, the + special stub section is named + .mips16.call.FNNAME + + If the function does return a floating point value, the stub + section is named + .mips16.call.fp.FNNAME + */ + + secname = (char *) alloca (strlen (fnname) + 40); + sprintf (secname, ".mips16.call.%s%s", + fpret ? "fp." : "", + fnname); + stubname = (char *) alloca (strlen (fnname) + 20); + sprintf (stubname, "__call_stub_%s%s", + fpret ? "fp_" : "", + fnname); + stubid = get_identifier (stubname); + stubdecl = build_decl (FUNCTION_DECL, stubid, + build_function_type (void_type_node, NULL_TREE)); + DECL_SECTION_NAME (stubdecl) = build_string (strlen (secname), secname); + + fprintf (asm_out_file, "\t# Stub function to call %s%s (", + (fpret + ? (GET_MODE (retval) == SFmode ? "float " : "double ") + : ""), + fnname); + need_comma = 0; + for (f = (unsigned int) fp_code; f != 0; f >>= 2) + { + fprintf (asm_out_file, "%s%s", + need_comma ? ", " : "", + (f & 3) == 1 ? "float" : "double"); + need_comma = 1; + } + fprintf (asm_out_file, ")\n"); + + fprintf (asm_out_file, "\t.set\tnomips16\n"); + assemble_start_function (stubdecl, stubname); + + if (!FUNCTION_NAME_ALREADY_DECLARED) + { + fputs ("\t.ent\t", asm_out_file); + assemble_name (asm_out_file, stubname); + fputs ("\n", asm_out_file); + + assemble_name (asm_out_file, stubname); + fputs (":\n", asm_out_file); + } + + /* We build the stub code by hand. That's the only way we can + do it, since we can't generate 32 bit code during a 16 bit + compilation. */ + + /* We don't want the assembler to insert any nops here. */ + fprintf (asm_out_file, "\t.set\tnoreorder\n"); + + mips16_fp_args (asm_out_file, fp_code, 0); + + if (! fpret) + { + fprintf (asm_out_file, "\t.set\tnoat\n"); + fprintf (asm_out_file, "\tla\t%s,%s\n", reg_names[GP_REG_FIRST + 1], + fnname); + fprintf (asm_out_file, "\tjr\t%s\n", reg_names[GP_REG_FIRST + 1]); + fprintf (asm_out_file, "\t.set\tat\n"); + /* Unfortunately, we can't fill the jump delay slot. We + can't fill with one of the mtc1 instructions, because the + result is not available for one instruction, so if the + very first instruction in the function refers to the + register, it will see the wrong value. */ + fprintf (asm_out_file, "\tnop\n"); + } + else + { + fprintf (asm_out_file, "\tmove\t%s,%s\n", + reg_names[GP_REG_FIRST + 18], reg_names[GP_REG_FIRST + 31]); + fprintf (asm_out_file, "\tjal\t%s\n", fnname); + /* As above, we can't fill the delay slot. */ + fprintf (asm_out_file, "\tnop\n"); + if (GET_MODE (retval) == SFmode) + fprintf (asm_out_file, "\tmfc1\t%s,%s\n", + reg_names[GP_REG_FIRST + 2], reg_names[FP_REG_FIRST + 0]); + else + { + if (TARGET_BIG_ENDIAN) + { + fprintf (asm_out_file, "\tmfc1\t%s,%s\n", + reg_names[GP_REG_FIRST + 2], + reg_names[FP_REG_FIRST + 1]); + fprintf (asm_out_file, "\tmfc1\t%s,%s\n", + reg_names[GP_REG_FIRST + 3], + reg_names[FP_REG_FIRST + 0]); + } + else + { + fprintf (asm_out_file, "\tmfc1\t%s,%s\n", + reg_names[GP_REG_FIRST + 2], + reg_names[FP_REG_FIRST + 0]); + fprintf (asm_out_file, "\tmfc1\t%s,%s\n", + reg_names[GP_REG_FIRST + 3], + reg_names[FP_REG_FIRST + 1]); + } + } + fprintf (asm_out_file, "\tj\t%s\n", reg_names[GP_REG_FIRST + 18]); + /* As above, we can't fill the delay slot. */ + fprintf (asm_out_file, "\tnop\n"); + } + + fprintf (asm_out_file, "\t.set\treorder\n"); + +#ifdef ASM_DECLARE_FUNCTION_SIZE + ASM_DECLARE_FUNCTION_SIZE (asm_out_file, stubname, stubdecl); +#endif + + if (!FUNCTION_NAME_ALREADY_DECLARED) + { + fputs ("\t.end\t", asm_out_file); + assemble_name (asm_out_file, stubname); + fputs ("\n", asm_out_file); + } + + fprintf (asm_out_file, "\t.set\tmips16\n"); + + /* Record this stub. */ + l = (struct mips16_stub *) xmalloc (sizeof *l); + l->name = xstrdup (fnname); + l->fpret = fpret; + 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 (fpret && ! l->fpret) + error ("cannot handle inconsistent calls to %qs", fnname); + + /* 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 (l->fpret) + { + rtx insn; + + if (retval == NULL_RTX) + insn = gen_call_internal (fn, arg_size); + else + insn = gen_call_value_internal (retval, fn, arg_size); + insn = emit_call_insn (insn); + + CALL_INSN_FUNCTION_USAGE (insn) = + gen_rtx_EXPR_LIST (VOIDmode, + gen_rtx_USE (VOIDmode, gen_rtx_REG (word_mode, 18)), + CALL_INSN_FUNCTION_USAGE (insn)); + + /* Return 1 to tell the caller that we've generated the call + insn. */ + return 1; + } + + /* Return 0 to let the caller generate the call insn. */ + return 0; +} + +/* 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 +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 pc-relative lw, addiu and daddiu instructions, the base PC value + is the address of the instruction with the lowest two bits clear. + The base PC value for ld has the lowest three bits clear. Assume + the worst case here. */ + 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 = (struct mips16_constant *) xmalloc (sizeof *c); + 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 +dump_constants_1 (enum machine_mode mode, rtx value, rtx insn) +{ + switch (GET_MODE_CLASS (mode)) + { + case MODE_INT: + { + rtx size = GEN_INT (GET_MODE_SIZE (mode)); + return emit_insn_after (gen_consttable_int (value, size), insn); + } + + case MODE_FLOAT: + return emit_insn_after (gen_consttable_float (value), insn); + + case MODE_VECTOR_FLOAT: + case MODE_VECTOR_INT: + { + int i; + for (i = 0; i < CONST_VECTOR_NUNITS (value); i++) + insn = dump_constants_1 (GET_MODE_INNER (mode), + CONST_VECTOR_ELT (value, i), insn); + return insn; + } + + default: + gcc_unreachable (); + } +} + + +/* Dump out the constants in CONSTANTS after INSN. */ + +static void +dump_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 = dump_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); +} + +/* Rewrite *X so that constant pool references refer to the constant's + label instead. DATA points to the constant pool structure. */ + +static int +mips16_rewrite_pool_refs (rtx *x, void *data) +{ + struct mips16_constant_pool *pool = data; + if (GET_CODE (*x) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (*x)) + *x = gen_rtx_LABEL_REF (Pmode, add_constant (pool, + get_pool_constant (*x), + get_pool_mode (*x))); + return 0; +} + +/* Build MIPS16 constant pools. */ + +static void +mips16_lay_out_constants (void) +{ + struct mips16_constant_pool pool; + rtx insn, barrier; + + barrier = 0; + memset (&pool, 0, sizeof (pool)); + for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) + { + /* Rewrite constant pool references in INSN. */ + if (INSN_P (insn)) + for_each_rtx (&PATTERN (insn), mips16_rewrite_pool_refs, &pool); + + 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) + { + dump_constants (pool.first, barrier); + pool.first = NULL; + barrier = 0; + } + else if (BARRIER_P (insn)) + barrier = insn; + } + } + dump_constants (pool.first, get_last_insn ()); +} + +/* 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 i; + + for (i = 0; i < HARD_REGNO_NREGS (REGNO (reg), GET_MODE (reg)); i++) + if (state->last_set[REGNO (reg) + i].insn != 0) + { + unsigned int t; + + t = state->last_set[REGNO (reg) + i].time; + t += insn_latency (state->last_set[REGNO (reg) + i].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 (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, rtx pat ATTRIBUTE_UNUSED, void *data) +{ + struct mips_sim *state; + unsigned int i; + + state = data; + if (REG_P (x)) + for (i = 0; i < HARD_REGNO_NREGS (REGNO (x), GET_MODE (x)); i++) + { + state->last_set[REGNO (x) + i].insn = mips_sim_insn; + state->last_set[REGNO (x) + i].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 instruction 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 (); +} + +/* Subroutine of mips_reorg. 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; + + if (!INSN_P (insn)) + return; + + 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 != 0); + *delayed_reg = SET_DEST (set); + break; + } +} + + +/* Go through the instruction stream and insert nops where necessary. + See if the whole function can then be put into .set noreorder & + .set nomacro. */ + +static void +mips_avoid_hazards (void) +{ + rtx insn, last_insn, lo_reg, delayed_reg; + int hilo_delay, i; + + /* 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; + + /* Profiled functions can't be all noreorder because the profiler + support uses assembler macros. */ + if (current_function_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; + + last_insn = 0; + hilo_delay = 2; + delayed_reg = 0; + lo_reg = gen_rtx_REG (SImode, LO_REGNUM); + + for (insn = get_insns (); insn != 0; insn = NEXT_INSN (insn)) + if (INSN_P (insn)) + { + if (GET_CODE (PATTERN (insn)) == SEQUENCE) + for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++) + mips_avoid_hazard (last_insn, XVECEXP (PATTERN (insn), 0, i), + &hilo_delay, &delayed_reg, lo_reg); + else + mips_avoid_hazard (last_insn, insn, &hilo_delay, + &delayed_reg, lo_reg); + + last_insn = insn; + } +} + + +/* Implement TARGET_MACHINE_DEPENDENT_REORG. */ + +static void +mips_reorg (void) +{ + if (TARGET_MIPS16) + mips16_lay_out_constants (); + else if (TARGET_EXPLICIT_RELOCS) + { + if (mips_flag_delayed_branch) + dbr_schedule (get_insns ()); + mips_avoid_hazards (); + if (TUNE_MIPS4130 && TARGET_VR4130_ALIGN) + vr4130_align_insns (); + } +} + +/* This function does three things: + + - Register the special divsi3 and modsi3 functions if -mfix-vr4120. + - Register the mips16 hardware floating point stubs. + - Register the gofast functions if selected using --enable-gofast. */ + +#include "config/gofast.h" + +static void +mips_init_libfuncs (void) +{ + if (TARGET_FIX_VR4120) + { + set_optab_libfunc (sdiv_optab, SImode, "__vr4120_divsi3"); + set_optab_libfunc (smod_optab, SImode, "__vr4120_modsi3"); + } + + if (TARGET_MIPS16 && mips16_hard_float) + { + 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_conv_libfunc (sfix_optab, SImode, SFmode, "__mips16_fix_truncsfsi"); + set_conv_libfunc (sfloat_optab, SFmode, SImode, "__mips16_floatsisf"); + + 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_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"); + } + } + else + gofast_maybe_init_libfuncs (); +} + +/* Return a number assessing the cost of moving a register in class + FROM to class TO. The classes are expressed using the enumeration + values such as `GENERAL_REGS'. A value of 2 is the default; other + values are interpreted relative to that. + + It is not required that the cost always equal 2 when FROM is the + same as TO; on some machines it is expensive to move between + registers if they are not general registers. + + If reload sees an insn consisting of a single `set' between two + hard registers, and if `REGISTER_MOVE_COST' applied to their + classes returns a value of 2, reload does not check to ensure that + the constraints of the insn are met. Setting a cost of other than + 2 will allow reload to verify that the constraints are met. You + should do this if the `movM' pattern's constraints do not allow + such copying. + + ??? We make the cost of moving from HI/LO into general + registers the same as for one of moving general registers to + HI/LO for TARGET_MIPS16 in order to prevent allocating a + pseudo to HI/LO. This might hurt optimizations though, it + isn't clear if it is wise. And it might not work in all cases. We + could solve the DImode LO reg problem by using a multiply, just + like reload_{in,out}si. We could solve the SImode/HImode HI reg + problem by using divide instructions. divu puts the remainder in + the HI reg, so doing a divide by -1 will move the value in the HI + reg for all values except -1. We could handle that case by using a + signed divide, e.g. -1 / 2 (or maybe 1 / -2?). We'd have to emit + a compare/branch to test the input value to see which instruction + we need to use. This gets pretty messy, but it is feasible. */ + +int +mips_register_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED, + enum reg_class to, enum reg_class from) +{ + if (from == M16_REGS && GR_REG_CLASS_P (to)) + return 2; + else if (from == M16_NA_REGS && GR_REG_CLASS_P (to)) + return 2; + else if (GR_REG_CLASS_P (from)) + { + if (to == M16_REGS) + return 2; + else if (to == M16_NA_REGS) + return 2; + else if (GR_REG_CLASS_P (to)) + { + if (TARGET_MIPS16) + return 4; + else + return 2; + } + else if (to == FP_REGS) + return 4; + else if (reg_class_subset_p (to, ACC_REGS)) + { + if (TARGET_MIPS16) + return 12; + else + return 6; + } + else if (COP_REG_CLASS_P (to)) + { + return 5; + } + } + else if (from == FP_REGS) + { + if (GR_REG_CLASS_P (to)) + return 4; + else if (to == FP_REGS) + return 2; + else if (to == ST_REGS) + return 8; + } + else if (reg_class_subset_p (from, ACC_REGS)) + { + if (GR_REG_CLASS_P (to)) + { + if (TARGET_MIPS16) + return 12; + else + return 6; + } + } + else if (from == ST_REGS && GR_REG_CLASS_P (to)) + return 4; + else if (COP_REG_CLASS_P (from)) + { + return 5; + } + + /* Fall through. + ??? What cases are these? Shouldn't we return 2 here? */ + + return 12; +} + +/* 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) +{ + /* 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; + } + + /* All MIPS16 instructions are a measly two bytes. */ + if (TARGET_MIPS16) + length /= 2; + + return length; +} + + +/* Return an asm sequence to start a noat block and load the address + of a label into $1. */ + +const char * +mips_output_load_label (void) +{ + if (TARGET_EXPLICIT_RELOCS) + switch (mips_abi) + { + case ABI_N32: + return "%[lw\t%@,%%got_page(%0)(%+)\n\taddiu\t%@,%@,%%got_ofst(%0)"; + + case ABI_64: + return "%[ld\t%@,%%got_page(%0)(%+)\n\tdaddiu\t%@,%@,%%got_ofst(%0)"; + + default: + if (ISA_HAS_LOAD_DELAY) + return "%[lw\t%@,%%got(%0)(%+)%#\n\taddiu\t%@,%@,%%lo(%0)"; + return "%[lw\t%@,%%got(%0)(%+)\n\taddiu\t%@,%@,%%lo(%0)"; + } + else + { + if (Pmode == DImode) + return "%[dla\t%@,%0"; + else + return "%[la\t%@,%0"; + } +} + +/* Return the assembly code for INSN, which has the operands given by + OPERANDS, and which branches to OPERANDS[1] if some condition is true. + BRANCH_IF_TRUE is the asm template that should be used if OPERANDS[1] + 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; + + 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[1]; + + /* Generate the reversed branch to NOT_TAKEN. */ + operands[1] = 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 (length <= 16) + output_asm_insn ("j\t%0%/", &taken); + else + { + output_asm_insn (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[1] + if some ordered condition is true. The condition is given by + OPERANDS[0] if !INVERTED_P, otherwise it is the inverse of + OPERANDS[0]. 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[1] when the condition is true. + Make BRANCH[0] branch on the inverse condition. */ + switch (GET_CODE (operands[0])) + { + /* 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,%.,%1"); + branch[inverted_p] = MIPS_BRANCH ("beq", "%2,%.,%1"); + 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", "%.,%.,%1"); + branch[inverted_p] = MIPS_BRANCH ("bne", "%.,%.,%1"); + break; + + default: + branch[!inverted_p] = MIPS_BRANCH ("b%C0z", "%2,%1"); + branch[inverted_p] = MIPS_BRANCH ("b%N0z", "%2,%1"); + break; + } + return mips_output_conditional_branch (insn, operands, branch[1], branch[0]); +} + +/* Used to output 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) + { + 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 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) +{ + const struct mips_cpu_info *p; + 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, "the cpu name 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. Look first at the -mgp options, if given, otherwise base + the choice on MASK_64BIT in TARGET_DEFAULT. */ + 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 0; + + for (p = mips_cpu_info_table; p->name != 0; p++) + if (mips_matching_cpu_name_p (p->name, cpu_string)) + return p; + + return 0; +} + + +/* 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) +{ + const struct mips_cpu_info *p; + + for (p = mips_cpu_info_table; p->name != 0; p++) + if (p->isa == isa) + return p; + + return 0; +} + +/* Implement HARD_REGNO_NREGS. The size of FP registers is controlled + by UNITS_PER_FPREG. The size of FP status registers is always 4, because + they only hold condition code modes, and CCmode is always considered to + be 4 bytes wide. All other registers are word sized. */ + +unsigned int +mips_hard_regno_nregs (int regno, enum machine_mode mode) +{ + if (ST_REG_P (regno)) + return ((GET_MODE_SIZE (mode) + 3) / 4); + else if (! FP_REG_P (regno)) + return ((GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD); + else + return ((GET_MODE_SIZE (mode) + UNITS_PER_FPREG - 1) / UNITS_PER_FPREG); +} + +/* Implement TARGET_RETURN_IN_MEMORY. Under the old (i.e., 32 and O64 ABIs) + all BLKmode objects are returned in memory. Under the new (N32 and + 64-bit MIPS 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 (tree type, tree fndecl ATTRIBUTE_UNUSED) +{ + if (TARGET_OLDABI) + return (TYPE_MODE (type) == BLKmode); + else + return ((int_size_in_bytes (type) > (2 * UNITS_PER_WORD)) + || (int_size_in_bytes (type) == -1)); +} + +static bool +mips_strict_argument_naming (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED) +{ + return !TARGET_OLDABI; +} + +/* Return true if INSN is a multiply-add or multiply-subtract + instruction and PREV assigns to the accumulator operand. */ + +bool +mips_linked_madd_p (rtx prev, rtx insn) +{ + rtx x; + + x = single_set (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), prev)) + return true; + + if (GET_CODE (x) == MINUS + && GET_CODE (XEXP (x, 1)) == MULT + && reg_set_p (XEXP (x, 0), prev)) + return true; + + return false; +} + +/* 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, rtx pat ATTRIBUTE_UNUSED, void *data) +{ + rtx *insn_ptr = 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) +{ + rtx 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 (dep = INSN_DEPEND (insn1); dep != 0; dep = XEXP (dep, 1)) + if (REG_NOTE_KIND (dep) == REG_DEP_ANTI + && INSN_PRIORITY (XEXP (dep, 0)) > INSN_PRIORITY (insn2) + && recog_memoized (XEXP (dep, 0)) >= 0 + && get_attr_vr4130_class (XEXP (dep, 0)) == 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 = vr4130_true_reg_dependence_p (insn1); + bool dep2 = vr4130_true_reg_dependence_p (insn2); + if (dep1 != dep2) + return dep1; + + /* 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); +} + +/* 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; +} + +/* Implement TARGET_SCHED_REORDER. */ + +static int +mips_sched_reorder (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED, + rtx *ready, int *nreadyp, int cycle) +{ + if (!reload_completed && TUNE_MACC_CHAINS) + { + if (cycle == 0) + mips_macc_chains_last_hilo = 0; + if (*nreadyp > 0) + mips_macc_chains_reorder (ready, *nreadyp); + } + if (reload_completed && TUNE_MIPS4130 && !TARGET_VR4130_ALIGN) + { + if (cycle == 0) + vr4130_last_insn = 0; + if (*nreadyp > 1) + vr4130_reorder (ready, *nreadyp); + } + return mips_issue_rate (); +} + +/* Implement TARGET_SCHED_VARIABLE_ISSUE. */ + +static int +mips_variable_issue (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED, + rtx insn, int more) +{ + switch (GET_CODE (PATTERN (insn))) + { + case USE: + case CLOBBER: + /* Don't count USEs and CLOBBERs against the issue rate. */ + break; + + default: + more--; + if (!reload_completed && TUNE_MACC_CHAINS) + mips_macc_chains_record (insn); + vr4130_last_insn = insn; + break; + } + return more; +} + +/* Implement TARGET_SCHED_ADJUST_COST. We assume that anti and output + dependencies have no cost. */ + +static int +mips_adjust_cost (rtx insn ATTRIBUTE_UNUSED, rtx link, + rtx dep ATTRIBUTE_UNUSED, int 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_R4130: + case PROCESSOR_R5400: + case PROCESSOR_R5500: + case PROCESSOR_R7000: + case PROCESSOR_R9000: + 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; + + default: + return 1; + } +} + +/* Implements 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; + + return 0; +} + +/* Implements a store data bypass check. We need this because the cprestore + pattern is type store, but defined using an UNSPEC. This UNSPEC causes the + default routine to abort. We just return false for that case. */ +/* ??? Should try to give a better result here than assuming false. */ + +int +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); +} + +/* 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); +} + +/* MIPS builtin function support. */ + +struct 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 builtin 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; + + /* The target flags required for this function. */ + int target_flags; +}; + +/* Define a MIPS_BUILTIN_DIRECT function for instruction CODE_FOR_mips_<INSN>. + FUNCTION_TYPE and TARGET_FLAGS are builtin_description fields. */ +#define DIRECT_BUILTIN(INSN, FUNCTION_TYPE, TARGET_FLAGS) \ + { CODE_FOR_mips_ ## INSN, 0, "__builtin_mips_" #INSN, \ + MIPS_BUILTIN_DIRECT, FUNCTION_TYPE, TARGET_FLAGS } + +/* Define __builtin_mips_<INSN>_<COND>_{s,d}, both of which require + TARGET_FLAGS. */ +#define CMP_SCALAR_BUILTINS(INSN, COND, TARGET_FLAGS) \ + { CODE_FOR_mips_ ## INSN ## _cond_s, MIPS_FP_COND_ ## COND, \ + "__builtin_mips_" #INSN "_" #COND "_s", \ + MIPS_BUILTIN_CMP_SINGLE, MIPS_INT_FTYPE_SF_SF, TARGET_FLAGS }, \ + { CODE_FOR_mips_ ## INSN ## _cond_d, MIPS_FP_COND_ ## COND, \ + "__builtin_mips_" #INSN "_" #COND "_d", \ + MIPS_BUILTIN_CMP_SINGLE, MIPS_INT_FTYPE_DF_DF, TARGET_FLAGS } + +/* Define __builtin_mips_{any,all,upper,lower}_<INSN>_<COND>_ps. + The lower and upper forms require TARGET_FLAGS while the any and all + forms require MASK_MIPS3D. */ +#define CMP_PS_BUILTINS(INSN, COND, TARGET_FLAGS) \ + { CODE_FOR_mips_ ## INSN ## _cond_ps, MIPS_FP_COND_ ## COND, \ + "__builtin_mips_any_" #INSN "_" #COND "_ps", \ + MIPS_BUILTIN_CMP_ANY, MIPS_INT_FTYPE_V2SF_V2SF, MASK_MIPS3D }, \ + { CODE_FOR_mips_ ## INSN ## _cond_ps, MIPS_FP_COND_ ## COND, \ + "__builtin_mips_all_" #INSN "_" #COND "_ps", \ + MIPS_BUILTIN_CMP_ALL, MIPS_INT_FTYPE_V2SF_V2SF, MASK_MIPS3D }, \ + { CODE_FOR_mips_ ## INSN ## _cond_ps, MIPS_FP_COND_ ## COND, \ + "__builtin_mips_lower_" #INSN "_" #COND "_ps", \ + MIPS_BUILTIN_CMP_LOWER, MIPS_INT_FTYPE_V2SF_V2SF, TARGET_FLAGS }, \ + { CODE_FOR_mips_ ## INSN ## _cond_ps, MIPS_FP_COND_ ## COND, \ + "__builtin_mips_upper_" #INSN "_" #COND "_ps", \ + MIPS_BUILTIN_CMP_UPPER, MIPS_INT_FTYPE_V2SF_V2SF, TARGET_FLAGS } + +/* Define __builtin_mips_{any,all}_<INSN>_<COND>_4s. The functions + require MASK_MIPS3D. */ +#define CMP_4S_BUILTINS(INSN, COND) \ + { CODE_FOR_mips_ ## INSN ## _cond_4s, MIPS_FP_COND_ ## COND, \ + "__builtin_mips_any_" #INSN "_" #COND "_4s", \ + MIPS_BUILTIN_CMP_ANY, MIPS_INT_FTYPE_V2SF_V2SF_V2SF_V2SF, \ + MASK_MIPS3D }, \ + { CODE_FOR_mips_ ## INSN ## _cond_4s, MIPS_FP_COND_ ## COND, \ + "__builtin_mips_all_" #INSN "_" #COND "_4s", \ + MIPS_BUILTIN_CMP_ALL, MIPS_INT_FTYPE_V2SF_V2SF_V2SF_V2SF, \ + MASK_MIPS3D } + +/* Define __builtin_mips_mov{t,f}_<INSN>_<COND>_ps. The comparison + instruction requires TARGET_FLAGS. */ +#define MOVTF_BUILTINS(INSN, COND, TARGET_FLAGS) \ + { CODE_FOR_mips_ ## INSN ## _cond_ps, MIPS_FP_COND_ ## COND, \ + "__builtin_mips_movt_" #INSN "_" #COND "_ps", \ + MIPS_BUILTIN_MOVT, MIPS_V2SF_FTYPE_V2SF_V2SF_V2SF_V2SF, \ + TARGET_FLAGS }, \ + { CODE_FOR_mips_ ## INSN ## _cond_ps, MIPS_FP_COND_ ## COND, \ + "__builtin_mips_movf_" #INSN "_" #COND "_ps", \ + MIPS_BUILTIN_MOVF, MIPS_V2SF_FTYPE_V2SF_V2SF_V2SF_V2SF, \ + TARGET_FLAGS } + +/* Define all the builtins related to c.cond.fmt condition COND. */ +#define CMP_BUILTINS(COND) \ + MOVTF_BUILTINS (c, COND, MASK_PAIRED_SINGLE_FLOAT), \ + MOVTF_BUILTINS (cabs, COND, MASK_MIPS3D), \ + CMP_SCALAR_BUILTINS (cabs, COND, MASK_MIPS3D), \ + CMP_PS_BUILTINS (c, COND, MASK_PAIRED_SINGLE_FLOAT), \ + CMP_PS_BUILTINS (cabs, COND, MASK_MIPS3D), \ + CMP_4S_BUILTINS (c, COND), \ + CMP_4S_BUILTINS (cabs, COND) + +static const struct builtin_description mips_bdesc[] = +{ + DIRECT_BUILTIN (pll_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, MASK_PAIRED_SINGLE_FLOAT), + DIRECT_BUILTIN (pul_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, MASK_PAIRED_SINGLE_FLOAT), + DIRECT_BUILTIN (plu_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, MASK_PAIRED_SINGLE_FLOAT), + DIRECT_BUILTIN (puu_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, MASK_PAIRED_SINGLE_FLOAT), + DIRECT_BUILTIN (cvt_ps_s, MIPS_V2SF_FTYPE_SF_SF, MASK_PAIRED_SINGLE_FLOAT), + DIRECT_BUILTIN (cvt_s_pl, MIPS_SF_FTYPE_V2SF, MASK_PAIRED_SINGLE_FLOAT), + DIRECT_BUILTIN (cvt_s_pu, MIPS_SF_FTYPE_V2SF, MASK_PAIRED_SINGLE_FLOAT), + DIRECT_BUILTIN (abs_ps, MIPS_V2SF_FTYPE_V2SF, MASK_PAIRED_SINGLE_FLOAT), + + DIRECT_BUILTIN (alnv_ps, MIPS_V2SF_FTYPE_V2SF_V2SF_INT, + MASK_PAIRED_SINGLE_FLOAT), + DIRECT_BUILTIN (addr_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, MASK_MIPS3D), + DIRECT_BUILTIN (mulr_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, MASK_MIPS3D), + DIRECT_BUILTIN (cvt_pw_ps, MIPS_V2SF_FTYPE_V2SF, MASK_MIPS3D), + DIRECT_BUILTIN (cvt_ps_pw, MIPS_V2SF_FTYPE_V2SF, MASK_MIPS3D), + + DIRECT_BUILTIN (recip1_s, MIPS_SF_FTYPE_SF, MASK_MIPS3D), + DIRECT_BUILTIN (recip1_d, MIPS_DF_FTYPE_DF, MASK_MIPS3D), + DIRECT_BUILTIN (recip1_ps, MIPS_V2SF_FTYPE_V2SF, MASK_MIPS3D), + DIRECT_BUILTIN (recip2_s, MIPS_SF_FTYPE_SF_SF, MASK_MIPS3D), + DIRECT_BUILTIN (recip2_d, MIPS_DF_FTYPE_DF_DF, MASK_MIPS3D), + DIRECT_BUILTIN (recip2_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, MASK_MIPS3D), + + DIRECT_BUILTIN (rsqrt1_s, MIPS_SF_FTYPE_SF, MASK_MIPS3D), + DIRECT_BUILTIN (rsqrt1_d, MIPS_DF_FTYPE_DF, MASK_MIPS3D), + DIRECT_BUILTIN (rsqrt1_ps, MIPS_V2SF_FTYPE_V2SF, MASK_MIPS3D), + DIRECT_BUILTIN (rsqrt2_s, MIPS_SF_FTYPE_SF_SF, MASK_MIPS3D), + DIRECT_BUILTIN (rsqrt2_d, MIPS_DF_FTYPE_DF_DF, MASK_MIPS3D), + DIRECT_BUILTIN (rsqrt2_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, MASK_MIPS3D), + + MIPS_FP_CONDITIONS (CMP_BUILTINS) +}; + +/* Builtin functions for the SB-1 processor. */ + +#define CODE_FOR_mips_sqrt_ps CODE_FOR_sqrtv2sf2 + +static const struct builtin_description sb1_bdesc[] = +{ + DIRECT_BUILTIN (sqrt_ps, MIPS_V2SF_FTYPE_V2SF, MASK_PAIRED_SINGLE_FLOAT) +}; + +/* Builtin functions for DSP ASE. */ + +#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 a MIPS_BUILTIN_DIRECT_NO_TARGET function for instruction + CODE_FOR_mips_<INSN>. FUNCTION_TYPE and TARGET_FLAGS are + builtin_description fields. */ +#define DIRECT_NO_TARGET_BUILTIN(INSN, FUNCTION_TYPE, TARGET_FLAGS) \ + { CODE_FOR_mips_ ## INSN, 0, "__builtin_mips_" #INSN, \ + MIPS_BUILTIN_DIRECT_NO_TARGET, FUNCTION_TYPE, TARGET_FLAGS } + +/* Define __builtin_mips_bposge<VALUE>. <VALUE> is 32 for the MIPS32 DSP + branch instruction. TARGET_FLAGS is a builtin_description field. */ +#define BPOSGE_BUILTIN(VALUE, TARGET_FLAGS) \ + { CODE_FOR_mips_bposge, 0, "__builtin_mips_bposge" #VALUE, \ + MIPS_BUILTIN_BPOSGE ## VALUE, MIPS_SI_FTYPE_VOID, TARGET_FLAGS } + +static const struct builtin_description dsp_bdesc[] = +{ + DIRECT_BUILTIN (addq_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSP), + DIRECT_BUILTIN (addq_s_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSP), + DIRECT_BUILTIN (addq_s_w, MIPS_SI_FTYPE_SI_SI, MASK_DSP), + DIRECT_BUILTIN (addu_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, MASK_DSP), + DIRECT_BUILTIN (addu_s_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, MASK_DSP), + DIRECT_BUILTIN (subq_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSP), + DIRECT_BUILTIN (subq_s_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSP), + DIRECT_BUILTIN (subq_s_w, MIPS_SI_FTYPE_SI_SI, MASK_DSP), + DIRECT_BUILTIN (subu_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, MASK_DSP), + DIRECT_BUILTIN (subu_s_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, MASK_DSP), + DIRECT_BUILTIN (addsc, MIPS_SI_FTYPE_SI_SI, MASK_DSP), + DIRECT_BUILTIN (addwc, MIPS_SI_FTYPE_SI_SI, MASK_DSP), + DIRECT_BUILTIN (modsub, MIPS_SI_FTYPE_SI_SI, MASK_DSP), + DIRECT_BUILTIN (raddu_w_qb, MIPS_SI_FTYPE_V4QI, MASK_DSP), + DIRECT_BUILTIN (absq_s_ph, MIPS_V2HI_FTYPE_V2HI, MASK_DSP), + DIRECT_BUILTIN (absq_s_w, MIPS_SI_FTYPE_SI, MASK_DSP), + DIRECT_BUILTIN (precrq_qb_ph, MIPS_V4QI_FTYPE_V2HI_V2HI, MASK_DSP), + DIRECT_BUILTIN (precrq_ph_w, MIPS_V2HI_FTYPE_SI_SI, MASK_DSP), + DIRECT_BUILTIN (precrq_rs_ph_w, MIPS_V2HI_FTYPE_SI_SI, MASK_DSP), + DIRECT_BUILTIN (precrqu_s_qb_ph, MIPS_V4QI_FTYPE_V2HI_V2HI, MASK_DSP), + DIRECT_BUILTIN (preceq_w_phl, MIPS_SI_FTYPE_V2HI, MASK_DSP), + DIRECT_BUILTIN (preceq_w_phr, MIPS_SI_FTYPE_V2HI, MASK_DSP), + DIRECT_BUILTIN (precequ_ph_qbl, MIPS_V2HI_FTYPE_V4QI, MASK_DSP), + DIRECT_BUILTIN (precequ_ph_qbr, MIPS_V2HI_FTYPE_V4QI, MASK_DSP), + DIRECT_BUILTIN (precequ_ph_qbla, MIPS_V2HI_FTYPE_V4QI, MASK_DSP), + DIRECT_BUILTIN (precequ_ph_qbra, MIPS_V2HI_FTYPE_V4QI, MASK_DSP), + DIRECT_BUILTIN (preceu_ph_qbl, MIPS_V2HI_FTYPE_V4QI, MASK_DSP), + DIRECT_BUILTIN (preceu_ph_qbr, MIPS_V2HI_FTYPE_V4QI, MASK_DSP), + DIRECT_BUILTIN (preceu_ph_qbla, MIPS_V2HI_FTYPE_V4QI, MASK_DSP), + DIRECT_BUILTIN (preceu_ph_qbra, MIPS_V2HI_FTYPE_V4QI, MASK_DSP), + DIRECT_BUILTIN (shll_qb, MIPS_V4QI_FTYPE_V4QI_SI, MASK_DSP), + DIRECT_BUILTIN (shll_ph, MIPS_V2HI_FTYPE_V2HI_SI, MASK_DSP), + DIRECT_BUILTIN (shll_s_ph, MIPS_V2HI_FTYPE_V2HI_SI, MASK_DSP), + DIRECT_BUILTIN (shll_s_w, MIPS_SI_FTYPE_SI_SI, MASK_DSP), + DIRECT_BUILTIN (shrl_qb, MIPS_V4QI_FTYPE_V4QI_SI, MASK_DSP), + DIRECT_BUILTIN (shra_ph, MIPS_V2HI_FTYPE_V2HI_SI, MASK_DSP), + DIRECT_BUILTIN (shra_r_ph, MIPS_V2HI_FTYPE_V2HI_SI, MASK_DSP), + DIRECT_BUILTIN (shra_r_w, MIPS_SI_FTYPE_SI_SI, MASK_DSP), + DIRECT_BUILTIN (muleu_s_ph_qbl, MIPS_V2HI_FTYPE_V4QI_V2HI, MASK_DSP), + DIRECT_BUILTIN (muleu_s_ph_qbr, MIPS_V2HI_FTYPE_V4QI_V2HI, MASK_DSP), + DIRECT_BUILTIN (mulq_rs_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSP), + DIRECT_BUILTIN (muleq_s_w_phl, MIPS_SI_FTYPE_V2HI_V2HI, MASK_DSP), + DIRECT_BUILTIN (muleq_s_w_phr, MIPS_SI_FTYPE_V2HI_V2HI, MASK_DSP), + DIRECT_BUILTIN (dpau_h_qbl, MIPS_DI_FTYPE_DI_V4QI_V4QI, MASK_DSP), + DIRECT_BUILTIN (dpau_h_qbr, MIPS_DI_FTYPE_DI_V4QI_V4QI, MASK_DSP), + DIRECT_BUILTIN (dpsu_h_qbl, MIPS_DI_FTYPE_DI_V4QI_V4QI, MASK_DSP), + DIRECT_BUILTIN (dpsu_h_qbr, MIPS_DI_FTYPE_DI_V4QI_V4QI, MASK_DSP), + DIRECT_BUILTIN (dpaq_s_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSP), + DIRECT_BUILTIN (dpsq_s_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSP), + DIRECT_BUILTIN (mulsaq_s_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSP), + DIRECT_BUILTIN (dpaq_sa_l_w, MIPS_DI_FTYPE_DI_SI_SI, MASK_DSP), + DIRECT_BUILTIN (dpsq_sa_l_w, MIPS_DI_FTYPE_DI_SI_SI, MASK_DSP), + DIRECT_BUILTIN (maq_s_w_phl, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSP), + DIRECT_BUILTIN (maq_s_w_phr, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSP), + DIRECT_BUILTIN (maq_sa_w_phl, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSP), + DIRECT_BUILTIN (maq_sa_w_phr, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSP), + DIRECT_BUILTIN (bitrev, MIPS_SI_FTYPE_SI, MASK_DSP), + DIRECT_BUILTIN (insv, MIPS_SI_FTYPE_SI_SI, MASK_DSP), + DIRECT_BUILTIN (repl_qb, MIPS_V4QI_FTYPE_SI, MASK_DSP), + DIRECT_BUILTIN (repl_ph, MIPS_V2HI_FTYPE_SI, MASK_DSP), + DIRECT_NO_TARGET_BUILTIN (cmpu_eq_qb, MIPS_VOID_FTYPE_V4QI_V4QI, MASK_DSP), + DIRECT_NO_TARGET_BUILTIN (cmpu_lt_qb, MIPS_VOID_FTYPE_V4QI_V4QI, MASK_DSP), + DIRECT_NO_TARGET_BUILTIN (cmpu_le_qb, MIPS_VOID_FTYPE_V4QI_V4QI, MASK_DSP), + DIRECT_BUILTIN (cmpgu_eq_qb, MIPS_SI_FTYPE_V4QI_V4QI, MASK_DSP), + DIRECT_BUILTIN (cmpgu_lt_qb, MIPS_SI_FTYPE_V4QI_V4QI, MASK_DSP), + DIRECT_BUILTIN (cmpgu_le_qb, MIPS_SI_FTYPE_V4QI_V4QI, MASK_DSP), + DIRECT_NO_TARGET_BUILTIN (cmp_eq_ph, MIPS_VOID_FTYPE_V2HI_V2HI, MASK_DSP), + DIRECT_NO_TARGET_BUILTIN (cmp_lt_ph, MIPS_VOID_FTYPE_V2HI_V2HI, MASK_DSP), + DIRECT_NO_TARGET_BUILTIN (cmp_le_ph, MIPS_VOID_FTYPE_V2HI_V2HI, MASK_DSP), + DIRECT_BUILTIN (pick_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, MASK_DSP), + DIRECT_BUILTIN (pick_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSP), + DIRECT_BUILTIN (packrl_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSP), + DIRECT_BUILTIN (extr_w, MIPS_SI_FTYPE_DI_SI, MASK_DSP), + DIRECT_BUILTIN (extr_r_w, MIPS_SI_FTYPE_DI_SI, MASK_DSP), + DIRECT_BUILTIN (extr_rs_w, MIPS_SI_FTYPE_DI_SI, MASK_DSP), + DIRECT_BUILTIN (extr_s_h, MIPS_SI_FTYPE_DI_SI, MASK_DSP), + DIRECT_BUILTIN (extp, MIPS_SI_FTYPE_DI_SI, MASK_DSP), + DIRECT_BUILTIN (extpdp, MIPS_SI_FTYPE_DI_SI, MASK_DSP), + DIRECT_BUILTIN (shilo, MIPS_DI_FTYPE_DI_SI, MASK_DSP), + DIRECT_BUILTIN (mthlip, MIPS_DI_FTYPE_DI_SI, MASK_DSP), + DIRECT_NO_TARGET_BUILTIN (wrdsp, MIPS_VOID_FTYPE_SI_SI, MASK_DSP), + DIRECT_BUILTIN (rddsp, MIPS_SI_FTYPE_SI, MASK_DSP), + DIRECT_BUILTIN (lbux, MIPS_SI_FTYPE_PTR_SI, MASK_DSP), + DIRECT_BUILTIN (lhx, MIPS_SI_FTYPE_PTR_SI, MASK_DSP), + DIRECT_BUILTIN (lwx, MIPS_SI_FTYPE_PTR_SI, MASK_DSP), + BPOSGE_BUILTIN (32, MASK_DSP) +}; + +/* This helps provide a mapping from builtin function codes to bdesc + arrays. */ + +struct bdesc_map +{ + /* The builtin function table that this entry describes. */ + const struct builtin_description *bdesc; + + /* The number of entries in the builtin function table. */ + unsigned int size; + + /* The target processor that supports these builtin functions. + PROCESSOR_MAX means we enable them for all processors. */ + enum processor_type proc; +}; + +static const struct bdesc_map bdesc_arrays[] = +{ + { mips_bdesc, ARRAY_SIZE (mips_bdesc), PROCESSOR_MAX }, + { sb1_bdesc, ARRAY_SIZE (sb1_bdesc), PROCESSOR_SB1 }, + { dsp_bdesc, ARRAY_SIZE (dsp_bdesc), PROCESSOR_MAX } +}; + +/* Take the head of argument list *ARGLIST and convert it into a form + suitable for input operand OP of instruction ICODE. Return the value + and point *ARGLIST at the next element of the list. */ + +static rtx +mips_prepare_builtin_arg (enum insn_code icode, + unsigned int op, tree *arglist) +{ + rtx value; + enum machine_mode mode; + + value = expand_normal (TREE_VALUE (*arglist)); + mode = insn_data[icode].operand[op].mode; + if (!insn_data[icode].operand[op].predicate (value, mode)) + { + value = copy_to_mode_reg (mode, value); + /* Check the predicate again. */ + if (!insn_data[icode].operand[op].predicate (value, mode)) + { + error ("invalid argument to builtin function"); + return const0_rtx; + } + } + + *arglist = TREE_CHAIN (*arglist); + 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 builtin functions. This is called from TARGET_EXPAND_BUILTIN. */ + +rtx +mips_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED, + enum machine_mode mode ATTRIBUTE_UNUSED, + int ignore ATTRIBUTE_UNUSED) +{ + enum insn_code icode; + enum mips_builtin_type type; + tree fndecl, arglist; + unsigned int fcode; + const struct builtin_description *bdesc; + const struct bdesc_map *m; + + fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0); + arglist = TREE_OPERAND (exp, 1); + fcode = DECL_FUNCTION_CODE (fndecl); + + bdesc = NULL; + for (m = bdesc_arrays; m < &bdesc_arrays[ARRAY_SIZE (bdesc_arrays)]; m++) + { + if (fcode < m->size) + { + bdesc = m->bdesc; + icode = bdesc[fcode].icode; + type = bdesc[fcode].builtin_type; + break; + } + fcode -= m->size; + } + if (bdesc == NULL) + return 0; + + switch (type) + { + case MIPS_BUILTIN_DIRECT: + return mips_expand_builtin_direct (icode, target, arglist, true); + + case MIPS_BUILTIN_DIRECT_NO_TARGET: + return mips_expand_builtin_direct (icode, target, arglist, false); + + case MIPS_BUILTIN_MOVT: + case MIPS_BUILTIN_MOVF: + return mips_expand_builtin_movtf (type, icode, bdesc[fcode].cond, + target, arglist); + + 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 (type, icode, bdesc[fcode].cond, + target, arglist); + + case MIPS_BUILTIN_BPOSGE32: + return mips_expand_builtin_bposge (type, target); + + default: + return 0; + } +} + +/* Init builtin functions. This is called from TARGET_INIT_BUILTIN. */ + +void +mips_init_builtins (void) +{ + const struct builtin_description *d; + const struct bdesc_map *m; + tree types[(int) MIPS_MAX_FTYPE_MAX]; + tree V2SF_type_node; + tree V2HI_type_node; + tree V4QI_type_node; + unsigned int offset; + + /* We have only builtins for -mpaired-single, -mips3d and -mdsp. */ + if (!TARGET_PAIRED_SINGLE_FLOAT && !TARGET_DSP) + return; + + if (TARGET_PAIRED_SINGLE_FLOAT) + { + V2SF_type_node = build_vector_type_for_mode (float_type_node, V2SFmode); + + types[MIPS_V2SF_FTYPE_V2SF] + = build_function_type_list (V2SF_type_node, V2SF_type_node, NULL_TREE); + + types[MIPS_V2SF_FTYPE_V2SF_V2SF] + = build_function_type_list (V2SF_type_node, + V2SF_type_node, V2SF_type_node, NULL_TREE); + + types[MIPS_V2SF_FTYPE_V2SF_V2SF_INT] + = build_function_type_list (V2SF_type_node, + V2SF_type_node, V2SF_type_node, + integer_type_node, NULL_TREE); + + types[MIPS_V2SF_FTYPE_V2SF_V2SF_V2SF_V2SF] + = build_function_type_list (V2SF_type_node, + V2SF_type_node, V2SF_type_node, + V2SF_type_node, V2SF_type_node, NULL_TREE); + + types[MIPS_V2SF_FTYPE_SF_SF] + = build_function_type_list (V2SF_type_node, + float_type_node, float_type_node, NULL_TREE); + + types[MIPS_INT_FTYPE_V2SF_V2SF] + = build_function_type_list (integer_type_node, + V2SF_type_node, V2SF_type_node, NULL_TREE); + + types[MIPS_INT_FTYPE_V2SF_V2SF_V2SF_V2SF] + = build_function_type_list (integer_type_node, + V2SF_type_node, V2SF_type_node, + V2SF_type_node, V2SF_type_node, NULL_TREE); + + types[MIPS_INT_FTYPE_SF_SF] + = build_function_type_list (integer_type_node, + float_type_node, float_type_node, NULL_TREE); + + types[MIPS_INT_FTYPE_DF_DF] + = build_function_type_list (integer_type_node, + double_type_node, double_type_node, NULL_TREE); + + types[MIPS_SF_FTYPE_V2SF] + = build_function_type_list (float_type_node, V2SF_type_node, NULL_TREE); + + types[MIPS_SF_FTYPE_SF] + = build_function_type_list (float_type_node, + float_type_node, NULL_TREE); + + types[MIPS_SF_FTYPE_SF_SF] + = build_function_type_list (float_type_node, + float_type_node, float_type_node, NULL_TREE); + + types[MIPS_DF_FTYPE_DF] + = build_function_type_list (double_type_node, + double_type_node, NULL_TREE); + + types[MIPS_DF_FTYPE_DF_DF] + = build_function_type_list (double_type_node, + double_type_node, double_type_node, NULL_TREE); + } + + if (TARGET_DSP) + { + V2HI_type_node = build_vector_type_for_mode (intHI_type_node, V2HImode); + V4QI_type_node = build_vector_type_for_mode (intQI_type_node, V4QImode); + + types[MIPS_V2HI_FTYPE_V2HI_V2HI] + = build_function_type_list (V2HI_type_node, + V2HI_type_node, V2HI_type_node, + NULL_TREE); + + types[MIPS_SI_FTYPE_SI_SI] + = build_function_type_list (intSI_type_node, + intSI_type_node, intSI_type_node, + NULL_TREE); + + types[MIPS_V4QI_FTYPE_V4QI_V4QI] + = build_function_type_list (V4QI_type_node, + V4QI_type_node, V4QI_type_node, + NULL_TREE); + + types[MIPS_SI_FTYPE_V4QI] + = build_function_type_list (intSI_type_node, + V4QI_type_node, + NULL_TREE); + + types[MIPS_V2HI_FTYPE_V2HI] + = build_function_type_list (V2HI_type_node, + V2HI_type_node, + NULL_TREE); + + types[MIPS_SI_FTYPE_SI] + = build_function_type_list (intSI_type_node, + intSI_type_node, + NULL_TREE); + + types[MIPS_V4QI_FTYPE_V2HI_V2HI] + = build_function_type_list (V4QI_type_node, + V2HI_type_node, V2HI_type_node, + NULL_TREE); + + types[MIPS_V2HI_FTYPE_SI_SI] + = build_function_type_list (V2HI_type_node, + intSI_type_node, intSI_type_node, + NULL_TREE); + + types[MIPS_SI_FTYPE_V2HI] + = build_function_type_list (intSI_type_node, + V2HI_type_node, + NULL_TREE); + + types[MIPS_V2HI_FTYPE_V4QI] + = build_function_type_list (V2HI_type_node, + V4QI_type_node, + NULL_TREE); + + types[MIPS_V4QI_FTYPE_V4QI_SI] + = build_function_type_list (V4QI_type_node, + V4QI_type_node, intSI_type_node, + NULL_TREE); + + types[MIPS_V2HI_FTYPE_V2HI_SI] + = build_function_type_list (V2HI_type_node, + V2HI_type_node, intSI_type_node, + NULL_TREE); + + types[MIPS_V2HI_FTYPE_V4QI_V2HI] + = build_function_type_list (V2HI_type_node, + V4QI_type_node, V2HI_type_node, + NULL_TREE); + + types[MIPS_SI_FTYPE_V2HI_V2HI] + = build_function_type_list (intSI_type_node, + V2HI_type_node, V2HI_type_node, + NULL_TREE); + + types[MIPS_DI_FTYPE_DI_V4QI_V4QI] + = build_function_type_list (intDI_type_node, + intDI_type_node, V4QI_type_node, V4QI_type_node, + NULL_TREE); + + types[MIPS_DI_FTYPE_DI_V2HI_V2HI] + = build_function_type_list (intDI_type_node, + intDI_type_node, V2HI_type_node, V2HI_type_node, + NULL_TREE); + + types[MIPS_DI_FTYPE_DI_SI_SI] + = build_function_type_list (intDI_type_node, + intDI_type_node, intSI_type_node, intSI_type_node, + NULL_TREE); + + types[MIPS_V4QI_FTYPE_SI] + = build_function_type_list (V4QI_type_node, + intSI_type_node, + NULL_TREE); + + types[MIPS_V2HI_FTYPE_SI] + = build_function_type_list (V2HI_type_node, + intSI_type_node, + NULL_TREE); + + types[MIPS_VOID_FTYPE_V4QI_V4QI] + = build_function_type_list (void_type_node, + V4QI_type_node, V4QI_type_node, + NULL_TREE); + + types[MIPS_SI_FTYPE_V4QI_V4QI] + = build_function_type_list (intSI_type_node, + V4QI_type_node, V4QI_type_node, + NULL_TREE); + + types[MIPS_VOID_FTYPE_V2HI_V2HI] + = build_function_type_list (void_type_node, + V2HI_type_node, V2HI_type_node, + NULL_TREE); + + types[MIPS_SI_FTYPE_DI_SI] + = build_function_type_list (intSI_type_node, + intDI_type_node, intSI_type_node, + NULL_TREE); + + types[MIPS_DI_FTYPE_DI_SI] + = build_function_type_list (intDI_type_node, + intDI_type_node, intSI_type_node, + NULL_TREE); + + types[MIPS_VOID_FTYPE_SI_SI] + = build_function_type_list (void_type_node, + intSI_type_node, intSI_type_node, + NULL_TREE); + + types[MIPS_SI_FTYPE_PTR_SI] + = build_function_type_list (intSI_type_node, + ptr_type_node, intSI_type_node, + NULL_TREE); + + types[MIPS_SI_FTYPE_VOID] + = build_function_type (intSI_type_node, void_list_node); + } + + /* Iterate through all of the bdesc arrays, initializing all of the + builtin functions. */ + + offset = 0; + for (m = bdesc_arrays; m < &bdesc_arrays[ARRAY_SIZE (bdesc_arrays)]; m++) + { + if (m->proc == PROCESSOR_MAX || (m->proc == mips_arch)) + for (d = m->bdesc; d < &m->bdesc[m->size]; d++) + if ((d->target_flags & target_flags) == d->target_flags) + lang_hooks.builtin_function (d->name, types[d->function_type], + d - m->bdesc + offset, + BUILT_IN_MD, NULL, NULL); + offset += m->size; + } +} + +/* Expand a MIPS_BUILTIN_DIRECT function. ICODE is the code of the + .md pattern and ARGLIST is the list of function arguments. TARGET, + if nonnull, suggests a good place to put the result. + HAS_TARGET indicates the function must return something. */ + +static rtx +mips_expand_builtin_direct (enum insn_code icode, rtx target, tree arglist, + bool has_target) +{ + rtx ops[MAX_RECOG_OPERANDS]; + int i = 0; + + if (has_target) + { + /* We save target to ops[0]. */ + ops[0] = mips_prepare_builtin_target (icode, 0, target); + i = 1; + } + + /* We need to test if arglist is not zero. Some instructions have extra + clobber registers. */ + for (; i < insn_data[icode].n_operands && arglist != 0; i++) + ops[i] = mips_prepare_builtin_arg (icode, i, &arglist); + + switch (i) + { + 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). ARGLIST is the list of arguments to 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 arglist) +{ + rtx cmp_result, op0, op1; + + cmp_result = mips_prepare_builtin_target (icode, 0, 0); + op0 = mips_prepare_builtin_arg (icode, 1, &arglist); + op1 = mips_prepare_builtin_arg (icode, 2, &arglist); + 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, &arglist); + op0 = mips_prepare_builtin_arg (icode, 1, &arglist); + } + else + { + op0 = mips_prepare_builtin_arg (icode, 1, &arglist); + op1 = mips_prepare_builtin_arg (icode, 2, &arglist); + } + 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. */ + emit_move_insn (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); + emit_move_insn (target, value_if_true); + + emit_label (done_label); + return target; +} + +/* Expand a comparison builtin of type BUILTIN_TYPE. ICODE is the code + of the comparison instruction and COND is the condition it should test. + ARGLIST is the list of function arguments and 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 arglist) +{ + rtx offset, condition, cmp_result, ops[MAX_RECOG_OPERANDS]; + int i; + + if (target == 0 || GET_MODE (target) != SImode) + target = gen_reg_rtx (SImode); + + /* Prepare the operands to the comparison. */ + cmp_result = mips_prepare_builtin_target (icode, 0, 0); + for (i = 1; i < insn_data[icode].n_operands - 1; i++) + ops[i] = mips_prepare_builtin_arg (icode, i, &arglist); + + switch (insn_data[icode].n_operands) + { + case 4: + emit_insn (GEN_FCN (icode) (cmp_result, ops[1], ops[2], GEN_INT (cond))); + break; + + case 6: + emit_insn (GEN_FCN (icode) (cmp_result, ops[1], ops[2], + ops[3], ops[4], 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 builtin 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); +} + +/* Set SYMBOL_REF_FLAGS for the SYMBOL_REF inside RTL, which belongs to DECL. + FIRST is true if this is the first time handling this decl. */ + +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 + && lookup_attribute ("long_call", TYPE_ATTRIBUTES (TREE_TYPE (decl)))) + { + rtx symbol = XEXP (rtl, 0); + SYMBOL_REF_FLAGS (symbol) |= SYMBOL_FLAG_LONG_CALL; + } +} + +/* Implement TARGET_EXTRA_LIVE_ON_ENTRY. PIC_FUNCTION_ADDR_REGNUM is live + on entry to a function when generating -mshared abicalls code. */ + +static void +mips_extra_live_on_entry (bitmap regs) +{ + if (TARGET_ABICALLS && !TARGET_ABSOLUTE_ABICALLS) + bitmap_set_bit (regs, PIC_FUNCTION_ADDR_REGNUM); +} + +/* SImode values are represented as sign-extended to DImode. */ + +int +mips_mode_rep_extended (enum machine_mode mode, enum machine_mode mode_rep) +{ + if (TARGET_64BIT && mode == SImode && mode_rep == DImode) + return SIGN_EXTEND; + + return UNKNOWN; +} + +#include "gt-mips.h" diff --git a/contrib/gcc/config/mips/mips.h b/contrib/gcc/config/mips/mips.h new file mode 100644 index 0000000..ba77d86 --- /dev/null +++ b/contrib/gcc/config/mips/mips.h @@ -0,0 +1,2711 @@ +/* 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 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 2, 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 COPYING. If not, write to +the Free Software Foundation, 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. */ + + +/* MIPS external variables defined in mips.c. */ + +/* Which processor to schedule for. Since there is no difference between + a R2000 and R3000 in terms of the scheduler, we collapse them into + just an R3000. The elements of the enumeration must match exactly + the cpu attribute in the mips.md machine description. */ + +enum processor_type { + PROCESSOR_R3000, + PROCESSOR_4KC, + PROCESSOR_4KP, + PROCESSOR_5KC, + PROCESSOR_5KF, + PROCESSOR_20KC, + PROCESSOR_24K, + PROCESSOR_24KX, + PROCESSOR_M4K, + PROCESSOR_R3900, + PROCESSOR_R6000, + PROCESSOR_R4000, + PROCESSOR_R4100, + PROCESSOR_R4111, + PROCESSOR_R4120, + PROCESSOR_R4130, + PROCESSOR_R4300, + PROCESSOR_R4600, + PROCESSOR_R4650, + PROCESSOR_R5000, + PROCESSOR_R5400, + PROCESSOR_R5500, + PROCESSOR_R7000, + PROCESSOR_R8000, + PROCESSOR_R9000, + PROCESSOR_SB1, + PROCESSOR_SB1A, + PROCESSOR_SR71000, + PROCESSOR_MAX +}; + +/* 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; +}; + +/* 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 + +/* 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 lower case. */ + 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_type cpu; + + /* The ISA level that the processor implements. */ + int isa; +}; + +#ifndef USED_FOR_TARGET +extern char mips_print_operand_punct[256]; /* print_operand punctuation chars */ +extern const char *current_function_file; /* filename current function is in */ +extern int num_source_filenames; /* current .file # */ +extern int mips_section_threshold; /* # bytes of data/sdata cutoff */ +extern int sym_lineno; /* sgi next label # for each stmt */ +extern int set_noreorder; /* # of nested .set noreorder's */ +extern int set_nomacro; /* # of nested .set nomacro's */ +extern int set_noat; /* # of nested .set noat's */ +extern int set_volatile; /* # of nested .set volatile's */ +extern int mips_branch_likely; /* emit 'l' after br (branch likely) */ +extern int mips_dbx_regno[]; /* Map register # to debug register # */ +extern bool mips_split_p[]; +extern GTY(()) rtx cmp_operands[2]; +extern enum processor_type mips_arch; /* which cpu to codegen for */ +extern enum processor_type mips_tune; /* which cpu to schedule for */ +extern int mips_isa; /* architectural level */ +extern int mips_abi; /* which ABI to use */ +extern int mips16_hard_float; /* mips16 without -msoft-float */ +extern const struct mips_cpu_info mips_cpu_info_table[]; +extern const struct mips_cpu_info *mips_arch_info; +extern const struct mips_cpu_info *mips_tune_info; +extern const struct mips_rtx_cost_data *mips_cost; +#endif + +/* 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 the call patterns should be split into a jalr followed by + an instruction to restore $gp. This is only ever true for SVR4 PIC, + in which $gp is call-clobbered. It is only safe to split the load + from the call when every use of $gp is explicit. */ + +#define TARGET_SPLIT_CALLS \ + (TARGET_EXPLICIT_RELOCS && TARGET_ABICALLS && !TARGET_NEWABI) + +/* 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_ABICALLS && !TARGET_EXPLICIT_RELOCS. call_insn_operand + accepts global constants, but "jr $25" is the only allowed + sibcall. */ + +#define TARGET_SIBCALLS \ + (!TARGET_MIPS16 && (!TARGET_ABICALLS || TARGET_EXPLICIT_RELOCS)) + +/* 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 && !(mips_abi == ABI_64 && TARGET_IRIX)) + +/* Generate mips16 code */ +#define TARGET_MIPS16 ((target_flags & MASK_MIPS16) != 0) +/* Generate mips16e code. Default 16bit ASE for mips32/mips32r2/mips64 */ +#define GENERATE_MIPS16E (TARGET_MIPS16 && mips_isa >= 32) + +/* 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) + +/* Architecture target defines. */ +#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_SB1 (mips_arch == PROCESSOR_SB1 \ + || mips_arch == PROCESSOR_SB1A) +#define TARGET_SR71K (mips_arch == PROCESSOR_SR71000) + +/* Scheduling target defines. */ +#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_SB1 (mips_tune == PROCESSOR_SB1 \ + || mips_tune == PROCESSOR_SB1A) + +/* 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) + +#define TARGET_OLDABI (mips_abi == ABI_32 || mips_abi == ABI_O64) +#define TARGET_NEWABI (mips_abi == ABI_N32 || mips_abi == ABI_64) + +/* IRIX specific stuff. */ +#define TARGET_IRIX 0 +#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_IRIX) \ + 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. */ \ + if (!flag_iso) \ + builtin_define ("mips"); \ + \ + if (TARGET_64BIT) \ + builtin_define ("__mips64"); \ + \ + if (!TARGET_IRIX) \ + { \ + /* 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 (TARGET_MIPS16) \ + builtin_define ("__mips16"); \ + \ + if (TARGET_MIPS3D) \ + builtin_define ("__mips3d"); \ + \ + if (TARGET_DSP) \ + builtin_define ("__mips_dsp"); \ + \ + 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"); \ + } \ + \ + 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_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"); \ + } \ + \ + /* 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"); \ + \ +} 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 */ + +#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 +# define MULTILIB_ISA_DEFAULT "mips1" +# endif +# endif +# endif +# endif +# endif +# endif +# endif +#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 + +/* 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). + --with-tune is ignored if -mtune is specified. + --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", "%{!march=*:%{mips16:-march=%(VALUE)}%{!mips*:-march=%(VALUE)}}" }, \ + {"tune", "%{!mtune=*:-mtune=%(VALUE)}" }, \ + {"abi", "%{!mabi=*:-mabi=%(VALUE)}" }, \ + {"float", "%{!msoft-float:%{!mhard-float:-m%(VALUE)-float}}" }, \ + {"divide", "%{!mdivide-traps:%{!mdivide-breaks:-mdivide-%(VALUE)}}" } + + +#define GENERATE_DIVIDE_TRAPS (TARGET_DIVIDE_TRAPS \ + && ISA_HAS_COND_TRAP) + +#define GENERATE_BRANCHLIKELY (TARGET_BRANCHLIKELY \ + && !TARGET_SR71K \ + && !TARGET_MIPS16) + +/* Generate three-operand multiply instructions for SImode. */ +#define GENERATE_MULT3_SI ((TARGET_MIPS3900 \ + || TARGET_MIPS5400 \ + || TARGET_MIPS5500 \ + || TARGET_MIPS7000 \ + || TARGET_MIPS9000 \ + || TARGET_MAD \ + || ISA_MIPS32 \ + || ISA_MIPS32R2 \ + || ISA_MIPS64) \ + && !TARGET_MIPS16) + +/* Generate three-operand multiply instructions for DImode. */ +#define GENERATE_MULT3_DI ((TARGET_MIPS3900) \ + && !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 symbols are 64 bits wide. At present, n64 is the only + ABI for which this is true. */ +#define ABI_HAS_64BIT_SYMBOLS (mips_abi == ABI_64 && !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 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 the conditional move instructions introduced in mips4. */ +#define ISA_HAS_CONDMOVE ((ISA_MIPS4 \ + || ISA_MIPS32 \ + || ISA_MIPS32R2 \ + || ISA_MIPS64) \ + && !TARGET_MIPS5500 \ + && !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) + +/* 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_MIPS64) \ + && !TARGET_MIPS16) + +/* 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 \ + ) && !TARGET_MIPS16) + +/* ISA has floating-point nmadd and nmsub instructions. */ +#define ISA_HAS_NMADD_NMSUB ((ISA_MIPS4 \ + || ISA_MIPS64) \ + && (!TARGET_MIPS5400 || TARGET_MAD) \ + && ! TARGET_MIPS16) + +/* ISA has count leading zeroes/ones instruction (not implemented). */ +#define ISA_HAS_CLZ_CLO ((ISA_MIPS32 \ + || ISA_MIPS32R2 \ + || ISA_MIPS64 \ + ) && !TARGET_MIPS16) + +/* ISA has double-word count leading zeroes/ones instruction (not + implemented). */ +#define ISA_HAS_DCLZ_DCLO (ISA_MIPS64 \ + && !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 \ + ) + +/* 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 \ + ) + +/* 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 \ + ) +/* 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_MIPS16) \ + || (TARGET_MIPS4130 && !TARGET_MIPS16) \ + || TARGET_MIPS5400 \ + || TARGET_MIPS5500 \ + || TARGET_SR71K \ + ) + +/* ISA has NEC VR-style MACC, MACCHI, DMACC and DMACCHI instructions. */ +#define ISA_HAS_MACCHI (!TARGET_MIPS16 \ + && (TARGET_MIPS4120 \ + || TARGET_MIPS4130)) + +/* ISA has 32-bit rotate right instruction. */ +#define ISA_HAS_ROTR_SI (!TARGET_MIPS16 \ + && (ISA_MIPS32R2 \ + || TARGET_MIPS5400 \ + || TARGET_MIPS5500 \ + || TARGET_SR71K \ + )) + +/* ISA has 64-bit rotate right instruction. */ +#define ISA_HAS_ROTR_DI (TARGET_64BIT \ + && !TARGET_MIPS16 \ + && (TARGET_MIPS5400 \ + || TARGET_MIPS5500 \ + || TARGET_SR71K \ + )) + +/* ISA has data prefetch instructions. This controls use of 'pref'. */ +#define ISA_HAS_PREFETCH ((ISA_MIPS4 \ + || ISA_MIPS32 \ + || ISA_MIPS32R2 \ + || ISA_MIPS64) \ + && !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_MIPS64) \ + && !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 (!TARGET_MIPS16 \ + && (ISA_MIPS32R2 \ + )) + +/* ISA includes the MIPS32/64 rev 2 ext and ins instructions. */ +#define ISA_HAS_EXT_INS (!TARGET_MIPS16 \ + && (ISA_MIPS32R2 \ + )) + +/* 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 (mips_isa == 1 \ + && !TARGET_MIPS3900 \ + && !TARGET_MIPS16) + +/* Likewise mtc1 and mfc1. */ +#define ISA_HAS_XFER_DELAY (mips_isa <= 3) + +/* Likewise floating-point comparisons. */ +#define ISA_HAS_FCMP_DELAY (mips_isa <= 3) + +/* 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 \ + || TARGET_MIPS5500) + +/* Add -G xx support. */ + +#undef SWITCH_TAKES_ARG +#define SWITCH_TAKES_ARG(CHAR) \ + (DEFAULT_SWITCH_TAKES_ARG (CHAR) || (CHAR) == 'G') + +#define OVERRIDE_OPTIONS override_options () + +#define CONDITIONAL_REGISTER_USAGE mips_conditional_register_usage () + +/* Show we can debug even without a frame pointer. */ +#define CAN_DEBUG_WITHOUT_FP + +/* Tell collect what flags to pass to nm. */ +#ifndef NM_FLAGS +#define NM_FLAGS "-Bn" +#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 + +/* 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} %{mips32r2} %{mips64} \ +%{mips16:%{!mno-mips16:-mips16}} %{mno-mips16:-no-mips16} \ +%{mips3d:-mips3d} \ +%{mdsp} \ +%{mfix-vr4120} %{mfix-vr4130} \ +%(subtarget_asm_optimizing_spec) \ +%(subtarget_asm_debugging_spec) \ +%{mabi=*} %{!mabi*: %(asm_abi_default_spec)} \ +%{mgp32} %{mgp64} %{march=*} %{mxgot:-xgot} \ +%{mshared} %{mno-shared} \ +%{msym32} %{mno-sym32} \ +%{mtune=*} %{v} \ +%(subtarget_asm_spec)" + +/* Extra switches sometimes passed to the linker. */ +/* ??? The bestGnum will never be passed to the linker, because the gcc driver + will interpret it as a -b option. */ + +#ifndef LINK_SPEC +#define LINK_SPEC "\ +%(endian_spec) \ +%{G*} %{mips1} %{mips2} %{mips3} %{mips4} %{mips32} %{mips32r2} %{mips64} \ +%{bestGnum} %{shared} %{non_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 "\ +%{gline:%{!g:%{!g0:%{!g1:%{!g2: -g1}}}}} \ +%{G*} %{EB:-meb} %{EL:-mel} %{EB:%{EL:%emay not use both -EB and -EL}} \ +%{save-temps: } \ +%(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 MIPS_DEBUGGING_INFO 1 /* MIPS specific debugging info */ +#define DWARF2_DEBUGGING_INFO 1 /* dwarf2 debugging info */ + +#ifndef PREFERRED_DEBUGGING_TYPE +#define PREFERRED_DEBUGGING_TYPE DWARF2_DEBUG +#endif + +#define DWARF2_ADDR_SIZE (ABI_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(REG) (REG) + +/* The DWARF 2 CFA column which tracks the return address. */ +#define DWARF_FRAME_RETURN_COLUMN (GP_REG_FIRST + 31) + +/* The DWARF 2 CFA column which tracks the return address from a + signal handler context. */ +#define SIGNAL_UNWIND_RETURN_COLUMN (FP_REG_LAST + 1) + +/* Before the prologue, RA lives in r31. */ +#define INCOMING_RETURN_ADDR_RTX gen_rtx_REG (VOIDmode, GP_REG_FIRST + 31) + +/* 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) + +/* 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 this to set the endianness to use in libgcc2.c, which can + not depend on target_flags. */ +#if !defined(MIPSEL) && !defined(__MIPSEL__) +#define LIBGCC2_WORDS_BIG_ENDIAN 1 +#else +#define LIBGCC2_WORDS_BIG_ENDIAN 0 +#endif + +#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) + +/* If register $f0 holds a floating-point value, $f(0 + FP_INC) is + the next available register. */ +#define FP_INC (TARGET_FLOAT64 || TARGET_SINGLE_FLOAT ? 1 : 2) + +/* 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 ? 0 : FP_INC * UNITS_PER_FPREG) + +/* The largest size of value that can be held in floating-point + registers. */ +#define UNITS_PER_FPVALUE \ + (TARGET_SOFT_FLOAT ? 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) + +#define UNITS_PER_SIMD_WORD (TARGET_PAIRED_SINGLE_FLOAT ? 8 : UNITS_PER_WORD) + +/* 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) + +/* 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)) + + +#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; \ + } + +/* 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), true) + +/* 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 + - 3 fake registers: + - ARG_POINTER_REGNUM + - FRAME_POINTER_REGNUM + - FAKE_CALL_REGNO (see the comment above load_callsi for details) + - 3 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., override_options() or + 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, 1, \ + /* 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 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) + +#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 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 (MD_REG_FIRST + 0) +#define LO_REGNUM (MD_REG_FIRST + 1) +#define AC1HI_REGNUM (DSP_ACC_REG_FIRST + 0) +#define AC1LO_REGNUM (DSP_ACC_REG_FIRST + 1) +#define AC2HI_REGNUM (DSP_ACC_REG_FIRST + 2) +#define AC2LO_REGNUM (DSP_ACC_REG_FIRST + 3) +#define AC3HI_REGNUM (DSP_ACC_REG_FIRST + 4) +#define AC3LO_REGNUM (DSP_ACC_REG_FIRST + 5) + +/* 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)) +/* Test if REGNO is HI or the first register of 3 new DSP accumulator pairs. */ +#define ACC_HI_REG_P(REGNO) \ + ((REGNO) == HI_REGNUM || (REGNO) == AC1HI_REGNUM || (REGNO) == AC2HI_REGNUM \ + || (REGNO) == AC3HI_REGNUM) + +#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) + +/* To make the code simpler, HARD_REGNO_MODE_OK just references an + array built in override_options. Because machmodes.h is not yet + included before this file is processed, the MODE bound can't be + expressed here. */ + +extern char mips_hard_regno_mode_ok[][FIRST_PSEUDO_REGISTER]; + +#define HARD_REGNO_MODE_OK(REGNO, MODE) \ + mips_hard_regno_mode_ok[ (int)(MODE) ][ (REGNO) ] + +/* Value is 1 if it is a good idea to tie two pseudo registers + when one has mode MODE1 and one has mode MODE2. + If HARD_REGNO_MODE_OK could produce different values for MODE1 and MODE2, + for any hard reg, then this must be 0 for correct output. */ +#define MODES_TIEABLE_P(MODE1, MODE2) \ + ((GET_MODE_CLASS (MODE1) == MODE_FLOAT || \ + GET_MODE_CLASS (MODE1) == MODE_COMPLEX_FLOAT) \ + == (GET_MODE_CLASS (MODE2) == MODE_FLOAT || \ + GET_MODE_CLASS (MODE2) == MODE_COMPLEX_FLOAT)) + +/* 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) + +/* Value should be nonzero if functions must have frame pointers. + Zero means the frame pointer need not be set up (and parms + may be accessed via the stack pointer) in functions that seem suitable. + This is computed in `reload', in reload1.c. */ +#define FRAME_POINTER_REQUIRED (current_function_calls_alloca) + +/* Register in which static-chain is passed to a function. */ +#define STATIC_CHAIN_REGNUM (GP_REG_FIRST + 2) + +/* Registers used as temporaries in prologue/epilogue code. If we're + generating mips16 code, these registers must come from the core set + of 8. The prologue register 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 + frame pointer, the EH stack adjustment, or the EH data registers. */ + +#define MIPS_PROLOGUE_TEMP_REGNUM (GP_REG_FIRST + 3) +#define MIPS_EPILOGUE_TEMP_REGNUM (GP_REG_FIRST + (TARGET_MIPS16 ? 6 : 8)) + +#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_NA_REGS, /* mips16 regs not used to pass args */ + 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 */ + HI_REG, /* hi register */ + LO_REG, /* lo register */ + MD_REGS, /* multiply/divide registers (hi/lo) */ + COP0_REGS, /* generic coprocessor classes */ + COP2_REGS, + COP3_REGS, + HI_AND_GR_REGS, /* union classes */ + LO_AND_GR_REGS, + HI_AND_FP_REGS, + COP0_AND_GR_REGS, + COP2_AND_GR_REGS, + COP3_AND_GR_REGS, + ALL_COP_REGS, + ALL_COP_AND_GR_REGS, + ST_REGS, /* status registers (fp status) */ + DSP_ACC_REGS, /* DSP accumulator registers */ + ACC_REGS, /* Hi/Lo and DSP accumulator registers */ + 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_NA_REGS", \ + "M16_REGS", \ + "T_REG", \ + "M16_T_REGS", \ + "PIC_FN_ADDR_REG", \ + "V1_REG", \ + "LEA_REGS", \ + "GR_REGS", \ + "FP_REGS", \ + "HI_REG", \ + "LO_REG", \ + "MD_REGS", \ + /* coprocessor registers */ \ + "COP0_REGS", \ + "COP2_REGS", \ + "COP3_REGS", \ + "HI_AND_GR_REGS", \ + "LO_AND_GR_REGS", \ + "HI_AND_FP_REGS", \ + "COP0_AND_GR_REGS", \ + "COP2_AND_GR_REGS", \ + "COP3_AND_GR_REGS", \ + "ALL_COP_REGS", \ + "ALL_COP_AND_GR_REGS", \ + "ST_REGS", \ + "DSP_ACC_REGS", \ + "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 registers */ \ + { 0x0003000c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, /* mips16 nonarg regs */\ + { 0x000300fc, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, /* mips16 registers */ \ + { 0x01000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, /* mips16 T register */ \ + { 0x010300fc, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, /* mips16 and T regs */ \ + { 0x02000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, /* SVR4 PIC function address register */ \ + { 0x00000008, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, /* only $v1 */ \ + { 0xfdffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, /* Every other GPR except $25 */ \ + { 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, /* integer registers */ \ + { 0x00000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, /* floating registers*/ \ + { 0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000000 }, /* hi register */ \ + { 0x00000000, 0x00000000, 0x00000002, 0x00000000, 0x00000000, 0x00000000 }, /* lo register */ \ + { 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000000, 0x00000000 }, /* mul/div registers */ \ + { 0x00000000, 0x00000000, 0xffff0000, 0x0000ffff, 0x00000000, 0x00000000 }, /* cop0 registers */ \ + { 0x00000000, 0x00000000, 0x00000000, 0xffff0000, 0x0000ffff, 0x00000000 }, /* cop2 registers */ \ + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffff0000, 0x0000ffff }, /* cop3 registers */ \ + { 0xffffffff, 0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000000 }, /* union classes */ \ + { 0xffffffff, 0x00000000, 0x00000002, 0x00000000, 0x00000000, 0x00000000 }, \ + { 0x00000000, 0xffffffff, 0x00000001, 0x00000000, 0x00000000, 0x00000000 }, \ + { 0xffffffff, 0x00000000, 0xffff0000, 0x0000ffff, 0x00000000, 0x00000000 }, \ + { 0xffffffff, 0x00000000, 0x00000000, 0xffff0000, 0x0000ffff, 0x00000000 }, \ + { 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0xffff0000, 0x0000ffff }, \ + { 0x00000000, 0x00000000, 0xffff0000, 0xffffffff, 0xffffffff, 0x0000ffff }, \ + { 0xffffffff, 0x00000000, 0xffff0000, 0xffffffff, 0xffffffff, 0x0000ffff }, \ + { 0x00000000, 0x00000000, 0x000007f8, 0x00000000, 0x00000000, 0x00000000 }, /* status registers */ \ + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x003f0000 }, /* dsp accumulator registers */ \ + { 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000000, 0x003f0000 }, /* hi/lo and dsp accumulator registers */ \ + { 0xffffffff, 0xffffffff, 0xffff07ff, 0xffffffff, 0xffffffff, 0x0fffffff } /* all registers */ \ +} + + +/* 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. */ + +extern const enum reg_class mips_regno_to_class[]; + +#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 + +/* When SMALL_REGISTER_CLASSES is nonzero, the compiler allows + registers explicitly used in the rtl to be used as spill registers + but prevents the compiler from extending the lifetime of these + registers. */ + +#define SMALL_REGISTER_CLASSES (TARGET_MIPS16) + +/* This macro is used later on in the file. */ +#define GR_REG_CLASS_P(CLASS) \ + ((CLASS) == GR_REGS || (CLASS) == M16_REGS || (CLASS) == T_REG \ + || (CLASS) == M16_T_REGS || (CLASS) == M16_NA_REGS \ + || (CLASS) == V1_REG \ + || (CLASS) == PIC_FN_ADDR_REG || (CLASS) == LEA_REGS) + +/* This macro is also used later on in the file. */ +#define COP_REG_CLASS_P(CLASS) \ + ((CLASS) == COP0_REGS || (CLASS) == COP2_REGS || (CLASS) == COP3_REGS) + +/* REG_ALLOC_ORDER is to order in which to allocate registers. This + is the default value (allocate the registers in numeric order). We + define it just so that we can override it for the mips16 target in + ORDER_REGS_FOR_LOCAL_ALLOC. */ + +#define REG_ALLOC_ORDER \ +{ 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, 29, 30, 31, \ + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, \ + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, \ + 64, 65, 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, \ + 176,177,178,179,180,181,182,183,184,185,186,187 \ +} + +/* ORDER_REGS_FOR_LOCAL_ALLOC 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 ORDER_REGS_FOR_LOCAL_ALLOC 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)) + +#define PREFERRED_RELOAD_CLASS(X,CLASS) \ + mips_preferred_reload_class (X, CLASS) + +/* Certain machines have the property that some registers cannot be + copied to some other registers without using memory. Define this + macro on those machines to be a C expression that is nonzero if + objects of mode MODE in registers of CLASS1 can only be copied to + registers of class CLASS2 by storing a register of CLASS1 into + memory and loading that memory location into a register of CLASS2. + + Do not define this macro if its value would always be zero. */ +#if 0 +#define SECONDARY_MEMORY_NEEDED(CLASS1, CLASS2, MODE) \ + ((!TARGET_DEBUG_H_MODE \ + && GET_MODE_CLASS (MODE) == MODE_INT \ + && ((CLASS1 == FP_REGS && GR_REG_CLASS_P (CLASS2)) \ + || (GR_REG_CLASS_P (CLASS1) && CLASS2 == FP_REGS))) \ + || (TARGET_FLOAT64 && !TARGET_64BIT && (MODE) == DFmode \ + && ((GR_REG_CLASS_P (CLASS1) && CLASS2 == FP_REGS) \ + || (GR_REG_CLASS_P (CLASS2) && CLASS1 == FP_REGS)))) +#endif +/* 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, 1) +#define SECONDARY_OUTPUT_RELOAD_CLASS(CLASS, MODE, X) \ + mips_secondary_reload_class (CLASS, MODE, X, 0) + +/* 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 + +/* The offset of the first local variable from the beginning of the frame. + See compute_frame_size for details about the frame layout. + + ??? If flag_profile_values is true, and we are generating 32-bit code, then + we assume that we will need 16 bytes of argument space. This is because + the value profiling code may emit calls to cmpdi2 in leaf functions. + Without this hack, the local variables will start at sp+8 and the gp save + area will be at sp+16, and thus they will overlap. compute_frame_size is + OK because it uses STARTING_FRAME_OFFSET to compute cprestore_size, which + will end up as 24 instead of 8. This won't be needed if profiling code is + inserted before virtual register instantiation. */ + +#define STARTING_FRAME_OFFSET \ + ((flag_profile_values && ! TARGET_64BIT \ + ? MAX (REG_PARM_STACK_SPACE(NULL), current_function_outgoing_args_size) \ + : current_function_outgoing_args_size) \ + + (TARGET_ABICALLS && !TARGET_NEWABI \ + ? MIPS_STACK_ALIGN (UNITS_PER_WORD) : 0)) + +#define RETURN_ADDR_RTX mips_return_addr + +/* Since the mips16 ISA mode is encoded in the least-significant bit + of the address, mask it off return addresses for purposes of + finding exception handling regions. */ + +#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}} + +/* We can always eliminate to the hard frame pointer. We can eliminate + to the stack pointer unless a frame pointer is needed. + + 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 temporary register. */ +#define CAN_ELIMINATE(FROM, TO) \ + ((TO) == HARD_FRAME_POINTER_REGNUM \ + || ((TO) == STACK_POINTER_REGNUM && !frame_pointer_needed \ + && (!TARGET_MIPS16 \ + || compute_frame_size (get_frame_size ()) < 32768))) + +#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 + `current_function_outgoing_args_size'. */ +#define OUTGOING_REG_PARM_STACK_SPACE + +#define STACK_BOUNDARY (TARGET_NEWABI ? 128 : 64) + +#define RETURN_POPS_ARGS(FUNDECL,FUNTYPE,SIZE) 0 + +/* 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, (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 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) \ + init_cumulative_args (&CUM, FNTYPE, LIBNAME) \ + +/* Update the data in CUM to advance over an argument + of mode MODE and data type TYPE. + (TYPE is null for libcalls where that information may not be available.) */ + +#define FUNCTION_ARG_ADVANCE(CUM, MODE, TYPE, NAMED) \ + function_arg_advance (&CUM, MODE, TYPE, NAMED) + +/* Determine where to put an argument to a function. + Value is zero to push the argument on the stack, + or a hard register in which to store the argument. + + MODE is the argument's machine mode. + TYPE is the data type of the argument (as a tree). + This is null for libcalls where that information may + not be available. + CUM is a variable of type CUMULATIVE_ARGS which gives info about + the preceding args and about the function being called. + NAMED is nonzero if this argument is a named parameter + (otherwise it is an extra parameter matching an ellipsis). */ + +#define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \ + function_arg( &CUM, MODE, TYPE, NAMED) + +#define FUNCTION_ARG_BOUNDARY function_arg_boundary + +#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) + + +/* 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. */ +#define EPILOGUE_USES(REGNO) ((REGNO) == 31) + +/* 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) + + +/* Implement `va_start' for varargs and stdarg. */ +#define EXPAND_BUILTIN_VA_START(valist, nextarg) \ + mips_va_start (valist, nextarg) + +/* Output assembler code to FILE to increment profiler label # LABELNO + for profiling a function entry. */ + +#define FUNCTION_PROFILER(FILE, LABELNO) \ +{ \ + if (TARGET_MIPS16) \ + sorry ("mips16 function profiling"); \ + fprintf (FILE, "\t.set\tnoat\n"); \ + fprintf (FILE, "\tmove\t%s,%s\t\t# save current return address\n", \ + reg_names[GP_REG_FIRST + 1], reg_names[GP_REG_FIRST + 31]); \ + 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); \ + } \ + fprintf (FILE, "\tjal\t_mcount\n"); \ + fprintf (FILE, "\t.set\tat\n"); \ +} + +/* 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 + + +/* A C statement to output, on the stream FILE, assembler code for a + block of data that contains the constant parts of a trampoline. + This code should not include a label--the label is taken care of + automatically. */ + +#define TRAMPOLINE_TEMPLATE(STREAM) \ +{ \ + if (ptr_mode == DImode) \ + fprintf (STREAM, "\t.word\t0x03e0082d\t\t# dmove $1,$31\n"); \ + else \ + fprintf (STREAM, "\t.word\t0x03e00821\t\t# move $1,$31\n"); \ + fprintf (STREAM, "\t.word\t0x04110001\t\t# bgezal $0,.+8\n"); \ + fprintf (STREAM, "\t.word\t0x00000000\t\t# nop\n"); \ + if (ptr_mode == DImode) \ + { \ + fprintf (STREAM, "\t.word\t0xdfe30014\t\t# ld $3,20($31)\n"); \ + fprintf (STREAM, "\t.word\t0xdfe2001c\t\t# ld $2,28($31)\n"); \ + fprintf (STREAM, "\t.word\t0x0060c82d\t\t# dmove $25,$3\n"); \ + } \ + else \ + { \ + fprintf (STREAM, "\t.word\t0x8fe30014\t\t# lw $3,20($31)\n"); \ + fprintf (STREAM, "\t.word\t0x8fe20018\t\t# lw $2,24($31)\n"); \ + fprintf (STREAM, "\t.word\t0x0060c821\t\t# move $25,$3\n"); \ + } \ + fprintf (STREAM, "\t.word\t0x00600008\t\t# jr $3\n"); \ + if (ptr_mode == DImode) \ + { \ + fprintf (STREAM, "\t.word\t0x0020f82d\t\t# dmove $31,$1\n"); \ + fprintf (STREAM, "\t.dword\t0x00000000\t\t# <function address>\n"); \ + fprintf (STREAM, "\t.dword\t0x00000000\t\t# <static chain value>\n"); \ + } \ + else \ + { \ + fprintf (STREAM, "\t.word\t0x0020f821\t\t# move $31,$1\n"); \ + fprintf (STREAM, "\t.word\t0x00000000\t\t# <function address>\n"); \ + fprintf (STREAM, "\t.word\t0x00000000\t\t# <static chain value>\n"); \ + } \ +} + +/* A C expression for the size in bytes of the trampoline, as an + integer. */ + +#define TRAMPOLINE_SIZE (32 + GET_MODE_SIZE (ptr_mode) * 2) + +/* Alignment required for trampolines, in bits. */ + +#define TRAMPOLINE_ALIGNMENT GET_MODE_BITSIZE (ptr_mode) + +/* INITIALIZE_TRAMPOLINE calls this library function to flush + program and data caches. */ + +#ifndef CACHE_FLUSH_FUNC +#define CACHE_FLUSH_FUNC "_flush_cache" +#endif + +/* A C statement to initialize the variable parts of a trampoline. + ADDR is an RTX for the address of the trampoline; FNADDR is an + RTX for the address of the nested function; STATIC_CHAIN is an + RTX for the static chain value that should be passed to the + function when it is called. */ + +#define INITIALIZE_TRAMPOLINE(ADDR, FUNC, CHAIN) \ +{ \ + rtx func_addr, chain_addr; \ + \ + func_addr = plus_constant (ADDR, 32); \ + chain_addr = plus_constant (func_addr, GET_MODE_SIZE (ptr_mode)); \ + emit_move_insn (gen_rtx_MEM (ptr_mode, func_addr), FUNC); \ + emit_move_insn (gen_rtx_MEM (ptr_mode, chain_addr), CHAIN); \ + \ + /* Flush both caches. We need to flush the data cache in case \ + the system has a write-back cache. */ \ + /* ??? Should check the return value for errors. */ \ + if (mips_cache_flush_func && mips_cache_flush_func[0]) \ + emit_library_call (gen_rtx_SYMBOL_REF (Pmode, mips_cache_flush_func), \ + 0, VOIDmode, 3, ADDR, Pmode, \ + GEN_INT (TRAMPOLINE_SIZE), TYPE_MODE (integer_type_node),\ + 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 + +#ifdef REG_OK_STRICT +#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR) \ +{ \ + if (mips_legitimate_address_p (MODE, X, 1)) \ + goto ADDR; \ +} +#else +#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR) \ +{ \ + if (mips_legitimate_address_p (MODE, X, 0)) \ + goto ADDR; \ +} +#endif + +/* 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) && mips_legitimate_address_p (SImode, X, 0)) + +#define LEGITIMATE_CONSTANT_P(X) (mips_const_insns (X) > 0) + +#define LEGITIMIZE_ADDRESS(X,OLDX,MODE,WIN) \ + do { \ + if (mips_legitimize_address (&(X), MODE)) \ + goto WIN; \ + } while (0) + + +/* A C statement or compound statement with a conditional `goto + LABEL;' executed if memory address X (an RTX) can have different + meanings depending on the machine mode of the memory reference it + is used for. + + Autoincrement and autodecrement addresses typically have + mode-dependent effects because the amount of the increment or + decrement is the size of the operand being addressed. Some + machines have other mode-dependent addresses. Many RISC machines + have no mode-dependent addresses. + + You may assume that ADDR is a valid address for the machine. */ + +#define GO_IF_MODE_DEPENDENT_ADDRESS(ADDR,LABEL) {} + +/* 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) + +/* Specify the machine mode that this machine uses + for the index in the tablejump instruction. + ??? Using HImode in mips16 mode can cause overflow. */ +#define CASE_VECTOR_MODE \ + (TARGET_MIPS16 ? HImode : ptr_mode) + +/* Define as C expression which evaluates to nonzero if the tablejump + instruction expects the table to contain offsets from the address of the + table. + Do not define this if the table should contain absolute addresses. */ +#define CASE_VECTOR_PC_RELATIVE (TARGET_MIPS16) + +/* Define this as 1 if `char' should by default be signed; else as 0. */ +#ifndef DEFAULT_SIGNED_CHAR +#define DEFAULT_SIGNED_CHAR 1 +#endif + +/* Max number of bytes we can move from memory to memory + in one reasonably fast instruction. */ +#define MOVE_MAX (TARGET_64BIT ? 8 : 4) +#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. */ +#define SLOW_BYTE_ACCESS 1 + +/* Define this to be nonzero if shift instructions ignore all but the low-order + few bits. */ +#define SHIFT_COUNT_TRUNCATED 1 + +/* 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 + + +/* The cost of loading values from the constant pool. It should be + larger than the cost of any constant we want to synthesize in-line. */ + +#define CONSTANT_POOL_COST COSTS_N_INSNS (8) + +/* A C expression for the cost of moving data from a register in + class FROM to one in class TO. The classes are expressed using + the enumeration values such as `GENERAL_REGS'. A value of 2 is + the default; other values are interpreted relative to that. + + It is not required that the cost always equal 2 when FROM is the + same as TO; on some machines it is expensive to move between + registers if they are not general registers. + + If reload sees an insn consisting of a single `set' between two + hard registers, and if `REGISTER_MOVE_COST' applied to their + classes returns a value of 2, reload does not check to ensure + that the constraints of the insn are met. Setting a cost of + other than 2 will allow reload to verify that the constraints are + met. You should do this if the `movM' pattern's constraints do + not allow such copying. */ + +#define REGISTER_MOVE_COST(MODE, FROM, TO) \ + mips_register_move_cost (MODE, FROM, TO) + +#define MEMORY_MOVE_COST(MODE,CLASS,TO_P) \ + (mips_cost->memory_latency \ + + memory_move_secondary_cost ((MODE), (CLASS), (TO_P))) + +/* 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 mips_cost->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 the asm template for a call. INSN is the instruction's mnemonic + ("j" or "jal"), OPERANDS are its operands, and OPNO is the operand number + of the target. + + When generating -mabicalls 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, OPNO) \ + (TARGET_ABICALLS && !TARGET_EXPLICIT_RELOCS \ + ? "%*" INSN "\t%" #OPNO "%/" \ + : REG_P (OPERANDS[OPNO]) \ + ? "%*" INSN "r\t%" #OPNO "%/" \ + : TARGET_ABICALLS \ + ? (".option\tpic0\n\t" \ + "%*" INSN "\t%" #OPNO "%/\n\t" \ + ".option\tpic2") \ + : "%*" INSN "\t%" #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","", "", "$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 + +/* A C compound statement to output to stdio stream STREAM the + assembler syntax for an instruction operand X. X is an RTL + expression. + + CODE is a value that can be used to specify one of several ways + of printing the operand. It is used when identical operands + must be printed differently depending on the context. CODE + comes from the `%' specification that was used to request + printing of the operand. If the specification was just `%DIGIT' + then CODE is 0; if the specification was `%LTR DIGIT' then CODE + is the ASCII code for LTR. + + If X is a register, this macro should print the register's name. + The names can be found in an array `reg_names' whose type is + `char *[]'. `reg_names' is initialized from `REGISTER_NAMES'. + + When the machine description has a specification `%PUNCT' (a `%' + followed by a punctuation character), this macro is called with + a null pointer for X and the punctuation character for CODE. + + See mips.c for the MIPS specific codes. */ + +#define PRINT_OPERAND(FILE, X, CODE) print_operand (FILE, X, CODE) + +/* A C expression which evaluates to true if CODE is a valid + punctuation character for use in the `PRINT_OPERAND' macro. If + `PRINT_OPERAND_PUNCT_VALID_P' is not defined, it means that no + punctuation characters (except for the standard one, `%') are + used in this way. */ + +#define PRINT_OPERAND_PUNCT_VALID_P(CODE) mips_print_operand_punct[CODE] + +/* A C compound statement to output to stdio stream STREAM the + assembler syntax for an instruction operand that is a memory + reference whose address is ADDR. ADDR is an RTL expression. */ + +#define PRINT_OPERAND_ADDRESS(FILE, ADDR) print_operand_address (FILE, ADDR) + + +/* A C statement, to be executed after all slot-filler instructions + have been output. If necessary, call `dbr_sequence_length' to + determine the number of slots filled in a sequence (zero if not + currently outputting a sequence), to decide how many no-ops to + output, or whatever. + + Don't define this macro if it has nothing to do, but it is + helpful in reading assembly output if the extent of the delay + sequence is made explicit (e.g. with white space). + + Note that output routines for instructions with delay slots must + be prepared to deal with not being output as part of a sequence + (i.e. when the scheduling pass is not run, or when no slot + fillers could be found.) The variable `final_sequence' is null + when not processing a sequence, otherwise it contains the + `sequence' rtx being output. */ + +#define DBR_OUTPUT_SEQEND(STREAM) \ +do \ + { \ + if (set_nomacro > 0 && --set_nomacro == 0) \ + fputs ("\t.set\tmacro\n", STREAM); \ + \ + if (set_noreorder > 0 && --set_noreorder == 0) \ + fputs ("\t.set\treorder\n", STREAM); \ + \ + fputs ("\n", STREAM); \ + } \ +while (0) + + +/* How to tell the debugger about changes of source files. */ +#define ASM_OUTPUT_SOURCE_FILENAME(STREAM, NAME) \ + mips_output_filename (STREAM, NAME) + +/* 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", 0) + +/* 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. */ + +#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) + +#ifndef FUNCTION_NAME_ALREADY_DECLARED +#define FUNCTION_NAME_ALREADY_DECLARED 0 +#endif + +/* 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)) + +/* 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) \ + 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 \ + fprintf (STREAM, "\t%s\t%sL%d\n", \ + ptr_mode == DImode ? ".dword" : ".word", \ + LOCAL_LABEL_PREFIX, VALUE); \ +} while (0) + +/* When generating MIPS16 code, we want the jump table to be in the text + section so that we can load its address using a PC-relative addition. */ +#define JUMP_TABLES_IN_TEXT_SECTION TARGET_MIPS16 + +/* 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(STREAM, STRING, LEN) \ + mips_output_ascii (STREAM, STRING, LEN, "\t.ascii\t") + +/* 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 ? "dsubu" : "subu", \ + 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 \ + { \ + if (! set_noreorder) \ + fprintf (STREAM, "\t.set\tnoreorder\n"); \ + \ + 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]); \ + \ + if (! set_noreorder) \ + fprintf (STREAM, "\t.set\treorder\n"); \ + } \ +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 + +/* Default definitions for size_t and ptrdiff_t. We must override the + definitions from ../svr4.h on mips-*-linux-gnu. */ + +#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") + +#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 diff --git a/contrib/gcc/config/mips/mips.md b/contrib/gcc/config/mips/mips.md new file mode 100644 index 0000000..969d22f --- /dev/null +++ b/contrib/gcc/config/mips/mips.md @@ -0,0 +1,5485 @@ +;; 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 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 2, 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 COPYING. If not, write to +;; the Free Software Foundation, 51 Franklin Street, Fifth Floor, +;; Boston, MA 02110-1301, USA. + +(define_constants + [(UNSPEC_LOAD_DF_LOW 0) + (UNSPEC_LOAD_DF_HIGH 1) + (UNSPEC_STORE_DF_HIGH 2) + (UNSPEC_GET_FNADDR 3) + (UNSPEC_BLOCKAGE 4) + (UNSPEC_CPRESTORE 5) + (UNSPEC_EH_RECEIVER 6) + (UNSPEC_EH_RETURN 7) + (UNSPEC_CONSTTABLE_INT 8) + (UNSPEC_CONSTTABLE_FLOAT 9) + (UNSPEC_ALIGN 14) + (UNSPEC_HIGH 17) + (UNSPEC_LOAD_LEFT 18) + (UNSPEC_LOAD_RIGHT 19) + (UNSPEC_STORE_LEFT 20) + (UNSPEC_STORE_RIGHT 21) + (UNSPEC_LOADGP 22) + (UNSPEC_LOAD_CALL 23) + (UNSPEC_LOAD_GOT 24) + (UNSPEC_GP 25) + (UNSPEC_MFHILO 26) + (UNSPEC_TLS_LDM 27) + (UNSPEC_TLS_GET_TP 28) + + (UNSPEC_ADDRESS_FIRST 100) + + (FAKE_CALL_REGNO 79) + + ;; For MIPS Paired-Singled Floating Point Instructions. + + (UNSPEC_MOVE_TF_PS 200) + (UNSPEC_C 201) + + ;; MIPS64/MIPS32R2 alnv.ps + (UNSPEC_ALNV_PS 202) + + ;; MIPS-3D instructions + (UNSPEC_CABS 203) + + (UNSPEC_ADDR_PS 204) + (UNSPEC_CVT_PW_PS 205) + (UNSPEC_CVT_PS_PW 206) + (UNSPEC_MULR_PS 207) + (UNSPEC_ABS_PS 208) + + (UNSPEC_RSQRT1 209) + (UNSPEC_RSQRT2 210) + (UNSPEC_RECIP1 211) + (UNSPEC_RECIP2 212) + (UNSPEC_SINGLE_CC 213) + (UNSPEC_SCC 214) + + ;; MIPS DSP ASE Revision 0.98 3/24/2005 + (UNSPEC_ADDQ 300) + (UNSPEC_ADDQ_S 301) + (UNSPEC_SUBQ 302) + (UNSPEC_SUBQ_S 303) + (UNSPEC_ADDSC 304) + (UNSPEC_ADDWC 305) + (UNSPEC_MODSUB 306) + (UNSPEC_RADDU_W_QB 307) + (UNSPEC_ABSQ_S 308) + (UNSPEC_PRECRQ_QB_PH 309) + (UNSPEC_PRECRQ_PH_W 310) + (UNSPEC_PRECRQ_RS_PH_W 311) + (UNSPEC_PRECRQU_S_QB_PH 312) + (UNSPEC_PRECEQ_W_PHL 313) + (UNSPEC_PRECEQ_W_PHR 314) + (UNSPEC_PRECEQU_PH_QBL 315) + (UNSPEC_PRECEQU_PH_QBR 316) + (UNSPEC_PRECEQU_PH_QBLA 317) + (UNSPEC_PRECEQU_PH_QBRA 318) + (UNSPEC_PRECEU_PH_QBL 319) + (UNSPEC_PRECEU_PH_QBR 320) + (UNSPEC_PRECEU_PH_QBLA 321) + (UNSPEC_PRECEU_PH_QBRA 322) + (UNSPEC_SHLL 323) + (UNSPEC_SHLL_S 324) + (UNSPEC_SHRL_QB 325) + (UNSPEC_SHRA_PH 326) + (UNSPEC_SHRA_R 327) + (UNSPEC_MULEU_S_PH_QBL 328) + (UNSPEC_MULEU_S_PH_QBR 329) + (UNSPEC_MULQ_RS_PH 330) + (UNSPEC_MULEQ_S_W_PHL 331) + (UNSPEC_MULEQ_S_W_PHR 332) + (UNSPEC_DPAU_H_QBL 333) + (UNSPEC_DPAU_H_QBR 334) + (UNSPEC_DPSU_H_QBL 335) + (UNSPEC_DPSU_H_QBR 336) + (UNSPEC_DPAQ_S_W_PH 337) + (UNSPEC_DPSQ_S_W_PH 338) + (UNSPEC_MULSAQ_S_W_PH 339) + (UNSPEC_DPAQ_SA_L_W 340) + (UNSPEC_DPSQ_SA_L_W 341) + (UNSPEC_MAQ_S_W_PHL 342) + (UNSPEC_MAQ_S_W_PHR 343) + (UNSPEC_MAQ_SA_W_PHL 344) + (UNSPEC_MAQ_SA_W_PHR 345) + (UNSPEC_BITREV 346) + (UNSPEC_INSV 347) + (UNSPEC_REPL_QB 348) + (UNSPEC_REPL_PH 349) + (UNSPEC_CMP_EQ 350) + (UNSPEC_CMP_LT 351) + (UNSPEC_CMP_LE 352) + (UNSPEC_CMPGU_EQ_QB 353) + (UNSPEC_CMPGU_LT_QB 354) + (UNSPEC_CMPGU_LE_QB 355) + (UNSPEC_PICK 356) + (UNSPEC_PACKRL_PH 357) + (UNSPEC_EXTR_W 358) + (UNSPEC_EXTR_R_W 359) + (UNSPEC_EXTR_RS_W 360) + (UNSPEC_EXTR_S_H 361) + (UNSPEC_EXTP 362) + (UNSPEC_EXTPDP 363) + (UNSPEC_SHILO 364) + (UNSPEC_MTHLIP 365) + (UNSPEC_WRDSP 366) + (UNSPEC_RDDSP 367) + ] +) + +(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 o32 and o64 abicalls because it includes an +;; instruction to restore $gp. Direct jals are also macros for -mshared +;; abicalls because they first load the target address into $25. +(define_attr "jal_macro" "no,yes" + (cond [(eq_attr "jal" "direct") + (symbol_ref "TARGET_ABICALLS + && (TARGET_OLDABI || !TARGET_ABSOLUTE_ABICALLS)") + (eq_attr "jal" "indirect") + (symbol_ref "TARGET_ABICALLS && TARGET_OLDABI")] + (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 +;; xfer transfer to/from coprocessor +;; mthilo transfer to hi/lo registers +;; mfhilo transfer from hi/lo registers +;; const load constant +;; arith integer arithmetic and logical instructions +;; shift integer shift instructions +;; slt set less than instructions +;; clz the clz and clo instructions +;; trap trap if instructions +;; imul integer multiply 2 operands +;; imul3 integer multiply 3 operands +;; imadd integer multiply-add +;; idiv integer divide +;; 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 +(define_attr "type" + "unknown,branch,jump,call,load,fpload,fpidxload,store,fpstore,fpidxstore,prefetch,prefetchx,condmove,xfer,mthilo,mfhilo,const,arith,shift,slt,clz,trap,imul,imul3,imadd,idiv,fmove,fadd,fmul,fmadd,fdiv,frdiv,frdiv1,frdiv2,fabs,fneg,fcmp,fcvt,fsqrt,frsqrt,frsqrt1,frsqrt2,multi,nop" + (cond [(eq_attr "jal" "!unset") (const_string "call") + (eq_attr "got" "load") (const_string "load")] + (const_string "unknown"))) + +;; Main data type used by the insn +(define_attr "mode" "unknown,none,QI,HI,SI,DI,SF,DF,FPSW" + (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" + (const_string "no")) + +;; Length of instruction in bytes. +(define_attr "length" "" + (cond [;; Direct branch instructions have a range of [-0x40000,0x3fffc]. + ;; 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: + ;; + ;; where the load address can be up to three instructions long + ;; (lw, nop, addiu). + ;; + ;; 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 1) (pc)) (const_int 131064)) + (le (minus (pc) (match_dup 1)) (const_int 131068))) + (const_int 4) + (ne (symbol_ref "flag_pic") (const_int 0)) + (const_int 24) + ] (const_int 12)) + + (eq_attr "got" "load") + (const_int 4) + (eq_attr "got" "xgot_high") + (const_int 8) + + (eq_attr "type" "const") + (symbol_ref "mips_const_insns (operands[1]) * 4") + (eq_attr "type" "load,fpload") + (symbol_ref "mips_fetch_insns (operands[1]) * 4") + (eq_attr "type" "store,fpstore") + (symbol_ref "mips_fetch_insns (operands[0]) * 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) + + (and (eq_attr "extended_mips16" "yes") + (ne (symbol_ref "TARGET_MIPS16") (const_int 0))) + (const_int 8) + + ;; 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") + (symbol_ref "mips_idiv_insns () * 4") + ] (const_int 4))) + +;; Attribute describing the processor. This attribute must match exactly +;; with the processor_type enumeration in mips.h. +(define_attr "cpu" + "r3000,4kc,4kp,5kc,5kf,20kc,24k,24kx,m4k,r3900,r6000,r4000,r4100,r4111,r4120,r4130,r4300,r4600,r4650,r5000,r5400,r5500,r7000,r8000,r9000,sb1,sb1a,sr71000" + (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" "xfer") + (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)")) + +;; 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" + (const + (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 macro allows 32-bit and 64-bit GPR patterns to be generated +;; from the same template. +(define_mode_macro GPR [SI (DI "TARGET_64BIT")]) + +;; This mode macro allows :P to be used for patterns that operate on +;; pointer-sized quantities. Exactly one of the two alternatives will match. +(define_mode_macro P [(SI "Pmode == SImode") (DI "Pmode == DImode")]) + +;; This mode macro allows :MOVECC to be used anywhere that a +;; conditional-move-type condition is needed. +(define_mode_macro MOVECC [SI (DI "TARGET_64BIT") (CC "TARGET_HARD_FLOAT")]) + +;; This mode macro allows the QI and HI extension patterns to be defined from +;; the same template. +(define_mode_macro SHORT [QI HI]) + +;; This mode macro allows :ANYF to be used wherever a scalar or vector +;; floating-point mode is allowed. +(define_mode_macro ANYF [(SF "TARGET_HARD_FLOAT") + (DF "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT") + (V2SF "TARGET_PAIRED_SINGLE_FLOAT")]) + +;; Like ANYF, but only applies to scalar modes. +(define_mode_macro SCALARF [(SF "TARGET_HARD_FLOAT") + (DF "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT")]) + +;; 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")]) + +;; 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 and stores. +(define_mode_attr load [(SI "lw") (DI "ld")]) +(define_mode_attr store [(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 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 condition for which sqrt instructions exist. +(define_mode_attr sqrt_condition + [(SF "!ISA_MIPS1") (DF "!ISA_MIPS1") (V2SF "TARGET_SB1")]) + +; This attribute gives the condition for which recip and rsqrt instructions +; exist. +(define_mode_attr recip_condition + [(SF "ISA_HAS_FP4") (DF "ISA_HAS_FP4") (V2SF "TARGET_SB1")]) + +;; This code macro allows all branch instructions to be generated from +;; a single define_expand template. +(define_code_macro any_cond [unordered ordered unlt unge uneq ltgt unle ungt + eq ne gt ge lt le gtu geu ltu leu]) + +;; This code macro allows signed and unsigned widening multiplications +;; to use the same template. +(define_code_macro any_extend [sign_extend zero_extend]) + +;; This code macro allows the three shift instructions to be generated +;; from the same template. +(define_code_macro any_shift [ashift ashiftrt lshiftrt]) + +;; This code macro allows all native floating-point comparisons to be +;; generated from the same template. +(define_code_macro fcond [unordered uneq unlt unle eq lt le]) + +;; This code macro is used for comparisons that can be implemented +;; by swapping the operands. +(define_code_macro swapped_fcond [ge gt unge ungt]) + +;; <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")]) + +;; <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")]) + +;; <insn> expands to the name of the insn that implements a particular code. +(define_code_attr insn [(ashift "sll") + (ashiftrt "sra") + (lshiftrt "srl")]) + +;; <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")]) + +;; ......................... +;; +;; Branch, call and jump delay slots +;; +;; ......................... + +(define_delay (and (eq_attr "type" "branch") + (eq (symbol_ref "TARGET_MIPS16") (const_int 0))) + [(eq_attr "can_delay" "yes") + (nil) + (and (eq_attr "branch_likely" "yes") + (eq_attr "can_delay" "yes"))]) + +(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") + +(include "4k.md") +(include "5k.md") +(include "24k.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 "sb1.md") +(include "sr71k.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 "conditional_trap" + [(trap_if (match_operator 0 "comparison_operator" + [(match_dup 2) (match_dup 3)]) + (match_operand 1 "const_int_operand"))] + "ISA_HAS_COND_TRAP" +{ + if (GET_MODE_CLASS (GET_MODE (cmp_operands[0])) == MODE_INT + && operands[1] == const0_rtx) + { + mips_gen_conditional_trap (operands); + DONE; + } + else + FAIL; +}) + +(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 "type" "arith") + (set_attr "mode" "<MODE>")]) + +;; We need to recognize MIPS16 stack pointer additions explicitly, since +;; we don't have a constraint for $sp. These insns will be generated by +;; the save_restore_insns functions. + +(define_insn "*add<mode>3_sp1" + [(set (reg:GPR 29) + (plus:GPR (reg:GPR 29) + (match_operand:GPR 0 "const_arith_operand" "")))] + "TARGET_MIPS16" + "<d>addiu\t%$,%$,%0" + [(set_attr "type" "arith") + (set_attr "mode" "<MODE>") + (set (attr "length") (if_then_else (match_operand 0 "m16_simm8_8") + (const_int 4) + (const_int 8)))]) + +(define_insn "*add<mode>3_sp2" + [(set (match_operand:GPR 0 "register_operand" "=d") + (plus:GPR (reg:GPR 29) + (match_operand:GPR 1 "const_arith_operand" "")))] + "TARGET_MIPS16" + "<d>addiu\t%0,%$,%1" + [(set_attr "type" "arith") + (set_attr "mode" "<MODE>") + (set (attr "length") (if_then_else (match_operand 1 "m16_uimm<si8_di5>_4") + (const_int 4) + (const_int 8)))]) + +(define_insn "*add<mode>3_mips16" + [(set (match_operand:GPR 0 "register_operand" "=d,d,d") + (plus:GPR (match_operand:GPR 1 "register_operand" "0,d,d") + (match_operand:GPR 2 "arith_operand" "Q,O,d")))] + "TARGET_MIPS16" + "@ + <d>addiu\t%0,%2 + <d>addiu\t%0,%1,%2 + <d>addu\t%0,%1,%2" + [(set_attr "type" "arith") + (set_attr "mode" "<MODE>") + (set_attr_alternative "length" + [(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 "register_operand") + (plus:SI (match_dup 0) + (match_operand:SI 1 "const_int_operand")))] + "TARGET_MIPS16 && reload_completed && !TARGET_DEBUG_D_MODE + && REG_P (operands[0]) + && M16_REG_P (REGNO (operands[0])) + && GET_CODE (operands[1]) == CONST_INT + && ((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 "register_operand") + (plus:SI (match_operand:SI 1 "register_operand") + (match_operand:SI 2 "const_int_operand")))] + "TARGET_MIPS16 && reload_completed && !TARGET_DEBUG_D_MODE + && REG_P (operands[0]) + && M16_REG_P (REGNO (operands[0])) + && REG_P (operands[1]) + && M16_REG_P (REGNO (operands[1])) + && REGNO (operands[0]) != REGNO (operands[1]) + && GET_CODE (operands[2]) == CONST_INT + && ((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 "register_operand") + (plus:DI (match_dup 0) + (match_operand:DI 1 "const_int_operand")))] + "TARGET_MIPS16 && TARGET_64BIT && reload_completed && !TARGET_DEBUG_D_MODE + && REG_P (operands[0]) + && M16_REG_P (REGNO (operands[0])) + && GET_CODE (operands[1]) == CONST_INT + && ((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 "register_operand") + (plus:DI (match_operand:DI 1 "register_operand") + (match_operand:DI 2 "const_int_operand")))] + "TARGET_MIPS16 && TARGET_64BIT && reload_completed && !TARGET_DEBUG_D_MODE + && REG_P (operands[0]) + && M16_REG_P (REGNO (operands[0])) + && REG_P (operands[1]) + && M16_REG_P (REGNO (operands[1])) + && REGNO (operands[0]) != REGNO (operands[1]) + && GET_CODE (operands[2]) == CONST_INT + && ((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 "type" "arith") + (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 "type" "arith") + (set_attr "mode" "SI") + (set_attr "extended_mips16" "yes")]) + +;; +;; .................... +;; +;; 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 "type" "arith") + (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 "type" "arith") + (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_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 (GENERATE_MULT3_<MODE>) + emit_insn (gen_mul<mode>3_mult3 (operands[0], operands[1], operands[2])); + else if (!TARGET_FIX_R4000) + emit_insn (gen_mul<mode>3_internal (operands[0], operands[1], + operands[2])); + else + emit_insn (gen_mul<mode>3_r4000 (operands[0], operands[1], operands[2])); + DONE; +}) + +(define_insn "mulsi3_mult3" + [(set (match_operand:SI 0 "register_operand" "=d,l") + (mult:SI (match_operand:SI 1 "register_operand" "d,d") + (match_operand:SI 2 "register_operand" "d,d"))) + (clobber (match_scratch:SI 3 "=h,h")) + (clobber (match_scratch:SI 4 "=l,X"))] + "GENERATE_MULT3_SI" +{ + if (which_alternative == 1) + return "mult\t%1,%2"; + if (TARGET_MAD + || TARGET_MIPS5400 + || TARGET_MIPS5500 + || TARGET_MIPS7000 + || TARGET_MIPS9000 + || ISA_MIPS32 + || ISA_MIPS32R2 + || ISA_MIPS64) + return "mul\t%0,%1,%2"; + return "mult\t%0,%1,%2"; +} + [(set_attr "type" "imul3,imul") + (set_attr "mode" "SI")]) + +(define_insn "muldi3_mult3" + [(set (match_operand:DI 0 "register_operand" "=d") + (mult:DI (match_operand:DI 1 "register_operand" "d") + (match_operand:DI 2 "register_operand" "d"))) + (clobber (match_scratch:DI 3 "=h")) + (clobber (match_scratch:DI 4 "=l"))] + "TARGET_64BIT && GENERATE_MULT3_DI" + "dmult\t%0,%1,%2" + [(set_attr "type" "imul3") + (set_attr "mode" "DI")]) + +;; 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: HI +;; Operand 4: GPR (destination) +(define_peephole2 + [(parallel + [(set (match_operand:SI 0 "register_operand") + (mult:SI (match_operand:SI 1 "register_operand") + (match_operand:SI 2 "register_operand"))) + (clobber (match_operand:SI 3 "register_operand")) + (clobber (scratch:SI))]) + (set (match_operand:SI 4 "register_operand") + (unspec [(match_dup 0) (match_dup 3)] UNSPEC_MFHILO))] + "GENERATE_MULT3_SI && peep2_reg_dead_p (2, operands[0])" + [(parallel + [(set (match_dup 4) + (mult:SI (match_dup 1) + (match_dup 2))) + (clobber (match_dup 3)) + (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"))) + (clobber (match_scratch:GPR 3 "=h"))] + "!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 "=h")) + (clobber (match_scratch:GPR 4 "=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: HI +;; Operand 4: GPR (destination) +(define_peephole2 + [(parallel + [(set (match_operand:SI 0 "register_operand") + (mult:SI (match_operand:SI 1 "register_operand") + (match_operand:SI 2 "register_operand"))) + (clobber (match_operand:SI 3 "register_operand"))]) + (set (match_operand:SI 4 "register_operand") + (unspec:SI [(match_dup 0) (match_dup 3)] UNSPEC_MFHILO))] + "ISA_HAS_MACC && !GENERATE_MULT3_SI" + [(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 4) + (plus:SI (mult:SI (match_dup 1) + (match_dup 2)) + (match_dup 0))) + (clobber (match_dup 3))])]) + +;; Multiply-accumulate patterns + +;; For processors that can copy the output to a general register: +;; +;; The all-d alternative is needed because the combiner will find this +;; pattern and then register alloc/reload will move registers around to +;; make them fit, and we don't want to trigger unnecessary loads to LO. +;; +;; The last alternative should be made slightly less desirable, but adding +;; "?" to the constraint is too strong, and causes values to be loaded into +;; LO even when that's more costly. For now, using "*d" mostly does the +;; trick. +(define_insn "*mul_acc_si" + [(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 "=h,h,h")) + (clobber (match_scratch:SI 5 "=X,3,l")) + (clobber (match_scratch:SI 6 "=X,X,&d"))] + "(TARGET_MIPS3900 + || ISA_HAS_MADD_MSUB) + && !TARGET_MIPS16" +{ + static const char *const madd[] = { "madd\t%1,%2", "madd\t%0,%1,%2" }; + if (which_alternative == 2) + return "#"; + if (ISA_HAS_MADD_MSUB && which_alternative != 0) + return "#"; + return madd[which_alternative]; +} + [(set_attr "type" "imadd,imadd,multi") + (set_attr "mode" "SI") + (set_attr "length" "4,4,8")]) + +;; Split the above insn if we failed to get LO allocated. +(define_split + [(set (match_operand:SI 0 "register_operand") + (plus:SI (mult:SI (match_operand:SI 1 "register_operand") + (match_operand:SI 2 "register_operand")) + (match_operand:SI 3 "register_operand"))) + (clobber (match_scratch:SI 4)) + (clobber (match_scratch:SI 5)) + (clobber (match_scratch:SI 6))] + "reload_completed && !TARGET_DEBUG_D_MODE + && GP_REG_P (true_regnum (operands[0])) + && GP_REG_P (true_regnum (operands[3]))" + [(parallel [(set (match_dup 6) + (mult:SI (match_dup 1) (match_dup 2))) + (clobber (match_dup 4)) + (clobber (match_dup 5))]) + (set (match_dup 0) (plus:SI (match_dup 6) (match_dup 3)))] + "") + +;; Splitter to copy result of MADD to a general register +(define_split + [(set (match_operand:SI 0 "register_operand") + (plus:SI (mult:SI (match_operand:SI 1 "register_operand") + (match_operand:SI 2 "register_operand")) + (match_operand:SI 3 "register_operand"))) + (clobber (match_scratch:SI 4)) + (clobber (match_scratch:SI 5)) + (clobber (match_scratch:SI 6))] + "reload_completed && !TARGET_DEBUG_D_MODE + && GP_REG_P (true_regnum (operands[0])) + && true_regnum (operands[3]) == LO_REGNUM" + [(parallel [(set (match_dup 3) + (plus:SI (mult:SI (match_dup 1) (match_dup 2)) + (match_dup 3))) + (clobber (match_dup 4)) + (clobber (match_dup 5)) + (clobber (match_dup 6))]) + (set (match_dup 0) (unspec:SI [(match_dup 5) (match_dup 4)] UNSPEC_MFHILO))] + "") + +(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 "=h,h")) + (clobber (match_scratch:SI 5 "=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 "=h,h")) + (clobber (match_scratch:SI 5 "=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 "=h,h")) + (clobber (match_scratch:SI 5 "=X,1")) + (clobber (match_scratch:SI 6 "=d,d"))] + "ISA_HAS_MACC && !ISA_HAS_MSAC" + "#" + "&& reload_completed" + [(set (match_dup 6) + (neg:SI (match_dup 3))) + (parallel + [(set (match_dup 0) + (plus:SI (mult:SI (match_dup 2) + (match_dup 6)) + (match_dup 1))) + (clobber (match_dup 4)) + (clobber (match_dup 5))])] + "" + [(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))) + (clobber (match_scratch:SI 4 "=h"))] + "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)))) + (clobber (match_scratch:SI 4 "=h"))] + "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: HI +;; Operand 3: GPR (destination) +(define_peephole2 + [(parallel + [(set (match_operand:SI 0 "register_operand") + (match_operand:SI 1 "macc_msac_operand")) + (clobber (match_operand:SI 2 "register_operand")) + (clobber (scratch:SI))]) + (set (match_operand:SI 3 "register_operand") + (unspec:SI [(match_dup 0) (match_dup 2)] UNSPEC_MFHILO))] + "" + [(parallel [(set (match_dup 0) + (match_dup 1)) + (set (match_dup 3) + (match_dup 1)) + (clobber (match_dup 2))])] + "") + +;; 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: HI +;; Operand 6: new multiplication +;; Operand 7: new addition/subtraction +(define_peephole2 + [(match_scratch:SI 0 "d") + (set (match_operand:SI 1 "register_operand") + (match_operand:SI 2 "register_operand")) + (match_dup 0) + (parallel + [(set (match_operand:SI 3 "register_operand") + (match_operand:SI 4 "macc_msac_operand")) + (clobber (match_operand:SI 5 "register_operand")) + (clobber (match_dup 1))])] + "GENERATE_MULT3_SI + && true_regnum (operands[1]) == LO_REGNUM + && peep2_reg_dead_p (2, operands[1]) + && GP_REG_P (true_regnum (operands[3]))" + [(parallel [(set (match_dup 0) + (match_dup 6)) + (clobber (match_dup 5)) + (clobber (match_dup 1))]) + (set (match_dup 3) + (match_dup 7))] +{ + operands[6] = XEXP (operands[4], GET_CODE (operands[4]) == PLUS ? 0 : 1); + operands[7] = 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: HI +;; Operand 5: GPR (destination) +;; Operand 6: new multiplication +;; Operand 7: new addition/subtraction +(define_peephole2 + [(match_scratch:SI 0 "d") + (set (match_operand:SI 1 "register_operand") + (match_operand:SI 2 "register_operand")) + (match_dup 0) + (parallel + [(set (match_dup 1) + (match_operand:SI 3 "macc_msac_operand")) + (clobber (match_operand:SI 4 "register_operand")) + (clobber (scratch:SI))]) + (match_dup 0) + (set (match_operand:SI 5 "register_operand") + (unspec:SI [(match_dup 1) (match_dup 4)] UNSPEC_MFHILO))] + "GENERATE_MULT3_SI && peep2_reg_dead_p (3, operands[1])" + [(parallel [(set (match_dup 0) + (match_dup 6)) + (clobber (match_dup 4)) + (clobber (match_dup 1))]) + (set (match_dup 5) + (match_dup 7))] +{ + operands[6] = XEXP (operands[4], GET_CODE (operands[4]) == PLUS ? 0 : 1); + operands[7] = gen_rtx_fmt_ee (GET_CODE (operands[4]), SImode, + operands[2], operands[0]); +}) + +(define_insn "*mul_sub_si" + [(set (match_operand:SI 0 "register_operand" "=l,*d,*d") + (minus:SI (match_operand:SI 1 "register_operand" "0,l,*d") + (mult:SI (match_operand:SI 2 "register_operand" "d,d,d") + (match_operand:SI 3 "register_operand" "d,d,d")))) + (clobber (match_scratch:SI 4 "=h,h,h")) + (clobber (match_scratch:SI 5 "=X,1,l")) + (clobber (match_scratch:SI 6 "=X,X,&d"))] + "ISA_HAS_MADD_MSUB" + "@ + msub\t%2,%3 + # + #" + [(set_attr "type" "imadd,multi,multi") + (set_attr "mode" "SI") + (set_attr "length" "4,8,8")]) + +;; Split the above insn if we failed to get LO allocated. +(define_split + [(set (match_operand:SI 0 "register_operand") + (minus:SI (match_operand:SI 1 "register_operand") + (mult:SI (match_operand:SI 2 "register_operand") + (match_operand:SI 3 "register_operand")))) + (clobber (match_scratch:SI 4)) + (clobber (match_scratch:SI 5)) + (clobber (match_scratch:SI 6))] + "reload_completed && !TARGET_DEBUG_D_MODE + && GP_REG_P (true_regnum (operands[0])) + && GP_REG_P (true_regnum (operands[1]))" + [(parallel [(set (match_dup 6) + (mult:SI (match_dup 2) (match_dup 3))) + (clobber (match_dup 4)) + (clobber (match_dup 5))]) + (set (match_dup 0) (minus:SI (match_dup 1) (match_dup 6)))] + "") + +;; Splitter to copy result of MSUB to a general register +(define_split + [(set (match_operand:SI 0 "register_operand") + (minus:SI (match_operand:SI 1 "register_operand") + (mult:SI (match_operand:SI 2 "register_operand") + (match_operand:SI 3 "register_operand")))) + (clobber (match_scratch:SI 4)) + (clobber (match_scratch:SI 5)) + (clobber (match_scratch:SI 6))] + "reload_completed && !TARGET_DEBUG_D_MODE + && GP_REG_P (true_regnum (operands[0])) + && true_regnum (operands[1]) == LO_REGNUM" + [(parallel [(set (match_dup 1) + (minus:SI (match_dup 1) + (mult:SI (match_dup 2) (match_dup 3)))) + (clobber (match_dup 4)) + (clobber (match_dup 5)) + (clobber (match_dup 6))]) + (set (match_dup 0) (unspec:SI [(match_dup 5) (match_dup 4)] UNSPEC_MFHILO))] + "") + +(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 "=h,h")) + (clobber (match_scratch:SI 4 "=X,l"))] + "ISA_HAS_MULS" + "@ + muls\t$0,%1,%2 + muls\t%0,%1,%2" + [(set_attr "type" "imul,imul3") + (set_attr "mode" "SI")]) + +;; ??? We could define a mulditi3 pattern when TARGET_64BIT. + +(define_expand "<u>mulsidi3" + [(parallel + [(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")))) + (clobber (scratch:DI)) + (clobber (scratch:DI)) + (clobber (scratch:DI))])] + "!TARGET_64BIT || !TARGET_FIX_R4000" +{ + if (!TARGET_64BIT) + { + if (!TARGET_FIX_R4000) + emit_insn (gen_<u>mulsidi3_32bit_internal (operands[0], operands[1], + operands[2])); + else + emit_insn (gen_<u>mulsidi3_32bit_r4000 (operands[0], operands[1], + operands[2])); + DONE; + } +}) + +(define_insn "<u>mulsidi3_32bit_internal" + [(set (match_operand:DI 0 "register_operand" "=x") + (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" + "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_and_split "*<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:DI 3 "=l")) + (clobber (match_scratch:DI 4 "=h")) + (clobber (match_scratch:DI 5 "=d"))] + "TARGET_64BIT && !TARGET_FIX_R4000" + "#" + "&& reload_completed" + [(parallel + [(set (match_dup 3) + (sign_extend:DI + (mult:SI (match_dup 1) + (match_dup 2)))) + (set (match_dup 4) + (ashiftrt:DI + (mult:DI (any_extend:DI (match_dup 1)) + (any_extend:DI (match_dup 2))) + (const_int 32)))]) + + ;; OP5 <- LO, OP0 <- HI + (set (match_dup 5) (unspec:DI [(match_dup 3) (match_dup 4)] UNSPEC_MFHILO)) + (set (match_dup 0) (unspec:DI [(match_dup 4) (match_dup 3)] UNSPEC_MFHILO)) + + ;; Zero-extend OP5. + (set (match_dup 5) + (ashift:DI (match_dup 5) + (const_int 32))) + (set (match_dup 5) + (lshiftrt:DI (match_dup 5) + (const_int 32))) + + ;; Shift OP0 into place. + (set (match_dup 0) + (ashift:DI (match_dup 0) + (const_int 32))) + + ;; OR the two halves together + (set (match_dup 0) + (ior:DI (match_dup 0) + (match_dup 5)))] + "" + [(set_attr "type" "imul") + (set_attr "mode" "SI") + (set_attr "length" "24")]) + +(define_insn "*<u>mulsidi3_64bit_parts" + [(set (match_operand:DI 0 "register_operand" "=l") + (sign_extend:DI + (mult:SI (match_operand:SI 2 "register_operand" "d") + (match_operand:SI 3 "register_operand" "d")))) + (set (match_operand:DI 1 "register_operand" "=h") + (ashiftrt:DI + (mult:DI (any_extend:DI (match_dup 2)) + (any_extend:DI (match_dup 3))) + (const_int 32)))] + "TARGET_64BIT && !TARGET_FIX_R4000" + "mult<u>\t%2,%3" + [(set_attr "type" "imul") + (set_attr "mode" "SI")]) + +;; 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")]) + +(define_insn "*msac<u>_di" + [(set (match_operand:DI 0 "register_operand" "=x") + (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" +{ + if (TARGET_MIPS5500) + 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))))] + "ISA_HAS_MULHI || !TARGET_FIX_R4000" +{ + 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 "<su>mulsi3_highpart_internal" + [(set (match_operand:SI 0 "register_operand" "=h") + (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 && !TARGET_FIX_R4000" + "mult<u>\t%1,%2" + [(set_attr "type" "imul") + (set_attr "mode" "SI")]) + +(define_insn "<su>mulsi3_highpart_mulhi_internal" + [(set (match_operand:SI 0 "register_operand" "=h,d") + (truncate:SI + (lshiftrt:DI + (mult:DI + (any_extend:DI (match_operand:SI 1 "register_operand" "d,d")) + (any_extend:DI (match_operand:SI 2 "register_operand" "d,d"))) + (const_int 32)))) + (clobber (match_scratch:SI 3 "=l,l")) + (clobber (match_scratch:SI 4 "=X,h"))] + "ISA_HAS_MULHI" + "@ + mult<u>\t%1,%2 + mulhi<u>\t%0,%1,%2" + [(set_attr "type" "imul,imul3") + (set_attr "mode" "SI")]) + +(define_insn "*<su>mulsi3_highpart_neg_mulhi_internal" + [(set (match_operand:SI 0 "register_operand" "=h,d") + (truncate:SI + (lshiftrt:DI + (neg:DI + (mult:DI + (any_extend:DI (match_operand:SI 1 "register_operand" "d,d")) + (any_extend:DI (match_operand:SI 2 "register_operand" "d,d")))) + (const_int 32)))) + (clobber (match_scratch:SI 3 "=l,l")) + (clobber (match_scratch:SI 4 "=X,h"))] + "ISA_HAS_MULHI" + "@ + mulshi<u>\t%.,%1,%2 + mulshi<u>\t%0,%1,%2" + [(set_attr "type" "imul,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 "<su>muldi3_highpart" + [(set (match_operand:DI 0 "register_operand" "=h") + (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 && !TARGET_FIX_R4000 + && !(<CODE> == ZERO_EXTEND && TARGET_FIX_VR4120)" + "dmult<u>\t%1,%2" + [(set_attr "type" "imul") + (set_attr "mode" "DI")]) + +;; 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))) + (clobber (match_scratch:SI 3 "=h"))] + "TARGET_MAD" + "mad\t%1,%2" + [(set_attr "type" "imadd") + (set_attr "mode" "SI")]) + +(define_insn "*<su>mul_acc_di" + [(set (match_operand:DI 0 "register_operand" "=x") + (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) + && !TARGET_64BIT" +{ + if (TARGET_MAD) + return "mad<u>\t%1,%2"; + else if (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 "*madd<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_FP4 && TARGET_FUSED_MADD" + "madd.<fmt>\t%0,%3,%1,%2" + [(set_attr "type" "fmadd") + (set_attr "mode" "<UNITMODE>")]) + +(define_insn "*msub<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_FP4 && TARGET_FUSED_MADD" + "msub.<fmt>\t%0,%3,%1,%2" + [(set_attr "type" "fmadd") + (set_attr "mode" "<UNITMODE>")]) + +(define_insn "*nmadd<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_NMADD_NMSUB && 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 "*nmadd<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_NMADD_NMSUB && 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 "*nmsub<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_NMADD_NMSUB && 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 "*nmsub<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_NMADD_NMSUB && 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>")]) + +;; +;; .................... +;; +;; 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 (!(ISA_HAS_FP4 && 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 "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" "=h") + (mod:GPR (match_dup 1) + (match_dup 2)))] + "!TARGET_FIX_VR4120" + { return mips_output_division ("<d>div\t$0,%1,%2", operands); } + [(set_attr "type" "idiv") + (set_attr "mode" "<MODE>")]) + +(define_insn "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" "=h") + (umod:GPR (match_dup 1) + (match_dup 2)))] + "" + { return mips_output_division ("<d>divu\t$0,%1,%2", operands); } + [(set_attr "type" "idiv") + (set_attr "mode" "<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>")]) + +;; +;; .................... +;; +;; 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 "type" "arith") + (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 "type" "arith") + (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 "type" "arith") + (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 "uns_arith_operand")))] + "" +{ + if (TARGET_MIPS16) + operands[2] = force_reg (<MODE>mode, operands[2]); +}) + +(define_insn "*and<mode>3" + [(set (match_operand:GPR 0 "register_operand" "=d,d") + (and:GPR (match_operand:GPR 1 "register_operand" "%d,d") + (match_operand:GPR 2 "uns_arith_operand" "d,K")))] + "!TARGET_MIPS16" + "@ + and\t%0,%1,%2 + andi\t%0,%1,%x2" + [(set_attr "type" "arith") + (set_attr "mode" "<MODE>")]) + +(define_insn "*and<mode>3_mips16" + [(set (match_operand:GPR 0 "register_operand" "=d") + (and:GPR (match_operand:GPR 1 "register_operand" "%0") + (match_operand:GPR 2 "register_operand" "d")))] + "TARGET_MIPS16" + "and\t%0,%2" + [(set_attr "type" "arith") + (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 "type" "arith") + (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 "type" "arith") + (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 "type" "arith") + (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 "type" "arith") + (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 "type" "arith") + (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 "truncdisi2" + [(set (match_operand:SI 0 "nonimmediate_operand" "=d,m") + (truncate:SI (match_operand:DI 1 "register_operand" "d,d")))] + "TARGET_64BIT" + "@ + sll\t%0,%1,0 + sw\t%1,%0" + [(set_attr "type" "shift,store") + (set_attr "mode" "SI") + (set_attr "extended_mips16" "yes,*")]) + +(define_insn "truncdihi2" + [(set (match_operand:HI 0 "nonimmediate_operand" "=d,m") + (truncate:HI (match_operand:DI 1 "register_operand" "d,d")))] + "TARGET_64BIT" + "@ + sll\t%0,%1,0 + sh\t%1,%0" + [(set_attr "type" "shift,store") + (set_attr "mode" "SI") + (set_attr "extended_mips16" "yes,*")]) + +(define_insn "truncdiqi2" + [(set (match_operand:QI 0 "nonimmediate_operand" "=d,m") + (truncate:QI (match_operand:DI 1 "register_operand" "d,d")))] + "TARGET_64BIT" + "@ + sll\t%0,%1,0 + sb\t%1,%0" + [(set_attr "type" "shift,store") + (set_attr "mode" "SI") + (set_attr "extended_mips16" "yes,*")]) + +;; Combiner patterns to optimize shift/truncate combinations. + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=d") + (truncate:SI + (ashiftrt:DI (match_operand:DI 1 "register_operand" "d") + (match_operand:DI 2 "const_arith_operand" ""))))] + "TARGET_64BIT && !TARGET_MIPS16 && INTVAL (operands[2]) >= 32" + "dsra\t%0,%1,%2" + [(set_attr "type" "shift") + (set_attr "mode" "SI")]) + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=d") + (truncate:SI (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" "SI")]) + + +;; Combiner patterns for truncate/sign_extend combinations. They use +;; the shift/truncate patterns above. + +(define_insn_and_split "" + [(set (match_operand:SI 0 "register_operand" "=d") + (sign_extend:SI + (truncate:HI (match_operand:DI 1 "register_operand" "d"))))] + "TARGET_64BIT && !TARGET_MIPS16" + "#" + "&& reload_completed" + [(set (match_dup 2) + (ashift:DI (match_dup 1) + (const_int 48))) + (set (match_dup 0) + (truncate:SI (ashiftrt:DI (match_dup 2) + (const_int 48))))] + { operands[2] = gen_lowpart (DImode, operands[0]); }) + +(define_insn_and_split "" + [(set (match_operand:SI 0 "register_operand" "=d") + (sign_extend:SI + (truncate:QI (match_operand:DI 1 "register_operand" "d"))))] + "TARGET_64BIT && !TARGET_MIPS16" + "#" + "&& reload_completed" + [(set (match_dup 2) + (ashift:DI (match_dup 1) + (const_int 56))) + (set (match_dup 0) + (truncate:SI (ashiftrt:DI (match_dup 2) + (const_int 56))))] + { operands[2] = gen_lowpart (DImode, operands[0]); }) + + +;; Combiner patterns to optimize truncate/zero_extend combinations. + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=d") + (zero_extend:SI (truncate:HI + (match_operand:DI 1 "register_operand" "d"))))] + "TARGET_64BIT && !TARGET_MIPS16" + "andi\t%0,%1,0xffff" + [(set_attr "type" "arith") + (set_attr "mode" "SI")]) + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=d") + (zero_extend:SI (truncate:QI + (match_operand:DI 1 "register_operand" "d"))))] + "TARGET_64BIT && !TARGET_MIPS16" + "andi\t%0,%1,0xff" + [(set_attr "type" "arith") + (set_attr "mode" "SI")]) + +(define_insn "" + [(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 "type" "arith") + (set_attr "mode" "HI")]) + +;; +;; .................... +;; +;; ZERO EXTENSION +;; +;; .................... + +;; Extension insns. + +(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" + "@ + # + 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 "type" "multi,load") + (set_attr "mode" "DI") + (set_attr "length" "8,*")]) + +;; Combine is not allowed to convert this insn into a zero_extendsidi2 +;; because of TRULY_NOOP_TRUNCATION. + +(define_insn_and_split "*clear_upper32" + [(set (match_operand:DI 0 "register_operand" "=d,d") + (and:DI (match_operand:DI 1 "nonimmediate_operand" "d,o") + (const_int 4294967295)))] + "TARGET_64BIT" +{ + if (which_alternative == 0) + return "#"; + + operands[1] = gen_lowpart (SImode, operands[1]); + return "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)))] + "" + [(set_attr "type" "multi,load") + (set_attr "mode" "DI") + (set_attr "length" "8,*")]) + +(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 "type" "arith,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" + [(set_attr "type" "arith") + (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 "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 "type" "arith,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 "type" "load") + (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 "type" "arith,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 "type" "arith,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 "type" "arith,load") + (set_attr "mode" "<GPR:MODE>") + (set_attr "length" "8,*")]) + +(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 "type" "arith,load") + (set_attr "mode" "<GPR:MODE>")]) + +;; This pattern generates the same code as extendqisi2; split it into +;; that form after reload. +(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")))] + "" + "#" + "reload_completed" + [(set (match_dup 0) (sign_extend:SI (match_dup 1)))] + { operands[0] = gen_lowpart (SImode, operands[0]); } + [(set_attr "type" "arith,load") + (set_attr "mode" "SI") + (set_attr "length" "8,*")]) + +(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") + (set_attr "length" "4")]) + +(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 (set_nomacro) + 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") + (set_attr "length" "4")]) + +(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 (set_nomacro) + 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") + (set_attr "length" "4")]) + + +(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") + (set_attr "length" "4")]) + + +(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") + (set_attr "length" "4")]) + + +(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") + (set_attr "length" "4")]) + + +(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") + (set_attr "length" "4")]) + + +(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") + (set_attr "length" "4")]) + + +(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 (); + REAL_VALUE_TYPE offset; + + real_2expN (&offset, 31); + + if (reg1) /* Turn off complaints about unreached code. */ + { + emit_move_insn (reg1, CONST_DOUBLE_FROM_REAL_VALUE (offset, DFmode)); + do_pending_stack_adjust (); + + emit_insn (gen_cmpdf (operands[1], reg1)); + emit_jump_insn (gen_bge (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); + emit_move_insn (reg2, gen_rtx_MINUS (DFmode, operands[1], reg1)); + emit_move_insn (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_insn (gen_rtx_USE (VOIDmode, 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 (); + REAL_VALUE_TYPE offset; + + real_2expN (&offset, 63); + + emit_move_insn (reg1, CONST_DOUBLE_FROM_REAL_VALUE (offset, DFmode)); + do_pending_stack_adjust (); + + emit_insn (gen_cmpdf (operands[1], reg1)); + emit_jump_insn (gen_bge (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); + emit_move_insn (reg2, gen_rtx_MINUS (DFmode, operands[1], reg1)); + emit_move_insn (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_insn (gen_rtx_USE (VOIDmode, 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 (); + REAL_VALUE_TYPE offset; + + real_2expN (&offset, 31); + + emit_move_insn (reg1, CONST_DOUBLE_FROM_REAL_VALUE (offset, SFmode)); + do_pending_stack_adjust (); + + emit_insn (gen_cmpsf (operands[1], reg1)); + emit_jump_insn (gen_bge (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); + emit_move_insn (reg2, gen_rtx_MINUS (SFmode, operands[1], reg1)); + emit_move_insn (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_insn (gen_rtx_USE (VOIDmode, 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 (); + REAL_VALUE_TYPE offset; + + real_2expN (&offset, 63); + + emit_move_insn (reg1, CONST_DOUBLE_FROM_REAL_VALUE (offset, SFmode)); + do_pending_stack_adjust (); + + emit_insn (gen_cmpsf (operands[1], reg1)); + emit_jump_insn (gen_bge (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); + emit_move_insn (reg2, gen_rtx_MINUS (SFmode, operands[1], reg1)); + emit_move_insn (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_insn (gen_rtx_USE (VOIDmode, 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:QI 1 "memory_operand") + (match_operand 2 "immediate_operand") + (match_operand 3 "immediate_operand")))] + "!TARGET_MIPS16" +{ + if (mips_expand_unaligned_load (operands[0], operands[1], + INTVAL (operands[2]), + INTVAL (operands[3]))) + DONE; + else + FAIL; +}) + +(define_expand "extzv" + [(set (match_operand 0 "register_operand") + (zero_extract (match_operand 1 "nonimmediate_operand") + (match_operand 2 "immediate_operand") + (match_operand 3 "immediate_operand")))] + "!TARGET_MIPS16" +{ + if (mips_expand_unaligned_load (operands[0], operands[1], + INTVAL (operands[2]), + INTVAL (operands[3]))) + DONE; + else if (mips_use_ins_ext_p (operands[1], operands[2], 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:SI 2 "immediate_operand" "I") + (match_operand:SI 3 "immediate_operand" "I")))] + "mips_use_ins_ext_p (operands[1], operands[2], operands[3])" + "<d>ext\t%0,%1,%3,%2" + [(set_attr "type" "arith") + (set_attr "mode" "<MODE>")]) + + +(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_unaligned_store (operands[0], operands[3], + INTVAL (operands[1]), + INTVAL (operands[2]))) + DONE; + else if (mips_use_ins_ext_p (operands[0], operands[1], 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], operands[1], operands[2])" + "<d>ins\t%0,%z3,%2,%1" + [(set_attr "type" "arith") + (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 "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 "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 "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 "type" "store") + (set_attr "mode" "<MODE>")]) + +;; An instruction to calculate the high part of a 64-bit SYMBOL_GENERAL. +;; 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 "general_symbolic_operand" "")))] + "TARGET_EXPLICIT_RELOCS && ABI_HAS_64BIT_SYMBOLS" + "#" + "&& flow2_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 "register_operand") + (high:DI (match_operand:DI 2 "general_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_GENERAL 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 "general_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")]) + +;; Insns to fetch a global 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 "global_got_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_GLOBAL); + 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 "global_got_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_GLOBAL); } + [(set_attr "got" "load") + (set_attr "mode" "<MODE>")]) + +;; Insns to fetch a global symbol from a normal GOT. + +(define_insn_and_split "*got_disp<mode>" + [(set (match_operand:P 0 "register_operand" "=d") + (match_operand:P 1 "global_got_operand" ""))] + "TARGET_EXPLICIT_RELOCS && !TARGET_XGOT" + "#" + "&& reload_completed" + [(set (match_dup 0) + (unspec:P [(match_dup 2) (match_dup 3)] UNSPEC_LOAD_GOT))] +{ + operands[2] = pic_offset_table_rtx; + operands[3] = mips_unspec_address (operands[1], SYMBOL_GOTOFF_GLOBAL); +} + [(set_attr "got" "load") + (set_attr "mode" "<MODE>")]) + +;; Insns for loading the high part of a local symbol. + +(define_insn_and_split "*got_page<mode>" + [(set (match_operand:P 0 "register_operand" "=d") + (high:P (match_operand:P 1 "local_got_operand" "")))] + "TARGET_EXPLICIT_RELOCS" + "#" + "&& reload_completed" + [(set (match_dup 0) + (unspec:P [(match_dup 2) (match_dup 3)] UNSPEC_LOAD_GOT))] +{ + operands[2] = pic_offset_table_rtx; + operands[3] = mips_unspec_address (operands[1], SYMBOL_GOTOFF_PAGE); +} + [(set_attr "got" "load") + (set_attr "mode" "<MODE>")]) + +;; 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 "type" "load") + (set_attr "mode" "<MODE>") + (set_attr "length" "4")]) + +;; Instructions for adding the low 16 bits of an address to a register. +;; Operand 2 is the address: 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 "type" "arith") + (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 "type" "arith") + (set_attr "mode" "<MODE>") + (set_attr "length" "8")]) + +;; 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[0], operands[2], INTVAL (operands[1])); + DONE; +}) + +;; Likewise, for symbolic operands. +(define_split + [(set (match_operand:P 0 "register_operand") + (match_operand:P 1 "splittable_symbolic_operand")) + (clobber (match_operand:P 2 "register_operand"))] + "" + [(set (match_dup 0) (match_dup 1))] + { operands[1] = mips_split_symbol (operands[2], operands[1]); }) + +;; 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 31))] + "TARGET_MIPS16" + "<store>\t$31,%0" + [(set_attr "type" "store") + (set_attr "mode" "<MODE>")]) + +(define_insn "*movdi_32bit" + [(set (match_operand:DI 0 "nonimmediate_operand" "=d,d,d,m,*a,*d,*B*C*D,*B*C*D,*d,*m") + (match_operand:DI 1 "move_operand" "d,i,m,d,*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 "type" "arith,arith,load,store,mthilo,mfhilo,xfer,load,xfer,store") + (set_attr "mode" "DI") + (set_attr "length" "8,16,*,*,8,8,8,*,8,*")]) + +(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 "type" "arith,arith,arith,arith,arith,load,store,mfhilo") + (set_attr "mode" "DI") + (set_attr "length" "8,8,8,8,12,*,*,8")]) + +(define_insn "*movdi_64bit" + [(set (match_operand:DI 0 "nonimmediate_operand" "=d,d,e,d,m,*f,*f,*f,*d,*m,*x,*B*C*D,*B*C*D,*d,*m") + (match_operand:DI 1 "move_operand" "d,U,T,m,dJ,*f,*d*J,*m,*f,*f,*J*d,*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 "type" "arith,const,const,load,store,fmove,xfer,fpload,xfer,fpstore,mthilo,xfer,load,xfer,store") + (set_attr "mode" "DI") + (set_attr "length" "4,*,*,*,*,4,4,*,4,*,4,8,*,8,*")]) + +(define_insn "*movdi_64bit_mips16" + [(set (match_operand:DI 0 "nonimmediate_operand" "=d,y,d,d,d,d,d,m") + (match_operand:DI 1 "move_operand" "d,d,y,K,N,U,m,d"))] + "TARGET_64BIT && TARGET_MIPS16 + && (register_operand (operands[0], DImode) + || register_operand (operands[1], DImode))" + { return mips_output_move (operands[0], operands[1]); } + [(set_attr "type" "arith,arith,arith,arith,arith,const,load,store") + (set_attr "mode" "DI") + (set_attr_alternative "length" + [(const_int 4) + (const_int 4) + (const_int 4) + (if_then_else (match_operand:VOID 1 "m16_uimm8_1") + (const_int 4) + (const_int 8)) + (if_then_else (match_operand:VOID 1 "m16_nuimm8_1") + (const_int 8) + (const_int 12)) + (const_string "*") + (const_string "*") + (const_string "*")])]) + + +;; 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 "register_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 + && REG_P (operands[0]) + && M16_REG_P (REGNO (operands[0])) + && GET_CODE (operands[1]) == CONST_INT + && ((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 "movsi" + [(set (match_operand:SI 0 "") + (match_operand:SI 1 ""))] + "" +{ + if (mips_legitimize_move (SImode, 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 "*movsi_internal" + [(set (match_operand:SI 0 "nonimmediate_operand" "=d,d,e,d,m,*f,*f,*f,*d,*m,*d,*z,*a,*d,*B*C*D,*B*C*D,*d,*m") + (match_operand:SI 1 "move_operand" "d,U,T,m,dJ,*f,*d*J,*m,*f,*f,*z,*d,*J*d,*A,*d,*m,*B*C*D,*B*C*D"))] + "!TARGET_MIPS16 + && (register_operand (operands[0], SImode) + || reg_or_0_operand (operands[1], SImode))" + { return mips_output_move (operands[0], operands[1]); } + [(set_attr "type" "arith,const,const,load,store,fmove,xfer,fpload,xfer,fpstore,xfer,xfer,mthilo,mfhilo,xfer,load,xfer,store") + (set_attr "mode" "SI") + (set_attr "length" "4,*,*,*,*,4,4,*,4,*,4,4,4,4,4,*,4,*")]) + +(define_insn "*movsi_mips16" + [(set (match_operand:SI 0 "nonimmediate_operand" "=d,y,d,d,d,d,d,m") + (match_operand:SI 1 "move_operand" "d,d,y,K,N,U,m,d"))] + "TARGET_MIPS16 + && (register_operand (operands[0], SImode) + || register_operand (operands[1], SImode))" + { return mips_output_move (operands[0], operands[1]); } + [(set_attr "type" "arith,arith,arith,arith,arith,const,load,store") + (set_attr "mode" "SI") + (set_attr_alternative "length" + [(const_int 4) + (const_int 4) + (const_int 4) + (if_then_else (match_operand:VOID 1 "m16_uimm8_1") + (const_int 4) + (const_int 8)) + (if_then_else (match_operand:VOID 1 "m16_nuimm8_1") + (const_int 8) + (const_int 12)) + (const_string "*") + (const_string "*") + (const_string "*")])]) + +;; 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 "register_operand") + (mem:SI (plus:SI (match_dup 0) + (match_operand:SI 1 "const_int_operand"))))] + "TARGET_MIPS16 && reload_completed && !TARGET_DEBUG_D_MODE + && REG_P (operands[0]) + && M16_REG_P (REGNO (operands[0])) + && GET_CODE (operands[1]) == CONST_INT + && ((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 "register_operand") + (match_operand:SI 1 "const_int_operand"))] + "TARGET_MIPS16 && reload_completed && !TARGET_DEBUG_D_MODE + && REG_P (operands[0]) + && M16_REG_P (REGNO (operands[0])) + && GET_CODE (operands[1]) == CONST_INT + && 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 "type" "xfer,arith,load,store,xfer,xfer,fmove,fpload,fpstore") + (set_attr "mode" "SI") + (set_attr "length" "8,4,*,*,4,4,4,*,*")]) + +;; 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_emit_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_emit_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 GO_IF_LEGITIMATE_ADDRESS 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>")]) + +;; 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,*d,*f,*f,*x") + (match_operand:HI 1 "move_operand" "d,I,m,dJ,*f,*d,*f,*d"))] + "!TARGET_MIPS16 + && (register_operand (operands[0], HImode) + || reg_or_0_operand (operands[1], HImode))" + "@ + move\t%0,%1 + li\t%0,%1 + lhu\t%0,%1 + sh\t%z1,%0 + mfc1\t%0,%1 + mtc1\t%1,%0 + mov.s\t%0,%1 + mt%0\t%1" + [(set_attr "type" "arith,arith,load,store,xfer,xfer,fmove,mthilo") + (set_attr "mode" "HI") + (set_attr "length" "4,4,*,*,4,4,4,4")]) + +(define_insn "*movhi_mips16" + [(set (match_operand:HI 0 "nonimmediate_operand" "=d,y,d,d,d,d,m") + (match_operand:HI 1 "move_operand" "d,d,y,K,N,m,d"))] + "TARGET_MIPS16 + && (register_operand (operands[0], HImode) + || register_operand (operands[1], HImode))" + "@ + move\t%0,%1 + move\t%0,%1 + move\t%0,%1 + li\t%0,%1 + # + lhu\t%0,%1 + sh\t%1,%0" + [(set_attr "type" "arith,arith,arith,arith,arith,load,store") + (set_attr "mode" "HI") + (set_attr_alternative "length" + [(const_int 4) + (const_int 4) + (const_int 4) + (if_then_else (match_operand:VOID 1 "m16_uimm8_1") + (const_int 4) + (const_int 8)) + (if_then_else (match_operand:VOID 1 "m16_nuimm8_1") + (const_int 8) + (const_int 12)) + (const_string "*") + (const_string "*")])]) + + +;; 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 "register_operand") + (mem:HI (plus:SI (match_dup 0) + (match_operand:SI 1 "const_int_operand"))))] + "TARGET_MIPS16 && reload_completed && !TARGET_DEBUG_D_MODE + && REG_P (operands[0]) + && M16_REG_P (REGNO (operands[0])) + && GET_CODE (operands[1]) == CONST_INT + && ((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,*d,*f,*f,*x") + (match_operand:QI 1 "move_operand" "d,I,m,dJ,*f,*d,*f,*d"))] + "!TARGET_MIPS16 + && (register_operand (operands[0], QImode) + || reg_or_0_operand (operands[1], QImode))" + "@ + move\t%0,%1 + li\t%0,%1 + lbu\t%0,%1 + sb\t%z1,%0 + mfc1\t%0,%1 + mtc1\t%1,%0 + mov.s\t%0,%1 + mt%0\t%1" + [(set_attr "type" "arith,arith,load,store,xfer,xfer,fmove,mthilo") + (set_attr "mode" "QI") + (set_attr "length" "4,4,*,*,4,4,4,4")]) + +(define_insn "*movqi_mips16" + [(set (match_operand:QI 0 "nonimmediate_operand" "=d,y,d,d,d,d,m") + (match_operand:QI 1 "move_operand" "d,d,y,K,N,m,d"))] + "TARGET_MIPS16 + && (register_operand (operands[0], QImode) + || register_operand (operands[1], QImode))" + "@ + move\t%0,%1 + move\t%0,%1 + move\t%0,%1 + li\t%0,%1 + # + lbu\t%0,%1 + sb\t%1,%0" + [(set_attr "type" "arith,arith,arith,arith,arith,load,store") + (set_attr "mode" "QI") + (set_attr "length" "4,4,4,4,8,*,*")]) + +;; 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 "register_operand") + (mem:QI (plus:SI (match_dup 0) + (match_operand:SI 1 "const_int_operand"))))] + "TARGET_MIPS16 && reload_completed && !TARGET_DEBUG_D_MODE + && REG_P (operands[0]) + && M16_REG_P (REGNO (operands[0])) + && GET_CODE (operands[1]) == CONST_INT + && ((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 "type" "fmove,xfer,fpload,fpstore,store,xfer,xfer,arith,load,store") + (set_attr "mode" "SF") + (set_attr "length" "4,4,*,*,*,4,4,4,*,*")]) + +(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 "type" "arith,load,store") + (set_attr "mode" "SF") + (set_attr "length" "4,*,*")]) + +(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 "type" "arith,arith,arith,load,store") + (set_attr "mode" "SF") + (set_attr "length" "4,4,4,*,*")]) + + +;; 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_64bit" + [(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 && TARGET_64BIT + && (register_operand (operands[0], DFmode) + || reg_or_0_operand (operands[1], DFmode))" + { return mips_output_move (operands[0], operands[1]); } + [(set_attr "type" "fmove,xfer,fpload,fpstore,store,xfer,xfer,arith,load,store") + (set_attr "mode" "DF") + (set_attr "length" "4,4,*,*,*,4,4,4,*,*")]) + +(define_insn "*movdf_hardfloat_32bit" + [(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 && !TARGET_64BIT + && (register_operand (operands[0], DFmode) + || reg_or_0_operand (operands[1], DFmode))" + { return mips_output_move (operands[0], operands[1]); } + [(set_attr "type" "fmove,xfer,fpload,fpstore,store,xfer,xfer,arith,load,store") + (set_attr "mode" "DF") + (set_attr "length" "4,8,*,*,*,8,8,8,*,*")]) + +(define_insn "*movdf_softfloat" + [(set (match_operand:DF 0 "nonimmediate_operand" "=d,d,m,d,f,f") + (match_operand:DF 1 "move_operand" "dG,m,dG,f,d,f"))] + "(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 "type" "arith,load,store,xfer,xfer,fmove") + (set_attr "mode" "DF") + (set_attr "length" "8,*,*,4,4,4")]) + +(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 "type" "arith,arith,arith,load,store") + (set_attr "mode" "DF") + (set_attr "length" "8,8,8,*,*")]) + +(define_split + [(set (match_operand:DI 0 "nonimmediate_operand") + (match_operand:DI 1 "move_operand"))] + "reload_completed && !TARGET_64BIT + && mips_split_64bit_move_p (operands[0], operands[1])" + [(const_int 0)] +{ + mips_split_64bit_move (operands[0], operands[1]); + DONE; +}) + +(define_split + [(set (match_operand:DF 0 "nonimmediate_operand") + (match_operand:DF 1 "move_operand"))] + "reload_completed && !TARGET_64BIT + && mips_split_64bit_move_p (operands[0], operands[1])" + [(const_int 0)] +{ + mips_split_64bit_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 "register_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_PAIRED_SINGLE_FLOAT" +{ + if (mips_legitimize_move (V2SFmode, operands[0], operands[1])) + DONE; +}) + +(define_insn "movv2sf_hardfloat_64bit" + [(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_PAIRED_SINGLE_FLOAT + && TARGET_64BIT + && (register_operand (operands[0], V2SFmode) + || reg_or_0_operand (operands[1], V2SFmode))" + { return mips_output_move (operands[0], operands[1]); } + [(set_attr "type" "fmove,xfer,fpload,fpstore,store,xfer,xfer,arith,load,store") + (set_attr "mode" "SF") + (set_attr "length" "4,4,*,*,*,4,4,4,*,*")]) + +;; The HI and LO registers are not truly independent. If we move an mthi +;; instruction before an mflo instruction, it will make the result of the +;; mflo unpredictable. The same goes for mtlo and mfhi. +;; +;; We cope with this by making the mflo and mfhi patterns use both HI and LO. +;; Operand 1 is the register we want, operand 2 is the other one. +;; +;; When generating VR4120 or VR4130 code, we use macc{,hi} and +;; dmacc{,hi} instead of mfhi and mflo. This avoids both the normal +;; MIPS III hi/lo hazards and the errata related to -mfix-vr4130. + +(define_expand "mfhilo_<mode>" + [(set (match_operand:GPR 0 "register_operand") + (unspec:GPR [(match_operand:GPR 1 "register_operand") + (match_operand:GPR 2 "register_operand")] + UNSPEC_MFHILO))]) + +(define_insn "*mfhilo_<mode>" + [(set (match_operand:GPR 0 "register_operand" "=d,d") + (unspec:GPR [(match_operand:GPR 1 "register_operand" "h,l") + (match_operand:GPR 2 "register_operand" "l,h")] + UNSPEC_MFHILO))] + "!ISA_HAS_MACCHI" + "mf%1\t%0" + [(set_attr "type" "mfhilo") + (set_attr "mode" "<MODE>")]) + +(define_insn "*mfhilo_<mode>_macc" + [(set (match_operand:GPR 0 "register_operand" "=d,d") + (unspec:GPR [(match_operand:GPR 1 "register_operand" "h,l") + (match_operand:GPR 2 "register_operand" "l,h")] + UNSPEC_MFHILO))] + "ISA_HAS_MACCHI" +{ + if (REGNO (operands[1]) == HI_REGNUM) + return "<d>macchi\t%0,%.,%."; + else + return "<d>macc\t%0,%.,%."; +} + [(set_attr "type" "mfhilo") + (set_attr "mode" "<MODE>")]) + +;; Patterns for loading or storing part of a paired floating point +;; register. We need them because odd-numbered floating-point registers +;; are not fully independent: see mips_split_64bit_move. + +;; Load the low word of operand 0 with operand 1. +(define_insn "load_df_low" + [(set (match_operand:DF 0 "register_operand" "=f,f") + (unspec:DF [(match_operand:SI 1 "general_operand" "dJ,m")] + UNSPEC_LOAD_DF_LOW))] + "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT && !TARGET_64BIT" +{ + operands[0] = mips_subword (operands[0], 0); + return mips_output_move (operands[0], operands[1]); +} + [(set_attr "type" "xfer,fpload") + (set_attr "mode" "SF")]) + +;; Load the high word of operand 0 from operand 1, preserving the value +;; in the low word. +(define_insn "load_df_high" + [(set (match_operand:DF 0 "register_operand" "=f,f") + (unspec:DF [(match_operand:SI 1 "general_operand" "dJ,m") + (match_operand:DF 2 "register_operand" "0,0")] + UNSPEC_LOAD_DF_HIGH))] + "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT && !TARGET_64BIT" +{ + operands[0] = mips_subword (operands[0], 1); + return mips_output_move (operands[0], operands[1]); +} + [(set_attr "type" "xfer,fpload") + (set_attr "mode" "SF")]) + +;; Store the high word of operand 1 in operand 0. The corresponding +;; low-word move is done in the normal way. +(define_insn "store_df_high" + [(set (match_operand:SI 0 "nonimmediate_operand" "=d,m") + (unspec:SI [(match_operand:DF 1 "register_operand" "f,f")] + UNSPEC_STORE_DF_HIGH))] + "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT && !TARGET_64BIT" +{ + operands[1] = mips_subword (operands[1], 1); + return mips_output_move (operands[0], operands[1]); +} + [(set_attr "type" "xfer,fpstore") + (set_attr "mode" "SF")]) + +;; 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" + [(unspec_volatile [(match_operand 0 "" "") + (match_operand 1 "register_operand" "")] UNSPEC_LOADGP)] + "mips_current_loadgp_style () == LOADGP_NEWABI" + "#" + "" + [(set (match_dup 2) (match_dup 3)) + (set (match_dup 2) (match_dup 4)) + (set (match_dup 2) (match_dup 5))] +{ + operands[2] = pic_offset_table_rtx; + operands[3] = gen_rtx_HIGH (Pmode, operands[0]); + operands[4] = gen_rtx_PLUS (Pmode, operands[2], operands[1]); + operands[5] = gen_rtx_LO_SUM (Pmode, operands[2], operands[0]); +} + [(set_attr "length" "12")]) + +;; Likewise, for -mno-shared code. Operand 0 is the __gnu_local_gp symbol. +(define_insn_and_split "loadgp_noshared" + [(unspec_volatile [(match_operand 0 "" "")] UNSPEC_LOADGP)] + "mips_current_loadgp_style () == LOADGP_ABSOLUTE" + "#" + "" + [(const_int 0)] +{ + emit_move_insn (pic_offset_table_rtx, operands[0]); + DONE; +} + [(set_attr "length" "8")]) + +;; The use of gp is hidden when not using explicit relocations. +;; 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:DI 28)] UNSPEC_BLOCKAGE)] + "" + "" + [(set_attr "type" "unknown") + (set_attr "mode" "none") + (set_attr "length" "0")]) + +;; Emit a .cprestore directive, which normally expands to a single store +;; instruction. Note that we continue to use .cprestore for explicit reloc +;; code so that jals inside inline asms will work correctly. +(define_insn "cprestore" + [(unspec_volatile [(match_operand 0 "const_int_operand" "I,i")] + UNSPEC_CPRESTORE)] + "" +{ + if (set_nomacro && which_alternative == 1) + return ".set\tmacro\;.cprestore\t%0\;.set\tnomacro"; + else + return ".cprestore\t%0"; +} + [(set_attr "type" "store") + (set_attr "length" "4,12")]) + +;; 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 function_arg, so we must + be careful not to allocate a new register if we've reached the + reload pass. */ + if (TARGET_MIPS16 + && optimize + && GET_CODE (operands[2]) == CONST_INT + && 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 (GET_CODE (operands[2]) == CONST_INT) + 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 (GET_CODE (operands[2]) == CONST_INT) + 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 (GET_CODE (operands[2]) == CONST_INT) + 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 (GET_CODE (operands[2]) == CONST_INT) + 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 "register_operand") + (any_shift:GPR (match_operand:GPR 1 "register_operand") + (match_operand:GPR 2 "const_int_operand")))] + "TARGET_MIPS16 && reload_completed && !TARGET_DEBUG_D_MODE + && GET_CODE (operands[2]) == CONST_INT + && 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_ROTR_<MODE>" +{ + if (GET_CODE (operands[2]) == CONST_INT) + 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>")]) + +;; +;; .................... +;; +;; COMPARISONS +;; +;; .................... + +;; Flow here is rather complex: +;; +;; 1) The cmp{si,di,sf,df} routine is called. It deposits the arguments +;; into cmp_operands[] but generates no RTL. +;; +;; 2) The appropriate branch define_expand is called, which then +;; creates the appropriate RTL for the comparison and branch. +;; Different CC modes are used, based on what type of branch is +;; done, so that we can constrain things appropriately. There +;; are assumptions in the rest of GCC that break if we fold the +;; operands into the branches for integer operations, and use cc0 +;; for floating point, so we use the fp status register instead. +;; If needed, an appropriate temporary is created to hold the +;; of the integer compare. + +(define_expand "cmp<mode>" + [(set (cc0) + (compare:CC (match_operand:GPR 0 "register_operand") + (match_operand:GPR 1 "nonmemory_operand")))] + "" +{ + cmp_operands[0] = operands[0]; + cmp_operands[1] = operands[1]; + DONE; +}) + +(define_expand "cmp<mode>" + [(set (cc0) + (compare:CC (match_operand:SCALARF 0 "register_operand") + (match_operand:SCALARF 1 "register_operand")))] + "" +{ + cmp_operands[0] = operands[0]; + cmp_operands[1] = operands[1]; + DONE; +}) + +;; +;; .................... +;; +;; CONDITIONAL BRANCHES +;; +;; .................... + +;; Conditional branches on floating-point equality tests. + +(define_insn "*branch_fp" + [(set (pc) + (if_then_else + (match_operator 0 "equality_operator" + [(match_operand:CC 2 "register_operand" "z") + (const_int 0)]) + (label_ref (match_operand 1 "" "")) + (pc)))] + "TARGET_HARD_FLOAT" +{ + return mips_output_conditional_branch (insn, operands, + MIPS_BRANCH ("b%F0", "%Z2%1"), + MIPS_BRANCH ("b%W0", "%Z2%1")); +} + [(set_attr "type" "branch") + (set_attr "mode" "none")]) + +(define_insn "*branch_fp_inverted" + [(set (pc) + (if_then_else + (match_operator 0 "equality_operator" + [(match_operand:CC 2 "register_operand" "z") + (const_int 0)]) + (pc) + (label_ref (match_operand 1 "" ""))))] + "TARGET_HARD_FLOAT" +{ + return mips_output_conditional_branch (insn, operands, + MIPS_BRANCH ("b%W0", "%Z2%1"), + MIPS_BRANCH ("b%F0", "%Z2%1")); +} + [(set_attr "type" "branch") + (set_attr "mode" "none")]) + +;; Conditional branches on ordered comparisons with zero. + +(define_insn "*branch_order<mode>" + [(set (pc) + (if_then_else + (match_operator 0 "order_operator" + [(match_operand:GPR 2 "register_operand" "d") + (const_int 0)]) + (label_ref (match_operand 1 "" "")) + (pc)))] + "!TARGET_MIPS16" + { return mips_output_order_conditional_branch (insn, operands, false); } + [(set_attr "type" "branch") + (set_attr "mode" "none")]) + +(define_insn "*branch_order<mode>_inverted" + [(set (pc) + (if_then_else + (match_operator 0 "order_operator" + [(match_operand:GPR 2 "register_operand" "d") + (const_int 0)]) + (pc) + (label_ref (match_operand 1 "" ""))))] + "!TARGET_MIPS16" + { return mips_output_order_conditional_branch (insn, operands, true); } + [(set_attr "type" "branch") + (set_attr "mode" "none")]) + +;; Conditional branch on equality comparison. + +(define_insn "*branch_equality<mode>" + [(set (pc) + (if_then_else + (match_operator 0 "equality_operator" + [(match_operand:GPR 2 "register_operand" "d") + (match_operand:GPR 3 "reg_or_0_operand" "dJ")]) + (label_ref (match_operand 1 "" "")) + (pc)))] + "!TARGET_MIPS16" +{ + return mips_output_conditional_branch (insn, operands, + MIPS_BRANCH ("b%C0", "%2,%z3,%1"), + MIPS_BRANCH ("b%N0", "%2,%z3,%1")); +} + [(set_attr "type" "branch") + (set_attr "mode" "none")]) + +(define_insn "*branch_equality<mode>_inverted" + [(set (pc) + (if_then_else + (match_operator 0 "equality_operator" + [(match_operand:GPR 2 "register_operand" "d") + (match_operand:GPR 3 "reg_or_0_operand" "dJ")]) + (pc) + (label_ref (match_operand 1 "" ""))))] + "!TARGET_MIPS16" +{ + return mips_output_conditional_branch (insn, operands, + MIPS_BRANCH ("b%N0", "%2,%z3,%1"), + MIPS_BRANCH ("b%C0", "%2,%z3,%1")); +} + [(set_attr "type" "branch") + (set_attr "mode" "none")]) + +;; 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") + (set_attr "mode" "none") + (set_attr "length" "8")]) + +(define_expand "b<code>" + [(set (pc) + (if_then_else (any_cond:CC (cc0) + (const_int 0)) + (label_ref (match_operand 0 "")) + (pc)))] + "" +{ + gen_conditional_branch (operands, <CODE>); + DONE; +}) + +;; Used to implement built-in functions. +(define_expand "condjump" + [(set (pc) + (if_then_else (match_operand 0) + (label_ref (match_operand 1)) + (pc)))]) + +;; +;; .................... +;; +;; SETTING A REGISTER FROM A COMPARISON +;; +;; .................... + +(define_expand "seq" + [(set (match_operand:SI 0 "register_operand") + (eq:SI (match_dup 1) + (match_dup 2)))] + "" + { if (mips_emit_scc (EQ, operands[0])) DONE; else FAIL; }) + +(define_insn "*seq_<mode>" + [(set (match_operand:GPR 0 "register_operand" "=d") + (eq:GPR (match_operand:GPR 1 "register_operand" "d") + (const_int 0)))] + "!TARGET_MIPS16" + "sltu\t%0,%1,1" + [(set_attr "type" "slt") + (set_attr "mode" "<MODE>")]) + +(define_insn "*seq_<mode>_mips16" + [(set (match_operand:GPR 0 "register_operand" "=t") + (eq:GPR (match_operand:GPR 1 "register_operand" "d") + (const_int 0)))] + "TARGET_MIPS16" + "sltu\t%1,1" + [(set_attr "type" "slt") + (set_attr "mode" "<MODE>")]) + +;; "sne" uses sltu instructions in which the first operand is $0. +;; This isn't possible in mips16 code. + +(define_expand "sne" + [(set (match_operand:SI 0 "register_operand") + (ne:SI (match_dup 1) + (match_dup 2)))] + "!TARGET_MIPS16" + { if (mips_emit_scc (NE, operands[0])) DONE; else FAIL; }) + +(define_insn "*sne_<mode>" + [(set (match_operand:GPR 0 "register_operand" "=d") + (ne:GPR (match_operand:GPR 1 "register_operand" "d") + (const_int 0)))] + "!TARGET_MIPS16" + "sltu\t%0,%.,%1" + [(set_attr "type" "slt") + (set_attr "mode" "<MODE>")]) + +(define_expand "sgt" + [(set (match_operand:SI 0 "register_operand") + (gt:SI (match_dup 1) + (match_dup 2)))] + "" + { if (mips_emit_scc (GT, operands[0])) DONE; else FAIL; }) + +(define_insn "*sgt_<mode>" + [(set (match_operand:GPR 0 "register_operand" "=d") + (gt:GPR (match_operand:GPR 1 "register_operand" "d") + (match_operand:GPR 2 "reg_or_0_operand" "dJ")))] + "!TARGET_MIPS16" + "slt\t%0,%z2,%1" + [(set_attr "type" "slt") + (set_attr "mode" "<MODE>")]) + +(define_insn "*sgt_<mode>_mips16" + [(set (match_operand:GPR 0 "register_operand" "=t") + (gt:GPR (match_operand:GPR 1 "register_operand" "d") + (match_operand:GPR 2 "register_operand" "d")))] + "TARGET_MIPS16" + "slt\t%2,%1" + [(set_attr "type" "slt") + (set_attr "mode" "<MODE>")]) + +(define_expand "sge" + [(set (match_operand:SI 0 "register_operand") + (ge:SI (match_dup 1) + (match_dup 2)))] + "" + { if (mips_emit_scc (GE, operands[0])) DONE; else FAIL; }) + +(define_insn "*sge_<mode>" + [(set (match_operand:GPR 0 "register_operand" "=d") + (ge:GPR (match_operand:GPR 1 "register_operand" "d") + (const_int 1)))] + "!TARGET_MIPS16" + "slt\t%0,%.,%1" + [(set_attr "type" "slt") + (set_attr "mode" "<MODE>")]) + +(define_expand "slt" + [(set (match_operand:SI 0 "register_operand") + (lt:SI (match_dup 1) + (match_dup 2)))] + "" + { if (mips_emit_scc (LT, operands[0])) DONE; else FAIL; }) + +(define_insn "*slt_<mode>" + [(set (match_operand:GPR 0 "register_operand" "=d") + (lt:GPR (match_operand:GPR 1 "register_operand" "d") + (match_operand:GPR 2 "arith_operand" "dI")))] + "!TARGET_MIPS16" + "slt\t%0,%1,%2" + [(set_attr "type" "slt") + (set_attr "mode" "<MODE>")]) + +(define_insn "*slt_<mode>_mips16" + [(set (match_operand:GPR 0 "register_operand" "=t,t") + (lt:GPR (match_operand:GPR 1 "register_operand" "d,d") + (match_operand:GPR 2 "arith_operand" "d,I")))] + "TARGET_MIPS16" + "slt\t%1,%2" + [(set_attr "type" "slt") + (set_attr "mode" "<MODE>") + (set_attr_alternative "length" + [(const_int 4) + (if_then_else (match_operand 2 "m16_uimm8_1") + (const_int 4) + (const_int 8))])]) + +(define_expand "sle" + [(set (match_operand:SI 0 "register_operand") + (le:SI (match_dup 1) + (match_dup 2)))] + "" + { if (mips_emit_scc (LE, operands[0])) DONE; else FAIL; }) + +(define_insn "*sle_<mode>" + [(set (match_operand:GPR 0 "register_operand" "=d") + (le:GPR (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\t%0,%1,%2"; +} + [(set_attr "type" "slt") + (set_attr "mode" "<MODE>")]) + +(define_insn "*sle_<mode>_mips16" + [(set (match_operand:GPR 0 "register_operand" "=t") + (le:GPR (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\t%1,%2"; +} + [(set_attr "type" "slt") + (set_attr "mode" "<MODE>") + (set (attr "length") (if_then_else (match_operand 2 "m16_uimm8_m1_1") + (const_int 4) + (const_int 8)))]) + +(define_expand "sgtu" + [(set (match_operand:SI 0 "register_operand") + (gtu:SI (match_dup 1) + (match_dup 2)))] + "" + { if (mips_emit_scc (GTU, operands[0])) DONE; else FAIL; }) + +(define_insn "*sgtu_<mode>" + [(set (match_operand:GPR 0 "register_operand" "=d") + (gtu:GPR (match_operand:GPR 1 "register_operand" "d") + (match_operand:GPR 2 "reg_or_0_operand" "dJ")))] + "!TARGET_MIPS16" + "sltu\t%0,%z2,%1" + [(set_attr "type" "slt") + (set_attr "mode" "<MODE>")]) + +(define_insn "*sgtu_<mode>_mips16" + [(set (match_operand:GPR 0 "register_operand" "=t") + (gtu:GPR (match_operand:GPR 1 "register_operand" "d") + (match_operand:GPR 2 "register_operand" "d")))] + "TARGET_MIPS16" + "sltu\t%2,%1" + [(set_attr "type" "slt") + (set_attr "mode" "<MODE>")]) + +(define_expand "sgeu" + [(set (match_operand:SI 0 "register_operand") + (geu:SI (match_dup 1) + (match_dup 2)))] + "" + { if (mips_emit_scc (GEU, operands[0])) DONE; else FAIL; }) + +(define_insn "*sge_<mode>" + [(set (match_operand:GPR 0 "register_operand" "=d") + (geu:GPR (match_operand:GPR 1 "register_operand" "d") + (const_int 1)))] + "!TARGET_MIPS16" + "sltu\t%0,%.,%1" + [(set_attr "type" "slt") + (set_attr "mode" "<MODE>")]) + +(define_expand "sltu" + [(set (match_operand:SI 0 "register_operand") + (ltu:SI (match_dup 1) + (match_dup 2)))] + "" + { if (mips_emit_scc (LTU, operands[0])) DONE; else FAIL; }) + +(define_insn "*sltu_<mode>" + [(set (match_operand:GPR 0 "register_operand" "=d") + (ltu:GPR (match_operand:GPR 1 "register_operand" "d") + (match_operand:GPR 2 "arith_operand" "dI")))] + "!TARGET_MIPS16" + "sltu\t%0,%1,%2" + [(set_attr "type" "slt") + (set_attr "mode" "<MODE>")]) + +(define_insn "*sltu_<mode>_mips16" + [(set (match_operand:GPR 0 "register_operand" "=t,t") + (ltu:GPR (match_operand:GPR 1 "register_operand" "d,d") + (match_operand:GPR 2 "arith_operand" "d,I")))] + "TARGET_MIPS16" + "sltu\t%1,%2" + [(set_attr "type" "slt") + (set_attr "mode" "<MODE>") + (set_attr_alternative "length" + [(const_int 4) + (if_then_else (match_operand 2 "m16_uimm8_1") + (const_int 4) + (const_int 8))])]) + +(define_expand "sleu" + [(set (match_operand:SI 0 "register_operand") + (leu:SI (match_dup 1) + (match_dup 2)))] + "" + { if (mips_emit_scc (LEU, operands[0])) DONE; else FAIL; }) + +(define_insn "*sleu_<mode>" + [(set (match_operand:GPR 0 "register_operand" "=d") + (leu:GPR (match_operand:GPR 1 "register_operand" "d") + (match_operand:GPR 2 "sleu_operand" "")))] + "!TARGET_MIPS16" +{ + operands[2] = GEN_INT (INTVAL (operands[2]) + 1); + return "sltu\t%0,%1,%2"; +} + [(set_attr "type" "slt") + (set_attr "mode" "<MODE>")]) + +(define_insn "*sleu_<mode>_mips16" + [(set (match_operand:GPR 0 "register_operand" "=t") + (leu:GPR (match_operand:GPR 1 "register_operand" "d") + (match_operand:GPR 2 "sleu_operand" "")))] + "TARGET_MIPS16" +{ + operands[2] = GEN_INT (INTVAL (operands[2]) + 1); + return "sltu\t%1,%2"; +} + [(set_attr "type" "slt") + (set_attr "mode" "<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_insn "jump" + [(set (pc) + (label_ref (match_operand 0 "" "")))] + "!TARGET_MIPS16" +{ + if (flag_pic) + { + if (get_attr_length (insn) <= 8) + return "%*b\t%l0%/"; + else + { + output_asm_insn (mips_output_load_label (), operands); + return "%*jr\t%@%/%]"; + } + } + else + return "%*j\t%l0%/"; +} + [(set_attr "type" "jump") + (set_attr "mode" "none") + (set (attr "length") + ;; We can't use `j' when emitting PIC. Emit a branch if it's + ;; in range, otherwise load the address of the branch target into + ;; $at and then jump to it. + (if_then_else + (ior (eq (symbol_ref "flag_pic") (const_int 0)) + (lt (abs (minus (match_dup 0) + (plus (pc) (const_int 4)))) + (const_int 131072))) + (const_int 4) (const_int 16)))]) + +;; We need a different insn for the mips16, because a mips16 branch +;; does not have a delay slot. + +(define_insn "" + [(set (pc) + (label_ref (match_operand 0 "" "")))] + "TARGET_MIPS16" + "b\t%l0" + [(set_attr "type" "branch") + (set_attr "mode" "none") + (set_attr "length" "8")]) + +(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) + 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); + + 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_ABICALLS, 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_ABICALLS" +{ + rtx addr; + + addr = plus_constant (operands[0], GET_MODE_SIZE (Pmode) * 3); + emit_move_insn (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_ABICALLS" +{ + /* 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. */ + emit_move_insn (hard_frame_pointer_rtx, fp); + emit_move_insn (pv, lab); + emit_stack_restore (SAVE_NONLOCAL, stack, NULL_RTX); + emit_move_insn (gp, gpv); + emit_insn (gen_rtx_USE (VOIDmode, hard_frame_pointer_rtx)); + emit_insn (gen_rtx_USE (VOIDmode, stack_pointer_rtx)); + emit_insn (gen_rtx_USE (VOIDmode, 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" "unknown") + (set_attr "mode" "none") + (set_attr "length" "0")]) + +(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_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")]) + +;; This is used in compiling the unwind routines. +(define_expand "eh_return" + [(use (match_operand 0 "general_operand"))] + "" +{ + enum machine_mode gpr_mode = TARGET_64BIT ? DImode : SImode; + + if (GET_MODE (operands[0]) != gpr_mode) + operands[0] = convert_to_mode (gpr_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 && !TARGET_DEBUG_D_MODE" + [(const_int 0)] +{ + mips_set_return_address (operands[0], operands[1]); + DONE; +}) + +(define_insn_and_split "exception_receiver" + [(set (reg:SI 28) + (unspec_volatile:SI [(const_int 0)] UNSPEC_EH_RECEIVER))] + "TARGET_ABICALLS && TARGET_OLDABI" + "#" + "&& reload_completed" + [(const_int 0)] +{ + mips_restore_gp (); + DONE; +} + [(set_attr "type" "load") + (set_attr "length" "12")]) + +;; +;; .................... +;; +;; 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 register that +;; acts like a GOT version number. By making the register call-clobbered, +;; we tell the target-independent code that the address could be changed +;; by any call insn. +(define_insn "load_call<mode>" + [(set (match_operand:P 0 "register_operand" "=c") + (unspec:P [(match_operand:P 1 "register_operand" "r") + (match_operand:P 2 "immediate_operand" "") + (reg:P FAKE_CALL_REGNO)] + UNSPEC_LOAD_CALL))] + "TARGET_ABICALLS" + "<load>\t%0,%R2(%1)" + [(set_attr "type" "load") + (set_attr "mode" "<MODE>") + (set_attr "length" "4")]) + +;; 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_ABICALLS forces us to +;; use $25 for this purpose -- and $25 is never clobbered by the +;; epilogue -- we might as well use it for !TARGET_ABICALLS 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 (0, XEXP (operands[0], 0), operands[1], operands[2], true); + 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); } + [(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 (operands[0], XEXP (operands[1], 0), + operands[2], operands[3], true); + DONE; +}) + +(define_insn "sibcall_value_internal" + [(set (match_operand 0 "register_operand" "=df,df") + (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); } + [(set_attr "type" "call")]) + +(define_insn "sibcall_value_multiple_internal" + [(set (match_operand 0 "register_operand" "=df,df") + (call (mem:SI (match_operand 1 "call_insn_operand" "j,S")) + (match_operand 2 "" ""))) + (set (match_operand 3 "register_operand" "=df,df") + (call (mem:SI (match_dup 1)) + (match_dup 2)))] + "TARGET_SIBCALLS && SIBLING_CALL_P (insn)" + { return MIPS_CALL ("j", operands, 1); } + [(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 (0, 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. +;; +;; ??? The operands[2] = insn check is a hack to make the original insn +;; available to the splitter. +(define_insn_and_split "call_internal" + [(call (mem:SI (match_operand 0 "call_insn_operand" "c,S")) + (match_operand 1 "" "")) + (clobber (reg:SI 31))] + "" + { return TARGET_SPLIT_CALLS ? "#" : MIPS_CALL ("jal", operands, 0); } + "reload_completed && TARGET_SPLIT_CALLS && (operands[2] = insn)" + [(const_int 0)] +{ + emit_call_insn (gen_call_split (operands[0], operands[1])); + if (!find_reg_note (operands[2], REG_NORETURN, 0)) + mips_restore_gp (); + DONE; +} + [(set_attr "jal" "indirect,direct") + (set_attr "extended_mips16" "no,yes")]) + +(define_insn "call_split" + [(call (mem:SI (match_operand 0 "call_insn_operand" "cS")) + (match_operand 1 "" "")) + (clobber (reg:SI 31)) + (clobber (reg:SI 28))] + "TARGET_SPLIT_CALLS" + { return MIPS_CALL ("jal", operands, 0); } + [(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 (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" "=df,df") + (call (mem:SI (match_operand 1 "call_insn_operand" "c,S")) + (match_operand 2 "" ""))) + (clobber (reg:SI 31))] + "" + { return TARGET_SPLIT_CALLS ? "#" : MIPS_CALL ("jal", operands, 1); } + "reload_completed && TARGET_SPLIT_CALLS && (operands[3] = insn)" + [(const_int 0)] +{ + emit_call_insn (gen_call_value_split (operands[0], operands[1], + operands[2])); + if (!find_reg_note (operands[3], REG_NORETURN, 0)) + mips_restore_gp (); + DONE; +} + [(set_attr "jal" "indirect,direct") + (set_attr "extended_mips16" "no,yes")]) + +(define_insn "call_value_split" + [(set (match_operand 0 "register_operand" "=df") + (call (mem:SI (match_operand 1 "call_insn_operand" "cS")) + (match_operand 2 "" ""))) + (clobber (reg:SI 31)) + (clobber (reg:SI 28))] + "TARGET_SPLIT_CALLS" + { return MIPS_CALL ("jal", operands, 1); } + [(set_attr "type" "call")]) + +;; See comment for call_internal. +(define_insn_and_split "call_value_multiple_internal" + [(set (match_operand 0 "register_operand" "=df,df") + (call (mem:SI (match_operand 1 "call_insn_operand" "c,S")) + (match_operand 2 "" ""))) + (set (match_operand 3 "register_operand" "=df,df") + (call (mem:SI (match_dup 1)) + (match_dup 2))) + (clobber (reg:SI 31))] + "" + { return TARGET_SPLIT_CALLS ? "#" : MIPS_CALL ("jal", operands, 1); } + "reload_completed && TARGET_SPLIT_CALLS && (operands[4] = insn)" + [(const_int 0)] +{ + emit_call_insn (gen_call_value_multiple_split (operands[0], operands[1], + operands[2], operands[3])); + if (!find_reg_note (operands[4], REG_NORETURN, 0)) + mips_restore_gp (); + DONE; +} + [(set_attr "jal" "indirect,direct") + (set_attr "extended_mips16" "no,yes")]) + +(define_insn "call_value_multiple_split" + [(set (match_operand 0 "register_operand" "=df") + (call (mem:SI (match_operand 1 "call_insn_operand" "cS")) + (match_operand 2 "" ""))) + (set (match_operand 3 "register_operand" "=df") + (call (mem:SI (match_dup 1)) + (match_dup 2))) + (clobber (reg:SI 31)) + (clobber (reg:SI 28))] + "TARGET_SPLIT_CALLS" + { return MIPS_CALL ("jal", operands, 1); } + [(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); + emit_move_insn (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" +{ + 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 (set_noreorder) + 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_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" +{ + gen_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_CONDMOVE" +{ + gen_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]); }) + +; Thread-Local Storage + +; The TLS base pointer is accessed via "rdhwr $v1, $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. + +(define_insn "tls_get_tp_<mode>" + [(set (match_operand:P 0 "register_operand" "=v") + (unspec:P [(const_int 0)] + UNSPEC_TLS_GET_TP))] + "HAVE_AS_TLS && !TARGET_MIPS16" + ".set\tpush\;.set\tmips32r2\t\;rdhwr\t%0,$29\;.set\tpop" + [(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>")]) + +; The MIPS Paired-Single Floating Point and MIPS-3D Instructions. + +(include "mips-ps-3d.md") + +; The MIPS DSP Instructions. + +(include "mips-dsp.md") diff --git a/contrib/gcc/config/mips/mips.opt b/contrib/gcc/config/mips/mips.opt new file mode 100644 index 0000000..7f8214b --- /dev/null +++ b/contrib/gcc/config/mips/mips.opt @@ -0,0 +1,222 @@ +; Options for the MIPS port of the compiler +; +; Copyright (C) 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 2, 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 COPYING. If not, write to the Free +; Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA +; 02110-1301, USA. + +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-likely +Target Report Mask(BRANCHLIKELY) +Use Branch Likely instructions, overriding the architecture default + +mcheck-zero-division +Target Report Mask(CHECK_ZERO_DIV) +Trap on integer divide by zero + +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 + +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 + +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 + +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-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 + +mhard-float +Target Report RejectNegative InverseMask(SOFT_FLOAT, HARD_FLOAT) +Allow the use of hardware floating-point instructions + +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 + +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 + +mmemcpy +Target Report Var(TARGET_MEMCPY) +Don't optimize block moves + +mmips-tfile +Target +Use the mips-tfile postpass + +mno-flush-func +Target RejectNegative +Do not use a cache-flushing function before calling stack trampolines + +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 + +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 + +msoft-float +Target Report RejectNegative Mask(SOFT_FLOAT) +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 + +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 diff --git a/contrib/gcc/config/mips/mips16.S b/contrib/gcc/config/mips/mips16.S new file mode 100644 index 0000000..a6afa0c --- /dev/null +++ b/contrib/gcc/config/mips/mips16.S @@ -0,0 +1,739 @@ +/* mips16 floating point support code + Copyright (C) 1996, 1997, 1998 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 2, or (at your option) any +later version. + +In addition to the permissions in the GNU General Public License, the +Free Software Foundation gives you unlimited permission to link the +compiled version of this file with other programs, and to distribute +those programs without any restriction coming from the use of this +file. (The General Public License restrictions do apply in other +respects; for example, they cover modification of the file, and +distribution when not linked into another program.) + +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. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, write to +the Free Software Foundation, 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. */ + +/* As a special exception, if you link this library with other files, + some of which are compiled with GCC, to produce an executable, + this library does not by itself cause the resulting executable + to be covered by the GNU General Public License. + This exception does not however invalidate any other reasons why + the executable file might be covered by the GNU General Public License. */ + +/* 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. */ + +/* 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 + +/* Single precision math. */ + +/* This macro defines a function which loads two single precision + values, performs an operation, and returns the single precision + result. */ + +#define SFOP(NAME, OPCODE) \ +STARTFN (NAME); \ + .set noreorder; \ + mtc1 $4,$f0; \ + mtc1 $5,$f2; \ + nop; \ + OPCODE $f0,$f0,$f2; \ + mfc1 $2,$f0; \ + j $31; \ + nop; \ + .set reorder; \ + ENDFN (NAME) + +#ifdef L_m16addsf3 +SFOP(__mips16_addsf3, add.s) +#endif +#ifdef L_m16subsf3 +SFOP(__mips16_subsf3, sub.s) +#endif +#ifdef L_m16mulsf3 +SFOP(__mips16_mulsf3, mul.s) +#endif +#ifdef L_m16divsf3 +SFOP(__mips16_divsf3, div.s) +#endif + +#define SFOP2(NAME, OPCODE) \ +STARTFN (NAME); \ + .set noreorder; \ + mtc1 $4,$f0; \ + nop; \ + OPCODE $f0,$f0; \ + mfc1 $2,$f0; \ + j $31; \ + nop; \ + .set reorder; \ + ENDFN (NAME) + +#ifdef L_m16negsf2 +SFOP2(__mips16_negsf2, neg.s) +#endif +#ifdef L_m16abssf2 +SFOP2(__mips16_abssf2, abs.s) +#endif + +/* Single precision comparisons. */ + +/* This macro defines a function which loads two single precision + values, performs a floating point comparison, and returns the + specified values according to whether the comparison is true or + false. */ + +#define SFCMP(NAME, OPCODE, TRUE, FALSE) \ +STARTFN (NAME); \ + mtc1 $4,$f0; \ + mtc1 $5,$f2; \ + OPCODE $f0,$f2; \ + li $2,TRUE; \ + bc1t 1f; \ + li $2,FALSE; \ +1:; \ + j $31; \ + ENDFN (NAME) + +/* This macro is like SFCMP, but it reverses the comparison. */ + +#define SFREVCMP(NAME, OPCODE, TRUE, FALSE) \ +STARTFN (NAME); \ + mtc1 $4,$f0; \ + mtc1 $5,$f2; \ + OPCODE $f2,$f0; \ + li $2,TRUE; \ + bc1t 1f; \ + li $2,FALSE; \ +1:; \ + j $31; \ + ENDFN (NAME) + +#ifdef L_m16eqsf2 +SFCMP(__mips16_eqsf2, c.eq.s, 0, 1) +#endif +#ifdef L_m16nesf2 +SFCMP(__mips16_nesf2, c.eq.s, 0, 1) +#endif +#ifdef L_m16gtsf2 +SFREVCMP(__mips16_gtsf2, c.lt.s, 1, 0) +#endif +#ifdef L_m16gesf2 +SFREVCMP(__mips16_gesf2, c.le.s, 0, -1) +#endif +#ifdef L_m16lesf2 +SFCMP(__mips16_lesf2, c.le.s, 0, 1) +#endif +#ifdef L_m16ltsf2 +SFCMP(__mips16_ltsf2, c.lt.s, -1, 0) +#endif + +/* Single precision conversions. */ + +#ifdef L_m16fltsisf +STARTFN (__mips16_floatsisf) + .set noreorder + mtc1 $4,$f0 + nop + cvt.s.w $f0,$f0 + mfc1 $2,$f0 + j $31 + nop + .set reorder + ENDFN (__mips16_floatsisf) +#endif + +#ifdef L_m16fix_truncsfsi +STARTFN (__mips16_fix_truncsfsi) + .set noreorder + mtc1 $4,$f0 + nop + trunc.w.s $f0,$f0,$4 + mfc1 $2,$f0 + j $31 + nop + .set reorder + ENDFN (__mips16_fix_truncsfsi) +#endif + +#if !defined(__mips_single_float) && !defined(__SINGLE_FLOAT) + +/* The double precision operations. We need to use different code + based on the preprocessor symbol __mips64, because the way in which + double precision values will change. Without __mips64, the value + is passed in two 32 bit registers. With __mips64, the value is + passed in a single 64 bit register. */ + +/* Load the first double precision operand. */ + +#if defined(__mips64) +#define LDDBL1 dmtc1 $4,$f12 +#elif defined(__mipsfp64) +#define LDDBL1 sw $4,0($29); sw $5,4($29); l.d $f12,0($29) +#elif defined(__MIPSEB__) +#define LDDBL1 mtc1 $4,$f13; mtc1 $5,$f12 +#else +#define LDDBL1 mtc1 $4,$f12; mtc1 $5,$f13 +#endif + +/* Load the second double precision operand. */ + +#if defined(__mips64) +/* XXX this should be $6 for Algo arg passing model */ +#define LDDBL2 dmtc1 $5,$f14 +#elif defined(__mipsfp64) +#define LDDBL2 sw $6,8($29); sw $7,12($29); l.d $f14,8($29) +#elif defined(__MIPSEB__) +#define LDDBL2 mtc1 $6,$f15; mtc1 $7,$f14 +#else +#define LDDBL2 mtc1 $6,$f14; mtc1 $7,$f15 +#endif + +/* Move the double precision return value to the right place. */ + +#if defined(__mips64) +#define RETDBL dmfc1 $2,$f0 +#elif defined(__mipsfp64) +#define RETDBL s.d $f0,0($29); lw $2,0($29); lw $3,4($29) +#elif defined(__MIPSEB__) +#define RETDBL mfc1 $2,$f1; mfc1 $3,$f0 +#else +#define RETDBL mfc1 $2,$f0; mfc1 $3,$f1 +#endif + +/* Double precision math. */ + +/* This macro defines a function which loads two double precision + values, performs an operation, and returns the double precision + result. */ + +#define DFOP(NAME, OPCODE) \ +STARTFN (NAME); \ + .set noreorder; \ + LDDBL1; \ + LDDBL2; \ + nop; \ + OPCODE $f0,$f12,$f14; \ + RETDBL; \ + j $31; \ + nop; \ + .set reorder; \ + ENDFN (NAME) + +#ifdef L_m16adddf3 +DFOP(__mips16_adddf3, add.d) +#endif +#ifdef L_m16subdf3 +DFOP(__mips16_subdf3, sub.d) +#endif +#ifdef L_m16muldf3 +DFOP(__mips16_muldf3, mul.d) +#endif +#ifdef L_m16divdf3 +DFOP(__mips16_divdf3, div.d) +#endif + +#define DFOP2(NAME, OPCODE) \ +STARTFN (NAME); \ + .set noreorder; \ + LDDBL1; \ + nop; \ + OPCODE $f0,$f12; \ + RETDBL; \ + j $31; \ + nop; \ + .set reorder; \ + ENDFN (NAME) + +#ifdef L_m16negdf2 +DFOP2(__mips16_negdf2, neg.d) +#endif +#ifdef L_m16absdf2 +DFOP2(__mips16_absdf2, abs.d) +#endif + + +/* Conversions between single and double precision. */ + +#ifdef L_m16extsfdf2 +STARTFN (__mips16_extendsfdf2) + .set noreorder + mtc1 $4,$f12 + nop + cvt.d.s $f0,$f12 + RETDBL + j $31 + nop + .set reorder + ENDFN (__mips16_extendsfdf2) +#endif + +#ifdef L_m16trdfsf2 +STARTFN (__mips16_truncdfsf2) + .set noreorder + LDDBL1 + nop + cvt.s.d $f0,$f12 + mfc1 $2,$f0 + j $31 + nop + .set reorder + ENDFN (__mips16_truncdfsf2) +#endif + +/* Double precision comparisons. */ + +/* This macro defines a function which loads two double precision + values, performs a floating point comparison, and returns the + specified values according to whether the comparison is true or + false. */ + +#define DFCMP(NAME, OPCODE, TRUE, FALSE) \ +STARTFN (NAME); \ + LDDBL1; \ + LDDBL2; \ + OPCODE $f12,$f14; \ + li $2,TRUE; \ + bc1t 1f; \ + li $2,FALSE; \ +1:; \ + j $31; \ + ENDFN (NAME) + +/* This macro is like DFCMP, but it reverses the comparison. */ + +#define DFREVCMP(NAME, OPCODE, TRUE, FALSE) \ +STARTFN (NAME); \ + LDDBL1; \ + LDDBL2; \ + OPCODE $f14,$f12; \ + li $2,TRUE; \ + bc1t 1f; \ + li $2,FALSE; \ +1:; \ + j $31; \ + ENDFN (NAME) + +#ifdef L_m16eqdf2 +DFCMP(__mips16_eqdf2, c.eq.d, 0, 1) +#endif +#ifdef L_m16nedf2 +DFCMP(__mips16_nedf2, c.eq.d, 0, 1) +#endif +#ifdef L_m16gtdf2 +DFREVCMP(__mips16_gtdf2, c.lt.d, 1, 0) +#endif +#ifdef L_m16gedf2 +DFREVCMP(__mips16_gedf2, c.le.d, 0, -1) +#endif +#ifdef L_m16ledf2 +DFCMP(__mips16_ledf2, c.le.d, 0, 1) +#endif +#ifdef L_m16ltdf2 +DFCMP(__mips16_ltdf2, c.lt.d, -1, 0) +#endif + +/* Double precision conversions. */ + +#ifdef L_m16fltsidf +STARTFN (__mips16_floatsidf) + .set noreorder + mtc1 $4,$f12 + nop + cvt.d.w $f0,$f12 + RETDBL + j $31 + nop + .set reorder + ENDFN (__mips16_floatsidf) +#endif + +#ifdef L_m16fix_truncdfsi +STARTFN (__mips16_fix_truncdfsi) + .set noreorder + LDDBL1 + nop + trunc.w.d $f0,$f12,$4 + mfc1 $2,$f0 + j $31 + nop + .set reorder + ENDFN (__mips16_fix_truncdfsi) +#endif +#endif /* !__mips_single_float */ + +/* These functions are used to return floating point values from + mips16 functions. In this case we can put mtc1 in a jump delay slot, + because we know that the next instruction will not refer to a floating + point register. */ + +#ifdef L_m16retsf +STARTFN (__mips16_ret_sf) + .set noreorder + j $31 + mtc1 $2,$f0 + .set reorder + ENDFN (__mips16_ret_sf) +#endif + +#if !defined(__mips_single_float) && !defined(__SINGLE_FLOAT) +#ifdef L_m16retdf +STARTFN (__mips16_ret_df) + .set noreorder +#if defined(__mips64) + j $31 + dmtc1 $2,$f0 +#elif defined(__mipsfp64) + sw $2,0($29) + sw $3,4($29) + l.d $f0,0($29) +#elif defined(__MIPSEB__) + mtc1 $2,$f1 + j $31 + mtc1 $3,$f0 +#else + mtc1 $2,$f0 + j $31 + mtc1 $3,$f1 +#endif + .set reorder + ENDFN (__mips16_ret_df) +#endif +#endif /* !__mips_single_float */ + +/* These functions are used by 16 bit code when calling via a function + pointer. They must copy the floating point arguments from the gp + regs into the fp regs. The function to call will be in $2. The + exact set of floating point arguments to copy is encoded in the + function name; the final number is an fp_code, as described in + mips.h in the comment about CUMULATIVE_ARGS. */ + +#ifdef L_m16stub1 +/* (float) */ +STARTFN (__mips16_call_stub_1) + .set noreorder + mtc1 $4,$f12 + j $2 + nop + .set reorder + ENDFN (__mips16_call_stub_1) +#endif + +#ifdef L_m16stub5 +/* (float, float) */ +STARTFN (__mips16_call_stub_5) + .set noreorder + mtc1 $4,$f12 + mtc1 $5,$f14 + j $2 + nop + .set reorder + ENDFN (__mips16_call_stub_5) +#endif + +#if !defined(__mips_single_float) && !defined(__SINGLE_FLOAT) + +#ifdef L_m16stub2 +/* (double) */ +STARTFN (__mips16_call_stub_2) + .set noreorder + LDDBL1 + j $2 + nop + .set reorder + ENDFN (__mips16_call_stub_2) +#endif + +#ifdef L_m16stub6 +/* (double, float) */ +STARTFN (__mips16_call_stub_6) + .set noreorder + LDDBL1 + mtc1 $6,$f14 + j $2 + nop + .set reorder + ENDFN (__mips16_call_stub_6) +#endif + +#ifdef L_m16stub9 +/* (float, double) */ +STARTFN (__mips16_call_stub_9) + .set noreorder + mtc1 $4,$f12 + LDDBL2 + j $2 + nop + .set reorder + ENDFN (__mips16_call_stub_9) +#endif + +#ifdef L_m16stub10 +/* (double, double) */ +STARTFN (__mips16_call_stub_10) + .set noreorder + LDDBL1 + LDDBL2 + j $2 + nop + .set reorder + ENDFN (__mips16_call_stub_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 value. 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. */ + +#ifdef L_m16stubsf0 +/* () */ +STARTFN (__mips16_call_stub_sf_0) + .set noreorder + move $18,$31 + jal $2 + nop + mfc1 $2,$f0 + j $18 + nop + .set reorder + ENDFN (__mips16_call_stub_sf_0) +#endif + +#ifdef L_m16stubsf1 +/* (float) */ +STARTFN (__mips16_call_stub_sf_1) + .set noreorder + mtc1 $4,$f12 + move $18,$31 + jal $2 + nop + mfc1 $2,$f0 + j $18 + nop + .set reorder + ENDFN (__mips16_call_stub_sf_1) +#endif + +#ifdef L_m16stubsf5 +/* (float, float) */ +STARTFN (__mips16_call_stub_sf_5) + .set noreorder + mtc1 $4,$f12 + mtc1 $5,$f14 + move $18,$31 + jal $2 + nop + mfc1 $2,$f0 + j $18 + nop + .set reorder + ENDFN (__mips16_call_stub_sf_5) +#endif + +#if !defined(__mips_single_float) && !defined(__SINGLE_FLOAT) +#ifdef L_m16stubsf2 +/* (double) */ +STARTFN (__mips16_call_stub_sf_2) + .set noreorder + LDDBL1 + move $18,$31 + jal $2 + nop + mfc1 $2,$f0 + j $18 + nop + .set reorder + ENDFN (__mips16_call_stub_sf_2) +#endif + +#ifdef L_m16stubsf6 +/* (double, float) */ +STARTFN (__mips16_call_stub_sf_6) + .set noreorder + LDDBL1 + mtc1 $6,$f14 + move $18,$31 + jal $2 + nop + mfc1 $2,$f0 + j $18 + nop + .set reorder + ENDFN (__mips16_call_stub_sf_6) +#endif + +#ifdef L_m16stubsf9 +/* (float, double) */ +STARTFN (__mips16_call_stub_sf_9) + .set noreorder + mtc1 $4,$f12 + LDDBL2 + move $18,$31 + jal $2 + nop + mfc1 $2,$f0 + j $18 + nop + .set reorder + ENDFN (__mips16_call_stub_sf_9) +#endif + +#ifdef L_m16stubsf10 +/* (double, double) */ +STARTFN (__mips16_call_stub_sf_10) + .set noreorder + LDDBL1 + LDDBL2 + move $18,$31 + jal $2 + nop + mfc1 $2,$f0 + j $18 + nop + .set reorder + ENDFN (__mips16_call_stub_sf_10) +#endif + +/* Now we have the same set of functions again, except that this time + the function being called returns an DFmode value. */ + +#ifdef L_m16stubdf0 +/* () */ +STARTFN (__mips16_call_stub_df_0) + .set noreorder + move $18,$31 + jal $2 + nop + RETDBL + j $18 + nop + .set reorder + ENDFN (__mips16_call_stub_df_0) +#endif + +#ifdef L_m16stubdf1 +/* (float) */ +STARTFN (__mips16_call_stub_df_1) + .set noreorder + mtc1 $4,$f12 + move $18,$31 + jal $2 + nop + RETDBL + j $18 + nop + .set reorder + ENDFN (__mips16_call_stub_df_1) +#endif + +#ifdef L_m16stubdf2 +/* (double) */ +STARTFN (__mips16_call_stub_df_2) + .set noreorder + LDDBL1 + move $18,$31 + jal $2 + nop + RETDBL + j $18 + nop + .set reorder + ENDFN (__mips16_call_stub_df_2) +#endif + +#ifdef L_m16stubdf5 +/* (float, float) */ +STARTFN (__mips16_call_stub_df_5) + .set noreorder + mtc1 $4,$f12 + mtc1 $5,$f14 + move $18,$31 + jal $2 + nop + RETDBL + j $18 + nop + .set reorder + ENDFN (__mips16_call_stub_df_5) +#endif + +#ifdef L_m16stubdf6 +/* (double, float) */ +STARTFN (__mips16_call_stub_df_6) + .set noreorder + LDDBL1 + mtc1 $6,$f14 + move $18,$31 + jal $2 + nop + RETDBL + j $18 + nop + .set reorder + ENDFN (__mips16_call_stub_df_6) +#endif + +#ifdef L_m16stubdf9 +/* (float, double) */ +STARTFN (__mips16_call_stub_df_9) + .set noreorder + mtc1 $4,$f12 + LDDBL2 + move $18,$31 + jal $2 + nop + RETDBL + j $18 + nop + .set reorder + ENDFN (__mips16_call_stub_df_9) +#endif + +#ifdef L_m16stubdf10 +/* (double, double) */ +STARTFN (__mips16_call_stub_df_10) + .set noreorder + LDDBL1 + LDDBL2 + move $18,$31 + jal $2 + nop + RETDBL + j $18 + nop + .set reorder + ENDFN (__mips16_call_stub_df_10) +#endif +#endif /* !__mips_single_float */ diff --git a/contrib/gcc/config/mips/netbsd.h b/contrib/gcc/config/mips/netbsd.h new file mode 100644 index 0000000..6b335ca --- /dev/null +++ b/contrib/gcc/config/mips/netbsd.h @@ -0,0 +1,203 @@ +/* Definitions of target machine for GNU compiler, for MIPS NetBSD systems. + Copyright (C) 1993, 1995, 1996, 1997, 1999, 2000, 2001, 2002, 2003, 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 2, 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 COPYING. If not, write to +the Free Software Foundation, 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. */ + + +/* 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) + + +/* Clean up after the generic MIPS/ELF configuration. */ +#undef MD_EXEC_PREFIX +#undef MD_STARTFILE_PREFIX + +/* 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} \ + %{bestGnum} %{call_shared} %{no_archive} %{exact_version} \ + %(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 + + +/* This defines which switch letters take arguments. -G is a MIPS + special. */ + +#undef SWITCH_TAKES_ARG +#define SWITCH_TAKES_ARG(CHAR) \ + (DEFAULT_SWITCH_TAKES_ARG (CHAR) \ + || (CHAR) == 'R' \ + || (CHAR) == 'G') + + +#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/contrib/gcc/config/mips/openbsd.h b/contrib/gcc/config/mips/openbsd.h new file mode 100644 index 0000000..69cc0c6 --- /dev/null +++ b/contrib/gcc/config/mips/openbsd.h @@ -0,0 +1,98 @@ +/* Configuration for a MIPS ABI32 OpenBSD target. + Copyright (C) 1999, 2003, 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 2, 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 COPYING. If not, write to +the Free Software Foundation, 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. */ + +/* Definitions needed for OpenBSD, to avoid picking mips 'defaults'. */ + +/* GAS must know this. */ +#undef SUBTARGET_ASM_SPEC +#define SUBTARGET_ASM_SPEC "%{fPIC|fPIE:-KPIC}" + +#define AS_NEEDS_DASH_FOR_PIPED_INPUT + +/* 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 "unsigned int" + +#undef PTRDIFF_TYPE +#define PTRDIFF_TYPE "int" + +#undef WCHAR_TYPE +#define WCHAR_TYPE "int" + +#undef WCHAR_TYPE_SIZE +#define WCHAR_TYPE_SIZE 32 + +/* 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} \ + %{bestGnum} %{shared} %{non_shared} \ + %{call_shared} %{no_archive} %{exact_version} \ + %{!shared: %{!non_shared: %{!call_shared: -non_shared}}} \ + %{!dynamic-linker:-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 diff --git a/contrib/gcc/config/mips/predicates.md b/contrib/gcc/config/mips/predicates.md new file mode 100644 index 0000000..9a6756c --- /dev/null +++ b/contrib/gcc/config/mips/predicates.md @@ -0,0 +1,286 @@ +;; Predicate definitions for MIPS. +;; 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 2, 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 COPYING. If not, write to +;; the Free Software Foundation, 51 Franklin Street, Fifth Floor, +;; Boston, MA 02110-1301, USA. + +(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 "fpr_operand" + (and (match_code "reg") + (match_test "FP_REG_P (REGNO (op))"))) + +(define_predicate "lo_operand" + (and (match_code "reg") + (match_test "REGNO (op) == LO_REGNUM"))) + +(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_type)) + return false; + + switch (symbol_type) + { + case SYMBOL_GENERAL: + /* We can only use direct calls for TARGET_ABSOLUTE_ABICALLS if we + are sure that the target function does not need $25 to be live + on entry. This is true for any locally-defined function because + any such function will use %hi/%lo accesses to set up $gp. */ + if (TARGET_ABSOLUTE_ABICALLS + && !(GET_CODE (op) == SYMBOL_REF + && SYMBOL_REF_DECL (op) + && !DECL_EXTERNAL (SYMBOL_REF_DECL (op)))) + return false; + + /* If -mlong-calls, force all calls to use register addressing. Also, + if this function has the long_call attribute, we must use register + addressing. */ + return !TARGET_LONG_CALLS && !SYMBOL_REF_LONG_CALL_P (op); + + case SYMBOL_GOT_GLOBAL: + /* 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); +}) + +;; A legitimate symbolic operand that takes more than one instruction +;; to load. +(define_predicate "splittable_symbolic_operand" + (match_code "const,symbol_ref,label_ref") +{ + enum mips_symbol_type symbol_type; + return (mips_symbolic_constant_p (op, &symbol_type) + && mips_split_p[symbol_type]); +}) + +(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_type) + && !mips_split_p[symbol_type]); + + default: + return 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, &type); +}) + +(define_predicate "general_symbolic_operand" + (match_code "const,symbol_ref,label_ref") +{ + enum mips_symbol_type type; + return mips_symbolic_constant_p (op, &type) && type == SYMBOL_GENERAL; +}) + +(define_predicate "global_got_operand" + (match_code "const,symbol_ref,label_ref") +{ + enum mips_symbol_type type; + return mips_symbolic_constant_p (op, &type) && type == SYMBOL_GOT_GLOBAL; +}) + +(define_predicate "local_got_operand" + (match_code "const,symbol_ref,label_ref") +{ + enum mips_symbol_type type; + return mips_symbolic_constant_p (op, &type) && type == SYMBOL_GOT_LOCAL; +}) + +(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")) + + +(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/contrib/gcc/config/mips/r3900.h b/contrib/gcc/config/mips/r3900.h new file mode 100644 index 0000000..d524689 --- /dev/null +++ b/contrib/gcc/config/mips/r3900.h @@ -0,0 +1,37 @@ +/* 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 + 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 2, 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 COPYING. If not, write to +the Free Software Foundation, 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. */ + +#define MIPS_CPU_STRING_DEFAULT "r3900" +#define MIPS_ISA_DEFAULT 1 + +#define MULTILIB_DEFAULTS { MULTILIB_ENDIAN_DEFAULT, "msoft-float" } + +/* We use the MIPS EABI by default. */ +#define MIPS_ABI_DEFAULT ABI_EABI + +/* By default (if not mips-something-else) produce code for the r3900 */ +#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/contrib/gcc/config/mips/rtems.h b/contrib/gcc/config/mips/rtems.h new file mode 100644 index 0000000..0143de7 --- /dev/null +++ b/contrib/gcc/config/mips/rtems.h @@ -0,0 +1,36 @@ +/* Definitions for rtems targeting a MIPS using ELF. + Copyright (C) 1996, 1997, 1998, 1999, 2000, 2002, 2003, 2005 + 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 2, 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 COPYING. If not, write to +the Free Software Foundation, 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. */ + +/* 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/contrib/gcc/config/mips/sb1.md b/contrib/gcc/config/mips/sb1.md new file mode 100644 index 0000000..7db31ef --- /dev/null +++ b/contrib/gcc/config/mips/sb1.md @@ -0,0 +1,564 @@ +;; +;; 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")) + "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")) + "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") + (and (eq_attr "type" "xfer") + (match_operand 0 "fpr_operand"))) + "sb1_fp0") + +;; mfc1 latency 1 cycle. + +(define_insn_reservation "ir_sb1_mfxfer" 1 + (and (eq_attr "cpu" "sb1,sb1a") + (and (eq_attr "type" "xfer") + (not (match_operand 0 "fpr_operand")))) + "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/contrib/gcc/config/mips/sdb.h b/contrib/gcc/config/mips/sdb.h new file mode 100644 index 0000000..a26826e --- /dev/null +++ b/contrib/gcc/config/mips/sdb.h @@ -0,0 +1,88 @@ +/* Generate SDB debugging info. + Copyright (C) 2003, 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 2, 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 COPYING. If not, write to the Free +Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301, USA. */ + +/* 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/contrib/gcc/config/mips/sr71k.md b/contrib/gcc/config/mips/sr71k.md new file mode 100644 index 0000000..268bc48 --- /dev/null +++ b/contrib/gcc/config/mips/sr71k.md @@ -0,0 +1,321 @@ +;; ......................... +;; +;; 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") + (and (eq_attr "type" "xfer") + (eq_attr "mode" "!SF,DF,FPSW"))) + "(cpu_iss+cp1_iss),(fpu_mov+ri_mem)") + +(define_insn_reservation "ir_sr70_xfer_to" 9 + (and (eq_attr "cpu" "sr71000") + (and (eq_attr "type" "xfer") + (eq_attr "mode" "SF,DF"))) + "(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,slt,clz,const,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/contrib/gcc/config/mips/t-elf b/contrib/gcc/config/mips/t-elf new file mode 100644 index 0000000..f2da07d --- /dev/null +++ b/contrib/gcc/config/mips/t-elf @@ -0,0 +1,40 @@ +# 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 + +LIB1ASMSRC = mips/mips16.S +LIB1ASMFUNCS = _m16addsf3 _m16subsf3 _m16mulsf3 _m16divsf3 \ + _m16eqsf2 _m16nesf2 _m16gtsf2 _m16gesf2 _m16lesf2 _m16ltsf2 \ + _m16fltsisf _m16fix_truncsfsi \ + _m16adddf3 _m16subdf3 _m16muldf3 _m16divdf3 \ + _m16extsfdf2 _m16trdfsf2 \ + _m16eqdf2 _m16nedf2 _m16gtdf2 _m16gedf2 _m16ledf2 _m16ltdf2 \ + _m16fltsidf _m16fix_truncdfsi \ + _m16retsf _m16retdf \ + _m16stub1 _m16stub2 _m16stub5 _m16stub6 _m16stub9 _m16stub10 \ + _m16stubsf0 _m16stubsf1 _m16stubsf2 _m16stubsf5 _m16stubsf6 \ + _m16stubsf9 _m16stubsf10 \ + _m16stubdf0 _m16stubdf1 _m16stubdf2 _m16stubdf5 _m16stubdf6 \ + _m16stubdf9 _m16stubdf10 + +# 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/contrib/gcc/config/mips/t-gofast b/contrib/gcc/config/mips/t-gofast new file mode 100644 index 0000000..a6c60d4 --- /dev/null +++ b/contrib/gcc/config/mips/t-gofast @@ -0,0 +1,19 @@ +# 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 US_SOFTWARE_GOFAST' >> 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 US_SOFTWARE_GOFAST' >> fp-bit.c + cat $(srcdir)/config/fp-bit.c >> fp-bit.c diff --git a/contrib/gcc/config/mips/t-iris b/contrib/gcc/config/mips/t-iris new file mode 100644 index 0000000..4a7143f --- /dev/null +++ b/contrib/gcc/config/mips/t-iris @@ -0,0 +1,12 @@ +# Find all of the declarations from the header files +FIXPROTO_DEFINES = -D__EXTENSIONS__ -D_SGI_SOURCE -D_LANGUAGE_C_PLUS_PLUS + +$(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/contrib/gcc/config/mips/t-iris6 b/contrib/gcc/config/mips/t-iris6 new file mode 100644 index 0000000..5155472 --- /dev/null +++ b/contrib/gcc/config/mips/t-iris6 @@ -0,0 +1,19 @@ +MULTILIB_OPTIONS=mabi=n32/mabi=32/mabi=64 +MULTILIB_DIRNAMES=n32 32 64 +MULTILIB_MATCHES= +MULTILIB_OSDIRNAMES=../lib32 ../lib ../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/contrib/gcc/config/mips/t-isa3264 b/contrib/gcc/config/mips/t-isa3264 new file mode 100644 index 0000000..6604fbe --- /dev/null +++ b/contrib/gcc/config/mips/t-isa3264 @@ -0,0 +1,40 @@ +# 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 + +LIB1ASMSRC = mips/mips16.S +LIB1ASMFUNCS = _m16addsf3 _m16subsf3 _m16mulsf3 _m16divsf3 \ + _m16eqsf2 _m16nesf2 _m16gtsf2 _m16gesf2 _m16lesf2 _m16ltsf2 \ + _m16fltsisf _m16fix_truncsfsi \ + _m16adddf3 _m16subdf3 _m16muldf3 _m16divdf3 \ + _m16extsfdf2 _m16trdfsf2 \ + _m16eqdf2 _m16nedf2 _m16gtdf2 _m16gedf2 _m16ledf2 _m16ltdf2 \ + _m16fltsidf _m16fix_truncdfsi \ + _m16retsf _m16retdf \ + _m16stub1 _m16stub2 _m16stub5 _m16stub6 _m16stub9 _m16stub10 \ + _m16stubsf0 _m16stubsf1 _m16stubsf2 _m16stubsf5 _m16stubsf6 \ + _m16stubsf9 _m16stubsf10 \ + _m16stubdf0 _m16stubdf1 _m16stubdf2 _m16stubdf5 _m16stubdf6 \ + _m16stubdf9 _m16stubdf10 + +# 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 mips32/mips32r2/mips64 +MULTILIB_DIRNAMES = soft-float el eb mips32 mips32r2 mips64 +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/contrib/gcc/config/mips/t-linux64 b/contrib/gcc/config/mips/t-linux64 new file mode 100644 index 0000000..0592e7f --- /dev/null +++ b/contrib/gcc/config/mips/t-linux64 @@ -0,0 +1,17 @@ +MULTILIB_OPTIONS = mabi=n32/mabi=32/mabi=64 +MULTILIB_DIRNAMES = n32 32 64 +MULTILIB_OSDIRNAMES = ../lib32 ../lib ../lib64 + +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/contrib/gcc/config/mips/t-mips b/contrib/gcc/config/mips/t-mips new file mode 100644 index 0000000..c431dcc --- /dev/null +++ b/contrib/gcc/config/mips/t-mips @@ -0,0 +1,23 @@ +# 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/contrib/gcc/config/mips/t-r3900 b/contrib/gcc/config/mips/t-r3900 new file mode 100644 index 0000000..3bc8c47 --- /dev/null +++ b/contrib/gcc/config/mips/t-r3900 @@ -0,0 +1,31 @@ +LIB1ASMSRC = mips/mips16.S +LIB1ASMFUNCS = _m16addsf3 _m16subsf3 _m16mulsf3 _m16divsf3 \ + _m16eqsf2 _m16nesf2 _m16gtsf2 _m16gesf2 _m16lesf2 _m16ltsf2 \ + _m16fltsisf _m16fix_truncsfsi \ + _m16adddf3 _m16subdf3 _m16muldf3 _m16divdf3 \ + _m16extsfdf2 _m16trdfsf2 \ + _m16eqdf2 _m16nedf2 _m16gtdf2 _m16gedf2 _m16ledf2 _m16ltdf2 \ + _m16fltsidf _m16fix_truncdfsi \ + _m16retsf _m16retdf \ + _m16stub1 _m16stub2 _m16stub5 _m16stub6 _m16stub9 _m16stub10 \ + _m16stubsf0 _m16stubsf1 _m16stubsf2 _m16stubsf5 _m16stubsf6 \ + _m16stubsf9 _m16stubsf10 \ + _m16stubdf0 _m16stubdf1 _m16stubdf2 _m16stubdf5 _m16stubdf6 \ + _m16stubdf9 _m16stubdf10 + +# 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/contrib/gcc/config/mips/t-rtems b/contrib/gcc/config/mips/t-rtems new file mode 100644 index 0000000..b012100 --- /dev/null +++ b/contrib/gcc/config/mips/t-rtems @@ -0,0 +1,16 @@ +# Custom multilibs for RTEMS + +# 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/contrib/gcc/config/mips/t-sb1 b/contrib/gcc/config/mips/t-sb1 new file mode 100644 index 0000000..b181bb7 --- /dev/null +++ b/contrib/gcc/config/mips/t-sb1 @@ -0,0 +1,44 @@ +# 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/contrib/gcc/config/mips/t-slibgcc-irix b/contrib/gcc/config/mips/t-slibgcc-irix new file mode 100644 index 0000000..cfb4bf4 --- /dev/null +++ b/contrib/gcc/config/mips/t-slibgcc-irix @@ -0,0 +1,34 @@ +# 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/contrib/gcc/config/mips/t-sr71k b/contrib/gcc/config/mips/t-sr71k new file mode 100644 index 0000000..2b2c27a --- /dev/null +++ b/contrib/gcc/config/mips/t-sr71k @@ -0,0 +1,51 @@ +# 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 + echo '#define US_SOFTWARE_GOFAST' >> 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 US_SOFTWARE_GOFAST' >> 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/contrib/gcc/config/mips/t-vr b/contrib/gcc/config/mips/t-vr new file mode 100644 index 0000000..9c046b0 --- /dev/null +++ b/contrib/gcc/config/mips/t-vr @@ -0,0 +1,112 @@ +# 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/contrib/gcc/config/mips/t-vxworks b/contrib/gcc/config/mips/t-vxworks new file mode 100644 index 0000000..51e006a --- /dev/null +++ b/contrib/gcc/config/mips/t-vxworks @@ -0,0 +1,16 @@ +# Multilibs for VxWorks. + +# default is mips1 EB hard-float +MULTILIB_OPTIONS = mips2/mips3 EL msoft-float +MULTILIB_MATCHES = EL=mel mips2=mips32 mips3=mips4 mips3=mips64 + +MULTILIB_EXCEPTIONS = EL EL/msoft-float mips3/msoft-float mips3/EL/msoft-float + +MUTLILIB_EXTRA_OPTS = -G 0 -mno-branch-likely + +MULTILIB_OSDIRNAMES = msoft-float=!MIPS32sfr3kgnu \ + mips2=!MIPS32gnu mips2/msoft-float=!MIPS32sfgnu \ + mips2/EL=!MIPS32gnule \ + mips2/EL/msoft-float=!MIPS32sfgnule \ + mips3=!MIPS64gnu mips3/EL=!MIPS64gnule + diff --git a/contrib/gcc/config/mips/vr.h b/contrib/gcc/config/mips/vr.h new file mode 100644 index 0000000..a723a48 --- /dev/null +++ b/contrib/gcc/config/mips/vr.h @@ -0,0 +1,53 @@ +/* Definitions of target machine for GNU compiler. + NEC VR Series Processors + Copyright (c) 2002, 2004, 2005 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 2, 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 COPYING. If not, write to +the Free Software Foundation, 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. */ + +#define DEFAULT_VR_ARCH "mfix-vr4130" +#define MIPS_ABI_DEFAULT ABI_EABI +#define MIPS_MARCH_CONTROLS_SOFT_FLOAT 1 +#define MULTILIB_DEFAULTS \ + { MULTILIB_ENDIAN_DEFAULT, \ + MULTILIB_ABI_DEFAULT, \ + DEFAULT_VR_ARCH } + +#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}}", \ + \ + /* 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}" diff --git a/contrib/gcc/config/mips/vr4120-div.S b/contrib/gcc/config/mips/vr4120-div.S new file mode 100644 index 0000000..1cef997 --- /dev/null +++ b/contrib/gcc/config/mips/vr4120-div.S @@ -0,0 +1,75 @@ +/* Support file for -mfix-vr4120. + Copyright (C) 2002, 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 2, 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 COPYING. If not, write to +the Free Software Foundation, 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. */ + +/* 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/contrib/gcc/config/mips/vxworks.h b/contrib/gcc/config/mips/vxworks.h new file mode 100644 index 0000000..bf37901 --- /dev/null +++ b/contrib/gcc/config/mips/vxworks.h @@ -0,0 +1,68 @@ +/* Copyright (C) 1999, 2003, 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 2, 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 COPYING. If not, write to +the Free Software Foundation, 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. */ + +#undef TARGET_VERSION +#define TARGET_VERSION fprintf (stderr, " (MIPS, VxWorks syntax)"); + +/* Combination of mips.h and svr4.h. */ +#undef SWITCH_TAKES_ARG +#define SWITCH_TAKES_ARG(CHAR) \ + (DEFAULT_SWITCH_TAKES_ARG (CHAR) \ + || (CHAR) == 'G' \ + || (CHAR) == 'h' \ + || (CHAR) == 'x' \ + || (CHAR) == 'z') + +#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=*} %{v} \ +%(subtarget_asm_spec)" + +#undef LINK_SPEC +/* LINK_SPEC is clobbered in svr4.h. ugh! */ +#define LINK_SPEC "\ +%(endian_spec) \ +%{!G:-G 0} %{G*} %{mips1} %{mips2} %{mips3} %{mips4} %{mips32} %{mips64} \ +%{bestGnum}" + +#define TARGET_OS_CPP_BUILTINS() \ + do \ + { \ + builtin_define ("__vxworks"); \ + builtin_assert ("system=unix"); \ + } \ + while (0) + +#undef SUBTARGET_CPP_SPEC +#define SUBTARGET_CPP_SPEC \ +"%{!DCPU=*: %{mips3|mips4|mips64:-DCPU=MIPS64;:-DCPU=MIPS32}} \ + %{EL|mel:-DMIPSEL;:-DMIPSEB} \ + %{msoft-float:-DSOFT_FLOAT} \ + %{mips1:-D_WRS_R3K_EXC_SUPPORT}" + +/* No sdata. */ +#undef MIPS_DEFAULT_GVALUE +#define MIPS_DEFAULT_GVALUE 0 diff --git a/contrib/gcc/config/mips/windiss.h b/contrib/gcc/config/mips/windiss.h new file mode 100644 index 0000000..bd69621 --- /dev/null +++ b/contrib/gcc/config/mips/windiss.h @@ -0,0 +1,101 @@ +/* Support for GCC on MIPS using WindISS simulator. + Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. + Contributed by CodeSourcery, LLC. + +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 2, 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 COPYING. If not, write to +the Free Software Foundation, 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. */ + +#undef TARGET_VERSION +#define TARGET_VERSION fprintf (stderr, " (MIPS WindISS)"); + +/* Combination of mips.h and svr4.h. */ +#undef SWITCH_TAKES_ARG +#define SWITCH_TAKES_ARG(CHAR) \ + (DEFAULT_SWITCH_TAKES_ARG (CHAR) \ + || (CHAR) == 'G' \ + || (CHAR) == 'h' \ + || (CHAR) == 'x' \ + || (CHAR) == 'z') + +#undef SUBTARGET_CPP_SPEC +#define SUBTARGET_CPP_SPEC \ +"%{!DCPU=*: %{mips3|mips4|mips64:-DCPU=MIPS64;:-DCPU=MIPS32}} \ + %{EL|mel:-DMIPSEL;:-DMIPSEB} \ + %{msoft-float:-DSOFT_FLOAT} \ + %{mips1:-D_WRS_R3K_EXC_SUPPORT}" + +#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=*} %{v} \ +%(subtarget_asm_spec)" + +#undef LINK_SPEC +/* LINK_SPEC is clobbered in svr4.h. ugh! */ +#define LINK_SPEC "\ +-m elf32mipswindiss \ +%{!G:-G 0} %{G*} %{mips1} %{mips2} %{mips3} %{mips4} %{mips32} %{mips64} \ +%{bestGnum}" + +/* Diab libs MIPS{,E,F,L,M,W,X,Y,Z}{,H,N,S} + + . + E - Elf (small-data/const=8 + F - Elf Far (small-data/const=0) + L - Little Elf + M - Little Elf Far + W - elf32 bigmips + X - elf32 bigmips (far?) + Y - elf32 littlemips + Z - elf32 littlemips (far?) + + . - Integer routines + H - Hard float + N - No float + S - Soft float + + Want {F,M}{,H,S} + +*/ + +#undef LIB_SPEC +#define LIB_SPEC "--start-group -li -lcfp -lwindiss -lram -limpl -limpfp --end-group" + +#undef STARTFILE_SPEC +#define STARTFILE_SPEC "crt0.o%s crtbegin.o%s" + +#undef ENDFILE_SPEC +#define ENDFILE_SPEC "crtend.o%s" + +/* We have no shared libraries. These two shouldn't be necessary. */ +#undef LINK_SHLIB_SPEC +#define LINK_SHLIB_SPEC "" +#undef LINK_EH_SPEC +#define LINK_EH_SPEC "" + +#undef CRTSAVRES_DEFAULT_SPEC +#define CRTSAVRES_DEFAULT_SPEC "" + +/* No sdata. */ +#undef MIPS_DEFAULT_GVALUE +#define MIPS_DEFAULT_GVALUE 0 |