Skip to content

cranelift: thread source span through jit_hd/jit_at/jit_tl#270

Merged
danieljohnmorris merged 2 commits into
mainfrom
fix/cranelift-span-id
May 14, 2026
Merged

cranelift: thread source span through jit_hd/jit_at/jit_tl#270
danieljohnmorris merged 2 commits into
mainfrom
fix/cranelift-span-id

Conversation

@danieljohnmorris
Copy link
Copy Markdown
Collaborator

Summary

Cranelift JIT runtime errors used to surface with span: None because the JIT_RUNTIME_ERROR TLS cell only stored a bare VmError. The diagnostic renderer had nothing to underline so errors came out with empty labels: [], breaking parity with tree and VM and forcing agents to re-read source code to locate the offending site. Manifesto cost: extra tokens on every cranelift failure.

This PR plumbs the call-site Span through the JIT helper ABI by packing it as a u64 immediate (start << 32 | end) at the cranelift call site and threading it as a trailing extern "C" arg into jit_hd, jit_at, jit_tl. The helpers store the (VmError, Span) pair on error; the entry point decodes the optional span onto the surfaced VmRuntimeError. Zero is the Span::UNKNOWN sentinel and round-trips to None.

Scoped to those three helpers for v1, as agreed. The other erroring helpers (jit_lst, jit_listget, jit_index, jit_jpth, jit_slc, ...) keep their existing permissive-nil semantics; widening them is parked. Each newly-promoted helper now costs +1 u64 arg and +1 iconst at the call site, which is the dominant runtime cost of this approach.

Repro

$ echo 'f>n;hd []' > /tmp/repro.ilo
$ ilo /tmp/repro.ilo --run-cranelift f --diagnostics-json

Before:

"labels":[]

After (matches --run-vm / --run-tree):

"labels":[{"col":7,"end":9,"line":1,"message":"here","primary":true,"start":6}]

What's in the diff

Commit 1, cranelift: thread source span through jit_hd/jit_at/jit_tl

  • src/vm/mod.rs: JIT_RUNTIME_ERROR now holds Option<(VmError, Option<Span>)>. New jit_set_runtime_error_with_span(err, span_bits); old jit_set_runtime_error(err) is a 0-bits wrapper for helpers not yet plumbed. jit_take_runtime_error returns the tuple. New decode_span_bits helper. jit_hd, jit_at, jit_tl extern "C" signatures gain a trailing span_bits: u64 and route every error path through the new helper. Inline unit-test calls updated.
  • src/vm/jit_cranelift.rs: New pack_span_bits(Span) -> i64. helpers.hd / helpers.at / helpers.tl declared with the new arity. Compile sites for OP_HD / OP_AT / OP_TL pack chunk.spans[ip] into an iconst and append it to the call args. JIT entry point attaches the decoded span to VmRuntimeError.

Commit 2, test + example: cross-engine span parity for cranelift errors

  • tests/regression_cranelift_error_span.rs: 9 tests asserting cranelift (start, end) equals VM (start, end) across every v1 helper error path, plus a belt-and-braces "labels not empty" check.
  • examples/cranelift-error-span.ilo: positive-path coverage of the extended helper ABI through the cross-engine harness, so a future call site that forgets the immediate crashes loudly on arity mismatch.

Test plan

  • cargo build --release --features cranelift
  • cargo test --release --features cranelift --test regression_cranelift_error_span (9 passed)
  • cargo test --release --features cranelift full suite (4700 passed, 0 failed)
  • cargo fmt --check
  • cargo clippy --release --features cranelift --all-targets -- -D warnings
  • CI green

Follow-ups

  • Widen span plumbing to the remaining erroring JIT helpers (jit_lst, jit_listget, jit_index, jit_jpth, jit_slc, the arithmetic helpers, ...). Each is mechanically the same change: +1 u64 arg, +1 iconst, swap to _with_span. Parking until a real diagnostic complaint surfaces from one of those paths.
  • Carry the JIT call stack through too. Currently VmRuntimeError::call_stack is always empty for cranelift; the helper TLS would need a Vec<Frame> push at every JIT call site, which is more invasive.

Cranelift JIT runtime errors surfaced with `span: None` because the
JIT_RUNTIME_ERROR TLS cell only stored a bare `VmError`, so the
diagnostic renderer had nothing to underline and emitted empty
`labels: []`. Tree and VM carry the call-site span all the way to the
error; cranelift now matches.

Pack `Span { start, end }` into a u64 immediate (`start << 32 | end`) at
the cranelift call site and pass it as a trailing extern "C" arg. The
helpers store the `(VmError, Span)` pair on error; the JIT entry point
decodes the optional span (`0` round-trips to `None`) onto the surfaced
`VmRuntimeError`.

Scoped to jit_hd, jit_at, jit_tl for v1. The other erroring helpers
(jit_lst, jit_listget, jit_index, jit_jpth, jit_slc, ...) still record
spanless errors; harmonising them is parked. Each new erroring helper
costs +1 u64 arg and +1 iconst at the call site.
Regression test asserts byte-for-byte equality of the `(start, end)`
fields in the diagnostic JSON `labels[0]` across VM and cranelift for
every v1-scoped helper: jit_hd empty list/text, jit_tl empty list/text,
jit_at out-of-range (positive list, negative list, text), and jit_at
fractional index. A belt-and-braces sanity test asserts the cranelift
case actually has a label (not just that cl and vm both have none).

The example file demonstrates the positive path with `-- run` / `-- out`
assertions so the cross-engine harness exercises the extended helper
ABI: if a future call site forgot the new immediate, the JIT would
crash on the arity mismatch before returning.
@codecov
Copy link
Copy Markdown

codecov Bot commented May 14, 2026

Codecov Report

❌ Patch coverage is 95.45455% with 3 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/vm/mod.rs 93.02% 3 Missing ⚠️

📢 Thoughts on this report? Let us know!

@danieljohnmorris danieljohnmorris merged commit 7ae6e31 into main May 14, 2026
5 checks passed
@danieljohnmorris danieljohnmorris deleted the fix/cranelift-span-id branch May 14, 2026 11:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant