--- /dev/null
+;; Machine Description for LoongArch for GNU compiler.
+;; Copyright (C) 2021-2022 Free Software Foundation, Inc.
+;; Contributed by Loongson Ltd.
+;; Based on MIPS target for GNU compiler.
+
+;; This file is part of GCC.
+
+;; GCC is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 3, or (at your option)
+;; any later version.
+
+;; GCC is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GCC; see the file COPYING3. If not see
+;; <http://www.gnu.org/licenses/>.
+
+(define_c_enum "unspec" [
+ ;; Integer operations that are too cumbersome to describe directly.
+ UNSPEC_REVB_2H
+ UNSPEC_REVB_4H
+ UNSPEC_REVH_D
+
+ ;; Floating-point moves.
+ UNSPEC_LOAD_LOW
+ UNSPEC_LOAD_HIGH
+ UNSPEC_STORE_WORD
+ UNSPEC_MOVGR2FRH
+ UNSPEC_MOVFRH2GR
+
+ ;; Floating point unspecs.
+ UNSPEC_FRINT
+ UNSPEC_FCLASS
+
+ ;; Override return address for exception handling.
+ UNSPEC_EH_RETURN
+
+ ;; Bit operation
+ UNSPEC_BYTEPICK_W
+ UNSPEC_BYTEPICK_D
+ UNSPEC_BITREV_4B
+ UNSPEC_BITREV_8B
+
+ ;; TLS
+ UNSPEC_TLS_GD
+ UNSPEC_TLS_LD
+ UNSPEC_TLS_LE
+ UNSPEC_TLS_IE
+
+ ;; Stack tie
+ UNSPEC_TIE
+
+ ;; CRC
+ UNSPEC_CRC
+ UNSPEC_CRCC
+])
+
+(define_c_enum "unspecv" [
+ ;; Blockage and synchronisation.
+ UNSPECV_BLOCKAGE
+ UNSPECV_DBAR
+ UNSPECV_IBAR
+
+ ;; Privileged instructions
+ UNSPECV_CSRRD
+ UNSPECV_CSRWR
+ UNSPECV_CSRXCHG
+ UNSPECV_IOCSRRD
+ UNSPECV_IOCSRWR
+ UNSPECV_CACOP
+ UNSPECV_LDDIR
+ UNSPECV_LDPTE
+ UNSPECV_ERTN
+
+ ;; Stack checking.
+ UNSPECV_PROBE_STACK_RANGE
+
+ ;; Floating-point environment.
+ UNSPECV_MOVFCSR2GR
+ UNSPECV_MOVGR2FCSR
+
+ ;; Others
+ UNSPECV_CPUCFG
+ UNSPECV_ASRTLE_D
+ UNSPECV_ASRTGT_D
+ UNSPECV_SYSCALL
+ UNSPECV_BREAK
+])
+
+(define_constants
+ [(RETURN_ADDR_REGNUM 1)
+ (T0_REGNUM 12)
+ (T1_REGNUM 13)
+ (S0_REGNUM 23)
+
+ ;; PIC long branch sequences are never longer than 100 bytes.
+ (MAX_PIC_BRANCH_LENGTH 100)
+])
+
+(include "predicates.md")
+(include "constraints.md")
+\f
+;; ....................
+;;
+;; Attributes
+;;
+;; ....................
+
+(define_attr "got" "unset,load"
+ (const_string "unset"))
+
+;; For jirl instructions, this attribute is DIRECT when the target address
+;; is symbolic and INDIRECT when it is a register.
+(define_attr "jirl" "unset,direct,indirect"
+ (const_string "unset"))
+
+
+;; Classification of moves, extensions and truncations. Most values
+;; are as for "type" (see below) but there are also the following
+;; move-specific values:
+;;
+;; sll0 "slli.w DEST,SRC,0", which on 64-bit targets is guaranteed
+;; to produce a sign-extended DEST, even if SRC is not
+;; properly sign-extended
+;; pick_ins BSTRPICK.W, BSTRPICK.D, BSTRINS.W or BSTRINS.D instruction
+;; andi a single ANDI instruction
+;; shift_shift a shift left followed by a shift right
+;;
+;; This attribute is used to determine the instruction's length and
+;; scheduling type. For doubleword moves, the attribute always describes
+;; the split instructions; in some cases, it is more appropriate for the
+;; scheduling type to be "multi" instead.
+(define_attr "move_type"
+ "unknown,load,fpload,store,fpstore,mgtf,mftg,imul,move,fmove,
+ const,signext,pick_ins,logical,arith,sll0,andi,shift_shift"
+ (const_string "unknown"))
+
+(define_attr "alu_type" "unknown,add,sub,not,nor,and,or,xor"
+ (const_string "unknown"))
+
+;; Main data type used by the insn
+(define_attr "mode" "unknown,none,QI,HI,SI,DI,TI,SF,DF,TF,FCC"
+ (const_string "unknown"))
+
+;; True if the main data type is twice the size of a word.
+(define_attr "dword_mode" "no,yes"
+ (cond [(and (eq_attr "mode" "DI,DF")
+ (not (match_test "TARGET_64BIT")))
+ (const_string "yes")
+
+ (and (eq_attr "mode" "TI,TF")
+ (match_test "TARGET_64BIT"))
+ (const_string "yes")]
+ (const_string "no")))
+
+;; True if the main data type is four times of the size of a word.
+(define_attr "qword_mode" "no,yes"
+ (cond [(and (eq_attr "mode" "TI,TF")
+ (not (match_test "TARGET_64BIT")))
+ (const_string "yes")]
+ (const_string "no")))
+
+;; Classification of each insn.
+;; branch conditional branch
+;; jump unconditional jump
+;; call unconditional call
+;; load load instruction(s)
+;; fpload floating point load
+;; fpidxload floating point indexed load
+;; store store instruction(s)
+;; fpstore floating point store
+;; fpidxstore floating point indexed store
+;; prefetch memory prefetch (register + offset)
+;; prefetchx memory indexed prefetch (register + register)
+;; condmove conditional moves
+;; mgtf move general-purpose register to floating point register
+;; mftg move floating point register to general-purpose register
+;; const load constant
+;; arith integer arithmetic instructions
+;; logical integer logical instructions
+;; shift integer shift instructions
+;; slt set less than instructions
+;; signext sign extend instructions
+;; clz the clz and clo instructions
+;; trap trap if instructions
+;; imul integer multiply
+;; idiv integer divide
+;; move integer move
+;; fmove floating point register move
+;; fadd floating point add/subtract
+;; fmul floating point multiply
+;; fmadd floating point multiply-add
+;; fdiv floating point divide
+;; frdiv floating point reciprocal divide
+;; 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
+;; multi multiword sequence (or user asm statements)
+;; atomic atomic memory update instruction
+;; syncloop memory atomic operation implemented as a sync loop
+;; nop no operation
+;; ghost an instruction that produces no real code
+(define_attr "type"
+ "unknown,branch,jump,call,load,fpload,fpidxload,store,fpstore,fpidxstore,
+ prefetch,prefetchx,condmove,mgtf,mftg,const,arith,logical,
+ shift,slt,signext,clz,trap,imul,idiv,move,
+ fmove,fadd,fmul,fmadd,fdiv,frdiv,fabs,fneg,fcmp,fcvt,fsqrt,
+ frsqrt,accext,accmod,multi,atomic,syncloop,nop,ghost"
+ (cond [(eq_attr "jirl" "!unset") (const_string "call")
+ (eq_attr "got" "load") (const_string "load")
+
+ (eq_attr "alu_type" "add,sub") (const_string "arith")
+
+ (eq_attr "alu_type" "not,nor,and,or,xor") (const_string "logical")
+
+ ;; If a doubleword move uses these expensive instructions,
+ ;; it is usually better to schedule them in the same way
+ ;; as the singleword form, rather than as "multi".
+ (eq_attr "move_type" "load") (const_string "load")
+ (eq_attr "move_type" "fpload") (const_string "fpload")
+ (eq_attr "move_type" "store") (const_string "store")
+ (eq_attr "move_type" "fpstore") (const_string "fpstore")
+ (eq_attr "move_type" "mgtf") (const_string "mgtf")
+ (eq_attr "move_type" "mftg") (const_string "mftg")
+
+ ;; These types of move are always single insns.
+ (eq_attr "move_type" "imul") (const_string "imul")
+ (eq_attr "move_type" "fmove") (const_string "fmove")
+ (eq_attr "move_type" "signext") (const_string "signext")
+ (eq_attr "move_type" "pick_ins") (const_string "arith")
+ (eq_attr "move_type" "arith") (const_string "arith")
+ (eq_attr "move_type" "logical") (const_string "logical")
+ (eq_attr "move_type" "sll0") (const_string "shift")
+ (eq_attr "move_type" "andi") (const_string "logical")
+
+ ;; These types of move are always split.
+ (eq_attr "move_type" "shift_shift")
+ (const_string "multi")
+
+ ;; These types of move are split for quadword modes only.
+ (and (eq_attr "move_type" "move,const")
+ (eq_attr "qword_mode" "yes"))
+ (const_string "multi")
+
+ ;; These types of move are split for doubleword modes only.
+ (and (eq_attr "move_type" "move,const")
+ (eq_attr "dword_mode" "yes"))
+ (const_string "multi")
+ (eq_attr "move_type" "move") (const_string "move")
+ (eq_attr "move_type" "const") (const_string "const")]
+ (const_string "unknown")))
+
+;; Mode for conversion types (fcvt)
+;; I2S integer to float single (SI/DI to SF)
+;; I2D integer to float double (SI/DI to DF)
+;; S2I float to integer (SF to SI/DI)
+;; D2I float to integer (DF to SI/DI)
+;; D2S double to float single
+;; S2D float single to double
+
+(define_attr "cnv_mode" "unknown,I2S,I2D,S2I,D2I,D2S,S2D"
+ (const_string "unknown"))
+
+;; The number of individual instructions that a non-branch pattern generates
+(define_attr "insn_count" ""
+ (cond [;; "Ghost" instructions occupy no space.
+ (eq_attr "type" "ghost")
+ (const_int 0)
+
+ ;; Check for doubleword moves that are decomposed into two
+ ;; instructions.
+ (and (eq_attr "move_type" "mgtf,mftg,move")
+ (eq_attr "dword_mode" "yes"))
+ (const_int 2)
+
+ ;; Check for quadword moves that are decomposed into four
+ ;; instructions.
+ (and (eq_attr "move_type" "mgtf,mftg,move")
+ (eq_attr "qword_mode" "yes"))
+ (const_int 4)
+
+ ;; Constants, loads and stores are handled by external routines.
+ (and (eq_attr "move_type" "const")
+ (eq_attr "dword_mode" "yes"))
+ (symbol_ref "loongarch_split_const_insns (operands[1])")
+ (eq_attr "move_type" "const")
+ (symbol_ref "loongarch_const_insns (operands[1])")
+ (eq_attr "move_type" "load,fpload")
+ (symbol_ref "loongarch_load_store_insns (operands[1], insn)")
+ (eq_attr "move_type" "store,fpstore")
+ (symbol_ref "loongarch_load_store_insns (operands[0], insn)")
+
+ (eq_attr "type" "idiv")
+ (symbol_ref "loongarch_idiv_insns (GET_MODE (PATTERN (insn)))")]
+(const_int 1)))
+
+;; Length of instruction in bytes.
+(define_attr "length" ""
+ (cond [
+ ;; Branch futher than +/- 128 KiB require two instructions.
+ (eq_attr "type" "branch")
+ (if_then_else (and (le (minus (match_dup 0) (pc)) (const_int 131064))
+ (le (minus (pc) (match_dup 0)) (const_int 131068)))
+ (const_int 4)
+ (const_int 8))]
+ (symbol_ref "get_attr_insn_count (insn) * 4")))
+
+;; Describe a user's asm statement.
+(define_asm_attributes
+ [(set_attr "type" "multi")])
+\f
+;; This mode iterator allows 32-bit and 64-bit GPR patterns to be generated
+;; from the same template.
+(define_mode_iterator GPR [SI (DI "TARGET_64BIT")])
+
+;; A copy of GPR that can be used when a pattern has two independent
+;; modes.
+(define_mode_iterator GPR2 [SI (DI "TARGET_64BIT")])
+
+;; This mode iterator allows 16-bit and 32-bit GPR patterns and 32-bit 64-bit
+;; FPR patterns to be generated from the same template.
+(define_mode_iterator JOIN_MODE [HI
+ SI
+ (SF "TARGET_HARD_FLOAT")
+ (DF "TARGET_DOUBLE_FLOAT")])
+
+;; This mode iterator allows :P to be used for patterns that operate on
+;; pointer-sized quantities. Exactly one of the two alternatives will match.
+(define_mode_iterator P [(SI "Pmode == SImode") (DI "Pmode == DImode")])
+
+;; Likewise, but for XLEN-sized quantities.
+(define_mode_iterator X [(SI "!TARGET_64BIT") (DI "TARGET_64BIT")])
+
+;; 64-bit modes for which we provide move patterns.
+(define_mode_iterator MOVE64 [DI DF])
+
+;; 128-bit modes for which we provide move patterns on 64-bit targets.
+(define_mode_iterator MOVE128 [TI TF])
+
+;; Iterator for sub-32-bit integer modes.
+(define_mode_iterator SHORT [QI HI])
+
+;; Likewise the 64-bit truncate-and-shift patterns.
+(define_mode_iterator SUBDI [QI HI SI])
+
+;; Iterator for scalar fixed point modes.
+(define_mode_iterator QHWD [QI HI SI (DI "TARGET_64BIT")])
+
+;; Iterator for hardware-supported floating-point modes.
+(define_mode_iterator ANYF [(SF "TARGET_HARD_FLOAT")
+ (DF "TARGET_DOUBLE_FLOAT")])
+
+;; A mode for which moves involving FPRs may need to be split.
+(define_mode_iterator SPLITF
+ [(DF "!TARGET_64BIT && TARGET_DOUBLE_FLOAT")
+ (DI "!TARGET_64BIT && TARGET_DOUBLE_FLOAT")
+ (TF "TARGET_64BIT && TARGET_DOUBLE_FLOAT")])
+
+;; In GPR templates, a string like "mul.<d>" will expand to "mul.w" in the
+;; 32-bit version and "mul.d" in the 64-bit version.
+(define_mode_attr d [(SI "w") (DI "d")])
+
+;; This attribute gives the length suffix for a load or store instruction.
+;; The same suffixes work for zero and sign extensions.
+(define_mode_attr size [(QI "b") (HI "h") (SI "w") (DI "d")])
+(define_mode_attr SIZE [(QI "B") (HI "H") (SI "W") (DI "D")])
+
+;; This attribute gives the mode mask of a SHORT.
+(define_mode_attr mask [(QI "0x00ff") (HI "0xffff")])
+
+;; This attribute gives the size (bits) of a SHORT.
+(define_mode_attr 7_or_15 [(QI "7") (HI "15")])
+
+;; Instruction names for stores.
+(define_mode_attr store [(QI "sb") (HI "sh") (SI "sw") (DI "sd")])
+
+;; This attribute gives the format suffix for floating-point operations.
+(define_mode_attr fmt [(SF "s") (DF "d")])
+(define_mode_attr ifmt [(SI "w") (DI "l")])
+
+;; This attribute gives the upper-case mode name for one unit of a
+;; floating-point mode or vector mode.
+(define_mode_attr UNITMODE [(SF "SF") (DF "DF")])
+
+;; This attribute gives the integer mode that has half the size of
+;; the controlling mode.
+(define_mode_attr HALFMODE [(DF "SI") (DI "SI") (TF "DI")])
+
+;; This code iterator allows signed and unsigned widening multiplications
+;; to use the same template.
+(define_code_iterator any_extend [sign_extend zero_extend])
+
+;; This code iterator allows the two right shift instructions to be
+;; generated from the same template.
+(define_code_iterator any_shiftrt [ashiftrt lshiftrt])
+
+;; This code iterator allows the three shift instructions to be generated
+;; from the same template.
+(define_code_iterator any_shift [ashift ashiftrt lshiftrt])
+
+;; This code iterator allows the three bitwise instructions to be generated
+;; from the same template.
+(define_code_iterator any_bitwise [and ior xor])
+(define_code_iterator neg_bitwise [and ior])
+
+;; This code iterator allows unsigned and signed division to be generated
+;; from the same template.
+(define_code_iterator any_div [div udiv mod umod])
+
+;; This code iterator allows all native floating-point comparisons to be
+;; generated from the same template.
+(define_code_iterator fcond [unordered uneq unlt unle eq lt le
+ ordered ltgt ne ge gt unge ungt])
+
+;; Equality operators.
+(define_code_iterator equality_op [eq ne])
+
+;; These code iterators allow the signed and unsigned scc operations to use
+;; the same template.
+(define_code_iterator any_gt [gt gtu])
+(define_code_iterator any_ge [ge geu])
+(define_code_iterator any_lt [lt ltu])
+(define_code_iterator any_le [le leu])
+
+(define_code_iterator any_return [return simple_return])
+
+;; <u> expands to an empty string when doing a signed operation and
+;; "u" when doing an unsigned operation.
+(define_code_attr u [(sign_extend "") (zero_extend "u")
+ (div "") (udiv "u")
+ (mod "") (umod "u")
+ (gt "") (gtu "u")
+ (ge "") (geu "u")
+ (lt "") (ltu "u")
+ (le "") (leu "u")])
+
+;; <U> is like <u> except uppercase.
+(define_code_attr U [(sign_extend "") (zero_extend "U")])
+
+;; <su> is like <u>, but the signed form expands to "s" rather than "".
+(define_code_attr su [(sign_extend "s") (zero_extend "u")])
+
+;; <optab> expands to the name of the optab for a particular code.
+(define_code_attr optab [(ashift "ashl")
+ (ashiftrt "ashr")
+ (lshiftrt "lshr")
+ (ior "ior")
+ (xor "xor")
+ (and "and")
+ (plus "add")
+ (minus "sub")
+ (mult "mul")
+ (div "div")
+ (udiv "udiv")
+ (mod "mod")
+ (umod "umod")
+ (return "return")
+ (simple_return "simple_return")])
+
+;; <insn> expands to the name of the insn that implements a particular code.
+(define_code_attr insn [(ashift "sll")
+ (ashiftrt "sra")
+ (lshiftrt "srl")
+ (ior "or")
+ (xor "xor")
+ (and "and")
+ (plus "addu")
+ (minus "subu")
+ (div "div")
+ (udiv "div")
+ (mod "mod")
+ (umod "mod")])
+
+;; <fcond> is the fcmp.cond.fmt condition associated with a particular code.
+(define_code_attr fcond [(unordered "cun")
+ (uneq "cueq")
+ (unlt "cult")
+ (unle "cule")
+ (eq "ceq")
+ (lt "slt")
+ (le "sle")
+ (ordered "cor")
+ (ltgt "sne")
+ (ne "cune")
+ (ge "sge")
+ (gt "sgt")
+ (unge "cuge")
+ (ungt "cugt")])
+
+;; The sel mnemonic to use depending on the condition test.
+(define_code_attr sel [(eq "masknez") (ne "maskeqz")])
+(define_code_attr selinv [(eq "maskeqz") (ne "masknez")])
+
+;;
+;; ....................
+;;
+;; CONDITIONAL TRAPS
+;;
+;; ....................
+;;
+
+(define_insn "trap"
+ [(trap_if (const_int 1) (const_int 0))]
+ ""
+{
+ return "break\t0";
+}
+ [(set_attr "type" "trap")])
+
+
+\f
+;;
+;; ....................
+;;
+;; ADDITION
+;;
+;; ....................
+;;
+
+(define_insn "add<mode>3"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (plus:ANYF (match_operand:ANYF 1 "register_operand" "f")
+ (match_operand:ANYF 2 "register_operand" "f")))]
+ ""
+ "fadd.<fmt>\t%0,%1,%2"
+ [(set_attr "type" "fadd")
+ (set_attr "mode" "<UNITMODE>")])
+
+(define_insn "add<mode>3"
+ [(set (match_operand:GPR 0 "register_operand" "=r,r")
+ (plus:GPR (match_operand:GPR 1 "register_operand" "r,r")
+ (match_operand:GPR 2 "arith_operand" "r,I")))]
+ ""
+ "add%i2.<d>\t%0,%1,%2";
+ [(set_attr "alu_type" "add")
+ (set_attr "mode" "<MODE>")])
+
+(define_insn "*addsi3_extended"
+ [(set (match_operand:DI 0 "register_operand" "=r,r")
+ (sign_extend:DI
+ (plus:SI (match_operand:SI 1 "register_operand" "r,r")
+ (match_operand:SI 2 "arith_operand" "r,I"))))]
+ "TARGET_64BIT"
+ "add%i2.w\t%0,%1,%2"
+ [(set_attr "alu_type" "add")
+ (set_attr "mode" "SI")])
+
+\f
+;;
+;; ....................
+;;
+;; SUBTRACTION
+;;
+;; ....................
+;;
+
+(define_insn "sub<mode>3"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (minus:ANYF (match_operand:ANYF 1 "register_operand" "f")
+ (match_operand:ANYF 2 "register_operand" "f")))]
+ ""
+ "fsub.<fmt>\t%0,%1,%2"
+ [(set_attr "type" "fadd")
+ (set_attr "mode" "<UNITMODE>")])
+
+(define_insn "sub<mode>3"
+ [(set (match_operand:GPR 0 "register_operand" "=r")
+ (minus:GPR (match_operand:GPR 1 "register_operand" "rJ")
+ (match_operand:GPR 2 "register_operand" "r")))]
+ ""
+ "sub.<d>\t%0,%z1,%2"
+ [(set_attr "alu_type" "sub")
+ (set_attr "mode" "<MODE>")])
+
+
+(define_insn "*subsi3_extended"
+ [(set (match_operand:DI 0 "register_operand" "= r")
+ (sign_extend:DI
+ (minus:SI (match_operand:SI 1 "reg_or_0_operand" " rJ")
+ (match_operand:SI 2 "register_operand" " r"))))]
+ "TARGET_64BIT"
+ "sub.w\t%0,%z1,%2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "SI")])
+\f
+;;
+;; ....................
+;;
+;; MULTIPLICATION
+;;
+;; ....................
+;;
+
+(define_insn "mul<mode>3"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (mult:ANYF (match_operand:ANYF 1 "register_operand" "f")
+ (match_operand:ANYF 2 "register_operand" "f")))]
+ ""
+ "fmul.<fmt>\t%0,%1,%2"
+ [(set_attr "type" "fmul")
+ (set_attr "mode" "<MODE>")])
+
+(define_insn "mul<mode>3"
+ [(set (match_operand:GPR 0 "register_operand" "=r")
+ (mult:GPR (match_operand:GPR 1 "register_operand" "r")
+ (match_operand:GPR 2 "register_operand" "r")))]
+ ""
+ "mul.<d>\t%0,%1,%2"
+ [(set_attr "type" "imul")
+ (set_attr "mode" "<MODE>")])
+
+(define_insn "mulsidi3_64bit"
+ [(set (match_operand:DI 0 "register_operand" "=r")
+ (mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "r"))
+ (sign_extend:DI (match_operand:SI 2 "register_operand" "r"))))]
+ "TARGET_64BIT"
+ "mul.d\t%0,%1,%2"
+ [(set_attr "type" "imul")
+ (set_attr "mode" "DI")])
+
+(define_insn "*mulsi3_extended"
+ [(set (match_operand:DI 0 "register_operand" "=r")
+ (sign_extend:DI
+ (mult:SI (match_operand:SI 1 "register_operand" "r")
+ (match_operand:SI 2 "register_operand" "r"))))]
+ "TARGET_64BIT"
+ "mul.w\t%0,%1,%2"
+ [(set_attr "type" "imul")
+ (set_attr "mode" "SI")])
+
+;;
+;; ........................
+;;
+;; MULTIPLICATION HIGH-PART
+;;
+;; ........................
+;;
+
+(define_expand "<u>mulditi3"
+ [(set (match_operand:TI 0 "register_operand")
+ (mult:TI (any_extend:TI (match_operand:DI 1 "register_operand"))
+ (any_extend:TI (match_operand:DI 2 "register_operand"))))]
+ "TARGET_64BIT"
+{
+ rtx low = gen_reg_rtx (DImode);
+ emit_insn (gen_muldi3 (low, operands[1], operands[2]));
+
+ rtx high = gen_reg_rtx (DImode);
+ emit_insn (gen_<u>muldi3_highpart (high, operands[1], operands[2]));
+
+ emit_move_insn (gen_lowpart (DImode, operands[0]), low);
+ emit_move_insn (gen_highpart (DImode, operands[0]), high);
+ DONE;
+})
+
+(define_insn "<u>muldi3_highpart"
+ [(set (match_operand:DI 0 "register_operand" "=r")
+ (truncate:DI
+ (lshiftrt:TI
+ (mult:TI (any_extend:TI
+ (match_operand:DI 1 "register_operand" " r"))
+ (any_extend:TI
+ (match_operand:DI 2 "register_operand" " r")))
+ (const_int 64))))]
+ "TARGET_64BIT"
+ "mulh.d<u>\t%0,%1,%2"
+ [(set_attr "type" "imul")
+ (set_attr "mode" "DI")])
+
+(define_expand "<u>mulsidi3"
+ [(set (match_operand:DI 0 "register_operand" "=r")
+ (mult:DI (any_extend:DI
+ (match_operand:SI 1 "register_operand" " r"))
+ (any_extend:DI
+ (match_operand:SI 2 "register_operand" " r"))))]
+ "!TARGET_64BIT"
+{
+ rtx temp = gen_reg_rtx (SImode);
+ emit_insn (gen_mulsi3 (temp, operands[1], operands[2]));
+ emit_insn (gen_<u>mulsi3_highpart (loongarch_subword (operands[0], true),
+ operands[1], operands[2]));
+ emit_insn (gen_movsi (loongarch_subword (operands[0], false), temp));
+ DONE;
+})
+
+(define_insn "<u>mulsi3_highpart"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (truncate:SI
+ (lshiftrt:DI
+ (mult:DI (any_extend:DI
+ (match_operand:SI 1 "register_operand" " r"))
+ (any_extend:DI
+ (match_operand:SI 2 "register_operand" " r")))
+ (const_int 32))))]
+ "!TARGET_64BIT"
+ "mulh.w<u>\t%0,%1,%2"
+ [(set_attr "type" "imul")
+ (set_attr "mode" "SI")])
+
+;;
+;; ....................
+;;
+;; DIVISION and REMAINDER
+;;
+;; ....................
+;;
+
+;; Float division and modulus.
+(define_insn "*div<mode>3"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (div:ANYF (match_operand:ANYF 1 "register_operand" "f")
+ (match_operand:ANYF 2 "register_operand" "f")))]
+ ""
+ "fdiv.<fmt>\t%0,%1,%2"
+ [(set_attr "type" "fdiv")
+ (set_attr "mode" "<UNITMODE>")])
+
+;; In 3A5000, the reciprocal operation is the same as the division operation.
+
+(define_insn "*recip<mode>3"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (div:ANYF (match_operand:ANYF 1 "const_1_operand" "")
+ (match_operand:ANYF 2 "register_operand" "f")))]
+ ""
+ "frecip.<fmt>\t%0,%2"
+ [(set_attr "type" "frdiv")
+ (set_attr "mode" "<UNITMODE>")])
+
+;; Integer division and modulus.
+(define_expand "<optab><mode>3"
+ [(set (match_operand:GPR 0 "register_operand")
+ (any_div:GPR (match_operand:GPR 1 "register_operand")
+ (match_operand:GPR 2 "register_operand")))]
+ ""
+{
+ if (GET_MODE (operands[0]) == SImode)
+ {
+ rtx reg1 = gen_reg_rtx (DImode);
+ rtx reg2 = gen_reg_rtx (DImode);
+
+ operands[1] = gen_rtx_SIGN_EXTEND (word_mode, operands[1]);
+ operands[2] = gen_rtx_SIGN_EXTEND (word_mode, operands[2]);
+
+ emit_insn (gen_rtx_SET (reg1, operands[1]));
+ emit_insn (gen_rtx_SET (reg2, operands[2]));
+
+ emit_insn (gen_<optab>di3_fake (operands[0], reg1, reg2));
+ DONE;
+ }
+})
+
+(define_insn "*<optab><mode>3"
+ [(set (match_operand:GPR 0 "register_operand" "=&r")
+ (any_div:GPR (match_operand:GPR 1 "register_operand" "r")
+ (match_operand:GPR 2 "register_operand" "r")))]
+ ""
+{
+ return loongarch_output_division ("<insn>.<d><u>\t%0,%1,%2", operands);
+}
+ [(set_attr "type" "idiv")
+ (set_attr "mode" "<MODE>")])
+
+(define_insn "<optab>di3_fake"
+ [(set (match_operand:SI 0 "register_operand" "=&r")
+ (any_div:SI (match_operand:DI 1 "register_operand" "r")
+ (match_operand:DI 2 "register_operand" "r")))]
+ ""
+{
+ return loongarch_output_division ("<insn>.w<u>\t%0,%1,%2", operands);
+}
+ [(set_attr "type" "idiv")
+ (set_attr "mode" "SI")])
+
+;; Floating point multiply accumulate instructions.
+
+;; a * b + c
+(define_insn "fma<mode>4"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (fma:ANYF (match_operand:ANYF 1 "register_operand" "f")
+ (match_operand:ANYF 2 "register_operand" "f")
+ (match_operand:ANYF 3 "register_operand" "f")))]
+ ""
+ "fmadd.<fmt>\t%0,%1,%2,%3"
+ [(set_attr "type" "fmadd")
+ (set_attr "mode" "<UNITMODE>")])
+
+;; a * b - c
+(define_insn "fms<mode>4"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (fma:ANYF (match_operand:ANYF 1 "register_operand" "f")
+ (match_operand:ANYF 2 "register_operand" "f")
+ (neg:ANYF (match_operand:ANYF 3 "register_operand" "f"))))]
+ ""
+ "fmsub.<fmt>\t%0,%1,%2,%3"
+ [(set_attr "type" "fmadd")
+ (set_attr "mode" "<UNITMODE>")])
+
+;; fnma is defined in GCC as (fma (neg op1) op2 op3)
+;; (-op1 * op2) + op3 ==> -(op1 * op2) + op3 ==> -((op1 * op2) - op3)
+;; The loongarch nmsub instructions implement -((op1 * op2) - op3)
+;; This transformation means we may return the wrong signed zero
+;; so we check HONOR_SIGNED_ZEROS.
+
+;; -a * b + c
+(define_insn "fnma<mode>4"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (fma:ANYF (neg:ANYF (match_operand:ANYF 1 "register_operand" "f"))
+ (match_operand:ANYF 2 "register_operand" "f")
+ (match_operand:ANYF 3 "register_operand" "f")))]
+ "!HONOR_SIGNED_ZEROS (<MODE>mode)"
+ "fnmsub.<fmt>\t%0,%1,%2,%3"
+ [(set_attr "type" "fmadd")
+ (set_attr "mode" "<UNITMODE>")])
+
+;; fnms is defined as: (fma (neg op1) op2 (neg op3))
+;; ((-op1) * op2) - op3 ==> -(op1 * op2) - op3 ==> -((op1 * op2) + op3)
+;; The loongarch nmadd instructions implement -((op1 * op2) + op3)
+;; This transformation means we may return the wrong signed zero
+;; so we check HONOR_SIGNED_ZEROS.
+
+;; -a * b - c
+(define_insn "fnms<mode>4"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (fma:ANYF
+ (neg:ANYF (match_operand:ANYF 1 "register_operand" "f"))
+ (match_operand:ANYF 2 "register_operand" "f")
+ (neg:ANYF (match_operand:ANYF 3 "register_operand" "f"))))]
+ "!HONOR_SIGNED_ZEROS (<MODE>mode)"
+ "fnmadd.<fmt>\t%0,%1,%2,%3"
+ [(set_attr "type" "fmadd")
+ (set_attr "mode" "<UNITMODE>")])
+
+;; -(-a * b - c), modulo signed zeros
+(define_insn "*fma<mode>4"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (neg:ANYF
+ (fma:ANYF
+ (neg:ANYF (match_operand:ANYF 1 "register_operand" " f"))
+ (match_operand:ANYF 2 "register_operand" " f")
+ (neg:ANYF (match_operand:ANYF 3 "register_operand" " f")))))]
+ "!HONOR_SIGNED_ZEROS (<MODE>mode)"
+ "fmadd.<fmt>\t%0,%1,%2,%3"
+ [(set_attr "type" "fmadd")
+ (set_attr "mode" "<UNITMODE>")])
+
+;; -(-a * b + c), modulo signed zeros
+(define_insn "*fms<mode>4"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (neg:ANYF
+ (fma:ANYF
+ (neg:ANYF (match_operand:ANYF 1 "register_operand" " f"))
+ (match_operand:ANYF 2 "register_operand" " f")
+ (match_operand:ANYF 3 "register_operand" " f"))))]
+ "!HONOR_SIGNED_ZEROS (<MODE>mode)"
+ "fmsub.<fmt>\t%0,%1,%2,%3"
+ [(set_attr "type" "fmadd")
+ (set_attr "mode" "<UNITMODE>")])
+
+;; -(a * b + c)
+(define_insn "*fnms<mode>4"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (neg:ANYF
+ (fma:ANYF
+ (match_operand:ANYF 1 "register_operand" " f")
+ (match_operand:ANYF 2 "register_operand" " f")
+ (match_operand:ANYF 3 "register_operand" " f"))))]
+ ""
+ "fnmadd.<fmt>\t%0,%1,%2,%3"
+ [(set_attr "type" "fmadd")
+ (set_attr "mode" "<UNITMODE>")])
+
+;; -(a * b - c)
+(define_insn "*fnma<mode>4"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (neg:ANYF
+ (fma:ANYF
+ (match_operand:ANYF 1 "register_operand" " f")
+ (match_operand:ANYF 2 "register_operand" " f")
+ (neg:ANYF (match_operand:ANYF 3 "register_operand" " f")))))]
+ ""
+ "fnmsub.<fmt>\t%0,%1,%2,%3"
+ [(set_attr "type" "fmadd")
+ (set_attr "mode" "<UNITMODE>")])
+\f
+;;
+;; ....................
+;;
+;; SQUARE ROOT
+;;
+;; ....................
+
+(define_insn "sqrt<mode>2"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (sqrt:ANYF (match_operand:ANYF 1 "register_operand" "f")))]
+ ""
+ "fsqrt.<fmt>\t%0,%1"
+ [(set_attr "type" "fsqrt")
+ (set_attr "mode" "<UNITMODE>")
+ (set_attr "insn_count" "1")])
+
+(define_insn "*rsqrt<mode>a"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (div:ANYF (match_operand:ANYF 1 "const_1_operand" "")
+ (sqrt:ANYF (match_operand:ANYF 2 "register_operand" "f"))))]
+ "flag_unsafe_math_optimizations"
+ "frsqrt.<fmt>\t%0,%2"
+ [(set_attr "type" "frsqrt")
+ (set_attr "mode" "<UNITMODE>")
+ (set_attr "insn_count" "1")])
+
+(define_insn "*rsqrt<mode>b"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (sqrt:ANYF (div:ANYF (match_operand:ANYF 1 "const_1_operand" "")
+ (match_operand:ANYF 2 "register_operand" "f"))))]
+ "flag_unsafe_math_optimizations"
+ "frsqrt.<fmt>\t%0,%2"
+ [(set_attr "type" "frsqrt")
+ (set_attr "mode" "<UNITMODE>")
+ (set_attr "insn_count" "1")])
+\f
+;;
+;; ....................
+;;
+;; ABSOLUTE VALUE
+;;
+;; ....................
+
+(define_insn "abs<mode>2"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (abs:ANYF (match_operand:ANYF 1 "register_operand" "f")))]
+ ""
+ "fabs.<fmt>\t%0,%1"
+ [(set_attr "type" "fabs")
+ (set_attr "mode" "<UNITMODE>")])
+\f
+;;
+;; ...................
+;;
+;; Count leading zeroes.
+;;
+;; ...................
+;;
+
+(define_insn "clz<mode>2"
+ [(set (match_operand:GPR 0 "register_operand" "=r")
+ (clz:GPR (match_operand:GPR 1 "register_operand" "r")))]
+ ""
+ "clz.<d>\t%0,%1"
+ [(set_attr "type" "clz")
+ (set_attr "mode" "<MODE>")])
+
+;;
+;; ...................
+;;
+;; Count trailing zeroes.
+;;
+;; ...................
+;;
+
+(define_insn "ctz<mode>2"
+ [(set (match_operand:GPR 0 "register_operand" "=r")
+ (ctz:GPR (match_operand:GPR 1 "register_operand" "r")))]
+ ""
+ "ctz.<d>\t%0,%1"
+ [(set_attr "type" "clz")
+ (set_attr "mode" "<MODE>")])
+
+;;
+;; ....................
+;;
+;; MIN/MAX
+;;
+;; ....................
+
+(define_insn "smax<mode>3"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (smax:ANYF (match_operand:ANYF 1 "register_operand" "f")
+ (match_operand:ANYF 2 "register_operand" "f")))]
+ ""
+ "fmax.<fmt>\t%0,%1,%2"
+ [(set_attr "type" "fmove")
+ (set_attr "mode" "<MODE>")])
+
+(define_insn "smin<mode>3"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (smin:ANYF (match_operand:ANYF 1 "register_operand" "f")
+ (match_operand:ANYF 2 "register_operand" "f")))]
+ ""
+ "fmin.<fmt>\t%0,%1,%2"
+ [(set_attr "type" "fmove")
+ (set_attr "mode" "<MODE>")])
+
+(define_insn "smaxa<mode>3"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (if_then_else:ANYF
+ (gt (abs:ANYF (match_operand:ANYF 1 "register_operand" "f"))
+ (abs:ANYF (match_operand:ANYF 2 "register_operand" "f")))
+ (match_dup 1)
+ (match_dup 2)))]
+ ""
+ "fmaxa.<fmt>\t%0,%1,%2"
+ [(set_attr "type" "fmove")
+ (set_attr "mode" "<MODE>")])
+
+(define_insn "smina<mode>3"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (if_then_else:ANYF
+ (lt (abs:ANYF (match_operand:ANYF 1 "register_operand" "f"))
+ (abs:ANYF (match_operand:ANYF 2 "register_operand" "f")))
+ (match_dup 1)
+ (match_dup 2)))]
+ ""
+ "fmina.<fmt>\t%0,%1,%2"
+ [(set_attr "type" "fmove")
+ (set_attr "mode" "<MODE>")])
+\f
+;;
+;; ....................
+;;
+;; NEGATION and ONE'S COMPLEMENT
+;;
+;; ....................
+
+(define_insn "neg<mode>2"
+ [(set (match_operand:GPR 0 "register_operand" "=r")
+ (neg:GPR (match_operand:GPR 1 "register_operand" "r")))]
+ ""
+ "sub.<d>\t%0,%.,%1"
+ [(set_attr "alu_type" "sub")
+ (set_attr "mode" "<MODE>")])
+
+(define_insn "one_cmpl<mode>2"
+ [(set (match_operand:GPR 0 "register_operand" "=r")
+ (not:GPR (match_operand:GPR 1 "register_operand" "r")))]
+ ""
+ "nor\t%0,%.,%1"
+ [(set_attr "alu_type" "not")
+ (set_attr "mode" "<MODE>")])
+
+(define_insn "neg<mode>2"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (neg:ANYF (match_operand:ANYF 1 "register_operand" "f")))]
+ ""
+ "fneg.<fmt>\t%0,%1"
+ [(set_attr "type" "fneg")
+ (set_attr "mode" "<UNITMODE>")])
+\f
+
+;;
+;; ....................
+;;
+;; LOGICAL
+;;
+;; ....................
+;;
+
+(define_insn "<optab><mode>3"
+ [(set (match_operand:GPR 0 "register_operand" "=r,r")
+ (any_bitwise:GPR (match_operand:GPR 1 "register_operand" "%r,r")
+ (match_operand:GPR 2 "uns_arith_operand" "r,K")))]
+ ""
+ "<insn>%i2\t%0,%1,%2"
+ [(set_attr "type" "logical")
+ (set_attr "mode" "<MODE>")])
+
+(define_insn "and<mode>3_extended"
+ [(set (match_operand:GPR 0 "register_operand" "=r")
+ (and:GPR (match_operand:GPR 1 "nonimmediate_operand" "r")
+ (match_operand:GPR 2 "low_bitmask_operand" "Yx")))]
+ ""
+{
+ int len;
+
+ len = low_bitmask_len (<MODE>mode, INTVAL (operands[2]));
+ operands[2] = GEN_INT (len-1);
+ return "bstrpick.<d>\t%0,%1,%2,0";
+}
+ [(set_attr "move_type" "pick_ins")
+ (set_attr "mode" "<MODE>")])
+
+(define_insn "*iorhi3"
+ [(set (match_operand:HI 0 "register_operand" "=r,r")
+ (ior:HI (match_operand:HI 1 "register_operand" "%r,r")
+ (match_operand:HI 2 "uns_arith_operand" "r,K")))]
+ ""
+ "or%i2\t%0,%1,%2"
+ [(set_attr "type" "logical")
+ (set_attr "mode" "HI")])
+
+(define_insn "*nor<mode>3"
+ [(set (match_operand:GPR 0 "register_operand" "=r")
+ (and:GPR (not:GPR (match_operand:GPR 1 "register_operand" "%r"))
+ (not:GPR (match_operand:GPR 2 "register_operand" "r"))))]
+ ""
+ "nor\t%0,%1,%2"
+ [(set_attr "type" "logical")
+ (set_attr "mode" "<MODE>")])
+
+(define_insn "<optab>n<mode>"
+ [(set (match_operand:GPR 0 "register_operand" "=r")
+ (neg_bitwise:GPR
+ (not:GPR (match_operand:GPR 1 "register_operand" "r"))
+ (match_operand:GPR 2 "register_operand" "r")))]
+ ""
+ "<insn>n\t%0,%2,%1"
+ [(set_attr "type" "logical")
+ (set_attr "mode" "<MODE>")])
+
+\f
+;;
+;; ....................
+;;
+;; TRUNCATION
+;;
+;; ....................
+
+(define_insn "truncdfsf2"
+ [(set (match_operand:SF 0 "register_operand" "=f")
+ (float_truncate:SF (match_operand:DF 1 "register_operand" "f")))]
+ "TARGET_DOUBLE_FLOAT"
+ "fcvt.s.d\t%0,%1"
+ [(set_attr "type" "fcvt")
+ (set_attr "cnv_mode" "D2S")
+ (set_attr "mode" "SF")])
+
+\f
+;;
+;; ....................
+;;
+;; ZERO EXTENSION
+;;
+;; ....................
+(define_expand "zero_extendsidi2"
+ [(set (match_operand:DI 0 "register_operand")
+ (zero_extend:DI (match_operand:SI 1 "nonimmediate_operand")))]
+ "TARGET_64BIT")
+
+(define_insn_and_split "*zero_extendsidi2_internal"
+ [(set (match_operand:DI 0 "register_operand" "=r,r,r,r")
+ (zero_extend:DI (match_operand:SI 1 "nonimmediate_operand" "r,m,ZC,k")))]
+ "TARGET_64BIT"
+ "@
+ bstrpick.d\t%0,%1,31,0
+ ld.wu\t%0,%1
+ #
+ ldx.wu\t%0,%1"
+ "&& reload_completed
+ && MEM_P (operands[1])
+ && (loongarch_14bit_shifted_offset_address_p (XEXP (operands[1], 0), SImode)
+ && !loongarch_12bit_offset_address_p (XEXP (operands[1], 0), SImode))
+ && !paradoxical_subreg_p (operands[0])"
+ [(set (match_dup 3) (match_dup 1))
+ (set (match_dup 0)
+ (ior:DI (zero_extend:DI
+ (subreg:SI (match_dup 0) 0))
+ (match_dup 2)))]
+ {
+ operands[1] = gen_lowpart (SImode, operands[1]);
+ operands[3] = gen_lowpart (SImode, operands[0]);
+ operands[2] = const0_rtx;
+ }
+ [(set_attr "move_type" "arith,load,load,load")
+ (set_attr "mode" "DI")])
+
+(define_insn "zero_extend<SHORT:mode><GPR:mode>2"
+ [(set (match_operand:GPR 0 "register_operand" "=r,r,r")
+ (zero_extend:GPR
+ (match_operand:SHORT 1 "nonimmediate_operand" "r,m,k")))]
+ ""
+ "@
+ bstrpick.w\t%0,%1,<SHORT:7_or_15>,0
+ ld.<SHORT:size>u\t%0,%1
+ ldx.<SHORT:size>u\t%0,%1"
+ [(set_attr "move_type" "pick_ins,load,load")
+ (set_attr "mode" "<GPR:MODE>")])
+
+(define_insn "zero_extendqihi2"
+ [(set (match_operand:HI 0 "register_operand" "=r,r,r")
+ (zero_extend:HI (match_operand:QI 1 "nonimmediate_operand" "r,k,m")))]
+ ""
+ "@
+ andi\t%0,%1,0xff
+ ldx.bu\t%0,%1
+ ld.bu\t%0,%1"
+ [(set_attr "move_type" "andi,load,load")
+ (set_attr "mode" "HI")])
+
+;; Combiner patterns to optimize truncate/zero_extend combinations.
+
+(define_insn "*zero_extend<GPR:mode>_trunc<SHORT:mode>"
+ [(set (match_operand:GPR 0 "register_operand" "=r")
+ (zero_extend:GPR
+ (truncate:SHORT (match_operand:DI 1 "register_operand" "r"))))]
+ "TARGET_64BIT"
+ "bstrpick.w\t%0,%1,<SHORT:7_or_15>,0"
+ [(set_attr "move_type" "pick_ins")
+ (set_attr "mode" "<GPR:MODE>")])
+
+(define_insn "*zero_extendhi_truncqi"
+ [(set (match_operand:HI 0 "register_operand" "=r")
+ (zero_extend:HI
+ (truncate:QI (match_operand:DI 1 "register_operand" "r"))))]
+ "TARGET_64BIT"
+ "andi\t%0,%1,0xff"
+ [(set_attr "alu_type" "and")
+ (set_attr "mode" "HI")])
+\f
+;;
+;; ....................
+;;
+;; SIGN EXTENSION
+;;
+;; ....................
+
+(define_insn "extendsidi2"
+ [(set (match_operand:DI 0 "register_operand" "=r,r,r,r")
+ (sign_extend:DI
+ (match_operand:SI 1 "nonimmediate_operand" "r,ZC,m,k")))]
+ "TARGET_64BIT"
+ "@
+ slli.w\t%0,%1,0
+ ldptr.w\t%0,%1
+ ld.w\t%0,%1
+ ldx.w\t%0,%1"
+ [(set_attr "move_type" "sll0,load,load,load")
+ (set_attr "mode" "DI")])
+
+(define_insn "extend<SHORT:mode><GPR:mode>2"
+ [(set (match_operand:GPR 0 "register_operand" "=r,r,r")
+ (sign_extend:GPR
+ (match_operand:SHORT 1 "nonimmediate_operand" "r,m,k")))]
+ ""
+ "@
+ ext.w.<SHORT:size>\t%0,%1
+ ld.<SHORT:size>\t%0,%1
+ ldx.<SHORT:size>\t%0,%1"
+ [(set_attr "move_type" "signext,load,load")
+ (set_attr "mode" "<GPR:MODE>")])
+
+(define_insn "extendqihi2"
+ [(set (match_operand:HI 0 "register_operand" "=r,r,r")
+ (sign_extend:HI
+ (match_operand:QI 1 "nonimmediate_operand" "r,m,k")))]
+ ""
+ "@
+ ext.w.b\t%0,%1
+ ld.b\t%0,%1
+ ldx.b\t%0,%1"
+ [(set_attr "move_type" "signext,load,load")
+ (set_attr "mode" "SI")])
+
+(define_insn "extendsfdf2"
+ [(set (match_operand:DF 0 "register_operand" "=f")
+ (float_extend:DF (match_operand:SF 1 "register_operand" "f")))]
+ "TARGET_DOUBLE_FLOAT"
+ "fcvt.d.s\t%0,%1"
+ [(set_attr "type" "fcvt")
+ (set_attr "cnv_mode" "S2D")
+ (set_attr "mode" "DF")])
+\f
+;;
+;; ....................
+;;
+;; CONVERSIONS
+;;
+;; ....................
+
+;; conversion of a floating-point value to a integer
+
+(define_insn "fix_trunc<ANYF:mode><GPR:mode>2"
+ [(set (match_operand:GPR 0 "register_operand" "=f")
+ (fix:GPR (match_operand:ANYF 1 "register_operand" "f")))]
+ ""
+ "ftintrz.<GPR:ifmt>.<ANYF:fmt> %0,%1"
+ [(set_attr "type" "fcvt")
+ (set_attr "mode" "<ANYF:MODE>")])
+
+;; conversion of an integeral (or boolean) value to a floating-point value
+
+(define_insn "floatsidf2"
+ [(set (match_operand:DF 0 "register_operand" "=f")
+ (float:DF (match_operand:SI 1 "register_operand" "f")))]
+ "TARGET_DOUBLE_FLOAT"
+ "ffint.d.w\t%0,%1"
+ [(set_attr "type" "fcvt")
+ (set_attr "mode" "DF")
+ (set_attr "cnv_mode" "I2D")])
+
+(define_insn "floatdidf2"
+ [(set (match_operand:DF 0 "register_operand" "=f")
+ (float:DF (match_operand:DI 1 "register_operand" "f")))]
+ "TARGET_DOUBLE_FLOAT"
+ "ffint.d.l\t%0,%1"
+ [(set_attr "type" "fcvt")
+ (set_attr "mode" "DF")
+ (set_attr "cnv_mode" "I2D")])
+
+(define_insn "floatsisf2"
+ [(set (match_operand:SF 0 "register_operand" "=f")
+ (float:SF (match_operand:SI 1 "register_operand" "f")))]
+ "TARGET_HARD_FLOAT"
+ "ffint.s.w\t%0,%1"
+ [(set_attr "type" "fcvt")
+ (set_attr "mode" "SF")
+ (set_attr "cnv_mode" "I2S")])
+
+(define_insn "floatdisf2"
+ [(set (match_operand:SF 0 "register_operand" "=f")
+ (float:SF (match_operand:DI 1 "register_operand" "f")))]
+ "TARGET_DOUBLE_FLOAT"
+ "ffint.s.l\t%0,%1"
+ [(set_attr "type" "fcvt")
+ (set_attr "mode" "SF")
+ (set_attr "cnv_mode" "I2S")])
+
+;; Convert a floating-point value to an unsigned integer.
+
+(define_expand "fixuns_truncdfsi2"
+ [(set (match_operand:SI 0 "register_operand")
+ (unsigned_fix:SI (match_operand:DF 1 "register_operand")))]
+ "TARGET_DOUBLE_FLOAT"
+{
+ rtx reg1 = gen_reg_rtx (DFmode);
+ rtx reg2 = gen_reg_rtx (DFmode);
+ rtx reg3 = gen_reg_rtx (SImode);
+ rtx_code_label *label1 = gen_label_rtx ();
+ rtx_code_label *label2 = gen_label_rtx ();
+ rtx test;
+ REAL_VALUE_TYPE offset;
+
+ real_2expN (&offset, 31, DFmode);
+
+ loongarch_emit_move (reg1,
+ const_double_from_real_value (offset, DFmode));
+ do_pending_stack_adjust ();
+
+ test = gen_rtx_GE (VOIDmode, operands[1], reg1);
+ emit_jump_insn (gen_cbranchdf4 (test, operands[1], reg1, label1));
+
+ emit_insn (gen_fix_truncdfsi2 (operands[0], operands[1]));
+ emit_jump_insn (gen_rtx_SET (pc_rtx,
+ gen_rtx_LABEL_REF (VOIDmode, label2)));
+ emit_barrier ();
+
+ emit_label (label1);
+ loongarch_emit_move (reg2, gen_rtx_MINUS (DFmode, operands[1], reg1));
+ loongarch_emit_move (reg3, GEN_INT (trunc_int_for_mode
+ (BITMASK_HIGH, SImode)));
+
+ emit_insn (gen_fix_truncdfsi2 (operands[0], reg2));
+ emit_insn (gen_iorsi3 (operands[0], operands[0], reg3));
+
+ emit_label (label2);
+
+ /* Allow REG_NOTES to be set on last insn (labels don't have enough
+ fields, and can't be used for REG_NOTES anyway). */
+ emit_use (stack_pointer_rtx);
+ DONE;
+})
+
+(define_expand "fixuns_truncdfdi2"
+ [(set (match_operand:DI 0 "register_operand")
+ (unsigned_fix:DI (match_operand:DF 1 "register_operand")))]
+ "TARGET_DOUBLE_FLOAT"
+{
+ rtx reg1 = gen_reg_rtx (DFmode);
+ rtx reg2 = gen_reg_rtx (DFmode);
+ rtx reg3 = gen_reg_rtx (DImode);
+ rtx_code_label *label1 = gen_label_rtx ();
+ rtx_code_label *label2 = gen_label_rtx ();
+ rtx test;
+ REAL_VALUE_TYPE offset;
+
+ real_2expN (&offset, 63, DFmode);
+
+ loongarch_emit_move (reg1, const_double_from_real_value (offset, DFmode));
+ do_pending_stack_adjust ();
+
+ test = gen_rtx_GE (VOIDmode, operands[1], reg1);
+ emit_jump_insn (gen_cbranchdf4 (test, operands[1], reg1, label1));
+
+ emit_insn (gen_fix_truncdfdi2 (operands[0], operands[1]));
+ emit_jump_insn (gen_rtx_SET (pc_rtx, gen_rtx_LABEL_REF (VOIDmode, label2)));
+ emit_barrier ();
+
+ emit_label (label1);
+ loongarch_emit_move (reg2, gen_rtx_MINUS (DFmode, operands[1], reg1));
+ loongarch_emit_move (reg3, GEN_INT (BITMASK_HIGH));
+ emit_insn (gen_ashldi3 (reg3, reg3, GEN_INT (32)));
+
+ emit_insn (gen_fix_truncdfdi2 (operands[0], reg2));
+ emit_insn (gen_iordi3 (operands[0], operands[0], reg3));
+
+ emit_label (label2);
+
+ /* Allow REG_NOTES to be set on last insn (labels don't have enough
+ fields, and can't be used for REG_NOTES anyway). */
+ emit_use (stack_pointer_rtx);
+ DONE;
+})
+
+(define_expand "fixuns_truncsfsi2"
+ [(set (match_operand:SI 0 "register_operand")
+ (unsigned_fix:SI (match_operand:SF 1 "register_operand")))]
+ "TARGET_HARD_FLOAT"
+{
+ rtx reg1 = gen_reg_rtx (SFmode);
+ rtx reg2 = gen_reg_rtx (SFmode);
+ rtx reg3 = gen_reg_rtx (SImode);
+ rtx_code_label *label1 = gen_label_rtx ();
+ rtx_code_label *label2 = gen_label_rtx ();
+ rtx test;
+ REAL_VALUE_TYPE offset;
+
+ real_2expN (&offset, 31, SFmode);
+
+ loongarch_emit_move (reg1, const_double_from_real_value (offset, SFmode));
+ do_pending_stack_adjust ();
+
+ test = gen_rtx_GE (VOIDmode, operands[1], reg1);
+ emit_jump_insn (gen_cbranchsf4 (test, operands[1], reg1, label1));
+
+ emit_insn (gen_fix_truncsfsi2 (operands[0], operands[1]));
+ emit_jump_insn (gen_rtx_SET (pc_rtx, gen_rtx_LABEL_REF (VOIDmode, label2)));
+ emit_barrier ();
+
+ emit_label (label1);
+ loongarch_emit_move (reg2, gen_rtx_MINUS (SFmode, operands[1], reg1));
+ loongarch_emit_move (reg3, GEN_INT (trunc_int_for_mode
+ (BITMASK_HIGH, SImode)));
+
+ emit_insn (gen_fix_truncsfsi2 (operands[0], reg2));
+ emit_insn (gen_iorsi3 (operands[0], operands[0], reg3));
+
+ emit_label (label2);
+
+ /* Allow REG_NOTES to be set on last insn (labels don't have enough
+ fields, and can't be used for REG_NOTES anyway). */
+ emit_use (stack_pointer_rtx);
+ DONE;
+})
+
+(define_expand "fixuns_truncsfdi2"
+ [(set (match_operand:DI 0 "register_operand")
+ (unsigned_fix:DI (match_operand:SF 1 "register_operand")))]
+ "TARGET_DOUBLE_FLOAT"
+{
+ rtx reg1 = gen_reg_rtx (SFmode);
+ rtx reg2 = gen_reg_rtx (SFmode);
+ rtx reg3 = gen_reg_rtx (DImode);
+ rtx_code_label *label1 = gen_label_rtx ();
+ rtx_code_label *label2 = gen_label_rtx ();
+ rtx test;
+ REAL_VALUE_TYPE offset;
+
+ real_2expN (&offset, 63, SFmode);
+
+ loongarch_emit_move (reg1, const_double_from_real_value (offset, SFmode));
+ do_pending_stack_adjust ();
+
+ test = gen_rtx_GE (VOIDmode, operands[1], reg1);
+ emit_jump_insn (gen_cbranchsf4 (test, operands[1], reg1, label1));
+
+ emit_insn (gen_fix_truncsfdi2 (operands[0], operands[1]));
+ emit_jump_insn (gen_rtx_SET (pc_rtx, gen_rtx_LABEL_REF (VOIDmode, label2)));
+ emit_barrier ();
+
+ emit_label (label1);
+ loongarch_emit_move (reg2, gen_rtx_MINUS (SFmode, operands[1], reg1));
+ loongarch_emit_move (reg3, GEN_INT (BITMASK_HIGH));
+ emit_insn (gen_ashldi3 (reg3, reg3, GEN_INT (32)));
+
+ emit_insn (gen_fix_truncsfdi2 (operands[0], reg2));
+ emit_insn (gen_iordi3 (operands[0], operands[0], reg3));
+
+ emit_label (label2);
+
+ /* Allow REG_NOTES to be set on last insn (labels don't have enough
+ fields, and can't be used for REG_NOTES anyway). */
+ emit_use (stack_pointer_rtx);
+ DONE;
+})
+\f
+;;
+;; ....................
+;;
+;; EXTRACT AND INSERT
+;;
+;; ....................
+
+(define_expand "extzv<mode>"
+ [(set (match_operand:X 0 "register_operand")
+ (zero_extract:X (match_operand:X 1 "register_operand")
+ (match_operand 2 "const_int_operand")
+ (match_operand 3 "const_int_operand")))]
+ ""
+{
+ if (!loongarch_use_ins_ext_p (operands[1], INTVAL (operands[2]),
+ INTVAL (operands[3])))
+ FAIL;
+})
+
+(define_insn "*extzv<mode>"
+ [(set (match_operand:X 0 "register_operand" "=r")
+ (zero_extract:X (match_operand:X 1 "register_operand" "r")
+ (match_operand 2 "const_int_operand" "")
+ (match_operand 3 "const_int_operand" "")))]
+ "loongarch_use_ins_ext_p (operands[1], INTVAL (operands[2]),
+ INTVAL (operands[3]))"
+{
+ operands[2] = GEN_INT (INTVAL (operands[2]) + INTVAL (operands[3]) - 1);
+ return "bstrpick.<d>\t%0,%1,%2,%3";
+}
+ [(set_attr "type" "arith")
+ (set_attr "mode" "<MODE>")])
+
+(define_expand "insv<mode>"
+ [(set (zero_extract:GPR (match_operand:GPR 0 "register_operand")
+ (match_operand 1 "const_int_operand")
+ (match_operand 2 "const_int_operand"))
+ (match_operand:GPR 3 "reg_or_0_operand"))]
+ ""
+{
+ if (!loongarch_use_ins_ext_p (operands[0], INTVAL (operands[1]),
+ INTVAL (operands[2])))
+ FAIL;
+})
+
+(define_insn "*insv<mode>"
+ [(set (zero_extract:GPR (match_operand:GPR 0 "register_operand" "+r")
+ (match_operand:SI 1 "const_int_operand" "")
+ (match_operand:SI 2 "const_int_operand" ""))
+ (match_operand:GPR 3 "reg_or_0_operand" "rJ"))]
+ "loongarch_use_ins_ext_p (operands[0], INTVAL (operands[1]),
+ INTVAL (operands[2]))"
+{
+ operands[1] = GEN_INT (INTVAL (operands[1]) + INTVAL (operands[2]) - 1);
+ return "bstrins.<d>\t%0,%z3,%1,%2";
+}
+ [(set_attr "type" "arith")
+ (set_attr "mode" "<MODE>")])
+\f
+;;
+;; ....................
+;;
+;; DATA MOVEMENT
+;;
+;; ....................
+
+;; 64-bit integer moves
+
+;; Unlike most other insns, the move insns can't be split with
+;; different predicates, because register spilling and other parts of
+;; the compiler, have memoized the insn number already.
+
+(define_expand "movdi"
+ [(set (match_operand:DI 0 "")
+ (match_operand:DI 1 ""))]
+ ""
+{
+ if (loongarch_legitimize_move (DImode, operands[0], operands[1]))
+ DONE;
+})
+
+(define_insn "*movdi_32bit"
+ [(set (match_operand:DI 0 "nonimmediate_operand" "=r,r,r,w,*f,*f,*r,*m")
+ (match_operand:DI 1 "move_operand" "r,i,w,r,*J*r,*m,*f,*f"))]
+ "!TARGET_64BIT
+ && (register_operand (operands[0], DImode)
+ || reg_or_0_operand (operands[1], DImode))"
+ { return loongarch_output_move (operands[0], operands[1]); }
+ [(set_attr "move_type" "move,const,load,store,mgtf,fpload,mftg,fpstore")
+ (set_attr "mode" "DI")])
+
+(define_insn "*movdi_64bit"
+ [(set (match_operand:DI 0 "nonimmediate_operand" "=r,r,r,w,*f,*f,*r,*m")
+ (match_operand:DI 1 "move_operand" "r,Yd,w,rJ,*r*J,*m,*f,*f"))]
+ "TARGET_64BIT
+ && (register_operand (operands[0], DImode)
+ || reg_or_0_operand (operands[1], DImode))"
+ { return loongarch_output_move (operands[0], operands[1]); }
+ [(set_attr "move_type" "move,const,load,store,mgtf,fpload,mftg,fpstore")
+ (set_attr "mode" "DI")])
+
+;; 32-bit Integer moves
+
+(define_expand "movsi"
+ [(set (match_operand:SI 0 "")
+ (match_operand:SI 1 ""))]
+ ""
+{
+ if (loongarch_legitimize_move (SImode, operands[0], operands[1]))
+ DONE;
+})
+
+(define_insn "*movsi_internal"
+ [(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,r,w,*f,*f,*r,*m,*r,*z")
+ (match_operand:SI 1 "move_operand" "r,Yd,w,rJ,*r*J,*m,*f,*f,*z,*r"))]
+ "(register_operand (operands[0], SImode)
+ || reg_or_0_operand (operands[1], SImode))"
+ { return loongarch_output_move (operands[0], operands[1]); }
+ [(set_attr "move_type" "move,const,load,store,mgtf,fpload,mftg,fpstore,mftg,mgtf")
+ (set_attr "mode" "SI")])
+
+;; 16-bit Integer moves
+
+;; Unlike most other insns, the move insns can't be split with
+;; different predicates, because register spilling and other parts of
+;; the compiler, have memoized the insn number already.
+;; Unsigned loads are used because LOAD_EXTEND_OP returns ZERO_EXTEND.
+
+(define_expand "movhi"
+ [(set (match_operand:HI 0 "")
+ (match_operand:HI 1 ""))]
+ ""
+{
+ if (loongarch_legitimize_move (HImode, operands[0], operands[1]))
+ DONE;
+})
+
+(define_insn "*movhi_internal"
+ [(set (match_operand:HI 0 "nonimmediate_operand" "=r,r,r,r,m,r,k")
+ (match_operand:HI 1 "move_operand" "r,Yd,I,m,rJ,k,rJ"))]
+ "(register_operand (operands[0], HImode)
+ || reg_or_0_operand (operands[1], HImode))"
+ { return loongarch_output_move (operands[0], operands[1]); }
+ [(set_attr "move_type" "move,const,const,load,store,load,store")
+ (set_attr "mode" "HI")])
+
+;; 8-bit Integer moves
+
+;; Unlike most other insns, the move insns can't be split with
+;; different predicates, because register spilling and other parts of
+;; the compiler, have memoized the insn number already.
+;; Unsigned loads are used because LOAD_EXTEND_OP returns ZERO_EXTEND.
+
+(define_expand "movqi"
+ [(set (match_operand:QI 0 "")
+ (match_operand:QI 1 ""))]
+ ""
+{
+ if (loongarch_legitimize_move (QImode, operands[0], operands[1]))
+ DONE;
+})
+
+(define_insn "*movqi_internal"
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=r,r,r,m,r,k")
+ (match_operand:QI 1 "move_operand" "r,I,m,rJ,k,rJ"))]
+ "(register_operand (operands[0], QImode)
+ || reg_or_0_operand (operands[1], QImode))"
+ { return loongarch_output_move (operands[0], operands[1]); }
+ [(set_attr "move_type" "move,const,load,store,load,store")
+ (set_attr "mode" "QI")])
+
+;; 32-bit floating point moves
+
+(define_expand "movsf"
+ [(set (match_operand:SF 0 "")
+ (match_operand:SF 1 ""))]
+ ""
+{
+ if (loongarch_legitimize_move (SFmode, operands[0], operands[1]))
+ DONE;
+})
+
+(define_insn "*movsf_hardfloat"
+ [(set (match_operand:SF 0 "nonimmediate_operand" "=f,f,f,m,f,k,m,*f,*r,*r,*r,*m")
+ (match_operand:SF 1 "move_operand" "f,G,m,f,k,f,G,*r,*f,*G*r,*m,*r"))]
+ "TARGET_HARD_FLOAT
+ && (register_operand (operands[0], SFmode)
+ || reg_or_0_operand (operands[1], SFmode))"
+ { return loongarch_output_move (operands[0], operands[1]); }
+ [(set_attr "move_type" "fmove,mgtf,fpload,fpstore,fpload,fpstore,store,mgtf,mftg,move,load,store")
+ (set_attr "mode" "SF")])
+
+(define_insn "*movsf_softfloat"
+ [(set (match_operand:SF 0 "nonimmediate_operand" "=r,r,m")
+ (match_operand:SF 1 "move_operand" "Gr,m,r"))]
+ "TARGET_SOFT_FLOAT
+ && (register_operand (operands[0], SFmode)
+ || reg_or_0_operand (operands[1], SFmode))"
+ { return loongarch_output_move (operands[0], operands[1]); }
+ [(set_attr "move_type" "move,load,store")
+ (set_attr "mode" "SF")])
+
+;; 64-bit floating point moves
+
+(define_expand "movdf"
+ [(set (match_operand:DF 0 "")
+ (match_operand:DF 1 ""))]
+ ""
+{
+ if (loongarch_legitimize_move (DFmode, operands[0], operands[1]))
+ DONE;
+})
+
+(define_insn "*movdf_hardfloat"
+ [(set (match_operand:DF 0 "nonimmediate_operand" "=f,f,f,m,f,k,m,*f,*r,*r,*r,*m")
+ (match_operand:DF 1 "move_operand" "f,G,m,f,k,f,G,*r,*f,*r*G,*m,*r"))]
+ "TARGET_DOUBLE_FLOAT
+ && (register_operand (operands[0], DFmode)
+ || reg_or_0_operand (operands[1], DFmode))"
+ { return loongarch_output_move (operands[0], operands[1]); }
+ [(set_attr "move_type" "fmove,mgtf,fpload,fpstore,fpload,fpstore,store,mgtf,mftg,move,load,store")
+ (set_attr "mode" "DF")])
+
+(define_insn "*movdf_softfloat"
+ [(set (match_operand:DF 0 "nonimmediate_operand" "=r,r,m")
+ (match_operand:DF 1 "move_operand" "rG,m,rG"))]
+ "(TARGET_SOFT_FLOAT || TARGET_SINGLE_FLOAT)
+ && (register_operand (operands[0], DFmode)
+ || reg_or_0_operand (operands[1], DFmode))"
+ { return loongarch_output_move (operands[0], operands[1]); }
+ [(set_attr "move_type" "move,load,store")
+ (set_attr "mode" "DF")])
+
+
+;; 128-bit integer moves
+
+(define_expand "movti"
+ [(set (match_operand:TI 0)
+ (match_operand:TI 1))]
+ "TARGET_64BIT"
+{
+ if (loongarch_legitimize_move (TImode, operands[0], operands[1]))
+ DONE;
+})
+
+(define_insn "*movti"
+ [(set (match_operand:TI 0 "nonimmediate_operand" "=r,r,r,m")
+ (match_operand:TI 1 "move_operand" "r,i,m,rJ"))]
+ "TARGET_64BIT
+ && (register_operand (operands[0], TImode)
+ || reg_or_0_operand (operands[1], TImode))"
+ { return loongarch_output_move (operands[0], operands[1]); }
+ [(set_attr "move_type" "move,const,load,store")
+ (set (attr "mode")
+ (if_then_else (eq_attr "move_type" "imul")
+ (const_string "SI")
+ (const_string "TI")))])
+
+;; 128-bit floating point moves
+
+(define_expand "movtf"
+ [(set (match_operand:TF 0)
+ (match_operand:TF 1))]
+ "TARGET_64BIT"
+{
+ if (loongarch_legitimize_move (TFmode, operands[0], operands[1]))
+ DONE;
+})
+
+;; This pattern handles both hard- and soft-float cases.
+(define_insn "*movtf"
+ [(set (match_operand:TF 0 "nonimmediate_operand" "=r,r,m,f,r,f,m")
+ (match_operand:TF 1 "move_operand" "rG,m,rG,rG,f,m,f"))]
+ "TARGET_64BIT
+ && (register_operand (operands[0], TFmode)
+ || reg_or_0_operand (operands[1], TFmode))"
+ "#"
+ [(set_attr "move_type" "move,load,store,mgtf,mftg,fpload,fpstore")
+ (set_attr "mode" "TF")])
+
+(define_split
+ [(set (match_operand:MOVE64 0 "nonimmediate_operand")
+ (match_operand:MOVE64 1 "move_operand"))]
+ "reload_completed && loongarch_split_move_insn_p (operands[0], operands[1])"
+ [(const_int 0)]
+{
+ loongarch_split_move_insn (operands[0], operands[1], curr_insn);
+ DONE;
+})
+
+(define_split
+ [(set (match_operand:MOVE128 0 "nonimmediate_operand")
+ (match_operand:MOVE128 1 "move_operand"))]
+ "reload_completed && loongarch_split_move_insn_p (operands[0], operands[1])"
+ [(const_int 0)]
+{
+ loongarch_split_move_insn (operands[0], operands[1], curr_insn);
+ DONE;
+})
+
+;; Emit a doubleword move in which exactly one of the operands is
+;; a floating-point register. We can't just emit two normal moves
+;; because of the constraints imposed by the FPU register model;
+;; see loongarch_can_change_mode_class for details. Instead, we keep
+;; the FPR whole and use special patterns to refer to each word of
+;; the other operand.
+
+(define_expand "move_doubleword_fpr<mode>"
+ [(set (match_operand:SPLITF 0)
+ (match_operand:SPLITF 1))]
+ ""
+{
+ if (FP_REG_RTX_P (operands[0]))
+ {
+ rtx low = loongarch_subword (operands[1], 0);
+ rtx high = loongarch_subword (operands[1], 1);
+ emit_insn (gen_load_low<mode> (operands[0], low));
+ if (!TARGET_64BIT)
+ emit_insn (gen_movgr2frh<mode> (operands[0], high, operands[0]));
+ else
+ emit_insn (gen_load_high<mode> (operands[0], high, operands[0]));
+ }
+ else
+ {
+ rtx low = loongarch_subword (operands[0], 0);
+ rtx high = loongarch_subword (operands[0], 1);
+ emit_insn (gen_store_word<mode> (low, operands[1], const0_rtx));
+ if (!TARGET_64BIT)
+ emit_insn (gen_movfrh2gr<mode> (high, operands[1]));
+ else
+ emit_insn (gen_store_word<mode> (high, operands[1], const1_rtx));
+ }
+ DONE;
+})
+
+;; Clear one FCC register
+
+(define_insn "movfcc"
+ [(set (match_operand:FCC 0 "register_operand" "=z")
+ (const_int 0))]
+ ""
+ "movgr2cf\t%0,$r0")
+
+;; Conditional move instructions.
+
+(define_insn "*sel<code><GPR:mode>_using_<GPR2:mode>"
+ [(set (match_operand:GPR 0 "register_operand" "=r,r")
+ (if_then_else:GPR
+ (equality_op:GPR2 (match_operand:GPR2 1 "register_operand" "r,r")
+ (const_int 0))
+ (match_operand:GPR 2 "reg_or_0_operand" "r,J")
+ (match_operand:GPR 3 "reg_or_0_operand" "J,r")))]
+ "register_operand (operands[2], <GPR:MODE>mode)
+ != register_operand (operands[3], <GPR:MODE>mode)"
+ "@
+ <sel>\t%0,%2,%1
+ <selinv>\t%0,%3,%1"
+ [(set_attr "type" "condmove")
+ (set_attr "mode" "<GPR:MODE>")])
+
+;; fsel copies the 3rd argument when the 1st is non-zero and the 2nd
+;; argument if the 1st is zero. This means operand 2 and 3 are
+;; inverted in the instruction.
+
+(define_insn "*sel<mode>"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (if_then_else:ANYF
+ (ne:FCC (match_operand:FCC 1 "register_operand" "z")
+ (const_int 0))
+ (match_operand:ANYF 2 "reg_or_0_operand" "f")
+ (match_operand:ANYF 3 "reg_or_0_operand" "f")))]
+ ""
+ "fsel\t%0,%3,%2,%1"
+ [(set_attr "type" "condmove")
+ (set_attr "mode" "<ANYF:MODE>")])
+
+;; These are the main define_expand's used to make conditional moves.
+
+(define_expand "mov<mode>cc"
+ [(set (match_operand:GPR 0 "register_operand")
+ (if_then_else:GPR (match_operator 1 "comparison_operator"
+ [(match_operand:GPR 2 "reg_or_0_operand")
+ (match_operand:GPR 3 "reg_or_0_operand")])))]
+ "TARGET_COND_MOVE_INT"
+{
+ if (!INTEGRAL_MODE_P (GET_MODE (XEXP (operands[1], 0))))
+ FAIL;
+
+ loongarch_expand_conditional_move (operands);
+ DONE;
+})
+
+(define_expand "mov<mode>cc"
+ [(set (match_operand:ANYF 0 "register_operand")
+ (if_then_else:ANYF (match_operator 1 "comparison_operator"
+ [(match_operand:ANYF 2 "reg_or_0_operand")
+ (match_operand:ANYF 3 "reg_or_0_operand")])))]
+ "TARGET_COND_MOVE_FLOAT"
+{
+ if (!FLOAT_MODE_P (GET_MODE (XEXP (operands[1], 0))))
+ FAIL;
+
+ loongarch_expand_conditional_move (operands);
+ DONE;
+})
+
+(define_insn "lu32i_d"
+ [(set (match_operand:DI 0 "register_operand" "=r")
+ (ior:DI
+ (zero_extend:DI
+ (subreg:SI (match_operand:DI 1 "register_operand" "0") 0))
+ (match_operand:DI 2 "const_lu32i_operand" "u")))]
+ "TARGET_64BIT"
+ "lu32i.d\t%0,%X2>>32"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "DI")])
+
+(define_insn "lu52i_d"
+ [(set (match_operand:DI 0 "register_operand" "=r")
+ (ior:DI
+ (and:DI (match_operand:DI 1 "register_operand" "r")
+ (match_operand 2 "lu52i_mask_operand"))
+ (match_operand 3 "const_lu52i_operand" "v")))]
+ "TARGET_64BIT"
+ "lu52i.d\t%0,%1,%X3>>52"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "DI")])
+
+;; Convert floating-point numbers to integers
+(define_insn "frint_<fmt>"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (unspec:ANYF [(match_operand:ANYF 1 "register_operand" "f")]
+ UNSPEC_FRINT))]
+ ""
+ "frint.<fmt>\t%0,%1"
+ [(set_attr "type" "fcvt")
+ (set_attr "mode" "<MODE>")])
+
+;; Load the low word of operand 0 with operand 1.
+(define_insn "load_low<mode>"
+ [(set (match_operand:SPLITF 0 "register_operand" "=f,f")
+ (unspec:SPLITF [(match_operand:<HALFMODE> 1 "general_operand" "rJ,m")]
+ UNSPEC_LOAD_LOW))]
+ "TARGET_HARD_FLOAT"
+{
+ operands[0] = loongarch_subword (operands[0], 0);
+ return loongarch_output_move (operands[0], operands[1]);
+}
+ [(set_attr "move_type" "mgtf,fpload")
+ (set_attr "mode" "<HALFMODE>")])
+
+;; Load the high word of operand 0 from operand 1, preserving the value
+;; in the low word.
+(define_insn "load_high<mode>"
+ [(set (match_operand:SPLITF 0 "register_operand" "=f,f")
+ (unspec:SPLITF [(match_operand:<HALFMODE> 1 "general_operand" "rJ,m")
+ (match_operand:SPLITF 2 "register_operand" "0,0")]
+ UNSPEC_LOAD_HIGH))]
+ "TARGET_HARD_FLOAT"
+{
+ operands[0] = loongarch_subword (operands[0], 1);
+ return loongarch_output_move (operands[0], operands[1]);
+}
+ [(set_attr "move_type" "mgtf,fpload")
+ (set_attr "mode" "<HALFMODE>")])
+
+;; Store one word of operand 1 in operand 0. Operand 2 is 1 to store the
+;; high word and 0 to store the low word.
+(define_insn "store_word<mode>"
+ [(set (match_operand:<HALFMODE> 0 "nonimmediate_operand" "=r,m")
+ (unspec:<HALFMODE> [(match_operand:SPLITF 1 "register_operand" "f,f")
+ (match_operand 2 "const_int_operand")]
+ UNSPEC_STORE_WORD))]
+ "TARGET_HARD_FLOAT"
+{
+ operands[1] = loongarch_subword (operands[1], INTVAL (operands[2]));
+ return loongarch_output_move (operands[0], operands[1]);
+}
+ [(set_attr "move_type" "mftg,fpstore")
+ (set_attr "mode" "<HALFMODE>")])
+
+;; Thread-Local Storage
+
+(define_insn "@got_load_tls_gd<mode>"
+ [(set (match_operand:P 0 "register_operand" "=r")
+ (unspec:P
+ [(match_operand:P 1 "symbolic_operand" "")]
+ UNSPEC_TLS_GD))]
+ ""
+ "la.tls.gd\t%0,%1"
+ [(set_attr "got" "load")
+ (set_attr "mode" "<MODE>")])
+
+(define_insn "@got_load_tls_ld<mode>"
+ [(set (match_operand:P 0 "register_operand" "=r")
+ (unspec:P
+ [(match_operand:P 1 "symbolic_operand" "")]
+ UNSPEC_TLS_LD))]
+ ""
+ "la.tls.ld\t%0,%1"
+ [(set_attr "got" "load")
+ (set_attr "mode" "<MODE>")])
+
+(define_insn "@got_load_tls_le<mode>"
+ [(set (match_operand:P 0 "register_operand" "=r")
+ (unspec:P
+ [(match_operand:P 1 "symbolic_operand" "")]
+ UNSPEC_TLS_LE))]
+ ""
+ "la.tls.le\t%0,%1"
+ [(set_attr "got" "load")
+ (set_attr "mode" "<MODE>")])
+
+(define_insn "@got_load_tls_ie<mode>"
+ [(set (match_operand:P 0 "register_operand" "=r")
+ (unspec:P
+ [(match_operand:P 1 "symbolic_operand" "")]
+ UNSPEC_TLS_IE))]
+ ""
+ "la.tls.ie\t%0,%1"
+ [(set_attr "got" "load")
+ (set_attr "mode" "<MODE>")])
+
+;; Move operand 1 to the high word of operand 0 using movgr2frh.w, preserving the
+;; value in the low word.
+(define_insn "movgr2frh<mode>"
+ [(set (match_operand:SPLITF 0 "register_operand" "=f")
+ (unspec:SPLITF [(match_operand:<HALFMODE> 1 "reg_or_0_operand" "rJ")
+ (match_operand:SPLITF 2 "register_operand" "0")]
+ UNSPEC_MOVGR2FRH))]
+ "TARGET_DOUBLE_FLOAT"
+ "movgr2frh.w\t%z1,%0"
+ [(set_attr "move_type" "mgtf")
+ (set_attr "mode" "<HALFMODE>")])
+
+;; Move high word of operand 1 to operand 0 using movfrh2gr.s.
+(define_insn "movfrh2gr<mode>"
+ [(set (match_operand:<HALFMODE> 0 "register_operand" "=r")
+ (unspec:<HALFMODE> [(match_operand:SPLITF 1 "register_operand" "f")]
+ UNSPEC_MOVFRH2GR))]
+ "TARGET_DOUBLE_FLOAT"
+ "movfrh2gr.s\t%0,%1"
+ [(set_attr "move_type" "mftg")
+ (set_attr "mode" "<HALFMODE>")])
+
+\f
+;; Expand in-line code to clear the instruction cache between operand[0] and
+;; operand[1].
+(define_expand "clear_cache"
+ [(match_operand 0 "pmode_register_operand")
+ (match_operand 1 "pmode_register_operand")]
+ ""
+{
+ emit_insn (gen_loongarch_ibar (const0_rtx));
+ DONE;
+})
+
+(define_insn "loongarch_ibar"
+ [(unspec_volatile:SI
+ [(match_operand 0 "const_uimm15_operand")] UNSPECV_IBAR)]
+ ""
+ "ibar\t%0")
+
+(define_insn "loongarch_dbar"
+ [(unspec_volatile:SI
+ [(match_operand 0 "const_uimm15_operand")] UNSPECV_DBAR)]
+ ""
+ "dbar\t%0")
+
+\f
+
+;; Privileged state instruction
+
+(define_insn "loongarch_cpucfg"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (unspec_volatile:SI [(match_operand:SI 1 "register_operand" "r")]
+ UNSPECV_CPUCFG))]
+ ""
+ "cpucfg\t%0,%1"
+ [(set_attr "type" "load")
+ (set_attr "mode" "SI")])
+
+(define_insn "loongarch_syscall"
+ [(unspec_volatile:SI
+ [(match_operand 0 "const_uimm15_operand")] UNSPECV_SYSCALL)]
+ ""
+ "syscall\t%0")
+
+(define_insn "loongarch_break"
+ [(unspec_volatile:SI
+ [(match_operand 0 "const_uimm15_operand")] UNSPECV_BREAK)]
+ ""
+ "break\t%0")
+
+(define_insn "loongarch_asrtle_d"
+ [(unspec_volatile:DI [(match_operand:DI 0 "register_operand" "r")
+ (match_operand:DI 1 "register_operand" "r")]
+ UNSPECV_ASRTLE_D)]
+ "TARGET_64BIT"
+ "asrtle.d\t%0,%1"
+ [(set_attr "type" "load")
+ (set_attr "mode" "DI")])
+
+(define_insn "loongarch_asrtgt_d"
+ [(unspec_volatile:DI [(match_operand:DI 0 "register_operand" "r")
+ (match_operand:DI 1 "register_operand" "r")]
+ UNSPECV_ASRTGT_D)]
+ "TARGET_64BIT"
+ "asrtgt.d\t%0,%1"
+ [(set_attr "type" "load")
+ (set_attr "mode" "DI")])
+
+(define_insn "loongarch_csrrd_<d>"
+ [(set (match_operand:GPR 0 "register_operand" "=r")
+ (unspec_volatile:GPR [(match_operand 1 "const_uimm14_operand")]
+ UNSPECV_CSRRD))]
+ ""
+ "csrrd\t%0,%1"
+ [(set_attr "type" "load")
+ (set_attr "mode" "<MODE>")])
+
+(define_insn "loongarch_csrwr_<d>"
+ [(set (match_operand:GPR 0 "register_operand" "=r")
+ (unspec_volatile:GPR
+ [(match_operand:GPR 1 "register_operand" "0")
+ (match_operand 2 "const_uimm14_operand")]
+ UNSPECV_CSRWR))]
+ ""
+ "csrwr\t%0,%2"
+ [(set_attr "type" "store")
+ (set_attr "mode" "<MODE>")])
+
+(define_insn "loongarch_csrxchg_<d>"
+ [(set (match_operand:GPR 0 "register_operand" "=r")
+ (unspec_volatile:GPR
+ [(match_operand:GPR 1 "register_operand" "0")
+ (match_operand:GPR 2 "register_operand" "q")
+ (match_operand 3 "const_uimm14_operand")]
+ UNSPECV_CSRXCHG))]
+ ""
+ "csrxchg\t%0,%2,%3"
+ [(set_attr "type" "load")
+ (set_attr "mode" "<MODE>")])
+
+(define_insn "loongarch_iocsrrd_<size>"
+ [(set (match_operand:QHWD 0 "register_operand" "=r")
+ (unspec_volatile:QHWD [(match_operand:SI 1 "register_operand" "r")]
+ UNSPECV_IOCSRRD))]
+ ""
+ "iocsrrd.<size>\t%0,%1"
+ [(set_attr "type" "load")
+ (set_attr "mode" "<MODE>")])
+
+(define_insn "loongarch_iocsrwr_<size>"
+ [(unspec_volatile:QHWD [(match_operand:QHWD 0 "register_operand" "r")
+ (match_operand:SI 1 "register_operand" "r")]
+ UNSPECV_IOCSRWR)]
+ ""
+ "iocsrwr.<size>\t%0,%1"
+ [(set_attr "type" "load")
+ (set_attr "mode" "<MODE>")])
+
+(define_insn "loongarch_cacop_<d>"
+ [(unspec_volatile:X [(match_operand 0 "const_uimm5_operand")
+ (match_operand:X 1 "register_operand" "r")
+ (match_operand 2 "const_imm12_operand")]
+ UNSPECV_CACOP)]
+ ""
+ "cacop\t%0,%1,%2"
+ [(set_attr "type" "load")
+ (set_attr "mode" "<MODE>")])
+
+(define_insn "loongarch_lddir_<d>"
+ [(unspec_volatile:X [(match_operand:X 0 "register_operand" "r")
+ (match_operand:X 1 "register_operand" "r")
+ (match_operand 2 "const_uimm5_operand")]
+ UNSPECV_LDDIR)]
+ ""
+ "lddir\t%0,%1,%2"
+ [(set_attr "type" "load")
+ (set_attr "mode" "<MODE>")])
+
+(define_insn "loongarch_ldpte_<d>"
+ [(unspec_volatile:X [(match_operand:X 0 "register_operand" "r")
+ (match_operand 1 "const_uimm5_operand")]
+ UNSPECV_LDPTE)]
+ ""
+ "ldpte\t%0,%1"
+ [(set_attr "type" "load")
+ (set_attr "mode" "<MODE>")])
+
+\f
+;; Block moves, see loongarch.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 "cpymemsi"
+ [(parallel [(set (match_operand:BLK 0 "general_operand")
+ (match_operand:BLK 1 "general_operand"))
+ (use (match_operand:SI 2 ""))
+ (use (match_operand:SI 3 "const_int_operand"))])]
+ ""
+{
+ if (TARGET_DO_OPTIMIZE_BLOCK_MOVE_P
+ && loongarch_expand_block_move (operands[0], operands[1], operands[2]))
+ DONE;
+ else
+ FAIL;
+})
+\f
+;;
+;; ....................
+;;
+;; SHIFTS
+;;
+;; ....................
+
+(define_insn "<optab><mode>3"
+ [(set (match_operand:GPR 0 "register_operand" "=r")
+ (any_shift:GPR (match_operand:GPR 1 "register_operand" "r")
+ (match_operand:SI 2 "arith_operand" "rI")))]
+ ""
+{
+ if (CONST_INT_P (operands[2]))
+ operands[2] = GEN_INT (INTVAL (operands[2])
+ & (GET_MODE_BITSIZE (<MODE>mode) - 1));
+
+ return "<insn>%i2.<d>\t%0,%1,%2";
+}
+ [(set_attr "type" "shift")
+ (set_attr "mode" "<MODE>")])
+
+(define_insn "*<optab>si3_extend"
+ [(set (match_operand:DI 0 "register_operand" "=r")
+ (sign_extend:DI
+ (any_shift:SI (match_operand:SI 1 "register_operand" "r")
+ (match_operand:SI 2 "arith_operand" "rI"))))]
+ "TARGET_64BIT"
+{
+ if (CONST_INT_P (operands[2]))
+ operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);
+
+ return "<insn>%i2.w\t%0,%1,%2";
+}
+ [(set_attr "type" "shift")
+ (set_attr "mode" "SI")])
+
+(define_insn "rotr<mode>3"
+ [(set (match_operand:GPR 0 "register_operand" "=r,r")
+ (rotatert:GPR (match_operand:GPR 1 "register_operand" "r,r")
+ (match_operand:SI 2 "arith_operand" "r,I")))]
+ ""
+ "rotr%i2.<d>\t%0,%1,%2"
+ [(set_attr "type" "shift,shift")
+ (set_attr "mode" "<MODE>")])
+
+\f
+;; The following templates were added to generate "bstrpick.d + alsl.d"
+;; instruction pairs.
+;; It is required that the values of const_immalsl_operand and
+;; immediate_operand must have the following correspondence:
+;;
+;; (immediate_operand >> const_immalsl_operand) == 0xffffffff
+
+(define_insn "zero_extend_ashift"
+ [(set (match_operand:DI 0 "register_operand" "=r")
+ (and:DI (ashift:DI (match_operand:DI 1 "register_operand" "r")
+ (match_operand 2 "const_immalsl_operand" ""))
+ (match_operand 3 "immediate_operand" "")))]
+ "TARGET_64BIT
+ && ((INTVAL (operands[3]) >> INTVAL (operands[2])) == 0xffffffff)"
+ "bstrpick.d\t%0,%1,31,0\n\talsl.d\t%0,%0,$r0,%2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "DI")
+ (set_attr "insn_count" "2")])
+
+(define_insn "bstrpick_alsl_paired"
+ [(set (match_operand:DI 0 "register_operand" "=&r")
+ (plus:DI (match_operand:DI 1 "register_operand" "r")
+ (and:DI (ashift:DI (match_operand:DI 2 "register_operand" "r")
+ (match_operand 3 "const_immalsl_operand" ""))
+ (match_operand 4 "immediate_operand" ""))))]
+ "TARGET_64BIT
+ && ((INTVAL (operands[4]) >> INTVAL (operands[3])) == 0xffffffff)"
+ "bstrpick.d\t%0,%2,31,0\n\talsl.d\t%0,%0,%1,%3"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "DI")
+ (set_attr "insn_count" "2")])
+
+(define_insn "alsl<mode>3"
+ [(set (match_operand:GPR 0 "register_operand" "=r")
+ (plus:GPR (ashift:GPR (match_operand:GPR 1 "register_operand" "r")
+ (match_operand 2 "const_immalsl_operand" ""))
+ (match_operand:GPR 3 "register_operand" "r")))]
+ ""
+ "alsl.<d>\t%0,%1,%3,%2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "<MODE>")])
+
+\f
+
+;; Reverse the order of bytes of operand 1 and store the result in operand 0.
+
+(define_insn "bswaphi2"
+ [(set (match_operand:HI 0 "register_operand" "=r")
+ (bswap:HI (match_operand:HI 1 "register_operand" "r")))]
+ ""
+ "revb.2h\t%0,%1"
+ [(set_attr "type" "shift")])
+
+(define_insn_and_split "bswapsi2"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (bswap:SI (match_operand:SI 1 "register_operand" "r")))]
+ ""
+ "#"
+ ""
+ [(set (match_dup 0) (unspec:SI [(match_dup 1)] UNSPEC_REVB_2H))
+ (set (match_dup 0) (rotatert:SI (match_dup 0) (const_int 16)))]
+ ""
+ [(set_attr "insn_count" "2")])
+
+(define_insn_and_split "bswapdi2"
+ [(set (match_operand:DI 0 "register_operand" "=r")
+ (bswap:DI (match_operand:DI 1 "register_operand" "r")))]
+ "TARGET_64BIT"
+ "#"
+ ""
+ [(set (match_dup 0) (unspec:DI [(match_dup 1)] UNSPEC_REVB_4H))
+ (set (match_dup 0) (unspec:DI [(match_dup 0)] UNSPEC_REVH_D))]
+ ""
+ [(set_attr "insn_count" "2")])
+
+(define_insn "revb_2h"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (unspec:SI [(match_operand:SI 1 "register_operand" "r")] UNSPEC_REVB_2H))]
+ ""
+ "revb.2h\t%0,%1"
+ [(set_attr "type" "shift")])
+
+(define_insn "revb_4h"
+ [(set (match_operand:DI 0 "register_operand" "=r")
+ (unspec:DI [(match_operand:DI 1 "register_operand" "r")] UNSPEC_REVB_4H))]
+ "TARGET_64BIT"
+ "revb.4h\t%0,%1"
+ [(set_attr "type" "shift")])
+
+(define_insn "revh_d"
+ [(set (match_operand:DI 0 "register_operand" "=r")
+ (unspec:DI [(match_operand:DI 1 "register_operand" "r")] UNSPEC_REVH_D))]
+ "TARGET_64BIT"
+ "revh.d\t%0,%1"
+ [(set_attr "type" "shift")])
+\f
+;;
+;; ....................
+;;
+;; CONDITIONAL BRANCHES
+;;
+;; ....................
+
+;; Conditional branches
+
+(define_insn "*branch_fp_FCCmode"
+ [(set (pc)
+ (if_then_else
+ (match_operator 1 "equality_operator"
+ [(match_operand:FCC 2 "register_operand" "z")
+ (const_int 0)])
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ "TARGET_HARD_FLOAT"
+{
+ return loongarch_output_conditional_branch (insn, operands,
+ LARCH_BRANCH ("b%F1", "%Z2%0"),
+ LARCH_BRANCH ("b%W1", "%Z2%0"));
+}
+ [(set_attr "type" "branch")])
+
+(define_insn "*branch_fp_inverted_FCCmode"
+ [(set (pc)
+ (if_then_else
+ (match_operator 1 "equality_operator"
+ [(match_operand:FCC 2 "register_operand" "z")
+ (const_int 0)])
+ (pc)
+ (label_ref (match_operand 0 "" ""))))]
+ "TARGET_HARD_FLOAT"
+{
+ return loongarch_output_conditional_branch (insn, operands,
+ LARCH_BRANCH ("b%W1", "%Z2%0"),
+ LARCH_BRANCH ("b%F1", "%Z2%0"));
+}
+ [(set_attr "type" "branch")])
+
+;; Conditional branches on ordered comparisons with zero.
+
+(define_insn "*branch_order<mode>"
+ [(set (pc)
+ (if_then_else
+ (match_operator 1 "order_operator"
+ [(match_operand:X 2 "register_operand" "r,r")
+ (match_operand:X 3 "reg_or_0_operand" "J,r")])
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ { return loongarch_output_order_conditional_branch (insn, operands, false); }
+ [(set_attr "type" "branch")])
+
+(define_insn "*branch_order<mode>_inverted"
+ [(set (pc)
+ (if_then_else
+ (match_operator 1 "order_operator"
+ [(match_operand:X 2 "register_operand" "r,r")
+ (match_operand:X 3 "reg_or_0_operand" "J,r")])
+ (pc)
+ (label_ref (match_operand 0 "" ""))))]
+ ""
+ { return loongarch_output_order_conditional_branch (insn, operands, true); }
+ [(set_attr "type" "branch")])
+
+;; Conditional branch on equality comparison.
+
+(define_insn "branch_equality<mode>"
+ [(set (pc)
+ (if_then_else
+ (match_operator 1 "equality_operator"
+ [(match_operand:X 2 "register_operand" "r")
+ (match_operand:X 3 "reg_or_0_operand" "rJ")])
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ ""
+ { return loongarch_output_equal_conditional_branch (insn, operands, false); }
+ [(set_attr "type" "branch")])
+
+
+(define_insn "*branch_equality<mode>_inverted"
+ [(set (pc)
+ (if_then_else
+ (match_operator 1 "equality_operator"
+ [(match_operand:X 2 "register_operand" "r")
+ (match_operand:X 3 "reg_or_0_operand" "rJ")])
+ (pc)
+ (label_ref (match_operand 0 "" ""))))]
+ ""
+ { return loongarch_output_equal_conditional_branch (insn, operands, true); }
+ [(set_attr "type" "branch")])
+
+
+(define_expand "cbranch<mode>4"
+ [(set (pc)
+ (if_then_else (match_operator 0 "comparison_operator"
+ [(match_operand:GPR 1 "register_operand")
+ (match_operand:GPR 2 "nonmemory_operand")])
+ (label_ref (match_operand 3 ""))
+ (pc)))]
+ ""
+{
+ loongarch_expand_conditional_branch (operands);
+ DONE;
+})
+
+(define_expand "cbranch<mode>4"
+ [(set (pc)
+ (if_then_else (match_operator 0 "comparison_operator"
+ [(match_operand:ANYF 1 "register_operand")
+ (match_operand:ANYF 2 "register_operand")])
+ (label_ref (match_operand 3 ""))
+ (pc)))]
+ ""
+{
+ loongarch_expand_conditional_branch (operands);
+ DONE;
+})
+
+;; Used to implement built-in functions.
+(define_expand "condjump"
+ [(set (pc)
+ (if_then_else (match_operand 0)
+ (label_ref (match_operand 1))
+ (pc)))])
+
+
+\f
+;;
+;; ....................
+;;
+;; SETTING A REGISTER FROM A COMPARISON
+;;
+;; ....................
+
+;; Destination is always set in SI mode.
+
+(define_expand "cstore<mode>4"
+ [(set (match_operand:SI 0 "register_operand")
+ (match_operator:SI 1 "loongarch_cstore_operator"
+ [(match_operand:GPR 2 "register_operand")
+ (match_operand:GPR 3 "nonmemory_operand")]))]
+ ""
+{
+ loongarch_expand_scc (operands);
+ DONE;
+})
+
+(define_insn "*seq_zero_<X:mode><GPR:mode>"
+ [(set (match_operand:GPR 0 "register_operand" "=r")
+ (eq:GPR (match_operand:X 1 "register_operand" "r")
+ (const_int 0)))]
+ ""
+ "sltui\t%0,%1,1"
+ [(set_attr "type" "slt")
+ (set_attr "mode" "<X:MODE>")])
+
+
+(define_insn "*sne_zero_<X:mode><GPR:mode>"
+ [(set (match_operand:GPR 0 "register_operand" "=r")
+ (ne:GPR (match_operand:X 1 "register_operand" "r")
+ (const_int 0)))]
+ ""
+ "sltu\t%0,%.,%1"
+ [(set_attr "type" "slt")
+ (set_attr "mode" "<X:MODE>")])
+
+(define_insn "*sgt<u>_<X:mode><GPR:mode>"
+ [(set (match_operand:GPR 0 "register_operand" "=r")
+ (any_gt:GPR (match_operand:X 1 "register_operand" "r")
+ (match_operand:X 2 "reg_or_0_operand" "rJ")))]
+ ""
+ "slt<u>\t%0,%z2,%1"
+ [(set_attr "type" "slt")
+ (set_attr "mode" "<X:MODE>")])
+
+(define_insn "*sge<u>_<X:mode><GPR:mode>"
+ [(set (match_operand:GPR 0 "register_operand" "=r")
+ (any_ge:GPR (match_operand:X 1 "register_operand" "r")
+ (const_int 1)))]
+ ""
+ "slt<u>i\t%0,%.,%1"
+ [(set_attr "type" "slt")
+ (set_attr "mode" "<X:MODE>")])
+
+(define_insn "*slt<u>_<X:mode><GPR:mode>"
+ [(set (match_operand:GPR 0 "register_operand" "=r")
+ (any_lt:GPR (match_operand:X 1 "register_operand" "r")
+ (match_operand:X 2 "arith_operand" "rI")))]
+ ""
+ "slt<u>%i2\t%0,%1,%2";
+ [(set_attr "type" "slt")
+ (set_attr "mode" "<X:MODE>")])
+
+(define_insn "*sle<u>_<X:mode><GPR:mode>"
+ [(set (match_operand:GPR 0 "register_operand" "=r")
+ (any_le:GPR (match_operand:X 1 "register_operand" "r")
+ (match_operand:X 2 "sle_operand" "")))]
+ ""
+{
+ operands[2] = GEN_INT (INTVAL (operands[2]) + 1);
+ return "slt<u>i\t%0,%1,%2";
+}
+ [(set_attr "type" "slt")
+ (set_attr "mode" "<X:MODE>")])
+
+\f
+;;
+;; ....................
+;;
+;; FLOATING POINT COMPARISONS
+;;
+;; ....................
+
+(define_insn "s<code>_<ANYF:mode>_using_FCCmode"
+ [(set (match_operand:FCC 0 "register_operand" "=z")
+ (fcond:FCC (match_operand:ANYF 1 "register_operand" "f")
+ (match_operand:ANYF 2 "register_operand" "f")))]
+ ""
+ "fcmp.<fcond>.<fmt>\t%Z0%1,%2"
+ [(set_attr "type" "fcmp")
+ (set_attr "mode" "FCC")])
+
+\f
+;;
+;; ....................
+;;
+;; UNCONDITIONAL BRANCHES
+;;
+;; ....................
+
+;; Unconditional branches.
+
+(define_expand "jump"
+ [(set (pc)
+ (label_ref (match_operand 0)))])
+
+(define_insn "*jump_absolute"
+ [(set (pc)
+ (label_ref (match_operand 0)))]
+ "!flag_pic"
+{
+ return "b\t%l0";
+}
+ [(set_attr "type" "branch")])
+
+(define_insn "*jump_pic"
+ [(set (pc)
+ (label_ref (match_operand 0)))]
+ "flag_pic"
+{
+ return "b\t%0";
+}
+ [(set_attr "type" "branch")])
+
+(define_expand "indirect_jump"
+ [(set (pc) (match_operand 0 "register_operand"))]
+ ""
+{
+ operands[0] = force_reg (Pmode, operands[0]);
+ emit_jump_insn (gen_indirect_jump (Pmode, operands[0]));
+ DONE;
+})
+
+(define_insn "@indirect_jump<mode>"
+ [(set (pc) (match_operand:P 0 "register_operand" "r"))]
+ ""
+ "jr\t%0"
+ [(set_attr "type" "jump")
+ (set_attr "mode" "none")])
+
+(define_expand "tablejump"
+ [(set (pc)
+ (match_operand 0 "register_operand"))
+ (use (label_ref (match_operand 1 "")))]
+ ""
+{
+ if (flag_pic)
+ operands[0] = expand_simple_binop (Pmode, PLUS, operands[0],
+ gen_rtx_LABEL_REF (Pmode,
+ operands[1]),
+ NULL_RTX, 0, OPTAB_DIRECT);
+ emit_jump_insn (gen_tablejump (Pmode, operands[0], operands[1]));
+ DONE;
+})
+
+(define_insn "@tablejump<mode>"
+ [(set (pc)
+ (match_operand:P 0 "register_operand" "r"))
+ (use (label_ref (match_operand 1 "" "")))]
+ ""
+ "jr\t%0"
+ [(set_attr "type" "jump")
+ (set_attr "mode" "none")])
+
+
+\f
+;;
+;; ....................
+;;
+;; Function prologue/epilogue
+;;
+;; ....................
+;;
+
+(define_expand "prologue"
+ [(const_int 1)]
+ ""
+{
+ loongarch_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)] UNSPECV_BLOCKAGE)]
+ ""
+ ""
+ [(set_attr "type" "ghost")
+ (set_attr "mode" "none")])
+
+(define_insn "@probe_stack_range<P:mode>"
+ [(set (match_operand:P 0 "register_operand" "=r")
+ (unspec_volatile:P [(match_operand:P 1 "register_operand" "0")
+ (match_operand:P 2 "register_operand" "r")
+ (match_operand:P 3 "register_operand" "r")]
+ UNSPECV_PROBE_STACK_RANGE))]
+ ""
+{
+ return loongarch_output_probe_stack_range (operands[0],
+ operands[2],
+ operands[3]);
+}
+ [(set_attr "type" "unknown")
+ (set_attr "mode" "<MODE>")])
+
+(define_expand "epilogue"
+ [(const_int 2)]
+ ""
+{
+ loongarch_expand_epilogue (false);
+ DONE;
+})
+
+(define_expand "sibcall_epilogue"
+ [(const_int 2)]
+ ""
+{
+ loongarch_expand_epilogue (true);
+ DONE;
+})
+
+;; Trivial return. Make it look like a normal return insn as that
+;; allows jump optimizations to work better.
+
+(define_expand "return"
+ [(simple_return)]
+ "loongarch_can_use_return_insn ()"
+ { })
+
+(define_expand "simple_return"
+ [(simple_return)]
+ ""
+ { })
+
+(define_insn "*<optab>"
+ [(any_return)]
+ ""
+{
+ operands[0] = gen_rtx_REG (Pmode, RETURN_ADDR_REGNUM);
+ return "jr\t%0";
+}
+ [(set_attr "type" "jump")
+ (set_attr "mode" "none")])
+
+;; Normal return.
+
+(define_insn "<optab>_internal"
+ [(any_return)
+ (use (match_operand 0 "pmode_register_operand" ""))]
+ ""
+ "jr\t%0"
+ [(set_attr "type" "jump")
+ (set_attr "mode" "none")])
+
+;; Exception return.
+(define_insn "loongarch_ertn"
+ [(return)
+ (unspec_volatile [(const_int 0)] UNSPECV_ERTN)]
+ ""
+ "ertn"
+ [(set_attr "type" "trap")
+ (set_attr "mode" "none")])
+
+;; This is used in compiling the unwind routines.
+(define_expand "eh_return"
+ [(use (match_operand 0 "general_operand"))]
+ ""
+{
+ if (GET_MODE (operands[0]) != word_mode)
+ operands[0] = convert_to_mode (word_mode, operands[0], 0);
+ if (TARGET_64BIT)
+ emit_insn (gen_eh_set_ra_di (operands[0]));
+ else
+ emit_insn (gen_eh_set_ra_si (operands[0]));
+ DONE;
+})
+
+;; Clobber the return address on the stack. We can't expand this
+;; until we know where it will be put in the stack frame.
+
+(define_insn "eh_set_ra_si"
+ [(unspec [(match_operand:SI 0 "register_operand" "r")] UNSPEC_EH_RETURN)
+ (clobber (match_scratch:SI 1 "=&r"))]
+ "! TARGET_64BIT"
+ "#")
+
+(define_insn "eh_set_ra_di"
+ [(unspec [(match_operand:DI 0 "register_operand" "r")] UNSPEC_EH_RETURN)
+ (clobber (match_scratch:DI 1 "=&r"))]
+ "TARGET_64BIT"
+ "#")
+
+(define_split
+ [(unspec [(match_operand 0 "register_operand")] UNSPEC_EH_RETURN)
+ (clobber (match_scratch 1))]
+ "reload_completed"
+ [(const_int 0)]
+{
+ loongarch_set_return_address (operands[0], operands[1]);
+ DONE;
+})
+
+
+\f
+;;
+;; ....................
+;;
+;; FUNCTION CALLS
+;;
+;; ....................
+
+;; Sibling calls. All these patterns use jump instructions.
+
+(define_expand "sibcall"
+ [(parallel [(call (match_operand 0 "")
+ (match_operand 1 ""))
+ (use (match_operand 2 "")) ;; next_arg_reg
+ (use (match_operand 3 ""))])] ;; struct_value_size_rtx
+ ""
+{
+ rtx target = loongarch_legitimize_call_address (XEXP (operands[0], 0));
+
+ emit_call_insn (gen_sibcall_internal (target, operands[1]));
+ DONE;
+})
+
+(define_insn "sibcall_internal"
+ [(call (mem:SI (match_operand 0 "call_insn_operand" "j,c,a,t,h"))
+ (match_operand 1 "" ""))]
+ "SIBLING_CALL_P (insn)"
+{
+ switch (which_alternative)
+ {
+ case 0:
+ return "jr\t%0";
+ case 1:
+ if (TARGET_CMODEL_LARGE)
+ return "pcaddu18i\t$r12,(%%pcrel(%0+0x20000))>>18\n\t"
+ "jirl\t$r0,$r12,%%pcrel(%0+4)-(%%pcrel(%0+4+0x20000)>>18<<18)";
+ else if (TARGET_CMODEL_EXTREME)
+ return "la.local\t$r12,$r13,%0\n\tjr\t$r12";
+ else
+ return "b\t%0";
+ case 2:
+ if (TARGET_CMODEL_TINY_STATIC)
+ return "b\t%0";
+ else if (TARGET_CMODEL_EXTREME)
+ return "la.global\t$r12,$r13,%0\n\tjr\t$r12";
+ else
+ return "la.global\t$r12,%0\n\tjr\t$r12";
+ case 3:
+ if (TARGET_CMODEL_EXTREME)
+ return "la.global\t$r12,$r13,%0\n\tjr\t$r12";
+ else
+ return "la.global\t$r12,%0\n\tjr\t$r12";
+ case 4:
+ if (TARGET_CMODEL_NORMAL || TARGET_CMODEL_TINY)
+ return "b\t%%plt(%0)";
+ else if (TARGET_CMODEL_LARGE)
+ return "pcaddu18i\t$r12,(%%plt(%0)+0x20000)>>18\n\t"
+ "jirl\t$r0,$r12,%%plt(%0)+4-((%%plt(%0)+(4+0x20000))>>18<<18)";
+ else
+ /* Cmodel extreme and tiny static not support plt. */
+ gcc_unreachable ();
+ default:
+ gcc_unreachable ();
+ }
+}
+ [(set_attr "jirl" "indirect,direct,direct,direct,direct")])
+
+(define_expand "sibcall_value"
+ [(parallel [(set (match_operand 0 "")
+ (call (match_operand 1 "")
+ (match_operand 2 "")))
+ (use (match_operand 3 ""))])] ;; next_arg_reg
+ ""
+{
+ rtx target = loongarch_legitimize_call_address (XEXP (operands[1], 0));
+
+ /* Handle return values created by loongarch_pass_fpr_pair. */
+ if (GET_CODE (operands[0]) == PARALLEL && XVECLEN (operands[0], 0) == 2)
+ {
+ rtx arg1 = XEXP (XVECEXP (operands[0],0, 0), 0);
+ rtx arg2 = XEXP (XVECEXP (operands[0],0, 1), 0);
+
+ emit_call_insn (gen_sibcall_value_multiple_internal (arg1, target,
+ operands[2],
+ arg2));
+ }
+ else
+ {
+ /* Handle return values created by loongarch_return_fpr_single. */
+ if (GET_CODE (operands[0]) == PARALLEL && XVECLEN (operands[0], 0) == 1)
+ operands[0] = XEXP (XVECEXP (operands[0], 0, 0), 0);
+
+ emit_call_insn (gen_sibcall_value_internal (operands[0], target,
+ operands[2]));
+ }
+ DONE;
+})
+
+(define_insn "sibcall_value_internal"
+ [(set (match_operand 0 "register_operand" "")
+ (call (mem:SI (match_operand 1 "call_insn_operand" "j,c,a,t,h"))
+ (match_operand 2 "" "")))]
+ "SIBLING_CALL_P (insn)"
+{
+ switch (which_alternative)
+ {
+ case 0:
+ return "jr\t%1";
+ case 1:
+ if (TARGET_CMODEL_LARGE)
+ return "pcaddu18i\t$r12,%%pcrel(%1+0x20000)>>18\n\t"
+ "jirl\t$r0,$r12,%%pcrel(%1+4)-((%%pcrel(%1+4+0x20000))>>18<<18)";
+ else if (TARGET_CMODEL_EXTREME)
+ return "la.local\t$r12,$r13,%1\n\tjr\t$r12";
+ else
+ return "b\t%1";
+ case 2:
+ if (TARGET_CMODEL_TINY_STATIC)
+ return "b\t%1";
+ else if (TARGET_CMODEL_EXTREME)
+ return "la.global\t$r12,$r13,%1\n\tjr\t$r12";
+ else
+ return "la.global\t$r12,%1\n\tjr\t$r12";
+ case 3:
+ if (TARGET_CMODEL_EXTREME)
+ return "la.global\t$r12,$r13,%1\n\tjr\t$r12";
+ else
+ return "la.global\t$r12,%1\n\tjr\t$r12";
+ case 4:
+ if (TARGET_CMODEL_NORMAL || TARGET_CMODEL_TINY)
+ return " b\t%%plt(%1)";
+ else if (TARGET_CMODEL_LARGE)
+ return "pcaddu18i\t$r12,(%%plt(%1)+0x20000)>>18\n\t"
+ "jirl\t$r0,$r12,%%plt(%1)+4-((%%plt(%1)+(4+0x20000))>>18<<18)";
+ else
+ /* Cmodel extreme and tiny static not support plt. */
+ gcc_unreachable ();
+ default:
+ gcc_unreachable ();
+ }
+}
+ [(set_attr "jirl" "indirect,direct,direct,direct,direct")])
+
+(define_insn "sibcall_value_multiple_internal"
+ [(set (match_operand 0 "register_operand" "")
+ (call (mem:SI (match_operand 1 "call_insn_operand" "j,c,a,t,h"))
+ (match_operand 2 "" "")))
+ (set (match_operand 3 "register_operand" "")
+ (call (mem:SI (match_dup 1))
+ (match_dup 2)))]
+ "SIBLING_CALL_P (insn)"
+{
+ switch (which_alternative)
+ {
+ case 0:
+ return "jr\t%1";
+ case 1:
+ if (TARGET_CMODEL_LARGE)
+ return "pcaddu18i\t$r12,%%pcrel(%1+0x20000)>>18\n\t"
+ "jirl\t$r0,$r12,%%pcrel(%1+4)-(%%pcrel(%1+4+0x20000)>>18<<18)";
+ else if (TARGET_CMODEL_EXTREME)
+ return "la.local\t$r12,$r13,%1\n\tjr\t$r12";
+ else
+ return "b\t%1";
+ case 2:
+ if (TARGET_CMODEL_TINY_STATIC)
+ return "b\t%1";
+ else if (TARGET_CMODEL_EXTREME)
+ return "la.global\t$r12,$r13,%1\n\tjr\t$r12";
+ else
+ return "la.global\t$r12,%1\n\tjr\t$r12";
+ case 3:
+ if (TARGET_CMODEL_EXTREME)
+ return "la.global\t$r12,$r13,%1\n\tjr\t$r12";
+ else
+ return "la.global\t$r12,%1\n\tjr\t$r12";
+ case 4:
+ if (TARGET_CMODEL_NORMAL || TARGET_CMODEL_TINY)
+ return "b\t%%plt(%1)";
+ else if (TARGET_CMODEL_LARGE)
+ return "pcaddu18i\t$r12,(%%plt(%1)+0x20000)>>18\n\t"
+ "jirl\t$r0,$r12,%%plt(%1)+4-((%%plt(%1)+(4+0x20000))>>18<<18)";
+ else
+ /* Cmodel extreme and tiny static not support plt. */
+ gcc_unreachable ();
+ default:
+ gcc_unreachable ();
+ }
+}
+ [(set_attr "jirl" "indirect,direct,direct,direct,direct")])
+
+(define_expand "call"
+ [(parallel [(call (match_operand 0 "")
+ (match_operand 1 ""))
+ (use (match_operand 2 "")) ;; next_arg_reg
+ (use (match_operand 3 ""))])] ;; struct_value_size_rtx
+ ""
+{
+ rtx target = loongarch_legitimize_call_address (XEXP (operands[0], 0));
+
+ emit_call_insn (gen_call_internal (target, operands[1]));
+ DONE;
+})
+
+(define_insn "call_internal"
+ [(call (mem:SI (match_operand 0 "call_insn_operand" "e,c,a,t,h"))
+ (match_operand 1 "" ""))
+ (clobber (reg:SI RETURN_ADDR_REGNUM))]
+ ""
+{
+ switch (which_alternative)
+ {
+ case 0:
+ return "jirl\t$r1,%0,0";
+ case 1:
+ if (TARGET_CMODEL_LARGE)
+ return "pcaddu18i\t$r1,%%pcrel(%0+0x20000)>>18\n\t"
+ "jirl\t$r1,$r1,%%pcrel(%0+4)-(%%pcrel(%0+4+0x20000)>>18<<18)";
+ else if (TARGET_CMODEL_EXTREME)
+ return "la.local\t$r1,$r12,%0\n\tjirl\t$r1,$r1,0";
+ else
+ return "bl\t%0";
+ case 2:
+ if (TARGET_CMODEL_TINY_STATIC)
+ return "bl\t%0";
+ else if (TARGET_CMODEL_EXTREME)
+ return "la.global\t$r1,$r12,%0\n\tjirl\t$r1,$r1,0";
+ else
+ return "la.global\t$r1,%0\n\tjirl\t$r1,$r1,0";
+ case 3:
+ if (TARGET_CMODEL_EXTREME)
+ return "la.global\t$r1,$r12,%0\n\tjirl\t$r1,$r1,0";
+ else
+ return "la.global\t$r1,%0\n\tjirl\t$r1,$r1,0";
+ case 4:
+ if (TARGET_CMODEL_LARGE)
+ return "pcaddu18i\t$r1,(%%plt(%0)+0x20000)>>18\n\t"
+ "jirl\t$r1,$r1,%%plt(%0)+4-((%%plt(%0)+(4+0x20000))>>18<<18)";
+ else if (TARGET_CMODEL_NORMAL || TARGET_CMODEL_TINY)
+ return "bl\t%%plt(%0)";
+ else
+ /* Cmodel extreme and tiny static not support plt. */
+ gcc_unreachable ();
+ default:
+ gcc_unreachable ();
+ }
+}
+ [(set_attr "jirl" "indirect,direct,direct,direct,direct")
+ (set_attr "insn_count" "1,2,3,3,2")])
+
+(define_expand "call_value"
+ [(parallel [(set (match_operand 0 "")
+ (call (match_operand 1 "")
+ (match_operand 2 "")))
+ (use (match_operand 3 ""))])] ;; next_arg_reg
+ ""
+{
+ rtx target = loongarch_legitimize_call_address (XEXP (operands[1], 0));
+ /* Handle return values created by loongarch_pass_fpr_pair. */
+ if (GET_CODE (operands[0]) == PARALLEL && XVECLEN (operands[0], 0) == 2)
+ {
+ rtx arg1 = XEXP (XVECEXP (operands[0], 0, 0), 0);
+ rtx arg2 = XEXP (XVECEXP (operands[0], 0, 1), 0);
+
+ emit_call_insn (gen_call_value_multiple_internal (arg1, target,
+ operands[2], arg2));
+ }
+ else
+ {
+ /* Handle return values created by loongarch_return_fpr_single. */
+ if (GET_CODE (operands[0]) == PARALLEL && XVECLEN (operands[0], 0) == 1)
+ operands[0] = XEXP (XVECEXP (operands[0], 0, 0), 0);
+
+ emit_call_insn (gen_call_value_internal (operands[0], target,
+ operands[2]));
+ }
+ DONE;
+})
+
+(define_insn "call_value_internal"
+ [(set (match_operand 0 "register_operand" "")
+ (call (mem:SI (match_operand 1 "call_insn_operand" "e,c,a,t,h"))
+ (match_operand 2 "" "")))
+ (clobber (reg:SI RETURN_ADDR_REGNUM))]
+ ""
+{
+ switch (which_alternative)
+ {
+ case 0:
+ return "jirl\t$r1,%1,0";
+ case 1:
+ if (TARGET_CMODEL_LARGE)
+ return "pcaddu18i\t$r1,%%pcrel(%1+0x20000)>>18\n\t"
+ "jirl\t$r1,$r1,%%pcrel(%1+4)-(%%pcrel(%1+4+0x20000)>>18<<18)";
+ else if (TARGET_CMODEL_EXTREME)
+ return "la.local\t$r1,$r12,%1\n\tjirl\t$r1,$r1,0";
+ else
+ return "bl\t%1";
+ case 2:
+ if (TARGET_CMODEL_TINY_STATIC)
+ return "bl\t%1";
+ else if (TARGET_CMODEL_EXTREME)
+ return "la.global\t$r1,$r12,%1\n\tjirl\t$r1,$r1,0";
+ else
+ return "la.global\t$r1,%1\n\tjirl\t$r1,$r1,0";
+ case 3:
+ if (TARGET_CMODEL_EXTREME)
+ return "la.global\t$r1,$r12,%1\n\tjirl\t$r1,$r1,0";
+ else
+ return "la.global\t$r1,%1\n\tjirl\t$r1,$r1,0";
+ case 4:
+ if (TARGET_CMODEL_LARGE)
+ return "pcaddu18i\t$r1,(%%plt(%1)+0x20000)>>18\n\t"
+ "jirl\t$r1,$r1,%%plt(%1)+4-((%%plt(%1)+(4+0x20000))>>18<<18)";
+ else if (TARGET_CMODEL_NORMAL || TARGET_CMODEL_TINY)
+ return "bl\t%%plt(%1)";
+ else
+ /* Cmodel extreme and tiny static not support plt. */
+ gcc_unreachable ();
+ default:
+ gcc_unreachable ();
+ }
+}
+ [(set_attr "jirl" "indirect,direct,direct,direct,direct")
+ (set_attr "insn_count" "1,2,3,3,2")])
+
+(define_insn "call_value_multiple_internal"
+ [(set (match_operand 0 "register_operand" "")
+ (call (mem:SI (match_operand 1 "call_insn_operand" "e,c,a,t,h"))
+ (match_operand 2 "" "")))
+ (set (match_operand 3 "register_operand" "")
+ (call (mem:SI (match_dup 1))
+ (match_dup 2)))
+ (clobber (reg:SI RETURN_ADDR_REGNUM))]
+ ""
+{
+ switch (which_alternative)
+ {
+ case 0:
+ return "jirl\t$r1,%1,0";
+ case 1:
+ if (TARGET_CMODEL_LARGE)
+ return "pcaddu18i\t$r1,%%pcrel(%1+0x20000)>>18\n\t"
+ "jirl\t$r1,$r1,%%pcrel(%1+4)-(%%pcrel(%1+4+0x20000)>>18<<18)";
+ else if (TARGET_CMODEL_EXTREME)
+ return "la.local\t$r1,$r12,%1\n\tjirl\t$r1,$r1,0";
+ else
+ return "bl\t%1";
+ case 2:
+ if (TARGET_CMODEL_TINY_STATIC)
+ return "bl\t%1";
+ else if (TARGET_CMODEL_EXTREME)
+ return "la.global\t$r1,$r12,%1\n\tjirl\t$r1,$r1,0 ";
+ else
+ return "la.global\t$r1,%1\n\tjirl\t$r1,$r1,0";
+ case 3:
+ if (TARGET_CMODEL_EXTREME)
+ return "la.global\t$r1,$r12,%1\n\tjirl\t$r1,$r1,0";
+ else
+ return "la.global\t$r1,%1\n\tjirl\t$r1,$r1,0";
+ case 4:
+ if (TARGET_CMODEL_LARGE)
+ return "pcaddu18i\t$r1,(%%plt(%1)+0x20000)>>18\n\t"
+ "jirl\t$r1,$r1,%%plt(%1)+4-((%%plt(%1)+(4+0x20000))>>18<<18)";
+ else if (TARGET_CMODEL_NORMAL || TARGET_CMODEL_TINY)
+ return "bl\t%%plt(%1)";
+ else
+ /* Cmodel extreme and tiny static not support plt. */
+ gcc_unreachable ();
+ default:
+ gcc_unreachable ();
+ }
+}
+ [(set_attr "jirl" "indirect,direct,direct,direct,direct")
+ (set_attr "insn_count" "1,2,3,3,2")])
+
+
+;; Call subroutine returning any type.
+(define_expand "untyped_call"
+ [(parallel [(call (match_operand 0 "")
+ (const_int 0))
+ (match_operand 1 "")
+ (match_operand 2 "")])]
+ ""
+{
+ int i;
+
+ emit_call_insn (gen_call (operands[0], const0_rtx, NULL, const0_rtx));
+
+ for (i = 0; i < XVECLEN (operands[2], 0); i++)
+ {
+ rtx set = XVECEXP (operands[2], 0, i);
+ loongarch_emit_move (SET_DEST (set), SET_SRC (set));
+ }
+
+ emit_insn (gen_blockage ());
+ DONE;
+})
+\f
+;;
+;; ....................
+;;
+;; MISC.
+;;
+;; ....................
+;;
+
+(define_insn "nop"
+ [(const_int 0)]
+ ""
+ "nop"
+ [(set_attr "type" "nop")
+ (set_attr "mode" "none")])
+
+;; __builtin_loongarch_movfcsr2gr: move the FCSR into operand 0.
+(define_insn "loongarch_movfcsr2gr"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (unspec_volatile:SI [(match_operand 1 "const_uimm5_operand")]
+ UNSPECV_MOVFCSR2GR))]
+ "TARGET_HARD_FLOAT"
+ "movfcsr2gr\t%0,$r%1")
+
+;; __builtin_loongarch_movgr2fcsr: move operand 0 into the FCSR.
+(define_insn "loongarch_movgr2fcsr"
+ [(unspec_volatile [(match_operand 0 "const_uimm5_operand")
+ (match_operand:SI 1 "register_operand" "r")]
+ UNSPECV_MOVGR2FCSR)]
+ "TARGET_HARD_FLOAT"
+ "movgr2fcsr\t$r%0,%1")
+
+(define_insn "fclass_<fmt>"
+ [(set (match_operand:ANYF 0 "register_operand" "=f")
+ (unspec:ANYF [(match_operand:ANYF 1 "register_operand" "f")]
+ UNSPEC_FCLASS))]
+ "TARGET_HARD_FLOAT"
+ "fclass.<fmt>\t%0,%1"
+ [(set_attr "type" "unknown")
+ (set_attr "mode" "<MODE>")])
+
+(define_insn "bytepick_w"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (unspec:SI [(match_operand:SI 1 "register_operand" "r")
+ (match_operand:SI 2 "register_operand" "r")
+ (match_operand:SI 3 "const_0_to_3_operand" "n")]
+ UNSPEC_BYTEPICK_W))]
+ ""
+ "bytepick.w\t%0,%1,%2,%z3"
+ [(set_attr "mode" "SI")])
+
+(define_insn "bytepick_d"
+ [(set (match_operand:DI 0 "register_operand" "=r")
+ (unspec:DI [(match_operand:DI 1 "register_operand" "r")
+ (match_operand:DI 2 "register_operand" "r")
+ (match_operand:DI 3 "const_0_to_7_operand" "n")]
+ UNSPEC_BYTEPICK_D))]
+ ""
+ "bytepick.d\t%0,%1,%2,%z3"
+ [(set_attr "mode" "DI")])
+
+(define_insn "bitrev_4b"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (unspec:SI [(match_operand:SI 1 "register_operand" "r")]
+ UNSPEC_BITREV_4B))]
+ ""
+ "bitrev.4b\t%0,%1"
+ [(set_attr "type" "unknown")
+ (set_attr "mode" "SI")])
+
+(define_insn "bitrev_8b"
+ [(set (match_operand:DI 0 "register_operand" "=r")
+ (unspec:DI [(match_operand:DI 1 "register_operand" "r")]
+ UNSPEC_BITREV_8B))]
+ ""
+ "bitrev.8b\t%0,%1"
+ [(set_attr "type" "unknown")
+ (set_attr "mode" "DI")])
+
+(define_insn "@stack_tie<mode>"
+ [(set (mem:BLK (scratch))
+ (unspec:BLK [(match_operand:X 0 "register_operand" "r")
+ (match_operand:X 1 "register_operand" "r")]
+ UNSPEC_TIE))]
+ ""
+ ""
+ [(set_attr "length" "0")
+ (set_attr "type" "ghost")])
+
+\f
+(define_split
+ [(match_operand 0 "small_data_pattern")]
+ "reload_completed"
+ [(match_dup 0)]
+ { operands[0] = loongarch_rewrite_small_data (operands[0]); })
+
+
+;; Match paired HI/SI/SF/DFmode load/stores.
+(define_insn "*join2_load_store<JOIN_MODE:mode>"
+ [(set (match_operand:JOIN_MODE 0 "nonimmediate_operand"
+ "=&r,f,m,m,&r,ZC")
+ (match_operand:JOIN_MODE 1 "nonimmediate_operand" "m,m,r,f,ZC,r"))
+ (set (match_operand:JOIN_MODE 2 "nonimmediate_operand"
+ "=r,f,m,m,r,ZC")
+ (match_operand:JOIN_MODE 3 "nonimmediate_operand" "m,m,r,f,ZC,r"))]
+ "reload_completed"
+ {
+ /* The load destination does not overlap the source. */
+ gcc_assert (!reg_overlap_mentioned_p (operands[0], operands[1]));
+ output_asm_insn (loongarch_output_move (operands[0], operands[1]),
+ operands);
+ output_asm_insn (loongarch_output_move (operands[2], operands[3]),
+ &operands[2]);
+ return "";
+ }
+ [(set_attr "move_type"
+ "load,fpload,store,fpstore,load,store")
+ (set_attr "insn_count" "2,2,2,2,2,2")])
+
+;; 2 HI/SI/SF/DF loads are bonded.
+(define_peephole2
+ [(set (match_operand:JOIN_MODE 0 "register_operand")
+ (match_operand:JOIN_MODE 1 "non_volatile_mem_operand"))
+ (set (match_operand:JOIN_MODE 2 "register_operand")
+ (match_operand:JOIN_MODE 3 "non_volatile_mem_operand"))]
+ "loongarch_load_store_bonding_p (operands, <JOIN_MODE:MODE>mode, true)"
+ [(parallel [(set (match_dup 0)
+ (match_dup 1))
+ (set (match_dup 2)
+ (match_dup 3))])]
+ "")
+
+;; 2 HI/SI/SF/DF stores are bonded.
+(define_peephole2
+ [(set (match_operand:JOIN_MODE 0 "memory_operand")
+ (match_operand:JOIN_MODE 1 "register_operand"))
+ (set (match_operand:JOIN_MODE 2 "memory_operand")
+ (match_operand:JOIN_MODE 3 "register_operand"))]
+ "loongarch_load_store_bonding_p (operands, <JOIN_MODE:MODE>mode, false)"
+ [(parallel [(set (match_dup 0)
+ (match_dup 1))
+ (set (match_dup 2)
+ (match_dup 3))])]
+ "")
+
+;; Match paired HImode loads.
+(define_insn "*join2_loadhi"
+ [(set (match_operand:SI 0 "register_operand" "=&r")
+ (any_extend:SI (match_operand:HI 1 "non_volatile_mem_operand" "m")))
+ (set (match_operand:SI 2 "register_operand" "=r")
+ (any_extend:SI (match_operand:HI 3 "non_volatile_mem_operand" "m")))]
+ "reload_completed"
+ {
+ /* The load destination does not overlap the source. */
+ gcc_assert (!reg_overlap_mentioned_p (operands[0], operands[1]));
+ output_asm_insn ("ld.h<u>\t%0,%1", operands);
+ output_asm_insn ("ld.h<u>\t%2,%3", operands);
+
+ return "";
+ }
+ [(set_attr "move_type" "load")
+ (set_attr "insn_count" "2")])
+
+
+;; 2 HI loads are bonded.
+(define_peephole2
+ [(set (match_operand:SI 0 "register_operand")
+ (any_extend:SI (match_operand:HI 1 "non_volatile_mem_operand")))
+ (set (match_operand:SI 2 "register_operand")
+ (any_extend:SI (match_operand:HI 3 "non_volatile_mem_operand")))]
+ "loongarch_load_store_bonding_p (operands, HImode, true)"
+ [(parallel [(set (match_dup 0)
+ (any_extend:SI (match_dup 1)))
+ (set (match_dup 2)
+ (any_extend:SI (match_dup 3)))])]
+ "")
+
+\f
+
+(define_mode_iterator QHSD [QI HI SI DI])
+
+(define_insn "loongarch_crc_w_<size>_w"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (unspec:SI [(match_operand:QHSD 1 "register_operand" "r")
+ (match_operand:SI 2 "register_operand" "r")]
+ UNSPEC_CRC))]
+ ""
+ "crc.w.<size>.w\t%0,%1,%2"
+ [(set_attr "type" "unknown")
+ (set_attr "mode" "<MODE>")])
+
+(define_insn "loongarch_crcc_w_<size>_w"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (unspec:SI [(match_operand:QHSD 1 "register_operand" "r")
+ (match_operand:SI 2 "register_operand" "r")]
+ UNSPEC_CRCC))]
+ ""
+ "crcc.w.<size>.w\t%0,%1,%2"
+ [(set_attr "type" "unknown")
+ (set_attr "mode" "<MODE>")])
+
+;; Synchronization instructions.
+
+(include "sync.md")
+
+(include "generic.md")
+(include "la464.md")
+
+(define_c_enum "unspec" [
+ UNSPEC_ADDRESS_FIRST
+])