Core

Globals, code storage, and emitter interface.

Overview

AsmJit library uses CodeHolder to hold code during code generation and emitters inheriting from BaseEmitter to emit code. CodeHolder uses containers to manage its data:

  • Section - stores information about a code or data section.
  • CodeBuffer - stores actual code or data, part of Section.
  • LabelEntry - stores information about a label - its name, offset, section where it belongs to, and other bits.
  • LabelLink - stores information about yet unbound label, which was already used by the assembler.
  • RelocEntry - stores information about a relocation.
  • AddressTableEntry - stores information about an address, which was used in a jump or call. Such address may need relocation.

To generate code you would need to instantiate at least the following classes:

There are also other core classes that are important:

  • Environment - describes where the code will run. Environment brings the concept of target triples or tuples into AsmJit, which means that users can specify target architecture, platform, and ABI.
  • TypeId - encapsulates lightweight type functionality that can be used to describe primitive and vector types. Types are used by higher level utilities, for example by Function and Compiler.
  • CpuInfo - encapsulates CPU information - stores both CPU information and CPU features described by CpuFeatures.

AsmJit also provides global constants:

  • Globals - namespace that provides global constants.
  • ByteOrder - byte-order constants and functionality.
Note
CodeHolder examples use x86::Assembler as abstract interfaces cannot be used to generate code.

CodeHolder & Emitters

The example below shows how the mentioned classes interact to generate X86 code:

#include <asmjit/x86.h>
#include <stdio.h>
using namespace asmjit;
// Signature of the generated function.
typedef int (*Func)(void);
int main() {
JitRuntime rt; // Runtime specialized for JIT code execution.
CodeHolder code; // Holds code and relocation information.
code.init(rt.environment()); // Initialize code to match the JIT environment.
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; // Holds address to the generated function.
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;
}

The example above used x86::Assembler as an emitter. AsmJit provides the following emitters that offer various levels of abstraction:

Targets and JitRuntime

AsmJit's Target is an interface that provides basic target abstraction. At the moment AsmJit provides only one implementation called JitRuntime, which as the name suggests provides JIT code target and execution runtime. JitRuntime provides all the necessary stuff to implement a simple JIT compiler with basic memory management. It only provides JitRuntime::add() and JitRuntime::release() functions that are used to either add code to the runtime or release it. JitRuntime doesn't do any decisions on when the code should be released, the decision is up to the developer.

See more at Virtual Memory group.

More About Environment

In the previous example the Environment is retrieved from JitRuntime. It's logical as JitRuntime always returns an Environment that is compatible with the host. For example if your application runs on X86_64 CPU the Environment returned will use Arch::kX64 architecture in contrast to Arch::kX86, which will be used in 32-bit mode on an X86 target.

AsmJit allows to setup the Environment manually and to select a different architecture and ABI when necessary. So let's do something else this time, let's always generate a 32-bit code and print its binary representation. To do that, we can create our own Environment and initialize it to Arch::kX86.

#include <asmjit/x86.h>
#include <stdio.h>
using namespace asmjit;
int main(int argc, char* argv[]) {
using namespace asmjit::x86;
// Create a custom environment initialized to 32-bit X86 architecture.
CodeHolder code; // Create a CodeHolder.
code.init(env); // Initialize CodeHolder with custom environment.
// Generate a 32-bit function that sums 4 floats and looks like:
// void func(float* dst, const float* a, const float* b)
x86::Assembler a(&code); // Create and attach x86::Assembler to `code`.
a.mov(eax, dword_ptr(esp, 4)); // Load the destination pointer.
a.mov(ecx, dword_ptr(esp, 8)); // Load the first source pointer.
a.mov(edx, dword_ptr(esp, 12)); // Load the second source pointer.
a.movups(xmm0, ptr(ecx)); // Load 4 floats from [ecx] to XMM0.
a.movups(xmm1, ptr(edx)); // Load 4 floats from [edx] to XMM1.
a.addps(xmm0, xmm1); // Add 4 floats in XMM1 to XMM0.
a.movups(ptr(eax), xmm0); // Store the result to [eax].
a.ret(); // Return from function.
// We have no Runtime this time, it's on us what we do with the code.
// CodeHolder stores code in Section, which provides some basic properties
// and CodeBuffer structure. We are interested in section's CodeBuffer.
//
// NOTE: The first section is always '.text', it can be retrieved by
// code.sectionById(0) or simply by code.textSection().
CodeBuffer& buffer = code.textSection()->buffer();
// Print the machine-code generated or do something else with it...
// 8B4424048B4C24048B5424040F28010F58010F2900C3
for (size_t i = 0; i < buffer.length; i++)
printf("%02X", buffer.data[i]);
return 0;
}

