[binutils-gdb] x86/APX: make .insn extended-EVEX capable

Jan Beulich jbeulich@sourceware.org
Fri Feb 14 08:34:36 GMT 2025


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=298a68339718add09a0026aeda4c427df7924372

commit 298a68339718add09a0026aeda4c427df7924372
Author: Jan Beulich <jbeulich@suse.com>
Date:   Fri Feb 14 09:32:35 2025 +0100

    x86/APX: make .insn extended-EVEX capable
    
    So far tricks had to be played to use .insn to encode extended-EVEX
    insns; the X4 bit couldn't be controlled at all. Extend the syntax just
    enough to cover all features, taking care to reject invalid feature
    combinations (albeit aiming at being as lax there as possible, to offer
    users as much flexibility as we can - we don't, after all, know what
    future will bring).
    
    In a pre-existing testcase replace all but one .byte; the one that needs
    to remain wants to have EVEX.U clear in a way that's neither
    controllable via AVX10/256 embedded rounding (would otherwise also set
    EVEX.ND), nor via the index register (EVEX.X4), as there's no memory
    operand. For one of the converted instances ModR/M.mod needs correcting:
    An 8-bit displacement requires that to be 1, not 2. Also adjust source
    comments to better represent what the bad insns mimic.

Diff:
---
 gas/config/tc-i386.c                               | 166 +++++++++++++++++++--
 gas/doc/c-i386.texi                                |  11 +-
 gas/testsuite/gas/i386/insn-64.d                   |   9 ++
 gas/testsuite/gas/i386/insn-64.s                   |  21 +++
 .../gas/i386/x86-64-apx-evex-promoted-bad.d        |   2 +-
 .../gas/i386/x86-64-apx-evex-promoted-bad.s        |  13 +-
 6 files changed, 197 insertions(+), 25 deletions(-)

diff --git a/gas/config/tc-i386.c b/gas/config/tc-i386.c
index 538e3fb06ad..246942a378d 100644
--- a/gas/config/tc-i386.c
+++ b/gas/config/tc-i386.c
@@ -2125,6 +2125,59 @@ check_Scc_OszcOperations (const char *l)
   /* Skip '{'.  */
   suffix_string++;
 
+  /* For .insn require 'scc=' as the first element.  */
+  if (dot_insn ())
+    {
+      char *copy;
+      valueT val;
+
+      while (is_whitespace (*suffix_string))
+	suffix_string++;
+
+      if (strncasecmp (suffix_string, "scc", 3) == 0)
+	suffix_string += 3;
+      else
+	{
+	  as_bad (_("unrecognized pseudo-suffix"));
+	  return -1;
+	}
+
+      while (is_whitespace (*suffix_string))
+	suffix_string++;
+
+      if (*suffix_string == '=')
+	suffix_string++;
+      else
+	{
+	  as_bad (_("unrecognized pseudo-suffix"));
+	  return -1;
+	}
+
+      copy = xstrdup (suffix_string);
+      /* No need to save/restore input_line_pointer; that's done in the
+	 caller already.  */
+      input_line_pointer = copy;
+      val = get_absolute_expression ();
+      suffix_string += input_line_pointer - copy;
+      free (copy);
+
+      if (val > 0xf)
+	{
+	  as_bad (_("scc= value must be between 0 and 15 (decimal)"));
+	  return -1;
+	}
+
+      i.scc = val;
+
+      /* Permit dfv= to be absent (implying all flag values being zero).  */
+      if (*suffix_string == '}')
+	return suffix_string + 1 - l;
+
+      if (*suffix_string != ',')
+	goto bad;
+      suffix_string++;
+    }
+
   /* Parse 'dfv='.  */
   while (is_whitespace (*suffix_string))
     suffix_string++;
@@ -2197,6 +2250,7 @@ check_Scc_OszcOperations (const char *l)
       suffix_string ++;
     }
 
