Sign in

    qbe

    QBE reimplemented in MoonBit: the optimizer pipeline plus a programmatic IR builder, an in-memory aarch64 JIT, and LoongArch64/WASM backends

    qbe
    compiler
    jit
    ssa
    Download zip
    Author
    Version
    0.32.0
    License
    Apache-2.0
    Last updated
    19 days ago
    Downloads
    58

    Dependencies

    #Qbe.mbt

    A QBE reimplementation in MoonBit — the upstream compiler plus a programmatic IR builder, an in-memory JIT, and LoongArch64/WASM backends

    #Features

    • Byte-faithful QBE core: the optimizer pipeline and the amd64 / arm64 / rv64 backends, checked byte-for-byte against the pinned vendor/qbe reference (661ceb2) — every debug stage and the emitted assembly (409/409 per target)
    • Extra backends: LoongArch64 and WASM, verified on every reference-compilable input with clang's LoongArch assembler and moon-wasm-opt respectively (python tools/check_all_backends.py)
    • Programmatic IR builder (ir_builder, C ABI ir_builder_capi, include/qbe_builder.h): construct QBE IL from MoonBit or C with no .ssa round-trip, print it back, or emit assembly text, a Mach-O arm64 object, or a JIT-ready code image
    • In-memory JIT for macOS/aarch64 (jit/): map and call arm64 machine code with no toolchain in the path, resolving libc via dlsym and host callbacks; usable from the C ABI or Rust
    • Rust bindings (rust/, crates.io qopple): Cranelift/inkwell-style Context / Module / FunctionBuilder / ins(), including Module::jit
    • Variadic functions (...): declare, emit va_start/va_arg and call them from the MoonBit builder, the C ABI or Rust. Every backend lowers them (amd64/arm64/rv64 reference-identical, la64 via the LP64D save area, wasm32 via a caller-built buffer), and the interpreter executes them directly (python tools/check_vararg_runtime.py)
    • Layered runtime: native/ (executable memory, symbol lookup, temp files, process spawn, dynamic linking) shared by jit/ and run_asm/
    • Debug info: the IL dbgfile/dbgloc statements emit .file/.loc DWARF line tables (byte-identical to the reference); -g adds a DWARF4 compilation unit, variables/types from the builder, and CFI/.eh_frame, giving source-level lldb debugging with frame variable (doc/debug_info.md)

    #Status

    • amd64 / arm64 / rv64 debug dumps and assembly are byte-identical to vendor/qbe; the self-contained arm64 object matches clang on all 336 compilable cases (python tools/check_route_b.py)
    • la64 emits assemblable GNU-as LoongArch for all 338 reference-compilable tests (python tools/check_la64_asm.py); wasm emits validator-clean WAT for all 338 (python tools/check_wasm.py)
    • --jit FUNC[,ARG]... runs code in-process; --run-asm FUNC[,ARG] does the same through clang; --run-wasm FUNC[,ARG] runs the WASM backend under node; --emit obj writes a self-contained Mach-O arm64 .o
    • dbgfile/dbgloc line info is emitted for amd64/arm64/rv64/la64 and matches vendor/qbe on every debug flag; -g adds a DWARF4 CU (subprograms, variables, types, .debug_loc) + CFI (.eh_frame), verified end to end under lldb

    #Roadmap

    • Debug info: per-scope variable ranges, rv64/la64 CFI, and DWARF in the self-contained object / JIT path (doc/debug_info.md)
    • (TODO) IR debugger

    #Contributors

    #Quick start

    Install a prebuilt binary — no MoonBit compiler or C toolchain needed:

    curl -fsSL https://i2pl.com/install-qbe-mbt.sh | sh

    The installer detects your OS and architecture and downloads the matching release asset — qbe-macos-aarch64, qbe-linux-x86_64 or qbe-linux-aarch64 — verifying its SHA-256 checksum before installing it on your PATH. (Intel macOS has no MoonBit toolchain and is not supported.) Use --version v0.32.0 to pin a release, --bin-dir DIR to choose the directory, or --no-path to leave your shell configuration alone.

    #From source

    ./scripts/bootstrap.sh is an interactive installer (rustup-style): it asks how to get the MoonBit compiler, builds the native qbe binary and offers to put it on your PATH.

    Building from source requires the MoonBit toolchain with moonc >= 0.10.14 (moon version --all prints the version).

    git clone https://github.com/azhzx/qbe.mbt.git cd qbe.mbt ./scripts/bootstrap.sh

    +--------------------------------------------+ | qbe.mbt - QBE reimplemented in MoonBit | +--------------------------------------------+ [1/3] MoonBit compiler How should we get the MoonBit compiler? 1) Use the moon on PATH (/Users/you/.moon/bin/moon) 2) Install MoonBit (Homebrew / install.sh) 3) Use a custom moon path Choose [1]: [2/3] Building qbe /Users/you/.moon/bin/moon build --target native ok built _build/native/debug/build/cmd/main/main.exe [3/3] Shell integration Add qbe to your PATH (install to /Users/you/.local/bin) [Y/n] ok installed /Users/you/.local/bin/qbe ok added /Users/you/.local/bin to /Users/you/.zshrc qbe.mbt is ready.

    Non-interactive options:

    ./scripts/bootstrap.sh --skip-install # use the moon on PATH, never install ./scripts/bootstrap.sh --install-moon # install MoonBit, then build ./scripts/bootstrap.sh --moon /opt/moon/bin/moon ./scripts/bootstrap.sh --yes --no-path # scripted build, leave PATH alone ./scripts/bootstrap.sh --with-reference # also build vendor/qbe (diff tests)

    Colors are used only on a TTY; set NO_COLOR=1 to disable them.

    On Windows, scripts/bootstrap.ps1 follows the same three steps:

    git clone https://github.com/azhzx/qbe.mbt.git cd qbe.mbt ./scripts/bootstrap.ps1

    It accepts -Moon PATH, -InstallMoon, -SkipInstall, -BinDir DIR, -NoPath, -WithReference, -Target native|wasm and -Yes. The native qbe.exe (route B object/JIT) currently targets macOS/Linux because the native shim uses POSIX mmap/dlfcn; on Windows the installer falls back to the default target and skips the PATH step.

    Documentation:

    #License

    qbe.mbt is licensed under the Apache License 2.0, an OSI-approved license. It is a MoonBit reimplementation of QBE (MIT, (c) Quentin Carbonneaux); the reference is vendored as a git submodule under vendor/qbe. Third-party notices are collected in THIRD_PARTY_NOTICES.md.

    BSet

    using @azhzx/qbe/types { type BSet }

    Blk

    using @azhzx/qbe/types { type Blk }

    Class

    using @azhzx/qbe/types { type Class }

    Class - corresponds to enum Class in all.h

    Con

    using @azhzx/qbe/types { type Con }

    Dat

    using @azhzx/qbe/types { type Dat }

    using @azhzx/qbe/types { type Fn }

    Ins

    using @azhzx/qbe/types { type Ins }

    InterpValue

    Jump

    using @azhzx/qbe/types { type Jump }

    using @azhzx/qbe/types { type Op }

    Ref

    using @azhzx/qbe/types { type Ref }

    ADT replacing C bit-field (uint type:3; uint val:29)

    Tmp

    using @azhzx/qbe/types { type Tmp }

    Typ

    using @azhzx/qbe/types { type Typ }

    Target

    pub(all) enum Target {
    Amd64Sysv
    Wasm32
    Rv64
    La64
    Arm64
    }

    arm64_function_signature

    fn arm64_function_signature(text : String, name : String) -> (Array[Int], Int) raise

    compile

    fn compile(text : String, gas? : String) -> Result[String,
    QbeError
    ]

    compile_arm64

    fn compile_arm64(text : String, gas? : String) -> Result[String,
    QbeError
    ]

    compile_arm64_bin

    fn compile_arm64_bin(text : String) -> Array[Array[Int]] raise

    compile_arm64_bin_module

    fn compile_arm64_bin_module(text : String) ->
    Arm64BinModule
    raise

    compile_arm64_bin_module_ex

    compile_arm64_debug

    fn compile_arm64_debug(text : String, flags : String) -> Result[String,
    QbeError
    ]

    compile_arm64_object

    fn compile_arm64_object(text : String) -> Bytes raise

    compile_debug

    fn compile_debug(text : String, flags : String) -> Result[String,
    QbeError
    ]

    compile_ir_asm

    fn compile_ir_asm(funcs : Array[
    Fn
    ], datas : Array[
    Dat
    ], order : Array[String], typs : Array[
    Typ
    ], interner :
    Interner
    , gas? : String) -> String raise

    compile_ir_object

    fn compile_ir_object(funcs : Array[
    Fn
    ], datas : Array[
    Dat
    ], typs : Array[
    Typ
    ], interner :
    Interner
    ) -> Bytes raise

    compile_la64

    fn compile_la64(text : String, gas? : String) -> Result[String,
    QbeError
    ]

    compile_la64_debug

    fn compile_la64_debug(text : String, flags : String) -> Result[String,
    QbeError
    ]

    compile_rv64

    fn compile_rv64(text : String, gas? : String) -> Result[String,
    QbeError
    ]

    compile_rv64_debug

    fn compile_rv64_debug(text : String, flags : String) -> Result[String,
    QbeError
    ]

    compile_target

    fn compile_target(text : String, target : Target, gas? : String) -> Result[String,
    QbeError
    ]

    compile_target_debug

    fn compile_target_debug(text : String, target : Target, flags : String) -> Result[String,
    QbeError
    ]

    compile_wasm

    fn compile_wasm(text : String) -> Result[String,
    QbeError
    ]

    compile_wasm_debug

    fn compile_wasm_debug(text : String, flags : String) -> Result[String,
    QbeError
    ]

    interpret

    fn interpret(src : String, entry? : String, args? : Array[
    InterpValue
    ], max_steps? : Int, max_depth? : Int, hook? : (String, Array[
    InterpValue
    ]) ->
    InterpValue
    ??) -> Result[(
    InterpValue
    , String),
    QbeError
    ]