Explicit Code Relocation

In addition to Environment, CodeHolder can be configured to specify a base-address (or a virtual base address in a linker terminology), which could be static (useful when you know the location where the target's machine code will be) or dynamic. AsmJit assumes dynamic base-address by default and relocates the code held by CodeHolder to a user provided address on-demand. To be able to relocate to a user provided address it needs to store some information about relocations, which is represented by RelocEntry. Relocation entries are only required if you call external functions from the generated code that cannot be encoded by using a 32-bit displacement (64-bit displacements are not provided by aby supported architecture).

There is also a concept called LabelLink - label link is a lightweight data structure that doesn't have any identifier and is stored in LabelEntry as a single-linked list. Label link represents either unbound yet used label and cross-sections links (only relevant to code that uses multiple sections). Since crossing sections is something that cannot be resolved immediately these links persist until offsets of these sections are assigned and until CodeHolder::resolveUnresolvedLinks() is called. It's an error if you end up with code that has unresolved label links after flattening. You can verify it by calling CodeHolder::hasUnresolvedLinks(), which inspects the value returned by CodeHolder::unresolvedLinkCount().

AsmJit can flatten code that uses multiple sections by assigning each section an incrementing offset that respects its alignment. Use CodeHolder::flatten() to do that. After the sections are flattened their offsets and virtual sizes are adjusted to respect each section's buffer size and alignment. The CodeHolder::resolveUnresolvedLinks() function must be called before relocating the code held by CodeHolder. You can also flatten your code manually by iterating over all sections and calculating their offsets (relative to base) by your own algorithm. In that case CodeHolder::flatten() should not be called, however, CodeHolder::resolveUnresolvedLinks() should be.

The example below shows how to use a built-in virtual memory allocator JitAllocator instead of using JitRuntime (just in case you want to use your own memory management) and how to relocate the generated code into your own memory block - you can use your own virtual memory allocator if you prefer that, but that's OS specific and not covered by the documentation.

The following code is similar to the previous one, but implements a function working in both 32-bit and 64-bit environments:

#include <asmjit/x86.h>
#include <stdio.h>
using namespace asmjit;
typedef void (*SumIntsFunc)(int* dst, const int* a, const int* b);
int main() {
// Create a custom environment that matches the current host environment.
CodeHolder code; // Create a CodeHolder.
code.init(env); // Initialize CodeHolder with environment.
x86::Assembler a(&code); // Create and attach x86::Assembler to `code`.
// Signature: 'void func(int* dst, const int* a, const int* b)'.
x86::Gp dst;
x86::Gp src_a;
x86::Gp src_b;
// Handle the difference between 32-bit and 64-bit calling conventions
// (arguments passed through stack vs. arguments passed by registers).
if (env.is32Bit()) {
dst = x86::eax;
src_a = x86::ecx;
src_b = x86::edx;
a.mov(dst , x86::dword_ptr(x86::esp, 4));
a.mov(src_a, x86::dword_ptr(x86::esp, 8));
a.mov(src_b, x86::dword_ptr(x86::esp, 12));
}
else {
if (env.isPlatformWindows()) {
dst = x86::rcx; // First argument (destination pointer).
src_a = x86::rdx; // Second argument (source 'a' pointer).
src_b = x86::r8; // Third argument (source 'b' pointer).
}
else {
dst = x86::rdi; // First argument (destination pointer).
src_a = x86::rsi; // Second argument (source 'a' pointer).
src_b = x86::rdx; // Third argument (source 'b' pointer).
}
}
a.movdqu(x86::xmm0, x86::ptr(src_a)); // Load 4 ints from [src_a] to XMM0.
a.movdqu(x86::xmm1, x86::ptr(src_b)); // Load 4 ints from [src_b] to XMM1.
a.paddd(x86::xmm0, x86::xmm1); // Add 4 ints in XMM1 to XMM0.
a.movdqu(x86::ptr(dst), x86::xmm0); // Store the result to [dst].
a.ret(); // Return from function.
// Even when we didn't use multiple sections AsmJit could insert one section
// called '.addrtab' (address table section), which would be filled by data
// required by relocations (absolute jumps and calls). You can omit this code
// if you are 100% sure your code doesn't contain multiple sections and
// such relocations. You can use `CodeHolder::hasAddressTable()` to verify
// whether the address table section does exist.
code.flatten();
// After the code was generated it can be relocated manually to any memory
// location, however, we need to know it's size before we perform memory
// allocation. `CodeHolder::codeSize()` returns the worst estimated code
// size in case that relocations are not possible without trampolines (in
// that case some extra code at the end of the current code buffer is
// generated during relocation).
size_t estimatedSize = code.codeSize();
// Instead of rolling up our own memory allocator we can use the one AsmJit
// provides. It's decoupled so you don't need to use `JitRuntime` for that.
JitAllocator allocator;
// Allocate an executable virtual memory and handle a possible failure.
void* p = allocator.alloc(estimatedSize);
if (!p)
return 0;
// Now relocate the code to the address provided by the memory allocator.
// Please note that this DOESN'T COPY anything to `p`. This function will
// store the address in CodeHolder and use relocation entries to patch the
// existing code in all sections to respect the base address provided.
code.relocateToBase((uint64_t)p);
// This is purely optional. There are cases in which the relocation can omit
// unneeded data, which would shrink the size of address table. If that
// happened the codeSize returned after relocateToBase() would be smaller
// than the originally `estimatedSize`.
size_t codeSize = code.codeSize();
// This will copy code from all sections to `p`. Iterating over all sections
// and calling `memcpy()` would work as well, however, this function supports
// additional options that can be used to also zero pad sections' virtual
// size, etc.
//
// With some additional features, copyFlattenData() does roughly this:
// for (Section* section : code.sections())
// memcpy((uint8_t*)p + section->offset(),
// section->data(),
// section->bufferSize());
// Execute the generated function.
int inA[4] = { 4, 3, 2, 1 };
int inB[4] = { 1, 5, 2, 8 };
int out[4];
// This code uses AsmJit's ptr_as_func<> to cast between void* and SumIntsFunc.
ptr_as_func<SumIntsFunc>(p)(out, inA, inB);
// Prints {5 8 4 9}
printf("{%d %d %d %d}\n", out[0], out[1], out[2], out[3]);
// Release 'p' is it's no longer needed. It will be destroyed with 'vm'
// instance anyway, but it's a good practice to release it explicitly
// when you know that the function will not be needed anymore.
allocator.release(p);
return 0;
}

If you know the base-address in advance (before the code generation) it can be passed as a second argument to CodeHolder::init(). In that case the Assembler will know the absolute position of each instruction and would be able to use it during instruction encoding to prevent relocations where possible. The following example shows how to configure the base address:

#include <asmjit/x86.h>
#include <stdio.h>
using namespace asmjit;
void initializeCodeHolder(CodeHolder& code) {
uint64_t baseAddress = uint64_t(0x1234);
// initialize CodeHolder with environment and custom base address.
code.init(env, baseAddress);
}

Label Offsets and Links

When a label that is not yet bound is used by the Assembler, it creates a LabelLink, which is then added to a LabelEntry. These links are also created if a label is used in a different section than in which it was bound. Let's examine some functions that can be used to check whether there are any unresolved links.

#include <asmjit/core.h>
#include <stdio.h>
using namespace asmjit;
void labelLinksExample(CodeHolder& code, const Label& label) {
// Tests whether the `label` is bound.
bool isBound = code.isLabelBound(label);
printf("Label %u is %s\n", label.id(), isBound ? "bound" : "not bound");
// Returns true if the code contains either referenced, but unbound
// labels, or cross-section label links that are not resolved yet.
bool hasUnresolved = code.hasUnresolvedLinks(); // Boolean answer.
size_t nUnresolved = code.unresolvedLinkCount(); // Count of unresolved links.
printf("Number of unresolved links: %zu\n", nUnresolved);
}

There is no function that would return the number of unbound labels as this is completely unimportant from CodeHolder's perspective. If a label is not used then it doesn't matter whether it's bound or not, only actually used labels matter. After a Label is bound it's possible to query its offset offset relative to the start of the section where it was bound:

#include <asmjit/core.h>
#include <stdio.h>
using namespace asmjit;
void labelOffsetExample(CodeHolder& code, const Label& label) {
// Label offset is known after it's bound. The offset provided is relative
// to the start of the section, see below for alternative. If the given
// label is not bound the offset returned will be zero. It's recommended
// to always check whether the label is bound before using its offset.
uint64_t sectionOffset = code.labelOffset(label);
printf("Label offset relative to section: %llu\n", (unsigned long long)sectionOffset);
// If you use multiple sections and want the offset relative to the base.
// NOTE: This function expects that the section has already an offset and
// the label-link was resolved (if this is not true you will still get an
// offset relative to the start of the section).
uint64_t baseOffset = code.labelOffsetFromBase(label);
printf("Label offset relative to base: %llu\n", (unsigned long long)baseOffset);
}

Sections

AsmJit allows to create multiple sections within the same CodeHolder. A test-case asmjit_test_x86_sections.cpp can be used as a reference point although the following example should also provide a useful insight:

#include <asmjit/x86.h>
#include <stdio.h>
using namespace asmjit;
void sectionsExample(CodeHolder& code) {
// Text section is always provided as the first section.
Section* text = code.textSection(); // or code.sectionById(0);
// To create another section use CodeHolder::newSection().
Section* data;
Error err = code.newSection(&data,
".data", // Section name
SIZE_MAX, // Name length if the name is not null terminated (or SIZE_MAX).
SectionFlags::kNone, // Section flags, see SectionFlags.
8, // Section alignment, must be power of 2.
0); // Section order value (optional, default 0).
// When you switch sections in Assembler, Builder, or Compiler the cursor
// will always move to the end of that section. When you create an Assembler
// the cursor would be placed at the end of the first (.text) section, which
// is initially empty.
x86::Assembler a(&code);
Label L_Data = a.newLabel();
a.mov(x86::eax, x86::ebx); // Emits in .text section.
a.section(data); // Switches to the end of .data section.
a.bind(L_Data); // Binds label in this .data section
a.db(0x01); // Emits byte in .data section.
a.section(text); // Switches to the end of .text section.
a.add(x86::ebx, x86::eax); // Emits in .text section.
// References a label in .text section, which was bound in .data section.
// This would create a LabelLink even when the L_Data is already bound,
// because the reference crosses sections. See below...
a.lea(x86::rsi, x86::ptr(L_Data));
}

The last line in the example above shows that a LabelLink would be created even for bound labels that cross sections. In this case a referenced label was bound in another section, which means that the link couldn't be resolved at that moment. If your code uses sections, but you wish AsmJit to flatten these sections (you don't plan to flatten them manually) then there is an API for that.

