Relent, and properly implement bt/bts/btc/btr for literal bit-offsets.
bt_literal.c is a somewhat halfhearted test case for it.


git-svn-id: svn://svn.valgrind.org/valgrind/trunk@76 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/vg_to_ucode.c b/vg_to_ucode.c
index 79519f4..1998c8e 100644
--- a/vg_to_ucode.c
+++ b/vg_to_ucode.c
@@ -1219,44 +1219,89 @@
 }
 
 
-#if 0
-/* Group 8 extended opcodes. */
+
+/* Group 8 extended opcodes (but BT/BTS/BTC/BTR only). */
 static
-Addr dis_Grp8 ( UCodeBlock* cb, Addr eip, UChar modrm,
-                Int am_sz, Int sz, UInt src_val )
+Addr dis_Grp8_BT ( UCodeBlock* cb, Addr eip, UChar modrm,
+                   Int am_sz, Int sz, UInt src_val )
 {
+#  define MODIFY_t2_AND_SET_CARRY_FLAG					\
+      /* t2 is the value to be op'd on.  Copy to t_fetched, then	\
+         modify t2, if non-BT. */					\
+      uInstr2(cb, MOV,   4,  TempReg, t2, TempReg, t_fetched);		\
+      uInstr2(cb, MOV,  sz,  Literal, 0,  TempReg, t_mask);		\
+      uLiteral(cb, v_mask);						\
+      switch (gregOfRM(modrm)) {					\
+         case 4: /* BT */  break;					\
+         case 5: /* BTS */ 						\
+            uInstr2(cb, OR, sz, TempReg, t_mask, TempReg, t2); break;	\
+         case 6: /* BTR */						\
+            uInstr2(cb, AND, sz, TempReg, t_mask, TempReg, t2); break;	\
+         case 7: /* BTC */ 						\
+            uInstr2(cb, XOR, sz, TempReg, t_mask, TempReg, t2); break;	\
+      }									\
+      /* Copy relevant bit from t_fetched into carry flag. */		\
+      uInstr2(cb, SHR, sz, Literal, 0, TempReg, t_fetched);		\
+      uLiteral(cb, src_val);						\
+      uInstr2(cb, MOV, sz, Literal, 0, TempReg, t_mask);		\
+      uLiteral(cb, 1);							\
+      uInstr2(cb, AND, sz, TempReg, t_mask, TempReg, t_fetched);	\
+      uInstr1(cb, NEG, sz, TempReg, t_fetched);				\
+      setFlagsFromUOpcode(cb, NEG);
+
+
    /* src_val denotes a d8.
       And eip on entry points at the modrm byte. */
-   Int   t1, t2, helper;
+   Int   t1, t2, t_fetched, t_mask;
    UInt  pair;
    UChar dis_buf[50];
+   UInt  v_mask;
 
-   switch (gregOfRM(modrm)) {
-      case 4: helper = VGOFF_(helper_bt);  break;
-      case 5: helper = VGOFF_(helper_bts); break;
-      case 6: helper = VGOFF_(helper_btr); break;
-      case 7: helper = VGOFF_(helper_btc); break;
-      /* If this needs to be extended, be careful to do the flag
-         setting in the parts below correctly. */
-      default: VG_(panic)("dis_Grp8");
+   /* There is no 1-byte form of this instruction, AFAICS. */
+   vg_assert(sz == 2 || sz == 4);
+
+   /* Limit src_val -- the bit offset -- to something within a word.
+      The Intel docs say that literal offsets larger than a word are
+      masked in this way. */
+   switch (sz) {
+      case 2: src_val &= 15; break;
+      case 4: src_val &= 31; break;
+      default: VG_(panic)("dis_Grp8_BT: invalid size");
    }
 
-   t1 = newTemp(cb);
-   uInstr2(cb, MOV,  4, Literal, 0, TempReg, t1);
-   uLiteral(cb, src_val);
-   uInstr0(cb, CALLM_S, 0);
-   uInstr1(cb, PUSH, 4, TempReg, t1);
-   
+   /* Invent a mask suitable for the operation. */
+
+   switch (gregOfRM(modrm)) {
+      case 4: /* BT */  v_mask = 0; break;
+      case 5: /* BTS */ v_mask = 1 << src_val; break;
+      case 6: /* BTR */ v_mask = ~(1 << src_val); break;
+      case 7: /* BTC */ v_mask = 1 << src_val; break;
+         /* If this needs to be extended, probably simplest to make a
+            new function to handle the other cases (0 .. 3).  The
+            Intel docs do however not indicate any use for 0 .. 3, so
+            we don't expect this to happen. */
+      default: VG_(panic)("dis_Grp8_BT");
+   }
+   /* Probably excessively paranoid. */
+   if (sz == 2)
+      v_mask &= 0x0000FFFF;
+
+   t1        = INVALID_TEMPREG;
+   t_fetched = newTemp(cb);
+   t_mask    = newTemp(cb);
+
    if (epartIsReg(modrm)) {
       vg_assert(am_sz == 1);
       t2 = newTemp(cb);
-      uInstr2(cb, GET,   sz, ArchReg, eregOfRM(modrm), TempReg, t2);
-      uInstr1(cb, PUSH,  sz, TempReg, t2);
-      uInstr1(cb, CALLM, 0,  Lit16,   helper);
-      uFlagsRWU(cb, FlagsEmpty, FlagC, FlagsOSZAP);
-      uInstr1(cb, POP,   sz, TempReg, t2);
-      uInstr2(cb, PUT,   sz, TempReg, t2, ArchReg, eregOfRM(modrm));
-      uInstr1(cb, CLEAR, 0,  Lit16,   4);
+
+      /* Fetch the value to be tested and modified. */
+      uInstr2(cb, GET, sz, ArchReg, eregOfRM(modrm), TempReg, t2);
+      /* Do it! */
+      MODIFY_t2_AND_SET_CARRY_FLAG;
+      /* Dump the result back, if non-BT. */
+      if (gregOfRM(modrm) != 4 /* BT */)
+         uInstr2(cb, PUT, sz, TempReg, t2, ArchReg, eregOfRM(modrm));
+
       eip += (am_sz + 1);
       if (dis)
          VG_(printf)("%s%c $0x%x, %s\n",
@@ -1269,23 +1314,25 @@
       t2   = newTemp(cb);
       eip  += HI8(pair);
       eip  += 1;
+
+      /* Fetch the value to be tested and modified. */
       uInstr2(cb, LOAD,  sz, TempReg, t1, TempReg, t2);
-      uInstr1(cb, PUSH,  sz, TempReg, t2);
-      uInstr1(cb, CALLM, 0,  Lit16,   helper);
-      uFlagsRWU(cb, FlagsEmpty, FlagC, FlagsOSZAP);
-      uInstr1(cb, POP,   sz, TempReg, t2);
-      uInstr2(cb, STORE, sz, TempReg, t2, TempReg, t1);
-      SMC_IF_ALL(cb);
-      uInstr1(cb, CLEAR, 0, Lit16,    4);
+      /* Do it! */
+      MODIFY_t2_AND_SET_CARRY_FLAG;
+      /* Dump the result back, if non-BT. */
+      if (gregOfRM(modrm) != 4 /* BT */) {
+         uInstr2(cb, STORE, sz, TempReg, t2, TempReg, t1);
+         SMC_IF_ALL(cb);
+      }
       if (dis)
             VG_(printf)("%s%c $0x%x, %s\n",
                         nameGrp8(gregOfRM(modrm)), nameISize(sz), src_val, 
                         dis_buf);
    }
-	uInstr0(cb, CALLM_E, 0);
    return eip;
+
+#  undef MODIFY_t2_AND_SET_CARRY_FLAG
 }
-#endif
 
 
 
@@ -4044,14 +4091,13 @@
 
       /* =-=-=-=-=-=-=-=-=- Grp8 =-=-=-=-=-=-=-=-=-=-=-= */
 
-#if 0
       case 0xBA: /* Grp8 Ib,Ev */
          modrm = getUChar(eip);
          am_sz = lengthAMode(eip);
          d32   = getSDisp8(eip + am_sz);
-         eip = dis_Grp8 ( cb, eip, modrm, am_sz, sz, d32 );
+         eip = dis_Grp8_BT ( cb, eip, modrm, am_sz, sz, d32 );
          break;
-#endif
+
       /* =-=-=-=-=-=-=-=-=- BSF/BSR -=-=-=-=-=-=-=-=-=-= */
 
       case 0xBC: /* BSF Gv,Ev */