Add stack offset propagation, clean up some deprecated things.

This commit is contained in:
Jesse Brault 2026-03-08 13:11:07 -05:00
parent 12c4d0eb83
commit 12174c9cf6
17 changed files with 128 additions and 61 deletions

View File

@ -64,16 +64,14 @@ fn main() {
} }
} }
for ir_function in &mut ir_functions { let mut functions: Vec<Function> = vec![];
ir_function.assign_registers(args.register_count);
}
let mut constants_table = ConstantsTable::new(); let mut constants_table = ConstantsTable::new();
let functions: Vec<Function> = ir_functions for ir_function in &mut ir_functions {
.iter() let (_, stack_size) = ir_function.assign_registers(args.register_count);
.map(|ir_function| ir_function.assemble(&mut constants_table)) let function = ir_function.assemble(stack_size, &mut constants_table);
.collect(); functions.push(function);
}
if args.show_asm { if args.show_asm {
for function in &functions { for function in &functions {

View File

@ -87,14 +87,9 @@ impl Function {
// handle parameters // handle parameters
symbol_table.push_scope(&format!("parameter_scope({})", self.declared_name)); symbol_table.push_scope(&format!("parameter_scope({})", self.declared_name));
let mut parameter_symbols = vec![]; let mut parameter_symbols = vec![];
let parameter_count = self.parameters.len(); for parameter in &mut self.parameters {
let stack_frame_offset_base = (parameter_count as isize).neg();
for (i, parameter) in self.parameters.iter_mut().enumerate() {
match parameter.gather_declared_names(symbol_table) { match parameter.gather_declared_names(symbol_table) {
Ok(parameter_symbol) => { Ok(parameter_symbol) => {
parameter_symbol
.borrow_mut()
.set_stack_frame_offset(stack_frame_offset_base + (i as isize));
parameter_symbols.push(parameter_symbol); parameter_symbols.push(parameter_symbol);
} }
Err(mut parameter_diagnostics) => { Err(mut parameter_diagnostics) => {

View File

@ -1,7 +1,7 @@
use crate::constants_table::ConstantsTable; use crate::constants_table::ConstantsTable;
use crate::ir::ir_expression::IrExpression; use crate::ir::ir_expression::IrExpression;
use crate::ir::ir_variable::IrVrVariableDescriptor; use crate::ir::ir_variable::IrVrVariableDescriptor;
use crate::ir::register_allocation::VrUser; use crate::ir::register_allocation::{OffsetCounter, VrUser};
use dvm_lib::instruction::AddOperand; use dvm_lib::instruction::AddOperand;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
@ -68,4 +68,8 @@ impl VrUser for IrAdd {
self.left.propagate_register_assignments(assignments); self.left.propagate_register_assignments(assignments);
self.right.propagate_register_assignments(assignments); self.right.propagate_register_assignments(assignments);
} }
fn propagate_stack_offsets(&mut self, _counter: &mut OffsetCounter) {
// no-op, no definitions here
}
} }

View File

@ -2,7 +2,7 @@ use crate::constants_table::ConstantsTable;
use crate::ir::assemble::{Assemble, InstructionsBuilder}; use crate::ir::assemble::{Assemble, InstructionsBuilder};
use crate::ir::ir_operation::IrOperation; use crate::ir::ir_operation::IrOperation;
use crate::ir::ir_variable::{IrVariable, IrVariableDescriptor, IrVrVariableDescriptor}; use crate::ir::ir_variable::{IrVariable, IrVariableDescriptor, IrVrVariableDescriptor};
use crate::ir::register_allocation::VrUser; use crate::ir::register_allocation::{OffsetCounter, VrUser};
use dvm_lib::instruction::{Instruction, Location}; use dvm_lib::instruction::{Instruction, Location};
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
@ -68,6 +68,13 @@ impl VrUser for IrAssign {
} }
self.initializer.propagate_register_assignments(assignments); self.initializer.propagate_register_assignments(assignments);
} }
fn propagate_stack_offsets(&mut self, counter: &mut OffsetCounter) {
let mut borrowed_destination = self.destination.borrow_mut();
if let IrVariableDescriptor::Stack(stack_variable) = borrowed_destination.descriptor_mut() {
stack_variable.set_offset(counter.next());
}
}
} }
impl Assemble for IrAssign { impl Assemble for IrAssign {

View File

@ -2,7 +2,7 @@ use crate::constants_table::ConstantsTable;
use crate::ir::assemble::{Assemble, InstructionsBuilder}; use crate::ir::assemble::{Assemble, InstructionsBuilder};
use crate::ir::ir_statement::IrStatement; use crate::ir::ir_statement::IrStatement;
use crate::ir::ir_variable::IrVrVariableDescriptor; use crate::ir::ir_variable::IrVrVariableDescriptor;
use crate::ir::register_allocation::{HasVrUsers, VrUser}; use crate::ir::register_allocation::{HasVrUsers, OffsetCounter, VrUser};
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
@ -75,6 +75,12 @@ impl VrUser for IrBlock {
statement.propagate_register_assignments(assignments); statement.propagate_register_assignments(assignments);
} }
} }
fn propagate_stack_offsets(&mut self, counter: &mut OffsetCounter) {
for statement in &mut self.statements {
statement.propagate_stack_offsets(counter);
}
}
} }
impl Assemble for IrBlock { impl Assemble for IrBlock {
@ -145,7 +151,7 @@ mod tests {
if let ModuleLevelDeclaration::Function(main) = main { if let ModuleLevelDeclaration::Function(main) = main {
let mut main_ir = main.to_ir(&symbol_table); let mut main_ir = main.to_ir(&symbol_table);
let register_assignments = main_ir.assign_registers(2); let (register_assignments, _) = main_ir.assign_registers(2);
assert_eq!(register_assignments.len(), 4); assert_eq!(register_assignments.len(), 4);
} else { } else {
unreachable!() unreachable!()

View File

@ -2,7 +2,7 @@ use crate::constants_table::ConstantsTable;
use crate::ir::assemble::{Assemble, InstructionsBuilder}; use crate::ir::assemble::{Assemble, InstructionsBuilder};
use crate::ir::ir_expression::IrExpression; use crate::ir::ir_expression::IrExpression;
use crate::ir::ir_variable::IrVrVariableDescriptor; use crate::ir::ir_variable::IrVrVariableDescriptor;
use crate::ir::register_allocation::VrUser; use crate::ir::register_allocation::{OffsetCounter, VrUser};
use crate::symbol::FunctionSymbol; use crate::symbol::FunctionSymbol;
use dvm_lib::instruction::Instruction; use dvm_lib::instruction::Instruction;
use std::cell::RefCell; use std::cell::RefCell;
@ -53,6 +53,10 @@ impl VrUser for IrCall {
argument.propagate_register_assignments(assignments); argument.propagate_register_assignments(assignments);
} }
} }
fn propagate_stack_offsets(&mut self, _counter: &mut OffsetCounter) {
// no-op, because no definitions here
}
} }
impl Assemble for IrCall { impl Assemble for IrCall {
@ -74,7 +78,6 @@ impl Assemble for IrCall {
symbol.parameters().len(), symbol.parameters().len(),
)); ));
} }
// todo: handle function postlude
} }
} }

