shape-jit 0.3.2

Tiered JIT compiler (Cranelift) for the Shape virtual machine
Documentation
# JIT Module Refactoring Plan

## Goal
Split `core.rs` (7,539 lines) into files of ~500 lines max.

## Current State
```
jit/
├── mod.rs           (30 lines)
├── nan_boxing.rs    (89 lines)  ✓
├── context.rs       (548 lines) ✓
└── core.rs          (7,539 lines) ← needs splitting
```

## Target Structure
```
jit/
├── mod.rs              (~80 lines)   - Public API, re-exports
├── nan_boxing.rs       (89 lines)    - NaN-boxing constants ✓
├── context.rs          (548 lines)   - JITContext, JITCandle ✓
├── compiler.rs         (~600 lines)  - JITCompiler struct + new/compile
├── ffi_refs.rs         (~200 lines)  - FFIFuncRefs struct, symbol registration
├── helpers.rs          (~300 lines)  - can_jit_compile, get_unsupported_opcodes, stubs
├── bytecode_ir/
│   ├── mod.rs          (~200 lines)  - BytecodeToIR struct, main compile loop
│   ├── stack_ops.rs    (~150 lines)  - PushConst, Pop, Dup, Swap
│   ├── arithmetic.rs   (~200 lines)  - Add, Sub, Mul, Div, Mod, Neg, Pow
│   ├── comparison.rs   (~200 lines)  - Gt, Lt, Eq, FuzzyEq, etc.
│   ├── control_flow.rs (~300 lines)  - Jump, JumpIf*, SetupTry, Throw, Return
│   ├── variables.rs    (~150 lines)  - LoadLocal, StoreLocal, LoadModuleBinding
│   ├── objects.rs      (~300 lines)  - NewArray, NewObject, GetProp, SetProp
│   ├── functions.rs    (~200 lines)  - Call, CallValue, CallMethod
│   └── builtins.rs     (~400 lines)  - BuiltinCall dispatch
└── ffi/
    ├── mod.rs          (~100 lines)  - Re-exports all FFI functions
    ├── array.rs        (~350 lines)  - jit_new_array, jit_array_*, jit_slice
    ├── object.rs       (~250 lines)  - jit_new_object, jit_get_prop, jit_set_prop
    ├── string.rs       (~200 lines)  - String method helpers (extracted from call_method)
    ├── candle.rs       (~250 lines)  - jit_get_*_at, jit_load_candle
    ├── indicator.rs    (~200 lines)  - jit_sma, jit_ema, jit_rsi
    ├── control.rs      (~350 lines)  - jit_control_fold/map/filter/reduce/foreach
    ├── iterator.rs     (~150 lines)  - jit_iter_done, jit_iter_next
    ├── math.rs         (~100 lines)  - jit_sin, jit_cos, jit_pow, etc.
    ├── conversion.rs   (~100 lines)  - jit_typeof, jit_to_string, jit_to_number
    ├── datetime.rs     (~150 lines)  - jit_eval_datetime_expr, jit_eval_time_reference
    ├── series.rs       (~200 lines)  - jit_series_method, jit_intrinsic_series
    └── method_dispatch.rs (~400 lines) - jit_call_method (dispatcher only)
        └── calls helper functions from array.rs, string.rs, object.rs
```

## Critical Refactoring: jit_call_method (1,000+ lines → ~400 lines)

**Problem:** One massive function with nested match statements.

**Solution:** Extract type-specific method handlers into separate functions:

```rust
// In ffi/method_dispatch.rs (~100 lines)
extern "C" fn jit_call_method(...) -> u64 {
    // Stack manipulation (~50 lines)
    // Type dispatch:
    match tag {
        TAG_ARRAY => call_array_method(arr, &method_name, &args),
        TAG_STRING => call_string_method(s, &method_name, &args),
        TAG_OBJECT => call_object_method(obj, &method_name, &args),
        TAG_DURATION => call_duration_method(dur, &method_name, &args),
        TAG_COLOR => call_color_method(color, &method_name, &args),
        TAG_SERIES => call_series_method(series, &method_name, &args),
        _ => TAG_NULL,
    }
}

// In ffi/array.rs - add helper (~200 lines)
pub fn call_array_method(arr: &[u64], method: &str, args: &[u64]) -> u64 {
    match method {
        "length" | "len" => ...,
        "first" => ...,
        "sum" => ...,
        // etc.
    }
}

// In ffi/string.rs - add helper (~150 lines)
pub fn call_string_method(s: &str, method: &str, args: &[u64]) -> u64 {
    match method {
        "length" | "len" => ...,
        "toUpperCase" => ...,
        // etc.
    }
}

// Similarly for object.rs, duration.rs, etc.
```

## BytecodeToIR Splitting Strategy

The BytecodeToIR has one giant match statement. We can split using a trait or by grouping related opcodes into methods:

```rust
// bytecode_ir/mod.rs
impl BytecodeToIR {
    pub fn compile_instruction(&mut self, instr: &Instruction) {
        match instr.opcode {
            // Stack ops
            OpCode::PushConst | OpCode::Pop | OpCode::Dup | OpCode::Swap
                => self.compile_stack_op(instr),

            // Arithmetic
            OpCode::Add | OpCode::Sub | OpCode::Mul | OpCode::Div | ...
                => self.compile_arithmetic(instr),

            // Comparisons
            OpCode::Gt | OpCode::Lt | OpCode::Eq | ...
                => self.compile_comparison(instr),

            // etc.
        }
    }
}

// bytecode_ir/stack_ops.rs
impl BytecodeToIR {
    pub fn compile_stack_op(&mut self, instr: &Instruction) { ... }
}

// bytecode_ir/arithmetic.rs
impl BytecodeToIR {
    pub fn compile_arithmetic(&mut self, instr: &Instruction) { ... }
}
```

## Execution Order

### Phase 1: FFI Extraction -- DONE
1. [x] Create `ffi/mod.rs`
2. [x] Extract `ffi/array.rs` (jit_new_array, jit_array_get, etc.)
3. [x] Extract `ffi/object.rs` (jit_new_object, jit_get_prop, etc.)
4. [x] Extract `ffi/control.rs` (jit_control_fold/map/filter)
5. [x] Extract `ffi/iterator.rs` (jit_iter_done, jit_iter_next)
6. [x] Extract `ffi/math.rs` (jit_sin, jit_cos, etc.)
7. [x] Extract `ffi/conversion.rs` (jit_typeof, jit_to_string)
8. [x] Extract `ffi/data.rs`, `ffi/window.rs`, `ffi/simd.rs`, `ffi/result.rs`
9. [x] Extract `ffi/call_method/` (dispatcher + per-type handlers)
10. [x] Extract `ffi/typed_object/` (allocation, field access, merge, FFI exports)
11. [x] Extract `ffi/gc.rs`, `ffi/references.rs`, `ffi/async_ops.rs`, `ffi/join.rs`

### Phase 2: Refactor jit_call_method -- DONE
12. [x] Create type-specific helper functions (call_array_method, call_string_method, etc.)
13. [x] Simplify jit_call_method to dispatcher only (`ffi/call_method/mod.rs`)
14. [x] Move helpers to `ffi/call_method/{array,string,object,duration,number,result,time,signal_builder}.rs`

### Phase 3: BytecodeToIR Extraction -- DONE
15. [x] Create `translator/mod.rs` with BytecodeToIR struct in `translator/types.rs`
16. [x] Extract opcode groups: `opcodes/{stack,arithmetic,control_flow,variables,data,functions,references,builtins/}`
17. [x] Extract `translator/compiler.rs`, `translator/helpers.rs`, `translator/storage.rs`, `translator/typed.rs`
18. [x] Further split `opcodes/data.rs` into `collections.rs` + `typed_objects.rs` + `data.rs`
19. [x] Extract `translator/inline_ops.rs` from helpers.rs (inline array/data access)

### Phase 4: JITCompiler & Helpers -- DONE
20. [x] Extract `compiler/setup.rs` (JITCompiler struct)
21. [x] Extract `compiler/ffi_builder.rs` (FFI func ref building)
22. [x] Extract `compiler/accessors.rs` (can_jit_compile, get_unsupported_opcodes)
23. [x] Extract `compiler/strategy.rs`, `compiler/program.rs`

### Phase 5: Verification -- DONE
24. [x] `cargo check -p shape-jit` passes
25. [x] `cargo test -p shape-jit` passes (66 tests)
26. [x] No new warnings introduced

## Current State (Post-Refactor)
- `core.rs`: 698 LOC (tests + re-exports only, down from 7,539)
- Largest file: `context.rs` at 767 LOC (JITContext struct, single cohesive unit)
- All opcode translation files < 620 LOC
- All FFI files < 650 LOC

## Notes
- Each extraction should be one commit
- Test after each extraction
- No functionality changes, just reorganization