]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Translation chaining for s390. To be debugged.
authorFlorian Krohm <florian@eich-krohm.de>
Fri, 13 Apr 2012 04:04:06 +0000 (04:04 +0000)
committerFlorian Krohm <florian@eich-krohm.de>
Fri, 13 Apr 2012 04:04:06 +0000 (04:04 +0000)
git-svn-id: svn://svn.valgrind.org/vex/branches/TCHAIN@2279

VEX/priv/guest_s390_defs.h
VEX/priv/guest_s390_helpers.c
VEX/priv/guest_s390_toIR.c
VEX/priv/host_s390_defs.c
VEX/priv/host_s390_defs.h
VEX/priv/host_s390_isel.c
VEX/priv/main_main.c
VEX/pub/libvex_guest_s390x.h
VEX/pub/libvex_s390x_common.h

index 754ce3d0910a6d60685c765e6c839ea835caa529..b7e57ba4a859a99ffe24a63305757accdf840b5c 100644 (file)
@@ -43,7 +43,6 @@
 /* Convert one s390 insn to IR.  See the type DisOneInstrFn in
    bb_to_IR.h. */
 DisResult disInstr_S390 ( IRSB*        irbb,
-                          Bool         put_IP,
                           Bool         (*resteerOkFn) ( void*, Addr64 ),
                           Bool         resteerCisOk,
                           void*        callback_opaque,
index 47a0635c0c6ec366a364b6101026fbf51d65d325..167d426a5890fd581b7ec350cefe9298621305f7 100644 (file)
@@ -130,6 +130,8 @@ LibVEX_GuestS390X_initialise(VexGuestS390XState *state)
    state->guest_TILEN = 0;
    state->guest_IP_AT_SYSCALL = 0;
    state->guest_EMWARN = EmWarn_NONE;
+   state->host_EvC_COUNTER = 0;
+   state->host_EvC_FAILADDR = 0;
 
 /*------------------------------------------------------------*/
 /*--- Initialise thunk                                     ---*/
index ca23acb0423ac95369836a8dc8d8941a02cc69e3..5befc74fad06bbcebff11b92ff2a5633f6aeb0db 100644 (file)
@@ -120,6 +120,13 @@ mkexpr(IRTemp tmp)
    return IRExpr_RdTmp(tmp);
 }
 
+/* Generate an expression node for an address. */
+static __inline__ IRExpr *
+mkaddr_expr(Addr64 addr)
+{
+   return IRExpr_Const(IRConst_U64(addr));
+}
+
 /* Add a statement that assigns to a temporary */
 static __inline__ void
 assign(IRTemp dst, IRExpr *expr)
@@ -127,6 +134,22 @@ assign(IRTemp dst, IRExpr *expr)
    stmt(IRStmt_WrTmp(dst, expr));
 }
 
+/* Write an address into the guest_IA */
+static __inline__ void
+put_IA(IRExpr *address)
+{
+   stmt(IRStmt_Put(S390X_GUEST_OFFSET(guest_IA), address));
+}
+
+/* Add a dummy put to the guest_IA to satisfy an assert in bb_to_IR
+   that wants the last statement in an IRSB to be a put to the guest_IA.
+   Mostly used for insns that use the "counter" pseudo guest reg. */
+static __inline__ void
+dummy_put_IA(void)
+{
+   put_IA(IRExpr_Get(S390X_GUEST_OFFSET(guest_IA), Ity_I64));
+}
+
 /* Create a temporary of the given type and assign the expression to it */
 static __inline__ IRTemp
 mktemp(IRType type, IRExpr *expr)
@@ -242,10 +265,10 @@ load(IRType type, IRExpr *addr)
 static void
 call_function(IRExpr *callee_address)
 {
-   irsb->next = callee_address;
-   irsb->jumpkind = Ijk_Call;
+   put_IA(callee_address);
 
-   dis_res->whatNext = Dis_StopHere;
+   dis_res->whatNext    = Dis_StopHere;
+   dis_res->jk_StopHere = Ijk_Call;
 }
 
 /* Function call with known target. */
@@ -256,9 +279,10 @@ call_function_and_chase(Addr64 callee_address)
       dis_res->whatNext   = Dis_ResteerU;
       dis_res->continueAt = callee_address;
    } else {
-      irsb->next = mkU64(callee_address);
-      irsb->jumpkind = Ijk_Call;
+      put_IA(mkaddr_expr(callee_address));
+
       dis_res->whatNext = Dis_StopHere;
+      dis_res->jk_StopHere = Ijk_Call;
    }
 }
 
@@ -266,10 +290,10 @@ call_function_and_chase(Addr64 callee_address)
 static void
 return_from_function(IRExpr *return_address)
 {
-   irsb->next = return_address;
-   irsb->jumpkind = Ijk_Ret;
+   put_IA(return_address);
 
-   dis_res->whatNext = Dis_StopHere;
+   dis_res->whatNext    = Dis_StopHere;
+   dis_res->jk_StopHere = Ijk_Ret;
 }
 
 /* A conditional branch whose target is not known at instrumentation time.
@@ -289,12 +313,13 @@ if_not_condition_goto_computed(IRExpr *condition, IRExpr *target)
 {
    vassert(typeOfIRExpr(irsb->tyenv, condition) == Ity_I1);
 
-   stmt(IRStmt_Exit3(condition, Ijk_Boring, IRConst_U64(guest_IA_next_instr)));
+   stmt(IRStmt_Exit(condition, Ijk_Boring, IRConst_U64(guest_IA_next_instr),
+                    S390X_GUEST_OFFSET(guest_IA)));
 
-   irsb->next = target;
-   irsb->jumpkind = Ijk_Boring;
+   put_IA(target);
 
-   dis_res->whatNext = Dis_StopHere;
+   dis_res->whatNext    = Dis_StopHere;
+   dis_res->jk_StopHere = Ijk_Boring;
 }
 
 /* A conditional branch whose target is known at instrumentation time. */
@@ -303,8 +328,13 @@ if_condition_goto(IRExpr *condition, Addr64 target)
 {
    vassert(typeOfIRExpr(irsb->tyenv, condition) == Ity_I1);
 
-   stmt(IRStmt_Exit3(condition, Ijk_Boring, IRConst_U64(target)));
-   dis_res->whatNext = Dis_Continue;
+   stmt(IRStmt_Exit(condition, Ijk_Boring, IRConst_U64(target),
+                    S390X_GUEST_OFFSET(guest_IA)));
+
+   put_IA(mkaddr_expr(target));
+
+   dis_res->whatNext    = Dis_StopHere;
+   dis_res->jk_StopHere = Ijk_Boring;
 }
 
 /* An unconditional branch. Target may or may not be known at instrumentation
@@ -312,23 +342,26 @@ if_condition_goto(IRExpr *condition, Addr64 target)
 static void
 always_goto(IRExpr *target)
 {
-   irsb->next = target;
-   irsb->jumpkind = Ijk_Boring;
+   put_IA(target);
 
-   dis_res->whatNext = Dis_StopHere;
+   dis_res->whatNext    = Dis_StopHere;
+   dis_res->jk_StopHere = Ijk_Boring;
 }
 
+
 /* An unconditional branch to a known target. */
 static void
 always_goto_and_chase(Addr64 target)
 {
    if (resteer_fn(resteer_data, target)) {
+      /* Follow into the target */
       dis_res->whatNext   = Dis_ResteerU;
       dis_res->continueAt = target;
    } else {
-      irsb->next = mkU64(target);
-      irsb->jumpkind = Ijk_Boring;
-      dis_res->whatNext = Dis_StopHere;
+      put_IA(mkaddr_expr(target));
+
+      dis_res->whatNext    = Dis_StopHere;
+      dis_res->jk_StopHere = Ijk_Boring;
    }
 }
 
@@ -343,14 +376,13 @@ system_call(IRExpr *sysno)
    stmt(IRStmt_Put(S390X_GUEST_OFFSET(guest_IP_AT_SYSCALL),
                    mkU64(guest_IA_curr_instr)));
 
+   put_IA(mkaddr_expr(guest_IA_next_instr));
+
    /* It's important that all ArchRegs carry their up-to-date value
       at this point.  So we declare an end-of-block here, which
       forces any TempRegs caching ArchRegs to be flushed. */
-   irsb->next = mkU64(guest_IA_next_instr);
-
-   irsb->jumpkind = Ijk_Sys_syscall;
-
-   dis_res->whatNext = Dis_StopHere;
+   dis_res->whatNext    = Dis_StopHere;
+   dis_res->jk_StopHere = Ijk_Sys_syscall;
 }
 
 /* Encode the s390 rounding mode as it appears in the m3/m4 fields of certain
@@ -8576,6 +8608,7 @@ s390_irgen_CLC(UChar length, IRTemp start1, IRTemp start2)
    if_condition_goto(binop(Iop_CmpNE64, mkexpr(counter), mkU64(length)),
                      guest_IA_curr_instr);
    put_counter_dw0(mkU64(0));
+   dummy_put_IA();
 
    return "clc";
 }
@@ -8869,8 +8902,8 @@ void (*irgen)(IRTemp length, IRTemp start1, IRTemp start2), int lensize)
    stmt(IRStmt_Put(S390X_GUEST_OFFSET(guest_TISTART),
                    mkU64(guest_IA_curr_instr)));
    stmt(IRStmt_Put(S390X_GUEST_OFFSET(guest_TILEN), mkU64(4)));
-   stmt(IRStmt_Exit3(mkexpr(cond), Ijk_TInval,
-        IRConst_U64(guest_IA_curr_instr)));
+   stmt(IRStmt_Exit(mkexpr(cond), Ijk_TInval, IRConst_U64(guest_IA_curr_instr),
+                    S390X_GUEST_OFFSET(guest_IA)));
 
    ss.bytes = last_execute_target;
    assign(start1, binop(Iop_Add64, mkU64(ss.dec.d1),
@@ -8880,6 +8913,8 @@ void (*irgen)(IRTemp length, IRTemp start1, IRTemp start2), int lensize)
    assign(len, unop(lensize == 64 ? Iop_8Uto64 : Iop_8Uto32, binop(Iop_Or8,
           r != 0 ? get_gpr_b7(r): mkU8(0), mkU8(ss.dec.l))));
    irgen(len, start1, start2);
+   dummy_put_IA();
+
    last_execute_target = 0;
 }
 
@@ -8900,8 +8935,9 @@ s390_irgen_EX(UChar r1, IRTemp addr2)
       stmt(IRStmt_Put(S390X_GUEST_OFFSET(guest_TISTART),
                       mkU64(guest_IA_curr_instr)));
       stmt(IRStmt_Put(S390X_GUEST_OFFSET(guest_TILEN), mkU64(4)));
-      stmt(IRStmt_Exit3(IRExpr_Const(IRConst_U1(True)), Ijk_TInval,
-           IRConst_U64(guest_IA_curr_instr)));
+      stmt(IRStmt_Exit(IRExpr_Const(IRConst_U1(True)), Ijk_TInval,
+                       IRConst_U64(guest_IA_curr_instr),
+                       S390X_GUEST_OFFSET(guest_IA)));
       /* we know that this will be invalidated */
       irsb->next = mkU64(guest_IA_next_instr);
       dis_res->whatNext = Dis_StopHere;
