;;- Instruction patterns for the System z vector facility builtins.
;;  Copyright (C) 2015-2023 Free Software Foundation, Inc.
;;  Contributed by Andreas Krebbel (Andreas.Krebbel@de.ibm.com)

;; This file is part of GCC.

;; GCC is free software; you can redistribute it and/or modify it under
;; the terms of the GNU General Public License as published by the Free
;; Software Foundation; either version 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/>.

; The patterns in this file are enabled with -mzvector

(define_mode_iterator V_HW_32_64 [V4SI V2DI V2DF (V4SF "TARGET_VXE")])
(define_mode_iterator VI_HW_SD [V4SI V2DI])

; Full size vector modes with more than one element which are directly supported in vector registers by the hardware.
(define_mode_iterator VEC_HW  [V16QI V8HI V4SI V2DI V2DF (V4SF "TARGET_VXE")])
(define_mode_iterator VECF_HW [(V4SF "TARGET_VXE") V2DF])

; The element type of the vector with floating point modes translated
; to int modes of the same size.
(define_mode_attr non_vec_int[(V1QI "QI") (V2QI "QI") (V4QI "QI") (V8QI "QI") (V16QI "QI")
			      (V1HI "HI") (V2HI "HI") (V4HI "HI") (V8HI "HI")
			      (V1SI "SI") (V2SI "SI") (V4SI "SI")
			      (V1DI "DI") (V2DI "DI")
			      (V1SF "SI") (V2SF "SI") (V4SF "SI")
			      (V1DF "DI") (V2DF "DI")])

; Condition code modes generated by int comparisons
(define_mode_iterator VICMP [CCVEQ CCVIH CCVIHU])

; Comparisons supported by the vec_cmp* builtins
(define_code_iterator intcmp [eq gt gtu ge geu lt ltu le leu])
(define_code_iterator fpcmp  [eq gt ge lt le])

; Comparisons supported by the vec_all/any* builtins
(define_code_iterator intcmpcc [eq ne gt ge lt le gtu geu ltu leu])
(define_code_iterator fpcmpcc  [eq ne gt ge unle unlt lt le])

; Flags for vector string instructions (vfae all 4, vfee only ZS and CS, vstrc all 4)
(define_constants
  [(VSTRING_FLAG_IN         8)   ; invert result
   (VSTRING_FLAG_RT         4)   ; result type
   (VSTRING_FLAG_ZS         2)   ; zero search
   (VSTRING_FLAG_CS         1)]) ; condition code set

; Rounding modes as being used for e.g. VFI
(define_constants
  [(VEC_RND_CURRENT                0)
   (VEC_RND_NEAREST_AWAY_FROM_ZERO 1)
   (VEC_RND_SHORT_PREC             3)
   (VEC_RND_NEAREST_TO_EVEN        4)
   (VEC_RND_TO_ZERO                5)
   (VEC_RND_TO_INF                 6)
   (VEC_RND_TO_MINF                7)])

; Inexact suppression facility flag as being used for e.g. VFI
(define_constants
  [(VEC_INEXACT                0)
   (VEC_NOINEXACT              4)])


; Vector gather element

; vgef, vgeg
(define_insn "vec_gather_element<mode>"
  [(set (match_operand:V_HW_32_64                     0 "register_operand"  "=v")
	(unspec:V_HW_32_64 [(match_operand:V_HW_32_64 1 "register_operand"   "0")
			    (match_operand:<TOINTVEC> 2 "register_operand"   "v")
			    (match_operand:BLK        3 "memory_operand"     "R")
			    (match_operand:QI         4 "const_mask_operand" "C")]
			   UNSPEC_VEC_GATHER))]
  "TARGET_VX && UINTVAL (operands[4]) < GET_MODE_NUNITS (<V_HW_32_64:MODE>mode)"
  "vge<bhfgq>\t%0,%O3(%v2,%R3),%b4"
  [(set_attr "op_type" "VRV")])

(define_expand "vec_genmask<mode>"
  [(match_operand:VI_HW 0 "register_operand" "=v")
   (match_operand:QI    1 "const_int_operand" "C")
   (match_operand:QI    2 "const_int_operand" "C")]
  "TARGET_VX"
{
  int bitlen = GET_MODE_UNIT_BITSIZE (<VI_HW:MODE>mode);
  /* To bit little endian style.  */
  int end = bitlen - 1 - INTVAL (operands[1]);
  int start = bitlen - 1 - INTVAL (operands[2]);
  int i;
  unsigned HOST_WIDE_INT mask;
  bool swapped_p = false;

  if (start > end)
    {
      i = start - 1; start = end + 1; end = i;
      swapped_p = true;
    }
  if (end == 63)
    mask = HOST_WIDE_INT_M1U;
  else
    mask = (HOST_WIDE_INT_1U << (end + 1)) - 1;

  mask &= ~((HOST_WIDE_INT_1U << start) - 1);

  if (swapped_p)
    mask = ~mask;

  rtx mask_rtx = gen_int_mode (mask, GET_MODE_INNER (<VI_HW:MODE>mode));

  emit_insn (gen_rtx_SET (operands[0],
			  gen_const_vec_duplicate (<VI_HW:MODE>mode,
						   mask_rtx)));
  DONE;
})

(define_expand "vec_genbytemaskv16qi"
  [(match_operand:V16QI 0 "register_operand"  "")
   (match_operand:HI    1 "const_int_operand" "")]
  "TARGET_VX"
{
  int i;
  unsigned mask = 0x8000;
  rtx const_vec[16];
  unsigned HOST_WIDE_INT byte_mask = UINTVAL (operands[1]);

  for (i = 0; i < 16; i++)
    {
      if (mask & byte_mask)
	const_vec[i] = constm1_rtx;
      else
	const_vec[i] = const0_rtx;
      mask = mask >> 1;
    }
  emit_insn (gen_rtx_SET (operands[0],
			  gen_rtx_CONST_VECTOR (V16QImode,
						gen_rtvec_v (16, const_vec))));
  DONE;
})

(define_expand "vec_splats<mode>"
  [(set (match_operand:VEC_HW                          0 "register_operand" "")
	(vec_duplicate:VEC_HW (match_operand:<non_vec> 1 "general_operand"  "")))]
  "TARGET_VX")

(define_expand "vec_insert<mode>"
  [(set (match_operand:VEC_HW                    0 "register_operand" "")
	(unspec:VEC_HW [(match_operand:<non_vec> 2 "register_operand" "")
			(match_operand:SI        3 "nonmemory_operand" "")
			(match_operand:VEC_HW    1 "register_operand" "")]
		       UNSPEC_VEC_SET))]
  "TARGET_VX"
  "")

; This is vec_set + modulo arithmetic on the element selector (op 2)
(define_expand "vec_promote<mode>"
  [(set (match_operand:VEC_HW                    0 "register_operand" "")
	(unspec:VEC_HW [(match_operand:<non_vec> 1 "register_operand" "")
			(match_operand:SI        2 "nonmemory_operand" "")
			(match_dup 0)]
		       UNSPEC_VEC_SET))]
  "TARGET_VX"
  "")

; vec_extract is also an RTL standard name -> vector.md

; vllezb, vllezh, vllezf, vllezg
(define_insn "vec_insert_and_zero<mode>"
  [(set (match_operand:VEC_HW                    0 "register_operand" "=v")
	(unspec:VEC_HW [(match_operand:<non_vec> 1 "memory_operand"    "R")]
		       UNSPEC_VEC_INSERT_AND_ZERO))]
  "TARGET_VX"
  "vllez<bhfgq>\t%v0,%1"
  [(set_attr "op_type" "VRX")])

; vec_revb (vec_insert_and_zero(x))             bswap-and-replicate-1.c
; vllebrzh, vllebrzf, vllebrzg
(define_insn "*vec_insert_and_zero_bswap<mode>"
  [(set (match_operand:V_HW_HSD                    0 "register_operand"       "=v")
	(bswap:V_HW_HSD (unspec:V_HW_HSD
			 [(match_operand:<non_vec> 1 "memory_operand"          "R")]
			 UNSPEC_VEC_INSERT_AND_ZERO)))
   (use (match_operand:V16QI                       2 "permute_pattern_operand" "X"))]
  "TARGET_VXE2"
  "vllebrz<bhfgq>\t%v0,%1"
  [(set_attr "op_type" "VRX")])


(define_insn "vlbb"
  [(set (match_operand:V16QI              0 "register_operand"   "=v")
	(unspec:V16QI [(match_operand:BLK 1 "memory_operand"      "R")
		       (match_operand:QI  2 "const_mask_operand"  "C")]
		      UNSPEC_VEC_LOAD_BNDRY))]
  "TARGET_VX && UINTVAL (operands[2]) < 7"
  "vlbb\t%v0,%1,%2"
  [(set_attr "op_type" "VRX")])

; Vector load rightmost with length

(define_expand "vlrlrv16qi"
  [(set (match_operand:V16QI              0 "register_operand"  "")
	(unspec:V16QI [(match_operand:BLK 2 "memory_operand"    "")
		       (match_operand:SI  1 "nonmemory_operand" "")]
		      UNSPEC_VEC_LOAD_LEN_R))]
  "TARGET_VXE"
{
  /* vlrlr sets all length values beyond 15 to 15.  Emulate the same
     behavior for immediate length operands.  vlrl would trigger a
     SIGILL for too large immediate operands.  */
  if (CONST_INT_P (operands[1])
      && (UINTVAL (operands[1]) & 0xffffffff) > 15)
    operands[1] = GEN_INT (15);
})

(define_insn "*vlrlrv16qi"
  [(set (match_operand:V16QI              0 "register_operand"  "=v,  v,  v")
	(unspec:V16QI [(match_operand:BLK 2 "memory_operand"     "Q,  R,  Q")
		       (match_operand:SI  1 "nonmemory_operand"  "d,j>f,jb4")]
		      UNSPEC_VEC_LOAD_LEN_R))]
  "TARGET_VXE"
  "@
   vlrlr\t%v0,%1,%2
   vl\t%v0,%2%A2
   vlrl\t%v0,%2,%1"
  [(set_attr "op_type" "VRS,VRX,VSI")])


; vmrhb, vmrhh, vmrhf, vmrhg
(define_expand "vec_mergeh<mode>"
  [(match_operand:V_128_NOSINGLE 0 "register_operand" "")
   (match_operand:V_128_NOSINGLE 1 "register_operand" "")
   (match_operand:V_128_NOSINGLE 2 "register_operand" "")]
  "TARGET_VX"
{
  s390_expand_merge (operands[0], operands[1], operands[2], true);
  DONE;
})

; vmrlb, vmrlh, vmrlf, vmrlg
(define_expand "vec_mergel<mode>"
  [(match_operand:V_128_NOSINGLE 0 "register_operand" "")
   (match_operand:V_128_NOSINGLE 1 "register_operand" "")
   (match_operand:V_128_NOSINGLE 2 "register_operand" "")]
  "TARGET_VX"
{
  s390_expand_merge (operands[0], operands[1], operands[2], false);
  DONE;
})


; Vector pack

; vpkh, vpkf, vpkg
(define_insn "vec_pack<mode>"
  [(set (match_operand:<vec_half>                    0 "register_operand" "=v")
	(unspec:<vec_half> [(match_operand:VI_HW_HSD 1 "register_operand"  "v")
			    (match_operand:VI_HW_HSD 2 "register_operand"  "v")]
			   UNSPEC_VEC_PACK))]
  "TARGET_VX"
  "vpk<bhfgq>\t%v0,%v1,%v2"
  [(set_attr "op_type" "VRR")])


; Vector pack saturate

; vpksh, vpksf, vpksg
(define_insn "vec_packs<mode>"
  [(set (match_operand:<vec_half>                    0 "register_operand" "=v")
	(unspec:<vec_half> [(match_operand:VI_HW_HSD 1 "register_operand"  "v")
			    (match_operand:VI_HW_HSD 2 "register_operand"  "v")]
			   UNSPEC_VEC_PACK_SATURATE))]
  "TARGET_VX"
  "vpks<bhfgq>\t%v0,%v1,%v2"
  [(set_attr "op_type" "VRR")])


; This is vec_packs_cc + loading cc into a caller specified memory location.
(define_expand "vec_packs_cc<mode>"
  [(parallel
    [(set (reg:CCRAW CC_REGNUM)
	  (unspec:CCRAW [(match_operand:VI_HW_HSD 1 "register_operand" "")
			 (match_operand:VI_HW_HSD 2 "register_operand" "")]
			UNSPEC_VEC_PACK_SATURATE_GENCC))
     (set (match_operand:<vec_half> 0 "register_operand" "")
	  (unspec:<vec_half> [(match_dup 1) (match_dup 2)]
			     UNSPEC_VEC_PACK_SATURATE_CC))])
   (set (match_dup 4)
	(unspec:SI [(reg:CCRAW CC_REGNUM)] UNSPEC_CC_TO_INT))
   (set (match_operand:SI 3 "memory_operand" "")
	(match_dup 4))]
  "TARGET_VX"
{
  operands[4] = gen_reg_rtx (SImode);
})

; vpksh, vpksf, vpksg
(define_insn "*vec_packs_cc<mode>"
  [(set (reg:CCRAW CC_REGNUM)
	(unspec:CCRAW [(match_operand:VI_HW_HSD 1 "register_operand" "v")
		       (match_operand:VI_HW_HSD 2 "register_operand" "v")]
		      UNSPEC_VEC_PACK_SATURATE_GENCC))
   (set (match_operand:<vec_half> 0 "register_operand" "=v")
	(unspec:<vec_half> [(match_dup 1) (match_dup 2)]
			   UNSPEC_VEC_PACK_SATURATE_CC))]
  "TARGET_VX"
  "vpks<bhfgq>s\t%v0,%v1,%v2"
  [(set_attr "op_type" "VRR")])


; Vector pack logical saturate

