[v4,5/7] x86: add ACE V1 block scale register instructions

Message ID ae54cf1f99aee4fc8b1ea144f14408dec7595716.1790705096.git.RohanRadhesh.Shenoy@amd.com
State New
Headers
Series x86: add ACE V1 ISA support |

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 success Test passed
linaro-tcwg-bot/tcwg_binutils_check--master-arm success Test passed

Commit Message

Rohan Shenoy Sept. 29, 2026, 6:30 p.m. UTC
  Add register bsr0, and assembler and disassembler support for
BSRINIT, BSRMOVF, BSRMOVH, and BSRMOVL.

Reference: AI Compute Extensions (ACE) Specification, v1.16.2
https://x86ecosystem.org/wp-content/uploads/2026/06/ACE_v1_Specification_public.pdf

Co-authored-by: Ganesh Gopalasubramanian <Ganesh.Gopalasubramanian@amd.com>
Co-authored-by: Umesh Kalvakuntla <Umesh.Kalvakuntla@amd.com>
---
 gas/config/tc-i386.c                   | 28 ++++++++++++-----
 gas/testsuite/gas/i386/x86-64-ace_v1.d | 11 +++++++
 gas/testsuite/gas/i386/x86-64-ace_v1.s | 10 ++++++
 opcodes/i386-dis-evex-len.h            | 12 ++++++++
 opcodes/i386-dis-evex-prefix.h         | 14 +++++++++
 opcodes/i386-dis-evex-w.h              |  7 ++++-
 opcodes/i386-dis-evex-x86-64.h         |  5 +++
 opcodes/i386-dis-evex.h                |  2 +-
 opcodes/i386-dis.c                     | 42 ++++++++++++++++++++++++++
 opcodes/i386-opc.tbl                   | 11 +++++++
 opcodes/i386-reg.tbl                   |  2 ++
 11 files changed, 134 insertions(+), 10 deletions(-)
  

Comments

Jan Beulich Oct. 2, 2026, 7:53 a.m. UTC | #1
On 29.09.2026 20:30, Rohan Shenoy wrote:
> @@ -10237,7 +10242,8 @@ match_template (char mnem_suffix)
>        i.tm.opcode_modifier.vexw ^= VEXW0 ^ VEXW1;
>  
>        /* In 3-operand insns XOP.W changes which operand goes into XOP.vvvv.  */
> -      i.tm.opcode_modifier.vexvvvv = VexVVVV_SRC1;
> +      if (!is_cpu (t, CpuACE_V1))
> +	i.tm.opcode_modifier.vexvvvv = VexVVVV_SRC1;

The comment wants slightly adjusting to continue to fit the actual code.
Perhaps as simple as adding "most".

> @@ -17091,6 +17097,12 @@ static bool check_register (const reg_entry *r)
>    if (r->reg_type.bitfield.class == RegBND && !cpu_arch_flags.bitfield.cpumpx)
>      return false;
>  
> +  if (r->reg_type.bitfield.class == RegSIMD
> +      && r->reg_type.bitfield.byte
> +      && (!cpu_arch_flags.bitfield.cpuace_v1
> +	  || flag_code != CODE_64BIT))
> +    return false;

Imo this would better move up ahead of the RegBND check, to be adjacent to
the similar and related tile register check. This then also fits the
ordering in opcodes/i386-reg.tbl.

> --- a/opcodes/i386-dis-evex-len.h
> +++ b/opcodes/i386-dis-evex-len.h
> @@ -212,4 +212,16 @@ static const struct dis386 evex_len_table[][3] = {
>      { "vbmacxor16x16x16", { XM, Vex, EXx }, NO_PREFIX },
>      { "vbmacxor16x16x16", { XM, Vex, EXx }, NO_PREFIX },
>    },
> +  /* EVEX_LEN_MAP6_95_X86_64_W_0 */
> +  {
> +    { Bad_Opcode },
> +    { Bad_Opcode },
> +    { PREFIX_TABLE (PREFIX_EVEX_MAP6_95_X86_64_W_0_L_2) },
> +  },
> +  /* EVEX_LEN_MAP6_95_X86_64_W_1 */
> +  {
> +    { Bad_Opcode },
> +    { Bad_Opcode },
> +    { PREFIX_TABLE (PREFIX_EVEX_MAP6_95_X86_64_W_1_L_2) },
> +  },
>  };

