| ;; 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") |