|
From: <sv...@va...> - 2012-04-13 04:04:15
|
florian 2012-04-13 05:04:06 +0100 (Fri, 13 Apr 2012)
New Revision: 2279
Log:
Translation chaining for s390. To be debugged.
Modified files:
branches/TCHAIN/priv/guest_s390_defs.h
branches/TCHAIN/priv/guest_s390_helpers.c
branches/TCHAIN/priv/guest_s390_toIR.c
branches/TCHAIN/priv/host_s390_defs.c
branches/TCHAIN/priv/host_s390_defs.h
branches/TCHAIN/priv/host_s390_isel.c
branches/TCHAIN/priv/main_main.c
branches/TCHAIN/pub/libvex_guest_s390x.h
branches/TCHAIN/pub/libvex_s390x_common.h
Modified: branches/TCHAIN/priv/host_s390_defs.h (+74 -11)
===================================================================
--- branches/TCHAIN/priv/host_s390_defs.h 2012-04-12 22:05:16 +01:00 (rev 2278)
+++ branches/TCHAIN/priv/host_s390_defs.h 2012-04-13 05:04:06 +01:00 (rev 2279)
@@ -130,7 +130,6 @@
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 @@
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 @@
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 @@
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_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_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,14 +521,31 @@
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;
Modified: branches/TCHAIN/priv/host_s390_defs.c (+786 -177)
===================================================================
--- branches/TCHAIN/priv/host_s390_defs.c 2012-04-12 22:05:16 +01:00 (rev 2278)
+++ branches/TCHAIN/priv/host_s390_defs.c 2012-04-13 05:04:06 +01:00 (rev 2279)
@@ -118,7 +118,7 @@
/* 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 @@
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 @@
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_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 @@
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_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 @@
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");
}
@@ -1403,6 +1435,16 @@
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)
{
if (UNLIKELY(vex_traceflags & VEX_TRACE_ASM))
@@ -4252,21 +4294,6 @@
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 *
+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 @@
}
+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 @@
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 @@
&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 @@
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;
}
@@ -6507,104 +6650,6 @@
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)
{
s390_cc_t cond;
@@ -7158,9 +7203,395 @@
}
+/* 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 @@
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 @@
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 @@
}
+/* 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 ---*/
/*---------------------------------------------------------------*/
Modified: branches/TCHAIN/priv/guest_s390_toIR.c (+109 -62)
===================================================================
--- branches/TCHAIN/priv/guest_s390_toIR.c 2012-04-12 22:05:16 +01:00 (rev 2278)
+++ branches/TCHAIN/priv/guest_s390_toIR.c 2012-04-13 05:04:06 +01:00 (rev 2279)
@@ -120,6 +120,13 @@
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 @@
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 @@
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 @@
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 @@
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 @@
{
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 @@
{
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
/* 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 @@
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 @@
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_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 @@
/* 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 @@
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_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 @@
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 @@
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 @@
guest_IA_curr_instr);
put_counter_dw0(mkU64(0));
+ dummy_put_IA();
return "tr";
}
@@ -13518,14 +13566,6 @@
}
-/* 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 @@
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 @@
/* 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)));
+ not been executed, and (is currently) the next to be executed. */
+ put_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;
+ 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 @@
DisResult
disInstr_S390(IRSB *irsb_IN,
- Bool put_IP __attribute__((unused)),
Bool (*resteerOkFn)(void *, Addr64),
Bool resteerCisOk,
void *callback_opaque,
@@ -13610,10 +13661,6 @@
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);
}
Modified: branches/TCHAIN/priv/host_s390_isel.c (+147 -18)
===================================================================
--- branches/TCHAIN/priv/host_s390_isel.c 2012-04-12 22:05:16 +01:00 (rev 2278)
+++ branches/TCHAIN/priv/host_s390_isel.c 2012-04-13 05:04:06 +01:00 (rev 2279)
@@ -69,6 +69,18 @@
- 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 @@
typedef struct {
IRTypeEnv *type_env;
+ HInstrArray *code;
HReg *vregmap;
HReg *vregmapHI;
UInt n_vregmap;
+ UInt vreg_ctr;
+ UInt hwcaps;
- HInstrArray *code;
-
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 @@
/* --------- 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 @@
/*---------------------------------------------------------*/
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 {");
+ vex_printf("\n-- PUT(%d) = ", offsIP);
+ ppIRExpr(next);
+ vex_printf("; exit-");
ppIRJumpKind(jk);
- vex_printf("} ");
- ppIRExpr(next);
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 {
+ ...
[truncated message content] |