On Wed, Sep 09, 2026 at 02:31:54PM +0100, [email protected] wrote:
> From: Ezra Sitorus <[email protected]>
> 
> This patch adds the following SVE2p3 intrinsics:
>       * svluti6[_s8_x2]
>       * svluti6_lane[_s16_x2]
> 
> and the following SME2p3 intrinsics:
>       * svluti6_zt_s8
>       * svluti6_zt_s8_x4
>       * svluti6_lane_s16_x4[_s16_x2_u8_x2]
>       * svluti6_lane_s16_x4[_s16_x2_u8_x3]
> 
> along with their other type variants.
> 
> Regarding this patch, there are 2 things I want to highlight:
> 
> 1) For those last 2 intrinsics, AFAIK, the current system won't allow us
>    to have multiple group suffixes, let alone match them with multiple
>    type suffixes. The way I've resolved it for these is to:
>       1) have the return type and group suffix to the base name
>       2) check if it's a luti6_lane_sme shape
>       3) manually append "_x2_u8" to the name if it is the right shape
>       4) add the group suffix (either x2 or x3). 2 - 3 is done in
>          
> gcc/config/aarch64/aarch64-acle-builtins.cc:function_builder::get_name.
> 
> I'd appreciate if there are better ways of doing this, or perhaps other
> intrinsics which follow this pattern which I've missed out on.

Hi Ezra,

I've taken a look and couldn't find any intrinsics with several unrelated
groupings either.  I'm not a reviewer, but see some ideas on how to handle
this below.

> 
> 2) In the tests, since some of these intrinsics don't have an overloaded
>    form, a few of them have the same code repeated:
> 
> TEST_XN_SINGLE (luti6_zt_s8_test, svint8_t, svuint8_t, z1,
>               svluti6_zt_s8 (0, z0), svluti6_zt_s8 (0, z0))
> 
> I'm wondering if this is OK? Or should I write new macros for these
> intrinsics which only have 1 form?

I think it's quite all right since these tests serve to verify that there
are no hidden parameters in the C intrinsics compared to the C++
overloads.

