#jit-compiler #compiler #llvm #jit #codegen #back-end

bin+lib b3

A Rust port of B3 WebKit codegen backend

10 releases

0.1.9 Oct 26, 2023
0.1.8 Sep 24, 2023
0.1.4 Jul 13, 2023
0.1.1 Jun 28, 2023

#121 in Programming languages

BSD-2-Clause

1.5MB
39K SLoC

Rust 30K SLoC // 0.1% comments GNU Style Assembly 7.5K SLoC Ruby 1K SLoC // 0.1% comments

b3-rs

Backend based on Bare Bones Backend from WebKit. I am making it for educational purposes.

Usage

See examples and src/tests.rs.

Here's iterative factorial generator:

// imports ARGUMENT_GPR0
use b3::macroassembler::jit::gpr_info::*;

fn main() {
    let opts = b3::Options::default();
    let mut proc = b3::Procedure::new(opts);

    // Create entry block. The argument to `add_block` is block frequency. 
    // Blocks with different frequences are ordered differently. By default
    // they are recalculated before lowering to Assembly IR.
    // set `opts.estimate_static_execution_counts` to `false` to use user provided frequency.
    let entry = proc.add_block(1.0);

    let mut builder = b3::BasicBlockBuilder::new(&mut proc, entry);

    // Argument management is offloaded to client code. Here we load argument from first GPR argument register,
    // it is RDI on x86-64
    let number = builder.argument(b3::Reg::new_gpr(ARGUMENT_GPR0), b3::Type::Int32);

    // Declare variables. They are automatically converted to SSA form.
    let i = builder.procedure.add_variable(b3::Type::Int32);
    let factorial = builder.procedure.add_variable(b3::Type::Int32);

    // Create blocks for `for` loop. 
    let for_header = builder.procedure.add_block(1.0);
    let for_body = builder.procedure.add_block(1.0);
    let for_exit = builder.procedure.add_block(1.0);

    let one = builder.const32(1);
    builder.var_set(factorial, one);
    builder.var_set(i, one);

    builder.jump(Some(for_header));

    builder.block = for_header;

    let i_value = builder.var_get(i);
    let cmp = builder.binary(b3::Opcode::LessEqual, i_value, number);

    // Conditional branch.
    builder.branch(cmp, for_body, (for_exit, b3::Frequency::Normal));

    builder.block = for_body;

    let i_value = builder.var_get(i);
    let factorial_value = builder.var_get(factorial);
    let mul = builder.binary(b3::Opcode::Mul, i_value, factorial_value);
    builder.var_set(factorial, mul);

    let i_value = builder.var_get(i);
    let one = builder.const32(1);
    let add = builder.binary(b3::Opcode::Add, i_value, one);

    builder.var_set(i, add);

    builder.jump(Some(for_header));

    builder.block = for_exit;

    let factorial_value = builder.var_get(factorial);
    builder.return_(Some(factorial_value));

    // Compiles B3 IR down to machine code. 
    let compilation = b3::compile(proc);
    // use `entrypoint(0)` to get first entrypoint of code. 
    // NOTE: Multiple entrypoints are not yet supported.
    let func: extern "C" fn(i32) -> i32 = unsafe { std::mem::transmute(compilation.entrypoint(0)) };

    assert_eq!(func(5), 120);
}

Contributors

  • PROMETHIA - helped to fix UB and port B3 to stable toolchain.

Licensing

Original B3 code belongs to WebKit and is licensed under BSD-2. Rust port of B3 because of that is also licensed with BSD-2 license.

Dependencies

~4–21MB
~329K SLoC