@@ -8958,8 +8994,9 @@ s390_irgen_EX(UChar r1, IRTemp addr2)
       /* and restart */
       stmt(IRStmt_Put(S390X_GUEST_OFFSET(guest_TISTART), mkU64(guest_IA_curr_instr)));
       stmt(IRStmt_Put(S390X_GUEST_OFFSET(guest_TILEN), mkU64(4)));
-      stmt(IRStmt_Exit3(mkexpr(cond), Ijk_TInval,
-           IRConst_U64(guest_IA_curr_instr)));
+      stmt(IRStmt_Exit(mkexpr(cond), Ijk_TInval,
+                       IRConst_U64(guest_IA_curr_instr),
+                       S390X_GUEST_OFFSET(guest_IA)));
 
       /* Now comes the actual translation */
       bytes = (UChar *) &last_execute_target;
@@ -9032,11 +9069,13 @@ s390_irgen_SRST(UChar r1, UChar r2)
    put_counter_dw0(binop(Iop_Add64, mkexpr(counter), mkU64(1)));
    put_gpr_dw0(r1, mkexpr(next));
    put_gpr_dw0(r2, binop(Iop_Add64, mkexpr(address), mkU64(1)));
-   stmt(IRStmt_Exit3(binop(Iop_CmpNE64, mkexpr(counter), mkU64(255)),
-                    Ijk_Boring, IRConst_U64(guest_IA_curr_instr)));
+   stmt(IRStmt_Exit(binop(Iop_CmpNE64, mkexpr(counter), mkU64(255)),
+                    Ijk_Boring, IRConst_U64(guest_IA_curr_instr),
+                    S390X_GUEST_OFFSET(guest_IA)));
    // >= 256 bytes done CC=3
    s390_cc_set(3);
    put_counter_dw0(mkU64(0));
+   dummy_put_IA();
 
    return "srst";
 }
@@ -9098,11 +9137,13 @@ s390_irgen_CLST(UChar r1, UChar r2)
    put_counter_dw0(binop(Iop_Add64, mkexpr(counter), mkU64(1)));
    put_gpr_dw0(r1, binop(Iop_Add64, get_gpr_dw0(r1), mkU64(1)));
    put_gpr_dw0(r2, binop(Iop_Add64, get_gpr_dw0(r2), mkU64(1)));
-   stmt(IRStmt_Exit3(binop(Iop_CmpNE64, mkexpr(counter), mkU64(255)),
-                    Ijk_Boring, IRConst_U64(guest_IA_curr_instr)));
+   stmt(IRStmt_Exit(binop(Iop_CmpNE64, mkexpr(counter), mkU64(255)),
+                    Ijk_Boring, IRConst_U64(guest_IA_curr_instr),
+                    S390X_GUEST_OFFSET(guest_IA)));
    // >= 256 bytes done CC=3
    s390_cc_set(3);
    put_counter_dw0(mkU64(0));
+   dummy_put_IA();
 
    return "clst";
 }
@@ -9297,6 +9338,7 @@ s390_irgen_xonc(IROp op, UChar length, IRTemp start1, IRTemp start2)
    s390_cc_thunk_put1(S390_CC_OP_BITWISE, mktemp(Ity_I32, get_counter_w1()),
                       False);
    put_counter_dw0(mkU64(0));
+   dummy_put_IA();
 }
 
 static HChar *
@@ -9338,6 +9380,7 @@ s390_irgen_XC_sameloc(UChar length, UChar b, UShort d)
 
      /* Reset counter */
      put_counter_dw0(mkU64(0));
+     dummy_put_IA();
    }
 
    s390_cc_thunk_put1(S390_CC_OP_BITWISE, mktemp(Ity_I32, mkU32(0)), False);
@@ -9378,6 +9421,7 @@ s390_irgen_MVC(UChar length, IRTemp start1, IRTemp start2)
    if_condition_goto(binop(Iop_CmpNE64, mkexpr(counter), mkU64(length)),
                      guest_IA_curr_instr);
    put_counter_dw0(mkU64(0));
+   dummy_put_IA();
 
    return "mvc";
 }
@@ -9558,6 +9602,7 @@ s390_irgen_MVST(UChar r1, UChar r2)
    s390_cc_set(1);
    put_gpr_dw0(r1, binop(Iop_Add64, mkexpr(addr1), mkexpr(counter)));
    put_counter_dw0(mkU64(0));
+   dummy_put_IA();
 
    return "mvst";
 }
@@ -9823,8 +9868,9 @@ s390_irgen_cas_32(UChar r1, UChar r3, IRTemp op2addr)
       Otherwise, store the old_value from memory in r1 and yield. */
    assign(nequal, binop(Iop_CmpNE32, s390_call_calculate_cc(), mkU32(0)));
    put_gpr_w1(r1, mkite(mkexpr(nequal), mkexpr(old_mem), mkexpr(op1)));
-   stmt(IRStmt_Exit3(mkexpr(nequal), Ijk_Yield,
-        IRConst_U64(guest_IA_next_instr)));
+   stmt(IRStmt_Exit(mkexpr(nequal), Ijk_Yield,
+                    IRConst_U64(guest_IA_next_instr),
+                    S390X_GUEST_OFFSET(guest_IA)));
 }
 
 static HChar *
@@ -9872,8 +9918,9 @@ s390_irgen_CSG(UChar r1, UChar r3, IRTemp op2addr)
       Otherwise, store the old_value from memory in r1 and yield. */
    assign(nequal, binop(Iop_CmpNE32, s390_call_calculate_cc(), mkU32(0)));
    put_gpr_dw0(r1, mkite(mkexpr(nequal), mkexpr(old_mem), mkexpr(op1)));
-   stmt(IRStmt_Exit3(mkexpr(nequal), Ijk_Yield,
-        IRConst_U64(guest_IA_next_instr)));
+   stmt(IRStmt_Exit(mkexpr(nequal), Ijk_Yield,
+                    IRConst_U64(guest_IA_next_instr),
+                    S390X_GUEST_OFFSET(guest_IA)));
 
    return "csg";
 }
@@ -11059,6 +11106,7 @@ s390_irgen_TR(UChar length, IRTemp start1, IRTemp start2)
                      guest_IA_curr_instr);
 
    put_counter_dw0(mkU64(0));
+   dummy_put_IA();
 
    return "tr";
 }
@@ -13518,14 +13566,6 @@ s390_decode_and_irgen(UChar *bytes, UInt insn_length, DisResult *dres)
 }
 
 
-/* Generate an IRExpr for an address. */
-static __inline__ IRExpr *
-mkaddr_expr(Addr64 addr)
-{
-   return IRExpr_Const(IRConst_U64(addr));
-}
-
-
 /* Disassemble a single instruction INSN into IR. */
 static DisResult
 disInstr_S390_WRK(UChar *insn)
@@ -13553,6 +13593,7 @@ disInstr_S390_WRK(UChar *insn)
    dres.whatNext   = Dis_Continue;
    dres.len        = insn_length;
    dres.continueAt = 0;
+   dres.jk_StopHere = Ijk_INVALID;
 
    /* fixs390: consider chasing of conditional jumps */
 
@@ -13561,17 +13602,28 @@ disInstr_S390_WRK(UChar *insn)
       /* All decode failures end up here. The decoder has already issued an
          error message.
          Tell the dispatcher that this insn cannot be decoded, and so has
-         not been executed, and (is currently) the next to be executed.
-         IA should be up-to-date since it made so at the start of each
-         insn, but nevertheless be paranoid and update it again right
-         now. */
-      stmt(IRStmt_Put(S390X_GUEST_OFFSET(guest_IA),
-                      mkaddr_expr(guest_IA_curr_instr)));
-
-      irsb->next = mkaddr_expr(guest_IA_next_instr);
-      irsb->jumpkind = Ijk_NoDecode;
-      dres.whatNext = Dis_StopHere;
-      dres.len = 0;
+         not been executed, and (is currently) the next to be executed. */
+      put_IA(mkaddr_expr(guest_IA_curr_instr));
+
+      dres.whatNext    = Dis_StopHere;
+      dres.jk_StopHere = Ijk_NoDecode;
+      dres.continueAt  = 0;
+      dres.len         = 0;
+   } else {
+      /* Decode success */
+      switch (dres.whatNext) {
+      case Dis_Continue:
+         put_IA(mkaddr_expr(guest_IA_curr_instr));
+         break;
+      case Dis_ResteerU:
+      case Dis_ResteerC:
+         put_IA(mkaddr_expr(dres.continueAt));
+         break;
+      case Dis_StopHere:
+         break;
+      default:
+         vassert(0);
+      }
    }
 
    return dres;
@@ -13587,7 +13639,6 @@ disInstr_S390_WRK(UChar *insn)
 
 DisResult
 disInstr_S390(IRSB        *irsb_IN,
-              Bool         put_IP __attribute__((unused)),
               Bool       (*resteerOkFn)(void *, Addr64),
               Bool         resteerCisOk,
               void        *callback_opaque,
@@ -13610,10 +13661,6 @@ disInstr_S390(IRSB        *irsb_IN,
    resteer_fn = resteerOkFn;
    resteer_data = callback_opaque;
 
-   /* Always update the guest IA. See comment in s390_isel_stmt for Ist_Put. */
-   stmt(IRStmt_Put(S390X_GUEST_OFFSET(guest_IA),
-                   mkaddr_expr(guest_IA_curr_instr)));
-
    return disInstr_S390_WRK(guest_code + delta);
 }
 
index 2de126d94a286cac6a00b2f0ddcfa4c22fb45876..0a3d98d38e60f1fd1be31f928bd46ca4880a9ea9 100644 (file)
@@ -118,7 +118,7 @@ s390_hreg_get_allocable(Int *nregs, HReg **arr)
    /* Total number of allocable registers (all classes) */
    *nregs =  16 /* GPRs */
       -  1 /* r0 */
-      -  1 /* r12 register holding VG_(dispatch_ctr) */
+      -  1 /* r12 scratch register for translation chaining support */
       -  1 /* r13 guest state pointer */
       -  1 /* r14 link register */
       -  1 /* r15 stack pointer */
@@ -144,12 +144,8 @@ s390_hreg_get_allocable(Int *nregs, HReg **arr)
       Otherwise, they are available to the allocator */
    (*arr)[i++] = mkHReg(10, HRcInt64, False);
    (*arr)[i++] = mkHReg(11, HRcInt64, False);
-   /* GPR12 is not available because it caches VG_(dispatch_ctr).
-      Setting aside a register for the counter gives slightly better
-      performance - most of the time. From the 10 tests in "make perf"
-      8 run faster with a max observed speedup of 2.6% for bz2. ffbench
-      is the counter example. It runs 1.3% faster without the dedicated
-      register. */
+   /* GPR12 is not available because it us used as a scratch register
+      in translation chaining. */
    /* GPR13 is not available because it is used as guest state pointer */
    /* GPR14 is not available because it is used as link register */
    /* GPR15 is not available because it is used as stack pointer */
@@ -183,6 +179,7 @@ s390_hreg_guest_state_pointer(void)
    return mkHReg(S390_REGNO_GUEST_STATE_POINTER, HRcInt64, False);
 }
 
+
 /* Is VALUE within the domain of a 20-bit signed integer. */
 static __inline__ Bool
 fits_signed_20bit(Int value)
@@ -617,14 +614,6 @@ s390_insn_get_reg_usage(HRegUsage *u, const s390_insn *insn)
       s390_opnd_RMI_get_reg_usage(u, insn->variant.compare.src2);
       break;
 
