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);
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
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*/
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);
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);
/* 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 )
{
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;
}
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:
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
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,
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),
}
-/* 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;
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 );
}
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:
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;
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));
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)
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;
}
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)
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++) {
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 )