LLVM-backed AOT evaluator for Relon. Phase B production envelope.
This crate is the second slice of the dual-backend strategy:
Cranelift covers the default native AOT route and the LLVM AOT
pipeline here chases Rust-native peak performance for the #main
entry path.
Scope (Phase B)
- Two entry shapes accepted:
- Legacy-i64 (
(I64...) -> I64) forfrom_ir_directcallers (tests, bench fixtures) — the Phase A bootstrap envelope, retained for cross-backend comparison. - Buffer-protocol (
(*state, i32, i32, i32, i32, i64) -> i32) forfrom_sourcecallers. Matches the cranelift backend'sEntryShape::BufferProtocolso the runtime envelopes line up.
- Legacy-i64 (
- Source-driven pipeline (
from_source): parse + analyze + lower (relon_ir::lower_workspace_single) + LLVM emit + JIT compile + per-call arena dispatch. The cmp_lua W1 / W2 workloads (list.sum(range(n)) / list.sum(range(n).map(...))) go end-to-end through this path. - The op set started with what
lower_workspace_singlesynthesised for the W1 / W2 shape after the IR'srange_pipelinepeephole collapsedrange.map.suminto a single accumulator loop:LocalGet,ConstI64/ConstI32/ConstBool,LetGet/LetSet,LoadField/StoreField(scalar slots),Add/Sub/Mul/Div/Mod/BitAnd(I32 + I64),Eq/Ne/Lt/Le/Gt/Ge, structured control flow (Block/Loop/Br/BrIf/If), andReturn. The emitter has since widened into strings, lists, pointer-indirect fields, host calls, closures, object emission, wasm32 object emission, and several stdlib surfaces; the tests are the authoritative coverage map for those later slices.
Safety contract
The source-driven buffer-protocol path is the production entry and
carries the backend sandbox contract: capability gates, div/mod
guards, checked signed Int arithmetic, arena bounds checks before
host-pointer formation, dynamic host-call trap lifting, and
deterministic step-budget fuel all report through typed
RuntimeErrors. The legacy / typed-fast i64 entries remain for
focused tests and benchmark kernels; they have no ArenaState error
lane, and public run_main routes trap-capable bodies through the
buffer entry.
Remaining limits
- Full language surface — tree-walk remains the complete reference implementation. LLVM AOT covers the explicitly tested compiled-backend surface and rejects shapes outside that envelope loudly rather than fabricating partial results.
.o/.soemit + dlopen — Phase B still uses the in-process MCJIT engine. The single-knobOptimizationLevelAPI hides the engine choice so Phase C / ORC migration is a localised diff.
Decision log (Phase A.1)
Picked inkwell over llvm-sys and external clang/llc:
inkwell0.9.0 with thellvm18-1feature pins llvm-sys 181.3.0 against the system LLVM 18.1.3 install at/usr/lib/llvm-18. Safe Rust wrappers eliminate the per-opunsafeblock the raw FFI path would impose.llvm-syswould force every IR-builder call throughunsaferaw pointer arithmetic — maintainability cost on the AOT widening Phase B/C is too high for the same target set.clang/llcshell-out drops in-memory JIT verification (we want a smoke test to round-trip without writing a file) and bloats cold-start with subprocess fork/exec latency.optpiping also forces stringly-typed IR generation that's awkward to debug.