If you decode L'L before W, you need only one new table entry here, and
the number of table entries elsewhere doesn't change.

> --- a/opcodes/i386-opc.tbl
> +++ b/opcodes/i386-opc.tbl
> @@ -62,6 +62,8 @@
>  
>  #define RegBND Class=RegBND
>  
> +#define RegBSR Class=RegSIMD|Byte

Please move up a few lines, adjacent to the other RegSIMD ones.

Jan
  

Patch

diff --git a/gas/config/tc-i386.c b/gas/config/tc-i386.c
index de99474e847..c111ea9f4a0 100644
--- a/gas/config/tc-i386.c
+++ b/gas/config/tc-i386.c
@@ -2613,7 +2613,10 @@  match_simd_size (const i386_operand_type *t_types, unsigned int wanted,
 	   || (i.types[given].bitfield.zmmword
 	       && !t_types[wanted].bitfield.zmmword)
 	   || (i.types[given].bitfield.tmmword
-	       && !t_types[wanted].bitfield.tmmword));
+	       && !t_types[wanted].bitfield.tmmword)
+	   /* RegSIMD|Byte marks %bsr0; it is not the operand size.  */
+	   || (i.types[given].bitfield.byte
+	       && !t_types[wanted].bitfield.byte));
 }
 
 /* Return 1 if there is no conflict in any size between operand GIVEN
@@ -3972,6 +3975,7 @@  const type_names[] =
   { { .bitfield = { .class = RegSIMD, .tmmword = 1 } }, "rTMM" },
   { { .bitfield = { .class = RegMask } }, "Mask reg" },
   { { .bitfield = { .class = RegBND } }, "rBND" },
+  { { .bitfield = { .class = RegSIMD, .byte = 1 } }, "bsr" },
 };
 
 static void
@@ -9911,7 +9915,8 @@  match_template (char mnem_suffix)
 		      && (intel_syntax || intel_mnemonic))
 		    found_reverse_match |= Opcode_FloatR;
 		}
-	      else if (is_cpu (t, CpuFMA4) || is_cpu (t, CpuXOP))
+	      else if (is_cpu (t, CpuFMA4) || is_cpu (t, CpuXOP)
+		       || is_cpu (t, CpuACE_V1))
 		{
 		  found_reverse_match = Opcode_VexW;
 		  goto check_operands_345;
@@ -10237,7 +10242,8 @@  match_template (char mnem_suffix)
       i.tm.opcode_modifier.vexw ^= VEXW0 ^ VEXW1;
 
       /* In 3-operand insns XOP.W changes which operand goes into XOP.vvvv.  */
-      i.tm.opcode_modifier.vexvvvv = VexVVVV_SRC1;
+      if (!is_cpu (t, CpuACE_V1))
+	i.tm.opcode_modifier.vexvvvv = VexVVVV_SRC1;
 
     swap_first_2:
       j = i.tm_types[0].bitfield.imm8;
@@ -11113,11 +11119,11 @@  process_operands (void)
   else if (i.tm.opcode_modifier.immext)
     process_immext ();
 
-  /* TILEZERO is unusual in that it has a single operand encoded in ModR/M.reg,
-     not ModR/M.rm.  To avoid special casing this in build_modrm_byte(), fake a
-     new destination operand here, while converting the source one to register
-     number 0.  */
-  if (i.tm.mnem_off == MN_tilezero)
+  /* TILEZERO and BSRINIT are unusual in that they have a single operand
+     encoded in ModR/M.reg, not ModR/M.rm.  To avoid special casing this in
+     build_modrm_byte(), fake a new destination operand here, while converting
+     the source one to register number 0.  */
+  if (i.tm.mnem_off == MN_tilezero || i.tm.mnem_off == MN_bsrinit)
     {
       copy_operand (1, 0);
       i.op[0].regs -= i.op[0].regs->reg_num;
@@ -17091,6 +17097,12 @@  static bool check_register (const reg_entry *r)
   if (r->reg_type.bitfield.class == RegBND && !cpu_arch_flags.bitfield.cpumpx)
     return false;
 
+  if (r->reg_type.bitfield.class == RegSIMD
+      && r->reg_type.bitfield.byte
+      && (!cpu_arch_flags.bitfield.cpuace_v1
+	  || flag_code != CODE_64BIT))
+    return false;
+
   /* Don't allow fake index register unless allow_index_reg isn't 0. */
   if (!allow_index_reg && r->reg_num == RegIZ)
     return false;
diff --git a/gas/testsuite/gas/i386/x86-64-ace_v1.d b/gas/testsuite/gas/i386/x86-64-ace_v1.d
index df5290c55a9..256792a8248 100644
--- a/gas/testsuite/gas/i386/x86-64-ace_v1.d
+++ b/gas/testsuite/gas/i386/x86-64-ace_v1.d
@@ -20,7 +20,18 @@  Disassembly of section \.text:
 [ 	]*[a-f0-9]+:[ 	]*62 63 7f 48 07 f5 08[ 	]+tcvtrowps2bf16h \$0x8,%tmm5,%zmm30
 [ 	]*[a-f0-9]+:[ 	]*62 62 6c 48 6d f5[ 	]+tcvtrowps2phh %edx,%tmm5,%zmm30
 [ 	]*[a-f0-9]+:[ 	]*62 63 7c 48 07 f5 08[ 	]+tcvtrowps2phh \$0x8,%tmm5,%zmm30
+[ 	]*[a-f0-9]+:[ 	]*62 d6 ff 48 95 c2[ 	]+bsrmovh %zmm10,%bsr0
+[ 	]*[a-f0-9]+:[ 	]*62 f6 ff 48 95 41 7f[ 	]+bsrmovh 0x1fc0\(%rcx\),%bsr0
+[ 	]*[a-f0-9]+:[ 	]*62 f6 7f 48 95 c4[ 	]+bsrmovh %bsr0,%zmm4
+[ 	]*[a-f0-9]+:[ 	]*62 f6 7f 48 95 41 80[ 	]+bsrmovh %bsr0,-0x2000\(%rcx\)
 [ 	]*[a-f0-9]+:[ 	]*62 62 6e 48 6d f5[ 	]+tcvtrowps2bf16l %edx,%tmm5,%zmm30
 [ 	]*[a-f0-9]+:[ 	]*62 63 7e 48 77 f5 08[ 	]+tcvtrowps2bf16l \$0x8,%tmm5,%zmm30
 [ 	]*[a-f0-9]+:[ 	]*62 62 6d 48 6d f5[ 	]+tcvtrowps2phl %edx,%tmm5,%zmm30
 [ 	]*[a-f0-9]+:[ 	]*62 63 7f 48 77 f5 08[ 	]+tcvtrowps2phl \$0x8,%tmm5,%zmm30
+[ 	]*[a-f0-9]+:[ 	]*62 d6 fe 48 95 c2[ 	]+bsrmovl %zmm10,%bsr0
+[ 	]*[a-f0-9]+:[ 	]*62 f6 fe 48 95 41 7f[ 	]+bsrmovl 0x1fc0\(%rcx\),%bsr0
+[ 	]*[a-f0-9]+:[ 	]*62 f6 7e 48 95 c4[ 	]+bsrmovl %bsr0,%zmm4
+[ 	]*[a-f0-9]+:[ 	]*62 f6 7e 48 95 41 80[ 	]+bsrmovl %bsr0,-0x2000\(%rcx\)
+[ 	]*[a-f0-9]+:[ 	]*c4 e2 fb 49 c0[ 	]+bsrinit %bsr0
+[ 	]*[a-f0-9]+:[ 	]*62 f6 f4 48 95 c3[ 	]+bsrmovf %zmm3,%zmm1,%bsr0
+[ 	]*[a-f0-9]+:[ 	]*62 f6 f4 48 95 41 7f[ 	]+bsrmovf 0x1fc0\(%rcx\),%zmm1,%bsr0
diff --git a/gas/testsuite/gas/i386/x86-64-ace_v1.s b/gas/testsuite/gas/i386/x86-64-ace_v1.s
index 5bec9cf9e89..4bb4152789a 100644
--- a/gas/testsuite/gas/i386/x86-64-ace_v1.s
+++ b/gas/testsuite/gas/i386/x86-64-ace_v1.s
@@ -19,4 +19,14 @@  start:
 
 	tcvtrowps2ph\p   %edx, %tmm5, %zmm30
 	tcvtrowps2ph\p   $8, %tmm5, %zmm30
+
+	bsrmov\p %zmm10, %bsr0
+	bsrmov\p 8128(%rcx), %bsr0
+
+	bsrmov\p %bsr0, %zmm4
+	bsrmov\p %bsr0, -8192(%rcx)
 	.endr
+
+	bsrinit %bsr0
+	bsrmovf %zmm3, %zmm1, %bsr0
+	bsrmovf 8128(%rcx), %zmm1, %bsr0
diff --git a/opcodes/i386-dis-evex-len.h b/opcodes/i386-dis-evex-len.h
index 23e9487ec68..178ad1c3ff0 100644
--- a/opcodes/i386-dis-evex-len.h
+++ b/opcodes/i386-dis-evex-len.h
@@ -212,4 +212,16 @@  static const struct dis386 evex_len_table[][3] = {
     { "vbmacxor16x16x16", { XM, Vex, EXx }, NO_PREFIX },
     { "vbmacxor16x16x16", { XM, Vex, EXx }, NO_PREFIX },
   },
+  /* EVEX_LEN_MAP6_95_X86_64_W_0 */
+  {
+    { Bad_Opcode },
+    { Bad_Opcode },
+    { PREFIX_TABLE (PREFIX_EVEX_MAP6_95_X86_64_W_0_L_2) },
+  },
+  /* EVEX_LEN_MAP6_95_X86_64_W_1 */
+  {
+    { Bad_Opcode },
+    { Bad_Opcode },
+    { PREFIX_TABLE (PREFIX_EVEX_MAP6_95_X86_64_W_1_L_2) },
+  },
 };