+ bad:
   as_bad (_("missing `}' or `,' in pseudo-suffix"));
   return -1;
 }
@@ -4573,7 +4627,7 @@ build_rex2_prefix (void)
    | z| L'L | b | `v | aaa |
 */
 static bool
-build_apx_evex_prefix (void)
+build_apx_evex_prefix (bool force_nd)
 {
   /* To mimic behavior for legacy insns, transform use of DATA16 and REX64 into
      their embedded-prefix representations.  */
@@ -4620,7 +4674,8 @@ build_apx_evex_prefix (void)
   /* Encode the NDD bit of the instruction promoted from the legacy
      space. ZU shares the same bit with NDD.  */
   if ((i.vex.register_specifier && i.tm.opcode_space == SPACE_MAP4)
-      || i.tm.opcode_modifier.operandconstraint == ZERO_UPPER)
+      || i.tm.opcode_modifier.operandconstraint == ZERO_UPPER
+      || force_nd)
     i.vex.bytes[3] |= 0x10;
 
   /* Encode SCC and oszc flags bits.  */
@@ -7462,7 +7517,7 @@ i386_assemble (char *line)
 
       if (is_apx_evex_encoding ())
 	{
-	  if (!build_apx_evex_prefix ())
+	  if (!build_apx_evex_prefix (false))
 	    return;
 	}
       else if (i.tm.opcode_modifier.vex)
@@ -10972,7 +11027,8 @@ build_modrm_byte (void)
     if (i.tm.operand_types[op].bitfield.baseindex)
       break;
 
-  if (i.reg_operands + i.mem_operands + (i.tm.extension_opcode != None) == 4)
+  if (i.reg_operands + i.mem_operands + (i.tm.extension_opcode != None)
+      + (i.tm.opcode_modifier.operandconstraint == SCC) == 4)
     {
       expressionS *exp;
 
@@ -10984,10 +11040,12 @@ build_modrm_byte (void)
 	 2. 4 operands: 4 register operands or 3 register operands
 	 plus 1 memory operand, with VexXDS.
 	 3. Other equivalent combinations when coming from s_insn().  */
-      gas_assert (i.tm.opcode_modifier.vexvvvv
-		  && i.tm.opcode_modifier.vexw);
-      gas_assert (dot_insn ()
-		  || i.tm.operand_types[dest].bitfield.class == RegSIMD);
+      if (!dot_insn ())
+	{
+	  gas_assert (i.tm.opcode_modifier.vexvvvv
+		      && i.tm.opcode_modifier.vexw);
+	  gas_assert (i.tm.operand_types[dest].bitfield.class == RegSIMD);
+	}
 
       /* Of the first two non-immediate operands the one with the template
 	 not allowing for a memory one is encoded in the immediate operand.  */
@@ -13275,7 +13333,8 @@ s_insn (int dummy ATTRIBUTE_UNUSED)
   const char *end;
   unsigned int j;
   valueT val;
-  bool vex = false, xop = false, evex = false;
+  bool vex = false, xop = false;
+  enum { evex_none, evex_basic, evex_nd } evex = evex_none;
   struct last_insn *last_insn;
 
   init_globals ();
@@ -13324,7 +13383,7 @@ s_insn (int dummy ATTRIBUTE_UNUSED)
   else if (startswith (line, "EVEX")
 	   && (line[4] == '.' || is_whitespace (line[4])))
     {
-      evex = true;
+      evex = evex_basic;
       line += 4;
     }
 
@@ -13543,6 +13602,20 @@ s_insn (int dummy ATTRIBUTE_UNUSED)
 	line += 3;
     }
 
