Library / SDK
asmjit/asmjit avatar
asmjit/asmjit

AsmJit: low-latency machine code generation in C++

Low-latency machine code generation

4,612 stars598 forksC++Zlib

At a glance

What is it?
AsmJit is a Zlib-licensed C++ library for emitting x86, x64 and AArch64 machine code at runtime. It suits engine and emulator authors who need a JIT, not application developers looking for a scripting layer.
Who is it for?
Adopt AsmJit if you are writing a JIT for an emulator, interpreter or numeric runtime and already think in registers and calling conventions. Do not adopt it if you want to compile C++ at runtime, since AsmJit emits instructions, not a language front end.
Can I use it commercially?
Yes. Zlib is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 9 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 September 30, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The problem AsmJit solves, and who actually needs it

Generating machine code at runtime usually means one of two bad options: hand-rolling an encoder per instruction, or embedding a full compiler toolchain and paying its startup cost. AsmJit targets the middle ground. The README describes it as "a library for low-latency machine code generation written in C++", and the emphasis on latency is the point. It is for people who need emitted code quickly, inside a running process, without shelling out to an assembler or linking LLVM.

The audience is narrow and specific. Emulator and interpreter authors, people writing tracing JITs, and anyone building a bytecode-to-native path. The topics list on the repository names aarch64, x86, x86-64, jit and code-generation, which maps exactly onto that audience. If your program never writes bytes into an executable page, AsmJit has nothing to offer you. It is not a scripting engine and it is not a C++ compiler.

The library is split by architecture in the tree: core holds the backend-independent API, x86 holds the X86 and X64 backends, arm holds a common AArch32 and AArch64 API, and a64 holds the AArch64-only pieces. There is also a ujit directory for a universal JIT API. That layout tells you the maintainers expect you to pick a backend, not to write portable instruction streams by accident.

How the code generation mechanism is put together

AsmJit exposes more than one way to produce code, and the tests in the repository name them directly. asmjit_test_emitters.cpp is described in the README as demonstrating "the purpose of emitters", while asmjit_test_assembler_x86.cpp targets the Assembler and asmjit_test_compiler_x86.cpp targets the Compiler. Those are two distinct levels of control.

The Assembler path is the low-level one: you emit instructions yourself, choosing registers and operands, and you are responsible for correctness. The Compiler path sits above it and manages register allocation for you, which is the difference between writing assembly by hand and writing something closer to an intermediate representation. Choosing between them is the first real decision in any AsmJit project, and it determines how much of your code is register bookkeeping.

Two more pieces of the layout matter. The core directory is described as backend independent "except relocations", which means relocation handling is where the abstraction leaks and where you should expect architecture-specific work. The db directory holds an instruction database, and tools contains utilities to "re-regenerate generated files (instruction DB, enum strings)". Instruction metadata is therefore generated rather than hand-written, which is why the repository ships regeneration tooling alongside the source.

Building AsmJit and emitting your first instructions

The README points to the build page on asmjit.com for build instructions, including a CMake integration section, and notes that basic configure scripts invoking cmake live in the project root. The repository layout confirms this: CMakeLists.txt, CMakePresets.json, configure.sh, configure_sanitizers.sh, and two Visual Studio batch files, configure_vs2026_x64.bat and configure_vs2026_x86.bat. Because the include path points at the project root, headers are included as asmjit/... rather than by a prefixed directory.

A typical configure-and-build sequence on a Unix-like system starts with the script the repository ships. Run it from the repository root after cloning.

bash
./configure.sh

The README does not spell out the resulting directory name, so check what configure.sh produces on your platform before assuming the build output location. For CMake consumers, the build page linked from the README has a dedicated CMake integration section; that is the authoritative place to copy target names from, not this article.

Once linked, the shortest useful program creates a code holder, attaches an emitter, and emits an instruction. The README does not include a runnable snippet, so the reliable starting point is the test suite: asmjit_test_assembler_x86.cpp for the Assembler and asmjit_test_compiler_x86.cpp for the Compiler. Both are described as tests that "always compile and provide implementation of many use-cases", which makes them better documentation than a truncated example here would be.

Do not invent build flags. The README does not document a test toggle, so confirm the actual option names in CMakeLists.txt or on the build page before using them. The honest instruction is: read asmjit-testing/tests, since the README explicitly warns that asmjit-testing should not be embedded in your project.

Where AsmJit is the wrong tool

AsmJit emits instructions. It does not parse C, it does not build an SSA form, and it does not optimise a loop nest. If your goal is "compile this C++ function at runtime", AsmJit is a component you would have to build a compiler on top of, and that is a research project, not an integration.