> 
> Ezra
> 
> Changes from v2:
>       * (define_insn "@aarch64_sme_luti6<mode>") register operands corrected.
>       * SVE_FULL_BIx2 mode iterator added.
> 
> gcc/ChangeLog:
>       * config/aarch64/aarch64-acle-builtins.cc
>       (function_builder::get_name): Add case for svluti6_lane names.
>       * config/aarch64/aarch64-acle-builtins.h
>       (TYPES_h_unsigned): New data type.
>       (TYPES_h_signed): Likewise.
>       (b_data): Likewise.
>       (groups_x23): New group suffix.
>       * config/aarch64/aarch64-sme.md
>       (@aarch64_sme_luti6<mode>): New insn.
>       (aarch64_sme_luti6_ztvnx16qi): Likewise.
>       (aarch64_sme_luti6_ztvnx64qi): Likewise.
>       * config/aarch64/aarch64-sve-builtins-shapes.cc
>       (parse_type): Add 'm', 'p' options for vectors per tuple minus/plus one.
>       (luti6_def): New class.
>       (luti6_no_lane_def): Likewise.
>       (luti6_lane_sme_def): Likewise.
>       (luti6_table_zt_def): Likewise.
>       (luti6_single_zt_def): Likewise.
>       * config/aarch64/aarch64-sve-builtins-shapes.h
>       (luti6, luti6_no_lane, luti6_lane_sme, luti6_single_zt)
>       (luti6_table_zt): Declare new shapes.
>       * config/aarch64/aarch64-sve-builtins-sme.cc
>       (svluti6_lane_impl, svluti6_zt_impl): New classes.
>       (svluti6_lane_s16_x4, svluti6_lane_u16_x4, svluti6_lane_f16_x4)
>       (svluti6_lane_bf16_x4, svluti6_zt): New function entries.
>       * config/aarch64/aarch64-sve-builtins-sme.def
>       (svluti6_lane_s16_x4, svluti6_lane_u16_x4, svluti6_lane_f16_x4)
>       (svluti6_lane_bf16_x4, svluti6_zt): Define new SME functions.
>       * config/aarch64/aarch64-sve-builtins-sme.h
>       (svluti6_lane_s16_x4, svluti6_lane_u16_x4, svluti6_lane_f16_x4)
>       (svluti6_lane_bf16_x4, svluti6_zt): Declare new function base.
>       * config/aarch64/aarch64-sve-builtins-sve2.cc
>       (svluti_lane_impl): Add luti6 case.
>       (svluti6_impl): New class.
>       (svluti6_lane, svluti6): New function entries.
>       * config/aarch64/aarch64-sve-builtins-sve2.def
>       (svluti6, svluti6_lane): Define new SVE functions.
>       * config/aarch64/aarch64-sve-builtins-sve2.h
>       (svluti6, svluti6_lane): Declare new function base.
>       * config/aarch64/aarch64-sve2.md
>       (@aarch64_sve_luti6<mode>): New pattern.
>       (@aarch64_sve_luti6_no_lane<mode>): Likewise.
>       * config/aarch64/iterators.md
>       (SVE_FULL_BIx2): New mode iterator.
> 
> gcc/testsuite/ChangeLog:
>       * gcc.target/aarch64/sme2/acle-asm/luti6_lane.c: New test.
>       * gcc.target/aarch64/sme2/acle-asm/luti6_zt.c: New test.
>       * gcc.target/aarch64/sve2/acle/asm/luti6.c: New test.
>       * gcc.target/aarch64/sve2/acle/asm/luti6_lane.c: New test.
> ---
>  gcc/config/aarch64/aarch64-acle-builtins.cc   |  10 +
>  gcc/config/aarch64/aarch64-acle-builtins.h    |  15 ++
>  gcc/config/aarch64/aarch64-sme.md             |  51 +++++
>  .../aarch64/aarch64-sve-builtins-shapes.cc    | 208 +++++++++++++++++-
>  .../aarch64/aarch64-sve-builtins-shapes.h     |   5 +
>  .../aarch64/aarch64-sve-builtins-sme.cc       |  58 +++++
>  .../aarch64/aarch64-sve-builtins-sme.def      |   9 +
>  gcc/config/aarch64/aarch64-sve-builtins-sme.h |   5 +
>  .../aarch64/aarch64-sve-builtins-sve2.cc      |  14 ++
>  .../aarch64/aarch64-sve-builtins-sve2.def     |   5 +
>  .../aarch64/aarch64-sve-builtins-sve2.h       |   2 +
>  gcc/config/aarch64/aarch64-sve2.md            |  24 ++
>  gcc/config/aarch64/iterators.md               |   3 +
>  .../aarch64/sme2/acle-asm/luti6_lane.c        | 183 +++++++++++++++
>  .../aarch64/sme2/acle-asm/luti6_zt.c          |  55 +++++
>  .../gcc.target/aarch64/sve2/acle/asm/luti6.c  |  40 ++++
>  .../aarch64/sve2/acle/asm/luti6_lane.c        |  60 +++++
>  17 files changed, 746 insertions(+), 1 deletion(-)
>  create mode 100644 
> gcc/testsuite/gcc.target/aarch64/sme2/acle-asm/luti6_lane.c
>  create mode 100644 gcc/testsuite/gcc.target/aarch64/sme2/acle-asm/luti6_zt.c
>  create mode 100644 gcc/testsuite/gcc.target/aarch64/sve2/acle/asm/luti6.c
>  create mode 100644 
> gcc/testsuite/gcc.target/aarch64/sve2/acle/asm/luti6_lane.c
> 
> diff --git a/gcc/config/aarch64/aarch64-acle-builtins.cc 
> b/gcc/config/aarch64/aarch64-acle-builtins.cc
> index b1a25d5cc4ed..868eeed77743 100644
> --- a/gcc/config/aarch64/aarch64-acle-builtins.cc
> +++ b/gcc/config/aarch64/aarch64-acle-builtins.cc
> @@ -639,6 +639,16 @@ function_builder::get_name (const function_instance 
> &instance,
>    for (unsigned int i = 0; i < 3; ++i)
>      if (!overloaded_p || instance.shape->explicit_type_suffix_p (i))
>        append_name (instance.type_suffix (i).string);
> +
> +  /* The non-overloaded SME LUTI6 lane forms have names like
> +     svluti6_lane_<t>_x4_<t>_x2_u8_x2 and svluti6_lane_<t>_x4_<t>_x2_u8_x3.
> +
> +     The current naming machinery adds the table type <t> and the final 
> _x2/_x3
> +     but has no way to express the table group suffix (e.g. <t>_x2) nor the
> +     indices type (fixed u8) so these are manually done here.  */
> +  if (!overloaded_p && instance.shape == shapes::luti6_lane_sme)
> +    append_name ("_x2_u8");
> +
>    if (!overloaded_p || instance.shape->explicit_group_suffix_p ())
>      append_name (instance.group_suffix ().string);
>    append_name (pred_suffixes[instance.pred]);
> diff --git a/gcc/config/aarch64/aarch64-acle-builtins.h 
> b/gcc/config/aarch64/aarch64-acle-builtins.h
> index 128df8a92936..da57cb4fe7e7 100644
> --- a/gcc/config/aarch64/aarch64-acle-builtins.h
> +++ b/gcc/config/aarch64/aarch64-acle-builtins.h
> @@ -1322,6 +1322,14 @@ function_expander::result_mode () const
>  #define TYPES_h_float(S, D, T) \
>    S (f16)
>  
> +/* _u16.  */
> +#define TYPES_h_unsigned(S, D, T) \
> +  S (u16)
> +
> +/* _s16.  */
> +#define TYPES_h_signed(S, D, T) \
> +  S (s16)
> +
>  /* _s16
>     _u16.  */
>  #define TYPES_h_integer(S, D, T) \
> @@ -2063,6 +2071,7 @@ DEF_SVE_TYPES_ARRAY (all_arith);
>  DEF_SVE_TYPES_ARRAY (all_arith_no_fp16);
>  DEF_SVE_TYPES_ARRAY (all_data);
>  DEF_SVE_TYPES_ARRAY (b);
> +DEF_SVE_TYPES_ARRAY (b_data);
>  DEF_SVE_TYPES_ARRAY (b_unsigned);
>  DEF_SVE_TYPES_ARRAY (b_integer);
>  DEF_SVE_TYPES_ARRAY (bh_integer);
> @@ -2079,6 +2088,8 @@ DEF_SVE_TYPES_ARRAY (h_float);
>  DEF_SVE_TYPES_ARRAY (h_float_mf8);
>  DEF_SVE_TYPES_ARRAY (h_integer);
>  DEF_SVE_TYPES_ARRAY (h_data);
> +DEF_SVE_TYPES_ARRAY (h_unsigned);
> +DEF_SVE_TYPES_ARRAY (h_signed);
>  DEF_SVE_TYPES_ARRAY (hs_signed);
>  DEF_SVE_TYPES_ARRAY (hs_integer);
>  DEF_SVE_TYPES_ARRAY (hs_float);
> @@ -2203,6 +2214,10 @@ static const group_suffix_index groups_x12[] = {
>  
>  static const group_suffix_index groups_x4[] = { GROUP_x4, NUM_GROUP_SUFFIXES 
> };
>  
> +static const group_suffix_index groups_x23[] = {
> +  GROUP_x2, GROUP_x3, NUM_GROUP_SUFFIXES
> +};
> +
>  static const group_suffix_index groups_x24[] = {
>    GROUP_x2, GROUP_x4, NUM_GROUP_SUFFIXES
>  };
> diff --git a/gcc/config/aarch64/aarch64-sme.md 
> b/gcc/config/aarch64/aarch64-sme.md
> index 9685824d22ea..609f9291c33b 100644
> --- a/gcc/config/aarch64/aarch64-sme.md
> +++ b/gcc/config/aarch64/aarch64-sme.md
> @@ -2880,6 +2880,7 @@
>  ;; Includes:
>  ;; - LUTI2 (SME2)
>  ;; - LUTI4 (SME2)
> +;; - LUTI6 (SME2p3)
>  ;; -------------------------------------------------------------------------
>  
>  (define_c_enum "unspec" [
> @@ -2928,3 +2929,53 @@
>    "TARGET_SME_LUTv2 && TARGET_STREAMING"
>    "luti4\t%0, zt0, {%Z1 - %T1}"
>  )
> +
> +;; LUTI6 (vector, 16-bit)
> +;; Variants are also available for: _u16, _f16 and _bf16.
> +;; svint16x4_t svluti6_lane_s16_x4_s16_x2_u8_x2 (svint16x2_t table,
> +;;                                               svuint8x2_t indices,
> +;;                                               uint64_t imm_idx) 
> __arm_streaming;
> +
> +(define_insn "@aarch64_sme_luti6<mode>"
> +  [(set (match_operand:VNx32HI 0 "aligned_register_operand" "=Uw4")
> +     (unspec:VNx32HI
> +      [(match_operand:SVE_FULL_Hx2 1 "register_operand" "w")
> +       (match_operand:VNx32QI  2 "register_operand" "w")
> +       (match_operand:DI 3 "const_int_operand")]
> +       UNSPEC_SME_LUTI))]
> +  "TARGET_SME2p3 && TARGET_STREAMING"
> +  "luti6\t%0, %1, {%Z2 - %T2}[%3]"
> +  [(set_attr "sve_type" "sve_int_general")]
> +)
> +
> +;; LUTI6 (table, single, 8-bit)
> +;; Variants are also available for: _u8 and _mf8.
> +;; svint8_t svluti6_zt_s8(uint64_t zt0, svuint8_t zn)
> +;;         __arm_streaming __arm_in("zt0");
> +
> +(define_insn "aarch64_sme_luti6_ztvnx16qi"
> +  [(set (match_operand:VNx16QI 0 "register_operand" "=w")
> +     (unspec:VNx16QI
> +       [(reg:V8DI ZT0_REGNUM)
> +        (reg:DI SME_STATE_REGNUM)
> +        (match_operand:VNx16QI 1 "register_operand" "w")]
> +       UNSPEC_SME_LUTI_ZT))]
> +  "TARGET_SME2p3 && TARGET_STREAMING"
> +  "luti6\t%0.b, zt0, %Z1"
> +)
> +
> +;; LUTI6 (table, four registers, 8-bit)
> +;; Variants are also available for: _u8 and _mf8.
> +;; svint8x4_t svluti6_zt_s8_x4(uint64_t zt0, svuint8x3_t zn)
> +;;         __arm_streaming __arm_in ("zt0");
> +
> +(define_insn "aarch64_sme_luti6_ztvnx64qi"
> +  [(set (match_operand:VNx64QI 0 "aligned_register_operand" "=Uw4")
> +     (unspec:VNx64QI
> +       [(reg:V8DI ZT0_REGNUM)
> +        (reg:DI SME_STATE_REGNUM)
> +        (match_operand:VNx48QI 1 "register_operand" "w")]
> +       UNSPEC_SME_LUTI_ZT))]
> +  "TARGET_SME2p3 && TARGET_STREAMING"
> +  "luti6\t%0, zt0, {%Z1 - %U1}"

You could merge this with the existing single-vector LUTI4 pattern
("aarch64_sme_lut_zt") and drive it via a new iterator containing VNx32QI
and VNx48QI.

> +)
> diff --git a/gcc/config/aarch64/aarch64-sve-builtins-shapes.cc 
> b/gcc/config/aarch64/aarch64-sve-builtins-shapes.cc
> index af12db249d8d..bc8ad559fe72 100644
> --- a/gcc/config/aarch64/aarch64-sve-builtins-shapes.cc
> +++ b/gcc/config/aarch64/aarch64-sve-builtins-shapes.cc
> @@ -202,6 +202,8 @@ parse_element_type (const function_instance &instance, 
> const char *&format)
>     t<elt>  - a vector or tuple type with given element suffix [*1]
>     T<elt>  - a vector or tuple type with given element suffix [*2]
>     u<elt>  - a vector or tuple type with given element suffix [*3]
> +   m<elt>  - a vector or tuple type with given element suffix [*4]
> +   p<elt>  - a vector or tuple type with given element suffix [*5]
>     v<elt>  - a vector with the given element suffix
>     D<elt>  - a 64 bit neon vector
>     Q<elt>  - a 128 bit neon vector
> @@ -214,6 +216,8 @@ parse_element_type (const function_instance &instance, 
> const char *&format)
>       be a tuple, and if so, how many vectors it should contain.
>     [*2] same as for [*1], but the tuple contains half as many vectors.
>     [*3] same as for [*1], but the tuple contains twice as many vectors.
> +   [*4] same as for [*1], but the tuple contains one fewer vector.
> +   [*5] same as for [*1], but the tuple contains one more vector.
>  */
>  static tree
>  parse_type (const function_instance &instance, const char *&format)
> @@ -317,6 +321,24 @@ parse_type (const function_instance &instance, const 
> char *&format)
>        return acle_vector_types[num_vectors - 1][vector_type];
>      }
>  
> +  if (ch == 'm')
> +    {
> +      type_suffix_index suffix = parse_element_type (instance, format);
> +      vector_type_index vector_type = type_suffixes[suffix].vector_type;
> +      unsigned int num_vectors = instance.vectors_per_tuple ();
> +      gcc_assert (num_vectors > 1);
> +      return acle_vector_types[num_vectors - 2][vector_type];
> +    }
> +
> +  if (ch == 'p')
> +    {
> +      type_suffix_index suffix = parse_element_type (instance, format);
> +      vector_type_index vector_type = type_suffixes[suffix].vector_type;
> +      unsigned int num_vectors = instance.vectors_per_tuple ();
> +      gcc_assert (num_vectors < 4);
> +      return acle_vector_types[num_vectors][vector_type];
> +    }
> +
>    if (ch == 'v')
>      {
>        type_suffix_index suffix = parse_element_type (instance, format);
> @@ -1003,13 +1025,45 @@ struct luti_base : public overloaded_base<0>
>    }
>  };
>  
> -/* Specializations for 2-bit and 4-bit indices.  */
> +/* Specializations for 2-bit, 4-bit and 6-bit indices.  */
>  using luti2_def = luti_base<2>;
>  SHAPE (luti2);
>  
>  using luti4_def = luti_base<4>;
>  SHAPE (luti4);
>  
> +using luti6_def = luti_base<6>;
> +SHAPE (luti6);
> +
> +/* sv<v0>_t svluti6[_<t0>](sv<t0>x2_t, svuint8_t)
> +
> +   note the final argument is not a constant index, the instruction divides
> +   the vector argument in BITS-bit quantities.  */
> +struct luti6_no_lane_def : public overloaded_base<0>
> +{
> +  bool explicit_group_suffix_p () const override { return false; }
> +
> +  void
> +  build (function_builder &b, const function_group_info &group) const 
> override
> +  {
> +    /* Format: return type, table vector, indices vector.  */
> +    b.add_overloaded_functions (group, MODE_none);
> +    build_all (b, "v0,t0,vu8", group, MODE_none);
> +  }
> +
> +  tree
> +  resolve (function_resolver &r) const override
> +  {
> +    sve_type type;
> +    if (!r.check_num_arguments (2)
> +     || !(type = r.infer_sve_type (0))
> +     || !r.require_vector_type (1, VECTOR_TYPE_svuint8_t))
> +      return error_mark_node;
> +
> +    return r.resolve_to (r.mode_suffix_id, type);
> +  }
> +};
> +SHAPE (luti6_no_lane);
>  
>  /* sv<t0>x<g>_t svfoo_t0_g(uint64_t, svuint8_t, uint64_t)
>  
> @@ -1052,6 +1106,158 @@ template <unsigned int BITS> struct luti_zt_base : 
> public nonoverloaded_base
>    }
>  };
>  
> +/* svint16x4_t svluti6_lane_s16_x4_<t0>_x2_u8_x2 (sv<t0>_x2_t table,
> +                                               svuint8x2_t indices,
> +                                               uint64_t imm_idx) 
> __arm_streaming;
> +   svint16x4_t svluti6_lane_s16_x4_<t0>_x2_u8_x3 (sv<t0>_x2_t table,
> +                                               svuint8x3_t indices,
> +                                               uint64_t imm_idx) 
> __arm_streaming;
> +*/
> +
> +struct luti6_lane_sme_def : public overloaded_base<0>
> +{
> +  bool explicit_group_suffix_p () const override { return false; }
> +
> +  void
> +  build (function_builder &b, const function_group_info &group) const 
> override
> +  {
> +    /* Format: return type, table vector, indices vector, immediate value.  
> */
> +    b.add_overloaded_functions (group, MODE_none);
> +    build_one (b, "u0,t0,tu8,su64", group, MODE_none, 0, 0, 0, false);
> +    build_one (b, "p0,m0,tu8,su64", group, MODE_none, 0, 1, 0, false);

Ah, so because groups = { x2, x3 }, the first signature will yield
"s16x4, s16x2, u8x2, u64" and the second, "s16x4, s16x2, u8x3, u64".
Clever, but an indication that a long-term solution is required as those
groups are clearly independent.

> +  }
> +
> +  bool
> +  check (function_checker &c) const override
> +  {
> +    return c.require_immediate_range (2, 0, 1);
> +  }
> +
> +  tree
> +  resolve (function_resolver &r) const override
> +  {
> +    if (!r.check_num_arguments (3))
> +      return error_mark_node;
> +
> +    sve_type table = r.infer_sve_type (0);
> +    if (!table)
> +      return error_mark_node;
> +
> +    if (table.num_vectors != 2)
> +      {
> +     r.report_incorrect_num_vectors (0, table, 2);
> +     return error_mark_node;
> +      }
> +
> +    if (!r.require_scalar_type (2, "uint64_t"))
> +      return error_mark_node;
> +
> +    sve_type indices = r.infer_sve_type (1);
> +    if (!indices)
> +      return error_mark_node;
> +
> +    if (indices.num_vectors == 2)
> +      {
> +     if (!r.require_derived_vector_type (1, 0, table, TYPE_unsigned, 8, 2))
> +       return error_mark_node;
> +     return r.resolve_to (r.mode_suffix_id, table.type, NUM_TYPE_SUFFIXES,
> +                          NUM_TYPE_SUFFIXES, GROUP_x2);
> +      }
> +
> +    if (indices.num_vectors == 3)
> +      {
> +     if (!r.require_derived_vector_type (1, 0, table, TYPE_unsigned, 8, 3))
> +       return error_mark_node;
> +     return r.resolve_to (r.mode_suffix_id, table.type, NUM_TYPE_SUFFIXES,
> +                          NUM_TYPE_SUFFIXES, GROUP_x3);
> +      }

The two top-level "if"s above can be de-duplicated.

> +
> +    r.report_no_such_form (indices);
> +    return error_mark_node;
> +  }
> +};
> +
> +SHAPE (luti6_lane_sme);
> +
> +/* LUTI6 (table, four registers, 8-bit)
> +     Variants are also available for: _u8 and _mf8.
> +  svint8x4_t svluti6_zt_s8_x4(uint64_t zt0, svuint8x3_t zn)
> +  __arm_streaming __arm_in("zt0"); */
> +struct luti6_table_zt_def : public nonoverloaded_base
> +{
> +  void build (function_builder &b,
> +           const function_group_info &group) const override
> +  {
> +    build_all (b, "t0,su64,mu8", group, MODE_none);
> +  }
> +
> +  bool check (function_checker &c) const override
> +  {
> +    return c.require_immediate_range (0, 0, 0);
> +  }
> +
> +  tree
> +  resolve (function_resolver &r) const override
> +  {
> +    if (!r.check_num_arguments (2))
> +      return error_mark_node;
> +
> +    if (!r.require_scalar_type (0, "uint64_t"))
> +      return error_mark_node;
> +
> +    sve_type type = r.infer_sve_type (1);
> +    if (!type)
> +      return error_mark_node;
> +
> +    if (type.num_vectors != 3)
> +      {
> +     r.report_incorrect_num_vectors (1, type, 3);
> +     return error_mark_node;
> +      }
> +
> +    return r.resolve_to (r.mode_suffix_id, type);
> +  }
> +};
> +
> +SHAPE (luti6_table_zt);
> +
> +struct luti6_single_zt_def : public nonoverloaded_base
> +{
> +  void build (function_builder &b,
> +           const function_group_info &group) const override
> +  {
> +    build_all (b, "v0,su64,vu8", group, MODE_none);
> +  }
> +
> +  bool check (function_checker &c) const override
> +  {
> +    return c.require_immediate_range (0, 0, 0);
> +  }
> +
> +  tree
> +  resolve (function_resolver &r) const override
> +  {
> +    if (!r.check_num_arguments (2))
> +      return error_mark_node;
> +
> +    if (!r.require_scalar_type (0, "uint64_t"))
> +      return error_mark_node;
> +
> +    sve_type type = r.infer_sve_type (1);
> +    if (!type)
> +      return error_mark_node;
> +
> +    if (type.num_vectors != 1)
> +      {
> +     r.report_incorrect_num_vectors (1, type, 1);
> +     return error_mark_node;
> +      }
> +
> +    return r.resolve_to (r.mode_suffix_id, type);
> +  }
> +};
> +SHAPE (luti6_single_zt);

There's a lot of code duplication between luti6_{table,single}_zt_def as
well as the pre-existing luti_zt_base; do you think they can all be merged
into a single class compile-time parameterized by number of bits and
expected number of vectors in the second operand?

> +
>  /* sv<t0>_t svfoo[_t0](sv<t0>_t, sv<t0:quarter>_t,
>                      sv<t0:quarter>_t)  (for integer t0)
>     sv<t0>_t svmmla[_t0](sv<t0>_t, sv<t0>_t, sv<t0>_t)  (for floating-point 
> t0)
> diff --git a/gcc/config/aarch64/aarch64-sve-builtins-shapes.h 
> b/gcc/config/aarch64/aarch64-sve-builtins-shapes.h
> index c7c8b300c8ea..d977f0e9b7e4 100644
> --- a/gcc/config/aarch64/aarch64-sve-builtins-shapes.h
> +++ b/gcc/config/aarch64/aarch64-sve-builtins-shapes.h
> @@ -169,9 +169,14 @@ namespace aarch64_acle
>      extern const function_shape *const load_za;
>      extern const function_shape *const luti2;
>      extern const function_shape *const luti4;
> +    extern const function_shape *const luti6;
> +    extern const function_shape *const luti6_no_lane;
>      extern const function_shape *const luti2_lane_zt;
>      extern const function_shape *const luti4_lane_zt;
>      extern const function_shape *const luti4_zt;
> +    extern const function_shape *const luti6_lane_sme;
> +    extern const function_shape *const luti6_single_zt;
> +    extern const function_shape *const luti6_table_zt;
>      extern const function_shape *const mmla;
>      extern const function_shape *const mop4;
>      extern const function_shape *const pattern_pred;
> diff --git a/gcc/config/aarch64/aarch64-sve-builtins-sme.cc 
> b/gcc/config/aarch64/aarch64-sve-builtins-sme.cc
> index 0d36d16fb020..203cc5c42791 100644
> --- a/gcc/config/aarch64/aarch64-sve-builtins-sme.cc
> +++ b/gcc/config/aarch64/aarch64-sve-builtins-sme.cc
> @@ -376,6 +376,59 @@ public:
>    unsigned int m_bits;
>  };
>  
> +class svluti6_lane_impl : public function_base
> +{
> +public:
> +  rtx
> +  expand (function_expander &e) const override
> +  {
> +    if (e.group_suffix_id == aarch64_acle::GROUP_x3)
> +      {
> +     rtx vecs[2];
> +     for (int i = 0; i < 2; i++)
> +       {
> +         vecs[i] = force_subreg (
> +             E_VNx16QImode, e.args[1], GET_MODE (e.args[1]),
> +             (i + INTVAL (e.args[2])) * BYTES_PER_SVE_VECTOR);
> +       }

Can you add a comment on what's going on please?  I can see that the _x2
and the _x3 versions of the instrinsic are backed by the same RTL
pattern, but what is the difference between them and why is this
x3 -> x2 conversion needed?

> +
> +     rtx tuple = gen_reg_rtx (E_VNx32QImode);
> +     for (int i = 0; i < 2; i++)
> +       emit_move_insn (simplify_gen_subreg (E_VNx16QImode, tuple,
> +                                            E_VNx32QImode,
> +                                            i * BYTES_PER_SVE_VECTOR),
> +                       vecs[i]);
> +     e.args[1] = tuple;
> +      }
> +
> +    machine_mode mode = GET_MODE (e.args[0]);
> +    rtx res = e.use_exact_insn (code_for_aarch64_sme_luti6 (mode));
> +    return aarch64_sve_reinterpret (e.result_mode (), res);
> +  }
> +};
> +
> +class svluti6_zt_impl : public read_zt0<function_base>

You should be able to use the already existing svluti_zt_impl class.

> +{
> +public:
> +  unsigned int
> +  call_properties (const function_instance &) const override
> +  {
> +    return CP_READ_ZT0;
> +  }
> +
> +  rtx
> +  expand (function_expander &e) const override
> +  {
> +    // Remove `zt0` argument, since it is ignored.
> +    e.args.ordered_remove (0);
> +    insn_code icode = e.vectors_per_tuple () == 4
> +                       ? CODE_FOR_aarch64_sme_luti6_ztvnx64qi
> +                       : CODE_FOR_aarch64_sme_luti6_ztvnx16qi;
> +    return e.use_exact_insn (icode);
> +  }
> +};
> +
> +
>  template<insn_code (*CODE) (machine_mode)>
>  class svread_za_slice_base : public function_base
>  {
> @@ -641,6 +694,11 @@ FUNCTION (svldr_zt, svldr_zt_impl, )
>  FUNCTION (svluti2_lane_zt, svluti_lane_zt_impl, (2))
>  FUNCTION (svluti4_lane_zt, svluti_lane_zt_impl, (4))
>  FUNCTION (svluti4_zt, svluti_zt_impl, (4))
> +FUNCTION (svluti6_lane_s16_x4, svluti6_lane_impl,)
> +FUNCTION (svluti6_lane_u16_x4, svluti6_lane_impl,)
> +FUNCTION (svluti6_lane_f16_x4, svluti6_lane_impl,)
> +FUNCTION (svluti6_lane_bf16_x4, svluti6_lane_impl,)
> +FUNCTION (svluti6_zt, svluti6_zt_impl,)
>  FUNCTION (svmla_za, sme_2mode_function, (UNSPEC_SME_SMLA, UNSPEC_SME_UMLA,
>                                        UNSPEC_SME_FMLA, UNSPEC_SME_FMLAL))
>  FUNCTION (svmla_lane_za, sme_2mode_lane_function, (UNSPEC_SME_SMLA,
> diff --git a/gcc/config/aarch64/aarch64-sve-builtins-sme.def 
> b/gcc/config/aarch64/aarch64-sve-builtins-sme.def
> index 9aaca87e1f80..46876e211c2e 100644
> --- a/gcc/config/aarch64/aarch64-sve-builtins-sme.def
> +++ b/gcc/config/aarch64/aarch64-sve-builtins-sme.def
> @@ -297,6 +297,15 @@ DEF_SME_FUNCTION_GS (svwrite_lane_zt, write_lane_zt, 
> all_data,  none, none)
>  DEF_SME_FUNCTION_GS (svluti4_zt,      luti4_zt,      b_integer, x4,   none)
>  #undef REQUIRED_EXTENSIONS
>  
> +#define REQUIRED_EXTENSIONS streaming_only (AARCH64_FL_SME2p3)
> +DEF_SME_FUNCTION_GS (svluti6_lane_s16_x4, luti6_lane_sme, h_signed, x23, 
> none)

Unfortunately, it looks to me that these new instrinsics require generic support
for the situation where there can be multiple unrelated group sets in the
signature.  Do you think it would be possible to implement something akin
to what we now have for types (TYPES_*, DEF_SVE_TYPE_ARRAY etc.) and then
just do something like:

#define GROUPS_luti6_lane(S, D, T) \
  T (x4, x2, x23)

#define TYPES_luti6_lane(S, D, T) \
  D (u16, u8), D (s16, u8), D (f16, u8), D (bf16, u8)

and

DEF_SME_FUNCTION_GS (svluti6_lane, luti6_lane_sme, luti6_lane, luti6_lane, none)

?

> +DEF_SME_FUNCTION_GS (svluti6_lane_u16_x4, luti6_lane_sme, h_unsigned, x23, 
> none)
> +DEF_SME_FUNCTION_GS (svluti6_lane_f16_x4, luti6_lane_sme, h_float, x23, none)
> +DEF_SME_FUNCTION_GS (svluti6_lane_bf16_x4, luti6_lane_sme, h_bfloat, x23, 
> none)
> +DEF_SME_FUNCTION_GS (svluti6_zt, luti6_single_zt, b_data, none, none)
> +DEF_SME_FUNCTION_GS (svluti6_zt, luti6_table_zt, b_data, x4, none)
> +#undef REQUIRED_EXTENSIONS
> +
>  #define REQUIRED_EXTENSIONS streaming_only (AARCH64_FL_SME_F8F16)
>  DEF_SME_ZA_FUNCTION_GS_FPM (svmla_lane, binary_za_slice_lane, za_h_mf8,
>                           vg2, none, set)
> diff --git a/gcc/config/aarch64/aarch64-sve-builtins-sme.h 
> b/gcc/config/aarch64/aarch64-sve-builtins-sme.h
> index d9166b8d0bc4..e74de6aefd2e 100644
> --- a/gcc/config/aarch64/aarch64-sve-builtins-sme.h
> +++ b/gcc/config/aarch64/aarch64-sve-builtins-sme.h
> @@ -47,6 +47,11 @@ namespace aarch64_acle
>      extern const function_base *const svluti2_lane_zt;
>      extern const function_base *const svluti4_lane_zt;
>      extern const function_base *const svluti4_zt;
> +    extern const function_base *const svluti6_zt;
> +    extern const function_base *const svluti6_lane_s16_x4;
> +    extern const function_base *const svluti6_lane_u16_x4;
> +    extern const function_base *const svluti6_lane_f16_x4;
> +    extern const function_base *const svluti6_lane_bf16_x4;
>      extern const function_base *const svmla_za;
>      extern const function_base *const svmla_lane_za;
>      extern const function_base *const svmls_za;
> diff --git a/gcc/config/aarch64/aarch64-sve-builtins-sve2.cc 
> b/gcc/config/aarch64/aarch64-sve-builtins-sve2.cc
> index 87c7f0319925..d1496820b2bf 100644
> --- a/gcc/config/aarch64/aarch64-sve-builtins-sve2.cc
> +++ b/gcc/config/aarch64/aarch64-sve-builtins-sve2.cc
> @@ -1004,12 +1004,24 @@ public:
>    rtx expand (function_expander &e) const override
>    {
>      auto mode = e.tuple_mode (0);
> +    if (m_bits == 6)
> +      return e.use_exact_insn (code_for_aarch64_sve_luti6 (mode));
>      return e.use_exact_insn (code_for_aarch64_sve_luti (m_bits, mode));

Here as with FMMLA, looks like this should work if we use an iterator to
form the RTL pattern name.

>    }
>  
>    unsigned int m_bits;
>  };
>  
> +class svluti6_impl : public function_base
> +{
> +public:
> +  rtx expand (function_expander &e) const override
> +  {
> +    auto mode = e.tuple_mode (0);
> +    return e.use_exact_insn (code_for_aarch64_sve_luti6_no_lane (mode));
> +  }
> +};
> +
>  /* The same as cond_or_uncond_unspec_function but the intrinsics with vector
>     modes are SME2 extensions instead of SVE.  */
>  class faminmaximpl : public function_base
> @@ -1351,5 +1363,7 @@ FUNCTION (svzipq1, svzipq_impl, (0))
>  FUNCTION (svzipq2, svzipq_impl, (1))
>  FUNCTION (svluti2_lane, svluti_lane_impl, (2))
>  FUNCTION (svluti4_lane, svluti_lane_impl, (4))
> +FUNCTION (svluti6_lane, svluti_lane_impl, (6))
> +FUNCTION (svluti6, svluti6_impl,)
>  
>  }
> diff --git a/gcc/config/aarch64/aarch64-sve-builtins-sve2.def 
> b/gcc/config/aarch64/aarch64-sve-builtins-sve2.def
> index 8b5340f4e30a..bf5030109bcd 100644
> --- a/gcc/config/aarch64/aarch64-sve-builtins-sve2.def
> +++ b/gcc/config/aarch64/aarch64-sve-builtins-sve2.def
> @@ -397,6 +397,11 @@ DEF_SVE_FUNCTION (svluti4_lane, luti4, bh_data, none)
>  DEF_SVE_FUNCTION_GS (svluti4_lane, luti4, h_data, x2, none)
>  #undef REQUIRED_EXTENSIONS
>  
> +#define REQUIRED_EXTENSIONS sve_and_sme (AARCH64_FL_SVE2p3, 
> AARCH64_FL_SME2p3)
> +DEF_SVE_FUNCTION_GS (svluti6, luti6_no_lane, b_data, x2, none)

