[v4,1/2] gas, aarch64: Add SME2 lutv2 extension

Message ID 20240614141353.1287038-1-saurabh.jha@arm.com
State Superseded
Headers
Series [v4,1/2] gas, aarch64: Add SME2 lutv2 extension |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_binutils_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_binutils_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_binutils_check--master-aarch64 fail Test failed
linaro-tcwg-bot/tcwg_binutils_check--master-arm success Test passed

Commit Message

Saurabh Jha June 14, 2024, 2:13 p.m. UTC
  Introduces instructions for the SME2 lutv2 extension for AArch64. They
are documented in the following links:

* https://developer.arm.com/documentation/ddi0602/2024-03/SME-Instructions/LUTI4--four-registers--8-bit---Lookup-table-read-with-4-bit-indexes-and-8-bit-elements-?lang=en
* https://developer.arm.com/documentation/ddi0602/2024-03/SME-Instructions/MOVT--vector-to-table---Move-vector-register-to-ZT0-?lang=en

For both luti4 instructions, we introduced an operand called
SME_Znx2_BIT_INDEX. We use the existing function parse_vector_reg_list
for parsing but modified that function so that it can accept operands
without qualifiers and rejects instructions that have operands with
qualifiers but are not supposed to have operands with qualifiers.
For disassembly, we modified print_register_list so that it could
accept register lists without qualifiers.

For one luti4 instruction, we introduced a SME_Zdnx4_STRIDED. It is
similar to SME_Ztx4_STRIDED and we could use existing code for parsing,
encoding, and disassembly.

For movt instruction, we introduced an operand called SME_ZT0_INDEX2_12.
This is a ZT0 register with a bit index encoded in [13:12]. It is
similar to SME_ZT0_INDEX.

We also introduced an iclass named sme_size_12_b so that we can encode
size bits [13:12] correctly when only 'b' is allowed as qualifier.
---
Hi,

Regression tested for aarch64-none-elf and found no regressions.

Ok for binutils-master? I don't have commit access so can someone please
commit on my behalf?

Regards,
Saurabh
---
 gas/NEWS                                      |  2 +
 gas/config/tc-aarch64.c                       | 91 +++++++++++++++++-
 gas/doc/c-aarch64.texi                        |  2 +
 gas/testsuite/gas/aarch64/sme2-8-invalid.l    | 14 +--
 gas/testsuite/gas/aarch64/sme2-lutv2-bad.d    |  3 +
 gas/testsuite/gas/aarch64/sme2-lutv2-bad.l    | 15 +++
 .../gas/aarch64/sme2-lutv2-illegal.d          |  3 +
 .../gas/aarch64/sme2-lutv2-illegal.l          | 70 ++++++++++++++
 .../gas/aarch64/sme2-lutv2-illegal.s          | 95 +++++++++++++++++++
 gas/testsuite/gas/aarch64/sme2-lutv2.d        | 24 +++++
 gas/testsuite/gas/aarch64/sme2-lutv2.s        | 22 +++++
 include/opcode/aarch64.h                      |  6 ++
 opcodes/aarch64-asm.c                         |  1 +
 opcodes/aarch64-dis.c                         |  6 ++
 opcodes/aarch64-opc.c                         | 30 +++++-
 opcodes/aarch64-opc.h                         |  2 +
 opcodes/aarch64-tbl.h                         | 33 +++++++
 17 files changed, 405 insertions(+), 14 deletions(-)
 create mode 100644 gas/testsuite/gas/aarch64/sme2-lutv2-bad.d
 create mode 100644 gas/testsuite/gas/aarch64/sme2-lutv2-bad.l
 create mode 100644 gas/testsuite/gas/aarch64/sme2-lutv2-illegal.d
 create mode 100644 gas/testsuite/gas/aarch64/sme2-lutv2-illegal.l
 create mode 100644 gas/testsuite/gas/aarch64/sme2-lutv2-illegal.s
 create mode 100644 gas/testsuite/gas/aarch64/sme2-lutv2.d
 create mode 100644 gas/testsuite/gas/aarch64/sme2-lutv2.s
  

Comments

Richard Earnshaw (lists) June 21, 2024, 2:18 p.m. UTC | #1
On 14/06/2024 15:13, saurabh.jha@arm.com wrote:
> 
> Introduces instructions for the SME2 lutv2 extension for AArch64. They
> are documented in the following links:
> 
> * https://developer.arm.com/documentation/ddi0602/2024-03/SME-Instructions/LUTI4--four-registers--8-bit---Lookup-table-read-with-4-bit-indexes-and-8-bit-elements-?lang=en
> * https://developer.arm.com/documentation/ddi0602/2024-03/SME-Instructions/MOVT--vector-to-table---Move-vector-register-to-ZT0-?lang=en
> 
> For both luti4 instructions, we introduced an operand called
> SME_Znx2_BIT_INDEX. We use the existing function parse_vector_reg_list
> for parsing but modified that function so that it can accept operands
> without qualifiers and rejects instructions that have operands with
> qualifiers but are not supposed to have operands with qualifiers.
> For disassembly, we modified print_register_list so that it could
> accept register lists without qualifiers.
> 
> For one luti4 instruction, we introduced a SME_Zdnx4_STRIDED. It is
> similar to SME_Ztx4_STRIDED and we could use existing code for parsing,
> encoding, and disassembly.
> 
> For movt instruction, we introduced an operand called SME_ZT0_INDEX2_12.
> This is a ZT0 register with a bit index encoded in [13:12]. It is
> similar to SME_ZT0_INDEX.
> 
> We also introduced an iclass named sme_size_12_b so that we can encode
> size bits [13:12] correctly when only 'b' is allowed as qualifier.
> ---
> Hi,
> 
> Regression tested for aarch64-none-elf and found no regressions.
> 
> Ok for binutils-master? I don't have commit access so can someone please
> commit on my behalf?
> 
> Regards,
> Saurabh
> ---
>  gas/NEWS                                      |  2 +
>  gas/config/tc-aarch64.c                       | 91 +++++++++++++++++-
>  gas/doc/c-aarch64.texi                        |  2 +
>  gas/testsuite/gas/aarch64/sme2-8-invalid.l    | 14 +--
>  gas/testsuite/gas/aarch64/sme2-lutv2-bad.d    |  3 +
>  gas/testsuite/gas/aarch64/sme2-lutv2-bad.l    | 15 +++
>  .../gas/aarch64/sme2-lutv2-illegal.d          |  3 +
>  .../gas/aarch64/sme2-lutv2-illegal.l          | 70 ++++++++++++++
>  .../gas/aarch64/sme2-lutv2-illegal.s          | 95 +++++++++++++++++++
>  gas/testsuite/gas/aarch64/sme2-lutv2.d        | 24 +++++
>  gas/testsuite/gas/aarch64/sme2-lutv2.s        | 22 +++++
>  include/opcode/aarch64.h                      |  6 ++
>  opcodes/aarch64-asm.c                         |  1 +
>  opcodes/aarch64-dis.c                         |  6 ++
>  opcodes/aarch64-opc.c                         | 30 +++++-
>  opcodes/aarch64-opc.h                         |  2 +
>  opcodes/aarch64-tbl.h                         | 33 +++++++
>  17 files changed, 405 insertions(+), 14 deletions(-)
>  create mode 100644 gas/testsuite/gas/aarch64/sme2-lutv2-bad.d
>  create mode 100644 gas/testsuite/gas/aarch64/sme2-lutv2-bad.l
>  create mode 100644 gas/testsuite/gas/aarch64/sme2-lutv2-illegal.d
>  create mode 100644 gas/testsuite/gas/aarch64/sme2-lutv2-illegal.l
>  create mode 100644 gas/testsuite/gas/aarch64/sme2-lutv2-illegal.s
>  create mode 100644 gas/testsuite/gas/aarch64/sme2-lutv2.d
>  create mode 100644 gas/testsuite/gas/aarch64/sme2-lutv2.s
> 

--- a/gas/config/tc-aarch64.c
+++ b/gas/config/tc-aarch64.c

+  if (!skip_past_comma(str))
+      return true;
+
+  if (!parse_shift(str, operand, mode))
+    return false;


