Lift map/flt/fld closure-bind to native VM dispatch (PR A of ILO-45)#619
Merged
Conversation
PR A of ILO-45. Adds three new compiler arms in src/vm/mod.rs that emit native OP_CALL_DYN loops for the closure-bind ctx variants: - map fn ctx xs argc=2, per-iter call(item, ctx) - flt fn ctx xs argc=2, per-iter call(item, ctx), bool typecheck + LISTAPPEND - fld fn ctx xs init argc=3, per-iter call(acc, item, ctx) Removes the three arms from is_tree_bridge_eligible. Cranelift JIT and AOT inherit via the existing OP_CALL_DYN codegen (jit_call_dyn), so this fixes the closure-bind gap on JIT that examples/closure-bind.@ had been engine-skipping since PR 3c. Reg layout mirrors the existing 2-arg HOF arms: res + N contiguous arg slots, with the same contiguity assertions. Closure captures auto-append after the user args via the dispatcher's closure path at mod.rs:9785, matching the tree-walker's [item, ctx, ...captures] / [acc, item, ctx, ...captures] call_args order at runtime/mod.rs:5737 / :5958. srt 3 and rsrt 3 still bridge pending PR B's finalizer-opcode extension. ct 2/3 and rsrt 2 are PR C. Test plan: examples/closure-bind.@ now exercises map/flt/fld closure-bind across default / --vm / --jit. All four assertions match (main, prices- demo, above-demo, weighted-demo). Full suite green: 3348 unit tests pass.
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
This was referenced May 22, 2026
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
PR A of ILO-45 — first slice of the actual tree-walker eval-loop deletion. Lifts
map fn ctx xs/flt fn ctx xs/fld fn ctx xs initfrom tree-bridge dispatch to native VM dispatch viaOP_CALL_DYN. Cranelift inherits via the existingjit_call_dynhelper.The bridge for these three HOFs called
eval_body(the tree-walker's expression evaluator) per-element to run the user's closure-bind callback. After this PR they dispatch natively. Three down, five HOFs (srt 3,rsrt 2,rsrt 3,ct 2,ct 3) plus the non-HOF bridge entries to go before the eval loop can actually be deleted.Why now
PR #613 shipped the user-visible promise (no
Engine::Tree, no--run-tree, runtime renamed) but kepteval_bodyalive because the bridge for closure-bind HOFs called into it. This PR starts unwinding that dependency.ILO-234's "bridge is the right abstraction" framing held for non-HOF builtins (regex / fmt / fs / crypto / calendar — cheap round-trip, no eval involvement). For HOFs the round-trip goes through
call_function→eval_body, which is the load-bearing path that keeps the tree-walker alive. Lifting these natively is the only way to actually delete it.What's in the diff
Single commit, two files:
src/vm/mod.rscompile_call:(Builtin::Map, 3),(Builtin::Flt, 3),(Builtin::Fld, 4). Each emitsOP_FOREACHPREP/OP_CALL_DYN/OP_LISTAPPEND(or fld's accumulator refresh) in the same shape as the existing 2-arg arms. Reg layout: res + 2 or 3 contiguous arg slots. flt 3 keeps the bool typecheck + WRAPERR/PANIC_UNWRAP error path from flt 2.is_tree_bridge_eligible: dropped the three entries;Srt, 3andRsrt, 3retained pending PR B.examples/closure-bind.@engine-skip: jitdirective (closure-bind HOFs now work on JIT)above-demo(flt 3) andweighted-demo(fld 4) with assertable outputCranelift inherits via the existing OP_CALL_DYN codegen — no Cranelift changes needed.
Test plan
cargo test --release --features cranelift— full suite green, 3348+ unit tests pass, no regressionsexamples/closure-bind.@runs identically across default /--vm/--jitfor all four demo functionscargo test --release --features cranelift --test examples_enginespassesFollow-ups
srt 3/rsrt 2/rsrt 3— needsOP_SRT_BY_KEYdesc-flag extension or newOP_RSRT_BY_KEYct 2/ct 3— needs finalizer or inline counter loopeval_bodyreachability and lift any that need iteval_body/eval_stmt/eval_expr/Env/trampoline/ACTIVE_AST_PROGRAM(headline LoC win)