View File

@ -3,7 +3,7 @@ use crate::ir::ir_parameter::IrParameter;
use crate::ir::ir_variable::{ use crate::ir::ir_variable::{
IrStackVariableDescriptor, IrVariable, IrVariableDescriptor, IrVrVariableDescriptor, IrStackVariableDescriptor, IrVariable, IrVariableDescriptor, IrVrVariableDescriptor,
}; };
use crate::ir::register_allocation::VrUser; use crate::ir::register_allocation::{OffsetCounter, VrUser};
use crate::type_info::TypeInfo; use crate::type_info::TypeInfo;
use dvm_lib::instruction::{AddOperand, Location, MoveOperand, PushOperand, ReturnOperand}; use dvm_lib::instruction::{AddOperand, Location, MoveOperand, PushOperand, ReturnOperand};
use std::cell::RefCell; use std::cell::RefCell;
@ -225,4 +225,8 @@ impl VrUser for IrExpression {
} }
} }
} }
fn propagate_stack_offsets(&mut self, _counter: &mut OffsetCounter) {
// no-op
}
} }

View File

@ -34,21 +34,21 @@ impl IrFunction {
} }
} }
/// Returns the register assignments for debugging purposes
pub fn assign_registers( pub fn assign_registers(
&mut self, &mut self,
register_count: usize, register_count: usize,
) -> HashMap<IrVrVariableDescriptor, usize> { ) -> (HashMap<IrVrVariableDescriptor, usize>, isize) {
self.entry.borrow_mut().assign_registers(register_count) self.entry.borrow_mut().assign_registers(register_count)
} }
pub fn assemble(&self, constants_table: &mut ConstantsTable) -> Function { pub fn assemble(&self, stack_size: isize, constants_table: &mut ConstantsTable) -> Function {
let mut builder = InstructionsBuilder::new(); let mut builder = InstructionsBuilder::new();
self.entry.borrow().assemble(&mut builder, constants_table); self.entry.borrow().assemble(&mut builder, constants_table);
let instructions = builder.take_instructions(); let instructions = builder.take_instructions();
Function::new( Function::new(
self.function_symbol.borrow().name_owned(), self.function_symbol.borrow().name_owned(),
self.parameters.len(), self.parameters.len(),
stack_size,
instructions, instructions,
) )
} }

