AsmJit

AsmJit is a lightweight library suitable for low-latency machine code generation written in C++. It can generate machine code for X86, X86_64, and AArch64 architectures. It has a type-safe API that allows C++ compiler to do semantic checks at compile-time even before the assembled code is generated or executed. It also provides an optional register allocator that makes it easy to start generating machine code without a significant development effort.

GitHub Projects

AsmJit project, as the name implies, started as a library to allow JIT code generation and execution. However, AsmJit evolved and now contains features that are far beyond the initial scope. AsmJit now consists of multiple projects:

  • AsmJit - AsmJit library, the main project (also provides instruction database, which used to be AsmDB).
  • AsmTK - AsmTK library, toolkit that implements some functionality on top of AsmJit, for example assembler parser.

Online Tools

  • AsmGrid - A grid view of assembler instructions (AsmDB) and their latencies (generated by CULT tool).

Highlights

  • Lightweight - ~500kB compiled binary with 3 architecture backends and all built-in features.
  • Modular - Unneeded features can be disabled at compile-time to reduce the size of the library.
  • Zero dependencies - No external libraries nor STL containers are used, easy to embed and/or link statically.
  • No exceptions & RTTI - AsmJit doesn't use exceptions, but allows to attach a throwable ErrorHandler if required.

Key Features

  • Complete X86/X64 instruction set - MMX, SSE+, BMI+, AVX+, FMA+, AVX-512+, AMX, privileged instructions, and other recently added ISA extensions are supported.
  • Complete AArch64 instruction set - Baseline instructions and ASIMD extensions.
  • Dynamic architecture that allows users to construct instructions and operands at runtime - to inspect them, to validate them, and to emit them.
  • Different emitters providing various abstraction levels - Assembler, Builder, and Compiler.
  • Support for sections that can be used to separate code and data or to use separate buffers during code generation.
  • Built-in logging (includes formatting) and user-friendly error handling.
  • JIT memory allocator with malloc-like API for JIT code generation and execution with a lot of features. JIT allocator can allocate dual-mapped pages, which is essential for supporting operating systems that restrict mapped pages to either be writable or executable (W^X). In addition, MAP_JIT is also supported on Apple platforms.
  • Compiler can be used to emit large chunks of code with the help of a built-in register allocator. This is a unique feature that makes it almost instant to start generating code and have results.

Package Managers

When consuming AsmJit as a dependency via package managers, please make sure that you are using a recent version. For convenience, the following links can be used to verify the versions offered by the most used package managers:

  • conan - 2024-05-31 is recent
  • vcpkg - 2023-03-25 is outdated

We don't recommend vcpkg at the moment as it has never offered an up-to-date AsmJit. In such cases, consider either vendoring it yourself or using a different way of obtaining it such as git submodule or git clone. AsmJit was designed to be easily embeddable in projects so it should not be an issue. CMake users can just include AsmJit project and pass asmjit::asmjit as a library into target_link_libraries() function.

A Minimal Example

The example below demonstrates how AsmJit separates concepts used during code generation. Check out AsmJit's documentation for more details and code snippets.

#include <asmjit/asmjit.h>
#include <stdio.h>

using namespace asmjit;

// Signature of the generated function.
typedef int (*Func)(void);

