Ask HN: How does a JIT-compiled function communicate with a bytecode function?
consider we have two functions compiled into bytecode first. if the first function is JIT-compiled, how does the second function call the x86 code of the first function ? how does it extract the results ?
basically how does this leap into the "x86 land" and from there back into the "bytecode land" occur ? also how is the "x86 land" stored and accessed ?
6 comments
[ 3.0 ms ] story [ 26.0 ms ] threadThe concept of "function" or "procedure" doesn't really exist concretely. Sure, some processors provide opcodes to view the accessible memory as a "stack", to accommodate some specific, if popular, block structure implementations, namely C and Fortran's.
The most critical part is mapping the language's concept of "function", an executable body of code where all the referenced variables have specific instances/allocation, reachable at the time of its invocation, with the processor's, or most likely, C's view of how the processor underneath haphazardly implements block structure.
If your JIT compiler can seal a body of code and guarantee to implement the environment (the mapping from address/name to value) in away accessible to the dumb processor, then you will win.
This requires that the compiler hacker implement code to convert values from HLL types, to lower level ones. And convert the implementation of closures and environment, to something more palatable to the lower implementations.
Below is a simple "JIT" interpreter. It uses pre-compiled C code in a shared library, along with its own "natively" compiled operations. A real JIT compiler following this protocol would just make sure it's able to find the functions by any other means, other than compile-time name lookup, as this one does. It can maintain its own explicit symbol table, hashing a function name along with type signature, and dispatch on it at run time.
In the code below, ADD and SUB are the usual addition and subtraction operations, precompiled "for speed", while DIV and MUL are "interpreted" byte-codes. In the current design, both types of functions (native and bytecode) are mapped into a single namespace; typically, there are predicates to distinguish between the two. In most Common Lisp implementations, the two co-exist side by side, and you can call and interpreted function just as you can call a native one.
This is just a quick 1-hour hack, let me know if it's not clear.
Here is the runtime library.
Here is the JIT compiler:http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.25.2...
The original block-structure implementation in Algol is still accessible in Pascal and Oberon literature.