asmjit::x86::Compiler Class Reference [¶]

X86/X64 compiler implementation.
Compiler Basics
The first x86::Compiler example shows how to generate a function that simply returns an integer value. It's an analogy to the first Assembler example:
The BaseCompiler::add_func() and BaseCompiler::end_func() functions are used to define the function and its end. Both must be called per function, but the body doesn't have to be generated in sequence. An example of generating two functions will be shown later. The next example shows more complicated code that contain a loop and generates a simple memory copy function that uses uint32_t
items:
AVX and AVX-512
AVX and AVX-512 code generation must be explicitly enabled via FuncFrame to work properly. If it's not setup correctly then Prolog & Epilog would use SSE instead of AVX instructions to work with SIMD registers. In addition, Compiler requires explicitly enable AVX-512 via FuncFrame in order to use all 32 SIMD registers.
Recursive Functions
It's possible to create more functions by using the same x86::Compiler instance and make links between them. In such case it's important to keep the pointer to FuncNode.
The example below creates a simple Fibonacci function that calls itself recursively:
Stack Management
Function's stack-frame is managed automatically, which is used by the register allocator to spill virtual registers. It also provides an interface to allocate user-defined block of the stack, which can be used as a temporary storage by the generated function. In the following example a stack of 256 bytes size is allocated, filled by bytes starting from 0 to 255 and then iterated again to sum all the values.
Constant Pool
Compiler provides two constant pools for a general purpose code generation:
- Local constant pool - Part of FuncNode, can be only used by a single function and added after the function epilog sequence (after
ret
instruction). - Global constant pool - Part of BaseCompiler, flushed at the end of the generated code by BaseEmitter::finalize().
The example below illustrates how a built-in constant pool can be used:
Jump Tables
x86::Compiler supports jmp
instruction with reg/mem operand, which is a commonly used pattern to implement indirect jumps within a function, for example to implement switch()
statement in a programming languages. By default AsmJit assumes that every basic block can be a possible jump target as it's unable to deduce targets from instruction's operands. This is a very pessimistic default that should be avoided if possible as it's costly and very unfriendly to liveness analysis and register allocation.
Instead of relying on such pessimistic default behavior, let's use JumpAnnotation to annotate a jump where all targets are known: