From d64a131f0fc9cab12fda9f2528afb0013fea0fbb Mon Sep 17 00:00:00 2001 From: Julian Seward Date: Fri, 16 Jul 2004 21:03:45 +0000 Subject: [PATCH] Mucho x86 instruction selector hacking. git-svn-id: svn://svn.valgrind.org/vex/trunk@90 --- VEX/priv/host-x86/isel_x86.c | 222 ++++++++++++++++++++++++---------- VEX/priv/host-x86/x86h_defs.c | 64 ++++++++-- VEX/priv/host-x86/x86h_defs.h | 26 +++- VEX/priv/main/vex_main.c | 3 +- VEX/pub/libvex_ir.h | 7 +- VEX/test_main.c | 4 +- 6 files changed, 243 insertions(+), 83 deletions(-) diff --git a/VEX/priv/host-x86/isel_x86.c b/VEX/priv/host-x86/isel_x86.c index faf37b0c5..776d1d456 100644 --- a/VEX/priv/host-x86/isel_x86.c +++ b/VEX/priv/host-x86/isel_x86.c @@ -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; diff --git a/VEX/priv/host-x86/x86h_defs.c b/VEX/priv/host-x86/x86h_defs.c index 4c5d8ba14..0a57fea81 100644 --- a/VEX/priv/host-x86/x86h_defs.c +++ b/VEX/priv/host-x86/x86h_defs.c @@ -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); diff --git a/VEX/priv/host-x86/x86h_defs.h b/VEX/priv/host-x86/x86h_defs.h index cf847ebf4..8f922fb67 100644 --- a/VEX/priv/host-x86/x86h_defs.h +++ b/VEX/priv/host-x86/x86h_defs.h @@ -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* ); diff --git a/VEX/priv/main/vex_main.c b/VEX/priv/main/vex_main.c index fd8aec4ac..64189dcd9 100644 --- a/VEX/priv/main/vex_main.c +++ b/VEX/priv/main/vex_main.c @@ -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++) { diff --git a/VEX/pub/libvex_ir.h b/VEX/pub/libvex_ir.h index b908c1700..7a42ed2b8 100644 --- a/VEX/pub/libvex_ir.h +++ b/VEX/pub/libvex_ir.h @@ -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 */ diff --git a/VEX/test_main.c b/VEX/test_main.c index c3d0daa31..15d5dd969 100644 --- a/VEX/test_main.c +++ b/VEX/test_main.c @@ -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);