target/i386: add ALU load/writeback core
Add generic code generation that takes care of preparing operands around calls to decode.e.gen in a table-driven manner, so that ALU operations need not take care of that. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
b3e22b2318
commit
6ba13999be
@ -513,6 +513,20 @@ static bool decode_insn(DisasContext *s, CPUX86State *env, X86DecodeFunc decode_
|
||||
return true;
|
||||
}
|
||||
|
||||
static void decode_temp_free(X86DecodedOp *op)
|
||||
{
|
||||
if (op->v_ptr) {
|
||||
tcg_temp_free_ptr(op->v_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
static void decode_temps_free(X86DecodedInsn *decode)
|
||||
{
|
||||
decode_temp_free(&decode->op[0]);
|
||||
decode_temp_free(&decode->op[1]);
|
||||
decode_temp_free(&decode->op[2]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert one instruction. s->base.is_jmp is set if the translation must
|
||||
* be stopped.
|
||||
@ -738,7 +752,24 @@ static void disas_insn_new(DisasContext *s, CPUState *cpu, int b)
|
||||
if (decode.op[0].has_ea || decode.op[1].has_ea || decode.op[2].has_ea) {
|
||||
gen_load_ea(s, &decode.mem);
|
||||
}
|
||||
decode.e.gen(s, env, &decode);
|
||||
if (s->prefix & PREFIX_LOCK) {
|
||||
if (decode.op[0].unit != X86_OP_INT || !decode.op[0].has_ea) {
|
||||
goto illegal_op;
|
||||
}
|
||||
gen_load(s, &decode, 2, s->T1);
|
||||
decode.e.gen(s, env, &decode);
|
||||
} else {
|
||||
if (decode.op[0].unit == X86_OP_MMX) {
|
||||
compute_mmx_offset(&decode.op[0]);
|
||||
} else if (decode.op[0].unit == X86_OP_SSE) {
|
||||
compute_xmm_offset(&decode.op[0]);
|
||||
}
|
||||
gen_load(s, &decode, 1, s->T0);
|
||||
gen_load(s, &decode, 2, s->T1);
|
||||
decode.e.gen(s, env, &decode);
|
||||
gen_writeback(s, &decode, 0, s->T0);
|
||||
}
|
||||
decode_temps_free(&decode);
|
||||
return;
|
||||
illegal_op:
|
||||
gen_illegal_opcode(s);
|
||||
|
@ -168,6 +168,13 @@ typedef struct X86DecodedOp {
|
||||
MemOp ot; /* For b/c/d/p/s/q/v/w/y/z */
|
||||
X86OpUnit unit;
|
||||
bool has_ea;
|
||||
int offset; /* For MMX and SSE */
|
||||
|
||||
/*
|
||||
* This field is used internally by macros OP0_PTR/OP1_PTR/OP2_PTR,
|
||||
* do not access directly!
|
||||
*/
|
||||
TCGv_ptr v_ptr;
|
||||
} X86DecodedOp;
|
||||
|
||||
struct X86DecodedInsn {
|
||||
|
@ -29,3 +29,158 @@ 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);
|
||||
}
|
||||
|
||||
static inline int mmx_offset(MemOp ot)
|
||||
{
|
||||
switch (ot) {
|
||||
case MO_8:
|
||||
return offsetof(MMXReg, MMX_B(0));
|
||||
case MO_16:
|
||||
return offsetof(MMXReg, MMX_W(0));
|
||||
case MO_32:
|
||||
return offsetof(MMXReg, MMX_L(0));
|
||||
case MO_64:
|
||||
return offsetof(MMXReg, MMX_Q(0));
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
static inline int xmm_offset(MemOp ot)
|
||||
{
|
||||
switch (ot) {
|
||||
case MO_8:
|
||||
return offsetof(ZMMReg, ZMM_B(0));
|
||||
case MO_16:
|
||||
return offsetof(ZMMReg, ZMM_W(0));
|
||||
case MO_32:
|
||||
return offsetof(ZMMReg, ZMM_L(0));
|
||||
case MO_64:
|
||||
return offsetof(ZMMReg, ZMM_Q(0));
|
||||
case MO_128:
|
||||
return offsetof(ZMMReg, ZMM_X(0));
|
||||
case MO_256:
|
||||
return offsetof(ZMMReg, ZMM_Y(0));
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
static void compute_mmx_offset(X86DecodedOp *op)
|
||||
{
|
||||
if (!op->has_ea) {
|
||||
op->offset = offsetof(CPUX86State, fpregs[op->n].mmx) + mmx_offset(op->ot);
|
||||
} else {
|
||||
op->offset = offsetof(CPUX86State, mmx_t0) + mmx_offset(op->ot);
|
||||
}
|
||||
}
|
||||
|
||||
static void compute_xmm_offset(X86DecodedOp *op)
|
||||
{
|
||||
if (!op->has_ea) {
|
||||
op->offset = ZMM_OFFSET(op->n) + xmm_offset(op->ot);
|
||||
} else {
|
||||
op->offset = offsetof(CPUX86State, xmm_t0) + xmm_offset(op->ot);
|
||||
}
|
||||
}
|
||||
|
||||
static void gen_load_sse(DisasContext *s, TCGv temp, MemOp ot, int dest_ofs, bool aligned)
|
||||
{
|
||||
switch(ot) {
|
||||
case MO_8:
|
||||
gen_op_ld_v(s, MO_8, temp, s->A0);
|
||||
tcg_gen_st8_tl(temp, cpu_env, dest_ofs);
|
||||
break;
|
||||
case MO_16:
|
||||
gen_op_ld_v(s, MO_16, temp, s->A0);
|
||||
tcg_gen_st16_tl(temp, cpu_env, dest_ofs);
|
||||
break;
|
||||
case MO_32:
|
||||
gen_op_ld_v(s, MO_32, temp, s->A0);
|
||||
tcg_gen_st32_tl(temp, cpu_env, dest_ofs);
|
||||
break;
|
||||
case MO_64:
|
||||
gen_ldq_env_A0(s, dest_ofs);
|
||||
break;
|
||||
case MO_128:
|
||||
gen_ldo_env_A0(s, dest_ofs, aligned);
|
||||
break;
|
||||
case MO_256:
|
||||
gen_ldy_env_A0(s, dest_ofs, aligned);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
static void gen_load(DisasContext *s, X86DecodedInsn *decode, int opn, TCGv v)
|
||||
{
|
||||
X86DecodedOp *op = &decode->op[opn];
|
||||
|
||||
switch (op->unit) {
|
||||
case X86_OP_SKIP:
|
||||
return;
|
||||
case X86_OP_SEG:
|
||||
tcg_gen_ld32u_tl(v, cpu_env,
|
||||
offsetof(CPUX86State,segs[op->n].selector));
|
||||
break;
|
||||
case X86_OP_CR:
|
||||
tcg_gen_ld_tl(v, cpu_env, offsetof(CPUX86State, cr[op->n]));
|
||||
break;
|
||||
case X86_OP_DR:
|
||||
tcg_gen_ld_tl(v, cpu_env, offsetof(CPUX86State, dr[op->n]));
|
||||
break;
|
||||
case X86_OP_INT:
|
||||
if (op->has_ea) {
|
||||
gen_op_ld_v(s, op->ot, v, s->A0);
|
||||
} else {
|
||||
gen_op_mov_v_reg(s, op->ot, v, op->n);
|
||||
}
|
||||
break;
|
||||
case X86_OP_IMM:
|
||||
tcg_gen_movi_tl(v, decode->immediate);
|
||||
break;
|
||||
|
||||
case X86_OP_MMX:
|
||||
compute_mmx_offset(op);
|
||||
goto load_vector;
|
||||
|
||||
case X86_OP_SSE:
|
||||
compute_xmm_offset(op);
|
||||
load_vector:
|
||||
if (op->has_ea) {
|
||||
gen_load_sse(s, v, op->ot, op->offset, true);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
static void gen_writeback(DisasContext *s, X86DecodedInsn *decode, int opn, TCGv v)
|
||||
{
|
||||
X86DecodedOp *op = &decode->op[opn];
|
||||
switch (op->unit) {
|
||||
case X86_OP_SKIP:
|
||||
break;
|
||||
case X86_OP_SEG:
|
||||
/* Note that gen_movl_seg_T0 takes care of interrupt shadow and TF. */
|
||||
gen_movl_seg_T0(s, op->n);
|
||||
break;
|
||||
case X86_OP_INT:
|
||||
if (op->has_ea) {
|
||||
gen_op_st_v(s, op->ot, v, s->A0);
|
||||
} else {
|
||||
gen_op_mov_reg_v(s, op->ot, op->n, v);
|
||||
}
|
||||
break;
|
||||
case X86_OP_MMX:
|
||||
case X86_OP_SSE:
|
||||
break;
|
||||
case X86_OP_CR:
|
||||
case X86_OP_DR:
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
@ -2913,6 +2913,24 @@ static inline void gen_sto_env_A0(DisasContext *s, int offset, bool align)
|
||||
tcg_gen_qemu_st_i64(s->tmp1_i64, s->tmp0, mem_index, MO_LEUQ);
|
||||
}
|
||||
|
||||
static void gen_ldy_env_A0(DisasContext *s, int offset, bool align)
|
||||
{
|
||||
int mem_index = s->mem_index;
|
||||
tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, mem_index,
|
||||
MO_LEUQ | (align ? MO_ALIGN_32 : 0));
|
||||
tcg_gen_st_i64(s->tmp1_i64, cpu_env, offset + offsetof(YMMReg, YMM_Q(0)));
|
||||
tcg_gen_addi_tl(s->tmp0, s->A0, 8);
|
||||
tcg_gen_qemu_ld_i64(s->tmp1_i64, s->tmp0, mem_index, MO_LEUQ);
|
||||
tcg_gen_st_i64(s->tmp1_i64, cpu_env, offset + offsetof(YMMReg, YMM_Q(1)));
|
||||
|
||||
tcg_gen_addi_tl(s->tmp0, s->A0, 16);
|
||||
tcg_gen_qemu_ld_i64(s->tmp1_i64, s->tmp0, mem_index, MO_LEUQ);
|
||||
tcg_gen_st_i64(s->tmp1_i64, cpu_env, offset + offsetof(YMMReg, YMM_Q(2)));
|
||||
tcg_gen_addi_tl(s->tmp0, s->A0, 24);
|
||||
tcg_gen_qemu_ld_i64(s->tmp1_i64, s->tmp0, mem_index, MO_LEUQ);
|
||||
tcg_gen_st_i64(s->tmp1_i64, cpu_env, offset + offsetof(YMMReg, YMM_Q(3)));
|
||||
}
|
||||
|
||||
static inline void gen_op_movo(DisasContext *s, int d_offset, int s_offset)
|
||||
{
|
||||
tcg_gen_ld_i64(s->tmp1_i64, cpu_env, s_offset + offsetof(XMMReg, XMM_Q(0)));
|
||||
|
Loading…
Reference in New Issue
Block a user