From: Julian Seward Date: Wed, 3 Nov 2004 15:24:42 +0000 (+0000) Subject: Make the x86 back end capable of generating conditional calls. X-Git-Tag: svn/VALGRIND_3_0_1^2~852 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e8e3446fe67866db39c3bd574a1ab5554beffac7;p=thirdparty%2Fvalgrind.git Make the x86 back end capable of generating conditional calls. NOTE, the argument marshalling for regparm'd calls is observed to be wrong. Arguments are computed directly into the real registers, thus trashing themselves if a regparm-call should be contained in an argument to another regparm-call. git-svn-id: svn://svn.valgrind.org/vex/trunk@482 --- diff --git a/VEX/priv/host-x86/hdefs.c b/VEX/priv/host-x86/hdefs.c index cb83fcc231..b74997641b 100644 --- a/VEX/priv/host-x86/hdefs.c +++ b/VEX/priv/host-x86/hdefs.c @@ -504,9 +504,10 @@ X86Instr* X86Instr_Push( X86RMI* src ) { i->Xin.Push.src = src; return i; } -X86Instr* X86Instr_Call ( HReg target, Int regparms ) { +X86Instr* X86Instr_Call ( X86CondCode cond, Addr32 target, Int regparms ) { X86Instr* i = LibVEX_Alloc(sizeof(X86Instr)); i->tag = Xin_Call; + i->Xin.Call.cond = cond; i->Xin.Call.target = target; i->Xin.Call.regparms = regparms; vassert(regparms >= 0 && regparms <= 3); @@ -697,8 +698,11 @@ void ppX86Instr ( X86Instr* i ) { ppX86RMI(i->Xin.Push.src); return; case Xin_Call: - vex_printf("call[%d] *", i->Xin.Call.regparms); - ppHRegX86(i->Xin.Call.target); + vex_printf("call%s[%d] ", + i->Xin.Call.cond==Xcc_ALWAYS + ? "" : showX86CondCode(i->Xin.Call.cond), + i->Xin.Call.regparms); + vex_printf("0x%x", i->Xin.Call.target); break; case Xin_Goto: if (i->Xin.Goto.jk == Ijk_ClientReq @@ -871,12 +875,14 @@ void getRegUsage_X86Instr (HRegUsage* u, X86Instr* i) addHRegUse(u, HRmModify, hregX86_ESP()); return; case Xin_Call: - addHRegUse(u, HRmRead, i->Xin.Call.target); - /* claim it trashes all the callee-saved regs */ + /* This is a bit subtle. */ + /* First off, claim it trashes all the callee-saved regs */ /* which I believe to be %eax,%ecx,%edx. */ addHRegUse(u, HRmWrite, hregX86_EAX()); addHRegUse(u, HRmWrite, hregX86_ECX()); addHRegUse(u, HRmWrite, hregX86_EDX()); + /* Now we have to state any parameter-carrying registers + which might be read. This depends on the regparmness. */ switch (i->Xin.Call.regparms) { case 3: addHRegUse(u, HRmRead, hregX86_ECX()); /*fallthru*/ case 2: addHRegUse(u, HRmRead, hregX86_EDX()); /*fallthru*/ @@ -884,6 +890,20 @@ void getRegUsage_X86Instr (HRegUsage* u, X86Instr* i) case 0: break; default: vpanic("getRegUsage_X86Instr:Call:regparms"); } + /* Finally, there is the issue that the insn trashes a + register because the literal target address has to be + loaded into a register. Fortunately, for the 0/1/2 + regparm case, we can use EAX, ECX and EDX respectively, so + this does not cause any further damage. For the 3-regparm + case, we'll have to choose another register arbitrarily -- + since A,C and D are used for parameters -- and so we might + as well choose EDI. */ + if (i->Xin.Call.regparms == 3) + addHRegUse(u, HRmWrite, hregX86_EDI()); + /* Upshot of this is that the assembler really must observe + the here-stated convention of which register to use as an + address temporary, depending on the regparmness: 0==EAX, + 1==ECX, 2==EDX, 3==EDI. */ return; case Xin_Goto: addRegUsage_X86RI(u, i->Xin.Goto.dst); @@ -993,7 +1013,6 @@ void mapRegs_X86Instr (HRegRemap* m, X86Instr* i) mapRegs_X86RMI(m, i->Xin.Push.src); return; case Xin_Call: - mapReg(m, &i->Xin.Call.target); return; case Xin_Goto: mapRegs_X86RI(m, i->Xin.Goto.dst); @@ -1083,7 +1102,8 @@ Bool isMove_X86Instr ( X86Instr* i, HReg* src, HReg* dst ) /* Generate x86 spill/reload instructions under the direction of the - register allocator. */ + register allocator. Note it's critical these don't write the + condition codes. */ X86Instr* genSpill_X86 ( HReg rreg, Int offsetB ) { @@ -1329,7 +1349,7 @@ static UChar* do_fop2_st ( UChar* p, X86FpOp op, Int i ) Int emit_X86Instr ( UChar* buf, Int nbuf, X86Instr* i ) { - UInt opc, opc_rr, subopc_imm, opc_imma, opc_cl, opc_imm, subopc; + UInt irno, opc, opc_rr, subopc_imm, opc_imma, opc_cl, opc_imm, subopc; UChar* p = &buf[0]; UChar* ptmp; @@ -1620,8 +1640,27 @@ Int emit_X86Instr ( UChar* buf, Int nbuf, X86Instr* i ) } case Xin_Call: + /* See detailed comment for Xin_Call in getRegUsage_X86Instr above + for explanation of this. */ + switch (i->Xin.Call.regparms) { + case 0: irno = iregNo(hregX86_EAX()); break; + case 1: irno = iregNo(hregX86_ECX()); break; + case 2: irno = iregNo(hregX86_EDX()); break; + case 3: irno = iregNo(hregX86_EDI()); break; + default: vpanic(" emit_X86Instr:call:regparms"); + } + /* jump over the following two insns if the condition does not + hold */ + if (i->Xin.Call.cond != Xcc_ALWAYS) { + *p++ = 0x70 + (0xF & (i->Xin.Call.cond ^ 1)); + *p++ = 0x07; /* 7 bytes in the next two insns */ + } + /* movl $target, %tmp */ + *p++ = 0xB8 + irno; + p = emit32(p, i->Xin.Call.target); + /* call *%tmp */ *p++ = 0xFF; - p = doAMode_R(p, fake(2), i->Xin.Call.target); + *p++ = 0xD0 + irno; goto done; case Xin_Goto: diff --git a/VEX/priv/host-x86/hdefs.h b/VEX/priv/host-x86/hdefs.h index e534416448..e0ae524118 100644 --- a/VEX/priv/host-x86/hdefs.h +++ b/VEX/priv/host-x86/hdefs.h @@ -340,9 +340,12 @@ typedef struct { X86RMI* src; } Push; + /* Pseudo-insn. Call target (an absolute address), on given + condition (which could be Xcc_ALWAYS). */ struct { - HReg target; - Int regparms; /* 0 .. 3 */ + X86CondCode cond; + Addr32 target; + Int regparms; /* 0 .. 3 */ } Call; /* Pseudo-insn. Goto dst, on given condition (which could be Xcc_ALWAYS). Note importantly that if the jump is @@ -449,7 +452,7 @@ extern X86Instr* X86Instr_MulL ( Bool syned, X86ScalarSz, X86RM* ); extern X86Instr* X86Instr_Div ( Bool syned, X86ScalarSz, X86RM* ); extern X86Instr* X86Instr_Sh3232 ( X86ShiftOp, UInt amt, HReg src, HReg dst ); extern X86Instr* X86Instr_Push ( X86RMI* ); -extern X86Instr* X86Instr_Call ( HReg, Int ); +extern X86Instr* X86Instr_Call ( X86CondCode, Addr32, Int ); extern X86Instr* X86Instr_Goto ( IRJumpKind, X86CondCode cond, X86RI* dst ); extern X86Instr* X86Instr_CMov32 ( X86CondCode, X86RM* src, HReg dst ); extern X86Instr* X86Instr_LoadEX ( UChar szSmall, Bool syned, diff --git a/VEX/priv/host-x86/isel.c b/VEX/priv/host-x86/isel.c index 5561dbaed6..1696564f8f 100644 --- a/VEX/priv/host-x86/isel.c +++ b/VEX/priv/host-x86/isel.c @@ -297,28 +297,15 @@ static Int pushArg ( ISelEnv* env, IRExpr* arg ) helper and clearing the args off the stack. */ static -void callHelperAndClearArgs ( ISelEnv* env, IRCallee* cee, Int n_arg_ws ) +void callHelperAndClearArgs ( ISelEnv* env, X86CondCode cc, + IRCallee* cee, Int n_arg_ws ) { - HReg freg; - /* Complication. Need to decide which reg to use as the fn address pointer, in a way that doesn't trash regparm-passed parameters. */ vassert(sizeof(void*) == 4); - switch (cee->regparms) { - case 0: freg = hregX86_EAX(); break; - case 1: freg = hregX86_EDX(); break; - case 2: freg = hregX86_ECX(); break; - case 3: freg = hregX86_EDI(); break; - default: vpanic("callHelperAndClearArgs(x86): > 3 regparms"); - } - - addInstr(env, X86Instr_Alu32R( - Xalu_MOV, - X86RMI_Imm((UInt)cee->addr), - freg)); - addInstr(env, X86Instr_Call(freg, cee->regparms)); + addInstr(env, X86Instr_Call( cc, (UInt)cee->addr, cee->regparms)); if (n_arg_ws > 0) addInstr(env, X86Instr_Alu32R(Xalu_ADD, X86RMI_Imm(4*n_arg_ws), @@ -326,12 +313,16 @@ void callHelperAndClearArgs ( ISelEnv* env, IRCallee* cee, Int n_arg_ws ) } -/* Do a complete function call. */ +/* Do a complete function call. guard is a Ity_Bit expression + indicating whether or not the call happens. If guard==NULL, the + call is unconditional. */ static void doHelperCall ( ISelEnv* env, - Bool passBBP, IRCallee* cee, IRExpr** args ) + Bool passBBP, + IRExpr* guard, IRCallee* cee, IRExpr** args ) { + X86CondCode cc; HReg argregs[3]; Int not_done_yet, n_args, n_arg_ws, stack_limit, i, argreg; @@ -402,8 +393,23 @@ void doHelperCall ( ISelEnv* env, vassert(not_done_yet == 0); + /* Now we can compute the condition. We can't do it earlier + because the argument computations could trash the condition + codes. Be a bit clever to handle the common case where the + guard is 1:Bit. */ + cc = Xcc_ALWAYS; + if (guard) { + if (guard->tag == Iex_Const + && guard->Iex.Const.con->tag == Ico_Bit + && guard->Iex.Const.con->Ico.Bit == True) { + /* unconditional -- do nothing */ + } else { + cc = iselCondCode( env, guard ); + } + } + /* call the helper, and get the args off the stack afterwards. */ - callHelperAndClearArgs( env, cee, n_arg_ws ); + callHelperAndClearArgs( env, cc, cee, n_arg_ws ); } @@ -519,22 +525,6 @@ static HReg iselIntExpr_R_wrk ( ISelEnv* env, IRExpr* e ) X86AluOp aluOp; X86ShiftOp shOp; -#if 0 - { DECLARE_PATTERN(p_rol32); - DEFINE_PATTERN(p_rol32, - binop(Iop_Or32, - binop(Iop_Shl32,bind(0),bind(1)), - binop(Iop_Shr32, - bind(2), - binop(Iop_Sub8,IRConst_U8(32),bind(3))))); - if (matchIRExpr(&mi,p_rol32,e) - && eqIRExpr(mi.bindee[0], mi.bindee[2]) - && eqIRExpr(mi.bindee[1], mi.bindee[3])) { - /* emit roll */ - } - } -#endif - /* Is it an addition or logical style op? */ switch (e->Iex.Binop.op) { case Iop_Add8: case Iop_Add16: case Iop_Add32: @@ -968,7 +958,7 @@ static HReg iselIntExpr_R_wrk ( ISelEnv* env, IRExpr* e ) goto irreducible; /* Marshal args, do the call, clear stack. */ - doHelperCall( env, False, e->Iex.CCall.cee, e->Iex.CCall.args ); + doHelperCall( env, False, NULL, e->Iex.CCall.cee, e->Iex.CCall.args ); addInstr(env, mk_MOVsd_RR(hregX86_EAX(), dst)); return dst; @@ -1695,7 +1685,7 @@ static void iselIntExpr64_wrk ( HReg* rHi, HReg* rLo, ISelEnv* env, IRExpr* e ) HReg tHi = newVRegI(env); /* Marshal args, do the call, clear stack. */ - doHelperCall( env, False, e->Iex.CCall.cee, e->Iex.CCall.args ); + doHelperCall( env, False, NULL, e->Iex.CCall.cee, e->Iex.CCall.args ); addInstr(env, mk_MOVsd_RR(hregX86_EDX(), tHi)); addInstr(env, mk_MOVsd_RR(hregX86_EAX(), tLo)); @@ -2202,7 +2192,7 @@ static void iselStmt ( ISelEnv* env, IRStmt* stmt ) passBBP = d->nFxState > 0 && d->needsBBP; /* Marshal args, do the call, clear stack. */ - doHelperCall( env, passBBP, d->cee, d->args ); + doHelperCall( env, passBBP, d->guard, d->cee, d->args ); /* Now figure out what to do with the returned value, if any. */ if (d->tmp == INVALID_IRTEMP) @@ -2219,6 +2209,13 @@ static void iselStmt ( ISelEnv* env, IRStmt* stmt ) addInstr(env, mk_MOVsd_RR(hregX86_EAX(),dstLo) ); return; } + if (retty == Ity_I32 || retty == Ity_I16) { + /* The returned value is in %eax. Park it in the register + associated with tmp. */ + HReg dst = lookupIRTemp(env, d->tmp); + addInstr(env, mk_MOVsd_RR(hregX86_EAX(),dst) ); + return; + } break; } diff --git a/VEX/priv/ir/irdefs.c b/VEX/priv/ir/irdefs.c index e76f77ed00..eec3759b3f 100644 --- a/VEX/priv/ir/irdefs.c +++ b/VEX/priv/ir/irdefs.c @@ -283,6 +283,10 @@ void ppIREffect ( IREffect fx ) void ppIRDirty ( IRDirty* d ) { Int i; + if (d->tmp != INVALID_IRTEMP) { + ppIRTemp(d->tmp); + vex_printf(" = "); + } vex_printf("DIRTY "); ppIRExpr(d->guard); if (d->needsBBP) @@ -300,10 +304,6 @@ void ppIRDirty ( IRDirty* d ) vex_printf("-gst(%d,%d)", d->fxState[i].offset, d->fxState[i].size); } vex_printf(" ::: "); - if (d->tmp != INVALID_IRTEMP) { - ppIRTemp(d->tmp); - vex_printf(" = "); - } ppIRCallee(d->cee); vex_printf("("); for (i = 0; d->args[i] != NULL; i++) { @@ -418,6 +418,8 @@ IRConst* IRConst_Bit ( Bool bit ) IRConst* c = LibVEX_Alloc(sizeof(IRConst)); c->tag = Ico_Bit; c->Ico.Bit = bit; + /* call me paranoid; I don't care :-) */ + vassert(bit == False || bit == True); return c; } IRConst* IRConst_U8 ( UChar u8 )