diff --git a/opcodes/i386-dis-evex-prefix.h b/opcodes/i386-dis-evex-prefix.h
index 0135703912a..9ec02d184a3 100644
--- a/opcodes/i386-dis-evex-prefix.h
+++ b/opcodes/i386-dis-evex-prefix.h
@@ -833,6 +833,20 @@ 
     { Bad_Opcode },
     { "vfnmsub231p%XH",	{ XM, Vex, EXxh, EXxEVexR }, 0 },
   },
+  /* PREFIX_EVEX_MAP6_95_X86_64_W_0_L_2 */
+  {
+    { Bad_Opcode },
+    { "bsrmovl", { EXEvexXNoBcst, BSR }, 0 },
+    { Bad_Opcode },
+    { "bsrmovh", { EXEvexXNoBcst, BSR }, 0 },
+  },
+  /* PREFIX_EVEX_MAP6_95_X86_64_W_1_L_2 */
+  {
+    { "bsrmovf", { BSR, Vex, EXEvexXNoBcst }, 0 },
+    { "bsrmovl", { BSR, EXEvexXNoBcst }, 0 },
+    { Bad_Opcode },
+    { "bsrmovh", { BSR, EXEvexXNoBcst }, 0 },
+  },
   /* PREFIX_EVEX_MAP6_D6 */
   {
     { Bad_Opcode },
diff --git a/opcodes/i386-dis-evex-w.h b/opcodes/i386-dis-evex-w.h
index f0f00e44143..0d8ccb1769e 100644
--- a/opcodes/i386-dis-evex-w.h
+++ b/opcodes/i386-dis-evex-w.h
@@ -570,4 +570,9 @@ 
   {
     { "vbitrevb", { XM, EXx }, NO_PREFIX },
     { Bad_Opcode }
-  }
+  },
+  /* EVEX_W_MAP6_95_X86_64 */
+  {
+    { EVEX_LEN_TABLE (EVEX_LEN_MAP6_95_X86_64_W_0) },
+    { EVEX_LEN_TABLE (EVEX_LEN_MAP6_95_X86_64_W_1) },
+  },
diff --git a/opcodes/i386-dis-evex-x86-64.h b/opcodes/i386-dis-evex-x86-64.h
index 8f8f5dfcd96..c59e12657de 100644
--- a/opcodes/i386-dis-evex-x86-64.h
+++ b/opcodes/i386-dis-evex-x86-64.h
@@ -28,3 +28,8 @@ 
     { Bad_Opcode },
     { PREFIX_TABLE (PREFIX_EVEX_MAP5_6F_X86_64) },
   },
