Sign in

    asm

    A native-only x86-64 code emitter for MoonBit.

    x86-64
    jit
    assembler
    native
    Download zip
    Version
    0.0.1
    License
    Apache-2.0
    Last updated
    2 hours ago
    Downloads
    1

    No dependencies

    #ASM.mbt

    ASM.mbt is a small native-only x86-64 dynamic code emitter for MoonBit. A JITContext owns a writable memory buffer while it appends machine-code bytes, then finalizes the buffer into a callable JITFunction[() -> Int64].

    The project is intentionally minimal. It is useful for experiments, tests, and learning how instruction encodings fit together; it is not a complete assembler.

    #Quick Example

    ///|
    test {
    let ctx = @asm.JITContext::new()

    ctx.mov(@asm.reg(@asm.rax), @asm.imm32(18))
    ctx.add(@asm.reg(@asm.rax), @asm.imm32(4))
    ctx.sub(@asm.reg(@asm.rax), @asm.imm32(2))
    ctx.imul(@asm.reg(@asm.rax), @asm.imm32(3))
    ctx.ret()

    let fn0 = ctx.finalize_fn_i64()
    assert_eq(fn0.call(), 60)
    }

    #Operand Construction

    Register constants are Register values. Wrap them with reg(...) when passing them to instruction helpers, and use explicit immediate or memory helpers for the other operand kinds:

    ///|
    test {
    let ctx = @asm.JITContext::new()

    ctx.mov(@asm.reg(@asm.r8), @asm.imm64(42L))
    ctx.mov(@asm.mem64(base=@asm.rsp, disp=8), @asm.reg(@asm.r8))
    ctx.mov(@asm.reg(@asm.rax), @asm.mem64(base=@asm.rsp, disp=8))
    ctx.ret()
    }

    Memory operands always carry a data width: mem8, mem16, mem32, mem64, or the generic mem(size=...). Addressing is the 64-bit ModR/M + SIB form: optional base, optional index, scale 1/2/4/8, signed displacement, optional FS/GS, and dedicated RIP-relative helpers such as rip64(disp).

    #Supported Instructions

    The public API currently emits these instructions:

    • mov for register, memory, and immediate integer forms
    • add, sub, and xor for register/memory with register or immediate forms
    • mul and two-operand imul for integer register/memory or immediate forms
    • inc, dec, push, and pop for legal integer register/memory forms
    • loop_rel8(Label) for short RCX-counted loops
    • call(Target), syscall, nop, and ret

    Unsupported operand combinations raise ASMContextError instead of aborting.

    #Labels, Calls, and Tracing

    Labels capture the current byte offset in a single JITContext. Convert a label to a call target with Label::target. External call targets can be built with JITContext::extern_fn(name), which resolves a symbol in the current process and stores the relative target used by x86-64 CALL rel32.

    ///|
    test {
    let ctx = @asm.JITContext::new()

    let target = ctx.label()
    ctx.call(target.target())

    assert_eq(ctx.bytes().length(), 5)
    }

    trace() returns a copy of the successfully emitted opcode-form trace. The trace uses the public Instr enum, whose constructors name the concrete opcode form chosen by the encoder.

    ///|
    test {
    let ctx = @asm.JITContext::new()

    ctx.nop()
    ctx.ret()

    let trace = ctx.trace()
    let expected : Array[@asm.Instr] = [NopOp90, RetNearOpC3]
    assert_true(trace == expected)

    trace.push(SyscallOp0F05)
    assert_true(ctx.trace() == expected)
    }

    #Encoding Notes

    • 16-bit operands emit the 0x66 operand-size override.
    • 64-bit operands and extended registers emit REX prefixes as needed.
    • spl, bpl, sil, and dil force a bare REX prefix; ah, ch, dh, and bh cannot be used in any instruction that needs REX.
    • rsp and r12 as a memory base always require SIB. rbp and r13 without a displacement are encoded with disp8 = 0.
    • rsp cannot be a SIB index. r12 can be an index because REX.X distinguishes it from rsp.
    • RIP-relative memory always uses a signed disp32 field.

    #Errors and Validation

    Most instruction helpers validate the requested form before appending bytes or trace entries. ASMContextError variants cover unsupported instruction forms, operand-width mismatches, out-of-range immediates, invalid memory addressing, high-8 register conflicts with REX, short-loop displacement overflow, allocation failure, mprotect failure, generated code that exceeds the executable allocation, and repeated finalization.

    call currently emits directly and does not raise an error for an out-of-range rel32 displacement. Keep in-buffer labels and external call targets close enough for x86-64 relative calls until range validation is implemented.

    #Native Notes

    This package targets native only and uses a C stub for mmap, dlopen, dlsym, and execution. The generated code must follow the host ABI well enough to return through ret; when calling C functions, preserve stack alignment and callee/caller-saved registers as required by the platform.

    JITContext::new raises ASMContextError when executable memory allocation fails. finalize_fn_i64 raises before copying if the generated code exceeds the mapping size, then switches the mapping from writable to executable memory.

    On Linux, linux_syscall_number("getpid") and other names in the C stub's syscall table expose the platform's SYS_* values, which can be loaded into rax before emitting syscall.

    JITFunction::func_ref() returns the finalized code as the typed FuncRef stored by the JITFunction. For JITFunction[() -> Int64], JITFunction::call() is a convenience wrapper over that function reference.

    #Known Limitations

    • imm32, normal memory displacements, RIP-relative displacements, and call rel32 are written as 4-byte fields. Very large Int values are not all checked before emission yet, so keep them within the intended 32-bit range.
    • JITContext::extern_fn does not currently report dlopen or dlsym failure. A missing symbol can still produce a Target, so use known process symbols and avoid treating it as a general-purpose dynamic linker API.
    • Finalized mappings are intentionally not manually freed in v1, so any returned FuncRef cannot dangle because of an explicit dispose call.
    • The width-specific memory helpers use internal high-8 register sentinels for omitted base and index arguments. Do not pass ah as a memory base or ch as an index; use the generic mem(size=..., base=..., index=...) helper when you need explicit optional registers.

    ASMContextError

    pub(all) suberror ASMContextError {
    UnsupportedAddOperands(Operand, Operand)
    UnsupportedSubOperands(Operand, Operand)
    UnsupportedXorOperands(Operand, Operand)
    UnsupportedMulOperand(Operand)
    UnsupportedIncOperand(Operand)
    UnsupportedDecOperand(Operand)
    LoopRel8OutOfRange(Int)
    UnsupportedIMulOperands(Operand, Operand)
    UnsupportedMovOperands(Operand, Operand)
    UnsupportedPushOperand(Operand)
    UnsupportedPopOperand(Operand)
    ExecutableMemoryAllocationFailed(UInt64, Int)
    GeneratedCodeExceedsMemory(UInt64, UInt64)
    ExecutableMemoryProtectionFailed(UInt64, UInt64, Int)
    JITContextAlreadyFinalized
    UnsupportedOperandWidth(Operand)
    OperandWidthMismatch(Width, Width)
    ImmediateOutOfRange(Immediate)
    InvalidMemoryScale(Int)
    InvalidMemoryBase(Register)
    InvalidMemoryIndex(Register)
    MemoryIndexCannotBeStackPointer(Register)
    High8RexConflict
    } derive(
    Debug
    )

    Errors raised when an instruction helper cannot encode the requested form.

    GprBase

    pub(all) enum GprBase {
    A
    C
    D
    B
    SP
    BP
    SI
    DI
    R8
    R9
    R10
    R11
    R12
    R13
    R14
    R15
    } derive(Eq,
    Debug
    )

    The 16 x86-64 general-purpose register bases.

    GprBase::equal

    fn GprBase::equal(GprBase, GprBase) -> Bool

    GprBase::not_equal

    fn GprBase::not_equal(x : GprBase, y : GprBase) -> Bool

    GprBase::to_repr

    High8Reg

    pub(all) enum High8Reg {
    AH
    CH
    DH
    BH
    } derive(Eq,
    Debug
    )

    Legacy high 8-bit register names. These cannot be encoded in instructions that require any REX prefix.

    High8Reg::equal

    fn High8Reg::equal(High8Reg, High8Reg) -> Bool

    High8Reg::not_equal

    fn High8Reg::not_equal(x : High8Reg, y : High8Reg) -> Bool

    High8Reg::to_repr

    Immediate

    pub(all) enum Immediate {
    Imm8(Int)
    Imm16(Int)
    Imm32(Int)
    Imm64(Int64)
    } derive(Eq,
    Debug
    )

    Immediate operand with an explicit encoded width.

    Immediate::equal

    fn Immediate::equal(Immediate, Immediate) -> Bool

    Immediate::not_equal

    fn Immediate::not_equal(x : Immediate, y : Immediate) -> Bool

    Instr

    pub(all) enum Instr {
    RetNearOpC3
    NopOp90
    SyscallOp0F05
    CallRel32OpE8(Target, Int)
    LoopRcxRel8OpE2(Label, Int)
    MovRm8Reg8Op88(Operand, Register)
    MovRmRegOp89(Operand, Register)
    MovReg8Rm8Op8A(Register, Operand)
    MovRegRmOp8B(Register, Operand)
    MovReg8Imm8OpB0(Register, Immediate)
    MovRegImmOpB8(Register, Immediate)
    MovRm8Imm8OpC6Ext0(Operand, Immediate)
    MovRmImmOpC7Ext0(Operand, Immediate)
    AddRm8Reg8Op00(Operand, Register)
    AddRmRegOp01(Operand, Register)
    AddReg8Rm8Op02(Register, Operand)
    AddRegRmOp03(Register, Operand)
    AddRm8Imm8Op80Ext0(Operand, Immediate)
    AddRmImmOp81Ext0(Operand, Immediate)
    AddRmImm8SignExtOp83Ext0(Operand, Immediate)
    SubRm8Reg8Op28(Operand, Register)
    SubRmRegOp29(Operand, Register)
    SubReg8Rm8Op2A(Register, Operand)
    SubRegRmOp2B(Register, Operand)
    SubRm8Imm8Op80Ext5(Operand, Immediate)
    SubRmImmOp81Ext5(Operand, Immediate)
    SubRmImm8SignExtOp83Ext5(Operand, Immediate)
    XorRm8Reg8Op30(Operand, Register)
    XorRmRegOp31(Operand, Register)
    XorReg8Rm8Op32(Register, Operand)
    XorRegRmOp33(Register, Operand)
    XorRm8Imm8Op80Ext6(Operand, Immediate)
    XorRmImmOp81Ext6(Operand, Immediate)
    XorRmImm8SignExtOp83Ext6(Operand, Immediate)
    MulRm8OpF6Ext4(Operand)
    MulRmOpF7Ext4(Operand)
    IMulRegRmOp0FAF(Register, Operand)
    IMulRegRmImm8Op6B(Register, Operand, Immediate)
    IMulRegRmImmOp69(Register, Operand, Immediate)
    IncRm8OpFEExt0(Operand)
    IncRmOpFFExt0(Operand)
    DecRm8OpFEExt1(Operand)
    DecRmOpFFExt1(Operand)
    PushRegOp50(Register)
    PushRmOpFFExt6(Operand)
    PushImm8Op6A(Immediate)
    PushImmOp68(Immediate)
    PopRegOp58(Register)
    PopRmOp8FExt0(Operand)
    } derive(Eq,
    Debug
    )

    Debug trace of the concrete x86 opcode forms emitted into a JITContext.

    The encoder records these values alongside raw bytes so tests and future diagnostics can inspect the encoded instruction stream without decoding machine code back from memory.

    Instr::equal

    fn Instr::equal(Instr, Instr) -> Bool

    Instr::not_equal

    fn Instr::not_equal(x : Instr, y : Instr) -> Bool

    Instr::to_repr

    JITContext

    type JITContext

    Mutable builder for one x86-64 machine-code buffer.

    A JITContext owns one writable code mapping while it builds machine code. It appends encoded bytes to an internal buffer and tracks the byte offset used by labels and relative jumps.

    JITContext::add

    fn JITContext::add(self : JITContext, lhs : Operand, rhs : Operand) -> Unit raise ASMContextError

    Emit ADD.

    Supported forms include reg/mem += reg, reg += mem, and reg/mem += imm when operand widths and immediate encoding widths are legal.

    JITContext::bytes

    fn JITContext::bytes(self : JITContext) -> Bytes

    Return a copy of the generated machine-code bytes.

    JITContext::call

    fn JITContext::call(self : JITContext, target : Target) -> Unit

    Emit CALL rel32 to either an in-buffer label or an external symbol.

    JITContext::dec

    fn JITContext::dec(self : JITContext, operand : Operand) -> Unit raise ASMContextError

    Emit DEC.

    Supports byte, word, dword, and qword register/memory operands.

    JITContext::extern_fn

    fn JITContext::extern_fn(self : JITContext, name : String) -> Target

    Resolve a symbol in the current process for use with call.

    The returned target stores an offset relative to this context's code buffer, matching x86-64 CALL rel32 encoding.

    JITContext::finalize_fn_i64

    fn JITContext::finalize_fn_i64(self : JITContext) -> JITFunction[() -> Int64] raise ASMContextError

    Copy the generated code into its mapping and make it executable.

    The generated function is expected to follow the platform ABI enough to return its result in RAX and preserve any registers required by its caller.

    JITContext::imul

    fn JITContext::imul(self : JITContext, lhs : Operand, rhs : Operand) -> Unit raise ASMContextError

    Emit signed IMUL.

    Supported forms are two-operand imul(reg, reg/mem) and imul(reg, imm). The register/register form uses the standard 0F AF /r encoding.

    JITContext::inc

    fn JITContext::inc(self : JITContext, operand : Operand) -> Unit raise ASMContextError

    Emit INC.

    Supports byte, word, dword, and qword register/memory operands.

    JITContext::label

    fn JITContext::label(self : JITContext) -> Label

    Capture the current byte offset as a label.

    Use this before emitting a backward branch or as a local call target.

    JITContext::loop_rel8

    fn JITContext::loop_rel8(self : JITContext, label : Label) -> Unit raise ASMContextError

    Emit LOOP with a signed 8-bit relative displacement.

    The branch target must be within -128..127 bytes from the next instruction. RCX is decremented by the CPU and the branch is taken while it is nonzero.

    JITContext::mov

    fn JITContext::mov(self : JITContext, lhs : Operand, rhs : Operand) -> Unit raise ASMContextError

    Emit MOV.

    Supports register/register, register/memory, memory/register, register/immediate, and memory/immediate integer forms. Memory operands must carry an explicit width.

    JITContext::mul

    fn JITContext::mul(self : JITContext, operand : Operand) -> Unit raise ASMContextError

    Emit unsigned MUL, which multiplies AL/AX/EAX/RAX by the operand.

    The implicit accumulator and result registers are defined by the operand width, following the x86 integer MUL r/m instruction.

    JITContext::new

    fn JITContext::new(mem_size? : UInt64) -> JITContext raise ASMContextError

    Create a new code-generation context backed by one writable memory mapping.

    JITContext::nop

    fn JITContext::nop(self : JITContext) -> Unit

    Emit a one-byte NOP instruction.

    JITContext::pop

    fn JITContext::pop(self : JITContext, operand : Operand) -> Unit raise ASMContextError

    Emit POP.

    Supports 16-bit and 64-bit register/memory operands.

    JITContext::push

    fn JITContext::push(self : JITContext, operand : Operand) -> Unit raise ASMContextError

    Emit PUSH.

    Supports 16-bit and 64-bit register/memory operands, plus encodable immediate forms.

    JITContext::ret

    fn JITContext::ret(self : JITContext) -> Unit

    Emit a RET instruction.

    JITContext::sub

    fn JITContext::sub(self : JITContext, lhs : Operand, rhs : Operand) -> Unit raise ASMContextError

    Emit SUB.

    Supported forms mirror add, with the left operand as the destination.

    JITContext::syscall

    fn JITContext::syscall(self : JITContext) -> Unit

    Emit SYSCALL.

    The caller is responsible for loading the platform-specific syscall number and arguments into the required registers.

    JITContext::trace

    fn JITContext::trace(self : JITContext) -> Array[Instr]

    Return a copy of the successfully emitted instruction trace.

    JITContext::xor

    fn JITContext::xor(self : JITContext, lhs : Operand, rhs : Operand) -> Unit raise ASMContextError

    Emit XOR.

    Supported forms mirror add, with the left operand as the destination.

    JITFunction

    type JITFunction[T]

    Finalized machine-code function with a known callable signature.

    JITFunction::call

    fn JITFunction::call(self : JITFunction[() -> Int64]) -> Int64

    Call this finalized function.

    JITFunction::get_fn

    fn[T] JITFunction::get_fn(self : JITFunction[T]) -> FuncRef[T]

    Return this finalized function as a callable MoonBit function reference.

    Label

    A position inside a JITContext code buffer.

    Labels store byte offsets, not absolute addresses, so they are only valid for the context that created them.

    Label::equal

    fn Label::equal(Label, Label) -> Bool

    Label::not_equal

    fn Label::not_equal(x : Label, y : Label) -> Bool

    Label::target

    fn Label::target(self : Label) -> Target

    Convert a label into a call target.

    Label::to_repr

    Memory

    pub(all) struct Memory {
    size : Width
    base : Register?
    index : Register?
    scale : Int
    disp : Int
    segment : Segment?
    rip_relative : Bool
    } derive(Eq,
    Debug
    )

    ModR/M + optional SIB memory operand.

    base and index are 64-bit address registers when present. rip_relative selects the special 64-bit mode [rip + disp32] encoding and ignores base, index, and scale.

    Memory::equal

    fn Memory::equal(Memory, Memory) -> Bool

    Memory::not_equal

    fn Memory::not_equal(x : Memory, y : Memory) -> Bool

    Memory::to_repr

    Operand

    pub(all) enum Operand {
    Reg(Register)
    Imm(Immediate)
    Mem(Memory)
    Rel(RelTarget)
    } derive(Eq,
    Debug
    )

    Operand forms accepted by instruction helpers.

    Operand::equal

    fn Operand::equal(Operand, Operand) -> Bool

    Operand::not_equal

    fn Operand::not_equal(x : Operand, y : Operand) -> Bool

    Operand::to_repr

    Register

    pub(all) enum Register {
    Gpr(GprBase, Width)
    High8(High8Reg)
    } derive(Eq,
    Debug
    )

    x86-64 integer register operand.

    Register::equal

    fn Register::equal(Register, Register) -> Bool

    Register::not_equal

    fn Register::not_equal(x : Register, y : Register) -> Bool

    Register::to_repr

    RelTarget

    pub(all) enum RelTarget {
    RelLabel(Label)
    Rel32(Int)
    } derive(Eq,
    Debug
    )

    Relative target operand reserved for instruction forms that encode relative immediates.

    RelTarget::equal

    fn RelTarget::equal(RelTarget, RelTarget) -> Bool

    RelTarget::not_equal

    fn RelTarget::not_equal(x : RelTarget, y : RelTarget) -> Bool

    Segment

    pub(all) enum Segment {
    FS
    GS
    } derive(Eq,
    Debug
    )

    Segment override supported by 64-bit integer memory operands.

    Segment::equal

    fn Segment::equal(Segment, Segment) -> Bool

    Segment::not_equal

    fn Segment::not_equal(x : Segment, y : Segment) -> Bool

    Segment::to_repr

    Target

    pub(all) enum Target {
    Label(Label)
    Extern(Int)
    } derive(Eq,
    Debug
    )

    Destination accepted by JITContext::call.

    Label is an in-buffer target. Extern stores an offset from the current code buffer start to an external function pointer so CALL can still emit a relative displacement.

    Target::equal

    fn Target::equal(Target, Target) -> Bool

    Target::not_equal

    fn Target::not_equal(x : Target, y : Target) -> Bool

    Target::to_repr

    Width

    pub(all) enum Width {
    W8
    W16
    W32
    W64
    } derive(Eq,
    Debug
    )

    Integer operand width used by registers, immediates, and memory operands.

    Width::equal

    fn Width::equal(Width, Width) -> Bool

    Width::not_equal

    fn Width::not_equal(x : Width, y : Width) -> Bool

    Width::to_repr

    let ah : Register

    let al : Register

    let ax : Register

    let bh : Register

    let bl : Register

    let bp : Register

    bpl

    let bpl : Register

    let bx : Register

    let ch : Register

    let cl : Register

    let cx : Register

    let dh : Register

    let di : Register

    dil

    let dil : Register

    let dl : Register

    let dx : Register

    eax

    let eax : Register

    ebp

    let ebp : Register

    ebx

    let ebx : Register

    ecx

    let ecx : Register

    edi

    let edi : Register

    edx

    let edx : Register

    esi

    let esi : Register

    esp

    let esp : Register

    imm16

    fn imm16(value : Int) -> Operand

    Build a 16-bit immediate operand.

    Values must fit in the 16-bit encoded field used by the selected instruction form.

    imm32

    fn imm32(value : Int) -> Operand

    Build a 32-bit immediate operand.

    Many 64-bit integer instructions encode imm32 and sign-extend it to the operand width, matching the x86-64 instruction tables.

    imm64

    fn imm64(value : Int64) -> Operand

    Build a 64-bit immediate operand.

    This is primarily used by mov r64, imm64; most other integer instruction forms do not have an imm64 encoding.

    imm8

    fn imm8(value : Int) -> Operand

    Build an 8-bit immediate operand.

    The encoder accepts signed 8-bit values and byte-sized unsigned literals in the range 0..255; out-of-range values raise ImmediateOutOfRange.

    linux_syscall_number

    fn linux_syscall_number(name : String) -> Int?

    Look up a Linux syscall number by canonical syscall name, such as "getpid" or "write".

    Returns None when the current native target does not expose that syscall macro, or when the name is not in the stub's syscall table.

    mem

    fn mem(size~ : Width, base? : Register?, index? : Register?, scale? : Int, disp? : Int, segment? : Segment?) -> Operand

    Build an explicit-width memory operand.

    base and index must be 64-bit GPRs when present. scale must be one of 1, 2, 4, or 8. Use the width-specific helpers (mem8, mem16, mem32, mem64) for the common forms.

    mem16

    fn mem16(base? : Register, index? : Register, scale? : Int, disp? : Int, segment? : Segment?) -> Operand

    Build a 16-bit memory operand.

    This controls the data width, not the address width; addresses are still 64-bit mode addresses.

    mem32

    fn mem32(base? : Register, index? : Register, scale? : Int, disp? : Int, segment? : Segment?) -> Operand

    Build a 32-bit memory operand.

    mem64

    fn mem64(base? : Register, index? : Register, scale? : Int, disp? : Int, segment? : Segment?) -> Operand

    Build a 64-bit memory operand.

    Examples include mem64(base=rax), mem64(base=rbp, disp=-8), and mem64(base=rax, index=r12, scale=4, disp=8).

    mem8

    fn mem8(base? : Register, index? : Register, scale? : Int, disp? : Int, segment? : Segment?) -> Operand

    Build an 8-bit memory operand such as [rax + rdx * 4 - 8].

    Omitting both base and index encodes an absolute disp32 memory operand.

    r10

    let r10 : Register

    r10b

    let r10b : Register

    r10d

    let r10d : Register

    r10w

    let r10w : Register

    r11

    let r11 : Register

    r11b

    let r11b : Register

    r11d

    let r11d : Register

    r11w

    let r11w : Register

    r12

    let r12 : Register

    r12b

    let r12b : Register

    r12d

    let r12d : Register

    r12w

    let r12w : Register

    r13

    let r13 : Register

    r13b

    let r13b : Register

    r13d

    let r13d : Register

    r13w

    let r13w : Register

    r14

    let r14 : Register

    r14b

    let r14b : Register

    r14d

    let r14d : Register

    r14w

    let r14w : Register

    r15

    let r15 : Register

    r15b

    let r15b : Register

    r15d

    let r15d : Register

    r15w

    let r15w : Register

    let r8 : Register

    r8b

    let r8b : Register

    r8d

    let r8d : Register

    r8w

    let r8w : Register

    let r9 : Register

    r9b

    let r9b : Register

    r9d

    let r9d : Register

    r9w

    let r9w : Register

    rax

    let rax : Register

    rbp

    let rbp : Register

    rbx

    let rbx : Register

    rcx

    let rcx : Register

    rdi

    let rdi : Register

    rdx

    let rdx : Register

    reg

    fn reg(register : Register) -> Operand

    Wrap a register value as an instruction operand.

    Register constants such as rax, eax, r8, and r8b are plain Register values, so instruction calls use reg(rax) when a register operand is needed.

    rip16

    fn rip16(disp : Int, segment? : Segment?) -> Operand

    Build a 16-bit RIP-relative memory operand.

    rip32

    fn rip32(disp : Int, segment? : Segment?) -> Operand

    Build a 32-bit RIP-relative memory operand.

    rip64

    fn rip64(disp : Int, segment? : Segment?) -> Operand

    Build a 64-bit RIP-relative memory operand.

    RIP-relative memory always uses the ModR/M mod=00 r/m=101 form with a disp32 field in 64-bit mode.

    rip8

    fn rip8(disp : Int, segment? : Segment?) -> Operand

    Build an 8-bit RIP-relative memory operand.

    rsi

    let rsi : Register

    rsp

    let rsp : Register

    let si : Register

    sil

    let sil : Register

    let sp : Register

    spl

    let spl : Register