Skip to content

🚀 Bytecode VM#93

Merged
timfennis merged 193 commits intomasterfrom
feature/bytecode-vm
Mar 21, 2026
Merged

🚀 Bytecode VM#93
timfennis merged 193 commits intomasterfrom
feature/bytecode-vm

Conversation

@timfennis
Copy link
Copy Markdown
Owner

@timfennis timfennis commented Mar 8, 2026

Summary

Replaces the tree-walk interpreter with a bytecode-compiled stack-based VM as the single execution path (~192 commits).

Compiler & VM

  • Bytecode compiler translates the annotated AST into a flat OpCode sequence
  • Stack-based VM executes bytecode with support for locals, globals, upvalues, and closures
  • Upvalue hoisting in the analyser enables mutable closed-over variables
  • Control flow: if/while/for/break/continue/return all compile to jump instructions
  • Tuple destructuring via Unpack opcode
  • Map/set/list comprehensions compile to inline loops
  • Op-assignment (+=, etc.) for identifiers, index expressions, and destructuring
  • Closure capture and upvalue resolution (including nested closures)
  • Memoization support for pure functions

Stdlib & dispatch

  • FunctionRegistry + vm_native field on Function for bridge-free native dispatch
  • Arithmetic, comparison, boolean, trig/math operators migrated to VM-native dispatch
  • HOF callbacks (map, filter, fold, all, any, etc.) work with VM closures
  • Dynamic dispatch for overloaded functions at runtime

Infrastructure

  • ndc_vm crate with compiler and VM
  • vm-trace feature for source-annotated instruction tracing
  • -disassemble` CLI subcommand
  • All existing tests pass against the VM backend

Benchmarks (v0.2.1 tree-walk vs VM)

Benchmark v0.2.1 (tree-walk) VM Speedup
ackermann stack overflow 155.0 ms -
bigint 9.3 ms 6.7 ms 1.4x
closures 248.5 ms 78.6 ms 3.2x
fibonacci 318.0 ms 80.1 ms 4.0x
hof_pipeline 84.8 ms 36.7 ms 2.3x
map_ops 74.7 ms 28.7 ms 2.6x
matrix_mul 133.1 ms 67.0 ms 2.0x
perlin 216.5 ms 67.5 ms 3.2x
pi_approx 103.8 ms 32.3 ms 3.2x
print_heavy 8.9 ms 5.6 ms 1.6x
quicksort 192.0 ms 75.2 ms 2.6x
sieve 439.4 ms 125.2 ms 3.5x
string_concat 16.0 ms 13.4 ms 1.2x

🤖 Generated with Claude Code

@timfennis timfennis changed the title Feature/bytecode vm 🚀 Bytecode VM Mar 10, 2026
@timfennis timfennis marked this pull request as ready for review March 21, 2026 20:11
…layout

- Parser: stop wrapping VariableDeclaration in Statement; add it to
  is_valid_statement so the parse loop continues
- Compiler: split VariableDeclaration (push value only) from Assignment
  (push value + SetLocal + push unit); remove TODO comment
- VM: start with empty stack instead of pushing the script function,
  fixing local slot 0 conflicts that corrupted all local variables
- Tests: add test_declaration and test_assignment compiler tests
timfennis and others added 26 commits March 21, 2026 21:19
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- M1: extract compile_for_iterations helper, removing ~120 lines of duplicated loop scaffolding
- M2: make VmIterator::deep_copy a required method; add missing impls for MinHeapIter, MaxHeapIter, StringIter
- RD1: comment explaining synthetic unit push in no-else branch
- RD2: remove dead `let memo =` binding in dispatch_call
- RD3: descriptive panic messages in Closure opcode handling
- RD4: rename max_local field to num_locals (matching its accessor)
- Remove REVIEW.md (all actionable items resolved)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…() builtin

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ly-qualified Value paths

- Rename match.rs → types.rs with unified NdcType enum and classify() function
- Replace panics with syn::Error for proper compiler diagnostics
- Remove dead #[function] stub, add arg_error() and refcell_collection_arg() helpers
- Add module-level documentation to all macro crate files
- Update stdlib to use short Value/Object/SeqValue/MapValue imports everywhere

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…n hot paths

- Merge MinHeapIter/MaxHeapIter into single HeapIter
- Merge List/Tuple unpack branches in VM
- Combine RangeInclusive/RangeExclusive compiler arms
- Inline vectorization_pairs into try_vectorized_call
- Use &OpCode references in Chunk::iter() and disassembler
- Cache Value::unit() via thread_local (9-16% faster on benchmarks)
- Minor: fix parens, rename shadowed param, update ndc_macros for HeapIter

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…resolvers

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…inition error

- Upvalues no longer short-circuit resolution: the scope walk continues
  to find exact matches for other overloads in parent scopes
- add_upvalue deduplicates by name+source so multiple overloads of the
  same function get separate upvalue entries
- Same-scope same-arity fn redefinition is now a compile error
- 9 new tests for shadowing/overloading semantics
- Updated manual with shadowing documentation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Function resolution now filters out bindings with known non-callable
types (Int, String, etc.) from the dynamic fallback, turning confusing
runtime errors into compile-time errors. Also tracks explicit `return`
types via a stack so `return value;` correctly infers the function's
return type instead of unit.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… Any

return/break/continue now produce Never (bottom type) instead of
unit. Statement propagates Never so blocks see that control doesn't
fall through. This prevents `return 1;` from polluting the function's
return type with unit via lub.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… upvalues

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@timfennis timfennis force-pushed the feature/bytecode-vm branch from 1015041 to 5b1cd6d Compare March 21, 2026 20:19
@timfennis timfennis enabled auto-merge (squash) March 21, 2026 20:20
@timfennis timfennis merged commit e9a5365 into master Mar 21, 2026
1 check passed
@timfennis timfennis deleted the feature/bytecode-vm branch March 21, 2026 20:21
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