]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Make the x86 back end capable of generating conditional calls.
authorJulian Seward <jseward@acm.org>
Wed, 3 Nov 2004 15:24:42 +0000 (15:24 +0000)
committerJulian Seward <jseward@acm.org>
Wed, 3 Nov 2004 15:24:42 +0000 (15:24 +0000)
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

VEX/priv/host-x86/hdefs.c
VEX/priv/host-x86/hdefs.h
VEX/priv/host-x86/isel.c
VEX/priv/ir/irdefs.c

index cb83fcc2315eca30cff055a96f81a13107bfc2f4..b74997641b7e46b09a8bd51b9dc81893fd786334 100644 (file)
@@ -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:
index e534416448c498b61fecde2515a61f603f00a9ca..e0ae524118e34c09ab1eb22a384127674d3921ea 100644 (file)
@@ -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,
index 5561dbaed625b27ed3dac988a51d57fdb0653879..1696564f8f1f7729b1b0106062ee10070bd1996a 100644 (file)
@@ -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;
    }
 
index e76f77ed00ad599e79d86c2922d645e545ba1d19..eec3759b3f079776dcf498f0645291ac99723d7d 100644 (file)
@@ -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 )