target-i386: Split up gen_lea_modrm

This is immediately usable by lea and multi-byte nop,
and will be required to implement parts of the mpx spec.

Signed-off-by: Richard Henderson <rth@twiddle.net>
This commit is contained in:
Richard Henderson 2015-07-09 08:22:01 +01:00
parent 7f0b7141b4
commit a074ce42a3

View File

@ -1807,37 +1807,52 @@ static void gen_shifti(DisasContext *s1, int op, TCGMemOp ot, int d, int c)
} }
} }
static void gen_lea_modrm(CPUX86State *env, DisasContext *s, int modrm) /* Decompose an address. */
{
typedef struct AddressParts {
int def_seg;
int base;
int index;
int scale;
target_long disp; target_long disp;
int havesib, base, index, scale; } AddressParts;
int mod, rm, code, def_seg, ovr_seg;
TCGv sum; static AddressParts gen_lea_modrm_0(CPUX86State *env, DisasContext *s,
int modrm)
{
int def_seg, base, index, scale, mod, rm;
target_long disp;
bool havesib;
def_seg = R_DS; def_seg = R_DS;
ovr_seg = s->override; index = -1;
scale = 0;
disp = 0;
mod = (modrm >> 6) & 3; mod = (modrm >> 6) & 3;
rm = modrm & 7; rm = modrm & 7;
base = rm | REX_B(s);
if (mod == 3) {
/* Normally filtered out earlier, but including this path
simplifies multi-byte nop, as well as bndcl, bndcu, bndcn. */
goto done;
}
switch (s->aflag) { switch (s->aflag) {
case MO_64: case MO_64:
case MO_32: case MO_32:
havesib = 0; havesib = 0;
base = rm; if (rm == 4) {
index = -1; int code = cpu_ldub_code(env, s->pc++);
scale = 0;
if (base == 4) {
havesib = 1;
code = cpu_ldub_code(env, s->pc++);
scale = (code >> 6) & 3; scale = (code >> 6) & 3;
index = ((code >> 3) & 7) | REX_X(s); index = ((code >> 3) & 7) | REX_X(s);
if (index == 4) { if (index == 4) {
index = -1; /* no index */ index = -1; /* no index */
} }
base = (code & 7); base = (code & 7) | REX_B(s);
havesib = 1;
} }
base |= REX_B(s);
switch (mod) { switch (mod) {
case 0: case 0:
@ -1846,10 +1861,9 @@ static void gen_lea_modrm(CPUX86State *env, DisasContext *s, int modrm)
disp = (int32_t)cpu_ldl_code(env, s->pc); disp = (int32_t)cpu_ldl_code(env, s->pc);
s->pc += 4; s->pc += 4;
if (CODE64(s) && !havesib) { if (CODE64(s) && !havesib) {
base = -2;
disp += s->pc + s->rip_offset; disp += s->pc + s->rip_offset;
} }
} else {
disp = 0;
} }
break; break;
case 1: case 1:
@ -1866,46 +1880,19 @@ static void gen_lea_modrm(CPUX86State *env, DisasContext *s, int modrm)
if (base == R_ESP && s->popl_esp_hack) { if (base == R_ESP && s->popl_esp_hack) {
disp += s->popl_esp_hack; disp += s->popl_esp_hack;
} }
/* Compute the address, with a minimum number of TCG ops. */
TCGV_UNUSED(sum);
if (index >= 0) {
if (scale == 0) {
sum = cpu_regs[index];
} else {
tcg_gen_shli_tl(cpu_A0, cpu_regs[index], scale);
sum = cpu_A0;
}
if (base >= 0) {
tcg_gen_add_tl(cpu_A0, sum, cpu_regs[base]);
sum = cpu_A0;
}
} else if (base >= 0) {
sum = cpu_regs[base];
}
if (TCGV_IS_UNUSED(sum)) {
tcg_gen_movi_tl(cpu_A0, disp);
sum = cpu_A0;
} else if (disp != 0) {
tcg_gen_addi_tl(cpu_A0, sum, disp);
sum = cpu_A0;
}
if (base == R_EBP || base == R_ESP) { if (base == R_EBP || base == R_ESP) {
def_seg = R_SS; def_seg = R_SS;
} }
break; break;
case MO_16: case MO_16:
sum = cpu_A0;
if (mod == 0) { if (mod == 0) {
if (rm == 6) { if (rm == 6) {
base = -1;
disp = cpu_lduw_code(env, s->pc); disp = cpu_lduw_code(env, s->pc);
s->pc += 2; s->pc += 2;
tcg_gen_movi_tl(cpu_A0, disp);
break; break;
} }
disp = 0;
} else if (mod == 1) { } else if (mod == 1) {
disp = (int8_t)cpu_ldub_code(env, s->pc++); disp = (int8_t)cpu_ldub_code(env, s->pc++);
} else { } else {
@ -1915,102 +1902,89 @@ static void gen_lea_modrm(CPUX86State *env, DisasContext *s, int modrm)
switch (rm) { switch (rm) {
case 0: case 0:
tcg_gen_add_tl(cpu_A0, cpu_regs[R_EBX], cpu_regs[R_ESI]); base = R_EBX;
index = R_ESI;
break; break;
case 1: case 1:
tcg_gen_add_tl(cpu_A0, cpu_regs[R_EBX], cpu_regs[R_EDI]); base = R_EBX;
index = R_EDI;
break; break;
case 2: case 2:
tcg_gen_add_tl(cpu_A0, cpu_regs[R_EBP], cpu_regs[R_ESI]); base = R_EBP;
index = R_ESI;
def_seg = R_SS; def_seg = R_SS;
break; break;
case 3: case 3:
tcg_gen_add_tl(cpu_A0, cpu_regs[R_EBP], cpu_regs[R_EDI]); base = R_EBP;
index = R_EDI;
def_seg = R_SS; def_seg = R_SS;
break; break;
case 4: case 4:
sum = cpu_regs[R_ESI]; base = R_ESI;
break; break;
case 5: case 5:
sum = cpu_regs[R_EDI]; base = R_EDI;
break; break;
case 6: case 6:
sum = cpu_regs[R_EBP]; base = R_EBP;
def_seg = R_SS; def_seg = R_SS;
break; break;
default: default:
case 7: case 7:
sum = cpu_regs[R_EBX]; base = R_EBX;
break; break;
} }
if (disp != 0) {
tcg_gen_addi_tl(cpu_A0, sum, disp);
sum = cpu_A0;
}
break; break;
default: default:
tcg_abort(); tcg_abort();
} }
gen_lea_v_seg(s, s->aflag, sum, def_seg, ovr_seg); done:
return (AddressParts){ def_seg, base, index, scale, disp };
}
/* Compute the address, with a minimum number of TCG ops. */
static TCGv gen_lea_modrm_1(AddressParts a)
{
TCGv ea;
TCGV_UNUSED(ea);
if (a.index >= 0) {
if (a.scale == 0) {
ea = cpu_regs[a.index];
} else {
tcg_gen_shli_tl(cpu_A0, cpu_regs[a.index], a.scale);
ea = cpu_A0;
}
if (a.base >= 0) {
tcg_gen_add_tl(cpu_A0, ea, cpu_regs[a.base]);
ea = cpu_A0;
}
} else if (a.base >= 0) {
ea = cpu_regs[a.base];
}
if (TCGV_IS_UNUSED(ea)) {
tcg_gen_movi_tl(cpu_A0, a.disp);
ea = cpu_A0;
} else if (a.disp != 0) {
tcg_gen_addi_tl(cpu_A0, ea, a.disp);
ea = cpu_A0;
}
return ea;
}
static void gen_lea_modrm(CPUX86State *env, DisasContext *s, int modrm)
{
AddressParts a = gen_lea_modrm_0(env, s, modrm);
TCGv ea = gen_lea_modrm_1(a);
gen_lea_v_seg(s, s->aflag, ea, a.def_seg, s->override);
} }
static void gen_nop_modrm(CPUX86State *env, DisasContext *s, int modrm) static void gen_nop_modrm(CPUX86State *env, DisasContext *s, int modrm)
{ {
int mod, rm, base, code; (void)gen_lea_modrm_0(env, s, modrm);
mod = (modrm >> 6) & 3;
if (mod == 3)
return;
rm = modrm & 7;
switch (s->aflag) {
case MO_64:
case MO_32:
base = rm;
if (base == 4) {
code = cpu_ldub_code(env, s->pc++);
base = (code & 7);
}
switch (mod) {
case 0:
if (base == 5) {
s->pc += 4;
}
break;
case 1:
s->pc++;
break;
default:
case 2:
s->pc += 4;
break;
}
break;
case MO_16:
switch (mod) {
case 0:
if (rm == 6) {
s->pc += 2;
}
break;
case 1:
s->pc++;
break;
default:
case 2:
s->pc += 2;
break;
}
break;
default:
tcg_abort();
}
} }
/* used for LEA and MOV AX, mem */ /* used for LEA and MOV AX, mem */
@ -5302,19 +5276,16 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
break; break;
case 0x8d: /* lea */ case 0x8d: /* lea */
ot = dflag;
modrm = cpu_ldub_code(env, s->pc++); modrm = cpu_ldub_code(env, s->pc++);
mod = (modrm >> 6) & 3; mod = (modrm >> 6) & 3;
if (mod == 3) if (mod == 3)
goto illegal_op; goto illegal_op;
reg = ((modrm >> 3) & 7) | rex_r; reg = ((modrm >> 3) & 7) | rex_r;
/* we must ensure that no segment is added */ {
s->override = -1; AddressParts a = gen_lea_modrm_0(env, s, modrm);
val = s->addseg; TCGv ea = gen_lea_modrm_1(a);
s->addseg = 0; gen_op_mov_reg_v(dflag, reg, ea);
gen_lea_modrm(env, s, modrm); }
s->addseg = val;
gen_op_mov_reg_v(ot, reg, cpu_A0);
break; break;
case 0xa0: /* mov EAX, Ov */ case 0xa0: /* mov EAX, Ov */