IndexCompiler

Compiler [¶]

Compiler interface.

Overview

BaseCompiler is a high-level interface, which provides register allocation and support for defining and invoking functions, built on top of BaseBuilder interface At the moment it's the easiest way of generating code in AsmJit as most architecture and OS specifics is properly abstracted and handled by AsmJit automatically. However, abstractions also mean restrictions, which means that BaseCompiler has more limitations than BaseAssembler or BaseBuilder.

Since BaseCompiler provides register allocation it also establishes the concept of functions - a function in Compiler sense is a unit in which virtual registers are allocated into physical registers by the register allocator. In addition, it enables to use such virtual registers in function invocations.

BaseCompiler automatically handles function calling conventions. It's still architecture dependent, but makes the code generation much easies. Functions are essential; the first-step to generate some code is to define a signature of the function to be generated (before generating the function body itself). Function arguments and return value(s) are handled by assigning virtual registers to them. Similarly, function calls are handled the same way.

Compiler Nodes [¶]

BaseCompiler adds some nodes that are required for function generation and invocation:

BaseCompiler also makes the use of passes (Pass) and automatically adds an architecture-dependent register allocator pass to the list of passes when attached to CodeHolder.

Compiler Examples [¶]

  • x86::Compiler - Compiler implementation targeting X86 and X86_64 architectures.
  • a64::Compiler - Compiler implementation targeting AArch64 architecture.

Compiler Tips [¶]

Users of AsmJit have done mistakes in the past, this section should provide some useful tips for beginners:

  • Virtual registers in compiler are bound to a single function. At the moment the implementation doesn't care whether a single virtual register is used in multiple functions, but it sees it as two independent virtual registers in that case. This means that virtual registers cannot be used to implement global variables. Global variables are basically memory addresses which functions can read from and write to, and they have to be implemented in the same way.
  • Compiler provides a useful debugging functionality, which can be turned on through FormatFlags. Use Logger::add_flags() to turn on additional logging features when using Compiler.

Enumerations

VirtRegFlags : uint8_tenum class[¶]

Flags associated with a virtual register VirtReg.

ConstantDescription
kIsFixed 

True if this is a fixed register, never reallocated.

kIsStackArea 

True if the virtual register is only used as a stack area (never accessed as register). Stack area is allocated via BaseCompiler::_new_stack() and then architecture dependent compilers like x86::Compiler::new_stack().

kHasStackSlot 

True if the virtual register has a stack slot.

Stack slots are assigned by the register allocator - so initially when a VirtReg is created this flag would not be set. When a virtual register is spilled, stack slot is automatically created for the register and the VirtReg::_stack_offset member is updated. Stack areas will always have associated stack slot during register allocation.

kAlignmentLog2Mask 

Virtual register log2(alignment) mask (for spilling) (3 bits in flags).

Note

For space purposes the alignment is stored as log2(alignment). So the alignment is 1 << log2(alignment).