GSoC19 - LLVM JIT Infrastructure

Hi Preejackie,

Sorry for the delayed reply. I was quite busy over the weekend.

  1. Is there any hard contract that the JIT should compile a full module (basic unit) into native code, or it can extract only hot functions from the module and compile it to native code, and leave the client of JIT (maybe a interpreter) interpret the remains of that module? Or it’s the client’s job to pack all hot functions into module and transfer that to JIT layer? If the latter is case, how JIT can benefit from speculating (I’m bit lost here).

It is possible to extract only hot functions. The mental model for doing this is slightly complicated though, because of the way ORC’s design has been influenced by a desire to integrate well with static compilers (like LLVM): The JIT always starts materialization of an entire MaterializationUnit (e.g. IR Module, object, etc.). This mirrors the way static compilers work — they always compiling a whole module at a time. In ORC however, clients are free to write custom MaterializationUnits that break up the underlying module representation and only continue compilation of part of it.

I have come up with some idea for the speculation task:

  1. Construct the local(per-module) function ordering list based on the sequence of their call in control flow graphs and create stub for each symbol from other module that is referenced by the current function, put the module in waiting list.

You should not need to worry about stub creation directly. ORC manages this via the lazy re-exports utility. You configure two JITDylibs, one (libFoo in this example) that everyone will link to, and a second (libFoo.impl) that will contain the actual definitions. For each definition, the lazyReexports utility will build a stub in libFoo which, when it is executed, will trigger a lookup of the corresponding symbol in libFoo.impl, then jump to it.

stubs: implementations:
±-------+ ±------------+

libFoo | | libFoo.impl |
+========+ ±------------+
foo | | foo |
bar | – on call, looks up → | bar |
baz | | baz |
±-------+ ±------------+

So for the speculation project you will want to track this association. When you see someone reference “bar” in libFoo, and you determine that it would be profitable to speculatively compile the body of “bar”, you would consult your tracker and find that the implementation of this function is provided by “bar” in libFoo.impl. You would then issue a lookup for “bar” in libFoo.impl and discard the result. This will trigger compilation of the body of bar. ORC will manage synchronization so that it doesn’t matter whether your lookup for speculation comes before, after, or during any other lookup of “bar”: it will only be compiled once, and nobody will call into it until it is ready.

These speculation actions should be buried into internals of concurrent compiler and should not be visible to clients right?

Actually, ORC is designed as a library of components. The aim would be to build components that support speculation so that other people can optionally use your work to add speculation to their compiler.

A good test environment would be the LLI llvm-interpreter: This uses the JIT to execute LLVM IR, and would provide a good test platform for speculation.