The course overall looks good, and it's great that so much is available online, so well done, Adrian.
The course overall looks good, and it's great that so much is available online, so well done, Adrian.
We spend a lot more time on type feedback, ICs, and deoptimization which are the more universal concepts that can be applied to multiple different compiler designs.
JAX is a tracing compiler!
(I know, I know, it sits in an extremely different part of the problem space than TraceMonkey or LuaJIT. Still.)
Numerical programs being very stable in terms of control is what enables GPU parallelization and loop optimizations in the long tradition of Fortran compilers. Optimizations like loop tiling, interchange, strip mining, etc aren't going to be easy to do with trace compilation.
Anyway my comment was more directed toward trace compilation in the context of dynamic languages, and there I think it's pretty well established it only works well for small programs.
ML frameworks even take advantage of this to compute, ahead-of-time, how much memory will be used at different points in the program graph, and thereafter schedule memcpy's to make space as necessary. Of course this only works for well-behaved program classes, but e.g. most LLM architectures fit into that category. Interestingly MoE models don't, since they require data-dependent control flow, thus the recent push towards accommodating dynamism in frameworks (like JAX, which until ~recently couldn't handle it at all).
Is your criticism of tracing specific to messy, confusing code, with lots of edge cases in the main loop, or does it also hold true for well written code?
I have no experience with compiler design, didn't even take a compilers course in college.
LuaJIT works just fine. On big programs. On hundreds of millions of servers and devices.
I find it deeply saddening that even scholars keep repeating this trope. Ignorance is bliss.
Best of luck.
'Real-world' JavaScript is a challenge for any compiler, no matter the underlying technology.
The technical debt in SpiderMonkey (at that time, anyway) was eye-watering. Grafting anything on top of that was hard. They haven't even gotten to the point of implementing the crucial pieces of a trace compiler before the project folded.
Trace compilers make nice textbook exercises. Getting them into production is a different matter: region selection, side-exit handling, trace graph evolution, deep VM integration, code generation adapted to all of this … far from trivial, but doable.
Neither is it trivial to create a production-quality method-at-a-time JIT compiler and VM for a dynamic language.
Ceterum censeo: The fundamental papers on trace compilation are from Fisher (1981) and the teams around Multiflow (1990) and Dynamo (1999). Franz, Gal, et al can be credited for later re-popularizing the idea, but they haven't added anything of note.