[HN Gopher] Retrofitting JIT Compilers into C Interpreters
       ___________________________________________________________________
        
       Retrofitting JIT Compilers into C Interpreters
        
       Author : ltratt
       Score  : 121 points
       Date   : 2026-04-15 12:00 UTC (1 days ago)
        
 (HTM) web link (tratt.net)
 (TXT) w3m dump (tratt.net)
        
       | sgbeal wrote:
       | i tend to think of myself as a computing nerd, but posts like
       | this one make me realize that i don't even rate on the computing
       | nerd scale.
        
         | throwaway1492 wrote:
         | Do you always make things about yourself? Have you written a
         | parser or interpreter? You should, it's an interesting
         | exercise. The idea is to add meta tracing to the interpreter
         | (the c code) that allows hot paths to be compiled to machine
         | code and be then executed instead of being interpreted.
        
           | sgbeal wrote:
           | > Do you always make things about yourself?
           | 
           | That's an abrasive question but i dare say that we all do.
           | It's our only constant point of reference.
           | 
           | > Have you written a parser or interpreter?
           | 
           | i have written many parsers, several parser generators, and a
           | handful of programming languages. This article, however,
           | covers a whole other level, way over my head (or well beyond
           | any of my ambitions, in any case).
           | 
           | Pics or it didn't happen: fossil.wanderinghorse.net/r/cwal
        
       | mwkaufma wrote:
       | TL;DR compile with a fork of LLVM that enables runtime IR
       | tracing. Very clever!
        
         | measurablefunc wrote:
         | That's not what they're doing. They're directly modifying the
         | IR to convert it into a tracing JIT. The final artifact is a
         | binary w/ no IR. The problem is of course not introducing any
         | subtle bugs in the process b/c they'd have to prove the
         | modification they're making do not change actual runtime
         | semantics for the final binary artifact.
        
       | fuhsnn wrote:
       | Took me a while to figure out whether it's interpreters for C
       | programs or if there's a particular class of interpreters called
       | "C". Turns out it's about interpreters implemented in C that they
       | use modified LLVM to do the retrofitting, but couldn't it be
       | applicable for other languages with LLVM IR, or other switch-in-
       | a-loop patterns in C?
        
         | itriednfaild wrote:
         | I've been a low level C and C++ programmer for 30 years. Even
         | with your explanation and having read the webpage twice I have
         | no idea what this technology does or how it works. So it takes
         | normal interpreted code and jits it somehow? But you have to
         | modify the source code of your program in some way?
        
           | fuhsnn wrote:
           | I don't fully grasp it either, the most appropriate analogy I
           | can think of is like how OpenMP turns #pragma annotated loops
           | into multi-threading, this work turns bytecode interpreting
           | loops into JIT VM.
        
             | moardiggin wrote:
             | It's a promising technology, but it's still in the research
             | domain. It's not an automated procedure. You need to use
             | the yk fork of LLVM to compile and link your code, and you
             | have to manually annotate and alter a fair amount of your
             | interpreter loop with yk macros in non-trivial ways:
             | while (true) {           __yk_tracebasicblock(0);
             | Instruction i = code[pc];           switch (GET_OPCODE(i))
             | {             case OP_LOOKUP:
             | __yk_tracebasicblock(1);
             | push(lookup(GET_OPVAL()));               pc++; break;
             | 
             | ...                   case OP_INT:
             | push(yk_promote(constant_pool[GET_OPVAL(i)])); pc++; break;
             | 
             | Knowledge of tracing compilers, LLVM and SSA are needed by
             | the user.
             | 
             | > added about 400LoC to PUC Lua, and changed under 50LoC
             | 
             | Lua 5.5.0 has 32106 lines of code including comments and
             | empty lines. The changes amount to 1.4% of the code base.
             | And then there's the code changes in the yk LLVM fork that
             | you'd have to maintain which I'm guessing would be a few
             | orders of magnitude larger.
             | 
             | If this project would be able to detect the interpreter
             | hotspots itself and completely automate the procedure, it
             | would be great.
        
               | ltratt wrote:
               | > If this project would be able to detect the interpreter
               | hotspots itself and completely automate the procedure, it
               | would be great.
               | 
               | I don't think that's realistic; or, at least, not if you
               | want good performance. You need to use quite a bit of
               | knowledge about your context to know when best to add
               | optimisation hints. That said, it's not impossible to
               | imagine an LLM working this out, if not today, then
               | perhaps in the not-too-distant future! But that's above
               | my pay grade.
        
               | moardiggin wrote:
               | Thanks for sharing this technology. I hope it gets
               | upstreamed into LLVM.
        
           | hencq wrote:
           | I think the website does an amazing job explaining it, but it
           | basically takes an interpreter written in C and turns it into
           | a JIT with minimal changes to the code of the interpreter
           | (i.e. not to the code of the program you're running in the
           | interpreter). For example they took the Lua interpreter and
           | with minimal changes were able to turn it into a JIT, which
           | runs Lua programs about 2x faster.
        
           | vkazanov wrote:
           | tracing jits are slightly harder to grasp than usual ones.
           | The technique comes from real CPUs so the mindset of people
           | behind the original idea is very different from the software
           | world.
           | 
           | Metatracing ones are kind of an interesting twist on the
           | original idea.
           | 
           | > So it takes normal interpreted code and jits it somehow?
           | 
           | Anyway, they use a patched LLVM to JIT-compile not just
           | interpreted code but the main loop of the bytecode
           | interpreter. Like, the C implementation itself.
           | 
           | > But you have to modify the source code of your program in
           | some way?
           | 
           | Generally speaking, this is not normally the goal. All JIT-s
           | try to support as much of the target language as possible.
           | Some JIT-s do limit the subset of features supported.
        
         | ltratt wrote:
         | You're quite right that since we're working with LLVM IR,
         | adapting to other languages is probably not _that_ difficult,
         | though these things always end up taking more time than I
         | expect! Since the majority of real-world problems in this area
         | depend on C interpreters, we put our limited resources to that
         | problem. You're also right that "interpreters" is a pretty
         | vague category, and there are other parts of C (and other)
         | programs that could be yk-ified, though I suspect it would be a
         | fairly specialised subset of programs.
        
         | pjmlp wrote:
         | There were a couple of C interpreters since the 1990's,
         | including with REPL support, but apparently never took off,
         | most likely a community culture issue, that doesn't seem much
         | value using them, beyond being in a debug session.
        
           | i_don_t_know wrote:
           | I used to work on LabWindows/CVI an integrated C development
           | environment. It included an "Interactive Execution Window"
           | where you could build programs piecemeal. You added pieces of
           | code, ran them, then appended more code, ran the new pieces,
           | etc. It was as text window so you had more freedom than in a
           | simple REPL.
           | 
           | It integrated with "Function panels". Function panels were
           | our attempt at documenting our library functions. See the
           | second link below. But you could enter values, declare
           | variables, etc and then run the function panel. Behind the
           | scenes, the code is inserted to the interactive window and
           | then run. Results are added back to the function panel.
           | 
           | These also worked while suspended on a breakpoint in your
           | project so available while debugging.
           | 
           | My understanding was that these features were quite popular
           | with customers. They also came it handy internally when we
           | wrote examples and did manual testing.
           | 
           | https://www.ni.com/docs/de-DE/bundle/labwindows-
           | cvi/page/cvi...
           | 
           | https://www.ni.com/docs/de-DE/bundle/labwindows-
           | cvi/page/cvi...
           | 
           | https://irkr.fei.tuke.sk/PPpET/_materialy/CVI/Quick_manual.p.
           | ..
        
             | pjmlp wrote:
             | Thanks for sharing.
             | 
             | Yeah, I find this valuable regardless of the programming
             | language, ideally the toolchain should be a mix of
             | interpreter/JIT/AOT, to cherry pick depending on the
             | deployment use case.
             | 
             | Naturally for dynamic languages pure AOT is not really
             | worth it, althought a JIT cache is helpful as alternative.
        
       | djwatson24 wrote:
       | It's quite impressive they're able to take nearly arbitrary C and
       | do this! Very similar to what pypy is doing here, but for C, and
       | not a python subset.
       | 
       | However not without downsides. It sounds like average code is
       | only 2x faster than Lua, vs. LuaJit which is often 5-10x faster.
        
         | hypercube33 wrote:
         | Hmm I'm wondering how hard it would be to redo the old timey
         | Microsoft jvm from the 90s for modern days....java > .net
         | assembly runtime
        
       | linzhangrun wrote:
       | It's truly a good thing to see a project like this in the era of
       | Vibe Coding taking flight :)
        
       | measurablefunc wrote:
       | Why do they need to change LLVM? Why can't they make this another
       | LLVM IR pass?
        
         | ltratt wrote:
         | Our fork of LLVM does add a pass, amongst other changes, but we
         | also have to do things like change stackmaps in a way that
         | breaks compatibility. Whether stackmaps in their current
         | incarnation are worth retaining compatibility for is above my
         | pay grade! So some of our changes are probably upstreamable,
         | but some might be considered too niche for wider integration.
        
       | 9fwfj9r wrote:
       | Those interested in this type of work can also visit
       | https://cfallin.org/blog/2024/08/28/weval/. The difference is
       | that they use this technique to derive an AOT compiler.
        
         | syrusakbary wrote:
         | Yeah, the strategy is literally the same
        
       | pjmlp wrote:
       | I find rather strange the complaint about compatibility across
       | JIT implementations, there is exactly the same problem across any
       | programming language with multiple implementations, interpreters,
       | compilers, JIT, whatever.
        
       | edmondx wrote:
       | Sounds very promising. Although right now I'm working on a
       | project together with MLIR.
        
       ___________________________________________________________________
       (page generated 2026-04-16 23:02 UTC)