Xbyak JIT Assembler

repository·master·Indexed 25 days ago

https://github.com/herumi/xbyak

A C++ header-only JIT assembler library for x86/x64 that enables dynamic assembly using Intel/MASM-like mnemonics. It supports modern instruction sets including AVX-512, APX, and AVX10.2, and is compatible with Windows, Linux, and macOS (Intel CPU). Key features include support for RIP-relative addressing, custom memory allocation strategies, and advanced encoding options for VNNI and APX architectures.

Tokens
5.3K
Snippets
16
Records
33
Agent score
81%

What's inside Xbyak

  1. Overview of Xbyak JIT Assembler

    master

    Xbyak is a C++ header-only library that enables dynamic assembly of x86/x64 instructions using Intel/MASM-like mnemonics. It supports advanced instruction sets including AVX-512, APX, and AVX10.2.

    Key Features:

    • Header-only integration.
    • Intel/MASM-like syntax.
    • Support for modern instruction sets (AVX-512, APX, AVX10.2).
    • Supported OS: Windows (XP through 11, 32/64-bit), Linux (32/64-bit), and macOS (Intel CPU only).
  2. Address data using RIP-relative addressing and Labels

    master

    In 64-bit mode, you can access data relative to the instruction pointer (rip). Using Xbyak::Label is the recommended way to handle byte offsets safely.

    Using Labels for Displacement

    When using a Label in an addressing expression (e.g., ptr[label + offset]), the offset is always treated as a byte offset.

    Caution with C++ Pointers

    If you use a raw C++ pointer (obtained via getCurr<T*>()) instead of a Label, be aware that Xbyak's +imm offset is in bytes, while C++ pointer arithmetic advances by sizeof(T). To avoid errors, cast the pointer to size_t or use Label instead.

  3. Use MmapAllocator for Unix-like systems

    master

    On Unix-like systems, you can use MmapAllocator by defining XBYAK_USE_MMAP_ALLOCATOR.

    By default, Xbyak uses posix_memalign for memory allocation. Using mprotect on many small allocations can exhaust the system's max_map_count (viewable in /proc/sys/vm/max_map_count). To avoid this when creating many Xbyak::CodeGenerator instances, use the MmapAllocator.

  4. Using AVX and AVX-512 instructions

    master

    Xbyak supports AVX and AVX-512 instructions.

    AVX-512 Specifics

    • Opmask Registers: Use k1 through k7. k0 is treated as no mask. For example, vmovaps(zmm0|k0, ptr[rax]) is equivalent to vmovaps(zmm0, ptr[rax]).
    • Masking/Suppression Flags: Instead of the NASM {z} or {sae} syntax, use bitwise OR with constants: | T_z, | T_sae, | T_rn_sae, | T_rd_sae, | T_ru_sae, or | T_rz_sae.
    • Broadcast: Use ptr_b for {1toX} broadcast operations, where X is automatically determined.
    • Memory Sizes: Use xword, yword, or zword(_b) to specify m128, m256, or m512 sizes when necessary.
  5. Xbyak instruction syntax and addressing

    master

    Xbyak uses a syntax similar to MASM/NASM but employs parentheses for function-like expressions (e.g., mov(eax, ebx) instead of mov eax, ebx).

    Addressing Modes

    To specify memory size explicitly, use qword, dword, word, or byte. If no size is specified, ptr is used by default.

    Syntax pattern: (ptr|qword|dword|word|byte) [base + index * (1|2|4|8) + displacement]

    Note: qword, dword, etc., are member variables; do not use dword as an unsigned integer type name.

  6. Avoid keyword conflicts with logical instructions

    master

    Because Xbyak uses mnemonics that overlap with C++ keywords (like and and or), you must use the underscored versions of these functions to avoid compilation errors:

    • Use and_() instead of and()
    • Use or_() instead of or()

    Alternatively, if you prefer using the standard names, you can pass the -fno-operator-names flag to gcc or clang during compilation.

  7. Use AutoGrow mode for dynamic memory expansion

    master

    By default, Xbyak::CodeGenerator has a fixed maximum size (default 4096 bytes). If you exceed this, an exception is thrown.

    In AutoGrow mode, the library automatically reallocates and expands the memory. To use it, pass Xbyak::AutoGrow to the CodeGenerator constructor.

    Critical Requirements:

    1. You must call ready() (or readyRE() for read/exec mode) before calling getCode(). This resolves jump addresses.
    2. Before calling ready(), any pointer obtained via getCurr() may become invalid due to reallocation. To safely use pointers, store the offset using getSize() and calculate the new pointer using getCode() + offset after ready() has completed.
    3. Relative addressing using [rip] is not supported in AutoGrow mode.
  8. Use string literals and the Label class for jumps and data addressing

    master

    Xbyak supports two ways to define labels: string literals and the Xbyak::Label class.

    String Literals

    Use L("name") to define a label. You can jump to it using jmp("name").

    • MASM-style local labels: Use @@, @f (forward), and @b (backward) for relative jumps.
    • Local labels: Labels starting with a period (e.g., .lp) are treated as local labels when wrapped between inLocalLabel() and outLocalLabel(). These can be nested.
    • Verification: Call hasUndefinedLabel() to ensure all labels are correctly defined.

    Label Class

    Use Xbyak::Label for more robust label management.

    • Jump Tables: Use putL(label) to emit labels into a jump table.
    • Binding: Use assignL(dstLabel, srcLabel) to bind a destination label to a source label (where srcLabel was created via L()).
    • Address Retrieval: label.getAddress() returns the address of the label (or 0 if not yet defined).
    // String literal example
    L("L1");
      jmp("L1");
    
    // Local label example
    inLocalLabel();
    L(".lp");
      jmp(".lp");
    outLocalLabel();
    
    // Label class example
    Xbyak::Label label1, label2;
    L(label1);
      jmp(label1);
    L(label2);
      jmp(label2);
  9. How to use Xbyak for JIT Assembly

    master

    To generate machine code, inherit from the Xbyak::CodeGenerator class. Inside your class methods, write x86/x64 assembly using a syntax similar to NASM (e.g., mov(eax, ebx);).

    After writing the assembly, call the getCode() method to retrieve a pointer to the generated machine code. You can then cast this pointer to a function pointer of your choice to execute the code.

    Note on Keywords: Since and and or are C++ keywords, use and_ and or_ instead. If you prefer using and/or, you must pass the -fno-operator-names flag to gcc or clang.

  10. Using Advanced Performance Extensions (APX)

    master

    Xbyak supports the Intel APX architecture, which includes:

    • Additional GPRs: r16 through r31 (and their 32-bit, 16-bit, and 8-bit counterparts).
    • Three-operand instructions: e.g., add(r20, r21, r23).
    • Status Flag Suppression: Use | T_nf to set EVEX.NF=1 (e.g., add(r20|T_nf, r21, r23)).
    • Zero Upper (ZU): Use | T_zu for imul and setcc instructions (e.g., imul(ax|T_zu, cx, 0x1234) or setb(r31b|T_zu)).
    // APX GPRs and 3-operand
    add(r20, r21, r23);
    lea(r30, ptr[r29+r31]);
    
    // APX Flag Suppression
    add(r20|T_nf, r21, r23);
    
    // APX Zero Upper
    imul(ax|T_zu, cx, 0x1234);
    setb(r31b|T_zu);
  11. Build Xbyak Samples with Visual Studio (Windows)

    master

    For Windows users, individual .vcxproj files are provided for use with the Visual Studio IDE or MSBuild.

    Using Visual Studio IDE

    1. Open the desired .vcxproj file (e.g., bf.vcxproj, test_util.vcxproj).
    2. Select your configuration (Debug/Release) and platform (x86/x64).
    3. Build via Build → Build Solution (or F7).

    Using MSBuild (Command Line)

    # Build a specific project in Release mode for x64
    msbuild test0.vcxproj /p:Configuration=Release /p:Platform=x64
    
    # Or build the entire solution
    msbuild xbyak_samples.sln /p:Configuration=Release
    msbuild test0.vcxproj /p:Configuration=Release /p:Platform=x64
  12. Build 32-bit Targets on Linux

    master

    To build 32-bit versions of the samples on a 64-bit Linux system, you must have multilib support installed.

    1. Install multilib (Ubuntu/Debian):
      sudo apt-get install gcc-multilib g++-multilib
    
    2. **Configure and Build:**
       Enable the `BUILD_32BIT_TARGETS` option during the CMake configuration step.
    
    ```bash
    cmake -DBUILD_32BIT_TARGETS=ON ..
    cmake --build . --config Release

    This will generate 32-bit versions of: test, bf, toyvm, test_util, memfunc, static_buf, jmp_table, and calc (if Boost is available).

    cmake -DBUILD_32BIT_TARGETS=ON ..
    cmake --build . --config Release