Gotchas
Yk has some unusual implications, requirements, and limitations that interpreter authors should be aware of.
Trace quality depends upon completeness of interpreter IR.
Yk works by recording traces (i.e. individual control flow paths) of your
interpreter's implementation. Each trace ends up as an ordered list of LLVM IR
blocks which are "stitched together" and compiled. Unless callees are marked
yk_noinline
, the JIT will seek to inline them into the trace because,
generally speaking, the more the JIT can inline, the more optimal the JITted
code will be.
In order to inline a function call, the JIT needs to have LLVM IR for the
callee. Yk uses fat LTO to collect (and embed into the resulting binary) a
"full-program" IR for your interpreter. yk-config
provides the relevant clang
flags to make this happen. You should make sure that the build system of your
interpreter uses the relevant flags. Namely yk-config --cppflgs --cflags
for
compiling C code, and yk-config --ldflags --libs
for linking.
It follows that shared objects, which at the time of writing cannot take part
in LTO, cannot be inlined into traces. If your interpreter dlopen()
s shared
objects at runtime (as is common for C extensions) Yk will be unable to trace
the newly loaded code.
Symbol visibility
The JIT relies upon the use of dlsym()
at runtime in order to lookup any
given symbol from its virtual address. For this to work all symbols must be
exposed in the dynamic symbol table.
yk-config
provides flags to put every function's symbol into the dynamic
symbol table. Since distinct symbols of the same name can exist (e.g. static
functions in C), but dynamic symbol names must be unique, symbols may be
mangled (mangling is done by the fat LTO module merger). If your interpreter
does its own symbol introspection, Yk may break it.
Extra sections in your interpreter binary.
yk-config
will add flags that add the following sections to your binary:
.llvmbc
: LLVM bytecode for your interpreter. Used to construct traces. This is a standard LLVM section (but extended by Yk)..llvm_bb_addr_map
: The basic block address map. Used to map virtual addresses back to LLVM IR blocks. This is a standard LLVM section (but extended by Yk)..llvm_stackmaps
: Stackmap table. Used to identify the locations of live LLVM IR variables. This is a standard LLVM section (but extended by Yk).
Other interpreter requirements and gotchas
-
Yk can only currently work with "simple interpreter loop"-style interpreters and cannot yet handle unstructured interpreter loops (e.g. threaded dispatch).
-
Yk currently assumes that no new code is loaded at runtime (e.g.
dlopen()
), and that no code is unloaded (e.g.dlclose()
). Self modifying interpreters will also confuse the JIT. -
Yk currently doesn't handle calls to
pthread_exit()
gracefully (more details). -
Yk currently doesn't handle
setjmp()
/longjmp()
. -
You cannot valgrind an interpreter that is using Intel PT for tracing (more details).