View File

@ -2,7 +2,7 @@ use crate::ir::ir_add::IrAdd;
use crate::ir::ir_call::IrCall; use crate::ir::ir_call::IrCall;
use crate::ir::ir_expression::IrExpression; use crate::ir::ir_expression::IrExpression;
use crate::ir::ir_variable::IrVrVariableDescriptor; use crate::ir::ir_variable::IrVrVariableDescriptor;
use crate::ir::register_allocation::VrUser; use crate::ir::register_allocation::{OffsetCounter, VrUser};
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
@ -75,4 +75,8 @@ impl VrUser for IrOperation {
} }
} }
} }
fn propagate_stack_offsets(&mut self, _counter: &mut OffsetCounter) {
// no-op
}
} }

View File

@ -2,7 +2,7 @@ use crate::constants_table::ConstantsTable;
use crate::ir::assemble::{Assemble, InstructionsBuilder}; use crate::ir::assemble::{Assemble, InstructionsBuilder};
use crate::ir::ir_expression::IrExpression; use crate::ir::ir_expression::IrExpression;
use crate::ir::ir_variable::IrVrVariableDescriptor; use crate::ir::ir_variable::IrVrVariableDescriptor;
use crate::ir::register_allocation::VrUser; use crate::ir::register_allocation::{OffsetCounter, VrUser};
use dvm_lib::instruction::Instruction; use dvm_lib::instruction::Instruction;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
@ -44,10 +44,14 @@ impl VrUser for IrReturn {
ir_expression.propagate_register_assignments(assignments); ir_expression.propagate_register_assignments(assignments);
} }
} }
fn propagate_stack_offsets(&mut self, _counter: &mut OffsetCounter) {
// no-op
}
} }
impl Assemble for IrReturn { impl Assemble for IrReturn {
fn assemble(&self, builder: &mut InstructionsBuilder, constants_table: &mut ConstantsTable) { fn assemble(&self, builder: &mut InstructionsBuilder, _constants_table: &mut ConstantsTable) {
if let Some(ir_expression) = self.value.as_ref() { if let Some(ir_expression) = self.value.as_ref() {
builder.push(Instruction::SetReturnValue(ir_expression.return_operand())); builder.push(Instruction::SetReturnValue(ir_expression.return_operand()));
} }

View File

@ -4,7 +4,7 @@ use crate::ir::ir_assign::IrAssign;
use crate::ir::ir_call::IrCall; use crate::ir::ir_call::IrCall;
use crate::ir::ir_return::IrReturn; use crate::ir::ir_return::IrReturn;
use crate::ir::ir_variable::IrVrVariableDescriptor; use crate::ir::ir_variable::IrVrVariableDescriptor;
use crate::ir::register_allocation::VrUser; use crate::ir::register_allocation::{OffsetCounter, VrUser};
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
@ -61,6 +61,20 @@ impl VrUser for IrStatement {
} }
} }
} }
fn propagate_stack_offsets(&mut self, counter: &mut OffsetCounter) {
match self {
IrStatement::Assign(ir_assign) => {
ir_assign.propagate_stack_offsets(counter);
}
IrStatement::Call(ir_call) => {
ir_call.propagate_stack_offsets(counter);
}
IrStatement::Return(ir_return) => {
ir_return.propagate_stack_offsets(counter);
}
}
}
} }
impl Assemble for IrStatement { impl Assemble for IrStatement {

View File

@ -139,6 +139,10 @@ impl IrStackVariableDescriptor {
&self.name &self.name
} }
pub fn set_offset(&mut self, offset: isize) {
self.offset = Some(offset);
}
pub fn offset(&self) -> isize { pub fn offset(&self) -> isize {
self.offset.unwrap() self.offset.unwrap()
} }
@ -146,6 +150,6 @@ impl IrStackVariableDescriptor {
impl Display for IrStackVariableDescriptor { impl Display for IrStackVariableDescriptor {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}_b{}", self.name, self.block_id) write!(f, "{}_{}", self.name, self.block_id)
} }
} }