+  /* X86_64_EVEX_MAP6_95 */
+  {
+    { Bad_Opcode },
+    { VEX_W_TABLE (EVEX_W_MAP6_95_X86_64) },
+  },
diff --git a/opcodes/i386-dis-evex.h b/opcodes/i386-dis-evex.h
index 52bf0d798ac..283287b6138 100644
--- a/opcodes/i386-dis-evex.h
+++ b/opcodes/i386-dis-evex.h
@@ -1624,7 +1624,7 @@  static const struct dis386 evex_table[][256] = {
     { Bad_Opcode },
     { Bad_Opcode },
     { Bad_Opcode },
-    { Bad_Opcode },
+    { X86_64_TABLE (X86_64_EVEX_MAP6_95) },
     { "vfmaddsub132p%XH",  { XM, Vex, EXxh, EXxEVexR }, PREFIX_DATA },
     { "vfmsubadd132p%XH",  { XM, Vex, EXxh, EXxEVexR }, PREFIX_DATA },
     /* 98 */
diff --git a/opcodes/i386-dis.c b/opcodes/i386-dis.c
index 95dbf96156f..a9377a6f0da 100644
--- a/opcodes/i386-dis.c
+++ b/opcodes/i386-dis.c
@@ -513,6 +513,7 @@  fetch_error (const instr_info *ins)
 #define RMBH { OP_REG, bh_reg }
 #define RMAX { OP_REG, ax_reg }
 #define RMDX { OP_REG, dx_reg }
+#define BSR { OP_G, bsr_mode }
 
 #define eAX { OP_IMREG, eAX_reg }
 #define AL { OP_IMREG, al_reg }
@@ -722,6 +723,8 @@  enum
   ymmq_mode,
   /* TMM operand */
   tmm_mode,
+  /* Block scale register operand.  */
+  bsr_mode,
   /* d_mode in 32bit, q_mode in 64bit mode.  */
   m_mode,
   /* pair of v_mode operands */
@@ -971,6 +974,7 @@  enum
   MOD_0F38F8,
 
   MOD_VEX_0F3849_X86_64_L_0_W_0,
+  MOD_VEX_0F3849_X86_64_L_0_W_1,
 
   MOD_EVEX_MAP4_60,
   MOD_EVEX_MAP4_61,
@@ -995,6 +999,7 @@  enum
 
   RM_VEX_0F3849_X86_64_L_0_W_0_M_1_P_0_R_0,
   RM_VEX_0F3849_X86_64_L_0_W_0_M_1_P_3,
+  RM_VEX_0F3849_X86_64_L_0_W_1_M_1_P_3,
 };
 
 enum
