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/coregrind/vg_to_ucode.c b/coregrind/vg_to_ucode.c
index c45ad81..382de0c 100644
--- a/coregrind/vg_to_ucode.c
+++ b/coregrind/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. */
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. */