From 737d7b423297896a57b6e1f03c672f9d14d44b44 Mon Sep 17 00:00:00 2001 From: Jesse Brault Date: Fri, 19 Jun 2026 17:07:11 -0500 Subject: [PATCH] Much work from semantic analysis to lowering to ir/register alloc to assembly. WIP. --- dmc-lib/src/ast/call.rs | 4 +- dmc-lib/src/ast/let_statement.rs | 4 + dmc-lib/src/ast/parameter.rs | 4 + dmc-lib/src/ir/assemble.rs | 374 ++++++++++++++- dmc-lib/src/ir/debug_print.rs | 194 ++++++++ dmc-lib/src/ir/ir_assign.rs | 120 +---- dmc-lib/src/ir/ir_binary_operation.rs | 76 +--- dmc-lib/src/ir/ir_block.rs | 100 ++-- dmc-lib/src/ir/ir_call.rs | 44 +- dmc-lib/src/ir/ir_expression.rs | 263 +++++++---- dmc-lib/src/ir/ir_function.rs | 100 ++-- dmc-lib/src/ir/ir_get_field_ref.rs | 23 +- dmc-lib/src/ir/ir_get_field_ref_mut.rs | 24 +- dmc-lib/src/ir/ir_operation.rs | 4 +- dmc-lib/src/ir/ir_parameter.rs | 8 +- dmc-lib/src/ir/ir_parameter_or_variable.rs | 5 +- dmc-lib/src/ir/ir_read_field.rs | 32 +- dmc-lib/src/ir/ir_return.rs | 21 +- dmc-lib/src/ir/ir_set_field.rs | 40 +- dmc-lib/src/ir/ir_statement.rs | 104 +---- dmc-lib/src/ir/ir_type_info.rs | 16 + dmc-lib/src/ir/ir_variable.rs | 202 +-------- dmc-lib/src/ir/mod.rs | 3 + dmc-lib/src/ir/register_allocation.rs | 296 ++++++------ dmc-lib/src/ir/util.rs | 25 +- dmc-lib/src/ir/variable_locations.rs | 41 ++ dmc-lib/src/lib.rs | 1 + dmc-lib/src/lowering/mod.rs | 426 ++++++++++++++++++ dmc-lib/src/lowering/util.rs | 23 + dmc-lib/src/parser.rs | 2 +- .../src/semantic_analysis/collect_scopes.rs | 21 +- .../src/semantic_analysis/collect_symbols.rs | 59 ++- .../src/semantic_analysis/collect_types.rs | 116 +++++ dmc-lib/src/semantic_analysis/mod.rs | 107 ++++- .../src/semantic_analysis/resolve_names.rs | 40 +- .../semantic_analysis/resolve_types/mod.rs | 336 ++++++++++++++ .../resolve_types/type_analysis.rs | 127 ++++++ dmc-lib/src/semantic_analysis/symbol.rs | 44 +- dmc-lib/src/semantic_analysis/type_info.rs | 55 +++ 39 files changed, 2496 insertions(+), 988 deletions(-) create mode 100644 dmc-lib/src/ir/debug_print.rs create mode 100644 dmc-lib/src/ir/ir_type_info.rs create mode 100644 dmc-lib/src/ir/variable_locations.rs create mode 100644 dmc-lib/src/lowering/mod.rs create mode 100644 dmc-lib/src/lowering/util.rs create mode 100644 dmc-lib/src/semantic_analysis/collect_types.rs create mode 100644 dmc-lib/src/semantic_analysis/resolve_types/mod.rs create mode 100644 dmc-lib/src/semantic_analysis/resolve_types/type_analysis.rs create mode 100644 dmc-lib/src/semantic_analysis/type_info.rs diff --git a/dmc-lib/src/ast/call.rs b/dmc-lib/src/ast/call.rs index beeb81e..557ce1a 100644 --- a/dmc-lib/src/ast/call.rs +++ b/dmc-lib/src/ast/call.rs @@ -48,8 +48,8 @@ impl Call { &self.callee } - pub fn arguments(&self) -> Vec<&Expression> { - self.arguments.iter().collect() + pub fn arguments(&self) -> &[Expression] { + &self.arguments } pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) { diff --git a/dmc-lib/src/ast/let_statement.rs b/dmc-lib/src/ast/let_statement.rs index 221beb5..4d4812a 100644 --- a/dmc-lib/src/ast/let_statement.rs +++ b/dmc-lib/src/ast/let_statement.rs @@ -60,6 +60,10 @@ impl LetStatement { self.declared_name_source_range.clone() } + pub fn is_mut(&self) -> bool { + self.is_mut + } + pub fn initializer(&self) -> &Expression { &self.initializer } diff --git a/dmc-lib/src/ast/parameter.rs b/dmc-lib/src/ast/parameter.rs index 4a8e135..10fadf9 100644 --- a/dmc-lib/src/ast/parameter.rs +++ b/dmc-lib/src/ast/parameter.rs @@ -48,6 +48,10 @@ impl Parameter { self.declared_name_source_range.clone() } + pub fn type_use(&self) -> &TypeUse { + &self.type_use + } + pub fn scope_id(&self) -> usize { self.scope_id.unwrap() } diff --git a/dmc-lib/src/ir/assemble.rs b/dmc-lib/src/ir/assemble.rs index 13894b1..542ea4a 100644 --- a/dmc-lib/src/ir/assemble.rs +++ b/dmc-lib/src/ir/assemble.rs @@ -1,10 +1,28 @@ use crate::constants_table::ConstantsTable; -use dvm_lib::instruction::Instruction; +use crate::ir::ir_assign::IrAssign; +use crate::ir::ir_binary_operation::IrBinaryOperator; +use crate::ir::ir_block::IrBlock; +use crate::ir::ir_call::IrCall; +use crate::ir::ir_expression::IrExpression; +use crate::ir::ir_function::IrFunction; +use crate::ir::ir_operation::IrOperation; +use crate::ir::ir_return::IrReturn; +use crate::ir::ir_statement::IrStatement; +use crate::ir::ir_type_info::{IrTypeInfo, IrTypeInfoId}; +use crate::ir::ir_variable::IrVariableId; +use crate::ir::variable_locations::{VariableLocation, VariableLocations}; +use dvm_lib::instruction::{ + AddOperand, Instruction, Location, LocationOrInteger, LocationOrNumber, MoveOperand, + MultiplyOperand, PushOperand, ReturnOperand, SubtractOperand, +}; +use std::collections::HashMap; +#[deprecated] pub trait Assemble { fn assemble(&self, builder: &mut InstructionsBuilder, constants_table: &mut ConstantsTable); } +#[deprecated] pub struct InstructionsBuilder { instructions: Vec, } @@ -24,3 +42,357 @@ impl InstructionsBuilder { std::mem::take(&mut self.instructions) } } + +struct FunctionAssemblyContext<'a> { + type_infos: &'a Vec, + variables_to_type_infos: &'a HashMap, + variable_locations: &'a VariableLocations, + constants_table: &'a mut ConstantsTable, + instructions: Vec, +} + +pub fn assemble_ir_function( + ir_function: &IrFunction, + type_infos: &Vec, + variables_to_type_infos: &HashMap, + variable_locations: &VariableLocations, + constants_table: &mut ConstantsTable, +) -> Vec { + let mut ctx = FunctionAssemblyContext { + type_infos, + variables_to_type_infos, + variable_locations, + constants_table, + instructions: Vec::new(), + }; + for block in ir_function.blocks() { + assemble_ir_block(block, &mut ctx); + } + ctx.instructions +} + +fn assemble_ir_block(block: &IrBlock, ctx: &mut FunctionAssemblyContext) { + for statement in block.statements() { + assemble_ir_statement(statement, ctx); + } +} + +fn assemble_ir_statement(statement: &IrStatement, ctx: &mut FunctionAssemblyContext) { + match statement { + IrStatement::Assign(ir_assign) => { + assemble_ir_assign(ir_assign, ctx); + } + IrStatement::Call(ir_call) => { + assemble_ir_call(ir_call, ctx); + } + IrStatement::Return(ir_return) => { + assemble_ir_return(ir_return, ctx); + } + IrStatement::SetField(ir_set_field) => { + todo!() + } + } +} + +fn assemble_ir_assign(ir_assign: &IrAssign, ctx: &mut FunctionAssemblyContext) { + let destination_location = to_location( + ctx.variable_locations + .get_variable_location(&ir_assign.destination()), + ); + match ir_assign.initializer() { + IrOperation::GetFieldRef(_) => {} + IrOperation::GetFieldRefMut(_) => {} + IrOperation::ReadField(_) => {} + IrOperation::Load(ir_expression) => { + let move_operand = to_move_operand(ir_expression, ctx); + let instruction = Instruction::Move(move_operand, destination_location); + ctx.instructions.push(instruction); + } + IrOperation::Binary(ir_binary_operation) => { + let instruction = match ir_binary_operation.op() { + IrBinaryOperator::Multiply => Instruction::Multiply( + to_multiply_operand(ir_binary_operation.left(), ctx), + to_multiply_operand(ir_binary_operation.right(), ctx), + destination_location, + ), + IrBinaryOperator::Divide => Instruction::Divide( + to_location_or_number(ir_binary_operation.left(), ctx), + to_location_or_number(ir_binary_operation.right(), ctx), + destination_location, + ), + IrBinaryOperator::Modulo => Instruction::Modulo( + to_location_or_number(ir_binary_operation.left(), ctx), + to_location_or_number(ir_binary_operation.right(), ctx), + destination_location, + ), + IrBinaryOperator::Add => Instruction::Add( + to_add_operand(ir_binary_operation.left(), ctx), + to_add_operand(ir_binary_operation.right(), ctx), + destination_location, + ), + IrBinaryOperator::Subtract => Instruction::Subtract( + to_subtract_operand(ir_binary_operation.left(), ctx), + to_subtract_operand(ir_binary_operation.right(), ctx), + destination_location, + ), + IrBinaryOperator::LeftShift => Instruction::LeftShift( + to_location_or_integer(ir_binary_operation.left(), ctx), + to_location_or_integer(ir_binary_operation.right(), ctx), + destination_location, + ), + IrBinaryOperator::RightShift => Instruction::RightShift( + to_location_or_integer(ir_binary_operation.left(), ctx), + to_location_or_integer(ir_binary_operation.right(), ctx), + destination_location, + ), + IrBinaryOperator::BitwiseAnd => Instruction::BitwiseAnd( + to_location_or_integer(ir_binary_operation.left(), ctx), + to_location_or_integer(ir_binary_operation.right(), ctx), + destination_location, + ), + IrBinaryOperator::BitwiseXor => Instruction::BitwiseXor( + to_location_or_integer(ir_binary_operation.left(), ctx), + to_location_or_integer(ir_binary_operation.right(), ctx), + destination_location, + ), + IrBinaryOperator::BitwiseOr => Instruction::BitwiseOr( + to_location_or_integer(ir_binary_operation.left(), ctx), + to_location_or_integer(ir_binary_operation.right(), ctx), + destination_location, + ), + }; + + ctx.instructions.push(instruction); + } + IrOperation::Call(_) => { + todo!() + } + IrOperation::Allocate(_) => { + todo!() + } + } +} + +fn assemble_ir_call(ir_call: &IrCall, ctx: &mut FunctionAssemblyContext) { + // push all args + let args_as_push_operands = ir_call + .arguments() + .iter() + .map(|ir_expression| to_push_operand(ir_expression, ctx)) + .collect::>(); + for push_operand in args_as_push_operands { + ctx.instructions.push(Instruction::Push(push_operand)); + } + + // push invoke instruction + if ir_call.is_extern() { + ctx.instructions.push(Instruction::InvokePlatformStatic( + ir_call.function_name_owned(), + ir_call.arguments().len(), + )); + } else { + ctx.instructions.push(Instruction::InvokeStatic( + ir_call.function_name_owned(), + ir_call.arguments().len(), + )); + } +} + +fn assemble_ir_return(ir_return: &IrReturn, ctx: &mut FunctionAssemblyContext) { + if let Some(ir_expression) = ir_return.value() { + let return_operand = to_return_operand(ir_expression, ctx); + ctx.instructions + .push(Instruction::SetReturnValue(return_operand)); + } + ctx.instructions.push(Instruction::Return); +} + +fn to_move_operand(ir_expression: &IrExpression, ctx: &mut FunctionAssemblyContext) -> MoveOperand { + match ir_expression { + IrExpression::Parameter(ir_parameter) => { + MoveOperand::Location(Location::StackFrameOffset(ir_parameter.stack_offset())) + } + IrExpression::Variable(ir_variable_id) => MoveOperand::Location(to_location( + ctx.variable_locations.get_variable_location(ir_variable_id), + )), + IrExpression::Int(i) => MoveOperand::Int(*i), + IrExpression::Double(d) => MoveOperand::Double(*d), + IrExpression::String(s) => { + let constant_name = ctx.constants_table.get_or_insert(s); + MoveOperand::String(constant_name) + } + } +} + +fn to_multiply_operand( + ir_expression: &IrExpression, + ctx: &mut FunctionAssemblyContext, +) -> MultiplyOperand { + match ir_expression { + IrExpression::Parameter(ir_parameter) => { + MultiplyOperand::Location(Location::StackFrameOffset(ir_parameter.stack_offset())) + } + IrExpression::Variable(ir_variable_id) => { + let ir_type_info_id = ctx.variables_to_type_infos[ir_variable_id]; + let ir_type_info = &ctx.type_infos[ir_type_info_id]; + match ir_type_info { + IrTypeInfo::Int | IrTypeInfo::Double => { + let location = + to_location(ctx.variable_locations.get_variable_location(ir_variable_id)); + MultiplyOperand::Location(location) + } + _ => panic!("Attempt to multiply non-number (found {})", ir_type_info), + } + } + IrExpression::Int(i) => MultiplyOperand::Int(*i), + IrExpression::Double(d) => MultiplyOperand::Double(*d), + IrExpression::String(_) => { + panic!("Attempt to multiply with a string"); + } + } +} + +fn to_add_operand(ir_expression: &IrExpression, ctx: &mut FunctionAssemblyContext) -> AddOperand { + match ir_expression { + IrExpression::Parameter(ir_parameter) => match ir_parameter.type_info() { + IrTypeInfo::Int | IrTypeInfo::Double | IrTypeInfo::String => { + AddOperand::Location(Location::StackFrameOffset(ir_parameter.stack_offset())) + } + }, + IrExpression::Variable(ir_variable_id) => { + let ir_type_info_id = ctx.variables_to_type_infos[ir_variable_id]; + let ir_type_info = &ctx.type_infos[ir_type_info_id]; + match ir_type_info { + IrTypeInfo::Int | IrTypeInfo::Double | IrTypeInfo::String => AddOperand::Location( + to_location(ctx.variable_locations.get_variable_location(ir_variable_id)), + ), + } + } + IrExpression::Int(i) => AddOperand::Int(*i), + IrExpression::Double(d) => AddOperand::Double(*d), + IrExpression::String(s) => { + let constant_name = ctx.constants_table.get_or_insert(s); + AddOperand::String(constant_name) + } + } +} + +fn to_subtract_operand( + ir_expression: &IrExpression, + ctx: &mut FunctionAssemblyContext, +) -> SubtractOperand { + match ir_expression { + IrExpression::Parameter(ir_parameter) => match ir_parameter.type_info() { + IrTypeInfo::Int | IrTypeInfo::Double => { + SubtractOperand::Location(Location::StackFrameOffset(ir_parameter.stack_offset())) + } + _ => panic!( + "Attempt to subtract with non-integer type (found {})", + ir_parameter.type_info() + ), + }, + IrExpression::Variable(ir_variable) => { + let ir_type_info_id = ctx.variables_to_type_infos[ir_variable]; + let ir_type_info = &ctx.type_infos[ir_type_info_id]; + match ir_type_info { + IrTypeInfo::Int | IrTypeInfo::Double => SubtractOperand::Location(to_location( + ctx.variable_locations.get_variable_location(ir_variable), + )), + _ => panic!( + "Attempt to subtract with non-number type (found {})", + ir_type_info + ), + } + } + IrExpression::Int(i) => SubtractOperand::Int(*i), + IrExpression::Double(d) => SubtractOperand::Double(*d), + IrExpression::String(_) => { + panic!("Attempt to subtract with a string"); + } + } +} + +fn to_location_or_number( + ir_expression: &IrExpression, + ctx: &mut FunctionAssemblyContext, +) -> LocationOrNumber { + match ir_expression { + IrExpression::Parameter(ir_parameter) => { + LocationOrNumber::Location(ir_parameter.as_location()) + } + IrExpression::Variable(ir_variable_id) => LocationOrNumber::Location(to_location( + ctx.variable_locations.get_variable_location(ir_variable_id), + )), + IrExpression::Int(i) => LocationOrNumber::Int(*i), + IrExpression::Double(d) => LocationOrNumber::Double(*d), + _ => panic!( + "Attempt to convert {} to a location or number", + ir_expression + ), + } +} + +fn to_push_operand(ir_expression: &IrExpression, ctx: &mut FunctionAssemblyContext) -> PushOperand { + match ir_expression { + IrExpression::Parameter(ir_parameter) => { + PushOperand::Location(Location::StackFrameOffset(ir_parameter.stack_offset())) + } + IrExpression::Variable(ir_variable_id) => PushOperand::Location(to_location( + ctx.variable_locations.get_variable_location(ir_variable_id), + )), + IrExpression::Int(i) => PushOperand::Int(*i), + IrExpression::Double(d) => PushOperand::Double(*d), + IrExpression::String(s) => { + let constant_name = ctx.constants_table.get_or_insert(s); + PushOperand::String(constant_name) + } + } +} + +fn to_return_operand( + ir_expression: &IrExpression, + ctx: &mut FunctionAssemblyContext, +) -> ReturnOperand { + match ir_expression { + IrExpression::Parameter(ir_parameter) => { + ReturnOperand::Location(Location::StackFrameOffset(ir_parameter.stack_offset())) + } + IrExpression::Variable(ir_variable) => ReturnOperand::Location(to_location( + ctx.variable_locations.get_variable_location(ir_variable), + )), + IrExpression::Int(i) => ReturnOperand::Int(*i), + IrExpression::Double(d) => ReturnOperand::Double(*d), + IrExpression::String(s) => { + let constant_name = ctx.constants_table.get_or_insert(s); + ReturnOperand::String(constant_name) + } + } +} + +fn to_location_or_integer( + ir_expression: &IrExpression, + ctx: &mut FunctionAssemblyContext, +) -> LocationOrInteger { + match ir_expression { + IrExpression::Parameter(ir_parameter) => { + LocationOrInteger::Location(ir_parameter.as_location()) + } + IrExpression::Variable(ir_variable_id) => LocationOrInteger::Location(to_location( + ctx.variable_locations.get_variable_location(ir_variable_id), + )), + IrExpression::Int(i) => LocationOrInteger::Int(*i), + _ => panic!( + "Attempt to convert {} to a location or integer", + ir_expression + ), + } +} + +fn to_location(variable_location: VariableLocation) -> Location { + match variable_location { + VariableLocation::Register(register_assignment) => Location::Register(register_assignment), + VariableLocation::Stack(stack_frame_offset) => { + Location::StackFrameOffset(stack_frame_offset) + } + } +} diff --git a/dmc-lib/src/ir/debug_print.rs b/dmc-lib/src/ir/debug_print.rs new file mode 100644 index 0000000..8cce0b9 --- /dev/null +++ b/dmc-lib/src/ir/debug_print.rs @@ -0,0 +1,194 @@ +use crate::ir::ir_allocate::IrAllocate; +use crate::ir::ir_assign::IrAssign; +use crate::ir::ir_binary_operation::{IrBinaryOperation, IrBinaryOperator}; +use crate::ir::ir_block::IrBlock; +use crate::ir::ir_call::IrCall; +use crate::ir::ir_expression::IrExpression; +use crate::ir::ir_function::IrFunction; +use crate::ir::ir_operation::IrOperation; +use crate::ir::ir_return::IrReturn; +use crate::ir::ir_statement::IrStatement; +use crate::ir::ir_variable::IrVariable; +use std::fmt::Formatter; + +struct DebugPrintContext<'a> { + ir_variables: &'a [IrVariable], +} + +pub fn debug_format(ir_function: &IrFunction, f: &mut Formatter) -> std::fmt::Result { + write!(f, "fn {}(", ir_function.fqn())?; + for (i, parameter) in ir_function.parameters().iter().enumerate() { + write!(f, "{}: {}", parameter, parameter.type_info())?; + if i < ir_function.parameters().len() - 1 { + write!(f, ", ")?; + } + } + write!(f, ")")?; + if let Some(return_type_info) = ir_function.return_type_info() { + write!(f, " -> {}\n", return_type_info)?; + } else { + write!(f, " -> Void\n")?; + } + + let ctx = DebugPrintContext { + ir_variables: ir_function.variables(), + }; + + for block in ir_function.blocks() { + debug_format_block(block, f, &ctx)?; + write!(f, "\n")?; + } + + Ok(()) +} + +fn debug_format_block( + ir_block: &IrBlock, + f: &mut Formatter, + ctx: &DebugPrintContext, +) -> std::fmt::Result { + writeln!(f, " {}:", ir_block.debug_label())?; + + for statement in ir_block.statements() { + write!(f, " ")?; + debug_format_statement(statement, f, ctx)?; + write!(f, "\n")?; + } + + Ok(()) +} + +fn debug_format_statement( + ir_statement: &IrStatement, + f: &mut Formatter, + ctx: &DebugPrintContext, +) -> std::fmt::Result { + match ir_statement { + IrStatement::Assign(ir_assign) => debug_format_assign(ir_assign, f, ctx), + IrStatement::Call(ir_call) => debug_format_call(ir_call, f, ctx), + IrStatement::Return(ir_return) => debug_format_return(ir_return, f, ctx), + IrStatement::SetField(ir_set_field) => { + todo!() + } + } +} + +fn debug_format_assign( + ir_assign: &IrAssign, + f: &mut Formatter, + ctx: &DebugPrintContext, +) -> std::fmt::Result { + let variable_name = ctx.ir_variables[ir_assign.destination()].name(); + write!(f, "{} = ", variable_name)?; + debug_format_operation(ir_assign.initializer(), f, ctx)?; + Ok(()) +} + +fn debug_format_operation( + ir_operation: &IrOperation, + f: &mut Formatter, + ctx: &DebugPrintContext, +) -> std::fmt::Result { + match ir_operation { + IrOperation::GetFieldRef(ir_get_field_ref) => { + todo!() + } + IrOperation::GetFieldRefMut(ir_get_field_ref_mut) => { + todo!() + } + IrOperation::ReadField(ir_read_field) => { + todo!() + } + IrOperation::Load(ir_expression) => debug_format_expression(ir_expression, f, ctx), + IrOperation::Binary(ir_binary_operation) => { + debug_format_binary_operation(ir_binary_operation, f, ctx) + } + IrOperation::Call(ir_call) => debug_format_call(ir_call, f, ctx), + IrOperation::Allocate(ir_allocate) => debug_format_allocate(ir_allocate, f), + } +} + +fn debug_format_expression( + ir_expression: &IrExpression, + f: &mut Formatter, + ctx: &DebugPrintContext, +) -> std::fmt::Result { + match ir_expression { + IrExpression::Parameter(ir_parameter) => { + todo!() + } + IrExpression::Variable(ir_variable_id) => { + let variable_name = ctx.ir_variables[*ir_variable_id].name(); + write!(f, "{}", variable_name) + } + IrExpression::Int(i) => { + write!(f, "{}", i) + } + IrExpression::Double(d) => { + write!(f, "{}", d) + } + IrExpression::String(s) => { + write!(f, "\"{}\"", s) + } + } +} + +fn debug_format_binary_operation( + ir_binary_operation: &IrBinaryOperation, + f: &mut Formatter, + ctx: &DebugPrintContext, +) -> std::fmt::Result { + let op_string = match ir_binary_operation.op() { + IrBinaryOperator::Multiply => "*", + IrBinaryOperator::Divide => "/", + IrBinaryOperator::Modulo => "%", + IrBinaryOperator::Add => "+", + IrBinaryOperator::Subtract => "-", + IrBinaryOperator::LeftShift => "<<", + IrBinaryOperator::RightShift => ">>", + IrBinaryOperator::BitwiseAnd => "&", + IrBinaryOperator::BitwiseXor => "^", + IrBinaryOperator::BitwiseOr => "|", + }; + + debug_format_expression(ir_binary_operation.left(), f, ctx)?; + write!(f, " {} ", op_string)?; + debug_format_expression(ir_binary_operation.right(), f, ctx)?; + + Ok(()) +} + +fn debug_format_call( + ir_call: &IrCall, + f: &mut Formatter, + ctx: &DebugPrintContext, +) -> std::fmt::Result { + write!(f, "{}(", ir_call.function_name())?; + for (i, argument) in ir_call.arguments().iter().enumerate() { + debug_format_expression(argument, f, ctx)?; + if i < ir_call.arguments().len() - 1 { + write!(f, ", ")?; + } + } + write!(f, ")")?; + + Ok(()) +} + +fn debug_format_allocate(ir_allocate: &IrAllocate, f: &mut Formatter) -> std::fmt::Result { + write!(f, "alloc {}", ir_allocate.class_fqn()) +} + +fn debug_format_return( + ir_return: &IrReturn, + f: &mut Formatter, + ctx: &DebugPrintContext, +) -> std::fmt::Result { + if let Some(ir_expression) = ir_return.value() { + write!(f, "ret ")?; + debug_format_expression(ir_expression, f, ctx)?; + } else { + write!(f, "ret")?; + } + Ok(()) +} diff --git a/dmc-lib/src/ir/ir_assign.rs b/dmc-lib/src/ir/ir_assign.rs index 65400b9..70ee32e 100644 --- a/dmc-lib/src/ir/ir_assign.rs +++ b/dmc-lib/src/ir/ir_assign.rs @@ -1,126 +1,36 @@ -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::ir_variable::IrVariableId; use crate::ir::register_allocation::VrUser; -use crate::ir::util::propagate_spills; -use crate::offset_counter::OffsetCounter; -use dvm_lib::instruction::Instruction; -use std::cell::RefCell; -use std::collections::{HashMap, HashSet}; -use std::fmt::{Display, Formatter}; -use std::rc::Rc; +use std::collections::HashSet; pub struct IrAssign { - destination: Rc>, + destination: IrVariableId, initializer: Box, } impl IrAssign { - pub fn new(destination: Rc>, initializer: IrOperation) -> Self { + pub fn new(destination: IrVariableId, initializer: IrOperation) -> Self { Self { destination, initializer: initializer.into(), } } + + pub fn destination(&self) -> IrVariableId { + self.destination + } + + pub fn initializer(&self) -> &IrOperation { + &self.initializer + } } impl VrUser for IrAssign { - fn vr_definitions(&self) -> HashSet { - self.destination - .borrow() - .descriptor() - .vr_variable_descriptors() + fn vr_definitions(&self) -> HashSet { + HashSet::from([self.destination]) } - fn vr_uses(&self) -> HashSet { + fn vr_uses(&self) -> HashSet { self.initializer.vr_uses() } - - fn propagate_spills(&mut self, spills: &HashSet) { - propagate_spills(&mut self.destination, spills); - } - - fn propagate_register_assignments( - &mut self, - assignments: &HashMap, - ) { - let mut borrowed_destination = self.destination.borrow_mut(); - if let IrVariableDescriptor::VirtualRegister(vr_variable) = - borrowed_destination.descriptor_mut() - { - if assignments.contains_key(vr_variable) { - vr_variable.set_assigned_register(assignments[vr_variable]); - } - } - } - - 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.use_next_offset(counter); - } - } -} - -impl Assemble for IrAssign { - fn assemble(&self, builder: &mut InstructionsBuilder, constants_table: &mut ConstantsTable) { - let destination = self.destination.borrow().descriptor().as_location(); - - match self.initializer.as_ref() { - IrOperation::GetFieldRef(ir_get_field_ref) => { - let self_location = ir_get_field_ref.self_parameter_or_variable().as_location(); - builder.push(Instruction::GetFieldPointer( - self_location, - ir_get_field_ref.field_index(), - destination, - )); - } - IrOperation::GetFieldRefMut(ir_get_field_ref_mut) => { - let self_location = ir_get_field_ref_mut - .self_parameter_or_variable() - .as_location(); - builder.push(Instruction::GetFieldPointerMut( - self_location, - ir_get_field_ref_mut.field_index(), - destination, - )); - } - IrOperation::ReadField(ir_read_field) => { - let field_ref_location = ir_read_field - .field_ref_variable() - .borrow() - .descriptor() - .as_location(); - builder.push(Instruction::ReadField(field_ref_location, destination)); - } - IrOperation::Load(ir_expression) => { - let move_operand = ir_expression.move_operand(constants_table); - builder.push(Instruction::Move(move_operand, destination)); - } - IrOperation::Binary(ir_binary_operation) => { - ir_binary_operation.assemble_assign(builder, constants_table, destination); - } - IrOperation::Call(ir_call) => { - ir_call.assemble(builder, constants_table); - builder.push(Instruction::Pop(Some(destination))); - } - IrOperation::Allocate(ir_allocate) => builder.push(Instruction::Allocate( - ir_allocate.class_fqn_owned(), - destination, - )), - } - } -} - -impl Display for IrAssign { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{}: {} = {}", - self.destination.borrow(), - self.destination.borrow().type_info(), - self.initializer - ) - } } diff --git a/dmc-lib/src/ir/ir_binary_operation.rs b/dmc-lib/src/ir/ir_binary_operation.rs index 3e5e0de..e1618e8 100644 --- a/dmc-lib/src/ir/ir_binary_operation.rs +++ b/dmc-lib/src/ir/ir_binary_operation.rs @@ -1,9 +1,6 @@ -use crate::constants_table::ConstantsTable; -use crate::ir::assemble::InstructionsBuilder; use crate::ir::ir_expression::IrExpression; -use crate::ir::ir_variable::IrVrVariableDescriptor; +use crate::ir::ir_variable::IrVariableId; use crate::ir::register_allocation::VrUser; -use dvm_lib::instruction::{Instruction, Location}; use std::collections::HashSet; use std::fmt::{Display, Formatter}; @@ -35,65 +32,16 @@ impl IrBinaryOperation { } } - pub fn assemble_assign( - &self, - builder: &mut InstructionsBuilder, - constants_table: &mut ConstantsTable, - destination: Location, - ) { - let instruction = match &self.op { - IrBinaryOperator::Multiply => Instruction::Multiply( - self.left.multiply_operand(), - self.right.multiply_operand(), - destination, - ), - IrBinaryOperator::Divide => Instruction::Divide( - self.left.as_location_or_number(), - self.right.as_location_or_number(), - destination, - ), - IrBinaryOperator::Modulo => Instruction::Modulo( - self.left.as_location_or_number(), - self.right.as_location_or_number(), - destination, - ), - IrBinaryOperator::Add => Instruction::Add( - self.left.add_operand(constants_table), - self.right.add_operand(constants_table), - destination, - ), - IrBinaryOperator::Subtract => Instruction::Subtract( - self.left.subtract_operand(), - self.right.subtract_operand(), - destination, - ), - IrBinaryOperator::LeftShift => Instruction::LeftShift( - self.left.as_location_or_integer(), - self.right.as_location_or_integer(), - destination, - ), - IrBinaryOperator::RightShift => Instruction::RightShift( - self.left.as_location_or_integer(), - self.right.as_location_or_integer(), - destination, - ), - IrBinaryOperator::BitwiseAnd => Instruction::BitwiseAnd( - self.left.as_location_or_integer(), - self.right.as_location_or_integer(), - destination, - ), - IrBinaryOperator::BitwiseXor => Instruction::BitwiseXor( - self.left.as_location_or_integer(), - self.right.as_location_or_integer(), - destination, - ), - IrBinaryOperator::BitwiseOr => Instruction::BitwiseOr( - self.left.as_location_or_integer(), - self.right.as_location_or_integer(), - destination, - ), - }; - builder.push(instruction); + pub fn left(&self) -> &IrExpression { + &self.left + } + + pub fn right(&self) -> &IrExpression { + &self.right + } + + pub fn op(&self) -> &IrBinaryOperator { + &self.op } } @@ -135,7 +83,7 @@ impl Display for IrBinaryOperation { } impl VrUser for IrBinaryOperation { - fn vr_uses(&self) -> HashSet { + fn vr_uses(&self) -> HashSet { [self.left.as_ref(), self.right.as_ref()] .iter() .flat_map(|e| e.vr_uses()) diff --git a/dmc-lib/src/ir/ir_block.rs b/dmc-lib/src/ir/ir_block.rs index d120390..cedda68 100644 --- a/dmc-lib/src/ir/ir_block.rs +++ b/dmc-lib/src/ir/ir_block.rs @@ -1,19 +1,14 @@ -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::ir_variable::IrVariableId; +use crate::ir::register_allocation::{HasVrUsers, RegisterAssignment, VrUser}; use crate::offset_counter::OffsetCounter; -use std::cell::RefCell; use std::collections::{HashMap, HashSet}; -use std::fmt::{Display, Formatter}; -use std::rc::Rc; + +pub type IrBlockId = usize; pub struct IrBlock { - id: usize, + id: IrBlockId, debug_label: String, - predecessors: Vec>>, - successors: Vec>>, statements: Vec, } @@ -22,16 +17,18 @@ impl IrBlock { Self { id, debug_label: debug_label.into(), - predecessors: vec![], - successors: vec![], statements, } } - pub fn id(&self) -> usize { + pub fn id(&self) -> IrBlockId { self.id } + pub fn debug_label(&self) -> &str { + &self.debug_label + } + pub fn statements(&self) -> &[IrStatement] { &self.statements } @@ -51,18 +48,18 @@ impl HasVrUsers for IrBlock { } impl VrUser for IrBlock { - fn vr_definitions(&self) -> HashSet { + fn vr_definitions(&self) -> HashSet { self.statements .iter() .flat_map(|s| s.vr_definitions()) .collect() } - fn vr_uses(&self) -> HashSet { + fn vr_uses(&self) -> HashSet { self.statements.iter().flat_map(|s| s.vr_uses()).collect() } - fn propagate_spills(&mut self, spills: &HashSet) { + fn propagate_spills(&mut self, spills: &HashSet) { for statement in &mut self.statements { statement.propagate_spills(spills); } @@ -70,7 +67,7 @@ impl VrUser for IrBlock { fn propagate_register_assignments( &mut self, - assignments: &HashMap, + assignments: &HashMap, ) { for statement in &mut self.statements { statement.propagate_register_assignments(assignments); @@ -84,41 +81,34 @@ impl VrUser for IrBlock { } } -impl Assemble for IrBlock { - fn assemble(&self, builder: &mut InstructionsBuilder, constants_table: &mut ConstantsTable) { - for statement in &self.statements { - statement.assemble(builder, constants_table); - } - } -} - -impl Display for IrBlock { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - writeln!(f, " {}:", self.debug_label)?; - - let (live_in, live_out) = self.live_in_live_out(); - - for (statement_index, statement) in self.statements.iter().enumerate() { - let statement_live_in = live_in.get(&statement_index).unwrap(); - let statement_live_out = live_out.get(&statement_index).unwrap(); - - writeln!( - f, - " {} // live_in: {:?}, live_out: {:?}", - statement, statement_live_in, statement_live_out - )?; - } - writeln!(f, " // ---- {} meta ----", self.debug_label)?; - writeln!(f, " // definitions: {:?}", self.vr_definitions())?; - writeln!(f, " // uses: {:?}", self.vr_uses())?; - writeln!( - f, - " // interference graph: {:?}", - self.interference_graph() - )?; - Ok(()) - } -} +// +// impl Display for IrBlock { +// fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { +// writeln!(f, " {}:", self.debug_label)?; +// +// let (live_in, live_out) = self.live_in_live_out(); +// +// for (statement_index, statement) in self.statements.iter().enumerate() { +// let statement_live_in = live_in.get(&statement_index).unwrap(); +// let statement_live_out = live_out.get(&statement_index).unwrap(); +// +// writeln!( +// f, +// " {} // live_in: {:?}, live_out: {:?}", +// statement, statement_live_in, statement_live_out +// )?; +// } +// writeln!(f, " // ---- {} meta ----", self.debug_label)?; +// writeln!(f, " // definitions: {:?}", self.vr_definitions())?; +// writeln!(f, " // uses: {:?}", self.vr_uses())?; +// writeln!( +// f, +// " // interference graph: {:?}", +// self.interference_graph() +// )?; +// Ok(()) +// } +// } #[cfg(test)] mod tests { @@ -157,9 +147,9 @@ mod tests { .find(|f| f.declared_name() == "main") .unwrap(); - let mut main_ir = main.to_ir(&symbol_table, &types_table, None); - let register_assignments = main_ir.assign_registers(2, &mut OffsetCounter::new()); - assert_eq!(register_assignments.len(), 4); + 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(()) } diff --git a/dmc-lib/src/ir/ir_call.rs b/dmc-lib/src/ir/ir_call.rs index 0fc4091..7d6402d 100644 --- a/dmc-lib/src/ir/ir_call.rs +++ b/dmc-lib/src/ir/ir_call.rs @@ -1,9 +1,6 @@ -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::ir_variable::IrVariableId; use crate::ir::register_allocation::VrUser; -use dvm_lib::instruction::Instruction; use std::collections::HashSet; use std::fmt::{Display, Formatter}; use std::rc::Rc; @@ -22,32 +19,27 @@ impl IrCall { is_extern, } } -} -impl VrUser for IrCall { - fn vr_uses(&self) -> HashSet { - self.arguments.iter().flat_map(|a| a.vr_uses()).collect() + pub fn function_name(&self) -> &str { + &self.function_name + } + + pub fn function_name_owned(&self) -> Rc { + self.function_name.clone() + } + + pub fn arguments(&self) -> &[IrExpression] { + &self.arguments + } + + pub fn is_extern(&self) -> bool { + self.is_extern } } -impl Assemble for IrCall { - fn assemble(&self, builder: &mut InstructionsBuilder, constants_table: &mut ConstantsTable) { - // push all args - self.arguments - .iter() - .map(|ir_expression| ir_expression.push_operand(constants_table)) - .for_each(|push_operand| builder.push(Instruction::Push(push_operand))); - if self.is_extern { - builder.push(Instruction::InvokePlatformStatic( - self.function_name.clone(), - self.arguments.len(), - )); - } else { - builder.push(Instruction::InvokeStatic( - self.function_name.clone(), - self.arguments.len(), - )); - } +impl VrUser for IrCall { + fn vr_uses(&self) -> HashSet { + self.arguments.iter().flat_map(|a| a.vr_uses()).collect() } } diff --git a/dmc-lib/src/ir/ir_expression.rs b/dmc-lib/src/ir/ir_expression.rs index 8019c35..49b8165 100644 --- a/dmc-lib/src/ir/ir_expression.rs +++ b/dmc-lib/src/ir/ir_expression.rs @@ -1,39 +1,45 @@ use crate::constants_table::ConstantsTable; use crate::ir::ir_parameter::IrParameter; -use crate::ir::ir_variable::{IrVariable, IrVariableDescriptor, IrVrVariableDescriptor}; +use crate::ir::ir_type_info::{IrTypeInfo, IrTypeInfoId}; +use crate::ir::ir_variable::IrVariableId; use crate::ir::register_allocation::VrUser; -use crate::type_info::TypeInfo; +use crate::ir::variable_locations::{VariableLocation, VariableLocations}; use dvm_lib::instruction::{ AddOperand, Location, LocationOrInteger, LocationOrNumber, MoveOperand, MultiplyOperand, PushOperand, ReturnOperand, SetFieldOperand, SubtractOperand, }; -use std::cell::RefCell; -use std::collections::HashSet; +use std::collections::{HashMap, HashSet}; use std::fmt::{Display, Formatter}; use std::rc::Rc; pub enum IrExpression { Parameter(Rc), - Variable(Rc>), + Variable(IrVariableId), Int(i32), Double(f64), String(Rc), } impl IrExpression { - pub fn move_operand(&self, constants_table: &mut ConstantsTable) -> MoveOperand { + pub fn move_operand( + &self, + variable_locations: &VariableLocations, + constants_table: &mut ConstantsTable, + ) -> MoveOperand { match self { IrExpression::Parameter(ir_parameter) => { MoveOperand::Location(Location::StackFrameOffset(ir_parameter.stack_offset())) } - IrExpression::Variable(ir_variable) => match ir_variable.borrow().descriptor() { - IrVariableDescriptor::VirtualRegister(register_variable) => { - MoveOperand::Location(Location::Register(register_variable.assigned_register())) + IrExpression::Variable(ir_variable_id) => { + match variable_locations.get_variable_location(ir_variable_id) { + VariableLocation::Register(register_assignment) => { + MoveOperand::Location(Location::Register(register_assignment)) + } + VariableLocation::Stack(stack_frame_offset) => { + MoveOperand::Location(Location::StackFrameOffset(stack_frame_offset)) + } } - IrVariableDescriptor::Stack(stack_variable) => { - MoveOperand::Location(Location::StackFrameOffset(stack_variable.offset())) - } - }, + } IrExpression::Int(i) => MoveOperand::Int(*i), IrExpression::Double(d) => MoveOperand::Double(*d), IrExpression::String(s) => { @@ -43,19 +49,25 @@ impl IrExpression { } } - pub fn push_operand(&self, constants_table: &mut ConstantsTable) -> PushOperand { + pub fn push_operand( + &self, + variable_locations: &VariableLocations, + constants_table: &mut ConstantsTable, + ) -> PushOperand { match self { IrExpression::Parameter(ir_parameter) => { PushOperand::Location(Location::StackFrameOffset(ir_parameter.stack_offset())) } - IrExpression::Variable(ir_variable) => match ir_variable.borrow().descriptor() { - IrVariableDescriptor::VirtualRegister(register_variable) => { - PushOperand::Location(Location::Register(register_variable.assigned_register())) + IrExpression::Variable(ir_variable_id) => { + match variable_locations.get_variable_location(ir_variable_id) { + VariableLocation::Register(register_assignment) => { + PushOperand::Location(Location::Register(register_assignment)) + } + VariableLocation::Stack(stack_frame_offset) => { + PushOperand::Location(Location::StackFrameOffset(stack_frame_offset)) + } } - IrVariableDescriptor::Stack(stack_variable) => { - PushOperand::Location(Location::StackFrameOffset(stack_variable.offset())) - } - }, + } IrExpression::Int(i) => PushOperand::Int(*i), IrExpression::Double(d) => PushOperand::Double(*d), IrExpression::String(s) => { @@ -65,35 +77,35 @@ impl IrExpression { } } - pub fn add_operand(&self, constants_table: &mut ConstantsTable) -> AddOperand { + pub fn add_operand( + &self, + type_infos: &Vec, + variables_to_type_infos: &HashMap, + variable_locations: &VariableLocations, + constants_table: &mut ConstantsTable, + ) -> AddOperand { match self { IrExpression::Parameter(ir_parameter) => match ir_parameter.type_info() { - TypeInfo::Integer | TypeInfo::Double | TypeInfo::String => { + IrTypeInfo::Int | IrTypeInfo::Double | IrTypeInfo::String => { AddOperand::Location(Location::StackFrameOffset(ir_parameter.stack_offset())) } - _ => panic!( - "Attempt to add non- integer/double/string (found: {})", - ir_parameter.type_info() - ), }, - IrExpression::Variable(ir_variable) => match ir_variable.borrow().type_info() { - TypeInfo::Integer | TypeInfo::Double | TypeInfo::String => { - match ir_variable.borrow().descriptor() { - IrVariableDescriptor::VirtualRegister(register_variable) => { - AddOperand::Location(Location::Register( - register_variable.assigned_register(), - )) + IrExpression::Variable(ir_variable_id) => { + let ir_type_info_id = variables_to_type_infos[ir_variable_id]; + let ir_type_info = &type_infos[ir_type_info_id]; + match ir_type_info { + IrTypeInfo::Int | IrTypeInfo::Double | IrTypeInfo::String => { + match variable_locations.get_variable_location(ir_variable_id) { + VariableLocation::Register(register_assignment) => { + AddOperand::Location(Location::Register(register_assignment)) + } + VariableLocation::Stack(stack_frame_offset) => { + AddOperand::Location(Location::StackFrameOffset(stack_frame_offset)) + } } - IrVariableDescriptor::Stack(stack_variable) => AddOperand::Location( - Location::StackFrameOffset(stack_variable.offset()), - ), } } - _ => panic!( - "Attempt to add non-integer/non-string (found: {})", - ir_variable.borrow().type_info() - ), - }, + } IrExpression::Int(i) => AddOperand::Int(*i), IrExpression::Double(d) => AddOperand::Double(*d), IrExpression::String(s) => { @@ -103,10 +115,15 @@ impl IrExpression { } } - pub fn subtract_operand(&self) -> SubtractOperand { + pub fn subtract_operand( + &self, + type_infos: &Vec, + variables_to_type_infos: &HashMap, + variable_locations: &VariableLocations, + ) -> SubtractOperand { match self { IrExpression::Parameter(ir_parameter) => match ir_parameter.type_info() { - TypeInfo::Integer | TypeInfo::Double => SubtractOperand::Location( + IrTypeInfo::Int | IrTypeInfo::Double => SubtractOperand::Location( Location::StackFrameOffset(ir_parameter.stack_offset()), ), _ => panic!( @@ -114,22 +131,26 @@ impl IrExpression { ir_parameter.type_info() ), }, - IrExpression::Variable(ir_variable) => match ir_variable.borrow().type_info() { - TypeInfo::Integer | TypeInfo::Double => match ir_variable.borrow().descriptor() { - IrVariableDescriptor::VirtualRegister(vr_variable) => { - SubtractOperand::Location(Location::Register( - vr_variable.assigned_register(), - )) - } - IrVariableDescriptor::Stack(stack_variable) => SubtractOperand::Location( - Location::StackFrameOffset(stack_variable.offset()), + IrExpression::Variable(ir_variable) => { + let ir_type_info_id = variables_to_type_infos[ir_variable]; + let ir_type_info = &type_infos[ir_type_info_id]; + match ir_type_info { + IrTypeInfo::Int | IrTypeInfo::Double => match variable_locations + .get_variable_location(ir_variable) + { + VariableLocation::Register(register_assignment) => { + SubtractOperand::Location(Location::Register(register_assignment)) + } + VariableLocation::Stack(stack_frame_offset) => SubtractOperand::Location( + Location::StackFrameOffset(stack_frame_offset), + ), + }, + _ => panic!( + "Attempt to subtract with non-number type (found {})", + ir_type_info ), - }, - _ => panic!( - "Attempt to subtract with non-integer type (found {})", - ir_variable.borrow().type_info() - ), - }, + } + } IrExpression::Int(i) => SubtractOperand::Int(*i), IrExpression::Double(d) => SubtractOperand::Double(*d), IrExpression::String(_) => { @@ -138,19 +159,35 @@ impl IrExpression { } } - pub fn multiply_operand(&self) -> MultiplyOperand { + pub fn multiply_operand( + &self, + type_infos: &Vec, + variables_to_type_infos: &HashMap, + variables_locations: &VariableLocations, + ) -> MultiplyOperand { match self { IrExpression::Parameter(ir_parameter) => { MultiplyOperand::Location(Location::StackFrameOffset(ir_parameter.stack_offset())) } - IrExpression::Variable(ir_variable) => match ir_variable.borrow().descriptor() { - IrVariableDescriptor::VirtualRegister(vr_variable) => { - MultiplyOperand::Location(Location::Register(vr_variable.assigned_register())) + IrExpression::Variable(ir_variable_id) => { + let ir_type_info_id = variables_to_type_infos[ir_variable_id]; + let ir_type_info = &type_infos[ir_type_info_id]; + match ir_type_info { + IrTypeInfo::Int | IrTypeInfo::Double => { + match variables_locations.get_variable_location(ir_variable_id) { + VariableLocation::Register(register_assignment) => { + MultiplyOperand::Location(Location::Register(register_assignment)) + } + VariableLocation::Stack(stack_frame_offset) => { + MultiplyOperand::Location(Location::StackFrameOffset( + stack_frame_offset, + )) + } + } + } + _ => panic!("Attempt to multiply non-number (found {})", ir_type_info), } - IrVariableDescriptor::Stack(stack_variable) => { - MultiplyOperand::Location(Location::StackFrameOffset(stack_variable.offset())) - } - }, + } IrExpression::Int(i) => MultiplyOperand::Int(*i), IrExpression::Double(d) => MultiplyOperand::Double(*d), IrExpression::String(_) => { @@ -159,21 +196,25 @@ impl IrExpression { } } - pub fn return_operand(&self, constants_table: &mut ConstantsTable) -> ReturnOperand { + pub fn return_operand( + &self, + variable_locations: &VariableLocations, + constants_table: &mut ConstantsTable, + ) -> ReturnOperand { match self { IrExpression::Parameter(ir_parameter) => { ReturnOperand::Location(Location::StackFrameOffset(ir_parameter.stack_offset())) } - IrExpression::Variable(ir_variable) => match ir_variable.borrow().descriptor() { - IrVariableDescriptor::VirtualRegister(register_variable) => { - ReturnOperand::Location(Location::Register( - register_variable.assigned_register(), - )) + IrExpression::Variable(ir_variable) => { + match variable_locations.get_variable_location(ir_variable) { + VariableLocation::Register(register_assignment) => { + ReturnOperand::Location(Location::Register(register_assignment)) + } + VariableLocation::Stack(stack_frame_offset) => { + ReturnOperand::Location(Location::StackFrameOffset(stack_frame_offset)) + } } - IrVariableDescriptor::Stack(stack_variable) => { - ReturnOperand::Location(Location::StackFrameOffset(stack_variable.offset())) - } - }, + } IrExpression::Int(i) => ReturnOperand::Int(*i), IrExpression::Double(d) => ReturnOperand::Double(*d), IrExpression::String(s) => { @@ -183,19 +224,25 @@ impl IrExpression { } } - pub fn set_field_operand(&self, constants_table: &mut ConstantsTable) -> SetFieldOperand { + pub fn set_field_operand( + &self, + variable_locations: &VariableLocations, + constants_table: &mut ConstantsTable, + ) -> SetFieldOperand { match self { IrExpression::Parameter(ir_parameter) => { SetFieldOperand::Location(Location::StackFrameOffset(ir_parameter.stack_offset())) } - IrExpression::Variable(ir_variable) => match ir_variable.borrow().descriptor() { - IrVariableDescriptor::VirtualRegister(vr_variable) => { - SetFieldOperand::Location(Location::Register(vr_variable.assigned_register())) + IrExpression::Variable(ir_variable_id) => { + match variable_locations.get_variable_location(ir_variable_id) { + VariableLocation::Register(register_assignment) => { + SetFieldOperand::Location(Location::Register(register_assignment)) + } + VariableLocation::Stack(stack_frame_offset) => { + SetFieldOperand::Location(Location::StackFrameOffset(stack_frame_offset)) + } } - IrVariableDescriptor::Stack(stack_variable) => { - SetFieldOperand::Location(Location::StackFrameOffset(stack_variable.offset())) - } - }, + } IrExpression::Int(i) => SetFieldOperand::Int(*i), IrExpression::Double(d) => SetFieldOperand::Double(*d), IrExpression::String(s) => { @@ -205,28 +252,48 @@ impl IrExpression { } } - pub fn as_location_or_number(&self) -> LocationOrNumber { + pub fn as_location_or_number( + &self, + variable_locations: &VariableLocations, + ) -> LocationOrNumber { match self { IrExpression::Parameter(ir_parameter) => { LocationOrNumber::Location(ir_parameter.as_location()) } - IrExpression::Variable(ir_variable) => { - LocationOrNumber::Location(ir_variable.borrow().descriptor().as_location()) - } + IrExpression::Variable(ir_variable_id) => LocationOrNumber::Location( + match variable_locations.get_variable_location(ir_variable_id) { + VariableLocation::Register(register_assignment) => { + Location::Register(register_assignment) + } + VariableLocation::Stack(stack_frame_offset) => { + Location::StackFrameOffset(stack_frame_offset) + } + }, + ), IrExpression::Int(i) => LocationOrNumber::Int(*i), IrExpression::Double(d) => LocationOrNumber::Double(*d), _ => panic!("Attempt to convert {} to a location or number", self), } } - pub fn as_location_or_integer(&self) -> LocationOrInteger { + pub fn as_location_or_integer( + &self, + variable_locations: &VariableLocations, + ) -> LocationOrInteger { match self { IrExpression::Parameter(ir_parameter) => { LocationOrInteger::Location(ir_parameter.as_location()) } - IrExpression::Variable(ir_variable) => { - LocationOrInteger::Location(ir_variable.borrow().descriptor().as_location()) - } + IrExpression::Variable(ir_variable_id) => LocationOrInteger::Location( + match variable_locations.get_variable_location(ir_variable_id) { + VariableLocation::Register(register_assignment) => { + Location::Register(register_assignment) + } + VariableLocation::Stack(stack_frame_offset) => { + Location::StackFrameOffset(stack_frame_offset) + } + }, + ), IrExpression::Int(i) => LocationOrInteger::Int(*i), _ => panic!("Attempt to convert {} to a location or integer", self), } @@ -239,8 +306,8 @@ impl Display for IrExpression { IrExpression::Parameter(ir_parameter) => { write!(f, "{}", ir_parameter) } - IrExpression::Variable(ir_variable) => { - write!(f, "{}", ir_variable.borrow()) + IrExpression::Variable(ir_variable_id) => { + write!(f, "{}", ir_variable_id) } IrExpression::Int(i) => { write!(f, "{}", i) @@ -256,12 +323,10 @@ impl Display for IrExpression { } impl VrUser for IrExpression { - fn vr_uses(&self) -> HashSet { + fn vr_uses(&self) -> HashSet { match self { IrExpression::Parameter(_) => HashSet::new(), - IrExpression::Variable(ir_variable) => { - ir_variable.borrow().descriptor().vr_variable_descriptors() - } + IrExpression::Variable(ir_variable) => HashSet::from([*ir_variable]), IrExpression::Int(_) => HashSet::new(), IrExpression::Double(_) => HashSet::new(), IrExpression::String(_) => HashSet::new(), diff --git a/dmc-lib/src/ir/ir_function.rs b/dmc-lib/src/ir/ir_function.rs index b5d890a..2a92af0 100644 --- a/dmc-lib/src/ir/ir_function.rs +++ b/dmc-lib/src/ir/ir_function.rs @@ -1,72 +1,90 @@ use crate::constants_table::ConstantsTable; -use crate::ir::assemble::{Assemble, InstructionsBuilder}; +use crate::ir::assemble::assemble_ir_function; use crate::ir::ir_block::IrBlock; use crate::ir::ir_parameter::IrParameter; -use crate::ir::ir_variable::IrVrVariableDescriptor; -use crate::ir::register_allocation::HasVrUsers; -use crate::offset_counter::OffsetCounter; -use crate::type_info::TypeInfo; +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::cell::RefCell; use std::collections::HashMap; -use std::fmt::Display; use std::rc::Rc; pub struct IrFunction { fqn: Rc, parameters: Vec>, - return_type_info: TypeInfo, - entry: Rc>, + variables: Vec, + return_type_info: Option, + blocks: Vec, } impl IrFunction { pub fn new( fqn: Rc, parameters: Vec>, - return_type_info: &TypeInfo, - entry: Rc>, + variables: Vec, + return_type_info: Option, + blocks: Vec, ) -> Self { Self { fqn, - parameters: parameters.to_vec(), - return_type_info: return_type_info.clone(), - entry, + parameters, + variables, + return_type_info, + blocks, } } - pub fn assign_registers( - &mut self, - register_count: usize, - offset_counter: &mut OffsetCounter, - ) -> HashMap { - self.entry - .borrow_mut() - .assign_registers(register_count, offset_counter) + pub fn fqn(&self) -> &str { + &self.fqn } - pub fn assemble(&self, stack_size: usize, constants_table: &mut ConstantsTable) -> Function { - let mut builder = InstructionsBuilder::new(); - self.entry.borrow().assemble(&mut builder, constants_table); - let instructions = builder.take_instructions(); + pub fn blocks(&self) -> &[IrBlock] { + &self.blocks + } + + pub fn parameters(&self) -> &[Rc] { + &self.parameters + } + + pub fn variables(&self) -> &[IrVariable] { + &self.variables + } + + pub fn return_type_info(&self) -> Option<&IrTypeInfo> { + self.return_type_info.as_ref() + } + + pub fn assign_registers(&self, register_count: usize) -> VariableLocations { + if self.blocks.is_empty() { + return VariableLocations::new(HashMap::new(), HashMap::new()); + } + if self.blocks.len() > 1 { + unimplemented!("having more than one block in a function is not yet implemented.") + } + let block = &self.blocks[0]; + block_assign_registers(block, register_count) + } + + pub fn assemble( + &self, + type_infos: &Vec, + variables_to_type_infos: &HashMap, + variable_locations: &VariableLocations, + constants_table: &mut ConstantsTable, + ) -> Function { + let instructions = assemble_ir_function( + self, + type_infos, + variables_to_type_infos, + variable_locations, + constants_table, + ); Function::new( self.fqn.clone(), self.parameters.len(), - stack_size, + variable_locations.stack_size(), instructions, ) } } - -impl Display for IrFunction { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "fn {}(", self.fqn)?; - for (i, parameter) in self.parameters.iter().enumerate() { - write!(f, "{}: {}", parameter, parameter.type_info())?; - if i < self.parameters.len() - 1 { - write!(f, ", ")?; - } - } - write!(f, ") -> {}\n{}", self.return_type_info, self.entry.borrow())?; - Ok(()) - } -} diff --git a/dmc-lib/src/ir/ir_get_field_ref.rs b/dmc-lib/src/ir/ir_get_field_ref.rs index b1ece0e..7e220b6 100644 --- a/dmc-lib/src/ir/ir_get_field_ref.rs +++ b/dmc-lib/src/ir/ir_get_field_ref.rs @@ -1,24 +1,23 @@ -use crate::ir::ir_parameter_or_variable::IrParameterOrVariable; -use crate::ir::ir_variable::IrVrVariableDescriptor; +use crate::ir::ir_variable::IrVariableId; use crate::ir::register_allocation::VrUser; use std::collections::HashSet; use std::fmt::{Display, Formatter}; pub struct IrGetFieldRef { - self_parameter_or_variable: IrParameterOrVariable, + self_variable_id: IrVariableId, field_index: usize, } impl IrGetFieldRef { - pub fn new(self_parameter_or_variable: IrParameterOrVariable, field_index: usize) -> Self { + pub fn new(self_variable_id: IrVariableId, field_index: usize) -> Self { Self { - self_parameter_or_variable, + self_variable_id, field_index, } } - pub fn self_parameter_or_variable(&self) -> &IrParameterOrVariable { - &self.self_parameter_or_variable + pub fn self_variable_id(self) -> IrVariableId { + self.self_variable_id } pub fn field_index(&self) -> usize { @@ -28,16 +27,12 @@ impl IrGetFieldRef { impl Display for IrGetFieldRef { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!( - f, - "&{}.{}", - self.self_parameter_or_variable, self.field_index - ) + write!(f, "&{}.{}", self.self_variable_id, self.field_index) } } impl VrUser for IrGetFieldRef { - fn vr_uses(&self) -> HashSet { - self.self_parameter_or_variable().vr_uses() + fn vr_uses(&self) -> HashSet { + todo!() } } diff --git a/dmc-lib/src/ir/ir_get_field_ref_mut.rs b/dmc-lib/src/ir/ir_get_field_ref_mut.rs index 9771653..37f4946 100644 --- a/dmc-lib/src/ir/ir_get_field_ref_mut.rs +++ b/dmc-lib/src/ir/ir_get_field_ref_mut.rs @@ -1,24 +1,23 @@ -use crate::ir::ir_parameter_or_variable::IrParameterOrVariable; -use crate::ir::ir_variable::IrVrVariableDescriptor; +use crate::ir::ir_variable::IrVariableId; use crate::ir::register_allocation::VrUser; use std::collections::HashSet; use std::fmt::{Display, Formatter}; pub struct IrGetFieldRefMut { - self_parameter_or_variable: IrParameterOrVariable, + self_ir_variable: IrVariableId, field_index: usize, } impl IrGetFieldRefMut { - pub fn new(self_parameter_or_variable: IrParameterOrVariable, field_index: usize) -> Self { + pub fn new(self_ir_variable: IrVariableId, field_index: usize) -> Self { Self { - self_parameter_or_variable, + self_ir_variable, field_index, } } - pub fn self_parameter_or_variable(&self) -> &IrParameterOrVariable { - &self.self_parameter_or_variable + pub fn self_ir_variable(&self) -> IrVariableId { + self.self_ir_variable } pub fn field_index(&self) -> usize { @@ -28,17 +27,12 @@ impl IrGetFieldRefMut { impl Display for IrGetFieldRefMut { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!( - f, - "&mut {}.{}", - self.self_parameter_or_variable, - self.field_index() - ) + write!(f, "&mut {}.{}", self.self_ir_variable, self.field_index()) } } impl VrUser for IrGetFieldRefMut { - fn vr_uses(&self) -> HashSet { - self.self_parameter_or_variable.vr_uses() + fn vr_uses(&self) -> HashSet { + todo!() } } diff --git a/dmc-lib/src/ir/ir_operation.rs b/dmc-lib/src/ir/ir_operation.rs index 6ecb947..e872716 100644 --- a/dmc-lib/src/ir/ir_operation.rs +++ b/dmc-lib/src/ir/ir_operation.rs @@ -5,7 +5,7 @@ use crate::ir::ir_expression::IrExpression; use crate::ir::ir_get_field_ref::IrGetFieldRef; use crate::ir::ir_get_field_ref_mut::IrGetFieldRefMut; use crate::ir::ir_read_field::IrReadField; -use crate::ir::ir_variable::IrVrVariableDescriptor; +use crate::ir::ir_variable::IrVariableId; use crate::ir::register_allocation::VrUser; use std::collections::HashSet; use std::fmt::{Display, Formatter}; @@ -49,7 +49,7 @@ impl Display for IrOperation { } impl VrUser for IrOperation { - fn vr_uses(&self) -> HashSet { + fn vr_uses(&self) -> HashSet { match self { IrOperation::GetFieldRef(ir_get_field_ref) => ir_get_field_ref.vr_uses(), IrOperation::GetFieldRefMut(ir_get_field_ref_mut) => ir_get_field_ref_mut.vr_uses(), diff --git a/dmc-lib/src/ir/ir_parameter.rs b/dmc-lib/src/ir/ir_parameter.rs index 72e1899..0f3f257 100644 --- a/dmc-lib/src/ir/ir_parameter.rs +++ b/dmc-lib/src/ir/ir_parameter.rs @@ -1,16 +1,16 @@ -use crate::type_info::TypeInfo; +use crate::ir::ir_type_info::IrTypeInfo; use dvm_lib::instruction::Location; use std::fmt::{Display, Formatter}; use std::rc::Rc; pub struct IrParameter { name: Rc, - type_info: TypeInfo, + type_info: IrTypeInfo, stack_offset: isize, } impl IrParameter { - pub fn new(name: &str, type_info: TypeInfo, stack_offset: isize) -> Self { + pub fn new(name: &str, type_info: IrTypeInfo, stack_offset: isize) -> Self { Self { name: name.into(), type_info, @@ -18,7 +18,7 @@ impl IrParameter { } } - pub fn type_info(&self) -> &TypeInfo { + pub fn type_info(&self) -> &IrTypeInfo { &self.type_info } diff --git a/dmc-lib/src/ir/ir_parameter_or_variable.rs b/dmc-lib/src/ir/ir_parameter_or_variable.rs index cf39697..79a82c8 100644 --- a/dmc-lib/src/ir/ir_parameter_or_variable.rs +++ b/dmc-lib/src/ir/ir_parameter_or_variable.rs @@ -1,5 +1,5 @@ use crate::ir::ir_parameter::IrParameter; -use crate::ir::ir_variable::{IrVariable, IrVariableDescriptor, IrVrVariableDescriptor}; +use crate::ir::ir_variable::{IrVariable, IrVariableDescriptor, IrVirtualRegisterVariable}; use dvm_lib::instruction::Location; use std::cell::RefCell; use std::collections::HashSet; @@ -7,6 +7,7 @@ use std::fmt::{Display, Formatter}; use std::rc::Rc; #[derive(Clone)] +#[deprecated] pub enum IrParameterOrVariable { IrParameter(Rc), Variable(Rc>), @@ -24,7 +25,7 @@ impl IrParameterOrVariable { } } - pub fn vr_uses(&self) -> HashSet { + pub fn vr_uses(&self) -> HashSet { if let IrParameterOrVariable::Variable(ir_variable) = &self { if let IrVariableDescriptor::VirtualRegister(vr_variable) = ir_variable.borrow().descriptor() diff --git a/dmc-lib/src/ir/ir_read_field.rs b/dmc-lib/src/ir/ir_read_field.rs index 1e4a1df..532fef4 100644 --- a/dmc-lib/src/ir/ir_read_field.rs +++ b/dmc-lib/src/ir/ir_read_field.rs @@ -1,38 +1,30 @@ -use crate::ir::ir_variable::{IrVariable, IrVariableDescriptor, IrVrVariableDescriptor}; +use crate::ir::ir_variable::IrVariableId; use crate::ir::register_allocation::VrUser; -use std::cell::RefCell; use std::collections::HashSet; use std::fmt::{Display, Formatter}; -use std::rc::Rc; pub struct IrReadField { - field_ref_variable: Rc>, + field_ref_variable: IrVariableId, } impl IrReadField { - pub fn new(field_ref_variable: Rc>) -> Self { + pub fn new(field_ref_variable: IrVariableId) -> Self { Self { field_ref_variable } } - pub fn field_ref_variable(&self) -> &Rc> { - &self.field_ref_variable + pub fn field_ref_variable(&self) -> IrVariableId { + self.field_ref_variable + } +} + +impl VrUser for IrReadField { + fn vr_uses(&self) -> HashSet { + todo!() } } impl Display for IrReadField { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.field_ref_variable.borrow(),) - } -} - -impl VrUser for IrReadField { - fn vr_uses(&self) -> HashSet { - let mut set = HashSet::new(); - if let IrVariableDescriptor::VirtualRegister(vr_variable) = - self.field_ref_variable.borrow().descriptor() - { - set.insert(vr_variable.clone()); - } - set + write!(f, "IrReadField({})", self.field_ref_variable) } } diff --git a/dmc-lib/src/ir/ir_return.rs b/dmc-lib/src/ir/ir_return.rs index 42c9692..4bd786b 100644 --- a/dmc-lib/src/ir/ir_return.rs +++ b/dmc-lib/src/ir/ir_return.rs @@ -1,9 +1,6 @@ -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::ir_variable::IrVariableId; use crate::ir::register_allocation::VrUser; -use dvm_lib::instruction::Instruction; use std::collections::HashSet; use std::fmt::{Display, Formatter}; @@ -15,10 +12,14 @@ impl IrReturn { pub fn new(value: Option) -> Self { Self { value } } + + pub fn value(&self) -> Option<&IrExpression> { + self.value.as_ref() + } } impl VrUser for IrReturn { - fn vr_uses(&self) -> HashSet { + fn vr_uses(&self) -> HashSet { if let Some(ir_expression) = self.value.as_ref() { ir_expression.vr_uses() } else { @@ -27,16 +28,6 @@ impl VrUser for IrReturn { } } -impl Assemble for IrReturn { - fn assemble(&self, builder: &mut InstructionsBuilder, constants_table: &mut ConstantsTable) { - if let Some(ir_expression) = self.value.as_ref() { - let return_operand = ir_expression.return_operand(constants_table); - builder.push(Instruction::SetReturnValue(return_operand)); - } - builder.push(Instruction::Return); - } -} - impl Display for IrReturn { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "return")?; diff --git a/dmc-lib/src/ir/ir_set_field.rs b/dmc-lib/src/ir/ir_set_field.rs index 4690287..61c1a0d 100644 --- a/dmc-lib/src/ir/ir_set_field.rs +++ b/dmc-lib/src/ir/ir_set_field.rs @@ -1,9 +1,6 @@ -use crate::constants_table::ConstantsTable; -use crate::ir::assemble::{Assemble, InstructionsBuilder}; use crate::ir::ir_expression::IrExpression; -use crate::ir::ir_variable::{IrVariable, IrVariableDescriptor, IrVrVariableDescriptor}; +use crate::ir::ir_variable::{IrVariable, IrVariableId}; use crate::ir::register_allocation::VrUser; -use dvm_lib::instruction::Instruction; use std::cell::RefCell; use std::collections::HashSet; use std::fmt::{Display, Formatter}; @@ -24,26 +21,27 @@ impl IrSetField { } impl VrUser for IrSetField { - fn vr_uses(&self) -> HashSet { - let mut set = HashSet::new(); - match self.field_ref_variable.borrow().descriptor() { - IrVariableDescriptor::VirtualRegister(vr_variable) => { - set.insert(vr_variable.clone()); - } - IrVariableDescriptor::Stack(_) => {} - } - set.extend(self.initializer.vr_uses()); - set + fn vr_uses(&self) -> HashSet { + // let mut set = HashSet::new(); + // match self.field_ref_variable.borrow().descriptor() { + // IrVariableDescriptor::VirtualRegister(vr_variable) => { + // set.insert(vr_variable.clone()); + // } + // IrVariableDescriptor::Stack(_) => {} + // } + // set.extend(self.initializer.vr_uses()); + // set + todo!() } } -impl Assemble for IrSetField { - fn assemble(&self, builder: &mut InstructionsBuilder, constants_table: &mut ConstantsTable) { - let field_ref_location = self.field_ref_variable.borrow().descriptor().as_location(); - let set_field_operand = self.initializer.set_field_operand(constants_table); - builder.push(Instruction::SetField(field_ref_location, set_field_operand)); - } -} +// impl Assemble for IrSetField { +// fn assemble(&self, builder: &mut InstructionsBuilder, constants_table: &mut ConstantsTable) { +// let field_ref_location = self.field_ref_variable.borrow().descriptor().as_location(); +// let set_field_operand = self.initializer.set_field_operand(constants_table); +// builder.push(Instruction::SetField(field_ref_location, set_field_operand)); +// } +// } impl Display for IrSetField { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { diff --git a/dmc-lib/src/ir/ir_statement.rs b/dmc-lib/src/ir/ir_statement.rs index 8360258..383d0d5 100644 --- a/dmc-lib/src/ir/ir_statement.rs +++ b/dmc-lib/src/ir/ir_statement.rs @@ -1,14 +1,10 @@ -use crate::constants_table::ConstantsTable; -use crate::ir::assemble::{Assemble, InstructionsBuilder}; use crate::ir::ir_assign::IrAssign; use crate::ir::ir_call::IrCall; use crate::ir::ir_return::IrReturn; use crate::ir::ir_set_field::IrSetField; -use crate::ir::ir_variable::IrVrVariableDescriptor; +use crate::ir::ir_variable::IrVariableId; use crate::ir::register_allocation::VrUser; -use crate::offset_counter::OffsetCounter; -use std::collections::{HashMap, HashSet}; -use std::fmt::{Display, Formatter}; +use std::collections::HashSet; pub enum IrStatement { Assign(IrAssign), @@ -18,7 +14,7 @@ pub enum IrStatement { } impl VrUser for IrStatement { - fn vr_definitions(&self) -> HashSet { + fn vr_definitions(&self) -> HashSet { match self { IrStatement::Assign(ir_assign) => ir_assign.vr_definitions(), IrStatement::Call(ir_call) => ir_call.vr_definitions(), @@ -27,7 +23,7 @@ impl VrUser for IrStatement { } } - fn vr_uses(&self) -> HashSet { + fn vr_uses(&self) -> HashSet { match self { IrStatement::Assign(ir_assign) => ir_assign.vr_uses(), IrStatement::Call(ir_call) => ir_call.vr_uses(), @@ -35,96 +31,4 @@ impl VrUser for IrStatement { IrStatement::SetField(ir_set_field) => ir_set_field.vr_uses(), } } - - fn propagate_spills(&mut self, spills: &HashSet) { - match self { - IrStatement::Assign(ir_assign) => { - ir_assign.propagate_spills(spills); - } - IrStatement::Call(ir_call) => { - ir_call.propagate_spills(spills); - } - IrStatement::Return(ir_return) => { - ir_return.propagate_spills(spills); - } - IrStatement::SetField(ir_set_field) => { - ir_set_field.propagate_spills(spills); - } - } - } - - fn propagate_register_assignments( - &mut self, - assignments: &HashMap, - ) { - match self { - IrStatement::Assign(ir_assign) => { - ir_assign.propagate_register_assignments(assignments); - } - IrStatement::Call(ir_call) => { - ir_call.propagate_register_assignments(assignments); - } - IrStatement::Return(ir_return) => { - ir_return.propagate_register_assignments(assignments); - } - IrStatement::SetField(ir_set_field) => { - ir_set_field.propagate_register_assignments(assignments); - } - } - } - - 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); - } - IrStatement::SetField(ir_set_field) => { - ir_set_field.propagate_stack_offsets(counter); - } - } - } -} - -impl Assemble for IrStatement { - fn assemble(&self, builder: &mut InstructionsBuilder, constants_table: &mut ConstantsTable) { - match self { - IrStatement::Assign(ir_assign) => { - ir_assign.assemble(builder, constants_table); - } - IrStatement::Call(ir_call) => { - ir_call.assemble(builder, constants_table); - } - IrStatement::Return(ir_return) => { - ir_return.assemble(builder, constants_table); - } - IrStatement::SetField(ir_set_field) => { - ir_set_field.assemble(builder, constants_table); - } - } - } -} - -impl Display for IrStatement { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - IrStatement::Assign(ir_assign) => { - write!(f, "{}", ir_assign) - } - IrStatement::Call(ir_call) => { - write!(f, "{}", ir_call) - } - IrStatement::Return(ir_return) => { - write!(f, "{}", ir_return) - } - IrStatement::SetField(ir_set_field) => { - write!(f, "{}", ir_set_field) - } - } - } } diff --git a/dmc-lib/src/ir/ir_type_info.rs b/dmc-lib/src/ir/ir_type_info.rs new file mode 100644 index 0000000..560ba71 --- /dev/null +++ b/dmc-lib/src/ir/ir_type_info.rs @@ -0,0 +1,16 @@ +use std::fmt::{Display, Formatter}; + +pub type IrTypeInfoId = usize; + +#[derive(Debug)] +pub enum IrTypeInfo { + String, + Int, + Double, +} + +impl Display for IrTypeInfo { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} diff --git a/dmc-lib/src/ir/ir_variable.rs b/dmc-lib/src/ir/ir_variable.rs index 005b3ad..fe29b2e 100644 --- a/dmc-lib/src/ir/ir_variable.rs +++ b/dmc-lib/src/ir/ir_variable.rs @@ -1,211 +1,33 @@ -use crate::offset_counter::OffsetCounter; -use crate::type_info::TypeInfo; -use dvm_lib::instruction::Location; -use std::collections::HashSet; -use std::fmt::{Debug, Display, Formatter}; -use std::hash::Hash; +use crate::ir::ir_type_info::IrTypeInfo; +use std::fmt::{Display, Formatter}; use std::rc::Rc; +pub type IrVariableId = usize; + pub struct IrVariable { - descriptor: IrVariableDescriptor, - type_info: TypeInfo, + name: Rc, + type_info: IrTypeInfo, } impl IrVariable { - pub fn new_vr(name: Rc, block_id: usize, type_info: &TypeInfo) -> Self { + pub fn new(name: &str, type_info: IrTypeInfo) -> Self { Self { - descriptor: IrVariableDescriptor::VirtualRegister(IrVrVariableDescriptor::new( - name, block_id, - )), - type_info: type_info.clone(), + name: name.into(), + type_info, } } - pub fn new_stack_with_offset( - name: Rc, - block_id: usize, - type_info: &TypeInfo, - offset: isize, - ) -> Self { - let mut descriptor = IrStackVariableDescriptor::new(name, block_id); - descriptor.set_offset(offset); - Self { - descriptor: IrVariableDescriptor::Stack(descriptor), - type_info: type_info.clone(), - } + pub fn name(&self) -> &str { + &self.name } - pub fn type_info(&self) -> &TypeInfo { + pub fn type_info(&self) -> &IrTypeInfo { &self.type_info } - - pub fn descriptor(&self) -> &IrVariableDescriptor { - &self.descriptor - } - - pub fn descriptor_mut(&mut self) -> &mut IrVariableDescriptor { - &mut self.descriptor - } - - pub fn set_descriptor(&mut self, descriptor: IrVariableDescriptor) { - self.descriptor = descriptor; - } } impl Display for IrVariable { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.descriptor) - } -} - -pub enum IrVariableDescriptor { - VirtualRegister(IrVrVariableDescriptor), - Stack(IrStackVariableDescriptor), -} - -impl Display for IrVariableDescriptor { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - IrVariableDescriptor::VirtualRegister(vr_variable) => { - write!(f, "{}", vr_variable) - } - IrVariableDescriptor::Stack(stack_variable) => { - write!(f, "{}", stack_variable) - } - } - } -} - -impl IrVariableDescriptor { - pub fn name(&self) -> &str { - match self { - IrVariableDescriptor::VirtualRegister(vr_variable) => vr_variable.name(), - IrVariableDescriptor::Stack(stack_variable) => stack_variable.name(), - } - } - - pub fn name_owned(&self) -> Rc { - match self { - IrVariableDescriptor::VirtualRegister(vr_variable) => vr_variable.name_owned(), - IrVariableDescriptor::Stack(stack_variable) => stack_variable.name_owned(), - } - } - - pub fn as_location(&self) -> Location { - match self { - IrVariableDescriptor::VirtualRegister(register_variable) => { - register_variable.as_location() - } - IrVariableDescriptor::Stack(stack_variable) => stack_variable.as_location(), - } - } - - pub fn vr_variable_descriptors(&self) -> HashSet { - if let IrVariableDescriptor::VirtualRegister(register_variable) = self { - HashSet::from([register_variable.clone()]) - } else { - HashSet::new() - } - } -} - -#[derive(Clone, Hash, PartialEq, Eq)] -pub struct IrVrVariableDescriptor { - name: Rc, - block_id: usize, - assigned_register: Option, -} - -impl IrVrVariableDescriptor { - pub fn new(name: Rc, block_id: usize) -> Self { - Self { - name, - block_id, - assigned_register: None, - } - } - - pub fn name(&self) -> &str { - &self.name - } - - pub fn name_owned(&self) -> Rc { - self.name.clone() - } - - pub fn block_id(&self) -> usize { - self.block_id - } - - pub fn set_assigned_register(&mut self, register: usize) { - self.assigned_register = Some(register); - } - - pub fn assigned_register(&self) -> usize { - self.assigned_register.unwrap() - } - - pub fn as_location(&self) -> Location { - Location::Register(self.assigned_register.unwrap()) - } -} - -impl Display for IrVrVariableDescriptor { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.name) } } - -impl Debug for IrVrVariableDescriptor { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.name) - } -} - -pub struct IrStackVariableDescriptor { - name: Rc, - block_id: usize, - offset: Option, -} - -impl IrStackVariableDescriptor { - pub fn new(name: Rc, block_id: usize) -> Self { - Self { - name, - block_id, - offset: None, - } - } - - pub fn name(&self) -> &str { - &self.name - } - - pub fn name_owned(&self) -> Rc { - self.name.clone() - } - - pub fn use_next_offset(&mut self, offset_counter: &mut OffsetCounter) { - if self.offset.is_none() { - self.offset = Some(offset_counter.next() as isize); - } - } - - pub fn set_offset(&mut self, offset: isize) { - self.offset = Some(offset); - } - - pub fn offset(&self) -> isize { - self.offset.unwrap() - } - - pub fn as_location(&self) -> Location { - Location::StackFrameOffset(self.offset.unwrap()) - } -} - -impl Display for IrStackVariableDescriptor { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{}_{}", self.name, self.block_id) - } -} diff --git a/dmc-lib/src/ir/mod.rs b/dmc-lib/src/ir/mod.rs index ebef855..a4ea96f 100644 --- a/dmc-lib/src/ir/mod.rs +++ b/dmc-lib/src/ir/mod.rs @@ -1,4 +1,5 @@ mod assemble; +mod debug_print; pub mod ir_allocate; pub mod ir_assign; pub mod ir_binary_operation; @@ -16,6 +17,8 @@ pub mod ir_read_field; pub mod ir_return; pub mod ir_set_field; pub mod ir_statement; +pub mod ir_type_info; pub mod ir_variable; mod register_allocation; mod util; +mod variable_locations; diff --git a/dmc-lib/src/ir/register_allocation.rs b/dmc-lib/src/ir/register_allocation.rs index d8b4c22..6940cf1 100644 --- a/dmc-lib/src/ir/register_allocation.rs +++ b/dmc-lib/src/ir/register_allocation.rs @@ -1,158 +1,153 @@ -use crate::ir::ir_variable::IrVrVariableDescriptor; +use crate::ir::ir_block::IrBlock; +use crate::ir::ir_variable::IrVariableId; +use crate::ir::variable_locations::VariableLocations; use crate::offset_counter::OffsetCounter; +use dvm_lib::instruction::{Register, StackFrameOffset}; use std::collections::{HashMap, HashSet}; -pub type InterferenceGraph = HashMap>; -pub type LivenessMap = HashMap>; +pub type RegisterAssignment = Register; +pub type InterferenceGraph = HashMap>; +pub type LivenessSets = Vec>; -pub trait HasVrUsers { - fn vr_users(&self) -> Vec<&dyn VrUser>; - fn vr_users_mut(&mut self) -> Vec<&mut dyn VrUser>; +pub 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]; + let mut live_out: LivenessSets = vec![HashSet::new(); n_statements]; - fn live_in_live_out(&self) -> (LivenessMap, LivenessMap) { - let mut live_in: LivenessMap = HashMap::new(); - let mut live_out: LivenessMap = HashMap::new(); - - loop { - let mut did_work = false; - for (block_index, statement) in self.vr_users().iter().enumerate().rev() { - // init if necessary - if !live_in.contains_key(&block_index) { - live_in.insert(block_index, HashSet::new()); - } - if !live_out.contains_key(&block_index) { - live_out.insert(block_index, HashSet::new()); - } - - // out (union of successors ins) - // for now, a statement can only have one successor - // this will need to be updated when we add jumps - if let Some(successor_live_in) = live_in.get(&(block_index + 1)) { - let statement_live_out = live_out.get_mut(&block_index).unwrap(); - for vr_variable in successor_live_in { - if statement_live_out.insert(vr_variable.clone()) { - did_work = true; - } - } - } - - // in: use(s) U ( out(s) - def(s) ) - let mut new_ins = statement - .vr_uses() - .iter() - .map(|u| (*u).clone()) - .collect::>(); - let statement_live_out = live_out.get(&block_index).unwrap(); - let defs = statement - .vr_definitions() - .iter() - .map(|d| (*d).clone()) - .collect::>(); - let rhs = statement_live_out - &defs; - new_ins.extend(rhs); - - let statement_live_in = live_in.get_mut(&block_index).unwrap(); - for new_in in new_ins { - if statement_live_in.insert(new_in) { - did_work = true; - } + loop { + let mut did_work = false; + // Go backwards for efficiency + for (statement_index, ir_statement) in ir_block.statements().iter().enumerate().rev() { + // out (union of successors ins) + // for now, a statement can only have one successor + // this will need to be updated when we add jumps + let statement_live_out = &mut live_out[statement_index]; + let successor_live_in = &live_in[statement_index + 1]; + for ir_variable_id in successor_live_in { + if statement_live_out.insert(*ir_variable_id) { + did_work = true; } } - if !did_work { - break; + + // in: use(s) U ( out(s) - def(s) ) + let use_s = ir_statement + .vr_uses() + .iter() + .map(|u| *u) + .collect::>(); + let out_s = &live_out[statement_index]; + let def_s = ir_statement + .vr_definitions() + .iter() + .map(|d| *d) + .collect::>(); + let rhs = out_s - &def_s; + let new_ins = use_s.union(&rhs).map(|v| *v).collect::>(); + + // add new ins to statement's live in + let statement_live_in = &mut live_in[statement_index]; + for new_in in new_ins { + if statement_live_in.insert(new_in) { + // if we added a previously unadded variable, we did work + did_work = true; + } } } - (live_in, live_out) + // break only if we did nothing! + if !did_work { + break; + } + } + (live_in, live_out) +} + +pub fn block_interference_graph( + ir_block: &IrBlock, + spilled: &HashSet, +) -> InterferenceGraph { + // create a set of all variables used in the block + let mut all_vr_variables: HashSet = HashSet::new(); + for statement in ir_block.statements() { + all_vr_variables.extend(statement.vr_definitions()); + all_vr_variables.extend(statement.vr_uses()); } - fn interference_graph(&self) -> InterferenceGraph { - let mut all_vr_variables: HashSet = HashSet::new(); - - for vr_user in self.vr_users() { - all_vr_variables.extend(vr_user.vr_definitions()); - all_vr_variables.extend(vr_user.vr_uses()); - } - - let mut graph: InterferenceGraph = HashMap::new(); - for variable in all_vr_variables { - graph.insert(variable, HashSet::new()); - } - - let (_, live_out) = self.live_in_live_out(); - - for (index, vr_user) in self.vr_users().iter().enumerate() { - let user_live_in = live_out.get(&index).unwrap(); - for definition_vr_variable in vr_user.vr_definitions() { - for live_out_variable in user_live_in { - // we do the following check to avoid adding an edge to itself - if definition_vr_variable.ne(live_out_variable) { - graph - .get_mut(&definition_vr_variable) - .unwrap() - .insert(live_out_variable.clone()); - graph - .get_mut(live_out_variable) - .unwrap() - .insert(definition_vr_variable.clone()); - } - } - } - } - - 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()); } - fn assign_registers( - &mut self, - register_count: usize, - offset_counter: &mut OffsetCounter, - ) -> HashMap { - let mut spills: HashSet = HashSet::new(); - loop { - let mut interference_graph = self.interference_graph(); - let (registers, new_spills) = - registers_and_spills(&mut interference_graph, register_count); + let (_, live_out) = block_live_in_live_out(ir_block); - if spills != new_spills { - spills = new_spills; - // propagate all spills, since those won't be used for the next interference graph - for vr_user in &mut self.vr_users_mut() { - vr_user.propagate_spills(&spills); + for (statement_index, ir_statement) in ir_block.statements().iter().enumerate() { + let statement_live_out = &live_out[statement_index]; + for definition_vr_variable in ir_statement.vr_definitions() { + for live_out_variable in statement_live_out { + // 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) + let definition_edges = graph.get_mut(&definition_vr_variable).unwrap(); + definition_edges.insert(*live_out_variable); + let live_out_variable_edges = graph.get_mut(live_out_variable).unwrap(); + live_out_variable_edges.insert(definition_vr_variable); } - } else { - // we've calculated final assignments, so propagate them - for vr_user in self.vr_users_mut() { - vr_user.propagate_register_assignments(®isters); - } - // also set offsets - for vr_user in self.vr_users_mut() { - vr_user.propagate_stack_offsets(offset_counter); - } - - return registers; } } } + + graph +} + +pub fn block_assign_registers(ir_block: &IrBlock, register_count: usize) -> VariableLocations { + let mut spilled: HashSet = HashSet::new(); + loop { + let mut interference_graph = block_interference_graph(ir_block, &spilled); + let (registers, new_spills) = registers_and_spills(&mut interference_graph, register_count); + + if spilled != new_spills { + spilled = new_spills; // todo: figure out if this works algorithmically + } else { + let mut spills_to_stack_offsets: HashMap = + HashMap::new(); + let mut offset_counter = 0isize; + for spill in spilled { + spills_to_stack_offsets.insert(spill, offset_counter); + offset_counter += 1; + } + return VariableLocations::new(registers, spills_to_stack_offsets); + } + } +} + +pub trait HasVrUsers { + fn vr_users(&self) -> Vec<&dyn VrUser>; + fn vr_users_mut(&mut self) -> Vec<&mut dyn VrUser>; } pub trait VrUser { - fn vr_definitions(&self) -> HashSet { + fn vr_definitions(&self) -> HashSet { HashSet::new() } - fn vr_uses(&self) -> HashSet; + #[deprecated] + fn vr_uses(&self) -> HashSet; - fn propagate_spills(&mut self, _spills: &HashSet) { + #[deprecated] + fn propagate_spills(&mut self, _spills: &HashSet) { // default no-op } + #[deprecated] fn propagate_register_assignments( &mut self, - _assignments: &HashMap, + _assignments: &HashMap, ) { // default no-op } + #[deprecated] fn propagate_stack_offsets(&mut self, _counter: &mut OffsetCounter) { // default no-op } @@ -160,8 +155,8 @@ pub trait VrUser { #[derive(Debug)] struct WorkItem { - vr: IrVrVariableDescriptor, - edges: HashSet, + vr: IrVariableId, + edges: HashSet, color: bool, } @@ -169,8 +164,8 @@ pub fn registers_and_spills( interference_graph: &mut InterferenceGraph, k: usize, ) -> ( - HashMap, - HashSet, + HashMap, + HashSet, ) { let mut work_stack: Vec = vec![]; @@ -180,8 +175,8 @@ pub fn registers_and_spills( // 3. assign colors to registers let mut rebuilt_graph: InterferenceGraph = HashMap::new(); - let mut register_assignments: HashMap = HashMap::new(); - let mut spills: HashSet = HashSet::new(); + let mut register_assignments: HashMap = HashMap::new(); + let mut spills: HashSet = HashSet::new(); while let Some(work_item) = work_stack.pop() { if work_item.color { @@ -201,7 +196,7 @@ fn assign_register( work_item: &WorkItem, graph: &mut InterferenceGraph, k: usize, - register_assignments: &mut HashMap, + register_assignments: &mut HashMap, ) { rebuild_vr_and_edges(graph, work_item); @@ -220,21 +215,19 @@ fn assign_register( } } -fn find_vr_lt_k( - interference_graph: &InterferenceGraph, - k: usize, -) -> Option<&IrVrVariableDescriptor> { +fn find_vr_lt_k(interference_graph: &InterferenceGraph, k: usize) -> Option { interference_graph.iter().find_map( |(vr, neighbors)| { - if neighbors.len() < k { Some(vr) } else { None } + if neighbors.len() < k { Some(*vr) } else { None } }, ) } +/// Returns the (removed) outgoing edges for the given vr fn remove_vr_and_edges( interference_graph: &mut InterferenceGraph, - vr: &IrVrVariableDescriptor, -) -> HashSet { + vr: &IrVariableId, +) -> HashSet { // first, outgoing let outgoing_edges = interference_graph.remove(vr).unwrap(); @@ -311,7 +304,7 @@ fn rebuild_vr_and_edges(graph: &mut InterferenceGraph, work_item: &WorkItem) { fn can_optimistically_color( work_item: &WorkItem, - register_assignments: &HashMap, + register_assignments: &HashMap, k: usize, ) -> bool { // see if we can optimistically color @@ -332,38 +325,29 @@ mod tests { fn line_graph() -> InterferenceGraph { let mut graph: InterferenceGraph = HashMap::new(); - let v0 = IrVrVariableDescriptor::new("v0".into(), 0); - let v1 = IrVrVariableDescriptor::new("v1".into(), 0); - let v2 = IrVrVariableDescriptor::new("v2".into(), 0); // v1 -- v0 -- v2 - graph.insert(v0.clone(), HashSet::from([v1.clone(), v2.clone()])); - graph.insert(v1.clone(), HashSet::from([v0.clone()])); - graph.insert(v2.clone(), HashSet::from([v0.clone()])); + graph.insert(0, HashSet::from([1, 2])); + graph.insert(1, HashSet::from([0])); + graph.insert(2, HashSet::from([0])); graph } fn triangle_graph() -> InterferenceGraph { let mut graph: InterferenceGraph = HashMap::new(); - let v0 = IrVrVariableDescriptor::new("v0".into(), 0); - let v1 = IrVrVariableDescriptor::new("v1".into(), 0); - let v2 = IrVrVariableDescriptor::new("v2".into(), 0); // triangle: each has two edges // v0 // | \ // v1--v2 - graph.insert(v0.clone(), HashSet::from([v1.clone(), v2.clone()])); - graph.insert(v1.clone(), HashSet::from([v0.clone(), v2.clone()])); - graph.insert(v2.clone(), HashSet::from([v0.clone(), v1.clone()])); + graph.insert(0, HashSet::from([1, 2])); + graph.insert(1, HashSet::from([0, 2])); + graph.insert(2, HashSet::from([0, 1])); graph } - fn get_vrs(graph: &InterferenceGraph) -> Vec { - let v0 = graph.keys().find(|k| k.name() == "v0").unwrap(); - let v1 = graph.keys().find(|k| k.name() == "v1").unwrap(); - let v2 = graph.keys().find(|k| k.name() == "v2").unwrap(); - vec![v0.clone(), v1.clone(), v2.clone()] + fn get_vrs() -> Vec { + vec![0, 1, 2] } #[test] @@ -371,7 +355,7 @@ mod tests { let graph = line_graph(); let found = find_vr_lt_k(&graph, 2); assert!(found.is_some()); - assert!(found.unwrap().name() == "v1" || found.unwrap().name() == "v2"); + assert!(found.unwrap() == 1 || found.unwrap() == 2); } #[test] @@ -384,7 +368,7 @@ mod tests { #[test] fn remove_edges_v0() { let mut graph = line_graph(); - let vrs = get_vrs(&graph); + let vrs = get_vrs(); let v0_outgoing = remove_vr_and_edges(&mut graph, &vrs[0]); assert!(v0_outgoing.contains(&vrs[1])); @@ -437,7 +421,7 @@ mod tests { } // we should have a triangle graph again - let vrs = get_vrs(&rebuilt_graph); + let vrs = get_vrs(); for vr in &vrs { assert!(rebuilt_graph.contains_key(vr)); assert_eq!(rebuilt_graph.get(vr).unwrap().len(), 2); diff --git a/dmc-lib/src/ir/util.rs b/dmc-lib/src/ir/util.rs index eb8cde9..997c8a7 100644 --- a/dmc-lib/src/ir/util.rs +++ b/dmc-lib/src/ir/util.rs @@ -1,22 +1,15 @@ -use crate::ir::ir_variable::{ - IrStackVariableDescriptor, IrVariable, IrVariableDescriptor, IrVrVariableDescriptor, -}; -use std::cell::RefCell; +use crate::ir::ir_variable::IrVariableId; use std::collections::HashSet; -use std::rc::Rc; pub fn propagate_spills( - ir_variable: &mut Rc>, - spills: &HashSet, + target_ir_variable: IrVariableId, + register_variables: &mut HashSet, + stack_variables: &mut HashSet, + new_spills: &HashSet, ) { - let mut borrowed_ir_variable = ir_variable.borrow_mut(); - if let IrVariableDescriptor::VirtualRegister(vr_variable) = borrowed_ir_variable.descriptor() { - if spills.contains(vr_variable) { - let name = vr_variable.name_owned(); - let block_id = vr_variable.block_id(); - borrowed_ir_variable.set_descriptor(IrVariableDescriptor::Stack( - IrStackVariableDescriptor::new(name, block_id), - )); - } + if new_spills.contains(&target_ir_variable) && register_variables.contains(&target_ir_variable) + { + register_variables.remove(&target_ir_variable); + stack_variables.insert(target_ir_variable); } } diff --git a/dmc-lib/src/ir/variable_locations.rs b/dmc-lib/src/ir/variable_locations.rs new file mode 100644 index 0000000..42d69eb --- /dev/null +++ b/dmc-lib/src/ir/variable_locations.rs @@ -0,0 +1,41 @@ +use crate::ir::ir_variable::IrVariableId; +use crate::ir::register_allocation::RegisterAssignment; +use dvm_lib::instruction::StackFrameOffset; +use std::collections::HashMap; + +pub enum VariableLocation { + Register(RegisterAssignment), + Stack(StackFrameOffset), +} + +pub struct VariableLocations { + register_variables: HashMap, + stack_variables: HashMap, +} + +impl VariableLocations { + pub fn new( + register_variables: HashMap, + stack_variables: HashMap, + ) -> Self { + Self { + register_variables, + stack_variables, + } + } + + pub fn get_variable_location(&self, id: &IrVariableId) -> VariableLocation { + match self.register_variables.get(id) { + Some(register_assignment) => VariableLocation::Register(*register_assignment), + None => VariableLocation::Stack(self.stack_variables[id]), + } + } + + pub fn register_variables_count(&self) -> usize { + self.register_variables.len() + } + + pub fn stack_size(&self) -> usize { + self.stack_variables.len() + } +} diff --git a/dmc-lib/src/lib.rs b/dmc-lib/src/lib.rs index 542bc2a..5434c73 100644 --- a/dmc-lib/src/lib.rs +++ b/dmc-lib/src/lib.rs @@ -7,6 +7,7 @@ pub mod error_codes; pub mod intrinsics; pub mod ir; pub mod lexer; +pub mod lowering; pub mod offset_counter; pub mod parser; pub mod scope; diff --git a/dmc-lib/src/lowering/mod.rs b/dmc-lib/src/lowering/mod.rs new file mode 100644 index 0000000..a9da20b --- /dev/null +++ b/dmc-lib/src/lowering/mod.rs @@ -0,0 +1,426 @@ +mod util; + +use crate::ast::NodeId; +use crate::ast::assign_statement::AssignStatement; +use crate::ast::binary_expression::BinaryOperation; +use crate::ast::call::Call; +use crate::ast::compilation_unit::CompilationUnit; +use crate::ast::expression::Expression; +use crate::ast::expression_statement::ExpressionStatement; +use crate::ast::function::Function; +use crate::ast::let_statement::LetStatement; +use crate::ast::statement::Statement; +use crate::ir::ir_assign::IrAssign; +use crate::ir::ir_binary_operation::{IrBinaryOperation, IrBinaryOperator}; +use crate::ir::ir_block::{IrBlock, IrBlockId}; +use crate::ir::ir_call::IrCall; +use crate::ir::ir_expression::IrExpression; +use crate::ir::ir_function::IrFunction; +use crate::ir::ir_operation::IrOperation; +use crate::ir::ir_parameter::IrParameter; +use crate::ir::ir_return::IrReturn; +use crate::ir::ir_statement::IrStatement; +use crate::ir::ir_type_info::IrTypeInfo; +use crate::ir::ir_variable::{IrVariable, IrVariableId}; +use crate::lowering::util::{return_type_info_to_ir_type_info, to_ir_type_info}; +use crate::semantic_analysis::symbol::{Symbol, SymbolId}; +use crate::semantic_analysis::type_info::{TypeInfo, TypeInfoId}; +use std::collections::HashMap; +use std::ops::Neg; +use std::rc::Rc; + +pub struct LowerToIrResult { + pub functions: Vec, +} + +struct LowerToIrContext<'a> { + symbols: &'a [Symbol], + nodes_to_symbols: &'a HashMap, + type_infos: &'a [TypeInfo], + symbols_to_type_infos: &'a HashMap, + nodes_to_type_infos: &'a HashMap, + ir_functions: Vec, +} + +pub fn lower_to_ir( + compilation_unit: &CompilationUnit, + symbols: &[Symbol], + nodes_to_symbols: &HashMap, + type_infos: &[TypeInfo], + symbols_to_type_infos: &HashMap, + nodes_to_type_infos: &HashMap, +) -> LowerToIrResult { + let mut ctx = LowerToIrContext { + symbols, + nodes_to_symbols, + type_infos, + symbols_to_type_infos, + nodes_to_type_infos, + ir_functions: Vec::new(), + }; + + for function in compilation_unit.functions() { + lower_to_ir_function(function, &mut ctx); + } + + LowerToIrResult { + functions: ctx.ir_functions, + } +} + +struct LowerToIrFunctionContext { + blocks: Vec, + ir_variables: Vec, + symbols_to_variables: HashMap, + current_block_statements: Vec, + t_var_counter: usize, +} + +impl LowerToIrFunctionContext { + fn new() -> Self { + Self { + blocks: Vec::new(), + ir_variables: Vec::new(), + symbols_to_variables: HashMap::new(), + current_block_statements: Vec::new(), + t_var_counter: 0, + } + } + + fn current_block_id(&self) -> IrBlockId { + self.blocks.len() + } + + fn finish_block(&mut self) { + let current_block_id = self.current_block_id(); + let ir_block = IrBlock::new( + current_block_id, + &format!("#{}", current_block_id), + std::mem::take(&mut self.current_block_statements), + ); + self.blocks.push(ir_block); + } + + fn insert_ir_variable( + &mut self, + ir_variable: IrVariable, + maybe_associated_symbol_id: Option, + ) -> IrVariableId { + self.ir_variables.push(ir_variable); + let ir_variable_id = self.ir_variables.len() - 1; + if let Some(associated_symbol_id) = maybe_associated_symbol_id { + self.symbols_to_variables + .insert(associated_symbol_id, ir_variable_id); + } + ir_variable_id + } + + fn make_t_var(&mut self, ir_type_info: IrTypeInfo) -> IrVariableId { + let t_var_id = self.t_var_counter; + self.t_var_counter += 1; + let t_var_ir_variable = IrVariable::new(&format!("t_{}", t_var_id), ir_type_info); + self.insert_ir_variable(t_var_ir_variable, None) + } +} + +fn lower_to_ir_function(function: &Function, ctx: &mut LowerToIrContext) { + fn lower_to_ir_parameters(function: &Function, ctx: &mut LowerToIrContext) -> Vec { + let mut ir_parameters = Vec::new(); + let n_parameters = function.parameters().len() as isize; + for (i, parameter) in function.parameters().iter().enumerate() { + let parameter_type_info_id = ctx.nodes_to_type_infos[¶meter.node_id()]; + let parameter_type_info = &ctx.type_infos[parameter_type_info_id]; + let ir_parameter = IrParameter::new( + parameter.declared_name(), + to_ir_type_info(parameter_type_info), + n_parameters.neg() + (i as isize), + ); + ir_parameters.push(ir_parameter); + } + ir_parameters + } + + let mut fn_ctx = LowerToIrFunctionContext::new(); + + let n_statements = function.statements().len(); + for (i, statement) in function.statements().iter().enumerate() { + let can_return_value = i == n_statements - 1; + lower_to_ir_statement(statement, ctx, &mut fn_ctx, can_return_value); + } + + fn_ctx.finish_block(); + + let function_symbol_id = ctx.nodes_to_symbols[&function.node_id()]; + let function_symbol = match &ctx.symbols[function_symbol_id] { + Symbol::Function(function_symbol) => function_symbol, + _ => panic!("Expected function symbol"), + }; + + let return_type_info_id = ctx.symbols_to_type_infos[&function_symbol_id]; + let return_type_info = &ctx.type_infos[return_type_info_id]; + + let ir_function = IrFunction::new( + function_symbol.fqn_owned(), + lower_to_ir_parameters(function, ctx) + .into_iter() + .map(|ir_parameter| Rc::new(ir_parameter)) + .collect(), + fn_ctx.ir_variables, + return_type_info_to_ir_type_info(return_type_info), + fn_ctx.blocks, + ); + + ctx.ir_functions.push(ir_function); +} + +fn lower_to_ir_statement( + statement: &Statement, + ctx: &LowerToIrContext, + fn_ctx: &mut LowerToIrFunctionContext, + can_return_value: bool, +) { + match statement { + Statement::Let(let_statement) => { + lower_to_ir_let_statement(let_statement, ctx, fn_ctx); + } + Statement::Expression(expression_statement) => { + lower_to_ir_expression_statement(expression_statement, ctx, fn_ctx, can_return_value); + } + Statement::Assign(assign_statement) => { + lower_to_ir_assign_statement(assign_statement, ctx, fn_ctx); + } + } +} + +fn lower_binary_operator(binary_operation: &BinaryOperation) -> IrBinaryOperator { + match binary_operation { + BinaryOperation::Multiply => IrBinaryOperator::Multiply, + BinaryOperation::Divide => IrBinaryOperator::Divide, + BinaryOperation::Modulo => IrBinaryOperator::Modulo, + BinaryOperation::Add => IrBinaryOperator::Add, + BinaryOperation::Subtract => IrBinaryOperator::Subtract, + BinaryOperation::LeftShift => IrBinaryOperator::LeftShift, + BinaryOperation::RightShift => IrBinaryOperator::RightShift, + BinaryOperation::BitwiseAnd => IrBinaryOperator::BitwiseAnd, + BinaryOperation::BitwiseXor => IrBinaryOperator::BitwiseXor, + BinaryOperation::BitwiseOr => IrBinaryOperator::BitwiseOr, + } +} + +fn lower_to_ir_let_statement( + let_statement: &LetStatement, + ctx: &LowerToIrContext, + fn_ctx: &mut LowerToIrFunctionContext, +) { + let symbol_id = ctx.nodes_to_symbols[&let_statement.node_id()]; + + let type_info_id = ctx.nodes_to_type_infos[&let_statement.node_id()]; + let type_info = &ctx.type_infos[type_info_id]; + + let ir_variable = IrVariable::new(let_statement.declared_name(), to_ir_type_info(type_info)); + let ir_variable_id = fn_ctx.insert_ir_variable(ir_variable, Some(symbol_id)); + + let initializer_ir_operation = + lower_expression_to_ir_operation(let_statement.initializer(), ctx, fn_ctx); + + let ir_assign = IrAssign::new(ir_variable_id, initializer_ir_operation); + let ir_statement = IrStatement::Assign(ir_assign); + fn_ctx.current_block_statements.push(ir_statement); +} + +fn lower_to_ir_expression_statement( + expression_statement: &ExpressionStatement, + ctx: &LowerToIrContext, + fn_ctx: &mut LowerToIrFunctionContext, + returns_value: bool, +) { + if returns_value { + let ir_expression = + lower_expression_to_ir_expression(expression_statement.expression(), ctx, fn_ctx); + let ir_statement = IrStatement::Return(IrReturn::new(Some(ir_expression))); + fn_ctx.current_block_statements.push(ir_statement); + } else { + // we could try to filter out useless code, but it's not guaranteed that there aren't + // intended side effects from the lowering, so, for now, let's throw the result in a t_var. + // in the future we may want to somehow enforce with the type system a way that we can't + // create useless, dead ir code + let ir_operation = + lower_expression_to_ir_operation(expression_statement.expression(), ctx, fn_ctx); + + let result_type_info_id = + ctx.nodes_to_type_infos[&expression_statement.expression().node_id()]; + let result_type_info = &ctx.type_infos[result_type_info_id]; + let t_var_ir_variable_id = fn_ctx.make_t_var(to_ir_type_info(result_type_info)); + + let ir_statement = IrStatement::Assign(IrAssign::new(t_var_ir_variable_id, ir_operation)); + fn_ctx.current_block_statements.push(ir_statement); + } +} + +fn lower_to_ir_assign_statement( + assign_statement: &AssignStatement, + ctx: &LowerToIrContext, + fn_ctx: &mut LowerToIrFunctionContext, +) { + match assign_statement.destination() { + Expression::Identifier(identifier) => { + let destination_symbol_id = ctx.nodes_to_symbols[&identifier.node_id()]; + let destination_ir_variable_id = fn_ctx.symbols_to_variables[&destination_symbol_id]; + + let ir_operation = + lower_expression_to_ir_operation(assign_statement.value(), ctx, fn_ctx); + let ir_assign = IrAssign::new(destination_ir_variable_id, ir_operation); + let ir_statement = IrStatement::Assign(ir_assign); + fn_ctx.current_block_statements.push(ir_statement); + } + _ => panic!("Assigning to non-Identifiers is not yet supported."), + } +} + +fn lower_expression_to_ir_operation( + expression: &Expression, + ctx: &LowerToIrContext, + fn_ctx: &mut LowerToIrFunctionContext, +) -> IrOperation { + match expression { + Expression::Binary(binary_expression) => { + let lhs = lower_expression_to_ir_expression(binary_expression.lhs(), ctx, fn_ctx); + let rhs = lower_expression_to_ir_expression(binary_expression.rhs(), ctx, fn_ctx); + IrOperation::Binary(IrBinaryOperation::new( + lhs, + rhs, + lower_binary_operator(binary_expression.op()), + )) + } + Expression::Negative(negative_expression) => { + let operand = + lower_expression_to_ir_expression(negative_expression.operand(), ctx, fn_ctx); + IrOperation::Binary(IrBinaryOperation::new( + operand, + IrExpression::Int(-1), + IrBinaryOperator::Multiply, + )) + } + Expression::Call(call) => IrOperation::Call(lower_to_ir_call(call, ctx, fn_ctx)), + Expression::Identifier(identifier) => { + let identifier_symbol_id = ctx.nodes_to_symbols[&identifier.node_id()]; + let identifier_ir_variable_id = fn_ctx.symbols_to_variables[&identifier_symbol_id]; + let ir_expression = IrExpression::Variable(identifier_ir_variable_id); + IrOperation::Load(ir_expression) + } + Expression::Integer(integer_literal) => { + IrOperation::Load(IrExpression::Int(integer_literal.value())) + } + Expression::Double(double_literal) => { + IrOperation::Load(IrExpression::Double(double_literal.value())) + } + Expression::String(string_literal) => { + IrOperation::Load(IrExpression::String(string_literal.content().into())) + } + } +} + +fn lower_expression_to_ir_expression( + expression: &Expression, + ctx: &LowerToIrContext, + fn_ctx: &mut LowerToIrFunctionContext, +) -> IrExpression { + match expression { + Expression::Binary(binary_expression) => { + // make binary operation + let lhs = lower_expression_to_ir_expression(binary_expression.lhs(), ctx, fn_ctx); + let rhs = lower_expression_to_ir_expression(binary_expression.rhs(), ctx, fn_ctx); + let ir_operation = IrOperation::Binary(IrBinaryOperation::new( + lhs, + rhs, + lower_binary_operator(binary_expression.op()), + )); + + // make destination temp var + let result_type_info_id = ctx.nodes_to_type_infos[&binary_expression.node_id()]; + let result_type_info = &ctx.type_infos[result_type_info_id]; + let destination_ir_variable_id = fn_ctx.make_t_var(to_ir_type_info(result_type_info)); + + // make assign statement to destination temp var + let ir_assign = IrAssign::new(destination_ir_variable_id, ir_operation); + fn_ctx + .current_block_statements + .push(IrStatement::Assign(ir_assign)); + + // return location of temp var + IrExpression::Variable(destination_ir_variable_id) + } + Expression::Negative(negative_expression) => { + let operand = + lower_expression_to_ir_expression(negative_expression.operand(), ctx, fn_ctx); + let negative_one = IrExpression::Int(-1); + let ir_operation = IrOperation::Binary(IrBinaryOperation::new( + operand, + negative_one, + IrBinaryOperator::Multiply, + )); + let result_type_info_id = ctx.nodes_to_type_infos[&negative_expression.node_id()]; + let result_type_info = &ctx.type_infos[result_type_info_id]; + let destination_ir_variable_id = fn_ctx.make_t_var(to_ir_type_info(result_type_info)); + let ir_assign = IrAssign::new(destination_ir_variable_id, ir_operation); + + // push the statement which does the multiply by negative one + fn_ctx + .current_block_statements + .push(IrStatement::Assign(ir_assign)); + + IrExpression::Variable(destination_ir_variable_id) + } + Expression::Call(call) => { + let ir_call = lower_to_ir_call(call, ctx, fn_ctx); + + // make temp var + let return_type_info_id = ctx.nodes_to_type_infos[&call.node_id()]; + let return_type_info = &ctx.type_infos[return_type_info_id]; + let return_ir_type_info = to_ir_type_info(return_type_info); + let t_var_ir_variable_id = fn_ctx.make_t_var(return_ir_type_info); + + // assign call to temp var, return temp var expression + let ir_operation = IrOperation::Call(ir_call); + let ir_assign = IrAssign::new(t_var_ir_variable_id, ir_operation); + fn_ctx + .current_block_statements + .push(IrStatement::Assign(ir_assign)); + + // return an expression referencing the temp var + IrExpression::Variable(t_var_ir_variable_id) + } + Expression::Identifier(identifier) => { + let rhs_symbol_id = ctx.nodes_to_symbols[&identifier.node_id()]; + let rhs_ir_variable_id = fn_ctx.symbols_to_variables[&rhs_symbol_id]; + IrExpression::Variable(rhs_ir_variable_id) + } + Expression::Integer(integer_literal) => IrExpression::Int(integer_literal.value()), + Expression::Double(double_literal) => IrExpression::Double(double_literal.value()), + Expression::String(string_literal) => IrExpression::String(string_literal.content().into()), + } +} + +fn lower_to_ir_call( + call: &Call, + ctx: &LowerToIrContext, + fn_ctx: &mut LowerToIrFunctionContext, +) -> IrCall { + let callee_symbol_id = ctx.nodes_to_symbols[&call.callee().node_id()]; + let callee_symbol = &ctx.symbols[callee_symbol_id]; + match callee_symbol { + Symbol::Function(function_symbol) => { + let arguments = call + .arguments() + .iter() + .map(|e| lower_expression_to_ir_expression(e, ctx, fn_ctx)) + .collect(); + IrCall::new( + function_symbol.fqn_owned(), + arguments, + function_symbol.is_extern(), + ) + } + _ => panic!("Expected function symbol"), + } +} diff --git a/dmc-lib/src/lowering/util.rs b/dmc-lib/src/lowering/util.rs new file mode 100644 index 0000000..71b661e --- /dev/null +++ b/dmc-lib/src/lowering/util.rs @@ -0,0 +1,23 @@ +use crate::ir::ir_type_info::IrTypeInfo; +use crate::semantic_analysis::type_info::TypeInfo; + +pub fn to_ir_type_info(sa_type_info: &TypeInfo) -> IrTypeInfo { + match sa_type_info { + TypeInfo::String => IrTypeInfo::String, + TypeInfo::Int => IrTypeInfo::Int, + TypeInfo::Double => IrTypeInfo::Double, + _ => { + panic!() + } + } +} + +pub fn return_type_info_to_ir_type_info(return_type_info: &TypeInfo) -> Option { + match return_type_info { + TypeInfo::String | TypeInfo::Int | TypeInfo::Double => { + Some(to_ir_type_info(return_type_info)) + } + TypeInfo::Void => None, + _ => panic!(), + } +} diff --git a/dmc-lib/src/parser.rs b/dmc-lib/src/parser.rs index 8581fcd..d10fd6c 100644 --- a/dmc-lib/src/parser.rs +++ b/dmc-lib/src/parser.rs @@ -1674,7 +1674,7 @@ mod concrete_tests { } let arguments = call.arguments(); assert_eq!(arguments.len(), 1); - let first_argument = arguments[0]; + let first_argument = &arguments[0]; match first_argument { Expression::String(s) => { assert_eq!(s.content(), "Hello, World!"); diff --git a/dmc-lib/src/semantic_analysis/collect_scopes.rs b/dmc-lib/src/semantic_analysis/collect_scopes.rs index 57f2048..722068e 100644 --- a/dmc-lib/src/semantic_analysis/collect_scopes.rs +++ b/dmc-lib/src/semantic_analysis/collect_scopes.rs @@ -14,7 +14,12 @@ use crate::ast::statement::Statement; use crate::semantic_analysis::scope::{Scope, ScopeId}; use std::collections::HashMap; -pub struct ScopeCollectionContext { +pub struct ScopeCollectionResult { + pub scopes: Vec, + pub nodes_to_scopes: HashMap, +} + +struct ScopeCollectionContext { scopes: Vec, current_scope_id: Option, nodes_to_scopes: HashMap, @@ -36,24 +41,19 @@ impl ScopeCollectionContext { } pub fn pop_scope(&mut self) { - let popped_scope = self.scopes.pop().unwrap(); - self.current_scope_id = popped_scope.parent_id(); + self.current_scope_id = self.scopes[self.current_scope_id.unwrap()].parent_id(); } pub fn current_scope_id(&self) -> Option { self.current_scope_id } - pub fn nodes_to_scopes(&self) -> &HashMap { - &self.nodes_to_scopes - } - pub fn nodes_to_scopes_mut(&mut self) -> &mut HashMap { &mut self.nodes_to_scopes } } -pub fn collect_scopes(compilation_unit: &CompilationUnit) -> ScopeCollectionContext { +pub fn collect_scopes(compilation_unit: &CompilationUnit) -> ScopeCollectionResult { let mut ctx = ScopeCollectionContext::new(); ctx.push_scope(Scope::new(None)); for function in compilation_unit.functions() { @@ -63,7 +63,10 @@ pub fn collect_scopes(compilation_unit: &CompilationUnit) -> ScopeCollectionCont collect_scopes_extern_function(extern_function, &mut ctx); } ctx.pop_scope(); - ctx + ScopeCollectionResult { + scopes: ctx.scopes, + nodes_to_scopes: ctx.nodes_to_scopes, + } } fn collect_scopes_function(function: &Function, ctx: &mut ScopeCollectionContext) { diff --git a/dmc-lib/src/semantic_analysis/collect_symbols.rs b/dmc-lib/src/semantic_analysis/collect_symbols.rs index 2531bef..c922276 100644 --- a/dmc-lib/src/semantic_analysis/collect_symbols.rs +++ b/dmc-lib/src/semantic_analysis/collect_symbols.rs @@ -8,19 +8,27 @@ use crate::semantic_analysis::diagnostic_helpers::symbol_already_declared; use crate::semantic_analysis::scope::{Scope, ScopeId}; use crate::semantic_analysis::symbol::{FunctionSymbol, ParameterSymbol, Symbol}; use std::collections::HashMap; +use std::rc::Rc; -pub struct SymbolCollectionContext { - scopes: Vec, - nodes_to_scopes: HashMap, +pub struct SymbolCollectionResult { + pub symbols: Vec, + pub diagnostics: Diagnostics, +} + +struct SymbolCollectionContext<'a> { + scopes: &'a mut Vec, + nodes_to_scopes: &'a HashMap, + fqn_context: Vec>, symbols: Vec, diagnostics: Diagnostics, } -impl SymbolCollectionContext { - pub fn new(scopes: Vec, nodes_to_scopes: HashMap) -> Self { +impl<'a> SymbolCollectionContext<'a> { + pub fn new(scopes: &'a mut Vec, nodes_to_scopes: &'a HashMap) -> Self { Self { scopes, nodes_to_scopes, + fqn_context: Vec::new(), symbols: Vec::new(), diagnostics: Diagnostics::new(), } @@ -45,26 +53,56 @@ impl SymbolCollectionContext { scope.symbols_mut().insert(declared_name, symbol_id); } + pub fn symbols_mut(&mut self) -> &mut Vec { + &mut self.symbols + } + pub fn diagnostics_mut(&mut self) -> &mut Diagnostics { &mut self.diagnostics } + + fn join_fqn_parts(parts: &[Rc]) -> String { + parts.join("::") + } + + pub fn get_fqn_base(&self) -> String { + Self::join_fqn_parts(&self.fqn_context) + } + + pub fn resolve_fqn(&self, suffix: &Rc) -> String { + Self::join_fqn_parts(&[self.get_fqn_base().into(), suffix.clone()]) + } } -pub fn collect_symbols(compilation_unit: &CompilationUnit, ctx: &mut SymbolCollectionContext) { +pub fn collect_symbols( + compilation_unit: &CompilationUnit, + scopes: &mut Vec, + nodes_to_scopes: &HashMap, +) -> SymbolCollectionResult { + let mut ctx = SymbolCollectionContext::new(scopes, nodes_to_scopes); + for function in compilation_unit.functions() { - collect_symbols_function(function, ctx); + collect_symbols_function(function, &mut ctx); } for extern_function in compilation_unit.extern_functions() { - collect_symbols_extern_function(extern_function, ctx); + collect_symbols_extern_function(extern_function, &mut ctx); + } + + SymbolCollectionResult { + symbols: ctx.symbols, + diagnostics: ctx.diagnostics, } } fn collect_symbols_function(function: &Function, ctx: &mut SymbolCollectionContext) { + let fqn = ctx.resolve_fqn(&function.declared_name_owned()).into(); + // function itself let function_symbol = Symbol::Function(FunctionSymbol::new( function.declared_name_owned(), Some(function.declared_name_source_range()), + fqn, false, )); @@ -90,10 +128,15 @@ fn collect_symbols_extern_function( extern_function: &ExternFunction, ctx: &mut SymbolCollectionContext, ) { + let fqn = ctx + .resolve_fqn(&extern_function.declared_name_owned()) + .into(); + // function itself let function_symbol = Symbol::Function(FunctionSymbol::new( extern_function.declared_name_owned(), Some(extern_function.declared_name_source_range()), + fqn, true, )); diff --git a/dmc-lib/src/semantic_analysis/collect_types.rs b/dmc-lib/src/semantic_analysis/collect_types.rs new file mode 100644 index 0000000..6f38f9e --- /dev/null +++ b/dmc-lib/src/semantic_analysis/collect_types.rs @@ -0,0 +1,116 @@ +use crate::ast::NodeId; +use crate::ast::compilation_unit::CompilationUnit; +use crate::ast::extern_function::ExternFunction; +use crate::ast::function::Function; +use crate::ast::parameter::Parameter; +use crate::semantic_analysis::symbol::SymbolId; +use crate::semantic_analysis::type_info::{FunctionTypeInfo, TypeInfo, TypeInfoId}; +use std::collections::HashMap; + +pub struct CollectTypesResult { + pub type_infos: Vec, + pub symbols_to_type_infos: HashMap, +} + +struct CollectTypesContext<'a> { + nodes_to_symbols: &'a HashMap, + type_infos: Vec, + symbols_to_type_infos: HashMap, +} + +impl<'a> CollectTypesContext<'a> { + pub fn new(nodes_to_symbols: &'a HashMap) -> Self { + Self { + nodes_to_symbols, + type_infos: Vec::new(), + symbols_to_type_infos: HashMap::new(), + } + } + + pub fn insert_type_info(&mut self, type_info: TypeInfo) -> TypeInfoId { + self.type_infos.push(type_info); + self.type_infos.len() - 1 + } +} + +pub fn collect_types( + compilation_unit: &CompilationUnit, + nodes_to_symbols: &HashMap, +) -> CollectTypesResult { + let mut ctx = CollectTypesContext::new(nodes_to_symbols); + for function in compilation_unit.functions() { + collect_types_function(function, &mut ctx); + } + + for extern_function in compilation_unit.extern_functions() { + collect_types_extern_function(extern_function, &mut ctx); + } + + CollectTypesResult { + type_infos: ctx.type_infos, + symbols_to_type_infos: ctx.symbols_to_type_infos, + } +} + +fn collect_types_function(function: &Function, ctx: &mut CollectTypesContext) { + let parameter_type_info_ids = get_parameter_type_info_ids(function.parameters(), ctx); + + let return_type_info = match function.return_type() { + None => TypeInfo::Void, + Some(type_use) => declared_name_to_type_info(type_use.declared_name()), + }; + let return_type_info_id = ctx.insert_type_info(return_type_info); + + let function_type_info = TypeInfo::Function(FunctionTypeInfo::new( + parameter_type_info_ids, + return_type_info_id, + )); + let function_type_info_id = ctx.insert_type_info(function_type_info); + + let symbol_id = ctx.nodes_to_symbols[&function.node_id()]; + ctx.symbols_to_type_infos + .insert(symbol_id, function_type_info_id); +} + +fn declared_name_to_type_info(declared_name: &str) -> TypeInfo { + match declared_name { + "Any" => TypeInfo::Any, + "String" => TypeInfo::String, + "Int" => TypeInfo::Int, + "Double" => TypeInfo::Double, + "Void" => TypeInfo::Void, + _ => TypeInfo::__Error, + } +} + +fn get_parameter_type_info_ids( + parameters: &[Parameter], + ctx: &mut CollectTypesContext, +) -> Vec { + let mut parameter_type_info_ids: Vec = Vec::new(); + for parameter in parameters { + let type_info = declared_name_to_type_info(parameter.type_use().declared_name()); + let type_info_id = ctx.insert_type_info(type_info); + parameter_type_info_ids.push(type_info_id); + } + parameter_type_info_ids +} + +fn collect_types_extern_function(extern_function: &ExternFunction, ctx: &mut CollectTypesContext) { + let parameter_type_info_ids = get_parameter_type_info_ids(extern_function.parameters(), ctx); + + let return_type_info = + declared_name_to_type_info(extern_function.return_type().declared_name()); + let return_type_info_id = ctx.insert_type_info(return_type_info); + + let function_type_info = TypeInfo::Function(FunctionTypeInfo::new( + parameter_type_info_ids, + return_type_info_id, + )); + + let function_type_info_id = ctx.insert_type_info(function_type_info); + + let symbol_id = ctx.nodes_to_symbols[&extern_function.node_id()]; + ctx.symbols_to_type_infos + .insert(symbol_id, function_type_info_id); +} diff --git a/dmc-lib/src/semantic_analysis/mod.rs b/dmc-lib/src/semantic_analysis/mod.rs index 0e178a2..2e84180 100644 --- a/dmc-lib/src/semantic_analysis/mod.rs +++ b/dmc-lib/src/semantic_analysis/mod.rs @@ -1,7 +1,112 @@ +use crate::ast::NodeId; +use crate::ast::compilation_unit::CompilationUnit; +use crate::compile_pipeline::FileId; +use crate::diagnostic::Diagnostics; +use crate::semantic_analysis::collect_scopes::collect_scopes; +use crate::semantic_analysis::collect_symbols::collect_symbols; +use crate::semantic_analysis::collect_types::collect_types; +use crate::semantic_analysis::resolve_names::resolve_names; +use crate::semantic_analysis::resolve_types::resolve_types; +use crate::semantic_analysis::scope::{Scope, ScopeId}; +use crate::semantic_analysis::symbol::{Symbol, SymbolId}; +use crate::semantic_analysis::type_info::{TypeInfo, TypeInfoId}; +use std::collections::HashMap; + mod collect_scopes; mod collect_symbols; +mod collect_types; mod diagnostic_helpers; mod resolve_names; +mod resolve_types; mod scope; mod semantic_context; -mod symbol; +pub mod symbol; +pub mod type_info; + +pub struct AnalysisResult { + pub type_infos: Vec, + pub nodes_to_type_infos: HashMap, +} + +pub fn analyze( + compilation_units: &HashMap, +) -> HashMap { + let mut diagnostics = Diagnostics::new(); + + let mut files_to_scopes: HashMap> = HashMap::new(); + let mut files_to_nodes_to_scopes: HashMap> = HashMap::new(); + + for (file_id, compilation_unit) in compilation_units { + let result = collect_scopes(compilation_unit); + files_to_scopes.insert(*file_id, result.scopes); + files_to_nodes_to_scopes.insert(*file_id, result.nodes_to_scopes); + } + + let mut files_to_symbols: HashMap> = HashMap::new(); + + for (file_id, compilation_unit) in compilation_units { + let scopes = files_to_scopes.get_mut(file_id).unwrap(); + let nodes_to_scopes = files_to_nodes_to_scopes.get(file_id).unwrap(); + let mut result = collect_symbols(compilation_unit, scopes, nodes_to_scopes); + files_to_symbols.insert(*file_id, result.symbols); + diagnostics.append(&mut result.diagnostics); + } + + let mut files_to_nodes_to_symbols: HashMap> = HashMap::new(); + + for (file_id, compilation_unit) in compilation_units { + let scopes = files_to_scopes.get_mut(file_id).unwrap(); + let symbols = files_to_symbols.get_mut(file_id).unwrap(); + let nodes_to_scopes = files_to_nodes_to_scopes.get(file_id).unwrap(); + + let mut result = resolve_names(compilation_unit, scopes, symbols, nodes_to_scopes); + + files_to_nodes_to_symbols.insert(*file_id, result.nodes_to_symbols); + diagnostics.append(&mut result.diagnostics); + } + + let mut files_to_type_infos: HashMap> = HashMap::new(); + let mut files_to_symbols_to_type_infos: HashMap> = + HashMap::new(); + + for (file_id, compilation_unit) in compilation_units { + let nodes_to_symbols = files_to_nodes_to_symbols.get(&file_id).unwrap(); + let result = collect_types(compilation_unit, nodes_to_symbols); + + files_to_type_infos.insert(*file_id, result.type_infos); + files_to_symbols_to_type_infos.insert(*file_id, result.symbols_to_type_infos); + } + + let mut files_to_nodes_to_type_infos: HashMap> = + HashMap::new(); + + for (file_id, compilation_unit) in compilation_units { + let nodes_to_symbols = files_to_nodes_to_symbols.get_mut(file_id).unwrap(); + let type_infos = files_to_type_infos.get_mut(file_id).unwrap(); + let symbols_to_type_infos = files_to_symbols_to_type_infos.get_mut(file_id).unwrap(); + + let mut result = resolve_types( + compilation_unit, + nodes_to_symbols, + type_infos, + symbols_to_type_infos, + ); + + files_to_nodes_to_type_infos.insert(*file_id, result.nodes_to_type_infos); + diagnostics.append(&mut result.diagnostics); + } + + let mut files_to_results: HashMap = HashMap::new(); + + for (file_id, _) in compilation_units { + let type_infos = files_to_type_infos.remove(file_id).unwrap(); + let nodes_to_type_infos = files_to_nodes_to_type_infos.remove(file_id).unwrap(); + let analysis_result = AnalysisResult { + type_infos, + nodes_to_type_infos, + }; + files_to_results.insert(*file_id, analysis_result); + } + + files_to_results +} diff --git a/dmc-lib/src/semantic_analysis/resolve_names.rs b/dmc-lib/src/semantic_analysis/resolve_names.rs index 816982b..e413605 100644 --- a/dmc-lib/src/semantic_analysis/resolve_names.rs +++ b/dmc-lib/src/semantic_analysis/resolve_names.rs @@ -16,19 +16,24 @@ use crate::semantic_analysis::scope::{Scope, ScopeId}; use crate::semantic_analysis::symbol::{Symbol, SymbolId, VariableSymbol}; use std::collections::HashMap; -pub struct NameResolutionContext { - scopes: Vec, - symbols: Vec, - nodes_to_scopes: HashMap, +pub struct NameResolutionResult { + pub nodes_to_symbols: HashMap, + pub diagnostics: Diagnostics, +} + +struct NameResolutionContext<'a> { + scopes: &'a mut Vec, + symbols: &'a mut Vec, + nodes_to_scopes: &'a HashMap, nodes_to_symbols: HashMap, diagnostics: Diagnostics, } -impl NameResolutionContext { +impl<'a> NameResolutionContext<'a> { pub fn new( - scopes: Vec, - symbols: Vec, - nodes_to_scopes: HashMap, + scopes: &'a mut Vec, + symbols: &'a mut Vec, + nodes_to_scopes: &'a HashMap, ) -> Self { Self { scopes, @@ -81,9 +86,21 @@ enum ExpressionResolutionPhase { Static, } -pub fn resolve_names(compilation_unit: &CompilationUnit, ctx: &mut NameResolutionContext) { +pub fn resolve_names( + compilation_unit: &CompilationUnit, + scopes: &mut Vec, + symbols: &mut Vec, + nodes_to_scopes: &HashMap, +) -> NameResolutionResult { + let mut ctx = NameResolutionContext::new(scopes, symbols, nodes_to_scopes); + for function in compilation_unit.functions() { - resolve_names_function(function, ctx); + resolve_names_function(function, &mut ctx); + } + + NameResolutionResult { + nodes_to_symbols: ctx.nodes_to_symbols, + diagnostics: ctx.diagnostics, } } @@ -129,6 +146,7 @@ fn resolve_names_let_statement( let symbol = Symbol::Variable(VariableSymbol::new( let_statement.declared_name_owned(), Some(let_statement.declared_name_source_range()), + let_statement.is_mut(), )); // the following could be a method, but this is probably the only place in this phase // where we are pushing symbols still @@ -228,7 +246,7 @@ fn resolve_names_identifier( ) { match phase { ExpressionResolutionPhase::Static => { - let scope_id = ctx.nodes_to_scopes()[&identifier.scope_id()]; + let scope_id = ctx.nodes_to_scopes()[&identifier.node_id()]; let mut maybe_scope = Some(&ctx.scopes()[scope_id]); let mut found_symbol_id: Option = None; while let Some(scope) = maybe_scope { diff --git a/dmc-lib/src/semantic_analysis/resolve_types/mod.rs b/dmc-lib/src/semantic_analysis/resolve_types/mod.rs new file mode 100644 index 0000000..d46a302 --- /dev/null +++ b/dmc-lib/src/semantic_analysis/resolve_types/mod.rs @@ -0,0 +1,336 @@ +mod type_analysis; + +use crate::ast::NodeId; +use crate::ast::assign_statement::AssignStatement; +use crate::ast::binary_expression::{BinaryExpression, BinaryOperation}; +use crate::ast::call::Call; +use crate::ast::compilation_unit::CompilationUnit; +use crate::ast::expression::Expression; +use crate::ast::function::Function; +use crate::ast::identifier::Identifier; +use crate::ast::let_statement::LetStatement; +use crate::ast::negative_expression::NegativeExpression; +use crate::ast::statement::Statement; +use crate::diagnostic::{Diagnostic, Diagnostics}; +use crate::error_codes::BINARY_INCOMPATIBLE_TYPES; +use crate::semantic_analysis::resolve_types::type_analysis::{ + are_binary_op_compatible, binary_op_result, can_assign_right_to_left, can_negate, negate_result, +}; +use crate::semantic_analysis::symbol::SymbolId; +use crate::semantic_analysis::type_info::{TypeInfo, TypeInfoId}; +use std::collections::HashMap; + +pub struct ResolveTypesResult { + pub nodes_to_type_infos: HashMap, + pub diagnostics: Diagnostics, +} + +struct ResolveTypesContext<'a> { + nodes_to_symbols: &'a HashMap, + type_infos: &'a mut Vec, + symbols_to_type_infos: &'a mut HashMap, + nodes_to_type_infos: HashMap, + diagnostics: Diagnostics, +} + +impl<'a> ResolveTypesContext<'a> { + pub fn new( + nodes_to_symbols: &'a HashMap, + type_infos: &'a mut Vec, + symbols_to_type_infos: &'a mut HashMap, + ) -> Self { + Self { + nodes_to_symbols, + type_infos, + symbols_to_type_infos, + nodes_to_type_infos: HashMap::new(), + diagnostics: Diagnostics::new(), + } + } +} + +pub fn resolve_types( + compilation_unit: &CompilationUnit, + nodes_to_symbols: &HashMap, + type_infos: &mut Vec, + symbols_to_type_infos: &mut HashMap, +) -> ResolveTypesResult { + let mut ctx = ResolveTypesContext::new(nodes_to_symbols, type_infos, symbols_to_type_infos); + for function in compilation_unit.functions() { + resolve_types_function(function, &mut ctx); + } + + ResolveTypesResult { + nodes_to_type_infos: ctx.nodes_to_type_infos, + diagnostics: ctx.diagnostics, + } +} + +fn resolve_types_function(function: &Function, ctx: &mut ResolveTypesContext) { + for statement in function.statements() { + resolve_types_statement(statement, ctx); + } +} + +fn resolve_types_statement(statement: &Statement, ctx: &mut ResolveTypesContext) { + match statement { + Statement::Let(let_statement) => { + resolve_types_let_statement(let_statement, ctx); + } + Statement::Expression(expression_statement) => { + resolve_types_expression(expression_statement.expression(), ctx); + } + Statement::Assign(assign_statement) => { + resolve_types_assign_statement(assign_statement, ctx); + } + } +} + +fn resolve_types_let_statement(let_statement: &LetStatement, ctx: &mut ResolveTypesContext) { + resolve_types_expression(let_statement.initializer(), ctx); + + let initializer_type_info_id = ctx.nodes_to_type_infos[&let_statement.initializer().node_id()]; + + // update symbol's type + let symbol_id = ctx.nodes_to_symbols[&let_statement.node_id()]; + ctx.symbols_to_type_infos + .insert(symbol_id, initializer_type_info_id); +} + +fn resolve_types_assign_statement( + assign_statement: &AssignStatement, + ctx: &mut ResolveTypesContext, +) { + resolve_types_expression(assign_statement.value(), ctx); + resolve_types_expression(assign_statement.destination(), ctx); + + // check assignability + let value_type_info_id = ctx.nodes_to_type_infos[&assign_statement.value().node_id()]; + let value_type_info = &ctx.type_infos[value_type_info_id]; + + let destination_type_info_id = + ctx.nodes_to_type_infos[&assign_statement.destination().node_id()]; + let destination_type_info = &ctx.type_infos[destination_type_info_id]; + + if !can_assign_right_to_left(destination_type_info, value_type_info) { + let message = format!( + "Incompatible types: cannot assign {} from {}", + destination_type_info, value_type_info + ); + let diagnostic = Diagnostic::new( + &message, + assign_statement.destination().source_range().start(), + assign_statement.destination().source_range().end(), + ); + ctx.diagnostics.push(diagnostic); + } +} + +fn resolve_types_expression(expression: &Expression, ctx: &mut ResolveTypesContext) { + match expression { + Expression::Binary(binary_expression) => { + resolve_types_binary_expression(binary_expression, ctx); + } + Expression::Negative(negative_expression) => { + resolve_types_negative_expression(negative_expression, ctx); + } + Expression::Call(call) => { + resolve_types_call(call, ctx); + } + Expression::Identifier(identifier) => { + resolve_types_identifier(identifier, ctx); + } + Expression::Integer(integer_literal) => { + // yes, this is slightly wasteful now, but keeping it simple for mental model. + ctx.type_infos.push(TypeInfo::Int); + let int_literal_type_info_id = ctx.type_infos.len() - 1; + ctx.nodes_to_type_infos + .insert(integer_literal.node_id(), int_literal_type_info_id); + } + Expression::Double(double_literal) => { + ctx.type_infos.push(TypeInfo::Double); + let double_literal_type_info_id = ctx.type_infos.len() - 1; + ctx.nodes_to_type_infos + .insert(double_literal.node_id(), double_literal_type_info_id); + } + Expression::String(string_literal) => { + ctx.type_infos.push(TypeInfo::String); + let string_literal_type_info_id = ctx.type_infos.len() - 1; + ctx.nodes_to_type_infos + .insert(string_literal.node_id(), string_literal_type_info_id); + } + } +} + +fn resolve_types_binary_expression( + binary_expression: &BinaryExpression, + ctx: &mut ResolveTypesContext, +) { + resolve_types_expression(binary_expression.lhs(), ctx); + resolve_types_expression(binary_expression.rhs(), ctx); + + let lhs_type_info_id = ctx.nodes_to_type_infos[&binary_expression.lhs().node_id()]; + let lhs_type_info = &ctx.type_infos[lhs_type_info_id]; + + let rhs_type_info_id = ctx.nodes_to_type_infos[&binary_expression.rhs().node_id()]; + let rhs_type_info = &ctx.type_infos[rhs_type_info_id]; + + if are_binary_op_compatible(binary_expression.op(), lhs_type_info, rhs_type_info) { + let result_type_info = + binary_op_result(binary_expression.op(), lhs_type_info, rhs_type_info); + ctx.type_infos.push(result_type_info); + let result_type_info_id = ctx.type_infos.len() - 1; + ctx.nodes_to_type_infos + .insert(binary_expression.node_id(), result_type_info_id); + } else { + let op_name = match binary_expression.op() { + BinaryOperation::Multiply => "multiply", + BinaryOperation::Divide => "divide", + BinaryOperation::Modulo => "modulo", + BinaryOperation::Add => "add", + BinaryOperation::Subtract => "subtract", + BinaryOperation::LeftShift => "left shift", + BinaryOperation::RightShift => "right shift", + BinaryOperation::BitwiseAnd => "bitwise and", + BinaryOperation::BitwiseXor => "bitwise xor", + BinaryOperation::BitwiseOr => "bitwise or", + }; + let message = format!( + "Incompatible types: cannot {} {} and {}", + op_name, lhs_type_info, rhs_type_info + ); + let diagnostic = Diagnostic::new( + &message, + binary_expression.source_range().start(), + binary_expression.source_range().end(), + ) + .with_error_code(BINARY_INCOMPATIBLE_TYPES); + ctx.diagnostics.push(diagnostic); + + // push error type so it bubbles up + ctx.type_infos.push(TypeInfo::__Error); + let result_type_info_id = ctx.type_infos.len() - 1; + ctx.nodes_to_type_infos + .insert(binary_expression.node_id(), result_type_info_id); + } +} + +fn resolve_types_negative_expression( + negative_expression: &NegativeExpression, + ctx: &mut ResolveTypesContext, +) { + resolve_types_expression(negative_expression.operand(), ctx); + + let operand_type_info_id = ctx.nodes_to_type_infos[&negative_expression.operand().node_id()]; + let operand_type_info = &ctx.type_infos[operand_type_info_id]; + + if can_negate(operand_type_info) { + let result_type_info = negate_result(operand_type_info); + ctx.type_infos.push(result_type_info); + let result_type_info_id = ctx.type_infos.len() - 1; + ctx.nodes_to_type_infos + .insert(negative_expression.node_id(), result_type_info_id); + } else { + let message = format!("Incompatible type: cannot negate {}", operand_type_info); + let diagnostic = Diagnostic::new( + &message, + negative_expression.source_range().start(), + negative_expression.source_range().end(), + ); + ctx.diagnostics.push(diagnostic); + + // bubble up error type + ctx.type_infos.push(TypeInfo::__Error); + let result_type_info_id = ctx.type_infos.len() - 1; + ctx.nodes_to_type_infos + .insert(negative_expression.node_id(), result_type_info_id); + } +} + +fn resolve_types_call(call: &Call, ctx: &mut ResolveTypesContext) { + resolve_types_expression(call.callee(), ctx); + + let callee_type_info_id = ctx.nodes_to_type_infos[&call.callee().node_id()]; + let callee_type_info = &ctx.type_infos[callee_type_info_id]; + + match callee_type_info { + TypeInfo::Function(function_type_info) => { + // check arguments length + let arguments = call.arguments(); + if arguments.len() != function_type_info.parameter_type_ids().len() { + let message = format!( + "Wrong number of arguments: expected {} but found {}", + function_type_info.parameter_type_ids().len(), + arguments.len() + ); + let diagnostic = Diagnostic::new( + &message, + call.source_range().start(), + call.source_range().end(), + ); + ctx.diagnostics.push(diagnostic); + + // do not push an error type because the result of the whole call is the return type + // of the function + } + + // check argument types + let parameter_type_ids = function_type_info.parameter_type_ids(); + for i in 0..parameter_type_ids.len() { + let argument_type_info_id = ctx.nodes_to_type_infos[&arguments[i].node_id()]; + let argument_type_info = &ctx.type_infos[argument_type_info_id]; + let parameter_type_info = &ctx.type_infos[parameter_type_ids[i]]; + if !can_assign_right_to_left(parameter_type_info, argument_type_info) { + let message = format!( + "Incompatible types: cannot assign {} to {}", + argument_type_info, parameter_type_info + ); + let diagnostic = Diagnostic::new( + &message, + arguments[i].source_range().start(), + arguments[i].source_range().end(), + ); + ctx.diagnostics.push(diagnostic); + + // do not push an error type because the result of the whole call is the return type + // of the function + } + } + + // set return type as type of call expression + let return_type_info_id = function_type_info.return_type_id(); + ctx.nodes_to_type_infos + .insert(call.node_id(), *return_type_info_id); + } + TypeInfo::__Error => { + // bubble it up + ctx.type_infos.push(TypeInfo::__Error); + let error_type_info_id = ctx.type_infos.len() - 1; + ctx.nodes_to_type_infos + .insert(call.node_id(), error_type_info_id); + } + _ => { + let message = format!("Incompatible type: cannot call type {}", callee_type_info); + let diagnostic = Diagnostic::new( + &message, + call.callee().source_range().start(), + call.callee().source_range().end(), + ); + ctx.diagnostics.push(diagnostic); + + // bubble up error type + ctx.type_infos.push(TypeInfo::__Error); + let error_type_info_id = ctx.type_infos.len() - 1; + ctx.nodes_to_type_infos + .insert(call.node_id(), error_type_info_id); + } + } +} + +fn resolve_types_identifier(identifier: &Identifier, ctx: &mut ResolveTypesContext) { + let symbol_id = ctx.nodes_to_symbols[&identifier.node_id()]; + let symbol_type_info_id = ctx.symbols_to_type_infos[&symbol_id]; + // bubble up + ctx.nodes_to_type_infos + .insert(identifier.node_id(), symbol_type_info_id); +} diff --git a/dmc-lib/src/semantic_analysis/resolve_types/type_analysis.rs b/dmc-lib/src/semantic_analysis/resolve_types/type_analysis.rs new file mode 100644 index 0000000..9004eea --- /dev/null +++ b/dmc-lib/src/semantic_analysis/resolve_types/type_analysis.rs @@ -0,0 +1,127 @@ +use crate::ast::binary_expression::BinaryOperation; +use crate::semantic_analysis::type_info::TypeInfo; + +pub fn are_binary_op_compatible(op: &BinaryOperation, left: &TypeInfo, right: &TypeInfo) -> bool { + match op { + BinaryOperation::Multiply + | BinaryOperation::Divide + | BinaryOperation::Modulo + | BinaryOperation::Subtract => are_numbers(left, right), + BinaryOperation::Add => are_numbers_or_strings(left, right), + BinaryOperation::LeftShift + | BinaryOperation::RightShift + | BinaryOperation::BitwiseAnd + | BinaryOperation::BitwiseXor + | BinaryOperation::BitwiseOr => are_ints(left, right), + } +} + +fn is_number(type_info: &TypeInfo) -> bool { + matches!(type_info, TypeInfo::Int | TypeInfo::Double) +} + +fn are_numbers(t0: &TypeInfo, t1: &TypeInfo) -> bool { + is_number(t0) && is_number(t1) +} + +fn are_numbers_or_strings(t0: &TypeInfo, t1: &TypeInfo) -> bool { + (is_number(t0) || matches!(t0, TypeInfo::String)) + && (is_number(t1) || matches!(t1, TypeInfo::String)) +} + +fn are_ints(t0: &TypeInfo, t1: &TypeInfo) -> bool { + matches!(t0, TypeInfo::Int) && matches!(t1, TypeInfo::Int) +} + +pub fn binary_op_result(op: &BinaryOperation, left: &TypeInfo, right: &TypeInfo) -> TypeInfo { + match op { + BinaryOperation::Multiply => numbers_binary_result(left, right), + BinaryOperation::Divide => TypeInfo::Double, // okay for now + BinaryOperation::Modulo => numbers_binary_result(left, right), // same properties as multiplication + BinaryOperation::Add => add_result(left, right), + BinaryOperation::Subtract => numbers_binary_result(left, right), + BinaryOperation::LeftShift + | BinaryOperation::RightShift + | BinaryOperation::BitwiseAnd + | BinaryOperation::BitwiseXor + | BinaryOperation::BitwiseOr => TypeInfo::Int, + } +} + +fn numbers_binary_result(left: &TypeInfo, right: &TypeInfo) -> TypeInfo { + match left { + TypeInfo::Int => match right { + TypeInfo::Int => TypeInfo::Int, + TypeInfo::Double => TypeInfo::Double, + TypeInfo::__Error => TypeInfo::__Error, + _ => panic!(), + }, + TypeInfo::Double => match right { + TypeInfo::Int | TypeInfo::Double => TypeInfo::Double, + TypeInfo::__Error => TypeInfo::__Error, + _ => panic!(), + }, + TypeInfo::__Error => TypeInfo::__Error, + _ => panic!(), + } +} + +fn add_result(left: &TypeInfo, right: &TypeInfo) -> TypeInfo { + match left { + TypeInfo::Int => match right { + TypeInfo::Int => TypeInfo::Int, + TypeInfo::Double => TypeInfo::Double, + TypeInfo::String => TypeInfo::String, + TypeInfo::__Error => TypeInfo::__Error, + _ => panic!(), + }, + TypeInfo::Double => match right { + TypeInfo::Int | TypeInfo::Double => TypeInfo::Double, + TypeInfo::String => TypeInfo::String, + TypeInfo::__Error => TypeInfo::__Error, + _ => panic!(), + }, + TypeInfo::String => TypeInfo::String, + TypeInfo::__Error => TypeInfo::__Error, + _ => panic!(), + } +} + +pub fn can_negate(operand: &TypeInfo) -> bool { + matches!( + operand, + TypeInfo::Int | TypeInfo::Double | TypeInfo::__Error + ) +} + +pub fn negate_result(operand: &TypeInfo) -> TypeInfo { + match operand { + TypeInfo::Int => TypeInfo::Int, + TypeInfo::Double => TypeInfo::Double, + TypeInfo::__Error => TypeInfo::__Error, + _ => panic!(), + } +} + +pub fn can_assign_right_to_left(left: &TypeInfo, right: &TypeInfo) -> bool { + match left { + TypeInfo::Any => true, + TypeInfo::Function(_) => { + panic!() + } + TypeInfo::String => match right { + TypeInfo::String => true, + _ => false, + }, + TypeInfo::Int => match right { + TypeInfo::Int => true, + _ => false, + }, + TypeInfo::Double => match right { + TypeInfo::Double | TypeInfo::Int => true, + _ => false, + }, + TypeInfo::Void => false, + TypeInfo::__Error => true, + } +} diff --git a/dmc-lib/src/semantic_analysis/symbol.rs b/dmc-lib/src/semantic_analysis/symbol.rs index a99d978..4b97de9 100644 --- a/dmc-lib/src/semantic_analysis/symbol.rs +++ b/dmc-lib/src/semantic_analysis/symbol.rs @@ -36,30 +36,49 @@ impl Symbol { } pub struct FunctionSymbol { - name: Rc, - source_range: Option, + declared_name: Rc, + declared_name_source_range: Option, + fqn: Rc, is_extern: bool, } impl FunctionSymbol { - pub fn new(name: Rc, source_range: Option, is_extern: bool) -> Self { + pub fn new( + declared_name: Rc, + declared_name_source_range: Option, + fqn: Rc, + is_extern: bool, + ) -> Self { Self { - name, - source_range, + declared_name, + declared_name_source_range, + fqn, is_extern, } } pub fn declared_name(&self) -> &str { - &self.name + &self.declared_name } pub fn declared_name_owned(&self) -> Rc { - self.name.clone() + self.declared_name.clone() } pub fn source_range(&self) -> Option<&SourceRange> { - self.source_range.as_ref() + self.declared_name_source_range.as_ref() + } + + pub fn fqn(&self) -> &str { + &self.fqn + } + + pub fn fqn_owned(&self) -> Rc { + self.fqn.clone() + } + + pub fn is_extern(&self) -> bool { + self.is_extern } } @@ -89,11 +108,16 @@ impl ParameterSymbol { pub struct VariableSymbol { name: Rc, source_range: Option, + is_mut: bool, } impl VariableSymbol { - pub fn new(name: Rc, source_range: Option) -> Self { - Self { name, source_range } + pub fn new(name: Rc, source_range: Option, is_mut: bool) -> Self { + Self { + name, + source_range, + is_mut, + } } pub fn declared_name(&self) -> &str { diff --git a/dmc-lib/src/semantic_analysis/type_info.rs b/dmc-lib/src/semantic_analysis/type_info.rs new file mode 100644 index 0000000..0a0588f --- /dev/null +++ b/dmc-lib/src/semantic_analysis/type_info.rs @@ -0,0 +1,55 @@ +use std::fmt::{Display, Formatter}; + +pub type TypeInfoId = usize; + +#[derive(Clone)] +pub enum TypeInfo { + Any, + Function(FunctionTypeInfo), + String, + Int, + Double, + Void, + __Error, +} + +impl Display for TypeInfo { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + match self { + TypeInfo::Any => "Any", + TypeInfo::Function(_) => "Function", + TypeInfo::String => "String", + TypeInfo::Int => "Int", + TypeInfo::Double => "Double", + TypeInfo::Void => "Void", + TypeInfo::__Error => panic!("This should never be shown to a user."), + } + ) + } +} + +#[derive(Clone)] +pub struct FunctionTypeInfo { + parameter_type_info_ids: Vec, + return_type_info_id: TypeInfoId, +} + +impl FunctionTypeInfo { + pub fn new(parameter_type_info_ids: Vec, return_type_info_id: TypeInfoId) -> Self { + Self { + parameter_type_info_ids, + return_type_info_id, + } + } + + pub fn parameter_type_ids(&self) -> &[TypeInfoId] { + &self.parameter_type_info_ids + } + + pub fn return_type_id(&self) -> &TypeInfoId { + &self.return_type_info_id + } +}