+	case AARCH64_OPND_SME_ZT0_INDEX2_12:
+	  po_misc_or_fail(parse_shifter_zt0_with_bit_index

Space after function name before open parenthesis.

There may be other instances of this that I haven't recorded here, please can you double check?

Apart from that, this looks OK.

R.
  
Saurabh Jha June 21, 2024, 3:35 p.m. UTC | #2
On 6/21/2024 3:18 PM, Richard Earnshaw (lists) wrote:
> On 14/06/2024 15:13, saurabh.jha@arm.com wrote:
>>
>> Introduces instructions for the SME2 lutv2 extension for AArch64. They
>> are documented in the following links:
>>
>> * https://developer.arm.com/documentation/ddi0602/2024-03/SME-Instructions/LUTI4--four-registers--8-bit---Lookup-table-read-with-4-bit-indexes-and-8-bit-elements-?lang=en
>> * https://developer.arm.com/documentation/ddi0602/2024-03/SME-Instructions/MOVT--vector-to-table---Move-vector-register-to-ZT0-?lang=en
>>
>> For both luti4 instructions, we introduced an operand called
>> SME_Znx2_BIT_INDEX. We use the existing function parse_vector_reg_list
>> for parsing but modified that function so that it can accept operands
>> without qualifiers and rejects instructions that have operands with
>> qualifiers but are not supposed to have operands with qualifiers.
>> For disassembly, we modified print_register_list so that it could
>> accept register lists without qualifiers.
>>
>> For one luti4 instruction, we introduced a SME_Zdnx4_STRIDED. It is
>> similar to SME_Ztx4_STRIDED and we could use existing code for parsing,
>> encoding, and disassembly.
>>
>> For movt instruction, we introduced an operand called SME_ZT0_INDEX2_12.
>> This is a ZT0 register with a bit index encoded in [13:12]. It is
>> similar to SME_ZT0_INDEX.
>>
>> We also introduced an iclass named sme_size_12_b so that we can encode
>> size bits [13:12] correctly when only 'b' is allowed as qualifier.
>> ---
>> Hi,
>>
>> Regression tested for aarch64-none-elf and found no regressions.
>>
>> Ok for binutils-master? I don't have commit access so can someone please
>> commit on my behalf?
>>
>> Regards,
>> Saurabh
>> ---
>>   gas/NEWS                                      |  2 +
>>   gas/config/tc-aarch64.c                       | 91 +++++++++++++++++-
>>   gas/doc/c-aarch64.texi                        |  2 +
>>   gas/testsuite/gas/aarch64/sme2-8-invalid.l    | 14 +--
>>   gas/testsuite/gas/aarch64/sme2-lutv2-bad.d    |  3 +
>>   gas/testsuite/gas/aarch64/sme2-lutv2-bad.l    | 15 +++
>>   .../gas/aarch64/sme2-lutv2-illegal.d          |  3 +
>>   .../gas/aarch64/sme2-lutv2-illegal.l          | 70 ++++++++++++++
>>   .../gas/aarch64/sme2-lutv2-illegal.s          | 95 +++++++++++++++++++
>>   gas/testsuite/gas/aarch64/sme2-lutv2.d        | 24 +++++
>>   gas/testsuite/gas/aarch64/sme2-lutv2.s        | 22 +++++
>>   include/opcode/aarch64.h                      |  6 ++
>>   opcodes/aarch64-asm.c                         |  1 +
>>   opcodes/aarch64-dis.c                         |  6 ++
>>   opcodes/aarch64-opc.c                         | 30 +++++-
>>   opcodes/aarch64-opc.h                         |  2 +
>>   opcodes/aarch64-tbl.h                         | 33 +++++++
>>   17 files changed, 405 insertions(+), 14 deletions(-)
>>   create mode 100644 gas/testsuite/gas/aarch64/sme2-lutv2-bad.d
>>   create mode 100644 gas/testsuite/gas/aarch64/sme2-lutv2-bad.l
>>   create mode 100644 gas/testsuite/gas/aarch64/sme2-lutv2-illegal.d
>>   create mode 100644 gas/testsuite/gas/aarch64/sme2-lutv2-illegal.l
>>   create mode 100644 gas/testsuite/gas/aarch64/sme2-lutv2-illegal.s
>>   create mode 100644 gas/testsuite/gas/aarch64/sme2-lutv2.d
>>   create mode 100644 gas/testsuite/gas/aarch64/sme2-lutv2.s
>>
> 
> --- a/gas/config/tc-aarch64.c
> +++ b/gas/config/tc-aarch64.c
> 
> +  if (!skip_past_comma(str))
> +      return true;
> +
> +  if (!parse_shift(str, operand, mode))
> +    return false;
> 
> 
> +	case AARCH64_OPND_SME_ZT0_INDEX2_12:
> +	  po_misc_or_fail(parse_shifter_zt0_with_bit_index
> 
> Space after function name before open parenthesis.
> 
> There may be other instances of this that I haven't recorded here, please can you double check?
> 
> Apart from that, this looks OK.
> 
> R.
Thanks for pointing it out Richard. There were three instances of these. 
I also had a comment which was not in the correct style. Fixed in the 
new version here: 
https://sourceware.org/pipermail/binutils/2024-June/135002.html.
> 
>
  

Patch

diff --git a/gas/NEWS b/gas/NEWS
index 2f82f45ec57..92d45777e82 100644
--- a/gas/NEWS
+++ b/gas/NEWS
@@ -43,6 +43,8 @@ 
 
 * Add support for the AArch64 Lookup Table Extension (LUT).
 
+* Add support for the AArch64 Lookup Table Extension v2 (LUTv2).
+
 Changes in 2.42:
 
 * Add support for AMD znver5 processor.
diff --git a/gas/config/tc-aarch64.c b/gas/config/tc-aarch64.c
index 885ea65b8eb..eeef291c35f 100644
--- a/gas/config/tc-aarch64.c
+++ b/gas/config/tc-aarch64.c
@@ -1365,6 +1365,11 @@  reg_type_mask (aarch64_reg_type reg_type)
 
    It returns PARSE_FAIL if the register list is invalid.
 
+   If HAS_QUALIFIER is true, the registers in the list must have qualifiers
+   or the list is invalid.
+   If HAS_QUALIFIER is false, the registers in the list must not have
+   qualifiers or the list is invalid.
+
    The list contains one to four registers.
    Each register can be one of:
    <Vt>.<T>[<index>]
@@ -1376,7 +1381,8 @@  reg_type_mask (aarch64_reg_type reg_type)
 
 static int
 parse_vector_reg_list (char **ccp, aarch64_reg_type type,
-		       struct vector_type_el *vectype)
+		       struct vector_type_el *vectype,
+		       bool has_qualifier)
 {
   char *str = *ccp;
   int nb_regs;
@@ -1413,8 +1419,15 @@  parse_vector_reg_list (char **ccp, aarch64_reg_type type,
 	  str++;		/* skip over '-' */
 	  val_range = val;
 	}
-      const reg_entry *reg = parse_typed_reg (&str, type, &typeinfo,
-					      ptr_flags);
+
+      const reg_entry *reg;
+      if (has_qualifier)
+	reg = parse_typed_reg (&str, type, &typeinfo, ptr_flags);
+      else
+	{
+	  reg = parse_reg (&str);
+	  typeinfo.defined = 0;
+	}
       if (!reg)
 	{
 	  set_first_syntax_error (_("invalid vector register in list"));
@@ -3893,6 +3906,54 @@  parse_shifter_operand_reloc (char **str, aarch64_opnd_info *operand,
   return parse_shifter_operand (str, operand, mode);
 }
 
+/* Parse a ZT0 vector with an optional bit index and a shifter operand.
+      zt0
+      zt0[0, mul vl]
+      zt0[2, mul vl]
+
+   We only support 'mul vl' as shifter operand and it is handled by
+   parse_shift.
+
+   Return TRUE on success; otherwise return FALSE.  */
+
+static bool
+parse_shifter_zt0_with_bit_index (char **str,
+				  aarch64_opnd_info *operand,
+				  enum parse_shift_mode mode)
+{
+  const reg_entry *reg = parse_reg (str);
+  if (reg == NULL)
+    return false;
+
+  if (reg->type != REG_TYPE_ZT0)
+    return false;
+
+  operand->reglane.regno = reg->number;
+
+  if (!skip_past_char(str, '['))
+    {
+      // It is not an error if the index is not present. Default index to 0.
+      operand->imm.value = 0;
+      return true;
+    }
+
+  int64_t index;
+  if (!parse_index_expression (str, &index))
+      return false;
+  operand->imm.value = index;
+
+  if (!skip_past_comma(str))
+      return true;
+
+  if (!parse_shift(str, operand, mode))
+    return false;
+
+  if (!skip_past_char (str, ']'))
+    return false;
+
+  return true;
+}
+
 /* Parse all forms of an address expression.  Information is written
    to *OPERAND and/or inst.reloc.
 
@@ -6860,10 +6921,26 @@  parse_operands (char *str, const aarch64_opcode *opcode)
 	  info->reglane.index = vectype.index;
 	  break;
 
+	case AARCH64_OPND_SME_Znx2_BIT_INDEX:
+	  // A vector register list encoding a bit index.
+	  reg_type = REG_TYPE_Z;
+	  val = parse_vector_reg_list (&str, reg_type, &vectype, false);
+	  if (val == PARSE_FAIL)
+	    goto failure;
+
+	  if (! reg_list_valid_p (val, &info->reglist, reg_type))
+	    {
+	      set_fatal_syntax_error (_("invalid register list"));
+	      goto failure;
+	    }
+
+	  break;
+
 	case AARCH64_OPND_SVE_ZnxN:
 	case AARCH64_OPND_SVE_ZtxN:
 	case AARCH64_OPND_SME_Zdnx2:
 	case AARCH64_OPND_SME_Zdnx4:
+	case AARCH64_OPND_SME_Zdnx4_STRIDED:
 	case AARCH64_OPND_SME_Zt2:
 	case AARCH64_OPND_SME_Zt3:
 	case AARCH64_OPND_SME_Zt4:
@@ -6901,7 +6978,7 @@  parse_operands (char *str, const aarch64_opcode *opcode)
 	    }
 	  else
 	    {
-	      val = parse_vector_reg_list (&str, reg_type, &vectype);
+	      val = parse_vector_reg_list (&str, reg_type, &vectype, true);
 	      if (val == PARSE_FAIL)
 		goto failure;
 
@@ -7968,6 +8045,11 @@  parse_operands (char *str, const aarch64_opcode *opcode)
 	  info->imm.value = vectype.index;
 	  break;
 
+	case AARCH64_OPND_SME_ZT0_INDEX2_12:
+	  po_misc_or_fail(parse_shifter_zt0_with_bit_index
+			  (&str, info, SHIFTED_MUL_VL));
+	  break;
+
 	case AARCH64_OPND_SME_ZT0_LIST:
 	  if (*str != '{')
 	    {
@@ -10597,6 +10679,7 @@  static const struct aarch64_option_cpu_value_table aarch64_features[] = {
   {"fp8",		AARCH64_FEATURE (FP8), AARCH64_FEATURE (SIMD)},
   {"lut",		AARCH64_FEATURE (LUT), AARCH64_FEATURE (SIMD)},
   {"brbe",		AARCH64_FEATURE (BRBE), AARCH64_NO_FEATURES},
+  {"sme-lutv2",		AARCH64_FEATURE (SME_LUTv2), AARCH64_FEATURE (SME2)},
   {NULL,		AARCH64_NO_FEATURES, AARCH64_NO_FEATURES},
 };
 
diff --git a/gas/doc/c-aarch64.texi b/gas/doc/c-aarch64.texi
index b29da1f0e8f..41a8d7e4a44 100644
--- a/gas/doc/c-aarch64.texi
+++ b/gas/doc/c-aarch64.texi
@@ -269,6 +269,8 @@  automatically cause those extensions to be disabled.
  @tab Enable SME F64F64 Extension.
 @item @code{sme-i16i64} @tab @code{sme}
  @tab Enable SME I16I64 Extension.
+@item @code{sme-lutv2} @tab
+ @tab Enable SME Lookup Table v2 (LUTv2) extension.
 @item @code{sme2} @tab @code{sme}
  @tab Enable SME2.
 @item @code{sme2p1} @tab @code{sme2}
diff --git a/gas/testsuite/gas/aarch64/sme2-8-invalid.l b/gas/testsuite/gas/aarch64/sme2-8-invalid.l
index aa393657c4b..aef9b1ad203 100644
--- a/gas/testsuite/gas/aarch64/sme2-8-invalid.l
+++ b/gas/testsuite/gas/aarch64/sme2-8-invalid.l
@@ -9,9 +9,9 @@ 
 [^ :]+:[0-9]+: Error: expected '}' after ZT0 at operand 1 -- `zero {zt0'
 [^ :]+:[0-9]+: Error: expected '}' after ZT0 at operand 1 -- `zero {zt0\.b}'
 [^ :]+:[0-9]+: Error: expected '}' after ZT0 at operand 1 -- `zero {zt0,zt0}'
-[^ :]+:[0-9]+: Error: expected a register at operand 1 -- `movt 0,zt0\[0\]'
+[^ :]+:[0-9]+: Error: operand 1 must be an integer register -- `movt 0,zt0\[0\]'
 [^ :]+:[0-9]+: Error: expected a register at operand 2 -- `movt x0,0'
-[^ :]+:[0-9]+: Error: missing register index at operand 1 -- `movt zt0,x0'
+[^ :]+:[0-9]+: Error: expected an SVE vector register at operand 2 -- `movt zt0,x0'
 [^ :]+:[0-9]+: Error: unexpected register type at operand 1 -- `movt za\[0\],x0'
 [^ :]+:[0-9]+: Error: unexpected register type at operand 1 -- `movt za0\[0\],x0'
 [^ :]+:[0-9]+: Error: bad expression at operand 1 -- `movt zt0\[#0\],x0'
@@ -26,17 +26,17 @@ 
 [^ :]+:[0-9]+: Error: register element index out of range 0 to 56 at operand 1 -- `movt zt0\[57\],x0'
 [^ :]+:[0-9]+: Error: register element index out of range 0 to 56 at operand 1 -- `movt zt0\[64\],x0'
 [^ :]+:[0-9]+: Error: register element index out of range 0 to 56 at operand 1 -- `movt zt0\[1<<32\],x0'
-[^ :]+:[0-9]+: Error: missing register index at operand 1 -- `movt zt0\.b\[0\],x0'
-[^ :]+:[0-9]+: Error: missing register index at operand 1 -- `movt zt0/z\[0\],x0'
-[^ :]+:[0-9]+: Error: expected an integer or zero register at operand 2 -- `movt zt0\[0\],sp'
+[^ :]+:[0-9]+: Error: comma expected between operands at operand 2 -- `movt zt0\.b\[0\],x0'
+[^ :]+:[0-9]+: Error: comma expected between operands at operand 2 -- `movt zt0/z\[0\],x0'
+[^ :]+:[0-9]+: Error: comma expected between operands at operand 2 -- `movt zt0\[0\],sp'
 [^ :]+:[0-9]+: Error: operand mismatch -- `movt zt0\[0\],w0'
 [^ :]+:[0-9]+: Info:    did you mean this\?
 [^ :]+:[0-9]+: Info:    	movt zt0\[0\], x0
-[^ :]+:[0-9]+: Error: expected an integer or zero register at operand 2 -- `movt zt0\[0\],wsp'
+[^ :]+:[0-9]+: Error: comma expected between operands at operand 2 -- `movt zt0\[0\],wsp'
 [^ :]+:[0-9]+: Error: operand mismatch -- `movt zt0\[0\],wzr'
 [^ :]+:[0-9]+: Info:    did you mean this\?
 [^ :]+:[0-9]+: Info:    	movt zt0\[0\], xzr
-[^ :]+:[0-9]+: Error: expected an integer or zero register at operand 2 -- `movt zt0\[0\],0'
+[^ :]+:[0-9]+: Error: comma expected between operands at operand 2 -- `movt zt0\[0\],0'
 [^ :]+:[0-9]+: Error: expected a register at operand 1 -- `ldr 0,\[x0\]'
 [^ :]+:[0-9]+: Error: invalid addressing mode at operand 2 -- `ldr zt0,0'
 [^ :]+:[0-9]+: Error: operand 2 must be an address with base register \(no offset\) -- `ldr zt0,\[x0,#0\]'
diff --git a/gas/testsuite/gas/aarch64/sme2-lutv2-bad.d b/gas/testsuite/gas/aarch64/sme2-lutv2-bad.d
new file mode 100644
index 00000000000..0e6e2b77604
--- /dev/null
+++ b/gas/testsuite/gas/aarch64/sme2-lutv2-bad.d
@@ -0,0 +1,3 @@ 
+#as: -march=armv8-a+sme2
+#source: sme2-lutv2.s
+#error_output: sme2-lutv2-bad.l
\ No newline at end of file
diff --git a/gas/testsuite/gas/aarch64/sme2-lutv2-bad.l b/gas/testsuite/gas/aarch64/sme2-lutv2-bad.l
new file mode 100644
index 00000000000..78837221539
--- /dev/null
+++ b/gas/testsuite/gas/aarch64/sme2-lutv2-bad.l
@@ -0,0 +1,15 @@ 
+[^ :]+: Assembler messages:
+.*: Error: selected processor does not support `movt zt0,z0'
+.*: Error: selected processor does not support `movt zt0\[0,mul vl\],z31'
+.*: Error: selected processor does not support `movt zt0\[3,mul vl\],z0'
+.*: Error: selected processor does not support `movt zt0\[3,mul vl\],z31'
+.*: Error: selected processor does not support `movt zt0\[2,mul vl\],z25'
+.*: Error: selected processor does not support `luti4 {z0.b-z3.b},zt0,{z0-z1}'
+.*: Error: selected processor does not support `luti4 {z28.b-z31.b},zt0,{z0-z1}'
+.*: Error: selected processor does not support `luti4 {z0.b-z3.b},zt0,{z30-z31}'
+.*: Error: selected processor does not support `luti4 {z20.b-z23.b},zt0,{z12-z13}'
+.*: Error: selected processor does not support `luti4 {z0.b,z4.b,z8.b,z12.b},zt0,{z0-z1}'
+.*: Error: selected processor does not support `luti4 {z19.b,z23.b,z27.b,z31.b},zt0,{z0-z1}'
+.*: Error: selected processor does not support `luti4 {z0.b,z4.b,z8.b,z12.b},zt0,{z30-z31}'
+.*: Error: selected processor does not support `luti4 {z17.b,z21.b,z25.b,z29.b},zt0,{z12-z13}'
+.*: Error: selected processor does not support `luti4 {z20.b,z21.b,z22.b,z23.b},zt0,{z12-z13}'
diff --git a/gas/testsuite/gas/aarch64/sme2-lutv2-illegal.d b/gas/testsuite/gas/aarch64/sme2-lutv2-illegal.d
new file mode 100644
index 00000000000..401bbb72094
--- /dev/null
+++ b/gas/testsuite/gas/aarch64/sme2-lutv2-illegal.d
@@ -0,0 +1,3 @@ 
+#as: -march=armv8-a+sme2+sme2p1+sme-lutv2
+#source: sme2-lutv2-illegal.s
+#error_output: sme2-lutv2-illegal.l
\ No newline at end of file
diff --git a/gas/testsuite/gas/aarch64/sme2-lutv2-illegal.l b/gas/testsuite/gas/aarch64/sme2-lutv2-illegal.l
new file mode 100644
index 00000000000..4495a9adc52
--- /dev/null
+++ b/gas/testsuite/gas/aarch64/sme2-lutv2-illegal.l
@@ -0,0 +1,70 @@ 
+[^ :]+: Assembler messages:
+[^ :]+:[0-9]+: Error: start register out of range at operand 3 -- `luti4 {z20.b-z23.h},zt0,{z13-z14}'
+[^ :]+:[0-9]+: Error: type mismatch in vector register list at operand 1 -- `luti4 {z19.b,z23.b,z27.b,z31.h},zt0,{z13-z14}'
+[^ :]+:[0-9]+: Error: unexpected register type at operand 1 -- `movt z3,zt0'
+[^ :]+:[0-9]+: Error: unexpected register type at operand 1 -- `movt z3\[0\],zt0'
+[^ :]+:[0-9]+: Error: operand 1 must be an integer register -- `movt zt1,z25'
+[^ :]+:[0-9]+: Error: expected a vector register at operand 1 -- `luti4 zt0,\{z0.b-z3.b\},\{z0-z1\}'
+[^ :]+:[0-9]+: Error: expected a register at operand 2 -- `luti4 \{z0.b-z3.b\},\{z0.b-z1.b\}\{z0-z1\}'
+[^ :]+:[0-9]+: Error: syntax error in register list at operand 1 -- `luti4 \{\},zt0,\{z12-z13\}'
+[^ :]+:[0-9]+: Error: invalid vector register in list at operand 3 -- `luti4 \{z20.b-z23.b\},zt0,\{\}'
+[^ :]+:[0-9]+: Error: unexpected register type at operand 2 -- `luti4 \{z20.b-z23.b\},z3,\{z12-z13\}'
+[^ :]+:[0-9]+: Error: start register out of range at operand 1 -- `luti4 \{z19.b-z22.b\},zt0,\{z12-z13\}'
+[^ :]+:[0-9]+: Error: start register out of range at operand 3 -- `luti4 \{z20.b-z23.b\},zt0,\{z13-z14\}'
+[^ :]+:[0-9]+: Error: expected a vector register at operand 1 -- `luti4 zt0,{z0.b,z4.b,z8.b,z12.b},{z0-z1}'
+[^ :]+:[0-9]+: Error: expected a register at operand 2 -- `luti4 {z0.b,z4.b,z8.b,z12.b},{z0.b-z1.b}{z0-z1}'
+[^ :]+:[0-9]+: Error: invalid vector register in list at operand 3 -- `luti4 {z19.b,z23.b,z27.b,z31.b},zt0,{}'
+[^ :]+:[0-9]+: Error: unexpected register type at operand 2 -- `luti4 {z19.b,z23.b,z27.b,z31.b},z3,{z12-z13}'
+[^ :]+:[0-9]+: Error: start register out of range at operand 3 -- `luti4 {z19.b,z23.b,z27.b,z31.b},zt0,{z13-z14}'
+[^ :]+:[0-9]+: Error: operand mismatch -- `luti4 \{z20.s-z23.s\},zt0,\{z20-z21\}'
+[^ :]+:[0-9]+: Info:    did you mean this\?
+[^ :]+:[0-9]+: Info:    	luti4 {z20.b-z23.b}, zt0, {z20-z21}
+[^ :]+:[0-9]+: Error: operand mismatch -- `luti4 {z19.s,z23.s,z27.s,z31.s},zt0,{z20-z21}'
+[^ :]+:[0-9]+: Info:    did you mean this\?
+[^ :]+:[0-9]+: Info:    	luti4 {z19.b, z23.b, z27.b, z31.b}, zt0, {z20-z21}
+[^ :]+:[0-9]+: Error: comma expected between operands at operand 2 -- `movt zt0.b,z31'
+[^ :]+:[0-9]+: Error: comma expected between operands at operand 2 -- `movt zt0.b\[0,mul vl\],z31'
+[^ :]+:[0-9]+: Error: comma expected between operands at operand 2 -- `movt zt0'
+[^ :]+:[0-9]+: Error: comma expected between operands at operand 2 -- `movt zt0\[1,mul vl\]'
+[^ :]+:[0-9]+: Error: unexpected characters following instruction at operand 2 -- `movt zt0,z23,z31'
+[^ :]+:[0-9]+: Error: unexpected characters following instruction at operand 2 -- `movt zt0\[1,mul vl\],z23,z31'
+[^ :]+:[0-9]+: Error: comma expected between operands at operand 2 -- `luti4 \{z20.b-z23.b\}'
+[^ :]+:[0-9]+: Error: comma expected between operands at operand 3 -- `luti4 \{z20.b-z23.b\},zt0'
+[^ :]+:[0-9]+: Error: unexpected characters following instruction at operand 3 -- `luti4 \{z20.b-z23.b\},zt0,\{z12-z13\},\{z20-z21\}'
+[^ :]+:[0-9]+: Error: comma expected between operands at operand 2 -- `luti4 {z19.b,z23.b,z27.b,z31.b}'
+[^ :]+:[0-9]+: Error: comma expected between operands at operand 3 -- `luti4 {z19.b,z23.b,z27.b,z31.b},zt0'
+[^ :]+:[0-9]+: Error: comma expected between operands at operand 3 -- `luti4 {z19.b,z23.b,z27.b,z31.b},zt0{z12-z13},{z20-z21}'
+[^ :]+:[0-9]+: Error: operand 1 must be an integer register -- `movt zy0,z16'
+[^ :]+:[0-9]+: Error: operand 1 must be an integer register -- `movt zt1,z16'
+[^ :]+:[0-9]+: Error: operand 1 must be an integer register -- `movt zy0\[1,mul vl\],z16'
+[^ :]+:[0-9]+: Error: expected an SVE vector register at operand 2 -- `movt zt0,y16'
+[^ :]+:[0-9]+: Error: expected an SVE vector register at operand 2 -- `movt zt0\[1,mul vl\],y16'
+[^ :]+:[0-9]+: Error: operand 1 must be a list of SVE vector registers -- `luti4 \{z20.b-y23.b\},zt0,\{z12-z13\}'
+[^ :]+:[0-9]+: Error: unexpected character `x' in element size at operand 1 -- `luti4 \{z20.x-z23.b\},zt0,\{z12-z13\}'
+[^ :]+:[0-9]+: Error: expected a register at operand 2 -- `luti4 \{z20.b-z23.b\},zy0,\{z12-z13\}'
+[^ :]+:[0-9]+: Error: operand 1 must be a list of SVE vector registers -- `luti4 \{z20.b-y23.b\},zt0,\{y12-z13\}'
+[^ :]+:[0-9]+: Error: operand 1 must be a list of SVE vector registers -- `luti4 {z19.b,z23.b,z27.b,y31.b},zt0,{z12-z13}'
+[^ :]+:[0-9]+: Error: unexpected character `x' in element size at operand 1 -- `luti4 {z19.x,z23.b,z27.b,z31.b},zt0,{z12-z13}'
+[^ :]+:[0-9]+: Error: expected a register at operand 2 -- `luti4 {z19.b,z23.b,z27.b,z31.b},zy0,{z12-z13}'
+[^ :]+:[0-9]+: Error: invalid vector register in list at operand 3 -- `luti4 {z19.b,z23.b,z27.b,z31.b},zt0,{y12-z13}'
+[^ :]+:[0-9]+: Error: missing type suffix at operand 1 -- `luti4 \{z20-z23.b\},zt0,\{z12-z13\}'
+[^ :]+:[0-9]+: Error: type mismatch in vector register list at operand 1 -- `luti4 {z19.b,z23,z27.b,z31.b},zt0,{z12-z13}'
+[^ :]+:[0-9]+: Error: operand 1 must be an integer register -- `movt zt,z25.b'
+[^ :]+:[0-9]+: Error: end of vector register list not found at operand 3 -- `luti4 {z20.b-z23.b},zt0,{z12.b-z13.b}'
+[^ :]+:[0-9]+: Error: end of vector register list not found at operand 3 -- `luti4 {z19.b,z23.b,z27.b,z31.b},zt0,{z12.b-z13.b}'
+[^ :]+:[0-9]+: Error: operand 2 must be an SVE vector register -- `movt zt0,z25\[1\]'
+[^ :]+:[0-9]+: Error: operand 2 must be an SVE vector register -- `movt zt0,z25\[1,mul vl\]'
+[^ :]+:[0-9]+: Error: expected an SVE vector register at operand 2 -- `movt zt0\[2,mul vl\],z32'
+[^ :]+:[0-9]+: Error: register element index out of range 0 to 3 at operand 1 -- `movt zt0\[4,mul vl\],z25'
+[^ :]+:[0-9]+: Error: register element index out of range 0 to 3 at operand 1 -- `movt zt0\[-1,mul vl\],z25'
+[^ :]+:[0-9]+: Error: expected an SVE vector register at operand 1 -- `luti4 {z32.b-z36.b},zt0,{z12-z13}'
+[^ :]+:[0-9]+: Error: invalid vector register in list at operand 3 -- `luti4 {z20.b-z23.b},zt0,{z32-z33}'
+[^ :]+:[0-9]+: Error: expected an SVE vector register at operand 1 -- `luti4 {z32.b,z36.b,z40.b,z44.b},zt0,{z12-z13}'
+[^ :]+:[0-9]+: Error: invalid vector register in list at operand 3 -- `luti4 {z19.b,z23.b,z27.b,z31.b},zt0,{z32-z33}'
+[^ :]+:[0-9]+: Error: too many registers in vector register list at operand 1 -- `luti4 {z20.b-z24.b},zt0,{z12-z13}'
+[^ :]+:[0-9]+: Error: expected a list of 4 registers at operand 1 -- `luti4 {z20.b-z22.b},zt0,{z12-z13}'
+[^ :]+:[0-9]+: Error: invalid register stride at operand 1 -- `luti4 {z20.b-z23.b},zt0,{z12-z14}'
+[^ :]+:[0-9]+: Error: invalid range in vector register list at operand 3 -- `luti4 {z20.b-z23.b},zt0,{z12-z12}'
+[^ :]+:[0-9]+: Error: invalid register list at operand 1 -- `luti4 {z19.b,z24.b,z27.b,z31.b},zt0,{z12-z13}'
+[^ :]+:[0-9]+: Error: invalid register list at operand 1 -- `luti4 {z19.b,z22.b,z27.b,z31.b},zt0,{z12-z13}'
+[^ :]+:[0-9]+: Error: the register list must have a stride of 1 at operand 1 -- `luti4 {z19.b,z23.b,z27.b,z31.b},zt0,{z12-z14}'
diff --git a/gas/testsuite/gas/aarch64/sme2-lutv2-illegal.s b/gas/testsuite/gas/aarch64/sme2-lutv2-illegal.s
new file mode 100644
index 00000000000..4e36ef687fc
--- /dev/null
+++ b/gas/testsuite/gas/aarch64/sme2-lutv2-illegal.s
@@ -0,0 +1,95 @@ 
+	// Operand mismatch
+	luti4	{ z20.b - z23.h }, zt0, { z13 - z14 }
+	luti4	{ z19.b, z23.b, z27.b, z31.h }, zt0, { z13 - z14 }
+
+	// Incorrect operands
+	movt	z3, zt0
+	movt 	z3[0], zt0
+	movt 	zt1, z25
+
+	luti4	zt0, { z0.b - z3.b }, { z0 - z1 }
+	luti4	{ z0.b - z3.b }, { z0.b - z1.b } { z0 - z1 }
+	luti4	{ }, zt0, { z12 - z13 }
+	luti4	{ z20.b - z23.b }, zt0, { }
+	luti4	{ z20.b - z23.b }, z3, { z12 - z13 }
+	luti4	{ z19.b - z22.b }, zt0, { z12 - z13 }
+	luti4	{ z20.b - z23.b }, zt0, { z13 - z14 }
+
+	luti4	zt0, { z0.b, z4.b, z8.b, z12.b }, { z0 - z1 }
+	luti4	{ z0.b, z4.b, z8.b, z12.b }, { z0.b - z1.b } { z0 - z1 }
+	luti4	{ z19.b, z23.b, z27.b, z31.b }, zt0, { }
+	luti4	{ z19.b, z23.b, z27.b, z31.b }, z3, { z12 - z13 }
+	luti4	{ z18.b, z22.b, z26.b, z30.b }, zt0, { z12 - z13 }
+	luti4	{ z19.b, z23.b, z27.b, z31.b }, zt0, { z13 - z14 }
+
+	// Disallowed types
+	luti4	{ z20.s - z23.s }, zt0, { z20 - z21 }
+	luti4	{ z19.s, z23.s, z27.s, z31.s }, zt0, { z20 - z21 }
+
+	// Index with qualifiers
+	movt zt0.b, z31
+	movt zt0.b[0, mul vl], z31
+
+	// Incorrect number of operands
+	movt	zt0
+	movt 	zt0[1, mul vl]
+	movt	zt0, z23, z31
+	movt	zt0[1, mul vl], z23, z31
+
+	luti4	{ z20.b - z23.b }
+	luti4	{ z20.b - z23.b }, zt0
+	luti4	{ z20.b - z23.b }, zt0, { z12 - z13 }, { z20 - z21 }
+
+	luti4	{ z19.b, z23.b, z27.b, z31.b }
+	luti4	{ z19.b, z23.b, z27.b, z31.b }, zt0
+	luti4	{ z19.b, z23.b, z27.b, z31.b }, zt0 { z12 - z13 }, { z20 - z21 }
+
+	// Spelling mistakes
+	movt	zy0, z16
+	movt	zt1, z16
+	movt	zy0[1, mul vl], z16
+	movt	zt0, y16
+	movt	zt0[1, mul vl], y16
+
+	luti4	{ z20.b - y23.b }, zt0, { z12 - z13 }
+	luti4	{ z20.x - z23.b }, zt0, { z12 - z13 }
+	luti4	{ z20.b - z23.b }, zy0, { z12 - z13 }
+	luti4	{ z20.b - y23.b }, zt0, { y12 - z13 }
+
+	luti4	{ z19.b, z23.b, z27.b, y31.b }, zt0, { z12 - z13 }
+	luti4	{ z19.x, z23.b, z27.b, z31.b }, zt0, { z12 - z13 }
+	luti4	{ z19.b, z23.b, z27.b, z31.b }, zy0, { z12 - z13 }
+	luti4	{ z19.b, z23.b, z27.b, z31.b }, zt0, { y12 - z13 }
+
+	// Missing qualifiers
+	luti4	{ z20 - z23.b }, zt0, { z12 - z13 }
+	luti4	{ z19.b, z23, z27.b, z31.b }, zt0, { z12 - z13 }
+
+	// Qualifier on the wrong operand
+	movt 	zt, z25.b
+	luti4	{ z20.b - z23.b }, zt0, { z12.b - z13.b }
+	luti4	{ z19.b, z23.b, z27.b, z31.b }, zt0, { z12.b - z13.b }
+
+	// Index on the wrong operand
+	movt	zt0, z25[1]
+	movt	zt0, z25[1, mul vl]
+
+	// Out of range numbers
+	movt	zt0[2, mul vl], z32
+	movt	zt0[4, mul vl], z25
+	movt	zt0[-1, mul vl], z25
+
+	luti4	{ z32.b - z36.b }, zt0, { z12 - z13 }
+	luti4	{ z20.b - z23.b }, zt0, { z32 - z33 }
+
+	luti4	{ z32.b, z36.b, z40.b, z44.b }, zt0, { z12 - z13 }
+	luti4	{ z19.b, z23.b, z27.b, z31.b }, zt0, { z32 - z33 }
+
+	// Incorrect stride
+	luti4	{ z20.b - z24.b }, zt0, { z12 - z13 }
+	luti4	{ z20.b - z22.b }, zt0, { z12 - z13 }
+	luti4	{ z20.b - z23.b }, zt0, { z12 - z14 }
+	luti4	{ z20.b - z23.b }, zt0, { z12 - z12 }
+	luti4	{ z19.b, z24.b, z27.b, z31.b }, zt0, { z12 - z13 }
+	luti4	{ z19.b, z22.b, z27.b, z31.b }, zt0, { z12 - z13 }
+	luti4	{ z19.b, z23.b, z27.b, z31.b }, zt0, { z12 - z14 }
diff --git a/gas/testsuite/gas/aarch64/sme2-lutv2.d b/gas/testsuite/gas/aarch64/sme2-lutv2.d
new file mode 100644
index 00000000000..f671982c070
--- /dev/null
+++ b/gas/testsuite/gas/aarch64/sme2-lutv2.d
@@ -0,0 +1,24 @@ 
+#objdump: -dr
+#as: -march=armv8-a+sme2+sme2p1+sme-lutv2
+
+.*:     file format .*
+
+Disassembly of section \.text:
+
+0+ <.*>:
+[^:]+:	c04f03e0 	movt	zt0\[0, mul vl\], z0
+[^:]+:	c04f03ff 	movt	zt0\[0, mul vl\], z31
+[^:]+:	c04f33e0 	movt	zt0\[3, mul vl\], z0
+[^:]+:	c04f33ff 	movt	zt0\[3, mul vl\], z31
+[^:]+:	c04f23f9 	movt	zt0\[2, mul vl\], z25
+[^:]+:	c08b0000 	luti4	{z0.b-z3.b}, zt0, {z0-z1}
+[^:]+:	c08b001c 	luti4	{z28.b-z31.b}, zt0, {z0-z1}
+[^:]+:	c08b03c0 	luti4	{z0.b-z3.b}, zt0, {z30-z31}
+[^:]+:	c08b0194 	luti4	{z20.b-z23.b}, zt0, {z12-z13}
+[^:]+:	c09b0000 	luti4	{z0.b, z4.b, z8.b, z12.b}, zt0, {z0-z1}
+[^:]+:	c09b0013 	luti4	{z19.b, z23.b, z27.b, z31.b}, zt0, {z0-z1}
+[^:]+:	c09b03c0 	luti4	{z0.b, z4.b, z8.b, z12.b}, zt0, {z30-z31}
+[^:]+:	c09b0191 	luti4	{z17.b, z21.b, z25.b, z29.b}, zt0, {z12-z13}
+[^:]+:	c08b0194 	luti4	{z20.b-z23.b}, zt0, {z12-z13}
+[^:]+:	c08b2194 	.inst	0xc08b2194 ; undefined
+[^:]+:	c09b2191 	.inst	0xc09b2191 ; undefined
diff --git a/gas/testsuite/gas/aarch64/sme2-lutv2.s b/gas/testsuite/gas/aarch64/sme2-lutv2.s
new file mode 100644
index 00000000000..cba9ad93b79
--- /dev/null
+++ b/gas/testsuite/gas/aarch64/sme2-lutv2.s
@@ -0,0 +1,22 @@ 
+	movt zt0, z0
+	movt zt0[0, mul vl], z31
+	movt zt0[3, mul vl], z0
+	movt zt0[3, mul vl], z31
+	movt zt0[2, mul vl], z25
+
+	luti4	{ z0.b - z3.b }, zt0, { z0 - z1 }
+	luti4	{ z28.b - z31.b }, zt0, { z0 - z1 }
+	luti4	{ z0.b - z3.b }, zt0, { z30 - z31 }
+	luti4	{ z20.b - z23.b }, zt0, { z12 - z13 }
+
+	luti4	{ z0.b, z4.b, z8.b, z12.b }, zt0, { z0 - z1 }
+	luti4	{ z19.b, z23.b, z27.b, z31.b }, zt0, { z0 - z1 }
+	luti4	{ z0.b, z4.b, z8.b, z12.b }, zt0, { z30 - z31 }
+	luti4	{ z17.b, z21.b, z25.b, z29.b }, zt0, { z12 - z13 }
+
+	// Explicitly listing registers in stride 1 variant
+	luti4	{ z20.b, z21.b, z22.b, z23.b }, zt0, { z12 - z13 }
+
+	// Invalid instructions because sz != 00
+	.inst	0xc08b2194
+	.inst	0xc09b2191
diff --git a/include/opcode/aarch64.h b/include/opcode/aarch64.h
index c83c0a4ebb4..19d25d6c261 100644
--- a/include/opcode/aarch64.h
+++ b/include/opcode/aarch64.h
@@ -240,6 +240,8 @@  enum aarch64_feature_bit {
   AARCH64_FEATURE_LUT,
   /* Branch Record Buffer Extension */
   AARCH64_FEATURE_BRBE,
+  /* SME LUTv2 instructions.  */
+  AARCH64_FEATURE_SME_LUTv2,
   AARCH64_NUM_FEATURES
 };
 
@@ -759,10 +761,12 @@  enum aarch64_opnd
   AARCH64_OPND_SVE_ZtxN,	/* SVE vector register list in Zt.  */
   AARCH64_OPND_SME_Zdnx2,	/* SVE vector register list from [4:1]*2.  */
   AARCH64_OPND_SME_Zdnx4,	/* SVE vector register list from [4:2]*4.  */
+  AARCH64_OPND_SME_Zdnx4_STRIDED, /* SVE vector register list from [4:2]*4.  */
   AARCH64_OPND_SME_Zm,		/* SVE vector register list in 4-bit Zm.  */
   AARCH64_OPND_SME_Zmx2,	/* SVE vector register list from [20:17]*2.  */
   AARCH64_OPND_SME_Zmx4,	/* SVE vector register list from [20:18]*4.  */
   AARCH64_OPND_SME_Znx2,	/* SVE vector register list from [9:6]*2.  */
+  AARCH64_OPND_SME_Znx2_BIT_INDEX, /* SVE vector register list encoding a bit index from [9:6]*2.  */
   AARCH64_OPND_SME_Znx4,	/* SVE vector register list from [9:7]*4.  */
   AARCH64_OPND_SME_Ztx2_STRIDED, /* SVE vector register list in [4:0]&23.  */
   AARCH64_OPND_SME_Ztx4_STRIDED, /* SVE vector register list in [4:0]&19.  */
@@ -811,6 +815,7 @@  enum aarch64_opnd
   AARCH64_OPND_SME_VLxN_13,	/* VLx2 or VLx4, in bit 13.  */
   AARCH64_OPND_SME_ZT0,		/* The fixed token zt0/ZT0 (not encoded).  */
   AARCH64_OPND_SME_ZT0_INDEX,	/* ZT0[<imm>], bits [14:12].  */
+  AARCH64_OPND_SME_ZT0_INDEX2_12, /* ZT0[<imm>], bits [13:12].  */
   AARCH64_OPND_SME_ZT0_LIST,	/* { zt0/ZT0 } (not encoded).  */
   AARCH64_OPND_TME_UIMM16,	/* TME unsigned 16-bit immediate.  */
   AARCH64_OPND_SM3_IMM2,	/* SM3 encodes lane in bits [13, 14].  */
@@ -1002,6 +1007,7 @@  enum aarch64_insn_class
   sme_shift,
   sme_size_12_bhs,
   sme_size_12_hs,
+  sme_size_12_b,
   sme_size_22,
   sme_size_22_hsd,
   sme_sz_23,
diff --git a/opcodes/aarch64-asm.c b/opcodes/aarch64-asm.c
index 5c6a31167c6..57fb8614dd0 100644
--- a/opcodes/aarch64-asm.c
+++ b/opcodes/aarch64-asm.c
@@ -2145,6 +2145,7 @@  aarch64_encode_variant_using_iclass (struct aarch64_inst *inst)
       break;
 
     case sme_size_12_bhs:
+    case sme_size_12_b:
       insert_field (FLD_SME_size_12, &inst->value,
 		    aarch64_get_variant (inst), 0);
       break;
diff --git a/opcodes/aarch64-dis.c b/opcodes/aarch64-dis.c
index 213df616608..045af49228a 100644
--- a/opcodes/aarch64-dis.c
+++ b/opcodes/aarch64-dis.c
@@ -3415,6 +3415,12 @@  aarch64_decode_variant_using_iclass (aarch64_inst *inst)
       variant -= 1;
       break;
 
+    case sme_size_12_b:
+      variant = extract_field (FLD_SME_size_12, inst->value, 0);
+      if (variant != 0)
+	return false;
+      break;
+
     case sme_size_22:
       variant = extract_field (FLD_SME_size_22, inst->value, 0);
       break;
diff --git a/opcodes/aarch64-opc.c b/opcodes/aarch64-opc.c
index ea278bfdfe5..b70467391bc 100644
--- a/opcodes/aarch64-opc.c
+++ b/opcodes/aarch64-opc.c
@@ -258,7 +258,9 @@  const aarch64_field fields[] =
     { 13,  1 }, /* SME_VL_13: VLx2 or VLx4, bit [13].  */
     {  0,  2 }, /* SME_ZAda_2b: tile ZA0-ZA3.  */
     {  0,  3 }, /* SME_ZAda_3b: tile ZA0-ZA7.  */
+    {  4,  1 }, /* SME_ZdnT: upper bit of Zt, bit [4].  */
     {  1,  4 }, /* SME_Zdn2: Z0-Z31, multiple of 2, bits [4:1].  */
+    {  0,  2 }, /* SME_Zdn2_0: lower 2 bits of Zt, bits [1:0].  */
     {  2,  3 }, /* SME_Zdn4: Z0-Z31, multiple of 4, bits [4:2].  */
     { 16,  4 }, /* SME_Zm: Z0-Z15, bits [19:16].  */
     { 17,  4 }, /* SME_Zm2: Z0-Z31, multiple of 2, bits [20:17].  */
@@ -1919,6 +1921,7 @@  operand_general_constraint_met_p (const aarch64_opnd_info *opnds, int idx,
 	case AARCH64_OPND_SME_Zmx2:
 	case AARCH64_OPND_SME_Zmx4:
 	case AARCH64_OPND_SME_Znx2:
+	case AARCH64_OPND_SME_Znx2_BIT_INDEX:
 	case AARCH64_OPND_SME_Znx4:
 	case AARCH64_OPND_SME_Zt2:
 	case AARCH64_OPND_SME_Zt3:
@@ -1934,6 +1937,7 @@  operand_general_constraint_met_p (const aarch64_opnd_info *opnds, int idx,
 	    }
 	  break;
 
+	case AARCH64_OPND_SME_Zdnx4_STRIDED:
 	case AARCH64_OPND_SME_Ztx2_STRIDED:
 	case AARCH64_OPND_SME_Ztx4_STRIDED:
 	  /* 2-register lists have a stride of 8 and 4-register lists
@@ -3153,6 +3157,14 @@  operand_general_constraint_met_p (const aarch64_opnd_info *opnds, int idx,
 	    }
 	  break;
 
+	case AARCH64_OPND_SME_ZT0_INDEX2_12:
+	  if (!value_in_range_p (opnd->imm.value, 0, 3))
+	    {
+	      set_elem_idx_out_of_range_error (mismatch_detail, idx, 0, 3);
+	      return 0;
+	    }
+	  break;
+
 	default:
 	  break;
 	}
@@ -3745,9 +3757,14 @@  print_register_list (char *buf, size_t size, const aarch64_opnd_info *opnd,
       && ((opnd->type != AARCH64_OPND_SME_Zt2)
 	  && (opnd->type != AARCH64_OPND_SME_Zt3)
 	  && (opnd->type != AARCH64_OPND_SME_Zt4)))
-    snprintf (buf, size, "{%s-%s}%s",
-	      style_reg (styler, "%s%d.%s", prefix, first_reg, qlf_name),
-	      style_reg (styler, "%s%d.%s", prefix, last_reg, qlf_name), tb);
+    if (opnd->qualifier == AARCH64_OPND_QLF_NIL)
+      snprintf (buf, size, "{%s-%s}%s",
+		style_reg (styler, "%s%d", prefix, first_reg),
+		style_reg (styler, "%s%d", prefix, last_reg), tb);
+    else
+      snprintf (buf, size, "{%s-%s}%s",
+		style_reg (styler, "%s%d.%s", prefix, first_reg, qlf_name),
+		style_reg (styler, "%s%d.%s", prefix, last_reg, qlf_name), tb);
   else
     {
       const int reg0 = first_reg;
@@ -4216,9 +4233,11 @@  aarch64_print_operand (char *buf, size_t size, bfd_vma pc,
     case AARCH64_OPND_SVE_ZtxN:
     case AARCH64_OPND_SME_Zdnx2:
     case AARCH64_OPND_SME_Zdnx4:
+    case AARCH64_OPND_SME_Zdnx4_STRIDED:
     case AARCH64_OPND_SME_Zmx2:
     case AARCH64_OPND_SME_Zmx4:
     case AARCH64_OPND_SME_Znx2:
+    case AARCH64_OPND_SME_Znx2_BIT_INDEX:
     case AARCH64_OPND_SME_Znx4:
     case AARCH64_OPND_SME_Ztx2_STRIDED:
     case AARCH64_OPND_SME_Ztx4_STRIDED:
@@ -4906,6 +4925,11 @@  aarch64_print_operand (char *buf, size_t size, bfd_vma pc,
       snprintf (buf, size, "%s[%s]", style_reg (styler, "zt0"),
 		style_imm (styler, "%d", (int) opnd->imm.value));
       break;
+    case AARCH64_OPND_SME_ZT0_INDEX2_12:
+      snprintf (buf, size, "%s[%s, %s]", style_reg (styler, "zt0"),
+		style_imm (styler, "%d", (int) opnd->imm.value),
+		style_sub_mnem (styler, "mul vl"));
+      break;
 
     case AARCH64_OPND_SME_ZT0_LIST:
       snprintf (buf, size, "{%s}", style_reg (styler, "zt0"));
diff --git a/opcodes/aarch64-opc.h b/opcodes/aarch64-opc.h
index 9b734d63a3b..8804e7c0038 100644
--- a/opcodes/aarch64-opc.h
+++ b/opcodes/aarch64-opc.h
@@ -62,7 +62,9 @@  enum aarch64_field_kind
   FLD_SME_VL_13,
   FLD_SME_ZAda_2b,
   FLD_SME_ZAda_3b,
+  FLD_SME_ZdnT,
   FLD_SME_Zdn2,
+  FLD_SME_Zdn2_0,
   FLD_SME_Zdn4,
   FLD_SME_Zm,
   FLD_SME_Zm2,
diff --git a/opcodes/aarch64-tbl.h b/opcodes/aarch64-tbl.h
index bffb422583a..58765928539 100644
--- a/opcodes/aarch64-tbl.h
+++ b/opcodes/aarch64-tbl.h
@@ -1814,6 +1814,13 @@ 
 {                                                       \
   QLF2(S_S,NIL),                                        \
 }
+/* e.g. movt ZT0{[<offs>, MUL VL]}, <Zt>  */
+/* The second operand doesn't have a qualifier and
+   is checked separetely during encoding.  */
+#define OP_SVE_SU_Q					\
+{                                                       \
+  QLF2(S_Q,NIL),                                        \
+}
 #define OP_SVE_SUS                                      \
 {                                                       \
   QLF3(S_S,NIL,S_S),                                    \
@@ -2050,6 +2057,13 @@ 
   QLF3(S_S,NIL,W),                                      \
   QLF3(S_D,NIL,X),                                      \
 }
+/* e.g. luti4 { <Zd1>.B-<Zd4>.B }, ZT0, { <Zn1>-<Zn2> }  */
+/* The second and third operands don't have qualifiers and
+   are checked separetely during encoding.  */
+#define OP_SVE_VUU_B					\
+{                                                       \
+  QLF3(S_B,NIL,NIL),                                    \
+}
 #define OP_SVE_VUU_BHS                                  \
 {                                                       \
   QLF3(S_B,NIL,NIL),                                    \
@@ -2752,6 +2766,8 @@  static const aarch64_feature_set aarch64_feature_lut_sve2 =
   AARCH64_FEATURES (2, LUT, SVE2);
 static const aarch64_feature_set aarch64_feature_brbe =
   AARCH64_FEATURE (BRBE);
+static const aarch64_feature_set aarch64_feature_sme_lutv2 =
+  AARCH64_FEATURES (3, SME_LUTv2, SME2, SME2p1);
 
 #define CORE		&aarch64_feature_v8
 #define FP		&aarch64_feature_fp
@@ -2829,6 +2845,7 @@  static const aarch64_feature_set aarch64_feature_brbe =
 #define LUT &aarch64_feature_lut
 #define LUT_SVE2 &aarch64_feature_lut_sve2
 #define BRBE		&aarch64_feature_brbe
+#define LUTv2_SME2 &aarch64_feature_sme_lutv2
 
 #define CORE_INSN(NAME,OPCODE,MASK,CLASS,OP,OPS,QUALS,FLAGS) \
   { NAME, OPCODE, MASK, CLASS, OP, CORE, OPS, QUALS, FLAGS, 0, 0, NULL }
@@ -3029,6 +3046,9 @@  static const aarch64_feature_set aarch64_feature_brbe =
     FLAGS, CONSTRAINTS, 0, NULL }
 #define BRBE_INSN(NAME,OPCODE,MASK,OPS,QUALS,FLAGS) \
   { NAME, OPCODE, MASK, ic_system, 0, BRBE, OPS, QUALS, FLAGS, 0, 0, NULL }
+#define LUTv2_SME2_INSN(NAME,OPCODE,MASK,CLASS,OPS,QUALS,FLAGS)	\
+  { NAME, OPCODE, MASK, CLASS, 0, LUTv2_SME2, OPS, QUALS, \
+    FLAGS, 0, 0, NULL }
 
 #define MOPS_CPY_OP1_OP2_PME_INSN(NAME, OPCODE, MASK, FLAGS, CONSTRAINTS) \
   MOPS_INSN (NAME, OPCODE, MASK, 0, \
@@ -6615,6 +6635,11 @@  const struct aarch64_opcode aarch64_opcode_table[] =
   LUT_SVE2_INSN ("luti4", 0x4520b400, 0xff20fc00, OP3 (SVE_Zd, SVE_ZnxN, SVE_Zm2_22_INDEX), OP_SVE_HHU, F_OD(2), 0),
   LUT_SVE2_INSN ("luti4", 0x4520bc00, 0xff20fc00, OP3 (SVE_Zd, SVE_ZnxN, SVE_Zm2_22_INDEX), OP_SVE_HHU, F_OD(1), 0),
 
+  /* SME2 lutv2.  */
+  LUTv2_SME2_INSN ("luti4", 0xc08b0000, 0xffffcc23, sme_size_12_b, OP3 (SME_Zdnx4, SME_ZT0, SME_Znx2_BIT_INDEX), OP_SVE_VUU_B, F_STRICT | 0),
+  LUTv2_SME2_INSN ("luti4", 0xc09b0000, 0xffffcc2c, sme_size_12_b, OP3 (SME_Zdnx4_STRIDED, SME_ZT0, SME_Znx2_BIT_INDEX), OP_SVE_VUU_B, F_STRICT | 0),
+  LUTv2_SME2_INSN ("movt", 0xc04f03e0, 0xffffcfe0, sme_misc, OP2 (SME_ZT0_INDEX2_12, SVE_Zt), {}, 0),
+
   {0, 0, 0, 0, 0, 0, {}, {}, 0, 0, 0, NULL},
 };
 
@@ -7139,6 +7164,9 @@  const struct aarch64_opcode aarch64_opcode_table[] =
       F(FLD_SME_Zdn2), "a list of SVE vector registers")		\
     Y(SVE_REGLIST, sve_aligned_reglist, "SME_Zdnx4", 4 << OPD_F_OD_LSB,	\
       F(FLD_SME_Zdn4), "a list of SVE vector registers")		\
+    Y(SVE_REGLIST, sve_strided_reglist, "SME_Zdnx4_STRIDED",		\
+      4 << OPD_F_OD_LSB, F(FLD_SME_ZdnT, FLD_SME_Zdn2_0),		\
+      "a list of SVE vector registers")					\
     Y(SVE_REG, regno, "SME_Zm", 0, F(FLD_SME_Zm),			\
       "an SVE vector register")						\
     Y(SVE_REGLIST, sve_aligned_reglist, "SME_Zmx2", 2 << OPD_F_OD_LSB,	\
@@ -7147,6 +7175,9 @@  const struct aarch64_opcode aarch64_opcode_table[] =
       F(FLD_SME_Zm4), "a list of SVE vector registers")			\
     Y(SVE_REGLIST, sve_aligned_reglist, "SME_Znx2", 2 << OPD_F_OD_LSB,	\
       F(FLD_SME_Zn2), "a list of SVE vector registers")			\
+    Y(SVE_REGLIST, sve_aligned_reglist, "SME_Znx2_BIT_INDEX",		\
+      2 << OPD_F_OD_LSB, F(FLD_SME_Zn2),				\
+      "a list of SVE vector registers")					\
     Y(SVE_REGLIST, sve_aligned_reglist, "SME_Znx4", 4 << OPD_F_OD_LSB,	\
       F(FLD_SME_Zn4), "a list of SVE vector registers")			\
     Y(SVE_REGLIST, sve_strided_reglist, "SME_Ztx2_STRIDED",		\
@@ -7257,6 +7288,8 @@  const struct aarch64_opcode aarch64_opcode_table[] =
     Y(SYSTEM, none, "SME_ZT0", 0, F (), "ZT0")				\
     Y(IMMEDIATE, imm, "SME_ZT0_INDEX", OPD_F_SHIFT_BY_3,		\
       F (FLD_imm3_12), "a ZT0 index")					\
+    Y(IMMEDIATE, imm, "SME_ZT0_INDEX2_12", 0,				\
+      F (FLD_imm3_12), "a ZT0 index")					\
     Y(SYSTEM, none, "SME_ZT0_LIST", 0, F (), "{ ZT0 }")			\
     Y(IMMEDIATE, imm, "TME_UIMM16", 0, F(FLD_imm16_5),			\
       "a 16-bit unsigned immediate for TME tcancel")			\