Files
WAFER/crates/core/src/consolidate.rs
T
ok2 4f59ffa19e Implement stack-to-local promotion and consolidation recompiler
Stack-to-local promotion (Phase 1: straight-line code):
- Words with no control flow/calls use WASM locals instead of memory stack
- Stack manipulation (Swap, Rot, Nip, Tuck, Dup, Drop) emits ZERO instructions
- ~7x instruction reduction for arithmetic-heavy words like DUP *
- Pre-loads consumed items from memory, writes results back at exit

Consolidation recompiler (CONSOLIDATE word):
- Recompiles all IR-based words into single WASM module
- Direct call instructions instead of call_indirect through function table
- Cranelift can inline and optimize across word boundaries
- All control flow variants support consolidated calls

342 unit tests + 11 compliance, all passing.
2026-04-01 22:56:00 +02:00

170 lines
5.7 KiB
Rust

//! Consolidation recompiler: merge all JIT-compiled words into a single WASM module.
//!
//! After interactive development, `CONSOLIDATE` recompiles everything:
//! - All `call_indirect` replaced with direct `call`
//! - Single WASM module output for maximum performance
//!
//! The implementation lives across two places:
//! - `codegen::compile_consolidated_module()` generates the multi-function WASM module
//! - `outer::ForthVM::consolidate()` orchestrates collection, compilation, and table update
#[cfg(test)]
mod tests {
use std::collections::HashMap;
use crate::codegen::compile_consolidated_module;
use crate::dictionary::WordId;
use crate::ir::IrOp;
#[test]
fn consolidated_module_validates_empty() {
// Empty word list should produce nothing (but we guard against this at call site)
let words = vec![];
let map = HashMap::new();
let result = compile_consolidated_module(&words, &map, 16);
// Empty is valid -- should produce a valid module with no functions
assert!(result.is_ok());
}
#[test]
fn consolidated_module_validates_single_word() {
let words = vec![(WordId(1), vec![IrOp::PushI32(42)])];
let mut map = HashMap::new();
map.insert(WordId(1), 1u32); // function index 1 (after emit import)
let result = compile_consolidated_module(&words, &map, 16);
assert!(result.is_ok());
}
#[test]
fn consolidated_module_validates_multiple_words() {
let words = vec![
(WordId(1), vec![IrOp::PushI32(1)]),
(WordId(2), vec![IrOp::PushI32(2), IrOp::Add]),
(
WordId(3),
vec![IrOp::Call(WordId(1)), IrOp::Call(WordId(2))],
),
];
let mut map = HashMap::new();
map.insert(WordId(1), 1u32);
map.insert(WordId(2), 2u32);
map.insert(WordId(3), 3u32);
let result = compile_consolidated_module(&words, &map, 16);
assert!(result.is_ok());
}
#[test]
fn consolidated_module_validates_external_call() {
// Word 3 calls WordId(99) which is NOT in the module -- should use call_indirect
let words = vec![(WordId(3), vec![IrOp::Call(WordId(99))])];
let mut map = HashMap::new();
map.insert(WordId(3), 1u32);
let result = compile_consolidated_module(&words, &map, 256);
assert!(result.is_ok());
}
#[test]
fn consolidated_module_validates_tail_call() {
let words = vec![
(WordId(1), vec![IrOp::PushI32(1)]),
(WordId(2), vec![IrOp::TailCall(WordId(1))]),
];
let mut map = HashMap::new();
map.insert(WordId(1), 1u32);
map.insert(WordId(2), 2u32);
let result = compile_consolidated_module(&words, &map, 16);
assert!(result.is_ok());
}
#[test]
fn consolidated_module_validates_control_flow_with_calls() {
// IF body contains a call to a consolidated word
let words = vec![
(WordId(1), vec![IrOp::PushI32(1)]),
(
WordId(2),
vec![
IrOp::PushI32(1),
IrOp::If {
then_body: vec![IrOp::Call(WordId(1))],
else_body: Some(vec![IrOp::PushI32(0)]),
},
],
),
];
let mut map = HashMap::new();
map.insert(WordId(1), 1u32);
map.insert(WordId(2), 2u32);
let result = compile_consolidated_module(&words, &map, 16);
assert!(result.is_ok());
}
#[test]
fn consolidated_module_validates_loop_with_calls() {
// DO LOOP body contains a call to a consolidated word
let words = vec![
(WordId(1), vec![IrOp::PushI32(1), IrOp::Add]),
(
WordId(2),
vec![
IrOp::PushI32(0),
IrOp::PushI32(3),
IrOp::PushI32(0),
IrOp::DoLoop {
body: vec![IrOp::Call(WordId(1))],
is_plus_loop: false,
},
],
),
];
let mut map = HashMap::new();
map.insert(WordId(1), 1u32);
map.insert(WordId(2), 2u32);
let result = compile_consolidated_module(&words, &map, 16);
assert!(result.is_ok());
}
#[test]
fn consolidated_module_validates_begin_until_with_calls() {
let words = vec![
(WordId(1), vec![IrOp::PushI32(1), IrOp::Sub]),
(
WordId(2),
vec![
IrOp::PushI32(5),
IrOp::BeginUntil {
body: vec![IrOp::Call(WordId(1)), IrOp::Dup, IrOp::ZeroEq],
},
],
),
];
let mut map = HashMap::new();
map.insert(WordId(1), 1u32);
map.insert(WordId(2), 2u32);
let result = compile_consolidated_module(&words, &map, 16);
assert!(result.is_ok());
}
#[test]
fn consolidated_module_validates_begin_while_repeat_with_calls() {
let words = vec![
(WordId(1), vec![IrOp::PushI32(1), IrOp::Sub]),
(
WordId(2),
vec![
IrOp::PushI32(3),
IrOp::BeginWhileRepeat {
test: vec![IrOp::Dup],
body: vec![IrOp::Call(WordId(1))],
},
],
),
];
let mut map = HashMap::new();
map.insert(WordId(1), 1u32);
map.insert(WordId(2), 2u32);
let result = compile_consolidated_module(&words, &map, 16);
assert!(result.is_ok());
}
}