♻️ refactor(mq-lang): remove tarn feature flag and tree-walking evaluator - #2307
Open
harehare wants to merge 20 commits into
Open
♻️ refactor(mq-lang): remove tarn feature flag and tree-walking evaluator#2307harehare wants to merge 20 commits into
harehare wants to merge 20 commits into
Conversation
Merging this PR will degrade performance by 8.18%
|
| Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|
| ❌ | eval_compiled_nested_function_calls |
772.6 µs | 1,000.6 µs | -22.79% |
| ❌ | eval_compiled_array_chained_operations |
2.8 ms | 3.4 ms | -17.78% |
| ❌ | eval_compiled_array_filter |
3.2 ms | 3.9 ms | -17.21% |
| ❌ | eval_compiled_array_fold |
369.4 µs | 435.6 µs | -15.21% |
| ❌ | eval_compiled_array_map |
1.8 ms | 2 ms | -12.27% |
| ❌ | eval_compiled_reused_single_input_with_globals |
21 µs | 23.8 µs | -11.75% |
| ⚡ | eval_compiled_owned_markdown_tree |
3.8 ms | 2.4 ms | +59.42% |
| 🆕 | eval_compiled_dynamic_builtin_call |
N/A | 1.7 ms | N/A |
| 🆕 | eval_compiled_dict_field_access |
N/A | 6 ms | N/A |
| 🆕 | eval_compiled_direct_builtin_calls |
N/A | 6.9 ms | N/A |
| 🆕 | eval_compiled_large_dict_field_access |
N/A | 7.2 ms | N/A |
| 🆕 | eval_compiled_owned_markdown_tree_without_matches |
N/A | 2.6 ms | N/A |
| 🆕 | eval_compiled_while |
N/A | 9.5 ms | N/A |
Tip
Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.
Comparing remove-tarn-feature-tree-walker (25f15ff) with main (e9d12c0)
Footnotes
-
36 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩
harehare
force-pushed
the
remove-tarn-feature-tree-walker
branch
3 times, most recently
from
September 10, 2026 10:47
3d2bc94 to
a6138f4
Compare
harehare
added this pull request to stack #2323
September 10, 2026 12:52
harehare
force-pushed
the
remove-tarn-feature-tree-walker
branch
from
September 10, 2026 13:18
25f15ff to
fe604ec
Compare
…ator Make the Tarn bytecode VM the sole, unconditional execution engine. Deletes the tree-walking evaluator (`eval.rs`) and its `Env` module, collapses the dual-backend split in Engine/RuntimeValue/DebugContext/builtins, drops the deprecated set_variable/get_variable builtins, removes the now-pointless `tarn` feature from every crate that passed it through, and deletes the tree-walker-vs-VM differential fuzz harness.
- Replace recursive run_chunk_inner_impl/run_chunk calls with an iterative frame-stack trampoline (run_frames/run_frame_slice), decoupling mq call depth from Rust's native stack. - Add CallStatic/CallSelf/CallUpvalue opcodes to call known-static, self-recursive, and captured-immutable functions directly, skipping closure materialization/loading. - Share closure upvalues via Option<Shared<Vec<Cell>>> instead of cloning a Vec<Cell> on every call; drop RefCell from non-capturing Locals::Flat in favor of direct &mut access.
Adds owning update_value/update_children_value variants (into_with_value/into_with_children_value) so the VM's markdown update path clones the tree once instead of cloning again for the mutated child, while keeping the existing COW value() API for callers that still need an unmodified copy. Also consolidates the benchmark suite to keep only steady-state eval_compiled_* benchmarks (dropping cold/duplicate eval_* variants) and fixes a test added in 862b7eb1 that called a soft (mq-defined) builtin through compile_program's BuiltinPrelude::None path, where it can never resolve — swapped for a native builtin so the test actually exercises the arg-order guarantee it documents.
Adds a bytecode-optimizer peephole that recognizes a BinaryLocalLocal or BinaryLocalConst comparison immediately followed by JumpIfFalse — the exact shape every `if`/`while`/`until` condition compiles to — and fuses it into a single JumpIfFalseLocalLocal/JumpIfFalseLocalConst instruction. The comparison is evaluated and branched on directly, without ever pushing the boolean result onto the operand stack, cutting one dispatch iteration (and a push+pop) from every loop/conditional check. The fusion runs entirely as a post-hoc rewrite in optimize_chunk, so the compiler's AST-to-bytecode lowering (and its identifier-shadowing rules) is untouched. eval_compiled_while median: 450.9µs -> 373.6µs (~17%, --sample-count 50). eval_compiled_fibonacci improves similarly since its recursion is dominated by a single comparison-guarded branch. No other benchmark regressed. Full workspace test suite (8746 tests) and clippy pass.
RuntimeValue::Dict was a BTreeMap<Ident, RuntimeValue>, ordered by each key's interned symbol id rather than its text or insertion order — an accidental, fragile ordering that could change based on unrelated code elsewhere in a program (e.g. an earlier `let apple = 1` reordering a later JSON object's "apple" key). Switches to IndexMap<Ident, RuntimeValue, FxBuildHasher> (re-exported as mq_lang::DictMap), which preserves true insertion order and replaces per-key tree comparisons with FxHash-based lookup. FxHash is safe here since keys are opaque interned symbol ids, not raw untrusted strings, matching how the compiler already hashes Ident elsewhere (FxHashMap<Ident, _> in tarn/compiler.rs). Benchmarked (--sample-count 30): a 100-key dict with varying-key get() lookups improves ~10% (433.3us -> 391.4us median); a tiny fixed 5-key dict accessed in a loop is a wash, since builtin-call dispatch and Ident interning dominate that shape's per-call cost. Updates every downstream Dict call site across mq-lang, mq-run, mq-web-api, and mq-test to the new type, changes del()'s dict removal to shift_remove (preserving order), and regenerates snapshot/test fixtures that had baked in the old accidental key order (output content is unchanged; only key order in the fixtures moved to match true insertion order).
node_values() matched on self.clone() to extract one field, deep-cloning the matched variant's other fields (e.g. position) for nothing. Match on &self and clone only the values field that's actually returned.
A failed frame (e.g. mid-construction of an array/dict literal) could leave partial operands above its own stack_base. unwind() never truncated them, so a subsequent catch frame's stack_base captured that leftover garbage, and the parent's own accumulator got overwritten once the catch's result was pushed. Truncate to the failed frame's stack_base right after popping it, matching what run_frame_slice already does on the success path.
harehare
force-pushed
the
remove-tarn-feature-tree-walker
branch
from
September 10, 2026 13:59
f7ea221 to
3d32c82
Compare
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
Make the Tarn bytecode VM the sole, unconditional execution engine. Deletes the tree-walking evaluator (
eval.rs) and itsEnvmodule, collapses the dual-backend split in Engine/RuntimeValue/DebugContext/builtins, drops the deprecated set_variable/get_variable builtins, removes the now-pointlesstarnfeature from every crate that passed it through, and deletes the tree-walker-vs-VM differential fuzz harness.Type of Change
Checklist
cargo fmtandcargo clippyand addressed any warningsjust test-alland all tests pass/docs, crateREADME.md) if neededAdditional Context