Fix test for overlapping register assignments when k=2.

This commit is contained in:
Jesse Brault 2026-07-03 15:29:07 -05:00
parent a568144ccc
commit f15ff43df8
8 changed files with 77 additions and 73 deletions

View File

@ -74,48 +74,3 @@ impl VrUser for IrBlock {
// Ok(())
// }
// }
#[cfg(test)]
mod tests {
use crate::diagnostic::Diagnostic;
use crate::offset_counter::OffsetCounter;
use crate::parser::get_compilation_unit;
use crate::symbol_table::SymbolTable;
use crate::types_table::TypesTable;
#[test]
fn overlapping_assignments_bug_when_k_2() -> Result<(), Vec<Diagnostic>> {
let mut compilation_unit = get_compilation_unit(
"
fn main()
let a = 1
let b = 2
let c = 3
let x = a + b + c
end
",
None,
)?;
let mut symbol_table = SymbolTable::new();
let mut types_table = TypesTable::new();
compilation_unit.init_scopes(&mut symbol_table);
compilation_unit.gather_symbols_into(&mut symbol_table)?;
compilation_unit.check_names(&mut symbol_table)?;
compilation_unit.gather_types_into(&symbol_table, &mut types_table)?;
compilation_unit.type_check(&symbol_table, &mut types_table)?;
let main = compilation_unit
.functions()
.iter()
.find(|f| f.declared_name() == "main")
.unwrap();
let main_ir = main.to_ir(&symbol_table, &types_table, None);
let variable_locations = main_ir.assign_registers(2);
assert_eq!(variable_locations.register_variables_count(), 4);
Ok(())
}
}

View File

@ -4,7 +4,6 @@ use crate::ir::ir_block::IrBlock;
use crate::ir::ir_parameter::IrParameter;
use crate::ir::ir_type_info::{IrTypeInfo, IrTypeInfoId};
use crate::ir::ir_variable::{IrVariable, IrVariableId};
use crate::ir::register_allocation::block_assign_registers;
use crate::ir::variable_locations::VariableLocations;
use dvm_lib::vm::function::Function;
use std::collections::HashMap;
@ -64,7 +63,8 @@ impl IrFunction {
unimplemented!("having more than one block in a function is not yet implemented.")
}
let block = &self.blocks[0];
block_assign_registers(block, register_count)
//block_assign_registers(block, register_count)
todo!()
}
#[deprecated]
@ -79,7 +79,7 @@ impl IrFunction {
Function::new(
self.fqn.clone(),
self.parameters.len(),
variable_locations.stack_size(),
variable_locations.stack_variables_count(),
instructions,
)
}

View File

@ -1,12 +1,8 @@
use crate::constants_table::ConstantsTable;
use crate::ir::assemble::assemble_ir_function;
use crate::ir::ir_function::IrFunction;
use crate::ir::ir_type_info::{IrTypeInfo, IrTypeInfoId};
use crate::ir::ir_variable::IrVariableId;
use crate::ir::register_allocation::block_assign_registers;
use crate::ir::variable_locations::VariableLocations;
use crate::ir::register_allocation::assign_registers;
use dvm_lib::vm::function::Function;
use std::collections::HashMap;
mod assemble;
mod debug_print;
@ -43,18 +39,7 @@ pub fn compile_dvm_function(
Function::new(
ir_function.fqn().into(),
ir_function.parameters().len(),
variable_locations.stack_size(),
variable_locations.stack_variables_count(),
instructions,
)
}
fn assign_registers(ir_function: &IrFunction, register_count: usize) -> VariableLocations {
if ir_function.blocks().is_empty() {
return VariableLocations::new(HashMap::new(), HashMap::new());
}
if ir_function.blocks().len() > 1 {
unimplemented!("having more than one block in a function is not yet implemented.")
}
let block = &ir_function.blocks()[0];
block_assign_registers(block, register_count)
}

View File

