summaryrefslogtreecommitdiff
path: root/gcc/config/mips/mips.md
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/config/mips/mips.md')
-rwxr-xr-xgcc/config/mips/mips.md10710
1 files changed, 10710 insertions, 0 deletions
diff --git a/gcc/config/mips/mips.md b/gcc/config/mips/mips.md
new file mode 100755
index 0000000..7e18299
--- /dev/null
+++ b/gcc/config/mips/mips.md
@@ -0,0 +1,10710 @@
+;; Mips.md Machine Description for MIPS based processors
+;; 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.
+;; Copyright (C) 1989, 90-98, 1999 Free Software Foundation, Inc.
+
+;; This file is part of GNU CC.
+
+;; GNU CC 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.
+
+;; GNU CC 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 GNU CC; see the file COPYING. If not, write to
+;; the Free Software Foundation, 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;; ??? Currently does not have define_function_unit support for the R8000.
+;; Must include new entries for fmadd in addition to existing entries.
+
+
+
+;; ....................
+;; Attributes
+;; ....................
+
+;; Classification of each insn.
+;; branch conditional branch
+;; jump unconditional jump
+;; call unconditional call
+;; load load instruction(s)
+;; store store instruction(s)
+;; move data movement within same register set
+;; xfer transfer to/from coprocessor
+;; hilo transfer of hi/lo registers
+;; arith integer arithmetic instruction
+;; darith double precision integer arithmetic instructions
+;; imul integer multiply
+;; idiv integer divide
+;; icmp integer compare
+;; fadd floating point add/subtract
+;; fmul floating point multiply
+;; fmadd floating point multiply-add
+;; fdiv floating point divide
+;; 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 CYGNUS LOCAL vr5400/raeburn
+;; multi multiword sequence (or user asm statements)
+;; nop no operation
+
+(define_attr "type"
+ "unknown,branch,jump,call,load,store,move,xfer,hilo,arith,darith,imul,idiv,icmp,fadd,fmul,fmadd,fdiv,fabs,fneg,fcmp,fcvt,fsqrt,frsqrt,multi,nop" ; CYGNUS LOCAL vr5400/raeburn
+ (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"))
+
+;; # instructions (4 bytes each)
+(define_attr "length" "" (const_int 1))
+
+;; Attribute describing the processor. This attribute must match exactly
+;; with the processor_type enumeration in mips.h.
+
+;; Attribute describing the processor
+;; (define_attr "cpu" "default,r3000,r6000,r4000"
+;; (const
+;; (cond [(eq (symbol_ref "mips_cpu") (symbol_ref "PROCESSOR_R3000")) (const_string "r3000")
+;; (eq (symbol_ref "mips_cpu") (symbol_ref "PROCESSOR_R4000")) (const_string "r4000")
+;; (eq (symbol_ref "mips_cpu") (symbol_ref "PROCESSOR_R6000")) (const_string "r6000")]
+;; (const_string "default"))))
+
+;; ??? Fix everything that tests this attribute.
+(define_attr "cpu"
+ "default,r3000,r3900,r6000,r4000,r4100,r4300,r4600,r4650,r5000,r5400,r8000" ; CYGNUS LOCAL vr5400/raeburn
+ (const (symbol_ref "mips_cpu_attr")))
+
+;; Does the instruction have a mandatory delay slot?
+;; The 3900, is (mostly) mips1, but does not have a manditory load delay
+;; slot.
+(define_attr "dslot" "no,yes"
+ (if_then_else (ior (eq_attr "type" "branch,jump,call,xfer,hilo,fcmp")
+ (and (eq_attr "type" "load")
+ (and (eq (symbol_ref "mips_isa") (const_int 1))
+ (and (eq (symbol_ref "mips16") (const_int 0))
+ (eq_attr "cpu" "!r3900")))))
+ (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"))))
+
+
+;; Describe a user's asm statement.
+(define_asm_attributes
+ [(set_attr "type" "multi")])
+
+;; whether or not generating calls to position independent functions
+(define_attr "abicalls" "no,yes"
+ (const (symbol_ref "mips_abicalls_attr")))
+
+
+
+;; .........................
+;; Delay slots, can't describe load/fcmp/xfer delay slots here
+;; .........................
+
+(define_delay (and (eq_attr "type" "branch")
+ (eq (symbol_ref "mips16") (const_int 0)))
+ [(and (eq_attr "dslot" "no") (eq_attr "length" "1"))
+ (nil)
+ (and (eq_attr "branch_likely" "yes") (and (eq_attr "dslot" "no") (eq_attr "length" "1")))])
+
+(define_delay (eq_attr "type" "jump")
+ [(and (eq_attr "dslot" "no") (eq_attr "length" "1"))
+ (nil)
+ (nil)])
+
+(define_delay (and (eq_attr "type" "call") (eq_attr "abicalls" "no"))
+ [(and (eq_attr "dslot" "no") (eq_attr "length" "1"))
+ (nil)
+ (nil)])
+
+
+
+;; .........................
+;; Functional units
+;; .........................
+
+; (define_function_unit NAME MULTIPLICITY SIMULTANEITY
+; TEST READY-DELAY ISSUE-DELAY [CONFLICT-LIST])
+
+;; Make the default case (PROCESSOR_DEFAULT) handle the worst case
+
+(define_function_unit "memory" 1 0
+ (and (eq_attr "type" "load")
+ (eq_attr "cpu" "!r3000,r3900,r4600,r4650,r4100,r4300,r5000,r5400")) ; CYGNUS LOCAL vr5400/raeburn
+ 3 0)
+
+(define_function_unit "memory" 1 0
+ (and (eq_attr "type" "load")
+ (eq_attr "cpu" "r3000,r3900,r4600,r4650,r4100,r4300,r5000,r5400")) ; CYGNUS LOCAL vr5400/raeburn
+ 2 0)
+
+;; CYGNUS LOCAL law
+(define_function_unit "memory" 1 0
+ (and (eq_attr "type" "store") (const_int 1)) 1 0) ;
+
+(define_function_unit "memory" 1 0
+ (and (eq_attr "type" "xfer") (const_int 1)) 2 0) ;
+
+(define_function_unit "imuldiv" 1 0
+ (and (eq_attr "type" "hilo")
+ (const_int 1)) ;
+ 1 3)
+
+(define_function_unit "imuldiv" 1 0
+ (and (eq_attr "type" "imul")
+ (eq_attr "cpu" "!r3000,r3900,r4000,r4600,r4650,r4100,r4300,r5000,r5400")) ; CYGNUS LOCAL vr5400/raeburn
+ 17 17)
+;; END CYGNUS LOCAL
+
+;; On them mips16, we want to stronly discourage a mult from appearing
+;; after an mflo, since that requires explicit nop instructions. We
+;; do this by pretending that mflo ties up the function unit for long
+;; enough that the scheduler will ignore load stalls and the like when
+;; selecting instructions to between the two instructions.
+
+(define_function_unit "imuldiv" 1 0
+ (and (eq_attr "type" "hilo") (ne (symbol_ref "mips16") (const_int 0)))
+ 1 5)
+
+(define_function_unit "imuldiv" 1 0
+ (and (eq_attr "type" "imul") (eq_attr "cpu" "r3000,r3900"))
+ 12 12)
+
+(define_function_unit "imuldiv" 1 0
+ (and (eq_attr "type" "imul") (eq_attr "cpu" "r4000,r4600"))
+ 10 10)
+
+(define_function_unit "imuldiv" 1 0
+ (and (eq_attr "type" "imul") (eq_attr "cpu" "r4650"))
+ 4 4)
+
+(define_function_unit "imuldiv" 1 0
+ (and (eq_attr "type" "imul")
+ (and (eq_attr "mode" "SI") (eq_attr "cpu" "r4100")))
+ 1 1)
+
+(define_function_unit "imuldiv" 1 0
+ (and (eq_attr "type" "imul")
+ (and (eq_attr "mode" "DI") (eq_attr "cpu" "r4100")))
+ 4 4)
+
+(define_function_unit "imuldiv" 1 0
+ (and (eq_attr "type" "imul")
+ (and (eq_attr "mode" "SI") (eq_attr "cpu" "r4300,r5000"))) ;
+ 5 5)
+
+(define_function_unit "imuldiv" 1 0
+ (and (eq_attr "type" "imul")
+ (and (eq_attr "mode" "DI") (eq_attr "cpu" "r4300"))) ;
+ 8 8)
+
+(define_function_unit "imuldiv" 1 0
+ (and (eq_attr "type" "imul")
+ (and (eq_attr "mode" "DI") (eq_attr "cpu" "r5000")))
+ 9 9)
+
+;; CYGNUS LOCAL vr5400/raeburn
+(define_function_unit "imuldiv" 1 0
+ (and (eq_attr "type" "imul")
+ (and (eq_attr "mode" "SI") (eq_attr "cpu" "r5400")))
+ 3 1)
+
+(define_function_unit "imuldiv" 1 0
+ (and (eq_attr "type" "imul")
+ (and (eq_attr "mode" "DI") (eq_attr "cpu" "r5400")))
+ 4 2)
+;; END CYGNUS LOCAL
+
+(define_function_unit "imuldiv" 1 0
+ (and (eq_attr "type" "idiv")
+ (eq_attr "cpu" "!r3000,r3900,r4000,r4600,r4650,r4100,r4300,r5000,r5400")) ; CYGNUS LOCAL vr5400/raeburn
+ 38 38)
+
+(define_function_unit "imuldiv" 1 0
+ (and (eq_attr "type" "idiv") (eq_attr "cpu" "r3000,r3900"))
+ 35 35)
+
+(define_function_unit "imuldiv" 1 0
+ (and (eq_attr "type" "idiv") (eq_attr "cpu" "r4600"))
+ 42 42)
+
+(define_function_unit "imuldiv" 1 0
+ (and (eq_attr "type" "idiv") (eq_attr "cpu" "r4650"))
+ 36 36)
+
+(define_function_unit "imuldiv" 1 0
+ (and (eq_attr "type" "idiv") (eq_attr "cpu" "r4000"))
+ 69 69)
+
+(define_function_unit "imuldiv" 1 0
+ (and (eq_attr "type" "idiv")
+ (and (eq_attr "mode" "SI") (eq_attr "cpu" "r4100")))
+ 35 35)
+
+(define_function_unit "imuldiv" 1 0
+ (and (eq_attr "type" "idiv")
+ (and (eq_attr "mode" "DI") (eq_attr "cpu" "r4100")))
+ 67 67)
+
+(define_function_unit "imuldiv" 1 0
+ (and (eq_attr "type" "idiv")
+ (and (eq_attr "mode" "SI") (eq_attr "cpu" "r4300"))) ;
+ 37 37)
+
+(define_function_unit "imuldiv" 1 0
+ (and (eq_attr "type" "idiv")
+ (and (eq_attr "mode" "DI") (eq_attr "cpu" "r4300"))) ;
+ 69 69)
+
+(define_function_unit "imuldiv" 1 0
+ (and (eq_attr "type" "idiv")
+ (and (eq_attr "mode" "SI") (eq_attr "cpu" "r5000")))
+ 36 36)
+
+(define_function_unit "imuldiv" 1 0
+ (and (eq_attr "type" "idiv")
+ (and (eq_attr "mode" "DI") (eq_attr "cpu" "r5000")))
+ 68 68)
+
+;; CYGNUS LOCAL vr5400/raeburn
+(define_function_unit "imuldiv" 1 0
+ (and (eq_attr "type" "idiv")
+ (and (eq_attr "mode" "SI") (eq_attr "cpu" "r5400")))
+ 34 34)
+
+(define_function_unit "imuldiv" 1 0
+ (and (eq_attr "type" "idiv")
+ (and (eq_attr "mode" "DI") (eq_attr "cpu" "r5400")))
+ 66 66)
+;; END CYGNUS LOCAL
+
+;; The R4300 does *NOT* have a separate Floating Point Unit, instead
+;; the FP hardware is part of the normal ALU circuitry. This means FP
+;; instructions affect the pipe-line, and no functional unit
+;; parallelism can occur on R4300 processors. To force GCC into coding
+;; for only a single functional unit, we force the R4300 FP
+;; instructions to be processed in the "imuldiv" unit.
+
+(define_function_unit "adder" 1 1
+ (and (eq_attr "type" "fcmp") (eq_attr "cpu" "!r3000,r3900,r6000,r4300,r5000,r5400")) ; CYGNUS LOCAL vr5400/raeburn
+ 3 0)
+
+(define_function_unit "adder" 1 1
+ (and (eq_attr "type" "fcmp") (eq_attr "cpu" "r3000,r3900,r6000"))
+ 2 0)
+
+(define_function_unit "adder" 1 1
+ (and (eq_attr "type" "fcmp") (eq_attr "cpu" "r5000"))
+ 1 0)
+
+(define_function_unit "adder" 1 1
+ (and (eq_attr "type" "fadd") (eq_attr "cpu" "!r3000,r3900,r6000,r4300,r5400")) ; CYGNUS LOCAL vr5400/raeburn
+ 4 0)
+
+(define_function_unit "adder" 1 1
+ (and (eq_attr "type" "fadd") (eq_attr "cpu" "r3000,r3900"))
+ 2 0)
+
+(define_function_unit "adder" 1 1
+ (and (eq_attr "type" "fadd") (eq_attr "cpu" "r6000"))
+ 3 0)
+
+(define_function_unit "adder" 1 1
+ (and (eq_attr "type" "fabs,fneg")
+ (eq_attr "cpu" "!r3000,r3900,r4600,r4650,r4300,r5000,r5400")) ; CYGNUS LOCAL vr5400/raeburn
+ 2 0)
+
+(define_function_unit "adder" 1 1
+ (and (eq_attr "type" "fabs,fneg") (eq_attr "cpu" "r3000,r3900,r4600,r4650,r5000"))
+ 1 0)
+
+(define_function_unit "mult" 1 1
+ (and (eq_attr "type" "fmul")
+ (and (eq_attr "mode" "SF")
+ (eq_attr "cpu" "!r3000,r3900,r6000,r4600,r4650,r4300,r5000,r5400"))) ; CYGNUS LOCAL vr5400/raeburn
+ 7 0)
+
+(define_function_unit "mult" 1 1
+ (and (eq_attr "type" "fmul")
+ (and (eq_attr "mode" "SF") (eq_attr "cpu" "r3000,r3900,r5000")))
+ 4 0)
+
+(define_function_unit "mult" 1 1
+ (and (eq_attr "type" "fmul")
+ (and (eq_attr "mode" "SF") (eq_attr "cpu" "r6000")))
+ 5 0)
+
+(define_function_unit "mult" 1 1
+ (and (eq_attr "type" "fmul")
+ (and (eq_attr "mode" "SF") (eq_attr "cpu" "r4600,r4650")))
+ 8 0)
+
+(define_function_unit "mult" 1 1
+ (and (eq_attr "type" "fmul")
+ (and (eq_attr "mode" "DF") (eq_attr "cpu" "!r3000,r3900,r6000,r4300,r5000,r5400"))) ; CYGNUS LOCAL vr5400/raeburn
+ 8 0)
+
+(define_function_unit "mult" 1 1
+ (and (eq_attr "type" "fmul")
+ (and (eq_attr "mode" "DF") (eq_attr "cpu" "r3000,r3900,r5000")))
+ 5 0)
+
+(define_function_unit "mult" 1 1
+ (and (eq_attr "type" "fmul")
+ (and (eq_attr "mode" "DF") (eq_attr "cpu" "r6000")))
+ 6 0)
+
+(define_function_unit "divide" 1 1
+ (and (eq_attr "type" "fdiv")
+ (and (eq_attr "mode" "SF")
+ (eq_attr "cpu" "!r3000,r3900,r6000,r4600,r4650,r4300,r5000,r5400"))) ; CYGNUS LOCAL vr5400/raeburn
+ 23 0)
+
+(define_function_unit "divide" 1 1
+ (and (eq_attr "type" "fdiv")
+ (and (eq_attr "mode" "SF") (eq_attr "cpu" "r3000,r3900")))
+ 12 0)
+
+(define_function_unit "divide" 1 1
+ (and (eq_attr "type" "fdiv")
+ (and (eq_attr "mode" "SF") (eq_attr "cpu" "r6000")))
+ 15 0)
+
+(define_function_unit "divide" 1 1
+ (and (eq_attr "type" "fdiv")
+ (and (eq_attr "mode" "SF") (eq_attr "cpu" "r4600,r4650")))
+ 32 0)
+
+(define_function_unit "divide" 1 1
+ (and (eq_attr "type" "fdiv")
+ (and (eq_attr "mode" "SF") (eq_attr "cpu" "r5000")))
+ 21 0)
+
+(define_function_unit "divide" 1 1
+ (and (eq_attr "type" "fdiv")
+ (and (eq_attr "mode" "DF")
+ (eq_attr "cpu" "!r3000,r3900,r6000,r4600,r4650,r4300,r5400"))) ; CYGNUS LOCAL vr5400/raeburn
+ 36 0)
+
+(define_function_unit "divide" 1 1
+ (and (eq_attr "type" "fdiv")
+ (and (eq_attr "mode" "DF") (eq_attr "cpu" "r3000,r3900")))
+ 19 0)
+
+(define_function_unit "divide" 1 1
+ (and (eq_attr "type" "fdiv")
+ (and (eq_attr "mode" "DF") (eq_attr "cpu" "r6000")))
+ 16 0)
+
+(define_function_unit "divide" 1 1
+ (and (eq_attr "type" "fdiv")
+ (and (eq_attr "mode" "DF") (eq_attr "cpu" "r4600,r4650")))
+ 61 0)
+
+;;; ??? Is this number right?
+(define_function_unit "divide" 1 1
+ (and (eq_attr "type" "fsqrt,frsqrt") ; CYGNUS LOCAL vr5400/raeburn
+ (and (eq_attr "mode" "SF") (eq_attr "cpu" "!r4600,r4650,r4300,r5000,r5400"))) ; CYGNUS LOCAL vr5400/raeburn
+ 54 0)
+
+(define_function_unit "divide" 1 1
+ (and (eq_attr "type" "fsqrt,frsqrt") ; CYGNUS LOCAL vr5400/raeburn
+ (and (eq_attr "mode" "SF") (eq_attr "cpu" "r4600,r4650")))
+ 31 0)
+
+(define_function_unit "divide" 1 1
+ (and (eq_attr "type" "fsqrt,frsqrt") ; CYGNUS LOCAL vr5400/raeburn
+ (and (eq_attr "mode" "SF") (eq_attr "cpu" "r5000")))
+ 21 0)
+
+;;; ??? Is this number right?
+(define_function_unit "divide" 1 1
+ (and (eq_attr "type" "fsqrt,frsqrt") ; CYGNUS LOCAL vr5400/raeburn
+ (and (eq_attr "mode" "DF") (eq_attr "cpu" "!r4600,r4650,r4300,r5000,r5400"))) ; CYGNUS LOCAL vr5400/raeburn
+ 112 0)
+
+(define_function_unit "divide" 1 1
+ (and (eq_attr "type" "fsqrt,frsqrt") ; CYGNUS LOCAL vr5400/raeburn
+ (and (eq_attr "mode" "DF") (eq_attr "cpu" "r4600,r4650")))
+ 60 0)
+
+(define_function_unit "divide" 1 1
+ (and (eq_attr "type" "fsqrt,frsqrt") ; CYGNUS LOCAL vr5400/raeburn
+ (and (eq_attr "mode" "DF") (eq_attr "cpu" "r5000")))
+ 36 0)
+
+;; R4300 FP instruction classes treated as part of the "imuldiv"
+;; functional unit:
+
+(define_function_unit "imuldiv" 1 0
+ (and (eq_attr "type" "fadd") (eq_attr "cpu" "r4300")) ;
+ 3 3)
+
+(define_function_unit "imuldiv" 1 0
+ (and (eq_attr "type" "fcmp,fabs,fneg") (eq_attr "cpu" "r4300")) ;
+ 1 1)
+
+(define_function_unit "imuldiv" 1 0
+ (and (eq_attr "type" "fmul") (and (eq_attr "mode" "SF") (eq_attr "cpu" "r4300"))) ;
+ 5 5)
+(define_function_unit "imuldiv" 1 0
+ (and (eq_attr "type" "fmul") (and (eq_attr "mode" "DF") (eq_attr "cpu" "r4300"))) ;
+ 8 8)
+
+(define_function_unit "imuldiv" 1 0
+ (and (and (eq_attr "type" "fdiv") (eq_attr "type" "fsqrt,frsqrt")) ; CYGNUS LOCAL vr5400/raeburn
+ (and (eq_attr "mode" "SF") (eq_attr "cpu" "r4300"))) ;
+ 29 29)
+(define_function_unit "imuldiv" 1 0
+ (and (and (eq_attr "type" "fdiv") (eq_attr "type" "fsqrt,frsqrt")) ; CYGNUS LOCAL vr5400/raeburn
+ (and (eq_attr "mode" "DF") (eq_attr "cpu" "r4300"))) ;
+ 58 58)
+
+;; CYGNUS LOCAL vr5400/raeburn
+(define_function_unit "alu_5400" 2 0
+ (and (eq_attr "type" "move,arith,darith,icmp,nop")
+ (eq_attr "cpu" "r5400"))
+ 1 0)
+
+(define_function_unit "alu_5400" 2 0
+ (and (eq_attr "type" "fadd")
+ (eq_attr "cpu" "r5400"))
+ 4 3)
+
+(define_function_unit "alu_5400" 2 0
+ (and (eq_attr "type" "fcmp,fabs,fneg")
+ (eq_attr "cpu" "r5400"))
+ 2 1)
+
+(define_function_unit "alu_5400" 2 0
+ (and (and (eq_attr "type" "fmul")
+ (eq_attr "mode" "SF"))
+ (eq_attr "cpu" "r5400"))
+ 5 4)
+
+(define_function_unit "alu_5400" 2 0
+ (and (and (eq_attr "type" "fmul")
+ (eq_attr "mode" "DF"))
+ (eq_attr "cpu" "r5400"))
+ 6 5)
+
+(define_function_unit "alu_5400" 2 0
+ (and (and (eq_attr "type" "fdiv,fsqrt")
+ (eq_attr "mode" "SF"))
+ (eq_attr "cpu" "r5400"))
+ 31 30)
+
+(define_function_unit "alu_5400" 2 0
+ (and (and (eq_attr "type" "fdiv,fsqrt")
+ (eq_attr "mode" "DF"))
+ (eq_attr "cpu" "r5400"))
+ 59 58)
+
+(define_function_unit "alu_5400" 2 0
+ (and (and (eq_attr "type" "frsqrt")
+ (eq_attr "mode" "SF"))
+ (eq_attr "cpu" "r5400"))
+ 61 60)
+
+(define_function_unit "alu_5400" 2 0
+ (and (and (eq_attr "type" "frsqrt")
+ (eq_attr "mode" "DF"))
+ (eq_attr "cpu" "r5400"))
+ 121 120)
+
+(define_function_unit "alu_5400" 2 0
+ (and (and (eq_attr "type" "fmadd")
+ (eq_attr "mode" "SF"))
+ (eq_attr "cpu" "r5400"))
+ 9 8)
+
+(define_function_unit "alu_5400" 2 0
+ (and (and (eq_attr "type" "fmadd")
+ (eq_attr "mode" "DF"))
+ (eq_attr "cpu" "r5400"))
+ 10 9)
+
+(define_function_unit "alu_5400" 2 0
+ (and (eq_attr "type" "fcvt")
+ (eq_attr "cpu" "r5400"))
+ 6 5)
+;; END CYGNUS LOCAL
+
+
+
+;; The following functional units do not use the cpu type, and use
+;; much less memory in genattrtab.c.
+
+;; (define_function_unit "memory" 1 0 (eq_attr "type" "load") 3 0)
+;; (define_function_unit "memory" 1 0 (eq_attr "type" "store") 1 0)
+;; (define_function_unit "fp_comp" 1 0 (eq_attr "type" "fcmp") 2 0)
+;; (define_function_unit "transfer" 1 0 (eq_attr "type" "xfer") 2 0)
+;; (define_function_unit "transfer" 1 0 (eq_attr "type" "hilo") 3 0)
+;; (define_function_unit "imuldiv" 1 1 (eq_attr "type" "imul") 17 0)
+;; (define_function_unit "imuldiv" 1 1 (eq_attr "type" "idiv") 38 0)
+;; (define_function_unit "adder" 1 1 (eq_attr "type" "fadd") 4 0)
+;; (define_function_unit "adder" 1 1 (eq_attr "type" "fabs,fneg") 2 0)
+;; (define_function_unit "mult" 1 1 (and (eq_attr "type" "fmul") (eq_attr "mode" "SF")) 7 0)
+;; (define_function_unit "mult" 1 1 (and (eq_attr "type" "fmul") (eq_attr "mode" "DF")) 8 0)
+;; (define_function_unit "divide" 1 1 (and (eq_attr "type" "fdiv") (eq_attr "mode" "SF")) 23 0)
+;; (define_function_unit "divide" 1 1 (and (eq_attr "type" "fdiv") (eq_attr "mode" "DF")) 36 0)
+;; (define_function_unit "sqrt" 1 1 (and (eq_attr "type" "fsqrt,frsqrt") (eq_attr "mode" "SF")) 54 0) CYGNUS LOCAL vr5400/raeburn
+;; (define_function_unit "sqrt" 1 1 (and (eq_attr "type" "fsqrt,frsqrt") (eq_attr "mode" "DF")) 112 0) CYGNUS LOCAL vr5400/raeburn
+
+
+;; ....................
+;; ADDITION
+;; ....................
+
+(define_insn "adddf3"
+ [(set (match_operand:DF 0 "register_operand" "=f")
+ (plus:DF (match_operand:DF 1 "register_operand" "f")
+ (match_operand:DF 2 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
+ "add.d\\t%0,%1,%2"
+ [(set_attr "type" "fadd")
+ (set_attr "mode" "DF")
+ (set_attr "length" "1")])
+
+(define_insn "addsf3"
+ [(set (match_operand:SF 0 "register_operand" "=f")
+ (plus:SF (match_operand:SF 1 "register_operand" "f")
+ (match_operand:SF 2 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT"
+ "add.s\\t%0,%1,%2"
+ [(set_attr "type" "fadd")
+ (set_attr "mode" "SF")
+ (set_attr "length" "1")])
+
+(define_expand "addsi3"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (plus:SI (match_operand:SI 1 "reg_or_0_operand" "dJ")
+ (match_operand:SI 2 "arith_operand" "dI")))]
+ ""
+ "
+{
+ /* The mips16 assembler handles -32768 correctly, and so does gas,
+ but some other MIPS assemblers think that -32768 needs to be
+ loaded into a register before it can be added in. */
+ if (! TARGET_MIPS16
+ && ! TARGET_GAS
+ && GET_CODE (operands[2]) == CONST_INT
+ && INTVAL (operands[2]) == -32768)
+ operands[2] = force_reg (SImode, operands[2]);
+}")
+
+(define_insn "addsi3_internal"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (plus:SI (match_operand:SI 1 "reg_or_0_operand" "dJ")
+ (match_operand:SI 2 "arith_operand" "dI")))]
+ "! TARGET_MIPS16
+ && (TARGET_GAS
+ || GET_CODE (operands[2]) != CONST_INT
+ || INTVAL (operands[2]) != -32768)"
+ "addu\\t%0,%z1,%2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1")])
+
+;; For the mips16, we need to recognize 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 ""
+ [(set (reg:SI 29)
+ (plus:SI (reg:SI 29)
+ (match_operand:SI 0 "small_int" "I")))]
+ "TARGET_MIPS16"
+ "addu\\t%$,%$,%0"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")
+ (set (attr "length") (if_then_else (match_operand:VOID 0 "m16_simm8_8" "")
+ (const_int 1)
+ (const_int 2)))])
+
+(define_insn ""
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (plus:SI (reg:SI 29)
+ (match_operand:SI 1 "small_int" "I")))]
+ "TARGET_MIPS16"
+ "addu\\t%0,%$,%1"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")
+ (set (attr "length") (if_then_else (match_operand:VOID 1 "m16_uimm8_4" "")
+ (const_int 1)
+ (const_int 2)))])
+
+(define_insn ""
+ [(set (match_operand:SI 0 "register_operand" "=d,d,d")
+ (plus:SI (match_operand:SI 1 "register_operand" "0,d,d")
+ (match_operand:SI 2 "arith_operand" "IQ,O,d")))]
+ "TARGET_MIPS16
+ && (GET_CODE (operands[1]) != REG
+ || REGNO (operands[1]) >= FIRST_PSEUDO_REGISTER
+ || M16_REG_P (REGNO (operands[1]))
+ || REGNO (operands[1]) == ARG_POINTER_REGNUM
+ || REGNO (operands[1]) == FRAME_POINTER_REGNUM
+ || REGNO (operands[1]) == STACK_POINTER_REGNUM)
+ && (GET_CODE (operands[2]) != REG
+ || REGNO (operands[2]) >= FIRST_PSEUDO_REGISTER
+ || M16_REG_P (REGNO (operands[2]))
+ || REGNO (operands[2]) == ARG_POINTER_REGNUM
+ || REGNO (operands[2]) == FRAME_POINTER_REGNUM
+ || REGNO (operands[2]) == STACK_POINTER_REGNUM)"
+ "*
+{
+ if (REGNO (operands[0]) == REGNO (operands[1]))
+ return \"addu\\t%0,%2\";
+ return \"addu\\t%0,%1,%2\";
+}"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")
+ (set_attr_alternative "length"
+ [(if_then_else (match_operand:VOID 2 "m16_simm8_1" "")
+ (const_int 1)
+ (const_int 2))
+ (if_then_else (match_operand:VOID 2 "m16_simm4_1" "")
+ (const_int 1)
+ (const_int 2))
+ (const_int 1)])])
+
+
+;; 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
+ && GET_CODE (operands[0]) == REG
+ && 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
+ && GET_CODE (operands[0]) == REG
+ && M16_REG_P (REGNO (operands[0]))
+ && GET_CODE (operands[1]) == REG
+ && 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_expand "adddi3"
+ [(parallel [(set (match_operand:DI 0 "register_operand" "")
+ (plus:DI (match_operand:DI 1 "se_register_operand" "")
+ (match_operand:DI 2 "se_arith_operand" "")))
+ (clobber (match_dup 3))])]
+ "TARGET_64BIT || (!TARGET_DEBUG_G_MODE && !TARGET_MIPS16)"
+ "
+{
+ /* The mips16 assembler handles -32768 correctly, and so does gas,
+ but some other MIPS assemblers think that -32768 needs to be
+ loaded into a register before it can be added in. */
+ if (! TARGET_MIPS16
+ && ! TARGET_GAS
+ && GET_CODE (operands[2]) == CONST_INT
+ && INTVAL (operands[2]) == -32768)
+ operands[2] = force_reg (DImode, operands[2]);
+
+ if (TARGET_64BIT)
+ {
+ emit_insn (gen_adddi3_internal_3 (operands[0], operands[1],
+ operands[2]));
+ DONE;
+ }
+
+ operands[3] = gen_reg_rtx (SImode);
+}")
+
+(define_insn "adddi3_internal_1"
+ [(set (match_operand:DI 0 "register_operand" "=d,&d")
+ (plus:DI (match_operand:DI 1 "register_operand" "0,d")
+ (match_operand:DI 2 "register_operand" "d,d")))
+ (clobber (match_operand:SI 3 "register_operand" "=d,d"))]
+ "!TARGET_64BIT && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16"
+ "*
+{
+ return (REGNO (operands[0]) == REGNO (operands[1])
+ && REGNO (operands[0]) == REGNO (operands[2]))
+ ? \"srl\\t%3,%L0,31\;sll\\t%M0,%M0,1\;sll\\t%L0,%L1,1\;addu\\t%M0,%M0,%3\"
+ : \"addu\\t%L0,%L1,%L2\;sltu\\t%3,%L0,%L2\;addu\\t%M0,%M1,%M2\;addu\\t%M0,%M0,%3\";
+}"
+ [(set_attr "type" "darith")
+ (set_attr "mode" "DI")
+ (set_attr "length" "4")])
+
+(define_split
+ [(set (match_operand:DI 0 "register_operand" "")
+ (plus:DI (match_operand:DI 1 "register_operand" "")
+ (match_operand:DI 2 "register_operand" "")))
+ (clobber (match_operand:SI 3 "register_operand" ""))]
+ "reload_completed && !WORDS_BIG_ENDIAN && !TARGET_64BIT
+ && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
+ && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0]))
+ && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1]))
+ && GET_CODE (operands[2]) == REG && GP_REG_P (REGNO (operands[2]))
+ && (REGNO (operands[0]) != REGNO (operands[1])
+ || REGNO (operands[0]) != REGNO (operands[2]))"
+
+ [(set (subreg:SI (match_dup 0) 0)
+ (plus:SI (subreg:SI (match_dup 1) 0)
+ (subreg:SI (match_dup 2) 0)))
+
+ (set (match_dup 3)
+ (ltu:SI (subreg:SI (match_dup 0) 0)
+ (subreg:SI (match_dup 2) 0)))
+
+ (set (subreg:SI (match_dup 0) 1)
+ (plus:SI (subreg:SI (match_dup 1) 1)
+ (subreg:SI (match_dup 2) 1)))
+
+ (set (subreg:SI (match_dup 0) 1)
+ (plus:SI (subreg:SI (match_dup 0) 1)
+ (match_dup 3)))]
+ "")
+
+(define_split
+ [(set (match_operand:DI 0 "register_operand" "")
+ (plus:DI (match_operand:DI 1 "register_operand" "")
+ (match_operand:DI 2 "register_operand" "")))
+ (clobber (match_operand:SI 3 "register_operand" ""))]
+ "reload_completed && WORDS_BIG_ENDIAN && !TARGET_64BIT
+ && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
+ && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0]))
+ && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1]))
+ && GET_CODE (operands[2]) == REG && GP_REG_P (REGNO (operands[2]))
+ && (REGNO (operands[0]) != REGNO (operands[1])
+ || REGNO (operands[0]) != REGNO (operands[2]))"
+
+ [(set (subreg:SI (match_dup 0) 1)
+ (plus:SI (subreg:SI (match_dup 1) 1)
+ (subreg:SI (match_dup 2) 1)))
+
+ (set (match_dup 3)
+ (ltu:SI (subreg:SI (match_dup 0) 1)
+ (subreg:SI (match_dup 2) 1)))
+
+ (set (subreg:SI (match_dup 0) 0)
+ (plus:SI (subreg:SI (match_dup 1) 0)
+ (subreg:SI (match_dup 2) 0)))
+
+ (set (subreg:SI (match_dup 0) 0)
+ (plus:SI (subreg:SI (match_dup 0) 0)
+ (match_dup 3)))]
+ "")
+
+(define_insn "adddi3_internal_2"
+ [(set (match_operand:DI 0 "register_operand" "=d,d,d")
+ (plus:DI (match_operand:DI 1 "register_operand" "%d,%d,%d")
+ (match_operand:DI 2 "small_int" "P,J,N")))
+ (clobber (match_operand:SI 3 "register_operand" "=d,d,d"))]
+ "!TARGET_64BIT && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
+ && (TARGET_GAS
+ || GET_CODE (operands[2]) != CONST_INT
+ || INTVAL (operands[2]) != -32768)"
+ "@
+ addu\\t%L0,%L1,%2\;sltu\\t%3,%L0,%2\;addu\\t%M0,%M1,%3
+ move\\t%L0,%L1\;move\\t%M0,%M1
+ subu\\t%L0,%L1,%n2\;sltu\\t%3,%L0,%2\;subu\\t%M0,%M1,1\;addu\\t%M0,%M0,%3"
+ [(set_attr "type" "darith")
+ (set_attr "mode" "DI")
+ (set_attr "length" "3,2,4")])
+
+(define_split
+ [(set (match_operand:DI 0 "register_operand" "")
+ (plus:DI (match_operand:DI 1 "register_operand" "")
+ (match_operand:DI 2 "small_int" "")))
+ (clobber (match_operand:SI 3 "register_operand" "=d"))]
+ "reload_completed && !WORDS_BIG_ENDIAN && !TARGET_64BIT
+ && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
+ && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0]))
+ && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1]))
+ && INTVAL (operands[2]) > 0"
+
+ [(set (subreg:SI (match_dup 0) 0)
+ (plus:SI (subreg:SI (match_dup 1) 0)
+ (match_dup 2)))
+
+ (set (match_dup 3)
+ (ltu:SI (subreg:SI (match_dup 0) 0)
+ (match_dup 2)))
+
+ (set (subreg:SI (match_dup 0) 1)
+ (plus:SI (subreg:SI (match_dup 1) 1)
+ (match_dup 3)))]
+ "")
+
+(define_split
+ [(set (match_operand:DI 0 "register_operand" "")
+ (plus:DI (match_operand:DI 1 "register_operand" "")
+ (match_operand:DI 2 "small_int" "")))
+ (clobber (match_operand:SI 3 "register_operand" "=d"))]
+ "reload_completed && WORDS_BIG_ENDIAN && !TARGET_64BIT
+ && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
+ && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0]))
+ && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1]))
+ && INTVAL (operands[2]) > 0"
+
+ [(set (subreg:SI (match_dup 0) 1)
+ (plus:SI (subreg:SI (match_dup 1) 1)
+ (match_dup 2)))
+
+ (set (match_dup 3)
+ (ltu:SI (subreg:SI (match_dup 0) 1)
+ (match_dup 2)))
+
+ (set (subreg:SI (match_dup 0) 0)
+ (plus:SI (subreg:SI (match_dup 1) 0)
+ (match_dup 3)))]
+ "")
+
+(define_insn "adddi3_internal_3"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (plus:DI (match_operand:DI 1 "se_reg_or_0_operand" "dJ")
+ (match_operand:DI 2 "se_arith_operand" "dI")))]
+ "TARGET_64BIT
+ && !TARGET_MIPS16
+ && (TARGET_GAS
+ || GET_CODE (operands[2]) != CONST_INT
+ || INTVAL (operands[2]) != -32768)"
+ "*
+{
+ return (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) < 0)
+ ? \"dsubu\\t%0,%z1,%n2\"
+ : \"daddu\\t%0,%z1,%2\";
+}"
+ [(set_attr "type" "darith")
+ (set_attr "mode" "DI")
+ (set_attr "length" "1")])
+
+;; For the mips16, we need to recognize 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 ""
+ [(set (reg:DI 29)
+ (plus:DI (reg:DI 29)
+ (match_operand:DI 0 "small_int" "I")))]
+ "TARGET_MIPS16 && TARGET_64BIT"
+ "daddu\\t%$,%$,%0"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "DI")
+ (set (attr "length") (if_then_else (match_operand:VOID 0 "m16_simm8_8" "")
+ (const_int 1)
+ (const_int 2)))])
+
+(define_insn ""
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (plus:DI (reg:DI 29)
+ (match_operand:DI 1 "small_int" "I")))]
+ "TARGET_MIPS16 && TARGET_64BIT"
+ "daddu\\t%0,%$,%1"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "DI")
+ (set (attr "length") (if_then_else (match_operand:VOID 0 "m16_uimm5_4" "")
+ (const_int 1)
+ (const_int 2)))])
+
+(define_insn ""
+ [(set (match_operand:DI 0 "register_operand" "=d,d,d")
+ (plus:DI (match_operand:DI 1 "register_operand" "0,d,d")
+ (match_operand:DI 2 "arith_operand" "IQ,O,d")))]
+ "TARGET_MIPS16 && TARGET_64BIT
+ && (GET_CODE (operands[1]) != REG
+ || REGNO (operands[1]) >= FIRST_PSEUDO_REGISTER
+ || M16_REG_P (REGNO (operands[1]))
+ || REGNO (operands[1]) == ARG_POINTER_REGNUM
+ || REGNO (operands[1]) == FRAME_POINTER_REGNUM
+ || REGNO (operands[1]) == STACK_POINTER_REGNUM)
+ && (GET_CODE (operands[2]) != REG
+ || REGNO (operands[2]) >= FIRST_PSEUDO_REGISTER
+ || M16_REG_P (REGNO (operands[2]))
+ || REGNO (operands[2]) == ARG_POINTER_REGNUM
+ || REGNO (operands[2]) == FRAME_POINTER_REGNUM
+ || REGNO (operands[2]) == STACK_POINTER_REGNUM)"
+ "*
+{
+ if (REGNO (operands[0]) == REGNO (operands[1]))
+ return \"daddu\\t%0,%2\";
+ return \"daddu\\t%0,%1,%2\";
+}"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "DI")
+ (set_attr_alternative "length"
+ [(if_then_else (match_operand:VOID 2 "m16_simm5_1" "")
+ (const_int 1)
+ (const_int 2))
+ (if_then_else (match_operand:VOID 2 "m16_simm4_1" "")
+ (const_int 1)
+ (const_int 2))
+ (const_int 1)])])
+
+
+;; 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:DI 0 "register_operand" "")
+ (plus:DI (match_dup 0)
+ (match_operand:DI 1 "const_int_operand" "")))]
+ "TARGET_MIPS16 && TARGET_64BIT && reload_completed
+ && GET_CODE (operands[0]) == REG
+ && 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
+ && GET_CODE (operands[0]) == REG
+ && M16_REG_P (REGNO (operands[0]))
+ && GET_CODE (operands[1]) == REG
+ && 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_internal_2"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (sign_extend:DI (plus:SI (match_operand:SI 1 "reg_or_0_operand" "dJ")
+ (match_operand:SI 2 "arith_operand" "dI"))))]
+ "TARGET_64BIT
+ && !TARGET_MIPS16
+ && (TARGET_GAS
+ || GET_CODE (operands[2]) != CONST_INT
+ || INTVAL (operands[2]) != -32768)"
+ "*
+{
+ return (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) < 0)
+ ? \"subu\\t%0,%z1,%n2\"
+ : \"addu\\t%0,%z1,%2\";
+}"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1")])
+
+(define_insn ""
+ [(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" "I,O,d"))))]
+ "TARGET_MIPS16 && TARGET_64BIT"
+ "*
+{
+ if (REGNO (operands[0]) == REGNO (operands[1]))
+ return \"addu\\t%0,%2\";
+ return \"addu\\t%0,%1,%2\";
+}"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")
+ (set_attr_alternative "length"
+ [(if_then_else (match_operand:VOID 2 "m16_simm8_1" "")
+ (const_int 1)
+ (const_int 2))
+ (if_then_else (match_operand:VOID 2 "m16_simm4_1" "")
+ (const_int 1)
+ (const_int 2))
+ (const_int 1)])])
+
+
+;; ....................
+;; SUBTRACTION
+;; ....................
+
+(define_insn "subdf3"
+ [(set (match_operand:DF 0 "register_operand" "=f")
+ (minus:DF (match_operand:DF 1 "register_operand" "f")
+ (match_operand:DF 2 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
+ "sub.d\\t%0,%1,%2"
+ [(set_attr "type" "fadd")
+ (set_attr "mode" "DF")
+ (set_attr "length" "1")])
+
+(define_insn "subsf3"
+ [(set (match_operand:SF 0 "register_operand" "=f")
+ (minus:SF (match_operand:SF 1 "register_operand" "f")
+ (match_operand:SF 2 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT"
+ "sub.s\\t%0,%1,%2"
+ [(set_attr "type" "fadd")
+ (set_attr "mode" "SF")
+ (set_attr "length" "1")])
+
+(define_expand "subsi3"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (minus:SI (match_operand:SI 1 "reg_or_0_operand" "dJ")
+ (match_operand:SI 2 "arith_operand" "dI")))]
+ ""
+ "
+{
+ if (GET_CODE (operands[2]) == CONST_INT
+ && (INTVAL (operands[2]) == -32768
+ || (TARGET_MIPS16
+ && INTVAL (operands[2]) == -0x4000)))
+ operands[2] = force_reg (SImode, operands[2]);
+}")
+
+(define_insn "subsi3_internal"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (minus:SI (match_operand:SI 1 "reg_or_0_operand" "dJ")
+ (match_operand:SI 2 "arith_operand" "dI")))]
+ "!TARGET_MIPS16
+ && (GET_CODE (operands[2]) != CONST_INT || INTVAL (operands[2]) != -32768)"
+ "subu\\t%0,%z1,%2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1")])
+
+;; For the mips16, we need to recognize stack pointer subtractions
+;; explicitly, since we don't have a constraint for $sp. These insns
+;; will be generated by the save_restore_insns functions.
+
+(define_insn ""
+ [(set (reg:SI 29)
+ (minus:SI (reg:SI 29)
+ (match_operand:SI 0 "small_int" "I")))]
+ "TARGET_MIPS16
+ && (GET_CODE (operands[2]) != CONST_INT || INTVAL (operands[2]) != -32768)"
+ "addu\\t%$,%$,%n0"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")
+ (set (attr "length") (if_then_else (match_operand:VOID 0 "m16_nsimm8_8" "")
+ (const_int 1)
+ (const_int 2)))])
+
+(define_insn ""
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (minus:SI (reg:SI 29)
+ (match_operand:SI 1 "small_int" "I")))]
+ "TARGET_MIPS16
+ && (GET_CODE (operands[2]) != CONST_INT || INTVAL (operands[2]) != -32768)"
+ "addu\\t%0,%$,%n1"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")
+ (set (attr "length") (if_then_else (match_operand:VOID 1 "m16_nuimm8_4" "")
+ (const_int 1)
+ (const_int 2)))])
+
+
+(define_insn ""
+ [(set (match_operand:SI 0 "register_operand" "=d,d,d")
+ (minus:SI (match_operand:SI 1 "register_operand" "0,d,d")
+ (match_operand:SI 2 "arith_operand" "I,O,d")))]
+ "TARGET_MIPS16
+ && (GET_CODE (operands[2]) != CONST_INT
+ || (INTVAL (operands[2]) != -32768 && INTVAL (operands[2]) != -0x4000))"
+ "*
+{
+ if (REGNO (operands[0]) == REGNO (operands[1]))
+ return \"subu\\t%0,%2\";
+ return \"subu\\t%0,%1,%2\";
+}"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")
+ (set_attr_alternative "length"
+ [(if_then_else (match_operand:VOID 2 "m16_nsimm8_1" "")
+ (const_int 1)
+ (const_int 2))
+ (if_then_else (match_operand:VOID 2 "m16_nsimm4_1" "")
+ (const_int 1)
+ (const_int 2))
+ (const_int 1)])])
+
+;; On the mips16, we can sometimes split an subtract 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 setting a
+;; register to a register minus a constant, and one where we are
+;; simply subtracting a constant from a register.
+
+(define_split
+ [(set (match_operand:SI 0 "register_operand" "")
+ (minus:SI (match_dup 0)
+ (match_operand:SI 1 "const_int_operand" "")))]
+ "TARGET_MIPS16 && reload_completed
+ && GET_CODE (operands[0]) == REG
+ && M16_REG_P (REGNO (operands[0]))
+ && GET_CODE (operands[1]) == CONST_INT
+ && ((INTVAL (operands[1]) > 0x80
+ && INTVAL (operands[1]) <= 0x80 + 0x80)
+ || (INTVAL (operands[1]) < - 0x7f
+ && INTVAL (operands[1]) >= - 0x7f - 0x7f))"
+ [(set (match_dup 0) (minus:SI (match_dup 0) (match_dup 1)))
+ (set (match_dup 0) (minus:SI (match_dup 0) (match_dup 2)))]
+ "
+{
+ HOST_WIDE_INT val = INTVAL (operands[1]);
+
+ if (val >= 0)
+ {
+ operands[1] = GEN_INT (0x80);
+ operands[2] = GEN_INT (val - 0x80);
+ }
+ else
+ {
+ operands[1] = GEN_INT (- 0x7f);
+ operands[2] = GEN_INT (val + 0x7f);
+ }
+}")
+
+(define_split
+ [(set (match_operand:SI 0 "register_operand" "")
+ (minus:SI (match_operand:SI 1 "register_operand" "")
+ (match_operand:SI 2 "const_int_operand" "")))]
+ "TARGET_MIPS16 && reload_completed
+ && GET_CODE (operands[0]) == REG
+ && M16_REG_P (REGNO (operands[0]))
+ && GET_CODE (operands[1]) == REG
+ && M16_REG_P (REGNO (operands[1]))
+ && REGNO (operands[0]) != REGNO (operands[1])
+ && GET_CODE (operands[2]) == CONST_INT
+ && ((INTVAL (operands[2]) > 0x8
+ && INTVAL (operands[2]) <= 0x8 + 0x80)
+ || (INTVAL (operands[2]) < - 0x7
+ && INTVAL (operands[2]) >= - 0x7 - 0x7f))"
+ [(set (match_dup 0) (minus:SI (match_dup 1) (match_dup 2)))
+ (set (match_dup 0) (minus:SI (match_dup 0) (match_dup 3)))]
+ "
+{
+ HOST_WIDE_INT val = INTVAL (operands[2]);
+
+ if (val >= 0)
+ {
+ operands[2] = GEN_INT (0x8);
+ operands[3] = GEN_INT (val - 0x8);
+ }
+ else
+ {
+ operands[2] = GEN_INT (- 0x7);
+ operands[3] = GEN_INT (val + 0x7);
+ }
+}")
+
+(define_expand "subdi3"
+ [(parallel [(set (match_operand:DI 0 "register_operand" "=d")
+ (minus:DI (match_operand:DI 1 "se_register_operand" "d")
+ (match_operand:DI 2 "se_register_operand" "d")))
+ (clobber (match_dup 3))])]
+ "TARGET_64BIT || (!TARGET_DEBUG_G_MODE && !TARGET_MIPS16)"
+ "
+{
+ if (TARGET_64BIT)
+ {
+ emit_insn (gen_subdi3_internal_3 (operands[0], operands[1],
+ operands[2]));
+ DONE;
+ }
+
+ operands[3] = gen_reg_rtx (SImode);
+}")
+
+(define_insn "subdi3_internal"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (minus:DI (match_operand:DI 1 "register_operand" "d")
+ (match_operand:DI 2 "register_operand" "d")))
+ (clobber (match_operand:SI 3 "register_operand" "=d"))]
+ "!TARGET_64BIT && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16"
+ "sltu\\t%3,%L1,%L2\;subu\\t%L0,%L1,%L2\;subu\\t%M0,%M1,%M2\;subu\\t%M0,%M0,%3"
+ [(set_attr "type" "darith")
+ (set_attr "mode" "DI")
+ (set_attr "length" "4")])
+
+(define_split
+ [(set (match_operand:DI 0 "register_operand" "")
+ (minus:DI (match_operand:DI 1 "register_operand" "")
+ (match_operand:DI 2 "register_operand" "")))
+ (clobber (match_operand:SI 3 "register_operand" ""))]
+ "reload_completed && !WORDS_BIG_ENDIAN && !TARGET_64BIT
+ && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
+ && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0]))
+ && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1]))
+ && GET_CODE (operands[2]) == REG && GP_REG_P (REGNO (operands[2]))"
+
+ [(set (match_dup 3)
+ (ltu:SI (subreg:SI (match_dup 1) 0)
+ (subreg:SI (match_dup 2) 0)))
+
+ (set (subreg:SI (match_dup 0) 0)
+ (minus:SI (subreg:SI (match_dup 1) 0)
+ (subreg:SI (match_dup 2) 0)))
+
+ (set (subreg:SI (match_dup 0) 1)
+ (minus:SI (subreg:SI (match_dup 1) 1)
+ (subreg:SI (match_dup 2) 1)))
+
+ (set (subreg:SI (match_dup 0) 1)
+ (minus:SI (subreg:SI (match_dup 0) 1)
+ (match_dup 3)))]
+ "")
+
+(define_split
+ [(set (match_operand:DI 0 "register_operand" "")
+ (minus:DI (match_operand:DI 1 "register_operand" "")
+ (match_operand:DI 2 "register_operand" "")))
+ (clobber (match_operand:SI 3 "register_operand" ""))]
+ "reload_completed && WORDS_BIG_ENDIAN && !TARGET_64BIT
+ && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
+ && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0]))
+ && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1]))
+ && GET_CODE (operands[2]) == REG && GP_REG_P (REGNO (operands[2]))"
+
+ [(set (match_dup 3)
+ (ltu:SI (subreg:SI (match_dup 1) 1)
+ (subreg:SI (match_dup 2) 1)))
+
+ (set (subreg:SI (match_dup 0) 1)
+ (minus:SI (subreg:SI (match_dup 1) 1)
+ (subreg:SI (match_dup 2) 1)))
+
+ (set (subreg:SI (match_dup 0) 0)
+ (minus:SI (subreg:SI (match_dup 1) 0)
+ (subreg:SI (match_dup 2) 0)))
+
+ (set (subreg:SI (match_dup 0) 0)
+ (minus:SI (subreg:SI (match_dup 0) 0)
+ (match_dup 3)))]
+ "")
+
+(define_insn "subdi3_internal_2"
+ [(set (match_operand:DI 0 "register_operand" "=d,d,d")
+ (minus:DI (match_operand:DI 1 "register_operand" "d,d,d")
+ (match_operand:DI 2 "small_int" "P,J,N")))
+ (clobber (match_operand:SI 3 "register_operand" "=d,d,d"))]
+ "!TARGET_64BIT && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
+ && INTVAL (operands[2]) != -32768"
+ "@
+ sltu\\t%3,%L1,%2\;subu\\t%L0,%L1,%2\;subu\\t%M0,%M1,%3
+ move\\t%L0,%L1\;move\\t%M0,%M1
+ sltu\\t%3,%L1,%2\;subu\\t%L0,%L1,%2\;subu\\t%M0,%M1,1\;subu\\t%M0,%M0,%3"
+ [(set_attr "type" "darith")
+ (set_attr "mode" "DI")
+ (set_attr "length" "3,2,4")])
+
+(define_split
+ [(set (match_operand:DI 0 "register_operand" "")
+ (minus:DI (match_operand:DI 1 "register_operand" "")
+ (match_operand:DI 2 "small_int" "")))
+ (clobber (match_operand:SI 3 "register_operand" ""))]
+ "reload_completed && !WORDS_BIG_ENDIAN && !TARGET_64BIT
+ && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
+ && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0]))
+ && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1]))
+ && INTVAL (operands[2]) > 0"
+
+ [(set (match_dup 3)
+ (ltu:SI (subreg:SI (match_dup 1) 0)
+ (match_dup 2)))
+
+ (set (subreg:SI (match_dup 0) 0)
+ (minus:SI (subreg:SI (match_dup 1) 0)
+ (match_dup 2)))
+
+ (set (subreg:SI (match_dup 0) 1)
+ (minus:SI (subreg:SI (match_dup 1) 1)
+ (match_dup 3)))]
+ "")
+
+(define_split
+ [(set (match_operand:DI 0 "register_operand" "")
+ (minus:DI (match_operand:DI 1 "register_operand" "")
+ (match_operand:DI 2 "small_int" "")))
+ (clobber (match_operand:SI 3 "register_operand" ""))]
+ "reload_completed && WORDS_BIG_ENDIAN && !TARGET_64BIT
+ && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
+ && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0]))
+ && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1]))
+ && INTVAL (operands[2]) > 0"
+
+ [(set (match_dup 3)
+ (ltu:SI (subreg:SI (match_dup 1) 1)
+ (match_dup 2)))
+
+ (set (subreg:SI (match_dup 0) 1)
+ (minus:SI (subreg:SI (match_dup 1) 1)
+ (match_dup 2)))
+
+ (set (subreg:SI (match_dup 0) 0)
+ (minus:SI (subreg:SI (match_dup 1) 0)
+ (match_dup 3)))]
+ "")
+
+(define_insn "subdi3_internal_3"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (minus:DI (match_operand:DI 1 "se_reg_or_0_operand" "dJ")
+ (match_operand:DI 2 "se_arith_operand" "dI")))]
+ "TARGET_64BIT && !TARGET_MIPS16
+ && (GET_CODE (operands[2]) != CONST_INT || INTVAL (operands[2]) != -32768)"
+ "*
+{
+ return (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) < 0)
+ ? \"daddu\\t%0,%z1,%n2\"
+ : \"dsubu\\t%0,%z1,%2\";
+}"
+ [(set_attr "type" "darith")
+ (set_attr "mode" "DI")
+ (set_attr "length" "1")])
+
+;; For the mips16, we need to recognize stack pointer subtractions
+;; explicitly, since we don't have a constraint for $sp. These insns
+;; will be generated by the save_restore_insns functions.
+
+(define_insn ""
+ [(set (reg:DI 29)
+ (minus:DI (reg:DI 29)
+ (match_operand:DI 0 "small_int" "I")))]
+ "TARGET_MIPS16
+ && (GET_CODE (operands[2]) != CONST_INT || INTVAL (operands[2]) != -32768)"
+ "daddu\\t%$,%$,%n0"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "DI")
+ (set (attr "length") (if_then_else (match_operand:VOID 0 "m16_nsimm8_8" "")
+ (const_int 1)
+ (const_int 2)))])
+
+(define_insn ""
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (minus:DI (reg:DI 29)
+ (match_operand:DI 1 "small_int" "I")))]
+ "TARGET_MIPS16
+ && (GET_CODE (operands[2]) != CONST_INT || INTVAL (operands[2]) != -32768)"
+ "daddu\\t%0,%$,%n1"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "DI")
+ (set (attr "length") (if_then_else (match_operand:VOID 0 "m16_nuimm5_4" "")
+ (const_int 1)
+ (const_int 2)))])
+
+(define_insn ""
+ [(set (match_operand:DI 0 "register_operand" "=d,d,d")
+ (minus:DI (match_operand:DI 1 "register_operand" "0,d,d")
+ (match_operand:DI 2 "arith_operand" "I,O,d")))]
+ "TARGET_MIPS16
+ && (GET_CODE (operands[2]) != CONST_INT
+ || (INTVAL (operands[2]) != -32768 && INTVAL (operands[2]) != -0x4000))"
+ "*
+{
+ if (REGNO (operands[0]) == REGNO (operands[1]))
+ return \"dsubu\\t%0,%2\";
+ return \"dsubu\\t%0,%1,%2\";
+}"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "DI")
+ (set_attr_alternative "length"
+ [(if_then_else (match_operand:VOID 2 "m16_nsimm5_1" "")
+ (const_int 1)
+ (const_int 2))
+ (if_then_else (match_operand:VOID 2 "m16_nsimm4_1" "")
+ (const_int 1)
+ (const_int 2))
+ (const_int 1)])])
+
+;; 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:DI 0 "register_operand" "")
+ (minus:DI (match_dup 0)
+ (match_operand:DI 1 "const_int_operand" "")))]
+ "TARGET_MIPS16 && TARGET_64BIT && reload_completed
+ && GET_CODE (operands[0]) == REG
+ && M16_REG_P (REGNO (operands[0]))
+ && GET_CODE (operands[1]) == CONST_INT
+ && ((INTVAL (operands[1]) > 0x10
+ && INTVAL (operands[1]) <= 0x10 + 0x10)
+ || (INTVAL (operands[1]) < - 0xf
+ && INTVAL (operands[1]) >= - 0xf - 0xf))"
+ [(set (match_dup 0) (minus:DI (match_dup 0) (match_dup 1)))
+ (set (match_dup 0) (minus: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" "")
+ (minus:DI (match_operand:DI 1 "register_operand" "")
+ (match_operand:DI 2 "const_int_operand" "")))]
+ "TARGET_MIPS16 && TARGET_64BIT && reload_completed
+ && GET_CODE (operands[0]) == REG
+ && M16_REG_P (REGNO (operands[0]))
+ && GET_CODE (operands[1]) == REG
+ && M16_REG_P (REGNO (operands[1]))
+ && REGNO (operands[0]) != REGNO (operands[1])
+ && GET_CODE (operands[2]) == CONST_INT
+ && ((INTVAL (operands[2]) > 0x8
+ && INTVAL (operands[2]) <= 0x8 + 0x10)
+ || (INTVAL (operands[2]) < - 0x7
+ && INTVAL (operands[2]) >= - 0x7 - 0xf))"
+ [(set (match_dup 0) (minus:DI (match_dup 1) (match_dup 2)))
+ (set (match_dup 0) (minus:DI (match_dup 0) (match_dup 3)))]
+ "
+{
+ HOST_WIDE_INT val = INTVAL (operands[2]);
+
+ if (val >= 0)
+ {
+ operands[2] = GEN_INT (0x8);
+ operands[3] = GEN_INT (val - 0x8);
+ }
+ else
+ {
+ operands[2] = GEN_INT (- 0x7);
+ operands[3] = GEN_INT (val + 0x7);
+ }
+}")
+
+(define_insn "subsi3_internal_2"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (sign_extend:DI (minus:SI (match_operand:SI 1 "reg_or_0_operand" "dJ")
+ (match_operand:SI 2 "arith_operand" "dI"))))]
+ "TARGET_64BIT && !TARGET_MIPS16
+ && (GET_CODE (operands[2]) != CONST_INT || INTVAL (operands[2]) != -32768)"
+ "*
+{
+ return (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) < 0)
+ ? \"addu\\t%0,%z1,%n2\"
+ : \"subu\\t%0,%z1,%2\";
+}"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "DI")
+ (set_attr "length" "1")])
+
+(define_insn ""
+ [(set (match_operand:DI 0 "register_operand" "=d,d,d")
+ (sign_extend:DI (minus:SI (match_operand:SI 1 "register_operand" "0,d,d")
+ (match_operand:SI 2 "arith_operand" "I,O,d"))))]
+ "TARGET_64BIT && TARGET_MIPS16
+ && (GET_CODE (operands[2]) != CONST_INT
+ || (INTVAL (operands[2]) != -32768 && INTVAL (operands[2]) != -0x4000))"
+ "*
+{
+ if (REGNO (operands[0]) == REGNO (operands[1]))
+ return \"subu\\t%0,%2\";
+ return \"subu\\t%0,%1,%2\";
+}"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")
+ (set_attr_alternative "length"
+ [(if_then_else (match_operand:VOID 2 "m16_nsimm8_1" "")
+ (const_int 1)
+ (const_int 2))
+ (if_then_else (match_operand:VOID 2 "m16_nsimm4_1" "")
+ (const_int 1)
+ (const_int 2))
+ (const_int 1)])])
+
+
+
+;; ....................
+;; MULTIPLICATION
+;; ....................
+
+;; 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_expand "muldf3"
+ [(set (match_operand:DF 0 "register_operand" "=f")
+ (mult:DF (match_operand:DF 1 "register_operand" "f")
+ (match_operand:DF 2 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
+ "
+{
+ if (mips_cpu != PROCESSOR_R4300)
+ emit_insn (gen_muldf3_internal (operands[0], operands[1], operands[2]));
+ else
+ emit_insn (gen_muldf3_r4300 (operands[0], operands[1], operands[2]));
+ DONE;
+}")
+
+(define_insn "muldf3_internal"
+ [(set (match_operand:DF 0 "register_operand" "=f")
+ (mult:DF (match_operand:DF 1 "register_operand" "f")
+ (match_operand:DF 2 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT && mips_cpu != PROCESSOR_R4300"
+ "mul.d\\t%0,%1,%2"
+ [(set_attr "type" "fmul")
+ (set_attr "mode" "DF")
+ (set_attr "length" "1")])
+
+(define_insn "muldf3_r4300"
+ [(set (match_operand:DF 0 "register_operand" "=f")
+ (mult:DF (match_operand:DF 1 "register_operand" "f")
+ (match_operand:DF 2 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT && mips_cpu == PROCESSOR_R4300"
+ "*
+{
+ output_asm_insn (\"mul.d\\t%0,%1,%2\", operands);
+ if (TARGET_4300_MUL_FIX)
+ output_asm_insn (\"nop\", operands);
+ return \"\";
+}"
+ [(set_attr "type" "fmul")
+ (set_attr "mode" "DF")
+ (set_attr "length" "2")]) ;; mul.d + nop
+
+(define_expand "mulsf3"
+ [(set (match_operand:SF 0 "register_operand" "=f")
+ (mult:SF (match_operand:SF 1 "register_operand" "f")
+ (match_operand:SF 2 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT"
+ "
+{
+ if (mips_cpu != PROCESSOR_R4300)
+ emit_insn( gen_mulsf3_internal (operands[0], operands[1], operands[2]));
+ else
+ emit_insn( gen_mulsf3_r4300 (operands[0], operands[1], operands[2]));
+ DONE;
+}")
+
+(define_insn "mulsf3_internal"
+ [(set (match_operand:SF 0 "register_operand" "=f")
+ (mult:SF (match_operand:SF 1 "register_operand" "f")
+ (match_operand:SF 2 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT && mips_cpu != PROCESSOR_R4300"
+ "mul.s\\t%0,%1,%2"
+ [(set_attr "type" "fmul")
+ (set_attr "mode" "SF")
+ (set_attr "length" "1")])
+
+(define_insn "mulsf3_r4300"
+ [(set (match_operand:SF 0 "register_operand" "=f")
+ (mult:SF (match_operand:SF 1 "register_operand" "f")
+ (match_operand:SF 2 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT && mips_cpu == PROCESSOR_R4300"
+ "*
+{
+ output_asm_insn (\"mul.s\\t%0,%1,%2\", operands);
+ if (TARGET_4300_MUL_FIX)
+ output_asm_insn (\"nop\", operands);
+ return \"\";
+}"
+ [(set_attr "type" "fmul")
+ (set_attr "mode" "SF")
+ (set_attr "length" "2")]) ;; mul.s + nop
+
+
+;; ??? The R4000 (only) has a cpu bug. If a double-word shift executes while
+;; a multiply is in progress, it may give an incorrect result. Avoid
+;; this by keeping the mflo with the mult on the R4000.
+
+(define_expand "mulsi3"
+ [(set (match_operand:SI 0 "register_operand" "=l")
+ (mult:SI (match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "register_operand" "d")))
+ (clobber (match_scratch:SI 3 "=h"))
+ (clobber (match_scratch:SI 4 "=a"))]
+ ""
+ "
+{
+ if (HAVE_mulsi3_mult3)
+ emit_insn (gen_mulsi3_mult3 (operands[0], operands[1], operands[2]));
+ else if (mips_cpu != PROCESSOR_R4000 || TARGET_MIPS16)
+ emit_insn (gen_mulsi3_internal (operands[0], operands[1], operands[2]));
+ else
+ emit_insn (gen_mulsi3_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"))
+ (clobber (match_scratch:SI 5 "=a,a"))]
+ "(GENERATE_MULT3
+ || TARGET_MIPS5400 /* CYGNUS LOCAL vr5400/raeburn */
+ || TARGET_MAD)
+ && !0" ;; CYGNUS LOCAL law
+ "*
+{
+ if (which_alternative == 1)
+ return \"mult\\t%1,%2\";
+ if (TARGET_MIPS5400 /* CYGNUS LOCAL vr5400/raeburn */
+ || TARGET_MAD)
+ return \"mul\\t%0,%1,%2\";
+ return \"mult\\t%0,%1,%2\";
+}"
+ [(set_attr "type" "imul")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1")])
+
+(define_insn "mulsi3_internal"
+ [(set (match_operand:SI 0 "register_operand" "=l")
+ (mult:SI (match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "register_operand" "d")))
+ (clobber (match_scratch:SI 3 "=h"))
+ (clobber (match_scratch:SI 4 "=a"))]
+ "mips_cpu != PROCESSOR_R4000 || TARGET_MIPS16"
+ "mult\\t%1,%2"
+ [(set_attr "type" "imul")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1")])
+
+(define_insn "mulsi3_r4000"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (mult:SI (match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "register_operand" "d")))
+ (clobber (match_scratch:SI 3 "=h"))
+ (clobber (match_scratch:SI 4 "=l"))
+ (clobber (match_scratch:SI 5 "=a"))]
+ "mips_cpu == PROCESSOR_R4000 && !TARGET_MIPS16"
+ "*
+{
+ rtx xoperands[10];
+
+ xoperands[0] = operands[0];
+ xoperands[1] = gen_rtx (REG, SImode, LO_REGNUM);
+
+ output_asm_insn (\"mult\\t%1,%2\", operands);
+ output_asm_insn (mips_move_1word (xoperands, insn, FALSE), xoperands);
+ return \"\";
+}"
+ [(set_attr "type" "imul")
+ (set_attr "mode" "SI")
+ (set_attr "length" "3")]) ;; mult + mflo + delay
+
+;; 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 "=a,a,a"))
+ (clobber (match_scratch:SI 7 "=X,X,d"))]
+ "(TARGET_MIPS3900
+ || TARGET_MIPS5400) /* CYGNUS LOCAL vr5400/raeburn */
+ && !TARGET_MIPS16"
+ "*
+{
+ static char *const madd[] = { \"madd\\t%1,%2\", \"madd\\t%0,%1,%2\" };
+ static char *const macc[] = { \"macc\\t$0,%1,%2\", \"macc\\t%0,%1,%2\" }; /* CYGNUS LOCAL vr5400/raeburn */
+ if (which_alternative == 2)
+ return \"#\";
+ /* CYGNUS LOCAL vr5400/raeburn */
+ if (TARGET_MIPS5400)
+ return macc[which_alternative];
+ /* END CYGNUS LOCAL */
+ return madd[which_alternative];
+}"
+ [(set_attr "type" "imul,imul,multi")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1,1,2")])
+
+;; 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 ""))
+ (clobber (match_scratch:SI 7 ""))]
+ "reload_completed && GP_REG_P (true_regnum (operands[0])) && GP_REG_P (true_regnum (operands[3]))"
+ [(parallel [(set (match_dup 7)
+ (mult:SI (match_dup 1) (match_dup 2)))
+ (clobber (match_dup 4))
+ (clobber (match_dup 5))
+ (clobber (match_dup 6))])
+ (set (match_dup 0) (plus:SI (match_dup 7) (match_dup 3)))]
+ "")
+
+;; CYGNUS LOCAL vr5400/raeburn
+(define_insn "*muls_r5400"
+ [(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 "=a,a"))
+ (clobber (match_scratch:SI 5 "=X,l"))]
+ "TARGET_MIPS5400"
+ "@
+ muls\\t$0,%1,%2
+ muls\\t%0,%1,%2"
+ [(set_attr "type" "imul")
+ (set_attr "mode" "SI")])
+
+;; See comments above for mul_acc_si.
+(define_insn "*msac_r5400"
+ [(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 "=a,a,a"))
+ (clobber (match_scratch:SI 7 "=X,X,d"))]
+ "TARGET_MIPS5400"
+ "@
+ msac\\t$0,%2,%3
+ msac\\t%0,%2,%3
+ #"
+ [(set_attr "type" "imul,imul,multi")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1,1,2")])
+
+(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 ""))
+ (clobber (match_scratch:SI 7 ""))]
+ "reload_completed && GP_REG_P (true_regnum (operands[0])) && GP_REG_P (true_regnum (operands[1]))"
+ [(parallel [(set (match_dup 7)
+ (mult:SI (match_dup 2) (match_dup 3)))
+ (clobber (match_dup 4))
+ (clobber (match_dup 5))
+ (clobber (match_dup 6))])
+ (set (match_dup 0) (minus:SI (match_dup 1) (match_dup 7)))]
+ "")
+;; END CYGNUS LOCAL
+
+(define_expand "muldi3"
+ [(set (match_operand:DI 0 "register_operand" "=l")
+ (mult:DI (match_operand:DI 1 "se_register_operand" "d")
+ (match_operand:DI 2 "register_operand" "d")))
+ (clobber (match_scratch:DI 3 "=h"))
+ (clobber (match_scratch:DI 4 "=a"))]
+ "TARGET_64BIT && !0" ;; CYGNUS LOCAL law
+
+ "
+{
+ if (GENERATE_MULT3 || mips_cpu == PROCESSOR_R4000 || TARGET_MIPS16)
+ emit_insn (gen_muldi3_internal2 (operands[0], operands[1], operands[2]));
+ else
+ emit_insn (gen_muldi3_internal (operands[0], operands[1], operands[2]));
+ DONE;
+}")
+
+;; Don't accept both operands using se_register_operand, because if
+;; both operands are sign extended we would prefer to use mult in the
+;; mulsidi3 pattern. Commutativity should permit either operand to be
+;; sign extended.
+
+(define_insn "muldi3_internal"
+ [(set (match_operand:DI 0 "register_operand" "=l")
+ (mult:DI (match_operand:DI 1 "se_register_operand" "d")
+ (match_operand:DI 2 "register_operand" "d")))
+ (clobber (match_scratch:DI 3 "=h"))
+ (clobber (match_scratch:DI 4 "=a"))]
+ "TARGET_64BIT && mips_cpu != PROCESSOR_R4000 && !TARGET_MIPS16 && !0" ;; CYGNUS LOCAL law
+ "dmult\\t%1,%2"
+ [(set_attr "type" "imul")
+ (set_attr "mode" "DI")
+ (set_attr "length" "1")])
+
+(define_insn "muldi3_internal2"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (mult:DI (match_operand:DI 1 "se_register_operand" "d")
+ (match_operand:DI 2 "register_operand" "d")))
+ (clobber (match_scratch:DI 3 "=h"))
+ (clobber (match_scratch:DI 4 "=l"))
+ (clobber (match_scratch:DI 5 "=a"))]
+ "TARGET_64BIT && (GENERATE_MULT3 || mips_cpu == PROCESSOR_R4000 || TARGET_MIPS16) && !0" ;; CYGNUS LOCAL law
+ "*
+{
+ if (GENERATE_MULT3)
+ output_asm_insn (\"dmult\\t%0,%1,%2\", operands);
+ else
+ {
+ rtx xoperands[10];
+
+ xoperands[0] = operands[0];
+ xoperands[1] = gen_rtx (REG, DImode, LO_REGNUM);
+
+ output_asm_insn (\"dmult\\t%1,%2\", operands);
+ output_asm_insn (mips_move_1word (xoperands, insn, FALSE), xoperands);
+ }
+ return \"\";
+}"
+ [(set_attr "type" "imul")
+ (set_attr "mode" "DI")
+ (set (attr "length")
+ (if_then_else (ne (symbol_ref "GENERATE_MULT3") (const_int 0))
+ (const_int 1)
+ (const_int 3)))]) ;; mult + mflo + delay
+
+;; ??? We could define a mulditi3 pattern when TARGET_64BIT.
+
+(define_expand "mulsidi3"
+ [(set (match_operand:DI 0 "register_operand" "=x")
+ (mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "d"))
+ (sign_extend:DI (match_operand:SI 2 "register_operand" "d"))))]
+ ""
+ "
+{
+ rtx dummy = gen_rtx (SIGN_EXTEND, DImode, const0_rtx);
+ if (TARGET_64BIT)
+ emit_insn (gen_mulsidi3_64bit (operands[0], operands[1], operands[2],
+ dummy, dummy));
+ else
+ emit_insn (gen_mulsidi3_internal (operands[0], operands[1], operands[2],
+ dummy, dummy));
+ DONE;
+}")
+
+(define_expand "umulsidi3"
+ [(set (match_operand:DI 0 "register_operand" "=x")
+ (mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "d"))
+ (zero_extend:DI (match_operand:SI 2 "register_operand" "d"))))]
+ ""
+ "
+{
+ rtx dummy = gen_rtx (ZERO_EXTEND, DImode, const0_rtx);
+ if (TARGET_64BIT)
+ emit_insn (gen_mulsidi3_64bit (operands[0], operands[1], operands[2],
+ dummy, dummy));
+ else
+ emit_insn (gen_mulsidi3_internal (operands[0], operands[1], operands[2],
+ dummy, dummy));
+ DONE;
+}")
+
+(define_insn "mulsidi3_internal"
+ [(set (match_operand:DI 0 "register_operand" "=x")
+ (mult:DI (match_operator:DI 3 "extend_operator"
+ [(match_operand:SI 1 "register_operand" "d")])
+ (match_operator:DI 4 "extend_operator"
+ [(match_operand:SI 2 "register_operand" "d")])))
+ (clobber (match_scratch:SI 5 "=a"))]
+ "!TARGET_64BIT && GET_CODE (operands[3]) == GET_CODE (operands[4])"
+ "*
+{
+ if (GET_CODE (operands[3]) == SIGN_EXTEND)
+ return \"mult\\t%1,%2\";
+ return \"multu\\t%1,%2\";
+}"
+ [(set_attr "type" "imul")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1")])
+
+
+(define_insn "mulsidi3_64bit"
+ [(set (match_operand:DI 0 "register_operand" "=a")
+ (mult:DI (match_operator:DI 3 "extend_operator"
+ [(match_operand:SI 1 "register_operand" "d")])
+ (match_operator:DI 4 "extend_operator"
+ [(match_operand:SI 2 "register_operand" "d")])))
+ (clobber (match_scratch:DI 5 "=l"))
+ (clobber (match_scratch:DI 6 "=h"))]
+ "TARGET_64BIT && !0 && GET_CODE (operands[3]) == GET_CODE (operands[4])" ;; CYGNUS LOCAL law
+ "*
+{
+ if (GET_CODE (operands[3]) == SIGN_EXTEND)
+ return \"mult\\t%1,%2\";
+ return \"multu\\t%1,%2\";
+}"
+ [(set_attr "type" "imul")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1")])
+
+;; CYGNUS LOCAL vr5400/raeburn
+
+;; widening multiply with accumulator and/or negation
+;; These don't match yet for zero-extending; too complex for combine?
+;; Possible additions we should have:
+;; "=x" variants for when !TARGET_64BIT ?
+;; all-d alternatives with splits like pure SImode versions
+(define_insn "*muls_r5400_di"
+ [(set (match_operand:DI 0 "register_operand" "=a")
+ (neg:DI
+ (mult:DI (match_operator:DI 3 "extend_operator"
+ [(match_operand:SI 1 "register_operand" "d")])
+ (match_operator:DI 4 "extend_operator"
+ [(match_operand:SI 2 "register_operand" "d")]))))
+ (clobber (match_scratch:SI 5 "=h"))
+ (clobber (match_scratch:SI 6 "=l"))]
+ "TARGET_64BIT && TARGET_MIPS5400 && GET_CODE (operands[3]) == GET_CODE (operands[4])"
+ "*
+{
+ if (GET_CODE (operands[3]) == SIGN_EXTEND)
+ return \"muls\\t$0,%1,%2\";
+ else
+ return \"mulsu\\t$0,%1,%2\";
+}"
+ [(set_attr "type" "imul")
+ (set_attr "mode" "SI")])
+
+(define_insn "*msac_r5400_di"
+ [(set (match_operand:DI 0 "register_operand" "=a")
+ (minus:DI (match_operand:DI 3 "register_operand" "0")
+ (mult:DI (match_operator:DI 4 "extend_operator"
+ [(match_operand:SI 1 "register_operand" "d")])
+ (match_operator:DI 5 "extend_operator"
+ [(match_operand:SI 2 "register_operand" "d")]))))
+ (clobber (match_scratch:SI 6 "=h"))
+ (clobber (match_scratch:SI 7 "=l"))]
+ "TARGET_64BIT && TARGET_MIPS5400 && GET_CODE (operands[4]) == GET_CODE (operands[5])"
+ "*
+{
+ if (GET_CODE (operands[4]) == SIGN_EXTEND)
+ return \"msac\\t$0,%1,%2\";
+ else
+ return \"msacu\\t$0,%1,%2\";
+}"
+ [(set_attr "type" "imul")
+ (set_attr "mode" "SI")])
+;; END CYGNUS LOCAL
+
+;; _highpart patterns
+(define_expand "smulsi3_highpart"
+ [(set (match_operand:SI 0 "register_operand" "=h")
+ (truncate:SI
+ (lshiftrt:DI (mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "d"))
+ (sign_extend:DI (match_operand:SI 2 "register_operand" "d")))
+ (const_int 32))))]
+ ""
+ "
+{
+ rtx dummy = gen_rtx (SIGN_EXTEND, DImode, const0_rtx);
+ rtx dummy2 = gen_rtx_LSHIFTRT (DImode, const0_rtx, const0_rtx);
+#ifndef NO_MD_PROTOTYPES
+ rtx (*genfn) PROTO((rtx, rtx, rtx, rtx, rtx, rtx));
+#else
+ rtx (*genfn) ();
+#endif
+ genfn = gen_xmulsi3_highpart_internal;
+ /* CYGNUS LOCAL vr5400/raeburn */
+ if (TARGET_MIPS5400)
+ genfn = gen_xmulsi3_highpart_5400;
+ /* END CYGNUS LOCAL */
+ emit_insn ((*genfn) (operands[0], operands[1], operands[2], dummy,
+ dummy, dummy2));
+ DONE;
+}")
+
+(define_expand "umulsi3_highpart"
+ [(set (match_operand:SI 0 "register_operand" "=h")
+ (truncate:SI
+ (lshiftrt:DI (mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "d"))
+ (zero_extend:DI (match_operand:SI 2 "register_operand" "d")))
+ (const_int 32))))]
+ ""
+ "
+{
+ rtx dummy = gen_rtx (ZERO_EXTEND, DImode, const0_rtx);
+ rtx dummy2 = gen_rtx_LSHIFTRT (DImode, const0_rtx, const0_rtx);
+#ifndef NO_MD_PROTOTYPES
+ rtx (*genfn) PROTO((rtx, rtx, rtx, rtx, rtx, rtx));
+#else
+ rtx (*genfn) ();
+#endif
+ genfn = gen_xmulsi3_highpart_internal;
+ /* CYGNUS LOCAL vr5400/raeburn */
+ if (TARGET_MIPS5400)
+ genfn = gen_xmulsi3_highpart_5400;
+ /* END CYGNUS LOCAL */
+ emit_insn ((*genfn) (operands[0], operands[1], operands[2], dummy,
+ dummy, dummy2));
+ DONE;
+}")
+
+
+(define_insn "xmulsi3_highpart_internal"
+ [(set (match_operand:SI 0 "register_operand" "=h")
+ (truncate:SI
+ (match_operator:DI 5 "highpart_shift_operator"
+ [(mult:DI (match_operator:DI 3 "extend_operator"
+ [(match_operand:SI 1 "register_operand" "d")])
+ (match_operator:DI 4 "extend_operator"
+ [(match_operand:SI 2 "register_operand" "d")]))
+ (const_int 32)])))
+ (clobber (match_scratch:SI 6 "=l"))
+ (clobber (match_scratch:SI 7 "=a"))]
+ "! TARGET_MIPS5400 && !0 && GET_CODE (operands[3]) == GET_CODE (operands[4])" ;; CYGNUS LOCAL vr5400/raeburn ;; CYGNUS LOCAL law
+ "*
+{
+ if (GET_CODE (operands[3]) == SIGN_EXTEND)
+ return \"mult\\t%1,%2\";
+ else
+ return \"multu\\t%1,%2\";
+}"
+ [(set_attr "type" "imul")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1")])
+
+;; CYGNUS LOCAL vr5400/raeburn
+(define_insn "xmulsi3_highpart_5400"
+ [(set (match_operand:SI 0 "register_operand" "=h,d")
+ (truncate:SI
+ (match_operator:DI 5 "highpart_shift_operator"
+ [(mult:DI (match_operator:DI 3 "extend_operator"
+ [(match_operand:SI 1 "register_operand" "d,d")])
+ (match_operator:DI 4 "extend_operator"
+ [(match_operand:SI 2 "register_operand" "d,d")]))
+ (const_int 32)])))
+ (clobber (match_scratch:SI 6 "=l,l"))
+ (clobber (match_scratch:SI 7 "=a,a"))
+ (clobber (match_scratch:SI 8 "=X,h"))]
+ "TARGET_MIPS5400 && GET_CODE (operands[3]) == GET_CODE (operands[4])"
+ "*
+{
+ char *const sign[] = { \"mult\\t%1,%2\", \"mulhi\\t%0,%1,%2\" };
+ char *const zero[] = { \"multu\\t%1,%2\", \"mulhiu\\t%0,%1,%2\" };
+ if (GET_CODE (operands[3]) == SIGN_EXTEND)
+ return sign[which_alternative];
+ else
+ return zero[which_alternative];
+}"
+ [(set_attr "type" "imul")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1")])
+
+(define_insn "*xmulsi3_neg_highpart_5400"
+ [(set (match_operand:SI 0 "register_operand" "=h,d")
+ (truncate:SI
+ (match_operator:DI 5 "highpart_shift_operator"
+ [(neg:DI
+ (mult:DI (match_operator:DI 3 "extend_operator"
+ [(match_operand:SI 1 "register_operand" "d,d")])
+ (match_operator:DI 4 "extend_operator"
+ [(match_operand:SI 2 "register_operand" "d,d")])))
+ (const_int 32)])))
+ (clobber (match_scratch:SI 6 "=l,l"))
+ (clobber (match_scratch:SI 7 "=a,a"))
+ (clobber (match_scratch:SI 8 "=X,h"))]
+ "TARGET_MIPS5400 && GET_CODE (operands[3]) == GET_CODE (operands[4])"
+ "*
+{
+ char *const sign[] = { \"mulshi\\t$0,%1,%2\", \"mulshi\\t%0,%1,%2\" };
+ char *const zero[] = { \"mulshiu\\t$0,%1,%2\", \"mulshiu\\t%0,%1,%2\" };
+ if (GET_CODE (operands[3]) == SIGN_EXTEND)
+ return sign[which_alternative];
+ else
+ return zero[which_alternative];
+}"
+ [(set_attr "type" "imul")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1")])
+;; END CYGNUS LOCAL
+
+(define_insn "smuldi3_highpart"
+ [(set (match_operand:DI 0 "register_operand" "=h")
+ (truncate:DI
+ (lshiftrt:TI (mult:TI (sign_extend:TI (match_operand:DI 1 "se_register_operand" "d"))
+ (sign_extend:TI (match_operand:DI 2 "se_register_operand" "d")))
+ (const_int 64))))
+ (clobber (match_scratch:DI 3 "=l"))
+ (clobber (match_scratch:DI 4 "=a"))]
+ "TARGET_64BIT && !0" ;; CYGNUS LOCAL law
+ "dmult\\t%1,%2"
+ [(set_attr "type" "imul")
+ (set_attr "mode" "DI")
+ (set_attr "length" "1")])
+
+(define_insn "umuldi3_highpart"
+ [(set (match_operand:DI 0 "register_operand" "=h")
+ (truncate:DI
+ (lshiftrt:TI (mult:TI (zero_extend:TI (match_operand:DI 1 "se_register_operand" "d"))
+ (zero_extend:TI (match_operand:DI 2 "se_register_operand" "d")))
+ (const_int 64))))
+ (clobber (match_scratch:DI 3 "=l"))
+ (clobber (match_scratch:DI 4 "=a"))]
+ "TARGET_64BIT && !0" ;; CYGNUS LOCAL law
+ "dmultu\\t%1,%2"
+ [(set_attr "type" "imul")
+ (set_attr "mode" "DI")
+ (set_attr "length" "1")])
+
+;; 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"))
+ (clobber (match_scratch:SI 4 "=a"))]
+ "TARGET_MAD"
+ "mad\\t%1,%2"
+ [(set_attr "type" "imul")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1")])
+
+(define_insn "*mul_acc_di"
+ [(set (match_operand:DI 0 "register_operand" "+x")
+ (plus:DI (mult:DI (match_operator:DI 3 "extend_operator"
+ [(match_operand:SI 1 "register_operand" "d")])
+ (match_operator:DI 4 "extend_operator"
+ [(match_operand:SI 2 "register_operand" "d")]))
+ (match_dup 0)))
+ (clobber (match_scratch:SI 5 "=a"))]
+ "TARGET_MAD
+ && ! TARGET_64BIT
+ && GET_CODE (operands[3]) == GET_CODE (operands[4])"
+ "*
+{
+ if (GET_CODE (operands[3]) == SIGN_EXTEND)
+ return \"mad\\t%1,%2\";
+ else
+ return \"madu\\t%1,%2\";
+}"
+ [(set_attr "type" "imul")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1")])
+
+(define_insn "*mul_acc_64bit_di"
+ [(set (match_operand:DI 0 "register_operand" "+a")
+ (plus:DI (mult:DI (match_operator:DI 3 "extend_operator"
+ [(match_operand:SI 1 "register_operand" "d")])
+ (match_operator:DI 4 "extend_operator"
+ [(match_operand:SI 2 "register_operand" "d")]))
+ (match_dup 0)))
+ (clobber (match_scratch:SI 5 "=h"))
+ (clobber (match_scratch:SI 6 "=l"))]
+ "(TARGET_MAD || TARGET_MIPS5400) /* CYGNUS LOCAL vr5400/raeburn */
+ && TARGET_64BIT && GET_CODE (operands[3]) == GET_CODE (operands[4])"
+ "*
+{
+ if (TARGET_MAD)
+ {
+ if (GET_CODE (operands[3]) == SIGN_EXTEND)
+ return \"mad\\t%1,%2\";
+ else
+ return \"madu\\t%1,%2\";
+ }
+ /* CYGNUS LOCAL vr5400/raeburn */
+ else if (TARGET_MIPS5400)
+ {
+ if (GET_CODE (operands[3]) == SIGN_EXTEND)
+ return \"macc\\t$0,%1,%2\";
+ else
+ return \"maccu\\t$0,%1,%2\";
+ }
+ /* END CYGNUS LOCAL */
+ else
+ abort ();
+}"
+ [(set_attr "type" "imul")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1")])
+
+;; Floating point multiply accumulate instructions.
+
+(define_insn ""
+ [(set (match_operand:DF 0 "register_operand" "=f")
+ (plus:DF (mult:DF (match_operand:DF 1 "register_operand" "f")
+ (match_operand:DF 2 "register_operand" "f"))
+ (match_operand:DF 3 "register_operand" "f")))]
+ "mips_isa >= 4 && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
+ "madd.d\\t%0,%3,%1,%2"
+ [(set_attr "type" "fmadd")
+ (set_attr "mode" "DF")
+ (set_attr "length" "1")])
+
+(define_insn ""
+ [(set (match_operand:SF 0 "register_operand" "=f")
+ (plus:SF (mult:SF (match_operand:SF 1 "register_operand" "f")
+ (match_operand:SF 2 "register_operand" "f"))
+ (match_operand:SF 3 "register_operand" "f")))]
+ "mips_isa >= 4 && TARGET_HARD_FLOAT"
+ "madd.s\\t%0,%3,%1,%2"
+ [(set_attr "type" "fmadd")
+ (set_attr "mode" "SF")
+ (set_attr "length" "1")])
+
+(define_insn ""
+ [(set (match_operand:DF 0 "register_operand" "=f")
+ (minus:DF (mult:DF (match_operand:DF 1 "register_operand" "f")
+ (match_operand:DF 2 "register_operand" "f"))
+ (match_operand:DF 3 "register_operand" "f")))]
+ "mips_isa >= 4 && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
+ "msub.d\\t%0,%3,%1,%2"
+ [(set_attr "type" "fmadd")
+ (set_attr "mode" "DF")
+ (set_attr "length" "1")])
+
+(define_insn ""
+ [(set (match_operand:SF 0 "register_operand" "=f")
+ (minus:SF (mult:SF (match_operand:SF 1 "register_operand" "f")
+ (match_operand:SF 2 "register_operand" "f"))
+ (match_operand:SF 3 "register_operand" "f")))]
+
+ "mips_isa >= 4 && TARGET_HARD_FLOAT"
+ "msub.s\\t%0,%3,%1,%2"
+ [(set_attr "type" "fmadd")
+ (set_attr "mode" "SF")
+ (set_attr "length" "1")])
+
+(define_insn ""
+ [(set (match_operand:DF 0 "register_operand" "=f")
+ (neg:DF (plus:DF (mult:DF (match_operand:DF 1 "register_operand" "f")
+ (match_operand:DF 2 "register_operand" "f"))
+ (match_operand:DF 3 "register_operand" "f"))))]
+ "mips_isa >= 4 && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
+ "nmadd.d\\t%0,%3,%1,%2"
+ [(set_attr "type" "fmadd")
+ (set_attr "mode" "DF")
+ (set_attr "length" "1")])
+
+(define_insn ""
+ [(set (match_operand:SF 0 "register_operand" "=f")
+ (neg:SF (plus:SF (mult:SF (match_operand:SF 1 "register_operand" "f")
+ (match_operand:SF 2 "register_operand" "f"))
+ (match_operand:SF 3 "register_operand" "f"))))]
+ "mips_isa >= 4 && TARGET_HARD_FLOAT"
+ "nmadd.s\\t%0,%3,%1,%2"
+ [(set_attr "type" "fmadd")
+ (set_attr "mode" "SF")
+ (set_attr "length" "1")])
+
+(define_insn ""
+ [(set (match_operand:DF 0 "register_operand" "=f")
+ (minus:DF (match_operand:DF 1 "register_operand" "f")
+ (mult:DF (match_operand:DF 2 "register_operand" "f")
+ (match_operand:DF 3 "register_operand" "f"))))]
+ "mips_isa >= 4 && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
+ "nmsub.d\\t%0,%1,%2,%3"
+ [(set_attr "type" "fmadd")
+ (set_attr "mode" "DF")
+ (set_attr "length" "1")])
+
+(define_insn ""
+ [(set (match_operand:SF 0 "register_operand" "=f")
+ (minus:SF (match_operand:SF 1 "register_operand" "f")
+ (mult:SF (match_operand:SF 2 "register_operand" "f")
+ (match_operand:SF 3 "register_operand" "f"))))]
+ "mips_isa >= 4 && TARGET_HARD_FLOAT"
+ "nmsub.s\\t%0,%1,%2,%3"
+ [(set_attr "type" "fmadd")
+ (set_attr "mode" "SF")
+ (set_attr "length" "1")])
+
+;; ....................
+;; DIVISION and REMAINDER
+;; ....................
+
+(define_insn "divdf3"
+ [(set (match_operand:DF 0 "register_operand" "=f")
+ (div:DF (match_operand:DF 1 "register_operand" "f")
+ (match_operand:DF 2 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
+ "div.d\\t%0,%1,%2"
+ [(set_attr "type" "fdiv")
+ (set_attr "mode" "DF")
+ (set_attr "length" "1")])
+
+(define_insn "divsf3"
+ [(set (match_operand:SF 0 "register_operand" "=f")
+ (div:SF (match_operand:SF 1 "register_operand" "f")
+ (match_operand:SF 2 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT"
+ "div.s\\t%0,%1,%2"
+ [(set_attr "type" "fdiv")
+ (set_attr "mode" "SF")
+ (set_attr "length" "1")])
+
+(define_insn ""
+ [(set (match_operand:DF 0 "register_operand" "=f")
+ (div:DF (match_operand:DF 1 "const_float_1_operand" "")
+ (match_operand:DF 2 "register_operand" "f")))]
+ "mips_isa >= 4 && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT && flag_fast_math"
+ "recip.d\\t%0,%2"
+ [(set_attr "type" "fdiv")
+ (set_attr "mode" "DF")
+ (set_attr "length" "1")])
+
+(define_insn ""
+ [(set (match_operand:SF 0 "register_operand" "=f")
+ (div:SF (match_operand:SF 1 "const_float_1_operand" "")
+ (match_operand:SF 2 "register_operand" "f")))]
+ "mips_isa >= 4 && TARGET_HARD_FLOAT && flag_fast_math"
+ "recip.s\\t%0,%2"
+ [(set_attr "type" "fdiv")
+ (set_attr "mode" "SF")
+ (set_attr "length" "1")])
+
+;; If optimizing, prefer the divmod functions over separate div and
+;; mod functions, since this will allow using one instruction for both
+;; the quotient and remainder. At present, the divmod is not moved out
+;; of loops if it is constant within the loop, so allow -mdebugc to
+;; use the old method of doing things.
+
+;; 64 is the multiply/divide hi register
+;; 65 is the multiply/divide lo register
+
+;; ??? We can't accept constants here, because the MIPS assembler will replace
+;; a divide by power of 2 with a shift, and then the remainder is no longer
+;; available.
+
+(define_expand "divmodsi4"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (div:SI (match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "register_operand" "d")))
+ (set (match_operand:SI 3 "register_operand" "=d")
+ (mod:SI (match_dup 1)
+ (match_dup 2)))
+ (clobber (match_scratch:SI 4 "=l"))
+ (clobber (match_scratch:SI 5 "=h"))
+ (clobber (match_scratch:SI 6 "=a"))]
+ "optimize"
+ "
+{
+ rtx label;
+
+
+ emit_insn (gen_divmodsi4_internal (operands[0], operands[1], operands[2],
+ operands[3]));
+ if (!TARGET_NO_CHECK_ZERO_DIV)
+ {
+ emit_insn (gen_div_trap (operands[2],
+ GEN_INT (0),
+ GEN_INT (0x7)));
+ }
+ if (TARGET_CHECK_RANGE_DIV)
+ {
+ emit_insn (gen_div_trap (operands[2],
+ copy_to_mode_reg (SImode, GEN_INT (-1)),
+ GEN_INT (0x6)));
+ emit_insn (gen_div_trap (operands[2],
+ copy_to_mode_reg (SImode, GEN_INT (0x80000000)),
+ GEN_INT (0x6)));
+ }
+
+ DONE;
+}")
+
+
+(define_insn "divmodsi4_internal"
+ [(set (match_operand:SI 0 "register_operand" "=l")
+ (div:SI (match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "register_operand" "d")))
+ (set (match_operand:SI 3 "register_operand" "=h")
+ (mod:SI (match_dup 1)
+ (match_dup 2)))
+ (clobber (match_scratch:SI 6 "=a"))]
+ "optimize && !0" ;; CYGNUS LOCAL law
+ "div\\t$0,%1,%2"
+ [(set_attr "type" "idiv")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1")])
+
+(define_expand "divmoddi4"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (div:DI (match_operand:DI 1 "se_register_operand" "d")
+ (match_operand:DI 2 "se_register_operand" "d")))
+ (set (match_operand:DI 3 "register_operand" "=d")
+ (mod:DI (match_dup 1)
+ (match_dup 2)))
+ (clobber (match_scratch:DI 4 "=l"))
+ (clobber (match_scratch:DI 5 "=h"))
+ (clobber (match_scratch:DI 6 "=a"))]
+ "TARGET_64BIT && optimize && !0" ;; CYGNUS LOCAL law
+ "
+{
+ rtx label;
+
+ emit_insn (gen_divmoddi4_internal (operands[0], operands[1], operands[2],
+ operands[3]));
+ if (!TARGET_NO_CHECK_ZERO_DIV)
+ {
+ emit_insn (gen_div_trap (operands[2],
+ GEN_INT (0),
+ GEN_INT (0x7)));
+ }
+ if (TARGET_CHECK_RANGE_DIV)
+ {
+ emit_insn (gen_div_trap (operands[2],
+ copy_to_mode_reg (DImode, GEN_INT (-1)),
+ GEN_INT (0x6)));
+ emit_insn (gen_div_trap (operands[2],
+ copy_to_mode_reg (DImode, GEN_INT (0x80000000)),
+ GEN_INT (0x6)));
+ }
+
+ DONE;
+}")
+
+(define_insn "divmoddi4_internal"
+ [(set (match_operand:DI 0 "register_operand" "=l")
+ (div:DI (match_operand:DI 1 "se_register_operand" "d")
+ (match_operand:DI 2 "se_register_operand" "d")))
+ (set (match_operand:DI 3 "register_operand" "=h")
+ (mod:DI (match_dup 1)
+ (match_dup 2)))
+ (clobber (match_scratch:DI 6 "=a"))]
+ "TARGET_64BIT && optimize && !0" ;; CYGNUS LOCAL law
+ "ddiv\\t$0,%1,%2"
+ [(set_attr "type" "idiv")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1")])
+
+(define_expand "udivmodsi4"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (udiv:SI (match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "register_operand" "d")))
+ (set (match_operand:SI 3 "register_operand" "=d")
+ (umod:SI (match_dup 1)
+ (match_dup 2)))
+ (clobber (match_scratch:SI 4 "=l"))
+ (clobber (match_scratch:SI 5 "=h"))
+ (clobber (match_scratch:SI 6 "=a"))]
+ "optimize"
+ "
+{
+ rtx label;
+
+ emit_insn (gen_udivmodsi4_internal (operands[0], operands[1], operands[2],
+ operands[3]));
+ if (!TARGET_NO_CHECK_ZERO_DIV)
+ {
+ emit_insn (gen_div_trap (operands[2],
+ GEN_INT (0),
+ GEN_INT (0x7)));
+ }
+
+ DONE;
+}")
+
+
+(define_insn "udivmodsi4_internal"
+ [(set (match_operand:SI 0 "register_operand" "=l")
+ (udiv:SI (match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "register_operand" "d")))
+ (set (match_operand:SI 3 "register_operand" "=h")
+ (umod:SI (match_dup 1)
+ (match_dup 2)))
+ (clobber (match_scratch:SI 6 "=a"))]
+ "optimize && !0" ;; CYGNUS LOCAL law
+ "divu\\t$0,%1,%2"
+ [(set_attr "type" "idiv")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1")])
+
+(define_expand "udivmoddi4"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (udiv:DI (match_operand:DI 1 "se_register_operand" "d")
+ (match_operand:DI 2 "se_register_operand" "d")))
+ (set (match_operand:DI 3 "register_operand" "=d")
+ (umod:DI (match_dup 1)
+ (match_dup 2)))
+ (clobber (match_scratch:DI 4 "=l"))
+ (clobber (match_scratch:DI 5 "=h"))
+ (clobber (match_scratch:DI 6 "=a"))]
+ "TARGET_64BIT && optimize && !0" ;; CYGNUS LOCAL law
+ "
+{
+ rtx label;
+
+ emit_insn (gen_udivmoddi4_internal (operands[0], operands[1], operands[2],
+ operands[3]));
+ if (!TARGET_NO_CHECK_ZERO_DIV)
+ {
+ emit_insn (gen_div_trap (operands[2],
+ GEN_INT (0),
+ GEN_INT (0x7)));
+ }
+
+ DONE;
+}")
+
+(define_insn "udivmoddi4_internal"
+ [(set (match_operand:DI 0 "register_operand" "=l")
+ (udiv:DI (match_operand:DI 1 "se_register_operand" "d")
+ (match_operand:DI 2 "se_register_operand" "d")))
+ (set (match_operand:DI 3 "register_operand" "=h")
+ (umod:DI (match_dup 1)
+ (match_dup 2)))
+ (clobber (match_scratch:DI 6 "=a"))]
+ "TARGET_64BIT && optimize && !0" ;; CYGNUS LOCAL law
+ "ddivu\\t$0,%1,%2"
+ [(set_attr "type" "idiv")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1")])
+
+;; Division trap
+
+ ;; Division trap
+
+(define_expand "div_trap"
+ [(trap_if (eq (match_operand 0 "register_operand" "d")
+ (match_operand 1 "true_reg_or_0_operand" "dJ"))
+ (match_operand 2 "immediate_operand" ""))]
+ ""
+ "
+{
+ if (TARGET_MIPS16)
+ emit_insn (gen_div_trap_mips16 (operands[0],operands[1],operands[2]));
+ else
+ emit_insn (gen_div_trap_normal (operands[0],operands[1],operands[2]));
+ DONE;
+}")
+
+(define_insn "div_trap_normal"
+ [(trap_if (eq (match_operand 0 "register_operand" "d")
+ (match_operand 1 "true_reg_or_0_operand" "dJ"))
+ (match_operand 2 "immediate_operand" ""))]
+ "!TARGET_MIPS16"
+ "*
+{
+ rtx link;
+ int have_dep_anti = 0;
+
+ /* For divmod if one division is not needed then we don't need an extra
+ divide by zero trap, which is anti dependent on previous trap */
+ for (link = LOG_LINKS (insn); link; link = XEXP (link, 1))
+
+ if ((int) REG_DEP_ANTI == (int) REG_NOTE_KIND (link)
+ && GET_CODE (XEXP (link, 0)) == INSN
+ && GET_CODE (PATTERN (XEXP (link, 0))) == TRAP_IF
+ && REGNO (operands[1]) == 0)
+ have_dep_anti = 1;
+ if (! have_dep_anti)
+ {
+ if (GENERATE_BRANCHLIKELY)
+ {
+ if (GET_CODE (operands[1]) == CONST_INT)
+ return \"%(beql\\t%0,$0,1f\\n\\tbreak\\t%2\\n1:%)\";
+ else
+ return \"%(beql\\t%0,%1,1f\\n\\tbreak\\t%2\\n1:%)\";
+ }
+ else
+ {
+ if (GET_CODE (operands[1]) == CONST_INT)
+ return \"%(bne\\t%0,$0,1f\\n\\tnop\\n\\tbreak\\t%2\\n1:%)\";
+ else
+ return \"%(bne\\t%0,%1,1f\\n\\tnop\\n\\tbreak\\t%2\\n1:%)\";
+ }
+ }
+ return \"\";
+}"
+ [(set_attr "type" "unknown")
+ (set_attr "length" "3")])
+
+
+;; The mips16 bne insns is a macro which uses reg 24 as an intermediate.
+
+(define_insn "div_trap_mips16"
+ [(trap_if (eq (match_operand 0 "register_operand" "d")
+ (match_operand 1 "true_reg_or_0_operand" "dJ"))
+ (match_operand 2 "immediate_operand" ""))
+ (clobber (reg:SI 24))]
+ "TARGET_MIPS16"
+ "*
+{
+ rtx link;
+ int have_dep_anti = 0;
+
+ /* For divmod if one division is not needed then we don't need an extra
+ divide by zero trap, which is anti dependent on previous trap */
+ for (link = LOG_LINKS (insn); link; link = XEXP (link, 1))
+
+ if ((int) REG_DEP_ANTI == (int) REG_NOTE_KIND (link)
+ && GET_CODE (XEXP (link, 0)) == INSN
+ && GET_CODE (PATTERN (XEXP (link, 0))) == TRAP_IF
+ && REGNO (operands[1]) == 0)
+ have_dep_anti = 1;
+ if (! have_dep_anti)
+ {
+ /* No branch delay slots on mips16. */
+ if (GET_CODE (operands[1]) == CONST_INT)
+ return \"%(bnez\\t%0,1f\\n\\tbreak\\t%2\\n1:%)\";
+ else
+ return \"%(bne\\t%0,%1,1f\\n\\tbreak\\t%2\\n1:%)\";
+ }
+ return \"\";
+}"
+ [(set_attr "type" "unknown")
+ (set_attr "length" "3")])
+
+(define_expand "divsi3"
+ [(set (match_operand:SI 0 "register_operand" "=l")
+ (div:SI (match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "register_operand" "d")))
+ (clobber (match_scratch:SI 3 "=h"))
+ (clobber (match_scratch:SI 4 "=a"))]
+ "!optimize"
+ "
+{
+ rtx label;
+
+ emit_insn (gen_divsi3_internal (operands[0], operands[1], operands[2]));
+ if (!TARGET_NO_CHECK_ZERO_DIV)
+ {
+ emit_insn (gen_div_trap (operands[2],
+ GEN_INT (0),
+ GEN_INT (0x7)));
+ }
+ if (TARGET_CHECK_RANGE_DIV)
+ {
+ emit_insn (gen_div_trap (operands[2],
+ copy_to_mode_reg (SImode, GEN_INT (-1)),
+ GEN_INT (0x6)));
+ emit_insn (gen_div_trap (operands[2],
+ copy_to_mode_reg (SImode, GEN_INT (0x80000000)),
+ GEN_INT (0x6)));
+ }
+
+ DONE;
+}")
+
+(define_insn "divsi3_internal"
+ [(set (match_operand:SI 0 "register_operand" "=l")
+ (div:SI (match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "nonmemory_operand" "di")))
+ (clobber (match_scratch:SI 3 "=h"))
+ (clobber (match_scratch:SI 4 "=a"))]
+ "!optimize"
+ "div\\t$0,%1,%2"
+ [(set_attr "type" "idiv")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1")])
+
+(define_expand "divdi3"
+ [(set (match_operand:DI 0 "register_operand" "=l")
+ (div:DI (match_operand:DI 1 "se_register_operand" "d")
+ (match_operand:DI 2 "se_register_operand" "d")))
+ (clobber (match_scratch:DI 3 "=h"))
+ (clobber (match_scratch:DI 4 "=a"))]
+ "TARGET_64BIT && !optimize && !0" ;; CYGNUS LOCAL law
+ "
+{
+ rtx label;
+
+ emit_insn (gen_divdi3_internal (operands[0], operands[1], operands[2]));
+ if (!TARGET_NO_CHECK_ZERO_DIV)
+ {
+ emit_insn (gen_div_trap (operands[2],
+ GEN_INT (0),
+ GEN_INT (0x7)));
+ }
+ if (TARGET_CHECK_RANGE_DIV)
+ {
+ emit_insn (gen_div_trap (operands[2],
+ copy_to_mode_reg (DImode, GEN_INT (-1)),
+ GEN_INT (0x6)));
+ emit_insn (gen_div_trap (operands[2],
+ copy_to_mode_reg (DImode, GEN_INT (0x80000000)),
+ GEN_INT (0x6)));
+ }
+
+ DONE;
+}")
+
+(define_insn "divdi3_internal"
+ [(set (match_operand:DI 0 "register_operand" "=l")
+ (div:DI (match_operand:DI 1 "se_register_operand" "d")
+ (match_operand:DI 2 "se_nonmemory_operand" "di")))
+ (clobber (match_scratch:SI 3 "=h"))
+ (clobber (match_scratch:SI 4 "=a"))]
+ "TARGET_64BIT && !optimize && !0" ;; CYGNUS LOCAL law
+ "ddiv\\t$0,%1,%2"
+ [(set_attr "type" "idiv")
+ (set_attr "mode" "DI")
+ (set_attr "length" "1")])
+
+(define_expand "modsi3"
+ [(set (match_operand:SI 0 "register_operand" "=h")
+ (mod:SI (match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "register_operand" "d")))
+ (clobber (match_scratch:SI 3 "=l"))
+ (clobber (match_scratch:SI 4 "=a"))]
+ "!optimize"
+ "
+{
+ rtx label;
+
+ emit_insn (gen_modsi3_internal (operands[0], operands[1], operands[2]));
+ if (!TARGET_NO_CHECK_ZERO_DIV)
+ {
+ emit_insn (gen_div_trap (operands[2],
+ GEN_INT (0),
+ GEN_INT (0x7)));
+ }
+ if (TARGET_CHECK_RANGE_DIV)
+ {
+ emit_insn (gen_div_trap (operands[2],
+ copy_to_mode_reg (SImode, GEN_INT (-1)),
+ GEN_INT (0x6)));
+ emit_insn (gen_div_trap (operands[2],
+ copy_to_mode_reg (SImode, GEN_INT (0x80000000)),
+ GEN_INT (0x6)));
+ }
+
+ DONE;
+}")
+
+(define_insn "modsi3_internal"
+ [(set (match_operand:SI 0 "register_operand" "=h")
+ (mod:SI (match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "nonmemory_operand" "di")))
+ (clobber (match_scratch:SI 3 "=l"))
+ (clobber (match_scratch:SI 4 "=a"))]
+ "!optimize"
+ "div\\t$0,%1,%2"
+ [(set_attr "type" "idiv")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1")])
+
+(define_expand "moddi3"
+ [(set (match_operand:DI 0 "register_operand" "=h")
+ (mod:DI (match_operand:DI 1 "se_register_operand" "d")
+ (match_operand:DI 2 "se_register_operand" "d")))
+ (clobber (match_scratch:DI 3 "=l"))
+ (clobber (match_scratch:DI 4 "=a"))]
+ "TARGET_64BIT && !optimize && !0" ;; CYGNUS LOCAL law
+ "
+{
+ rtx label;
+
+ emit_insn (gen_moddi3_internal (operands[0], operands[1], operands[2]));
+ if (!TARGET_NO_CHECK_ZERO_DIV)
+ {
+ emit_insn (gen_div_trap (operands[2],
+ GEN_INT (0),
+ GEN_INT (0x7)));
+ }
+ if (TARGET_CHECK_RANGE_DIV)
+ {
+ emit_insn (gen_div_trap (operands[2],
+ copy_to_mode_reg (DImode, GEN_INT (-1)),
+ GEN_INT (0x6)));
+ emit_insn (gen_div_trap (operands[2],
+ copy_to_mode_reg (DImode, GEN_INT (0x80000000)),
+ GEN_INT (0x6)));
+ }
+
+ DONE;
+}")
+
+(define_insn "moddi3_internal"
+ [(set (match_operand:DI 0 "register_operand" "=h")
+ (mod:DI (match_operand:DI 1 "se_register_operand" "d")
+ (match_operand:DI 2 "se_nonmemory_operand" "di")))
+ (clobber (match_scratch:SI 3 "=l"))
+ (clobber (match_scratch:SI 4 "=a"))]
+ "TARGET_64BIT && !optimize && !0" ;; CYGNUS LOCAL law
+ "ddiv\\t$0,%1,%2"
+ [(set_attr "type" "idiv")
+ (set_attr "mode" "DI")
+ (set_attr "length" "1")])
+
+(define_expand "udivsi3"
+ [(set (match_operand:SI 0 "register_operand" "=l")
+ (udiv:SI (match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "register_operand" "d")))
+ (clobber (match_scratch:SI 3 "=h"))
+ (clobber (match_scratch:SI 4 "=a"))]
+ "!optimize"
+ "
+{
+ rtx label;
+
+ emit_insn (gen_udivsi3_internal (operands[0], operands[1], operands[2]));
+ if (!TARGET_NO_CHECK_ZERO_DIV)
+ {
+ emit_insn (gen_div_trap (operands[2],
+ GEN_INT (0),
+ GEN_INT (0x7)));
+ }
+
+ DONE;
+}")
+
+(define_insn "udivsi3_internal"
+ [(set (match_operand:SI 0 "register_operand" "=l")
+ (udiv:SI (match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "nonmemory_operand" "di")))
+ (clobber (match_scratch:SI 3 "=h"))
+ (clobber (match_scratch:SI 4 "=a"))]
+ "!optimize"
+ "divu\\t$0,%1,%2"
+ [(set_attr "type" "idiv")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1")])
+
+(define_expand "udivdi3"
+ [(set (match_operand:DI 0 "register_operand" "=l")
+ (udiv:DI (match_operand:DI 1 "se_register_operand" "d")
+ (match_operand:DI 2 "se_register_operand" "di")))
+ (clobber (match_scratch:DI 3 "=h"))
+ (clobber (match_scratch:DI 4 "=a"))]
+ "TARGET_64BIT && !optimize && !0" ;; CYGNUS LOCAL law
+ "
+{
+ rtx label;
+
+ emit_insn (gen_udivdi3_internal (operands[0], operands[1], operands[2]));
+ if (!TARGET_NO_CHECK_ZERO_DIV)
+ {
+ emit_insn (gen_div_trap (operands[2],
+ GEN_INT (0),
+ GEN_INT (0x7)));
+ }
+
+ DONE;
+}")
+
+(define_insn "udivdi3_internal"
+ [(set (match_operand:DI 0 "register_operand" "=l")
+ (udiv:DI (match_operand:DI 1 "se_register_operand" "d")
+ (match_operand:DI 2 "se_nonmemory_operand" "di")))
+ (clobber (match_scratch:SI 3 "=h"))
+ (clobber (match_scratch:SI 4 "=a"))]
+ "TARGET_64BIT && !optimize && !0" ;; CYGNUS LOCAL law
+ "ddivu\\t$0,%1,%2"
+ [(set_attr "type" "idiv")
+ (set_attr "mode" "DI")
+ (set_attr "length" "1")])
+
+(define_expand "umodsi3"
+ [(set (match_operand:SI 0 "register_operand" "=h")
+ (umod:SI (match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "register_operand" "d")))
+ (clobber (match_scratch:SI 3 "=l"))
+ (clobber (match_scratch:SI 4 "=a"))]
+ "!optimize"
+ "
+{
+ rtx label;
+
+ emit_insn (gen_umodsi3_internal (operands[0], operands[1], operands[2]));
+ if (!TARGET_NO_CHECK_ZERO_DIV)
+ {
+ emit_insn (gen_div_trap (operands[2],
+ GEN_INT (0),
+ GEN_INT (0x7)));
+ }
+
+ DONE;
+}")
+
+(define_insn "umodsi3_internal"
+ [(set (match_operand:SI 0 "register_operand" "=h")
+ (umod:SI (match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "nonmemory_operand" "di")))
+ (clobber (match_scratch:SI 3 "=l"))
+ (clobber (match_scratch:SI 4 "=a"))]
+ "!optimize"
+ "divu\\t$0,%1,%2"
+ [(set_attr "type" "idiv")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1")])
+
+(define_expand "umoddi3"
+ [(set (match_operand:DI 0 "register_operand" "=h")
+ (umod:DI (match_operand:DI 1 "se_register_operand" "d")
+ (match_operand:DI 2 "se_register_operand" "di")))
+ (clobber (match_scratch:DI 3 "=l"))
+ (clobber (match_scratch:DI 4 "=a"))]
+ "TARGET_64BIT && !optimize && !0" ;; CYGNUS LOCAL law
+ "
+{
+ rtx label;
+
+ emit_insn (gen_umoddi3_internal (operands[0], operands[1], operands[2]));
+ if (!TARGET_NO_CHECK_ZERO_DIV)
+ {
+ emit_insn (gen_div_trap (operands[2],
+ GEN_INT (0),
+ GEN_INT (0x7)));
+ }
+
+ DONE;
+}")
+
+(define_insn "umoddi3_internal"
+ [(set (match_operand:DI 0 "register_operand" "=h")
+ (umod:DI (match_operand:DI 1 "se_register_operand" "d")
+ (match_operand:DI 2 "se_nonmemory_operand" "di")))
+ (clobber (match_scratch:SI 3 "=l"))
+ (clobber (match_scratch:SI 4 "=a"))]
+ "TARGET_64BIT && !optimize && !0" ;; CYGNUS LOCAL law
+ "ddivu\\t$0,%1,%2"
+ [(set_attr "type" "idiv")
+ (set_attr "mode" "DI")
+ (set_attr "length" "1")])
+
+;; ....................
+;; SQUARE ROOT
+;; ....................
+
+(define_insn "sqrtdf2"
+ [(set (match_operand:DF 0 "register_operand" "=f")
+ (sqrt:DF (match_operand:DF 1 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT && HAVE_SQRT_P() && TARGET_DOUBLE_FLOAT"
+ "sqrt.d\\t%0,%1"
+ [(set_attr "type" "fsqrt")
+ (set_attr "mode" "DF")
+ (set_attr "length" "1")])
+
+(define_insn "sqrtsf2"
+ [(set (match_operand:SF 0 "register_operand" "=f")
+ (sqrt:SF (match_operand:SF 1 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT && HAVE_SQRT_P()"
+ "sqrt.s\\t%0,%1"
+ [(set_attr "type" "fsqrt")
+ (set_attr "mode" "SF")
+ (set_attr "length" "1")])
+
+(define_insn ""
+ [(set (match_operand:DF 0 "register_operand" "=f")
+ (div:DF (match_operand:DF 1 "const_float_1_operand" "")
+ (sqrt:DF (match_operand:DF 2 "register_operand" "f"))))]
+ "mips_isa >= 4 && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT && flag_fast_math"
+ "rsqrt.d\\t%0,%2"
+ [(set_attr "type" "frsqrt") ; CYGNUS LOCAL vr5400/raeburn
+ (set_attr "mode" "DF")
+ (set_attr "length" "1")])
+
+(define_insn ""
+ [(set (match_operand:SF 0 "register_operand" "=f")
+ (div:SF (match_operand:SF 1 "const_float_1_operand" "")
+ (sqrt:SF (match_operand:SF 2 "register_operand" "f"))))]
+ "mips_isa >= 4 && TARGET_HARD_FLOAT && flag_fast_math"
+ "rsqrt.s\\t%0,%2"
+ [(set_attr "type" "frsqrt") ; CYGNUS LOCAL vr5400/raeburn
+ (set_attr "mode" "SF")
+ (set_attr "length" "1")])
+
+
+;; ....................
+;; ABSOLUTE VALUE
+;; ....................
+
+;; Do not use the integer abs macro instruction, since that signals an
+;; exception on -2147483648 (sigh).
+
+(define_insn "abssi2"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (abs:SI (match_operand:SI 1 "register_operand" "d")))]
+ "!TARGET_MIPS16"
+ "*
+{
+ dslots_jump_total++;
+ dslots_jump_filled++;
+ operands[2] = const0_rtx;
+
+ if (REGNO (operands[0]) == REGNO (operands[1]))
+ {
+ if (GENERATE_BRANCHLIKELY)
+ return \"%(bltzl\\t%1,1f\\n\\tsubu\\t%0,%z2,%0\\n1:%)\";
+ else
+ return \"bgez\\t%1,1f%#\\n\\tsubu\\t%0,%z2,%0\\n1:\";
+ }
+ else
+ return \"%(bgez\\t%1,1f\\n\\tmove\\t%0,%1\\n\\tsubu\\t%0,%z2,%0\\n1:%)\";
+}"
+ [(set_attr "type" "multi")
+ (set_attr "mode" "SI")
+ (set_attr "length" "3")])
+
+(define_insn "absdi2"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (abs:DI (match_operand:DI 1 "se_register_operand" "d")))]
+ "TARGET_64BIT && !TARGET_MIPS16"
+ "*
+{
+ dslots_jump_total++;
+ dslots_jump_filled++;
+ operands[2] = const0_rtx;
+
+ if (REGNO (operands[0]) == REGNO (operands[1]))
+ return \"%(bltzl\\t%1,1f\\n\\tdsubu\\t%0,%z2,%0\\n1:%)\";
+ else
+ return \"%(bgez\\t%1,1f\\n\\tmove\\t%0,%1\\n\\tdsubu\\t%0,%z2,%0\\n1:%)\";
+}"
+ [(set_attr "type" "multi")
+ (set_attr "mode" "DI")
+ (set_attr "length" "3")])
+
+(define_insn "absdf2"
+ [(set (match_operand:DF 0 "register_operand" "=f")
+ (abs:DF (match_operand:DF 1 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
+ "abs.d\\t%0,%1"
+ [(set_attr "type" "fabs")
+ (set_attr "mode" "DF")
+ (set_attr "length" "1")])
+
+(define_insn "abssf2"
+ [(set (match_operand:SF 0 "register_operand" "=f")
+ (abs:SF (match_operand:SF 1 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT"
+ "abs.s\\t%0,%1"
+ [(set_attr "type" "fabs")
+ (set_attr "mode" "SF")
+ (set_attr "length" "1")])
+
+
+;; ....................
+;; FIND FIRST BIT INSTRUCTION
+;; ....................
+
+(define_insn "ffssi2"
+ [(set (match_operand:SI 0 "register_operand" "=&d")
+ (ffs:SI (match_operand:SI 1 "register_operand" "d")))
+ (clobber (match_scratch:SI 2 "=&d"))
+ (clobber (match_scratch:SI 3 "=&d"))]
+ "!TARGET_MIPS16"
+ "*
+{
+ dslots_jump_total += 2;
+ dslots_jump_filled += 2;
+ operands[4] = const0_rtx;
+
+ if (optimize && find_reg_note (insn, REG_DEAD, operands[1]))
+ return \"%(\\
+move\\t%0,%z4\\n\\
+\\tbeq\\t%1,%z4,2f\\n\\
+1:\\tand\\t%2,%1,0x0001\\n\\
+\\taddu\\t%0,%0,1\\n\\
+\\tbeq\\t%2,%z4,1b\\n\\
+\\tsrl\\t%1,%1,1\\n\\
+2:%)\";
+
+ return \"%(\\
+move\\t%0,%z4\\n\\
+\\tmove\\t%3,%1\\n\\
+\\tbeq\\t%3,%z4,2f\\n\\
+1:\\tand\\t%2,%3,0x0001\\n\\
+\\taddu\\t%0,%0,1\\n\\
+\\tbeq\\t%2,%z4,1b\\n\\
+\\tsrl\\t%3,%3,1\\n\\
+2:%)\";
+}"
+ [(set_attr "type" "multi")
+ (set_attr "mode" "SI")
+ (set_attr "length" "6")])
+
+(define_insn "ffsdi2"
+ [(set (match_operand:DI 0 "register_operand" "=&d")
+ (ffs:DI (match_operand:DI 1 "se_register_operand" "d")))
+ (clobber (match_scratch:DI 2 "=&d"))
+ (clobber (match_scratch:DI 3 "=&d"))]
+ "TARGET_64BIT && !TARGET_MIPS16"
+ "*
+{
+ dslots_jump_total += 2;
+ dslots_jump_filled += 2;
+ operands[4] = const0_rtx;
+
+ if (optimize && find_reg_note (insn, REG_DEAD, operands[1]))
+ return \"%(\\
+move\\t%0,%z4\\n\\
+\\tbeq\\t%1,%z4,2f\\n\\
+1:\\tand\\t%2,%1,0x0001\\n\\
+\\tdaddu\\t%0,%0,1\\n\\
+\\tbeq\\t%2,%z4,1b\\n\\
+\\tdsrl\\t%1,%1,1\\n\\
+2:%)\";
+
+ return \"%(\\
+move\\t%0,%z4\\n\\
+\\tmove\\t%3,%1\\n\\
+\\tbeq\\t%3,%z4,2f\\n\\
+1:\\tand\\t%2,%3,0x0001\\n\\
+\\tdaddu\\t%0,%0,1\\n\\
+\\tbeq\\t%2,%z4,1b\\n\\
+\\tdsrl\\t%3,%3,1\\n\\
+2:%)\";
+}"
+ [(set_attr "type" "multi")
+ (set_attr "mode" "DI")
+ (set_attr "length" "6")])
+
+
+;; ....................
+;; 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\";
+ operands[2] = const0_rtx;
+ return \"subu\\t%0,%z2,%1\";
+}"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1")])
+
+(define_expand "negdi2"
+ [(parallel [(set (match_operand:DI 0 "register_operand" "=d")
+ (neg:DI (match_operand:DI 1 "se_register_operand" "d")))
+ (clobber (match_dup 2))])]
+ "(TARGET_64BIT || !TARGET_DEBUG_G_MODE) && !TARGET_MIPS16"
+ "
+{
+ if (TARGET_64BIT)
+ {
+ emit_insn (gen_negdi2_internal_2 (operands[0], operands[1]));
+ DONE;
+ }
+
+ operands[2] = gen_reg_rtx (SImode);
+}")
+
+(define_insn "negdi2_internal"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (neg:DI (match_operand:DI 1 "register_operand" "d")))
+ (clobber (match_operand:SI 2 "register_operand" "=d"))]
+ "! TARGET_64BIT && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16"
+ "*
+{
+ operands[3] = const0_rtx;
+ return \"subu\\t%L0,%z3,%L1\;subu\\t%M0,%z3,%M1\;sltu\\t%2,%z3,%L0\;subu\\t%M0,%M0,%2\";
+}"
+ [(set_attr "type" "darith")
+ (set_attr "mode" "DI")
+ (set_attr "length" "4")])
+
+(define_insn "negdi2_internal_2"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (neg:DI (match_operand:DI 1 "se_register_operand" "d")))]
+ "TARGET_64BIT && !TARGET_MIPS16"
+ "*
+{
+ operands[2] = const0_rtx;
+ return \"dsubu\\t%0,%z2,%1\";
+}"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "DI")
+ (set_attr "length" "1")])
+
+(define_insn "negdf2"
+ [(set (match_operand:DF 0 "register_operand" "=f")
+ (neg:DF (match_operand:DF 1 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
+ "neg.d\\t%0,%1"
+ [(set_attr "type" "fneg")
+ (set_attr "mode" "DF")
+ (set_attr "length" "1")])
+
+(define_insn "negsf2"
+ [(set (match_operand:SF 0 "register_operand" "=f")
+ (neg:SF (match_operand:SF 1 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT"
+ "neg.s\\t%0,%1"
+ [(set_attr "type" "fneg")
+ (set_attr "mode" "SF")
+ (set_attr "length" "1")])
+
+(define_insn "one_cmplsi2"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (not:SI (match_operand:SI 1 "register_operand" "d")))]
+ ""
+ "*
+{
+ if (TARGET_MIPS16)
+ return \"not\\t%0,%1\";
+ operands[2] = const0_rtx;
+ return \"nor\\t%0,%z2,%1\";
+}"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1")])
+
+(define_insn "one_cmpldi2"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (not:DI (match_operand:DI 1 "se_register_operand" "d")))]
+ ""
+ "*
+{
+ if (TARGET_MIPS16)
+ {
+ if (TARGET_64BIT)
+ return \"not\\t%0,%1\";
+ return \"not\\t%M0,%M1\;not\\t%L0,%L1\";
+ }
+ operands[2] = const0_rtx;
+ if (TARGET_64BIT)
+ return \"nor\\t%0,%z2,%1\";
+ return \"nor\\t%M0,%z2,%M1\;nor\\t%L0,%z2,%L1\";
+}"
+ [(set_attr "type" "darith")
+ (set_attr "mode" "DI")
+ (set (attr "length")
+ (if_then_else (ge (symbol_ref "mips_isa") (const_int 3))
+ (const_int 1)
+ (const_int 2)))])
+
+(define_split
+ [(set (match_operand:DI 0 "register_operand" "")
+ (not:DI (match_operand:DI 1 "register_operand" "")))]
+ "reload_completed && !TARGET_64BIT && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE
+ && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0]))
+ && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1]))"
+
+ [(set (subreg:SI (match_dup 0) 0) (not:SI (subreg:SI (match_dup 1) 0)))
+ (set (subreg:SI (match_dup 0) 1) (not:SI (subreg:SI (match_dup 1) 1)))]
+ "")
+
+
+;; ....................
+;; LOGICAL
+;; ....................
+
+;; Many of these instructions uses trivial define_expands, because we
+;; want to use a different set of constraints when TARGET_MIPS16.
+
+(define_expand "andsi3"
+ [(set (match_operand:SI 0 "register_operand" "=d,d")
+ (and:SI (match_operand:SI 1 "uns_arith_operand" "%d,d")
+ (match_operand:SI 2 "uns_arith_operand" "d,K")))]
+ ""
+ "
+{
+ if (TARGET_MIPS16)
+ operands[2] = force_reg (SImode, operands[2]);
+}")
+
+(define_insn ""
+ [(set (match_operand:SI 0 "register_operand" "=d,d")
+ (and:SI (match_operand:SI 1 "uns_arith_operand" "%d,d")
+ (match_operand:SI 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" "SI")
+ (set_attr "length" "1")])
+
+(define_insn ""
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (and:SI (match_operand:SI 1 "register_operand" "%0")
+ (match_operand:SI 2 "register_operand" "d")))]
+ "TARGET_MIPS16"
+ "and\\t%0,%2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1")])
+
+(define_expand "anddi3"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (and:DI (match_operand:DI 1 "se_register_operand" "d")
+ (match_operand:DI 2 "se_register_operand" "d")))]
+ "TARGET_64BIT || !TARGET_DEBUG_G_MODE"
+ "
+{
+ if (TARGET_MIPS16)
+ operands[2] = force_reg (DImode, operands[2]);
+}")
+
+(define_insn ""
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (and:DI (match_operand:DI 1 "se_register_operand" "d")
+ (match_operand:DI 2 "se_register_operand" "d")))]
+ "(TARGET_64BIT || !TARGET_DEBUG_G_MODE) && !TARGET_MIPS16"
+ "*
+{
+ if (TARGET_64BIT)
+ return \"and\\t%0,%1,%2\";
+ return \"and\\t%M0,%M1,%M2\;and\\t%L0,%L1,%L2\";
+}"
+ [(set_attr "type" "darith")
+ (set_attr "mode" "DI")
+ (set (attr "length")
+ (if_then_else (ne (symbol_ref "TARGET_64BIT") (const_int 0))
+ (const_int 1)
+ (const_int 2)))])
+
+(define_insn ""
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (and:DI (match_operand:DI 1 "se_register_operand" "0")
+ (match_operand:DI 2 "se_register_operand" "d")))]
+ "(TARGET_64BIT || !TARGET_DEBUG_G_MODE) && TARGET_MIPS16"
+ "*
+{
+ if (TARGET_64BIT)
+ return \"and\\t%0,%2\";
+ return \"and\\t%M0,%M2\;and\\t%L0,%L2\";
+}"
+ [(set_attr "type" "darith")
+ (set_attr "mode" "DI")
+ (set (attr "length")
+ (if_then_else (ge (symbol_ref "mips_isa") (const_int 3))
+ (const_int 1)
+ (const_int 2)))])
+
+(define_split
+ [(set (match_operand:DI 0 "register_operand" "")
+ (and:DI (match_operand:DI 1 "register_operand" "")
+ (match_operand:DI 2 "register_operand" "")))]
+ "reload_completed && !TARGET_64BIT && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE
+ && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0]))
+ && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1]))
+ && GET_CODE (operands[2]) == REG && GP_REG_P (REGNO (operands[2]))"
+
+ [(set (subreg:SI (match_dup 0) 0) (and:SI (subreg:SI (match_dup 1) 0) (subreg:SI (match_dup 2) 0)))
+ (set (subreg:SI (match_dup 0) 1) (and:SI (subreg:SI (match_dup 1) 1) (subreg:SI (match_dup 2) 1)))]
+ "")
+
+(define_insn "anddi3_internal1"
+ [(set (match_operand:DI 0 "register_operand" "=d,d")
+ (and:DI (match_operand:DI 1 "se_register_operand" "%d,d")
+ (match_operand:DI 2 "se_uns_arith_operand" "d,K")))]
+ "TARGET_64BIT && !TARGET_MIPS16"
+ "@
+ and\\t%0,%1,%2
+ andi\\t%0,%1,%x2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "DI")
+ (set_attr "length" "1")])
+
+(define_expand "iorsi3"
+ [(set (match_operand:SI 0 "register_operand" "=d,d")
+ (ior:SI (match_operand:SI 1 "uns_arith_operand" "%d,d")
+ (match_operand:SI 2 "uns_arith_operand" "d,K")))]
+ ""
+ "
+{
+ if (TARGET_MIPS16)
+ operands[2] = force_reg (SImode, operands[2]);
+}")
+
+(define_insn ""
+ [(set (match_operand:SI 0 "register_operand" "=d,d")
+ (ior:SI (match_operand:SI 1 "uns_arith_operand" "%d,d")
+ (match_operand:SI 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" "SI")
+ (set_attr "length" "1")])
+
+(define_insn ""
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (ior:SI (match_operand:SI 1 "register_operand" "%0")
+ (match_operand:SI 2 "register_operand" "d")))]
+ "TARGET_MIPS16"
+ "or\\t%0,%2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1")])
+
+;;; ??? There is no iordi3 pattern which accepts 'K' constants when
+;;; TARGET_64BIT
+
+(define_expand "iordi3"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (ior:DI (match_operand:DI 1 "se_register_operand" "d")
+ (match_operand:DI 2 "se_register_operand" "d")))]
+ "TARGET_64BIT || !TARGET_DEBUG_G_MODE"
+ "")
+
+(define_insn ""
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (ior:DI (match_operand:DI 1 "se_register_operand" "d")
+ (match_operand:DI 2 "se_register_operand" "d")))]
+ "(TARGET_64BIT || !TARGET_DEBUG_G_MODE) && !TARGET_MIPS16"
+ "*
+{
+ if (TARGET_64BIT)
+ return \"or\\t%0,%1,%2\";
+ return \"or\\t%M0,%M1,%M2\;or\\t%L0,%L1,%L2\";
+}"
+ [(set_attr "type" "darith")
+ (set_attr "mode" "DI")
+ (set (attr "length")
+ (if_then_else (ne (symbol_ref "TARGET_64BIT") (const_int 0))
+ (const_int 1)
+ (const_int 2)))])
+
+(define_insn ""
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (ior:DI (match_operand:DI 1 "se_register_operand" "0")
+ (match_operand:DI 2 "se_register_operand" "d")))]
+ "(TARGET_64BIT || !TARGET_DEBUG_G_MODE) && TARGET_MIPS16"
+ "*
+{
+ if (TARGET_64BIT)
+ return \"or\\t%0,%2\";
+ return \"or\\t%M0,%M2\;or\\t%L0,%L2\";
+}"
+ [(set_attr "type" "darith")
+ (set_attr "mode" "DI")
+ (set (attr "length")
+ (if_then_else (ge (symbol_ref "mips_isa") (const_int 3))
+ (const_int 1)
+ (const_int 2)))])
+
+(define_split
+ [(set (match_operand:DI 0 "register_operand" "")
+ (ior:DI (match_operand:DI 1 "register_operand" "")
+ (match_operand:DI 2 "register_operand" "")))]
+ "reload_completed && !TARGET_64BIT && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE
+ && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0]))
+ && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1]))
+ && GET_CODE (operands[2]) == REG && GP_REG_P (REGNO (operands[2]))"
+
+ [(set (subreg:SI (match_dup 0) 0) (ior:SI (subreg:SI (match_dup 1) 0) (subreg:SI (match_dup 2) 0)))
+ (set (subreg:SI (match_dup 0) 1) (ior:SI (subreg:SI (match_dup 1) 1) (subreg:SI (match_dup 2) 1)))]
+ "")
+
+(define_expand "xorsi3"
+ [(set (match_operand:SI 0 "register_operand" "=d,d")
+ (xor:SI (match_operand:SI 1 "uns_arith_operand" "%d,d")
+ (match_operand:SI 2 "uns_arith_operand" "d,K")))]
+ ""
+ "")
+
+(define_insn ""
+ [(set (match_operand:SI 0 "register_operand" "=d,d")
+ (xor:SI (match_operand:SI 1 "uns_arith_operand" "%d,d")
+ (match_operand:SI 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" "SI")
+ (set_attr "length" "1")])
+
+(define_insn ""
+ [(set (match_operand:SI 0 "register_operand" "=d,t,t")
+ (xor:SI (match_operand:SI 1 "uns_arith_operand" "%0,d,d")
+ (match_operand:SI 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" "SI")
+ (set_attr_alternative "length"
+ [(const_int 1)
+ (if_then_else (match_operand:VOID 2 "m16_uimm8_1" "")
+ (const_int 1)
+ (const_int 2))
+ (const_int 1)])])
+
+;; ??? If delete the 32-bit long long patterns, then could merge this with
+;; the following xordi3_internal pattern.
+(define_expand "xordi3"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (xor:DI (match_operand:DI 1 "se_register_operand" "d")
+ (match_operand:DI 2 "se_register_operand" "d")))]
+ "TARGET_64BIT || !TARGET_DEBUG_G_MODE"
+ "")
+
+(define_insn ""
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (xor:DI (match_operand:DI 1 "se_register_operand" "d")
+ (match_operand:DI 2 "se_register_operand" "d")))]
+ "(TARGET_64BIT || !TARGET_DEBUG_G_MODE) && !TARGET_MIPS16"
+ "*
+{
+ if (TARGET_64BIT)
+ return \"xor\\t%0,%1,%2\";
+ return \"xor\\t%M0,%M1,%M2\;xor\\t%L0,%L1,%L2\";
+}"
+ [(set_attr "type" "darith")
+ (set_attr "mode" "DI")
+ (set (attr "length")
+ (if_then_else (ne (symbol_ref "TARGET_64BIT") (const_int 0))
+ (const_int 1)
+ (const_int 2)))])
+
+(define_insn ""
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (xor:DI (match_operand:DI 1 "se_register_operand" "0")
+ (match_operand:DI 2 "se_register_operand" "d")))]
+ "!TARGET_64BIT && TARGET_MIPS16"
+ "xor\\t%M0,%M2\;xor\\t%L0,%L2"
+ [(set_attr "type" "darith")
+ (set_attr "mode" "DI")
+ (set_attr "length" "2")])
+
+(define_insn ""
+ [(set (match_operand:DI 0 "register_operand" "=d,t,t")
+ (xor:DI (match_operand:DI 1 "se_register_operand" "%0,d,d")
+ (match_operand:DI 2 "se_uns_arith_operand" "d,K,d")))]
+ "TARGET_64BIT && TARGET_MIPS16"
+ "@
+ xor\\t%0,%2
+ cmpi\\t%1,%2
+ cmp\\t%1,%2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "DI")
+ (set_attr_alternative "length"
+ [(const_int 1)
+ (if_then_else (match_operand:VOID 2 "m16_uimm8_1" "")
+ (const_int 1)
+ (const_int 2))
+ (const_int 1)])])
+
+(define_split
+ [(set (match_operand:DI 0 "register_operand" "")
+ (xor:DI (match_operand:DI 1 "register_operand" "")
+ (match_operand:DI 2 "register_operand" "")))]
+ "reload_completed && !TARGET_64BIT && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE
+ && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0]))
+ && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1]))
+ && GET_CODE (operands[2]) == REG && GP_REG_P (REGNO (operands[2]))"
+
+ [(set (subreg:SI (match_dup 0) 0) (xor:SI (subreg:SI (match_dup 1) 0) (subreg:SI (match_dup 2) 0)))
+ (set (subreg:SI (match_dup 0) 1) (xor:SI (subreg:SI (match_dup 1) 1) (subreg:SI (match_dup 2) 1)))]
+ "")
+
+(define_insn "xordi3_immed"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (xor:DI (match_operand:DI 1 "se_register_operand" "d")
+ (match_operand:DI 2 "se_uns_arith_operand" "K")))]
+ "TARGET_64BIT && !TARGET_MIPS16"
+ "xori\\t%0,%1,%x2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "DI")
+ (set_attr "length" "1")])
+
+(define_insn "*norsi3"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (and:SI (not:SI (match_operand:SI 1 "register_operand" "d"))
+ (not:SI (match_operand:SI 2 "register_operand" "d"))))]
+ "!TARGET_MIPS16"
+ "nor\\t%0,%z1,%z2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1")])
+
+(define_insn "*nordi3"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (and:DI (not:DI (match_operand:DI 1 "se_register_operand" "d"))
+ (not:DI (match_operand:DI 2 "se_register_operand" "d"))))]
+ "!TARGET_MIPS16"
+ "*
+{
+ if (TARGET_64BIT)
+ return \"nor\\t%0,%z1,%z2\";
+ return \"nor\\t%M0,%M1,%M2\;nor\\t%L0,%L1,%L2\";
+}"
+ [(set_attr "type" "darith")
+ (set_attr "mode" "DI")
+ (set (attr "length")
+ (if_then_else (ne (symbol_ref "TARGET_64BIT") (const_int 0))
+ (const_int 1)
+ (const_int 2)))])
+
+(define_split
+ [(set (match_operand:DI 0 "register_operand" "")
+ (and:DI (not:DI (match_operand:DI 1 "register_operand" ""))
+ (not:DI (match_operand:DI 2 "register_operand" ""))))]
+ "reload_completed && !TARGET_MIPS16 && !TARGET_64BIT && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE
+ && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0]))
+ && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1]))
+ && GET_CODE (operands[2]) == REG && GP_REG_P (REGNO (operands[2]))"
+
+ [(set (subreg:SI (match_dup 0) 0) (and:SI (not:SI (subreg:SI (match_dup 1) 0)) (not:SI (subreg:SI (match_dup 2) 0))))
+ (set (subreg:SI (match_dup 0) 1) (and:SI (not:SI (subreg:SI (match_dup 1) 1)) (not:SI (subreg:SI (match_dup 2) 1))))]
+ "")
+
+;; ....................
+;; 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 "mode" "SF")
+ (set_attr "length" "1")])
+
+(define_insn "truncdisi2"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (truncate:SI (match_operand:DI 1 "se_register_operand" "d")))]
+ "TARGET_64BIT"
+ "*
+{
+ if (TARGET_MIPS16)
+ return \"dsll\\t%0,%1,32\;dsra\\t%0,32\";
+ return \"dsll\\t%0,%1,32\;dsra\\t%0,%0,32\";
+}"
+ [(set_attr "type" "darith")
+ (set_attr "mode" "SI")
+ (set (attr "length") (if_then_else (eq (symbol_ref "mips16") (const_int 0))
+ (const_int 2)
+ (const_int 4)))])
+
+(define_insn "truncdihi2"
+ [(set (match_operand:HI 0 "register_operand" "=d")
+ (truncate:HI (match_operand:DI 1 "se_register_operand" "d")))]
+ "TARGET_64BIT"
+ "*
+{
+ if (TARGET_MIPS16)
+ return \"dsll\\t%0,%1,48\;dsra\\t%0,48\";
+ return \"andi\\t%0,%1,0xffff\";
+}"
+ [(set_attr "type" "darith")
+ (set_attr "mode" "HI")
+ (set (attr "length") (if_then_else (eq (symbol_ref "mips16") (const_int 0))
+ (const_int 1)
+ (const_int 4)))])
+(define_insn "truncdiqi2"
+ [(set (match_operand:QI 0 "register_operand" "=d")
+ (truncate:QI (match_operand:DI 1 "se_register_operand" "d")))]
+ "TARGET_64BIT"
+ "*
+{
+ if (TARGET_MIPS16)
+ return \"dsll\\t%0,%1,56\;dsra\\t%0,56\";
+ return \"andi\\t%0,%1,0x00ff\";
+}"
+ [(set_attr "type" "darith")
+ (set_attr "mode" "QI")
+ (set (attr "length") (if_then_else (eq (symbol_ref "mips16") (const_int 0))
+ (const_int 1)
+ (const_int 4)))])
+
+;; 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 "se_register_operand" "d")
+ (match_operand:DI 2 "small_int" "I"))))]
+ "TARGET_64BIT && !TARGET_MIPS16"
+ "*
+{
+ int shift_amt = INTVAL (operands[2]) & 0x3f;
+
+ if (shift_amt < 32)
+ {
+ operands[2] = GEN_INT (32 - shift_amt);
+ return \"dsll\\t%0,%1,%2\;dsra\\t%0,%0,32\";
+ }
+ else
+ {
+ operands[2] = GEN_INT (shift_amt);
+ return \"dsra\\t%0,%1,%2\";
+ }
+}"
+ [(set_attr "type" "darith")
+ (set_attr "mode" "SI")
+ (set_attr "length" "2")])
+
+(define_insn ""
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (truncate:SI (lshiftrt:DI (match_operand:DI 1 "se_register_operand" "d")
+ (match_operand:DI 2 "small_int" "I"))))]
+ "TARGET_64BIT && !TARGET_MIPS16"
+ "*
+{
+ int shift_amt = INTVAL (operands[2]) & 0x3f;
+
+ if (shift_amt < 32)
+ {
+ operands[2] = GEN_INT (32 - shift_amt);
+ return \"dsll\\t%0,%1,%2\;dsra\\t%0,%0,32\";
+ }
+ else if (shift_amt == 32)
+ return \"dsra\\t%0,%1,32\";
+ else
+ {
+ operands[2] = GEN_INT (shift_amt);
+ return \"dsrl\\t%0,%1,%2\";
+ }
+}"
+ [(set_attr "type" "darith")
+ (set_attr "mode" "SI")
+ (set_attr "length" "2")])
+
+(define_insn ""
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (truncate:SI (ashift:DI (match_operand:DI 1 "se_register_operand" "d")
+ (match_operand:DI 2 "small_int" "I"))))]
+ "TARGET_64BIT"
+ "*
+{
+ int shift_amt = INTVAL (operands[2]) & 0x3f;
+
+ if (shift_amt < 32)
+ {
+ operands[2] = GEN_INT (32 + shift_amt);
+ if (TARGET_MIPS16)
+ return \"dsll\\t%0,%1,%2\;dsra\\t%0,32\";
+ return \"dsll\\t%0,%1,%2\;dsra\\t%0,%0,32\";
+ }
+ else
+ return \"move\\t%0,%.\";
+}"
+ [(set_attr "type" "darith")
+ (set_attr "mode" "SI")
+ (set_attr "length" "2")])
+
+;; 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 "se_register_operand" "d"))))]
+ "TARGET_64BIT && !TARGET_MIPS16"
+ "andi\\t%0,%1,0xffff"
+ [(set_attr "type" "darith")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1")])
+
+(define_insn ""
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (zero_extend:SI (truncate:QI
+ (match_operand:DI 1 "se_register_operand" "d"))))]
+ "TARGET_64BIT && !TARGET_MIPS16"
+ "andi\\t%0,%1,0xff"
+ [(set_attr "type" "darith")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1")])
+
+(define_insn ""
+ [(set (match_operand:HI 0 "register_operand" "=d")
+ (zero_extend:HI (truncate:QI
+ (match_operand:DI 1 "se_register_operand" "d"))))]
+ "TARGET_64BIT && !TARGET_MIPS16"
+ "andi\\t%0,%1,0xff"
+ [(set_attr "type" "darith")
+ (set_attr "mode" "HI")
+ (set_attr "length" "1")])
+
+;; ....................
+;; ZERO EXTENSION
+;; ....................
+
+;; Extension insns.
+;; Those for integer source operand are ordered widest source type first.
+
+(define_expand "zero_extendsidi2"
+ [(set (match_operand:DI 0 "register_operand" "")
+ (zero_extend:DI (match_operand:SI 1 "nonimmediate_operand" "")))]
+ "TARGET_64BIT"
+ "
+{
+ if (optimize && GET_CODE (operands[1]) == MEM)
+ operands[1] = force_not_mem (operands[1]);
+
+ if (GET_CODE (operands[1]) != MEM)
+ {
+ rtx op1 = gen_lowpart (DImode, operands[1]);
+ rtx temp = gen_reg_rtx (DImode);
+ rtx shift = GEN_INT (32);
+
+ emit_insn (gen_ashldi3 (temp, op1, shift));
+ emit_insn (gen_lshrdi3 (operands[0], temp, shift));
+ DONE;
+ }
+}")
+
+(define_insn "zero_extendsidi2_internal"
+ [(set (match_operand:DI 0 "register_operand" "=d,d")
+ (zero_extend:DI (match_operand:SI 1 "memory_operand" "R,m")))]
+ "TARGET_64BIT"
+ "* return mips_move_1word (operands, insn, TRUE);"
+ [(set_attr "type" "load")
+ (set_attr "mode" "DI")
+ (set_attr "length" "1,2")])
+
+(define_expand "zero_extendhisi2"
+ [(set (match_operand:SI 0 "register_operand" "")
+ (zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "")))]
+ ""
+ "
+{
+ if (TARGET_MIPS16 && GET_CODE (operands[1]) != MEM)
+ {
+ rtx op = gen_lowpart (SImode, operands[1]);
+ rtx temp = force_reg (SImode, GEN_INT (0xffff));
+
+ emit_insn (gen_andsi3 (operands[0], op, temp));
+ DONE;
+ }
+}")
+
+(define_insn ""
+ [(set (match_operand:SI 0 "register_operand" "=d,d,d")
+ (zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "d,R,m")))]
+ "!TARGET_MIPS16"
+ "*
+{
+ if (which_alternative == 0)
+ return \"andi\\t%0,%1,0xffff\";
+ else
+ return mips_move_1word (operands, insn, TRUE);
+}"
+ [(set_attr "type" "arith,load,load")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1,1,2")])
+
+(define_insn ""
+ [(set (match_operand:SI 0 "register_operand" "=d,d")
+ (zero_extend:SI (match_operand:HI 1 "memory_operand" "R,m")))]
+ "TARGET_MIPS16"
+ "* return mips_move_1word (operands, insn, TRUE);"
+ [(set_attr "type" "load,load")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1,2")])
+
+(define_expand "zero_extendhidi2"
+ [(set (match_operand:DI 0 "register_operand" "")
+ (zero_extend:DI (match_operand:HI 1 "nonimmediate_operand" "")))]
+ "TARGET_64BIT"
+ "
+{
+ if (TARGET_MIPS16 && GET_CODE (operands[1]) != MEM)
+ {
+ rtx op = gen_lowpart (DImode, operands[1]);
+ rtx temp = force_reg (DImode, GEN_INT (0xffff));
+
+ emit_insn (gen_anddi3 (operands[0], op, temp));
+ DONE;
+ }
+}")
+
+(define_insn ""
+ [(set (match_operand:DI 0 "register_operand" "=d,d,d")
+ (zero_extend:DI (match_operand:HI 1 "nonimmediate_operand" "d,R,m")))]
+ "TARGET_64BIT && !TARGET_MIPS16"
+ "*
+{
+ if (which_alternative == 0)
+ return \"andi\\t%0,%1,0xffff\";
+ else
+ return mips_move_1word (operands, insn, TRUE);
+}"
+ [(set_attr "type" "arith,load,load")
+ (set_attr "mode" "DI")
+ (set_attr "length" "1,1,2")])
+
+(define_insn ""
+ [(set (match_operand:DI 0 "register_operand" "=d,d")
+ (zero_extend:DI (match_operand:HI 1 "memory_operand" "R,m")))]
+ "TARGET_64BIT && TARGET_MIPS16"
+ "* return mips_move_1word (operands, insn, TRUE);"
+ [(set_attr "type" "load,load")
+ (set_attr "mode" "DI")
+ (set_attr "length" "1,2")])
+
+(define_expand "zero_extendqihi2"
+ [(set (match_operand:HI 0 "register_operand" "")
+ (zero_extend:HI (match_operand:QI 1 "nonimmediate_operand" "")))]
+ ""
+ "
+{
+ if (TARGET_MIPS16 && GET_CODE (operands[1]) != MEM)
+ {
+ rtx op0 = gen_lowpart (SImode, operands[0]);
+ rtx op1 = gen_lowpart (SImode, operands[1]);
+ rtx temp = force_reg (SImode, GEN_INT (0xff));
+
+ emit_insn (gen_andsi3 (op0, op1, temp));
+ DONE;
+ }
+}")
+
+(define_insn ""
+ [(set (match_operand:HI 0 "register_operand" "=d,d,d")
+ (zero_extend:HI (match_operand:QI 1 "nonimmediate_operand" "d,R,m")))]
+ "!TARGET_MIPS16"
+ "*
+{
+ if (which_alternative == 0)
+ return \"andi\\t%0,%1,0x00ff\";
+ else
+ return mips_move_1word (operands, insn, TRUE);
+}"
+ [(set_attr "type" "arith,load,load")
+ (set_attr "mode" "HI")
+ (set_attr "length" "1,1,2")])
+
+(define_insn ""
+ [(set (match_operand:HI 0 "register_operand" "=d,d")
+ (zero_extend:HI (match_operand:QI 1 "memory_operand" "R,m")))]
+ "TARGET_MIPS16"
+ "* return mips_move_1word (operands, insn, TRUE);"
+ [(set_attr "type" "load,load")
+ (set_attr "mode" "HI")
+ (set_attr "length" "1,2")])
+
+(define_expand "zero_extendqisi2"
+ [(set (match_operand:SI 0 "register_operand" "")
+ (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "")))]
+ ""
+ "
+{
+ if (TARGET_MIPS16 && GET_CODE (operands[1]) != MEM)
+ {
+ rtx op = gen_lowpart (SImode, operands[1]);
+ rtx temp = force_reg (SImode, GEN_INT (0xff));
+
+ emit_insn (gen_andsi3 (operands[0], op, temp));
+ DONE;
+ }
+}")
+
+(define_insn ""
+ [(set (match_operand:SI 0 "register_operand" "=d,d,d")
+ (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "d,R,m")))]
+ "!TARGET_MIPS16"
+ "*
+{
+ if (which_alternative == 0)
+ return \"andi\\t%0,%1,0x00ff\";
+ else
+ return mips_move_1word (operands, insn, TRUE);
+}"
+ [(set_attr "type" "arith,load,load")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1,1,2")])
+
+(define_insn ""
+ [(set (match_operand:SI 0 "register_operand" "=d,d")
+ (zero_extend:SI (match_operand:QI 1 "memory_operand" "R,m")))]
+ "TARGET_MIPS16"
+ "* return mips_move_1word (operands, insn, TRUE);"
+ [(set_attr "type" "load,load")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1,2")])
+
+(define_expand "zero_extendqidi2"
+ [(set (match_operand:DI 0 "register_operand" "")
+ (zero_extend:DI (match_operand:QI 1 "nonimmediate_operand" "")))]
+ "TARGET_64BIT"
+ "
+{
+ if (TARGET_MIPS16 && GET_CODE (operands[1]) != MEM)
+ {
+ rtx op = gen_lowpart (DImode, operands[1]);
+ rtx temp = force_reg (DImode, GEN_INT (0xff));
+
+ emit_insn (gen_anddi3 (operands[0], op, temp));
+ DONE;
+ }
+}")
+
+(define_insn ""
+ [(set (match_operand:DI 0 "register_operand" "=d,d,d")
+ (zero_extend:DI (match_operand:QI 1 "nonimmediate_operand" "d,R,m")))]
+ "TARGET_64BIT && !TARGET_MIPS16"
+ "*
+{
+ if (which_alternative == 0)
+ return \"andi\\t%0,%1,0x00ff\";
+ else
+ return mips_move_1word (operands, insn, TRUE);
+}"
+ [(set_attr "type" "arith,load,load")
+ (set_attr "mode" "DI")
+ (set_attr "length" "1,1,2")])
+
+;; These can be created when a paradoxical subreg operand with an implicit
+;; sign_extend operator is reloaded. Because of the subreg, this is really
+;; a zero extend.
+;; ??? It might be possible to eliminate the need for these patterns by adding
+;; more support to reload for implicit sign_extend operators.
+(define_insn "*paradoxical_extendhidi2"
+ [(set (match_operand:DI 0 "register_operand" "=d,d")
+ (sign_extend:DI
+ (subreg:SI (match_operand:HI 1 "memory_operand" "R,m") 0)))]
+ "TARGET_64BIT"
+ "*
+{
+ return mips_move_1word (operands, insn, TRUE);
+}"
+ [(set_attr "type" "load,load")
+ (set_attr "mode" "DI")
+ (set_attr "length" "1,2")])
+
+(define_insn ""
+ [(set (match_operand:DI 0 "register_operand" "=d,d")
+ (zero_extend:DI (match_operand:QI 1 "memory_operand" "R,m")))]
+ "TARGET_64BIT && TARGET_MIPS16"
+ "* return mips_move_1word (operands, insn, TRUE);"
+ [(set_attr "type" "load,load")
+ (set_attr "mode" "DI")
+ (set_attr "length" "1,2")])
+
+;; ....................
+;; SIGN EXTENSION
+;; ....................
+
+;; Extension insns.
+;; Those for integer source operand are ordered widest source type first.
+
+;; In 64 bit mode, 32 bit values in general registers are always
+;; correctly sign extended. That means that if the target is a
+;; general register, we can sign extend from SImode to DImode just by
+;; doing a move.
+
+;; CYGNUS LOCAL law
+(define_insn "extendsidi2"
+ [(set (match_operand:DI 0 "register_operand" "=d,y,d,*d,d,d")
+ (sign_extend:DI (match_operand:SI 1 "nonimmediate_operand" "d,d,y,*x*w,R,m")))]
+ "TARGET_64BIT"
+ "* return mips_move_1word (operands, insn, FALSE);"
+ [(set_attr "type" "move,move,move,hilo,load,load")
+ (set_attr "mode" "DI")
+ (set_attr "length" "1,1,1,1,1,2")])
+
+;; These patterns originally accepted general_operands, however, slightly
+;; better code is generated by only accepting register_operands, and then
+;; letting combine generate the lh and lb insns.
+
+(define_expand "extendhidi2"
+ [(set (match_operand:DI 0 "register_operand" "")
+ (sign_extend:DI (match_operand:HI 1 "nonimmediate_operand" "")))]
+ "TARGET_64BIT"
+ "
+{
+ if (optimize && GET_CODE (operands[1]) == MEM)
+ operands[1] = force_not_mem (operands[1]);
+
+ if (GET_CODE (operands[1]) != MEM)
+ {
+ rtx op1 = gen_lowpart (DImode, operands[1]);
+ rtx temp = gen_reg_rtx (DImode);
+ rtx shift = GEN_INT (48);
+
+ emit_insn (gen_ashldi3 (temp, op1, shift));
+ emit_insn (gen_ashrdi3 (operands[0], temp, shift));
+ DONE;
+ }
+}")
+
+(define_insn "extendhidi2_internal"
+ [(set (match_operand:DI 0 "register_operand" "=d,d")
+ (sign_extend:DI (match_operand:HI 1 "memory_operand" "R,m")))]
+ "TARGET_64BIT"
+ "* return mips_move_1word (operands, insn, FALSE);"
+ [(set_attr "type" "load")
+ (set_attr "mode" "DI")
+ (set_attr "length" "1,2")])
+
+(define_expand "extendhisi2"
+ [(set (match_operand:SI 0 "register_operand" "")
+ (sign_extend:SI (match_operand:HI 1 "nonimmediate_operand" "")))]
+ ""
+ "
+{
+ if (optimize && GET_CODE (operands[1]) == MEM)
+ operands[1] = force_not_mem (operands[1]);
+
+ if (GET_CODE (operands[1]) != MEM)
+ {
+ rtx op1 = gen_lowpart (SImode, operands[1]);
+ rtx temp = gen_reg_rtx (SImode);
+ rtx shift = GEN_INT (16);
+
+ emit_insn (gen_ashlsi3 (temp, op1, shift));
+ emit_insn (gen_ashrsi3 (operands[0], temp, shift));
+ DONE;
+ }
+}")
+
+(define_insn "extendhisi2_internal"
+ [(set (match_operand:SI 0 "register_operand" "=d,d")
+ (sign_extend:SI (match_operand:HI 1 "memory_operand" "R,m")))]
+ ""
+ "* return mips_move_1word (operands, insn, FALSE);"
+ [(set_attr "type" "load")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1,2")])
+
+(define_expand "extendqihi2"
+ [(set (match_operand:HI 0 "register_operand" "")
+ (sign_extend:HI (match_operand:QI 1 "nonimmediate_operand" "")))]
+ ""
+ "
+{
+ if (optimize && GET_CODE (operands[1]) == MEM)
+ operands[1] = force_not_mem (operands[1]);
+
+ if (GET_CODE (operands[1]) != MEM)
+ {
+ rtx op0 = gen_lowpart (SImode, operands[0]);
+ rtx op1 = gen_lowpart (SImode, operands[1]);
+ rtx temp = gen_reg_rtx (SImode);
+ rtx shift = GEN_INT (24);
+
+ emit_insn (gen_ashlsi3 (temp, op1, shift));
+ emit_insn (gen_ashrsi3 (op0, temp, shift));
+ DONE;
+ }
+}")
+
+(define_insn "extendqihi2_internal"
+ [(set (match_operand:HI 0 "register_operand" "=d,d")
+ (sign_extend:HI (match_operand:QI 1 "memory_operand" "R,m")))]
+ ""
+ "* return mips_move_1word (operands, insn, FALSE);"
+ [(set_attr "type" "load")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1,2")])
+
+
+(define_expand "extendqisi2"
+ [(set (match_operand:SI 0 "register_operand" "")
+ (sign_extend:SI (match_operand:QI 1 "nonimmediate_operand" "")))]
+ ""
+ "
+{
+ if (optimize && GET_CODE (operands[1]) == MEM)
+ operands[1] = force_not_mem (operands[1]);
+
+ if (GET_CODE (operands[1]) != MEM)
+ {
+ rtx op1 = gen_lowpart (SImode, operands[1]);
+ rtx temp = gen_reg_rtx (SImode);
+ rtx shift = GEN_INT (24);
+
+ emit_insn (gen_ashlsi3 (temp, op1, shift));
+ emit_insn (gen_ashrsi3 (operands[0], temp, shift));
+ DONE;
+ }
+}")
+
+(define_insn "extendqisi2_insn"
+ [(set (match_operand:SI 0 "register_operand" "=d,d")
+ (sign_extend:SI (match_operand:QI 1 "memory_operand" "R,m")))]
+ ""
+ "* return mips_move_1word (operands, insn, FALSE);"
+ [(set_attr "type" "load")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1,2")])
+
+(define_expand "extendqidi2"
+ [(set (match_operand:DI 0 "register_operand" "")
+ (sign_extend:DI (match_operand:QI 1 "nonimmediate_operand" "")))]
+ "TARGET_64BIT"
+ "
+{
+ if (optimize && GET_CODE (operands[1]) == MEM)
+ operands[1] = force_not_mem (operands[1]);
+
+ if (GET_CODE (operands[1]) != MEM)
+ {
+ rtx op1 = gen_lowpart (DImode, operands[1]);
+ rtx temp = gen_reg_rtx (DImode);
+ rtx shift = GEN_INT (56);
+
+ emit_insn (gen_ashldi3 (temp, op1, shift));
+ emit_insn (gen_ashrdi3 (operands[0], temp, shift));
+ DONE;
+ }
+}")
+
+(define_insn "extendqidi2_insn"
+ [(set (match_operand:DI 0 "register_operand" "=d,d")
+ (sign_extend:DI (match_operand:QI 1 "memory_operand" "R,m")))]
+ "TARGET_64BIT"
+ "* return mips_move_1word (operands, insn, FALSE);"
+ [(set_attr "type" "load")
+ (set_attr "mode" "DI")
+ (set_attr "length" "1,2")])
+
+
+(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 "mode" "DF")
+ (set_attr "length" "1")])
+
+
+
+;; ....................
+;; CONVERSIONS
+;; ....................
+
+;; The SImode scratch register can not be shared with address regs used for
+;; operand zero, because then the address in the move instruction will be
+;; clobbered. We mark the scratch register as early clobbered to prevent this.
+
+;; We need the ?X in alternative 1 so that it will be choosen only if the
+;; destination is a floating point register. Otherwise, alternative 1 can
+;; have lower cost than alternative 0 (because there is one less loser), and
+;; can be choosen when it won't work (because integral reloads into FP
+;; registers are not supported).
+
+(define_insn "fix_truncdfsi2"
+ [(set (match_operand:SI 0 "general_operand" "=d,*f,R,To")
+ (fix:SI (match_operand:DF 1 "register_operand" "f,*f,f,f")))
+ (clobber (match_scratch:SI 2 "=d,*d,&d,&d"))
+ (clobber (match_scratch:DF 3 "=f,?*X,f,f"))]
+ "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
+ "*
+{
+ rtx xoperands[10];
+
+ if (which_alternative == 1)
+ return \"trunc.w.d %0,%1,%2\";
+
+ output_asm_insn (\"trunc.w.d %3,%1,%2\", operands);
+
+ xoperands[0] = operands[0];
+ xoperands[1] = operands[3];
+ output_asm_insn (mips_move_1word (xoperands, insn, FALSE), xoperands);
+ return \"\";
+}"
+ [(set_attr "type" "fcvt")
+ (set_attr "mode" "DF")
+ (set_attr "length" "11,9,10,11")])
+
+
+(define_insn "fix_truncsfsi2"
+ [(set (match_operand:SI 0 "general_operand" "=d,*f,R,To")
+ (fix:SI (match_operand:SF 1 "register_operand" "f,*f,f,f")))
+ (clobber (match_scratch:SI 2 "=d,*d,&d,&d"))
+ (clobber (match_scratch:SF 3 "=f,?*X,f,f"))]
+ "TARGET_HARD_FLOAT && !0" ;; CYGNUS LOCAL law
+ "*
+{
+ rtx xoperands[10];
+
+ if (which_alternative == 1)
+ return \"trunc.w.s %0,%1,%2\";
+
+ output_asm_insn (\"trunc.w.s %3,%1,%2\", operands);
+
+ xoperands[0] = operands[0];
+ xoperands[1] = operands[3];
+ output_asm_insn (mips_move_1word (xoperands, insn, FALSE), xoperands);
+ return \"\";
+}"
+ [(set_attr "type" "fcvt")
+ (set_attr "mode" "SF")
+ (set_attr "length" "11,9,10,11")])
+
+
+;;; ??? trunc.l.d is mentioned in the appendix of the 1993 r4000/r4600 manuals
+;;; but not in the chapter that describes the FPU. It is not mentioned at all
+;;; in the 1991 manuals. The r4000 at Cygnus does not have this instruction.
+
+;;; Deleting this means that we now need two libgcc2.a libraries. One for
+;;; the 32 bit calling convention and one for the 64 bit calling convention.
+
+;;; If this is disabled, then fixuns_truncdfdi2 must be disabled also.
+
+(define_insn "fix_truncdfdi2"
+ [(set (match_operand:DI 0 "general_operand" "=d,*f,R,To")
+ (fix:DI (match_operand:DF 1 "register_operand" "f,*f,f,f")))
+ (clobber (match_scratch:DF 2 "=f,?*X,f,f"))]
+ "TARGET_HARD_FLOAT && TARGET_64BIT && TARGET_DOUBLE_FLOAT"
+ "*
+{
+ rtx xoperands[10];
+
+ if (which_alternative == 1)
+ return \"trunc.l.d %0,%1\";
+
+ output_asm_insn (\"trunc.l.d %2,%1\", operands);
+
+ xoperands[0] = operands[0];
+ xoperands[1] = operands[2];
+ output_asm_insn (mips_move_2words (xoperands, insn, FALSE), xoperands);
+ return \"\";
+}"
+ [(set_attr "type" "fcvt")
+ (set_attr "mode" "DF")
+ (set_attr "length" "2,1,2,3")])
+
+
+;;; ??? trunc.l.s is mentioned in the appendix of the 1993 r4000/r4600 manuals
+;;; but not in the chapter that describes the FPU. It is not mentioned at all
+;;; in the 1991 manuals. The r4000 at Cygnus does not have this instruction.
+(define_insn "fix_truncsfdi2"
+ [(set (match_operand:DI 0 "general_operand" "=d,*f,R,To")
+ (fix:DI (match_operand:SF 1 "register_operand" "f,*f,f,f")))
+ (clobber (match_scratch:DF 2 "=f,?*X,f,f"))]
+ "TARGET_HARD_FLOAT && TARGET_64BIT && TARGET_DOUBLE_FLOAT"
+ "*
+{
+ rtx xoperands[10];
+
+ if (which_alternative == 1)
+ return \"trunc.l.s %0,%1\";
+
+ output_asm_insn (\"trunc.l.s %2,%1\", operands);
+
+ xoperands[0] = operands[0];
+ xoperands[1] = operands[2];
+ output_asm_insn (mips_move_2words (xoperands, insn, FALSE), xoperands);
+ return \"\";
+}"
+ [(set_attr "type" "fcvt")
+ (set_attr "mode" "SF")
+ (set_attr "length" "2,1,2,3")])
+
+
+(define_insn "floatsidf2"
+ [(set (match_operand:DF 0 "register_operand" "=f,f,f")
+ (float:DF (match_operand:SI 1 "nonimmediate_operand" "d,R,m")))]
+ "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
+ "*
+{
+ dslots_load_total++;
+ if (GET_CODE (operands[1]) == MEM)
+ return \"l.s\\t%0,%1%#\;cvt.d.w\\t%0,%0\";
+
+ return \"mtc1\\t%1,%0%#\;cvt.d.w\\t%0,%0\";
+}"
+ [(set_attr "type" "fcvt")
+ (set_attr "mode" "DF")
+ (set_attr "length" "3,4,3")])
+
+
+(define_insn "floatdidf2"
+ [(set (match_operand:DF 0 "register_operand" "=f,f,f")
+ (float:DF (match_operand:DI 1 "se_nonimmediate_operand" "d,R,m")))]
+ "TARGET_HARD_FLOAT && TARGET_64BIT && TARGET_DOUBLE_FLOAT"
+ "*
+{
+ dslots_load_total++;
+ if (GET_CODE (operands[1]) == MEM)
+ return \"l.d\\t%0,%1%#\;cvt.d.l\\t%0,%0\";
+
+ return \"dmtc1\\t%1,%0%#\;cvt.d.l\\t%0,%0\";
+}"
+ [(set_attr "type" "fcvt")
+ (set_attr "mode" "DF")
+ (set_attr "length" "3,4,3")])
+
+
+(define_insn "floatsisf2"
+ [(set (match_operand:SF 0 "register_operand" "=f,f,f")
+ (float:SF (match_operand:SI 1 "nonimmediate_operand" "d,R,m")))]
+ "TARGET_HARD_FLOAT"
+ "*
+{
+ dslots_load_total++;
+ if (GET_CODE (operands[1]) == MEM)
+ return \"l.s\\t%0,%1%#\;cvt.s.w\\t%0,%0\";
+
+ return \"mtc1\\t%1,%0%#\;cvt.s.w\\t%0,%0\";
+}"
+ [(set_attr "type" "fcvt")
+ (set_attr "mode" "SF")
+ (set_attr "length" "3,4,3")])
+
+
+(define_insn "floatdisf2"
+ [(set (match_operand:SF 0 "register_operand" "=f,f,f")
+ (float:SF (match_operand:DI 1 "se_nonimmediate_operand" "d,R,m")))]
+ "TARGET_HARD_FLOAT && TARGET_64BIT && TARGET_DOUBLE_FLOAT"
+ "*
+{
+ dslots_load_total++;
+ if (GET_CODE (operands[1]) == MEM)
+ return \"l.d\\t%0,%1%#\;cvt.s.l\\t%0,%0\";
+
+ return \"dmtc1\\t%1,%0%#\;cvt.s.l\\t%0,%0\";
+}"
+ [(set_attr "type" "fcvt")
+ (set_attr "mode" "SF")
+ (set_attr "length" "3,4,3")])
+
+
+(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_VALUE_LDEXP (1.0, 31);
+
+ if (reg1) /* turn off complaints about unreached code */
+ {
+ emit_move_insn (reg1, immed_real_const_1 (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 (0x80000000));
+
+ 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_VALUE_LDEXP (1.0, 63);
+
+ if (reg1) /* turn off complaints about unreached code */
+ {
+ emit_move_insn (reg1, immed_real_const_1 (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 (0x80000000));
+ 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 && !0" ;; CYGNUS LOCAL law
+ "
+{
+ 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_VALUE_LDEXP (1.0, 31);
+
+ if (reg1) /* turn off complaints about unreached code */
+ {
+ emit_move_insn (reg1, immed_real_const_1 (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 (0x80000000));
+
+ 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_VALUE_LDEXP (1.0, 63);
+
+ if (reg1) /* turn off complaints about unreached code */
+ {
+ emit_move_insn (reg1, immed_real_const_1 (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 (0x80000000));
+ 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.
+
+;; ??? There could be HImode variants for the ulh/ulhu/ush macros.
+;; It isn't clear whether this will give better code.
+
+;; Only specify the mode operand 1, the rest are assumed to be word_mode.
+(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 the field does not start on a byte boundary, then fail. */
+ if (INTVAL (operands[3]) % 8 != 0)
+ FAIL;
+
+ /* MIPS I and MIPS II can only handle a 32bit field. */
+ if (!TARGET_64BIT && INTVAL (operands[2]) != 32)
+ FAIL;
+
+ /* MIPS III and MIPS IV can handle both 32bit and 64bit fields. */
+ if (TARGET_64BIT
+ && INTVAL (operands[2]) != 64
+ && INTVAL (operands[2]) != 32)
+ FAIL;
+
+ /* This can happen for a 64 bit target, when extracting a value from
+ a 64 bit union member. extract_bit_field doesn't verify that our
+ source matches the predicate, so we force it to be a MEM here. */
+ if (GET_CODE (operands[1]) != MEM)
+ FAIL;
+
+ /* Change the mode to BLKmode for aliasing purposes. */
+ operands[1] = change_address (operands[1], BLKmode, XEXP (operands[1], 0));
+
+ /* Otherwise, emit a l[wd]l/l[wd]r pair to load the value. */
+ if (INTVAL (operands[2]) == 64)
+ emit_insn (gen_movdi_uld (operands[0], operands[1]));
+ else
+ {
+ if (TARGET_64BIT)
+ {
+ operands[0] = gen_lowpart (SImode, operands[0]);
+ if (operands[0] == NULL_RTX)
+ FAIL;
+ }
+ emit_insn (gen_movsi_ulw (operands[0], operands[1]));
+ }
+ DONE;
+}")
+
+;; Only specify the mode operand 1, the rest are assumed to be word_mode.
+(define_expand "extzv"
+ [(set (match_operand 0 "register_operand" "")
+ (zero_extract (match_operand:QI 1 "memory_operand" "")
+ (match_operand 2 "immediate_operand" "")
+ (match_operand 3 "immediate_operand" "")))]
+ "!TARGET_MIPS16"
+ "
+{
+ /* If the field does not start on a byte boundary, then fail. */
+ if (INTVAL (operands[3]) % 8 != 0)
+ FAIL;
+
+ /* MIPS I and MIPS II can only handle a 32bit field. */
+ if (!TARGET_64BIT && INTVAL (operands[2]) != 32)
+ FAIL;
+
+ /* MIPS III and MIPS IV can handle both 32bit and 64bit fields. */
+ if (TARGET_64BIT
+ && INTVAL (operands[2]) != 64
+ && INTVAL (operands[2]) != 32)
+ FAIL;
+
+ /* This can happen for a 64 bit target, when extracting a value from
+ a 64 bit union member. extract_bit_field doesn't verify that our
+ source matches the predicate, so we force it to be a MEM here. */
+ if (GET_CODE (operands[1]) != MEM)
+ FAIL;
+
+ /* Change the mode to BLKmode for aliasing purposes. */
+ operands[1] = change_address (operands[1], BLKmode, XEXP (operands[1], 0));
+
+ /* Otherwise, emit a lwl/lwr pair to load the value. */
+ if (INTVAL (operands[2]) == 64)
+ emit_insn (gen_movdi_uld (operands[0], operands[1]));
+ else
+ {
+ if (TARGET_64BIT)
+ {
+ operands[0] = gen_lowpart (SImode, operands[0]);
+ if (operands[0] == NULL_RTX)
+ FAIL;
+ }
+ emit_insn (gen_movsi_ulw (operands[0], operands[1]));
+ }
+ DONE;
+}")
+
+;; Only specify the mode operands 0, the rest are assumed to be word_mode.
+(define_expand "insv"
+ [(set (zero_extract (match_operand:QI 0 "memory_operand" "")
+ (match_operand 1 "immediate_operand" "")
+ (match_operand 2 "immediate_operand" ""))
+ (match_operand 3 "register_operand" ""))]
+ "!TARGET_MIPS16"
+ "
+{
+ /* If the field does not start on a byte boundary, then fail. */
+ if (INTVAL (operands[2]) % 8 != 0)
+ FAIL;
+
+ /* MIPS I and MIPS II can only handle a 32bit field. */
+ if (!TARGET_64BIT && INTVAL (operands[1]) != 32)
+ FAIL;
+
+ /* MIPS III and MIPS IV can handle both 32bit and 64bit fields. */
+ if (TARGET_64BIT
+ && INTVAL (operands[1]) != 64
+ && INTVAL (operands[1]) != 32)
+ FAIL;
+
+ /* This can happen for a 64 bit target, when storing into a 32 bit union
+ member. store_bit_field doesn't verify that our target matches the
+ predicate, so we force it to be a MEM here. */
+ if (GET_CODE (operands[0]) != MEM)
+ FAIL;
+
+ /* Change the mode to BLKmode for aliasing purposes. */
+ operands[0] = change_address (operands[0], BLKmode, XEXP (operands[0], 0));
+
+ /* Otherwise, emit a s[wd]l/s[wd]r pair to load the value. */
+ if (INTVAL (operands[1]) == 64)
+ emit_insn (gen_movdi_usd (operands[0], operands[3]));
+ else
+ {
+ if (TARGET_64BIT)
+ {
+ operands[3] = gen_lowpart (SImode, operands[3]);
+ if (operands[3] == NULL_RTX)
+ FAIL;
+ }
+ emit_insn (gen_movsi_usw (operands[0], operands[3]));
+ }
+ DONE;
+}")
+
+;; unaligned word moves generated by the bit field patterns
+
+(define_insn "movsi_ulw"
+ [(set (match_operand:SI 0 "register_operand" "=&d,&d")
+ (unspec:SI [(match_operand:BLK 1 "general_operand" "R,o")] 0))]
+ "!TARGET_MIPS16"
+ "*
+{
+ rtx offset = const0_rtx;
+ rtx addr = XEXP (operands[1], 0);
+ rtx mem_addr = eliminate_constant_term (addr, &offset);
+ char *ret;
+
+ if (TARGET_STATS)
+ mips_count_memory_refs (operands[1], 2);
+
+ /* The stack/frame pointers are always aligned, so we can convert
+ to the faster lw if we are referencing an aligned stack location. */
+
+ if ((INTVAL (offset) & 3) == 0
+ && (mem_addr == stack_pointer_rtx || mem_addr == frame_pointer_rtx))
+ ret = \"lw\\t%0,%1\";
+ else
+ ret = \"ulw\\t%0,%1\";
+
+ return mips_fill_delay_slot (ret, DELAY_LOAD, operands, insn);
+}"
+ [(set_attr "type" "load,load")
+ (set_attr "mode" "SI")
+ (set_attr "length" "2,4")])
+
+(define_insn "movsi_usw"
+ [(set (match_operand:BLK 0 "memory_operand" "=R,o")
+ (unspec:BLK [(match_operand:SI 1 "reg_or_0_operand" "dJ,dJ")] 1))]
+ "!TARGET_MIPS16"
+ "*
+{
+ rtx offset = const0_rtx;
+ rtx addr = XEXP (operands[0], 0);
+ rtx mem_addr = eliminate_constant_term (addr, &offset);
+
+ if (TARGET_STATS)
+ mips_count_memory_refs (operands[0], 2);
+
+ /* The stack/frame pointers are always aligned, so we can convert
+ to the faster sw if we are referencing an aligned stack location. */
+
+ if ((INTVAL (offset) & 3) == 0
+ && (mem_addr == stack_pointer_rtx || mem_addr == frame_pointer_rtx))
+ return \"sw\\t%1,%0\";
+
+ return \"usw\\t%z1,%0\";
+}"
+ [(set_attr "type" "store")
+ (set_attr "mode" "SI")
+ (set_attr "length" "2,4")])
+
+;; Bit field extract patterns which use ldl/ldr.
+
+;; unaligned double word moves generated by the bit field patterns
+
+(define_insn "movdi_uld"
+ [(set (match_operand:DI 0 "register_operand" "=&d,&d")
+ (unspec:DI [(match_operand:BLK 1 "general_operand" "R,o")] 0))]
+ ""
+ "*
+{
+ rtx offset = const0_rtx;
+ rtx addr = XEXP (operands[1], 0);
+ rtx mem_addr = eliminate_constant_term (addr, &offset);
+ char *ret;
+
+ if (TARGET_STATS)
+ mips_count_memory_refs (operands[1], 2);
+
+ /* The stack/frame pointers are always aligned, so we can convert
+ to the faster lw if we are referencing an aligned stack location. */
+
+ if ((INTVAL (offset) & 7) == 0
+ && (mem_addr == stack_pointer_rtx || mem_addr == frame_pointer_rtx))
+ ret = \"ld\\t%0,%1\";
+ else
+ ret = \"uld\\t%0,%1\";
+
+ return mips_fill_delay_slot (ret, DELAY_LOAD, operands, insn);
+}"
+ [(set_attr "type" "load,load")
+ (set_attr "mode" "SI")
+ (set_attr "length" "2,4")])
+
+(define_insn "movdi_usd"
+ [(set (match_operand:BLK 0 "memory_operand" "=R,o")
+ (unspec:BLK [(match_operand:DI 1 "reg_or_0_operand" "dJ,dJ")] 1))]
+ ""
+ "*
+{
+ rtx offset = const0_rtx;
+ rtx addr = XEXP (operands[0], 0);
+ rtx mem_addr = eliminate_constant_term (addr, &offset);
+
+ if (TARGET_STATS)
+ mips_count_memory_refs (operands[0], 2);
+
+ /* The stack/frame pointers are always aligned, so we can convert
+ to the faster sw if we are referencing an aligned stack location. */
+
+ if ((INTVAL (offset) & 7) == 0
+ && (mem_addr == stack_pointer_rtx || mem_addr == frame_pointer_rtx))
+ return \"sd\\t%1,%0\";
+
+ return \"usd\\t%z1,%0\";
+}"
+ [(set_attr "type" "store")
+ (set_attr "mode" "SI")
+ (set_attr "length" "2,4")])
+
+;; These two patterns support loading addresses with two instructions instead
+;; of using the macro instruction la.
+
+;; ??? mips_move_1word has support for HIGH, so this pattern may be
+;; unnecessary.
+
+(define_insn "high"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (high:SI (match_operand:SI 1 "immediate_operand" "")))]
+ "mips_split_addresses && !TARGET_MIPS16"
+ "lui\\t%0,%%hi(%1) # high"
+ [(set_attr "type" "move")
+ (set_attr "length" "1")])
+
+(define_insn "low"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (lo_sum:SI (match_operand:SI 1 "register_operand" "r")
+ (match_operand:SI 2 "immediate_operand" "")))]
+ "mips_split_addresses && !TARGET_MIPS16"
+ "addiu\\t%0,%1,%%lo(%2) # low"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")
+ (set_attr "length" "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 "nonimmediate_operand" "")
+ (match_operand:DI 1 "general_operand" ""))]
+ ""
+ "
+{
+ if (mips_split_addresses && mips_check_split (operands[1], DImode))
+ {
+ enum machine_mode mode = GET_MODE (operands[0]);
+ rtx tem = ((reload_in_progress | reload_completed)
+ ? operands[0] : gen_reg_rtx (mode));
+
+ emit_insn (gen_rtx (SET, VOIDmode, tem,
+ gen_rtx (HIGH, mode, operands[1])));
+
+ operands[1] = gen_rtx (LO_SUM, mode, tem, operands[1]);
+ }
+
+ /* If we are generating embedded PIC code, and we are referring to a
+ symbol in the .text section, we must use an offset from the start
+ of the function. */
+ if (TARGET_EMBEDDED_PIC
+ && (GET_CODE (operands[1]) == LABEL_REF
+ || (GET_CODE (operands[1]) == SYMBOL_REF
+ && ! SYMBOL_REF_FLAG (operands[1]))))
+ {
+ rtx temp;
+
+ temp = embedded_pic_offset (operands[1]);
+ temp = gen_rtx (PLUS, Pmode, embedded_pic_fnaddr_rtx,
+ force_reg (DImode, temp));
+ emit_move_insn (operands[0], force_reg (DImode, temp));
+ DONE;
+ }
+
+ /* If operands[1] is a constant address illegal for pic, then we need to
+ handle it just like LEGITIMIZE_ADDRESS does. */
+ if (flag_pic && pic_address_needs_scratch (operands[1]))
+ {
+ rtx temp = force_reg (DImode, XEXP (XEXP (operands[1], 0), 0));
+ rtx temp2 = XEXP (XEXP (operands[1], 0), 1);
+
+ if (! SMALL_INT (temp2))
+ temp2 = force_reg (DImode, temp2);
+
+ emit_move_insn (operands[0], gen_rtx (PLUS, DImode, temp, temp2));
+ DONE;
+ }
+
+ /* On the mips16, we can handle a GP relative reference by adding in
+ $gp. We need to check the name to see whether this is a string
+ constant. */
+ if (TARGET_MIPS16
+ && register_operand (operands[0], DImode)
+ && GET_CODE (operands[1]) == SYMBOL_REF
+ && SYMBOL_REF_FLAG (operands[1]))
+ {
+ char *name = XSTR (operands[1], 0);
+
+ if (name[0] != '*'
+ || strncmp (name + 1, LOCAL_LABEL_PREFIX,
+ sizeof LOCAL_LABEL_PREFIX - 1) != 0)
+ {
+ rtx base_reg;
+
+ if (reload_in_progress || reload_completed)
+ {
+ /* In movsi we use the constant table here. However, in
+ this case, we're better off copying $28 into a
+ register and adding, because the constant table entry
+ would be 8 bytes. */
+ base_reg = operands[0];
+ emit_move_insn (base_reg,
+ gen_rtx (CONST, DImode,
+ gen_rtx (REG, DImode,
+ GP_REG_FIRST + 28)));
+ }
+ else
+ {
+ base_reg = gen_reg_rtx (Pmode);
+ emit_move_insn (base_reg, mips16_gp_pseudo_reg ());
+ }
+
+ emit_move_insn (operands[0],
+ gen_rtx (PLUS, Pmode, base_reg,
+ mips16_gp_offset (operands[1])));
+ DONE;
+ }
+ }
+
+ if ((reload_in_progress | reload_completed) == 0
+ && !register_operand (operands[0], DImode)
+ && !register_operand (operands[1], DImode)
+ && (TARGET_MIPS16
+ || ((GET_CODE (operands[1]) != CONST_INT || INTVAL (operands[1]) != 0)
+ && operands[1] != CONST0_RTX (DImode))))
+ {
+ rtx temp = force_reg (DImode, operands[1]);
+ emit_move_insn (operands[0], temp);
+ 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 ""
+ [(set (match_operand:DI 0 "memory_operand" "R,m")
+ (reg:DI 31))]
+ "TARGET_MIPS16 && TARGET_64BIT"
+ "*
+{
+ operands[1] = gen_rtx (REG, DImode, 31);
+ return mips_move_2words (operands, insn);
+}"
+ [(set_attr "type" "store")
+ (set_attr "mode" "DI")
+ (set_attr "length" "1,2")])
+
+;; CYGNUS LOCAL law
+(define_insn "movdi_internal"
+ [(set (match_operand:DI 0 "nonimmediate_operand" "=d,d,d,d,R,o,*x*w,*d,*x*w")
+ (match_operand:DI 1 "general_operand" "d,iF,R,o,d,d,J,*x,*d"))]
+ "!TARGET_64BIT && !TARGET_MIPS16
+ && (register_operand (operands[0], DImode)
+ || register_operand (operands[1], DImode)
+ || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0)
+ || operands[1] == CONST0_RTX (DImode))"
+ "* return mips_move_2words (operands, insn); "
+ [(set_attr "type" "move,arith,load,load,store,store,hilo,hilo,hilo")
+ (set_attr "mode" "DI")
+ (set_attr "length" "2,4,2,4,2,4,2,2,2")])
+
+;; CYGNUS LOCAL law
+(define_insn ""
+ [(set (match_operand:DI 0 "nonimmediate_operand" "=d,y,d,d,d,d,d,R,To,*d")
+ (match_operand:DI 1 "general_operand" "d,d,y,K,N,R,To,d,d,*x*w"))]
+ "!TARGET_64BIT && TARGET_MIPS16
+ && (register_operand (operands[0], DImode)
+ || register_operand (operands[1], DImode))"
+ "* return mips_move_2words (operands, insn);"
+ [(set_attr "type" "move,move,move,arith,arith,load,load,store,store,hilo")
+ (set_attr "mode" "DI")
+ (set_attr "length" "2,2,2,2,3,2,4,2,4,2")])
+
+(define_split
+ [(set (match_operand:DI 0 "register_operand" "")
+ (match_operand:DI 1 "register_operand" ""))]
+ "reload_completed && !TARGET_64BIT && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE
+ && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0]))
+ && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1]))"
+
+ [(set (subreg:SI (match_dup 0) 0) (subreg:SI (match_dup 1) 0))
+ (set (subreg:SI (match_dup 0) 1) (subreg:SI (match_dup 1) 1))]
+ "")
+
+;; CYGNUS LOCAL law
+(define_insn "movdi_internal2"
+ [(set (match_operand:DI 0 "nonimmediate_operand" "=d,d,d,d,d,d,R,m,*x*w,*d,*x*w,*a*q")
+ (match_operand:DI 1 "movdi_operand" "d,S,IKL,Mnis,R,m,dJ,dJ,J,*x*w,*d,*J"))]
+ "TARGET_64BIT && !TARGET_MIPS16
+ && (register_operand (operands[0], DImode)
+ || se_register_operand (operands[1], DImode)
+ || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0)
+ || operands[1] == CONST0_RTX (DImode))"
+ "* return mips_move_2words (operands, insn); "
+ [(set_attr "type" "move,load,arith,arith,load,load,store,store,hilo,hilo,hilo,hilo")
+ (set_attr "mode" "DI")
+ (set_attr "length" "1,2,1,2,1,2,1,2,1,1,1,2")])
+
+;; CYGNUS LOCAL law
+(define_insn ""
+ [(set (match_operand:DI 0 "nonimmediate_operand" "=d,y,d,d,d,d,d,d,R,m,*d")
+ (match_operand:DI 1 "movdi_operand" "d,d,y,K,N,s,R,m,d,d,*x*w"))]
+ "TARGET_64BIT && TARGET_MIPS16
+ && (register_operand (operands[0], DImode)
+ || se_register_operand (operands[1], DImode))"
+ "* return mips_move_2words (operands, insn);"
+ [(set_attr "type" "move,move,move,arith,arith,arith,load,load,store,store,hilo")
+ (set_attr "mode" "DI")
+ (set_attr_alternative "length"
+ [(const_int 1)
+ (const_int 1)
+ (const_int 1)
+ (if_then_else (match_operand:VOID 1 "m16_uimm8_1" "")
+ (const_int 1)
+ (const_int 2))
+ (if_then_else (match_operand:VOID 1 "m16_nuimm8_1" "")
+ (const_int 2)
+ (const_int 3))
+ (if_then_else (match_operand:VOID 1 "m16_usym5_4" "")
+ (const_int 1)
+ (const_int 2))
+ (const_int 1)
+ (const_int 2)
+ (const_int 1)
+ (const_int 2)
+ (const_int 1)])])
+
+;; 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
+ && GET_CODE (operands[0]) == REG
+ && 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] = GEN_INT (0);
+ 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);
+ }
+}")
+
+;; Handle input reloads in DImode.
+;; This is mainly to handle reloading HILO_REGNUM. Note that we may
+;; see it as the source or the destination, depending upon which way
+;; reload handles the instruction.
+;; Making the second operand TImode is a trick. The compiler may
+;; reuse the same register for operand 0 and operand 2. Using TImode
+;; gives us two registers, so we can always use the one which is not
+;; used.
+
+(define_expand "reload_indi"
+ [(set (match_operand:DI 0 "register_operand" "=b")
+ (match_operand:DI 1 "" "b"))
+ (clobber (match_operand:TI 2 "register_operand" "=&d"))]
+ "TARGET_64BIT"
+ "
+{
+ rtx scratch = gen_rtx (REG, DImode,
+ (REGNO (operands[0]) == REGNO (operands[2])
+ ? REGNO (operands[2]) + 1
+ : REGNO (operands[2])));
+
+ if (GET_CODE (operands[0]) == REG && REGNO (operands[0]) == HILO_REGNUM)
+ {
+ if (GET_CODE (operands[1]) == MEM)
+ {
+ rtx memword, offword, hiword, loword;
+ rtx addr = find_replacement (&XEXP (operands[1], 0));
+ rtx op1 = change_address (operands[1], VOIDmode, addr);
+
+ scratch = gen_rtx (REG, SImode, REGNO (scratch));
+ memword = change_address (op1, SImode, NULL_RTX);
+ offword = change_address (adj_offsettable_operand (op1, 4),
+ SImode, NULL_RTX);
+ if (BYTES_BIG_ENDIAN)
+ {
+ hiword = memword;
+ loword = offword;
+ }
+ else
+ {
+ hiword = offword;
+ loword = memword;
+ }
+ emit_move_insn (scratch, hiword);
+ emit_move_insn (gen_rtx (REG, SImode, 64), scratch);
+ emit_move_insn (scratch, loword);
+ emit_move_insn (gen_rtx (REG, SImode, 65), scratch);
+ emit_insn (gen_rtx_USE (VOIDmode, operands[0]));
+ }
+ else
+ {
+ emit_insn (gen_ashrdi3 (scratch, operands[1], GEN_INT (32)));
+ emit_insn (gen_movdi (gen_rtx (REG, DImode, 64), scratch));
+ emit_insn (gen_ashldi3 (scratch, operands[1], GEN_INT (32)));
+ emit_insn (gen_ashrdi3 (scratch, scratch, GEN_INT (32)));
+ emit_insn (gen_movdi (gen_rtx (REG, DImode, 65), scratch));
+ emit_insn (gen_rtx_USE (VOIDmode, operands[0]));
+ }
+ DONE;
+ }
+ if (GET_CODE (operands[1]) == REG && REGNO (operands[1]) == HILO_REGNUM)
+ {
+ emit_insn (gen_movdi (scratch, gen_rtx (REG, DImode, 65)));
+ emit_insn (gen_ashldi3 (scratch, scratch, GEN_INT (32)));
+ emit_insn (gen_lshrdi3 (scratch, scratch, GEN_INT (32)));
+ emit_insn (gen_movdi (operands[0], gen_rtx (REG, DImode, 64)));
+ emit_insn (gen_ashldi3 (operands[0], operands[0], GEN_INT (32)));
+ emit_insn (gen_iordi3 (operands[0], operands[0], scratch));
+ emit_insn (gen_rtx_USE (VOIDmode, operands[1]));
+ DONE;
+ }
+ /* This handles moves between a float register and HI/LO. */
+ emit_move_insn (scratch, operands[1]);
+ emit_move_insn (operands[0], scratch);
+ DONE;
+}")
+
+;; Handle output reloads in DImode.
+
+;; Reloading HILO_REG in MIPS16 mode requires two scratch registers, so we
+;; use a TImode scratch reg.
+
+(define_expand "reload_outdi"
+ [(set (match_operand:DI 0 "" "=b")
+ (match_operand:DI 1 "se_register_operand" "b"))
+ (clobber (match_operand:TI 2 "register_operand" "=&d"))]
+ "TARGET_64BIT"
+ "
+{
+ rtx scratch = gen_rtx_REG (DImode, REGNO (operands[2]));
+
+ if (GET_CODE (operands[0]) == REG && REGNO (operands[0]) == HILO_REGNUM)
+ {
+ emit_insn (gen_ashrdi3 (scratch, operands[1], GEN_INT (32)));
+ emit_insn (gen_movdi (gen_rtx (REG, DImode, 64), scratch));
+ emit_insn (gen_ashldi3 (scratch, operands[1], GEN_INT (32)));
+ emit_insn (gen_ashrdi3 (scratch, scratch, GEN_INT (32)));
+ emit_insn (gen_movdi (gen_rtx (REG, DImode, 65), scratch));
+ emit_insn (gen_rtx_USE (VOIDmode, operands[0]));
+ DONE;
+ }
+ if (GET_CODE (operands[1]) == REG && REGNO (operands[1]) == HILO_REGNUM)
+ {
+ if (GET_CODE (operands[0]) == MEM)
+ {
+ rtx scratch, memword, offword, hiword, loword;
+ rtx addr = find_replacement (&XEXP (operands[0], 0));
+ rtx op0 = change_address (operands[0], VOIDmode, addr);
+
+ scratch = gen_rtx (REG, SImode, REGNO (operands[2]));
+ memword = change_address (op0, SImode, NULL_RTX);
+ offword = change_address (adj_offsettable_operand (op0, 4),
+ SImode, NULL_RTX);
+ if (BYTES_BIG_ENDIAN)
+ {
+ hiword = memword;
+ loword = offword;
+ }
+ else
+ {
+ hiword = offword;
+ loword = memword;
+ }
+ emit_move_insn (scratch, gen_rtx (REG, SImode, 64));
+ emit_move_insn (hiword, scratch);
+ emit_move_insn (scratch, gen_rtx (REG, SImode, 65));
+ emit_move_insn (loword, scratch);
+ emit_insn (gen_rtx_USE (VOIDmode, operands[1]));
+ }
+ else if (TARGET_MIPS16 && ! M16_REG_P (REGNO (operands[0])))
+ {
+ /* Handle the case where operand[0] is not a 'd' register,
+ and hence we can not directly move from the HILO register
+ into it. */
+ rtx scratch2 = gen_rtx_REG (DImode, REGNO (operands[2]) + 1);
+ emit_insn (gen_movdi (scratch, gen_rtx (REG, DImode, 65)));
+ emit_insn (gen_ashldi3 (scratch, scratch, GEN_INT (32)));
+ emit_insn (gen_lshrdi3 (scratch, scratch, GEN_INT (32)));
+ emit_insn (gen_movdi (scratch2, gen_rtx (REG, DImode, 64)));
+ emit_insn (gen_ashldi3 (scratch2, scratch2, GEN_INT (32)));
+ emit_insn (gen_iordi3 (scratch, scratch, scratch2));
+ emit_insn (gen_movdi (operands[0], scratch));
+ emit_insn (gen_rtx_USE (VOIDmode, operands[1]));
+ }
+ else
+ {
+ emit_insn (gen_movdi (scratch, gen_rtx (REG, DImode, 65)));
+ emit_insn (gen_ashldi3 (scratch, scratch, GEN_INT (32)));
+ emit_insn (gen_lshrdi3 (scratch, scratch, GEN_INT (32)));
+ emit_insn (gen_movdi (operands[0], gen_rtx (REG, DImode, 64)));
+ emit_insn (gen_ashldi3 (operands[0], operands[0], GEN_INT (32)));
+ emit_insn (gen_iordi3 (operands[0], operands[0], scratch));
+ emit_insn (gen_rtx_USE (VOIDmode, operands[1]));
+ }
+ DONE;
+ }
+ /* This handles moves between a float register and HI/LO. */
+ emit_move_insn (scratch, operands[1]);
+ emit_move_insn (operands[0], scratch);
+ DONE;
+}")
+
+;; 32-bit Integer moves
+
+(define_split
+ [(set (match_operand:SI 0 "register_operand" "")
+ (match_operand:SI 1 "large_int" ""))]
+ "!TARGET_DEBUG_D_MODE && !TARGET_MIPS16"
+ [(set (match_dup 0)
+ (match_dup 2))
+ (set (match_dup 0)
+ (ior:SI (match_dup 0)
+ (match_dup 3)))]
+ "
+{
+ operands[2] = GEN_INT (INTVAL (operands[1]) & 0xffff0000);
+ operands[3] = GEN_INT (INTVAL (operands[1]) & 0x0000ffff);
+}")
+
+;; 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 "nonimmediate_operand" "")
+ (match_operand:SI 1 "general_operand" ""))]
+ ""
+ "
+{
+ if (mips_split_addresses && mips_check_split (operands[1], SImode))
+ {
+ enum machine_mode mode = GET_MODE (operands[0]);
+ rtx tem = ((reload_in_progress | reload_completed)
+ ? operands[0] : gen_reg_rtx (mode));
+
+ emit_insn (gen_rtx (SET, VOIDmode, tem,
+ gen_rtx (HIGH, mode, operands[1])));
+
+ operands[1] = gen_rtx (LO_SUM, mode, tem, operands[1]);
+ }
+
+ /* If we are generating embedded PIC code, and we are referring to a
+ symbol in the .text section, we must use an offset from the start
+ of the function. */
+ if (TARGET_EMBEDDED_PIC
+ && (GET_CODE (operands[1]) == LABEL_REF
+ || (GET_CODE (operands[1]) == SYMBOL_REF
+ && ! SYMBOL_REF_FLAG (operands[1]))))
+ {
+ rtx temp;
+
+ temp = embedded_pic_offset (operands[1]);
+ temp = gen_rtx (PLUS, Pmode, embedded_pic_fnaddr_rtx,
+ force_reg (SImode, temp));
+ emit_move_insn (operands[0], force_reg (SImode, temp));
+ DONE;
+ }
+
+ /* If operands[1] is a constant address invalid for pic, then we need to
+ handle it just like LEGITIMIZE_ADDRESS does. */
+ if (flag_pic && pic_address_needs_scratch (operands[1]))
+ {
+ rtx temp = force_reg (SImode, XEXP (XEXP (operands[1], 0), 0));
+ rtx temp2 = XEXP (XEXP (operands[1], 0), 1);
+
+ if (! SMALL_INT (temp2))
+ temp2 = force_reg (SImode, temp2);
+
+ emit_move_insn (operands[0], gen_rtx (PLUS, SImode, temp, temp2));
+ DONE;
+ }
+
+ /* On the mips16, we can handle a GP relative reference by adding in
+ $gp. We need to check the name to see whether this is a string
+ constant. */
+ if (TARGET_MIPS16
+ && register_operand (operands[0], SImode)
+ && GET_CODE (operands[1]) == SYMBOL_REF
+ && SYMBOL_REF_FLAG (operands[1]))
+ {
+ char *name = XSTR (operands[1], 0);
+
+ if (name[0] != '*'
+ || strncmp (name + 1, LOCAL_LABEL_PREFIX,
+ sizeof LOCAL_LABEL_PREFIX - 1) != 0)
+ {
+ rtx base_reg;
+
+ if (reload_in_progress || reload_completed)
+ {
+ /* We need to reload this address. In this case we
+ aren't going to have a chance to combine loading the
+ address with the load or store. That means that we
+ can either generate a 2 byte move followed by a 4
+ byte addition, or a 2 byte load with a 4 byte entry
+ in the constant table. Since the entry in the
+ constant table might be shared, we're better off, on
+ average, loading the address from the constant table. */
+ emit_move_insn (operands[0],
+ force_const_mem (SImode, operands[1]));
+ DONE;
+ }
+
+ base_reg = gen_reg_rtx (Pmode);
+ emit_move_insn (base_reg, mips16_gp_pseudo_reg ());
+
+ emit_move_insn (operands[0],
+ gen_rtx (PLUS, Pmode, base_reg,
+ mips16_gp_offset (operands[1])));
+ DONE;
+ }
+ }
+
+ if ((reload_in_progress | reload_completed) == 0
+ && !register_operand (operands[0], SImode)
+ && !register_operand (operands[1], SImode)
+ && (TARGET_MIPS16
+ || GET_CODE (operands[1]) != CONST_INT
+ || INTVAL (operands[1]) != 0))
+ {
+ rtx temp = force_reg (SImode, operands[1]);
+ emit_move_insn (operands[0], temp);
+ 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 ""
+ [(set (match_operand:SI 0 "memory_operand" "R,m")
+ (reg:SI 31))]
+ "TARGET_MIPS16"
+ "*
+{
+ operands[1] = gen_rtx (REG, SImode, 31);
+ return mips_move_1word (operands, insn, FALSE);
+}"
+ [(set_attr "type" "store")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1,2")])
+
+;; The difference between these two is whether or not ints are allowed
+;; in FP registers (off by default, use -mdebugh to enable).
+
+;; CYGNUS LOCAL law
+(define_insn "movsi_internal1"
+ [(set (match_operand:SI 0 "nonimmediate_operand" "=d,d,d,d,d,d,R,m,*d,*f*z,*f,*f,*f,*R,*m,*x*w,*x*w,*d,*d")
+ (match_operand:SI 1 "move_operand" "d,S,IKL,Mnis,R,m,dJ,dJ,*f*z,*d,*f,*R,*m,*f,*f,J,*d,*x*w,*a*q"))]
+ "TARGET_DEBUG_H_MODE && !TARGET_MIPS16
+ && (register_operand (operands[0], SImode)
+ || register_operand (operands[1], SImode)
+ || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0))"
+ "* return mips_move_1word (operands, insn, FALSE);"
+ [(set_attr "type" "move,load,arith,arith,load,load,store,store,xfer,xfer,move,load,load,store,store,hilo,hilo,hilo,hilo")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1,2,1,2,1,2,1,2,1,1,1,1,2,1,2,1,1,1,1")])
+
+;; CYGNUS LOCAL law
+(define_insn "movsi_internal2"
+ [(set (match_operand:SI 0 "nonimmediate_operand" "=d,d,d,d,d,d,R,m,*d,*z,*x*w,*d,*x*w,*d")
+ (match_operand:SI 1 "move_operand" "d,S,IKL,Mnis,R,m,dJ,dJ,*z,*d,J,*x*w,*d,*a*q"))]
+ "!TARGET_DEBUG_H_MODE && !TARGET_MIPS16
+ && (register_operand (operands[0], SImode)
+ || register_operand (operands[1], SImode)
+ || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0))"
+ "* return mips_move_1word (operands, insn, FALSE);"
+ [(set_attr "type" "move,load,arith,arith,load,load,store,store,xfer,xfer,hilo,hilo,hilo,hilo")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1,2,1,2,1,2,1,2,1,1,1,1,1,1")])
+
+;; This is the mips16 movsi instruction. We accept a small integer as
+;; the source if the destination is a GP memory reference. This is
+;; because we want the combine pass to turn adding a GP reference to a
+;; register into a direct GP reference, but the combine pass will pass
+;; in the source as a constant if it finds an equivalent one. If the
+;; instruction is recognized, reload will force the constant back out
+;; into a register.
+
+(define_insn ""
+ [(set (match_operand:SI 0 "nonimmediate_operand" "=d,y,d,d,d,d,d,d,d,R,m,*d,*d")
+ (match_operand:SI 1 "move_operand" "d,d,y,S,K,N,s,R,m,d,d,*x,*a"))]
+ "TARGET_MIPS16
+ && (register_operand (operands[0], SImode)
+ || register_operand (operands[1], SImode)
+ || (GET_CODE (operands[0]) == MEM
+ && GET_CODE (XEXP (operands[0], 0)) == PLUS
+ && GET_CODE (XEXP (XEXP (operands[0], 0), 1)) == CONST
+ && mips16_gp_offset_p (XEXP (XEXP (operands[0], 0), 1))
+ && GET_CODE (operands[1]) == CONST_INT
+ && (SMALL_INT (operands[1])
+ || SMALL_INT_UNSIGNED (operands[1]))))"
+ "* return mips_move_1word (operands, insn, FALSE);"
+ [(set_attr "type" "move,move,move,load,arith,arith,arith,load,load,store,store,hilo,hilo")
+ (set_attr "mode" "SI")
+ (set_attr_alternative "length"
+ [(const_int 1)
+ (const_int 1)
+ (const_int 1)
+ (const_int 2)
+ (if_then_else (match_operand:VOID 1 "m16_uimm8_1" "")
+ (const_int 1)
+ (const_int 2))
+ (if_then_else (match_operand:VOID 1 "m16_nuimm8_1" "")
+ (const_int 2)
+ (const_int 3))
+ (if_then_else (match_operand:VOID 1 "m16_usym8_4" "")
+ (const_int 1)
+ (const_int 2))
+ (const_int 1)
+ (const_int 2)
+ (const_int 1)
+ (const_int 2)
+ (const_int 1)
+ (const_int 1)])])
+
+;; 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
+ && GET_CODE (operands[0]) == REG
+ && 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] = GEN_INT (0);
+ 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
+ && GET_CODE (operands[0]) == REG
+ && 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);
+}")
+
+;; On the mips16, we can split a load of a negative constant into a
+;; load and a neg. That's what mips_move_1word will generate anyhow.
+
+(define_split
+ [(set (match_operand:SI 0 "register_operand" "")
+ (match_operand:SI 1 "const_int_operand" ""))]
+ "TARGET_MIPS16 && reload_completed
+ && GET_CODE (operands[0]) == REG
+ && M16_REG_P (REGNO (operands[0]))
+ && GET_CODE (operands[1]) == CONST_INT
+ && INTVAL (operands[1]) < 0
+ && INTVAL (operands[1]) > - 0x8000"
+ [(set (match_dup 0) (match_dup 1))
+ (set (match_dup 0) (neg:SI (match_dup 0)))]
+ "
+{
+ operands[1] = GEN_INT (- INTVAL (operands[1]));
+}")
+
+;; Reload HILO_REGNUM in SI mode. This needs a scratch register in
+;; order to set the sign bit correctly in the HI register.
+
+(define_expand "reload_outsi"
+ [(set (match_operand:SI 0 "general_operand" "=b")
+ (match_operand:SI 1 "register_operand" "b"))
+ (clobber (match_operand:SI 2 "register_operand" "=&d"))]
+ "TARGET_64BIT || TARGET_MIPS16"
+ "
+{
+ if (TARGET_64BIT
+ && GET_CODE (operands[0]) == REG && REGNO (operands[0]) == HILO_REGNUM)
+ {
+ emit_insn (gen_movsi (gen_rtx (REG, SImode, 65), operands[1]));
+ emit_insn (gen_ashrsi3 (operands[2], operands[1], GEN_INT (31)));
+ emit_insn (gen_movsi (gen_rtx (REG, SImode, 64), operands[2]));
+ emit_insn (gen_rtx_USE (VOIDmode, operands[0]));
+ DONE;
+ }
+ /* Use a mult to reload LO on mips16. ??? This is hideous. */
+ if (TARGET_MIPS16
+ && GET_CODE (operands[0]) == REG && REGNO (operands[0]) == LO_REGNUM)
+ {
+ emit_insn (gen_movsi (operands[2], GEN_INT (1)));
+ /* This is gen_mulsi3_internal, but we need to fill in the
+ scratch registers. */
+ emit_insn (gen_rtx (PARALLEL, VOIDmode,
+ gen_rtvec (3,
+ gen_rtx (SET, VOIDmode,
+ operands[0],
+ gen_rtx (MULT, SImode,
+ operands[1],
+ operands[2])),
+ gen_rtx (CLOBBER, VOIDmode,
+ gen_rtx (REG, SImode, 64)),
+ gen_rtx (CLOBBER, VOIDmode,
+ gen_rtx (REG, SImode, 66)))));
+ DONE;
+ }
+ /* FIXME: I don't know how to get a value into the HI register. */
+ if (GET_CODE (operands[0]) == REG
+ && (TARGET_MIPS16 ? M16_REG_P (REGNO (operands[0]))
+ : GP_REG_P (REGNO (operands[0]))))
+ {
+ emit_move_insn (operands[0], operands[1]);
+ DONE;
+ }
+ /* This handles moves between a float register and HI/LO. */
+ emit_move_insn (operands[2], operands[1]);
+ emit_move_insn (operands[0], operands[2]);
+ DONE;
+}")
+
+;; Reload a value into HI or LO. There is no mthi or mtlo on mips16,
+;; so we use a mult. ??? This is hideous, and we ought to figure out
+;; something better.
+
+;; We use no predicate for operand1, because it may be a PLUS, and there
+;; is no convenient predicate for that.
+
+(define_expand "reload_insi"
+ [(set (match_operand:SI 0 "register_operand" "=b")
+ (match_operand:SI 1 "" "b"))
+ (clobber (match_operand:SI 2 "register_operand" "=&d"))]
+ "TARGET_MIPS16"
+ "
+{
+ if (TARGET_MIPS16
+ && GET_CODE (operands[0]) == REG && REGNO (operands[0]) == LO_REGNUM)
+ {
+ emit_insn (gen_movsi (operands[2], GEN_INT (1)));
+ /* This is gen_mulsi3_internal, but we need to fill in the
+ scratch registers. */
+ emit_insn (gen_rtx (PARALLEL, VOIDmode,
+ gen_rtvec (3,
+ gen_rtx (SET, VOIDmode,
+ operands[0],
+ gen_rtx (MULT, SImode,
+ operands[1],
+ operands[2])),
+ gen_rtx (CLOBBER, VOIDmode,
+ gen_rtx (REG, SImode, 64)),
+ gen_rtx (CLOBBER, VOIDmode,
+ gen_rtx (REG, SImode, 66)))));
+ DONE;
+ }
+
+ /* If this is a plus, then this must be an add of the stack pointer against
+ either a hard register or a pseudo. */
+ if (TARGET_MIPS16 && GET_CODE (operands[1]) == PLUS)
+ {
+ rtx plus_op;
+
+ if (XEXP (operands[1], 0) == stack_pointer_rtx)
+ plus_op = XEXP (operands[1], 1);
+ else if (XEXP (operands[1], 1) == stack_pointer_rtx)
+ plus_op = XEXP (operands[1], 0);
+ else
+ abort ();
+
+ /* We should have a register now. */
+ if (GET_CODE (plus_op) != REG)
+ abort ();
+
+ if (REGNO (plus_op) < FIRST_PSEUDO_REGISTER)
+ {
+ /* We have to have at least one temporary register which is not
+ overlapping plus_op. */
+ if (! rtx_equal_p (plus_op, operands[0]))
+ {
+ emit_move_insn (operands[0], stack_pointer_rtx);
+ emit_insn (gen_addsi3 (operands[0], operands[0], plus_op));
+ }
+ else if (! rtx_equal_p (plus_op, operands[2]))
+ {
+ emit_move_insn (operands[2], stack_pointer_rtx);
+ emit_insn (gen_addsi3 (operands[0], plus_op, operands[2]));
+ }
+ else
+ abort ();
+ }
+ else
+ {
+ /* We need two registers in this case. */
+ if (! rtx_equal_p (operands[0], operands[2]))
+ {
+ emit_move_insn (operands[0], stack_pointer_rtx);
+ emit_move_insn (operands[2], plus_op);
+ emit_insn (gen_addsi3 (operands[0], operands[0], operands[2]));
+ }
+ else
+ abort ();
+ }
+ DONE;
+ }
+
+ /* FIXME: I don't know how to get a value into the HI register. */
+ emit_move_insn (operands[0], operands[1]);
+ DONE;
+}")
+
+;; 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,*d,*R,*m,*d,*f,*f,*f,*f,*R,*m")
+ (match_operand:CC 1 "general_operand" "z,*d,*R,*m,*d,*d,*f,*d,*f,*R,*m,*f,*f"))]
+ "mips_isa >= 4 && TARGET_HARD_FLOAT"
+ "* return mips_move_1word (operands, insn, FALSE);"
+ [(set_attr "type" "move,move,load,load,store,store,xfer,xfer,move,load,load,store,store")
+ (set_attr "mode" "SI")
+ (set_attr "length" "2,1,1,2,1,2,1,1,1,1,2,1,2")])
+
+;; Reload condition code registers. These need scratch registers.
+
+(define_expand "reload_incc"
+ [(set (match_operand:CC 0 "register_operand" "=z")
+ (match_operand:CC 1 "general_operand" "z"))
+ (clobber (match_operand:TF 2 "register_operand" "=&f"))]
+ "mips_isa >= 4 && TARGET_HARD_FLOAT"
+ "
+{
+ rtx source;
+ rtx fp1, fp2;
+
+ /* This is called when are copying some value into a condition code
+ register. Operand 0 is the condition code register. Operand 1
+ is the source. Operand 2 is a scratch register; we use TFmode
+ because we actually need two floating point registers. */
+ if (! ST_REG_P (true_regnum (operands[0]))
+ || ! FP_REG_P (true_regnum (operands[2])))
+ abort ();
+
+ /* We need to get the source in SFmode so that the insn is
+ recognized. */
+ if (GET_CODE (operands[1]) == MEM)
+ source = change_address (operands[1], SFmode, NULL_RTX);
+ else if (GET_CODE (operands[1]) == REG || GET_CODE (operands[1]) == SUBREG)
+ source = gen_rtx (REG, SFmode, true_regnum (operands[1]));
+ else
+ source = operands[1];
+
+ fp1 = gen_rtx (REG, SFmode, REGNO (operands[2]));
+ fp2 = gen_rtx (REG, SFmode, REGNO (operands[2]) + 1);
+
+ emit_insn (gen_move_insn (fp1, source));
+ emit_insn (gen_move_insn (fp2, gen_rtx (REG, SFmode, 0)));
+ emit_insn (gen_rtx (SET, VOIDmode, operands[0],
+ gen_rtx (LT, CCmode, fp2, fp1)));
+
+ DONE;
+}")
+
+(define_expand "reload_outcc"
+ [(set (match_operand:CC 0 "general_operand" "=z")
+ (match_operand:CC 1 "register_operand" "z"))
+ (clobber (match_operand:CC 2 "register_operand" "=&d"))]
+ "mips_isa >= 4 && TARGET_HARD_FLOAT"
+ "
+{
+ /* This is called when we are copying a condition code register out
+ to save it somewhere. Operand 0 should be the location we are
+ going to save it to. Operand 1 should be the condition code
+ register. Operand 2 should be a scratch general purpose register
+ created for us by reload. The mips_secondary_reload_class
+ function should have told reload that we don't need a scratch
+ register if the destination is a general purpose register anyhow. */
+ if (ST_REG_P (true_regnum (operands[0]))
+ || GP_REG_P (true_regnum (operands[0]))
+ || ! ST_REG_P (true_regnum (operands[1]))
+ || ! GP_REG_P (true_regnum (operands[2])))
+ abort ();
+
+ /* All we have to do is copy the value from the condition code to
+ the data register, which movcc can handle, and then store the
+ value into the real final destination. */
+ emit_insn (gen_move_insn (operands[2], operands[1]));
+ emit_insn (gen_move_insn (operands[0], 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 ""
+ [(set (match_operand:SF 0 "register_operand" "=f")
+ (mem:SF (plus:SI (match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "register_operand" "d"))))]
+ "mips_isa >= 4 && TARGET_HARD_FLOAT"
+ "lwxc1\\t%0,%1(%2)"
+ [(set_attr "type" "load")
+ (set_attr "mode" "SF")
+ (set_attr "length" "1")])
+
+(define_insn ""
+ [(set (match_operand:SF 0 "register_operand" "=f")
+ (mem:SF (plus:DI (match_operand:DI 1 "se_register_operand" "d")
+ (match_operand:DI 2 "se_register_operand" "d"))))]
+ "mips_isa >= 4 && TARGET_HARD_FLOAT"
+ "lwxc1\\t%0,%1(%2)"
+ [(set_attr "type" "load")
+ (set_attr "mode" "SF")
+ (set_attr "length" "1")])
+
+(define_insn ""
+ [(set (match_operand:DF 0 "register_operand" "=f")
+ (mem:DF (plus:SI (match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "register_operand" "d"))))]
+ "mips_isa >= 4 && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
+ "ldxc1\\t%0,%1(%2)"
+ [(set_attr "type" "load")
+ (set_attr "mode" "DF")
+ (set_attr "length" "1")])
+
+(define_insn ""
+ [(set (match_operand:DF 0 "register_operand" "=f")
+ (mem:DF (plus:DI (match_operand:DI 1 "se_register_operand" "d")
+ (match_operand:DI 2 "se_register_operand" "d"))))]
+ "mips_isa >= 4 && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
+ "ldxc1\\t%0,%1(%2)"
+ [(set_attr "type" "load")
+ (set_attr "mode" "DF")
+ (set_attr "length" "1")])
+
+(define_insn ""
+ [(set (mem:SF (plus:SI (match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "register_operand" "d")))
+ (match_operand:SF 0 "register_operand" "=f"))]
+ "mips_isa >= 4 && TARGET_HARD_FLOAT"
+ "swxc1\\t%0,%1(%2)"
+ [(set_attr "type" "store")
+ (set_attr "mode" "SF")
+ (set_attr "length" "1")])
+
+(define_insn ""
+ [(set (mem:SF (plus:DI (match_operand:DI 1 "se_register_operand" "d")
+ (match_operand:DI 2 "se_register_operand" "d")))
+ (match_operand:SF 0 "register_operand" "=f"))]
+ "mips_isa >= 4 && TARGET_HARD_FLOAT"
+ "swxc1\\t%0,%1(%2)"
+ [(set_attr "type" "store")
+ (set_attr "mode" "SF")
+ (set_attr "length" "1")])
+
+(define_insn ""
+ [(set (mem:DF (plus:SI (match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "register_operand" "d")))
+ (match_operand:DF 0 "register_operand" "=f"))]
+ "mips_isa >= 4 && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
+ "sdxc1\\t%0,%1(%2)"
+ [(set_attr "type" "store")
+ (set_attr "mode" "DF")
+ (set_attr "length" "1")])
+
+(define_insn ""
+ [(set (mem:DF (plus:DI (match_operand:DI 1 "se_register_operand" "d")
+ (match_operand:DI 2 "se_register_operand" "d")))
+ (match_operand:DF 0 "register_operand" "=f"))]
+ "mips_isa >= 4 && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
+ "sdxc1\\t%0,%1(%2)"
+ [(set_attr "type" "store")
+ (set_attr "mode" "DF")
+ (set_attr "length" "1")])
+
+;; 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 BYTE_LOADS_ZERO_EXTEND is defined
+
+(define_expand "movhi"
+ [(set (match_operand:HI 0 "nonimmediate_operand" "")
+ (match_operand:HI 1 "general_operand" ""))]
+ ""
+ "
+{
+ if ((reload_in_progress | reload_completed) == 0
+ && !register_operand (operands[0], HImode)
+ && !register_operand (operands[1], HImode)
+ && (TARGET_MIPS16
+ || (GET_CODE (operands[1]) != CONST_INT
+ || INTVAL (operands[1]) != 0)))
+ {
+ rtx temp = force_reg (HImode, operands[1]);
+ emit_move_insn (operands[0], temp);
+ 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 "movhi_internal1"
+ [(set (match_operand:HI 0 "nonimmediate_operand" "=d,d,d,d,R,m,*d,*f,*f*z,*x,*d")
+ (match_operand:HI 1 "general_operand" "d,IK,R,m,dJ,dJ,*f*z,*d,*f,*d,*x"))]
+ "TARGET_DEBUG_H_MODE && !TARGET_MIPS16
+ && (register_operand (operands[0], HImode)
+ || register_operand (operands[1], HImode)
+ || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0))"
+ "* return mips_move_1word (operands, insn, TRUE);"
+ [(set_attr "type" "move,arith,load,load,store,store,xfer,xfer,move,hilo,hilo")
+ (set_attr "mode" "HI")
+ (set_attr "length" "1,1,1,2,1,2,1,1,1,1,1")])
+
+(define_insn "movhi_internal2"
+ [(set (match_operand:HI 0 "nonimmediate_operand" "=d,d,d,d,R,m,*d,*z,*x,*d")
+ (match_operand:HI 1 "general_operand" "d,IK,R,m,dJ,dJ,*z,*d,*d,*x"))]
+ "!TARGET_DEBUG_H_MODE && !TARGET_MIPS16
+ && (register_operand (operands[0], HImode)
+ || register_operand (operands[1], HImode)
+ || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0))"
+ "* return mips_move_1word (operands, insn, TRUE);"
+ [(set_attr "type" "move,arith,load,load,store,store,xfer,xfer,hilo,hilo")
+ (set_attr "mode" "HI")
+ (set_attr "length" "1,1,1,2,1,2,1,1,1,1")])
+
+(define_insn ""
+ [(set (match_operand:HI 0 "nonimmediate_operand" "=d,y,d,d,d,d,d,R,m,*d")
+ (match_operand:HI 1 "general_operand" "d,d,y,K,N,R,m,d,d,*x"))]
+ "TARGET_MIPS16
+ && (register_operand (operands[0], HImode)
+ || register_operand (operands[1], HImode))"
+ "* return mips_move_1word (operands, insn, TRUE);"
+ [(set_attr "type" "move,move,move,arith,arith,load,load,store,store,hilo")
+ (set_attr "mode" "HI")
+ (set_attr_alternative "length"
+ [(const_int 1)
+ (const_int 1)
+ (const_int 1)
+ (if_then_else (match_operand:VOID 1 "m16_uimm8_1" "")
+ (const_int 1)
+ (const_int 2))
+ (if_then_else (match_operand:VOID 1 "m16_nuimm8_1" "")
+ (const_int 2)
+ (const_int 3))
+ (const_int 1)
+ (const_int 2)
+ (const_int 1)
+ (const_int 2)
+ (const_int 1)])])
+
+
+;; 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
+ && GET_CODE (operands[0]) == REG
+ && 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] = GEN_INT (0);
+ 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 BYTE_LOADS_ZERO_EXTEND is defined
+
+(define_expand "movqi"
+ [(set (match_operand:QI 0 "nonimmediate_operand" "")
+ (match_operand:QI 1 "general_operand" ""))]
+ ""
+ "
+{
+ if ((reload_in_progress | reload_completed) == 0
+ && !register_operand (operands[0], QImode)
+ && !register_operand (operands[1], QImode)
+ && (TARGET_MIPS16
+ || (GET_CODE (operands[1]) != CONST_INT
+ || INTVAL (operands[1]) != 0)))
+ {
+ rtx temp = force_reg (QImode, operands[1]);
+ emit_move_insn (operands[0], temp);
+ 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 "movqi_internal1"
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=d,d,d,d,R,m,*d,*f*z,*f,*x,*d")
+ (match_operand:QI 1 "general_operand" "d,IK,R,m,dJ,dJ,*f*z,*d,*f,*d,*x"))]
+ "TARGET_DEBUG_H_MODE && !TARGET_MIPS16
+ && (register_operand (operands[0], QImode)
+ || register_operand (operands[1], QImode)
+ || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0))"
+ "* return mips_move_1word (operands, insn, TRUE);"
+ [(set_attr "type" "move,arith,load,load,store,store,xfer,xfer,move,hilo,hilo")
+ (set_attr "mode" "QI")
+ (set_attr "length" "1,1,1,2,1,2,1,1,1,1,1")])
+
+(define_insn "movqi_internal2"
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=d,d,d,d,R,m,*d,*z,*x,*d")
+ (match_operand:QI 1 "general_operand" "d,IK,R,m,dJ,dJ,*z,*d,*d,*x"))]
+ "!TARGET_DEBUG_H_MODE && !TARGET_MIPS16
+ && (register_operand (operands[0], QImode)
+ || register_operand (operands[1], QImode)
+ || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0))"
+ "* return mips_move_1word (operands, insn, TRUE);"
+ [(set_attr "type" "move,arith,load,load,store,store,xfer,xfer,hilo,hilo")
+ (set_attr "mode" "QI")
+ (set_attr "length" "1,1,1,2,1,2,1,1,1,1")])
+
+(define_insn ""
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=d,y,d,d,d,d,d,R,m,*d")
+ (match_operand:QI 1 "general_operand" "d,d,y,K,N,R,m,d,d,*x"))]
+ "TARGET_MIPS16
+ && (register_operand (operands[0], QImode)
+ || register_operand (operands[1], QImode))"
+ "* return mips_move_1word (operands, insn, TRUE);"
+ [(set_attr "type" "move,move,move,arith,arith,load,load,store,store,hilo")
+ (set_attr "mode" "QI")
+ (set_attr_alternative "length"
+ [(const_int 1)
+ (const_int 1)
+ (const_int 1)
+ (if_then_else (match_operand:VOID 1 "m16_uimm8_1" "")
+ (const_int 1)
+ (const_int 2))
+ (if_then_else (match_operand:VOID 1 "m16_nuimm8_1" "")
+ (const_int 2)
+ (const_int 3))
+ (const_int 1)
+ (const_int 2)
+ (const_int 1)
+ (const_int 2)
+ (const_int 1)])])
+
+
+;; 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
+ && GET_CODE (operands[0]) == REG
+ && 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] = GEN_INT (0);
+ 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 "nonimmediate_operand" "")
+ (match_operand:SF 1 "general_operand" ""))]
+ ""
+ "
+{
+ if ((reload_in_progress | reload_completed) == 0
+ && !register_operand (operands[0], SFmode)
+ && !register_operand (operands[1], SFmode)
+ && (TARGET_MIPS16
+ || ((GET_CODE (operands[1]) != CONST_INT || INTVAL (operands[1]) != 0)
+ && operands[1] != CONST0_RTX (SFmode))))
+ {
+ rtx temp = force_reg (SFmode, operands[1]);
+ emit_move_insn (operands[0], temp);
+ DONE;
+ }
+}")
+
+(define_insn "movsf_internal1"
+ [(set (match_operand:SF 0 "nonimmediate_operand" "=f,f,f,f,R,m,*f,*d,*d,*d,*d,*R,*m")
+ (match_operand:SF 1 "general_operand" "f,G,R,Fm,fG,fG,*d,*f,*G*d,*R,*F*m,*d,*d"))]
+ "TARGET_HARD_FLOAT
+ && (register_operand (operands[0], SFmode)
+ || register_operand (operands[1], SFmode)
+ || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0)
+ || operands[1] == CONST0_RTX (SFmode))"
+ "* return mips_move_1word (operands, insn, FALSE);"
+ [(set_attr "type" "move,xfer,load,load,store,store,xfer,xfer,move,load,load,store,store")
+ (set_attr "mode" "SF")
+ (set_attr "length" "1,1,1,2,1,2,1,1,1,1,2,1,2")])
+
+
+(define_insn "movsf_internal2"
+ [(set (match_operand:SF 0 "nonimmediate_operand" "=d,d,d,R,m")
+ (match_operand:SF 1 "general_operand" " Gd,R,Fm,d,d"))]
+ "TARGET_SOFT_FLOAT && !TARGET_MIPS16
+ && (register_operand (operands[0], SFmode)
+ || register_operand (operands[1], SFmode)
+ || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0)
+ || operands[1] == CONST0_RTX (SFmode))"
+ "* return mips_move_1word (operands, insn, FALSE);"
+ [(set_attr "type" "move,load,load,store,store")
+ (set_attr "mode" "SF")
+ (set_attr "length" "1,1,2,1,2")])
+
+(define_insn ""
+ [(set (match_operand:SF 0 "nonimmediate_operand" "=d,y,d,d,d,R,m")
+ (match_operand:SF 1 "general_operand" "d,d,y,R,Fm,d,d"))]
+ "TARGET_MIPS16
+ && (register_operand (operands[0], SFmode)
+ || register_operand (operands[1], SFmode))"
+ "* return mips_move_1word (operands, insn, FALSE);"
+ [(set_attr "type" "move,move,move,load,load,store,store")
+ (set_attr "mode" "SF")
+ (set_attr "length" "1,1,1,1,2,1,2")])
+
+
+;; 64-bit floating point moves
+
+(define_expand "movdf"
+ [(set (match_operand:DF 0 "nonimmediate_operand" "")
+ (match_operand:DF 1 "general_operand" ""))]
+ ""
+ "
+{
+ if ((reload_in_progress | reload_completed) == 0
+ && !register_operand (operands[0], DFmode)
+ && !register_operand (operands[1], DFmode)
+ && (TARGET_MIPS16
+ || ((GET_CODE (operands[1]) != CONST_INT || INTVAL (operands[1]) != 0)
+ && operands[1] != CONST0_RTX (DFmode))))
+ {
+ rtx temp = force_reg (DFmode, operands[1]);
+ emit_move_insn (operands[0], temp);
+ DONE;
+ }
+}")
+
+(define_insn "movdf_internal1"
+ [(set (match_operand:DF 0 "nonimmediate_operand" "=f,f,f,R,To,f,*f,*d,*d,*d,*d,*R,*T")
+ (match_operand:DF 1 "general_operand" "f,R,To,fG,fG,F,*d,*f,*d*G,*R,*T*F,*d,*d"))]
+ "TARGET_HARD_FLOAT && !(TARGET_FLOAT64 && !TARGET_64BIT)
+ && TARGET_DOUBLE_FLOAT
+ && (register_operand (operands[0], DFmode)
+ || register_operand (operands[1], DFmode)
+ || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0)
+ || operands[1] == CONST0_RTX (DFmode))"
+ "* return mips_move_2words (operands, insn); "
+ [(set_attr "type" "move,load,load,store,store,load,xfer,xfer,move,load,load,store,store")
+ (set_attr "mode" "DF")
+ (set_attr "length" "1,2,4,2,4,4,2,2,2,2,4,2,4")])
+
+(define_insn "movdf_internal1a"
+ [(set (match_operand:DF 0 "nonimmediate_operand" "=f,f,R,R,To,To,f,*d,*d,*d,*To,*R")
+ (match_operand:DF 1 "general_operand" " f,To,f,G,f,G,F,*F,*To,*R,*d,*d"))]
+ "TARGET_HARD_FLOAT && (TARGET_FLOAT64 && !TARGET_64BIT)
+ && TARGET_DOUBLE_FLOAT
+ && (register_operand (operands[0], DFmode)
+ || register_operand (operands[1], DFmode)
+ || (GET_CODE (operands [0]) == MEM
+ && ((GET_CODE (operands[1]) == CONST_INT
+ && INTVAL (operands[1]) == 0)
+ || operands[1] == CONST0_RTX (DFmode))))"
+ "* return mips_move_2words (operands, insn); "
+ [(set_attr "type" "move,load,store,store,store,store,load,load,load,load,store,store")
+ (set_attr "mode" "DF")
+ (set_attr "length" "1,2,1,1,2,2,2,2,2,1,2,1")])
+
+(define_insn "movdf_internal2"
+ [(set (match_operand:DF 0 "nonimmediate_operand" "=d,d,d,R,To")
+ (match_operand:DF 1 "general_operand" "dG,R,ToF,d,d"))]
+ "(TARGET_SOFT_FLOAT || TARGET_SINGLE_FLOAT) && !TARGET_MIPS16
+ && (register_operand (operands[0], DFmode)
+ || register_operand (operands[1], DFmode)
+ || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0)
+ || operands[1] == CONST0_RTX (DFmode))"
+ "* return mips_move_2words (operands, insn); "
+ [(set_attr "type" "move,load,load,store,store")
+ (set_attr "mode" "DF")
+ (set_attr "length" "2,2,4,2,4")])
+
+(define_insn ""
+ [(set (match_operand:DF 0 "nonimmediate_operand" "=d,y,d,d,d,R,To")
+ (match_operand:DF 1 "general_operand" "d,d,y,R,ToF,d,d"))]
+ "TARGET_MIPS16
+ && (register_operand (operands[0], DFmode)
+ || register_operand (operands[1], DFmode))"
+ "* return mips_move_2words (operands, insn);"
+ [(set_attr "type" "move,move,move,load,load,store,store")
+ (set_attr "mode" "DF")
+ (set_attr "length" "2,2,2,2,4,2,4")])
+
+(define_split
+ [(set (match_operand:DF 0 "register_operand" "")
+ (match_operand:DF 1 "register_operand" ""))]
+ "reload_completed && !TARGET_64BIT && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE
+ && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0]))
+ && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1]))"
+ [(set (subreg:SI (match_dup 0) 0) (subreg:SI (match_dup 1) 0))
+ (set (subreg:SI (match_dup 0) 1) (subreg:SI (match_dup 1) 1))]
+ "")
+
+;; Instructions to load the global pointer register.
+;; This is volatile to make sure that the scheduler won't move any symbol_ref
+;; uses in front of it. All symbol_refs implicitly use the gp reg.
+
+(define_insn "loadgp"
+ [(set (reg:DI 28)
+ (unspec_volatile:DI [(match_operand:DI 0 "address_operand" "")
+ (match_operand:DI 1 "register_operand" "")] 2))
+ (clobber (reg:DI 1))]
+ ""
+ "%[lui\\t$1,%%hi(%%neg(%%gp_rel(%a0)))\\n\\taddiu\\t$1,$1,%%lo(%%neg(%%gp_rel(%a0)))\\n\\tdaddu\\t$gp,$1,%1%]"
+ [(set_attr "type" "move")
+ (set_attr "mode" "DI")
+ (set_attr "length" "3")])
+
+;; 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 "movstrsi"
+ [(parallel [(set (match_operand:BLK 0 "general_operand" "")
+ (match_operand:BLK 1 "general_operand" ""))
+ (use (match_operand:SI 2 "arith32_operand" ""))
+ (use (match_operand:SI 3 "immediate_operand" ""))])]
+ "!TARGET_MIPS16"
+ "
+{
+ if (operands[0]) /* avoid unused code messages */
+ {
+ expand_block_move (operands);
+ DONE;
+ }
+}")
+
+;; Insn generated by block moves
+
+(define_insn "movstrsi_internal"
+ [(set (match_operand:BLK 0 "memory_operand" "=o") ;; destination
+ (match_operand:BLK 1 "memory_operand" "o")) ;; source
+ (clobber (match_scratch:SI 4 "=&d")) ;; temp 1
+ (clobber (match_scratch:SI 5 "=&d")) ;; temp 2
+ (clobber (match_scratch:SI 6 "=&d")) ;; temp 3
+ (clobber (match_scratch:SI 7 "=&d")) ;; temp 4
+ (use (match_operand:SI 2 "small_int" "I")) ;; # bytes to move
+ (use (match_operand:SI 3 "small_int" "I")) ;; alignment
+ (use (const_int 0))] ;; normal block move
+ ""
+ "* return output_block_move (insn, operands, 4, BLOCK_MOVE_NORMAL);"
+ [(set_attr "type" "store")
+ (set_attr "mode" "none")
+ (set_attr "length" "20")])
+
+;; We need mips16 versions, because an offset from the stack pointer
+;; is not offsettable, since the stack pointer can only handle 4 and 8
+;; byte loads.
+
+(define_insn ""
+ [(set (match_operand:BLK 0 "memory_operand" "=d") ;; destination
+ (match_operand:BLK 1 "memory_operand" "d")) ;; source
+ (clobber (match_scratch:SI 4 "=&d")) ;; temp 1
+ (clobber (match_scratch:SI 5 "=&d")) ;; temp 2
+ (clobber (match_scratch:SI 6 "=&d")) ;; temp 3
+ (clobber (match_scratch:SI 7 "=&d")) ;; temp 4
+ (use (match_operand:SI 2 "small_int" "I")) ;; # bytes to move
+ (use (match_operand:SI 3 "small_int" "I")) ;; alignment
+ (use (const_int 0))] ;; normal block move
+ "TARGET_MIPS16"
+ "* return output_block_move (insn, operands, 4, BLOCK_MOVE_NORMAL);"
+ [(set_attr "type" "multi")
+ (set_attr "mode" "none")
+ (set_attr "length" "20")])
+
+(define_insn ""
+ [(set (match_operand:BLK 0 "memory_operand" "=d") ;; destination
+ (match_operand:BLK 1 "memory_operand" "o")) ;; source
+ (clobber (match_scratch:SI 4 "=&d")) ;; temp 1
+ (clobber (match_scratch:SI 5 "=&d")) ;; temp 2
+ (clobber (match_scratch:SI 6 "=&d")) ;; temp 3
+ (clobber (match_scratch:SI 7 "=&d")) ;; temp 4
+ (use (match_operand:SI 2 "small_int" "I")) ;; # bytes to move
+ (use (match_operand:SI 3 "small_int" "I")) ;; alignment
+ (use (const_int 0))] ;; normal block move
+ "TARGET_MIPS16"
+ "* return output_block_move (insn, operands, 4, BLOCK_MOVE_NORMAL);"
+ [(set_attr "type" "multi")
+ (set_attr "mode" "none")
+ (set_attr "length" "20")])
+
+(define_insn ""
+ [(set (match_operand:BLK 0 "memory_operand" "=o") ;; destination
+ (match_operand:BLK 1 "memory_operand" "d")) ;; source
+ (clobber (match_scratch:SI 4 "=&d")) ;; temp 1
+ (clobber (match_scratch:SI 5 "=&d")) ;; temp 2
+ (clobber (match_scratch:SI 6 "=&d")) ;; temp 3
+ (clobber (match_scratch:SI 7 "=&d")) ;; temp 4
+ (use (match_operand:SI 2 "small_int" "I")) ;; # bytes to move
+ (use (match_operand:SI 3 "small_int" "I")) ;; alignment
+ (use (const_int 0))] ;; normal block move
+ "TARGET_MIPS16"
+ "* return output_block_move (insn, operands, 4, BLOCK_MOVE_NORMAL);"
+ [(set_attr "type" "multi")
+ (set_attr "mode" "none")
+ (set_attr "length" "20")])
+
+;; Split a block move into 2 parts, the first part is everything
+;; except for the last move, and the second part is just the last
+;; store, which is exactly 1 instruction (ie, not a usw), so it can
+;; fill a delay slot. This also prevents a bug in delayed branches
+;; from showing up, which reuses one of the registers in our clobbers.
+
+(define_split
+ [(set (mem:BLK (match_operand:SI 0 "register_operand" ""))
+ (mem:BLK (match_operand:SI 1 "register_operand" "")))
+ (clobber (match_operand:SI 4 "register_operand" ""))
+ (clobber (match_operand:SI 5 "register_operand" ""))
+ (clobber (match_operand:SI 6 "register_operand" ""))
+ (clobber (match_operand:SI 7 "register_operand" ""))
+ (use (match_operand:SI 2 "small_int" ""))
+ (use (match_operand:SI 3 "small_int" ""))
+ (use (const_int 0))]
+
+ "reload_completed && !TARGET_DEBUG_D_MODE && INTVAL (operands[2]) > 0"
+
+ ;; All but the last move
+ [(parallel [(set (mem:BLK (match_dup 0))
+ (mem:BLK (match_dup 1)))
+ (clobber (match_dup 4))
+ (clobber (match_dup 5))
+ (clobber (match_dup 6))
+ (clobber (match_dup 7))
+ (use (match_dup 2))
+ (use (match_dup 3))
+ (use (const_int 1))])
+
+ ;; The last store, so it can fill a delay slot
+ (parallel [(set (mem:BLK (match_dup 0))
+ (mem:BLK (match_dup 1)))
+ (clobber (match_dup 4))
+ (clobber (match_dup 5))
+ (clobber (match_dup 6))
+ (clobber (match_dup 7))
+ (use (match_dup 2))
+ (use (match_dup 3))
+ (use (const_int 2))])]
+
+ "")
+
+(define_insn "movstrsi_internal2"
+ [(set (match_operand:BLK 0 "memory_operand" "=o") ;; destination
+ (match_operand:BLK 1 "memory_operand" "o")) ;; source
+ (clobber (match_scratch:SI 4 "=&d")) ;; temp 1
+ (clobber (match_scratch:SI 5 "=&d")) ;; temp 2
+ (clobber (match_scratch:SI 6 "=&d")) ;; temp 3
+ (clobber (match_scratch:SI 7 "=&d")) ;; temp 4
+ (use (match_operand:SI 2 "small_int" "I")) ;; # bytes to move
+ (use (match_operand:SI 3 "small_int" "I")) ;; alignment
+ (use (const_int 1))] ;; all but last store
+ ""
+ "* return output_block_move (insn, operands, 4, BLOCK_MOVE_NOT_LAST);"
+ [(set_attr "type" "store")
+ (set_attr "mode" "none")
+ (set_attr "length" "20")])
+
+(define_insn ""
+ [(set (match_operand:BLK 0 "memory_operand" "=d") ;; destination
+ (match_operand:BLK 1 "memory_operand" "d")) ;; source
+ (clobber (match_scratch:SI 4 "=&d")) ;; temp 1
+ (clobber (match_scratch:SI 5 "=&d")) ;; temp 2
+ (clobber (match_scratch:SI 6 "=&d")) ;; temp 3
+ (clobber (match_scratch:SI 7 "=&d")) ;; temp 4
+ (use (match_operand:SI 2 "small_int" "I")) ;; # bytes to move
+ (use (match_operand:SI 3 "small_int" "I")) ;; alignment
+ (use (const_int 1))] ;; all but last store
+ "TARGET_MIPS16"
+ "* return output_block_move (insn, operands, 4, BLOCK_MOVE_NOT_LAST);"
+ [(set_attr "type" "multi")
+ (set_attr "mode" "none")
+ (set_attr "length" "20")])
+
+(define_insn "movstrsi_internal3"
+ [(set (match_operand:BLK 0 "memory_operand" "=Ro") ;; destination
+ (match_operand:BLK 1 "memory_operand" "Ro")) ;; source
+ (clobber (match_scratch:SI 4 "=&d")) ;; temp 1
+ (clobber (match_scratch:SI 5 "=&d")) ;; temp 2
+ (clobber (match_scratch:SI 6 "=&d")) ;; temp 3
+ (clobber (match_scratch:SI 7 "=&d")) ;; temp 4
+ (use (match_operand:SI 2 "small_int" "I")) ;; # bytes to move
+ (use (match_operand:SI 3 "small_int" "I")) ;; alignment
+ (use (const_int 2))] ;; just last store of block move
+ ""
+ "* return output_block_move (insn, operands, 4, BLOCK_MOVE_LAST);"
+ [(set_attr "type" "store")
+ (set_attr "mode" "none")
+ (set_attr "length" "1")])
+
+(define_insn ""
+ [(set (match_operand:BLK 0 "memory_operand" "=d") ;; destination
+ (match_operand:BLK 1 "memory_operand" "d")) ;; source
+ (clobber (match_scratch:SI 4 "=&d")) ;; temp 1
+ (clobber (match_scratch:SI 5 "=&d")) ;; temp 2
+ (clobber (match_scratch:SI 6 "=&d")) ;; temp 3
+ (clobber (match_scratch:SI 7 "=&d")) ;; temp 4
+ (use (match_operand:SI 2 "small_int" "I")) ;; # bytes to move
+ (use (match_operand:SI 3 "small_int" "I")) ;; alignment
+ (use (const_int 2))] ;; just last store of block move
+ "TARGET_MIPS16"
+ "* return output_block_move (insn, operands, 4, BLOCK_MOVE_LAST);"
+ [(set_attr "type" "store")
+ (set_attr "mode" "none")
+ (set_attr "length" "1")])
+
+
+;; ....................
+;; SHIFTS
+;; ....................
+
+;; Many of these instructions uses trivial define_expands, because we
+;; want to use a different set of constraints when TARGET_MIPS16.
+
+(define_expand "ashlsi3"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (ashift:SI (match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "arith_operand" "dI")))]
+ ""
+ "
+{
+ /* 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 (SImode);
+
+ emit_insn (gen_ashlsi3_internal2 (temp, operands[1], GEN_INT (8)));
+ emit_insn (gen_ashlsi3_internal2 (operands[0], temp,
+ GEN_INT (INTVAL (operands[2]) - 8)));
+ DONE;
+ }
+}")
+
+(define_insn "ashlsi3_internal1"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (ashift:SI (match_operand:SI 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]) & 0x1f);
+
+ return \"sll\\t%0,%1,%2\";
+}"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1")])
+
+(define_insn "ashlsi3_internal2"
+ [(set (match_operand:SI 0 "register_operand" "=d,d")
+ (ashift:SI (match_operand:SI 1 "register_operand" "0,d")
+ (match_operand:SI 2 "arith_operand" "d,I")))]
+ "TARGET_MIPS16"
+ "*
+{
+ if (which_alternative == 0)
+ return \"sll\\t%0,%2\";
+
+ if (GET_CODE (operands[2]) == CONST_INT)
+ operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);
+
+ return \"sll\\t%0,%1,%2\";
+}"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")
+ (set_attr_alternative "length"
+ [(const_int 1)
+ (if_then_else (match_operand:VOID 2 "m16_uimm3_b" "")
+ (const_int 1)
+ (const_int 2))])])
+
+;; On the mips16, we can split a 4 byte shift into 2 2 byte shifts.
+
+(define_split
+ [(set (match_operand:SI 0 "register_operand" "")
+ (ashift:SI (match_operand:SI 1 "register_operand" "")
+ (match_operand:SI 2 "const_int_operand" "")))]
+ "TARGET_MIPS16
+ && reload_completed
+ && GET_CODE (operands[2]) == CONST_INT
+ && INTVAL (operands[2]) > 8
+ && INTVAL (operands[2]) <= 16"
+ [(set (match_dup 0) (ashift:SI (match_dup 1) (const_int 8)))
+ (set (match_dup 0) (ashift:SI (match_dup 0) (match_dup 2)))]
+"
+{
+ operands[2] = GEN_INT (INTVAL (operands[2]) - 8);
+}")
+
+(define_expand "ashldi3"
+ [(parallel [(set (match_operand:DI 0 "register_operand" "")
+ (ashift:DI (match_operand:DI 1 "se_register_operand" "")
+ (match_operand:SI 2 "arith_operand" "")))
+ (clobber (match_dup 3))])]
+ "TARGET_64BIT || (!TARGET_DEBUG_G_MODE && !TARGET_MIPS16)"
+ "
+{
+ if (TARGET_64BIT)
+ {
+ /* 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 (DImode);
+
+ emit_insn (gen_ashldi3_internal4 (temp, operands[1], GEN_INT (8)));
+ emit_insn (gen_ashldi3_internal4 (operands[0], temp,
+ GEN_INT (INTVAL (operands[2]) - 8)));
+ DONE;
+ }
+
+ emit_insn (gen_ashldi3_internal4 (operands[0], operands[1],
+ operands[2]));
+ DONE;
+ }
+
+ operands[3] = gen_reg_rtx (SImode);
+}")
+
+
+(define_insn "ashldi3_internal"
+ [(set (match_operand:DI 0 "register_operand" "=&d")
+ (ashift:DI (match_operand:DI 1 "register_operand" "d")
+ (match_operand:SI 2 "register_operand" "d")))
+ (clobber (match_operand:SI 3 "register_operand" "=d"))]
+ "!TARGET_64BIT && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16"
+ "*
+{
+ operands[4] = const0_rtx;
+ dslots_jump_total += 3;
+ dslots_jump_filled += 2;
+
+ return \"sll\\t%3,%2,26\\n\\
+\\tbgez\\t%3,1f\\n\\
+\\tsll\\t%M0,%L1,%2\\n\\
+\\t%(b\\t3f\\n\\
+\\tmove\\t%L0,%z4%)\\n\\
+\\n\\
+1:\\n\\
+\\t%(beq\\t%3,%z4,2f\\n\\
+\\tsll\\t%M0,%M1,%2%)\\n\\
+\\n\\
+\\tsubu\\t%3,%z4,%2\\n\\
+\\tsrl\\t%3,%L1,%3\\n\\
+\\tor\\t%M0,%M0,%3\\n\\
+2:\\n\\
+\\tsll\\t%L0,%L1,%2\\n\\
+3:\";
+}"
+ [(set_attr "type" "darith")
+ (set_attr "mode" "SI")
+ (set_attr "length" "12")])
+
+
+(define_insn "ashldi3_internal2"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (ashift:DI (match_operand:DI 1 "register_operand" "d")
+ (match_operand:SI 2 "small_int" "IJK")))
+ (clobber (match_operand:SI 3 "register_operand" "=d"))]
+ "!TARGET_64BIT && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
+ && (INTVAL (operands[2]) & 32) != 0"
+ "*
+{
+ operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);
+ operands[4] = const0_rtx;
+ return \"sll\\t%M0,%L1,%2\;move\\t%L0,%z4\";
+}"
+ [(set_attr "type" "darith")
+ (set_attr "mode" "DI")
+ (set_attr "length" "2")])
+
+
+(define_split
+ [(set (match_operand:DI 0 "register_operand" "")
+ (ashift:DI (match_operand:DI 1 "register_operand" "")
+ (match_operand:SI 2 "small_int" "")))
+ (clobber (match_operand:SI 3 "register_operand" ""))]
+ "reload_completed && !WORDS_BIG_ENDIAN && !TARGET_64BIT
+ && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
+ && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER
+ && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER
+ && (INTVAL (operands[2]) & 32) != 0"
+
+ [(set (subreg:SI (match_dup 0) 1) (ashift:SI (subreg:SI (match_dup 1) 0) (match_dup 2)))
+ (set (subreg:SI (match_dup 0) 0) (const_int 0))]
+
+ "operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);")
+
+
+(define_split
+ [(set (match_operand:DI 0 "register_operand" "")
+ (ashift:DI (match_operand:DI 1 "register_operand" "")
+ (match_operand:SI 2 "small_int" "")))
+ (clobber (match_operand:SI 3 "register_operand" ""))]
+ "reload_completed && WORDS_BIG_ENDIAN && !TARGET_64BIT
+ && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
+ && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER
+ && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER
+ && (INTVAL (operands[2]) & 32) != 0"
+
+ [(set (subreg:SI (match_dup 0) 0) (ashift:SI (subreg:SI (match_dup 1) 1) (match_dup 2)))
+ (set (subreg:SI (match_dup 0) 1) (const_int 0))]
+
+ "operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);")
+
+
+(define_insn "ashldi3_internal3"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (ashift:DI (match_operand:DI 1 "register_operand" "d")
+ (match_operand:SI 2 "small_int" "IJK")))
+ (clobber (match_operand:SI 3 "register_operand" "=d"))]
+ "!TARGET_64BIT && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
+ && (INTVAL (operands[2]) & 63) < 32
+ && (INTVAL (operands[2]) & 63) != 0"
+ "*
+{
+ int amount = INTVAL (operands[2]);
+
+ operands[2] = GEN_INT ((amount & 31));
+ operands[4] = const0_rtx;
+ operands[5] = GEN_INT (((-amount) & 31));
+
+ return \"sll\\t%M0,%M1,%2\;srl\\t%3,%L1,%5\;or\\t%M0,%M0,%3\;sll\\t%L0,%L1,%2\";
+}"
+ [(set_attr "type" "darith")
+ (set_attr "mode" "DI")
+ (set_attr "length" "4")])
+
+
+(define_split
+ [(set (match_operand:DI 0 "register_operand" "")
+ (ashift:DI (match_operand:DI 1 "register_operand" "")
+ (match_operand:SI 2 "small_int" "")))
+ (clobber (match_operand:SI 3 "register_operand" ""))]
+ "reload_completed && !WORDS_BIG_ENDIAN && !TARGET_64BIT
+ && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
+ && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER
+ && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER
+ && (INTVAL (operands[2]) & 63) < 32
+ && (INTVAL (operands[2]) & 63) != 0"
+
+ [(set (subreg:SI (match_dup 0) 1)
+ (ashift:SI (subreg:SI (match_dup 1) 1)
+ (match_dup 2)))
+
+ (set (match_dup 3)
+ (lshiftrt:SI (subreg:SI (match_dup 1) 0)
+ (match_dup 4)))
+
+ (set (subreg:SI (match_dup 0) 1)
+ (ior:SI (subreg:SI (match_dup 0) 1)
+ (match_dup 3)))
+
+ (set (subreg:SI (match_dup 0) 0)
+ (ashift:SI (subreg:SI (match_dup 1) 0)
+ (match_dup 2)))]
+ "
+{
+ int amount = INTVAL (operands[2]);
+ operands[2] = GEN_INT ((amount & 31));
+ operands[4] = GEN_INT (((-amount) & 31));
+}")
+
+
+(define_split
+ [(set (match_operand:DI 0 "register_operand" "")
+ (ashift:DI (match_operand:DI 1 "register_operand" "")
+ (match_operand:SI 2 "small_int" "")))
+ (clobber (match_operand:SI 3 "register_operand" ""))]
+ "reload_completed && WORDS_BIG_ENDIAN && !TARGET_64BIT
+ && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
+ && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER
+ && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER
+ && (INTVAL (operands[2]) & 63) < 32
+ && (INTVAL (operands[2]) & 63) != 0"
+
+ [(set (subreg:SI (match_dup 0) 0)
+ (ashift:SI (subreg:SI (match_dup 1) 0)
+ (match_dup 2)))
+
+ (set (match_dup 3)
+ (lshiftrt:SI (subreg:SI (match_dup 1) 1)
+ (match_dup 4)))
+
+ (set (subreg:SI (match_dup 0) 0)
+ (ior:SI (subreg:SI (match_dup 0) 0)
+ (match_dup 3)))
+
+ (set (subreg:SI (match_dup 0) 1)
+ (ashift:SI (subreg:SI (match_dup 1) 1)
+ (match_dup 2)))]
+ "
+{
+ int amount = INTVAL (operands[2]);
+ operands[2] = GEN_INT ((amount & 31));
+ operands[4] = GEN_INT (((-amount) & 31));
+}")
+
+
+(define_insn "ashldi3_internal4"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (ashift:DI (match_operand:DI 1 "se_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]) & 0x3f);
+
+ return \"dsll\\t%0,%1,%2\";
+}"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "DI")
+ (set_attr "length" "1")])
+
+(define_insn ""
+ [(set (match_operand:DI 0 "register_operand" "=d,d")
+ (ashift:DI (match_operand:DI 1 "se_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\";
+
+ if (GET_CODE (operands[2]) == CONST_INT)
+ operands[2] = GEN_INT (INTVAL (operands[2]) & 0x3f);
+
+ return \"dsll\\t%0,%1,%2\";
+}"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "DI")
+ (set_attr_alternative "length"
+ [(const_int 1)
+ (if_then_else (match_operand:VOID 2 "m16_uimm3_b" "")
+ (const_int 1)
+ (const_int 2))])])
+
+
+;; On the mips16, we can split a 4 byte shift into 2 2 byte shifts.
+
+(define_split
+ [(set (match_operand:DI 0 "register_operand" "")
+ (ashift:DI (match_operand:DI 1 "register_operand" "")
+ (match_operand:SI 2 "const_int_operand" "")))]
+ "TARGET_MIPS16 && TARGET_64BIT
+ && reload_completed
+ && GET_CODE (operands[2]) == CONST_INT
+ && INTVAL (operands[2]) > 8
+ && INTVAL (operands[2]) <= 16"
+ [(set (match_dup 0) (ashift:DI (match_dup 1) (const_int 8)))
+ (set (match_dup 0) (ashift:DI (match_dup 0) (match_dup 2)))]
+"
+{
+ operands[2] = GEN_INT (INTVAL (operands[2]) - 8);
+}")
+
+(define_expand "ashrsi3"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (ashiftrt:SI (match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "arith_operand" "dI")))]
+ ""
+ "
+{
+ /* 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. */
+ if (TARGET_MIPS16
+ && optimize
+ && GET_CODE (operands[2]) == CONST_INT
+ && INTVAL (operands[2]) > 8
+ && INTVAL (operands[2]) <= 16)
+ {
+ rtx temp = gen_reg_rtx (SImode);
+
+ emit_insn (gen_ashrsi3_internal2 (temp, operands[1], GEN_INT (8)));
+ emit_insn (gen_ashrsi3_internal2 (operands[0], temp,
+ GEN_INT (INTVAL (operands[2]) - 8)));
+ DONE;
+ }
+}")
+
+(define_insn "ashrsi3_internal1"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (ashiftrt:SI (match_operand:SI 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]) & 0x1f);
+
+ return \"sra\\t%0,%1,%2\";
+}"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1")])
+
+(define_insn "ashrsi3_internal2"
+ [(set (match_operand:SI 0 "register_operand" "=d,d")
+ (ashiftrt:SI (match_operand:SI 1 "register_operand" "0,d")
+ (match_operand:SI 2 "arith_operand" "d,I")))]
+ "TARGET_MIPS16"
+ "*
+{
+ if (which_alternative == 0)
+ return \"sra\\t%0,%2\";
+
+ if (GET_CODE (operands[2]) == CONST_INT)
+ operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);
+
+ return \"sra\\t%0,%1,%2\";
+}"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")
+ (set_attr_alternative "length"
+ [(const_int 1)
+ (if_then_else (match_operand:VOID 2 "m16_uimm3_b" "")
+ (const_int 1)
+ (const_int 2))])])
+
+
+;; On the mips16, we can split a 4 byte shift into 2 2 byte shifts.
+
+(define_split
+ [(set (match_operand:SI 0 "register_operand" "")
+ (ashiftrt:SI (match_operand:SI 1 "register_operand" "")
+ (match_operand:SI 2 "const_int_operand" "")))]
+ "TARGET_MIPS16
+ && reload_completed
+ && GET_CODE (operands[2]) == CONST_INT
+ && INTVAL (operands[2]) > 8
+ && INTVAL (operands[2]) <= 16"
+ [(set (match_dup 0) (ashiftrt:SI (match_dup 1) (const_int 8)))
+ (set (match_dup 0) (ashiftrt:SI (match_dup 0) (match_dup 2)))]
+"
+{
+ operands[2] = GEN_INT (INTVAL (operands[2]) - 8);
+}")
+
+(define_expand "ashrdi3"
+ [(parallel [(set (match_operand:DI 0 "register_operand" "")
+ (ashiftrt:DI (match_operand:DI 1 "se_register_operand" "")
+ (match_operand:SI 2 "arith_operand" "")))
+ (clobber (match_dup 3))])]
+ "TARGET_64BIT || (!TARGET_DEBUG_G_MODE && !TARGET_MIPS16)"
+ "
+{
+ if (TARGET_64BIT)
+ {
+ /* 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. */
+ if (TARGET_MIPS16
+ && optimize
+ && GET_CODE (operands[2]) == CONST_INT
+ && INTVAL (operands[2]) > 8
+ && INTVAL (operands[2]) <= 16)
+ {
+ rtx temp = gen_reg_rtx (DImode);
+
+ emit_insn (gen_ashrdi3_internal4 (temp, operands[1], GEN_INT (8)));
+ emit_insn (gen_ashrdi3_internal4 (operands[0], temp,
+ GEN_INT (INTVAL (operands[2]) - 8)));
+ DONE;
+ }
+
+ emit_insn (gen_ashrdi3_internal4 (operands[0], operands[1],
+ operands[2]));
+ DONE;
+ }
+
+ operands[3] = gen_reg_rtx (SImode);
+}")
+
+
+(define_insn "ashrdi3_internal"
+ [(set (match_operand:DI 0 "register_operand" "=&d")
+ (ashiftrt:DI (match_operand:DI 1 "register_operand" "d")
+ (match_operand:SI 2 "register_operand" "d")))
+ (clobber (match_operand:SI 3 "register_operand" "=d"))]
+ "!TARGET_64BIT && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16"
+ "*
+{
+ operands[4] = const0_rtx;
+ dslots_jump_total += 3;
+ dslots_jump_filled += 2;
+
+ return \"sll\\t%3,%2,26\\n\\
+\\tbgez\\t%3,1f\\n\\
+\\tsra\\t%L0,%M1,%2\\n\\
+\\t%(b\\t3f\\n\\
+\\tsra\\t%M0,%M1,31%)\\n\\
+\\n\\
+1:\\n\\
+\\t%(beq\\t%3,%z4,2f\\n\\
+\\tsrl\\t%L0,%L1,%2%)\\n\\
+\\n\\
+\\tsubu\\t%3,%z4,%2\\n\\
+\\tsll\\t%3,%M1,%3\\n\\
+\\tor\\t%L0,%L0,%3\\n\\
+2:\\n\\
+\\tsra\\t%M0,%M1,%2\\n\\
+3:\";
+}"
+ [(set_attr "type" "darith")
+ (set_attr "mode" "DI")
+ (set_attr "length" "12")])
+
+
+(define_insn "ashrdi3_internal2"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (ashiftrt:DI (match_operand:DI 1 "register_operand" "d")
+ (match_operand:SI 2 "small_int" "IJK")))
+ (clobber (match_operand:SI 3 "register_operand" "=d"))]
+ "!TARGET_64BIT && !TARGET_DEBUG_G_MODE && (INTVAL (operands[2]) & 32) != 0"
+ "*
+{
+ operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);
+ return \"sra\\t%L0,%M1,%2\;sra\\t%M0,%M1,31\";
+}"
+ [(set_attr "type" "darith")
+ (set_attr "mode" "DI")
+ (set_attr "length" "2")])
+
+
+(define_split
+ [(set (match_operand:DI 0 "register_operand" "")
+ (ashiftrt:DI (match_operand:DI 1 "register_operand" "")
+ (match_operand:SI 2 "small_int" "")))
+ (clobber (match_operand:SI 3 "register_operand" ""))]
+ "reload_completed && !WORDS_BIG_ENDIAN && !TARGET_64BIT && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE
+ && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER
+ && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER
+ && (INTVAL (operands[2]) & 32) != 0"
+
+ [(set (subreg:SI (match_dup 0) 0) (ashiftrt:SI (subreg:SI (match_dup 1) 1) (match_dup 2)))
+ (set (subreg:SI (match_dup 0) 1) (ashiftrt:SI (subreg:SI (match_dup 1) 1) (const_int 31)))]
+
+ "operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);")
+
+
+(define_split
+ [(set (match_operand:DI 0 "register_operand" "")
+ (ashiftrt:DI (match_operand:DI 1 "register_operand" "")
+ (match_operand:SI 2 "small_int" "")))
+ (clobber (match_operand:SI 3 "register_operand" ""))]
+ "reload_completed && WORDS_BIG_ENDIAN && !TARGET_64BIT && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE
+ && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER
+ && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER
+ && (INTVAL (operands[2]) & 32) != 0"
+
+ [(set (subreg:SI (match_dup 0) 1) (ashiftrt:SI (subreg:SI (match_dup 1) 0) (match_dup 2)))
+ (set (subreg:SI (match_dup 0) 0) (ashiftrt:SI (subreg:SI (match_dup 1) 0) (const_int 31)))]
+
+ "operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);")
+
+
+(define_insn "ashrdi3_internal3"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (ashiftrt:DI (match_operand:DI 1 "register_operand" "d")
+ (match_operand:SI 2 "small_int" "IJK")))
+ (clobber (match_operand:SI 3 "register_operand" "=d"))]
+ "!TARGET_64BIT && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
+ && (INTVAL (operands[2]) & 63) < 32
+ && (INTVAL (operands[2]) & 63) != 0"
+ "*
+{
+ int amount = INTVAL (operands[2]);
+
+ operands[2] = GEN_INT ((amount & 31));
+ operands[4] = GEN_INT (((-amount) & 31));
+
+ return \"srl\\t%L0,%L1,%2\;sll\\t%3,%M1,%4\;or\\t%L0,%L0,%3\;sra\\t%M0,%M1,%2\";
+}"
+ [(set_attr "type" "darith")
+ (set_attr "mode" "DI")
+ (set_attr "length" "4")])
+
+
+(define_split
+ [(set (match_operand:DI 0 "register_operand" "")
+ (ashiftrt:DI (match_operand:DI 1 "register_operand" "")
+ (match_operand:SI 2 "small_int" "")))
+ (clobber (match_operand:SI 3 "register_operand" ""))]
+ "reload_completed && !WORDS_BIG_ENDIAN && !TARGET_64BIT
+ && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
+ && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER
+ && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER
+ && (INTVAL (operands[2]) & 63) < 32
+ && (INTVAL (operands[2]) & 63) != 0"
+
+ [(set (subreg:SI (match_dup 0) 0)
+ (lshiftrt:SI (subreg:SI (match_dup 1) 0)
+ (match_dup 2)))
+
+ (set (match_dup 3)
+ (ashift:SI (subreg:SI (match_dup 1) 1)
+ (match_dup 4)))
+
+ (set (subreg:SI (match_dup 0) 0)
+ (ior:SI (subreg:SI (match_dup 0) 0)
+ (match_dup 3)))
+
+ (set (subreg:SI (match_dup 0) 1)
+ (ashiftrt:SI (subreg:SI (match_dup 1) 1)
+ (match_dup 2)))]
+ "
+{
+ int amount = INTVAL (operands[2]);
+ operands[2] = GEN_INT ((amount & 31));
+ operands[4] = GEN_INT (((-amount) & 31));
+}")
+
+
+(define_split
+ [(set (match_operand:DI 0 "register_operand" "")
+ (ashiftrt:DI (match_operand:DI 1 "register_operand" "")
+ (match_operand:SI 2 "small_int" "")))
+ (clobber (match_operand:SI 3 "register_operand" ""))]
+ "reload_completed && WORDS_BIG_ENDIAN && !TARGET_64BIT
+ && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
+ && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER
+ && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER
+ && (INTVAL (operands[2]) & 63) < 32
+ && (INTVAL (operands[2]) & 63) != 0"
+
+ [(set (subreg:SI (match_dup 0) 1)
+ (lshiftrt:SI (subreg:SI (match_dup 1) 1)
+ (match_dup 2)))
+
+ (set (match_dup 3)
+ (ashift:SI (subreg:SI (match_dup 1) 0)
+ (match_dup 4)))
+
+ (set (subreg:SI (match_dup 0) 1)
+ (ior:SI (subreg:SI (match_dup 0) 1)
+ (match_dup 3)))
+
+ (set (subreg:SI (match_dup 0) 0)
+ (ashiftrt:SI (subreg:SI (match_dup 1) 0)
+ (match_dup 2)))]
+ "
+{
+ int amount = INTVAL (operands[2]);
+ operands[2] = GEN_INT ((amount & 31));
+ operands[4] = GEN_INT (((-amount) & 31));
+}")
+
+
+(define_insn "ashrdi3_internal4"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (ashiftrt:DI (match_operand:DI 1 "se_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]) & 0x3f);
+
+ return \"dsra\\t%0,%1,%2\";
+}"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "DI")
+ (set_attr "length" "1")])
+
+(define_insn ""
+ [(set (match_operand:DI 0 "register_operand" "=d,d")
+ (ashiftrt:DI (match_operand:DI 1 "se_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" "arith")
+ (set_attr "mode" "DI")
+ (set_attr_alternative "length"
+ [(const_int 1)
+ (if_then_else (match_operand:VOID 2 "m16_uimm3_b" "")
+ (const_int 1)
+ (const_int 2))])])
+
+;; On the mips16, we can split a 4 byte shift into 2 2 byte shifts.
+
+(define_split
+ [(set (match_operand:DI 0 "register_operand" "")
+ (ashiftrt:DI (match_operand:DI 1 "register_operand" "")
+ (match_operand:SI 2 "const_int_operand" "")))]
+ "TARGET_MIPS16 && TARGET_64BIT
+ && reload_completed
+ && GET_CODE (operands[2]) == CONST_INT
+ && INTVAL (operands[2]) > 8
+ && INTVAL (operands[2]) <= 16"
+ [(set (match_dup 0) (ashiftrt:DI (match_dup 1) (const_int 8)))
+ (set (match_dup 0) (ashiftrt:DI (match_dup 0) (match_dup 2)))]
+"
+{
+ operands[2] = GEN_INT (INTVAL (operands[2]) - 8);
+}")
+
+(define_expand "lshrsi3"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (lshiftrt:SI (match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "arith_operand" "dI")))]
+ ""
+ "
+{
+ /* 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. */
+ if (TARGET_MIPS16
+ && optimize
+ && GET_CODE (operands[2]) == CONST_INT
+ && INTVAL (operands[2]) > 8
+ && INTVAL (operands[2]) <= 16)
+ {
+ rtx temp = gen_reg_rtx (SImode);
+
+ emit_insn (gen_lshrsi3_internal2 (temp, operands[1], GEN_INT (8)));
+ emit_insn (gen_lshrsi3_internal2 (operands[0], temp,
+ GEN_INT (INTVAL (operands[2]) - 8)));
+ DONE;
+ }
+}")
+
+(define_insn "lshrsi3_internal1"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (lshiftrt:SI (match_operand:SI 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]) & 0x1f);
+
+ return \"srl\\t%0,%1,%2\";
+}"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1")])
+
+(define_insn "lshrsi3_internal2"
+ [(set (match_operand:SI 0 "register_operand" "=d,d")
+ (lshiftrt:SI (match_operand:SI 1 "register_operand" "0,d")
+ (match_operand:SI 2 "arith_operand" "d,I")))]
+ "TARGET_MIPS16"
+ "*
+{
+ if (which_alternative == 0)
+ return \"srl\\t%0,%2\";
+
+ if (GET_CODE (operands[2]) == CONST_INT)
+ operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);
+
+ return \"srl\\t%0,%1,%2\";
+}"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")
+ (set_attr_alternative "length"
+ [(const_int 1)
+ (if_then_else (match_operand:VOID 2 "m16_uimm3_b" "")
+ (const_int 1)
+ (const_int 2))])])
+
+
+;; On the mips16, we can split a 4 byte shift into 2 2 byte shifts.
+
+(define_split
+ [(set (match_operand:SI 0 "register_operand" "")
+ (lshiftrt:SI (match_operand:SI 1 "register_operand" "")
+ (match_operand:SI 2 "const_int_operand" "")))]
+ "TARGET_MIPS16
+ && reload_completed
+ && GET_CODE (operands[2]) == CONST_INT
+ && INTVAL (operands[2]) > 8
+ && INTVAL (operands[2]) <= 16"
+ [(set (match_dup 0) (lshiftrt:SI (match_dup 1) (const_int 8)))
+ (set (match_dup 0) (lshiftrt:SI (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.
+
+(define_insn ""
+ [(set (match_operand:SI 0 "register_operand" "=d,d")
+ (lshiftrt:SI (match_operand:SI 1 "memory_operand" "R,m")
+ (match_operand:SI 2 "immediate_operand" "I,I")))]
+ "TARGET_MIPS16"
+ "lw\\t%0,%1\;srl\\t%0,%2"
+ [(set_attr "type" "load")
+ (set_attr "mode" "SI")
+ (set_attr_alternative "length"
+ [(if_then_else (match_operand:VOID 2 "m16_uimm3_b" "")
+ (const_int 2)
+ (const_int 3))
+ (if_then_else (match_operand:VOID 2 "m16_uimm3_b" "")
+ (const_int 3)
+ (const_int 4))])])
+
+(define_split
+ [(set (match_operand:SI 0 "register_operand" "")
+ (lshiftrt:SI (match_operand:SI 1 "memory_operand" "")
+ (match_operand:SI 2 "immediate_operand" "")))]
+ "TARGET_MIPS16"
+ [(set (match_dup 0) (match_dup 1))
+ (set (match_dup 0) (lshiftrt:SI (match_dup 0) (match_dup 2)))]
+ "")
+
+(define_expand "lshrdi3"
+ [(parallel [(set (match_operand:DI 0 "register_operand" "")
+ (lshiftrt:DI (match_operand:DI 1 "se_register_operand" "")
+ (match_operand:SI 2 "arith_operand" "")))
+ (clobber (match_dup 3))])]
+ "TARGET_64BIT || (!TARGET_DEBUG_G_MODE && !TARGET_MIPS16)"
+ "
+{
+ if (TARGET_64BIT)
+ {
+ /* 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. */
+ if (TARGET_MIPS16
+ && optimize
+ && GET_CODE (operands[2]) == CONST_INT
+ && INTVAL (operands[2]) > 8
+ && INTVAL (operands[2]) <= 16)
+ {
+ rtx temp = gen_reg_rtx (DImode);
+
+ emit_insn (gen_lshrdi3_internal4 (temp, operands[1], GEN_INT (8)));
+ emit_insn (gen_lshrdi3_internal4 (operands[0], temp,
+ GEN_INT (INTVAL (operands[2]) - 8)));
+ DONE;
+ }
+
+ emit_insn (gen_lshrdi3_internal4 (operands[0], operands[1],
+ operands[2]));
+ DONE;
+ }
+
+ operands[3] = gen_reg_rtx (SImode);
+}")
+
+
+(define_insn "lshrdi3_internal"
+ [(set (match_operand:DI 0 "register_operand" "=&d")
+ (lshiftrt:DI (match_operand:DI 1 "register_operand" "d")
+ (match_operand:SI 2 "register_operand" "d")))
+ (clobber (match_operand:SI 3 "register_operand" "=d"))]
+ "!TARGET_64BIT && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16"
+ "*
+{
+ operands[4] = const0_rtx;
+ dslots_jump_total += 3;
+ dslots_jump_filled += 2;
+
+ return \"sll\\t%3,%2,26\\n\\
+\\tbgez\\t%3,1f\\n\\
+\\tsrl\\t%L0,%M1,%2\\n\\
+\\t%(b\\t3f\\n\\
+\\tmove\\t%M0,%z4%)\\n\\
+\\n\\
+1:\\n\\
+\\t%(beq\\t%3,%z4,2f\\n\\
+\\tsrl\\t%L0,%L1,%2%)\\n\\
+\\n\\
+\\tsubu\\t%3,%z4,%2\\n\\
+\\tsll\\t%3,%M1,%3\\n\\
+\\tor\\t%L0,%L0,%3\\n\\
+2:\\n\\
+\\tsrl\\t%M0,%M1,%2\\n\\
+3:\";
+}"
+ [(set_attr "type" "darith")
+ (set_attr "mode" "DI")
+ (set_attr "length" "12")])
+
+
+(define_insn "lshrdi3_internal2"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (lshiftrt:DI (match_operand:DI 1 "register_operand" "d")
+ (match_operand:SI 2 "small_int" "IJK")))
+ (clobber (match_operand:SI 3 "register_operand" "=d"))]
+ "!TARGET_64BIT && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
+ && (INTVAL (operands[2]) & 32) != 0"
+ "*
+{
+ operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);
+ operands[4] = const0_rtx;
+ return \"srl\\t%L0,%M1,%2\;move\\t%M0,%z4\";
+}"
+ [(set_attr "type" "darith")
+ (set_attr "mode" "DI")
+ (set_attr "length" "2")])
+
+
+(define_split
+ [(set (match_operand:DI 0 "register_operand" "")
+ (lshiftrt:DI (match_operand:DI 1 "register_operand" "")
+ (match_operand:SI 2 "small_int" "")))
+ (clobber (match_operand:SI 3 "register_operand" ""))]
+ "reload_completed && !WORDS_BIG_ENDIAN && !TARGET_64BIT
+ && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
+ && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER
+ && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER
+ && (INTVAL (operands[2]) & 32) != 0"
+
+ [(set (subreg:SI (match_dup 0) 0) (lshiftrt:SI (subreg:SI (match_dup 1) 1) (match_dup 2)))
+ (set (subreg:SI (match_dup 0) 1) (const_int 0))]
+
+ "operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);")
+
+
+(define_split
+ [(set (match_operand:DI 0 "register_operand" "")
+ (lshiftrt:DI (match_operand:DI 1 "register_operand" "")
+ (match_operand:SI 2 "small_int" "")))
+ (clobber (match_operand:SI 3 "register_operand" ""))]
+ "reload_completed && WORDS_BIG_ENDIAN && !TARGET_64BIT
+ && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
+ && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER
+ && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER
+ && (INTVAL (operands[2]) & 32) != 0"
+
+ [(set (subreg:SI (match_dup 0) 1) (lshiftrt:SI (subreg:SI (match_dup 1) 0) (match_dup 2)))
+ (set (subreg:SI (match_dup 0) 0) (const_int 0))]
+
+ "operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);")
+
+
+(define_insn "lshrdi3_internal3"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (lshiftrt:DI (match_operand:DI 1 "register_operand" "d")
+ (match_operand:SI 2 "small_int" "IJK")))
+ (clobber (match_operand:SI 3 "register_operand" "=d"))]
+ "!TARGET_64BIT && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
+ && (INTVAL (operands[2]) & 63) < 32
+ && (INTVAL (operands[2]) & 63) != 0"
+ "*
+{
+ int amount = INTVAL (operands[2]);
+
+ operands[2] = GEN_INT ((amount & 31));
+ operands[4] = GEN_INT (((-amount) & 31));
+
+ return \"srl\\t%L0,%L1,%2\;sll\\t%3,%M1,%4\;or\\t%L0,%L0,%3\;srl\\t%M0,%M1,%2\";
+}"
+ [(set_attr "type" "darith")
+ (set_attr "mode" "DI")
+ (set_attr "length" "4")])
+
+
+(define_split
+ [(set (match_operand:DI 0 "register_operand" "")
+ (lshiftrt:DI (match_operand:DI 1 "register_operand" "")
+ (match_operand:SI 2 "small_int" "")))
+ (clobber (match_operand:SI 3 "register_operand" ""))]
+ "reload_completed && !WORDS_BIG_ENDIAN && !TARGET_64BIT
+ && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
+ && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER
+ && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER
+ && (INTVAL (operands[2]) & 63) < 32
+ && (INTVAL (operands[2]) & 63) != 0"
+
+ [(set (subreg:SI (match_dup 0) 0)
+ (lshiftrt:SI (subreg:SI (match_dup 1) 0)
+ (match_dup 2)))
+
+ (set (match_dup 3)
+ (ashift:SI (subreg:SI (match_dup 1) 1)
+ (match_dup 4)))
+
+ (set (subreg:SI (match_dup 0) 0)
+ (ior:SI (subreg:SI (match_dup 0) 0)
+ (match_dup 3)))
+
+ (set (subreg:SI (match_dup 0) 1)
+ (lshiftrt:SI (subreg:SI (match_dup 1) 1)
+ (match_dup 2)))]
+ "
+{
+ int amount = INTVAL (operands[2]);
+ operands[2] = GEN_INT ((amount & 31));
+ operands[4] = GEN_INT (((-amount) & 31));
+}")
+
+
+(define_split
+ [(set (match_operand:DI 0 "register_operand" "")
+ (lshiftrt:DI (match_operand:DI 1 "register_operand" "")
+ (match_operand:SI 2 "small_int" "")))
+ (clobber (match_operand:SI 3 "register_operand" ""))]
+ "reload_completed && WORDS_BIG_ENDIAN && !TARGET_64BIT
+ && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
+ && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER
+ && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER
+ && (INTVAL (operands[2]) & 63) < 32
+ && (INTVAL (operands[2]) & 63) != 0"
+
+ [(set (subreg:SI (match_dup 0) 1)
+ (lshiftrt:SI (subreg:SI (match_dup 1) 1)
+ (match_dup 2)))
+
+ (set (match_dup 3)
+ (ashift:SI (subreg:SI (match_dup 1) 0)
+ (match_dup 4)))
+
+ (set (subreg:SI (match_dup 0) 1)
+ (ior:SI (subreg:SI (match_dup 0) 1)
+ (match_dup 3)))
+
+ (set (subreg:SI (match_dup 0) 0)
+ (lshiftrt:SI (subreg:SI (match_dup 1) 0)
+ (match_dup 2)))]
+ "
+{
+ int amount = INTVAL (operands[2]);
+ operands[2] = GEN_INT ((amount & 31));
+ operands[4] = GEN_INT (((-amount) & 31));
+}")
+
+
+(define_insn "lshrdi3_internal4"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (lshiftrt:DI (match_operand:DI 1 "se_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]) & 0x3f);
+
+ return \"dsrl\\t%0,%1,%2\";
+}"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "DI")
+ (set_attr "length" "1")])
+
+;; CYGNUS LOCAL vr5400/raeburn
+(define_insn "rotrsi3"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (rotatert:SI (match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "arith_operand" "dn")))]
+ "TARGET_MIPS5400"
+ "*
+{
+ if (GET_CODE (operands[2]) == CONST_INT)
+ operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);
+
+ return \"ror\\t%0,%1,%2\";
+}"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+
+(define_insn "rotrdi3"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (rotatert:DI (match_operand:DI 1 "register_operand" "d")
+ (match_operand:DI 2 "arith_operand" "dn")))]
+ "TARGET_MIPS5400 && TARGET_64BIT"
+ "*
+{
+ if (GET_CODE (operands[2]) == CONST_INT)
+ operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);
+
+ return \"dror\\t%0,%1,%2\";
+}"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "DI")])
+;; END CYGNUS LOCAL
+
+(define_insn ""
+ [(set (match_operand:DI 0 "register_operand" "=d,d")
+ (lshiftrt:DI (match_operand:DI 1 "se_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" "arith")
+ (set_attr "mode" "DI")
+ (set_attr_alternative "length"
+ [(const_int 1)
+ (if_then_else (match_operand:VOID 2 "m16_uimm3_b" "")
+ (const_int 1)
+ (const_int 2))])])
+
+;; On the mips16, we can split a 4 byte shift into 2 2 byte shifts.
+
+(define_split
+ [(set (match_operand:DI 0 "register_operand" "")
+ (lshiftrt:DI (match_operand:DI 1 "register_operand" "")
+ (match_operand:SI 2 "const_int_operand" "")))]
+ "TARGET_MIPS16
+ && reload_completed
+ && GET_CODE (operands[2]) == CONST_INT
+ && INTVAL (operands[2]) > 8
+ && INTVAL (operands[2]) <= 16"
+ [(set (match_dup 0) (lshiftrt:DI (match_dup 1) (const_int 8)))
+ (set (match_dup 0) (lshiftrt:DI (match_dup 0) (match_dup 2)))]
+"
+{
+ operands[2] = GEN_INT (INTVAL (operands[2]) - 8);
+}")
+
+
+;; ....................
+;; COMPARISONS
+;; ....................
+
+;; Flow here is rather complex:
+;; 1) The cmp{si,di,sf,df} routine is called. It deposits the
+;; arguments into the branch_cmp array, and the type into
+;; branch_type. No RTL is generated.
+;; 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 branchs 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 "cmpsi"
+ [(set (cc0)
+ (compare:CC (match_operand:SI 0 "register_operand" "")
+ (match_operand:SI 1 "arith_operand" "")))]
+ ""
+ "
+{
+ if (operands[0]) /* avoid unused code message */
+ {
+ branch_cmp[0] = operands[0];
+ branch_cmp[1] = operands[1];
+ branch_type = CMP_SI;
+ DONE;
+ }
+}")
+
+(define_expand "tstsi"
+ [(set (cc0)
+ (match_operand:SI 0 "register_operand" ""))]
+ ""
+ "
+{
+ if (operands[0]) /* avoid unused code message */
+ {
+ branch_cmp[0] = operands[0];
+ branch_cmp[1] = const0_rtx;
+ branch_type = CMP_SI;
+ DONE;
+ }
+}")
+
+(define_expand "cmpdi"
+ [(set (cc0)
+ (compare:CC (match_operand:DI 0 "se_register_operand" "")
+ (match_operand:DI 1 "se_arith_operand" "")))]
+ "TARGET_64BIT"
+ "
+{
+ if (operands[0]) /* avoid unused code message */
+ {
+ branch_cmp[0] = operands[0];
+ branch_cmp[1] = operands[1];
+ branch_type = CMP_DI;
+ DONE;
+ }
+}")
+
+(define_expand "tstdi"
+ [(set (cc0)
+ (match_operand:DI 0 "se_register_operand" ""))]
+ "TARGET_64BIT"
+ "
+{
+ if (operands[0]) /* avoid unused code message */
+ {
+ branch_cmp[0] = operands[0];
+ branch_cmp[1] = const0_rtx;
+ branch_type = CMP_DI;
+ DONE;
+ }
+}")
+
+(define_expand "cmpdf"
+ [(set (cc0)
+ (compare:CC (match_operand:DF 0 "register_operand" "")
+ (match_operand:DF 1 "register_operand" "")))]
+ "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
+ "
+{
+ if (operands[0]) /* avoid unused code message */
+ {
+ branch_cmp[0] = operands[0];
+ branch_cmp[1] = operands[1];
+ branch_type = CMP_DF;
+ DONE;
+ }
+}")
+
+(define_expand "cmpsf"
+ [(set (cc0)
+ (compare:CC (match_operand:SF 0 "register_operand" "")
+ (match_operand:SF 1 "register_operand" "")))]
+ "TARGET_HARD_FLOAT"
+ "
+{
+ if (operands[0]) /* avoid unused code message */
+ {
+ branch_cmp[0] = operands[0];
+ branch_cmp[1] = operands[1];
+ branch_type = CMP_SF;
+ DONE;
+ }
+}")
+
+
+;; ....................
+;; CONDITIONAL BRANCHES
+;; ....................
+
+(define_insn "branch_fp_ne"
+ [(set (pc)
+ (if_then_else (ne:CC (match_operand:CC 0 "register_operand" "z")
+ (const_int 0))
+ (match_operand 1 "pc_or_label_operand" "")
+ (match_operand 2 "pc_or_label_operand" "")))]
+ "TARGET_HARD_FLOAT"
+ "*
+{
+ mips_branch_likely = (final_sequence && INSN_ANNULLED_BRANCH_P (insn));
+ return (operands[1] != pc_rtx) ? \"%*bc1t%?\\t%Z0%1\" : \"%*bc1f%?\\t%Z0%2\";
+}"
+ [(set_attr "type" "branch")
+ (set_attr "mode" "none")
+ (set_attr "length" "1")])
+
+(define_insn "branch_fp_eq"
+ [(set (pc)
+ (if_then_else (eq:CC (match_operand:CC 0 "register_operand" "z")
+ (const_int 0))
+ (match_operand 1 "pc_or_label_operand" "")
+ (match_operand 2 "pc_or_label_operand" "")))]
+ "TARGET_HARD_FLOAT"
+ "*
+{
+ mips_branch_likely = (final_sequence && INSN_ANNULLED_BRANCH_P (insn));
+ return (operands[1] != pc_rtx) ? \"%*bc1f%?\\t%Z0%1\" : \"%*bc1t%?\\t%Z0%2\";
+}"
+ [(set_attr "type" "branch")
+ (set_attr "mode" "none")
+ (set_attr "length" "1")])
+
+(define_insn "branch_zero"
+ [(set (pc)
+ (if_then_else (match_operator:SI 0 "cmp_op"
+ [(match_operand:SI 1 "register_operand" "d")
+ (const_int 0)])
+ (match_operand 2 "pc_or_label_operand" "")
+ (match_operand 3 "pc_or_label_operand" "")))]
+ "!TARGET_MIPS16"
+ "*
+{
+ mips_branch_likely = (final_sequence && INSN_ANNULLED_BRANCH_P (insn));
+ if (operands[2] != pc_rtx)
+ { /* normal jump */
+ switch (GET_CODE (operands[0]))
+ {
+ case EQ: return \"%*beq%?\\t%z1,%.,%2\";
+ case NE: return \"%*bne%?\\t%z1,%.,%2\";
+ case GTU: return \"%*bne%?\\t%z1,%.,%2\";
+ case LEU: return \"%*beq%?\\t%z1,%.,%2\";
+ case GEU: return \"%*j\\t%2\";
+ case LTU: return \"%*bne%?\\t%.,%.,%2\";
+ default:
+ break;
+ }
+
+ return \"%*b%C0z%?\\t%z1,%2\";
+ }
+ else
+ { /* inverted jump */
+ switch (GET_CODE (operands[0]))
+ {
+ case EQ: return \"%*bne%?\\t%z1,%.,%3\";
+ case NE: return \"%*beq%?\\t%z1,%.,%3\";
+ case GTU: return \"%*beq%?\\t%z1,%.,%3\";
+ case LEU: return \"%*bne%?\\t%z1,%.,%3\";
+ case GEU: return \"%*beq%?\\t%.,%.,%3\";
+ case LTU: return \"%*j\\t%3\";
+ default:
+ break;
+ }
+
+ return \"%*b%N0z%?\\t%z1,%3\";
+ }
+}"
+ [(set_attr "type" "branch")
+ (set_attr "mode" "none")
+ (set_attr "length" "1")])
+
+
+(define_insn ""
+ [(set (pc)
+ (if_then_else (match_operator:SI 0 "equality_op"
+ [(match_operand:SI 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" "2")])
+
+(define_insn "branch_zero_di"
+ [(set (pc)
+ (if_then_else (match_operator:DI 0 "cmp_op"
+ [(match_operand:DI 1 "se_register_operand" "d")
+ (const_int 0)])
+ (match_operand 2 "pc_or_label_operand" "")
+ (match_operand 3 "pc_or_label_operand" "")))]
+ "!TARGET_MIPS16"
+ "*
+{
+ mips_branch_likely = (final_sequence && INSN_ANNULLED_BRANCH_P (insn));
+ if (operands[2] != pc_rtx)
+ { /* normal jump */
+ switch (GET_CODE (operands[0]))
+ {
+ case EQ: return \"%*beq%?\\t%z1,%.,%2\";
+ case NE: return \"%*bne%?\\t%z1,%.,%2\";
+ case GTU: return \"%*bne%?\\t%z1,%.,%2\";
+ case LEU: return \"%*beq%?\\t%z1,%.,%2\";
+ case GEU: return \"%*j\\t%2\";
+ case LTU: return \"%*bne%?\\t%.,%.,%2\";
+ default:
+ break;
+ }
+
+ return \"%*b%C0z%?\\t%z1,%2\";
+ }
+ else
+ { /* inverted jump */
+ switch (GET_CODE (operands[0]))
+ {
+ case EQ: return \"%*bne%?\\t%z1,%.,%3\";
+ case NE: return \"%*beq%?\\t%z1,%.,%3\";
+ case GTU: return \"%*beq%?\\t%z1,%.,%3\";
+ case LEU: return \"%*bne%?\\t%z1,%.,%3\";
+ case GEU: return \"%*beq%?\\t%.,%.,%3\";
+ case LTU: return \"%*j\\t%3\";
+ default:
+ break;
+ }
+
+ return \"%*b%N0z%?\\t%z1,%3\";
+ }
+}"
+ [(set_attr "type" "branch")
+ (set_attr "mode" "none")
+ (set_attr "length" "1")])
+
+(define_insn ""
+ [(set (pc)
+ (if_then_else (match_operator:DI 0 "equality_op"
+ [(match_operand:DI 1 "se_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" "2")])
+
+
+(define_insn "branch_equality"
+ [(set (pc)
+ (if_then_else (match_operator:SI 0 "equality_op"
+ [(match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "register_operand" "d")])
+ (match_operand 3 "pc_or_label_operand" "")
+ (match_operand 4 "pc_or_label_operand" "")))]
+ "!TARGET_MIPS16"
+ "*
+{
+ mips_branch_likely = (final_sequence && INSN_ANNULLED_BRANCH_P (insn));
+ return (operands[3] != pc_rtx)
+ ? \"%*b%C0%?\\t%z1,%z2,%3\"
+ : \"%*b%N0%?\\t%z1,%z2,%4\";
+}"
+ [(set_attr "type" "branch")
+ (set_attr "mode" "none")
+ (set_attr "length" "1")])
+
+
+(define_insn "branch_equality_di"
+ [(set (pc)
+ (if_then_else (match_operator:DI 0 "equality_op"
+ [(match_operand:DI 1 "se_register_operand" "d")
+ (match_operand:DI 2 "se_register_operand" "d")])
+ (match_operand 3 "pc_or_label_operand" "")
+ (match_operand 4 "pc_or_label_operand" "")))]
+ "!TARGET_MIPS16"
+ "*
+{
+ mips_branch_likely = (final_sequence && INSN_ANNULLED_BRANCH_P (insn));
+ return (operands[3] != pc_rtx)
+ ? \"%*b%C0%?\\t%z1,%z2,%3\"
+ : \"%*b%N0%?\\t%z1,%z2,%4\";
+}"
+ [(set_attr "type" "branch")
+ (set_attr "mode" "none")
+ (set_attr "length" "1")])
+
+
+(define_expand "beq"
+ [(set (pc)
+ (if_then_else (eq:CC (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "
+{
+ if (operands[0]) /* avoid unused code warning */
+ {
+ gen_conditional_branch (operands, EQ);
+ DONE;
+ }
+}")
+
+(define_expand "bne"
+ [(set (pc)
+ (if_then_else (ne:CC (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "
+{
+ if (operands[0]) /* avoid unused code warning */
+ {
+ gen_conditional_branch (operands, NE);
+ DONE;
+ }
+}")
+
+(define_expand "bgt"
+ [(set (pc)
+ (if_then_else (gt:CC (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "
+{
+ if (operands[0]) /* avoid unused code warning */
+ {
+ gen_conditional_branch (operands, GT);
+ DONE;
+ }
+}")
+
+(define_expand "bge"
+ [(set (pc)
+ (if_then_else (ge:CC (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "
+{
+ if (operands[0]) /* avoid unused code warning */
+ {
+ gen_conditional_branch (operands, GE);
+ DONE;
+ }
+}")
+
+(define_expand "blt"
+ [(set (pc)
+ (if_then_else (lt:CC (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "
+{
+ if (operands[0]) /* avoid unused code warning */
+ {
+ gen_conditional_branch (operands, LT);
+ DONE;
+ }
+}")
+
+(define_expand "ble"
+ [(set (pc)
+ (if_then_else (le:CC (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "
+{
+ if (operands[0]) /* avoid unused code warning */
+ {
+ gen_conditional_branch (operands, LE);
+ DONE;
+ }
+}")
+
+(define_expand "bgtu"
+ [(set (pc)
+ (if_then_else (gtu:CC (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "
+{
+ if (operands[0]) /* avoid unused code warning */
+ {
+ gen_conditional_branch (operands, GTU);
+ DONE;
+ }
+}")
+
+(define_expand "bgeu"
+ [(set (pc)
+ (if_then_else (geu:CC (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "
+{
+ if (operands[0]) /* avoid unused code warning */
+ {
+ gen_conditional_branch (operands, GEU);
+ DONE;
+ }
+}")
+
+
+(define_expand "bltu"
+ [(set (pc)
+ (if_then_else (ltu:CC (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "
+{
+ if (operands[0]) /* avoid unused code warning */
+ {
+ gen_conditional_branch (operands, LTU);
+ DONE;
+ }
+}")
+
+(define_expand "bleu"
+ [(set (pc)
+ (if_then_else (leu:CC (cc0)
+ (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ "
+{
+ if (operands[0]) /* avoid unused code warning */
+ {
+ gen_conditional_branch (operands, LEU);
+ DONE;
+ }
+}")
+
+
+;; ....................
+;; SETTING A REGISTER FROM A COMPARISON
+;; ....................
+
+(define_expand "seq"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (eq:SI (match_dup 1)
+ (match_dup 2)))]
+ ""
+ "
+{
+ if (branch_type != CMP_SI && (!TARGET_64BIT || branch_type != CMP_DI))
+ FAIL;
+
+ /* set up operands from compare. */
+ operands[1] = branch_cmp[0];
+ operands[2] = branch_cmp[1];
+
+ if (TARGET_64BIT || !TARGET_DEBUG_C_MODE || TARGET_MIPS16)
+ {
+ gen_int_relational (EQ, operands[0], operands[1], operands[2], (int *)0);
+ DONE;
+ }
+
+ if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) < 0)
+ operands[2] = force_reg (SImode, operands[2]);
+
+ /* fall through and generate default code */
+}")
+
+
+(define_insn "seq_si_zero"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (eq:SI (match_operand:SI 1 "register_operand" "d")
+ (const_int 0)))]
+ "!TARGET_MIPS16"
+ "sltu\\t%0,%1,1"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1")])
+
+(define_insn ""
+ [(set (match_operand:SI 0 "register_operand" "=t")
+ (eq:SI (match_operand:SI 1 "register_operand" "d")
+ (const_int 0)))]
+ "TARGET_MIPS16"
+ "sltu\\t%1,1"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1")])
+
+(define_insn "seq_di_zero"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (eq:DI (match_operand:DI 1 "se_register_operand" "d")
+ (const_int 0)))]
+ "TARGET_64BIT && !TARGET_MIPS16"
+ "sltu\\t%0,%1,1"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "DI")
+ (set_attr "length" "1")])
+
+(define_insn ""
+ [(set (match_operand:DI 0 "register_operand" "=t")
+ (eq:DI (match_operand:DI 1 "se_register_operand" "d")
+ (const_int 0)))]
+ "TARGET_64BIT && TARGET_MIPS16"
+ "sltu\\t%1,1"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "DI")
+ (set_attr "length" "1")])
+
+(define_insn "seq_si"
+ [(set (match_operand:SI 0 "register_operand" "=d,d")
+ (eq:SI (match_operand:SI 1 "register_operand" "%d,d")
+ (match_operand:SI 2 "uns_arith_operand" "d,K")))]
+ "TARGET_DEBUG_C_MODE && !TARGET_MIPS16"
+ "@
+ xor\\t%0,%1,%2\;sltu\\t%0,%0,1
+ xori\\t%0,%1,%2\;sltu\\t%0,%0,1"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")
+ (set_attr "length" "2")])
+
+(define_split
+ [(set (match_operand:SI 0 "register_operand" "")
+ (eq:SI (match_operand:SI 1 "register_operand" "")
+ (match_operand:SI 2 "uns_arith_operand" "")))]
+ "TARGET_DEBUG_C_MODE && !TARGET_DEBUG_D_MODE && !TARGET_MIPS16
+ && (GET_CODE (operands[2]) != CONST_INT || INTVAL (operands[2]) != 0)"
+ [(set (match_dup 0)
+ (xor:SI (match_dup 1)
+ (match_dup 2)))
+ (set (match_dup 0)
+ (ltu:SI (match_dup 0)
+ (const_int 1)))]
+ "")
+
+(define_insn "seq_di"
+ [(set (match_operand:DI 0 "register_operand" "=d,d")
+ (eq:DI (match_operand:DI 1 "se_register_operand" "%d,d")
+ (match_operand:DI 2 "se_uns_arith_operand" "d,K")))]
+ "TARGET_64BIT && TARGET_DEBUG_C_MODE && !TARGET_MIPS16"
+ "@
+ xor\\t%0,%1,%2\;sltu\\t%0,%0,1
+ xori\\t%0,%1,%2\;sltu\\t%0,%0,1"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "DI")
+ (set_attr "length" "2")])
+
+(define_split
+ [(set (match_operand:DI 0 "register_operand" "")
+ (eq:DI (match_operand:DI 1 "se_register_operand" "")
+ (match_operand:DI 2 "se_uns_arith_operand" "")))]
+ "TARGET_64BIT && TARGET_DEBUG_C_MODE && !TARGET_DEBUG_D_MODE
+ && !TARGET_MIPS16
+ && (GET_CODE (operands[2]) != CONST_INT || INTVAL (operands[2]) != 0)"
+ [(set (match_dup 0)
+ (xor:DI (match_dup 1)
+ (match_dup 2)))
+ (set (match_dup 0)
+ (ltu:DI (match_dup 0)
+ (const_int 1)))]
+ "")
+
+;; On the mips16 the default code is better than using sltu.
+
+(define_expand "sne"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (ne:SI (match_dup 1)
+ (match_dup 2)))]
+ "!TARGET_MIPS16"
+ "
+{
+ if (branch_type != CMP_SI && (!TARGET_64BIT || branch_type != CMP_DI))
+ FAIL;
+
+ /* set up operands from compare. */
+ operands[1] = branch_cmp[0];
+ operands[2] = branch_cmp[1];
+
+ if (TARGET_64BIT || !TARGET_DEBUG_C_MODE)
+ {
+ gen_int_relational (NE, operands[0], operands[1], operands[2], (int *)0);
+ DONE;
+ }
+
+ if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) < 0)
+ operands[2] = force_reg (SImode, operands[2]);
+
+ /* fall through and generate default code */
+}")
+
+(define_insn "sne_si_zero"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (ne:SI (match_operand:SI 1 "register_operand" "d")
+ (const_int 0)))]
+ "!TARGET_MIPS16"
+ "sltu\\t%0,%.,%1"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1")])
+
+(define_insn "sne_di_zero"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (ne:DI (match_operand:DI 1 "se_register_operand" "d")
+ (const_int 0)))]
+ "TARGET_64BIT && !TARGET_MIPS16"
+ "sltu\\t%0,%.,%1"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "DI")
+ (set_attr "length" "1")])
+
+(define_insn "sne_si"
+ [(set (match_operand:SI 0 "register_operand" "=d,d")
+ (ne:SI (match_operand:SI 1 "register_operand" "%d,d")
+ (match_operand:SI 2 "uns_arith_operand" "d,K")))]
+ "TARGET_DEBUG_C_MODE && !TARGET_MIPS16"
+ "@
+ xor\\t%0,%1,%2\;sltu\\t%0,%.,%0
+ xori\\t%0,%1,%x2\;sltu\\t%0,%.,%0"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")
+ (set_attr "length" "2")])
+
+(define_split
+ [(set (match_operand:SI 0 "register_operand" "")
+ (ne:SI (match_operand:SI 1 "register_operand" "")
+ (match_operand:SI 2 "uns_arith_operand" "")))]
+ "TARGET_DEBUG_C_MODE && !TARGET_DEBUG_D_MODE && !TARGET_MIPS16
+ && (GET_CODE (operands[2]) != CONST_INT || INTVAL (operands[2]) != 0)"
+ [(set (match_dup 0)
+ (xor:SI (match_dup 1)
+ (match_dup 2)))
+ (set (match_dup 0)
+ (gtu:SI (match_dup 0)
+ (const_int 0)))]
+ "")
+
+(define_insn "sne_di"
+ [(set (match_operand:DI 0 "register_operand" "=d,d")
+ (ne:DI (match_operand:DI 1 "se_register_operand" "%d,d")
+ (match_operand:DI 2 "se_uns_arith_operand" "d,K")))]
+ "TARGET_64BIT && TARGET_DEBUG_C_MODE && !TARGET_MIPS16"
+ "@
+ xor\\t%0,%1,%2\;sltu\\t%0,%.,%0
+ xori\\t%0,%1,%x2\;sltu\\t%0,%.,%0"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "DI")
+ (set_attr "length" "2")])
+
+(define_split
+ [(set (match_operand:DI 0 "register_operand" "")
+ (ne:DI (match_operand:DI 1 "se_register_operand" "")
+ (match_operand:DI 2 "se_uns_arith_operand" "")))]
+ "TARGET_64BIT && TARGET_DEBUG_C_MODE && !TARGET_DEBUG_D_MODE
+ && !TARGET_MIPS16
+ && (GET_CODE (operands[2]) != CONST_INT || INTVAL (operands[2]) != 0)"
+ [(set (match_dup 0)
+ (xor:DI (match_dup 1)
+ (match_dup 2)))
+ (set (match_dup 0)
+ (gtu:DI (match_dup 0)
+ (const_int 0)))]
+ "")
+
+(define_expand "sgt"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (gt:SI (match_dup 1)
+ (match_dup 2)))]
+ ""
+ "
+{
+ if (branch_type != CMP_SI && (!TARGET_64BIT || branch_type != CMP_DI))
+ FAIL;
+
+ /* set up operands from compare. */
+ operands[1] = branch_cmp[0];
+ operands[2] = branch_cmp[1];
+
+ if (TARGET_64BIT || !TARGET_DEBUG_C_MODE || TARGET_MIPS16)
+ {
+ gen_int_relational (GT, operands[0], operands[1], operands[2], (int *)0);
+ DONE;
+ }
+
+ if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) != 0)
+ operands[2] = force_reg (SImode, operands[2]);
+
+ /* fall through and generate default code */
+}")
+
+(define_insn "sgt_si"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (gt:SI (match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "reg_or_0_operand" "dJ")))]
+ "!TARGET_MIPS16"
+ "slt\\t%0,%z2,%1"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1")])
+
+(define_insn ""
+ [(set (match_operand:SI 0 "register_operand" "=t")
+ (gt:SI (match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "register_operand" "d")))]
+ "TARGET_MIPS16"
+ "slt\\t%2,%1"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1")])
+
+(define_insn "sgt_di"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (gt:DI (match_operand:DI 1 "se_register_operand" "d")
+ (match_operand:DI 2 "se_reg_or_0_operand" "dJ")))]
+ "TARGET_64BIT && !TARGET_MIPS16"
+ "slt\\t%0,%z2,%1"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "DI")
+ (set_attr "length" "1")])
+
+(define_insn ""
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (gt:DI (match_operand:DI 1 "se_register_operand" "d")
+ (match_operand:DI 2 "se_register_operand" "d")))]
+ "TARGET_64BIT && TARGET_MIPS16"
+ "slt\\t%2,%1"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "DI")
+ (set_attr "length" "1")])
+
+(define_expand "sge"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (ge:SI (match_dup 1)
+ (match_dup 2)))]
+ ""
+ "
+{
+ if (branch_type != CMP_SI && (!TARGET_64BIT || branch_type != CMP_DI))
+ FAIL;
+
+ /* set up operands from compare. */
+ operands[1] = branch_cmp[0];
+ operands[2] = branch_cmp[1];
+
+ if (TARGET_64BIT || !TARGET_DEBUG_C_MODE || TARGET_MIPS16)
+ {
+ gen_int_relational (GE, operands[0], operands[1], operands[2], (int *)0);
+ DONE;
+ }
+
+ /* fall through and generate default code */
+}")
+
+(define_insn "sge_si"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (ge:SI (match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "arith_operand" "dI")))]
+ "TARGET_DEBUG_C_MODE && !TARGET_MIPS16"
+ "slt\\t%0,%1,%2\;xori\\t%0,%0,0x0001"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")
+ (set_attr "length" "2")])
+
+(define_split
+ [(set (match_operand:SI 0 "register_operand" "")
+ (ge:SI (match_operand:SI 1 "register_operand" "")
+ (match_operand:SI 2 "arith_operand" "")))]
+ "TARGET_DEBUG_C_MODE && !TARGET_DEBUG_D_MODE && !TARGET_MIPS16"
+ [(set (match_dup 0)
+ (lt:SI (match_dup 1)
+ (match_dup 2)))
+ (set (match_dup 0)
+ (xor:SI (match_dup 0)
+ (const_int 1)))]
+ "")
+
+(define_insn "sge_di"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (ge:DI (match_operand:DI 1 "se_register_operand" "d")
+ (match_operand:DI 2 "se_arith_operand" "dI")))]
+ "TARGET_64BIT && TARGET_DEBUG_C_MODE && !TARGET_MIPS16"
+ "slt\\t%0,%1,%2\;xori\\t%0,%0,0x0001"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "DI")
+ (set_attr "length" "2")])
+
+(define_split
+ [(set (match_operand:DI 0 "register_operand" "")
+ (ge:DI (match_operand:DI 1 "se_register_operand" "")
+ (match_operand:DI 2 "se_arith_operand" "")))]
+ "TARGET_64BIT && TARGET_DEBUG_C_MODE && !TARGET_DEBUG_D_MODE
+ && !TARGET_MIPS16"
+ [(set (match_dup 0)
+ (lt:DI (match_dup 1)
+ (match_dup 2)))
+ (set (match_dup 0)
+ (xor:DI (match_dup 0)
+ (const_int 1)))]
+ "")
+
+(define_expand "slt"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (lt:SI (match_dup 1)
+ (match_dup 2)))]
+ ""
+ "
+{
+ if (branch_type != CMP_SI && (!TARGET_64BIT || branch_type != CMP_DI))
+ FAIL;
+
+ /* set up operands from compare. */
+ operands[1] = branch_cmp[0];
+ operands[2] = branch_cmp[1];
+
+ if (TARGET_64BIT || !TARGET_DEBUG_C_MODE || TARGET_MIPS16)
+ {
+ gen_int_relational (LT, operands[0], operands[1], operands[2], (int *)0);
+ DONE;
+ }
+
+ /* fall through and generate default code */
+}")
+
+(define_insn "slt_si"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (lt:SI (match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "arith_operand" "dI")))]
+ "!TARGET_MIPS16"
+ "slt\\t%0,%1,%2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1")])
+
+(define_insn ""
+ [(set (match_operand:SI 0 "register_operand" "=t,t")
+ (lt:SI (match_operand:SI 1 "register_operand" "d,d")
+ (match_operand:SI 2 "arith_operand" "d,I")))]
+ "TARGET_MIPS16"
+ "slt\\t%1,%2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")
+ (set_attr_alternative "length"
+ [(const_int 1)
+ (if_then_else (match_operand:VOID 2 "m16_uimm8_1" "")
+ (const_int 1)
+ (const_int 2))])])
+
+(define_insn "slt_di"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (lt:DI (match_operand:DI 1 "se_register_operand" "d")
+ (match_operand:DI 2 "se_arith_operand" "dI")))]
+ "TARGET_64BIT && !TARGET_MIPS16"
+ "slt\\t%0,%1,%2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "DI")
+ (set_attr "length" "1")])
+
+(define_insn ""
+ [(set (match_operand:DI 0 "register_operand" "=t,t")
+ (lt:DI (match_operand:DI 1 "se_register_operand" "d,d")
+ (match_operand:DI 2 "se_arith_operand" "d,I")))]
+ "TARGET_64BIT && TARGET_MIPS16"
+ "slt\\t%1,%2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "DI")
+ (set_attr_alternative "length"
+ [(const_int 1)
+ (if_then_else (match_operand:VOID 2 "m16_uimm8_1" "")
+ (const_int 1)
+ (const_int 2))])])
+
+(define_expand "sle"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (le:SI (match_dup 1)
+ (match_dup 2)))]
+ ""
+ "
+{
+ if (branch_type != CMP_SI && (!TARGET_64BIT || branch_type != CMP_DI))
+ FAIL;
+
+ /* set up operands from compare. */
+ operands[1] = branch_cmp[0];
+ operands[2] = branch_cmp[1];
+
+ if (TARGET_64BIT || !TARGET_DEBUG_C_MODE || TARGET_MIPS16)
+ {
+ gen_int_relational (LE, operands[0], operands[1], operands[2], (int *)0);
+ DONE;
+ }
+
+ if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) >= 32767)
+ operands[2] = force_reg (SImode, operands[2]);
+
+ /* fall through and generate default code */
+}")
+
+(define_insn "sle_si_const"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (le:SI (match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "small_int" "I")))]
+ "!TARGET_MIPS16 && INTVAL (operands[2]) < 32767"
+ "*
+{
+ operands[2] = GEN_INT (INTVAL (operands[2])+1);
+ return \"slt\\t%0,%1,%2\";
+}"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1")])
+
+(define_insn ""
+ [(set (match_operand:SI 0 "register_operand" "=t")
+ (le:SI (match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "small_int" "I")))]
+ "TARGET_MIPS16 && INTVAL (operands[2]) < 32767"
+ "*
+{
+ operands[2] = GEN_INT (INTVAL (operands[2])+1);
+ return \"slt\\t%1,%2\";
+}"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")
+ (set (attr "length") (if_then_else (match_operand:VOID 2 "m16_uimm8_m1_1" "")
+ (const_int 1)
+ (const_int 2)))])
+
+(define_insn "sle_di_const"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (le:DI (match_operand:DI 1 "se_register_operand" "d")
+ (match_operand:DI 2 "small_int" "I")))]
+ "TARGET_64BIT && !TARGET_MIPS16 && INTVAL (operands[2]) < 32767"
+ "*
+{
+ operands[2] = GEN_INT (INTVAL (operands[2])+1);
+ return \"slt\\t%0,%1,%2\";
+}"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "DI")
+ (set_attr "length" "1")])
+
+(define_insn ""
+ [(set (match_operand:DI 0 "register_operand" "=t")
+ (le:DI (match_operand:DI 1 "se_register_operand" "d")
+ (match_operand:DI 2 "small_int" "I")))]
+ "TARGET_64BIT && TARGET_MIPS16 && INTVAL (operands[2]) < 32767"
+ "*
+{
+ operands[2] = GEN_INT (INTVAL (operands[2])+1);
+ return \"slt\\t%1,%2\";
+}"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "DI")
+ (set (attr "length") (if_then_else (match_operand:VOID 2 "m16_uimm8_m1_1" "")
+ (const_int 1)
+ (const_int 2)))])
+
+(define_insn "sle_si_reg"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (le:SI (match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "register_operand" "d")))]
+ "TARGET_DEBUG_C_MODE && !TARGET_MIPS16"
+ "slt\\t%0,%z2,%1\;xori\\t%0,%0,0x0001"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")
+ (set_attr "length" "2")])
+
+(define_split
+ [(set (match_operand:SI 0 "register_operand" "")
+ (le:SI (match_operand:SI 1 "register_operand" "")
+ (match_operand:SI 2 "register_operand" "")))]
+ "TARGET_DEBUG_C_MODE && !TARGET_DEBUG_D_MODE && !TARGET_MIPS16"
+ [(set (match_dup 0)
+ (lt:SI (match_dup 2)
+ (match_dup 1)))
+ (set (match_dup 0)
+ (xor:SI (match_dup 0)
+ (const_int 1)))]
+ "")
+
+(define_insn "sle_di_reg"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (le:DI (match_operand:DI 1 "se_register_operand" "d")
+ (match_operand:DI 2 "se_register_operand" "d")))]
+ "TARGET_64BIT && TARGET_DEBUG_C_MODE && !TARGET_MIPS16"
+ "slt\\t%0,%z2,%1\;xori\\t%0,%0,0x0001"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "DI")
+ (set_attr "length" "2")])
+
+(define_split
+ [(set (match_operand:DI 0 "register_operand" "")
+ (le:DI (match_operand:DI 1 "se_register_operand" "")
+ (match_operand:DI 2 "se_register_operand" "")))]
+ "TARGET_64BIT && TARGET_DEBUG_C_MODE && !TARGET_DEBUG_D_MODE
+ && !TARGET_MIPS16"
+ [(set (match_dup 0)
+ (lt:DI (match_dup 2)
+ (match_dup 1)))
+ (set (match_dup 0)
+ (xor:DI (match_dup 0)
+ (const_int 1)))]
+ "")
+
+(define_expand "sgtu"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (gtu:SI (match_dup 1)
+ (match_dup 2)))]
+ ""
+ "
+{
+ if (branch_type != CMP_SI && (!TARGET_64BIT || branch_type != CMP_DI))
+ FAIL;
+
+ /* set up operands from compare. */
+ operands[1] = branch_cmp[0];
+ operands[2] = branch_cmp[1];
+
+ if (TARGET_64BIT || !TARGET_DEBUG_C_MODE || TARGET_MIPS16)
+ {
+ gen_int_relational (GTU, operands[0], operands[1], operands[2], (int *)0);
+ DONE;
+ }
+
+ if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) != 0)
+ operands[2] = force_reg (SImode, operands[2]);
+
+ /* fall through and generate default code */
+}")
+
+(define_insn "sgtu_si"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (gtu:SI (match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "reg_or_0_operand" "dJ")))]
+ ""
+ "sltu\\t%0,%z2,%1"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1")])
+
+(define_insn ""
+ [(set (match_operand:SI 0 "register_operand" "=t")
+ (gtu:SI (match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "register_operand" "d")))]
+ ""
+ "sltu\\t%2,%1"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1")])
+
+(define_insn "sgtu_di"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (gtu:DI (match_operand:DI 1 "se_register_operand" "d")
+ (match_operand:DI 2 "se_reg_or_0_operand" "dJ")))]
+ "TARGET_64BIT"
+ "sltu\\t%0,%z2,%1"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "DI")
+ (set_attr "length" "1")])
+
+(define_insn ""
+ [(set (match_operand:DI 0 "register_operand" "=t")
+ (gtu:DI (match_operand:DI 1 "se_register_operand" "d")
+ (match_operand:DI 2 "se_register_operand" "d")))]
+ "TARGET_64BIT"
+ "sltu\\t%2,%1"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "DI")
+ (set_attr "length" "1")])
+
+(define_expand "sgeu"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (geu:SI (match_dup 1)
+ (match_dup 2)))]
+ ""
+ "
+{
+ if (branch_type != CMP_SI && (!TARGET_64BIT || branch_type != CMP_DI))
+ FAIL;
+
+ /* set up operands from compare. */
+ operands[1] = branch_cmp[0];
+ operands[2] = branch_cmp[1];
+
+ if (TARGET_64BIT || !TARGET_DEBUG_C_MODE || TARGET_MIPS16)
+ {
+ gen_int_relational (GEU, operands[0], operands[1], operands[2], (int *)0);
+ DONE;
+ }
+
+ /* fall through and generate default code */
+}")
+
+(define_insn "sgeu_si"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (geu:SI (match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "arith_operand" "dI")))]
+ "TARGET_DEBUG_C_MODE && !TARGET_MIPS16"
+ "sltu\\t%0,%1,%2\;xori\\t%0,%0,0x0001"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")
+ (set_attr "length" "2")])
+
+(define_split
+ [(set (match_operand:SI 0 "register_operand" "")
+ (geu:SI (match_operand:SI 1 "register_operand" "")
+ (match_operand:SI 2 "arith_operand" "")))]
+ "TARGET_DEBUG_C_MODE && !TARGET_DEBUG_D_MODE && !TARGET_MIPS16"
+ [(set (match_dup 0)
+ (ltu:SI (match_dup 1)
+ (match_dup 2)))
+ (set (match_dup 0)
+ (xor:SI (match_dup 0)
+ (const_int 1)))]
+ "")
+
+(define_insn "sgeu_di"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (geu:DI (match_operand:DI 1 "se_register_operand" "d")
+ (match_operand:DI 2 "se_arith_operand" "dI")))]
+ "TARGET_64BIT && TARGET_DEBUG_C_MODE && !TARGET_MIPS16"
+ "sltu\\t%0,%1,%2\;xori\\t%0,%0,0x0001"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "DI")
+ (set_attr "length" "2")])
+
+(define_split
+ [(set (match_operand:DI 0 "register_operand" "")
+ (geu:DI (match_operand:DI 1 "se_register_operand" "")
+ (match_operand:DI 2 "se_arith_operand" "")))]
+ "TARGET_64BIT && TARGET_DEBUG_C_MODE && !TARGET_DEBUG_D_MODE
+ && !TARGET_MIPS16"
+ [(set (match_dup 0)
+ (ltu:DI (match_dup 1)
+ (match_dup 2)))
+ (set (match_dup 0)
+ (xor:DI (match_dup 0)
+ (const_int 1)))]
+ "")
+
+(define_expand "sltu"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (ltu:SI (match_dup 1)
+ (match_dup 2)))]
+ ""
+ "
+{
+ if (branch_type != CMP_SI && (!TARGET_64BIT || branch_type != CMP_DI))
+ FAIL;
+
+ /* set up operands from compare. */
+ operands[1] = branch_cmp[0];
+ operands[2] = branch_cmp[1];
+
+ if (TARGET_64BIT || !TARGET_DEBUG_C_MODE || TARGET_MIPS16)
+ {
+ gen_int_relational (LTU, operands[0], operands[1], operands[2], (int *)0);
+ DONE;
+ }
+
+ /* fall through and generate default code */
+}")
+
+(define_insn "sltu_si"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (ltu:SI (match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "arith_operand" "dI")))]
+ "!TARGET_MIPS16"
+ "sltu\\t%0,%1,%2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1")])
+
+(define_insn ""
+ [(set (match_operand:SI 0 "register_operand" "=t,t")
+ (ltu:SI (match_operand:SI 1 "register_operand" "d,d")
+ (match_operand:SI 2 "arith_operand" "d,I")))]
+ "TARGET_MIPS16"
+ "sltu\\t%1,%2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")
+ (set_attr_alternative "length"
+ [(const_int 1)
+ (if_then_else (match_operand:VOID 2 "m16_uimm8_1" "")
+ (const_int 1)
+ (const_int 2))])])
+
+(define_insn "sltu_di"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (ltu:DI (match_operand:DI 1 "se_register_operand" "d")
+ (match_operand:DI 2 "se_arith_operand" "dI")))]
+ "TARGET_64BIT && !TARGET_MIPS16"
+ "sltu\\t%0,%1,%2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "DI")
+ (set_attr "length" "1")])
+
+(define_insn ""
+ [(set (match_operand:DI 0 "register_operand" "=t,t")
+ (ltu:DI (match_operand:DI 1 "se_register_operand" "d,d")
+ (match_operand:DI 2 "se_arith_operand" "d,I")))]
+ "TARGET_64BIT && TARGET_MIPS16"
+ "sltu\\t%1,%2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "DI")
+ (set_attr_alternative "length"
+ [(const_int 1)
+ (if_then_else (match_operand:VOID 2 "m16_uimm8_1" "")
+ (const_int 1)
+ (const_int 2))])])
+
+(define_expand "sleu"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (leu:SI (match_dup 1)
+ (match_dup 2)))]
+ ""
+ "
+{
+ if (branch_type != CMP_SI && (!TARGET_64BIT || branch_type != CMP_DI))
+ FAIL;
+
+ /* set up operands from compare. */
+ operands[1] = branch_cmp[0];
+ operands[2] = branch_cmp[1];
+
+ if (TARGET_64BIT || !TARGET_DEBUG_C_MODE || TARGET_MIPS16)
+ {
+ gen_int_relational (LEU, operands[0], operands[1], operands[2], (int *)0);
+ DONE;
+ }
+
+ if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) >= 32767)
+ operands[2] = force_reg (SImode, operands[2]);
+
+ /* fall through and generate default code */
+}")
+
+(define_insn "sleu_si_const"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (leu:SI (match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "small_int" "I")))]
+ "!TARGET_MIPS16 && INTVAL (operands[2]) < 32767"
+ "*
+{
+ operands[2] = GEN_INT (INTVAL (operands[2])+1);
+ return \"sltu\\t%0,%1,%2\";
+}"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")
+ (set_attr "length" "1")])
+
+(define_insn ""
+ [(set (match_operand:SI 0 "register_operand" "=t")
+ (leu:SI (match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "small_int" "I")))]
+ "TARGET_MIPS16 && INTVAL (operands[2]) < 32767"
+ "*
+{
+ operands[2] = GEN_INT (INTVAL (operands[2])+1);
+ return \"sltu\\t%1,%2\";
+}"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")
+ (set (attr "length") (if_then_else (match_operand:VOID 2 "m16_uimm8_m1_1" "")
+ (const_int 1)
+ (const_int 2)))])
+
+(define_insn "sleu_di_const"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (leu:DI (match_operand:DI 1 "se_register_operand" "d")
+ (match_operand:DI 2 "small_int" "I")))]
+ "TARGET_64BIT && !TARGET_MIPS16 && INTVAL (operands[2]) < 32767"
+ "*
+{
+ operands[2] = GEN_INT (INTVAL (operands[2])+1);
+ return \"sltu\\t%0,%1,%2\";
+}"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "DI")
+ (set_attr "length" "1")])
+
+(define_insn ""
+ [(set (match_operand:DI 0 "register_operand" "=t")
+ (leu:DI (match_operand:DI 1 "se_register_operand" "d")
+ (match_operand:DI 2 "small_int" "I")))]
+ "TARGET_64BIT && TARGET_MIPS16 && INTVAL (operands[2]) < 32767"
+ "*
+{
+ operands[2] = GEN_INT (INTVAL (operands[2])+1);
+ return \"sltu\\t%1,%2\";
+}"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "DI")
+ (set (attr "length") (if_then_else (match_operand:VOID 2 "m16_uimm8_m1_1" "")
+ (const_int 1)
+ (const_int 2)))])
+
+(define_insn "sleu_si_reg"
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (leu:SI (match_operand:SI 1 "register_operand" "d")
+ (match_operand:SI 2 "register_operand" "d")))]
+ "TARGET_DEBUG_C_MODE && !TARGET_MIPS16"
+ "sltu\\t%0,%z2,%1\;xori\\t%0,%0,0x0001"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")
+ (set_attr "length" "2")])
+
+(define_split
+ [(set (match_operand:SI 0 "register_operand" "")
+ (leu:SI (match_operand:SI 1 "register_operand" "")
+ (match_operand:SI 2 "register_operand" "")))]
+ "TARGET_DEBUG_C_MODE && !TARGET_DEBUG_D_MODE && !TARGET_MIPS16"
+ [(set (match_dup 0)
+ (ltu:SI (match_dup 2)
+ (match_dup 1)))
+ (set (match_dup 0)
+ (xor:SI (match_dup 0)
+ (const_int 1)))]
+ "")
+
+(define_insn "sleu_di_reg"
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (leu:DI (match_operand:DI 1 "se_register_operand" "d")
+ (match_operand:DI 2 "se_register_operand" "d")))]
+ "TARGET_64BIT && TARGET_DEBUG_C_MODE && !TARGET_MIPS16"
+ "sltu\\t%0,%z2,%1\;xori\\t%0,%0,0x0001"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "DI")
+ (set_attr "length" "2")])
+
+(define_split
+ [(set (match_operand:DI 0 "register_operand" "")
+ (leu:DI (match_operand:DI 1 "se_register_operand" "")
+ (match_operand:DI 2 "se_register_operand" "")))]
+ "TARGET_64BIT && TARGET_DEBUG_C_MODE && !TARGET_DEBUG_D_MODE
+ && !TARGET_MIPS16"
+ [(set (match_dup 0)
+ (ltu:DI (match_dup 2)
+ (match_dup 1)))
+ (set (match_dup 0)
+ (xor:DI (match_dup 0)
+ (const_int 1)))]
+ "")
+
+
+;; ....................
+;; FLOATING POINT COMPARISONS
+;; ....................
+
+(define_insn "seq_df"
+ [(set (match_operand:CC 0 "register_operand" "=z")
+ (eq:CC (match_operand:DF 1 "register_operand" "f")
+ (match_operand:DF 2 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
+ "*
+{
+ return mips_fill_delay_slot (\"c.eq.d\\t%Z0%1,%2\", DELAY_FCMP, operands, insn);
+}"
+ [(set_attr "type" "fcmp")
+ (set_attr "mode" "FPSW")
+ (set_attr "length" "1")])
+
+(define_insn "slt_df"
+ [(set (match_operand:CC 0 "register_operand" "=z")
+ (lt:CC (match_operand:DF 1 "register_operand" "f")
+ (match_operand:DF 2 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
+ "*
+{
+ return mips_fill_delay_slot (\"c.lt.d\\t%Z0%1,%2\", DELAY_FCMP, operands, insn);
+}"
+ [(set_attr "type" "fcmp")
+ (set_attr "mode" "FPSW")
+ (set_attr "length" "1")])
+
+(define_insn "sle_df"
+ [(set (match_operand:CC 0 "register_operand" "=z")
+ (le:CC (match_operand:DF 1 "register_operand" "f")
+ (match_operand:DF 2 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
+ "*
+{
+ return mips_fill_delay_slot (\"c.le.d\\t%Z0%1,%2\", DELAY_FCMP, operands, insn);
+}"
+ [(set_attr "type" "fcmp")
+ (set_attr "mode" "FPSW")
+ (set_attr "length" "1")])
+
+(define_insn "sgt_df"
+ [(set (match_operand:CC 0 "register_operand" "=z")
+ (gt:CC (match_operand:DF 1 "register_operand" "f")
+ (match_operand:DF 2 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
+ "*
+{
+ return mips_fill_delay_slot (\"c.lt.d\\t%Z0%2,%1\", DELAY_FCMP, operands, insn);
+}"
+ [(set_attr "type" "fcmp")
+ (set_attr "mode" "FPSW")
+ (set_attr "length" "1")])
+
+(define_insn "sge_df"
+ [(set (match_operand:CC 0 "register_operand" "=z")
+ (ge:CC (match_operand:DF 1 "register_operand" "f")
+ (match_operand:DF 2 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
+ "*
+{
+ return mips_fill_delay_slot (\"c.le.d\\t%Z0%2,%1\", DELAY_FCMP, operands, insn);
+}"
+ [(set_attr "type" "fcmp")
+ (set_attr "mode" "FPSW")
+ (set_attr "length" "1")])
+
+(define_insn "seq_sf"
+ [(set (match_operand:CC 0 "register_operand" "=z")
+ (eq:CC (match_operand:SF 1 "register_operand" "f")
+ (match_operand:SF 2 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT"
+ "*
+{
+ return mips_fill_delay_slot (\"c.eq.s\\t%Z0%1,%2\", DELAY_FCMP, operands, insn);
+}"
+ [(set_attr "type" "fcmp")
+ (set_attr "mode" "FPSW")
+ (set_attr "length" "1")])
+
+(define_insn "slt_sf"
+ [(set (match_operand:CC 0 "register_operand" "=z")
+ (lt:CC (match_operand:SF 1 "register_operand" "f")
+ (match_operand:SF 2 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT"
+ "*
+{
+ return mips_fill_delay_slot (\"c.lt.s\\t%Z0%1,%2\", DELAY_FCMP, operands, insn);
+}"
+ [(set_attr "type" "fcmp")
+ (set_attr "mode" "FPSW")
+ (set_attr "length" "1")])
+
+(define_insn "sle_sf"
+ [(set (match_operand:CC 0 "register_operand" "=z")
+ (le:CC (match_operand:SF 1 "register_operand" "f")
+ (match_operand:SF 2 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT"
+ "*
+{
+ return mips_fill_delay_slot (\"c.le.s\\t%Z0%1,%2\", DELAY_FCMP, operands, insn);
+}"
+ [(set_attr "type" "fcmp")
+ (set_attr "mode" "FPSW")
+ (set_attr "length" "1")])
+
+(define_insn "sgt_sf"
+ [(set (match_operand:CC 0 "register_operand" "=z")
+ (gt:CC (match_operand:SF 1 "register_operand" "f")
+ (match_operand:SF 2 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT"
+ "*
+{
+ return mips_fill_delay_slot (\"c.lt.s\\t%Z0%2,%1\", DELAY_FCMP, operands, insn);
+}"
+ [(set_attr "type" "fcmp")
+ (set_attr "mode" "FPSW")
+ (set_attr "length" "1")])
+
+(define_insn "sge_sf"
+ [(set (match_operand:CC 0 "register_operand" "=z")
+ (ge:CC (match_operand:SF 1 "register_operand" "f")
+ (match_operand:SF 2 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT"
+ "*
+{
+ return mips_fill_delay_slot (\"c.le.s\\t%Z0%2,%1\", DELAY_FCMP, operands, insn);
+}"
+ [(set_attr "type" "fcmp")
+ (set_attr "mode" "FPSW")
+ (set_attr "length" "1")])
+
+
+;; ....................
+;; UNCONDITIONAL BRANCHES
+;; ....................
+
+;; Unconditional branches.
+
+(define_insn "jump"
+ [(set (pc)
+ (label_ref (match_operand 0 "" "")))]
+ "!TARGET_MIPS16"
+ "*
+{
+ if (GET_CODE (operands[0]) == REG)
+ return \"%*j\\t%0\";
+ /* ??? I don't know why this is necessary. This works around an
+ assembler problem that appears when a label is defined, then referenced
+ in a switch table, then used in a `j' instruction. */
+ else if (mips_abi != ABI_32 && mips_abi != ABI_O64)
+ return \"%*b\\t%l0\";
+ else
+ return \"%*j\\t%l0\";
+}"
+ [(set_attr "type" "jump")
+ (set_attr "mode" "none")
+ (set_attr "length" "1")])
+
+;; 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 && GET_CODE (operands[0]) != REG"
+ "b\\t%l0"
+ [(set_attr "type" "branch")
+ (set_attr "mode" "none")
+ (set_attr "length" "2")])
+
+(define_expand "indirect_jump"
+ [(set (pc) (match_operand 0 "register_operand" "d"))]
+ ""
+ "
+{
+ rtx dest;
+
+ if (operands[0]) /* eliminate unused code warnings */
+ {
+ dest = operands[0];
+ if (GET_CODE (dest) != REG || GET_MODE (dest) != Pmode)
+ operands[0] = copy_to_mode_reg (Pmode, dest);
+
+ if (!(Pmode == DImode))
+ emit_jump_insn (gen_indirect_jump_internal1 (operands[0]));
+ else
+ emit_jump_insn (gen_indirect_jump_internal2 (operands[0]));
+
+ DONE;
+ }
+}")
+
+(define_insn "indirect_jump_internal1"
+ [(set (pc) (match_operand:SI 0 "register_operand" "d"))]
+ "!(Pmode == DImode)"
+ "%*j\\t%0"
+ [(set_attr "type" "jump")
+ (set_attr "mode" "none")
+ (set_attr "length" "1")])
+
+(define_insn "indirect_jump_internal2"
+ [(set (pc) (match_operand:DI 0 "se_register_operand" "d"))]
+ "Pmode == DImode"
+ "%*j\\t%0"
+ [(set_attr "type" "jump")
+ (set_attr "mode" "none")
+ (set_attr "length" "1")])
+
+(define_expand "tablejump"
+ [(set (pc)
+ (match_operand 0 "register_operand" "d"))
+ (use (label_ref (match_operand 1 "" "")))]
+ ""
+ "
+{
+ if (operands[0]) /* eliminate unused code warnings */
+ {
+ if (TARGET_MIPS16)
+ {
+ if (GET_MODE (operands[0]) != HImode)
+ abort ();
+ if (!(Pmode == DImode))
+ emit_jump_insn (gen_tablejump_mips161 (operands[0], operands[1]));
+ else
+ emit_jump_insn (gen_tablejump_mips162 (operands[0], operands[1]));
+ DONE;
+ }
+
+ if (GET_MODE (operands[0]) != Pmode)
+ abort ();
+
+ if (! flag_pic)
+ {
+ if (!(Pmode == DImode))
+ emit_jump_insn (gen_tablejump_internal1 (operands[0], operands[1]));
+ else
+ emit_jump_insn (gen_tablejump_internal2 (operands[0], operands[1]));
+ }
+ else
+ {
+ if (!(Pmode == DImode))
+ emit_jump_insn (gen_tablejump_internal3 (operands[0], operands[1]));
+ else
+ emit_jump_insn (gen_tablejump_internal4 (operands[0], operands[1]));
+ }
+
+ DONE;
+ }
+}")
+
+(define_insn "tablejump_internal1"
+ [(set (pc)
+ (match_operand:SI 0 "register_operand" "d"))
+ (use (label_ref (match_operand 1 "" "")))]
+ "!(Pmode == DImode)"
+ "%*j\\t%0"
+ [(set_attr "type" "jump")
+ (set_attr "mode" "none")
+ (set_attr "length" "1")])
+
+(define_insn "tablejump_internal2"
+ [(set (pc)
+ (match_operand:DI 0 "se_register_operand" "d"))
+ (use (label_ref (match_operand 1 "" "")))]
+ "Pmode == DImode"
+ "%*j\\t%0"
+ [(set_attr "type" "jump")
+ (set_attr "mode" "none")
+ (set_attr "length" "1")])
+
+(define_expand "tablejump_internal3"
+ [(parallel [(set (pc)
+ (plus:SI (match_operand:SI 0 "register_operand" "d")
+ (label_ref:SI (match_operand:SI 1 "" ""))))
+ (use (label_ref:SI (match_dup 1)))])]
+ ""
+ "")
+
+(define_expand "tablejump_mips161"
+ [(set (pc) (plus:SI (sign_extend:SI
+ (match_operand:HI 0 "register_operand" "d"))
+ (label_ref:SI (match_operand:SI 1 "" ""))))]
+ "TARGET_MIPS16 && !(Pmode == DImode)"
+ "
+{
+ if (operands[0]) /* eliminate unused code warnings. */
+ {
+ rtx t1, t2, t3;
+
+ t1 = gen_reg_rtx (SImode);
+ t2 = gen_reg_rtx (SImode);
+ t3 = gen_reg_rtx (SImode);
+ emit_insn (gen_extendhisi2 (t1, operands[0]));
+ emit_move_insn (t2, gen_rtx (LABEL_REF, SImode, operands[1]));
+ emit_insn (gen_addsi3 (t3, t1, t2));
+ emit_insn (gen_tablejump_internal1 (t3, operands[1]));
+ DONE;
+ }
+}")
+
+(define_expand "tablejump_mips162"
+ [(set (pc) (plus:DI (sign_extend:DI
+ (match_operand:HI 0 "register_operand" "d"))
+ (label_ref:DI (match_operand:SI 1 "" ""))))]
+ "TARGET_MIPS16 && Pmode == DImode"
+ "
+{
+ if (operands[0]) /* eliminate unused code warnings. */
+ {
+ rtx t1, t2, t3;
+
+ t1 = gen_reg_rtx (DImode);
+ t2 = gen_reg_rtx (DImode);
+ t3 = gen_reg_rtx (DImode);
+ emit_insn (gen_extendhidi2 (t1, operands[0]));
+ emit_move_insn (t2, gen_rtx (LABEL_REF, DImode, operands[1]));
+ emit_insn (gen_adddi3 (t3, t1, t2));
+ emit_insn (gen_tablejump_internal2 (t3, operands[1]));
+ DONE;
+ }
+}")
+
+;;; Make sure that this only matches the insn before ADDR_DIFF_VEC. Otherwise
+;;; it is not valid. ??? With the USE, the condition tests may not be required
+;;; any longer.
+
+;;; ??? The length depends on the ABI. It is two for o32, and one for n32.
+;;; We just use the conservative number here.
+
+(define_insn ""
+ [(set (pc)
+ (plus:SI (match_operand:SI 0 "register_operand" "d")
+ (label_ref:SI (match_operand:SI 1 "" ""))))
+ (use (label_ref:SI (match_dup 1)))]
+ "!(Pmode == DImode) && next_active_insn (insn) != 0
+ && GET_CODE (PATTERN (next_active_insn (insn))) == ADDR_DIFF_VEC
+ && PREV_INSN (next_active_insn (insn)) == operands[1]"
+ "*
+{
+ /* .cpadd expands to add REG,REG,$gp when pic, and nothing when not pic. */
+ if (mips_abi == ABI_32 || mips_abi == ABI_O64)
+ output_asm_insn (\".cpadd\\t%0\", operands);
+ return \"%*j\\t%0\";
+}"
+ [(set_attr "type" "jump")
+ (set_attr "mode" "none")
+ (set_attr "length" "2")])
+
+(define_expand "tablejump_internal4"
+ [(parallel [(set (pc)
+ (plus:DI (match_operand:DI 0 "se_register_operand" "d")
+ (label_ref:DI (match_operand:SI 1 "" ""))))
+ (use (label_ref:DI (match_dup 1)))])]
+ ""
+ "")
+
+;;; Make sure that this only matches the insn before ADDR_DIFF_VEC. Otherwise
+;;; it is not valid. ??? With the USE, the condition tests may not be required
+;;; any longer.
+
+(define_insn ""
+ [(set (pc)
+ (plus:DI (match_operand:DI 0 "se_register_operand" "d")
+ (label_ref:DI (match_operand:SI 1 "" ""))))
+ (use (label_ref:DI (match_dup 1)))]
+ "Pmode == DImode && next_active_insn (insn) != 0
+ && GET_CODE (PATTERN (next_active_insn (insn))) == ADDR_DIFF_VEC
+ && PREV_INSN (next_active_insn (insn)) == operands[1]"
+ "%*j\\t%0"
+ [(set_attr "type" "jump")
+ (set_attr "mode" "none")
+ (set_attr "length" "1")])
+
+;; Implement a switch statement when generating embedded PIC code.
+;; Switches are implemented by `tablejump' when not using -membedded-pic.
+
+(define_expand "casesi"
+ [(set (match_dup 5)
+ (minus:SI (match_operand:SI 0 "register_operand" "d")
+ (match_operand:SI 1 "arith_operand" "dI")))
+ (set (cc0)
+ (compare:CC (match_dup 5)
+ (match_operand:SI 2 "arith_operand" "")))
+ (set (pc)
+ (if_then_else (gtu (cc0)
+ (const_int 0))
+ (label_ref (match_operand 4 "" ""))
+ (pc)))
+ (parallel
+ [(set (pc)
+ (mem:SI (plus:SI (mult:SI (match_dup 5)
+ (const_int 4))
+ (label_ref (match_operand 3 "" "")))))
+ (clobber (match_scratch:SI 6 ""))
+ (clobber (reg:SI 31))])]
+ "TARGET_EMBEDDED_PIC"
+ "
+{
+ /* We need slightly different code for eight byte table entries. */
+ if (Pmode == DImode)
+ abort ();
+
+ if (operands[0])
+ {
+ rtx reg = gen_reg_rtx (SImode);
+
+ /* If the index is too large, go to the default label. */
+ emit_insn (gen_subsi3 (reg, operands[0], operands[1]));
+ emit_insn (gen_cmpsi (reg, operands[2]));
+ emit_insn (gen_bgtu (operands[4]));
+
+ /* Do the PIC jump. */
+ emit_insn (gen_casesi_internal (reg, operands[3], gen_reg_rtx (SImode)));
+
+ DONE;
+ }
+}")
+
+;; An embedded PIC switch statement looks like this:
+;; bal $LS1
+;; sll $reg,$index,2
+;; $LS1:
+;; addu $reg,$reg,$31
+;; lw $reg,$L1-$LS1($reg)
+;; addu $reg,$reg,$31
+;; j $reg
+;; $L1:
+;; .word case1-$LS1
+;; .word case2-$LS1
+;; ...
+
+(define_insn "casesi_internal"
+ [(set (pc)
+ (mem:SI (plus:SI (mult:SI (match_operand:SI 0 "register_operand" "d")
+ (const_int 4))
+ (label_ref (match_operand 1 "" "")))))
+ (clobber (match_operand:SI 2 "register_operand" "d"))
+ (clobber (reg:SI 31))]
+ "TARGET_EMBEDDED_PIC"
+ "*
+{
+ output_asm_insn (\"%(bal\\t%S1\;sll\\t%0,2\\n%S1:\", operands);
+ output_asm_insn (\"addu\\t%0,%0,$31%)\", operands);
+ output_asm_insn (\"lw\\t%0,%1-%S1(%0)\;addu\\t%0,%0,$31\", operands);
+ return \"j\\t%0\";
+}"
+ [(set_attr "type" "jump")
+ (set_attr "mode" "none")
+ (set_attr "length" "6")])
+
+;; For o32/n32/n64, 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, and
+;; this is easy.
+
+(define_expand "builtin_setjmp_setup"
+ [(unspec [(match_operand 0 "register_operand" "r")] 20)]
+ "TARGET_ABICALLS"
+ "
+{
+ if (Pmode == DImode)
+ emit_insn (gen_builtin_setjmp_setup_64 (operands[0]));
+ else
+ emit_insn (gen_builtin_setjmp_setup_32 (operands[0]));
+ DONE;
+}")
+
+(define_expand "builtin_setjmp_setup_32"
+ [(set (mem:SI (plus:SI (match_operand:SI 0 "register_operand" "r")
+ (const_int 12)))
+ (reg:SI 28))]
+ "TARGET_ABICALLS && ! (Pmode == DImode)"
+ "")
+
+(define_expand "builtin_setjmp_setup_64"
+ [(set (mem:DI (plus:DI (match_operand:DI 0 "register_operand" "r")
+ (const_int 24)))
+ (reg:DI 28))]
+ "TARGET_ABICALLS && Pmode == DImode"
+ "")
+
+;; For o32/n32/n64, we need to arrange for longjmp to put the
+;; target address in t9 so that we can use it for loading $gp.
+
+(define_expand "builtin_longjmp"
+ [(unspec_volatile [(match_operand 0 "register_operand" "r")] 3)]
+ "TARGET_ABICALLS"
+ "
+{
+ /* The elements of the buffer are, in order: */
+ int W = (Pmode == DImode ? 8 : 4);
+ 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, 25);
+ rtx gp = gen_rtx_REG (Pmode, 28);
+
+ /* This bit is the same as expand_builtin_longjmp. */
+ 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)]
+ ""
+ "
+{
+ if (mips_isa >= 0) /* avoid unused code warnings */
+ {
+ 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)] 0)]
+ ""
+ ""
+ [(set_attr "type" "unknown")
+ (set_attr "mode" "none")
+ (set_attr "length" "0")])
+
+(define_expand "epilogue"
+ [(const_int 2)]
+ ""
+ "
+{
+ if (mips_isa >= 0) /* avoid unused code warnings */
+ {
+ mips_expand_epilogue ();
+ 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")
+ (set_attr "length" "1")])
+
+;; Normal return.
+;; We match any mode for the return address, so that this will work with
+;; both 32 bit and 64 bit targets.
+(define_insn "return_internal"
+ [(use (match_operand 0 "register_operand" ""))
+ (return)]
+ ""
+ "*
+{
+ return \"%*j\\t%0\";
+}"
+ [(set_attr "type" "jump")
+ (set_attr "mode" "none")
+ (set_attr "length" "1")])
+
+;; When generating embedded PIC code we need to get the address of the
+;; current function. This specialized instruction does just that.
+
+(define_insn "get_fnaddr"
+ [(set (match_operand 0 "register_operand" "=d")
+ (unspec [(match_operand 1 "" "")] 1))
+ (clobber (reg:SI 31))]
+ "TARGET_EMBEDDED_PIC
+ && GET_CODE (operands[1]) == SYMBOL_REF"
+ "%($LF%= = . + 8\;bal\\t$LF%=\;la\\t%0,%1-$LF%=%)\;addu\\t%0,%0,$31"
+ [(set_attr "type" "call")
+ (set_attr "mode" "none")
+ (set_attr "length" "4")])
+
+
+;; ....................
+;; FUNCTION CALLS
+;; ....................
+
+;; calls.c now passes a third argument, make saber happy
+
+(define_expand "call"
+ [(parallel [(call (match_operand 0 "memory_operand" "m")
+ (match_operand 1 "" "i"))
+ (clobber (reg:SI 31))
+ (use (match_operand 2 "" "")) ;; next_arg_reg
+ (use (match_operand 3 "" ""))])] ;; struct_value_size_rtx
+ ""
+ "
+{
+ rtx addr;
+
+ if (operands[0]) /* eliminate unused code warnings */
+ {
+ addr = XEXP (operands[0], 0);
+ if ((GET_CODE (addr) != REG && (!CONSTANT_ADDRESS_P (addr) || TARGET_LONG_CALLS))
+ || ! call_insn_operand (addr, VOIDmode))
+ XEXP (operands[0], 0) = copy_to_mode_reg (Pmode, addr);
+
+ /* In order to pass small structures by value in registers
+ compatibly with the MIPS compiler, we need to shift the value
+ into the high part of the register. Function_arg has encoded
+ a PARALLEL rtx, holding a vector of adjustments to be made
+ as the next_arg_reg variable, so we split up the insns,
+ and emit them separately. */
+
+ if (operands[2] != (rtx)0 && GET_CODE (operands[2]) == PARALLEL)
+ {
+ rtvec adjust = XVEC (operands[2], 0);
+ int num = GET_NUM_ELEM (adjust);
+ int i;
+
+ for (i = 0; i < num; i++)
+ emit_insn (RTVEC_ELT (adjust, i));
+ }
+
+ if (TARGET_MIPS16
+ && mips16_hard_float
+ && operands[2] != 0
+ && (int) GET_MODE (operands[2]) != 0)
+ {
+ if (build_mips16_call_stub (NULL_RTX, operands[0], operands[1],
+ (int) GET_MODE (operands[2])))
+ DONE;
+ }
+
+ emit_call_insn (gen_call_internal0 (operands[0], operands[1],
+ gen_rtx (REG, SImode, GP_REG_FIRST + 31)));
+
+ DONE;
+ }
+}")
+
+(define_expand "call_internal0"
+ [(parallel [(call (match_operand 0 "" "")
+ (match_operand 1 "" ""))
+ (clobber (match_operand:SI 2 "" ""))])]
+ ""
+ "")
+
+;; We need to recognize reg:SI 31 specially for the mips16, because we
+;; don't have a constraint letter for it.
+
+(define_insn ""
+ [(call (mem (match_operand 0 "call_insn_operand" "ei"))
+ (match_operand 1 "" "i"))
+ (clobber (match_operand:SI 2 "register_operand" "=y"))]
+ "TARGET_MIPS16 && !TARGET_ABICALLS && !TARGET_LONG_CALLS
+ && GET_CODE (operands[2]) == REG && REGNO (operands[2]) == 31"
+ "%*jal\\t%0"
+ [(set_attr "type" "call")
+ (set_attr "mode" "none")
+ (set_attr "length" "2")])
+
+(define_insn "call_internal1"
+ [(call (mem (match_operand 0 "call_insn_operand" "ri"))
+ (match_operand 1 "" "i"))
+ (clobber (match_operand:SI 2 "register_operand" "=d"))]
+ "!TARGET_ABICALLS && !TARGET_LONG_CALLS"
+ "*
+{
+ register rtx target = operands[0];
+
+ if (GET_CODE (target) == SYMBOL_REF)
+ return \"%*jal\\t%0\";
+ else if (GET_CODE (target) == CONST_INT)
+ return \"%[li\\t%@,%0\\n\\t%*jal\\t%2,%@%]\";
+ else
+ return \"%*jal\\t%2,%0\";
+}"
+ [(set_attr "type" "call")
+ (set_attr "mode" "none")
+ (set_attr "length" "1")])
+
+(define_insn "call_internal2"
+ [(call (mem (match_operand 0 "call_insn_operand" "ri"))
+ (match_operand 1 "" "i"))
+ (clobber (match_operand:SI 2 "register_operand" "=d"))]
+ "TARGET_ABICALLS && !TARGET_LONG_CALLS"
+ "*
+{
+ register rtx target = operands[0];
+
+ if (GET_CODE (target) == SYMBOL_REF)
+ {
+ if (GET_MODE (target) == SImode)
+ return \"la\\t%^,%0\\n\\tjal\\t%2,%^\";
+ else
+ return \"dla\\t%^,%0\\n\\tjal\\t%2,%^\";
+ }
+ else if (GET_CODE (target) == CONST_INT)
+ return \"li\\t%^,%0\\n\\tjal\\t%2,%^\";
+ else if (REGNO (target) != PIC_FUNCTION_ADDR_REGNUM)
+ return \"move\\t%^,%0\\n\\tjal\\t%2,%^\";
+ else
+ return \"jal\\t%2,%0\";
+}"
+ [(set_attr "type" "call")
+ (set_attr "mode" "none")
+ (set_attr "length" "2")])
+
+(define_insn "call_internal3a"
+ [(call (mem:SI (match_operand:SI 0 "register_operand" "r"))
+ (match_operand 1 "" "i"))
+ (clobber (match_operand:SI 2 "register_operand" "=d"))]
+ "!(Pmode == DImode) && !TARGET_ABICALLS && TARGET_LONG_CALLS"
+ "%*jal\\t%2,%0"
+ [(set_attr "type" "call")
+ (set_attr "mode" "none")
+ (set_attr "length" "1")])
+
+(define_insn "call_internal3b"
+ [(call (mem:DI (match_operand:DI 0 "se_register_operand" "r"))
+ (match_operand 1 "" "i"))
+ (clobber (match_operand:SI 2 "register_operand" "=d"))]
+ "Pmode == DImode && !TARGET_ABICALLS && TARGET_LONG_CALLS"
+ "%*jal\\t%2,%0"
+ [(set_attr "type" "call")
+ (set_attr "mode" "none")
+ (set_attr "length" "1")])
+
+(define_insn "call_internal4a"
+ [(call (mem:SI (match_operand:SI 0 "register_operand" "r"))
+ (match_operand 1 "" "i"))
+ (clobber (match_operand:SI 2 "register_operand" "=d"))]
+ "!(Pmode == DImode) && TARGET_ABICALLS && TARGET_LONG_CALLS"
+ "*
+{
+ if (REGNO (operands[0]) != PIC_FUNCTION_ADDR_REGNUM)
+ return \"move\\t%^,%0\\n\\tjal\\t%2,%^\";
+ else
+ return \"jal\\t%2,%0\";
+}"
+ [(set_attr "type" "call")
+ (set_attr "mode" "none")
+ (set_attr "length" "2")])
+
+(define_insn "call_internal4b"
+ [(call (mem:DI (match_operand:DI 0 "se_register_operand" "r"))
+ (match_operand 1 "" "i"))
+ (clobber (match_operand:SI 2 "register_operand" "=d"))]
+ "Pmode == DImode && TARGET_ABICALLS && TARGET_LONG_CALLS"
+ "*
+{
+ if (REGNO (operands[0]) != PIC_FUNCTION_ADDR_REGNUM)
+ return \"move\\t%^,%0\\n\\tjal\\t%2,%^\";
+ else
+ return \"jal\\t%2,%0\";
+}"
+ [(set_attr "type" "call")
+ (set_attr "mode" "none")
+ (set_attr "length" "2")])
+
+;; calls.c now passes a fourth argument, make saber happy
+
+(define_expand "call_value"
+ [(parallel [(set (match_operand 0 "register_operand" "=df")
+ (call (match_operand 1 "memory_operand" "m")
+ (match_operand 2 "" "i")))
+ (clobber (reg:SI 31))
+ (use (match_operand 3 "" ""))])] ;; next_arg_reg
+ ""
+ "
+{
+ rtx addr;
+
+ if (operands[0]) /* eliminate unused code warning */
+ {
+ addr = XEXP (operands[1], 0);
+ if ((GET_CODE (addr) != REG && (!CONSTANT_ADDRESS_P (addr) || TARGET_LONG_CALLS))
+ || ! call_insn_operand (addr, VOIDmode))
+ XEXP (operands[1], 0) = copy_to_mode_reg (Pmode, addr);
+
+ /* In order to pass small structures by value in registers
+ compatibly with the MIPS compiler, we need to shift the value
+ into the high part of the register. Function_arg has encoded
+ a PARALLEL rtx, holding a vector of adjustments to be made
+ as the next_arg_reg variable, so we split up the insns,
+ and emit them separately. */
+
+ if (operands[3] != (rtx)0 && GET_CODE (operands[3]) == PARALLEL)
+ {
+ rtvec adjust = XVEC (operands[3], 0);
+ int num = GET_NUM_ELEM (adjust);
+ int i;
+
+ for (i = 0; i < num; i++)
+ emit_insn (RTVEC_ELT (adjust, i));
+ }
+
+ if (TARGET_MIPS16
+ && mips16_hard_float
+ && ((operands[3] != 0
+ && (int) GET_MODE (operands[3]) != 0)
+ || GET_MODE_CLASS (GET_MODE (operands[0])) == MODE_FLOAT))
+ {
+ if (build_mips16_call_stub (operands[0], operands[1], operands[2],
+ (operands[3] == 0 ? 0
+ : (int) GET_MODE (operands[3]))))
+ DONE;
+ }
+
+ /* Handle Irix6 function calls that have multiple non-contiguous
+ results. */
+ if (GET_CODE (operands[0]) == PARALLEL && XVECLEN (operands[0], 0) > 1)
+ {
+ emit_call_insn (gen_call_value_multiple_internal0
+ (XEXP (XVECEXP (operands[0], 0, 0), 0),
+ operands[1], operands[2],
+ XEXP (XVECEXP (operands[0], 0, 1), 0),
+ gen_rtx (REG, SImode, GP_REG_FIRST + 31)));
+ DONE;
+ }
+
+ /* We have a call returning a DImode structure in an FP reg.
+ Strip off the now unnecessary PARALLEL. */
+ if (GET_CODE (operands[0]) == PARALLEL)
+ operands[0] = XEXP (XVECEXP (operands[0], 0, 0), 0);
+
+ emit_call_insn (gen_call_value_internal0 (operands[0], operands[1], operands[2],
+ gen_rtx (REG, SImode, GP_REG_FIRST + 31)));
+
+ DONE;
+ }
+}")
+
+(define_expand "call_value_internal0"
+ [(parallel [(set (match_operand 0 "" "")
+ (call (match_operand 1 "" "")
+ (match_operand 2 "" "")))
+ (clobber (match_operand:SI 3 "" ""))])]
+ ""
+ "")
+
+;; Recognize $31 specially on the mips16, because we don't have a
+;; constraint letter for it.
+
+(define_insn ""
+ [(set (match_operand 0 "register_operand" "=d")
+ (call (mem (match_operand 1 "call_insn_operand" "ei"))
+ (match_operand 2 "" "i")))
+ (clobber (match_operand:SI 3 "register_operand" "=y"))]
+ "TARGET_MIPS16 && !TARGET_ABICALLS && !TARGET_LONG_CALLS
+ && GET_CODE (operands[3]) == REG && REGNO (operands[3]) == 31"
+ "%*jal\\t%1"
+ [(set_attr "type" "call")
+ (set_attr "mode" "none")
+ (set_attr "length" "2")])
+
+(define_insn "call_value_internal1"
+ [(set (match_operand 0 "register_operand" "=df")
+ (call (mem (match_operand 1 "call_insn_operand" "ri"))
+ (match_operand 2 "" "i")))
+ (clobber (match_operand:SI 3 "register_operand" "=d"))]
+ "!TARGET_ABICALLS && !TARGET_LONG_CALLS"
+ "*
+{
+ register rtx target = operands[1];
+
+ if (GET_CODE (target) == SYMBOL_REF)
+ return \"%*jal\\t%1\";
+ else if (GET_CODE (target) == CONST_INT)
+ return \"%[li\\t%@,%1\\n\\t%*jal\\t%3,%@%]\";
+ else
+ return \"%*jal\\t%3,%1\";
+}"
+ [(set_attr "type" "call")
+ (set_attr "mode" "none")
+ (set_attr "length" "1")])
+
+(define_insn "call_value_internal2"
+ [(set (match_operand 0 "register_operand" "=df")
+ (call (mem (match_operand 1 "call_insn_operand" "ri"))
+ (match_operand 2 "" "i")))
+ (clobber (match_operand:SI 3 "register_operand" "=d"))]
+ "TARGET_ABICALLS && !TARGET_LONG_CALLS"
+ "*
+{
+ register rtx target = operands[1];
+
+ if (GET_CODE (target) == SYMBOL_REF)
+ {
+ if (GET_MODE (target) == SImode)
+ return \"la\\t%^,%1\\n\\tjal\\t%3,%^\";
+ else
+ return \"dla\\t%^,%1\\n\\tjal\\t%3,%^\";
+ }
+ else if (GET_CODE (target) == CONST_INT)
+ return \"li\\t%^,%1\\n\\tjal\\t%3,%^\";
+ else if (REGNO (target) != PIC_FUNCTION_ADDR_REGNUM)
+ return \"move\\t%^,%1\\n\\tjal\\t%3,%^\";
+ else
+ return \"jal\\t%3,%1\";
+}"
+ [(set_attr "type" "call")
+ (set_attr "mode" "none")
+ (set_attr "length" "2")])
+
+(define_insn "call_value_internal3a"
+ [(set (match_operand 0 "register_operand" "=df")
+ (call (mem:SI (match_operand:SI 1 "register_operand" "r"))
+ (match_operand 2 "" "i")))
+ (clobber (match_operand:SI 3 "register_operand" "=d"))]
+ "!TARGET_MIPS16
+ && !(Pmode == DImode) && !TARGET_ABICALLS && TARGET_LONG_CALLS"
+ "%*jal\\t%3,%1"
+ [(set_attr "type" "call")
+ (set_attr "mode" "none")
+ (set_attr "length" "1")])
+
+(define_insn "call_value_internal3b"
+ [(set (match_operand 0 "register_operand" "=df")
+ (call (mem:DI (match_operand:DI 1 "se_register_operand" "r"))
+ (match_operand 2 "" "i")))
+ (clobber (match_operand:SI 3 "register_operand" "=d"))]
+ "!TARGET_MIPS16
+ && Pmode == DImode && !TARGET_ABICALLS && TARGET_LONG_CALLS"
+ "%*jal\\t%3,%1"
+ [(set_attr "type" "call")
+ (set_attr "mode" "none")
+ (set_attr "length" "1")])
+
+(define_insn "call_value_internal3c"
+ [(set (match_operand 0 "register_operand" "=df")
+ (call (mem:SI (match_operand:SI 1 "register_operand" "e"))
+ (match_operand 2 "" "i")))
+ (clobber (match_operand:SI 3 "register_operand" "=y"))]
+ "TARGET_MIPS16 && !(Pmode == DImode) && !TARGET_ABICALLS && TARGET_LONG_CALLS
+ && GET_CODE (operands[3]) == REG && REGNO (operands[3]) == 31"
+ "%*jal\\t%3,%1"
+ [(set_attr "type" "call")
+ (set_attr "mode" "none")
+ (set_attr "length" "1")])
+
+(define_insn "call_value_internal4a"
+ [(set (match_operand 0 "register_operand" "=df")
+ (call (mem:SI (match_operand:SI 1 "register_operand" "r"))
+ (match_operand 2 "" "i")))
+ (clobber (match_operand:SI 3 "register_operand" "=d"))]
+ "!(Pmode == DImode) && TARGET_ABICALLS && TARGET_LONG_CALLS"
+ "*
+{
+ if (REGNO (operands[1]) != PIC_FUNCTION_ADDR_REGNUM)
+ return \"move\\t%^,%1\\n\\tjal\\t%3,%^\";
+ else
+ return \"jal\\t%3,%1\";
+}"
+ [(set_attr "type" "call")
+ (set_attr "mode" "none")
+ (set_attr "length" "2")])
+
+(define_insn "call_value_internal4b"
+ [(set (match_operand 0 "register_operand" "=df")
+ (call (mem:DI (match_operand:DI 1 "se_register_operand" "r"))
+ (match_operand 2 "" "i")))
+ (clobber (match_operand:SI 3 "register_operand" "=d"))]
+ "Pmode == DImode && TARGET_ABICALLS && TARGET_LONG_CALLS"
+ "*
+{
+ if (REGNO (operands[1]) != PIC_FUNCTION_ADDR_REGNUM)
+ return \"move\\t%^,%1\\n\\tjal\\t%3,%^\";
+ else
+ return \"jal\\t%3,%1\";
+}"
+ [(set_attr "type" "call")
+ (set_attr "mode" "none")
+ (set_attr "length" "2")])
+
+(define_expand "call_value_multiple_internal0"
+ [(parallel [(set (match_operand 0 "" "")
+ (call (match_operand 1 "" "")
+ (match_operand 2 "" "")))
+ (set (match_operand 3 "" "")
+ (call (match_dup 1)
+ (match_dup 2)))
+ (clobber (match_operand:SI 4 "" ""))])]
+ ""
+ "")
+
+;; ??? May eventually need all 6 versions of the call patterns with multiple
+;; return values.
+
+(define_insn "call_value_multiple_internal2"
+ [(set (match_operand 0 "register_operand" "=df")
+ (call (mem (match_operand 1 "call_insn_operand" "ri"))
+ (match_operand 2 "" "i")))
+ (set (match_operand 3 "register_operand" "=df")
+ (call (mem (match_dup 1))
+ (match_dup 2)))
+ (clobber (match_operand:SI 4 "register_operand" "=d"))]
+ "TARGET_ABICALLS && !TARGET_LONG_CALLS"
+ "*
+{
+ register rtx target = operands[1];
+
+ if (GET_CODE (target) == SYMBOL_REF)
+ {
+ if (GET_MODE (target) == SImode)
+ return \"la\\t%^,%1\\n\\tjal\\t%4,%^\";
+ else
+ return \"la\\t%^,%1\\n\\tjal\\t%4,%^\";
+ }
+ else if (GET_CODE (target) == CONST_INT)
+ return \"li\\t%^,%1\\n\\tjal\\t%4,%^\";
+ else if (REGNO (target) != PIC_FUNCTION_ADDR_REGNUM)
+ return \"move\\t%^,%1\\n\\tjal\\t%4,%^\";
+ else
+ return \"jal\\t%4,%1\";
+}"
+ [(set_attr "type" "call")
+ (set_attr "mode" "none")
+ (set_attr "length" "2")])
+
+
+;; Call subroutine returning any type.
+
+(define_expand "untyped_call"
+ [(parallel [(call (match_operand 0 "" "")
+ (const_int 0))
+ (match_operand 1 "" "")
+ (match_operand 2 "" "")])]
+ ""
+ "
+{
+ if (operands[0]) /* silence statement not reached warnings */
+ {
+ 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 "nop"
+ [(const_int 0)]
+ ""
+ "%(nop%)"
+ [(set_attr "type" "nop")
+ (set_attr "mode" "none")
+ (set_attr "length" "1")])
+
+;; The MIPS chip does not seem to require stack probes.
+;; (define_expand "probe"
+;; [(set (match_dup 0)
+;; (match_dup 1))]
+;; ""
+;; "
+;; {
+;; operands[0] = gen_reg_rtx (SImode);
+;; operands[1] = gen_rtx (MEM, SImode, stack_pointer_rtx);
+;; MEM_VOLATILE_P (operands[1]) = TRUE;
+;; /* fall through and generate default code */
+;; }")
+
+;; MIPS4 Conditional move instructions.
+
+(define_insn ""
+ [(set (match_operand:SI 0 "register_operand" "=d,d")
+ (if_then_else:SI
+ (match_operator 4 "equality_op"
+ [(match_operand:SI 1 "register_operand" "d,d")
+ (const_int 0)])
+ (match_operand:SI 2 "reg_or_0_operand" "dJ,0")
+ (match_operand:SI 3 "reg_or_0_operand" "0,dJ")))]
+ "mips_isa >= 4 || 0" ;; CYGNUS LOCAL law
+ "@
+ mov%B4\\t%0,%z2,%1
+ mov%b4\\t%0,%z3,%1"
+ [(set_attr "type" "move")
+ (set_attr "mode" "SI")])
+
+(define_insn ""
+ [(set (match_operand:SI 0 "register_operand" "=d,d")
+ (if_then_else:SI
+ (match_operator 4 "equality_op"
+ [(match_operand:DI 1 "se_register_operand" "d,d")
+ (const_int 0)])
+ (match_operand:SI 2 "reg_or_0_operand" "dJ,0")
+ (match_operand:SI 3 "reg_or_0_operand" "0,dJ")))]
+ "mips_isa >= 4 || 0" ;; CYGNUS LOCAL law
+ "@
+ mov%B4\\t%0,%z2,%1
+ mov%b4\\t%0,%z3,%1"
+ [(set_attr "type" "move")
+ (set_attr "mode" "SI")])
+
+(define_insn ""
+ [(set (match_operand:SI 0 "register_operand" "=d,d")
+ (if_then_else:SI
+ (match_operator 3 "equality_op" [(match_operand:CC 4
+ "register_operand"
+ "z,z")
+ (const_int 0)])
+ (match_operand:SI 1 "reg_or_0_operand" "dJ,0")
+ (match_operand:SI 2 "reg_or_0_operand" "0,dJ")))]
+ "mips_isa >= 4 && TARGET_HARD_FLOAT"
+ "@
+ mov%T3\\t%0,%z1,%4
+ mov%t3\\t%0,%z2,%4"
+ [(set_attr "type" "move")
+ (set_attr "mode" "SI")])
+
+(define_insn ""
+ [(set (match_operand:DI 0 "register_operand" "=d,d")
+ (if_then_else:DI
+ (match_operator 4 "equality_op"
+ [(match_operand:SI 1 "register_operand" "d,d")
+ (const_int 0)])
+ (match_operand:DI 2 "se_reg_or_0_operand" "dJ,0")
+ (match_operand:DI 3 "se_reg_or_0_operand" "0,dJ")))]
+ "mips_isa >= 4 || 0" ;; CYGNUS LOCAL law
+ "@
+ mov%B4\\t%0,%z2,%1
+ mov%b4\\t%0,%z3,%1"
+ [(set_attr "type" "move")
+ (set_attr "mode" "DI")])
+
+(define_insn ""
+ [(set (match_operand:DI 0 "register_operand" "=d,d")
+ (if_then_else:DI
+ (match_operator 4 "equality_op"
+ [(match_operand:DI 1 "se_register_operand" "d,d")
+ (const_int 0)])
+ (match_operand:DI 2 "se_reg_or_0_operand" "dJ,0")
+ (match_operand:DI 3 "se_reg_or_0_operand" "0,dJ")))]
+ "mips_isa >= 4 || 0" ;; CYGNUS LOCAL law
+ "@
+ mov%B4\\t%0,%z2,%1
+ mov%b4\\t%0,%z3,%1"
+ [(set_attr "type" "move")
+ (set_attr "mode" "DI")])
+
+(define_insn ""
+ [(set (match_operand:DI 0 "register_operand" "=d,d")
+ (if_then_else:DI
+ (match_operator 3 "equality_op" [(match_operand:CC 4
+ "register_operand"
+ "z,z")
+ (const_int 0)])
+ (match_operand:DI 1 "se_reg_or_0_operand" "dJ,0")
+ (match_operand:DI 2 "se_reg_or_0_operand" "0,dJ")))]
+ "mips_isa >= 4 && TARGET_HARD_FLOAT"
+ "@
+ mov%T3\\t%0,%z1,%4
+ mov%t3\\t%0,%z2,%4"
+ [(set_attr "type" "move")
+ (set_attr "mode" "DI")])
+
+(define_insn ""
+ [(set (match_operand:SF 0 "register_operand" "=f,f")
+ (if_then_else:SF
+ (match_operator 4 "equality_op"
+ [(match_operand:SI 1 "register_operand" "d,d")
+ (const_int 0)])
+ (match_operand:SF 2 "register_operand" "f,0")
+ (match_operand:SF 3 "register_operand" "0,f")))]
+ "mips_isa >= 4 && TARGET_HARD_FLOAT"
+ "@
+ mov%B4.s\\t%0,%2,%1
+ mov%b4.s\\t%0,%3,%1"
+ [(set_attr "type" "move")
+ (set_attr "mode" "SF")])
+
+(define_insn ""
+ [(set (match_operand:SF 0 "register_operand" "=f,f")
+ (if_then_else:SF
+ (match_operator 3 "equality_op" [(match_operand:CC 4
+ "register_operand"
+ "z,z")
+ (const_int 0)])
+ (match_operand:SF 1 "register_operand" "f,0")
+ (match_operand:SF 2 "register_operand" "0,f")))]
+ "mips_isa >= 4 && TARGET_HARD_FLOAT"
+ "@
+ mov%T3.s\\t%0,%1,%4
+ mov%t3.s\\t%0,%2,%4"
+ [(set_attr "type" "move")
+ (set_attr "mode" "SF")])
+
+(define_insn ""
+ [(set (match_operand:DF 0 "register_operand" "=f,f")
+ (if_then_else:DF
+ (match_operator 4 "equality_op"
+ [(match_operand:SI 1 "register_operand" "d,d")
+ (const_int 0)])
+ (match_operand:DF 2 "register_operand" "f,0")
+ (match_operand:DF 3 "register_operand" "0,f")))]
+ "mips_isa >= 4 && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
+ "@
+ mov%B4.d\\t%0,%2,%1
+ mov%b4.d\\t%0,%3,%1"
+ [(set_attr "type" "move")
+ (set_attr "mode" "DF")])
+
+(define_insn ""
+ [(set (match_operand:DF 0 "register_operand" "=f,f")
+ (if_then_else:DF
+ (match_operator 3 "equality_op" [(match_operand:CC 4
+ "register_operand"
+ "z,z")
+ (const_int 0)])
+ (match_operand:DF 1 "register_operand" "f,0")
+ (match_operand:DF 2 "register_operand" "0,f")))]
+ "mips_isa >= 4 && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
+ "@
+ mov%T3.d\\t%0,%1,%4
+ mov%t3.d\\t%0,%2,%4"
+ [(set_attr "type" "move")
+ (set_attr "mode" "DF")])
+
+;; These are the main define_expand's used to make conditional moves.
+
+(define_expand "movsicc"
+ [(set (match_dup 4) (match_operand 1 "comparison_operator" ""))
+ (set (match_operand:SI 0 "register_operand" "")
+ (if_then_else:SI (match_dup 5)
+ (match_operand:SI 2 "reg_or_0_operand" "")
+ (match_operand:SI 3 "reg_or_0_operand" "")))]
+ "mips_isa >= 4 || 0" ;; CYGNUS LOCAL law
+ "
+{
+
+ gen_conditional_move (operands);
+ DONE;
+}")
+
+(define_expand "movdicc"
+ [(set (match_dup 4) (match_operand 1 "comparison_operator" ""))
+ (set (match_operand:DI 0 "register_operand" "")
+ (if_then_else:DI (match_dup 5)
+ (match_operand:DI 2 "se_reg_or_0_operand" "")
+ (match_operand:DI 3 "se_reg_or_0_operand" "")))]
+ "mips_isa >= 4 || 0" ;; CYGNUS LOCAL law
+ "
+{
+
+ gen_conditional_move (operands);
+ DONE;
+}")
+
+(define_expand "movsfcc"
+ [(set (match_dup 4) (match_operand 1 "comparison_operator" ""))
+ (set (match_operand:SF 0 "register_operand" "")
+ (if_then_else:SF (match_dup 5)
+ (match_operand:SF 2 "register_operand" "")
+ (match_operand:SF 3 "register_operand" "")))]
+ "mips_isa >= 4 && TARGET_HARD_FLOAT"
+ "
+{
+ gen_conditional_move (operands);
+ DONE;
+}")
+
+(define_expand "movdfcc"
+ [(set (match_dup 4) (match_operand 1 "comparison_operator" ""))
+ (set (match_operand:DF 0 "register_operand" "")
+ (if_then_else:DF (match_dup 5)
+ (match_operand:DF 2 "register_operand" "")
+ (match_operand:DF 3 "register_operand" "")))]
+ "mips_isa >= 4 && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
+ "
+{
+ gen_conditional_move (operands);
+ DONE;
+}")
+
+;; ....................
+;; mips16 inline constant tables
+;; ....................
+
+(define_insn "consttable_qi"
+ [(unspec_volatile [(match_operand:QI 0 "consttable_operand" "=g")] 10)]
+ "TARGET_MIPS16"
+ "*
+{
+ assemble_integer (operands[0], 1, 1);
+ return \"\";
+}"
+ [(set_attr "type" "unknown")
+ (set_attr "mode" "QI")
+ (set_attr "length" "2")])
+
+(define_insn "consttable_hi"
+ [(unspec_volatile [(match_operand:HI 0 "consttable_operand" "=g")] 11)]
+ "TARGET_MIPS16"
+ "*
+{
+ assemble_integer (operands[0], 2, 1);
+ return \"\";
+}"
+ [(set_attr "type" "unknown")
+ (set_attr "mode" "HI")
+ (set_attr "length" "2")])
+
+(define_insn "consttable_si"
+ [(unspec_volatile [(match_operand:SI 0 "consttable_operand" "=g")] 12)]
+ "TARGET_MIPS16"
+ "*
+{
+ assemble_integer (operands[0], 4, 1);
+ return \"\";
+}"
+ [(set_attr "type" "unknown")
+ (set_attr "mode" "SI")
+ (set_attr "length" "2")])
+
+(define_insn "consttable_di"
+ [(unspec_volatile [(match_operand:DI 0 "consttable_operand" "=g")] 13)]
+ "TARGET_MIPS16"
+ "*
+{
+ assemble_integer (operands[0], 8, 1);
+ return \"\";
+}"
+ [(set_attr "type" "unknown")
+ (set_attr "mode" "DI")
+ (set_attr "length" "4")])
+
+(define_insn "consttable_sf"
+ [(unspec_volatile [(match_operand:SF 0 "consttable_operand" "=g")] 14)]
+ "TARGET_MIPS16"
+ "*
+{
+ union real_extract u;
+
+ if (GET_CODE (operands[0]) != CONST_DOUBLE)
+ abort ();
+ bcopy ((char *) &CONST_DOUBLE_LOW (operands[0]), (char *) &u, sizeof u);
+ assemble_real (u.d, SFmode);
+ return \"\";
+}"
+ [(set_attr "type" "unknown")
+ (set_attr "mode" "SF")
+ (set_attr "length" "2")])
+
+(define_insn "consttable_df"
+ [(unspec_volatile [(match_operand:DF 0 "consttable_operand" "=g")] 15)]
+ "TARGET_MIPS16"
+ "*
+{
+ union real_extract u;
+
+ if (GET_CODE (operands[0]) != CONST_DOUBLE)
+ abort ();
+ bcopy ((char *) &CONST_DOUBLE_LOW (operands[0]), (char *) &u, sizeof u);
+ assemble_real (u.d, DFmode);
+ return \"\";
+}"
+ [(set_attr "type" "unknown")
+ (set_attr "mode" "DF")
+ (set_attr "length" "4")])
+
+(define_insn "align_2"
+ [(unspec_volatile [(const_int 0)] 16)]
+ "TARGET_MIPS16"
+ ".align 1"
+ [(set_attr "type" "unknown")
+ (set_attr "mode" "HI")
+ (set_attr "length" "2")])
+
+(define_insn "align_4"
+ [(unspec_volatile [(const_int 0)] 17)]
+ "TARGET_MIPS16"
+ ".align 2"
+ [(set_attr "type" "unknown")
+ (set_attr "mode" "SI")
+ (set_attr "length" "2")])
+
+(define_insn "align_8"
+ [(unspec_volatile [(const_int 0)] 18)]
+ "TARGET_MIPS16"
+ ".align 3"
+ [(set_attr "type" "unknown")
+ (set_attr "mode" "DI")
+ (set_attr "length" "3")])
+
+;; ....................
+;; mips16 peepholes
+;; ....................
+
+;; On the mips16, reload will sometimes decide that a pseudo register
+;; should go into $24, and then later on have to reload that register.
+;; When that happens, we get a load of a general register followed by
+;; a move from the general register to $24 followed by a branch.
+;; These peepholes catch the common case, and fix it to just use the
+;; general register for the branch.
+
+(define_peephole
+ [(set (match_operand:SI 0 "register_operand" "=t")
+ (match_operand:SI 1 "register_operand" "d"))
+ (set (pc)
+ (if_then_else (match_operator:SI 2 "equality_op" [(match_dup 0)
+ (const_int 0)])
+ (match_operand 3 "pc_or_label_operand" "")
+ (match_operand 4 "pc_or_label_operand" "")))]
+ "TARGET_MIPS16
+ && GET_CODE (operands[0]) == REG
+ && REGNO (operands[0]) == 24
+ && dead_or_set_p (insn, operands[0])
+ && GET_CODE (operands[1]) == REG
+ && M16_REG_P (REGNO (operands[1]))"
+ "*
+{
+ if (operands[3] != pc_rtx)
+ return \"%*b%C2z\\t%1,%3\";
+ else
+ return \"%*b%N2z\\t%1,%4\";
+}"
+ [(set_attr "type" "branch")
+ (set_attr "mode" "none")
+ (set_attr "length" "2")])
+
+(define_peephole
+ [(set (match_operand:DI 0 "register_operand" "=t")
+ (match_operand:DI 1 "register_operand" "d"))
+ (set (pc)
+ (if_then_else (match_operator:DI 2 "equality_op" [(match_dup 0)
+ (const_int 0)])
+ (match_operand 3 "pc_or_label_operand" "")
+ (match_operand 4 "pc_or_label_operand" "")))]
+ "TARGET_MIPS16 && TARGET_64BIT
+ && GET_CODE (operands[0]) == REG
+ && REGNO (operands[0]) == 24
+ && dead_or_set_p (insn, operands[0])
+ && GET_CODE (operands[1]) == REG
+ && M16_REG_P (REGNO (operands[1]))"
+ "*
+{
+ if (operands[3] != pc_rtx)
+ return \"%*b%C2z\\t%1,%3\";
+ else
+ return \"%*b%N2z\\t%1,%4\";
+}"
+ [(set_attr "type" "branch")
+ (set_attr "mode" "none")
+ (set_attr "length" "2")])
+
+;; We can also have the reverse reload: reload will spill $24 into
+;; another register, and then do a branch on that register when it
+;; could have just stuck with $24.
+
+(define_peephole
+ [(set (match_operand:SI 0 "register_operand" "=d")
+ (match_operand:SI 1 "register_operand" "t"))
+ (set (pc)
+ (if_then_else (match_operator:SI 2 "equality_op" [(match_dup 0)
+ (const_int 0)])
+ (match_operand 3 "pc_or_label_operand" "")
+ (match_operand 4 "pc_or_label_operand" "")))]
+ "TARGET_MIPS16
+ && GET_CODE (operands[1]) == REG
+ && REGNO (operands[1]) == 24
+ && GET_CODE (operands[0]) == REG
+ && M16_REG_P (REGNO (operands[0]))
+ && dead_or_set_p (insn, operands[0])"
+ "*
+{
+ if (operands[3] != pc_rtx)
+ return \"%*bt%C2z\\t%3\";
+ else
+ return \"%*bt%N2z\\t%4\";
+}"
+ [(set_attr "type" "branch")
+ (set_attr "mode" "none")
+ (set_attr "length" "2")])
+
+(define_peephole
+ [(set (match_operand:DI 0 "register_operand" "=d")
+ (match_operand:DI 1 "register_operand" "t"))
+ (set (pc)
+ (if_then_else (match_operator:DI 2 "equality_op" [(match_dup 0)
+ (const_int 0)])
+ (match_operand 3 "pc_or_label_operand" "")
+ (match_operand 4 "pc_or_label_operand" "")))]
+ "TARGET_MIPS16 && TARGET_64BIT
+ && GET_CODE (operands[1]) == REG
+ && REGNO (operands[1]) == 24
+ && GET_CODE (operands[0]) == REG
+ && M16_REG_P (REGNO (operands[0]))
+ && dead_or_set_p (insn, operands[0])"
+ "*
+{
+ if (operands[3] != pc_rtx)
+ return \"%*bt%C2z\\t%3\";
+ else
+ return \"%*bt%N2z\\t%4\";
+}"
+ [(set_attr "type" "branch")
+ (set_attr "mode" "none")
+ (set_attr "length" "2")])