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 {
ir_function.assign_registers(args.register_count);
}
let mut functions: Vec<Function> = vec![];
let mut constants_table = ConstantsTable::new();
let functions: Vec<Function> = ir_functions
.iter()
.map(|ir_function| ir_function.assemble(&mut constants_table))
.collect();
for ir_function in &mut ir_functions {
let (_, stack_size) = ir_function.assign_registers(args.register_count);
let function = ir_function.assemble(stack_size, &mut constants_table);
functions.push(function);
}
if args.show_asm {
for function in &functions {

View File

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

View File

@ -1,7 +1,7 @@
use crate::constants_table::ConstantsTable;
use crate::ir::ir_expression::IrExpression;
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 std::collections::{HashMap, HashSet};
use std::fmt::{Display, Formatter};
@ -68,4 +68,8 @@ impl VrUser for IrAdd {
self.left.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::ir_operation::IrOperation;
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 std::cell::RefCell;
use std::collections::{HashMap, HashSet};
@ -68,6 +68,13 @@ impl VrUser for IrAssign {
}
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 {

View File

@ -2,7 +2,7 @@ use crate::constants_table::ConstantsTable;
use crate::ir::assemble::{Assemble, InstructionsBuilder};
use crate::ir::ir_statement::IrStatement;
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::collections::{HashMap, HashSet};
use std::fmt::{Display, Formatter};
@ -75,6 +75,12 @@ impl VrUser for IrBlock {
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 {
@ -145,7 +151,7 @@ mod tests {
if let ModuleLevelDeclaration::Function(main) = main {
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);
} else {
unreachable!()

View File

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

View File

@ -3,7 +3,7 @@ use crate::ir::ir_parameter::IrParameter;
use crate::ir::ir_variable::{
IrStackVariableDescriptor, IrVariable, IrVariableDescriptor, IrVrVariableDescriptor,
};
use crate::ir::register_allocation::VrUser;
use crate::ir::register_allocation::{OffsetCounter, VrUser};
use crate::type_info::TypeInfo;
use dvm_lib::instruction::{AddOperand, Location, MoveOperand, PushOperand, ReturnOperand};
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(
&mut self,
register_count: usize,
) -> HashMap<IrVrVariableDescriptor, usize> {
) -> (HashMap<IrVrVariableDescriptor, usize>, isize) {
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();
self.entry.borrow().assemble(&mut builder, constants_table);
let instructions = builder.take_instructions();
Function::new(
self.function_symbol.borrow().name_owned(),
self.parameters.len(),
stack_size,
instructions,
)
}

View File

@ -2,7 +2,7 @@ use crate::ir::ir_add::IrAdd;
use crate::ir::ir_call::IrCall;
use crate::ir::ir_expression::IrExpression;
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::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::ir_expression::IrExpression;
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 std::collections::{HashMap, HashSet};
use std::fmt::{Display, Formatter};
@ -44,10 +44,14 @@ impl VrUser for IrReturn {
ir_expression.propagate_register_assignments(assignments);
}
}
fn propagate_stack_offsets(&mut self, _counter: &mut OffsetCounter) {
// no-op
}
}
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() {
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_return::IrReturn;
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::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 {

View File

@ -139,6 +139,10 @@ impl IrStackVariableDescriptor {
&self.name
}
pub fn set_offset(&mut self, offset: isize) {
self.offset = Some(offset);
}
pub fn offset(&self) -> isize {
self.offset.unwrap()
}
@ -146,6 +150,6 @@ impl IrStackVariableDescriptor {
impl Display for IrStackVariableDescriptor {
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(
&mut self,
register_count: usize,
) -> HashMap<IrVrVariableDescriptor, usize> {
) -> (HashMap<IrVrVariableDescriptor, usize>, isize) {
let mut spills: HashSet<IrVrVariableDescriptor> = HashSet::new();
loop {
let mut interference_graph = self.interference_graph();
@ -122,7 +122,13 @@ pub trait HasVrUsers {
for vr_user in self.vr_users_mut() {
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,
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)]

View File

@ -51,9 +51,6 @@ pub struct ParameterSymbol {
name: Rc<str>,
type_info: TypeInfo,
ir_parameter: Option<Rc<IrParameter>>,
#[deprecated]
stack_frame_offset: Option<isize>,
}
impl ParameterSymbol {
@ -62,7 +59,6 @@ impl ParameterSymbol {
name: name.into(),
type_info,
ir_parameter: None,
stack_frame_offset: None,
}
}
@ -85,25 +81,12 @@ impl ParameterSymbol {
pub fn ir_parameter(&self) -> &Rc<IrParameter> {
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 {
name: Rc<str>,
type_info: Option<TypeInfo>,
vr_variable: Option<Rc<RefCell<IrVariable>>>,
#[deprecated]
register: Option<usize>,
}
impl VariableSymbol {
@ -112,7 +95,6 @@ impl VariableSymbol {
name: name.into(),
type_info: None,
vr_variable: None,
register: None,
}
}
@ -143,14 +125,6 @@ impl VariableSymbol {
.as_ref()
.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 {

View File

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

View File

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

View File

@ -234,10 +234,16 @@ pub fn call<'a>(
call_stack.push(CallFrame::new(function));
// put each arg on the stack
for argument in arguments {
call_stack.top_mut().stack_mut().push(argument);
for argument in &arguments {
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()
&& call_stack.top().ip() < call_stack.top().instructions().len()
{
@ -314,6 +320,12 @@ pub fn call<'a>(
// push 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
let callee_frame = call_stack.top_mut();
callee_frame.set_fp(*arg_count);