blob: 3d1e3e7a787ad4347401c0d4cea4e2d7b88c4af1 [file] [log] [blame] [view]
;; Machine Description for LoongArch SIMD instructions for GNU compiler.
;; Copyright (C) 2023-2026 Free Software Foundation, Inc.
;; This file is part of GCC.
;; GCC is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 3, or (at your option)
;; any later version.
;; GCC is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GCC; see the file COPYING3. If not see
;; <http://www.gnu.org/licenses/>.
;; Integer modes supported by LSX.
(define_mode_iterator ILSX_DWH [V2DI V4SI V8HI])
(define_mode_iterator ILSX [ILSX_DWH V16QI])
(define_mode_iterator LSX_DWH [ILSX_DWH V2DF V4SF])
;; Integer modes supported by LASX.
(define_mode_iterator ILASX_DWH [V4DI V8SI V16HI])
(define_mode_iterator ILASX [ILASX_DWH V32QI])
(define_mode_iterator LASX_DWH [ILASX_DWH V4DF V8SF])
;; Only integer modes smaller than a word.
(define_mode_iterator ILSX_HB [V8HI V16QI])
(define_mode_iterator ILASX_HB [V16HI V32QI])
;; FP modes supported by LSX
(define_mode_iterator FLSX [V2DF V4SF])
;; FP modes supported by LASX
(define_mode_iterator FLASX [V4DF V8SF])
;; All modes supported by LSX
(define_mode_iterator LSX [ILSX FLSX])
;; ALL modes supported by LASX
(define_mode_iterator LASX [ILASX FLASX])
;; All integer modes available
(define_mode_iterator IVEC [(ILSX "ISA_HAS_LSX") (ILASX "ISA_HAS_LASX")])
;; All integer modes smaller than a word.
(define_mode_iterator IVEC_HB [(ILSX_HB "ISA_HAS_LSX")
(ILASX_HB "ISA_HAS_LASX")])
;; All modes longer than a byte
(define_mode_iterator VEC_DWH [(LSX_DWH "ISA_HAS_LSX")
(LASX_DWH "ISA_HAS_LASX")])
;; All FP modes available
(define_mode_iterator FVEC [(FLSX "ISA_HAS_LSX") (FLASX "ISA_HAS_LASX")])
;; All vector modes available
(define_mode_iterator ALLVEC [(LSX "ISA_HAS_LSX") (LASX "ISA_HAS_LASX")])
;; Mnemonic prefix, "x" for LASX modes.
(define_mode_attr x [(V2DI "") (V4SI "") (V8HI "") (V16QI "")
(V2DF "") (V4SF "")
(V4DI "x") (V8SI "x") (V16HI "x") (V32QI "x")
(V4DF "x") (V8SF "x")])
;; Modifier for vector register, "w" for LSX modes, "u" for LASX modes.
(define_mode_attr wu [(V2DI "w") (V4SI "w") (V8HI "w") (V16QI "w")
(V2DF "w") (V4SF "w")
(V4DI "u") (V8SI "u") (V16HI "u") (V32QI "u")
(V4DF "u") (V8SF "u")])
;; define_insn name prefix, "lsx" or "lasx"
(define_mode_attr simd_isa
[(V2DI "lsx") (V4SI "lsx") (V8HI "lsx") (V16QI "lsx")
(V2DF "lsx") (V4SF "lsx")
(V4DI "lasx") (V8SI "lasx") (V16HI "lasx") (V32QI "lasx")
(V4DF "lasx") (V8SF "lasx")])
;; Widen integer modes for intermediate values in RTX pattern.
(define_mode_attr WVEC [(V2DI "V2TI") (V4DI "V4TI")
(V4SI "V4DI") (V8SI "V8DI")
(V8HI "V8SI") (V16HI "V16SI")
(V16QI "V16HI") (V32QI "V32HI")])
;; The element type is not changed but the number of elements is halved.
(define_mode_attr VEC_HALF [(V2DI "V1DI") (V4DI "V2DI")
(V4SI "V2SI") (V8SI "V4SI")
(V8HI "V4HI") (V16HI "V8HI")
(V16QI "V8QI") (V32QI "V16QI")])
;; Modes with doubled length for intermediate values in RTX pattern.
(define_mode_attr LVEC [(V2DF "V4DF") (V4DF "V8DF")
(V4SF "V8SF") (V8SF "V16SF")
(V2DI "V4DI") (V4DI "V8DI")
(V4SI "V8SI") (V8SI "V16SI")
(V8HI "V16HI") (V16HI "V32HI")
(V16QI "V32QI") (V32QI "V64QI")])
;; The elements are widen but the total size is unchanged
;; (i.e. the number of elements is halfed).
(define_mode_attr WVEC_HALF [(V2DI "V1TI") (V4DI "V2TI")
(V4SI "V2DI") (V8SI "V4DI")
(V8HI "V4SI") (V16HI "V8SI")
(V16QI "V8HI") (V32QI "V16HI")])
(define_mode_attr WVEC_QUARTER [(V8HI "V2DI") (V16HI "V4DI")
(V16QI "V4SI") (V32QI "V8SI")])
;; Lower-case version.
(define_mode_attr wvec_half [(V2DI "v1ti") (V4DI "v2ti")
(V4SI "v2di") (V8SI "v4di")
(V8HI "v4si") (V16HI "v8si")
(V16QI "v8hi") (V32QI "v16hi")])
(define_mode_attr wvec_quarter [(V8HI "v2di") (V16HI "v4di")
(V16QI "v4si") (V32QI "v8si")])
;; Integer vector modes with the same length and unit size as a mode.
(define_mode_attr VIMODE [(V2DI "V2DI") (V4SI "V4SI")
(V8HI "V8HI") (V16QI "V16QI")
(V2DF "V2DI") (V4SF "V4SI")
(V4DI "V4DI") (V8SI "V8SI")
(V16HI "V16HI") (V32QI "V32QI")
(V4DF "V4DI") (V8SF "V8SI")])
;; Lower-case version.
(define_mode_attr vimode [(V2DF "v2di") (V4SF "v4si")
(V4DF "v4di") (V8SF "v8si")])
;; Integer vector modes with the same size, in lower-case.
(define_mode_attr allmode_i [(V2DI "v2di") (V4SI "v4si")
(V8HI "v8hi") (V16QI "v16qi")
(V2DF "v2di") (V4SF "v4si")
(V4DI "v4di") (V8SI "v8si")
(V16HI "v16hi") (V32QI "v32qi")
(V4DF "v4di") (V8SF "v8si")])
;; Suffix for LSX or LASX instructions.
(define_mode_attr simdfmt [(V2DF "d") (V4DF "d")
(V4SF "s") (V8SF "s")
(V2DI "d") (V4DI "d")
(V4SI "w") (V8SI "w")
(V8HI "h") (V16HI "h")
(V16QI "b") (V32QI "b")])
;; Suffix for double widening LSX or LASX instructions.
(define_mode_attr simdfmt_w [(V2DI "q") (V4DI "q")
(V4SI "d") (V8SI "d")
(V8HI "w") (V16HI "w")
(V16QI "h") (V32QI "h")])
;; Suffix for quadruple widening LSX or LASX instructions.
(define_mode_attr simdfmt_qw [(V8HI "d") (V16HI "d")
(V16QI "w") (V32QI "w")])
;; Suffix for integer mode in LSX or LASX instructions with FP input but
;; integer output.
(define_mode_attr simdifmt_for_f [(V2DF "l") (V4DF "l")
(V4SF "w") (V8SF "w")])
;; Suffix for integer mode in LSX or LASX instructions to operate FP
;; vectors using integer vector operations.
(define_mode_attr simdfmt_as_i [(V2DF "d") (V4DF "d")
(V4SF "w") (V8SF "w")
(V2DI "d") (V4DI "d")
(V4SI "w") (V8SI "w")
(V8HI "h") (V16HI "h")
(V16QI "b") (V32QI "b")])
;; "_f" for FP vectors, "" for integer vectors
(define_mode_attr _f [(V2DF "_f") (V4DF "_f")
(V4SF "_f") (V8SF "_f")
(V2DI "") (V4DI "")
(V4SI "") (V8SI "")
(V8HI "") (V16HI "")
(V16QI "") (V32QI "")])
;; Size of vector elements in bits.
(define_mode_attr elmbits [(V2DI "64") (V4DI "64")
(V4SI "32") (V8SI "32")
(V8HI "16") (V16HI "16")
(V16QI "8") (V32QI "8")])
;; The index of sign bit in FP vector elements.
(define_mode_attr elmsgnbit [(V2DF "63") (V4DF "63")
(V4SF "31") (V8SF "31")])
;; This attribute is used to form an immediate operand constraint using
;; "const_<bitimm>_operand".
(define_mode_attr bitimm [(V16QI "uimm3") (V32QI "uimm3")
(V8HI "uimm4") (V16HI "uimm4")
(V4SI "uimm5") (V8SI "uimm5")
(V2DI "uimm6") (V4DI "uimm6")])
(define_int_attr hi_lo [(0 "lo") (1 "hi")])
;; =======================================================================
;; For many LASX instructions, the only difference of it from the LSX
;; counterpart is the length of vector operands. Describe these LSX/LASX
;; instruction here so we can avoid duplicating logics.
;; =======================================================================
;; Move
;; Some immediate values in V1TI or V2TI may be stored in LSX or LASX
;; registers, thus we need to allow moving them for reload.
(define_mode_iterator ALLVEC_TI [ALLVEC
(V1TI "ISA_HAS_LSX")
(V2TI "ISA_HAS_LASX")])
(define_expand "mov<mode>"
[(set (match_operand:ALLVEC_TI 0)
(match_operand:ALLVEC_TI 1))]
""
{
if (loongarch_legitimize_move (<MODE>mode, operands[0], operands[1]))
DONE;
})
(define_expand "movmisalign<mode>"
[(set (match_operand:ALLVEC_TI 0)
(match_operand:ALLVEC_TI 1))]
""
{
if (loongarch_legitimize_move (<MODE>mode, operands[0], operands[1]))
DONE;
})
(define_insn_and_split "mov<mode>_simd"
[(set (match_operand:ALLVEC_TI 0 "nonimmediate_operand" "=f,f,R,*r,*f,*r")
(match_operand:ALLVEC_TI 1 "move_operand" "fYGYI,R,f,*f,*r,*r"))]
""
{ return loongarch_output_move (operands); }
"reload_completed && loongarch_split_move_p (operands[0], operands[1])"
[(const_int 0)]
{
loongarch_split_move (operands[0], operands[1]);
DONE;
}
[(set_attr "type" "simd_move,simd_load,simd_store,simd_copy,simd_insert,simd_copy")
(set_attr "mode" "<MODE>")])
;; REG + REG load
(define_mode_iterator QIVEC [(V16QI "ISA_HAS_LSX") (V32QI "ISA_HAS_LASX")])
(define_expand "<simd_isa>_<x>vldx"
[(set (match_operand:QIVEC 0 "register_operand" "=f")
(mem:QIVEC (plus:DI (match_operand:DI 1 "register_operand")
(match_operand:DI 2 "register_operand"))))]
"TARGET_64BIT")
;;
;; FP vector rounding instructions
;;
(define_c_enum "unspec"
[UNSPEC_SIMD_FRINTRP
UNSPEC_SIMD_FRINTRZ
UNSPEC_SIMD_FRINT
UNSPEC_SIMD_FRINTRM
UNSPEC_SIMD_FRINTRNE
UNSPEC_SIMD_VSHUF])
(define_int_iterator SIMD_FRINT
[UNSPEC_SIMD_FRINTRP
UNSPEC_SIMD_FRINTRZ
UNSPEC_SIMD_FRINT
UNSPEC_SIMD_FRINTRM
UNSPEC_SIMD_FRINTRNE])
(define_int_attr simd_frint_rounding
[(UNSPEC_SIMD_FRINTRP "rp")
(UNSPEC_SIMD_FRINTRZ "rz")
(UNSPEC_SIMD_FRINT "")
(UNSPEC_SIMD_FRINTRM "rm")
(UNSPEC_SIMD_FRINTRNE "rne")])
;; All these, but rint, are controlled by -ffp-int-builtin-inexact.
;; Note: nearbyint is NOT allowed to raise FE_INEXACT even if
;; -ffp-int-builtin-inexact, but rint is ALLOWED to raise it even if
;; -fno-fp-int-builtin-inexact.
(define_int_attr simd_frint_pattern
[(UNSPEC_SIMD_FRINTRP "ceil")
(UNSPEC_SIMD_FRINTRZ "btrunc")
(UNSPEC_SIMD_FRINT "rint")
(UNSPEC_SIMD_FRINTRNE "roundeven")
(UNSPEC_SIMD_FRINTRM "floor")])
;; <x>vfrint.{/rp/rz/rm}
(define_insn "<simd_isa>_<x>vfrint<simd_frint_rounding>_<simdfmt>"
[(set (match_operand:FVEC 0 "register_operand" "=f")
(unspec:FVEC [(match_operand:FVEC 1 "register_operand" "f")]
SIMD_FRINT))]
""
"<x>vfrint<simd_frint_rounding>.<simdfmt>\t%<wu>0,%<wu>1"
[(set_attr "type" "simd_fcvt")
(set_attr "mode" "<MODE>")])
;; Expand the standard-named patterns to <x>vfrint instructions if
;; raising inexact exception is allowed.
(define_expand "<simd_frint_pattern><mode>2"
[(set (match_operand:FVEC 0 "register_operand" "=f")
(unspec:FVEC [(match_operand:FVEC 1 "register_operand" "f")]
SIMD_FRINT))]
"<SIMD_FRINT> == UNSPEC_SIMD_FRINT ||
flag_fp_int_builtin_inexact ||
!flag_trapping_math")
;; ftrunc is like btrunc, but it's allowed to raise inexact exception
;; even if -fno-fp-int-builtin-inexact.
(define_expand "ftrunc<mode>2"
[(set (match_operand:FVEC 0 "register_operand" "=f")
(unspec:FVEC [(match_operand:FVEC 1 "register_operand" "f")]
UNSPEC_SIMD_FRINTRZ))]
"")
;; Use LSX for scalar ceil/floor/trunc/roundeven when -mlsx and -ffp-int-
;; builtin-inexact. The base FP instruction set lacks these operations.
;; Yes we are wasting 50% or even 75% of the CPU horsepower, but it's still
;; much faster than calling a libc function: on LA464 and LA664 there is a
;; 3x ~ 5x speed up.
;;
;; Note that a vreplvei instruction is needed or we'll also operate on the
;; junk in high bits of the vector register and produce random FP exceptions.
(define_int_iterator LSX_SCALAR_FRINT
[UNSPEC_SIMD_FRINTRP
UNSPEC_SIMD_FRINTRZ
UNSPEC_SIMD_FRINTRM
UNSPEC_SIMD_FRINTRNE])
(define_mode_attr VLSX_FOR_FMODE [(DF "V2DF") (SF "V4SF")])
(define_expand "<simd_frint_pattern><mode>2"
[(set (match_dup 2)
(vec_duplicate:<VLSX_FOR_FMODE>
(match_operand:ANYF 1 "register_operand")))
(set (match_dup 2)
(unspec:<VLSX_FOR_FMODE> [(match_dup 2)] LSX_SCALAR_FRINT))
(set (match_operand:ANYF 0 "register_operand")
(vec_select:ANYF (match_dup 2) (parallel [(const_int 0)])))]
"ISA_HAS_LSX && (flag_fp_int_builtin_inexact || !flag_trapping_math)"
"operands[2] = gen_reg_rtx (<VLSX_FOR_FMODE>mode);")
;; <x>vftint.{/rp/rz/rm}
(define_insn
"<simd_isa>_<x>vftint<simd_frint_rounding>_<simdifmt_for_f>_<simdfmt>"
[(set (match_operand:<VIMODE> 0 "register_operand" "=f")
(fix:<VIMODE>
(unspec:FVEC [(match_operand:FVEC 1 "register_operand" "f")]
SIMD_FRINT)))]
""
"<x>vftint<simd_frint_rounding>.<simdifmt_for_f>.<simdfmt>\t%<wu>0,%<wu>1"
[(set_attr "type" "simd_fcvt")
(set_attr "mode" "<MODE>")])
;; Expand the standard-named patterns to <x>vftint instructions if
;; raising inexact exception.
(define_expand "l<simd_frint_pattern><mode><vimode>2"
[(set (match_operand:<VIMODE> 0 "register_operand" "=f")
(fix:<VIMODE>
(unspec:FVEC [(match_operand:FVEC 1 "register_operand" "f")]
SIMD_FRINT)))]
"<SIMD_FRINT> == UNSPEC_SIMD_FRINT ||
flag_fp_int_builtin_inexact ||
!flag_trapping_math")
;; fix_trunc is allowed to raise inexact exception even if
;; -fno-fp-int-builtin-inexact. Because the middle end trys to match
;; (FIX x) and it does not know (FIX (UNSPEC_SIMD_FRINTRZ x)), we need
;; to use define_insn_and_split instead of define_expand (expanders are
;; not considered during matching).
(define_insn_and_split "fix_trunc<mode><vimode>2"
[(set (match_operand:<VIMODE> 0 "register_operand" "=f")
(fix:<VIMODE> (match_operand:FVEC 1 "register_operand" "f")))]
""
"#"
""
[(const_int 0)]
{
emit_insn (gen_<simd_isa>_<x>vftintrz_<simdifmt_for_f>_<simdfmt> (
operands[0], operands[1]));
DONE;
}
[(set_attr "type" "simd_fcvt")
(set_attr "mode" "<MODE>")])
;; <x>vmuh.{b/h/w/d}
(define_code_attr muh
[(sign_extend "smul_highpart")
(zero_extend "umul_highpart")])
(define_insn "<su>mul<mode>3_highpart"
[(set (match_operand:IVEC 0 "register_operand" "=f")
(<muh>:IVEC (match_operand:IVEC 1 "register_operand" "f")
(match_operand:IVEC 2 "register_operand" "f")))
(any_extend (const_int 0))]
""
"<x>vmuh.<simdfmt><u>\t%<wu>0,%<wu>1,%<wu>2"
[(set_attr "type" "simd_int_arith")
(set_attr "mode" "<MODE>")])
;; <x>vrotr.{b/h/w/d}
(define_insn "vrotr<mode>3"
[(set (match_operand:IVEC 0 "register_operand" "=f")
(rotatert:IVEC (match_operand:IVEC 1 "register_operand" "f")
(match_operand:IVEC 2 "register_operand" "f")))]
""
"<x>vrotr.<simdfmt>\t%<wu>0,%<wu>1,%<wu>2"
[(set_attr "type" "simd_int_arith")
(set_attr "mode" "<MODE>")])
;; Expand left rotate to right rotate.
(define_expand "vrotl<mode>3"
[(set (match_dup 3)
(neg:IVEC (match_operand:IVEC 2 "register_operand")))
(set (match_operand:IVEC 0 "register_operand")
(rotatert:IVEC (match_operand:IVEC 1 "register_operand")
(match_dup 3)))]
""
{
operands[3] = gen_reg_rtx (<MODE>mode);
});
;; Expand left rotate with a scalar amount to right rotate: negate the
;; scalar before broadcasting it because scalar negation is cheaper than
;; vector negation.
(define_expand "rotl<mode>3"
[(set (match_dup 3)
(neg:SI (match_operand:SI 2 "register_operand")))
(set (match_dup 4)
(vec_duplicate:IVEC (subreg:<IVEC:UNITMODE> (match_dup 3) 0)))
(set (match_operand:IVEC 0 "register_operand")
(rotatert:IVEC (match_operand:IVEC 1 "register_operand")
(match_dup 4)))]
""
{
operands[3] = gen_reg_rtx (SImode);
operands[4] = gen_reg_rtx (<MODE>mode);
});
;; <x>v{rotr/sll/sra/srl}i.{b/h/w/d}
(define_insn "<optab><mode>3"
[(set (match_operand:IVEC 0 "register_operand" "=f")
(shift_w:IVEC
(match_operand:IVEC 1 "register_operand" "f")
(match_operand:SI 2 "const_<bitimm>_operand")))]
"ISA_HAS_LSX"
"<x>v<insn>i.<simdfmt>\t%<wu>0,%<wu>1,%2"
[(set_attr "type" "simd_int_arith")
(set_attr "mode" "<MODE>")])
;; <x>vfnmsub.{s/d}
(define_insn "fnma<mode>4"
[(set (match_operand:FVEC 0 "register_operand" "=f")
(fma:FVEC (neg:FVEC (match_operand:FVEC 1 "register_operand" "f"))
(match_operand:FVEC 2 "register_operand" "f")
(match_operand:FVEC 3 "register_operand" "f")))]
"!HONOR_SIGNED_ZEROS (<MODE>mode)"
"<x>vfnmsub.<simdfmt>\t%<wu>0,%<wu>1,%<wu>2,%<wu>3"
[(set_attr "type" "simd_fmadd")
(set_attr "mode" "<MODE>")])
;; <x>vfcmp.*.{s/d} with defined RTX code
;; There are no fcmp.{sugt/suge/cgt/cge}.{s/d} menmonics in GAS, so we have
;; to reverse the operands ourselves :(.
(define_code_iterator fcond_simd [unordered uneq unlt unle eq lt le
ordered ltgt ne])
(define_insn "<simd_isa>_<x>vfcmp_<fcond>_<simdfmt>"
[(set (match_operand:<VIMODE> 0 "register_operand" "=f")
(fcond_simd:<VIMODE>
(match_operand:FVEC 1 "register_operand" "f")
(match_operand:FVEC 2 "register_operand" "f")))]
""
"<x>vfcmp.<fcond>.<simdfmt>\t%<wu>0,%<wu>1,%<wu>2"
[(set_attr "type" "simd_fcmp")
(set_attr "mode" "<MODE>")])
;; There are no fcmp.{sge/sgt/cuge/cugt}.{s/d} menmonics in GAS, so we have
;; to reverse the operands ourselves.
(define_code_iterator fcond_simd_rev [ge gt unge ungt])
(define_code_attr fcond_rev_asm
[(ge "sle")
(gt "slt")
(unge "cule")
(ungt "cult")])
(define_insn "<simd_isa>_<x>vfcmp_<fcond>_<simdfmt>"
[(set (match_operand:<VIMODE> 0 "register_operand" "=f")
(fcond_simd_rev:<VIMODE>
(match_operand:FVEC 1 "register_operand" "f")
(match_operand:FVEC 2 "register_operand" "f")))]
""
"<x>vfcmp.<fcond_rev_asm>.<simdfmt>\t%<wu>0,%<wu>2,%<wu>1";
[(set_attr "type" "simd_fcmp")
(set_attr "mode" "<MODE>")])
;; <x>vfcmp.*.{s/d} without defined RTX code, but with defined RTX code for
;; its inverse. Again, there are no fcmp.{sugt/suge/cgt/cge}.{s/d}
;; menmonics in GAS, so we have to reverse the operands ourselves.
(define_code_iterator fcond_inv [ge gt unge ungt])
(define_code_iterator fcond_inv_rev [le lt unle unlt])
(define_code_attr fcond_inv
[(ge "sult")
(gt "sule")
(unge "clt")
(ungt "cle")
(le "sugt")
(lt "suge")
(unle "cgt")
(unlt "cge")])
(define_code_attr fcond_inv_rev_asm
[(le "sult")
(lt "sule")
(unle "clt")
(unlt "cle")])
(define_insn "<simd_isa>_<x>vfcmp_<fcond_inv>_<simdfmt>"
[(set (match_operand:<VIMODE> 0 "register_operand" "=f")
(not:<VIMODE>
(fcond_inv:<VIMODE>
(match_operand:FVEC 1 "register_operand" "f")
(match_operand:FVEC 2 "register_operand" "f"))))]
""
"<x>vfcmp.<fcond_inv>.<simdfmt>\t%<wu>0,%<wu>1,%<wu>2"
[(set_attr "type" "simd_fcmp")
(set_attr "mode" "<MODE>")])
(define_insn "<simd_isa>_<x>vfcmp_<fcond_inv>_<simdfmt>"
[(set (match_operand:<VIMODE> 0 "register_operand" "=f")
(not:<VIMODE>
(fcond_inv_rev:<VIMODE>
(match_operand:FVEC 1 "register_operand" "f")
(match_operand:FVEC 2 "register_operand" "f"))))]
""
"<x>vfcmp.<fcond_inv_rev_asm>.<simdfmt>\t%<wu>0,%<wu>2,%<wu>1"
[(set_attr "type" "simd_fcmp")
(set_attr "mode" "<MODE>")])
;; <x>vfcmp.*.{s/d} instructions only as instrinsics
(define_c_enum "unspec"
[UNSPEC_SIMD_FCMP_CAF
UNSPEC_SIMD_FCMP_SAF
UNSPEC_SIMD_FCMP_SEQ
UNSPEC_SIMD_FCMP_SUN
UNSPEC_SIMD_FCMP_SUEQ
UNSPEC_SIMD_FCMP_CNE
UNSPEC_SIMD_FCMP_SOR
UNSPEC_SIMD_FCMP_SUNE])
(define_int_iterator SIMD_FCMP
[UNSPEC_SIMD_FCMP_CAF
UNSPEC_SIMD_FCMP_SAF
UNSPEC_SIMD_FCMP_SEQ
UNSPEC_SIMD_FCMP_SUN
UNSPEC_SIMD_FCMP_SUEQ
UNSPEC_SIMD_FCMP_CNE
UNSPEC_SIMD_FCMP_SOR
UNSPEC_SIMD_FCMP_SUNE])
(define_int_attr fcond_unspec
[(UNSPEC_SIMD_FCMP_CAF "caf")
(UNSPEC_SIMD_FCMP_SAF "saf")
(UNSPEC_SIMD_FCMP_SEQ "seq")
(UNSPEC_SIMD_FCMP_SUN "sun")
(UNSPEC_SIMD_FCMP_SUEQ "sueq")
(UNSPEC_SIMD_FCMP_CNE "cne")
(UNSPEC_SIMD_FCMP_SOR "sor")
(UNSPEC_SIMD_FCMP_SUNE "sune")])
(define_insn "<simd_isa>_<x>vfcmp_<fcond_unspec>_<simdfmt>"
[(set (match_operand:<VIMODE> 0 "register_operand" "=f")
(unspec:<VIMODE> [(match_operand:FVEC 1 "register_operand" "f")
(match_operand:FVEC 2 "register_operand" "f")]
SIMD_FCMP))]
""
"<x>vfcmp.<fcond_unspec>.<simdfmt>\t%<wu>0,%<wu>1,%<wu>2"
[(set_attr "type" "simd_fcmp")
(set_attr "mode" "<MODE>")])
; [x]vf{min/max} instructions are IEEE-754-2008 conforming, use them for
; the corresponding IEEE-754-2008 operations. We must use UNSPEC instead
; of smin/smax though, see PR105414 and PR107013.
(define_int_iterator UNSPEC_FMAXMIN [UNSPEC_FMAX UNSPEC_FMIN])
(define_int_attr fmaxmin [(UNSPEC_FMAX "fmax") (UNSPEC_FMIN "fmin")])
(define_insn "<fmaxmin><mode>3"
[(set (match_operand:FVEC 0 "register_operand" "=f")
(unspec:FVEC [(match_operand:FVEC 1 "register_operand" "f")
(match_operand:FVEC 2 "register_operand" "f")]
UNSPEC_FMAXMIN))]
""
"<x>v<fmaxmin>.<simdfmt>\t%<wu>0,%<wu>1,%<wu>2"
[(set_attr "type" "simd_fminmax")
(set_attr "mode" "<MODE>")])
;; ... and also reduc operations.
(define_expand "reduc_<fmaxmin>_scal_<mode>"
[(match_operand:<UNITMODE> 0 "register_operand")
(match_operand:FVEC 1 "register_operand")
(const_int UNSPEC_FMAXMIN)]
""
{
rtx tmp = gen_reg_rtx (<MODE>mode);
loongarch_expand_vector_reduc (gen_<fmaxmin><mode>3, tmp, operands[1]);
emit_insn (gen_vec_extract<mode><unitmode> (operands[0], tmp,
const0_rtx));
DONE;
})
;; FP negation.
(define_insn "neg<mode>2"
[(set (match_operand:FVEC 0 "register_operand" "=f")
(neg:FVEC (match_operand:FVEC 1 "register_operand" "f")))]
""
"<x>vbitrevi.<simdfmt_as_i>\t%<wu>0,%<wu>1,<elmsgnbit>"
[(set_attr "type" "simd_logic")
(set_attr "mode" "<MODE>")])
;; vector compare
(define_expand "vec_cmp<mode><allmode_i>"
[(set (match_operand:<VIMODE> 0 "register_operand")
(match_operator 1 ""
[(match_operand:ALLVEC 2 "register_operand")
(match_operand:ALLVEC 3 "nonmemory_operand")]))]
""
{
loongarch_expand_vec_cmp (operands);
DONE;
})
(define_expand "vec_cmpu<mode><allmode_i>"
[(set (match_operand:<VIMODE> 0 "register_operand")
(match_operator 1 ""
[(match_operand:IVEC 2 "register_operand")
(match_operand:IVEC 3 "nonmemory_operand")]))]
""
{
loongarch_expand_vec_cmp (operands);
DONE;
})
;; cbranch
(define_expand "cbranch<mode>4"
[(set (pc)
(if_then_else
(match_operator 0 "equality_operator"
[(match_operand:IVEC 1 "register_operand")
(match_operand:IVEC 2 "reg_or_vector_same_val_operand")])
(label_ref (match_operand 3 ""))
(pc)))]
""
{
RTX_CODE code = GET_CODE (operands[0]);
rtx tmp = operands[1];
rtx const0 = CONST0_RTX (SImode);
/* If comparing against a non-zero vector we have to do a comparison first
so we can have a != 0 comparison with the result. */
if (operands[2] != CONST0_RTX (<MODE>mode))
{
tmp = gen_reg_rtx (<MODE>mode);
emit_insn (gen_xor<mode>3 (tmp, operands[1], operands[2]));
}
if (code == NE)
emit_jump_insn (gen_<simd_isa>_<x>bnz_v_b (operands[3], tmp, const0));
else
emit_jump_insn (gen_<simd_isa>_<x>bz_v_b (operands[3], tmp, const0));
DONE;
})
;; Operations on elements at even/odd indices.
(define_int_iterator zero_one [0 1])
(define_int_attr ev_od [(0 "ev") (1 "od")])
(define_int_attr even_odd [(0 "even") (1 "odd")])
;; Integer widening add/sub/mult.
(define_insn "simd_<optab>w_evod_<mode>_<su>"
[(set (match_operand:<WVEC_HALF> 0 "register_operand" "=f")
(addsubmul:<WVEC_HALF>
(any_extend:<WVEC_HALF>
(vec_select:<VEC_HALF>
(match_operand:IVEC 1 "register_operand" "f")
(match_operand:IVEC 3 "vect_par_cnst_even_or_odd_half")))
(any_extend:<WVEC_HALF>
(vec_select:<VEC_HALF>
(match_operand:IVEC 2 "register_operand" "f")
(match_dup 3)))))]
""
"<x>v<optab>w%O3.<simdfmt_w>.<simdfmt><u>\t%<wu>0,%<wu>1,%<wu>2"
[(set_attr "type" "simd_int_arith")
(set_attr "mode" "<WVEC_HALF>")])
(define_expand "<simd_isa>_<x>v<optab>w<ev_od>_<simdfmt_w>_<simdfmt><u>"
[(match_operand:<WVEC_HALF> 0 "register_operand" "=f")
(match_operand:IVEC 1 "register_operand" " f")
(match_operand:IVEC 2 "register_operand" " f")
(any_extend (const_int 0))
(addsubmul (const_int 0) (const_int 0))
(const_int zero_one)]
""
{
int nelts = GET_MODE_NUNITS (<WVEC_HALF>mode);
rtx op3 = loongarch_gen_stepped_int_parallel (nelts, <zero_one>, 2);
rtx insn = gen_simd_<optab>w_evod_<mode>_<su> (operands[0], operands[1],
operands[2], op3);
emit_insn (insn);
DONE;
})
(define_expand "vec_widen_<su>mult_<even_odd>_<mode>"
[(match_operand:<WVEC_HALF> 0 "register_operand" "=f")
(match_operand:IVEC 1 "register_operand" " f")
(match_operand:IVEC 2 "register_operand" " f")
(any_extend (const_int 0))
(const_int zero_one)]
""
{
emit_insn (
gen_<simd_isa>_<x>vmulw<ev_od>_<simdfmt_w>_<simdfmt><u> (operands[0],
operands[1],
operands[2]));
DONE;
})
(define_insn "simd_<optab>w_evod_<mode>_hetero"
[(set (match_operand:<WVEC_HALF> 0 "register_operand" "=f")
(addsubmul:<WVEC_HALF>
(zero_extend:<WVEC_HALF>
(vec_select:<VEC_HALF>
(match_operand:IVEC 1 "register_operand" "f")
(match_operand:IVEC 3 "vect_par_cnst_even_or_odd_half")))
(sign_extend:<WVEC>
(vec_select:<VEC_HALF>
(match_operand:IVEC 2 "register_operand" "f")
(match_dup 3)))))]
""
"<x>v<optab>w%O3.<simdfmt_w>.<simdfmt>u.<simdfmt>\t%<wu>0,%<wu>1,%<wu>2"
[(set_attr "type" "simd_int_arith")
(set_attr "mode" "<WVEC_HALF>")])
(define_expand "<simd_isa>_<x>v<optab>w<ev_od>_<simdfmt_w>_<simdfmt>u_<simdfmt>"
[(match_operand:<WVEC_HALF> 0 "register_operand" "=f")
(match_operand:IVEC 1 "register_operand" " f")
(match_operand:IVEC 2 "register_operand" " f")
(addmul (const_int 0) (const_int 0))
(const_int zero_one)]
""
{
int nelts = GET_MODE_NUNITS (<WVEC_HALF>mode);
rtx op3 = loongarch_gen_stepped_int_parallel (nelts, <zero_one>, 2);
rtx insn = gen_simd_<optab>w_evod_<mode>_hetero (operands[0], operands[1],
operands[2], op3);
emit_insn (insn);
DONE;
})
(define_insn "simd_h<optab>w_<mode>_<su>"
[(set (match_operand:<WVEC_HALF> 0 "register_operand" "=f")
(addsub:<WVEC_HALF>
(any_extend:<WVEC_HALF>
(vec_select:<VEC_HALF>
(match_operand:IVEC 1 "register_operand" "f")
(match_operand:IVEC 3 "vect_par_cnst_even_or_odd_half")))
(any_extend:<WVEC_HALF>
(vec_select:<VEC_HALF>
(match_operand:IVEC 2 "register_operand" "f")
(match_operand:IVEC 4 "vect_par_cnst_even_or_odd_half")))))]
"!rtx_equal_p (operands[3], operands[4])"
{
if (!INTVAL (XVECEXP (operands[3], 0, 0)))
std::swap (operands[1], operands[2]);
return "<x>vh<optab>w.<simdfmt_w><u>.<simdfmt><u>\t%<wu>0,%<wu>1,%<wu>2";
}
[(set_attr "type" "simd_int_arith")
(set_attr "mode" "<WVEC_HALF>")])
(define_expand "<simd_isa>_<x>vh<optab>w_<simdfmt_w><u>_<simdfmt><u>"
[(match_operand:<WVEC_HALF> 0 "register_operand" "=f")
(match_operand:IVEC 1 "register_operand" " f")
(match_operand:IVEC 2 "register_operand" " f")
(any_extend (const_int 0))
(addsub (const_int 0) (const_int 0))]
""
{
int nelts = GET_MODE_NUNITS (<WVEC_HALF>mode);
rtx op3 = loongarch_gen_stepped_int_parallel (nelts, 1, 2);
rtx op4 = loongarch_gen_stepped_int_parallel (nelts, 0, 2);
rtx insn = gen_simd_h<optab>w_<mode>_<su> (operands[0], operands[1],
operands[2], op3, op4);
emit_insn (insn);
DONE;
})
(define_insn "simd_maddw_evod_<mode>_<su>"
[(set (match_operand:<WVEC_HALF> 0 "register_operand" "=f")
(plus:<WVEC_HALF>
(mult:<WVEC_HALF>
(any_extend:<WVEC_HALF>
(vec_select:<VEC_HALF>
(match_operand:IVEC 2 "register_operand" "f")
(match_operand:IVEC 4 "vect_par_cnst_even_or_odd_half")))
(any_extend:<WVEC_HALF>
(vec_select:<VEC_HALF>
(match_operand:IVEC 3 "register_operand" "f")
(match_dup 4))))
(match_operand:<WVEC_HALF> 1 "register_operand" "0")))]
""
"<x>vmaddw%O4.<simdfmt_w>.<simdfmt><u>\t%<wu>0,%<wu>2,%<wu>3"
[(set_attr "type" "simd_int_arith")
(set_attr "mode" "<WVEC_HALF>")])
(define_expand "<simd_isa>_<x>vmaddw<ev_od>_<simdfmt_w>_<simdfmt><u>"
[(match_operand:<WVEC_HALF> 0 "register_operand" "=f")
(match_operand:<WVEC_HALF> 1 "register_operand" " 0")
(match_operand:IVEC 2 "register_operand" " f")
(match_operand:IVEC 3 "register_operand" " f")
(any_extend (const_int 0))
(const_int zero_one)]
""
{
int nelts = GET_MODE_NUNITS (<WVEC_HALF>mode);
rtx op4 = loongarch_gen_stepped_int_parallel (nelts, <zero_one>, 2);
rtx insn = gen_simd_maddw_evod_<mode>_<su> (operands[0], operands[1],
operands[2], operands[3],
op4);
emit_insn (insn);
DONE;
})
(define_expand "<su>dot_prod<wvec_half><mode>"
[(match_operand:<WVEC_HALF> 0 "register_operand" "=f,f")
(match_operand:IVEC 1 "register_operand" " f,f")
(match_operand:IVEC 2 "register_operand" " f,f")
(match_operand:<WVEC_HALF> 3 "reg_or_0_operand" " 0,YG")
(any_extend (const_int 0))]
""
{
rtx *op = operands;
if (op[3] == CONST0_RTX (<WVEC_HALF>mode))
emit_insn (
gen_<simd_isa>_<x>vmulwev_<simdfmt_w>_<simdfmt><u> (op[0], op[1],
op[2]));
else
emit_insn (
gen_<simd_isa>_<x>vmaddwev_<simdfmt_w>_<simdfmt><u> (op[0], op[3],
op[1], op[2]));
emit_insn (
gen_<simd_isa>_<x>vmaddwod_<simdfmt_w>_<simdfmt><u> (op[0], op[0],
op[1], op[2]));
DONE;
})
(define_expand "<su>dot_prod<wvec_quarter><mode>"
[(match_operand:<WVEC_QUARTER> 0 "register_operand" "=f,f")
(match_operand:IVEC_HB 1 "register_operand" "f,f")
(match_operand:IVEC_HB 2 "register_operand" "f,f")
(match_operand:<WVEC_QUARTER> 3 "reg_or_0_operand" "f, YG")
(any_extend (const_int 0))]
""
{
rtx *op = operands;
rtx res_mulev = gen_reg_rtx (<WVEC_HALF>mode);
rtx res_mulod = gen_reg_rtx (<WVEC_HALF>mode);
rtx res_addev = gen_reg_rtx (<WVEC_QUARTER>mode);
rtx res_addod = gen_reg_rtx (<WVEC_QUARTER>mode);
emit_insn (gen_<simd_isa>_<x>vmulwev_<simdfmt_w>_<simdfmt><u>
(res_mulev, op[1], op[2]));
emit_insn (gen_<simd_isa>_<x>vmulwod_<simdfmt_w>_<simdfmt><u>
(res_mulod, op[1], op[2]));
emit_insn (gen_<simd_isa>_<x>vhaddw_<simdfmt_qw><u>_<simdfmt_w><u>
(res_addev, res_mulev, res_mulev));
emit_insn (gen_<simd_isa>_<x>vhaddw_<simdfmt_qw><u>_<simdfmt_w><u>
(res_addod, res_mulod, res_mulod));
if (op[3] == CONST0_RTX (<WVEC_QUARTER>mode))
emit_insn (gen_add<wvec_quarter>3 (op[0], res_addev,
res_addod));
else
{
emit_insn (gen_add<wvec_quarter>3 (res_addev, res_addev,
res_addod));
emit_insn (gen_add<wvec_quarter>3 (op[0], res_addev, op[3]));
}
DONE;
})
(define_insn "simd_maddw_evod_<mode>_hetero"
[(set (match_operand:<WVEC_HALF> 0 "register_operand" "=f")
(plus:<WVEC_HALF>
(mult:<WVEC_HALF>
(zero_extend:<WVEC_HALF>
(vec_select:<VEC_HALF>
(match_operand:IVEC 2 "register_operand" "f")
(match_operand:IVEC 4 "vect_par_cnst_even_or_odd_half")))
(sign_extend:<WVEC_HALF>
(vec_select:<VEC_HALF>
(match_operand:IVEC 3 "register_operand" "f")
(match_dup 4))))
(match_operand:<WVEC_HALF> 1 "register_operand" "0")))]
""
"<x>vmaddw%O4.<simdfmt_w>.<simdfmt>u.<simdfmt>\t%<wu>0,%<wu>2,%<wu>3"
[(set_attr "type" "simd_int_arith")
(set_attr "mode" "<WVEC_HALF>")])
(define_expand "<simd_isa>_<x>vmaddw<ev_od>_<simdfmt_w>_<simdfmt>u_<simdfmt>"
[(match_operand:<WVEC_HALF> 0 "register_operand" "=f")
(match_operand:<WVEC_HALF> 1 "register_operand" " 0")
(match_operand:IVEC 2 "register_operand" " f")
(match_operand:IVEC 3 "register_operand" " f")
(const_int zero_one)]
""
{
int nelts = GET_MODE_NUNITS (<WVEC_HALF>mode);
rtx op4 = loongarch_gen_stepped_int_parallel (nelts, <zero_one>, 2);
rtx insn = gen_simd_maddw_evod_<mode>_hetero (operands[0], operands[1],
operands[2], operands[3],
op4);
emit_insn (insn);
DONE;
})
; For "historical" reason we need a punned version of q_d variants.
(define_mode_iterator DIVEC [(V2DI "ISA_HAS_LSX") (V4DI "ISA_HAS_LASX")])
(define_expand "<simd_isa>_<optab>w<ev_od>_q_d<u>_punned"
[(match_operand:DIVEC 0 "register_operand" "=f")
(match_operand:DIVEC 1 "register_operand" " f")
(match_operand:DIVEC 2 "register_operand" " f")
(any_extend (const_int 0))
(addsubmul (const_int 0) (const_int 0))
(const_int zero_one)]
""
{
rtx t = gen_reg_rtx (<WVEC_HALF>mode);
emit_insn (gen_<simd_isa>_<x>v<optab>w<ev_od>_q_d<u> (t, operands[1],
operands[2]));
emit_move_insn (operands[0], gen_lowpart (<MODE>mode, t));
DONE;
})
(define_expand "<simd_isa>_<optab>w<ev_od>_q_du_d_punned"
[(match_operand:DIVEC 0 "register_operand" "=f")
(match_operand:DIVEC 1 "register_operand" " f")
(match_operand:DIVEC 2 "register_operand" " f")
(addmul (const_int 0) (const_int 0))
(const_int zero_one)]
""
{
rtx t = gen_reg_rtx (<WVEC_HALF>mode);
emit_insn (gen_<simd_isa>_<x>v<optab>w<ev_od>_q_du_d (t, operands[1],
operands[2]));
emit_move_insn (operands[0], gen_lowpart (<MODE>mode, t));
DONE;
})
(define_expand "<simd_isa>_h<optab>w_q<u>_d<u>_punned"
[(match_operand:DIVEC 0 "register_operand" "=f")
(match_operand:DIVEC 1 "register_operand" "f")
(match_operand:DIVEC 2 "register_operand" "f")
(any_extend (const_int 0))
(addsub (const_int 0) (const_int 0))]
""
{
rtx t = gen_reg_rtx (<WVEC_HALF>mode);
emit_insn (gen_<simd_isa>_<x>vh<optab>w_q<u>_d<u> (t, operands[1],
operands[2]));
emit_move_insn (operands[0], gen_lowpart (<MODE>mode, t));
DONE;
})
(define_expand "<simd_isa>_maddw<ev_od>_q_d<u>_punned"
[(match_operand:DIVEC 0 "register_operand" "=f")
(match_operand:DIVEC 1 "register_operand" " 0")
(match_operand:DIVEC 2 "register_operand" " f")
(match_operand:DIVEC 3 "register_operand" " f")
(const_int zero_one)
(any_extend (const_int 0))]
""
{
rtx t = gen_reg_rtx (<WVEC_HALF>mode);
rtx op1 = gen_lowpart (<WVEC_HALF>mode, operands[1]);
emit_insn (gen_<simd_isa>_<x>vmaddw<ev_od>_q_d<u> (t, op1, operands[2],
operands[3]));
emit_move_insn (operands[0], gen_lowpart (<MODE>mode, t));
DONE;
})
(define_expand "<simd_isa>_maddw<ev_od>_q_du_d_punned"
[(match_operand:DIVEC 0 "register_operand" "=f")
(match_operand:DIVEC 1 "register_operand" " 0")
(match_operand:DIVEC 2 "register_operand" " f")
(match_operand:DIVEC 3 "register_operand" " f")
(const_int zero_one)]
""
{
rtx t = gen_reg_rtx (<WVEC_HALF>mode);
rtx op1 = gen_lowpart (<WVEC_HALF>mode, operands[1]);
emit_insn (gen_<simd_isa>_<x>vmaddw<ev_od>_q_du_d (t, op1, operands[2],
operands[3]));
emit_move_insn (operands[0], gen_lowpart (<MODE>mode, t));
DONE;
})
;; Integer shift right with rounding.
(define_insn "simd_<optab>_imm_round_<mode>"
[(set (match_operand:IVEC 0 "register_operand" "=f")
(any_shiftrt:IVEC
(plus:IVEC
(match_operand:IVEC 1 "register_operand" "f")
(match_operand:IVEC 2 "const_vector_same_val_operand" "Uuvx"))
(match_operand:SI 3 "const_<bitimm>_operand" "I")))]
"(HOST_WIDE_INT_1U << UINTVAL (operands[3]) >> 1)
== UINTVAL (CONST_VECTOR_ELT (operands[2], 0))"
"<x>v<insn>ri.<simdfmt>\t%<wu>0,%<wu>1,%d3"
[(set_attr "type" "simd_shift")
(set_attr "mode" "<MODE>")])
(define_expand "<simd_isa>_<x>v<insn>ri_<simdfmt>"
[(match_operand:IVEC 0 "register_operand" "=f")
(match_operand:IVEC 1 "register_operand" " f")
(match_operand 2 "const_<bitimm>_operand")
(any_shiftrt (const_int 0) (const_int 0))]
""
{
auto addend = HOST_WIDE_INT_1U << UINTVAL (operands[2]) >> 1;
rtx addend_v = loongarch_gen_const_int_vector (<MODE>mode, addend);
emit_insn (gen_simd_<optab>_imm_round_<mode> (operands[0], operands[1],
addend_v, operands[2]));
DONE;
})
(define_insn "xor<mode>3"
[(set (match_operand:IVEC 0 "register_operand" "=f,f,f")
(xor:IVEC
(match_operand:IVEC 1 "register_operand" "f,f,f")
(match_operand:IVEC 2 "reg_or_vector_same_val_operand" "f,YC,Urv8")))]
""
"@
<x>vxor.v\t%<wu>0,%<wu>1,%<wu>2
<x>vbitrevi.%v0\t%<wu>0,%<wu>1,%V2
<x>vxori.b\t%<wu>0,%<wu>1,%B2"
[(set_attr "type" "simd_logic,simd_bit,simd_logic")
(set_attr "mode" "<MODE>")])
(define_insn "ior<mode>3"
[(set (match_operand:IVEC 0 "register_operand" "=f,f,f")
(ior:IVEC
(match_operand:IVEC 1 "register_operand" "f,f,f")
(match_operand:IVEC 2 "reg_or_vector_same_val_operand" "f,YC,Urv8")))]
""
"@
<x>vor.v\t%<wu>0,%<wu>1,%<wu>2
<x>vbitseti.%v0\t%<wu>0,%<wu>1,%V2
<x>vori.b\t%<wu>0,%<wu>1,%B2"
[(set_attr "type" "simd_logic,simd_bit,simd_logic")
(set_attr "mode" "<MODE>")])
(define_insn "and<mode>3"
[(set (match_operand:IVEC 0 "register_operand" "=f,f,f")
(and:IVEC
(match_operand:IVEC 1 "register_operand" "f,f,f")
(match_operand:IVEC 2 "reg_or_vector_same_val_operand" "f,YZ,Urv8")))]
""
{
switch (which_alternative)
{
case 0:
return "<x>vand.v\t%<wu>0,%<wu>1,%<wu>2";
case 1:
{
operands[2] = simplify_const_unary_operation (NOT, <MODE>mode,
operands[2],
<MODE>mode);
return "<x>vbitclri.%v0\t%<wu>0,%<wu>1,%V2";
}
case 2:
return "<x>vandi.b\t%<wu>0,%<wu>1,%B2";
default:
gcc_unreachable ();
}
}
[(set_attr "type" "simd_logic,simd_bit,simd_logic")
(set_attr "mode" "<MODE>")])
(define_expand "copysign<mode>3"
[(match_operand:FVEC 0 "register_operand")
(match_operand:FVEC 1 "register_operand")
(match_operand:FVEC 2 "reg_or_vector_neg_fp_operand")]
""
{
machine_mode imode = <VIMODE>mode;
rtx op[3], mask = loongarch_build_signbit_mask (imode, 1, 0);
/* Pun the operation into fixed-point bitwise operations. */
for (int i = 0; i < 3; i++)
op[i] = lowpart_subreg (imode, operands[i], <MODE>mode);
/* Copysign from a positive const should have been already simplified
to abs, ignore the case here. Copysign from a negative const is
a simple vbitset which is an alternative of ior (see above). */
if (const_vector_neg_fp_operand (operands[2], <MODE>mode))
emit_insn (gen_ior<vimode>3 (op[0], op[1], mask));
else
{
mask = force_reg (imode, mask);
emit_insn (gen_<simd_isa>_<x>vbitsel_<simdfmt_as_i> (op[0], op[1],
op[2], mask));
}
DONE;
})
(define_expand "@xorsign<mode>3"
[(match_operand:FVEC 0 "register_operand")
(match_operand:FVEC 1 "register_operand")
(match_operand:FVEC 2 "register_operand")]
""
{
machine_mode imode = <VIMODE>mode;
rtx op[3];
for (int i = 0; i < 3; i++)
op[i] = lowpart_subreg (imode, operands[i], <MODE>mode);
rtx t = loongarch_build_signbit_mask (imode, 1, 0);
t = force_reg (imode, simplify_gen_binary (AND, imode, op[2], t));
emit_move_insn (op[0], simplify_gen_binary (XOR, imode, op[1], t));
DONE;
})
(define_insn "@simd_vshuf_<mode>"
[(set (match_operand:QIVEC 0 "register_operand" "=f")
(unspec:QIVEC [(match_operand:QIVEC 1 "register_operand" "f")
(match_operand:QIVEC 2 "register_operand" "f")
(match_operand:QIVEC 3 "register_operand" "f")]
UNSPEC_SIMD_VSHUF))]
""
{
return "<x>vshuf.b\t%<wu>0,%<wu>1,%<wu>2,%<wu>3";
}
[(set_attr "type" "simd_sld")
(set_attr "mode" "<MODE>")])
(define_insn "@simd_vshuf_<mode>"
[(set (match_operand:VEC_DWH 0 "register_operand" "=f")
(unspec:VEC_DWH [(match_operand:VEC_DWH 1 "register_operand" "f")
(match_operand:VEC_DWH 2 "register_operand" "f")
(match_operand:<VIMODE> 3 "register_operand" "0")]
UNSPEC_SIMD_VSHUF))]
""
{
return "<x>vshuf.<simdfmt_as_i>\t%<wu>0,%<wu>1,%<wu>2";
}
[(set_attr "type" "simd_sld")
(set_attr "mode" "<MODE>")])
;; Backward compatibility wrapper. New code should use simd_vshuf
;; directly instead: gen_simd_vshuf (mode, ...) can often significantly
;; simplify the logic.
(define_expand "<simd_isa>_<x>vshuf_<simdfmt><_f>"
[(match_operand:ALLVEC 0 "register_operand")
(match_operand 1 "register_operand")
(match_operand 2 "register_operand")
(match_operand 3 "register_operand")]
""
{
rtx op0 = operands[0], op1, op2, op3;
switch (<MODE>mode)
{
case V32QImode:
case V16QImode:
op1 = operands[1];
op2 = operands[2];
op3 = operands[3];
break;
default:
op3 = operands[1];
op1 = operands[2];
op2 = operands[3];
}
gcc_assert (GET_MODE (op1) == <MODE>mode);
gcc_assert (GET_MODE (op2) == <MODE>mode);
gcc_assert (GET_MODE (op3) == <VIMODE>mode);
emit_insn (gen_simd_vshuf (<MODE>mode, op0, op1, op2, op3));
DONE;
})
; The LoongArch SX Instructions.
(include "lsx.md")
; The LoongArch ASX Instructions.
(include "lasx.md")