Documentation
XCX Bytecode Emitters to Cranelift IR
Emitters transform virtual machine bytecode instructions into Cranelift IR using runtime FFI helpers for complex operations and native instructions for fast-path primitive operations.
Emitter Submodules Structure
The emission routines are split by operation groups under src/jit/:
1. Arithmetic Emitters (emit_arith.rs)
Arithmetic operations compile through fast-path primitives if types are statically verified, or fallback to polymorphic FFI helper functions:
- Fast-Path Integers: Generates Cranelift machine instructions (
iadd,isub,imul). Division and modulo (sdiv/srem) normally emit a guard block checking for a zero divisor and for thei64::MIN / -1overflow case; when the divisor is a statically known constant (tracked viactx.register_const, seecompiler/jit/jit_codegen.md) other than0or-1, the guard is skipped and the raw instruction is emitted directly. A known power-of-two divisor toModadditionally bypassessrementirely in favor of an equivalent branchless bitwise sequence. - Fast-Path Floats: Emits Cranelift floating-point directives (
fadd,fsub,fmul,fdiv), selected only when both operands are statically known to beTypeTag::Float- a mismatched pair (e.g.Float + Int) always falls through to the polymorphic path so that the mixed-type numeric conversion is performed correctly at runtime instead of reinterpreting an integer's bit pattern as a float. - Polymorphic Paths: Calls FFI helpers (
xcx_jit_add,xcx_jit_sub,xcx_jit_mul,xcx_jit_div,xcx_jit_mod) when variables have anUnknowntype tag, or when operand types are known but mixed. It packs values as boxed quiet-NaN representations.
2. Call Emitters (emit_call.rs)
Calculates call offsets and invokes local JIT frames or VM wrappers.
emit_call: Routes function calls. For local, matching-signature functions, it generates direct local recursive JIT-to-JIT calls guarded by the recursion-depth check described incompiler/jit/jit_core.md. For other functions, the compiler emits a fast-path directcall_indirectto the callee's JIT memory pointer if compiled - reloading global variables afterward, narrowed to the callee's transitive global-slot write set (fixpoint over the call graph; callees that write no globals cost nothing) - otherwise it emits a slow-path FFI call toxcx_jit_call_recursive. Method calls with more than 16 arguments are rejected at compile time before any emission.emit_method_call&emit_method_call_custom: Resolves method dispatch targets by callingxcx_jit_method_dispatchor invoking FFI handlers.Get/Setmethod calls on anArrayreceiver with a statically knownIntelement type route through the same inlined bounds-checked fast path used forGetIndex/SetIndex- seecompiler/jit/jit_codegen.md.
3. Control Flow Emitters (emit_control.rs)
Manages execution bounds, conditional checks, loop construction, and fiber state machine yielding.
- Type Guards: No type-guard IR is generated. The
xcx_jit_report_guard_failuresymbol is registered but never called, andemit_guard_int/float/boolwere removed in Phase 3B. Type-directed selection happens purely at compile time viactx.known_types(fast path chosen when the tag is statically known, polymorphic FFI otherwise). - Conditional Branches (
emit_jump_if): BacksJumpIfFalse/JumpIfTrue. When the source register is statically known to beTypeTag::Bool, the branch condition reduces to a single bit comparison (icmp_imm) instead of comparing both the type tag and the bit pattern against the boxedfalsevalue. After branching,clear_block_stateresets any tracked register constants (ctx.register_const) for the continuation block, since a value known constant on one incoming path cannot be assumed constant after merging with a path where it wasn't. - Loop Structs: Standard loop operations (
LoopNext,LoopPrev,IncLocalLoopNext,ArrayLoopNext,TableIter) translate into Cranelift block branches. Loops evaluate constraints against limits, jumping backwards to block headers or forward to exit targets. Since 4.4, hot loop headers are aligned to 64-byte boundaries (probe build + padding, seecompiler/jit/jit_codegen.md). - Return (
emit_return): Returns control to the interpreter parent frame, passing execution status states. (Note:emit_yield,emit_method_yield, andemit_return_fiberwere removed in Phase 3B;YieldVoidis still JIT-compiled viaemit_method_yield_voidcallingxcx_jit_yield, whileYieldcompiles to a bare register read andYieldWithTargetto aMove).
4. Load & Store Emitters (emit_load_store.rs)
Stores and transfers registers while orchestrating garbage collection routines:
- GC Refcounting: Coordinates reference counters. Emitter injections call
emit_conditional_inc_refandemit_conditional_dec_refto clean up old pointer resources during overrides. - Variable Mapping: Implements const loading (
emit_load_const) and variable assignments (emit_get_var,emit_set_var). Loading an integer constant additionally records its value inctx.register_const, so later arithmetic on that register can take the constant-divisor fast paths described under Arithmetic Emitters above. - JSON Binding: Connects variables to JSON pathways (
emit_json_bind_local,emit_json_bind_global). - Inlined Array Access: only the
BoolArraycase ofGetIndex/SetIndex(and the.get/.setmethod fast paths described incompiler/jit/jit_codegen.md) compiles to a native bounds check followed by a direct buffer read/write; non-BoolArrayGetIndex/SetIndexfall back unconditionally to FFI helpers.
5. Object & Database Emitters (emit_object.rs)
Links variables to tables, disk arrays, and structured I/O endpoints:
- Disk Storage (
emit_store_read,emit_store_write,emit_store_exists): Connects to file-backed database storage helpers. - Database Initializer: Spills register states and hooks database drivers (
emit_database_init). - Table Member Accessors:
emit_row_get,emit_table_push_row, andemit_table_clone_skeletonlive inemit_load_store.rs;emit_get_member/emit_set_memberare implemented here.
6. Misc Emitters (emit_misc.rs)
Handles environment lookups and fatal errors.
- Halt Handling (
emit_halt_alert,emit_halt_error,emit_halt_fatal): Halts compiler execution, registers error context fields, and executes clean return patterns. - OS Environment: the standalone
emit_env_get/emit_env_argshelpers were removed in Phase 3B;EnvGet/EnvArgsare now JIT-compiled inline as direct FFI calls toxcx_jit_env_get/xcx_jit_env_args.
7. Eager JIT Compilation Pre-Pass
Before IR generation begins for a method or fiber segment, the JIT scans its bytecode for OpCode::Call instructions to eagerly compile dependencies:
- Callee Pre-compilation: Statically pre-compiling callees ensures that the target
jit_ptris resolved and ready in the fast-path directcall_indirectcheck, preventing slow FFI roundtrips. - Compiler Cycle Prevention: Uses a thread-safe compiler state context tracker (
in_progress: HashSet<usize>on the JIT compiler struct) containing bytecode chunk indexes currently undergoing compilation. If a circular call reference chain is encountered during eager compilation, the compiler immediately yields a null JIT pointer, safely fallbacking to the interpreter linkage for runtime resolution.