target/i386: remove duplicate prefix decoding

Now that a bulk of opcodes go through the new decoder, it is sensible
to do some cleanup.  Go immediately through disas_insn_new and only jump
back after parsing the prefixes.

disas_insn() now only contains the three sigsetjmp cases, and they
are more easily managed if they are inlined into i386_tr_translate_insn.

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
master
Paolo Bonzini 2024-04-09 17:31:23 +02:00
parent ef309ec2a6
commit d4e6d40c36
2 changed files with 103 additions and 219 deletions

View File

@ -2219,22 +2219,31 @@ illegal:
* Convert one instruction. s->base.is_jmp is set if the translation must
* be stopped.
*/
static void disas_insn_new(DisasContext *s, CPUState *cpu, int b)
static void disas_insn(DisasContext *s, CPUState *cpu)
{
CPUX86State *env = cpu_env(cpu);
bool first = true;
X86DecodedInsn decode;
X86DecodeFunc decode_func = decode_root;
uint8_t cc_live;
uint8_t cc_live, b;
s->pc = s->base.pc_next;
s->override = -1;
s->popl_esp_hack = 0;
#ifdef TARGET_X86_64
s->rex_r = 0;
s->rex_x = 0;
s->rex_b = 0;
#endif
s->rip_offset = 0; /* for relative ip address */
s->vex_l = 0;
s->vex_v = 0;
s->vex_w = false;
s->has_modrm = false;
s->prefix = 0;
next_byte:
if (first) {
first = false;
} else {
b = x86_ldub_code(env, s);
}
b = x86_ldub_code(env, s);
/* Collect prefixes. */
switch (b) {
case 0xf3:
@ -2346,10 +2355,6 @@ static void disas_insn_new(DisasContext *s, CPUState *cpu, int b)
}
break;
default:
if (b >= 0x100) {
b -= 0x100;
decode_func = do_decode_0F;
}
break;
}
@ -2378,6 +2383,40 @@ static void disas_insn_new(DisasContext *s, CPUState *cpu, int b)
}
}
/* Go back to old decoder for unconverted opcodes. */
if (!(s->prefix & PREFIX_VEX)) {
if ((b & ~7) == 0xd8) {
if (!disas_insn_x87(s, cpu, b)) {
goto unknown_op;
}
return;
}
if (b == 0x0f) {
b = x86_ldub_code(env, s);
switch (b) {
case 0x00 ... 0x03: /* mostly privileged instructions */
case 0x05 ... 0x09:
case 0x0d: /* 3DNow! prefetch */
case 0x18 ... 0x23: /* prefetch, MPX, mov from/to CR and DR */
case 0x30 ... 0x35: /* more privileged instructions */
case 0xa2 ... 0xa5: /* CPUID, BT, SHLD */
case 0xaa ... 0xae: /* RSM, SHRD, grp15 */
case 0xb0 ... 0xb1: /* cmpxchg */
case 0xb3: /* btr */
case 0xb8: /* integer ops */
case 0xba ... 0xbd: /* integer ops */
case 0xc0 ... 0xc1: /* xadd */
case 0xc7: /* grp9 */
disas_insn_old(s, cpu, b + 0x100);
return;
default:
decode_func = do_decode_0F;
break;
}
}
}
memset(&decode, 0, sizeof(decode));
decode.cc_op = -1;
decode.b = b;

View File

@ -2451,10 +2451,6 @@ static void gen_sty_env_A0(DisasContext *s, int offset, bool align)
tcg_gen_qemu_st_i128(t, s->tmp0, mem_index, mop);
}
#include "decode-new.h"
#include "emit.c.inc"
#include "decode-new.c.inc"
static void gen_cmpxchg8b(DisasContext *s, CPUX86State *env, int modrm)
{
TCGv_i64 cmp, val, old;
@ -3117,183 +3113,6 @@ static bool disas_insn_x87(DisasContext *s, CPUState *cpu, int b)
return true;
}
static void disas_insn_old(DisasContext *s, CPUState *cpu, int b);
/* convert one instruction. s->base.is_jmp is set if the translation must
be stopped. Return the next pc value */
static bool disas_insn(DisasContext *s, CPUState *cpu)
{
CPUX86State *env = cpu_env(cpu);
int b, prefixes;
MemOp aflag, dflag;
bool orig_cc_op_dirty = s->cc_op_dirty;
CCOp orig_cc_op = s->cc_op;
target_ulong orig_pc_save = s->pc_save;
s->pc = s->base.pc_next;
s->override = -1;
s->popl_esp_hack = 0;
#ifdef TARGET_X86_64
s->rex_r = 0;
s->rex_x = 0;
s->rex_b = 0;
#endif
s->rip_offset = 0; /* for relative ip address */
s->vex_l = 0;
s->vex_v = 0;
s->vex_w = false;
switch (sigsetjmp(s->jmpbuf, 0)) {
case 0:
break;
case 1:
gen_exception_gpf(s);
return true;
case 2:
/* Restore state that may affect the next instruction. */
s->pc = s->base.pc_next;
/*
* TODO: These save/restore can be removed after the table-based
* decoder is complete; we will be decoding the insn completely
* before any code generation that might affect these variables.
*/
s->cc_op_dirty = orig_cc_op_dirty;
s->cc_op = orig_cc_op;
s->pc_save = orig_pc_save;
/* END TODO */
s->base.num_insns--;
tcg_remove_ops_after(s->prev_insn_end);
s->base.insn_start = s->prev_insn_start;
s->base.is_jmp = DISAS_TOO_MANY;
return false;
default:
g_assert_not_reached();
}
prefixes = 0;
next_byte:
s->prefix = prefixes;
b = x86_ldub_code(env, s);
/* Collect prefixes. */
switch (b) {
case 0x0f:
b = x86_ldub_code(env, s) + 0x100;
break;
case 0xf3:
prefixes |= PREFIX_REPZ;
prefixes &= ~PREFIX_REPNZ;
goto next_byte;
case 0xf2:
prefixes |= PREFIX_REPNZ;
prefixes &= ~PREFIX_REPZ;
goto next_byte;
case 0xf0:
prefixes |= PREFIX_LOCK;
goto next_byte;
case 0x2e:
s->override = R_CS;
goto next_byte;
case 0x36:
s->override = R_SS;
goto next_byte;
case 0x3e:
s->override = R_DS;
goto next_byte;
case 0x26:
s->override = R_ES;
goto next_byte;
case 0x64:
s->override = R_FS;
goto next_byte;
case 0x65:
s->override = R_GS;
goto next_byte;
case 0x66:
prefixes |= PREFIX_DATA;
goto next_byte;
case 0x67:
prefixes |= PREFIX_ADR;
goto next_byte;
#ifdef TARGET_X86_64
case 0x40 ... 0x4f:
if (CODE64(s)) {
/* REX prefix */
prefixes |= PREFIX_REX;
s->vex_w = (b >> 3) & 1;
s->rex_r = (b & 0x4) << 1;
s->rex_x = (b & 0x2) << 2;
s->rex_b = (b & 0x1) << 3;
goto next_byte;
}
break;
#endif
case 0xc5: /* 2-byte VEX */
case 0xc4: /* 3-byte VEX */
if (CODE32(s) && !VM86(s)) {
int vex2 = x86_ldub_code(env, s);
s->pc--; /* rewind the advance_pc() x86_ldub_code() did */
if (!CODE64(s) && (vex2 & 0xc0) != 0xc0) {
/* 4.1.4.6: In 32-bit mode, bits [7:6] must be 11b,
otherwise the instruction is LES or LDS. */
break;
}
disas_insn_new(s, cpu, b);
return s->pc;
}
break;
}
/* Post-process prefixes. */
if (CODE64(s)) {
/* In 64-bit mode, the default data size is 32-bit. Select 64-bit
data with rex_w, and 16-bit data with 0x66; rex_w takes precedence
over 0x66 if both are present. */
dflag = (REX_W(s) ? MO_64 : prefixes & PREFIX_DATA ? MO_16 : MO_32);
/* In 64-bit mode, 0x67 selects 32-bit addressing. */
aflag = (prefixes & PREFIX_ADR ? MO_32 : MO_64);
} else {
/* In 16/32-bit mode, 0x66 selects the opposite data size. */
if (CODE32(s) ^ ((prefixes & PREFIX_DATA) != 0)) {
dflag = MO_32;
} else {
dflag = MO_16;
}
/* In 16/32-bit mode, 0x67 selects the opposite addressing. */
if (CODE32(s) ^ ((prefixes & PREFIX_ADR) != 0)) {
aflag = MO_32;
} else {
aflag = MO_16;
}
}
s->prefix = prefixes;
s->aflag = aflag;
s->dflag = dflag;
switch (b) {
case 0 ... 0xd7:
case 0xe0 ... 0xff:
case 0x10e ... 0x117:
case 0x128 ... 0x12f:
case 0x138 ... 0x19f:
case 0x1a0 ... 0x1a1:
case 0x1a8 ... 0x1a9:
case 0x1af:
case 0x1b2:
case 0x1b4 ... 0x1b7:
case 0x1be ... 0x1bf:
case 0x1c2 ... 0x1c6:
case 0x1c8 ... 0x1ff:
disas_insn_new(s, cpu, b);
break;
default:
disas_insn_old(s, cpu, b);
break;
}
return true;
}
static void disas_insn_old(DisasContext *s, CPUState *cpu, int b)
{
CPUX86State *env = cpu_env(cpu);
@ -3502,14 +3321,6 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b)
}
break;
/************************/
/* floats */
case 0xd8 ... 0xdf:
if (!disas_insn_x87(s, cpu, b)) {
goto unknown_op;
}
break;
/************************/
/* bit operations */
case 0x1ba: /* bt/bts/btr/btc Gv, im */
@ -4759,7 +4570,7 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b)
set_cc_op(s, CC_OP_POPCNT);
break;
default:
goto unknown_op;
g_assert_not_reached();
}
return;
illegal_op:
@ -4769,6 +4580,10 @@ static void disas_insn_old(DisasContext *s, CPUState *cpu, int b)
gen_unknown_opcode(env, s);
}
#include "decode-new.h"
#include "emit.c.inc"
#include "decode-new.c.inc"
void tcg_x86_init(void)
{
static const char reg_names[CPU_NB_REGS][4] = {
@ -4890,7 +4705,6 @@ static void i386_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu)
dc->cc_op = CC_OP_DYNAMIC;
dc->cc_op_dirty = false;
dc->popl_esp_hack = 0;
/* select memory access functions */
dc->mem_index = cpu_mmu_index(cpu, false);
dc->cpuid_features = env->features[FEAT_1_EDX];
@ -4942,6 +4756,9 @@ static void i386_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu)
static void i386_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
{
DisasContext *dc = container_of(dcbase, DisasContext, base);
bool orig_cc_op_dirty = dc->cc_op_dirty;
CCOp orig_cc_op = dc->cc_op;
target_ulong orig_pc_save = dc->pc_save;
#ifdef TARGET_VSYSCALL_PAGE
/*
@ -4954,23 +4771,51 @@ static void i386_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
}
#endif
if (disas_insn(dc, cpu)) {
target_ulong pc_next = dc->pc;
dc->base.pc_next = pc_next;
switch (sigsetjmp(dc->jmpbuf, 0)) {
case 0:
disas_insn(dc, cpu);
break;
case 1:
gen_exception_gpf(dc);
break;
case 2:
/* Restore state that may affect the next instruction. */
dc->pc = dc->base.pc_next;
/*
* TODO: These save/restore can be removed after the table-based
* decoder is complete; we will be decoding the insn completely
* before any code generation that might affect these variables.
*/
dc->cc_op_dirty = orig_cc_op_dirty;
dc->cc_op = orig_cc_op;
dc->pc_save = orig_pc_save;
/* END TODO */
dc->base.num_insns--;
tcg_remove_ops_after(dc->prev_insn_end);
dc->base.insn_start = dc->prev_insn_start;
dc->base.is_jmp = DISAS_TOO_MANY;
return;
default:
g_assert_not_reached();
}
if (dc->base.is_jmp == DISAS_NEXT) {
if (dc->flags & (HF_TF_MASK | HF_INHIBIT_IRQ_MASK)) {
/*
* If single step mode, we generate only one instruction and
* generate an exception.
* If irq were inhibited with HF_INHIBIT_IRQ_MASK, we clear
* the flag and abort the translation to give the irqs a
* chance to happen.
*/
dc->base.is_jmp = DISAS_EOB_NEXT;
} else if (!is_same_page(&dc->base, pc_next)) {
dc->base.is_jmp = DISAS_TOO_MANY;
}
/*
* Instruction decoding completed (possibly with #GP if the
* 15-byte boundary was exceeded).
*/
dc->base.pc_next = dc->pc;
if (dc->base.is_jmp == DISAS_NEXT) {
if (dc->flags & (HF_TF_MASK | HF_INHIBIT_IRQ_MASK)) {
/*
* If single step mode, we generate only one instruction and
* generate an exception.
* If irq were inhibited with HF_INHIBIT_IRQ_MASK, we clear
* the flag and abort the translation to give the irqs a
* chance to happen.
*/
dc->base.is_jmp = DISAS_EOB_NEXT;
} else if (!is_same_page(&dc->base, dc->base.pc_next)) {
dc->base.is_jmp = DISAS_TOO_MANY;
}
}
}