; vpklsh, vpklsf, vpklsg
(define_insn "vec_packsu<mode>"
  [(set (match_operand:<vec_half>                    0 "register_operand" "=v")
	(unspec:<vec_half> [(match_operand:VI_HW_HSD 1 "register_operand"  "v")
			    (match_operand:VI_HW_HSD 2 "register_operand"  "v")]
			   UNSPEC_VEC_PACK_UNSIGNED_SATURATE))]
  "TARGET_VX"
  "vpkls<bhfgq>\t%v0,%v1,%v2"
  [(set_attr "op_type" "VRR")])

; Emulate saturate unsigned pack on signed operands.
; Zero out negative elements and continue with the unsigned saturating pack.
(define_expand "vec_packsu_u<mode>"
  [(set (match_operand:<vec_half>                    0 "register_operand" "=v")
	(unspec:<vec_half> [(match_operand:VI_HW_HSD 1 "register_operand"  "v")
			    (match_operand:VI_HW_HSD 2 "register_operand"  "v")]
			   UNSPEC_VEC_PACK_UNSIGNED_SATURATE))]
  "TARGET_VX"
{
   rtx null_vec = CONST0_RTX(<MODE>mode);
   machine_mode half_mode;
   switch (<MODE>mode)
   {
     case E_V8HImode: half_mode = V16QImode; break;
     case E_V4SImode: half_mode = V8HImode; break;
     case E_V2DImode: half_mode = V4SImode; break;
     default: gcc_unreachable ();
   }
   s390_expand_vcond (operands[1], operands[1], null_vec,
		      GE, operands[1], null_vec);
   s390_expand_vcond (operands[2], operands[2], null_vec,
		      GE, operands[2], null_vec);
   emit_insn (gen_rtx_SET (operands[0],
			   gen_rtx_UNSPEC (half_mode,
					   gen_rtvec (2, operands[1], operands[2]),
					   UNSPEC_VEC_PACK_UNSIGNED_SATURATE)));
   DONE;
})

; This is vec_packsu_cc + loading cc into a caller specified memory location.
; FIXME: The reg to target mem copy should be issued by reload?!
(define_expand "vec_packsu_cc<mode>"
  [(parallel
    [(set (reg:CCRAW CC_REGNUM)
	  (unspec:CCRAW [(match_operand:VI_HW_HSD 1 "register_operand" "")
			 (match_operand:VI_HW_HSD 2 "register_operand" "")]
			UNSPEC_VEC_PACK_UNSIGNED_SATURATE_GENCC))
     (set (match_operand:<vec_half> 0 "register_operand" "")
	  (unspec:<vec_half> [(match_dup 1) (match_dup 2)]
			     UNSPEC_VEC_PACK_UNSIGNED_SATURATE_CC))])
   (set (match_dup 4)
	(unspec:SI [(reg:CCRAW CC_REGNUM)] UNSPEC_CC_TO_INT))
   (set (match_operand:SI 3 "memory_operand" "")
	(match_dup 4))]
  "TARGET_VX"
{
  operands[4] = gen_reg_rtx (SImode);
})

; vpklsh, vpklsf, vpklsg
(define_insn "*vec_packsu_cc<mode>"
  [(set (reg:CCRAW CC_REGNUM)
	(unspec:CCRAW [(match_operand:VI_HW_HSD 1 "register_operand" "v")
		       (match_operand:VI_HW_HSD 2 "register_operand" "v")]
		      UNSPEC_VEC_PACK_UNSIGNED_SATURATE_GENCC))
   (set (match_operand:<vec_half> 0 "register_operand" "=v")
	(unspec:<vec_half> [(match_dup 1) (match_dup 2)]
			   UNSPEC_VEC_PACK_UNSIGNED_SATURATE_CC))]
  "TARGET_VX"
  "vpkls<bhfgq>s\t%v0,%v1,%v2"
  [(set_attr "op_type" "VRR")])


; Vector permute

; vec_perm is also RTL standard name, but we can only use it for V16QI

(define_insn "vec_zperm<mode>"
  [(set (match_operand:V_HW_HSD                   0 "register_operand" "=v")
	(unspec:V_HW_HSD [(match_operand:V_HW_HSD 1 "register_operand"  "v")
			  (match_operand:V_HW_HSD 2 "register_operand"  "v")
			  (match_operand:V16QI    3 "register_operand"  "v")]
			 UNSPEC_VEC_PERM))]
  "TARGET_VX"
  "vperm\t%v0,%v1,%v2,%v3"
  [(set_attr "op_type" "VRR")])

; Incoming op3 is in vec_permi format and will we turned into a
; permute vector consisting of op3 and op4.
(define_expand "vec_permi<mode>"
  [(set (match_operand:V_HW_2   0 "register_operand" "")
	(vec_select:V_HW_2
	 (vec_concat:<vec_2x_nelts>
	  (match_operand:V_HW_2 1 "register_operand" "")
	  (match_operand:V_HW_2 2 "register_operand" ""))
	 (parallel [(match_operand:QI 3 "const_mask_operand" "") (match_dup 4)])))]
  "TARGET_VX"
{
  HOST_WIDE_INT val = INTVAL (operands[3]);
  operands[3] = GEN_INT ((val & 2) >> 1);
  operands[4] = GEN_INT ((val & 1) + 2);
})


; Vector replicate


; Replicate from vector element
(define_expand "vec_splat<mode>"
  [(set (match_operand:V_HW                      0 "register_operand"  "")
	(vec_duplicate:V_HW (vec_select:<non_vec>
			     (match_operand:V_HW 1 "register_operand"  "")
			     (parallel
			      [(match_operand:QI 2 "const_mask_operand" "")]))))]
  "TARGET_VX")

; Vector scatter element

; vscef, vsceg

; A 64 bit target address generated from 32 bit elements
(define_insn "vec_scatter_element<V_HW_4:mode>_DI"
  [(set (mem:<non_vec>
	 (plus:DI (zero_extend:DI
		   (vec_select:SI
		    (match_operand:V4SI           1 "register_operand"   "v")
		    (parallel [(match_operand:QI  3 "const_mask_operand" "C")])))
	  (match_operand:SI                       2 "address_operand"    "ZQ")))
	(vec_select:<non_vec>
	 (match_operand:V_HW_4                    0 "register_operand"   "v")
	 (parallel [(match_dup 3)])))]
  "TARGET_VX && TARGET_64BIT && UINTVAL (operands[3]) < 4"
  "vscef\t%v0,%O2(%v1,%R2),%3"
  [(set_attr "op_type" "VRV")])

; A 31 bit target address is generated from 64 bit elements
; vsceg
(define_insn "vec_scatter_element<V_HW_2:mode>_SI"
  [(set (mem:<non_vec>
	 (plus:SI (subreg:SI
		   (vec_select:<non_vec_int>
		    (match_operand:<TOINTVEC>     1 "register_operand"   "v")
		    (parallel [(match_operand:QI  3 "const_mask_operand" "C")])) 4)
	  (match_operand:SI                       2 "address_operand"    "ZQ")))
    (vec_select:<non_vec>
     (match_operand:V_HW_2                        0 "register_operand"   "v")
     (parallel [(match_dup 3)])))]
  "TARGET_VX && !TARGET_64BIT && UINTVAL (operands[3]) < GET_MODE_NUNITS (<V_HW_2:MODE>mode)"
  "vsce<V_HW_2:bhfgq>\t%v0,%O2(%v1,%R2),%3"
  [(set_attr "op_type" "VRV")])

; Element size and target address size is the same
; vscef, vsceg
(define_insn "vec_scatter_element<mode>_<non_vec_int>"
  [(set (mem:<non_vec>
	 (plus:<non_vec_int>
	  (vec_select:<non_vec_int>
	   (match_operand:<TOINTVEC>      1 "register_operand"   "v")
	    (parallel [(match_operand:QI  3 "const_mask_operand" "C")]))
	  (match_operand:DI               2 "address_operand"   "ZQ")))
	(vec_select:<non_vec>
	 (match_operand:V_HW_32_64        0 "register_operand"   "v")
	 (parallel [(match_dup 3)])))]
  "TARGET_VX && UINTVAL (operands[3]) < GET_MODE_NUNITS (<V_HW_32_64:MODE>mode)"
  "vsce<bhfgq>\t%v0,%O2(%v1,%R2),%3"
  [(set_attr "op_type" "VRV")])

; Depending on the address size we have to expand a different pattern.
; This however cannot be represented in s390-builtins.def so we do the
; multiplexing here in the expander.
(define_expand "vec_scatter_element<V_HW_32_64:mode>"
  [(match_operand:V_HW_32_64 0 "register_operand" "")
   (match_operand:<TOINTVEC> 1 "register_operand" "")
   (match_operand 2 "address_operand" "")
   (match_operand:QI 3 "const_mask_operand" "")]
  "TARGET_VX"
{
  if (TARGET_64BIT)
    {
      PUT_MODE (operands[2], DImode);
      emit_insn (
	gen_vec_scatter_element<V_HW_32_64:mode>_DI (operands[0], operands[1],
						     operands[2], operands[3]));
    }
  else
    {
      PUT_MODE (operands[2], SImode);
      emit_insn (
	gen_vec_scatter_element<V_HW_32_64:mode>_SI (operands[0], operands[1],
						     operands[2], operands[3]));
    }
  DONE;
})


; Vector select

; for all b in bits op0[b] = op3[b] == 0 ? op2[b] : op1[b]
; implemented as: op0 = (op1 & op3) | (op2 & ~op3)

; Used to expand the vec_sel builtin. Operands op1 and op2 already got
; swapped in s390-c.cc when we get here.

(define_insn "vsel<mode>"
  [(set (match_operand:V_HW_FT               0 "register_operand" "=v")
	(ior:V_HW_FT
	 (and:V_HW_FT (match_operand:V_HW_FT 1 "register_operand"  "v")
		      (match_operand:V_HW_FT 3 "register_operand"  "v"))
	 (and:V_HW_FT (not:V_HW_FT (match_dup 3))
		      (match_operand:V_HW_FT 2 "register_operand"  "v"))))]
  "TARGET_VX"
  "vsel\t%v0,%1,%2,%3"
  [(set_attr "op_type" "VRR")])


; Vector sign extend to doubleword

; Sign extend of right most vector element to respective double-word
; vsegb, vsegh, vsegf
(define_insn "vec_extend<mode>"
  [(set (match_operand:VI_HW_QHS                    0 "register_operand" "=v")
	(unspec:VI_HW_QHS [(match_operand:VI_HW_QHS 1 "register_operand"  "v")]
			  UNSPEC_VEC_EXTEND))]
  "TARGET_VX"
  "vseg<bhfgq>\t%v0,%1"
  [(set_attr "op_type" "VRR")])


; Vector store with length

; Store bytes in OP1 from OP0 with the highest indexed byte to be
; stored from OP0 given by OP2
(define_insn "vstl<mode>"
  [(set (match_operand:BLK             2 "memory_operand"   "=Q")
	(unspec:BLK [(match_operand:V  0 "register_operand"  "v")
		     (match_operand:SI 1 "register_operand"  "d")]
		    UNSPEC_VEC_STORE_LEN))]
  "TARGET_VX"
  "vstl\t%v0,%1,%2"
  [(set_attr "op_type" "VRS")])

; Vector store rightmost with length

(define_expand "vstrlrv16qi"
  [(set (match_operand:BLK                2 "memory_operand"    "")
	(unspec:BLK [(match_operand:V16QI 0 "register_operand"  "")
		     (match_operand:SI    1 "nonmemory_operand" "")]
		    UNSPEC_VEC_STORE_LEN_R))]
  "TARGET_VXE"
{
  /* vstrlr sets all length values beyond 15 to 15.  Emulate the same
     behavior for immediate length operands.  vstrl would trigger a
     SIGILL for too large immediate operands.  */
  if (CONST_INT_P (operands[1])
      && (UINTVAL (operands[1]) & 0xffffffff) > 15)
    operands[1] = GEN_INT (15);
})

(define_insn "*vstrlrv16qi"
  [(set (match_operand:BLK                2 "memory_operand"    "=Q,  R,  Q")
	(unspec:BLK [(match_operand:V16QI 0 "register_operand"   "v,  v,  v")
		     (match_operand:SI    1 "nonmemory_operand"  "d,j>f,jb4")]
		    UNSPEC_VEC_STORE_LEN_R))]
  "TARGET_VXE"
  "@
   vstrlr\t%v0,%1,%2
   vst\t%v0,%2%A2
   vstrl\t%v0,%2,%1"
  [(set_attr "op_type" "VRS,VRX,VSI")])



; vector bit permute

(define_insn "vbpermv16qi"
  [(set (match_operand:V2DI                0 "register_operand" "=v")
	(unspec:V2DI [(match_operand:V16QI 1 "register_operand"  "v")
		      (match_operand:V16QI 2 "register_operand"  "v")]
		     UNSPEC_VEC_VBPERM))]
  "TARGET_VXE"
  "vbperm\t%v0,%v1,%v2"
  [(set_attr "op_type" "VRR")])

; Vector unpack high

; vuphb, vuphh, vuphf
(define_insn "vec_unpackh<mode>"
  [(set (match_operand:<vec_double>                    0 "register_operand" "=v")
	(unspec:<vec_double> [(match_operand:VI_HW_QHS 1 "register_operand"  "v")]
			     UNSPEC_VEC_UNPACKH))]
  "TARGET_VX"
  "vuph<bhfgq>\t%v0,%v1"
  [(set_attr "op_type" "VRR")])

; vuplhb, vuplhh, vuplhf
(define_insn "vec_unpackh_l<mode>"
  [(set (match_operand:<vec_double>                    0 "register_operand" "=v")
	(unspec:<vec_double> [(match_operand:VI_HW_QHS 1 "register_operand"  "v")]
			     UNSPEC_VEC_UNPACKH_L))]
  "TARGET_VX"
  "vuplh<bhfgq>\t%v0,%v1"
  [(set_attr "op_type" "VRR")])


; Vector unpack low

; vuplb, vuplhw, vuplf
(define_insn "vec_unpackl<mode>"
  [(set (match_operand:<vec_double>                    0 "register_operand" "=v")
	(unspec:<vec_double> [(match_operand:VI_HW_QHS 1 "register_operand"  "v")]
			     UNSPEC_VEC_UNPACKL))]
  "TARGET_VX"
  "vupl<bhfgq><w>\t%v0,%v1"
  [(set_attr "op_type" "VRR")])

; vupllb, vupllh, vupllf
(define_insn "vec_unpackl_l<mode>"
  [(set (match_operand:<vec_double>                    0 "register_operand" "=v")
	(unspec:<vec_double> [(match_operand:VI_HW_QHS 1 "register_operand"  "v")]
			     UNSPEC_VEC_UNPACKL_L))]
  "TARGET_VX"
  "vupll<bhfgq>\t%v0,%v1"
  [(set_attr "op_type" "VRR")])


; Vector add

; Vector add compute carry

; vaccb, vacch, vaccf, vaccg, vaccq
(define_insn "vacc<bhfgq>_<mode>"
  [(set (match_operand:VIT_HW                 0 "register_operand" "=v")
	(unspec:VIT_HW [(match_operand:VIT_HW 1 "register_operand"  "v")
			(match_operand:VIT_HW 2 "register_operand"  "v")]
		       UNSPEC_VEC_ADDC))]
  "TARGET_VX"
  "vacc<bhfgq>\t%v0,%v1,%v2"
  [(set_attr "op_type" "VRR")])

; Vector add with carry

(define_insn "vacq"
  [(set (match_operand:TI             0 "register_operand" "=v")
	(unspec:TI [(match_operand:TI 1 "register_operand"  "v")
		    (match_operand:TI 2 "register_operand"  "v")
		    (match_operand:TI 3 "register_operand"  "v")]
		   UNSPEC_VEC_ADDE_U128))]
  "TARGET_VX"
  "vacq\t%v0,%v1,%v2,%v3"
  [(set_attr "op_type" "VRR")])


; Vector add with carry compute carry

(define_insn "vacccq"
  [(set (match_operand:TI             0 "register_operand" "=v")
	(unspec:TI [(match_operand:TI 1 "register_operand"  "v")
		    (match_operand:TI 2 "register_operand"  "v")
		    (match_operand:TI 3 "register_operand"  "v")]
		   UNSPEC_VEC_ADDEC_U128))]
  "TARGET_VX"
  "vacccq\t%v0,%v1,%v2,%v3"
  [(set_attr "op_type" "VRR")])


; Vector and

; Vector and with complement

; vnc
(define_insn "vec_andc<mode>3"
  [(set (match_operand:VT_HW                       0 "register_operand" "=v")
	(and:VT_HW (not:VT_HW (match_operand:VT_HW 2 "register_operand"  "v"))
		  (match_operand:VT_HW             1 "register_operand"  "v")))]
  "TARGET_VX"
  "vnc\t%v0,%v1,%v2"
  [(set_attr "op_type" "VRR")])


; Vector average

; vavgb, vavgh, vavgf, vavgg
(define_insn "vec_avg<mode>"
  [(set (match_operand:VI_HW                0 "register_operand" "=v")
	(unspec:VI_HW [(match_operand:VI_HW 1 "register_operand"  "v")
		       (match_operand:VI_HW 2 "register_operand"  "v")]
		      UNSPEC_VEC_AVG))]
  "TARGET_VX"
  "vavg<bhfgq>\t%v0,%v1,%v2"
  [(set_attr "op_type" "VRR")])

; Vector average logical

; vavglb, vavglh, vavglf, vavglg
(define_insn "vec_avgu<mode>"
  [(set (match_operand:VI_HW                0 "register_operand" "=v")
	(unspec:VI_HW [(match_operand:VI_HW 1 "register_operand"  "v")
		       (match_operand:VI_HW 2 "register_operand"  "v")]
		      UNSPEC_VEC_AVGU))]
  "TARGET_VX"
  "vavgl<bhfgq>\t%v0,%v1,%v2"
  [(set_attr "op_type" "VRR")])


; Vector checksum

(define_insn "vec_checksum"
  [(set (match_operand:V4SI               0 "register_operand" "=v")
	(unspec:V4SI [(match_operand:V4SI 1 "register_operand"  "v")
		      (match_operand:V4SI 2 "register_operand"  "v")]
		     UNSPEC_VEC_CHECKSUM))]
  "TARGET_VX"
  "vcksm\t%v0,%v1,%v2"
  [(set_attr "op_type" "VRR")])

;;
;; Vector compare
;;

; vec_all/any int compares

(define_expand "vec_all_<intcmpcc:code><VI_HW:mode>"
  [(match_operand:SI                0 "register_operand" "")
   (intcmpcc (match_operand:VI_HW 1 "register_operand" "")
	     (match_operand:VI_HW 2 "register_operand" ""))]
  "TARGET_VX"
{
  s390_expand_vec_compare_cc (operands[0],
			      <intcmpcc:CODE>,
			      operands[1],
			      operands[2],
			      true);
  DONE;
})

(define_expand "vec_any_<intcmpcc:code><VI_HW:mode>"
  [(match_operand:SI                0 "register_operand" "")
   (intcmpcc (match_operand:VI_HW 1 "register_operand" "")
	     (match_operand:VI_HW 2 "register_operand" ""))]
  "TARGET_VX"
{
  s390_expand_vec_compare_cc (operands[0],
			      <intcmpcc:CODE>,
			      operands[1],
			      operands[2],
			      false);
  DONE;
})

; vec_all/any fp compares

(define_expand "vec_all_<fpcmpcc:code><mode>"
  [(match_operand:SI               0 "register_operand" "")
   (fpcmpcc (match_operand:VECF_HW 1 "register_operand" "")
	    (match_operand:VECF_HW 2 "register_operand" ""))]
  "TARGET_VX"
{
  s390_expand_vec_compare_cc (operands[0],
			      <fpcmpcc:CODE>,
			      operands[1],
			      operands[2],
			      true);
  DONE;
})

(define_expand "vec_any_<fpcmpcc:code><mode>"
  [(match_operand:SI               0 "register_operand" "")
   (fpcmpcc (match_operand:VECF_HW 1 "register_operand" "")
	    (match_operand:VECF_HW 2 "register_operand" ""))]
  "TARGET_VX"
{
  s390_expand_vec_compare_cc (operands[0],
			      <fpcmpcc:CODE>,
			      operands[1],
			      operands[2],
			      false);
  DONE;
})


; Compare without generating CC

(define_expand "vec_cmp<intcmp:code><VI_HW:mode>"
  [(set (match_operand:VI_HW               0 "register_operand" "=v")
	(intcmp:VI_HW (match_operand:VI_HW 1 "register_operand"  "v")
		      (match_operand:VI_HW 2 "register_operand"  "v")))]
  "TARGET_VX"
{
  s390_expand_vec_compare (operands[0], <intcmp:CODE>, operands[1], operands[2]);
  DONE;
})

(define_expand "vec_cmp<fpcmp:code><mode>"
  [(set (match_operand:<TOINTVEC>              0 "register_operand" "=v")
	(fpcmp:<TOINTVEC> (match_operand:VF_HW 1 "register_operand"  "v")
		       (match_operand:VF_HW 2 "register_operand"  "v")))]
  "TARGET_VX"
{
  s390_expand_vec_compare (operands[0], <fpcmp:CODE>, operands[1], operands[2]);
  DONE;
})


; Vector count leading zeros

; vec_cntlz -> clz
; vec_cnttz -> ctz

; Vector xor

; vec_xor -> xor

; Vector Galois field multiply sum

; vgfmb, vgfmh, vgfmf
(define_insn "vec_gfmsum<mode>"
  [(set (match_operand:VI_HW_QHS 0 "register_operand" "=v")
	(unspec:VI_HW_QHS [(match_operand:VI_HW_QHS 1 "register_operand" "v")
			   (match_operand:VI_HW_QHS 2 "register_operand" "v")]
			  UNSPEC_VEC_GFMSUM))]
  "TARGET_VX"
  "vgfm<bhfgq>\t%v0,%v1,%v2"
  [(set_attr "op_type" "VRR")])

(define_insn "vec_gfmsum_128"
  [(set (match_operand:V16QI 0 "register_operand" "=v")
	(unspec:V16QI [(match_operand:V2DI 1 "register_operand" "v")
		       (match_operand:V2DI 2 "register_operand" "v")]
		      UNSPEC_VEC_GFMSUM_128))]
  "TARGET_VX"
  "vgfmg\t%v0,%v1,%v2"
  [(set_attr "op_type" "VRR")])

; vgfmab, vgfmah, vgfmaf
(define_insn "vec_gfmsum_accum<mode>"
  [(set (match_operand:<vec_double> 0 "register_operand" "=v")
	(unspec:<vec_double> [(match_operand:VI_HW_QHS 1 "register_operand" "v")
			      (match_operand:VI_HW_QHS 2 "register_operand" "v")
			      (match_operand:<vec_double> 3 "register_operand" "v")]
			     UNSPEC_VEC_GFMSUM_ACCUM))]
  "TARGET_VX"
  "vgfma<bhfgq>\t%v0,%v1,%v2,%v3"
  [(set_attr "op_type" "VRR")])

(define_insn "vec_gfmsum_accum_128"
  [(set (match_operand:V16QI 0 "register_operand" "=v")
	(unspec:V16QI [(match_operand:V2DI 1 "register_operand" "v")
		       (match_operand:V2DI 2 "register_operand" "v")
		       (match_operand:V16QI 3 "register_operand" "v")]
		      UNSPEC_VEC_GFMSUM_ACCUM_128))]
  "TARGET_VX"
  "vgfmag\t%v0,%v1,%v2,%v3"
  [(set_attr "op_type" "VRR")])


; FIXME: vec_neg ?

; Vector load positive: vec_abs -> abs
; Vector maximum vec_max -> smax, logical vec_max -> umax
; Vector maximum vec_min -> smin, logical vec_min -> umin


; Vector multiply and add high

; vec_mladd -> vec_vmal
; vmalb, vmalh, vmalf, vmalg
(define_insn "vec_vmal<mode>"
  [(set (match_operand:VI_HW_QHS 0 "register_operand" "=v")
	(unspec:VI_HW_QHS [(match_operand:VI_HW_QHS 1 "register_operand" "v")
			   (match_operand:VI_HW_QHS 2 "register_operand" "v")
			   (match_operand:VI_HW_QHS 3 "register_operand" "v")]
			  UNSPEC_VEC_VMAL))]
  "TARGET_VX"
  "vmal<bhfgq><w>\t%v0,%v1,%v2,%v3"
  [(set_attr "op_type" "VRR")])

; vec_mhadd -> vec_vmah/vec_vmalh

; vmahb; vmahh, vmahf, vmahg
(define_insn "vec_vmah<mode>"
  [(set (match_operand:VI_HW_QHS 0 "register_operand" "=v")
	(unspec:VI_HW_QHS [(match_operand:VI_HW_QHS 1 "register_operand" "v")
			   (match_operand:VI_HW_QHS 2 "register_operand" "v")
			   (match_operand:VI_HW_QHS 3 "register_operand" "v")]
			  UNSPEC_VEC_VMAH))]
  "TARGET_VX"
  "vmah<bhfgq>\t%v0,%v1,%v2,%v3"
  [(set_attr "op_type" "VRR")])

; vmalhb; vmalhh, vmalhf, vmalhg
(define_insn "vec_vmalh<mode>"
  [(set (match_operand:VI_HW_QHS 0 "register_operand"                   "=v")
	(unspec:VI_HW_QHS [(match_operand:VI_HW_QHS 1 "register_operand" "v")
			   (match_operand:VI_HW_QHS 2 "register_operand" "v")
			   (match_operand:VI_HW_QHS 3 "register_operand" "v")]
			  UNSPEC_VEC_VMALH))]
  "TARGET_VX"
  "vmalh<bhfgq>\t%v0,%v1,%v2,%v3"
  [(set_attr "op_type" "VRR")])

; vec_meadd -> vec_vmae/vec_vmale

; vmaeb; vmaeh, vmaef, vmaeg
(define_insn "vec_vmae<mode>"
  [(set (match_operand:<vec_double> 0 "register_operand"                      "=v")
	(unspec:<vec_double> [(match_operand:VI_HW_QHS 1 "register_operand"    "v")
			      (match_operand:VI_HW_QHS 2 "register_operand"    "v")
			      (match_operand:<vec_double> 3 "register_operand" "v")]
			     UNSPEC_VEC_VMAE))]
  "TARGET_VX"
  "vmae<bhfgq>\t%v0,%v1,%v2,%v3"
  [(set_attr "op_type" "VRR")])

; vmaleb; vmaleh, vmalef, vmaleg
(define_insn "vec_vmale<mode>"
  [(set (match_operand:<vec_double> 0 "register_operand"                      "=v")
	(unspec:<vec_double> [(match_operand:VI_HW_QHS 1 "register_operand"    "v")
			      (match_operand:VI_HW_QHS 2 "register_operand"    "v")
			      (match_operand:<vec_double> 3 "register_operand" "v")]
			     UNSPEC_VEC_VMALE))]
  "TARGET_VX"
  "vmale<bhfgq>\t%v0,%v1,%v2,%v3"
  [(set_attr "op_type" "VRR")])

; vec_moadd -> vec_vmao/vec_vmalo

; vmaob; vmaoh, vmaof, vmaog
(define_insn "vec_vmao<mode>"
  [(set (match_operand:<vec_double> 0 "register_operand"                      "=v")
	(unspec:<vec_double> [(match_operand:VI_HW_QHS 1 "register_operand"    "v")
			      (match_operand:VI_HW_QHS 2 "register_operand"    "v")
			      (match_operand:<vec_double> 3 "register_operand" "v")]
			     UNSPEC_VEC_VMAO))]
  "TARGET_VX"
  "vmao<bhfgq>\t%v0,%v1,%v2,%v3"
  [(set_attr "op_type" "VRR")])

; vmalob; vmaloh, vmalof, vmalog
(define_insn "vec_vmalo<mode>"
  [(set (match_operand:<vec_double> 0 "register_operand"                      "=v")
	(unspec:<vec_double> [(match_operand:VI_HW_QHS 1 "register_operand"    "v")
			      (match_operand:VI_HW_QHS 2 "register_operand"    "v")
			      (match_operand:<vec_double> 3 "register_operand" "v")]
			     UNSPEC_VEC_VMALO))]
  "TARGET_VX"
  "vmalo<bhfgq>\t%v0,%v1,%v2,%v3"
  [(set_attr "op_type" "VRR")])


