vm + cranelift: native flt + fld + flatmap HOF dispatch (PR 3a of 4)#278
Merged
Conversation
Extends the `map` pattern from #277 to the next three pure-bytecode HOFs. Each gets a native bytecode loop in the compiler that calls back into the FnRef via OP_CALL_DYN, so VM and Cranelift both run them end-to-end: - flt fn xs: same FOREACHPREP/NEXT shape as map, but the call result drives a bool typecheck (OP_ISBOOL → OP_JMPT) and the OP_LISTAPPEND only fires on the true arm. Non-bool predicate returns raise via OP_WRAPERR + OP_PANIC_UNWRAP with a message containing "flt" and "bool" (matches the tree-walker semantics pinned by the existing tests). - fld fn xs init: no result list. Accumulator reg carries the running value across iterations; the per-iter call is 2-arg (acc, item), result is the final acc. - flatmap fn xs: nested foreach. The outer loop drives the user fn over xs; the inner FOREACHPREP/NEXT iterates each call result and splices it into acc via OP_LISTAPPEND. A non-list return surfaces as "foreach requires a list" through the inner FOREACHPREP error path, which the test asserts matches. is_tree_bridge_eligible's doc-comment drops flt/fld/flatmap from the not-yet list, parking grp/uniqby/partition/2-arg srt for the next PR in the chain. Also drops the `#[ignore]` from vm_flt_positive, vm_flt_predicate_returns_non_bool, vm_flt_wrong_list_arg, vm_fld_sum, and vm_fld_wrong_list_arg, which pinned the absence of these code paths.
The JIT path already supports OP_PANIC_UNWRAP via the same helper (jit_panic_unwrap in src/vm/mod.rs); the AOT codegen was the remaining gap. Without this, any program using `flt` with the new native dispatch refuses to compile to a binary because the predicate non-bool guard emits OP_PANIC_UNWRAP unconditionally. The lowering mirrors the JIT side: declare the helper FuncId, emit a call to it on the failed value, then `return` the helper's TAG_NIL result so subsequent instructions don't execute against the bad state. The helper itself sets JIT_RUNTIME_ERROR which the post-call check in the caller surfaces as a runtime error, the same contract the VM dispatcher uses with `vm_err!`. No new behaviour for tree or VM — only AOT parity.
Pins the value-level behaviour of the three new native HOF lifts
across --run-tree, --run-vm, and --run-cranelift:
- flt: positives, empty list, all-pass, all-fail, then map
composition (15 assertions per HOF cover empty / single /
multi / fully-filtered shapes).
- fld: integer sum, empty list returns init, single element,
text concat (covers type-changing accumulators), then map
composition.
- flatmap: rep-n shape from the example file, empty outer,
every-empty-inner (pins the inner FOREACHPREP short-circuit
on empty results), mixed inner sizes.
Builtin-callback coverage stays in regression_hof_map.rs; the
Cranelift jit_call_dyn helper is dispatch-uniform across HOFs, so
once builtin-callback works for `map` it works for all of them.
Adds examples/flt-basics.ilo and examples/fld-sum.ilo as the
in-context learning artefacts (per the project convention, also
exercised by tests/examples_engines.rs across every engine). The
existing examples/flatmap.ilo already covers flatmap.
Native flatmap dispatch landed in the preceding commit, so the example runs on every engine now. Drops the two engine-skip lines and the explanatory comment; the tests/examples_engines.rs harness picks the file up across tree/vm/cranelift automatically.
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
This was referenced May 15, 2026
Merged
danieljohnmorris
added a commit
that referenced
this pull request
May 15, 2026
- slc / take / drop accept negative indices counting from end (bounds clamp), matching at xs i. Closes the quant-trader fencepost and the slc xs -np 1 np ergonomics gap (#266). - Map keys are typed: text or integer. mset m 7 v and mget m 7 work directly, no str conversion. Int(1) and Text("1") are distinct. Float keys floor to i64; jdmp stringifies numeric keys for JSON (#267). - Add map / flt / fld to the builtin reference. All HOFs (map, flt, fld, srt, grp, uniqby, partition, flatmap) now work cross-engine on tree, VM, Cranelift JIT, and AOT (#274 #277 #278 #279 #280 #283). - New Inline lambdas subsection: Phase 1 literals are cross-engine, Phase 2 closure capture is tree-only with automatic fallthrough surfacing ILO-R012 on VM and Cranelift (#265 #284). - AOT-compiled binaries from ilo compile now strip the top-level ~/^ wrapper byte-for-byte the same as in-process runners (#281).
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Extends the
mapnative HOF dispatch from #277 to the next three pure-bytecode HOFs:flt,fld,flatmap. Each gets a real bytecode loop in the compiler that calls back into the FnRef via OP_CALL_DYN, so VM and Cranelift run them end-to-end with no tree-bridge round-trip.Builds on #274 (FnRef NaN-tagging) and #277 (
mapnative + OP_CALL_DYN lowering). The Cranelift JIT helper for OP_CALL_DYN is dispatch-uniform across HOFs, so the new arms light up--run-craneliftfor free.This is PR 3a of a four-PR chain that closes out HOF dispatch on VM/Cranelift. The remaining HOFs (
grp,uniqby, 2-argsrt,partition), closure-bind ctx-arg forms, and direct FnRef-variable calls land in PR 3b, 3c, 3d, then PR 4 lifts the lambda examples.Repro before/after
Same story for
fld add xs 0(now15) andflatmap rep xs(now[1, 2, 2, 3, 3, 3]).What's in the diff (per-commit)
RegCompiler::compile_exprmirroring the(Builtin::Map, 2)shape from vm + cranelift: native map HOF dispatch (PR 2 of 4) #277.fltadds an OP_ISBOOL typecheck and routes non-bool through OP_WRAPERR + OP_PANIC_UNWRAP.fldallocates a 3-reg contiguous slice for(res, arg0, arg1)and overwrites the accumulator each iter.flatmapnests an inner FOREACHPREP/NEXT over each call result. Drops#[ignore]fromvm_flt_positive,vm_flt_predicate_returns_non_bool,vm_flt_wrong_list_arg,vm_fld_sum,vm_fld_wrong_list_arg. Updates theis_tree_bridge_eligibledoc comment to reflect what's still parked for PR 3b.compile_to_binary) was the remaining gap. Without it,flt-using programs would refuse to compile to a binary because the non-bool predicate guard emits OP_PANIC_UNWRAP unconditionally. Pure parity with the JIT lowering, no new behaviour for tree or VM.tests/regression_hof_flt_fld_flatmap.rscovering happy paths, empty lists, single-element, all-pass / all-fail, mixed inner sizes (flatmap), text accumulator (fld), and composition withmap. Addsexamples/flt-basics.iloandexamples/fld-sum.ilowith-- run/-- outdirectives sotests/examples_engines.rsexercises them across every engine.Test plan
cargo build --release --features craneliftcleancargo test --release --features cranelift— 4847 passed, 0 failed, 66 ignored (down from 71)cargo fmt --checkcleancargo clippy --release --features cranelift --all-targets -- -D warningscleantests/regression_hof_flt_fld_flatmap.rspasses on tree + VM + Cranelift (14/14)tests/examples_engines.rspasses (includes newflt-basics.ilo,fld-sum.ilo, and liftedflatmap.ilo)Follow-ups
grp,uniqby, 2-argsrt,partition. These need either new opcodes (Map ops, Set ops, sort) or a tree-bridge variant that takes the active ASTProgramfor user-fn callback resolution. Approx 17 moreDynamic dispatch (OP_CALL_DYN emission) arrives in PR 2.tests get unignored.map fn ctx xs, etc.) across all HOFs. Liftsengine-skipfromclosure-bind.ilo.f=dbl; f 10). Unignoresvm_fnref_callee_from_scope,vm_fn_ref_via_ref_expr,vm_text_callee_from_scope,vm_user_hof_fn_type.