View File

@ -104,7 +104,7 @@ pub trait HasVrUsers {
fn assign_registers( fn assign_registers(
&mut self, &mut self,
register_count: usize, register_count: usize,
) -> HashMap<IrVrVariableDescriptor, usize> { ) -> (HashMap<IrVrVariableDescriptor, usize>, isize) {
let mut spills: HashSet<IrVrVariableDescriptor> = HashSet::new(); let mut spills: HashSet<IrVrVariableDescriptor> = HashSet::new();
loop { loop {
let mut interference_graph = self.interference_graph(); let mut interference_graph = self.interference_graph();
@ -122,7 +122,13 @@ pub trait HasVrUsers {
for vr_user in self.vr_users_mut() { for vr_user in self.vr_users_mut() {
vr_user.propagate_register_assignments(&registers); vr_user.propagate_register_assignments(&registers);
} }
return registers; // also set offsets
let mut offset_counter = OffsetCounter::new();
for vr_user in self.vr_users_mut() {
vr_user.propagate_stack_offsets(&mut offset_counter);
}
return (registers, offset_counter.get_count());
} }
} }
} }
@ -136,6 +142,27 @@ pub trait VrUser {
&mut self, &mut self,
assignments: &HashMap<IrVrVariableDescriptor, usize>, assignments: &HashMap<IrVrVariableDescriptor, usize>,
); );
fn propagate_stack_offsets(&mut self, counter: &mut OffsetCounter);
}
pub struct OffsetCounter {
counter: isize,
}
impl OffsetCounter {
pub fn new() -> Self {
Self { counter: 0 }
}
pub fn next(&mut self) -> isize {
let offset = self.counter;
self.counter += 1;
offset
}
pub fn get_count(&self) -> isize {
self.counter
}
} }
#[derive(Debug)] #[derive(Debug)]

View File

