x86/insn: Add support for APX EVEX to the instruction decoder logic
authorAdrian Hunter <adrian.hunter@intel.com>
Thu, 2 May 2024 10:58:50 +0000 (13:58 +0300)
committerIngo Molnar <mingo@kernel.org>
Thu, 2 May 2024 11:13:45 +0000 (13:13 +0200)
Intel Advanced Performance Extensions (APX) extends the EVEX prefix to
support:

 - extended general purpose registers (EGPRs) i.e. r16 to r31
 - Push-Pop Acceleration (PPX) hints
 - new data destination (NDD) register
 - suppress status flags writes (NF) of common instructions
 - new instructions

Refer to the Intel Advanced Performance Extensions (Intel APX) Architecture
Specification for details.

The extended EVEX prefix does not need amended instruction decoder logic,
except in one area. Some instructions are defined as SCALABLE which means
the EVEX.W bit and EVEX.pp bits are used to determine operand size.
Specifically, if an instruction is SCALABLE and EVEX.W is zero, then
EVEX.pp value 0 (representing no prefix NP) means default operand size,
whereas EVEX.pp value 1 (representing 66 prefix) means operand size
override i.e. 16 bits

Add an attribute (INAT_EVEX_SCALABLE) to identify such instructions, and
amend the logic appropriately.

Amend the awk script that generates the attribute tables from the opcode
map, to recognise "(es)" as attribute INAT_EVEX_SCALABLE.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Link: https://lore.kernel.org/r/20240502105853.5338-8-adrian.hunter@intel.com
arch/x86/include/asm/inat.h
arch/x86/include/asm/insn.h
arch/x86/lib/insn.c
arch/x86/tools/gen-insn-attr-x86.awk
tools/arch/x86/include/asm/inat.h
tools/arch/x86/include/asm/insn.h
tools/arch/x86/lib/insn.c
tools/arch/x86/tools/gen-insn-attr-x86.awk

index 1331bdd39a23e08d21dce7bd5cb7c82bca0ad972..53e4015242b4f7f86b1a6ab95891a4005219de47 100644 (file)
@@ -81,6 +81,7 @@
 #define INAT_EVEXONLY  (1 << (INAT_FLAG_OFFS + 7))
 #define INAT_NO_REX2   (1 << (INAT_FLAG_OFFS + 8))
 #define INAT_REX2_VARIANT      (1 << (INAT_FLAG_OFFS + 9))
+#define INAT_EVEX_SCALABLE     (1 << (INAT_FLAG_OFFS + 10))
 /* Attribute making macros for attribute tables */
 #define INAT_MAKE_PREFIX(pfx)  (pfx << INAT_PFX_OFFS)
 #define INAT_MAKE_ESCAPE(esc)  (esc << INAT_ESC_OFFS)
@@ -236,4 +237,9 @@ static inline int inat_must_evex(insn_attr_t attr)
 {
        return attr & INAT_EVEXONLY;
 }
+
+static inline int inat_evex_scalable(insn_attr_t attr)
+{
+       return attr & INAT_EVEX_SCALABLE;
+}
 #endif
index 95249ec1f24ec6946cc13bcede7523d4294b9b3b..7152ea809e6a5ef392fe2d914e4963139eaac350 100644 (file)
@@ -215,6 +215,13 @@ static inline insn_byte_t insn_vex_p_bits(struct insn *insn)
                return X86_VEX_P(insn->vex_prefix.bytes[2]);
 }
 
