Getting started
Quick start
Rayzor compiles Haxe 4.x to native code. Picking what you want — a fast dev loop, a shippable binary, a portable artifact — means picking a command.
$ cargo install --git https://github.com/rayzor-blade/rayzor
$ rayzor init --name my-app
$ rayzor run src/Main.hx
Compilation modes
The same MIR goes through a different backend depending on the command.
You want to
Command
What happens
Run code while developing
rayzor run main.hx
Tiered JIT: starts interpreted, promotes hot functions to Cranelift, then LLVM
Type-check only
rayzor check main.hx
Parse and type-check, no codegen
Ship a native binary
rayzor aot main.hx -o app
Whole program through LLVM, linked against the runtime
Ship one portable artifact
rayzor bundle main.hx
Serialized MIR in a single .rzb, run later with rayzor run
Target the browser or WASI
rayzor build --target wasm
WebAssembly module; --browser also emits an HTML harness
Inspect the pipeline
rayzor compile --stage mir
Stop at any stage — ast, tast, hir, mir, native — and print it
Tiering
rayzor run starts in the MIR interpreter so execution begins immediately, then promotes a function to a compiled tier once it has run enough times. A preset is a policy, not a backend.
script
CLI tools and one-shot scripts — instant startup, no promotion
application
Desktop apps and web servers — balanced, includes LLVM (default)
server
Long-running services — aggressive optimization
benchmark
Performance testing — immediate bailout, manual LLVM upgrade
development
Debugging — verbose logging
embedded
Constrained environments — interpreter only
--tier-thresholds <I/W/H[/B]> overrides the preset's thresholds directly.
Memory model
There is no garbage collector. The compiler decides at compile time when a value is freed, from last-use and escape analysis, with a MIR-level pass as the backstop for anything the HIR analysis cannot see. Ownership is opt-in through annotations.
@:move class UniqueResource { ... } // move semantics, no aliasing
@:arc class SharedState { ... } // atomic reference counting
@:derive([Send, Sync]) class Data { ... } // thread-safety markers
@:safety on the Main class selects a program-wide mode: strict requires every class to be annotated, non-strict wraps the rest in Rc. Use-after-move is a hard error for @:move types and a warning otherwise.
Artifacts
.blade
Per-module MIR cache for incremental builds. On by default; --no-cache disables it.
.rzb
Every compiled module in one file. rayzor run app.rzb skips compilation entirely.
.rpkg
Haxe sources plus optional native libraries. strip reduces it to one platform.
The cache is invalidated by source content, compiler version, and a content-derived compiler cache ABI id — not by a redundant relink of identical sources.
Project manifest
A project is described by a rayzor.toml at its root.
rayzor.toml
[project]
name = "my-app"
version = "0.1.0"
entry = "src/Main.hx"
[build]
class-paths = ["src"]
opt-level = 2
preset = "application"
[cache]
enabled = true
An existing HXML build can be delegated to rather than ported — set hxml = "build.hxml" under [project]. A workspace lists members instead.
A project depending on native plugins must declare both the class paths and the native libraries; only one of the two fails to resolve at run time.
Debug toolkit
rayzor debug run
Forensic run with crash handlers pre-armed
rayzor debug bench
Run N times, per-run metrics plus aggregate stats
rayzor debug compare
A/B two git refs and report the median delta; restores the tree on exit
rayzor debug resolve
Hex PCs from a crash dump → Haxe functions and lines
rayzor debug server
Live metrics over HTTP with a browser dashboard
rayzor dump --diff
MIR before and after optimization — the fastest way to see what a pass did
Full reference lives in the repo
Every flag, the pass ordering, the runtime ABI and the format specs.
Open docs/ ↗