+  if (line > end && evex && *line == '.')
+    {
+      if (line[1] == 'N' && line[2] == 'D')
+	{
+	  evex = evex_nd;
+	  line += 3;
+	}
+      else if (line[1] == 'Z' && line[2] == 'U')
+	{
+	  i.tm.opcode_modifier.operandconstraint = ZERO_UPPER;
+	  line += 3;
+	}
+    }
+
   if (line > end && *line && !is_whitespace (*line))
     {
       /* Improve diagnostic a little.  */
@@ -13612,6 +13685,25 @@ s_insn (int dummy ATTRIBUTE_UNUSED)
 	}
     }
 
+  if (evex == evex_basic && *line == '{')
+    {
+      int length = check_Scc_OszcOperations (line);
+
+      if (length > 0)
+	{
+	  line += length;
+	  if (is_whitespace (*line))
+	    ++line;
+
+	  if (i.tm.opcode_modifier.operandconstraint)
+	    {
+	      as_bad (_("SCC/OSZC specifier cannot be used here"));
+	      goto bad;
+	    }
+	  i.tm.opcode_modifier.operandconstraint = SCC;
+	}
+    }
+
   /* Parse operands, if any, before evaluating encoding space.  */
   if (*line == ',')
     {
@@ -13713,7 +13805,8 @@ s_insn (int dummy ATTRIBUTE_UNUSED)
 
       /* Enforce certain constraints on operands.  */
       switch (i.reg_operands + i.mem_operands
-	      + (i.tm.extension_opcode != None))
+	      + (i.tm.extension_opcode != None)
+	      + (i.tm.opcode_modifier.operandconstraint == SCC))
 	{
 	case 0:
 	  if (i.short_form)
@@ -13728,9 +13821,13 @@ s_insn (int dummy ATTRIBUTE_UNUSED)
 	      as_bad (_("too few register/memory operands"));
 	      goto done;
 	    }
-	  break;
-
+	  /* Fall through.  */
 	case 2:
+	  if (evex == evex_nd)
+	    {
+	      as_bad (_("too few register/memory operands"));
+	      goto done;
+	    }
 	  break;
 
 	case 4:
@@ -13743,9 +13840,12 @@ s_insn (int dummy ATTRIBUTE_UNUSED)
 	    }
 	  /* Fall through.  */
 	case 3:
+	  if (i.tm.opcode_modifier.operandconstraint == SCC)
+	    break;
 	  if (pp.encoding != encoding_default)
 	    {
-	      i.tm.opcode_modifier.vexvvvv = i.tm.extension_opcode == None
+	      i.tm.opcode_modifier.vexvvvv = (i.tm.extension_opcode == None
+					      && evex != evex_nd)
 					     ? VexVVVV_SRC1 : VexVVVV_DST;
 	      break;
 	    }
@@ -14043,6 +14143,13 @@ s_insn (int dummy ATTRIBUTE_UNUSED)
 
   if (vex || xop)
     {
+      if (is_apx_evex_encoding ())
+	{
+	  as_bad (_("APX functionality cannot be used with %s encodings"),
+		  vex ? "VEX" : "XOP");
+	  goto done;
+	}
+
       if (!i.tm.opcode_modifier.vex)
 	i.tm.opcode_modifier.vex = VEXScalar; /* LIG */
 
@@ -14054,7 +14161,36 @@ s_insn (int dummy ATTRIBUTE_UNUSED)
       if (!i.tm.opcode_modifier.evex)
 	i.tm.opcode_modifier.evex = EVEXLIG;
 
-      build_evex_prefix ();
+      /* To keep earlier .insn uses working as far as possible, take the
+	 legacy path when opcode space is 4 bits wide (impossible to encode in
+	 extended EVEX), and when no "extended" syntax elements are used.  */
+      if ((!is_apx_evex_encoding () || i.insn_opcode_space > 7)
+	  && evex == evex_basic
+	  && !i.tm.opcode_modifier.operandconstraint)
+	build_evex_prefix ();
+      else if (i.insn_opcode_space > 7)
+	{
+	  as_bad (_("opcode space cannot be larger than 7"));
+	  goto done;
+	}
+      else if (evex == evex_nd && (i.broadcast.type || i.broadcast.bytes))
+	{
+	  as_bad (_("ND and broadcast cannot be used at the same time"));
+	  goto done;
+	}
+      else if (pp.has_nf && i.mask.reg)
+	{
+	  as_bad (_("{nf} and masking cannot be used at the same time"));
+	  goto done;
+	}
+      else if (i.tm.opcode_modifier.operandconstraint == SCC
+	       && (pp.has_nf || i.mask.reg))
+	{
+	  as_bad (_("SCC cannot be used at the same time {nf} / masking"));
+	  goto done;
+	}
+      else if (!build_apx_evex_prefix (evex == evex_nd))
+	goto done;
       i.rex &= REX_OPCODE;
     }
   else
diff --git a/gas/doc/c-i386.texi b/gas/doc/c-i386.texi
index d488703a6e7..4d59958fb90 100644
--- a/gas/doc/c-i386.texi
+++ b/gas/doc/c-i386.texi
@@ -702,7 +702,7 @@ operand, as long as there is one.
 syntax tries to resemble that used in documentation:
 @itemize @bullet
 @item @code{VEX}[@code{.@var{len}}][@code{.@var{prefix}}][@code{.@var{space}}][@code{.@var{w}}]
-@item @code{EVEX}[@code{.@var{len}}][@code{.@var{prefix}}][@code{.@var{space}}][@code{.@var{w}}]
+@item @code{EVEX}[@code{.@var{len}}][@code{.@var{prefix}}][@code{.@var{space}}][@code{.@var{w}}][@code{.@var{opt}}]
 @item @code{XOP}@var{space}[@code{.@var{len}}][@code{.@var{prefix}}][@code{.@var{w}}]
 @end itemize
 
@@ -717,10 +717,11 @@ only) @code{512} as well as @code{L0} / @code{L1} for VEX / XOP and
 @item @code{0f}, @code{0f38}, @code{0f3a}, or @code{M0}...@code{M31}
 for VEX
 @item @code{08}...@code{1f} for XOP
-@item @code{0f}, @code{0f38}, @code{0f3a}, or @code{M0}...@code{M15}
+@item @code{0f}, @code{0f38}, @code{0f3a}, or @code{M0}...@code{M7}
 for EVEX
 @end itemize
 @item @var{w} can be @code{WIG}, @code{W0}, or @code{W1}
+@item @var{opt} can be @code{ND} or @code{ZU}
 @end itemize
 
 Defaults:
@@ -808,6 +809,12 @@ be suffixed by @code{@{:d@var{n}@}} to specify the size (in bytes).
 This can be combined with an embedded broadcast specifier:
 @samp{8(%eax)@{1to8:d8@}}.
 
+For SCC EVEX the @code{@{dfv=@}} specifier used by ordinary insns is
+extended and immediately follows the opcode specifier.  The extension
+is that the SCC value needs to be specified and goes first, as in
+@code{@{scc=@var{n},dfv=...@}}.  Unlike for ordinary insns @code{dfv=}
+may be omitted for brevity.
+
 @cindex @code{noopt} directive
 @item .noopt
 Disable instruction size optimization.
diff --git a/gas/testsuite/gas/i386/insn-64.d b/gas/testsuite/gas/i386/insn-64.d
index 0c1f0c53770..cbfbabd7df5 100644
--- a/gas/testsuite/gas/i386/insn-64.d
+++ b/gas/testsuite/gas/i386/insn-64.d
@@ -63,4 +63,13 @@ Disassembly of section .text:
 [ 	]*[a-f0-9]+:	62 f5 fd 58 5a 40 01[ 	]+vcvtpd2ph (0x)?8\(%rax\)\{1to8\},%xmm0
 [ 	]*[a-f0-9]+:	62 f5 7c 48 5a 40 01[ 	]+vcvtph2pd 0x10\(%rax\),%zmm0
 [ 	]*[a-f0-9]+:	62 f5 7c 58 5a 40 01[ 	]+vcvtph2pd (0x)?2\(%rax\)\{1to8\},%zmm0