#include <asmjit/x86.h>
#include <stdio.h>
using namespace asmjit;
// ... (continuing the previous example) ...
void sectionsExampleContinued(CodeHolder& code) {
// Suppose we have some code that contains multiple sections and
// we would like to flatten it by using AsmJit's built-in API:
Error err = code.flatten();
if (err) {
// There are many reasons it can fail, so always handle a possible error.
printf("Failed to flatten the code: %s\n", DebugUtils::errorAsString(err));
exit(1);
}
// After flattening all sections would contain assigned offsets
// relative to base. Offsets are 64-bit unsigned integers so we
// cast them to `size_t` for simplicity. On 32-bit targets it's
// guaranteed that the offset cannot be greater than `2^32 - 1`.
printf("Data section offset %zu", size_t(data->offset()));
// The flattening doesn't resolve unresolved label links, this
// has to be done manually as flattening can be done separately.
err = code.resolveUnresolvedLinks();
if (err) {
// This is the kind of error that should always be handled...
printf("Failed to resolve label links: %s\n", DebugUtils::errorAsString(err));
exit(1);
}
if (code.hasUnresolvedLinks()) {
// This would mean either unbound label or some other issue.
printf("The code has %zu unbound labels\n", code.unresovedLinkCount());
exit(1);
}
}