; Vector multiply high

; vec_mulh -> vec_smulh/vec_umulh

; vmhb, vmhh, vmhf
(define_insn "vec_smulh<mode>"
  [(set (match_operand:VI_HW_QHS 0 "register_operand"                   "=v")
	(unspec:VI_HW_QHS [(match_operand:VI_HW_QHS 1 "register_operand" "v")
			   (match_operand:VI_HW_QHS 2 "register_operand" "v")]
			  UNSPEC_VEC_SMULT_HI))]
  "TARGET_VX"
  "vmh<bhfgq>\t%v0,%v1,%v2"
  [(set_attr "op_type" "VRR")])

; vmlhb, vmlhh, vmlhf
(define_insn "vec_umulh<mode>"
  [(set (match_operand:VI_HW_QHS 0 "register_operand"                   "=v")
	(unspec:VI_HW_QHS [(match_operand:VI_HW_QHS 1 "register_operand" "v")
			   (match_operand:VI_HW_QHS 2 "register_operand" "v")]
			  UNSPEC_VEC_UMULT_HI))]
  "TARGET_VX"
  "vmlh<bhfgq>\t%v0,%v1,%v2"
  [(set_attr "op_type" "VRR")])


; Vector multiply low

; vec_mule -> vec_widen_umult_even/vec_widen_smult_even
; vec_mulo -> vec_widen_umult_odd/vec_widen_smult_odd


; Vector nor

(define_insn "vec_nor<mode>3"
  [(set (match_operand:VT_HW 0 "register_operand"            "=v")
	(not:VT_HW
	 (ior:VT_HW (match_operand:VT_HW 1 "register_operand" "v")
		    (match_operand:VT_HW 2 "register_operand" "v"))))]
  "TARGET_VX"
  "vno\t%v0,%v1,%v2"
  [(set_attr "op_type" "VRR")])


; Vector or

; Vector population count vec_popcnt -> popcount
; Vector element rotate left logical vec_rl -> vrotl, vec_rli -> rot

; Vector element rotate and insert under mask

; verimb, verimh, verimf, verimg
(define_insn "verim<mode>"
  [(set (match_operand:VI_HW                0 "register_operand" "=v")
	(unspec:VI_HW [(match_operand:VI_HW 1 "register_operand"  "0")
		       (match_operand:VI_HW 2 "register_operand"  "v")
		       (match_operand:VI_HW 3 "register_operand"  "v")
		       (match_operand:QI    4 "const_int_operand" "C")]
		      UNSPEC_VEC_RL_MASK))]
  "TARGET_VX"
  "verim<bhfgq>\t%v0,%v2,%v3,%b4"
  [(set_attr "op_type" "VRI")])


; Vector shift left

(define_insn "vec_sll<VI_HW:mode><VI_HW_QHS:mode>"
  [(set (match_operand:VI_HW                    0 "register_operand" "=v")
	(unspec:VI_HW [(match_operand:VI_HW     1 "register_operand"  "v")
		       (match_operand:VI_HW_QHS 2 "register_operand"  "v")]
		      UNSPEC_VEC_SLL))]
  "TARGET_VX"
  "vsl\t%v0,%v1,%v2"
  [(set_attr "op_type" "VRR")])


; Vector shift left by byte

; Pattern definition in vector.md, see vec_vslb
(define_expand "vec_slb<mode>"
  [(set (match_operand:V_HW 0 "register_operand"                     "")
	(unspec:V_HW [(match_operand:V_HW 1 "register_operand"       "")
		      (match_operand:<TOINTVEC> 2 "register_operand" "")]
		     UNSPEC_VEC_SLB))]
  "TARGET_VX"
{
  PUT_MODE (operands[2], V16QImode);
})

; Vector shift left double by byte

(define_insn "vec_sld<mode>"
  [(set (match_operand:V_HW 0 "register_operand"              "=v")
	(unspec:V_HW [(match_operand:V_HW 1 "register_operand" "v")
		      (match_operand:V_HW 2 "register_operand" "v")
		      (match_operand:QI 3 "const_int_operand"  "C")]
		     UNSPEC_VEC_SLDBYTE))]
  "TARGET_VX"
  "vsldb\t%v0,%v1,%v2,%b3"
  [(set_attr "op_type" "VRI")])

(define_expand "vec_sldw<mode>"
  [(set (match_operand:V_HW 0 "register_operand"               "")
	(unspec:V_HW [(match_operand:V_HW 1 "register_operand" "")
		      (match_operand:V_HW 2 "register_operand" "")
		      (match_operand:QI 3 "const_int_operand"  "")]
		     UNSPEC_VEC_SLDBYTE))]
  "TARGET_VX"
{
  operands[3] = GEN_INT (INTVAL (operands[3]) << 2);
})

; Vector shift left double by bit

(define_insn "vec_sldb<mode>"
  [(set (match_operand:V_HW 0 "register_operand"              "=v")
	(unspec:V_HW [(match_operand:V_HW 1 "register_operand" "v")
		      (match_operand:V_HW 2 "register_operand" "v")
		      (match_operand:QI 3 "const_int_operand"  "C")]
		     UNSPEC_VEC_SLDBIT))]
  "TARGET_VXE2"
  "vsld\t%v0,%v1,%v2,%b3"
  [(set_attr "op_type" "VRI")])

; Vector shift right double by bit

(define_insn "vec_srdb<mode>"
  [(set (match_operand:V_HW 0 "register_operand"              "=v")
	(unspec:V_HW [(match_operand:V_HW 1 "register_operand" "v")
		      (match_operand:V_HW 2 "register_operand" "v")
		      (match_operand:QI 3 "const_int_operand"  "C")]
		     UNSPEC_VEC_SRDBIT))]
  "TARGET_VXE2"
  "vsrd\t%v0,%v1,%v2,%b3"
  [(set_attr "op_type" "VRI")])

; Vector shift right arithmetic

(define_insn "vec_sral<VI_HW:mode><VI_HW_QHS:mode>"
  [(set (match_operand:VI_HW                    0 "register_operand" "=v")
	(unspec:VI_HW [(match_operand:VI_HW     1 "register_operand"  "v")
		       (match_operand:VI_HW_QHS 2 "register_operand"  "v")]
		      UNSPEC_VEC_SRAL))]
  "TARGET_VX"
  "vsra\t%v0,%v1,%v2"
  [(set_attr "op_type" "VRR")])


; Vector shift right arithmetic by byte

(define_insn "vec_srab<mode>"
  [(set (match_operand:V_HW 0 "register_operand"                    "=v")
	(unspec:V_HW [(match_operand:V_HW 1 "register_operand"       "v")
		      (match_operand:<TOINTVEC> 2 "register_operand" "v")]
		     UNSPEC_VEC_SRAB))]
  "TARGET_VX"
  "vsrab\t%v0,%v1,%v2"
  [(set_attr "op_type" "VRR")])


; Vector shift right logical

(define_insn "vec_srl<VI_HW:mode><VI_HW_QHS:mode>"
  [(set (match_operand:VI_HW                    0 "register_operand" "=v")
	(unspec:VI_HW [(match_operand:VI_HW     1 "register_operand"  "v")
		       (match_operand:VI_HW_QHS 2 "register_operand"  "v")]
		      UNSPEC_VEC_SRL))]
  "TARGET_VX"
  "vsrl\t%v0,%v1,%v2"
  [(set_attr "op_type" "VRR")])


; Vector shift right logical by byte

; Pattern definition in vector.md, see vec_vsrb
(define_expand "vec_srb<mode>"
  [(set (match_operand:V_HW 0 "register_operand"                     "")
	(unspec:V_HW [(match_operand:V_HW 1 "register_operand"       "")
		      (match_operand:<TOINTVEC> 2 "register_operand" "")]
		     UNSPEC_VEC_SRLB))]
  "TARGET_VX"
{
  PUT_MODE (operands[2], V16QImode);
})

; Vector subtract

; Vector subtract compute borrow indication

; vscbib, vscbih, vscbif, vscbig, vscbiq
(define_insn "vscbi<bhfgq>_<mode>"
  [(set (match_operand:VIT_HW 0 "register_operand"                "=v")
	(unspec:VIT_HW [(match_operand:VIT_HW 1 "register_operand" "v")
			(match_operand:VIT_HW 2 "register_operand" "v")]
		      UNSPEC_VEC_SUBC))]
  "TARGET_VX"
  "vscbi<bhfgq>\t%v0,%v1,%v2"
  [(set_attr "op_type" "VRR")])

; Vector subtract with borrow indication

(define_insn "vsbiq"
  [(set (match_operand:TI 0 "register_operand"               "=v")
	(unspec:TI [(match_operand:TI 1 "register_operand"    "v")
		       (match_operand:TI 2 "register_operand" "v")
		       (match_operand:TI 3 "register_operand" "v")]
		      UNSPEC_VEC_SUBE_U128))]
  "TARGET_VX"
  "vsbiq\t%v0,%v1,%v2,%v3"
  [(set_attr "op_type" "VRR")])


; Vector subtract with borrow compute and borrow indication

(define_insn "vsbcbiq"
  [(set (match_operand:TI 0 "register_operand"               "=v")
	(unspec:TI [(match_operand:TI 1 "register_operand"    "v")
		       (match_operand:TI 2 "register_operand" "v")
		       (match_operand:TI 3 "register_operand" "v")]
		      UNSPEC_VEC_SUBEC_U128))]
  "TARGET_VX"
  "vsbcbiq\t%v0,%v1,%v2,%v3"
  [(set_attr "op_type" "VRR")])


; Vector sum across

; Sum across DImode parts of the 1st operand and add the rightmost
; element of 2nd operand
; vsumgh, vsumgf
(define_expand "vec_sum2<mode>"
  [(set (match_operand:V2DI 0 "register_operand" "")
	(unspec:V2DI [(match_operand:VI_HW_HS 1 "register_operand" "")
		      (match_operand:VI_HW_HS 2 "register_operand" "")]
		     UNSPEC_VEC_VSUMG))]
  "TARGET_VX")

; vsumqh, vsumqf
(define_insn "vec_sum_u128<mode>"
  [(set (match_operand:V2DI 0 "register_operand" "=v")
	(unspec:V2DI [(match_operand:VI_HW_SD 1 "register_operand" "v")
		      (match_operand:VI_HW_SD 2 "register_operand" "v")]
		     UNSPEC_VEC_VSUMQ))]
  "TARGET_VX"
  "vsumq<bhfgq>\t%v0,%v1,%v2"
  [(set_attr "op_type" "VRR")])

; vsumb, vsumh
(define_expand "vec_sum4<mode>"
  [(set (match_operand:V4SI 0 "register_operand" "")
	(unspec:V4SI [(match_operand:VI_HW_QH 1 "register_operand" "")
		      (match_operand:VI_HW_QH 2 "register_operand" "")]
		     UNSPEC_VEC_VSUM))]
  "TARGET_VX")


; Vector test under mask

(define_expand "vec_test_mask_int<mode>"
  [(set (reg:CCRAW CC_REGNUM)
	(unspec:CCRAW [(match_operand:V_HW 1 "register_operand" "")
		       (match_operand:<TOINTVEC> 2 "register_operand" "")]
		      UNSPEC_VEC_TEST_MASK))
   (set (match_operand:SI 0 "register_operand" "")
	(unspec:SI [(reg:CCRAW CC_REGNUM)] UNSPEC_CC_TO_INT))]
  "TARGET_VX")

(define_insn "*vec_test_mask<mode>"
  [(set (reg:CCRAW CC_REGNUM)
	(unspec:CCRAW [(match_operand:V_HW 0 "register_operand" "v")
		       (match_operand:<TOINTVEC> 1 "register_operand" "v")]
		      UNSPEC_VEC_TEST_MASK))]
  "TARGET_VX"
  "vtm\t%v0,%v1"
  [(set_attr "op_type" "VRR")])


; Vector multiply sum logical

(define_insn "vec_msumv2di"
  [(set (match_operand:V16QI 0 "register_operand" "=v")
	(unspec:V16QI [(match_operand:V2DI  1 "register_operand"   "v")
		       (match_operand:V2DI  2 "register_operand"   "v")
		       (match_operand:V16QI 3 "register_operand"   "v")
		       (match_operand:QI    4 "const_mask_operand" "C")]
		      UNSPEC_VEC_MSUM))]
  "TARGET_VXE"
  "vmslg\t%v0,%v1,%v2,%v3,%4"
  [(set_attr "op_type" "VRR")])

(define_insn "vmslg"
  [(set (match_operand:TI 0 "register_operand" "=v")
	(unspec:TI [(match_operand:V2DI  1 "register_operand"   "v")
		    (match_operand:V2DI  2 "register_operand"   "v")
		    (match_operand:TI    3 "register_operand"   "v")
		    (match_operand:QI    4 "const_mask_operand" "C")]
		   UNSPEC_VEC_MSUM))]
  "TARGET_VXE"
  "vmslg\t%v0,%v1,%v2,%v3,%4"
  [(set_attr "op_type" "VRR")])


; Vector find any element equal

; vfaeb, vfaeh, vfaef
; vfaezb, vfaezh, vfaezf
(define_insn "vfae<mode>"
  [(set (match_operand:VI_HW_QHS 0 "register_operand" "=v")
	(unspec:VI_HW_QHS [(match_operand:VI_HW_QHS 1 "register_operand" "v")
			   (match_operand:VI_HW_QHS 2 "register_operand" "v")
			   (match_operand:QI        3 "const_mask_operand" "C")]
			  UNSPEC_VEC_VFAE))]
  "TARGET_VX"
{
  unsigned HOST_WIDE_INT flags = UINTVAL (operands[3]);

  if (flags & VSTRING_FLAG_ZS)
    {
      flags &= ~VSTRING_FLAG_ZS;
      operands[3] = GEN_INT (flags);
      return "vfaez<bhfgq>\t%v0,%v1,%v2,%b3";
    }
  return "vfae<bhfgq>\t%v0,%v1,%v2,%b3";
}
[(set_attr "op_type" "VRR")])

