Add translation chaining support for amd64, x86 and ARM (VEX side).
See #296422.
git-svn-id: svn://svn.valgrind.org/vex/branches/TCHAIN@2273 8f6e269a-dfd6-0310-a8e1-e2731360e62c
diff --git a/priv/guest_generic_bb_to_IR.c b/priv/guest_generic_bb_to_IR.c
index 32dca8c..6066fe0 100644
--- a/priv/guest_generic_bb_to_IR.c
+++ b/priv/guest_generic_bb_to_IR.c
@@ -140,6 +140,43 @@
(In fact it's a VgInstrumentClosure.)
*/
+/* Regarding IP updating. dis_instr_fn (that does the guest specific
+ work of disassembling an individual instruction) must finish the
+ resulting IR with "PUT(guest_IP) = ". Hence in all cases it must
+ state the next instruction address.
+
+ If the block is to be ended at that point, then this routine
+ (bb_to_IR) will set up the next/jumpkind/offsIP fields so as to
+ make a transfer (of the right kind) to "GET(guest_IP)". Hence if
+ dis_instr_fn generates incorrect IP updates we will see it
+ immediately (due to jumping to the wrong next guest address).
+
+ However it is also necessary to set this up so it can be optimised
+ nicely. The IRSB exit is defined to update the guest IP, so that
+ chaining works -- since the chain_me stubs expect the chain-to
+ address to be in the guest state. Hence what the IRSB next fields
+ will contain initially is (implicitly)
+
+ PUT(guest_IP) [implicitly] = GET(guest_IP) [explicit expr on ::next]
+
+ which looks pretty strange at first. Eg so unconditional branch
+ to some address 0x123456 looks like this:
+
+ PUT(guest_IP) = 0x123456; // dis_instr_fn generates this
+ // the exit
+ PUT(guest_IP) [implicitly] = GET(guest_IP); exit-Boring
+
+ after redundant-GET and -PUT removal by iropt, we get what we want:
+
+ // the exit
+ PUT(guest_IP) [implicitly] = 0x123456; exit-Boring
+
+ This makes the IRSB-end case the same as the side-exit case: update
+ IP, then transfer. There is no redundancy of representation for
+ the destination, and we use the destination specified by
+ dis_instr_fn, so any errors it makes show up sooner.
+*/
+
IRSB* bb_to_IR (
/*OUT*/VexGuestExtents* vge,
/*OUT*/UInt* n_sc_extents,
@@ -155,13 +192,15 @@
/*IN*/ IRType guest_word_type,
/*IN*/ UInt (*needs_self_check)(void*,VexGuestExtents*),
/*IN*/ Bool (*preamble_function)(void*,IRSB*),
- /*IN*/ Int offB_TISTART,
- /*IN*/ Int offB_TILEN
+ /*IN*/ Int offB_GUEST_TISTART,
+ /*IN*/ Int offB_GUEST_TILEN,
+ /*IN*/ Int offB_GUEST_IP,
+ /*IN*/ Int szB_GUEST_IP
)
{
Long delta;
Int i, n_instrs, first_stmt_idx;
- Bool resteerOK, need_to_put_IP, debug_print;
+ Bool resteerOK, debug_print;
DisResult dres;
IRStmt* imark;
IRStmt* nop;
@@ -185,6 +224,14 @@
vassert(vex_control.guest_chase_thresh < vex_control.guest_max_insns);
vassert(guest_word_type == Ity_I32 || guest_word_type == Ity_I64);
+ if (guest_word_type == Ity_I32) {
+ vassert(szB_GUEST_IP == 4);
+ vassert((offB_GUEST_IP % 4) == 0);
+ } else {
+ vassert(szB_GUEST_IP == 8);
+ vassert((offB_GUEST_IP % 8) == 0);
+ }
+
/* Start a new, empty extent. */
vge->n_used = 1;
vge->base[0] = guest_IP_bbstart;
@@ -297,13 +344,12 @@
);
}
- /* for the first insn, the dispatch loop will have set
- %IP, but for all the others we have to do it ourselves. */
- need_to_put_IP = toBool(n_instrs > 0);
+ if (debug_print && n_instrs > 0)
+ vex_printf("\n");
/* Finally, actually disassemble an instruction. */
+ vassert(irsb->next == NULL);
dres = dis_instr_fn ( irsb,
- need_to_put_IP,
resteerOKfn,
toBool(n_cond_resteers_allowed > 0),
callback_opaque,
@@ -347,18 +393,22 @@
}
}
- /* If dis_instr_fn terminated the BB at this point, check it
- also filled in the irsb->next field. */
- if (dres.whatNext == Dis_StopHere) {
- vassert(irsb->next != NULL);
- if (debug_print) {
- vex_printf(" ");
- vex_printf( "goto {");
- ppIRJumpKind(irsb->jumpkind);
- vex_printf( "} ");
- ppIRExpr( irsb->next );
- vex_printf( "\n");
- }
+ /* Individual insn disassembly may not mess with irsb->next.
+ This function is the only place where it can be set. */
+ vassert(irsb->next == NULL);
+ vassert(irsb->jumpkind == Ijk_Boring);
+ vassert(irsb->offsIP == 0);
+
+ /* Individual insn disassembly must finish the IR for each
+ instruction with an assignment to the guest PC. */
+ vassert(first_stmt_idx < irsb->stmts_used);
+ /* it follows that irsb->stmts_used must be > 0 */
+ { IRStmt* st = irsb->stmts[irsb->stmts_used-1];
+ vassert(st);
+ vassert(st->tag == Ist_Put);
+ vassert(st->Ist.Put.offset == offB_GUEST_IP);
+ /* Really we should also check that the type of the Put'd data
+ == guest_word_type, but that's a bit expensive. */
}
/* Update the VexGuestExtents we are constructing. */
@@ -370,36 +420,38 @@
vge->len[vge->n_used-1]
= toUShort(toUInt( vge->len[vge->n_used-1] + dres.len ));
n_instrs++;
- if (debug_print)
- vex_printf("\n");
/* Advance delta (inconspicuous but very important :-) */
delta += (Long)dres.len;
switch (dres.whatNext) {
case Dis_Continue:
- vassert(irsb->next == NULL);
+ vassert(dres.continueAt == 0);
+ vassert(dres.jk_StopHere == Ijk_INVALID);
if (n_instrs < vex_control.guest_max_insns) {
/* keep going */
} else {
- /* We have to stop. */
- irsb->next
- = IRExpr_Const(
- guest_word_type == Ity_I32
- ? IRConst_U32(toUInt(guest_IP_bbstart+delta))
- : IRConst_U64(guest_IP_bbstart+delta)
- );
+ /* We have to stop. See comment above re irsb field
+ settings here. */
+ irsb->next = IRExpr_Get(offB_GUEST_IP, guest_word_type);
+ /* irsb->jumpkind must already by Ijk_Boring */
+ irsb->offsIP = offB_GUEST_IP;
goto done;
}
break;
case Dis_StopHere:
- vassert(irsb->next != NULL);
+ vassert(dres.continueAt == 0);
+ vassert(dres.jk_StopHere != Ijk_INVALID);
+ /* See comment above re irsb field settings here. */
+ irsb->next = IRExpr_Get(offB_GUEST_IP, guest_word_type);
+ irsb->jumpkind = dres.jk_StopHere;
+ irsb->offsIP = offB_GUEST_IP;
goto done;
+
case Dis_ResteerU:
case Dis_ResteerC:
/* Check that we actually allowed a resteer .. */
vassert(resteerOK);
- vassert(irsb->next == NULL);
if (dres.whatNext == Dis_ResteerC) {
vassert(n_cond_resteers_allowed > 0);
n_cond_resteers_allowed--;
@@ -628,10 +680,10 @@
= IRStmt_WrTmp(tilen_tmp, IRExpr_Const(len2check_IRConst) );
irsb->stmts[selfcheck_idx + i * 5 + 2]
- = IRStmt_Put( offB_TISTART, IRExpr_RdTmp(tistart_tmp) );
+ = IRStmt_Put( offB_GUEST_TISTART, IRExpr_RdTmp(tistart_tmp) );
irsb->stmts[selfcheck_idx + i * 5 + 3]
- = IRStmt_Put( offB_TILEN, IRExpr_RdTmp(tilen_tmp) );
+ = IRStmt_Put( offB_GUEST_TILEN, IRExpr_RdTmp(tilen_tmp) );
/* Generate the entry point descriptors */
if (abiinfo_both->host_ppc_calls_use_fndescrs) {
@@ -685,11 +737,25 @@
/* Where we must restart if there's a failure: at the
first extent, regardless of which extent the
failure actually happened in. */
- guest_IP_bbstart_IRConst
+ guest_IP_bbstart_IRConst,
+ offB_GUEST_IP
);
} /* for (i = 0; i < vge->n_used; i++) */
}
+ /* irsb->next must now be set, since we've finished the block.
+ Print it if necessary.*/
+ vassert(irsb->next != NULL);
+ if (debug_print) {
+ vex_printf(" ");
+ vex_printf( "PUT(%d) = ", irsb->offsIP);
+ ppIRExpr( irsb->next );
+ vex_printf( "; exit-");
+ ppIRJumpKind(irsb->jumpkind);
+ vex_printf( "\n");
+ vex_printf( "\n");
+ }
+
return irsb;
}