-   case S390_INSN_BRANCH:
-      s390_opnd_RMI_get_reg_usage(u, insn->variant.branch.dst);
-      /* The destination address is loaded into S390_REGNO_RETURN_VALUE.
-         See s390_insn_branch_emit. */
-      addHRegUse(u, HRmWrite,
-                 mkHReg(S390_REGNO_RETURN_VALUE, HRcInt64, False));
-      break;
-
    case S390_INSN_HELPER_CALL: {
       UInt i;
 
@@ -718,6 +707,29 @@ s390_insn_get_reg_usage(HRegUsage *u, const s390_insn *insn)
    case S390_INSN_GADD:
       break;
 
+   case S390_INSN_EVCHECK:
+      s390_amode_get_reg_usage(u, insn->variant.evcheck.counter);
+      s390_amode_get_reg_usage(u, insn->variant.evcheck.fail_addr);
+      break;
+
+   case S390_INSN_PROFINC:
+      /* Does not use any register visible to the register allocator */
+      break;
+
+   case S390_INSN_XDIRECT:
+      s390_amode_get_reg_usage(u, insn->variant.xdirect.guest_IA);
+      break;
+
+   case S390_INSN_XINDIR:
+      addHRegUse(u, HRmRead, insn->variant.xindir.dst);
+      s390_amode_get_reg_usage(u, insn->variant.xindir.guest_IA);
+      break;
+
+   case S390_INSN_XASSISTED:
+      addHRegUse(u, HRmRead, insn->variant.xassisted.dst);
+      s390_amode_get_reg_usage(u, insn->variant.xassisted.guest_IA);
+      break;
+
    default:
       vpanic("s390_insn_get_reg_usage");
    }
@@ -829,11 +841,6 @@ s390_insn_map_regs(HRegRemap *m, s390_insn *insn)
       s390_opnd_RMI_map_regs(m, &insn->variant.compare.src2);
       break;
 
-   case S390_INSN_BRANCH:
-      s390_opnd_RMI_map_regs(m, &insn->variant.branch.dst);
-      /* No need to map S390_REGNO_RETURN_VALUE. It's not virtual */
-      break;
-
    case S390_INSN_HELPER_CALL:
       /* s390_insn_helper_call_emit also reads / writes the link register
          and stack pointer. But those registers are not visible to the
@@ -923,6 +930,31 @@ s390_insn_map_regs(HRegRemap *m, s390_insn *insn)
    case S390_INSN_GADD:
       break;
 
+   case S390_INSN_EVCHECK:
+      s390_amode_map_regs(m, insn->variant.evcheck.counter);
+      s390_amode_map_regs(m, insn->variant.evcheck.fail_addr);
+      break;
+
+   case S390_INSN_PROFINC:
+      /* Does not use any register visible to the register allocator */
+      break;
+
+   case S390_INSN_XDIRECT:
+      s390_amode_map_regs(m, insn->variant.xdirect.guest_IA);
+      break;
+
+   case S390_INSN_XINDIR:
+      s390_amode_map_regs(m, insn->variant.xindir.guest_IA);
+      insn->variant.xindir.dst =
+         lookupHRegRemap(m, insn->variant.xindir.dst);
+      break;
+
+   case S390_INSN_XASSISTED:
+      s390_amode_map_regs(m, insn->variant.xassisted.guest_IA);
+      insn->variant.xassisted.dst =
+         lookupHRegRemap(m, insn->variant.xassisted.dst);
+      break;
+
    default:
       vpanic("s390_insn_map_regs");
    }
@@ -1402,6 +1434,16 @@ s390_emit_BRC(UChar *p, UChar r1, UShort i2)
 }
 
 
+static UChar *
+s390_emit_BRCL(UChar *p, UChar r1, ULong i2)
+{
+   if (UNLIKELY(vex_traceflags & VEX_TRACE_ASM))
+      s390_disasm(ENC2(XMNM, PCREL), S390_XMNM_BRCL, r1, i2);
+
+   return emit_RIL(p, 0xc00400000000ULL, r1, i2);
+}
+
+
 static UChar *
 s390_emit_CR(UChar *p, UChar r1, UChar r2)
 {
@@ -4251,21 +4293,6 @@ s390_insn_compare(UChar size, HReg src1, s390_opnd_RMI src2,
 }
 
 
-s390_insn *
-s390_insn_branch(IRJumpKind kind, s390_cc_t cond, s390_opnd_RMI dst)
-{
-   s390_insn *insn = LibVEX_Alloc(sizeof(s390_insn));
-
-   insn->tag  = S390_INSN_BRANCH;
-   insn->size = 0;  /* does not matter */
-   insn->variant.branch.kind = kind;
-   insn->variant.branch.dst  = dst;
-   insn->variant.branch.cond = cond;
-
-   return insn;
-}
-
-
 s390_insn *
 s390_insn_helper_call(s390_cc_t cond, Addr64 target, UInt num_args,
                       HChar *name)
@@ -4489,6 +4516,89 @@ s390_insn_gadd(UChar size, UInt offset, UChar delta, ULong value)
 }
 
 
+s390_insn *
+s390_insn_xdirect(s390_cc_t cond, Addr64 dst, s390_amode *guest_IA,
+                  Bool to_fast_entry)
+{
+   s390_insn *insn = LibVEX_Alloc(sizeof(s390_insn));
+
+   insn->tag  = S390_INSN_XDIRECT;
+   insn->size = 0;   /* does not matter */
+
+   insn->variant.xdirect.cond = cond;
+   insn->variant.xdirect.dst = dst;
+   insn->variant.xdirect.guest_IA = guest_IA;
+   insn->variant.xdirect.to_fast_entry = to_fast_entry;
+
+   return insn;
+}
+
+
+s390_insn *
+s390_insn_xindir(s390_cc_t cond, HReg dst, s390_amode *guest_IA)
+{
+   s390_insn *insn = LibVEX_Alloc(sizeof(s390_insn));
+
+   insn->tag  = S390_INSN_XASSISTED;
+   insn->size = 0;   /* does not matter */
+
+   insn->variant.xdirect.cond = cond;
+   insn->variant.xdirect.dst = dst;
+   insn->variant.xdirect.guest_IA = guest_IA;
+
+   return insn;
+}
+
+
+s390_insn *
+s390_insn_xassisted(s390_cc_t cond, HReg dst, s390_amode *guest_IA,
+                    IRJumpKind kind)
+{
+   s390_insn *insn = LibVEX_Alloc(sizeof(s390_insn));
+
+   insn->tag  = S390_INSN_XASSISTED;
+   insn->size = 0;   /* does not matter */
+
+   insn->variant.xassisted.cond = cond;
+   insn->variant.xassisted.dst = dst;
+   insn->variant.xassisted.guest_IA = guest_IA;
+   insn->variant.xassisted.kind = kind;
+
+   return insn;
+}
+
+
+s390_insn *
+s390_insn_evcheck(s390_amode *counter, s390_amode *fail_addr)
+{
+   s390_insn *insn = LibVEX_Alloc(sizeof(s390_insn));
+
+   vassert(counter->tag == S390_AMODE_B12 || counter->tag == S390_AMODE_BX12);
+   vassert(fail_addr->tag == S390_AMODE_B12 ||
+           fail_addr->tag == S390_AMODE_BX12);
+
+   insn->tag  = S390_INSN_EVCHECK;
+   insn->size = 0;   /* does not matter */
+
+   insn->variant.evcheck.counter = counter;
+   insn->variant.evcheck.fail_addr = fail_addr;
+
+   return insn;
+}
+
+
+s390_insn *
+s390_insn_profinc(void)
+{
+   s390_insn *insn = LibVEX_Alloc(sizeof(s390_insn));
+
+   insn->tag  = S390_INSN_PROFINC;
+   insn->size = 0;   /* does not matter */
+
+   return insn;
+}
+
+
 /*---------------------------------------------------------------*/
 /*--- Debug print                                             ---*/
 /*---------------------------------------------------------------*/
@@ -4519,6 +4629,31 @@ s390_cc_as_string(s390_cc_t cc)
 }
 
 
+static const HChar *
+s390_jump_kind_as_string(IRJumpKind kind)
+{
+   switch (kind) {
+   case Ijk_Boring:      return "Boring";
+   case Ijk_Call:        return "Call";
+   case Ijk_Ret:         return "Return";
+   case Ijk_ClientReq:   return "ClientReq";
+   case Ijk_Yield:       return "Yield";
+   case Ijk_EmWarn:      return "EmWarn";
+   case Ijk_EmFail:      return "EmFail";
+   case Ijk_NoDecode:    return "NoDecode";
+   case Ijk_MapFail:     return "MapFail";
+   case Ijk_TInval:      return "Invalidate";
+   case Ijk_NoRedir:     return "NoRedir";
+   case Ijk_SigTRAP:     return "SigTRAP";
+   case Ijk_SigSEGV:     return "SigSEGV";
+   case Ijk_SigBUS:      return "SigBUS";
+   case Ijk_Sys_syscall: return "Sys_syscall";
+   default:
+      vpanic("s390_jump_kind_as_string");
+   }
+}
+
+
 /* Helper function for writing out a V insn */
 static void
 s390_sprintf(HChar *buf, HChar *fmt, ...)
@@ -4568,6 +4703,11 @@ s390_sprintf(HChar *buf, HChar *fmt, ...)
          p += vex_sprintf(p, "%s", s390_cc_as_string(va_arg(args, s390_cc_t)));
          continue;
 
+      case 'J':     /* &J = jump kind */
+         p += vex_sprintf(p, "%s",
+                          s390_jump_kind_as_string(va_arg(args, IRJumpKind)));
+         continue;
+
       case 'L': {   /* %L = argument list in helper call*/
          UInt i, num_args;
 
@@ -4762,43 +4902,13 @@ s390_insn_as_string(const s390_insn *insn)
                    &insn->variant.compare.src2);
       break;
 
-   case S390_INSN_BRANCH:
-      switch (insn->variant.branch.kind) {
-      case Ijk_ClientReq:   op = "clientreq"; break;
-      case Ijk_Sys_syscall: op = "syscall";   break;
-      case Ijk_Yield:       op = "yield";     break;
-      case Ijk_EmWarn:      op = "emwarn";    break;
-      case Ijk_EmFail:      op = "emfail";    break;
-      case Ijk_MapFail:     op = "mapfail";   break;
-      case Ijk_NoDecode:    op = "nodecode";  break;
-      case Ijk_TInval:      op = "tinval";    break;
-      case Ijk_NoRedir:     op = "noredir";   break;
-      case Ijk_SigTRAP:     op = "sigtrap";   break;
-      case Ijk_Boring:      op = "goto";      break;
-      case Ijk_Call:        op = "call";      break;
-      case Ijk_Ret:         op = "return";    break;
-      default:
-         goto fail;
-      }
-      s390_sprintf(buf, "if (%C) %s %O", insn->variant.branch.cond, op,
-                   &insn->variant.branch.dst);
-      break;
-
    case S390_INSN_HELPER_CALL: {
-
-      if (insn->variant.helper_call.cond != S390_CC_ALWAYS) {
-         s390_sprintf(buf, "%M if (%C) %s{%I}(%L)", "v-call",
-                      insn->variant.helper_call.cond,
-                      insn->variant.helper_call.name,
-                      insn->variant.helper_call.target,
-                      insn->variant.helper_call.num_args);
-      } else {
-         s390_sprintf(buf, "%M %s{%I}(%L)", "v-call",
-                      insn->variant.helper_call.name,
-                      insn->variant.helper_call.target,
-                      insn->variant.helper_call.num_args);
-      }
-      break;
+      s390_sprintf(buf, "%M if (%C) %s{%I}(%L)", "v-call",
+                   insn->variant.helper_call.cond,
+                   insn->variant.helper_call.name,
+                   insn->variant.helper_call.target,
+                   insn->variant.helper_call.num_args);
+      return buf;   /* avoid printing "size = ..." which is meaningless */
    }
 
    case S390_INSN_BFP_TRIOP:
@@ -4919,6 +5029,39 @@ s390_insn_as_string(const s390_insn *insn)
                    insn->variant.gadd.value);
       break;
 
+   case S390_INSN_EVCHECK:
+      s390_sprintf(buf, "%M counter = %A, fail-addr = %A", "v-evcheck",
+                   insn->variant.evcheck.counter,
+                   insn->variant.evcheck.fail_addr);
+      return buf;   /* avoid printing "size = ..." which is meaningless */
+
+   case S390_INSN_PROFINC:
+      s390_sprintf(buf, "%M", "v-profinc");
+      return buf;   /* avoid printing "size = ..." which is meaningless */
+
+   case S390_INSN_XDIRECT:
+      s390_sprintf(buf, "%M if (%C) %A = %I  %s", "v-xdirect",
+                   insn->variant.xdirect.cond,
+                   insn->variant.xdirect.guest_IA,
+                   insn->variant.xdirect.dst,
+                   insn->variant.xdirect.to_fast_entry ? "fast" : "slow");
+      return buf;   /* avoid printing "size = ..." which is meaningless */
+
+   case S390_INSN_XINDIR:
+      s390_sprintf(buf, "%M if (%C) %A = %R", "v-xindir",
+                   insn->variant.xindir.cond,
+                   insn->variant.xindir.guest_IA,
+                   insn->variant.xindir.dst);
+      return buf;   /* avoid printing "size = ..." which is meaningless */
+
+   case S390_INSN_XASSISTED:
+      s390_sprintf(buf, "%M if (%C) %J %A = %R", "v-xassisted",
+                   insn->variant.xassisted.cond,
+                   insn->variant.xassisted.kind,
+                   insn->variant.xassisted.guest_IA,
+                   insn->variant.xassisted.dst);
+      return buf;   /* avoid printing "size = ..." which is meaningless */
+
    default: goto fail;
    }
 
@@ -6506,104 +6649,6 @@ s390_insn_clz_emit(UChar *buf, const s390_insn *insn)
 }
 
 
-static UChar *
-s390_insn_branch_emit(UChar *buf, const s390_insn *insn)
-{
-   s390_opnd_RMI dst;
-   s390_cc_t cond;
-   UInt       trc;
-   UChar *p, *ptmp = 0;  /* avoid compiler warnings */
-
-   cond = insn->variant.branch.cond;
-   dst  = insn->variant.branch.dst;
-
-   p = buf;
-   trc = 0;
-
-   if (cond != S390_CC_ALWAYS) {
-      /* So we have something like this
-         if (cond) goto X;
-         Y: ...
-         We convert this into
-         if (! cond) goto Y;        // BRC insn; 4 bytes
-         return_reg = X;
-         return to dispatcher
-         Y:
-      */
-      ptmp = p; /* 4 bytes (a BRC insn) to be filled in here */
-      p += 4;
-   }
-
-   /* If a non-boring, set guest-state-pointer appropriately. */
-
-   switch (insn->variant.branch.kind) {
-   case Ijk_ClientReq:   trc = VEX_TRC_JMP_CLIENTREQ;   break;
-   case Ijk_Sys_syscall: trc = VEX_TRC_JMP_SYS_SYSCALL; break;
-   case Ijk_Yield:       trc = VEX_TRC_JMP_YIELD;       break;
-   case Ijk_EmWarn:      trc = VEX_TRC_JMP_EMWARN;      break;
-   case Ijk_EmFail:      trc = VEX_TRC_JMP_EMFAIL;      break;
-   case Ijk_MapFail:     trc = VEX_TRC_JMP_MAPFAIL;     break;
-   case Ijk_NoDecode:    trc = VEX_TRC_JMP_NODECODE;    break;
-   case Ijk_TInval:      trc = VEX_TRC_JMP_TINVAL;      break;
-   case Ijk_NoRedir:     trc = VEX_TRC_JMP_NOREDIR;     break;
-   case Ijk_SigTRAP:     trc = VEX_TRC_JMP_SIGTRAP;     break;
-   case Ijk_Ret:         trc = 0; break;
-   case Ijk_Call:        trc = 0; break;
-   case Ijk_Boring:      trc = 0; break;
-      break;
-
-   default:
-      vpanic("s390_insn_branch_emit: unknown jump kind");
-   }
-
-   /* Get the destination address into the return register */
-   switch (dst.tag) {
-   case S390_OPND_REG:
-      p = s390_emit_LGR(p, S390_REGNO_RETURN_VALUE, hregNumber(dst.variant.reg));
-      break;
-
-   case S390_OPND_AMODE: {
-      const s390_amode *am = dst.variant.am;
-      UChar b = hregNumber(am->b);
-      UChar x = hregNumber(am->x);
-      Int   d = am->d;
-
-      p = s390_emit_LG(p, S390_REGNO_RETURN_VALUE, x, b, DISP20(d));
-      break;
-   }
-
-   case S390_OPND_IMMEDIATE:
-      p = s390_emit_load_64imm(p, S390_REGNO_RETURN_VALUE, dst.variant.imm);
-      break;
-
-   default:
-      goto fail;
-   }
-
-   if (trc != 0) {
-      /* Something special. Set guest-state pointer appropriately */
-      p = s390_emit_LGHI(p, S390_REGNO_GUEST_STATE_POINTER, trc);
-   } else {
-      /* Nothing special needs to be done for calls and returns. */
-   }
-
-   p = s390_emit_BCR(p, S390_CC_ALWAYS, S390_REGNO_LINK_REGISTER);
-
-   if (cond != S390_CC_ALWAYS) {
-      Int delta = p - ptmp;
-
-      delta >>= 1;  /* immediate constant is #half-words */
-      vassert(delta > 0 && delta < (1 << 16));
-      s390_emit_BRC(ptmp, s390_cc_invert(cond), delta);
-   }
-
-   return p;
-
- fail:
-   vpanic("s390_insn_branch_emit");
-}
-
-
 static UChar *
 s390_insn_helper_call_emit(UChar *buf, const s390_insn *insn)
 {
@@ -7158,9 +7203,395 @@ s390_insn_gadd_emit(UChar *buf, const s390_insn *insn)
 }
 
 
+/* Define convenience functions needed for translation chaining.
+   Any changes need to be applied to the functions in concert. */
+
+/* Load the 64-bit VALUE into REG. Note that this function must NOT
+   optimise the generated code by looking at the value. I.e. using
+   LGHI if value == 0 would be very wrong.
+   fixs390: Do it in a way that works everywhere for now. */
+static UChar *
+s390_tchain_load64(UChar *buf, UChar regno, ULong value)
+{
+   buf = s390_emit_IILL(buf, regno, value & 0xFFFF);
+   value >>= 16;
+   buf = s390_emit_IILH(buf, regno, value & 0xFFFF);
+   value >>= 16;
+   buf = s390_emit_IIHL(buf, regno, value & 0xFFFF);
+   value >>= 16;
+   buf = s390_emit_IIHH(buf, regno, value & 0xFFFF);
+
+   return buf;
+}
+
+/* Return number of bytes generated by s390_tchain_load64 */
+static UInt
+s390_tchain_load64_len(void)
+{
+   vassert(S390_TCHAIN_LOAD64_LEN == 16);
+   return 16;
+}
+
+/* Verify that CODE is the code sequence generated by s390_tchain_load64
+   to load VALUE into REGNO. Return pointer to the byte following the
+   insn sequence. */
+static const UChar *
+s390_tchain_verify_load64(const UChar *code, UChar regno, ULong value)
+{
+   UInt regmask = regno << 4;
+   UInt hw;
+
+   /* Check for IILL */
+   hw = value & 0xFFFF;
+   vassert(code[0]  ==  0xA5);
+   vassert(code[1]  == (0x03 | regmask));
+   vassert(code[2]  == (hw >> 8));
+   vassert(code[3]  == (hw & 0xFF));
+
+   /* Check for IILH */
+   hw = (value >> 16) & 0xFFFF;
+   vassert(code[4]  ==  0xA5);
+   vassert(code[5]  == (0x02 | regmask));
+   vassert(code[6]  == (hw >> 8));
+   vassert(code[7]  == (hw & 0xFF));
+
+   /* Check for IIHL */
+   hw = (value >> 32) & 0xFFFF;
+   vassert(code[8]  ==  0xA5);
+   vassert(code[9]  == (0x01 | regmask));
+   vassert(code[10] == (hw >> 8));
+   vassert(code[11] == (hw & 0xFF));
+
+   /* Check for IIHH */
+   hw = (value >> 48) & 0xFFFF;
+   vassert(code[12] ==  0xA5);
+   vassert(code[13] == (0x00 | regmask));
+   vassert(code[14] == (hw >> 8));
+   vassert(code[15] == (hw & 0xFF));
+
+   return code + 16;
+}
+
+/* CODE points to the code sequence as generated by s390_tchain_load64.
+   Change the loaded value to VALUE. Return pointer to the byte following
+   the patched code sequence. */
+static UChar *
+s390_tchain_patch_load64(UChar *code, ULong imm64)
+{
+   code[2]  = imm64 & 0xFF; imm64 >>= 8;
+   code[3]  = imm64 & 0xFF; imm64 >>= 8;
+   code[6]  = imm64 & 0xFF; imm64 >>= 8;
+   code[7]  = imm64 & 0xFF; imm64 >>= 8;
+   code[10] = imm64 & 0xFF; imm64 >>= 8;
+   code[11] = imm64 & 0xFF; imm64 >>= 8;
+   code[14] = imm64 & 0xFF; imm64 >>= 8;
+   code[15] = imm64 & 0xFF; imm64 >>= 8;
+
+   return code + 16;
+}
+
+
+/* NB: what goes on here has to be very closely coordinated with the
+   chainXDirect_S390 and unchainXDirect_S390 below. */
+static UChar *
+s390_insn_xdirect_emit(UChar *buf, const s390_insn *insn,
+                       void *disp_cp_chain_me_to_slowEP,
+                       void *disp_cp_chain_me_to_fastEP)
+{
+   /* We're generating chain-me requests here, so we need to be
+      sure this is actually allowed -- no-redir translations can't
+      use chain-me's.  Hence: */
+   vassert(disp_cp_chain_me_to_slowEP != NULL);
+   vassert(disp_cp_chain_me_to_fastEP != NULL);
+
+   /* Use ptmp for backpatching conditional jumps. */
+   UChar *ptmp = buf;
+
+   /* First off, if this is conditional, create a conditional
+      jump over the rest of it. */
+   s390_cc_t cond = insn->variant.xdirect.cond;
+
+   if (cond != S390_CC_ALWAYS) {
+      /* So we have something like this
+         if (cond) do_xdirect;
+         Y: ...
+         We convert this into
+         if (! cond) goto Y;        // BRC opcode; 4 bytes
+         do_xdirect;
+         Y:
+      */
+      /* 4 bytes (a BRC insn) to be filled in here */
+      buf += 4;
+   }
+
+   /* Update the guest IA. */
+   buf = s390_emit_load_64imm(buf, R0, insn->variant.xdirect.dst);
+
+   const s390_amode *amode = insn->variant.xdirect.guest_IA;
+   vassert(amode->tag == S390_AMODE_B12 || amode->tag == S390_AMODE_BX12);
+   UInt b = hregNumber(amode->b);
+   UInt x = hregNumber(amode->x);  /* 0 for B12 and B20 */
+   UInt d = amode->d;
+
+   buf = s390_emit_STG(buf, R0, x, b, DISP20(d));
+
+   /* --- FIRST PATCHABLE BYTE follows --- */
+   /* VG_(disp_cp_chain_me_to_{slowEP,fastEP}) (where we're calling
+      to) backs up the return address, so as to find the address of
+      the first patchable byte.  So: don't change the length of the
+      two instructions below. */
+
+   /* Load the chosen entry point into the scratch reg */
+   void *disp_cp_chain_me;
+
+   disp_cp_chain_me =
+      insn->variant.xdirect.to_fast_entry ? disp_cp_chain_me_to_fastEP 
+                                          : disp_cp_chain_me_to_slowEP;
+
+   ULong addr = Ptr_to_ULong(disp_cp_chain_me);
+   buf = s390_tchain_load64(buf, S390_REGNO_TCHAIN_SCRATCH, addr);
+
+   /* call *tchain_scratch */
+   buf = s390_emit_BASR(buf, 1, S390_REGNO_TCHAIN_SCRATCH);
+
+   /* --- END of PATCHABLE BYTES --- */
+
+   /* Fix up the conditional jump, if there was one. */
+   if (cond != S390_CC_ALWAYS) {
+      Int delta = buf - ptmp;
+
+      delta >>= 1;  /* immediate constant is #half-words */
+      vassert(delta > 0 && delta < (1 << 16));
+      s390_emit_BRC(ptmp, s390_cc_invert(cond), delta);
+   }
+
+   return buf;
+}
+
+/* Return the number of patchable bytes from an xdirect insn. */
+static UInt
+s390_xdirect_patchable_len(void)
+{
+   return s390_tchain_load64_len() + 2;  /* 2 == BASR */
+}
+
+
+static UChar *
+s390_insn_xindir_emit(UChar *buf, const s390_insn *insn, void *disp_cp_xindir)
+{
+   /* We're generating transfers that could lead indirectly to a
+      chain-me, so we need to be sure this is actually allowed --
+      no-redir translations are not allowed to reach normal
+      translations without going through the scheduler.  That means
+      no XDirects or XIndirs out from no-redir translations.
+      Hence: */
+   vassert(disp_cp_xindir != NULL);
+
+   /* Use ptmp for backpatching conditional jumps. */
+   UChar *ptmp = buf;
+
+   /* First off, if this is conditional, create a conditional
+      jump over the rest of it. */
+   s390_cc_t cond = insn->variant.xdirect.cond;
+
+   if (cond != S390_CC_ALWAYS) {
+      /* So we have something like this
+         if (cond) do_xdirect;
+         Y: ...
+         We convert this into
+         if (! cond) goto Y;        // BRC opcode; 4 bytes
+         do_xdirect;
+         Y:
+      */
+      /* 4 bytes (a BRC insn) to be filled in here */
+      buf += 4;
+   }
+
+   /* Update the guest IA with the address in xdirect.dst. */
+   const s390_amode *amode = insn->variant.xdirect.guest_IA;
+
+   vassert(amode->tag == S390_AMODE_B12 || amode->tag == S390_AMODE_BX12);
+   UInt b = hregNumber(amode->b);
+   UInt x = hregNumber(amode->x);  /* 0 for B12 and B20 */
+   UInt d = amode->d;
+   UInt regno = hregNumber(insn->variant.xdirect.dst);
+
+   buf = s390_emit_STG(buf, regno, x, b, DISP20(d));
+
+   /* load tchain_scratch, #disp_indir */
+   buf = s390_tchain_load64(buf, S390_REGNO_TCHAIN_SCRATCH,
+                            Ptr_to_ULong(disp_cp_xindir));
+   /* BR *tchain_direct */
+   buf = s390_emit_BCR(buf, S390_CC_ALWAYS, S390_REGNO_TCHAIN_SCRATCH);
+
+   /* Fix up the conditional jump, if there was one. */
+   if (cond != S390_CC_ALWAYS) {
+      Int delta = buf - ptmp;
+
+      delta >>= 1;  /* immediate constant is #half-words */
+      vassert(delta > 0 && delta < (1 << 16));
+      s390_emit_BRC(ptmp, s390_cc_invert(cond), delta);
+   }
+
+   return buf;
+}
+
+static UChar *
+s390_insn_xassisted_emit(UChar *buf, const s390_insn *insn,
+                         void *disp_cp_xassisted)
+{
+   /* Use ptmp for backpatching conditional jumps. */
+   UChar *ptmp = buf;
+
+   /* First off, if this is conditional, create a conditional
+      jump over the rest of it. */
+   s390_cc_t cond = insn->variant.xdirect.cond;
+
+   if (cond != S390_CC_ALWAYS) {
+      /* So we have something like this
+         if (cond) do_xdirect;
+         Y: ...
+         We convert this into
+         if (! cond) goto Y;        // BRC opcode; 4 bytes
+         do_xdirect;
+         Y:
+      */
+      /* 4 bytes (a BRC insn) to be filled in here */
+      buf += 4;
+   }
+
+   /* Update the guest IA with the address in xassisted.dst. */
+   const s390_amode *amode = insn->variant.xassisted.guest_IA;
+
+   vassert(amode->tag == S390_AMODE_B12 || amode->tag == S390_AMODE_BX12);
+   UInt b = hregNumber(amode->b);
+   UInt x = hregNumber(amode->x);  /* 0 for B12 and B20 */
+   UInt d = amode->d;
+   UInt regno = hregNumber(insn->variant.xassisted.dst);
+
+   buf = s390_emit_STG(buf, regno, x, b, DISP20(d));
+
+   UInt trcval = 0;
+
+   switch (insn->variant.xassisted.kind) {
+   case Ijk_ClientReq:   trcval = VEX_TRC_JMP_CLIENTREQ;   break;
+   case Ijk_Sys_syscall: trcval = VEX_TRC_JMP_SYS_SYSCALL; break;
+   case Ijk_Sys_int32:   trcval = VEX_TRC_JMP_SYS_INT32;   break;
+   case Ijk_Yield:       trcval = VEX_TRC_JMP_YIELD;       break;
+   case Ijk_EmWarn:      trcval = VEX_TRC_JMP_EMWARN;      break;
+   case Ijk_MapFail:     trcval = VEX_TRC_JMP_MAPFAIL;     break;
+   case Ijk_NoDecode:    trcval = VEX_TRC_JMP_NODECODE;    break;
+   case Ijk_TInval:      trcval = VEX_TRC_JMP_TINVAL;      break;
+   case Ijk_NoRedir:     trcval = VEX_TRC_JMP_NOREDIR;     break;
+   case Ijk_SigTRAP:     trcval = VEX_TRC_JMP_SIGTRAP;     break;
+   case Ijk_SigSEGV:     trcval = VEX_TRC_JMP_SIGSEGV;     break;
+   case Ijk_Boring:      trcval = VEX_TRC_JMP_BORING;      break;
+      /* We don't expect to see the following being assisted. */
+   case Ijk_Ret:
+   case Ijk_Call:
+      /* fallthrough */
+   default: 
+      ppIRJumpKind(insn->variant.xassisted.kind);
+      vpanic("s390_insn_xassisted_emit: unexpected jump kind");
+   }
+
+   vassert(trcval != 0);
+
+   /* guest_state_pointer = trcval */
+   buf = s390_emit_LGHI(buf, S390_REGNO_GUEST_STATE_POINTER, trcval);
+
+   /* load tchain_scratch, #disp_assisted */
+   buf = s390_tchain_load64(buf, S390_REGNO_TCHAIN_SCRATCH,
+                            Ptr_to_ULong(disp_cp_xassisted));
+
+   /* BR *tchain_direct */
+   buf = s390_emit_BCR(buf, S390_CC_ALWAYS, S390_REGNO_TCHAIN_SCRATCH);
+
+   /* Fix up the conditional jump, if there was one. */
+   if (cond != S390_CC_ALWAYS) {
+      Int delta = buf - ptmp;
+
+      delta >>= 1;  /* immediate constant is #half-words */
+      vassert(delta > 0 && delta < (1 << 16));
+      s390_emit_BRC(ptmp, s390_cc_invert(cond), delta);
+   }
+
+   return buf;
+}
+
+
+/* Pseudo code:
+
+   guest_state[host_EvC_COUNTER] -= 1;
+   if (guest_state[host_EvC_COUNTER] >= 0) goto nofail;
+   goto guest_state[host_EvC_FAILADDR];
+   nofail: ;
+
+   The dispatch counter is a 32-bit value. */
+static UChar *
+s390_insn_evcheck_emit(UChar *buf, const s390_insn *insn)
+{
+   s390_amode *amode;
+   UInt b, x, d;
+   UChar *code_begin, *code_end;
+
+   code_begin = buf;
+
+   amode = insn->variant.evcheck.counter;
+   vassert(amode->tag == S390_AMODE_B12 || amode->tag == S390_AMODE_BX12);
+   b = hregNumber(amode->b);
+   x = hregNumber(amode->x);  /* 0 for B12 and B20 */
+   d = amode->d;
+
+   /* Decrement the dispatch counter in the guest state */
+   /* fixs390: ASI if available */
+   buf = s390_emit_LHI(buf, R0, -1);             /* 4 bytes */
+   buf = s390_emit_A(buf, R0, x, b, d);          /* 4 bytes */
+   buf = s390_emit_ST(buf, R0, x, b, d);         /* 4 bytes */
+
+   /* Jump over the next insn if >= 0 */
+   buf = s390_emit_BRC(buf, S390_CC_HE, (4 + 6 + 2) / 2);  /* 4 bytes */
+
+   /* Computed goto to fail_address */
+   amode = insn->variant.evcheck.fail_addr;
+   b = hregNumber(amode->b);
+   x = hregNumber(amode->x);  /* 0 for B12 and B20 */
+   d = amode->d;
+   buf = s390_emit_LG(buf, S390_REGNO_TCHAIN_SCRATCH, x, b, DISP20(d));  /* 6 bytes */
+   buf = s390_emit_BCR(buf, S390_CC_ALWAYS, S390_REGNO_TCHAIN_SCRATCH);  /* 2 bytes */
+
+   code_end = buf;
+   
+   /* Make sure the size of the generated code is identical to the size
+      returned by evCheckSzB_S390 */
+   vassert(evCheckSzB_S390() == code_end - code_begin);
+
+   return buf;
+}
+
+
+static UChar *
+s390_insn_profinc_emit(UChar *buf,
+                       const s390_insn *insn __attribute__((unused)))
+{
+   /* Generate a code template to increment a memory location whose
+      address will be known later as an immediate value. This code
+      template will be patched once the memory location is known.
+      For now we do this with address == 0. */
+   buf = s390_tchain_load64(buf, S390_REGNO_TCHAIN_SCRATCH, 0);
+   buf = s390_emit_LGHI(buf, R0, 1);
+   buf = s390_emit_AG( buf, R0, 0, S390_REGNO_TCHAIN_SCRATCH, DISP20(0));
+   buf = s390_emit_STG(buf, R0, 0, S390_REGNO_TCHAIN_SCRATCH, DISP20(0));
+
+   return buf;
+}
+
+
 Int