; vfaebs, vfaehs, vfaefs
; vfaezbs, vfaezhs, vfaezfs
(define_insn "*vfaes<mode>"
  [(set (match_operand:VI_HW_QHS 0 "register_operand" "=v")
	(unspec:VI_HW_QHS [(match_operand:VI_HW_QHS 1 "register_operand"   "v")
			   (match_operand:VI_HW_QHS 2 "register_operand"   "v")
			   (match_operand:QI        3 "const_mask_operand" "C")]
			  UNSPEC_VEC_VFAE))
   (set (reg:CCRAW CC_REGNUM)
	(unspec:CCRAW [(match_dup 1)
		       (match_dup 2)
		       (match_dup 3)]
		      UNSPEC_VEC_VFAECC))]
  "TARGET_VX"
{
  unsigned HOST_WIDE_INT flags = UINTVAL (operands[3]);

  if (flags & VSTRING_FLAG_ZS)
    {
      flags &= ~VSTRING_FLAG_ZS;
      operands[3] = GEN_INT (flags);
      return "vfaez<bhfgq>s\t%v0,%v1,%v2,%b3";
    }
  return "vfae<bhfgq>s\t%v0,%v1,%v2,%b3";
}
  [(set_attr "op_type" "VRR")])

(define_expand "vfaez<mode>"
  [(set (match_operand:VI_HW_QHS 0 "register_operand" "=v")
	(unspec:VI_HW_QHS [(match_operand:VI_HW_QHS 1 "register_operand"  "")
			   (match_operand:VI_HW_QHS 2 "register_operand"  "")
			   (match_operand:QI        3 "const_mask_operand" "")]
			  UNSPEC_VEC_VFAE))]
  "TARGET_VX"
{
  operands[3] = GEN_INT (INTVAL (operands[3]) | VSTRING_FLAG_ZS);
})

(define_expand "vfaes<mode>"
  [(parallel
    [(set (match_operand:VI_HW_QHS 0 "register_operand" "")
	(unspec:VI_HW_QHS [(match_operand:VI_HW_QHS 1 "register_operand"  "")
			   (match_operand:VI_HW_QHS 2 "register_operand"  "")
			   (match_operand:QI        3 "const_mask_operand" "")]
			  UNSPEC_VEC_VFAE))
   (set (reg:CCRAW CC_REGNUM)
	(unspec:CCRAW [(match_dup 1)
		       (match_dup 2)
		       (match_dup 3)]
		      UNSPEC_VEC_VFAECC))])
   (set (match_operand:SI 4 "memory_operand" "")
	(unspec:SI [(reg:CCRAW CC_REGNUM)] UNSPEC_CC_TO_INT))]
  "TARGET_VX"
{
  operands[3] = GEN_INT (INTVAL (operands[3]) | VSTRING_FLAG_CS);
})

(define_expand "vfaezs<mode>"
  [(parallel
    [(set (match_operand:VI_HW_QHS 0 "register_operand" "")
	(unspec:VI_HW_QHS [(match_operand:VI_HW_QHS 1 "register_operand"  "")
			   (match_operand:VI_HW_QHS 2 "register_operand"  "")
			   (match_operand:SI        3 "const_mask_operand" "")]
			  UNSPEC_VEC_VFAE))
   (set (reg:CCRAW CC_REGNUM)
	(unspec:CCRAW [(match_dup 1)
		       (match_dup 2)
		       (match_dup 3)]
		      UNSPEC_VEC_VFAECC))])
   (set (match_operand:SI 4 "memory_operand" "")
	(unspec:SI [(reg:CCRAW CC_REGNUM)] UNSPEC_CC_TO_INT))]
  "TARGET_VX"
{
  operands[3] = GEN_INT (INTVAL (operands[3]) | VSTRING_FLAG_CS | VSTRING_FLAG_ZS);
})


; Vector find element equal

; vfeeb, vfeeh, vfeef
(define_insn "vfee<mode>"
  [(set (match_operand:VI_HW_QHS                    0 "register_operand" "=v")
	(unspec:VI_HW_QHS [(match_operand:VI_HW_QHS 1 "register_operand"  "v")
			   (match_operand:VI_HW_QHS 2 "register_operand"  "v")
			   (const_int 0)]
			  UNSPEC_VEC_VFEE))]
  "TARGET_VX"
  "vfee<bhfgq>\t%v0,%v1,%v2,0"
  [(set_attr "op_type" "VRR")])

; vfeezb, vfeezh, vfeezf
(define_insn "vfeez<mode>"
  [(set (match_operand:VI_HW_QHS                    0 "register_operand" "=v")
	(unspec:VI_HW_QHS [(match_operand:VI_HW_QHS 1 "register_operand"  "v")
			   (match_operand:VI_HW_QHS 2 "register_operand"  "v")
			   (const_int VSTRING_FLAG_ZS)]
			  UNSPEC_VEC_VFEE))]
  "TARGET_VX"
  "vfeez<bhfgq>s\t%v0,%v1,%v2,2"
  [(set_attr "op_type" "VRR")])

(define_expand "vfees<mode>"
  [(parallel
    [(set (match_operand:VI_HW_QHS                    0 "register_operand" "")
	  (unspec:VI_HW_QHS [(match_operand:VI_HW_QHS 1 "register_operand" "")
			     (match_operand:VI_HW_QHS 2 "register_operand" "")
			     (const_int VSTRING_FLAG_CS)]
			    UNSPEC_VEC_VFEE))
   (set (reg:CCRAW CC_REGNUM)
	(unspec:CCRAW [(match_dup 1)
		       (match_dup 2)
		       (const_int VSTRING_FLAG_CS)]
		      UNSPEC_VEC_VFEECC))])
   (set (match_operand:SI 3 "memory_operand" "")
	(unspec:SI [(reg:CCRAW CC_REGNUM)] UNSPEC_CC_TO_INT))]
  "TARGET_VX")

(define_expand "vfeezs<mode>"
  [(parallel
    [(set (match_operand:VI_HW_QHS                    0 "register_operand" "")
	  (unspec:VI_HW_QHS [(match_operand:VI_HW_QHS 1 "register_operand" "")
			     (match_operand:VI_HW_QHS 2 "register_operand" "")
			     (match_dup 4)]
			    UNSPEC_VEC_VFEE))
   (set (reg:CCRAW CC_REGNUM)
	(unspec:CCRAW [(match_dup 1)
		       (match_dup 2)
		       (match_dup 4)]
		      UNSPEC_VEC_VFEECC))])
   (set (match_operand:SI 3 "memory_operand" "")
	(unspec:SI [(reg:CCRAW CC_REGNUM)] UNSPEC_CC_TO_INT))]
  "TARGET_VX"
{
  operands[4] = GEN_INT (VSTRING_FLAG_ZS | VSTRING_FLAG_CS);
})

; Vector find element not equal

; vfeneb, vfeneh, vfenef
(define_insn "vfene<mode>"
  [(set (match_operand:VI_HW_QHS                    0 "register_operand" "=v")
	(unspec:VI_HW_QHS [(match_operand:VI_HW_QHS 1 "register_operand"  "v")
			   (match_operand:VI_HW_QHS 2 "register_operand"  "v")
			   (const_int 0)]
			  UNSPEC_VEC_VFENE))]
  "TARGET_VX"
  "vfene<bhfgq>\t%v0,%v1,%v2,0"
  [(set_attr "op_type" "VRR")])

; vec_vfenes can be found in vector.md since it is used for strlen

; vfenezb, vfenezh, vfenezf
(define_insn "vfenez<mode>"
  [(set (match_operand:VI_HW_QHS                    0 "register_operand" "=v")
	(unspec:VI_HW_QHS [(match_operand:VI_HW_QHS 1 "register_operand"  "v")
			   (match_operand:VI_HW_QHS 2 "register_operand"  "v")
			   (const_int VSTRING_FLAG_ZS)]
			  UNSPEC_VEC_VFENE))]
  "TARGET_VX"
  "vfenez<bhfgq>\t%v0,%v1,%v2"
  [(set_attr "op_type" "VRR")])

(define_expand "vfenes<mode>"
  [(parallel
    [(set (match_operand:VI_HW_QHS                    0 "register_operand" "")
	  (unspec:VI_HW_QHS [(match_operand:VI_HW_QHS 1 "register_operand" "")
			     (match_operand:VI_HW_QHS 2 "register_operand" "")
			     (const_int VSTRING_FLAG_CS)]
			    UNSPEC_VEC_VFENE))
   (set (reg:CCRAW CC_REGNUM)
	(unspec:CCRAW [(match_dup 1)
		       (match_dup 2)
		       (const_int VSTRING_FLAG_CS)]
		      UNSPEC_VEC_VFENECC))])
   (set (match_operand:SI 3 "memory_operand" "")
	(unspec:SI [(reg:CCRAW CC_REGNUM)] UNSPEC_CC_TO_INT))]
  "TARGET_VX")

(define_expand "vfenezs<mode>"
  [(parallel
    [(set (match_operand:VI_HW_QHS                    0 "register_operand" "")
	  (unspec:VI_HW_QHS [(match_operand:VI_HW_QHS 1 "register_operand" "")
			     (match_operand:VI_HW_QHS 2 "register_operand" "")
			     (match_dup 4)]
			    UNSPEC_VEC_VFENE))
     (set (reg:CCRAW CC_REGNUM)
	  (unspec:CCRAW [(match_dup 1)
			 (match_dup 2)
			 (match_dup 4)]
			UNSPEC_VEC_VFENECC))])
   (set (match_operand:SI 3 "memory_operand" "")
	(unspec:SI [(reg:CCRAW CC_REGNUM)] UNSPEC_CC_TO_INT))]
  "TARGET_VX"
{
  operands[4] = GEN_INT (VSTRING_FLAG_ZS | VSTRING_FLAG_CS);
})

; Vector isolate string

; vistrb, vistrh, vistrf
(define_insn "vistr<mode>"
  [(set (match_operand:VI_HW_QHS 0 "register_operand" "=v")
	(unspec:VI_HW_QHS [(match_operand:VI_HW_QHS 1 "register_operand" "v")]
			  UNSPEC_VEC_VISTR))]
  "TARGET_VX"
  "vistr<bhfgq>\t%v0,%v1"
  [(set_attr "op_type" "VRR")])

; vistrbs, vistrhs, vistrfs
(define_insn "*vistrs<mode>"
  [(set (match_operand:VI_HW_QHS 0 "register_operand" "=v")
	(unspec:VI_HW_QHS [(match_operand:VI_HW_QHS 1 "register_operand" "v")]
			  UNSPEC_VEC_VISTR))
   (set (reg:CCRAW CC_REGNUM)
	(unspec:CCRAW [(match_dup 1)] UNSPEC_VEC_VISTRCC))]
  "TARGET_VX"
  "vistr<bhfgq>s\t%v0,%v1"
  [(set_attr "op_type" "VRR")])

(define_expand "vistrs<mode>"
  [(parallel
    [(set (match_operand:VI_HW_QHS                    0 "register_operand" "")
	  (unspec:VI_HW_QHS [(match_operand:VI_HW_QHS 1 "register_operand" "")]
			    UNSPEC_VEC_VISTR))
     (set (reg:CCRAW CC_REGNUM)
	  (unspec:CCRAW [(match_dup 1)]
			UNSPEC_VEC_VISTRCC))])
   (set (match_operand:SI 2 "memory_operand" "")
	(unspec:SI [(reg:CCRAW CC_REGNUM)] UNSPEC_CC_TO_INT))]
  "TARGET_VX")


; Vector compare range

; vstrcb, vstrch, vstrcf
; vstrczb, vstrczh, vstrczf
(define_insn "vstrc<mode>"
  [(set (match_operand:VI_HW_QHS                    0 "register_operand"  "=v")
	(unspec:VI_HW_QHS [(match_operand:VI_HW_QHS 1 "register_operand"   "v")
			   (match_operand:VI_HW_QHS 2 "register_operand"   "v")
			   (match_operand:VI_HW_QHS 3 "register_operand"   "v")
			   (match_operand:QI        4 "const_mask_operand" "C")]
			  UNSPEC_VEC_VSTRC))]
  "TARGET_VX"
{
  unsigned HOST_WIDE_INT flags = UINTVAL (operands[4]);

  if (flags & VSTRING_FLAG_ZS)
    {
      flags &= ~VSTRING_FLAG_ZS;
      operands[4] = GEN_INT (flags);
      return "vstrcz<bhfgq>\t%v0,%v1,%v2,%v3,%b4";
    }
  return "vstrc<bhfgq>\t%v0,%v1,%v2,%v3,%b4";
}
[(set_attr "op_type" "VRR")])

; vstrcbs, vstrchs, vstrcfs
; vstrczbs, vstrczhs, vstrczfs
(define_insn "*vstrcs<mode>"
  [(set (match_operand:VI_HW_QHS                    0 "register_operand"  "=v")
	(unspec:VI_HW_QHS [(match_operand:VI_HW_QHS 1 "register_operand"   "v")
			   (match_operand:VI_HW_QHS 2 "register_operand"   "v")
			   (match_operand:VI_HW_QHS 3 "register_operand"   "v")
			   (match_operand:QI        4 "const_mask_operand" "C")]
			  UNSPEC_VEC_VSTRC))
   (set (reg:CCRAW CC_REGNUM)
	(unspec:CCRAW [(match_dup 1)
		       (match_dup 2)
		       (match_dup 3)
		       (match_dup 4)]
		      UNSPEC_VEC_VSTRCCC))]
  "TARGET_VX"
{
  unsigned HOST_WIDE_INT flags = UINTVAL (operands[4]);

  if (flags & VSTRING_FLAG_ZS)
    {
      flags &= ~VSTRING_FLAG_ZS;
      operands[4] = GEN_INT (flags);
      return "vstrcz<bhfgq>s\t%v0,%v1,%v2,%v3,%b4";
    }
  return "vstrc<bhfgq>s\t%v0,%v1,%v2,%v3,%b4";
}
  [(set_attr "op_type" "VRR")])

