Much work from semantic analysis to lowering to ir/register alloc to assembly. WIP.
This commit is contained in:
parent
d5bfe9ad28
commit
737d7b4232
@ -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) {
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
@ -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<Instruction>,
|
||||
}
|
||||
@ -24,3 +42,357 @@ impl InstructionsBuilder {
|
||||
std::mem::take(&mut self.instructions)
|
||||
}
|
||||
}
|
||||
|
||||
struct FunctionAssemblyContext<'a> {
|
||||
type_infos: &'a Vec<IrTypeInfo>,
|
||||
variables_to_type_infos: &'a HashMap<IrVariableId, IrTypeInfoId>,
|
||||
variable_locations: &'a VariableLocations,
|
||||
constants_table: &'a mut ConstantsTable,
|
||||
instructions: Vec<Instruction>,
|
||||
}
|
||||
|
||||
pub fn assemble_ir_function(
|
||||
ir_function: &IrFunction,
|
||||
type_infos: &Vec<IrTypeInfo>,
|
||||
variables_to_type_infos: &HashMap<IrVariableId, IrTypeInfoId>,
|
||||
variable_locations: &VariableLocations,
|
||||
constants_table: &mut ConstantsTable,
|
||||
) -> Vec<Instruction> {
|
||||
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::<Vec<_>>();
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
194
dmc-lib/src/ir/debug_print.rs
Normal file
194
dmc-lib/src/ir/debug_print.rs
Normal file
@ -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(())
|
||||
}
|
||||
@ -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<RefCell<IrVariable>>,
|
||||
destination: IrVariableId,
|
||||
initializer: Box<IrOperation>,
|
||||
}
|
||||
|
||||
impl IrAssign {
|
||||
pub fn new(destination: Rc<RefCell<IrVariable>>, 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<IrVrVariableDescriptor> {
|
||||
self.destination
|
||||
.borrow()
|
||||
.descriptor()
|
||||
.vr_variable_descriptors()
|
||||
fn vr_definitions(&self) -> HashSet<IrVariableId> {
|
||||
HashSet::from([self.destination])
|
||||
}
|
||||
|
||||
fn vr_uses(&self) -> HashSet<IrVrVariableDescriptor> {
|
||||
fn vr_uses(&self) -> HashSet<IrVariableId> {
|
||||
self.initializer.vr_uses()
|
||||
}
|
||||
|
||||
fn propagate_spills(&mut self, spills: &HashSet<IrVrVariableDescriptor>) {
|
||||
propagate_spills(&mut self.destination, spills);
|
||||
}
|
||||
|
||||
fn propagate_register_assignments(
|
||||
&mut self,
|
||||
assignments: &HashMap<IrVrVariableDescriptor, usize>,
|
||||
) {
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<IrVrVariableDescriptor> {
|
||||
fn vr_uses(&self) -> HashSet<IrVariableId> {
|
||||
[self.left.as_ref(), self.right.as_ref()]
|
||||
.iter()
|
||||
.flat_map(|e| e.vr_uses())
|
||||
|
||||
@ -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<Rc<RefCell<IrBlock>>>,
|
||||
successors: Vec<Rc<RefCell<IrBlock>>>,
|
||||
statements: Vec<IrStatement>,
|
||||
}
|
||||
|
||||
@ -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<IrVrVariableDescriptor> {
|
||||
fn vr_definitions(&self) -> HashSet<IrVariableId> {
|
||||
self.statements
|
||||
.iter()
|
||||
.flat_map(|s| s.vr_definitions())
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn vr_uses(&self) -> HashSet<IrVrVariableDescriptor> {
|
||||
fn vr_uses(&self) -> HashSet<IrVariableId> {
|
||||
self.statements.iter().flat_map(|s| s.vr_uses()).collect()
|
||||
}
|
||||
|
||||
fn propagate_spills(&mut self, spills: &HashSet<IrVrVariableDescriptor>) {
|
||||
fn propagate_spills(&mut self, spills: &HashSet<IrVariableId>) {
|
||||
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<IrVrVariableDescriptor, usize>,
|
||||
assignments: &HashMap<IrVariableId, RegisterAssignment>,
|
||||
) {
|
||||
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(())
|
||||
}
|
||||
|
||||
@ -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,35 +19,30 @@ impl IrCall {
|
||||
is_extern,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn function_name(&self) -> &str {
|
||||
&self.function_name
|
||||
}
|
||||
|
||||
pub fn function_name_owned(&self) -> Rc<str> {
|
||||
self.function_name.clone()
|
||||
}
|
||||
|
||||
pub fn arguments(&self) -> &[IrExpression] {
|
||||
&self.arguments
|
||||
}
|
||||
|
||||
pub fn is_extern(&self) -> bool {
|
||||
self.is_extern
|
||||
}
|
||||
}
|
||||
|
||||
impl VrUser for IrCall {
|
||||
fn vr_uses(&self) -> HashSet<IrVrVariableDescriptor> {
|
||||
fn vr_uses(&self) -> HashSet<IrVariableId> {
|
||||
self.arguments.iter().flat_map(|a| a.vr_uses()).collect()
|
||||
}
|
||||
}
|
||||
|
||||
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 Display for IrCall {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}(", self.function_name)?;
|
||||
|
||||
@ -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<IrParameter>),
|
||||
Variable(Rc<RefCell<IrVariable>>),
|
||||
Variable(IrVariableId),
|
||||
Int(i32),
|
||||
Double(f64),
|
||||
String(Rc<str>),
|
||||
}
|
||||
|
||||
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<IrTypeInfo>,
|
||||
variables_to_type_infos: &HashMap<IrVariableId, IrTypeInfoId>,
|
||||
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<IrTypeInfo>,
|
||||
variables_to_type_infos: &HashMap<IrVariableId, IrTypeInfoId>,
|
||||
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(),
|
||||
))
|
||||
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))
|
||||
}
|
||||
IrVariableDescriptor::Stack(stack_variable) => SubtractOperand::Location(
|
||||
Location::StackFrameOffset(stack_variable.offset()),
|
||||
VariableLocation::Stack(stack_frame_offset) => SubtractOperand::Location(
|
||||
Location::StackFrameOffset(stack_frame_offset),
|
||||
),
|
||||
},
|
||||
_ => panic!(
|
||||
"Attempt to subtract with non-integer type (found {})",
|
||||
ir_variable.borrow().type_info()
|
||||
"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(_) => {
|
||||
@ -138,19 +159,35 @@ impl IrExpression {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn multiply_operand(&self) -> MultiplyOperand {
|
||||
pub fn multiply_operand(
|
||||
&self,
|
||||
type_infos: &Vec<IrTypeInfo>,
|
||||
variables_to_type_infos: &HashMap<IrVariableId, IrTypeInfoId>,
|
||||
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<IrVrVariableDescriptor> {
|
||||
fn vr_uses(&self) -> HashSet<IrVariableId> {
|
||||
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(),
|
||||
|
||||
@ -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<str>,
|
||||
parameters: Vec<Rc<IrParameter>>,
|
||||
return_type_info: TypeInfo,
|
||||
entry: Rc<RefCell<IrBlock>>,
|
||||
variables: Vec<IrVariable>,
|
||||
return_type_info: Option<IrTypeInfo>,
|
||||
blocks: Vec<IrBlock>,
|
||||
}
|
||||
|
||||
impl IrFunction {
|
||||
pub fn new(
|
||||
fqn: Rc<str>,
|
||||
parameters: Vec<Rc<IrParameter>>,
|
||||
return_type_info: &TypeInfo,
|
||||
entry: Rc<RefCell<IrBlock>>,
|
||||
variables: Vec<IrVariable>,
|
||||
return_type_info: Option<IrTypeInfo>,
|
||||
blocks: Vec<IrBlock>,
|
||||
) -> 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<IrVrVariableDescriptor, usize> {
|
||||
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<IrParameter>] {
|
||||
&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<IrTypeInfo>,
|
||||
variables_to_type_infos: &HashMap<IrVariableId, IrTypeInfoId>,
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<IrVrVariableDescriptor> {
|
||||
self.self_parameter_or_variable().vr_uses()
|
||||
fn vr_uses(&self) -> HashSet<IrVariableId> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<IrVrVariableDescriptor> {
|
||||
self.self_parameter_or_variable.vr_uses()
|
||||
fn vr_uses(&self) -> HashSet<IrVariableId> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<IrVrVariableDescriptor> {
|
||||
fn vr_uses(&self) -> HashSet<IrVariableId> {
|
||||
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(),
|
||||
|
||||
@ -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<str>,
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@ -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<IrParameter>),
|
||||
Variable(Rc<RefCell<IrVariable>>),
|
||||
@ -24,7 +25,7 @@ impl IrParameterOrVariable {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn vr_uses(&self) -> HashSet<IrVrVariableDescriptor> {
|
||||
pub fn vr_uses(&self) -> HashSet<IrVirtualRegisterVariable> {
|
||||
if let IrParameterOrVariable::Variable(ir_variable) = &self {
|
||||
if let IrVariableDescriptor::VirtualRegister(vr_variable) =
|
||||
ir_variable.borrow().descriptor()
|
||||
|
||||
@ -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<RefCell<IrVariable>>,
|
||||
field_ref_variable: IrVariableId,
|
||||
}
|
||||
|
||||
impl IrReadField {
|
||||
pub fn new(field_ref_variable: Rc<RefCell<IrVariable>>) -> Self {
|
||||
pub fn new(field_ref_variable: IrVariableId) -> Self {
|
||||
Self { field_ref_variable }
|
||||
}
|
||||
|
||||
pub fn field_ref_variable(&self) -> &Rc<RefCell<IrVariable>> {
|
||||
&self.field_ref_variable
|
||||
pub fn field_ref_variable(&self) -> IrVariableId {
|
||||
self.field_ref_variable
|
||||
}
|
||||
}
|
||||
|
||||
impl VrUser for IrReadField {
|
||||
fn vr_uses(&self) -> HashSet<IrVariableId> {
|
||||
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<IrVrVariableDescriptor> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<IrExpression>) -> Self {
|
||||
Self { value }
|
||||
}
|
||||
|
||||
pub fn value(&self) -> Option<&IrExpression> {
|
||||
self.value.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl VrUser for IrReturn {
|
||||
fn vr_uses(&self) -> HashSet<IrVrVariableDescriptor> {
|
||||
fn vr_uses(&self) -> HashSet<IrVariableId> {
|
||||
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")?;
|
||||
|
||||
@ -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<IrVrVariableDescriptor> {
|
||||
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<IrVariableId> {
|
||||
// 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 {
|
||||
|
||||
@ -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<IrVrVariableDescriptor> {
|
||||
fn vr_definitions(&self) -> HashSet<IrVariableId> {
|
||||
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<IrVrVariableDescriptor> {
|
||||
fn vr_uses(&self) -> HashSet<IrVariableId> {
|
||||
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<IrVrVariableDescriptor>) {
|
||||
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<IrVrVariableDescriptor, usize>,
|
||||
) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
16
dmc-lib/src/ir/ir_type_info.rs
Normal file
16
dmc-lib/src/ir/ir_type_info.rs
Normal file
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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<str>,
|
||||
type_info: IrTypeInfo,
|
||||
}
|
||||
|
||||
impl IrVariable {
|
||||
pub fn new_vr(name: Rc<str>, 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<str>,
|
||||
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<str> {
|
||||
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<IrVrVariableDescriptor> {
|
||||
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<str>,
|
||||
block_id: usize,
|
||||
assigned_register: Option<usize>,
|
||||
}
|
||||
|
||||
impl IrVrVariableDescriptor {
|
||||
pub fn new(name: Rc<str>, block_id: usize) -> Self {
|
||||
Self {
|
||||
name,
|
||||
block_id,
|
||||
assigned_register: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn name_owned(&self) -> Rc<str> {
|
||||
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<str>,
|
||||
block_id: usize,
|
||||
offset: Option<isize>,
|
||||
}
|
||||
|
||||
impl IrStackVariableDescriptor {
|
||||
pub fn new(name: Rc<str>, block_id: usize) -> Self {
|
||||
Self {
|
||||
name,
|
||||
block_id,
|
||||
offset: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn name_owned(&self) -> Rc<str> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -1,63 +1,60 @@
|
||||
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<IrVrVariableDescriptor, HashSet<IrVrVariableDescriptor>>;
|
||||
pub type LivenessMap = HashMap<usize, HashSet<IrVrVariableDescriptor>>;
|
||||
pub type RegisterAssignment = Register;
|
||||
pub type InterferenceGraph = HashMap<IrVariableId, HashSet<IrVariableId>>;
|
||||
pub type LivenessSets = Vec<HashSet<IrVariableId>>;
|
||||
|
||||
pub trait HasVrUsers {
|
||||
fn vr_users(&self) -> Vec<&dyn VrUser>;
|
||||
fn vr_users_mut(&mut self) -> Vec<&mut dyn VrUser>;
|
||||
|
||||
fn live_in_live_out(&self) -> (LivenessMap, LivenessMap) {
|
||||
let mut live_in: LivenessMap = HashMap::new();
|
||||
let mut live_out: LivenessMap = HashMap::new();
|
||||
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];
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
// 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
|
||||
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()) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// in: use(s) U ( out(s) - def(s) )
|
||||
let mut new_ins = statement
|
||||
let use_s = ir_statement
|
||||
.vr_uses()
|
||||
.iter()
|
||||
.map(|u| (*u).clone())
|
||||
.map(|u| *u)
|
||||
.collect::<HashSet<_>>();
|
||||
let statement_live_out = live_out.get(&block_index).unwrap();
|
||||
let defs = statement
|
||||
let out_s = &live_out[statement_index];
|
||||
let def_s = ir_statement
|
||||
.vr_definitions()
|
||||
.iter()
|
||||
.map(|d| (*d).clone())
|
||||
.map(|d| *d)
|
||||
.collect::<HashSet<_>>();
|
||||
let rhs = statement_live_out - &defs;
|
||||
new_ins.extend(rhs);
|
||||
let rhs = out_s - &def_s;
|
||||
let new_ins = use_s.union(&rhs).map(|v| *v).collect::<HashSet<_>>();
|
||||
|
||||
let statement_live_in = live_in.get_mut(&block_index).unwrap();
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
// break only if we did nothing!
|
||||
if !did_work {
|
||||
break;
|
||||
}
|
||||
@ -65,35 +62,36 @@ pub trait HasVrUsers {
|
||||
(live_in, live_out)
|
||||
}
|
||||
|
||||
fn interference_graph(&self) -> InterferenceGraph {
|
||||
let mut all_vr_variables: HashSet<IrVrVariableDescriptor> = 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());
|
||||
pub fn block_interference_graph(
|
||||
ir_block: &IrBlock,
|
||||
spilled: &HashSet<IrVariableId>,
|
||||
) -> InterferenceGraph {
|
||||
// create a set of all variables used in the block
|
||||
let mut all_vr_variables: HashSet<IrVariableId> = HashSet::new();
|
||||
for statement in ir_block.statements() {
|
||||
all_vr_variables.extend(statement.vr_definitions());
|
||||
all_vr_variables.extend(statement.vr_uses());
|
||||
}
|
||||
|
||||
// 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());
|
||||
}
|
||||
|
||||
let (_, live_out) = self.live_in_live_out();
|
||||
let (_, live_out) = block_live_in_live_out(ir_block);
|
||||
|
||||
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 {
|
||||
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.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());
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -102,57 +100,54 @@ pub trait HasVrUsers {
|
||||
graph
|
||||
}
|
||||
|
||||
fn assign_registers(
|
||||
&mut self,
|
||||
register_count: usize,
|
||||
offset_counter: &mut OffsetCounter,
|
||||
) -> HashMap<IrVrVariableDescriptor, usize> {
|
||||
let mut spills: HashSet<IrVrVariableDescriptor> = HashSet::new();
|
||||
pub fn block_assign_registers(ir_block: &IrBlock, register_count: usize) -> VariableLocations {
|
||||
let mut spilled: HashSet<IrVariableId> = HashSet::new();
|
||||
loop {
|
||||
let mut interference_graph = self.interference_graph();
|
||||
let (registers, new_spills) =
|
||||
registers_and_spills(&mut interference_graph, register_count);
|
||||
let mut interference_graph = block_interference_graph(ir_block, &spilled);
|
||||
let (registers, new_spills) = registers_and_spills(&mut interference_graph, register_count);
|
||||
|
||||
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);
|
||||
}
|
||||
if spilled != new_spills {
|
||||
spilled = new_spills; // todo: figure out if this works algorithmically
|
||||
} else {
|
||||
// we've calculated final assignments, so propagate them
|
||||
for vr_user in self.vr_users_mut() {
|
||||
vr_user.propagate_register_assignments(®isters);
|
||||
let mut spills_to_stack_offsets: HashMap<IrVariableId, StackFrameOffset> =
|
||||
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);
|
||||
}
|
||||
}
|
||||
// also set offsets
|
||||
for vr_user in self.vr_users_mut() {
|
||||
vr_user.propagate_stack_offsets(offset_counter);
|
||||
}
|
||||
|
||||
return registers;
|
||||
}
|
||||
}
|
||||
}
|
||||
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<IrVrVariableDescriptor> {
|
||||
fn vr_definitions(&self) -> HashSet<IrVariableId> {
|
||||
HashSet::new()
|
||||
}
|
||||
|
||||
fn vr_uses(&self) -> HashSet<IrVrVariableDescriptor>;
|
||||
#[deprecated]
|
||||
fn vr_uses(&self) -> HashSet<IrVariableId>;
|
||||
|
||||
fn propagate_spills(&mut self, _spills: &HashSet<IrVrVariableDescriptor>) {
|
||||
#[deprecated]
|
||||
fn propagate_spills(&mut self, _spills: &HashSet<IrVariableId>) {
|
||||
// default no-op
|
||||
}
|
||||
|
||||
#[deprecated]
|
||||
fn propagate_register_assignments(
|
||||
&mut self,
|
||||
_assignments: &HashMap<IrVrVariableDescriptor, usize>,
|
||||
_assignments: &HashMap<IrVariableId, RegisterAssignment>,
|
||||
) {
|
||||
// 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<IrVrVariableDescriptor>,
|
||||
vr: IrVariableId,
|
||||
edges: HashSet<IrVariableId>,
|
||||
color: bool,
|
||||
}
|
||||
|
||||
@ -169,8 +164,8 @@ pub fn registers_and_spills(
|
||||
interference_graph: &mut InterferenceGraph,
|
||||
k: usize,
|
||||
) -> (
|
||||
HashMap<IrVrVariableDescriptor, usize>,
|
||||
HashSet<IrVrVariableDescriptor>,
|
||||
HashMap<IrVariableId, RegisterAssignment>,
|
||||
HashSet<IrVariableId>,
|
||||
) {
|
||||
let mut work_stack: Vec<WorkItem> = 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<IrVrVariableDescriptor, usize> = HashMap::new();
|
||||
let mut spills: HashSet<IrVrVariableDescriptor> = HashSet::new();
|
||||
let mut register_assignments: HashMap<IrVariableId, RegisterAssignment> = HashMap::new();
|
||||
let mut spills: HashSet<IrVariableId> = 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<IrVrVariableDescriptor, usize>,
|
||||
register_assignments: &mut HashMap<IrVariableId, RegisterAssignment>,
|
||||
) {
|
||||
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<IrVariableId> {
|
||||
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<IrVrVariableDescriptor> {
|
||||
vr: &IrVariableId,
|
||||
) -> HashSet<IrVariableId> {
|
||||
// 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<IrVrVariableDescriptor, usize>,
|
||||
register_assignments: &HashMap<IrVariableId, usize>,
|
||||
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<IrVrVariableDescriptor> {
|
||||
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<IrVariableId> {
|
||||
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);
|
||||
|
||||
@ -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<RefCell<IrVariable>>,
|
||||
spills: &HashSet<IrVrVariableDescriptor>,
|
||||
target_ir_variable: IrVariableId,
|
||||
register_variables: &mut HashSet<IrVariableId>,
|
||||
stack_variables: &mut HashSet<IrVariableId>,
|
||||
new_spills: &HashSet<IrVariableId>,
|
||||
) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
41
dmc-lib/src/ir/variable_locations.rs
Normal file
41
dmc-lib/src/ir/variable_locations.rs
Normal file
@ -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<IrVariableId, RegisterAssignment>,
|
||||
stack_variables: HashMap<IrVariableId, StackFrameOffset>,
|
||||
}
|
||||
|
||||
impl VariableLocations {
|
||||
pub fn new(
|
||||
register_variables: HashMap<IrVariableId, RegisterAssignment>,
|
||||
stack_variables: HashMap<IrVariableId, StackFrameOffset>,
|
||||
) -> 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()
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
|
||||
426
dmc-lib/src/lowering/mod.rs
Normal file
426
dmc-lib/src/lowering/mod.rs
Normal file
@ -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<IrFunction>,
|
||||
}
|
||||
|
||||
struct LowerToIrContext<'a> {
|
||||
symbols: &'a [Symbol],
|
||||
nodes_to_symbols: &'a HashMap<NodeId, SymbolId>,
|
||||
type_infos: &'a [TypeInfo],
|
||||
symbols_to_type_infos: &'a HashMap<SymbolId, TypeInfoId>,
|
||||
nodes_to_type_infos: &'a HashMap<NodeId, TypeInfoId>,
|
||||
ir_functions: Vec<IrFunction>,
|
||||
}
|
||||
|
||||
pub fn lower_to_ir(
|
||||
compilation_unit: &CompilationUnit,
|
||||
symbols: &[Symbol],
|
||||
nodes_to_symbols: &HashMap<NodeId, SymbolId>,
|
||||
type_infos: &[TypeInfo],
|
||||
symbols_to_type_infos: &HashMap<SymbolId, TypeInfoId>,
|
||||
nodes_to_type_infos: &HashMap<NodeId, TypeInfoId>,
|
||||
) -> 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<IrBlock>,
|
||||
ir_variables: Vec<IrVariable>,
|
||||
symbols_to_variables: HashMap<SymbolId, IrVariableId>,
|
||||
current_block_statements: Vec<IrStatement>,
|
||||
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<SymbolId>,
|
||||
) -> 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<IrParameter> {
|
||||
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"),
|
||||
}
|
||||
}
|
||||
23
dmc-lib/src/lowering/util.rs
Normal file
23
dmc-lib/src/lowering/util.rs
Normal file
@ -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<IrTypeInfo> {
|
||||
match return_type_info {
|
||||
TypeInfo::String | TypeInfo::Int | TypeInfo::Double => {
|
||||
Some(to_ir_type_info(return_type_info))
|
||||
}
|
||||
TypeInfo::Void => None,
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
@ -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!");
|
||||
|
||||
@ -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<Scope>,
|
||||
pub nodes_to_scopes: HashMap<NodeId, ScopeId>,
|
||||
}
|
||||
|
||||
struct ScopeCollectionContext {
|
||||
scopes: Vec<Scope>,
|
||||
current_scope_id: Option<ScopeId>,
|
||||
nodes_to_scopes: HashMap<NodeId, ScopeId>,
|
||||
@ -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<ScopeId> {
|
||||
self.current_scope_id
|
||||
}
|
||||
|
||||
pub fn nodes_to_scopes(&self) -> &HashMap<NodeId, ScopeId> {
|
||||
&self.nodes_to_scopes
|
||||
}
|
||||
|
||||
pub fn nodes_to_scopes_mut(&mut self) -> &mut HashMap<NodeId, ScopeId> {
|
||||
&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) {
|
||||
|
||||
@ -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<Scope>,
|
||||
nodes_to_scopes: HashMap<NodeId, ScopeId>,
|
||||
pub struct SymbolCollectionResult {
|
||||
pub symbols: Vec<Symbol>,
|
||||
pub diagnostics: Diagnostics,
|
||||
}
|
||||
|
||||
struct SymbolCollectionContext<'a> {
|
||||
scopes: &'a mut Vec<Scope>,
|
||||
nodes_to_scopes: &'a HashMap<NodeId, ScopeId>,
|
||||
fqn_context: Vec<Rc<str>>,
|
||||
symbols: Vec<Symbol>,
|
||||
diagnostics: Diagnostics,
|
||||
}
|
||||
|
||||
impl SymbolCollectionContext {
|
||||
pub fn new(scopes: Vec<Scope>, nodes_to_scopes: HashMap<NodeId, ScopeId>) -> Self {
|
||||
impl<'a> SymbolCollectionContext<'a> {
|
||||
pub fn new(scopes: &'a mut Vec<Scope>, nodes_to_scopes: &'a HashMap<NodeId, ScopeId>) -> 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<Symbol> {
|
||||
&mut self.symbols
|
||||
}
|
||||
|
||||
pub fn diagnostics_mut(&mut self) -> &mut Diagnostics {
|
||||
&mut self.diagnostics
|
||||
}
|
||||
|
||||
fn join_fqn_parts(parts: &[Rc<str>]) -> String {
|
||||
parts.join("::")
|
||||
}
|
||||
|
||||
pub fn collect_symbols(compilation_unit: &CompilationUnit, ctx: &mut SymbolCollectionContext) {
|
||||
pub fn get_fqn_base(&self) -> String {
|
||||
Self::join_fqn_parts(&self.fqn_context)
|
||||
}
|
||||
|
||||
pub fn resolve_fqn(&self, suffix: &Rc<str>) -> String {
|
||||
Self::join_fqn_parts(&[self.get_fqn_base().into(), suffix.clone()])
|
||||
}
|
||||
}
|
||||
|
||||
pub fn collect_symbols(
|
||||
compilation_unit: &CompilationUnit,
|
||||
scopes: &mut Vec<Scope>,
|
||||
nodes_to_scopes: &HashMap<NodeId, ScopeId>,
|
||||
) -> 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,
|
||||
));
|
||||
|
||||
|
||||
116
dmc-lib/src/semantic_analysis/collect_types.rs
Normal file
116
dmc-lib/src/semantic_analysis/collect_types.rs
Normal file
@ -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<TypeInfo>,
|
||||
pub symbols_to_type_infos: HashMap<SymbolId, TypeInfoId>,
|
||||
}
|
||||
|
||||
struct CollectTypesContext<'a> {
|
||||
nodes_to_symbols: &'a HashMap<NodeId, SymbolId>,
|
||||
type_infos: Vec<TypeInfo>,
|
||||
symbols_to_type_infos: HashMap<SymbolId, TypeInfoId>,
|
||||
}
|
||||
|
||||
impl<'a> CollectTypesContext<'a> {
|
||||
pub fn new(nodes_to_symbols: &'a HashMap<NodeId, SymbolId>) -> 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<NodeId, SymbolId>,
|
||||
) -> 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<TypeInfoId> {
|
||||
let mut parameter_type_info_ids: Vec<TypeInfoId> = 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);
|
||||
}
|
||||
@ -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<TypeInfo>,
|
||||
pub nodes_to_type_infos: HashMap<NodeId, TypeInfoId>,
|
||||
}
|
||||
|
||||
pub fn analyze(
|
||||
compilation_units: &HashMap<FileId, CompilationUnit>,
|
||||
) -> HashMap<FileId, AnalysisResult> {
|
||||
let mut diagnostics = Diagnostics::new();
|
||||
|
||||
let mut files_to_scopes: HashMap<FileId, Vec<Scope>> = HashMap::new();
|
||||
let mut files_to_nodes_to_scopes: HashMap<FileId, HashMap<NodeId, ScopeId>> = 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<FileId, Vec<Symbol>> = 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<FileId, HashMap<NodeId, SymbolId>> = 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<FileId, Vec<TypeInfo>> = HashMap::new();
|
||||
let mut files_to_symbols_to_type_infos: HashMap<FileId, HashMap<SymbolId, TypeInfoId>> =
|
||||
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<FileId, HashMap<NodeId, TypeInfoId>> =
|
||||
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<FileId, AnalysisResult> = 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
|
||||
}
|
||||
|
||||
@ -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<Scope>,
|
||||
symbols: Vec<Symbol>,
|
||||
nodes_to_scopes: HashMap<NodeId, ScopeId>,
|
||||
pub struct NameResolutionResult {
|
||||
pub nodes_to_symbols: HashMap<NodeId, SymbolId>,
|
||||
pub diagnostics: Diagnostics,
|
||||
}
|
||||
|
||||
struct NameResolutionContext<'a> {
|
||||
scopes: &'a mut Vec<Scope>,
|
||||
symbols: &'a mut Vec<Symbol>,
|
||||
nodes_to_scopes: &'a HashMap<NodeId, ScopeId>,
|
||||
nodes_to_symbols: HashMap<NodeId, SymbolId>,
|
||||
diagnostics: Diagnostics,
|
||||
}
|
||||
|
||||
impl NameResolutionContext {
|
||||
impl<'a> NameResolutionContext<'a> {
|
||||
pub fn new(
|
||||
scopes: Vec<Scope>,
|
||||
symbols: Vec<Symbol>,
|
||||
nodes_to_scopes: HashMap<NodeId, ScopeId>,
|
||||
scopes: &'a mut Vec<Scope>,
|
||||
symbols: &'a mut Vec<Symbol>,
|
||||
nodes_to_scopes: &'a HashMap<NodeId, ScopeId>,
|
||||
) -> 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<Scope>,
|
||||
symbols: &mut Vec<Symbol>,
|
||||
nodes_to_scopes: &HashMap<NodeId, ScopeId>,
|
||||
) -> 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<SymbolId> = None;
|
||||
while let Some(scope) = maybe_scope {
|
||||
|
||||
336
dmc-lib/src/semantic_analysis/resolve_types/mod.rs
Normal file
336
dmc-lib/src/semantic_analysis/resolve_types/mod.rs
Normal file
@ -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<NodeId, TypeInfoId>,
|
||||
pub diagnostics: Diagnostics,
|
||||
}
|
||||
|
||||
struct ResolveTypesContext<'a> {
|
||||
nodes_to_symbols: &'a HashMap<NodeId, SymbolId>,
|
||||
type_infos: &'a mut Vec<TypeInfo>,
|
||||
symbols_to_type_infos: &'a mut HashMap<SymbolId, TypeInfoId>,
|
||||
nodes_to_type_infos: HashMap<NodeId, TypeInfoId>,
|
||||
diagnostics: Diagnostics,
|
||||
}
|
||||
|
||||
impl<'a> ResolveTypesContext<'a> {
|
||||
pub fn new(
|
||||
nodes_to_symbols: &'a HashMap<NodeId, SymbolId>,
|
||||
type_infos: &'a mut Vec<TypeInfo>,
|
||||
symbols_to_type_infos: &'a mut HashMap<SymbolId, TypeInfoId>,
|
||||
) -> 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<NodeId, SymbolId>,
|
||||
type_infos: &mut Vec<TypeInfo>,
|
||||
symbols_to_type_infos: &mut HashMap<SymbolId, TypeInfoId>,
|
||||
) -> 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);
|
||||
}
|
||||
127
dmc-lib/src/semantic_analysis/resolve_types/type_analysis.rs
Normal file
127
dmc-lib/src/semantic_analysis/resolve_types/type_analysis.rs
Normal file
@ -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,
|
||||
}
|
||||
}
|
||||
@ -36,30 +36,49 @@ impl Symbol {
|
||||
}
|
||||
|
||||
pub struct FunctionSymbol {
|
||||
name: Rc<str>,
|
||||
source_range: Option<SourceRange>,
|
||||
declared_name: Rc<str>,
|
||||
declared_name_source_range: Option<SourceRange>,
|
||||
fqn: Rc<str>,
|
||||
is_extern: bool,
|
||||
}
|
||||
|
||||
impl FunctionSymbol {
|
||||
pub fn new(name: Rc<str>, source_range: Option<SourceRange>, is_extern: bool) -> Self {
|
||||
pub fn new(
|
||||
declared_name: Rc<str>,
|
||||
declared_name_source_range: Option<SourceRange>,
|
||||
fqn: Rc<str>,
|
||||
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<str> {
|
||||
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<str> {
|
||||
self.fqn.clone()
|
||||
}
|
||||
|
||||
pub fn is_extern(&self) -> bool {
|
||||
self.is_extern
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,11 +108,16 @@ impl ParameterSymbol {
|
||||
pub struct VariableSymbol {
|
||||
name: Rc<str>,
|
||||
source_range: Option<SourceRange>,
|
||||
is_mut: bool,
|
||||
}
|
||||
|
||||
impl VariableSymbol {
|
||||
pub fn new(name: Rc<str>, source_range: Option<SourceRange>) -> Self {
|
||||
Self { name, source_range }
|
||||
pub fn new(name: Rc<str>, source_range: Option<SourceRange>, is_mut: bool) -> Self {
|
||||
Self {
|
||||
name,
|
||||
source_range,
|
||||
is_mut,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn declared_name(&self) -> &str {
|
||||
|
||||
55
dmc-lib/src/semantic_analysis/type_info.rs
Normal file
55
dmc-lib/src/semantic_analysis/type_info.rs
Normal file
@ -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<TypeInfoId>,
|
||||
return_type_info_id: TypeInfoId,
|
||||
}
|
||||
|
||||
impl FunctionTypeInfo {
|
||||
pub fn new(parameter_type_info_ids: Vec<TypeInfoId>, 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
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user