1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
//! Virtual Stack Management
//!
//! This module handles the virtual register stack for optimizing stack operations.
//! Values are kept in SSA variables instead of memory when possible.
use super::{CodeGen, CodeGenError, MAX_VIRTUAL_STACK, VirtualValue};
use std::fmt::Write as _;
impl CodeGen {
/// Generate a fresh temporary variable name
pub(super) fn fresh_temp(&mut self) -> String {
let name = format!("{}", self.temp_counter);
self.temp_counter += 1;
name
}
/// Generate a fresh block label
pub(super) fn fresh_block(&mut self, prefix: &str) -> String {
let name = format!("{}{}", prefix, self.block_counter);
self.block_counter += 1;
name
}
/// Spill all virtual register values to memory (Issue #189).
///
/// This must be called before:
/// - Function/word calls (callee expects values in memory)
/// - Control flow points (branches need consistent memory state)
/// - Operations that access values deeper than virtual stack
///
/// Returns the new stack pointer after spilling all values.
pub(super) fn spill_virtual_stack(&mut self, stack_var: &str) -> Result<String, CodeGenError> {
if self.virtual_stack.is_empty() {
return Ok(stack_var.to_string());
}
let mut current_sp = stack_var.to_string();
// Spill each value to memory (oldest first, so they're in correct order)
for value in std::mem::take(&mut self.virtual_stack) {
match &value {
VirtualValue::Int { ssa_var, .. } => {
self.emit_store_int(¤t_sp, ssa_var)?;
}
VirtualValue::Bool { ssa_var } => {
self.emit_store_bool(¤t_sp, ssa_var)?;
}
VirtualValue::Float { ssa_var } => {
// Floats are heap-boxed via runtime call.
// push_float handles both the store and SP advance.
let next = self.fresh_temp();
writeln!(
&mut self.output,
" %{} = call ptr @patch_seq_push_float(ptr %{}, double %{})",
next, current_sp, ssa_var
)?;
current_sp = next;
continue;
}
}
// Advance stack pointer to next Value slot
let next_sp = self.emit_stack_gep(¤t_sp, 1)?;
current_sp = next_sp;
}
Ok(current_sp)
}
/// Push a value to the virtual stack, spilling if at capacity.
///
/// Returns the new memory stack pointer (unchanged if value stays virtual,
/// advanced if we had to spill).
pub(super) fn push_virtual(
&mut self,
value: VirtualValue,
stack_var: &str,
) -> Result<String, CodeGenError> {
// If at capacity, spill all to memory first
if self.virtual_stack.len() >= MAX_VIRTUAL_STACK {
let new_sp = self.spill_virtual_stack(stack_var)?;
self.virtual_stack.push(value);
Ok(new_sp)
} else {
self.virtual_stack.push(value);
Ok(stack_var.to_string())
}
}
}