@ -51,9 +51,6 @@ pub struct ParameterSymbol {
name: Rc<str>, name: Rc<str>,
type_info: TypeInfo, type_info: TypeInfo,
ir_parameter: Option<Rc<IrParameter>>, ir_parameter: Option<Rc<IrParameter>>,
#[deprecated]
stack_frame_offset: Option<isize>,
} }
impl ParameterSymbol { impl ParameterSymbol {
@ -62,7 +59,6 @@ impl ParameterSymbol {
name: name.into(), name: name.into(),
type_info, type_info,
ir_parameter: None, ir_parameter: None,
stack_frame_offset: None,
} }
} }
@ -85,25 +81,12 @@ impl ParameterSymbol {
pub fn ir_parameter(&self) -> &Rc<IrParameter> { pub fn ir_parameter(&self) -> &Rc<IrParameter> {
self.ir_parameter.as_ref().unwrap() self.ir_parameter.as_ref().unwrap()
} }
#[deprecated]
pub fn set_stack_frame_offset(&mut self, offset: isize) {
self.stack_frame_offset = Some(offset);
}
#[deprecated]
pub fn stack_frame_offset(&self) -> isize {
self.stack_frame_offset.unwrap()
}
} }
pub struct VariableSymbol { pub struct VariableSymbol {
name: Rc<str>, name: Rc<str>,
type_info: Option<TypeInfo>, type_info: Option<TypeInfo>,
vr_variable: Option<Rc<RefCell<IrVariable>>>, vr_variable: Option<Rc<RefCell<IrVariable>>>,
#[deprecated]
register: Option<usize>,
} }
impl VariableSymbol { impl VariableSymbol {
@ -112,7 +95,6 @@ impl VariableSymbol {
name: name.into(), name: name.into(),
type_info: None, type_info: None,
vr_variable: None, vr_variable: None,
register: None,
} }
} }
@ -143,14 +125,6 @@ impl VariableSymbol {
.as_ref() .as_ref()
.expect("ir_variable not yet initialized") .expect("ir_variable not yet initialized")
} }
pub fn set_register(&mut self, register: usize) {
self.register = Some(register);
}
pub fn register(&self) -> usize {
self.register.unwrap()
}
} }
pub enum ExpressibleSymbol { pub enum ExpressibleSymbol {

View File

@ -72,7 +72,7 @@ impl Display for Location {
write!(f, "r{}", register) write!(f, "r{}", register)
} }
Location::StackFrameOffset(offset) => { Location::StackFrameOffset(offset) => {
if offset.is_positive() { if offset.is_positive() || *offset == 0 {
write!(f, "fp+{}", offset) write!(f, "fp+{}", offset)
} else { } else {
write!(f, "fp{}", offset) write!(f, "fp{}", offset)

View File

@ -5,14 +5,21 @@ use std::rc::Rc;
pub struct Function { pub struct Function {
name: Rc<str>, name: Rc<str>,
parameter_count: usize, parameter_count: usize,
stack_size: isize,
instructions: Vec<Instruction>, instructions: Vec<Instruction>,
} }
impl Function { impl Function {
pub fn new(name: Rc<str>, parameter_count: usize, instructions: Vec<Instruction>) -> Self { pub fn new(
name: Rc<str>,
parameter_count: usize,
stack_size: isize,
instructions: Vec<Instruction>,
) -> Self {
Self { Self {
name, name,
parameter_count, parameter_count,
stack_size,
instructions, instructions,
} }
} }
@ -29,6 +36,10 @@ impl Function {
self.parameter_count self.parameter_count
} }
pub fn stack_size(&self) -> isize {
self.stack_size
}
pub fn instructions(&self) -> &Vec<Instruction> { pub fn instructions(&self) -> &Vec<Instruction> {
&self.instructions &self.instructions
} }

View File

@ -234,10 +234,16 @@ pub fn call<'a>(
call_stack.push(CallFrame::new(function)); call_stack.push(CallFrame::new(function));
// put each arg on the stack // put each arg on the stack
for argument in arguments { for argument in &arguments {
call_stack.top_mut().stack_mut().push(argument); call_stack.top_mut().stack_mut().push(argument.clone());
} }
// ensure enough stack space
call_stack.top_mut().stack_mut().resize(
arguments.len() + (function.stack_size() as usize),
Value::Null,
);
while call_stack.maybe_top().is_some() while call_stack.maybe_top().is_some()
&& call_stack.top().ip() < call_stack.top().instructions().len() && call_stack.top().ip() < call_stack.top().instructions().len()
{ {
@ -314,6 +320,12 @@ pub fn call<'a>(
// push args // push args
call_stack.top_mut().stack_mut().append(&mut args); call_stack.top_mut().stack_mut().append(&mut args);
// ensure enough stack space
call_stack
.top_mut()
.stack_mut()
.resize(arg_count + (function.stack_size() as usize), Value::Null);
// set fp for callee frame // set fp for callee frame
let callee_frame = call_stack.top_mut(); let callee_frame = call_stack.top_mut();
callee_frame.set_fp(*arg_count); callee_frame.set_fp(*arg_count);