int main(int argc, char* argv[]) {
  // Runtime designed for JIT - it holds relocated functions and controls their lifetime.
  JitRuntime rt;

  // Holds code and relocation information during code generation.
  CodeHolder code;

  // Code holder must be initialized before it can be used. The simples way to initialize
  // it is to use 'Environment' from JIT runtime, which matches the target architecture,
  // operating system, ABI, and other important properties.
  code.init(rt.environment(), rt.cpuFeatures());

  // Emitters can emit code to CodeHolder - let's create 'x86::Assembler', which can emit
  // either 32-bit (x86) or 64-bit (x86_64) code. The following line also attaches the
  // assembler to CodeHolder, which calls 'code.attach(&a)' implicitly.
  x86::Assembler a(&code);

  // Use the x86::Assembler to emit some code to .text section in CodeHolder:
  a.mov(x86::eax, 1);  // Emits 'mov eax, 1' - moves one to 'eax' register.
  a.ret();             // Emits 'ret'        - returns from a function.

  // 'x86::Assembler' is no longer needed from here and can be destroyed or explicitly
  // detached via 'code.detach(&a)' - which detaches an attached emitter from code holder.

  // Now add the generated code to JitRuntime via JitRuntime::add(). This function would
  // copy the code from CodeHolder into memory with executable permission and relocate it.
  Func fn;
  Error err = rt.add(&fn, &code);

  // It's always a good idea to handle errors, especially those returned from the Runtime.
  if (err) {
    printf("AsmJit failed: %s\n", DebugUtils::errorAsString(err));
    return 1;
  }

  // CodeHolder is no longer needed from here and can be safely destroyed. The runtime now
  // holds the relocated function, which we have generated, and controls its lifetime. The
  // function will be freed with the runtime, so it's necessary to keep the runtime around.
  //
  // Use 'code.reset()' to explicitly free CodeHolder's content when necessary.

  // Execute the generated function and print the resulting '1', which it moves to 'eax'.
  int result = fn();
  printf("%d\n", result);

  // All classes use RAII, all resources will be released before `main()` returns, the
  // generated function can be, however, released explicitly if you intend to reuse or
  // keep the runtime alive, which you should in a production-ready code.
  rt.release(fn);

  return 0;
}

Use Cases

AsmJit has been the preferred choice of many projects for years, because of its API, size, and features. It's ideal for implementing high performance and low latency code generators, be it JIT backends of programming languages, recompilers, or specialized compilers that generate code based on data that is only known at runtime. AsmJit has been also successfully used in other domains such as debuggers, reverse engineering tools, and computer graphics.

AsmJit in Research

AsmJit in Open Source

  • Blend2D - A high performance 2D vector graphics engine written in C++ (git)
  • Cinder - Instagram's performance oriented Python fork that uses AsmJit in its JIT backend
  • CULT - A tool that can be used to measure instruction latencies in user-space
  • Erlang OTP - Erlang OTP uses AsmJit in its BEAM JIT compiler (git, blog)
  • FBGEMM - Facebook's matrix multiplication library
  • GZDoom - Doom engine, which uses AsmJit for JIT compilation of ZScript code (git)
  • MathPresso - Mathematical expression parser and JIT compiler (example of using AsmJit)
  • QuestDB - Fast SQL for time series (git, blog)
  • X64dbg - An open-source x64/x32 debugger for Windows (git)
  • (this is not a complete list, there are many other open-source projects using AsmJit)

AsmJit in Commercial Products

  • (in preparation)

If you would like to have a closed-source product on the list above, please use our Support page (Commercial Support) and contact the author of AsmJit. The email must contain the product that uses AsmJit, the company, link to the product or a company homepage, and a permission to list the product on AsmJit homepage. Alternatively, if you write about the use of AsmJit in a company's blog and provide a link to the post, that would be sufficient as well.

Sponsors

AsmJit Team would like to thank all companies and individuals that support its development and maintenance.

Help AsmJit

AsmJit development costs time, which is currently provided mostly for free. It's not just the cost of the development itself; it's maintenance and community support as well. In general, issues are resolved quickly and questions answered instantly, which can feel like enterprise support to those who have such experience. In addition, the CI pipeline currently supports more than 100 configurations to ensure that AsmJit can be compiled by all mainstream C++ compilers on a variety of platforms.

Development

Since the AsmJit development is only done in a spare time, the project is not where its authors would like it to be. AsmJit offered initially only X86 backend, but now it supports both 32-bit and 64-bit X86, AArch64, and AArch32 is already in a separate branch for testing. Thus, the effort to update each backend when a new ISA extension is introduced grows with all the architectures supported. Unfortunately, we are already behind - the most recent AArch64 extensions or Intel APX extensions are not supported yet.

Sustainability

We are looking for a sustainable development model that would allow the lead AsmJit developer to work on AsmJit at least one day a week, which is currently not possible.

Monthly Sponsors

  • Companies & Foundations:
  • Individuals:
    • GitHub Sponsors - provides list of all GitHub sponsors and a possibility to become a sponsor

We would like to specially thank Shiguredo Inc. for a long-term sponsorship of AsmJit.

One Time Sponsors

Get Involved!