BMI uses operand type B when VEX.vvvv provides one of the register indices of three-operand instructions. However, APX needs to use it even for two-operand instructions, similar to how SSE and AVX share destination but use the modrm operand when there is no VEX prefix.
To do this , operand type B can mostly reuse the code for operand type H but there are two extra possibilities: - instructions with VEX/EVEX prefix but no new data destination. To handle this, use a new field s->vex_ndd to retrieve whether the B (or H) operand comes from VEX.vvvv or from modrm. For now, this uses the same existing logic from validate_vex(), but later it will be extended to read EVEX.NDD. - instructions with operands "m,r" that NDD extends to "r,m,r". In this case, in the non-NDD format "m,r" the B operand corresponds 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 if VEX.vvvv is the destination operand. Signed-off-by: Paolo Bonzini <[email protected]> --- 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 ea70e9a5568..7e50cf2355f 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 d49f467cc84..08f1fdddf16 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -2331,10 +2331,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; @@ -2464,18 +2463,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); 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 */ @@ -2718,6 +2724,14 @@ static bool validate_vex(DisasContext *s, X86DecodedInsn *decode) { X86OpEntry *e = &decode->e; + if (s->prefix & PREFIX_VEX) { + 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; @@ -2805,10 +2819,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; } @@ -2887,6 +2898,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.55.0