-emit_S390Instr(UChar *buf, Int nbuf, s390_insn *insn, Bool mode64,
-               void *dispatch_unassisted, void *dispatch_assisted)
+emit_S390Instr(Bool *is_profinc, UChar *buf, Int nbuf, s390_insn *insn,
+               Bool mode64, void *disp_cp_chain_me_to_slowEP,
+               void *disp_cp_chain_me_to_fastEP, void *disp_cp_xindir,
+               void *disp_cp_xassisted)
 {
    UChar *end;
 
@@ -7225,12 +7656,6 @@ emit_S390Instr(UChar *buf, Int nbuf, s390_insn *insn, Bool mode64,
       end = s390_insn_compare_emit(buf, insn);
       break;
 
-   case S390_INSN_BRANCH:
-      vassert(dispatch_unassisted == NULL);
-      vassert(dispatch_assisted == NULL);
-      end = s390_insn_branch_emit(buf, insn);
-      break;
-
    case S390_INSN_HELPER_CALL:
       end = s390_insn_helper_call_emit(buf, insn);
       break;
@@ -7283,6 +7708,30 @@ emit_S390Instr(UChar *buf, Int nbuf, s390_insn *insn, Bool mode64,
       end = s390_insn_gadd_emit(buf, insn);
       break;
 
+   case S390_INSN_PROFINC:
+      end = s390_insn_profinc_emit(buf, insn);
+      /* Tell the caller .. */
+      vassert(*is_profinc == False);
+      *is_profinc = True;
+      break;
+
+   case S390_INSN_EVCHECK:
+      end = s390_insn_evcheck_emit(buf, insn);
+      break;
+
+   case S390_INSN_XDIRECT:
+      end = s390_insn_xdirect_emit(buf, insn, disp_cp_chain_me_to_slowEP,
+                                   disp_cp_chain_me_to_fastEP);
+      break;
+
+   case S390_INSN_XINDIR:
+      end = s390_insn_xindir_emit(buf, insn, disp_cp_xindir);
+      break;
+
+   case S390_INSN_XASSISTED:
+      end = s390_insn_xassisted_emit(buf, insn, disp_cp_xassisted);
+      break;
+
    default:
       vpanic("emit_S390Instr");
    }
@@ -7293,6 +7742,166 @@ emit_S390Instr(UChar *buf, Int nbuf, s390_insn *insn, Bool mode64,
 }
 
 
+/* Return the number of bytes emitted for an S390_INSN_EVCHECK.
+   See s390_insn_evcheck_emit */
+Int
+evCheckSzB_S390(void)
+{
+   return 24;
+}
+
+
+/* Patch the counter address into CODE_TO_PATCH as previously
+   generated by s390_insn_profinc_emit. */
+VexInvalRange
+patchProfInc_S390(void *code_to_patch, ULong *location_of_counter)
+{
+   vassert(sizeof(ULong *) == 8);
+
+   s390_tchain_verify_load64(code_to_patch, S390_REGNO_TCHAIN_SCRATCH, 0);
+
+   s390_tchain_patch_load64(code_to_patch, Ptr_to_ULong(location_of_counter));
+
+   VexInvalRange vir = {0, 0};
+   return vir;
+}
+
+
+/* NB: what goes on here has to be very closely coordinated with the
+   s390_insn_xdirect_emit code above. */
+VexInvalRange
+chainXDirect_S390(void *place_to_chain,
+                  void *disp_cp_chain_me_EXPECTED,
+                  void *place_to_jump_to)
+{
+   /* What we're expecting to see @ PLACE_TI_CHAIN is:
+
+        load disp_cp_chain_me_EXPECTED into S390_REGNO_TCHAIN_SCRATCH
+        BASR  1,S390_REGNO_TCHAIN_SCRATCH
+   */
+   const UChar *next;
+   next = s390_tchain_verify_load64(place_to_chain, S390_REGNO_TCHAIN_SCRATCH,
+                                    Ptr_to_ULong(disp_cp_chain_me_EXPECTED));
+   vassert(next[0] == 0x0D);  // BASR  1,tchain_scratch
+   vassert(next[1] == (0x10 | S390_REGNO_TCHAIN_SCRATCH));
+
+   /* And what we want to change it to is either:
+        (general case):
+
+          load tchain_scratch, #place_to_jump_to
+          BR  *tchain_scratch
+
+      ---OR---
+
+        in the case where the displacement is small enough
+
+          BRCL delta       where delta is in half-words
+          invalid opcodes
+
+      In both cases the replacement has the same length as the original.
+      To remain sane & verifiable,
+      (1) limit the displacement for the short form to 
+          (say) +/- one billion, so as to avoid wraparound
+          off-by-ones
+      (2) even if the short form is applicable, once every (say)
+          1024 times use the long form anyway, so as to maintain
+          verifiability
+   */
+
+   /* This is the delta we need to put into a BRCL insn. Note, that the
+      offset in BRCL is in half-words. Hence division by 2. */
+   Long delta = (Long)((UChar *)place_to_jump_to - (UChar *)place_to_chain) / 2;
+   Bool shortOK = delta >= -1000*1000*1000 && delta < 1000*1000*1000;
+
+   static UInt shortCTR = 0; /* DO NOT MAKE NON-STATIC */
+   if (shortOK) {
+      shortCTR++; // thread safety bleh
+      if (0 == (shortCTR & 0x3FF)) {
+         shortOK = False;
+         if (0)
+            vex_printf("QQQ chainXDirect_S390: shortCTR = %u, "
+                       "using long jmp\n", shortCTR);
+      }
+   }
+
+   /* And make the modifications. */
+   UChar *p = (UChar *)place_to_chain;
+   if (shortOK) {
+      p = s390_emit_BRCL(p, S390_CC_ALWAYS, delta);  /* 6 bytes */
+
+      /* Fill remaining bytes with 0x00 (invalid opcode) */
+      Int i;
+      for (i = 0; i < s390_xdirect_patchable_len() - 6; ++i)
+         p[i] = 0x00;
+   } else {
+      /* Minimal modifications from the starting sequence. */   
+      p = s390_tchain_patch_load64(p, Ptr_to_ULong(place_to_jump_to));
+      /* Turn the call into a branch: BR *tchain_scratch */
+      s390_emit_BCR(p, S390_CC_ALWAYS, S390_REGNO_TCHAIN_SCRATCH);
+   }
+
+   VexInvalRange vir = {0, 0};
+   return vir;
+}
+
+
+/* NB: what goes on here has to be very closely coordinated with the
+   s390_insn_xdirect_emit code above. */
+VexInvalRange
+unchainXDirect_S390(void *place_to_unchain,
+                    void *place_to_jump_to_EXPECTED,
+                    void *disp_cp_chain_me)
+{
+   /* What we're expecting to see @ PLACE_TO_UNCHAIN:
+
+          load tchain_scratch, #place_to_jump_to_EXPECTED
+          BR  *tchain_scratch
+
+      ---OR---
+        in the case where the displacement falls within 32 bits
+
+          BRCL delta
+          invalid opcodes
+   */
+   UChar *p = place_to_unchain;
+   if (p[0] == 0xc0 && p[1] == (0x04 | S390_REGNO_TCHAIN_SCRATCH)) {
+      /* Looks like the short form */
+      Int num_hw = *(Int *)&p[2];
+      Int delta = 2 *num_hw;
+
+      vassert(p + delta == place_to_jump_to_EXPECTED);
+
+      p += 6;
+      Int i;
+      for (i = 0; i < s390_xdirect_patchable_len() - 6; ++i)
+         vassert(p[i] == 0x00);
+   } else {
+      /* Should be the long form */
+      const UChar *next;
+
+      next = s390_tchain_verify_load64(p, S390_REGNO_TCHAIN_SCRATCH,
+                                       Ptr_to_ULong(place_to_jump_to_EXPECTED));
+      /* Check for BR *tchain_scratch */
+      vassert(next[0] == 0x07 && /* BCR */
+              (next[1] == (0xF0 | S390_REGNO_TCHAIN_SCRATCH)));
+   }
+
+   /* And what we want to change it to is:
+
+        load  tchain_scratch, #disp_cp_chain_me
+        call *tchain_scratch
+   */
+   ULong addr = Ptr_to_ULong(disp_cp_chain_me);
+   p = s390_tchain_load64(p, S390_REGNO_TCHAIN_SCRATCH, addr);
+
+   /* call *tchain_scratch */
+   s390_emit_BASR(p, 1, S390_REGNO_TCHAIN_SCRATCH);
+
+   VexInvalRange vir = {0, 0};
+   return vir;
+}
+
 /*---------------------------------------------------------------*/
 /*--- end                                    host_s390_defs.c ---*/
 /*---------------------------------------------------------------*/
index 8b7548652c340815aa6e4f655b14b589c3d8915e..ad99c4ffcfdf8e6c73ca5ba9da37ad4cbf5e2bf0 100644 (file)
@@ -130,7 +130,6 @@ typedef enum {
    S390_INSN_TEST,   /* test operand and set cc */
    S390_INSN_CC2BOOL,/* convert condition code to 0/1 */
    S390_INSN_COMPARE,
-   S390_INSN_BRANCH, /* un/conditional goto */
    S390_INSN_HELPER_CALL,
    S390_INSN_CAS,    /* compare and swap */
    S390_INSN_BFP_BINOP, /* Binary floating point 32-bit / 64-bit */
@@ -144,7 +143,13 @@ typedef enum {
    S390_INSN_BFP128_CONVERT_FROM,
    S390_INSN_MFENCE,
    S390_INSN_GZERO,   /* Assign zero to a guest register */
-   S390_INSN_GADD     /* Add a value to a guest register */
+   S390_INSN_GADD,    /* Add a value to a guest register */
+   /* The following 5 insns are mandated by translation chaining */
+   S390_INSN_XDIRECT,     /* direct transfer to guest address */
+   S390_INSN_XINDIR,      /* indirect transfer to guest address */
+   S390_INSN_XASSISTED,   /* assisted transfer to guest address */
+   S390_INSN_EVCHECK,     /* Event check */
+   S390_INSN_PROFINC      /* 64-bit profile counter increment */
 } s390_insn_tag;
 
 
@@ -338,11 +343,6 @@ typedef struct {
          HReg        op3;
          HReg        old_mem;
       } cas;
-      struct {
-         IRJumpKind    kind;
-         s390_cc_t     cond;
-         s390_opnd_RMI dst;
-      } branch;
       /* Pseudo-insn for representing a helper call.
          TARGET is the absolute address of the helper function
          NUM_ARGS says how many arguments are being passed.
@@ -407,6 +407,44 @@ typedef struct {
          UChar            delta;
          ULong            value;  /* for debugging only */
       } gadd;
+
+      /* The next 5 entries are generic to support translation chaining */
+
+      /* Update the guest IA value, then exit requesting to chain
+         to it.  May be conditional. */
+      struct {
+         s390_cc_t     cond;
+         Bool          to_fast_entry;  /* chain to the what entry point? */
+         Addr64        dst;            /* next guest address */
+         s390_amode   *guest_IA;
+      } xdirect;
+      /* Boring transfer to a guest address not known at JIT time.
+         Not chainable.  May be conditional. */
+      struct {
+         s390_cc_t     cond;
+         HReg          dst;
+         s390_amode   *guest_IA;
+      } xindir;
+      /* Assisted transfer to a guest address, most general case.
+         Not chainable.  May be conditional. */
+      struct {
+         s390_cc_t     cond;
+         IRJumpKind    kind;
+         HReg          dst;
+         s390_amode   *guest_IA;
+      } xassisted;
+      struct {
+         /* fixs390: I don't think these are really needed
+            as the gsp and the offset are fixed  no ? */
+         s390_amode   *counter;    /* dispatch counter */
+         s390_amode   *fail_addr;
+      } evcheck;
+      struct {
+         /* No fields.  The address of the counter to increment is
+            installed later, post-translation, by patching it in,
+            as it is not known at translation time. */
+      } profinc;
+
    } variant;
 } s390_insn;
 
