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;
 }