]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Mucho x86 instruction selector hacking.
authorJulian Seward <jseward@acm.org>
Fri, 16 Jul 2004 21:03:45 +0000 (21:03 +0000)
committerJulian Seward <jseward@acm.org>
Fri, 16 Jul 2004 21:03:45 +0000 (21:03 +0000)
git-svn-id: svn://svn.valgrind.org/vex/trunk@90

VEX/priv/host-x86/isel_x86.c
VEX/priv/host-x86/x86h_defs.c
VEX/priv/host-x86/x86h_defs.h
VEX/priv/main/vex_main.c
VEX/pub/libvex_ir.h
VEX/test_main.c

index faf37b0c56aec96fd31938b702f544c1a9393b70..776d1d4565b7fd7577a7daf30c178010f96f3e5a 100644 (file)
      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_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:
-      /* Add32(x,y).  For commutative ops we assume any literal
+   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;
+         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;
+   }
 
    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, 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();
+   }
 
-#if 0
-   /* 32-bit literals */
+   /* 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: {
-            HReg r = newVRegI(env);
-            addInstr(env,
-               X86Instr_Mov32(X86Operand_Imm(e->Iex.Const.con->Ico.U32),
-                             X86Operand_Reg(r)));
-            return r;
-         }
-         default: break;
+         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;
 
index 4c5d8ba14e6e5df09ee51600ff3fe5a881116e68..0a57fea818343b17b2b98ff7bdd0fddcec5df5ba 100644 (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);
index cf847ebf4b5d000f94ebfd8cc2a14af1a9d80342..8f922fb673716ec01bcfb8abb3cd68064a4f75bc 100644 (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* );
 
index fd8aec4ac300b0b022fefb11bd73b85ea6606289..64189dcd996a5b210cda0b5feb02ea0a3101bd9c 100644 (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++) {
index b908c1700ff95559c2a04b08257dee9fec9bf97a..7a42ed2b839bb2d7c65287a3b9d0f177a6c6382d 100644 (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 */
index c3d0daa318da71f792ba3b0f3b2cab53d78f95d8..15d5dd969bcb88773de100fd6443168d4aee5f7e 100644 (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);