@@ -433,7 +471,6 @@ s390_insn *s390_insn_cc2bool(HReg dst, s390_cc_t src);
 s390_insn *s390_insn_test(UChar size, s390_opnd_RMI src);
 s390_insn *s390_insn_compare(UChar size, HReg dst, s390_opnd_RMI opnd,
                              Bool signed_comparison);
-s390_insn *s390_insn_branch(IRJumpKind jk, s390_cc_t cond, s390_opnd_RMI dst);
 s390_insn *s390_insn_helper_call(s390_cc_t cond, Addr64 target, UInt num_args,
                                  HChar *name);
 s390_insn *s390_insn_bfp_triop(UChar size, s390_bfp_triop_t, HReg dst, HReg op2,
@@ -460,6 +497,15 @@ s390_insn *s390_insn_mfence(void);
 s390_insn *s390_insn_gzero(UChar size, UInt offset);
 s390_insn *s390_insn_gadd(UChar size, UInt offset, UChar delta, ULong value);
 
+/* Five for translation chaining */
+s390_insn *s390_insn_xdirect(s390_cc_t cond, Addr64 dst, s390_amode *guest_IA,
+                             Bool to_fast_entry);
+s390_insn *s390_insn_xindir(s390_cc_t cond, HReg dst, s390_amode *guest_IA);
+s390_insn *s390_insn_xassisted(s390_cc_t cond, HReg dst, s390_amode *guest_IA,
+                               IRJumpKind kind);
+s390_insn *s390_insn_evcheck(s390_amode *counter, s390_amode *fail_addr);
+s390_insn *s390_insn_profinc(void);
+
 const HChar *s390_insn_as_string(const s390_insn *);
 
 /*--------------------------------------------------------*/
@@ -475,13 +521,30 @@ void ppHRegS390(HReg);
 void  getRegUsage_S390Instr( HRegUsage *, s390_insn *, Bool );
 void  mapRegs_S390Instr    ( HRegRemap *, s390_insn *, Bool );
 Bool  isMove_S390Instr     ( s390_insn *, HReg *, HReg * );
-Int   emit_S390Instr       ( UChar *, Int, s390_insn *, Bool,
-                             void *, void * );
+Int   emit_S390Instr       ( Bool *, UChar *, Int, s390_insn *, Bool,
+                             void *, void *, void *, void *);
 void  getAllocableRegs_S390( Int *, HReg **, Bool );
 void  genSpill_S390        ( HInstr **, HInstr **, HReg , Int , Bool );
 void  genReload_S390       ( HInstr **, HInstr **, HReg , Int , Bool );
 s390_insn *directReload_S390 ( s390_insn *, HReg, Short );
-HInstrArray *iselSB_S390   ( IRSB *, VexArch, VexArchInfo *, VexAbiInfo * );
+HInstrArray *iselSB_S390   ( IRSB *, VexArch, VexArchInfo *, VexAbiInfo *,
+                             Int, Int, Bool, Bool, Addr64);
+
+/* Return the number of bytes of code needed for an event check */
+Int evCheckSzB_S390(void);
+
+/* Perform a chaining and unchaining of an XDirect jump. */
+VexInvalRange chainXDirect_S390(void *place_to_chain,
+                                void *disp_cp_chain_me_EXPECTED,
+                                void *place_to_jump_to);
+
+VexInvalRange unchainXDirect_S390(void *place_to_unchain,
+                                  void *place_to_jump_to_EXPECTED,
+                                  void *disp_cp_chain_me);
+
+/* Patch the counter location into an existing ProfInc point. */
+VexInvalRange patchProfInc_S390(void  *code_to_patch,
+                                ULong *location_of_counter);
 
 /* KLUDGE: See detailled comment in host_s390_defs.c. */
 extern const VexArchInfo *s390_archinfo_host;
index a2217d4b8a12d647bef2dd8fe1cc6ba761a1dc2b..6c63cb3cf28f9e2d670152c6ea3ed0aa43304acf 100644 (file)
     - The host subarchitecture we are selecting insns for.
       This is set at the start and does not change.
 
+   - A Bool for indicating whether we may generate chain-me
+     instructions for control flow transfers, or whether we must use
+     XAssisted.
+
+   - The maximum guest address of any guest insn in this block.
+     Actually, the address of the highest-addressed byte from any insn
+     in this block.  Is set at the start and does not change.  This is
+     used for detecting jumps which are definitely forward-edges from
+     this block, and therefore can be made (chained) to the fast entry
+     point of the destination, thereby avoiding the destination's
+     event check.
+
     - A flag to indicate whether the guest IA has been assigned to.
 
     - Values of certain guest registers which are often assigned constants.
@@ -92,16 +104,19 @@ enum {
 typedef struct {
    IRTypeEnv   *type_env;
 
+   HInstrArray *code;
    HReg        *vregmap;
    HReg        *vregmapHI;
    UInt         n_vregmap;
-
-   HInstrArray *code;
+   UInt         vreg_ctr;
+   UInt         hwcaps;
 
    ULong        old_value[NUM_TRACKED_REGS];
-   UInt         vreg_ctr;
 
-   UInt         hwcaps;
+   /* The next two are for translation chaining */
+   Addr64       max_ga;
+   Bool         chaining_allowed;
+
    Bool         first_IA_assignment;
    Bool         old_value_valid[NUM_TRACKED_REGS];
 } ISelEnv;
@@ -2437,17 +2452,53 @@ s390_isel_stmt(ISelEnv *env, IRStmt *stmt)
 
       /* --------- EXIT --------- */
    case Ist_Exit: {
-      s390_opnd_RMI dst;
       s390_cc_t cond;
       IRConstTag tag = stmt->Ist.Exit.dst->tag;
 
       if (tag != Ico_U64)
          vpanic("s390_isel_stmt: Ist_Exit: dst is not a 64-bit value");
 
-      dst  = s390_isel_int_expr_RMI(env, IRExpr_Const(stmt->Ist.Exit.dst));
+      s390_amode *guest_IA = s390_amode_for_guest_state(stmt->Ist.Exit.offsIP);
       cond = s390_isel_cc(env, stmt->Ist.Exit.guard);
-      addInstr(env, s390_insn_branch(stmt->Ist.Exit.jk, cond, dst));
-      return;
+
+      /* Case: boring transfer to known address */
+      if (stmt->Ist.Exit.jk == Ijk_Boring) {
+         if (env->chaining_allowed) {
+            /* .. almost always true .. */
+            /* Skip the event check at the dst if this is a forwards
+               edge. */
+            Bool to_fast_entry
+               = ((Addr64)stmt->Ist.Exit.dst->Ico.U64) > env->max_ga;
+            if (0) vex_printf("%s", to_fast_entry ? "Y" : ",");
+            addInstr(env, s390_insn_xdirect(cond, stmt->Ist.Exit.dst->Ico.U64,
+                                            guest_IA, to_fast_entry));
+         } else {
+            /* .. very occasionally .. */
+            /* We can't use chaining, so ask for an assisted transfer,
+               as that's the only alternative that is allowable. */
+            HReg dst = s390_isel_int_expr(env,
+                                          IRExpr_Const(stmt->Ist.Exit.dst));
+            addInstr(env, s390_insn_xassisted(cond, dst, guest_IA, Ijk_Boring));
+         }
+         return;
+      }
+
+      /* Case: assisted transfer to arbitrary address */
+      switch (stmt->Ist.Exit.jk) {
+      case Ijk_SigSEGV:
+      case Ijk_TInval:
+      case Ijk_EmWarn: {
+         HReg dst = s390_isel_int_expr(env, IRExpr_Const(stmt->Ist.Exit.dst));
+         addInstr(env, s390_insn_xassisted(cond, dst, guest_IA,
+                                           stmt->Ist.Exit.jk));
+         return;
+      }
+      default:
+         break;
+      }
+
+      /* Do we ever expect to see any other kind? */
+      goto stmt_fail;
    }
 
       /* --------- MEM FENCE --------- */
@@ -2484,20 +2535,79 @@ s390_isel_stmt(ISelEnv *env, IRStmt *stmt)
 /*---------------------------------------------------------*/
 
 static void
-iselNext(ISelEnv *env, IRExpr *next, IRJumpKind jk)
+iselNext(ISelEnv *env, IRExpr *next, IRJumpKind jk, int offsIP)
 {
-   s390_opnd_RMI dst;
-
    if (vex_traceflags & VEX_TRACE_VCODE) {
-      vex_printf("\n-- goto {");
-      ppIRJumpKind(jk);
-      vex_printf("} ");
+      vex_printf("\n-- PUT(%d) = ", offsIP);
       ppIRExpr(next);
+      vex_printf("; exit-");
+      ppIRJumpKind(jk);
       vex_printf("\n");
    }
 
-   dst = s390_isel_int_expr_RMI(env, next);
-   addInstr(env, s390_insn_branch(jk, S390_CC_ALWAYS, dst));
+   s390_amode *guest_IA = s390_amode_for_guest_state(offsIP);
+
+   /* Case: boring transfer to known address */
+   if (next->tag == Iex_Const) {
+      IRConst *cdst = next->Iex.Const.con;
+      vassert(cdst->tag == Ico_U64);
+      if (jk == Ijk_Boring || jk == Ijk_Call) {
+         /* Boring transfer to known address */
+         if (env->chaining_allowed) {
+            /* .. almost always true .. */
+            /* Skip the event check at the dst if this is a forwards
+               edge. */
+            Bool to_fast_entry
+               = ((Addr64)cdst->Ico.U64) > env->max_ga;
+            if (0) vex_printf("%s", to_fast_entry ? "X" : ".");
+            addInstr(env, s390_insn_xdirect(S390_CC_ALWAYS, cdst->Ico.U64,
+                                            guest_IA, to_fast_entry));
+         } else {
+            /* .. very occasionally .. */
+            /* We can't use chaining, so ask for an indirect transfer,
+               as that's the cheapest alternative that is allowable. */
+            HReg dst = s390_isel_int_expr(env, next);
+            addInstr(env, s390_insn_xassisted(S390_CC_ALWAYS, dst, guest_IA,
+                                              Ijk_Boring));
+         }
+         return;
+      }
+   }
+
+   /* Case: call/return (==boring) transfer to any address */
+   switch (jk) {
+   case Ijk_Boring:
+   case Ijk_Ret:
+   case Ijk_Call: {
+      HReg dst = s390_isel_int_expr(env, next);
+      if (env->chaining_allowed) {
+         addInstr(env, s390_insn_xindir(S390_CC_ALWAYS, dst, guest_IA));
+      } else {
+         addInstr(env, s390_insn_xassisted(S390_CC_ALWAYS, dst, guest_IA,
+                                           Ijk_Boring));
+      }
+      return;
+   }
+   default:
+      break;
+   }
+
+   /* Case: some other kind of transfer to any address */
+   switch (jk) {
+   case Ijk_Sys_syscall:
+   case Ijk_ClientReq:
+   case Ijk_NoRedir:
+   case Ijk_Yield:
+   case Ijk_SigTRAP: {
+      HReg dst = s390_isel_int_expr(env, next);
+      addInstr(env, s390_insn_xassisted(S390_CC_ALWAYS, dst, guest_IA, jk));
+      return;
+   }
+   default:
+      break;
+   }
+
+   vpanic("iselNext");
 }
 
 
@@ -2509,7 +2619,9 @@ iselNext(ISelEnv *env, IRExpr *next, IRJumpKind jk)
 
 HInstrArray *
 iselSB_S390(IRSB *bb, VexArch arch_host, VexArchInfo *archinfo_host,
-             VexAbiInfo *vbi)
+            VexAbiInfo *vbi, Int offset_host_evcheck_counter,
+            Int offset_host_evcheck_fail_addr, Bool chaining_allowed,
+            Bool add_profinc, Addr64 max_ga)
 {
    UInt     i, j;
    HReg     hreg, hregHI;
@@ -2552,6 +2664,9 @@ iselSB_S390(IRSB *bb, VexArch arch_host, VexArchInfo *archinfo_host,
    /* and finally ... */
    env->hwcaps    = hwcaps_host;
 
+   env->max_ga = max_ga;
+   env->chaining_allowed = chaining_allowed;
+
    /* For each IR temporary, allocate a suitably-kinded virtual
       register. */
    j = 0;
@@ -2595,12 +2710,26 @@ iselSB_S390(IRSB *bb, VexArch arch_host, VexArchInfo *archinfo_host,
    }
    env->vreg_ctr = j;
 
+   /* The very first instruction must be an event check. */
+   s390_amode *counter, *fail_addr;
+   counter   = s390_amode_for_guest_state(offset_host_evcheck_counter);
+   fail_addr = s390_amode_for_guest_state(offset_host_evcheck_fail_addr);
+   addInstr(env, s390_insn_evcheck(counter, fail_addr));
+
+   /* Possibly a block counter increment (for profiling).  At this
+      point we don't know the address of the counter, so just pretend
+      it is zero.  It will have to be patched later, but before this
+      translation is used, by a call to LibVEX_patchProfInc. */
+   if (add_profinc) {
+      addInstr(env, s390_insn_profinc());
+   }
+
    /* Ok, finally we can iterate over the statements. */
    for (i = 0; i < bb->stmts_used; i++)
       if (bb->stmts[i])
          s390_isel_stmt(env, bb->stmts[i]);
 
-   iselNext(env, bb->next, bb->jumpkind);
+   iselNext(env, bb->next, bb->jumpkind, bb->offsIP);
 
    /* Record the number of vregs we used. */
    env->code->n_vregs = env->vreg_ctr;
index 521e63caf47e0e906515cfcc575ebddae203938a..b0f03dea69a502e16829326c2dd0b5d478c850f0 100644 (file)
@@ -358,7 +358,7 @@ VexTranslateResult LibVEX_Translate ( VexTranslateArgs* vta )
          vassert(vta->dispatch_unassisted == NULL);
          vassert(vta->dispatch_assisted == NULL);
          break;
-
+#endif
       case VexArchS390X:
          mode64      = True;
          getAllocableRegs_S390 ( &n_available_real_regs,
@@ -371,16 +371,13 @@ VexTranslateResult LibVEX_Translate ( VexTranslateArgs* vta )
          ppInstr     = (void(*)(HInstr*, Bool)) ppS390Instr;
          ppReg       = (void(*)(HReg)) ppHRegS390;
          iselSB      = iselSB_S390;
-         emit        = (Int(*)(UChar*,Int,HInstr*,Bool,void*,void*))
-                       emit_S390Instr;
+         emit        = (Int(*)(Bool*,UChar*,Int,HInstr*,Bool,
+                               void*,void*,void*,void*)) emit_S390Instr;
          host_is_bigendian = True;
          host_word_type    = Ity_I64;
          vassert(are_valid_hwcaps(VexArchS390X, vta->archinfo_host.hwcaps));
-         /* return-to-dispatcher scheme */
-         vassert(vta->dispatch_unassisted == NULL);
-         vassert(vta->dispatch_assisted == NULL);
          break;
-#endif
+
       case VexArchARM:
          mode64      = False;
          getAllocableRegs_ARM ( &n_available_real_regs,
@@ -480,7 +477,7 @@ VexTranslateResult LibVEX_Translate ( VexTranslateArgs* vta )
          vassert(sizeof( ((VexGuestPPC64State*)0)->guest_NRADDR     ) == 8);
          vassert(sizeof( ((VexGuestPPC64State*)0)->guest_NRADDR_GPR2) == 8);
          break;
-
+#endif
       case VexArchS390X:
          preciseMemExnsFn = guest_s390x_state_requires_precise_mem_exns;
          disInstrFn       = disInstr_S390;
@@ -490,13 +487,17 @@ VexTranslateResult LibVEX_Translate ( VexTranslateArgs* vta )
          guest_layout     = &s390xGuest_layout;
          offB_TISTART     = offsetof(VexGuestS390XState,guest_TISTART);
          offB_TILEN       = offsetof(VexGuestS390XState,guest_TILEN);
+         offB_GUEST_IP          = offsetof(VexGuestS390XState,guest_IA);
+         szB_GUEST_IP           = sizeof( ((VexGuestS390XState*)0)->guest_IA);
+         offB_HOST_EvC_COUNTER  = offsetof(VexGuestS390XState,host_EvC_COUNTER);
+         offB_HOST_EvC_FAILADDR = offsetof(VexGuestS390XState,host_EvC_FAILADDR);
          vassert(are_valid_hwcaps(VexArchS390X, vta->archinfo_guest.hwcaps));
          vassert(0 == sizeof(VexGuestS390XState) % 16);
          vassert(sizeof( ((VexGuestS390XState*)0)->guest_TISTART    ) == 8);
          vassert(sizeof( ((VexGuestS390XState*)0)->guest_TILEN      ) == 8);
          vassert(sizeof( ((VexGuestS390XState*)0)->guest_NRADDR     ) == 8);
          break;
-#endif
+
       case VexArchARM:
          preciseMemExnsFn       = guest_arm_state_requires_precise_mem_exns;
          disInstrFn             = disInstr_ARM;
@@ -827,6 +828,8 @@ VexInvalRange LibVEX_Chain ( VexArch arch_host,
          chainXDirect = chainXDirect_AMD64; break;
       case VexArchARM:
          chainXDirect = chainXDirect_ARM; break;
+      case VexArchS390X:
+         chainXDirect = chainXDirect_S390; break;
       default:
          vassert(0);
    }
@@ -850,6 +853,8 @@ VexInvalRange LibVEX_UnChain ( VexArch arch_host,
          unchainXDirect = unchainXDirect_AMD64; break;
       case VexArchARM:
          unchainXDirect = unchainXDirect_ARM; break;
+      case VexArchS390X:
+         unchainXDirect = unchainXDirect_S390; break;
       default:
          vassert(0);
    }
@@ -871,6 +876,8 @@ Int LibVEX_evCheckSzB ( VexArch arch_host )
             cached = evCheckSzB_AMD64(); break;
          case VexArchARM:
             cached = evCheckSzB_ARM(); break;
+         case VexArchS390X:
+            cached = evCheckSzB_S390(); break;
          default:
             vassert(0);
       }
@@ -890,6 +897,8 @@ VexInvalRange LibVEX_PatchProfInc ( VexArch arch_host,
          patchProfInc = patchProfInc_AMD64; break;
       case VexArchARM:
          patchProfInc = patchProfInc_ARM; break;
+      case VexArchS390X:
+         patchProfInc = patchProfInc_S390; break;
       default:
          vassert(0);
    }
index 3bbeaf2b37d0534f81c693f2e9f19fedd1161cb3..84d8bdcdea4b070da222d8c73cd3334396887238 100644 (file)
@@ -144,10 +144,14 @@ typedef struct {
    /* Emulation warnings; see comments in libvex_emwarn.h */
    /*  416 */  UInt guest_EMWARN;
 
+   /* For translation chaining */
+   /*  420 */  UInt  host_EvC_COUNTER;
+   /*  424 */  ULong host_EvC_FAILADDR;
+
 /*------------------------------------------------------------*/
 /*--- Force alignment to 16 bytes                          ---*/
 /*------------------------------------------------------------*/
-   /*  420 */  UChar padding[12];
+   /* No padding needed */
 
    /*  432 */  /* This is the size of the guest state */
 } VexGuestS390XState;
index 95efef6b5a7a274902ea0b5acb946834681307bd..6d8ef5c3f836babb0c3fcd97d3886b5690f1aa9b 100644 (file)
@@ -1,3 +1,4 @@
+/* -*- mode: C; c-basic-offset: 3; -*- */
 
 /*--------------------------------------------------------------------*/
 /*--- Common defs for s390x                  libvex_s390x_common.h ---*/
@@ -27,8 +28,6 @@
    The GNU General Public License is contained in the file COPYING.
 */
 
-/* -*- mode: C; c-basic-offset: 3; -*- */
-
 #ifndef __LIBVEX_PUB_S390X_H
 #define __LIBVEX_PUB_S390X_H
 
@@ -42,7 +41,7 @@
 /*--------------------------------------------------------------*/
 
 #define S390_REGNO_RETURN_VALUE         2
-#define S390_REGNO_DISPATCH_CTR        12   /* Holds VG_(dispatch_ctr) */
+#define S390_REGNO_TCHAIN_SCRATCH      12
 #define S390_REGNO_GUEST_STATE_POINTER 13
 #define S390_REGNO_LINK_REGISTER       14
 #define S390_REGNO_STACK_POINTER       15
@@ -52,7 +51,7 @@
 /*--- Offsets in the stack frame allocated by the dispatcher ---*/
 /*--------------------------------------------------------------*/
 
-/* Where the profiling dispatcher saves the r2 contents. */
+/* Where the dispatcher saves the r2 contents. */
 #define S390_OFFSET_SAVED_R2 160+96
 
 /* Where client's FPC register is saved. */
 /* Number of double words needed to store all facility bits. */
 #define S390_NUM_FACILITY_DW 2
 
+/* The length of the instructions issued by s390_tchain_load64 */
+#define S390_TCHAIN_LOAD64_LEN 16
+
+/* The length of the call insn (BASR) used in translation chaining */
+#define S390_TCHAIN_CALL_LEN  2
+
 #endif /* __LIBVEX_PUB_S390X_H */
 
 /*--------------------------------------------------------------------*/