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.
This commit is contained in:
@@ -2,15 +2,168 @@
|
||||
//!
|
||||
//! After interactive development, `CONSOLIDATE` recompiles everything:
|
||||
//! - All `call_indirect` replaced with direct `call`
|
||||
//! - Cross-word optimizations (inlining, constant propagation)
|
||||
//! - Single WASM module output for maximum performance
|
||||
|
||||
// TODO: Step 12 - Consolidation recompiler implementation
|
||||
//!
|
||||
//! 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 placeholder() {
|
||||
// Consolidation tests will be added in Step 12
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user