SLJIT and the low-level JIT layer that hides the CPU behind an API
Platform independent low-level JIT compiler
At a glance
- What is it?
- SLJIT is a platform-independent low-level JIT compiler written in C, aimed at projects that translate bytecode into machine code across several architectures. It is infrastructure rather than a runtime, and the documentation lives in a single header file.
- Who is it for?
- Adopt SLJIT if you are writing a language runtime or a bytecode interpreter that must generate native code and target more than one CPU family, and if you are willing to work at the register and instruction level. Skip it if you need a managed runtime with a garbage collector, a debugger or a standard library, since none of that is here, and if you would rather link against a compiler that exposes a normal source language interface.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 39 days ago.
- What is it written in?
- Mainly C, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 7, 2026, and from our analysis. They are not legal advice.
Star history
- zherczeg/sljit1,136 ★ · +4
| Repository | 2026-10-22 | 2026-11-07 |
|---|---|---|
| zherczeg/sljit | 1.1k | 1.1k |
4 Stars in the last 16 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
A JIT compiler that stops below the language level
SLJIT is described in one line as a platform-independent low-level JIT compiler, and the project says it is very well suited for translating bytecode into machine code. That is the whole scope, and it is worth being precise about what sits on either side of it.
Above SLJIT there is your interpreter, your bytecode format, your object model. Below it there is the CPU. In between, SLJIT gives you a portable instruction set for a virtual register machine that it lowers to real machine code for whichever architecture you are targeting.
The audience is therefore not application developers. It is the people writing language implementations and embedded interpreters who need to emit code that runs at native speed without writing a backend per architecture themselves. The primary language is C, which is the expected choice for a component that gets linked into a host runtime.
It is infrastructure, and it behaves like infrastructure. There is no package to install, no service to run, and no user interface to learn. The deliverable is a header and a source tree that you compile into your own project.
Nine target architectures, from x86 to LoongArch
The list of supported target architectures is the clearest statement of what problem this solves. The documentation names `x86` in 32 and 64 bit, `ARM` in 32 and 64 bit, `RISC-V` in 32 and 64 bit, `s390x` in 64 bit, `PowerPC` in 32 and 64 bit, `LoongArch` in 64 bit, and `MIPS` in 32 and 64 bit.
Two details in that list matter for practical planning. First, the coverage spans both mainstream desktop and server CPUs and the smaller architectures that show up in embedded and router firmware, which is where a single backend implementation pays for itself most clearly. Second, the credit list at the end of the project documentation names contributors for specific architecture work, including Jiong Wang for TileGX support and Mingtao Zhou for LoongArch support, which is a reasonable signal about where the depth of testing lies for each target.
Because SLJIT is platform-independent rather than platform-specific, the abstraction it exposes is not an instruction set but a set of operations. You describe what you want in terms of SLJIT's own virtual instructions, and it selects the best sequence for the CPU in front of you.
The operation set, including the awkward cases
The feature list is not a list of conveniences. It is a list of the things that are genuinely hard to do portably, which is the best signal you can get about where the effort went.
Self-modifying code is supported, so a generated routine can rewrite itself. Tail calls are supported, which matters for interpreters that dispatch on a stack of continuations, because without them you get an unbounded native stack. Fast calls are supported as a distinct concept from ordinary calls. Byte order reverse, described as endianness switching, is supported, which is what you need on a big-endian target when your bytecode format is little-endian. Unaligned memory accesses and SIMD are supported. Atomic operations are supported.
Beyond that, direct access to registers is allowed for both integer and floating point, and there is support for stack space allocation for function local variables.
Each of those features is a portability trap that an application author would otherwise have to solve by hand per architecture. An implementation that offers them at a uniform API level is buying you back the duplicated backend work.
All-in-one compilation and serialization into a byte buffer
Two capabilities stand out because they change how the component fits into a build or shipping pipeline.
All-in-one compilation allows SLJIT's API to be completely hidden from external use. Instead of your project calling into SLJIT's interface at runtime, the generated code and the compiler are combined so that the API never appears in the external surface. For a project that wants to expose a stable public interface and keep the JIT machinery private, that means the machinery does not leak into your headers or your ABI.
The second is serialization. The compiler can be serialized into a byte buffer, and the documentation gives two uses for it. It is useful for ahead-of-time compilation, and code generation can be resumed after deserialization, which the project calls partial AOT compilation.
That second use is the interesting one. A build machine can generate code for a target, ship the buffer, and let the device finish the job later, which is the shape you want when compilation is too slow to do on the device or when the code must be produced by a machine you control. The full AOT case is simpler and more common, and the resume case covers workflows where only part of the code can be prepared ahead of time.
Where the documentation lives, and what the repository root tells you
The project names its primary source of documentation as `sljitLir.h`, the header file in the `sljit_src` directory. That is unusual and it is the single most important fact for anyone evaluating this project. You are expected to read the API through the header itself, with additional documentation on the project website at zherczeg.github.io/sljit and in the `docs` folder.
The top level of the repository tells you the rest of the shape. There is `sljit_src` for the library, `test_src` for the test suite, `regex_src` for a separate regular expression component, `docs` for the documentation, and `CMakeLists.txt` alongside a `GNUmakefile` for the two build routes.
Two files at the root exist purely for people tracking the interface. `API_CHANGES` records changes to the public API and `INTERNAL_CHANGES` records internal ones. For a library that others embed, that pair is a practical substitute for a changelog, and the honest way to size the upgrade cost of moving to a newer revision is to read the API change list rather than to guess from the commit log.
There is no hosted project page beyond the documentation site and no release history in the publishing sense, because the repository has no GitHub releases at all. There is also no home page URL field for the project beyond that documentation site.
For a first build, the practical path is the one the repository layout implies. You take the source tree, which holds `sljit_src` for the library, `test_src` for the test suite and `regex_src` for the regular expression component, and you compile it with either `CMakeLists.txt` or the `GNUmakefile` at the root. There is no package manager entry, no configure script and no documented dependency list, so the compiler toolchain and the C library on your machine are the only prerequisites the project implies. The documentation does not describe an install step or a system-wide install target, so in practice you build the library and link it into your runtime rather than installing it as a shared component.
What it does not give you, and when a full runtime is the better choice
The most common mistake with a library like this is expecting a runtime and getting a code generator.
SLJIT does not manage memory for you. It gives you a way to allocate stack space for function locals in generated code, and that is a different thing from a garbage collector, an allocator arena or a handle table. If your interpreter needs object lifetimes, you implement them above SLJIT.
There is no debugger integration, no profiling interface, no bytecode format, no standard library, and no FFI layer. Nothing here tells you how to represent a string or how to call a C function from your virtual machine. Those are decisions that belong to your language design, and every language built on SLJIT has answered them differently.
There is also a maintenance consideration specific to this kind of project. There are no tagged releases to pin, so a project that depends on SLJIT has to decide how it tracks the upstream branch. Vendoring a copy and reviewing the API change file before each update is the pattern that keeps a fork from drifting.
The comparison with a full optimising compiler is instructive. A compiler of that kind works from a typed intermediate representation, runs an optimiser over it and then generates code, and it takes much longer to learn and to integrate into a runtime. SLJIT deliberately trades all of that for a small API that a runtime author can read in an afternoon and control precisely, at the cost of doing the register allocation and instruction selection yourself.
Licence terms and where the project stands
SLJIT is licensed under the Simplified BSD License, with the text in the LICENSE file at the root of the repository. The licence field on the repository shows a custom licence that GitHub cannot classify, which reflects the fact that the Simplified BSD variant is not one of the identifiers the platform ships with by default.
For an embedder, the practical questions are whether you can ship it inside a proprietary runtime, whether you must publish modifications, and whether you must keep the notice text. Those are the questions the LICENSE file answers, and anyone planning to vendor this code into a commercial product should read it there rather than rely on the badge.
The repository is not archived and the last push was on 2026-09-30, so the work is still moving. The upstream author keeps a single point of contact for the project, either through an issue or by email, which tells you something about the shape of the project: one maintainer with a public issue tracker rather than a committee.
What the repository does not include is any tagged release history, any automated release pipeline that publishes artifacts, and any compatibility promise beyond the API change files. The version you build is the commit you choose, and the API change file is how you find out what moved.
Editorial conclusion
Adopt SLJIT if you are writing a language runtime or a bytecode interpreter that must generate native code and target more than one CPU family, and if you are willing to work at the register and instruction level. Skip it if you need a managed runtime with a garbage collector, a debugger or a standard library, since none of that is here, and if you would rather link against a compiler that exposes a normal source language interface. Before starting, read sljitLir.h, which the project names as its primary documentation, and check the API_CHANGES and INTERNAL_CHANGES files at the repository root so you know how far the interface has moved since the version you last saw.
Frequently asked questions
What is SLJIT used for?
SLJIT is a low-level, platform-independent JIT compiler well suited for translating bytecode into machine code. It sits between your bytecode interpreter and the CPU, so it is used by people writing language runtimes rather than by application developers.
Which CPU architectures does SLJIT support?
The documentation lists x86 32 and 64 bit, ARM 32 and 64 bit, RISC-V 32 and 64 bit, s390x 64 bit, PowerPC 32 and 64 bit, LoongArch 64 bit and MIPS 32 and 64 bit. The credit list names contributors for TileGX and LoongArch support.
Where is the SLJIT documentation?
The primary source of documentation is sljitLir.h in the sljit_src directory, with additional documentation on the project website at zherczeg.github.io/sljit and in the docs folder.
Can SLJIT be used for ahead-of-time compilation?
Yes. The compiler can be serialized into a byte buffer, which the documentation lists as useful for ahead-of-time compilation, and code generation can be resumed after deserialization for partial AOT compilation.
Official sources
Was this useful?