Just in Time

AsmJit is a complete JIT and AOT assembler for C++ language. It can generate native code for x86 and x64 architectures and supports the whole x86/x64 instruction set - from legacy MMX to the newest AVX512. It has a type-safe API that allows C++ compiler to do semantic checks at compile-time even before the assembled code is generated and/or executed.

GitHub Projects

AsmJit, 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. The list of sub-projects:

  • AsmJit - AsmJit library.
  • AsmTK - AsmTk library, toolkit that adds some functionality to AsmJit.
  • AsmDB - Assembler database in JSON-like format, used to generate AsmJit tables.
  • CULT - A tool that can be used to measure instruction latencies, based on AsmJit.

Online Tools

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

AsmJit Example

#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[]) {
  JitRuntime rt;                          // Runtime specialized for JIT code execution.

  CodeHolder code;                        // Holds code and relocation information.
  code.init(rt.codeInfo());               // Initialize to the same arch as JIT runtime.

  x86::Assembler a(&code);                // Create and attach x86::Assembler to `code`.
  a.mov(x86::eax, 1);                     // Move one to 'eax' register.
  a.ret();                                // Return from function.
  // ----> x86::Assembler is no longer needed from here and can be destroyed <----

  Func fn;
  Error err = rt.add(&fn, &code);         // Add the generated code to the runtime.
  if (err) return 1;                      // Handle a possible error returned by AsmJit.
  // ----> CodeHolder is no longer needed from here and can be destroyed <----

  int result = fn();                      // Execute the generated code.
  printf("%d\n", result);                 // Print the resulting "1".

  // 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;
}

Why AsmJit

  • Lightweight - 250-300kB compiled with all built-in features depending on compiler & feature settings.
  • Modular - disable unneeded features at compile-time or link AsmJit statically to save space.
  • Zero dependencies - no external libraries, no STL/RTTI, easy to embed and/or link statically.
  • No exceptions - but still allows to attach a throwable error handler of your choice.

Key Features

  • Complete X86/X64 instruction set - MMX, SSE+, BMI+, AVX+, FMA+, AVX-512+, and other recently added extensions supported.
  • Different emitters providing various abstraction levels - Assembler, Builder, Compiler.
  • Support for sections that can be used to separate code and data or to create separate buffers during code generation.
  • Built-in logging, formatting, and user-friendly error handling.
  • JIT memory allocator similar to malloc/free for JIT code generation and execution.
  • Dynamic architecture that allows users to construct instructions and operands at runtime and emit them.
  • Instruction encoding control to override defaults including instruction size and encoding.
  • Optional Compiler tool that can be used to emit large chunks of code that use a built-in register allocator.