Correctly implement x86 bt/btc/bts/btr insn.  Previous impl was wrong:

* Didn't handle correctly operands in memory, where arbitrary signed
  bit offsets are allowed.  Prior impl will trash the client's stack
  and give the wrong answer.

* Was done by a helper function and therefore could give spurious
  value errors.

Now the address computations are done in-line.

Old implementation is there, but unused and scheduled for demolition.

Test case to follow.


git-svn-id: svn://svn.valgrind.org/valgrind/trunk@73 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/vg_to_ucode.c b/vg_to_ucode.c
index c45ad81..382de0c 100644
--- a/vg_to_ucode.c
+++ b/vg_to_ucode.c
@@ -2537,6 +2537,7 @@
    }
 }
 
+#if 0
 static
 Addr dis_bt_G_E ( UCodeBlock* cb, Int sz, Addr eip, BtOp op )
 {
@@ -2599,6 +2600,148 @@
 
    return eip;
 }
+#endif
+
+
+static
+Addr dis_bt_G_E ( UCodeBlock* cb, Int sz, Addr eip, BtOp op )
+{
+   UInt  pair;
+   UChar dis_buf[50];
+   UChar modrm;
+
+   Int t_addr, t_bitno, t_mask, t_fetched, t_esp, temp, lit;
+
+   /* 2 and 4 are actually possible. */
+   vg_assert(sz == 2 || sz == 4);
+   /* We only handle 4. */
+   vg_assert(sz == 4);
+
+   t_addr = t_bitno = t_mask 
+          = t_fetched = t_esp = temp = INVALID_TEMPREG;
+
+   t_fetched = newTemp(cb);
+   t_bitno   = newTemp(cb);
+   temp      = newTemp(cb);
+   lit       = newTemp(cb);
+
+   modrm = getUChar(eip);
+
+   uInstr2(cb, GET,  sz, ArchReg, gregOfRM(modrm), TempReg, t_bitno);
+
+   if (epartIsReg(modrm)) {
+      eip++;
+      /* Get it onto the client's stack. */
+      t_esp = newTemp(cb);
+      t_addr = newTemp(cb);
+      uInstr2(cb, GET,   4, ArchReg,  R_ESP, TempReg, t_esp);
+      uInstr2(cb, SUB,  sz, Literal,  0,     TempReg, t_esp);
+      uLiteral(cb, sz);
+      uInstr2(cb, PUT,   4, TempReg,  t_esp, ArchReg, R_ESP);
+      uInstr2(cb, GET,   sz, ArchReg, eregOfRM(modrm), TempReg, temp);
+      uInstr2(cb, STORE, sz, TempReg, temp, TempReg, t_esp);
+      /* Make ta point at it. */
+      uInstr2(cb, MOV,   4,  TempReg, t_esp, TempReg, t_addr);
+      /* Mask out upper bits of the shift amount, since we're doing a
+         reg. */
+      uInstr2(cb, MOV, 4, Literal, 0, TempReg, lit);
+      uLiteral(cb, sz == 4 ? 31 : 15);
+      uInstr2(cb, AND, 4, TempReg, lit, TempReg, t_bitno);
+   } else {
+      pair   = disAMode ( cb, eip, dis?dis_buf:NULL );
+      t_addr = LOW24(pair);
+      eip   += HI8(pair);
+   }
+  
+   /* At this point: ta points to the address being operated on.  If
+      it was a reg, we will have pushed it onto the client's stack.
+      t_bitno is the bit number, suitable masked in the case of a reg.  */
+   
+   /* Now the main sequence. */
+
+   uInstr2(cb, MOV, 4, TempReg, t_bitno, TempReg, temp);
+   uInstr2(cb, SAR, 4, Literal, 0, TempReg, temp);
+   uLiteral(cb, 3);
+   uInstr2(cb, ADD, 4, TempReg, temp, TempReg, t_addr);
+   /* ta now holds effective address */
+
+   uInstr2(cb, MOV, 4, Literal, 0, TempReg, lit);
+   uLiteral(cb, 7);
+   uInstr2(cb, AND, 4, TempReg, lit, TempReg, t_bitno);
+   /* bitno contains offset of bit within byte */
+
+   if (op != BtOpNone) {
+      t_mask = newTemp(cb);
+      uInstr2(cb, MOV, 4, Literal, 0, TempReg, t_mask);
+      uLiteral(cb, 1);
+      uInstr2(cb, SHL, 4, TempReg, t_bitno, TempReg, t_mask);
+   }
+   /* mask is now a suitable byte mask */
+
+   uInstr2(cb, LOAD, 1, TempReg, t_addr, TempReg, t_fetched);
+   if (op != BtOpNone) {
+      uInstr2(cb, MOV, 4, TempReg, t_fetched, TempReg, temp);
+      switch (op) {
+         case BtOpSet: 
+            uInstr2(cb, OR, 4, TempReg, t_mask, TempReg, temp); 
+            break;
+         case BtOpComp: 
+            uInstr2(cb, XOR, 4, TempReg, t_mask, TempReg, temp); 
+            break;
+         case BtOpReset: 
+            uInstr1(cb, NOT, 4, TempReg, t_mask);
+            uInstr2(cb, AND, 4, TempReg, t_mask, TempReg, temp); 
+            break;
+         default: 
+            VG_(panic)("dis_bt_G_E");
+      }
+      uInstr2(cb, STORE, 1, TempReg, temp, TempReg, t_addr);
+   }
+
+   /* Side effect done; now get selected bit into Carry flag */
+
+   uInstr2(cb, SHR, 4, TempReg, t_bitno, TempReg, t_fetched);
+   /* at bit 0 of fetched */
+
+   uInstr2(cb, MOV, 4, Literal, 0, TempReg, lit);
+   uLiteral(cb, 1);
+   uInstr2(cb, AND, 4, TempReg, lit, TempReg, t_fetched);
+   /* fetched is now 1 or 0 */
+
+   /* NEG is a handy way to convert zero/nonzero into the carry
+      flag. */
+   uInstr1(cb, NEG, 4, TempReg, t_fetched);
+   setFlagsFromUOpcode(cb, NEG);
+   /* fetched is now in carry flag */
+
+   /* Move reg operand from stack back to reg */
+   if (epartIsReg(modrm)) {
+      /* t_esp still points at it. */
+      uInstr2(cb, LOAD, sz, TempReg, t_esp, TempReg, temp);
+      uInstr2(cb, PUT,  sz, TempReg, temp, ArchReg, eregOfRM(modrm));
+      uInstr2(cb, ADD,  sz, Literal, 0, TempReg, t_esp);
+      uLiteral(cb, sz);
+      uInstr2(cb, PUT,  4,  TempReg, t_esp, ArchReg, R_ESP);
+   }
+
+   if (epartIsReg(modrm)) {
+      if (dis)
+         VG_(printf)("bt%s%c %s, %s\n",
+                     nameBtOp(op),
+                     nameISize(sz), nameIReg(sz, gregOfRM(modrm)), 
+                     nameIReg(sz, eregOfRM(modrm)));
+   } else {
+      if (dis)
+         VG_(printf)("bt%s%c %s, %s\n",
+                     nameBtOp(op),
+                     nameISize(sz), nameIReg(sz, gregOfRM(modrm)), 
+                     dis_buf);
+   }
+ 
+   return eip;
+}
+
+
 
 
 /* Handle BSF/BSR.  Only v-size seems necessary. */