The API stability caveat is stated plainly. The README has a Breaking Changes section that opens with "Breaking the API is sometimes inevitable", and it points to a Breaking Changes Guide in the documentation. It also suggests reading the tests as a source of working usage. That is a maintenance signal worth taking seriously: upgrading across versions can require source edits, and the guide exists because it has happened.

There is a structural limitation too. The core API is backend independent except for relocations, so code that relies on relocation behaviour is not portable across x86 and AArch64 by construction. If you need one code path that emits for both, plan for a per-architecture layer, which is what the arm and a64 directories exist to support. Finally, the README gives no performance numbers of any kind. Anyone quoting AsmJit throughput figures is quoting something other than this repository.

AsmJit against LLVM and other JIT approaches

The comparison people actually search for is AsmJit versus LLVM, and the difference is scope, not speed. LLVM is a compiler infrastructure: you hand it an IR, it runs optimisation passes, and it produces object code. AsmJit starts where LLVM's back end ends. You hand it instructions or a register-allocated representation, and it encodes them. There is no optimiser in the picture, and the README makes no claim to one.

That means the trade is control and startup cost against optimisation. With LLVM you get better generated code and a much larger dependency. With AsmJit you get a small C++ library, Zlib-licensed, with no IR to construct and no pass pipeline to configure. For a tracing JIT that emits short, hot fragments and re-emits them when assumptions break, the second shape often fits better. For ahead-of-time compilation of a large program, it does not.

Within AsmJit itself the relevant comparison is Assembler versus Compiler, and it is a genuine fork in the road. The Assembler gives you exact instruction selection and exact register assignment, which is what you want when you are translating known machine code or patching a known sequence. The Compiler allocates registers for you, which is what you want when you are generating fresh code from a higher-level description. asmjit_test_emitters.cpp exists to show why the emitter abstraction is layered this way.

Licence, maintenance and the cost of upgrading

AsmJit is released under the Zlib licence, per the README and LICENSE.md. Zlib is permissive and short. It is not copyleft, so it does not force you to release your own source, but it does carry attribution conditions, and the exact wording is in LICENSE.md rather than paraphrased here. If your organisation classifies licences formally, route it through that process rather than treating this paragraph as clearance.

The repository is not archived, and the last push was on 2026-09-22. The README documents both community and commercial support, with a support page and a funding page, and names Petr Kobalicek as author and maintainer. A single named maintainer is a real bus-factor consideration for a library that sits in your code generation path, and it is worth weighing against the permissive licence.

Upgrade cost is the part teams underestimate. Because breaking changes are described as inevitable and a dedicated guide exists for them, a version bump is a code review, not a dependency edit. The README's own mitigation is to read the tests, which "always compile" and cover many use-cases. That is a workable strategy: treat asmjit-testing/tests as the compatibility reference when you move versions, and read the Breaking Changes Guide before you do.

Editorial conclusion

Adopt AsmJit if you are writing a JIT for an emulator, interpreter or numeric runtime and already think in registers and calling conventions. Do not adopt it if you want to compile C++ at runtime, since AsmJit emits instructions, not a language front end. Before committing, read the Breaking Changes Guide, check that the build page covers your compiler and target, and confirm which backends your target architecture needs.

Frequently asked questions

What is AsmJit used for?

It is a C++ library for low-latency machine code generation, aimed at emitting x86, x64 or AArch64 instructions at runtime. The repository's topics list jit, code-generation and compiler, and the tests cover emitters, an Assembler and a Compiler.

How do I install or build AsmJit?

The README points to the build instructions page on asmjit.com, which includes a CMake integration section, and notes that basic configure scripts invoking cmake are provided in the project root. The repository ships CMakeLists.txt, CMakePresets.json and configure.sh, and the include path points at the project root.

Does AsmJit support ARM and AArch64?

Yes. The source tree has an arm directory described as a common API for AArch32 and AArch64, plus an a64 directory used only by AArch64 backends. The core API is described as backend independent except for relocations.

What is the difference between the AsmJit Assembler and the Compiler?

They are separate emitters with separate tests in the repository. asmjit_test_assembler_x86.cpp targets the Assembler and asmjit_test_compiler_x86.cpp targets the Compiler, while asmjit_test_emitters.cpp is described as demonstrating the purpose of emitters.

What licence does AsmJit use?

The Zlib licence, per the README and the LICENSE.md file in the repository root.

Official sources

  1. asmjit/asmjit on GitHub
  2. Issues
  3. License: Zlib
  4. Project website
  5. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/asmjit-asmjit.svg)](https://hysenlabs.com/projects/asmjit-asmjit)