+[ 	]*[a-f0-9]+:	62 e4 7c 08 8b 00[ 	]+movrs  \(%rax\),%r16d
+[ 	]*[a-f0-9]+:	62 fc 7c 08 8b 00[ 	]+movrs  \(%r16\),%eax
+[ 	]*[a-f0-9]+:	62 f4 78 08 8b 04 00[ 	]+movrs  \(%rax,%r16(,1)?\),%eax
+[ 	]*[a-f0-9]+:	62 fc 7c 08 60 c0[ 	]+movbe  %r16d,%eax
+[ 	]*[a-f0-9]+:	62 f4 7c 0c 01 c0[ 	]+\{nf\} add %eax,%eax
+[ 	]*[a-f0-9]+:	62 f4 7c 18 01 c0[ 	]+add    %eax,%eax,%eax
+[ 	]*[a-f0-9]+:	62 f4 ec 18 ff f1[ 	]+push2p %rcx,%rdx
+[ 	]*[a-f0-9]+:	62 f4 7f 18 42 c0[ 	]+setzub %al
+[ 	]*[a-f0-9]+:	62 f4 44 0b 39 c0[ 	]+ccmpf \{dfv=of\} %eax,%eax
 #pass
diff --git a/gas/testsuite/gas/i386/insn-64.s b/gas/testsuite/gas/i386/insn-64.s
index 3152ec94f17..f79db65e347 100644
--- a/gas/testsuite/gas/i386/insn-64.s
+++ b/gas/testsuite/gas/i386/insn-64.s
@@ -104,3 +104,24 @@ insn:
 	# vcvtph2pd
 	.insn EVEX.M5.W0 0x5a, 16(%rax){:d16}, %zmm0
 	.insn EVEX.M5.W0 0x5a, 2(%rax){1to8:d2}, %zmm0
+
+	# movrs (APX)
+	.insn EVEX.L0.NP.M4 0x8b, (%rax), %r16d
+	.insn EVEX.L0.NP.M4 0x8b, (%r16), %eax
+	.insn EVEX.L0.NP.M4 0x8b, (%rax,%r16), %eax
+
+	# movbe (APX)
+	.insn EVEX.L0.NP.M4 0x60, %r16d, %eax
+
+	# add (APX)
+	.insn {nf} EVEX.L0.NP.M4 0x01, %eax, %eax
+	.insn EVEX.L0.NP.M4.ND 0x01, %eax, %eax, %eax
+
+	# push2p
+	.insn EVEX.L0.NP.M4.W1.ND 0xff/6, %rcx, %rdx
+
+	# setzub
+	.insn EVEX.L0.F2.M4.ZU 0x42/0, %eax
+
+	# ccmpf
+	.insn EVEX.L0.NP.M4 0x39 {scc=0b1011,dfv=of}, %eax, %eax
diff --git a/gas/testsuite/gas/i386/x86-64-apx-evex-promoted-bad.d b/gas/testsuite/gas/i386/x86-64-apx-evex-promoted-bad.d
index 667e6f27130..53d38fc3ee5 100644
--- a/gas/testsuite/gas/i386/x86-64-apx-evex-promoted-bad.d
+++ b/gas/testsuite/gas/i386/x86-64-apx-evex-promoted-bad.d
@@ -12,7 +12,7 @@ Disassembly of section .text:
 [ 	]*[a-f0-9]+:[ 	]+c7[ 	]+\(bad\)
 [ 	]*[a-f0-9]+:[ 	]+62 fc 7f 08 60[ 	]+\(bad\)
 [ 	]*[a-f0-9]+:[ 	]+c7[ 	]+\(bad\)
-[ 	]*[a-f0-9]+:[ 	]+62 e2 f9 41 91 84[ 	]+vpgatherqq \(bad\),%zmm16\{%k1\}
+[ 	]*[a-f0-9]+:[ 	]+62 e2 f9 41 91 44[ 	]+vpgatherqq \(bad\),%zmm16\{%k1\}
 [ 	]*[a-f0-9]+:[ 	]+cd ff[ 	]+int    \$0xff
 [ 	]*[a-f0-9]+:[ 	]+62 fd 7d 08 60[ 	]+\(bad\)
 [ 	]*[a-f0-9]+:[ 	]+c7[ 	]+.*
diff --git a/gas/testsuite/gas/i386/x86-64-apx-evex-promoted-bad.s b/gas/testsuite/gas/i386/x86-64-apx-evex-promoted-bad.s
index 7b2df4b36ea..c98422e90c6 100644
--- a/gas/testsuite/gas/i386/x86-64-apx-evex-promoted-bad.s
+++ b/gas/testsuite/gas/i386/x86-64-apx-evex-promoted-bad.s
@@ -9,9 +9,8 @@ _start:
 	#movbe %r23w,%ax set EVEX.pp = f2.
 	.insn EVEX.L0.f2.M12.W0 0x60, %di, %ax
 
-	#VSIB vpgatherqq (%rbp,%zmm17,8),%zmm16{%k1} set EVEX.P[10] == 0
-	.byte 0x62, 0xe2, 0xf9, 0x41, 0x91, 0x84, 0xcd
-	.byte 0xff
+	#VSIB vpgatherqq -8(%rbp,%zmm17,8),%zmm16{%k1} set EVEX.U = 0.
+	.insn EVEX.512.66.0f38.W1 0x91, -8(%rbp,%r17,8){:d8}, %zmm16{%k1}, %r16
 
 	#EVEX_MAP4 movbe %r23w,%ax set EVEX.mm == 0b01.
 	.insn EVEX.L0.66.M13.W0 0x60, %di, %ax
@@ -38,11 +37,11 @@ _start:
 	#EVEX from VEX bzhi %rax,(%rax,%rbx),%rcx EVEX.P[20](EVEX.b) == 0b1
 	.insn EVEX.L0.NP.0f38.W1 0xf5, %rax, (%rax,%rbx){1to8}, %rcx
 
-	#{evex} inc %rax %rbx EVEX.vvvv != 1111 && EVEX.ND = 0.
-	.byte 0x62, 0xf4, 0xe4, 0x08, 0xff, 0x04, 0x08
+	#{evex} inc (%rax,%rcx), %rbx EVEX.vvvv != 1111 && EVEX.ND = 0.
+	.insn EVEX.L0.NP.M4 0xff/0, (%rax,%rcx), %rbx
 
 	# pop2 %rdi, %r8 set EVEX.ND=0.
-	.byte 0x62, 0xf4, 0x3c, 0x08, 0x8f, 0xc7
+	.insn EVEX.L0.NP.M4.W0 0x8f/0, %rdi, %r8
 
 	# pop2 %rax, %rax
 	.insn EVEX.L0.NP.M4.W0 0x8f/0, %rax, {sae}, %rax
@@ -59,7 +58,7 @@ _start:
 	#EVEX_MAP4 movbe %r18w,%ax set EVEX.nf = 1.
 	.insn EVEX.L0.66.M12.W0 0x60, %di, %ax {%k4}
 
-	# EVEX_MAP4 movbe %r23w,%ax set EVEX.P[10] = 0.
+	# EVEX_MAP4 movbe %r23w,%ax set EVEX.U = 0.
 	.byte 0x62, 0xfc, 0x79, 0x08, 0x60, 0xc7
 
 	# ccmps {dfv=of,sf,zf,cf} %r15, %rdx set EVEX.ND = 1.


More information about the Binutils-cvs mailing list