Namespaces

Classes

Macros

Enumerations

Functions

Variables

Macro Definition Documentation

#define ASMJIT_LIBRARY_VERSION 0x010900 /* 1.9.0* /

AsmJit library version in (Major << 16) | (Minor << 8) | (Patch) format.

#define ASMJIT_ABI_NAMESPACE _abi_1_9

AsmJit ABI namespace is an inline namespace within asmjit namespace.

It's used to make sure that when user links to an incompatible version of AsmJit, it won't link. It has also some additional properties as well. When ASMJIT_ABI_NAMESPACE is defined by the user it would override the AsmJit default, which makes it possible to use use multiple AsmJit libraries within a single project, totally controlled by the users. This is useful especially in cases in which some of such library comes from a third party.

Enumeration Type Documentation

Arch : uint8_tenumstrong

Instruction set architecture (ISA).

ConstantDescription
kUnknown 

Unknown or uninitialized ISA.

kX86 

32-bit X86 ISA.

kX64 

64-bit X86 ISA also known as X64, X86_64, and AMD64.

kRISCV32 

32-bit RISC-V ISA.

kRISCV64 

64-bit RISC-V ISA.

kARM 

32-bit ARM ISA (little endian).

kAArch64 

64-bit ARM ISA in (little endian).

kThumb 

