logicaffeine-forge 0.10.0

Copy-and-patch JIT for LOGOS — executable-memory layer and stencil runtime (native only).
Documentation

logicaffeine-forge

The copy-and-patch JIT's executable-memory layer: page-aligned W^X executable pages, build-time-baked machine-code stencils, the copy-and-patch assembler, the J1 micro-op compiler, and the EXODIA contiguous register-allocating x86-64 codegen tier.

Part of the Logicaffeine workspace. Tier 0, native-only (cfg-gated off wasm32); no internal dependencies. Wired into the VM by logicaffeine_jit.

Role in the workspace

logicaffeine-jit (the LOGOS native tier) depends on this crate and lowers hot integer/float regions and recursive functions to machine code; anything outside the supported subset declines (None / JitCompileError::Unsupported) and the VM keeps interpreting. The whole crate is #![cfg(not(target_arch = "wasm32"))] — a copy-and-patch JIT emits raw machine code and cannot run in the WASM sandbox, so the browser falls back to the bytecode VM.

The model is copy-and-patch: compiling a function is memcpy(stencil bytes) + patch relocations, then flip the page to executable. build.rs compiles the #![no_std] stencils/int_stencils.rs (94 hand-written logos_stencil_* fns) with rustc --emit=obj — no linker, so it works for any installed target — then extracts each symbol's code + relocations via the object crate, normalizing every reloc across Mach-O / ELF / COFF. Holes are undefined extern symbols (logos_hole_cont_N continuations, LOGOS_HOLE_I64_N constants) LLVM cannot fold. Two hard build gates: leaf purity (a reloc to a non-hole symbol is an error) and tail calls (each continuation site must decode as an unconditional b/jmp). The build also generates the EXODIA register-threading variants and emits STENCILS, the per-stencil ST_* statics, and the V_BINOP/V_FBINOP/V_SQRTF/V_DIVF/… variant tables (all include!d into lib.rs). See execution-and-performance.md.

Public API

Crate root (lib.rs):

  • JitPage — a page-aligned block of executable memory; Send + Sync (sealed read/execute-only after construction). JitPage::new(code) -> Result<JitPage, JitError> maps, copies, and seals. with_layout(len, fill: FnOnce(u64) -> Vec<u8>) is two-phase for PATCHED code: map first to learn the base, fill bytes against it, then write+seal. patch_word(offset, value) rewrites one 8-byte literal-pool word post-seal (the self-call entry; Err(EmptyCode) on MAP_JIT targets, where callers use the table-indirect call stencil instead). unsafe as_fn_i64_i64() -> extern "C" fn(i64, i64) -> i64, plus as_ptr, len, alloc_len, is_empty.
  • JitErrorEmptyCode | Map(io::Error) | Protect(io::Error).
  • Re-exported from stencil_model (private, include!d by both build.rs and the runtime so it stays dependency-free): Stencil { name, code, relocs }, Reloc { offset, kind: RelocKind, target: HoleId, addend }, RelocKind (Branch26, Page21, PageOff12 { scale }, GotPage21, GotPageOff12, Rel32, GotRel32, Abs64), HoleId (Cont(u8), ConstI64(u8)).

jit — the J1 straight-line micro-op compiler. MicroOp is the int/float subset of the VM's register ops (Slot = u16); Cmp/IOp/FOp/AffOp/RmwOp/StrSrc are its operation tags. The compilers:

  • compile_straightline(ops) -> Result<CompiledChain, JitCompileError> and compile_straightline_with(ops, shared_status) / …_coded(ops, shared_status, deopt_codes, depth_addr) (the latter adds a per-op PRECISE deopt table emitting (pc << 2) | 3).
  • compile_straightline_pinned(ops, pins) / …_pinned_with(ops, pins, shared_status) / …_pinned_float(ops, pins, fpins, shared_status) thread up to 4 GP slots through r0..r3 and up to 6 float slots through XMM f0..f5, keeping hot values register-resident.
  • CompiledChain::run_with_frame(&mut [i64]) -> ChainOutcome where ChainOutcome is Return(i64) | Deopt(i64) (checked Div/Mod side-exit on a zero divisor); plus bytes, base, piece_count, patch_marked, has_patch_marks. JitCompileError = Empty | FallsOffTheEnd | BadJumpTarget | Assembly(String) | Unsupported(&str). reference_eval(ops, frame, fuel) is the differential oracle; magic_eval and BAKED_CALL_DEPTH = 2_500 are exposed.

