@david_chisnall@infosec.exchange
Post #2707891
2026-04-15 12:14 UTC
@ltratt@mastodon.social
There's another approach that's worth mentioning, popularised by Apple's old shader JIT, which looks like a more ad-hoc version of what you've built.
Each operation was written as a function that took a pointer to the interpreter state and updated it. The interpreter is then a big switch statement calling these functions. These typically all get inlined so you end up with one massive function that runs in a loop.
To build the JIT, you compile those individual functions to LLVM IR, then JIT compile a function that is equivalent to the calls of a sequence of bytecode. The normal LLVM optimisers can then inline small or infrequently-used opcode bodies, and optimise across the whole program (or whole function, trace, or whatever else you want to JIT). The JIT'd code has the same interpreter state (though may update it only at the end of a trace - apparently marking it as not-aliasing-anything gets you around 10% extra performance), so you can JIT whatever size fragment makes sense.
Replies (1)
-
@ltratt@mastodon.social 2026-04-15 12:27
@david_chisnall@infosec.exchange A very early prototype of yk used LLVM for these purposes, but the compilation performance was awful (from memory something like 1000x worse than we needed). It's not really LLVM's fault though: we were feeding it an input it never expected to see. [We also encountered multiple threading bugs, but I imagine those have been fixed in the interim.]