32-bit ARM ISA in Thumb mode (little endian).

kMIPS32_LE 

32-bit MIPS ISA in (little endian).

kMIPS64_LE 

64-bit MIPS ISA in (little endian).

kARM_BE 

32-bit ARM ISA (big endian).

kAArch64_BE 

64-bit ARM ISA in (big endian).

kThumb_BE 

32-bit ARM ISA in Thumb mode (big endian).

kMIPS32_BE 

32-bit MIPS ISA in (big endian).

kMIPS64_BE 

64-bit MIPS ISA in (big endian).

kMaxValue 

Maximum value of Arch.

k32BitMask 

Mask used by 32-bit ISAs (odd are 32-bit, even are 64-bit).

kBigEndian 

First big-endian architecture.

kHost 

ISA detected at compile-time (ISA of the host).

SubArch : uint8_tenumstrong

Sub-architecture.

ConstantDescription
kUnknown 

Unknown or uninitialized architecture sub-type.

kMaxValue 

Maximum value of SubArch.

kHost 

Sub-architecture detected at compile-time (sub-architecture of the host).

ArchTypeNameId : uint8_tenumstrong

Identifier used to represent names of different data types across architectures.

ConstantDescription
kDB 

Describes 'db' (X86/X86_64 convention, always 8-bit quantity).

kDW 

Describes 'dw' (X86/X86_64 convention, always 16-bit word).

kDD 

Describes 'dd' (X86/X86_64 convention, always 32-bit word).

kDQ 

Describes 'dq' (X86/X86_64 convention, always 64-bit word).

kByte 

Describes 'byte' (always 8-bit quantity).

kHalf 

Describes 'half' (most likely 16-bit word).

kWord 

Describes 'word' (either 16-bit or 32-bit word).

kHWord 

Describes 'hword' (most likely 16-bit word).

kDWord 

Describes 'dword' (either 32-bit or 64-bit word).

kQWord 

Describes 'qword' (64-bit word).

kXWord 

Describes 'xword' (64-bit word).

kShort 

Describes 'short' (always 16-bit word).

kLong 

Describes 'long' (most likely 32-bit word).

kQuad 

Describes 'quad' (64-bit word).

kMaxValue 

Maximum value of ArchTypeNameId.

InstHints : uint8_tenumstrong

Instruction feature hints for each register group provided by ArchTraits.

Instruction feature hints describe miscellaneous instructions provided by the architecture that can be used by register allocator to make certain things simpler - like register swaps or emitting register push/pop sequences.

Remarks
Instruction feature hints are only defined for register groups that can be used with Compiler infrastructure. Register groups that are not managed by Compiler are not provided by ArchTraits and cannot be queried.
ConstantDescription
kNoHints 

No feature hints.

kRegSwap 

Architecture supports a register swap by using a single instructio.

kPushPop 

Architecture provides push/pop instructions.