@ -1,6 +1,7 @@
/// Used this video as source for algorithms in this module:
/// https://www.youtube.com/watch?v=eWp_-XCwN1A
use crate::ir::ir_block::IrBlock;
use crate::ir::ir_function::IrFunction;
use crate::ir::ir_variable::IrVariableId;
use crate::ir::variable_locations::VariableLocations;
use dvm_lib::instruction::{Register, StackFrameOffset};
@ -10,7 +11,18 @@ pub type RegisterAssignment = Register;
pub type InterferenceGraph = HashMap<IrVariableId, HashSet<IrVariableId>>;
pub type LivenessSets = Vec<HashSet<IrVariableId>>;
pub fn block_live_in_live_out(ir_block: &IrBlock) -> (LivenessSets, LivenessSets) {
pub fn assign_registers(ir_function: &IrFunction, register_count: usize) -> VariableLocations {
if ir_function.blocks().is_empty() {
return VariableLocations::new(HashMap::new(), HashMap::new());
}
if ir_function.blocks().len() > 1 {
unimplemented!("having more than one block in a function is not yet implemented.")
}
let block = &ir_function.blocks()[0];
block_assign_registers(block, register_count)
}
fn block_live_in_live_out(ir_block: &IrBlock) -> (LivenessSets, LivenessSets) {
// init
let n_statements = ir_block.statements().len();
let mut live_in: LivenessSets = vec![HashSet::new(); n_statements];
@ -56,7 +68,7 @@ pub fn block_live_in_live_out(ir_block: &IrBlock) -> (LivenessSets, LivenessSets
(live_in, live_out)
}
pub fn block_interference_graph(
fn block_interference_graph(
ir_block: &IrBlock,
spilled: &HashSet<IrVariableId>,
) -> InterferenceGraph {
@ -74,8 +86,8 @@ pub fn block_interference_graph(
// create graph and init all variables' outgoing-edge sets
let mut graph: InterferenceGraph = HashMap::new();
for variable in all_vr_variables {
graph.insert(variable, HashSet::new());
for variable in &all_vr_variables {
graph.insert(*variable, HashSet::new());
}
let (_, live_out) = block_live_in_live_out(ir_block);
@ -89,6 +101,10 @@ pub fn block_interference_graph(
}
for live_out_variable in statement_live_out {
// check that we aren't working with an out variable we have already spilled.
if spilled.contains(live_out_variable) {
continue;
}
// we do the following check to avoid adding an edge to itself
if definition_vr_variable != *live_out_variable {
// add edges to both sets (two-way)
@ -104,7 +120,7 @@ pub fn block_interference_graph(
graph
}
pub fn block_assign_registers(ir_block: &IrBlock, register_count: usize) -> VariableLocations {
fn block_assign_registers(ir_block: &IrBlock, register_count: usize) -> VariableLocations {
let mut spilled: HashSet<IrVariableId> = HashSet::new();
loop {
let mut interference_graph = block_interference_graph(ir_block, &spilled);
@ -303,6 +319,10 @@ fn can_optimistically_color(
#[cfg(test)]
mod tests {
use super::*;
use crate::diagnostic::Diagnostics;
use crate::lowering::lower_to_ir;
use crate::parser::get_compilation_unit;
use crate::semantic_analysis::analyze_compilation_unit;
fn line_graph() -> InterferenceGraph {
let mut graph: InterferenceGraph = HashMap::new();
@ -417,4 +437,39 @@ mod tests {
assert_eq!(registers.len(), 2);
assert_eq!(spills.len(), 1);
}
#[test]
fn overlapping_assignments_bug_when_k_2() -> Result<(), Diagnostics> {
let compilation_unit = get_compilation_unit(
"
fn main()
let a = 1
let b = 2
let c = 3
let x = a + b + c
end
",
None,
)?;
let (analysis_result, diagnostics) = analyze_compilation_unit(&compilation_unit);
assert!(diagnostics.is_empty());
let lower_result = lower_to_ir(
&compilation_unit,
&analysis_result.symbols,
&analysis_result.nodes_to_symbols,
&analysis_result.type_infos,
&analysis_result.symbols_to_type_infos,
&analysis_result.nodes_to_type_infos,
); // todo: make this API friendlier
assert_eq!(lower_result.functions.len(), 1);
let main_fn = &lower_result.functions[0];
let variable_locations = assign_registers(main_fn, 2);
assert_eq!(variable_locations.register_variables_count(), 4);
Ok(())
}
}

View File

@ -36,7 +36,7 @@ impl VariableLocations {
self.register_variables.len()
}
pub fn stack_size(&self) -> usize {
pub fn stack_variables_count(&self) -> usize {
self.stack_variables.len()
}
}

View File

@ -144,6 +144,9 @@ fn collect_scopes_statement(statement: &Statement, ctx: &mut ScopeCollectionCont
fn collect_scopes_let_statement(let_statement: &LetStatement, ctx: &mut ScopeCollectionContext) {
collect_scopes_expression(let_statement.initializer(), ctx);
let current_scope_id = ctx.current_scope_id().unwrap();
ctx.nodes_to_scopes_mut()
.insert(let_statement.node_id(), current_scope_id);
}
fn collect_scopes_expression_statement(

View File

@ -1,4 +1,3 @@
use crate::ast::NodeId;
use crate::ast::assign_statement::AssignStatement;
use crate::ast::binary_expression::BinaryExpression;
use crate::ast::call::Call;
@ -11,10 +10,11 @@ use crate::ast::let_statement::LetStatement;
use crate::ast::negative_expression::NegativeExpression;
use crate::ast::parameter::Parameter;
use crate::ast::statement::Statement;
use crate::ast::NodeId;
use crate::diagnostic::Diagnostics;
use crate::diagnostic_factories::symbol_not_found;
use crate::semantic_analysis::scope::{Scope, ScopeId};
use crate::semantic_analysis::symbol::{ParameterSymbol, Symbol, SymbolId, VariableSymbol};
use crate::semantic_analysis::symbol::{Symbol, SymbolId, VariableSymbol};
use std::collections::HashMap;
pub struct NameResolutionResult {
@ -175,6 +175,9 @@ fn resolve_names_let_statement(
scope
.symbols_mut()
.insert(let_statement.declared_name_owned(), symbol_id);
// associate the let statement with the symbol id
ctx.nodes_to_symbols
.insert(let_statement.node_id(), symbol_id);
}
}
}

View File

@ -108,6 +108,9 @@ fn resolve_types_let_statement(let_statement: &LetStatement, ctx: &mut ResolveTy
let symbol_id = ctx.nodes_to_symbols[&let_statement.node_id()];
ctx.symbols_to_type_infos
.insert(symbol_id, initializer_type_info_id);
// also save it as the let statement's type info
ctx.nodes_to_type_infos
.insert(let_statement.node_id(), initializer_type_info_id);
}
fn resolve_types_assign_statement(