AsmJit
Low-Latency Machine Code Generation
X86/X64 compiler implementation.
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::addFunc() and BaseCompiler::endFunc() 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 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.
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:
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.
Compiler provides two constant pools for a general purpose code generation:
ret
instruction).The example below illustrates how a built-in constant pool can be used:
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:
nullptr
)◆ Creates a new memory chunk allocated on the current function's stack.
Put data to a constant-pool and get a memory reference to it.
Put a BYTE val
to a constant-pool.
Put a WORD val
to a constant-pool.
Put a DWORD val
to a constant-pool.
Put a QWORD val
to a constant-pool.
Put a WORD val
to a constant-pool.
Put a WORD val
to a constant-pool.
Put a DWORD val
to a constant-pool.
Put a DWORD val
to a constant-pool.
Put a QWORD val
to a constant-pool.
Put a QWORD val
to a constant-pool.
Put a SP-FP val
to a constant-pool.
Put a DP-FP val
to a constant-pool.
Force the compiler to not follow the conditional or unconditional jump.
Tell the compiler that the destination variable will be overwritten.
Invoke a function call without target
type enforcement.
Invoke a function call of the given target
and signature
and store the added node to out
.
Creates a new InvokeNode, initializes all the necessary members to match the given function signature
, adds the node to the compiler, and stores its pointer to out
. The operation is atomic, if anything fails nullptr is stored in out
and error code is returned.
This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.
This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.
This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.
This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.
Return from function.
This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.
This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.
Adds a jump to the given target
with the provided jump annotation
.
This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.
Called after the emitter was attached to CodeHolder
.
Reimplemented from asmjit::BaseCompiler.
Called after the emitter was detached from CodeHolder
.
Reimplemented from asmjit::BaseCompiler.
Finalizes this emitter.
Materializes the content of the emitter by serializing it to the attached CodeHolder through an architecture specific BaseAssembler. This function won't do anything if the emitter inherits from BaseAssembler as assemblers emit directly to a CodeBuffer held by CodeHolder. However, if this is an emitter that inherits from BaseBuilder or BaseCompiler then these emitters need the materialization phase as they store their content in a representation not visible to CodeHolder.
Reimplemented from asmjit::BaseEmitter.
© 2018-2024 AsmJit Authors | GitHub | Chat | Blog
AsmJit and AsmTK libraries are open source software released under the Zlib license and can be used safely in any open-source or commercial product, statically or dynamically linked, and without having to advertise the use of the libraries. Code snippets and examples are released into public domain, see Unlicense for more details.