CodeBufferFlags : uint32_tenumstrong

Flags used by CodeBuffer.

ConstantDescription
kNone 

No flags.

kIsExternal 

Buffer is external (not allocated by asmjit).

kIsFixed 

Buffer is fixed (cannot be reallocated).

ExpressionOpType : uint8_tenumstrong

Operator type that can be used within an Expression.

ConstantDescription
kAdd 

Addition.

kSub 

Subtraction.

kMul 

Multiplication.

kSll 

Logical left shift.

kSrl 

Logical right shift.

kSra 

Arithmetic right shift.

ExpressionValueType : uint8_tenumstrong

Value tyoe that can be used within an Expression.

ConstantDescription
kNone 

No value or invalid.

kConstant 

Value is 64-bit unsigned integer (constant).

kLabel 

Value is LabelEntry, which references a Label.

kExpression 

Value is Expression.

SectionFlags : uint32_tenumstrong

Section flags, used by Section.

ConstantDescription
kNone 

No flags.

kExecutable 

Executable (.text sections).

kReadOnly 

Read-only (.text and .data sections).

kZeroInitialized 

Zero initialized by the loader (BSS).

kComment 

Info / comment flag.

kImplicit 

Section created implicitly, can be deleted by Target.

CopySectionFlags : uint32_tenumstrong

Flags that can be used with CodeHolder::copySectionData() and CodeHolder::copyFlattenedData().

ConstantDescription
kNone 

No flags.

kPadSectionBuffer 

If virtual size of a section is greater than the size of its CodeBuffer then all bytes between the buffer size and virtual size will be zeroed.

If this option is not set then those bytes would be left as is, which means that if the user didn't initialize them they would have a previous content, which may be unwanted.

kPadTargetBuffer 

Clears the target buffer if the flattened data is less than the destination size.

This option works only with CodeHolder::copyFlattenedData() as it processes multiple sections. It is ignored by CodeHolder::copySectionData().

OffsetType : uint8_tenumstrong

Offset format type, used by OffsetFormat.

ConstantDescription
kSignedOffset 

A value having _immBitCount bits and shifted by _immBitShift.

This offset type is sufficient for many targets that store offset as a continuous set bits within an instruction word / sequence of bytes.

kUnsignedOffset 

An unsigned value having _immBitCount bits and shifted by _immBitShift.

kAArch64_ADR 

AARCH64 ADR format of [.|immlo:2|.....|immhi:19|.....].

kAArch64_ADRP 

AARCH64 ADRP format of [.|immlo:2|.....|immhi:19|.....] (4kB pages).

kMaxValue 

Maximum value of OffsetFormatType.

RelocType : uint32_tenumstrong

Relocation type.

ConstantDescription
kNone 

None/deleted (no relocation).

kExpression 

Expression evaluation, _payload is pointer to Expression.

kAbsToAbs 

Relocate absolute to absolute.

kRelToAbs 

Relocate relative to absolute.

kAbsToRel 

Relocate absolute to relative.

kX64AddressEntry 

Relocate absolute to relative or use trampoline.

LabelType : uint8_tenumstrong

Type of the Label.

ConstantDescription
kAnonymous 

Anonymous label that can optionally have a name, which is only used for debugging purposes.

kLocal 

Local label (always has parentId).

kGlobal 

Global label (never has parentId).

kExternal 

External label (references an external symbol).

kMaxValue 

Maximum value of LabelType.

AlignMode : uint8_tenumstrong

Align mode, used by BaseEmitter::align().

ConstantDescription
kCode 

Align executable code.

kData 

Align non-executable code.

kZero 

Align by a sequence of zeros.

kMaxValue 

Maximum value of AlignMode.

EmitterType : uint8_tenumstrong

Emitter type used by BaseEmitter.

ConstantDescription
kNone 

Unknown or uninitialized.

kAssembler 

Emitter inherits from BaseAssembler.

kBuilder 

Emitter inherits from BaseBuilder.

kCompiler 

Emitter inherits from BaseCompiler.

kMaxValue 

Maximum value of EmitterType.

EmitterFlags : uint8_tenumstrong

Emitter flags, used by BaseEmitter.

ConstantDescription
kNone 

No flags.

kAttached 

Emitter is attached to CodeHolder.

kLogComments 

The emitter must emit comments.

kOwnLogger 

The emitter has its own Logger (not propagated from CodeHolder).

kOwnErrorHandler 

The emitter has its own ErrorHandler (not propagated from CodeHolder).

kFinalized 

The emitter was finalized.

kDestroyed 

The emitter was destroyed.

This flag is used for a very short time when an emitter is being destroyed by CodeHolder.

EncodingOptions : uint32_tenumstrong

Encoding options.

ConstantDescription
kNone 

No encoding options.

kOptimizeForSize 

Emit instructions that are optimized for size, if possible.

Default: false.

X86 Specific

When this option is set it the assembler will try to fix instructions if possible into operation equivalent instructions that take less bytes by taking advantage of implicit zero extension. For example instruction like mov r64, imm and and r64, imm can be translated to mov r32, imm and and r32, imm when the immediate constant is lesser than 2^31.

kOptimizedAlign 

Emit optimized code-alignment sequences.

Default: false.

X86 Specific

Default align sequence used by X86 architecture is one-byte (0x90) opcode that is often shown by disassemblers as NOP. However there are more optimized align sequences for 2-11 bytes that may execute faster on certain CPUs. If this feature is enabled AsmJit will generate specialized sequences for alignment between 2 to 11 bytes.

kPredictedJumps 

Emit jump-prediction hints.

Default: false.

X86 Specific

Jump prediction is usually based on the direction of the jump. If the jump is backward it is usually predicted as taken; and if the jump is forward it is usually predicted as not-taken. The reason is that loops generally use backward jumps and conditions usually use forward jumps. However this behavior can be overridden by using instruction prefixes. If this option is enabled these hints will be emitted.

This feature is disabled by default, because the only processor that used to take into consideration prediction hints was P4. Newer processors implement heuristics for branch prediction and ignore static hints. This means that this feature can be only used for annotation purposes.

DiagnosticOptions : uint32_tenumstrong

Diagnostic options are used to tell emitters and their passes to perform diagnostics when emitting or processing user code.

These options control validation and extra diagnostics that can be performed by higher level emitters.

Instruction Validation

BaseAssembler implementation perform by default only basic checks that are necessary to identify all variations of an instruction so the correct encoding can be selected. This is fine for production-ready code as the assembler doesn't have to perform checks that would slow it down. However, sometimes these checks are beneficial especially when the project that uses AsmJit is in a development phase, in which mistakes happen often. To make the experience of using AsmJit seamless it offers validation features that can be controlled by DiagnosticOptions.

Compiler Diagnostics

Diagnostic options work with BaseCompiler passes (precisely with its register allocation pass). These options can be used to enable logging of all operations that the Compiler does.

ConstantDescription
kNone 

No validation options.

kValidateAssembler 

Perform strict validation in BaseAssembler::emit() implementations.

This flag ensures that each instruction is checked before it's encoded into a binary representation. This flag is only relevant for BaseAssembler implementations, but can be set in any other emitter type, in that case if that emitter needs to create an assembler on its own, for the purpose of BaseEmitter::finalize() it would propagate this flag to such assembler so all instructions passed to it are explicitly validated.

Default: false.

kValidateIntermediate 

Perform strict validation in BaseBuilder::emit() and BaseCompiler::emit() implementations.

This flag ensures that each instruction is checked before an InstNode representing the instruction is created by BaseBuilder or BaseCompiler. This option could be more useful than kValidateAssembler in cases in which there is an invalid instruction passed to an assembler, which was invalid much earlier, most likely when such instruction was passed to Builder/Compiler.

This is a separate option that was introduced, because it's possible to manipulate the instruction stream emitted by BaseBuilder and BaseCompiler - this means that it's allowed to emit invalid instructions (for example with missing operands) that will be fixed later before finalizing it.

Default: false.

kRAAnnotate 

Annotate all nodes processed by register allocator (Compiler/RA).

Note
Annotations don't need debug options, however, some debug options like kRADebugLiveness may influence their output (for example the mentioned option would add liveness information to per-instruction annotation).
kRADebugCFG 

Debug CFG generation and other related algorithms / operations (Compiler/RA).

kRADebugLiveness 

Debug liveness analysis (Compiler/RA).

