#register #assembly #head #regs

bin+lib cranelift-assembler-x64

A Cranelift-specific x64 assembler

4 releases

new 0.118.0 Mar 20, 2025
0.117.2 Feb 25, 2025
0.117.1 Feb 21, 2025
0.117.0 Feb 20, 2025

#2 in #head

Download history 5736/week @ 2025-02-18 14721/week @ 2025-02-25 16286/week @ 2025-03-04 17845/week @ 2025-03-11 16103/week @ 2025-03-18

66,282 downloads per month
Used in 43 crates (2 directly)

Apache-2.0 WITH LLVM-exception

57KB
1K SLoC

cranelift-assembler-x64

A Cranelift-specific x64 assembler. Unlike the existing cranelift-codegen assembler, this assembler uses instructions, not instruction classes, as the core abstraction.

Use

Like cranelift-codegen, using this assembler starts with enum Inst. For convenience, a main.rs script prints the path to this generated code:

$ cat $(cargo run) | head
...
pub enum Inst<R:Registers> {
    andb_i(andb_i),
    andw_i(andw_i),
    andl_i(andl_i),
    ...

Test

In order to check that this assembler emits correct machine code, we fuzz it against a known-good disassembler. We can run a quick, one-second check:

$ cargo test -- --nocapture

Or we can run the fuzzer indefinitely:

$ cargo +nightly fuzz run -s none roundtrip -j16

lib.rs:

A Cranelift-specific x64 assembler.

All instructions known to this assembler are listed in the inst module. The Inst enumeration contains a variant for each, allowing matching over all these instructions. All of this is parameterized by a Registers trait, allowing users of this assembler to plug in their own register types.

// Tell the assembler the type of registers we're using; we can always
// encode a HW register as a `u8` (e.g., `eax = 0`).
pub struct Regs;
impl Registers for Regs {
    type ReadGpr = u8;
    type ReadWriteGpr = u8;
    type ReadXmm = u8;
    type ReadWriteXmm = u8;
}

// Then, build one of the `AND` instructions; this one operates on an
// implicit `AL` register with an immediate. We can collect a sequence of
// instructions by converting to the `Inst` type.
let and = inst::andb_i::new(Imm8::new(0b10101010));
let seq: Vec<Inst<Regs>> = vec![and.into()];

// Now we can encode this sequence into a code buffer, checking that each
// instruction is valid in 64-bit mode.
let mut buffer = vec![];
let offsets = vec![];
for inst in seq {
    if inst.features().contains(&Feature::_64b) {
        inst.encode(&mut buffer, &offsets);
    }
}
assert_eq!(buffer, vec![0x24, 0b10101010]);

With an Inst, we can encode the instruction into a code buffer; see the example.

Dependencies

~0–5MB
~157K SLoC