Mucho x86 instruction selector hacking.

git-svn-id: svn://svn.valgrind.org/vex/trunk@90
This commit is contained in:
Julian Seward
2004-07-16 21:03:45 +00:00
parent 5bca5080f6
commit d64a131f0f
6 changed files with 243 additions and 83 deletions

View File

@@ -26,23 +26,29 @@
and does not change.
- A mapping from IRTemp to HReg. This tells the insn selector
which virtual register is associated with each IRTemp temporary.
This is computed before insn selection starts, and does not
change. We expect this mapping to map precisely the same set
of IRTemps as the type mapping does.
which virtual register(s) are associated with each IRTemp
temporary. This is computed before insn selection starts, and
does not change. We expect this mapping to map precisely the
same set of IRTemps as the type mapping does.
- vregmap holds the primary register for the IRTemp.
- vregmapHI is only used for 64-bit integer-typed
IRTemps. It holds the identity of a second
32-bit virtual HReg, which holds the high half
of the value.
- The code array, that is, the insns selected so far.
- A counter, for generating new virtual registers.
Note, this is all host-independent.
*/
Note, this is all host-independent. */
typedef
struct {
IRTypeEnv* type_env;
HReg* vregmap;
HReg* vregmapHI;
Int n_vregmap;
HInstrArray* code;
@@ -59,6 +65,14 @@ static HReg lookupIRTemp ( ISelEnv* env, IRTemp tmp )
return env->vregmap[tmp];
}
static void lookupIRTemp64 ( ISelEnv* env, IRTemp tmp, HReg* vrHI, HReg* vrLO )
{
vassert(tmp >= 0);
vassert(tmp < env->n_vregmap);
*vrLO = env->vregmap[tmp];
*vrHI = env->vregmapHI[tmp];
}
static void addInstr ( ISelEnv* env, X86Instr* instr )
{
addHInstr(env->code, instr);
@@ -80,6 +94,7 @@ static HReg newVRegI ( ISelEnv* env )
/* forwards ... */
static X86RMI* iselIntExpr_RMI ( ISelEnv* env, IRExpr* e );
static X86AMode* iselIntExpr_AMode ( ISelEnv* env, IRExpr* e );
static X86Instr* mk_MOV_RR ( HReg src, HReg dst )
@@ -100,69 +115,130 @@ static X86Instr* mk_MOV_RR ( HReg src, HReg dst )
static HReg iselIntExpr_R ( ISelEnv* env, IRExpr* e )
{
vassert(e);
vassert(typeOfIRExpr(env->type_env,e) == Ity_I32);
IRType ty = typeOfIRExpr(env->type_env,e);
switch (e->tag) {
case Iex_Tmp:
return lookupIRTemp(env, e->Iex.Tmp.tmp);
case Iex_Tmp: {
vassert(ty == Ity_I32);
return lookupIRTemp(env, e->Iex.Tmp.tmp);
}
case Iex_Binop:
/* Add32(x,y). For commutative ops we assume any literal
case Iex_LDle: {
X86AMode* amode = iselIntExpr_AMode ( env, e->Iex.LDle.addr );
HReg dst = newVRegI(env);
vassert(ty == Ity_I32);
addInstr(env, X86Instr_Alu32R(Xalu_MOV,
X86RMI_Mem(amode), dst) );
return dst;
}
case Iex_Binop: {
X86AluOp aluOp;
X86ShiftOp shOp;
vassert(ty == Ity_I32);
switch (e->Iex.Binop.op) {
case Iop_Add32: aluOp = Xalu_ADD; break;
case Iop_Sub32: aluOp = Xalu_SUB; break;
case Iop_And32: aluOp = Xalu_AND; break;
case Iop_Or32: aluOp = Xalu_OR; break;
case Iop_Xor32: aluOp = Xalu_XOR; break;
default: aluOp = Xalu_INVALID; break;
}
/* For commutative ops we assume any literal
values are on the second operand. */
if (e->Iex.Binop.op == Iop_Add32
|| e->Iex.Binop.op == Iop_Sub32) {
if (aluOp != Xalu_INVALID) {
HReg dst = newVRegI(env);
HReg reg = iselIntExpr_R(env, e->Iex.Binop.arg1);
X86RMI* rmi = iselIntExpr_RMI(env, e->Iex.Binop.arg2);
addInstr(env, mk_MOV_RR(reg,dst));
addInstr(env, X86Instr_Alu32R(e->Iex.Binop.op == Iop_Add32
? Xalu_ADD : Xalu_SUB, rmi, dst));
addInstr(env, X86Instr_Alu32R(aluOp, rmi, dst));
return dst;
}
if (e->Iex.Binop.op == Iop_Shl32) {
switch (e->Iex.Binop.op) {
case Iop_Shl32: shOp = Xsh_SHL; break;
case Iop_Shr32: shOp = Xsh_SHR; break;
case Iop_Sar32: shOp = Xsh_SAR; break;
default: shOp = Xsh_INVALID; break;
}
if (shOp != Xsh_INVALID) {
HReg dst = newVRegI(env);
HReg regL = iselIntExpr_R(env, e->Iex.Binop.arg1);
HReg regR = iselIntExpr_R(env, e->Iex.Binop.arg2);
addInstr(env, mk_MOV_RR(regL,dst));
addInstr(env, mk_MOV_RR(regR,hregX86_ECX()));
addInstr(env, X86Instr_Sh32(Xsh_SHL, 0/* %cl */, X86RM_Reg(dst)));
return dst;
}
case Iex_Get: {
IRType ty = e->Iex.Get.ty;
if (ty == Ity_I32) {
HReg dst = newVRegI(env);
addInstr(env, X86Instr_Alu32R(Xalu_MOV,
X86RMI_Mem(X86AMode_IR(e->Iex.Get.offset, hregX86_EBP())),
dst));
addInstr(env, mk_MOV_RR(regL,dst));
addInstr(env, mk_MOV_RR(regR,hregX86_ECX()));
addInstr(env, X86Instr_Sh32(shOp, 0/* %cl */, X86RM_Reg(dst)));
return dst;
}
break;
}
#if 0
/* 32-bit literals */
case Iex_Const: {
switch (e->Iex.Const.con->tag) {
case Ico_U32: {
HReg r = newVRegI(env);
addInstr(env,
X86Instr_Mov32(X86Operand_Imm(e->Iex.Const.con->Ico.U32),
X86Operand_Reg(r)));
return r;
}
default: break;
case Iex_Get: {
if (ty == Ity_I32) {
HReg dst = newVRegI(env);
addInstr(env, X86Instr_Alu32R(
Xalu_MOV,
X86RMI_Mem(X86AMode_IR(e->Iex.Get.offset,
hregX86_EBP())),
dst));
return dst;
}
break;
}
case Iex_CCall: {
Int i, nargs;
UInt target;
IRExpr* arg;
vassert(ty == Ity_I32);
/* be very restrictive for now. Only 32-bit ints allowed
for args and return type. */
if (e->Iex.CCall.retty != Ity_I32)
break;
/* push args on the stack, right to left. */
nargs = 0;
while (e->Iex.CCall.args[nargs]) nargs++;
for (i = nargs-1; i >= 0; i--) {
arg = e->Iex.CCall.args[i];
if (typeOfIRExpr(env->type_env,arg) != Ity_I32)
goto irreducible;
addInstr(env, X86Instr_Push(iselIntExpr_RMI(env, arg)));
}
target = 0x12345678; //FIND_HELPER(e->Iex.CCall.name);
addInstr(env, X86Instr_Alu32R(
Xalu_MOV,
X86RMI_Imm(target),
hregX86_EAX()));
addInstr(env, X86Instr_Call(hregX86_EAX()));
if (nargs > 0)
addInstr(env, X86Instr_Alu32R(Xalu_ADD,
X86RMI_Imm(4*nargs),
hregX86_ESP()));
return hregX86_EAX();
}
/* 32/16/8-bit literals */
case Iex_Const: {
UInt u;
vassert(ty == Ity_I32 || ty == Ity_I8);
switch (e->Iex.Const.con->tag) {
case Ico_U32: u = e->Iex.Const.con->Ico.U32; break;
case Ico_U16: u = 0xFFFF & (e->Iex.Const.con->Ico.U16); break;
case Ico_U8: u = 0xFF & (e->Iex.Const.con->Ico.U8); break;
default: vpanic("iselIntExpr_R.Iex_Const(x86h)");
}
HReg r = newVRegI(env);
addInstr(env, X86Instr_Alu32R(Xalu_MOV, X86RMI_Imm(u), r));
return r;
}
#endif
default:
break;
} /* switch (e->tag) */
/* We get here if no pattern matched. */
irreducible:
ppIRExpr(e);
vpanic("iselIntExpr_R: cannot reduce tree");
}
@@ -227,6 +303,12 @@ static X86RMI* iselIntExpr_RMI ( ISelEnv* env, IRExpr* e )
return X86RMI_Imm(e->Iex.Const.con->Ico.U32);
}
/* special case: 32-bit GET */
if (e->tag == Iex_Get && e->Iex.Get.ty ==Ity_I32) {
return X86RMI_Mem(X86AMode_IR(e->Iex.Get.offset,
hregX86_EBP()));
}
/* special case: load from memory */
/* default case: calculate into a register and return that */
@@ -299,6 +381,7 @@ static void iselStmt ( ISelEnv* env, IRStmt* stmt )
}
break;
}
case Ist_Tmp: {
IRTemp tmp = stmt->Ist.Tmp.tmp;
IRType ty = lookupIRTypeEnv(env->type_env, tmp);
@@ -311,6 +394,25 @@ static void iselStmt ( ISelEnv* env, IRStmt* stmt )
break;
}
case Ist_Exit: {
if (stmt->Ist.Exit.dst->tag != Ico_U32)
vpanic("isel_x86: Ist_Exit: dst is not a 32-bit value");
/* For the moment, only handle conditions of the form
32to1(...). */
IRExpr* cond = stmt->Ist.Exit.cond;
if (cond->tag == Iex_Unop && cond->Iex.Unop.op == Iop_32to1) {
cond = cond->Iex.Unop.arg;
} else {
break; /* give up */
}
HReg reg = iselIntExpr_R( env, cond );
/* Set the Z flag -- as the inverse of the lowest bit of cond */
addInstr(env, X86Instr_Alu32R(Xalu_AND,X86RMI_Imm(1),reg));
addInstr(env, X86Instr_GotoNZ(
True, X86RI_Imm(stmt->Ist.Exit.dst->Ico.U32)));
return;
}
default: break;
}
ppIRStmt(stmt);
@@ -324,25 +426,13 @@ static void iselStmt ( ISelEnv* env, IRStmt* stmt )
static void iselNext ( ISelEnv* env, IRExpr* next, IRJumpKind jk )
{
X86RI* ri;
vex_printf("-- goto ");
ppIRExpr(next);
vex_printf("\n");
switch (next->tag) {
#if 0
case Inx_DJump: {
vassert(next->Inx.DJump.dst->tag == Ico_U32);
addInstr(env, X86Instr_Alu32R(
Xalu_MOV,
X86RMI_Imm(next->Inx.DJump.dst->Ico.U32),
hregX86_EAX()));
addInstr(env, X86Instr_RET());
return;
}
#endif
default:
vpanic("iselNext");
}
ri = iselIntExpr_RI(env, next);
addInstr(env, X86Instr_GotoNZ(False,ri));
}
@@ -355,7 +445,7 @@ static void iselNext ( ISelEnv* env, IRExpr* next, IRJumpKind jk )
HInstrArray* iselBB_X86 ( IRBB* bb )
{
Int i;
HReg hreg;
HReg hreg, hregHI;
IRStmt* stmt;
/* Make up an initial environment to use. */
@@ -372,15 +462,23 @@ HInstrArray* iselBB_X86 ( IRBB* bb )
change as we go along. */
env->n_vregmap = bb->tyenv->types_used;
env->vregmap = LibVEX_Alloc(env->n_vregmap * sizeof(HReg));
env->vregmapHI = LibVEX_Alloc(env->n_vregmap * sizeof(HReg));
/* For each IR temporary, allocate a suitably-kinded virtual
register. */
for (i = 0; i < env->n_vregmap; i++) {
hregHI = hreg = INVALID_HREG;
switch (bb->tyenv->types[i]) {
case Ity_I32: hreg = mkHReg(i, HRcInt, True); break;
default: vpanic("iselBB: IRTemp type");
case Ity_Bit:
case Ity_I8:
case Ity_I32: hreg = mkHReg(i, HRcInt, True); break;
case Ity_I64: hreg = mkHReg(i, HRcInt, True);
hregHI = mkHReg(i, HRcInt, True); break;
default: ppIRType(bb->tyenv->types[i]);
vpanic("iselBB: IRTemp type");
}
env->vregmap[i] = hreg;
env->vregmap[i] = hreg;
env->vregmapHI[i] = hregHI;
}
env->vreg_ctr = env->n_vregmap;

View File

@@ -387,9 +387,25 @@ X86Instr* X86Instr_Sh32 ( X86ShiftOp op, UInt src, X86RM* dst ) {
return i;
}
X86Instr* X86Instr_RET ( void ) {
X86Instr* X86Instr_Push( X86RMI* src ) {
X86Instr* i = LibVEX_Alloc(sizeof(X86Instr));
i->tag = Xin_RET;
i->tag = Xin_Push;
i->Xin.Push.src = src;
return i;
}
X86Instr* X86Instr_Call ( HReg target ) {
X86Instr* i = LibVEX_Alloc(sizeof(X86Instr));
i->tag = Xin_Call;
i->Xin.Call.target = target;
return i;
}
X86Instr* X86Instr_GotoNZ ( Bool onlyWhenNZ, X86RI* dst ) {
X86Instr* i = LibVEX_Alloc(sizeof(X86Instr));
i->tag = Xin_GotoNZ;
i->Xin.GotoNZ.onlyWhenNZ = onlyWhenNZ;
i->Xin.GotoNZ.dst = dst;
return i;
}
@@ -418,8 +434,24 @@ void ppX86Instr ( X86Instr* i ) {
vex_printf(" $%d,", i->Xin.Sh32.src);
ppX86RM(i->Xin.Sh32.dst);
return;
case Xin_RET:
vex_printf("ret");
case Xin_Push:
vex_printf("pushl ");
ppX86RMI(i->Xin.Push.src);
return;
case Xin_Call:
vex_printf("call *");
ppHRegX86(i->Xin.Call.target);
break;
case Xin_GotoNZ:
if (i->Xin.GotoNZ.onlyWhenNZ) {
vex_printf("if (%%eflags.Z) { movl ");
ppX86RI(i->Xin.GotoNZ.dst);
vex_printf(",%%eax ; ret }");
} else {
vex_printf("movl ");
ppX86RI(i->Xin.GotoNZ.dst);
vex_printf(",%%eax ; ret");
}
return;
default:
vpanic("ppX86Instr");
@@ -448,12 +480,21 @@ void getRegUsage_X86Instr (HRegUsage* u, X86Instr* i)
if (i->Xin.Sh32.src == 0)
addHRegUse(u, HRmRead, hregX86_ECX());
return;
case Xin_RET:
/* Using our calling conventions, %eax is live into a ret,
because we know the dispatcher -- to which we're returning
-- uses that value as the next guest address. Hmm. What
if we're simulating a 64-bit guest on an x86 host? */
addHRegUse(u, HRmRead, hregX86_EAX());
case Xin_Push:
addRegUsage_X86RMI(u, i->Xin.Push.src);
addHRegUse(u, HRmModify, hregX86_ESP());
return;
case Xin_Call:
addHRegUse(u, HRmRead, i->Xin.Call.target);
/* claim it trashes all the callee-saved regs */
/* except I have no idea what they are */
addHRegUse(u, HRmWrite, hregX86_EAX());
addHRegUse(u, HRmWrite, hregX86_ECX());
addHRegUse(u, HRmWrite, hregX86_EDX());
return;
case Xin_GotoNZ:
addRegUsage_X86RI(u, i->Xin.GotoNZ.dst);
addHRegUse(u, HRmWrite, hregX86_EAX());
return;
default:
ppX86Instr(i);
@@ -475,7 +516,8 @@ void mapRegs_X86Instr (HRegRemap* m, X86Instr* i)
case Xin_Sh32:
mapRegs_X86RM(m, i->Xin.Sh32.dst);
return;
case Xin_RET:
case Xin_GotoNZ:
mapRegs_X86RI(m, i->Xin.GotoNZ.dst);
return;
default:
ppX86Instr(i);

View File

@@ -160,7 +160,8 @@ extern void ppX86RM ( X86RM* );
/* --------- */
typedef
enum {
enum {
Xalu_INVALID,
Xalu_MOV,
Xalu_ADD, Xalu_SUB, Xalu_ADC, Xalu_SBB,
Xalu_AND, Xalu_OR, Xalu_XOR
@@ -173,6 +174,7 @@ extern void ppX86AluOp ( X86AluOp );
/* --------- */
typedef
enum {
Xsh_INVALID,
Xsh_SHL, Xsh_SHR, Xsh_SAR,
Xsh_ROL, Xsh_ROR
}
@@ -187,7 +189,9 @@ typedef
Xin_Alu32R, /* 32-bit mov/arith/logical, dst=REG */
Xin_Alu32M, /* 32-bit mov/arith/logical, dst=MEM */
Xin_Sh32, /* 32-bit shift/rotate, dst=REG or MEM */
Xin_RET
Xin_Push, /* push (32-bit?) value on stack */
Xin_Call, /* call to address in register */
Xin_GotoNZ /* conditional/unconditional jmp to dst */
}
X86InstrTag;
@@ -212,8 +216,18 @@ typedef
UInt src; /* shift amount, or 0 means %cl */
X86RM* dst;
} Sh32;
struct {
} RET;
struct {
X86RMI* src;
} Push;
struct {
HReg target;
} Call;
/* Pseudo-insn. Goto dst, optionally only when Z flag is
clear. */
struct {
Bool onlyWhenNZ;
X86RI* dst;
} GotoNZ;
} Xin;
}
X86Instr;
@@ -221,7 +235,9 @@ typedef
extern X86Instr* X86Instr_Alu32R ( X86AluOp, X86RMI*, HReg );
extern X86Instr* X86Instr_Alu32M ( X86AluOp, X86RI*, X86AMode* );
extern X86Instr* X86Instr_Sh32 ( X86ShiftOp, UInt, X86RM* );
extern X86Instr* X86Instr_RET ( void );
extern X86Instr* X86Instr_Push ( X86RMI* );
extern X86Instr* X86Instr_Call ( HReg );
extern X86Instr* X86Instr_GotoNZ ( Bool onlyWhenNZ, X86RI* dst );
extern void ppX86Instr ( X86Instr* );

View File

@@ -139,13 +139,14 @@ TranslateResult LibVEX_Translate (
return TransAccessFail;
}
sanityCheckIRBB(irbb, Ity_I32);
return TransOK;
/* Get the thing instrumented. */
if (instrument)
irbb = (*instrument)(irbb);
/* Turn it into virtual-registerised code. */
vcode = iselBB ( irbb );
return TransOK;
vex_printf("\n-------- Virtual registerised code --------\n");
for (i = 0; i < vcode->arr_used; i++) {

View File

@@ -30,7 +30,8 @@ extern void ppIRType ( IRType );
/* ------------------ Constants ------------------ */
typedef
enum { Ico_U8, Ico_U16, Ico_U32, Ico_U64 }
enum { Ico_U8=0x12000,
Ico_U16, Ico_U32, Ico_U64 }
IRConstTag;
typedef
@@ -68,7 +69,7 @@ typedef
enum {
/* Do not change this ordering. The IR generators
rely on (eg) Iop_Add64 == IopAdd8 + 3. */
Iop_Add8=0x12000,
Iop_Add8=0x13000,
Iop_Add16, Iop_Add32, Iop_Add64,
Iop_Sub8, Iop_Sub16, Iop_Sub32, Iop_Sub64,
Iop_Adc8, Iop_Adc16, Iop_Adc32, Iop_Adc64,
@@ -256,7 +257,7 @@ extern void ppIRStmt ( IRStmt* );
typedef
enum {
Ijk_Boring=0x13000, /* not interesting; just goto next */
Ijk_Boring=0x14000, /* not interesting; just goto next */
Ijk_Call, /* guest is doing a call */
Ijk_Ret, /* guest is doing a return */
Ijk_ClientReq, /* do guest client req before continuing */

View File

@@ -63,7 +63,9 @@ int main ( int argc, char** argv )
}
LibVEX_Init ( &failure_exit, &log_bytes,
1, 1, True, 100 );
1, 1, //False,
True,
100 );
while (!feof(f)) {
fgets(linebuf, N_LINEBUF,f);