The "no_lane" naming feels a bit inconsistent; could you please rename
the existing shapes (luti2 and luti4) to luti{2,4}_lane instead for
alignment with the rest of them?

> +DEF_SVE_FUNCTION_GS (svluti6_lane, luti6, h_data, x2, none)
> +#undef REQUIRED_EXTENSIONS
> +
>  #define REQUIRED_EXTENSIONS streaming_only (AARCH64_FL_SME_F16F16)
>  DEF_SVE_FUNCTION_GS (svcvt, unary_convertxn, cvt_f32_f16, x2, none)
>  DEF_SVE_FUNCTION_GS (svcvtl, unary_convertxn, cvt_f32_f16, x2, none)
> diff --git a/gcc/config/aarch64/aarch64-sve-builtins-sve2.h 
> b/gcc/config/aarch64/aarch64-sve-builtins-sve2.h
> index b6ddba43f229..30bd68c479d7 100644
> --- a/gcc/config/aarch64/aarch64-sve-builtins-sve2.h
> +++ b/gcc/config/aarch64/aarch64-sve-builtins-sve2.h
> @@ -275,6 +275,8 @@ namespace aarch64_acle
>      extern const function_base *const svzipq2;
>      extern const function_base *const svluti2_lane;
>      extern const function_base *const svluti4_lane;
> +    extern const function_base *const svluti6;
> +    extern const function_base *const svluti6_lane;
>    }
>  }
>  
> diff --git a/gcc/config/aarch64/aarch64-sve2.md 
> b/gcc/config/aarch64/aarch64-sve2.md
> index 0e11347098db..6b50aef0234d 100644
> --- a/gcc/config/aarch64/aarch64-sve2.md
> +++ b/gcc/config/aarch64/aarch64-sve2.md
> @@ -4878,6 +4878,7 @@
>  ;; Includes:
>  ;; - LUTI2
>  ;; - LUTI4
> +;; - LUTI6 (SVE2p3)
>  ;; -------------------------------------------------------------------------
>  
>  (define_insn "@aarch64_sve_luti<LUTI_BITS><mode>"
> @@ -4906,6 +4907,29 @@
>    [(set_attr "sve_type" "sve_int_general")]
>  )
>  
> +(define_insn "@aarch64_sve_luti6<mode>"
> +  [(set (match_operand:<VSINGLE> 0 "register_operand" "=w")
> +     (unspec:<VSINGLE>
> +      [(match_operand:SVE_FULL_Hx2 1 "register_operand" "w")
> +       (match_operand:VNx16QI 2 "register_operand" "w")
> +       (match_operand:DI 3 "const_int_operand")]
> +       UNSPEC_SVE_LUTI))]
> +  "TARGET_SVE2p3_OR_SME2p3"
> +  "luti6\t%0.<Vetype>, %1, %2[%3]"
> +  [(set_attr "sve_type" "sve_int_general")]
> +)
> +
> +(define_insn "@aarch64_sve_luti6_no_lane<mode>"
> +  [(set (match_operand:<VSINGLE> 0 "register_operand" "=w")
> +     (unspec:<VSINGLE>
> +      [(match_operand:SVE_FULL_BIx2 1 "register_operand" "w")
> +       (match_operand:VNx16QI 2 "register_operand" "w")]
> +       UNSPEC_SVE_LUTI))]
> +  "TARGET_SVE2p3_OR_SME2p3"
> +  "luti6\t%0.<Vetype>, %1, %2"
> +  [(set_attr "sve_type" "sve_int_general")]

Here as well, I think the first pattern should be called
"@aarch64_sve_luti6_lane<mode>" and the second "@aarch64_sve_luti6<mode>",
which probably means that the other LUTI patterns will also need to be
renamed for consistency.

> +)
> +
>  ;; =========================================================================
>  ;; == Cryptographic extensions
>  ;; =========================================================================
> diff --git a/gcc/config/aarch64/iterators.md b/gcc/config/aarch64/iterators.md
> index ef31f513d08a..681f805f5e51 100644
> --- a/gcc/config/aarch64/iterators.md
> +++ b/gcc/config/aarch64/iterators.md
> @@ -575,6 +575,9 @@
>  ;; Fully-packed SVE integer vector modes that have 8-bit or 16-bit elements.
>  (define_mode_iterator SVE_FULL_BHI [VNx16QI VNx8HI])
>  
> +;; {u8, s8, mf8}x2
> +(define_mode_iterator SVE_FULL_BIx2 [VNx32QI])
> +
>  ;; Fully-packed SVE integer vector modes that have 8-bit, 16-bit or 32-bit
>  ;; elements.
>  (define_mode_iterator SVE_FULL_BHSI [VNx16QI VNx8HI VNx4SI])
> diff --git a/gcc/testsuite/gcc.target/aarch64/sme2/acle-asm/luti6_lane.c 
> b/gcc/testsuite/gcc.target/aarch64/sme2/acle-asm/luti6_lane.c
> new file mode 100644
> index 000000000000..5fe03ba8db3a