(define_expand "vstrcz<mode>"
  [(set (match_operand:VI_HW_QHS 0 "register_operand" "")
	(unspec:VI_HW_QHS [(match_operand:VI_HW_QHS 1 "register_operand"   "")
			   (match_operand:VI_HW_QHS 2 "register_operand"   "")
			   (match_operand:VI_HW_QHS 3 "register_operand"   "")
			   (match_operand:QI        4 "const_mask_operand" "")]
			  UNSPEC_VEC_VSTRC))]
  "TARGET_VX"
{
  operands[4] = GEN_INT (INTVAL (operands[4]) | VSTRING_FLAG_ZS);
})

(define_expand "vstrcs<mode>"
  [(parallel
    [(set (match_operand:VI_HW_QHS 0 "register_operand" "")
	(unspec:VI_HW_QHS [(match_operand:VI_HW_QHS 1 "register_operand" "")
			   (match_operand:VI_HW_QHS 2 "register_operand" "")
			   (match_operand:VI_HW_QHS 3 "register_operand" "")
			   (match_operand:QI        4 "const_mask_operand" "")]
			  UNSPEC_VEC_VSTRC))
   (set (reg:CCRAW CC_REGNUM)
	(unspec:CCRAW [(match_dup 1)
		       (match_dup 2)
		       (match_dup 3)
		       (match_dup 4)]
		      UNSPEC_VEC_VSTRCCC))])
   (set (match_operand:SI 5 "memory_operand" "")
	(unspec:SI [(reg:CCRAW CC_REGNUM)] UNSPEC_CC_TO_INT))]
  "TARGET_VX"
{
  operands[4] = GEN_INT (INTVAL (operands[4]) | VSTRING_FLAG_CS);
})

(define_expand "vstrczs<mode>"
  [(parallel
    [(set (match_operand:VI_HW_QHS 0 "register_operand" "")
	(unspec:VI_HW_QHS [(match_operand:VI_HW_QHS 1 "register_operand" "")
			   (match_operand:VI_HW_QHS 2 "register_operand" "")
			   (match_operand:VI_HW_QHS 3 "register_operand" "")
			   (match_operand:QI        4 "const_mask_operand" "")]
			  UNSPEC_VEC_VSTRC))
   (set (reg:CCRAW CC_REGNUM)
	(unspec:CCRAW [(match_dup 1)
		       (match_dup 2)
		       (match_dup 3)
		       (match_dup 4)]
		      UNSPEC_VEC_VSTRCCC))])
   (set (match_operand:SI 5 "memory_operand" "")
	(unspec:SI [(reg:CCRAW CC_REGNUM)] UNSPEC_CC_TO_INT))]
  "TARGET_VX"
{
  operands[4] = GEN_INT (INTVAL (operands[4]) | VSTRING_FLAG_CS | VSTRING_FLAG_ZS);
})

; Vector string search

(define_expand "vstrs<mode>"
  [(parallel
    [(set (match_operand:V16QI                    0 "register_operand" "")
	  (unspec:V16QI [(match_operand:VI_HW_QHS 1 "register_operand" "")
			 (match_operand:VI_HW_QHS 2 "register_operand" "")
			 (match_operand:V16QI     3 "register_operand" "")
			 (const_int 0)]
			UNSPEC_VEC_VSTRS))
     (set (reg:CCRAW CC_REGNUM)
	  (unspec:CCRAW [(match_dup 1)
			 (match_dup 2)
			 (match_dup 3)
			 (const_int 0)]
			UNSPEC_VEC_VSTRSCC))])
   (set (match_operand:SI 4 "memory_operand" "")
	(unspec:SI [(reg:CCRAW CC_REGNUM)] UNSPEC_CC_TO_INT))]
  "TARGET_VXE2")

(define_expand "vstrsz<mode>"
  [(parallel
    [(set (match_operand:V16QI                    0 "register_operand" "")
	  (unspec:V16QI [(match_operand:VI_HW_QHS 1 "register_operand" "")
			 (match_operand:VI_HW_QHS 2 "register_operand" "")
			 (match_operand:V16QI     3 "register_operand" "")
			 (const_int VSTRING_FLAG_ZS)]
			UNSPEC_VEC_VSTRS))
     (set (reg:CCRAW CC_REGNUM)
	  (unspec:CCRAW [(match_dup 1)
			 (match_dup 2)
			 (match_dup 3)
			 (const_int VSTRING_FLAG_ZS)]
			UNSPEC_VEC_VSTRSCC))])
   (set (match_operand:SI 4 "memory_operand" "")
	(unspec:SI [(reg:CCRAW CC_REGNUM)] UNSPEC_CC_TO_INT))]
  "TARGET_VXE2")

; vstrsb, vstrsh, vstrsf
; vstrszb, vstrszh, vstrszf
(define_insn "vec_vstrs<mode>"
  [(set (match_operand:V16QI                    0 "register_operand" "=v")
	(unspec:V16QI [(match_operand:VI_HW_QHS 1 "register_operand" "v")
		       (match_operand:VI_HW_QHS 2 "register_operand" "v")
		       (match_operand:V16QI     3 "register_operand" "v")
		       (match_operand:QI        4 "const_mask_operand" "C")]
		      UNSPEC_VEC_VSTRS))
   (set (reg:CCRAW CC_REGNUM)
	(unspec:CCRAW [(match_dup 1)
		       (match_dup 2)
		       (match_dup 3)
		       (match_dup 4)]
		      UNSPEC_VEC_VSTRSCC))]
  "TARGET_VXE2"
{
  unsigned HOST_WIDE_INT flags = UINTVAL (operands[4]);

  gcc_assert (!(flags & ~VSTRING_FLAG_ZS));

  if (flags == VSTRING_FLAG_ZS)
    return "vstrsz<bhfgq>\t%v0,%v1,%v2,%v3";
  return "vstrs<bhfgq>\t%v0,%v1,%v2,%v3";
}
  [(set_attr "op_type" "VRR")])


; Vector convert int<->float

(define_insn "vcdgb"
  [(set (match_operand:V2DF 0 "register_operand"                "=v")
	(unspec:V2DF [(match_operand:V2DI 1 "register_operand"   "v")
		      (match_operand:QI   2 "const_mask_operand" "C")  ; inexact suppression
		      (match_operand:QI   3 "const_mask_operand" "C")] ; rounding mode
		     UNSPEC_VEC_VCDGB))]
  "TARGET_VX && UINTVAL (operands[3]) != 2 && UINTVAL (operands[3]) <= 7"
  "vcdgb\t%v0,%v1,%b2,%b3"
  [(set_attr "op_type" "VRR")])


; The result needs to be multiplied with 2**-op2
(define_expand "vec_ctd_s64"
  [(set (match_operand:V2DF               0 "register_operand" "")
	(unspec:V2DF [(match_operand:V2DI 1 "register_operand" "")
		      (const_int VEC_NOINEXACT)
		      (const_int VEC_RND_CURRENT)]
		     UNSPEC_VEC_VCDGB))
   (use (match_operand:QI 2 "const_int_operand" ""))
   (set (match_dup 0) (mult:V2DF (match_dup 0) (match_dup 3)))]
  "TARGET_VX"
{
  REAL_VALUE_TYPE f;
  rtx c;

  real_2expN (&f, -INTVAL (operands[2]), DFmode);
  c = const_double_from_real_value (f, DFmode);

  operands[3] = gen_const_vec_duplicate (V2DFmode, c);
  operands[3] = force_reg (V2DFmode, operands[3]);
})

(define_insn "vcdlgb"
  [(set (match_operand:V2DF 0 "register_operand"                 "=v")
	(unspec:V2DF [(match_operand:V2DI 1 "register_operand"    "v")
		      (match_operand:QI   2 "const_mask_operand"  "C")  ; inexact suppression
		      (match_operand:QI   3 "const_mask_operand"  "C")] ; rounding mode
		     UNSPEC_VEC_VCDLGB))]
  "TARGET_VX && UINTVAL (operands[3]) != 2 && UINTVAL (operands[3]) <= 7"
  "vcdlgb\t%v0,%v1,%b2,%b3"
  [(set_attr "op_type" "VRR")])

; The result needs to be multiplied with 2**-op2
(define_expand "vec_ctd_u64"
  [(set (match_operand:V2DF               0 "register_operand" "")
	(unspec:V2DF [(match_operand:V2DI 1 "register_operand" "")
		      (const_int VEC_NOINEXACT)
		      (const_int VEC_RND_CURRENT)]
		     UNSPEC_VEC_VCDLGB))
   (use (match_operand:QI 2 "const_int_operand" ""))
   (set (match_dup 0) (mult:V2DF (match_dup 0) (match_dup 3)))]
  "TARGET_VX"
{
  REAL_VALUE_TYPE f;
  rtx c;

  real_2expN (&f, -INTVAL (operands[2]), DFmode);
  c = const_double_from_real_value (f, DFmode);

  operands[3] = gen_const_vec_duplicate (V2DFmode, c);
  operands[3] = force_reg (V2DFmode, operands[3]);
})

(define_insn "vcgdb"
  [(set (match_operand:V2DI 0 "register_operand"                "=v")
	(unspec:V2DI [(match_operand:V2DF 1 "register_operand"   "v")
		      (match_operand:QI   2 "const_mask_operand" "C")
		      (match_operand:QI   3 "const_mask_operand" "C")]
		     UNSPEC_VEC_VCGDB))]
  "TARGET_VX && UINTVAL (operands[3]) != 2 && UINTVAL (operands[3]) <= 7"
  "vcgdb\t%v0,%v1,%b2,%b3"
  [(set_attr "op_type" "VRR")])

; The input needs to be multiplied with 2**op2
(define_expand "vec_ctsl"
  [(use (match_operand:QI 2 "const_int_operand" ""))
   (set (match_dup 4) (mult:V2DF (match_operand:V2DF 1 "register_operand" "")
				 (match_dup 3)))
   (set (match_operand:V2DI 0 "register_operand" "")
	(unspec:V2DI [(match_dup 4)
		      (const_int VEC_NOINEXACT)
		      (const_int VEC_RND_CURRENT)]
		     UNSPEC_VEC_VCGDB))]
  "TARGET_VX"
{
  REAL_VALUE_TYPE f;
  rtx c;

  real_2expN (&f, INTVAL (operands[2]), DFmode);
  c = const_double_from_real_value (f, DFmode);

  operands[3] = gen_const_vec_duplicate (V2DFmode, c);
  operands[3] = force_reg (V2DFmode, operands[3]);
  operands[4] = gen_reg_rtx (V2DFmode);
})

(define_insn "vclgdb"
  [(set (match_operand:V2DI 0 "register_operand"               "=v")
	(unspec:V2DI [(match_operand:V2DF 1 "register_operand"  "v")
		      (match_operand:QI   2 "const_mask_operand" "C")
		      (match_operand:QI   3 "const_mask_operand" "C")]
		     UNSPEC_VEC_VCLGDB))]
  "TARGET_VX && UINTVAL (operands[3]) != 2 && UINTVAL (operands[3]) <= 7"
  "vclgdb\t%v0,%v1,%b2,%b3"
  [(set_attr "op_type" "VRR")])

; The input needs to be multiplied with 2**op2
(define_expand "vec_ctul"
  [(use (match_operand:QI 2 "const_int_operand" ""))
   (set (match_dup 4) (mult:V2DF (match_operand:V2DF 1 "register_operand" "")
				 (match_dup 3)))
   (set (match_operand:V2DI 0 "register_operand" "")
	(unspec:V2DI [(match_dup 4)
		      (const_int VEC_NOINEXACT)
		      (const_int VEC_RND_CURRENT)]
		     UNSPEC_VEC_VCLGDB))]
  "TARGET_VX"
{
  REAL_VALUE_TYPE f;
  rtx c;

  real_2expN (&f, INTVAL (operands[2]), DFmode);
  c = const_double_from_real_value (f, DFmode);

  operands[3] = gen_const_vec_duplicate (V2DFmode, c);
  operands[3] = force_reg (V2DFmode, operands[3]);
  operands[4] = gen_reg_rtx (V2DFmode);
})

; Vector load fp integer - IEEE inexact exception is suppressed
; vfisb, vfidb, wfisb, wfidb, wfixb
(define_insn "vec_fpint<mode>"
  [(set (match_operand:VFT              0 "register_operand"  "=v")
	(unspec:VFT [(match_operand:VFT 1 "register_operand"   "v")
		     (match_operand:QI  2 "const_mask_operand" "C")  ; inexact suppression control
		     (match_operand:QI  3 "const_mask_operand" "C")] ; rounding mode
		     UNSPEC_VEC_VFI))]
  "TARGET_VX"
  "<vw>fi<sdx>b\t%v0,%v1,%b2,%b3"
  [(set_attr "op_type" "VRR")])


; Vector load lengthened - V4SF -> V2DF

(define_insn "vflls"
  [(set (match_operand:V2DF 0 "register_operand"               "=v")
	(unspec:V2DF [(match_operand:V4SF 1 "register_operand"  "v")]
		     UNSPEC_VEC_VFLL))]
  "TARGET_VX"
  "vldeb\t%v0,%v1"
  [(set_attr "op_type" "VRR")])

(define_expand "vec_ld2f"
  [; Initialize a vector to all zeroes.  FIXME: This should not be
   ; necessary since all elements of the vector will be set anyway.
   ; This is just to make it explicit to the data flow framework.
   (set (match_dup 2) (match_dup 3))
   (set (match_dup 2) (unspec:V4SF [(match_operand:SF 1 "memory_operand" "")
				    (const_int 0)
				    (match_dup 2)]
				    UNSPEC_VEC_SET))
   (set (match_dup 2) (unspec:V4SF [(match_dup 4)
				    (const_int 2)
				    (match_dup 2)]
				    UNSPEC_VEC_SET))
   (set (match_operand:V2DF 0 "register_operand" "")
	(unspec:V2DF [(match_dup 2)] UNSPEC_VEC_VFLL))]
  "TARGET_VX"
{
  operands[2] = gen_reg_rtx (V4SFmode);
  operands[3] = CONST0_RTX (V4SFmode);
  operands[4] = adjust_address (operands[1], SFmode, 4);
})