@@ -1141,6 +1146,7 @@  enum
   PREFIX_VEX_0F3848_X86_64_L_0_W_0,
   PREFIX_VEX_0F3849_X86_64_L_0_W_0_M_0,
   PREFIX_VEX_0F3849_X86_64_L_0_W_0_M_1,
+  PREFIX_VEX_0F3849_X86_64_L_0_W_1_M_1,
   PREFIX_VEX_0F384A_X86_64_W_0_L_0,
   PREFIX_VEX_0F384B_X86_64_L_0_W_0,
   PREFIX_VEX_0F3850_W_0,
@@ -1311,6 +1317,8 @@  enum
   PREFIX_EVEX_MAP6_BA,
   PREFIX_EVEX_MAP6_BC,
   PREFIX_EVEX_MAP6_BE,
+  PREFIX_EVEX_MAP6_95_X86_64_W_0_L_2,
+  PREFIX_EVEX_MAP6_95_X86_64_W_1_L_2,
   PREFIX_EVEX_MAP6_D6,
   PREFIX_EVEX_MAP6_D7,
 };
@@ -1410,6 +1418,7 @@  enum
   X86_64_EVEX_0F3A77,
 
   X86_64_EVEX_MAP5_6F,
+  X86_64_EVEX_MAP6_95,
 };
 
 enum
@@ -1630,6 +1639,8 @@  enum
   EVEX_LEN_MAP5_7E,
   EVEX_LEN_MAP6_80_W_0,
   EVEX_LEN_MAP6_80_W_1,
+  EVEX_LEN_MAP6_95_X86_64_W_0,
+  EVEX_LEN_MAP6_95_X86_64_W_1,
 };
 
 enum
@@ -1894,6 +1905,7 @@  enum
   EVEX_W_MAP5_7E_P_1,
   EVEX_W_MAP6_80,
   EVEX_W_MAP6_81,
+  EVEX_W_MAP6_95_X86_64,
 };
 
 typedef bool (*op_rtn) (instr_info *ins, int bytemode, int sizeflag);
@@ -2720,6 +2732,10 @@  static const char att_names_tmm[][8] = {
   "%tmm4", "%tmm5", "%tmm6", "%tmm7"
 };
 
+static const char att_names_bsr[][8] = {
+  "%bsr0"
+};
+
 static const char att_names_mask[][8] = {
   "%k0", "%k1", "%k2", "%k3", "%k4", "%k5", "%k6", "%k7"
 };
@@ -4187,6 +4203,14 @@  static const struct dis386 prefix_table[][4] = {
     { RM_TABLE (RM_VEX_0F3849_X86_64_L_0_W_0_M_1_P_3) },
   },
 