buffer — the copy-and-patch assembler. JitBuffer::push_stencil(stencil, holes: &[HoleValue]) -> Label, mark_patch_hole, then finish() -> Result<JitChain, BufferError> lays out code + an 8-byte literal pool + GOT-style slots, maps a page, patches against the base, writes once, and seals. JitChain::{from_code, run, run_with_frame, bytes, base, piece_count, patch_marked, has_patch_marks}. The CPS entry ABI is unsafe extern "C" fn(base: *mut i64, sp: *mut i64, r0..r3: i64, f0..f5: f64) -> i64. Also Label, HoleValue, BufferError, jit_canary_enabled().

regalloc (x86-64 only) — the EXODIA backend: emits a whole region/function as ONE x86-64 function with a global per-slot assignment (INT in GPRs, FLOAT in XMM, else frame), removing the per-piece frame round-trip. compile_region_regalloc, compile_region_regalloc_precise, compile_function_regalloc (real SysV recursive self-calls via a baked entry cell), compile_function_regalloc_precise (list-param mode-B, per-op resume (pc << 2) | 3 for in-place-mutating functions) — each returns Option<CompiledChain> and is bit-identical to reference_eval. regalloc_enabled() / simd_enabled() read the env toggles.

x64asm (x86-64 only) — the minimal encoder the regalloc backend emits through: Asm with Reg/Xmm/Cond/LabelId, moves, three-operand arithmetic, compares/setcc, late-bound jmp/jcc, frame load/store, idiv, scalar SSE (addsd/mulsd/sqrtsd/…), and packed addpd/mulpd/cmppd/…; resolve() -> Vec<u8> binds the labels.

vectorizerecognize_elementwise_map(ops) -> Option<MapPlan> proves a pure element-wise float map (the only bit-exactly vectorizable shape; reductions reassociate and are rejected); emit_map_kernel / emit_packed_arith lower it to a 2-wide packed-double loop.

patch — pure relocation byte-math (patch_aarch64_branch26/_page21/ _pageoff12, patch_x64_rel32, patch_abs64, is_indirect; PatchError), compiled and tested on every host so the x86-64 patchers run on an arm64 box and vice versa. segv_trace::install() is an opt-in SIGSEGV/SIGBUS tracer dumping the faulting address, RIP, and registers.

Platform support

W^X executable memory, per platform:

  • macOS / aarch64 (Apple Silicon): mmap(PROT_RWX, MAP_JIT) + per-thread pthread_jit_write_protect_np toggling + a mandatory sys_icache_invalidate.
  • Other Unix (Linux, Intel macOS): mmap(RW) → copy → mprotect(RX), with an inline-asm I-cache flush on aarch64 Linux (mprotect does not flush it).
  • Windows: VirtualAlloc(RW)VirtualProtect(EXECUTE_READ)FlushInstructionCache.

All writes happen inside JitPage::new on the constructing thread before any function pointer can escape, so there is no cross-thread W^X hazard. The copy-and-patch stencil tier runs on both x86-64 and aarch64; the EXODIA contiguous codegen (regalloc/x64asm) is x86-64 only.

Dependencies

Internal: none. External: libc (Unix mmap/mprotect/icache), windows-sys (Windows VirtualAlloc/VirtualProtect/FlushInstructionCache), and object (a build-dependency for parsing the compiled stencil object files).

License

Business Source License 1.1 — see LICENSE.md.


Docs index · Root README · Changelog