kRADebugAssignment 

Debug register allocation assignment (Compiler/RA).

kRADebugUnreachable 

Debug the removal of code part of unreachable blocks.

kRADebugAll 

Enable all debug options (Compiler/RA).

Vendor : uint8_tenumstrong

Vendor.

Note
AsmJit doesn't use vendor information at the moment. It's provided for future use, if required.
ConstantDescription
kUnknown 

Unknown or uninitialized platform vendor.

kMaxValue 

Maximum value of PlatformVendor.

kHost 

Platform vendor detected at compile-time.

Platform : uint8_tenumstrong

Platform - runtime environment or operating system.

ConstantDescription
kUnknown 

Unknown or uninitialized platform.

kWindows 

Windows OS.

kOther 

Other platform that is not Windows, most likely POSIX based.

kLinux 

Linux OS.

kHurd 

GNU/Hurd OS.

kFreeBSD 

FreeBSD OS.

kOpenBSD 

OpenBSD OS.

kNetBSD 

NetBSD OS.

kDragonFlyBSD 

DragonFly BSD OS.

kHaiku 

Haiku OS.

kOSX 

Apple OSX.

kIOS 

Apple iOS.

kTVOS 

Apple TVOS.

kWatchOS 

Apple WatchOS.

kEmscripten 

Emscripten platform.

kMaxValue 

Maximum value of Platform.

kHost 

Platform detected at compile-time (platform of the host).

PlatformABI : uint8_tenumstrong

Platform ABI (application binary interface).

ConstantDescription
kUnknown 

Unknown or uninitialied environment.

kMSVC 

Microsoft ABI.

kGNU 

GNU ABI.

kAndroid 

Android Environment / ABI.

kCygwin 

Cygwin ABI.

kMaxValue 

Maximum value of PlatformABI.

kHost 

Host ABI detected at compile-time.

ObjectFormat : uint8_tenumstrong

Object format.

Note
AsmJit doesn't really use anything except ObjectFormat::kUnknown and ObjectFormat::kJIT at the moment. Object file formats are provided for future extensibility and a possibility to generate object files at some point.
ConstantDescription
kUnknown 

Unknown or uninitialized object format.

kJIT 

JIT code generation object, most likely JitRuntime or a custom Target implementation.

kELF 

Executable and linkable format (ELF).

kCOFF 

Common object file format.

kXCOFF 

Extended COFF object format.

kMachO 

Mach object file format.

kMaxValue 

Maximum value of ObjectFormat.

ByteOrderenumstrong

Byte order.

ConstantDescription
kLE 

Little endian.

kBE 

Big endian.

kNative 

Native byte order of the target architecture.

kSwapped 

Swapped byte order of the target architecture.

ResetPolicy : uint32_tenumstrong

A policy that can be used with some reset() member functions.

ConstantDescription
kSoft 

Soft reset, doesn't deallocate memory (default).

kHard 

Hard reset, releases all memory used, if any.

TypeId : uint8_tenumstrong

Type identifier provides a minimalist type system used across AsmJit library.

This is an additional information that can be used to describe a value-type of physical or virtual register. It's used mostly by BaseCompiler to describe register representation (the group of data stored in the register and the width used) and it's also used by APIs that allow to describe and work with function signatures.

ConstantDescription
kVoid 

Void type.

kIntPtr 

Abstract signed integer type that has a native size.

kUIntPtr 

Abstract unsigned integer type that has a native size.

kInt8 

8-bit signed integer type.

kUInt8 

8-bit unsigned integer type.

kInt16 

16-bit signed integer type.

kUInt16 

16-bit unsigned integer type.

kInt32 

32-bit signed integer type.

kUInt32 

32-bit unsigned integer type.

kInt64 

64-bit signed integer type.

kUInt64 

64-bit unsigned integer type.

kFloat32 

32-bit floating point type.

kFloat64 

64-bit floating point type.

kFloat80 

80-bit floating point type.

kMask8 

8-bit opmask register (K).

kMask16 

16-bit opmask register (K).

kMask32 

32-bit opmask register (K).

kMask64 

64-bit opmask register (K).

kMmx32 

64-bit MMX register only used for 32 bits.

kMmx64 

64-bit MMX register.