+static inline insn_byte_t insn_vex_w_bit(struct insn *insn)
+{
+       if (insn->vex_prefix.nbytes < 3)
+               return 0;
+       return X86_VEX_W(insn->vex_prefix.bytes[2]);
+}
+
 /* Get the last prefix id from last prefix or VEX prefix */
 static inline int insn_last_prefix_id(struct insn *insn)
 {
index 6126ddc6e5f501d1f6b56ea989f405c66122e262..5952ab41c60f4def412a27f97e287c482b6d3832 100644 (file)
@@ -294,6 +294,10 @@ int insn_get_opcode(struct insn *insn)
                m = insn_vex_m_bits(insn);
                p = insn_vex_p_bits(insn);
                insn->attr = inat_get_avx_attribute(op, m, p);
+               /* SCALABLE EVEX uses p bits to encode operand size */
+               if (inat_evex_scalable(insn->attr) && !insn_vex_w_bit(insn) &&
+                   p == INAT_PFX_OPNDSZ)
+                       insn->opnd_bytes = 2;
                if ((inat_must_evex(insn->attr) && !insn_is_evex(insn)) ||
                    (!inat_accept_vex(insn->attr) &&
                     !inat_is_group(insn->attr))) {
index 3f43aa7d8fefbf76cbd57bea2e8d0a5f63381d82..5770c8097f320643e4d43be7a9cb2fb82c1f554e 100644 (file)
@@ -83,6 +83,8 @@ BEGIN {
        vexonly_expr = "\\(v\\)"
        # All opcodes with (ev) superscript supports *only* EVEX prefix
        evexonly_expr = "\\(ev\\)"
+       # (es) is the same as (ev) but also "SCALABLE" i.e. W and pp determine operand size
+       evex_scalable_expr = "\\(es\\)"
 
        prefix_expr = "\\(Prefix\\)"
        prefix_num["Operand-Size"] = "INAT_PFX_OPNDSZ"
@@ -332,6 +334,8 @@ function convert_operands(count,opnd,       i,j,imm,mod)
                # check VEX codes
                if (match(ext, evexonly_expr))
                        flags = add_flags(flags, "INAT_VEXOK | INAT_EVEXONLY")
+               else if (match(ext, evex_scalable_expr))
+                       flags = add_flags(flags, "INAT_VEXOK | INAT_EVEXONLY | INAT_EVEX_SCALABLE")
                else if (match(ext, vexonly_expr))
                        flags = add_flags(flags, "INAT_VEXOK | INAT_VEXONLY")
                else if (match(ext, vexok_expr) || match(opcode, vexok_opcode_expr))
index 2e65312cae524951a0eb5baf1df1a8a6dce2c4ee..253690eb3c268a86a7d9a933279815deb185f6b6 100644 (file)
@@ -81,6 +81,7 @@
 #define INAT_EVEXONLY  (1 << (INAT_FLAG_OFFS + 7))
 #define INAT_NO_REX2   (1 << (INAT_FLAG_OFFS + 8))
 #define INAT_REX2_VARIANT      (1 << (INAT_FLAG_OFFS + 9))
+#define INAT_EVEX_SCALABLE     (1 << (INAT_FLAG_OFFS + 10))
 /* Attribute making macros for attribute tables */
 #define INAT_MAKE_PREFIX(pfx)  (pfx << INAT_PFX_OFFS)
 #define INAT_MAKE_ESCAPE(esc)  (esc << INAT_ESC_OFFS)
@@ -236,4 +237,9 @@ static inline int inat_must_evex(insn_attr_t attr)
 {
        return attr & INAT_EVEXONLY;
 }
+
+static inline int inat_evex_scalable(insn_attr_t attr)
+{
+       return attr & INAT_EVEX_SCALABLE;
+}
 #endif
index 1a7e8fc4d75a97f8d207b11353313427a0296b95..0e5abd896ad42d89770a1da35cdbb28510d9a85c 100644 (file)
@@ -215,6 +215,13 @@ static inline insn_byte_t insn_vex_p_bits(struct insn *insn)
                return X86_VEX_P(insn->vex_prefix.bytes[2]);
 }
 
+static inline insn_byte_t insn_vex_w_bit(struct insn *insn)
+{
+       if (insn->vex_prefix.nbytes < 3)
+               return 0;
+       return X86_VEX_W(insn->vex_prefix.bytes[2]);
+}
+
 /* Get the last prefix id from last prefix or VEX prefix */
 static inline int insn_last_prefix_id(struct insn *insn)
 {
index f761adeb8e8c5fcff1c7e43120f5cbc2fa068c71..a43b37346a22d435ee722131b182efeefea8b267 100644 (file)
@@ -294,6 +294,10 @@ int insn_get_opcode(struct insn *insn)
                m = insn_vex_m_bits(insn);
                p = insn_vex_p_bits(insn);
                insn->attr = inat_get_avx_attribute(op, m, p);
+               /* SCALABLE EVEX uses p bits to encode operand size */
+               if (inat_evex_scalable(insn->attr) && !insn_vex_w_bit(insn) &&
+                   p == INAT_PFX_OPNDSZ)
+                       insn->opnd_bytes = 2;
                if ((inat_must_evex(insn->attr) && !insn_is_evex(insn)) ||
                    (!inat_accept_vex(insn->attr) &&
                     !inat_is_group(insn->attr))) {
index 3f43aa7d8fefbf76cbd57bea2e8d0a5f63381d82..5770c8097f320643e4d43be7a9cb2fb82c1f554e 100644 (file)
@@ -83,6 +83,8 @@ BEGIN {
        vexonly_expr = "\\(v\\)"
        # All opcodes with (ev) superscript supports *only* EVEX prefix
        evexonly_expr = "\\(ev\\)"
+       # (es) is the same as (ev) but also "SCALABLE" i.e. W and pp determine operand size
+       evex_scalable_expr = "\\(es\\)"
 
        prefix_expr = "\\(Prefix\\)"
        prefix_num["Operand-Size"] = "INAT_PFX_OPNDSZ"
@@ -332,6 +334,8 @@ function convert_operands(count,opnd,       i,j,imm,mod)
                # check VEX codes
                if (match(ext, evexonly_expr))
                        flags = add_flags(flags, "INAT_VEXOK | INAT_EVEXONLY")
+               else if (match(ext, evex_scalable_expr))
+                       flags = add_flags(flags, "INAT_VEXOK | INAT_EVEXONLY | INAT_EVEX_SCALABLE")
                else if (match(ext, vexonly_expr))
                        flags = add_flags(flags, "INAT_VEXOK | INAT_VEXONLY")
                else if (match(ext, vexok_expr) || match(opcode, vexok_opcode_expr))