Exactly a week ago (as a joke), I started writing THC, my "Turbo Haskell compiler," while on vacation visiting Bartosz Milewski. It has grown a tiny bit since then.
THC now implements every one of GHC 9.14.1's prim-ops and provides a JIT for GHC Core that runs Haskell on the JVM. It uses the approach for running typed functional languages I developed several years ago in Cadenza, using Truffle and GraalVM.
GHC still handles parsing, typechecking, desugaring, and Core optimization. THC takes over from there, compiling and executing that Core through its own runtime on Truffle/GraalVM. Advanced language features such as Template Haskell and Linear Haskell are fully supported.
While it can be used as a JIT for GHC-grade Haskell, it also supports ahead-of-time (AOT) compilation with Native Image, allowing it to produce executables. THC is capable of JIT- or AOT-compiling a number of Haskell programs, including pandoc, happy, alex, and, as of today, even GHC itself. THC resolves packages using Cabal and fully supports packages with multiple libraries, including Backpack.
Borrowing libraries
THC provides polyglot FFI to Python, Ruby, R, and JavaScript, letting Haskell raid libraries from other languages and bring their output straight into a JIT-compiled Haskell program. Conversion between Data.Text and Truffle strings over FFI is zero-copy for UTF-8-encoded strings inside other polyglot languages. C/C++ bits in Haskell libraries run via FFI to native-mode Sulong (LLVM on the JVM).
Evaluation and concurrency
Internally, THC supports two different backends for Truffle evaluation: a bytecode-based JIT target and a traditional AST-based JIT target. Both can run in a single-threaded or multi-threaded style. It also supports GHC bytecode itself, so it can run BCO code as produced by GHCi. THC fully supports throwTo, asynchronous exceptions that leave behind resumable code, and masking. It supports both "normal" Java threading and Project Loom, upon which it offers lightweight GHC-style green threading with a HEC-style runtime executor permitting cheap MVars and the like.
SIMD and tail calls
THC supports SIMD using available GHC prim-ops and can go further with runtime selection of SIMD "species" width through the incubating Vector API. Hot tail calls become loops. During recursion in tracing mode, THC fills a 64-bit Bloom filter to detect likely recursive tail calls; when later paths diverge it grows side loops like a tracing JIT. Stack frames that leak can be compacted with a strategy similar to CHICKEN Scheme's GC, reusing machinery needed for resumable async exceptions. On Data.Map benchmarks, something like 66 fallback trampoline calls compared to several million fast-path calls.
The runtime can use compressed ordinary object pointers (compressed oops). For early tests on Data.Map and the like, results ran within a general range of 3× faster to 3× slower after warmup, mostly hovering around 10–20% slower than GHC—though broader coverage work introduced temporary regressions that continue to be addressed.
Development
The code is available at github.com/ekmett/thc, with documentation covering how to build, run, and use THC. Development is proceeding on irc.libera.chat in the ##thc channel.
—Edward Kmett