+  /* PREFIX_VEX_0F3849_X86_64_L_0_W_1_M_1 */
+  {
+    { Bad_Opcode },
+    { Bad_Opcode },
+    { Bad_Opcode },
+    { RM_TABLE (RM_VEX_0F3849_X86_64_L_0_W_1_M_1_P_3) },
+  },
+
   /* PREFIX_VEX_0F384A_X86_64_W_0_L_0 */
   {
     { Bad_Opcode },
@@ -8138,6 +8162,7 @@  static const struct dis386 vex_w_table[][2] = {
   {
     /* VEX_W_0F3849_X86_64_L_0 */
     { MOD_TABLE (MOD_VEX_0F3849_X86_64_L_0_W_0) },
+    { MOD_TABLE (MOD_VEX_0F3849_X86_64_L_0_W_1) },
   },
   {
     /* VEX_W_0F384A_X86_64 */
@@ -8714,6 +8739,11 @@  static const struct dis386 mod_table[][2] = {
     { PREFIX_TABLE (PREFIX_VEX_0F3849_X86_64_L_0_W_0_M_0) },
     { PREFIX_TABLE (PREFIX_VEX_0F3849_X86_64_L_0_W_0_M_1) },
   },
+  {
+    /* MOD_VEX_0F3849_X86_64_L_0_W_1 */
+    { Bad_Opcode },
+    { PREFIX_TABLE (PREFIX_VEX_0F3849_X86_64_L_0_W_1_M_1) },
+  },
 
 #include "i386-dis-evex-mod.h"
 };
@@ -8824,6 +8854,10 @@  static const struct dis386 rm_table[][8] = {
     /* RM_VEX_0F3849_X86_64_L_0_W_0_M_1_P_3 */
     { "tilezero",	{ TMM, Skip_MODRM }, 0 },
   },
+  {
+    /* RM_VEX_0F3849_X86_64_L_0_W_1_M_1_P_3 */
+    { "bsrinit",	{ BSR, Skip_MODRM }, 0 },
+  },
 };
 
 #define INTERNAL_DISASSEMBLER_ERROR _("<internal disassembler error>")
@@ -12123,6 +12157,14 @@  print_register (instr_info *ins, unsigned int reg, unsigned int rexmask,
 	}
       names = att_names_bnd;
       break;
+    case bsr_mode:
+      if (reg)
+	{
+	  oappend (ins, "(bad)");
+	  return;
+	}
+      names = att_names_bsr;
+      break;
     case indir_v_mode:
       if (ins->address_mode == mode_64bit && ins->isa64 == intel64)
 	{
diff --git a/opcodes/i386-opc.tbl b/opcodes/i386-opc.tbl
index 2cf717b48a1..f1fa86b76c3 100644
--- a/opcodes/i386-opc.tbl
+++ b/opcodes/i386-opc.tbl
@@ -62,6 +62,8 @@ 
 
 #define RegBND Class=RegBND
 
+#define RegBSR Class=RegSIMD|Byte
+
 #define Mmword Qword
 #define Oword  Xmmword
 
@@ -3316,6 +3318,15 @@  tdphf8ps, 0x66fd, AMX_FP8, Modrm|Vex128|Map5|Src2VVVV|VexW0|NoSuf, { RegTMM, Reg
 
 // AMX instructions end.
 
+// ACE instructions.
+
+bsrinit, 0xf249, ACE_V1, Modrm|Vex128|Space0F38|VexW1|NoSuf, { RegBSR }
+bsrmovf, 0x95, ACE_V1, Modrm|EVex512|Map6|VexW1|Src1VVVV|Disp8MemShift=6|NoSuf, { RegZMM|Unspecified|BaseIndex, RegZMM, RegBSR }
+bsrmovh, 0xf295, ACE_V1, D|Modrm|EVex512|Map6|VexW1|Disp8MemShift=6|NoSuf, { RegZMM|Unspecified|BaseIndex, RegBSR }
+bsrmovl, 0xf395, ACE_V1, D|Modrm|EVex512|Map6|VexW1|Disp8MemShift=6|NoSuf, { RegZMM|Unspecified|BaseIndex, RegBSR }
+
+// ACE instructions end.
+
 // KEYLOCKER instructions.
 
 loadiwkey, 0xf30f38dc, KL, Load|Modrm|NoSuf, { RegXMM, RegXMM }
diff --git a/opcodes/i386-reg.tbl b/opcodes/i386-reg.tbl
index c042c182921..311013f6dd3 100644
--- a/opcodes/i386-reg.tbl
+++ b/opcodes/i386-reg.tbl
@@ -353,6 +353,8 @@  tmm4, Class=RegSIMD|Tmmword, 0, 4, Dw2Inval, Dw2Inval
 tmm5, Class=RegSIMD|Tmmword, 0, 5, Dw2Inval, Dw2Inval
 tmm6, Class=RegSIMD|Tmmword, 0, 6, Dw2Inval, Dw2Inval
 tmm7, Class=RegSIMD|Tmmword, 0, 7, Dw2Inval, Dw2Inval
+// Block scale register for ACE
+bsr0, Class=RegSIMD|Byte, 0, 0, Dw2Inval, Dw2Inval
 // Bound registers for MPX
 bnd0, Class=RegBND, 0, 0, Dw2Inval, 126
 bnd1, Class=RegBND, 0, 1, Dw2Inval, 127