; Vector load rounded - V2DF -> V4SF

(define_insn "vflrd"
  [(set (match_operand:V4SF 0 "register_operand"                "=v")
	(unspec:V4SF [(match_operand:V2DF 1 "register_operand"   "v")
		      (match_operand:QI   2 "const_mask_operand" "C")
		      (match_operand:QI   3 "const_mask_operand" "C")]
		     UNSPEC_VEC_VFLR))]
  "TARGET_VX"
  "vledb\t%v0,%v1,%b2,%b3"
  [(set_attr "op_type" "VRR")])

(define_expand "vec_st2f"
  [(set (match_dup 2)
	(unspec:V4SF [(match_operand:V2DF 0 "register_operand" "")
		      (const_int VEC_INEXACT)
		      (const_int VEC_RND_CURRENT)]
		     UNSPEC_VEC_VFLR))
   (set (match_operand:SF 1 "memory_operand" "")
	(vec_select:SF (match_dup 2)
	 (parallel [(const_int 0)])))
   (set (match_dup 3)
	(vec_select:SF (match_dup 2)
	 (parallel [(const_int 2)])))]
  "TARGET_VX"
{
  operands[2] = gen_reg_rtx (V4SFmode);
  operands[3] = adjust_address (operands[1], SFmode, 4);
})


; Vector square root fp vec_sqrt -> sqrt rtx standard name

;; Vector FP test data class immediate

; vec_all_nan, vec_all_numeric, vec_any_nan, vec_any_numeric
; These ignore the vector result and only want CC stored to an int
; pointer.

; vftcisb, vftcidb, wftcixb
(define_insn "*vftci<mode>_cconly"
  [(set (reg:CCRAW CC_REGNUM)
	(unspec:CCRAW [(match_operand:VF_HW 1 "register_operand"  "v")
		       (match_operand:HI    2 "const_int_operand" "J")]
		      UNSPEC_VEC_VFTCICC))
   (clobber (match_scratch:<TOINTVEC> 0 "=v"))]
  "TARGET_VX && CONST_OK_FOR_CONSTRAINT_P (INTVAL (operands[2]), 'J', \"J\")"
  "<vw>ftci<sdx>b\t%v0,%v1,%x2"
  [(set_attr "op_type" "VRR")])

(define_expand "vftci<mode>_intcconly"
  [(parallel
    [(set (reg:CCRAW CC_REGNUM)
	  (unspec:CCRAW [(match_operand:VF_HW 0 "register_operand")
			 (match_operand:HI    1 "const_int_operand")]
			UNSPEC_VEC_VFTCICC))
     (clobber (scratch:<TOINTVEC>))])
   (set (match_operand:SI 2 "register_operand" "")
	(unspec:SI [(reg:CCRAW CC_REGNUM)] UNSPEC_CC_TO_INT))]
  "TARGET_VX && CONST_OK_FOR_CONSTRAINT_P (INTVAL (operands[1]), 'J', \"J\")")

; vec_fp_test_data_class wants the result vector and the CC stored to
; an int pointer.

; vftcisb, vftcidb, wftcixb
(define_insn "vftci<mode>"
  [(set (match_operand:VF_HW                0 "register_operand"  "=v")
	(unspec:VF_HW [(match_operand:VF_HW 1 "register_operand"   "v")
		       (match_operand:HI    2 "const_int_operand"  "J")]
		      UNSPEC_VEC_VFTCI))
   (set (reg:CCRAW CC_REGNUM)
	(unspec:CCRAW [(match_dup 1) (match_dup 2)] UNSPEC_VEC_VFTCICC))]
  "TARGET_VX && CONST_OK_FOR_CONSTRAINT_P (INTVAL (operands[2]), 'J', \"J\")"
  "<vw>ftci<sdx>b\t%v0,%v1,%x2"
  [(set_attr "op_type" "VRR")])

(define_expand "vftci<mode>_intcc"
  [(parallel
    [(set (match_operand:VF_HW                0 "register_operand")
	  (unspec:VF_HW [(match_operand:VF_HW 1 "register_operand")
			 (match_operand:HI    2 "const_int_operand")]
			UNSPEC_VEC_VFTCI))
     (set (reg:CCRAW CC_REGNUM)
	  (unspec:CCRAW [(match_dup 1) (match_dup 2)] UNSPEC_VEC_VFTCICC))])
   (set (match_operand:SI                     3 "nonimmediate_operand")
	(unspec:SI [(reg:CCRAW CC_REGNUM)] UNSPEC_CC_TO_INT))]
  "TARGET_VX && CONST_OK_FOR_CONSTRAINT_P (INTVAL (operands[2]), 'J', \"J\")")

;;
;; Integer compares
;;

; All comparisons which produce a CC need fully populated (VI_HW)
; vector arguments.  Otherwise the any/all CCs would be just bogus.

(define_insn "*vec_cmp<VICMP:insn_cmp><VI_HW:mode>_cconly"
  [(set (reg:VICMP CC_REGNUM)
	(compare:VICMP (match_operand:VI_HW 0 "register_operand" "v")
		       (match_operand:VI_HW 1 "register_operand" "v")))
   (clobber (match_scratch:VI_HW 2 "=v"))]
  "TARGET_VX"
  "vc<VICMP:insn_cmp><VI_HW:bhfgq>s\t%v2,%v0,%v1"
  [(set_attr "op_type" "VRR")])

; FIXME: The following 2x3 definitions should be merged into 2 with
; VICMP like above but I could not find a way to set the comparison
; operator (eq) depending on the mode CCVEQ (mode_iterator). Or the
; other way around - setting the mode depending on the code
; (code_iterator).
(define_expand "vec_cmpeq<VI_HW:mode>_cc"
  [(parallel
    [(set (reg:CCVEQ CC_REGNUM)
	(compare:CCVEQ (match_operand:VI_HW 1 "register_operand" "v")
		       (match_operand:VI_HW 2 "register_operand" "v")))
     (set (match_operand:VI_HW 0 "register_operand" "=v")
	  (eq:VI_HW (match_dup 1) (match_dup 2)))])
   (set (match_operand:SI 3 "memory_operand" "")
	(unspec:SI [(reg:CCVEQ CC_REGNUM)] UNSPEC_CC_TO_INT))]
  "TARGET_VX")

(define_expand "vec_cmph<VI_HW:mode>_cc"
  [(parallel
    [(set (reg:CCVIH CC_REGNUM)
	  (compare:CCVIH (match_operand:VI_HW 1 "register_operand" "v")
			 (match_operand:VI_HW 2 "register_operand" "v")))
     (set (match_operand:VI_HW 0 "register_operand" "=v")
	  (gt:VI_HW (match_dup 1) (match_dup 2)))])
   (set (match_operand:SI 3 "memory_operand" "")
	(unspec:SI [(reg:CCVIH CC_REGNUM)] UNSPEC_CC_TO_INT))]
  "TARGET_VX")

(define_expand "vec_cmphl<VI_HW:mode>_cc"
  [(parallel
    [(set (reg:CCVIHU CC_REGNUM)
	  (compare:CCVIHU (match_operand:VI_HW 1 "register_operand" "v")
			  (match_operand:VI_HW 2 "register_operand" "v")))
     (set (match_operand:VI_HW 0 "register_operand" "=v")
	  (gtu:VI_HW (match_dup 1) (match_dup 2)))])
   (set (match_operand:SI 3 "memory_operand" "")
	(unspec:SI [(reg:CCVIHU CC_REGNUM)] UNSPEC_CC_TO_INT))]
  "TARGET_VX")


(define_insn "*vec_cmpeq<VI_HW:mode>_cc"
  [(set (reg:CCVEQ CC_REGNUM)
	(compare:CCVEQ (match_operand:VI_HW 0 "register_operand"  "v")
		       (match_operand:VI_HW 1 "register_operand"  "v")))
   (set (match_operand:VI_HW                2 "register_operand" "=v")
	(eq:VI_HW (match_dup 0) (match_dup 1)))]
  "TARGET_VX"
  "vceq<VI_HW:bhfgq>s\t%v2,%v0,%v1"
  [(set_attr "op_type" "VRR")])

(define_insn "*vec_cmph<VI_HW:mode>_cc"
  [(set (reg:CCVIH CC_REGNUM)
	(compare:CCVIH (match_operand:VI_HW 0 "register_operand"  "v")
		       (match_operand:VI_HW 1 "register_operand"  "v")))
   (set (match_operand:VI_HW               2 "register_operand" "=v")
	(gt:VI_HW (match_dup 0) (match_dup 1)))]
  "TARGET_VX"
  "vch<VI_HW:bhfgq>s\t%v2,%v0,%v1"
  [(set_attr "op_type" "VRR")])

(define_insn "*vec_cmphl<VI_HW:mode>_cc"
  [(set (reg:CCVIHU CC_REGNUM)
	(compare:CCVIHU (match_operand:VI_HW 0 "register_operand"  "v")
			(match_operand:VI_HW 1 "register_operand"  "v")))
   (set (match_operand:VI_HW                2 "register_operand" "=v")
	(gtu:VI_HW (match_dup 0) (match_dup 1)))]
  "TARGET_VX"
  "vchl<VI_HW:bhfgq>s\t%v2,%v0,%v1"
  [(set_attr "op_type" "VRR")])

;;
;; Floating point compares
;;

; vfcesbs, vfcedbs, wfcexbs, vfchsbs, vfchdbs, wfchxbs, vfchesbs, vfchedbs, wfchexbs
(define_insn "*vec_cmp<insn_cmp><VF_HW:mode>_cconly"
  [(set (reg:VFCMP CC_REGNUM)
	(compare:VFCMP (match_operand:VF_HW 0 "register_operand" "v")
		       (match_operand:VF_HW 1 "register_operand" "v")))
   (clobber (match_scratch:<TOINTVEC> 2 "=v"))]
  "TARGET_VX"
  "<vw>fc<asm_fcmp><sdx>bs\t%v2,%v0,%v1"
  [(set_attr "op_type" "VRR")])

; FIXME: Merge the following 2x3 patterns with VFCMP
(define_expand "vec_cmpeq<mode>_cc"
  [(parallel
    [(set (reg:CCVEQ CC_REGNUM)
	  (compare:CCVEQ (match_operand:VF_HW 1 "register_operand"  "v")
			 (match_operand:VF_HW 2 "register_operand"  "v")))
     (set (match_operand:<TOINTVEC> 0 "register_operand" "=v")
	  (eq:<TOINTVEC> (match_dup 1) (match_dup 2)))])
   (set (match_operand:SI 3 "memory_operand" "")
	(unspec:SI [(reg:CCVEQ CC_REGNUM)] UNSPEC_CC_TO_INT))]
  "TARGET_VX")

(define_expand "vec_cmph<mode>_cc"
  [(parallel
    [(set (reg:CCVFH CC_REGNUM)
	  (compare:CCVFH (match_operand:VF_HW 1 "register_operand"  "v")
			 (match_operand:VF_HW 2 "register_operand"  "v")))
     (set (match_operand:<TOINTVEC> 0 "register_operand" "=v")
	  (gt:<TOINTVEC> (match_dup 1) (match_dup 2)))])
   (set (match_operand:SI 3 "memory_operand" "")
	(unspec:SI [(reg:CCVIH CC_REGNUM)] UNSPEC_CC_TO_INT))]
  "TARGET_VX")

(define_expand "vec_cmphe<mode>_cc"
  [(parallel
    [(set (reg:CCVFHE CC_REGNUM)
	  (compare:CCVFHE (match_operand:VF_HW 1 "register_operand"  "v")
			  (match_operand:VF_HW 2 "register_operand"  "v")))
     (set (match_operand:<TOINTVEC> 0 "register_operand" "=v")
	  (ge:<TOINTVEC> (match_dup 1) (match_dup 2)))])
   (set (match_operand:SI 3 "memory_operand" "")
	(unspec:SI [(reg:CCVFHE CC_REGNUM)] UNSPEC_CC_TO_INT))]
  "TARGET_VX")

; These 3 cannot be merged as the insn defintion above since it also
; requires to rewrite the RTL equality operator that the same time as
; the CC mode.

; vfcesbs, vfcedbs, wfcexbs
(define_insn "*vec_cmpeq<mode>_cc"
  [(set (reg:CCVEQ CC_REGNUM)
	(compare:CCVEQ (match_operand:VF_HW 0 "register_operand"  "v")
		       (match_operand:VF_HW 1 "register_operand"  "v")))
   (set (match_operand:<TOINTVEC>              2 "register_operand" "=v")
	(eq:<TOINTVEC> (match_dup 0) (match_dup 1)))]
  "TARGET_VX"
  "<vw>fce<sdx>bs\t%v2,%v0,%v1"
  [(set_attr "op_type" "VRR")])

; vfchsbs, vfchdbs, wfchxbs
(define_insn "*vec_cmph<mode>_cc"
  [(set (reg:CCVFH CC_REGNUM)
	(compare:CCVFH (match_operand:VF_HW 0 "register_operand"  "v")
		       (match_operand:VF_HW 1 "register_operand"  "v")))
   (set (match_operand:<TOINTVEC>              2 "register_operand" "=v")
	(gt:<TOINTVEC> (match_dup 0) (match_dup 1)))]
  "TARGET_VX"
  "<vw>fch<sdx>bs\t%v2,%v0,%v1"
  [(set_attr "op_type" "VRR")])

; vfchesbs, vfchedbs, wfchexbs
(define_insn "*vec_cmphe<mode>_cc"
  [(set (reg:CCVFHE CC_REGNUM)
	(compare:CCVFHE (match_operand:VF_HW 0 "register_operand"  "v")
			(match_operand:VF_HW 1 "register_operand"  "v")))
   (set (match_operand:<TOINTVEC>            2 "register_operand" "=v")
	(ge:<TOINTVEC> (match_dup 0) (match_dup 1)))]
  "TARGET_VX"
  "<vw>fche<sdx>bs\t%v2,%v0,%v1"
  [(set_attr "op_type" "VRR")])


(define_insn "vfmin<mode>"
  [(set (match_operand:VF_HW                0 "register_operand"  "=v")
	(unspec:VF_HW [(match_operand:VF_HW 1 "register_operand"   "v")
		       (match_operand:VF_HW 2 "register_operand"   "v")
		       (match_operand:QI    3 "const_mask_operand" "C")]
		      UNSPEC_VEC_VFMIN))]
  "TARGET_VXE"
  "<vw>fmin<sdx>b\t%v0,%v1,%v2,%b3"
  [(set_attr "op_type" "VRR")])

(define_insn "vfmax<mode>"
  [(set (match_operand:VF_HW                0 "register_operand"  "=v")
	(unspec:VF_HW [(match_operand:VF_HW 1 "register_operand"   "v")
		       (match_operand:VF_HW 2 "register_operand"   "v")
		       (match_operand:QI    3 "const_mask_operand" "C")]
		      UNSPEC_VEC_VFMAX))]
  "TARGET_VXE"
  "<vw>fmax<sdx>b\t%v0,%v1,%v2,%b3"
  [(set_attr "op_type" "VRR")])

; The element reversal builtins introduced with z15 have been made
; available also for older CPUs down to z13.
(define_expand "eltswap<mode>"
  [(set (match_operand:VEC_HW                 0 "nonimmediate_operand" "")
	(unspec:VEC_HW [(match_operand:VEC_HW 1 "nonimmediate_operand" "")]
		       UNSPEC_VEC_ELTSWAP))]
  "TARGET_VX")

; The byte element reversal is implemented as 128 bit byte swap.
; Alternatively this could be emitted as bswap:V1TI but the required
; subregs appear to confuse combine.
(define_insn "*eltswapv16qi"
  [(set (match_operand:V16QI                0 "nonimmediate_operand" "=v,v,R")
	(unspec:V16QI [(match_operand:V16QI 1 "nonimmediate_operand"  "v,R,v")]
		      UNSPEC_VEC_ELTSWAP))]
  "TARGET_VXE2"
  "@
   #
   vlbrq\t%v0,%v1
   vstbrq\t%v1,%v0"
  [(set_attr "op_type" "*,VRX,VRX")])

; vlerh, vlerf, vlerg, vsterh, vsterf, vsterg
(define_insn "*eltswap<mode>"
  [(set (match_operand:V_HW_HSD                   0 "nonimmediate_operand" "=v,v,R")
	(unspec:V_HW_HSD [(match_operand:V_HW_HSD 1 "nonimmediate_operand"  "v,R,v")]
			 UNSPEC_VEC_ELTSWAP))]
  "TARGET_VXE2"
  "@
   #
   vler<bhfgq>\t%v0,%v1
   vster<bhfgq>\t%v1,%v0"
  [(set_attr "op_type" "*,VRX,VRX")])

; The emulation pattern below will also accept
;  vst (eltswap (vl))
; i.e. both operands in memory, which reload needs to fix.
; Split into
;  vl
;  vster (=vst (eltswap))
; since we prefer vster over vler as long as the latter
; does not support alignment hints.
(define_split
  [(set (match_operand:VEC_HW                 0 "memory_operand" "")
	(unspec:VEC_HW [(match_operand:VEC_HW 1 "memory_operand" "")]
		       UNSPEC_VEC_ELTSWAP))]
  "TARGET_VXE2 && can_create_pseudo_p ()"
  [(set (match_dup 2) (match_dup 1))
   (set (match_dup 0)
	(unspec:VEC_HW [(match_dup 2)] UNSPEC_VEC_ELTSWAP))]
{
  operands[2] = gen_reg_rtx (<MODE>mode);
})


; Swapping v2df/v2di can be done via vpdi on z13 and z14.
(define_split
  [(set (match_operand:V_HW_2                 0 "register_operand" "")
	(unspec:V_HW_2 [(match_operand:V_HW_2 1 "register_operand" "")]
		       UNSPEC_VEC_ELTSWAP))]
  "TARGET_VX && can_create_pseudo_p ()"
  [(set (match_operand:V_HW_2     0 "register_operand" "=v")
	(vec_select:V_HW_2
	 (vec_concat:<vec_2x_nelts>
	  (match_operand:V_HW_2 1 "register_operand"  "v")
	  (match_dup 1))
	 (parallel [(const_int 1) (const_int 2)])))]
)


; Swapping v4df/v4si can be done via vpdi and rot.
(define_split
  [(set (match_operand:V_HW_4                 0 "register_operand" "")
	(unspec:V_HW_4 [(match_operand:V_HW_4 1 "register_operand" "")]
		       UNSPEC_VEC_ELTSWAP))]
  "TARGET_VX && can_create_pseudo_p ()"
  [(set (match_dup 2)
	(vec_select:V_HW_4
	 (vec_concat:<vec_2x_nelts>
	  (match_dup 1)
	  (match_dup 1))
	 (parallel [(const_int 2) (const_int 3) (const_int 4) (const_int 5)])))
 (set (match_dup 3)
  (subreg:V2DI (match_dup 2) 0))
 (set (match_dup 4)
  (rotate:V2DI
   (match_dup 3)
   (const_int 32)))
 (set (match_operand:V_HW_4 0)
  (subreg:V_HW_4 (match_dup 4) 0))]
{
  operands[2] = gen_reg_rtx (<MODE>mode);
  operands[3] = gen_reg_rtx (V2DImode);
  operands[4] = gen_reg_rtx (V2DImode);
})

; z15 has instructions for doing element reversal from mem to reg
; or the other way around.  For reg to reg or on pre z15 machines
; we have to emulate it with vector permute.
(define_insn_and_split "*eltswap<mode>_emu"
  [(set (match_operand:VEC_HW                 0 "nonimmediate_operand" "=vR")
	(unspec:VEC_HW [(match_operand:VEC_HW 1 "nonimmediate_operand" "vR")]
		       UNSPEC_VEC_ELTSWAP))]
  "TARGET_VX && can_create_pseudo_p ()"
  "#"
  "&& ((!memory_operand (operands[0], <MODE>mode)
        && !memory_operand (operands[1], <MODE>mode))
       || !TARGET_VXE2)"
  [(set (match_dup 3)
	(unspec:V16QI [(match_dup 4)
		       (match_dup 4)
		       (match_dup 2)]
		      UNSPEC_VEC_PERM))
   (set (match_dup 0) (subreg:VEC_HW (match_dup 3) 0))]
{
  static char p[4][16] =
    { { 15, 14, 13, 12, 11, 10, 9,  8,  7,  6,  5,  4,  3,  2,  1,  0 },   /* Q */
      { 14, 15, 12, 13, 10, 11, 8,  9,  6,  7,  4,  5,  2,  3,  0,  1 },   /* H */
      { 12, 13, 14, 15, 8,  9,  10, 11, 4,  5,  6,  7,  0,  1,  2,  3 },   /* S */
      { 8,  9,  10, 11, 12, 13, 14, 15, 0,  1,  2,  3,  4,  5,  6,  7 } }; /* D */
  char *perm;
  rtx perm_rtx[16], constv;

  switch (GET_MODE_SIZE (GET_MODE_INNER (<MODE>mode)))
    {
    case 1: perm = p[0]; break;
    case 2: perm = p[1]; break;
    case 4: perm = p[2]; break;
    case 8: perm = p[3]; break;
    default: gcc_unreachable ();
    }

  for (int i = 0; i < 16; i++)
    perm_rtx[i] = GEN_INT (perm[i]);

  operands[1] = force_reg (<MODE>mode, operands[1]);
  operands[2] = gen_reg_rtx (V16QImode);
  operands[3] = gen_reg_rtx (V16QImode);
  operands[4] = simplify_gen_subreg (V16QImode, operands[1], <MODE>mode, 0);
  constv = force_const_mem (V16QImode, gen_rtx_CONST_VECTOR (V16QImode, gen_rtvec_v (16, perm_rtx)));
  emit_move_insn (operands[2], constv);
})

; vec_insert (__builtin_bswap32 (*a), b, 1)        set-element-bswap-2.c
; b[1] = __builtin_bswap32 (*a)                    set-element-bswap-3.c
; vlebrh, vlebrf, vlebrg
(define_insn "*vec_set_bswap_elem<mode>"
  [(set (match_operand:V_HW_HSD                                     0 "register_operand" "=v")
	(unspec:V_HW_HSD [(bswap:<non_vec> (match_operand:<non_vec> 1 "memory_operand"    "R"))
		                           (match_operand:SI        2 "const_int_operand" "C")
					   (match_operand:V_HW_HSD  3 "register_operand"  "0")]
		  UNSPEC_VEC_SET))]
  "TARGET_VXE2 && UINTVAL (operands[2]) < GET_MODE_NUNITS (<V_HW_HSD:MODE>mode)"
  "vlebr<bhfgq>\t%v0,%1,%2"
  [(set_attr "op_type" "VRX")])

; vec_revb (vec_insert (*a, vec_revb (b), 1))      set-element-bswap-1.c
; vlebrh, vlebrf, vlebrg
(define_insn "*vec_set_bswap_vec<mode>"
  [(set (match_operand:V_HW_HSD                                     0 "register_operand"       "=v")
	(bswap:V_HW_HSD
	 (unspec:V_HW_HSD [(match_operand:<non_vec>                 1 "memory_operand"          "R")
		           (match_operand:SI                        2 "const_int_operand"       "C")
			   (bswap:V_HW_HSD (match_operand:V_HW_HSD  3 "register_operand"        "0"))]
			  UNSPEC_VEC_SET)))
   (use (match_operand:V16QI                                        4 "permute_pattern_operand" "X"))]
  "TARGET_VXE2 && UINTVAL (operands[2]) < GET_MODE_NUNITS (<V_HW_HSD:MODE>mode)"
  "vlebr<bhfgq>\t%v0,%1,%2"
  [(set_attr "op_type" "VRX")])

; *a = vec_extract (vec_revb (b), 1);              get-element-bswap-3.c
; *a = vec_revb (b)[1];                            get-element-bswap-4.c
; vstebrh, vstebrf, vstebrg
(define_insn "*vec_extract_bswap_vec<mode>"
  [(set (match_operand:<non_vec>                  0 "memory_operand"   "=R")
	(vec_select:<non_vec>
	 (bswap:V_HW_HSD (match_operand:V_HW_HSD  1 "register_operand"  "v"))
	 (parallel [(match_operand:SI             2 "const_int_operand" "C")])))]
  "TARGET_VXE2 && UINTVAL (operands[2]) < GET_MODE_NUNITS (<V_HW_HSD:MODE>mode)"
  "vstebr<bhfgq>\t%v1,%0,%2"
  [(set_attr "op_type" "VRX")])

; *a = __builtin_bswap32 (vec_extract (b, 1));     get-element-bswap-1.c
; *a = __builtin_bswap32 (b[1]);                   get-element-bswap-2.c
; vstebrh, vstebrf, vstebrg
(define_insn "*vec_extract_bswap_elem<mode>"
  [(set (match_operand:<non_vec>                  0 "memory_operand"   "=R")
	(bswap:<non_vec>
	 (vec_select:<non_vec>
	  (match_operand:V_HW_HSD                 1 "register_operand"  "v")
	  (parallel [(match_operand:SI            2 "const_int_operand" "C")]))))]
  "TARGET_VXE2 && UINTVAL (operands[2]) < GET_MODE_NUNITS (<V_HW_HSD:MODE>mode)"
  "vstebr<bhfgq>\t%v1,%0,%2"
  [(set_attr "op_type" "VRX")])


;;
;; NNPA Facility
;;

(define_insn "vclfnhs_v8hi"
  [(set (match_operand:V4SF                0 "register_operand"  "=v")
	(unspec:V4SF [(vec_select:V4HI
		       (match_operand:V8HI 1 "register_operand"   "v")
		       (parallel [(const_int 0) (const_int 1) (const_int 2) (const_int 3)]))
		      (match_operand:QI    2 "const_mask_operand" "C")]
		     UNSPEC_NNPA_VCLFNHS_V8HI))]
  "TARGET_NNPA"
  "vclfnh\t%v0,%v1,2,%2"
  [(set_attr "op_type" "VRR")])

(define_insn "vclfnls_v8hi"
  [(set (match_operand:V4SF                0 "register_operand"   "=v")
	(unspec:V4SF [(vec_select:V4HI
		       (match_operand:V8HI 1 "register_operand"   "v")
		       (parallel [(const_int 4) (const_int 5) (const_int 6) (const_int 7)]))
		      (match_operand:QI    2 "const_mask_operand"  "C")]
		     UNSPEC_NNPA_VCLFNLS_V8HI))]
  "TARGET_NNPA"
  "vclfnl\t%v0,%v1,2,%2"
  [(set_attr "op_type" "VRR")])

(define_insn "vcrnfs_v8hi"
  [(set (match_operand:V8HI               0 "register_operand"   "=v")
	(unspec:V8HI [(match_operand:V4SF 1 "register_operand"    "v")
		      (match_operand:V4SF 2 "register_operand"    "v")
		      (match_operand:QI   3 "const_mask_operand"  "C")]
		     UNSPEC_NNPA_VCRNFS_V8HI))]
  "TARGET_NNPA"
  "vcrnf\t%v0,%v1,%v2,%3,2"
  [(set_attr "op_type" "VRR")])

(define_insn "vcfn_v8hi"
  [(set (match_operand:V8HI               0 "register_operand"   "=v")
	(unspec:V8HI [(match_operand:V8HI 1 "register_operand"    "v")
		      (match_operand:QI   2 "const_mask_operand"  "C")]
		     UNSPEC_NNPA_VCFN_V8HI))]
  "TARGET_NNPA"
  "vcfn\t%v0,%v1,1,%2"
  [(set_attr "op_type" "VRR")])

(define_insn "vcnf_v8hi"
  [(set (match_operand:V8HI               0 "register_operand"   "=v")
	(unspec:V8HI [(match_operand:V8HI 1 "register_operand"    "v")
		      (match_operand:QI   2 "const_mask_operand"  "C")]
		     UNSPEC_NNPA_VCNF_V8HI))]
  "TARGET_NNPA"
  "vcnf\t%v0,%v1,%2,1"
  [(set_attr "op_type" "VRR")])
