Machine Code Generation

AsmJit is a lightweight library for machine code generation written in C++ language. It can generate machine code for X86 and X86_64 architectures with the support for the whole instruction set - from legacy MMX to the newest AVX-512 and AMX. 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 generate complex code without a significant development effort.

AsmJit was initially developed for X86 and X86_64 architectures, however, an experimental AArch64 backend is already in aarch64 branch.

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.
  • AsmTK - AsmTK library, toolkit that implements some functionality on top of AsmJit, for example assembler parser.
  • AsmDB - Assembler database in JSON format, used to generate AsmJit tables.

Online Tools

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

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 hold 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());

  // 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 used in various projects that required high performance code generation. Most of the time it's about generating specialized functions that implement calculations based on inputs that can change - this also includes scripting languages, recompilers, and machine learning. AsmJit has been the preferred choice for many people and companies because of its size and features.

Highlights

  • Lightweight - ~300kB compiled binary with all built-in features depending on C++ compiler and optimization level.
  • Modular - Unneeded features can be disabled at compile-time to save space.
  • 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.
  • 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.
  • Compiler can be used to emit large chunks of code with the help of a built-in register allocator.

Open Source Using AsmJit

  • Blend2D - A high performance 2D vector graphics engine written in C++ (git).
  • 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 GEneral 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).
  • X64dbg - An open-source x64/x32 debugger for Windows (git).
  • (this is not a complete list, there are many other projects using AsmJit)

Sponsors

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

Companies

List of leading companies and foundations that sponsor AsmJit on a regular basis:

Individuals

List of individuals who donated a significant amount to AsmJit:

Platforms

  • GitHub Sponsors - provides list of all GitHub sponsors and a possibility to become a sponsor.

Want to Get Involved?