Reuse the code that is already in place for AVX's operand type H,
so that APX can use type B without the VEX/EVEX prefix.
APX mostly uses VEX.vvvv for the destination. This is already
supported for AVX, though that one only uses VEX.vvvv for
the destination in the case of shifts. However, APX has two
extra possibilities that we need to consider:
- instructions with VEX/EVEX prefix but no new data destination.
To handle this, store into s->vex_ndd whether the B (or H)
operand comes from VEX.vvvv or from modrm.
- instructions with operands "m,r" that are extended to "r,m,r".
In this case, the non-NDD format "m,r" expands the B operand to a
memory location; when this happens, it is incorrect to parse again op1
as a memory location, because that would read the displacement again
from the instruction stream. Instead, transform op1 into X86_TYPE_2op.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
target/i386/tcg/translate.c | 1 +
target/i386/tcg/decode-new.c.inc | 38 +++++++++++++++++++++-----------
2 files changed, 26 insertions(+), 13 deletions(-)
diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c
index 47eef81ba05..9bf4a1fd516 100644
--- a/target/i386/tcg/translate.c
+++ b/target/i386/tcg/translate.c
@@ -105,6 +105,7 @@ typedef struct DisasContext {
#endif
uint8_t vex_l; /* vex vector length */
uint8_t vex_v; /* vex vvvv register, without 1's complement. */
+ bool vex_ndd; /* is this a 3-operand instruction? */
uint8_t popl_esp_hack; /* for correct popl with esp base handling */
uint8_t rip_offset; /* only used in x86_64, but left for simplicity */
diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc
index a17fc8550fb..b7988c64f86 100644
--- a/target/i386/tcg/decode-new.c.inc
+++ b/target/i386/tcg/decode-new.c.inc
@@ -2330,10 +2330,9 @@ static bool decode_op(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode,
case X86_TYPE_Y: /* string destination */
break;
- case X86_TYPE_B: /* VEX.vvvv selects a GPR */
+ case X86_TYPE_B: /* VEX.vvvv selects a GPR, else use modrm */
op->unit = X86_OP_INT;
- op->n = s->vex_v;
- break;
+ goto get_vex_v;
case X86_TYPE_C: /* REG in the modrm byte selects a control register */
op->unit = X86_OP_CR;
@@ -2463,18 +2462,25 @@ static bool decode_op(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode,
};
break;
- case X86_TYPE_H: /* For AVX, VEX.vvvv selects an XMM/YMM register */
- if ((s->prefix & PREFIX_VEX)) {
- op->unit = X86_OP_SSE;
+ case X86_TYPE_H: /* VEX.vvvv selects an XMM/YMM register, else use modrm */
+ op->unit = X86_OP_SSE;
+ get_vex_v:
+ if (s->vex_ndd) {
+ assert(s->prefix & (PREFIX_VEX | PREFIX_EVEX));
op->n = s->vex_v;
break;
}
+ /* Two-operand form: the other one of op0 and op1 is a modrm operand, use it. */
+ assert(decode->e.s1 == decode->e.s0);
if (op == &decode->op[0]) {
- /* shifts place the destination in VEX.vvvv, use modrm */
- return decode_op(s, env, decode, op, decode->e.op1, b);
+ if (!decode_op(s, env, decode, op, decode->e.op1, b)) {
+ return false;
+ }
+ decode->e.op1 = X86_TYPE_2op;
} else {
- return decode_op(s, env, decode, op, decode->e.op0, b);
+ *op = decode->op[0];
}
+ break;
case X86_TYPE_I: /* Immediate */
case X86_TYPE_J: /* Relative offset for a jump */
@@ -2717,6 +2723,14 @@ static bool validate_vex(DisasContext *s, X86DecodedInsn *decode)
{
X86OpEntry *e = &decode->e;
+ if (s->prefix & (PREFIX_VEX | PREFIX_EVEX)) {
+ if (e->op0 == X86_TYPE_H || e->op0 == X86_TYPE_B ||
+ e->op1 == X86_TYPE_H || e->op1 == X86_TYPE_B ||
+ e->op2 == X86_TYPE_H || e->op2 == X86_TYPE_B) {
+ s->vex_ndd = true;
+ }
+ }
+
switch (e->vex_special) {
case X86_VEX_None:
break;
@@ -2804,10 +2818,7 @@ static bool validate_vex(DisasContext *s, X86DecodedInsn *decode)
return true;
}
- if (s->vex_v != 0 &&
- e->op0 != X86_TYPE_H && e->op0 != X86_TYPE_B &&
- e->op1 != X86_TYPE_H && e->op1 != X86_TYPE_B &&
- e->op2 != X86_TYPE_H && e->op2 != X86_TYPE_B) {
+ if (s->vex_v != 0 && !s->vex_ndd) {
goto illegal;
}
@@ -2886,6 +2897,7 @@ static void disas_insn(DisasContext *s, CPUState *cpu)
s->vex_l = 0;
s->vex_v = 0;
s->vex_w = false;
+ s->vex_ndd = false;
s->has_modrm = false;
s->prefix = 0;
--
2.52.0