A general comment regarding all the tests: it is customary to split tests
into separate files by varying type(s) and group multiplier(s).  I
recommend using the structure of sme2/acle-asm/luti4_* files as your
starting point for both how to do the split as well as inspiration for
some more tests.

This should be enough food for thought for now,
Thanks,
Artemiy

> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/aarch64/sme2/acle-asm/luti6_lane.c
> @@ -0,0 +1,183 @@
> +/* { dg-do assemble { target aarch64_asm_sme2p3_ok } } */
> +/* { dg-do compile { target { ! aarch64_asm_sme2p3_ok } } } */
> +/* { dg-final { check-function-bodies "**" "" "-DCHECK_ASM" } }  */
> +
> +#pragma GCC target "+sme2p3"
> +#define SHARED_ZT0
> +#include "test_sme2_acle.h"
> +
> +/*
> +** luti6_lane_s16_x4_u8_x2_min_test:
> +** luti6     \{z0\.h - z3\.h\}, \{z16\.h - z17\.h\}, \{z22 - z23\}\[0\]
> +** ret
> +*/
> +TEST_1X2_NARROW (luti6_lane_s16_x4_u8_x2_min_test, svint16x4_t, svint16x2_t,
> +              svuint8x2_t,
> +              z0 = svluti6_lane_s16_x4_s16_x2_u8_x2 (z16, z22, 0),
> +              z0 = svluti6_lane_s16_x4 (z16, z22, 0))
> +
> +/*
> +** luti6_lane_s16_x4_u8_x2_max_test:
> +** luti6     \{z0\.h - z3\.h\}, \{z16\.h - z17\.h\}, \{z22 - z23\}\[1\]
> +** ret
> +*/
> +TEST_1X2_NARROW (luti6_lane_s16_x4_u8_x2_max_test, svint16x4_t, svint16x2_t,
> +              svuint8x2_t,
> +              z0 = svluti6_lane_s16_x4_s16_x2_u8_x2 (z16, z22, 1),
> +              z0 = svluti6_lane_s16_x4 (z16, z22, 1))
> +
> +/*
> +** luti6_lane_u16_x4_u8_x2_min_test:
> +** luti6     \{z0\.h - z3\.h\}, \{z16\.h - z17\.h\}, \{z22 - z23\}\[0\]
> +** ret
> +*/
> +TEST_1X2_NARROW (luti6_lane_u16_x4_u8_x2_min_test, svuint16x4_t, 
> svuint16x2_t,
> +              svuint8x2_t,
> +              z0 = svluti6_lane_u16_x4_u16_x2_u8_x2 (z16, z22, 0),
> +              z0 = svluti6_lane_u16_x4 (z16, z22, 0))
> +
> +/*
> +** luti6_lane_u16_x4_u8_x2_max_test:
> +** luti6     \{z0\.h - z3\.h\}, \{z16\.h - z17\.h\}, \{z22 - z23\}\[1\]
> +** ret
> +*/
> +TEST_1X2_NARROW (luti6_lane_u16_x4_u8_x2_max_test, svuint16x4_t, 
> svuint16x2_t,
> +              svuint8x2_t,
> +              z0 = svluti6_lane_u16_x4_u16_x2_u8_x2 (z16, z22, 1),
> +              z0 = svluti6_lane_u16_x4 (z16, z22, 1))
> +
> +/*
> +** luti6_lane_f16_x4_u8_x2_min_test:
> +** luti6     \{z0\.h - z3\.h\}, \{z16\.h - z17\.h\}, \{z22 - z23\}\[0\]
> +** ret
> +*/
> +TEST_1X2_NARROW (luti6_lane_f16_x4_u8_x2_min_test, svfloat16x4_t, 
> svfloat16x2_t,
> +              svuint8x2_t,
> +              z0 = svluti6_lane_f16_x4_f16_x2_u8_x2 (z16, z22, 0),
> +              z0 = svluti6_lane_f16_x4 (z16, z22, 0))
> +
> +/*
> +** luti6_lane_f16_x4_u8_x2_max_test:
> +** luti6     \{z0\.h - z3\.h\}, \{z16\.h - z17\.h\}, \{z22 - z23\}\[1\]
> +** ret
> +*/
> +TEST_1X2_NARROW (luti6_lane_f16_x4_u8_x2_max_test, svfloat16x4_t, 
> svfloat16x2_t,
> +              svuint8x2_t,
> +              z0 = svluti6_lane_f16_x4_f16_x2_u8_x2 (z16, z22, 1),
> +              z0 = svluti6_lane_f16_x4 (z16, z22, 1))
> +
> +/*
> +** luti6_lane_bf16_x4_u8_x2_min_test:
> +** luti6     \{z0\.h - z3\.h\}, \{z16\.h - z17\.h\}, \{z22 - z23\}\[0\]
> +** ret
> +*/
> +TEST_1X2_NARROW (luti6_lane_bf16_x4_u8_x2_min_test, svbfloat16x4_t, 
> svbfloat16x2_t,
> +              svuint8x2_t,
> +              z0 = svluti6_lane_bf16_x4_bf16_x2_u8_x2 (z16, z22, 0),
> +              z0 = svluti6_lane_bf16_x4 (z16, z22, 0))
> +
> +/*
> +** luti6_lane_bf16_x4_u8_x2_max_test:
> +** luti6     \{z0\.h - z3\.h\}, \{z16\.h - z17\.h\}, \{z22 - z23\}\[1\]
> +** ret
> +*/
> +TEST_1X2_NARROW (luti6_lane_bf16_x4_u8_x2_max_test, svbfloat16x4_t, 
> svbfloat16x2_t,
> +              svuint8x2_t,
> +              z0 = svluti6_lane_bf16_x4_bf16_x2_u8_x2 (z16, z22, 1),
> +              z0 = svluti6_lane_bf16_x4 (z16, z22, 1))
> +
> +/*
> +** luti6_lane_s16_x4_u8_x3_min_test:
> +** mov       z0\.d, z22\.d
> +** mov       z1\.d, z23\.d
> +** luti6     \{z0\.h - z3\.h\}, \{z16\.h - z17\.h\}, \{z0 - z1\}\[0\]
> +** ret
> +*/
> +TEST_1X2_NARROW (luti6_lane_s16_x4_u8_x3_min_test, svint16x4_t, svint16x2_t,
> +              svuint8x3_t,
> +              z0 = svluti6_lane_s16_x4_s16_x2_u8_x3 (z16, z22, 0),
> +              z0 = svluti6_lane_s16_x4 (z16, z22, 0))
> +
> +/*
> +** luti6_lane_s16_x4_u8_x3_max_test:
> +** mov       z0\.d, z23\.d
> +** mov       z1\.d, z24\.d
> +** luti6     \{z0\.h - z3\.h\}, \{z16\.h - z17\.h\}, \{z0 - z1\}\[1\]
> +** ret
> +*/
> +TEST_1X2_NARROW (luti6_lane_s16_x4_u8_x3_max_test, svint16x4_t, svint16x2_t,
> +              svuint8x3_t,
> +              z0 = svluti6_lane_s16_x4_s16_x2_u8_x3 (z16, z22, 1),
> +              z0 = svluti6_lane_s16_x4 (z16, z22, 1))
> +
> +/*
> +** luti6_lane_u16_x4_u8_x3_min_test:
> +** mov       z0\.d, z22\.d
> +** mov       z1\.d, z23\.d
> +** luti6     \{z0\.h - z3\.h\}, \{z16\.h - z17\.h\}, \{z0 - z1\}\[0\]
> +** ret
> +*/
> +TEST_1X2_NARROW (luti6_lane_u16_x4_u8_x3_min_test, svuint16x4_t, 
> svuint16x2_t,
> +              svuint8x3_t,
> +              z0 = svluti6_lane_u16_x4_u16_x2_u8_x3 (z16, z22, 0),
> +              z0 = svluti6_lane_u16_x4 (z16, z22, 0))
> +
> +/*
> +** luti6_lane_u16_x4_u8_x3_max_test:
> +** mov       z0\.d, z23\.d
> +** mov       z1\.d, z24\.d
> +** luti6     \{z0\.h - z3\.h\}, \{z16\.h - z17\.h\}, \{z0 - z1\}\[1\]
> +** ret
> +*/
> +TEST_1X2_NARROW (luti6_lane_u16_x4_u8_x3_max_test, svuint16x4_t, 
> svuint16x2_t,
> +              svuint8x3_t,
> +              z0 = svluti6_lane_u16_x4_u16_x2_u8_x3 (z16, z22, 1),
> +              z0 = svluti6_lane_u16_x4 (z16, z22, 1))
> +
> +/*
> +** luti6_lane_f16_x4_u8_x3_min_test:
> +** mov       z0\.d, z22\.d
> +** mov       z1\.d, z23\.d
> +** luti6     \{z0\.h - z3\.h\}, \{z16\.h - z17\.h\}, \{z0 - z1\}\[0\]
> +** ret
> +*/
> +TEST_1X2_NARROW (luti6_lane_f16_x4_u8_x3_min_test, svfloat16x4_t, 
> svfloat16x2_t,
> +              svuint8x3_t,
> +              z0 = svluti6_lane_f16_x4_f16_x2_u8_x3 (z16, z22, 0),
> +              z0 = svluti6_lane_f16_x4 (z16, z22, 0))
> +
> +/*
> +** luti6_lane_f16_x4_u8_x3_max_test:
> +** mov       z0\.d, z23\.d
> +** mov       z1\.d, z24\.d
> +** luti6     \{z0\.h - z3\.h\}, \{z16\.h - z17\.h\}, \{z0 - z1\}\[1\]
> +** ret
> +*/
> +TEST_1X2_NARROW (luti6_lane_f16_x4_u8_x3_max_test, svfloat16x4_t, 
> svfloat16x2_t,
> +              svuint8x3_t,
> +              z0 = svluti6_lane_f16_x4_f16_x2_u8_x3 (z16, z22, 1),
> +              z0 = svluti6_lane_f16_x4 (z16, z22, 1))
> +
> +/*
> +** luti6_lane_bf16_x4_u8_x3_min_test:
> +** mov       z0\.d, z22\.d
> +** mov       z1\.d, z23\.d
> +** luti6     \{z0\.h - z3\.h\}, \{z16\.h - z17\.h\}, \{z0 - z1\}\[0\]
> +** ret
> +*/
> +TEST_1X2_NARROW (luti6_lane_bf16_x4_u8_x3_min_test, svbfloat16x4_t, 
> svbfloat16x2_t,
> +              svuint8x3_t,
> +              z0 = svluti6_lane_bf16_x4_bf16_x2_u8_x3 (z16, z22, 0),
> +              z0 = svluti6_lane_bf16_x4 (z16, z22, 0))
> +
> +/*
> +** luti6_lane_bf16_x4_u8_x3_max_test:
> +** mov       z0\.d, z23\.d
> +** mov       z1\.d, z24\.d
> +** luti6     \{z0\.h - z3\.h\}, \{z16\.h - z17\.h\}, \{z0 - z1\}\[1\]
> +** ret
> +*/
> +TEST_1X2_NARROW (luti6_lane_bf16_x4_u8_x3_max_test, svbfloat16x4_t, 
> svbfloat16x2_t,
> +              svuint8x3_t,
> +              z0 = svluti6_lane_bf16_x4_bf16_x2_u8_x3 (z16, z22, 1),
> +              z0 = svluti6_lane_bf16_x4 (z16, z22, 1))
> diff --git a/gcc/testsuite/gcc.target/aarch64/sme2/acle-asm/luti6_zt.c 
> b/gcc/testsuite/gcc.target/aarch64/sme2/acle-asm/luti6_zt.c
> new file mode 100644
> index 000000000000..33520a0c817a
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/aarch64/sme2/acle-asm/luti6_zt.c
> @@ -0,0 +1,55 @@
> +/* { dg-do assemble { target aarch64_asm_sme2p3_ok } } */
> +/* { dg-do compile { target { ! aarch64_asm_sme2p3_ok } } } */
> +/* { dg-final { check-function-bodies "**" "" "-DCHECK_ASM" } }  */
> +
> +#pragma GCC target "+sme2p3"
> +#define SHARED_ZT0
> +#include "test_sme2_acle.h"
> +
> +/*
> +** luti6_zt_s8_test:
> +** luti6     z1\.b, zt0, z0
> +** ret
> +*/
> +TEST_XN_SINGLE (luti6_zt_s8_test, svint8_t, svuint8_t, z1,
> +             svluti6_zt_s8 (0, z0), svluti6_zt_s8 (0, z0))
> +
> +/*
> +** luti6_zt_u8_test:
> +** luti6     z1\.b, zt0, z0
> +** ret
> +*/
> +TEST_XN_SINGLE (luti6_zt_u8_test, svuint8_t, svuint8_t, z1,
> +             svluti6_zt_u8 (0, z0), svluti6_zt_u8 (0, z0))
> +
> +/*
> +** luti6_zt_mf8_test:
> +** luti6     z1\.b, zt0, z0
> +** ret
> +*/
> +TEST_XN_SINGLE (luti6_zt_mf8_test, svmfloat8_t, svuint8_t, z1,
> +             svluti6_zt_mf8 (0, z0), svluti6_zt_mf8 (0, z0))
> +
> +/*
> +** luti6_zt_s8_x4_test:
> +** luti6     \{z0\.b - z3\.b\}, zt0, \{z4 - z6\}
> +** ret
> +*/
> +TEST_DUAL_XN (luti6_zt_s8_x4_test, svint8x4_t, svuint8x3_t, z0,
> +           svluti6_zt_s8_x4 (0, z4), svluti6_zt_s8_x4 (0, z4))
> +
> +/*
> +** luti6_zt_u8_x4_test:
> +** luti6     \{z0\.b - z3\.b\}, zt0, \{z4 - z6\}
> +** ret
> +*/
> +TEST_DUAL_XN (luti6_zt_u8_x4_test, svuint8x4_t, svuint8x3_t, z0,
> +           svluti6_zt_u8_x4 (0, z4), svluti6_zt_u8_x4 (0, z4))
> +
> +/*
> +** luti6_zt_mf8_x4_test:
> +** luti6     \{z0\.b - z3\.b\}, zt0, \{z4 - z6\}
> +** ret
> +*/
> +TEST_DUAL_XN (luti6_zt_mf8_x4_test, svmfloat8x4_t, svuint8x3_t, z0,
> +           svluti6_zt_mf8_x4 (0, z4), svluti6_zt_mf8_x4 (0, z4))
> diff --git a/gcc/testsuite/gcc.target/aarch64/sve2/acle/asm/luti6.c 
> b/gcc/testsuite/gcc.target/aarch64/sve2/acle/asm/luti6.c
> new file mode 100644
> index 000000000000..df3736a890e1
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/aarch64/sve2/acle/asm/luti6.c
> @@ -0,0 +1,40 @@
> +/* { dg-do assemble { target aarch64_asm_sve2p3_ok } } */
> +/* { dg-do compile { target { ! aarch64_asm_sve2p3_ok } } } */
> +/* { dg-final { check-function-bodies "**" "" "-DCHECK_ASM" } } */
> +
> +#pragma GCC target "+sve2p3"
> +#include <arm_sve.h>
> +#include "test_sve_acle.h"
> +#ifdef STREAMING_COMPATIBLE
> +#pragma GCC target "+sme2p3"
> +#endif
> +
> +/*
> +** luti6_s8_x2_test:
> +**   luti6   z0\.b, \{z6\.b - z7\.b\}, z22
> +**   ret
> +*/
> +
> +TEST_1X2_NARROW(luti6_s8_x2_test, svint8_t, svint8x2_t, svuint8_t,
> +             z0 = svluti6_s8_x2 (z6, z22),
> +             z0 = svluti6 (z6, z22))
> +
> +/*
> +** luti6_u8_x2_test:
> +**   luti6   z0\.b, \{z6\.b - z7\.b\}, z22
> +**   ret
> +*/
> +
> +TEST_1X2_NARROW(luti6_u8_x2_test, svuint8_t, svuint8x2_t, svuint8_t,
> +             z0 = svluti6_u8_x2 (z6, z22),
> +             z0 = svluti6 (z6, z22))
> +
> +/*
> +** luti6_mf8_x2_test:
> +**   luti6   z0\.b, \{z6\.b - z7\.b\}, z22
> +**   ret
> +*/
> +
> +TEST_1X2_NARROW(luti6_mf8_x2_test, svmfloat8_t, svmfloat8x2_t, svuint8_t,
> +             z0 = svluti6_mf8_x2 (z6, z22),
> +             z0 = svluti6 (z6, z22))
> diff --git a/gcc/testsuite/gcc.target/aarch64/sve2/acle/asm/luti6_lane.c 
> b/gcc/testsuite/gcc.target/aarch64/sve2/acle/asm/luti6_lane.c
> new file mode 100644
> index 000000000000..6891ce054e84
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/aarch64/sve2/acle/asm/luti6_lane.c
> @@ -0,0 +1,60 @@
> +/* { dg-do assemble { target aarch64_asm_sve2p3_ok } } */
> +/* { dg-do compile { target { ! aarch64_asm_sve2p3_ok } } } */
> +/* { dg-final { check-function-bodies "**" "" "-DCHECK_ASM" } } */
> +
> +#pragma GCC target "+sve2p3"
> +#include <arm_sve.h>
> +#include "test_sve_acle.h"
> +#ifdef STREAMING_COMPATIBLE
> +#pragma GCC target "+sme2p3"
> +#endif
> +
> +/*
> +**  luti6_s16_x2_min_idx_test:
> +**   luti6   z0\.h, \{z6\.h - z7\.h\}, z22\[0\]
> +**   ret
> +*/
> +
> +TEST_1X2_NARROW(luti6_s16_x2_min_idx_test, svint16_t, svint16x2_t, svuint8_t,
> +             z0 = svluti6_lane_s16_x2 (z6, z22, 0),
> +             z0 = svluti6_lane (z6, z22, 0))
> +
> +/*
> +**  luti6_s16_x2_max_idx_test:
> +**   luti6   z0\.h, \{z6\.h - z7\.h\}, z22\[1\]
> +**   ret
> +*/
> +
> +TEST_1X2_NARROW(luti6_s16_x2_max_idx_test, svint16_t, svint16x2_t, svuint8_t,
> +             z0 = svluti6_lane_s16_x2 (z6, z22, 1),
> +             z0 = svluti6_lane (z6, z22, 1))
> +
> +/*
> +**  luti6_u16_x2_test:
> +**   luti6   z0\.h, \{z6\.h - z7\.h\}, z22\[1\]
> +**   ret
> +*/
> +
> +TEST_1X2_NARROW(luti6_u16_x2_test, svuint16_t, svuint16x2_t, svuint8_t,
> +             z0 = svluti6_lane_u16_x2 (z6, z22, 1),
> +             z0 = svluti6_lane (z6, z22, 1))
> +
> +/*
> +**  luti6_f16_x2_test:
> +**   luti6   z0\.h, \{z6\.h - z7\.h\}, z22\[1\]
> +**   ret
> +*/
> +
> +TEST_1X2_NARROW(luti6_f16_x2_test, svfloat16_t, svfloat16x2_t, svuint8_t,
> +             z0 = svluti6_lane_f16_x2 (z6, z22, 1),
> +             z0 = svluti6_lane (z6, z22, 1))
> +
> +/*
> +**  luti6_bf16_x2_test:
> +**   luti6   z0\.h, \{z6\.h - z7\.h\}, z22\[1\]
> +**   ret
> +*/
> +
> +TEST_1X2_NARROW(luti6_bf16_x2_test, svbfloat16_t, svbfloat16x2_t, svuint8_t,
> +             z0 = svluti6_lane_bf16_x2 (z6, z22, 1),
> +             z0 = svluti6_lane (z6, z22, 1))
> -- 
> 2.53.0
> 

Reply via email to