mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
target/i386: add core of new i386 decoder
The new decoder is based on three principles: - use mostly table-driven decoding, using tables derived as much as possible from the Intel manual. Centralizing the decode the operands makes it more homogeneous, for example all immediates are signed. All modrm handling is in one function, and can be shared between SSE and ALU instructions (including XMM<->GPR instructions). The SSE/AVX decoder will also not have duplicated code between the 0F, 0F38 and 0F3A tables. - keep the code as "non-branchy" as possible. Generally, the code for the new decoder is more verbose, but the control flow is simpler. Conditionals are not nested and have small bodies. All instruction groups are resolved even before operands are decoded, and code generation is separated as much as possible within small functions that only handle one instruction each. - keep address generation and (for ALU operands) memory loads and writeback as much in common code as possible. All ALU operations for example are implemented as T0=f(T0,T1). For non-ALU instructions, read-modify-write memory operations are rare, but registers do not have TCGv equivalents: therefore, the common logic sets up pointer temporaries with the operands, while load and writeback are handled by gvec or by helpers. These principles make future code review and extensibility simpler, at the cost of having a relatively large amount of code in the form of this patch. Even EVEX should not be _too_ hard to implement (it's just a crazy large amount of possibilities). This patch introduces the main decoder flow, and integrates the old decoder with the new one. The old decoder takes care of parsing prefixes and then optionally drops to the new one. The changes to the old decoder are minimal and allow it to be replaced incrementally with the new one. There is a debugging mechanism through a "LIMIT" environment variable. In user-mode emulation, the variable is the number of instructions decoded by the new decoder before permanently switching to the old one. In system emulation, the variable is the highest opcode that is decoded by the new decoder (this is less friendly, but it's the best that can be done without requiring deterministic execution). Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* Decode table flags, mostly based on Intel SDM.
|
||||
*
|
||||
* Copyright (c) 2022 Red Hat, Inc.
|
||||
*
|
||||
* Author: Paolo Bonzini <pbonzini@redhat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
typedef enum X86OpType {
|
||||
X86_TYPE_None,
|
||||
|
||||
X86_TYPE_A, /* Implicit */
|
||||
X86_TYPE_B, /* VEX.vvvv selects a GPR */
|
||||
X86_TYPE_C, /* REG in the modrm byte selects a control register */
|
||||
X86_TYPE_D, /* REG in the modrm byte selects a debug register */
|
||||
X86_TYPE_E, /* ALU modrm operand */
|
||||
X86_TYPE_F, /* EFLAGS/RFLAGS */
|
||||
X86_TYPE_G, /* REG in the modrm byte selects a GPR */
|
||||
X86_TYPE_H, /* For AVX, VEX.vvvv selects an XMM/YMM register */
|
||||
X86_TYPE_I, /* Immediate */
|
||||
X86_TYPE_J, /* Relative offset for a jump */
|
||||
X86_TYPE_L, /* The upper 4 bits of the immediate select a 128-bit register */
|
||||
X86_TYPE_M, /* modrm byte selects a memory operand */
|
||||
X86_TYPE_N, /* R/M in the modrm byte selects an MMX register */
|
||||
X86_TYPE_O, /* Absolute address encoded in the instruction */
|
||||
X86_TYPE_P, /* reg in the modrm byte selects an MMX register */
|
||||
X86_TYPE_Q, /* MMX modrm operand */
|
||||
X86_TYPE_R, /* R/M in the modrm byte selects a register */
|
||||
X86_TYPE_S, /* reg selects a segment register */
|
||||
X86_TYPE_U, /* R/M in the modrm byte selects an XMM/YMM register */
|
||||
X86_TYPE_V, /* reg in the modrm byte selects an XMM/YMM register */
|
||||
X86_TYPE_W, /* XMM/YMM modrm operand */
|
||||
X86_TYPE_X, /* string source */
|
||||
X86_TYPE_Y, /* string destination */
|
||||
|
||||
/* Custom */
|
||||
X86_TYPE_2op, /* 2-operand RMW instruction */
|
||||
X86_TYPE_LoBits, /* encoded in bits 0-2 of the operand + REX.B */
|
||||
X86_TYPE_0, /* Hard-coded GPRs (RAX..RDI) */
|
||||
X86_TYPE_1,
|
||||
X86_TYPE_2,
|
||||
X86_TYPE_3,
|
||||
X86_TYPE_4,
|
||||
X86_TYPE_5,
|
||||
X86_TYPE_6,
|
||||
X86_TYPE_7,
|
||||
X86_TYPE_ES, /* Hard-coded segment registers */
|
||||
X86_TYPE_CS,
|
||||
X86_TYPE_SS,
|
||||
X86_TYPE_DS,
|
||||
X86_TYPE_FS,
|
||||
X86_TYPE_GS,
|
||||
} X86OpType;
|
||||
|
||||
typedef enum X86OpSize {
|
||||
X86_SIZE_None,
|
||||
|
||||
X86_SIZE_a, /* BOUND operand */
|
||||
X86_SIZE_b, /* byte */
|
||||
X86_SIZE_d, /* 32-bit */
|
||||
X86_SIZE_dq, /* SSE/AVX 128-bit */
|
||||
X86_SIZE_p, /* Far pointer */
|
||||
X86_SIZE_pd, /* SSE/AVX packed double precision */
|
||||
X86_SIZE_pi, /* MMX */
|
||||
X86_SIZE_ps, /* SSE/AVX packed single precision */
|
||||
X86_SIZE_q, /* 64-bit */
|
||||
X86_SIZE_qq, /* AVX 256-bit */
|
||||
X86_SIZE_s, /* Descriptor */
|
||||
X86_SIZE_sd, /* SSE/AVX scalar double precision */
|
||||
X86_SIZE_ss, /* SSE/AVX scalar single precision */
|
||||
X86_SIZE_si, /* 32-bit GPR */
|
||||
X86_SIZE_v, /* 16/32/64-bit, based on operand size */
|
||||
X86_SIZE_w, /* 16-bit */
|
||||
X86_SIZE_x, /* 128/256-bit, based on operand size */
|
||||
X86_SIZE_y, /* 32/64-bit, based on operand size */
|
||||
X86_SIZE_z, /* 16-bit for 16-bit operand size, else 32-bit */
|
||||
|
||||
/* Custom */
|
||||
X86_SIZE_d64,
|
||||
X86_SIZE_f64,
|
||||
} X86OpSize;
|
||||
|
||||
/* Execution flags */
|
||||
|
||||
typedef enum X86OpUnit {
|
||||
X86_OP_SKIP, /* not valid or managed by emission function */
|
||||
X86_OP_SEG, /* segment selector */
|
||||
X86_OP_CR, /* control register */
|
||||
X86_OP_DR, /* debug register */
|
||||
X86_OP_INT, /* loaded into/stored from s->T0/T1 */
|
||||
X86_OP_IMM, /* immediate */
|
||||
X86_OP_SSE, /* address in either s->ptrX or s->A0 depending on has_ea */
|
||||
X86_OP_MMX, /* address in either s->ptrX or s->A0 depending on has_ea */
|
||||
} X86OpUnit;
|
||||
|
||||
typedef enum X86InsnSpecial {
|
||||
X86_SPECIAL_None,
|
||||
|
||||
/* Always locked if it has a memory operand (XCHG) */
|
||||
X86_SPECIAL_Locked,
|
||||
|
||||
/* Fault outside protected mode */
|
||||
X86_SPECIAL_ProtMode,
|
||||
|
||||
/*
|
||||
* Register operand 0/2 is zero extended to 32 bits. Rd/Mb or Rd/Mw
|
||||
* in the manual.
|
||||
*/
|
||||
X86_SPECIAL_ZExtOp0,
|
||||
X86_SPECIAL_ZExtOp2,
|
||||
|
||||
/*
|
||||
* MMX instruction exists with no prefix; if there is no prefix, V/H/W/U operands
|
||||
* become P/P/Q/N, and size "x" becomes "q".
|
||||
*/
|
||||
X86_SPECIAL_MMX,
|
||||
|
||||
/* Illegal or exclusive to 64-bit mode */
|
||||
X86_SPECIAL_i64,
|
||||
X86_SPECIAL_o64,
|
||||
} X86InsnSpecial;
|
||||
|
||||
typedef struct X86OpEntry X86OpEntry;
|
||||
typedef struct X86DecodedInsn X86DecodedInsn;
|
||||
|
||||
/* Decode function for multibyte opcodes. */
|
||||
typedef void (*X86DecodeFunc)(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b);
|
||||
|
||||
/* Code generation function. */
|
||||
typedef void (*X86GenFunc)(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode);
|
||||
|
||||
struct X86OpEntry {
|
||||
/* Based on the is_decode flags. */
|
||||
union {
|
||||
X86GenFunc gen;
|
||||
X86DecodeFunc decode;
|
||||
};
|
||||
/* op0 is always written, op1 and op2 are always read. */
|
||||
X86OpType op0:8;
|
||||
X86OpSize s0:8;
|
||||
X86OpType op1:8;
|
||||
X86OpSize s1:8;
|
||||
X86OpType op2:8;
|
||||
X86OpSize s2:8;
|
||||
/* Must be I and b respectively if present. */
|
||||
X86OpType op3:8;
|
||||
X86OpSize s3:8;
|
||||
|
||||
X86InsnSpecial special:8;
|
||||
bool is_decode:1;
|
||||
};
|
||||
|
||||
typedef struct X86DecodedOp {
|
||||
int8_t n;
|
||||
MemOp ot; /* For b/c/d/p/s/q/v/w/y/z */
|
||||
X86OpUnit unit;
|
||||
bool has_ea;
|
||||
} X86DecodedOp;
|
||||
|
||||
struct X86DecodedInsn {
|
||||
X86OpEntry e;
|
||||
X86DecodedOp op[3];
|
||||
target_ulong immediate;
|
||||
AddressParts mem;
|
||||
|
||||
uint8_t b;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* New-style TCG opcode generator for i386 instructions
|
||||
*
|
||||
* Copyright (c) 2022 Red Hat, Inc.
|
||||
*
|
||||
* Author: Paolo Bonzini <pbonzini@redhat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
static void gen_illegal(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
|
||||
{
|
||||
gen_illegal_opcode(s);
|
||||
}
|
||||
|
||||
static void gen_load_ea(DisasContext *s, AddressParts *mem)
|
||||
{
|
||||
TCGv ea = gen_lea_modrm_1(s, *mem);
|
||||
gen_lea_v_seg(s, s->aflag, ea, mem->def_seg, s->override);
|
||||
}
|
||||
@@ -86,6 +86,9 @@ typedef struct DisasContext {
|
||||
int8_t override; /* -1 if no override, else R_CS, R_DS, etc */
|
||||
uint8_t prefix;
|
||||
|
||||
bool has_modrm;
|
||||
uint8_t modrm;
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
uint8_t cpl; /* code priv level */
|
||||
uint8_t iopl; /* i/o priv level */
|
||||
@@ -2425,6 +2428,31 @@ static inline uint32_t insn_get(CPUX86State *env, DisasContext *s, MemOp ot)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static target_long insn_get_signed(CPUX86State *env, DisasContext *s, MemOp ot)
|
||||
{
|
||||
target_long ret;
|
||||
|
||||
switch (ot) {
|
||||
case MO_8:
|
||||
ret = (int8_t) x86_ldub_code(env, s);
|
||||
break;
|
||||
case MO_16:
|
||||
ret = (int16_t) x86_lduw_code(env, s);
|
||||
break;
|
||||
case MO_32:
|
||||
ret = (int32_t) x86_ldl_code(env, s);
|
||||
break;
|
||||
#ifdef TARGET_X86_64
|
||||
case MO_64:
|
||||
ret = x86_ldq_code(env, s);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int insn_const_size(MemOp ot)
|
||||
{
|
||||
if (ot <= MO_32) {
|
||||
@@ -2927,6 +2955,11 @@ typedef void (*SSEFunc_0_ppi)(TCGv_ptr reg_a, TCGv_ptr reg_b, TCGv_i32 val);
|
||||
typedef void (*SSEFunc_0_eppt)(TCGv_ptr env, TCGv_ptr reg_a, TCGv_ptr reg_b,
|
||||
TCGv val);
|
||||
|
||||
static bool first = true; static unsigned long limit;
|
||||
#include "decode-new.h"
|
||||
#include "emit.c.inc"
|
||||
#include "decode-new.c.inc"
|
||||
|
||||
#define SSE_OPF_CMP (1 << 1) /* does not write for first operand */
|
||||
#define SSE_OPF_SPECIAL (1 << 3) /* magic */
|
||||
#define SSE_OPF_3DNOW (1 << 4) /* 3DNow! instruction */
|
||||
@@ -4859,10 +4892,35 @@ static bool disas_insn(DisasContext *s, CPUState *cpu)
|
||||
|
||||
prefixes = 0;
|
||||
|
||||
if (first) first = false, limit = getenv("LIMIT") ? atol(getenv("LIMIT")) : -1;
|
||||
bool use_new = true;
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
use_new &= limit > 0;
|
||||
#endif
|
||||
next_byte:
|
||||
s->prefix = prefixes;
|
||||
b = x86_ldub_code(env, s);
|
||||
/* Collect prefixes. */
|
||||
switch (b) {
|
||||
default:
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
use_new &= b <= limit;
|
||||
#endif
|
||||
if (use_new && 0) {
|
||||
disas_insn_new(s, cpu, b);
|
||||
return s->pc;
|
||||
}
|
||||
break;
|
||||
case 0x0f:
|
||||
b = x86_ldub_code(env, s) + 0x100;
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
use_new &= b <= limit;
|
||||
#endif
|
||||
if (use_new && 0) {
|
||||
disas_insn_new(s, cpu, b + 0x100);
|
||||
return s->pc;
|
||||
}
|
||||
break;
|
||||
case 0xf3:
|
||||
prefixes |= PREFIX_REPZ;
|
||||
prefixes &= ~PREFIX_REPNZ;
|
||||
@@ -4913,6 +4971,7 @@ static bool disas_insn(DisasContext *s, CPUState *cpu)
|
||||
#endif
|
||||
case 0xc5: /* 2-byte VEX */
|
||||
case 0xc4: /* 3-byte VEX */
|
||||
use_new = false;
|
||||
/* VEX prefixes cannot be used except in 32-bit mode.
|
||||
Otherwise the instruction is LES or LDS. */
|
||||
if (CODE32(s) && !VM86(s)) {
|
||||
@@ -4997,14 +5056,7 @@ static bool disas_insn(DisasContext *s, CPUState *cpu)
|
||||
s->dflag = dflag;
|
||||
|
||||
/* now check op code */
|
||||
reswitch:
|
||||
switch(b) {
|
||||
case 0x0f:
|
||||
/**************************/
|
||||
/* extended op code */
|
||||
b = x86_ldub_code(env, s) | 0x100;
|
||||
goto reswitch;
|
||||
|
||||
switch (b) {
|
||||
/**************************/
|
||||
/* arith & logic */
|
||||
case 0x00 ... 0x05:
|
||||
|
||||
Reference in New Issue
Block a user