Commit 87bbaf1a authored by Adrian Hunter's avatar Adrian Hunter Committed by Ingo Molnar

x86/insn: Add support for APX EVEX to the instruction decoder logic

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: default avatarAdrian Hunter <adrian.hunter@intel.com>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
Link: https://lore.kernel.org/r/20240502105853.5338-8-adrian.hunter@intel.com
parent 159039af
...@@ -81,6 +81,7 @@ ...@@ -81,6 +81,7 @@
#define INAT_EVEXONLY (1 << (INAT_FLAG_OFFS + 7)) #define INAT_EVEXONLY (1 << (INAT_FLAG_OFFS + 7))
#define INAT_NO_REX2 (1 << (INAT_FLAG_OFFS + 8)) #define INAT_NO_REX2 (1 << (INAT_FLAG_OFFS + 8))
#define INAT_REX2_VARIANT (1 << (INAT_FLAG_OFFS + 9)) #define INAT_REX2_VARIANT (1 << (INAT_FLAG_OFFS + 9))
#define INAT_EVEX_SCALABLE (1 << (INAT_FLAG_OFFS + 10))
/* Attribute making macros for attribute tables */ /* Attribute making macros for attribute tables */
#define INAT_MAKE_PREFIX(pfx) (pfx << INAT_PFX_OFFS) #define INAT_MAKE_PREFIX(pfx) (pfx << INAT_PFX_OFFS)
#define INAT_MAKE_ESCAPE(esc) (esc << INAT_ESC_OFFS) #define INAT_MAKE_ESCAPE(esc) (esc << INAT_ESC_OFFS)
...@@ -236,4 +237,9 @@ static inline int inat_must_evex(insn_attr_t attr) ...@@ -236,4 +237,9 @@ static inline int inat_must_evex(insn_attr_t attr)
{ {
return attr & INAT_EVEXONLY; return attr & INAT_EVEXONLY;
} }
static inline int inat_evex_scalable(insn_attr_t attr)
{
return attr & INAT_EVEX_SCALABLE;
}
#endif #endif
...@@ -215,6 +215,13 @@ static inline insn_byte_t insn_vex_p_bits(struct insn *insn) ...@@ -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]); 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 */ /* Get the last prefix id from last prefix or VEX prefix */
static inline int insn_last_prefix_id(struct insn *insn) static inline int insn_last_prefix_id(struct insn *insn)
{ {
......
...@@ -294,6 +294,10 @@ int insn_get_opcode(struct insn *insn) ...@@ -294,6 +294,10 @@ int insn_get_opcode(struct insn *insn)
m = insn_vex_m_bits(insn); m = insn_vex_m_bits(insn);
p = insn_vex_p_bits(insn); p = insn_vex_p_bits(insn);
insn->attr = inat_get_avx_attribute(op, m, p); 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)) || if ((inat_must_evex(insn->attr) && !insn_is_evex(insn)) ||
(!inat_accept_vex(insn->attr) && (!inat_accept_vex(insn->attr) &&
!inat_is_group(insn->attr))) { !inat_is_group(insn->attr))) {
......
...@@ -83,6 +83,8 @@ BEGIN { ...@@ -83,6 +83,8 @@ BEGIN {
vexonly_expr = "\\(v\\)" vexonly_expr = "\\(v\\)"
# All opcodes with (ev) superscript supports *only* EVEX prefix # All opcodes with (ev) superscript supports *only* EVEX prefix
evexonly_expr = "\\(ev\\)" 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_expr = "\\(Prefix\\)"
prefix_num["Operand-Size"] = "INAT_PFX_OPNDSZ" prefix_num["Operand-Size"] = "INAT_PFX_OPNDSZ"
...@@ -332,6 +334,8 @@ function convert_operands(count,opnd, i,j,imm,mod) ...@@ -332,6 +334,8 @@ function convert_operands(count,opnd, i,j,imm,mod)
# check VEX codes # check VEX codes
if (match(ext, evexonly_expr)) if (match(ext, evexonly_expr))
flags = add_flags(flags, "INAT_VEXOK | INAT_EVEXONLY") 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)) else if (match(ext, vexonly_expr))
flags = add_flags(flags, "INAT_VEXOK | INAT_VEXONLY") flags = add_flags(flags, "INAT_VEXOK | INAT_VEXONLY")
else if (match(ext, vexok_expr) || match(opcode, vexok_opcode_expr)) else if (match(ext, vexok_expr) || match(opcode, vexok_opcode_expr))
......
...@@ -81,6 +81,7 @@ ...@@ -81,6 +81,7 @@
#define INAT_EVEXONLY (1 << (INAT_FLAG_OFFS + 7)) #define INAT_EVEXONLY (1 << (INAT_FLAG_OFFS + 7))
#define INAT_NO_REX2 (1 << (INAT_FLAG_OFFS + 8)) #define INAT_NO_REX2 (1 << (INAT_FLAG_OFFS + 8))
#define INAT_REX2_VARIANT (1 << (INAT_FLAG_OFFS + 9)) #define INAT_REX2_VARIANT (1 << (INAT_FLAG_OFFS + 9))
#define INAT_EVEX_SCALABLE (1 << (INAT_FLAG_OFFS + 10))
/* Attribute making macros for attribute tables */ /* Attribute making macros for attribute tables */
#define INAT_MAKE_PREFIX(pfx) (pfx << INAT_PFX_OFFS) #define INAT_MAKE_PREFIX(pfx) (pfx << INAT_PFX_OFFS)
#define INAT_MAKE_ESCAPE(esc) (esc << INAT_ESC_OFFS) #define INAT_MAKE_ESCAPE(esc) (esc << INAT_ESC_OFFS)
...@@ -236,4 +237,9 @@ static inline int inat_must_evex(insn_attr_t attr) ...@@ -236,4 +237,9 @@ static inline int inat_must_evex(insn_attr_t attr)
{ {
return attr & INAT_EVEXONLY; return attr & INAT_EVEXONLY;
} }
static inline int inat_evex_scalable(insn_attr_t attr)
{
return attr & INAT_EVEX_SCALABLE;
}
#endif #endif
...@@ -215,6 +215,13 @@ static inline insn_byte_t insn_vex_p_bits(struct insn *insn) ...@@ -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]); 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 */ /* Get the last prefix id from last prefix or VEX prefix */
static inline int insn_last_prefix_id(struct insn *insn) static inline int insn_last_prefix_id(struct insn *insn)
{ {
......
...@@ -294,6 +294,10 @@ int insn_get_opcode(struct insn *insn) ...@@ -294,6 +294,10 @@ int insn_get_opcode(struct insn *insn)
m = insn_vex_m_bits(insn); m = insn_vex_m_bits(insn);
p = insn_vex_p_bits(insn); p = insn_vex_p_bits(insn);
insn->attr = inat_get_avx_attribute(op, m, p); 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)) || if ((inat_must_evex(insn->attr) && !insn_is_evex(insn)) ||
(!inat_accept_vex(insn->attr) && (!inat_accept_vex(insn->attr) &&
!inat_is_group(insn->attr))) { !inat_is_group(insn->attr))) {
......
...@@ -83,6 +83,8 @@ BEGIN { ...@@ -83,6 +83,8 @@ BEGIN {
vexonly_expr = "\\(v\\)" vexonly_expr = "\\(v\\)"
# All opcodes with (ev) superscript supports *only* EVEX prefix # All opcodes with (ev) superscript supports *only* EVEX prefix
evexonly_expr = "\\(ev\\)" 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_expr = "\\(Prefix\\)"
prefix_num["Operand-Size"] = "INAT_PFX_OPNDSZ" prefix_num["Operand-Size"] = "INAT_PFX_OPNDSZ"
...@@ -332,6 +334,8 @@ function convert_operands(count,opnd, i,j,imm,mod) ...@@ -332,6 +334,8 @@ function convert_operands(count,opnd, i,j,imm,mod)
# check VEX codes # check VEX codes
if (match(ext, evexonly_expr)) if (match(ext, evexonly_expr))
flags = add_flags(flags, "INAT_VEXOK | INAT_EVEXONLY") 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)) else if (match(ext, vexonly_expr))
flags = add_flags(flags, "INAT_VEXOK | INAT_VEXONLY") flags = add_flags(flags, "INAT_VEXOK | INAT_VEXONLY")
else if (match(ext, vexok_expr) || match(opcode, vexok_opcode_expr)) else if (match(ext, vexok_expr) || match(opcode, vexok_opcode_expr))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment