blob: e62240c1a79c793a7b9a959e546e4fc24899374a [file] [log] [blame]
Verification todo
~~~~~~~~~~~~~~~~~
check that illegal insns on all targets don't cause the _toIR.c's to
assert. [DONE: amd64 x86 ppc32 ppc64 arm s390]
check also with --vex-guest-chase-cond=yes
check that all targets can run their insn set tests with
--vex-guest-max-insns=1.
all targets: run some tests using --profile-flags=... to exercise
function patchProfInc_<arch> [DONE: amd64 x86 ppc32 ppc64 arm s390]
figure out if there is a way to write a test program that checks
that event checks are actually getting triggered
Cleanups
~~~~~~~~
host_arm_isel.c and host_arm_defs.c: get rid of global var arm_hwcaps.
host_x86_defs.c, host_amd64_defs.c: return proper VexInvalRange
records from the patchers, instead of {0,0}, so that transparent
self hosting works properly.
host_ppc_defs.h: is RdWrLR still needed? If not delete.
ditto ARM, Ld8S
Comments that used to be in m_scheduler.c:
tchaining tests:
- extensive spinrounds
- with sched quantum = 1 -- check that handle_noredir_jump
doesn't return with INNER_COUNTERZERO
other:
- out of date comment w.r.t. bit 0 set in libvex_trc_values.h
- can VG_TRC_BORING still happen? if not, rm
- memory leaks in m_transtab (InEdgeArr/OutEdgeArr leaking?)
- move do_cacheflush out of m_transtab
- more economical unchaining when nuking an entire sector
- ditto w.r.t. cache flushes
- verify case of 2 paths from A to B
- check -- is IP_AT_SYSCALL still right?
Optimisations
~~~~~~~~~~~~~
ppc: chain_XDirect: generate short form jumps when possible
ppc64: immediate generation is terrible .. should be able
to do better
arm codegen: Generate ORRS for CmpwNEZ32(Or32(x,y))
all targets: when nuking an entire sector, don't bother to undo the
patching for any translations within the sector (nor with their
invalidations).
(somewhat implausible) for jumps to disp_cp_indir, have multiple
copies of disp_cp_indir, one for each of the possible registers that
could have held the target guest address before jumping to the stub.
Then disp_cp_indir wouldn't have to reload it from memory each time.
Might also have the effect of spreading out the indirect mispredict
burden somewhat (across the multiple copies.)
Implementation notes
~~~~~~~~~~~~~~~~~~~~
T-chaining changes -- summary
* The code generators (host_blah_isel.c, host_blah_defs.[ch]) interact
more closely with Valgrind than before. In particular the
instruction selectors must use one of 3 different kinds of
control-transfer instructions: XDirect, XIndir and XAssisted.
All archs must use these the same; no more ad-hoc control transfer
instructions.
(more detail below)
* With T-chaining, translations can jump between each other without
going through the dispatcher loop every time. This means that the
event check (counter dec, and exit if negative) the dispatcher loop
previously did now needs to be compiled into each translation.
* The assembly dispatcher code (dispatch-arch-os.S) is still
present. It still provides table lookup services for
indirect branches, but it also provides a new feature:
dispatch points, to which the generated code jumps. There
are 5:
VG_(disp_cp_chain_me_to_slowEP):
VG_(disp_cp_chain_me_to_fastEP):
These are chain-me requests, used for Boring conditional and
unconditional jumps to destinations known at JIT time. The
generated code calls these (doesn't jump to them) and the
stub recovers the return address. These calls never return;
instead the call is done so that the stub knows where the
calling point is. It needs to know this so it can patch
the calling point to the requested destination.
VG_(disp_cp_xindir):
Old-style table lookup and go; used for indirect jumps
VG_(disp_cp_xassisted):
Most general and slowest kind. Can transfer to anywhere, but
first returns to scheduler to do some other event (eg a syscall)
before continuing.
VG_(disp_cp_evcheck_fail):
Code jumps here when the event check fails.
* new instructions in backends: XDirect, XIndir and XAssisted.
XDirect is used for chainable jumps. It is compiled into a
call to VG_(disp_cp_chain_me_to_slowEP) or
VG_(disp_cp_chain_me_to_fastEP).
XIndir is used for indirect jumps. It is compiled into a jump
to VG_(disp_cp_xindir)
XAssisted is used for "assisted" (do something first, then jump)
transfers. It is compiled into a jump to VG_(disp_cp_xassisted)
All 3 of these may be conditional.
More complexity: in some circumstances (no-redir translations)
all transfers must be done with XAssisted. In such cases the
instruction selector will be told this.
* Patching: XDirect is compiled basically into
%r11 = &VG_(disp_cp_chain_me_to_{slow,fast}EP)
call *%r11
Backends must provide a function (eg) chainXDirect_AMD64
which converts it into a jump to a specified destination
jmp $delta-of-PCs
or
%r11 = 64-bit immediate
jmpq *%r11
depending on branch distance.
Backends must provide a function (eg) unchainXDirect_AMD64
which restores the original call-to-the-stub version.
* Event checks. Each translation now has two entry points,
the slow one (slowEP) and fast one (fastEP). Like this:
slowEP:
counter--
if (counter < 0) goto VG_(disp_cp_evcheck_fail)
fastEP:
(rest of the translation)
slowEP is used for control flow transfers that are or might be
a back edge in the control flow graph. Insn selectors are
given the address of the highest guest byte in the block so
they can determine which edges are definitely not back edges.
The counter is placed in the first 8 bytes of the guest state,
and the address of VG_(disp_cp_evcheck_fail) is placed in
the next 8 bytes. This allows very compact checks on all
targets, since no immediates need to be synthesised, eg:
decq 0(%baseblock-pointer)
jns fastEP
jmpq *8(baseblock-pointer)
fastEP:
On amd64 a non-failing check is therefore 2 insns; all 3 occupy
just 8 bytes.
On amd64 the event check is created by a special single
pseudo-instruction AMD64_EvCheck.
* BB profiling (for --profile-flags=). The dispatch assembly
dispatch-arch-os.S no longer deals with this and so is much
simplified. Instead the profile inc is compiled into each
translation, as the insn immediately following the event
check. Again, on amd64 a pseudo-insn AMD64_ProfInc is used.
Counters are now 64 bit even on 32 bit hosts, to avoid overflow.
One complexity is that at JIT time it is not known where the
address of the counter is. To solve this, VexTranslateResult
now returns the offset of the profile inc in the generated
code. When the counter address is known, VEX can be called
again to patch it in. Backends must supply eg
patchProfInc_AMD64 to make this happen.
* Front end changes (guest_blah_toIR.c)
The way the guest program counter is handled has changed
significantly. Previously, the guest PC was updated (in IR)
at the start of each instruction, except for the first insn
in an IRSB. This is inconsistent and doesn't work with the
new framework.
Now, each instruction must update the guest PC as its last
IR statement -- not its first. And no special exemption for
the first insn in the block. As before most of these are
optimised out by ir_opt, so no concerns about efficiency.
As a logical side effect of this, exits (IRStmt_Exit) and the
block-end transfer are both considered to write to the guest state
(the guest PC) and so need to be told the offset of it.
IR generators (eg disInstr_AMD64) are no longer allowed to set the
IRSB::next, to specify the block-end transfer address. Instead they
now indicate, to the generic steering logic that drives them (iow,
guest_generic_bb_to_IR.c), that the block has ended. This then
generates effectively "goto GET(PC)" (which, again, is optimised
away). What this does mean is that if the IR generator function
ends the IR of the last instruction in the block with an incorrect
assignment to the guest PC, execution will transfer to an incorrect
destination -- making the error obvious quickly.