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
|
&self.callee
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn arguments(&self) -> Vec<&Expression> {
|
pub fn arguments(&self) -> &[Expression] {
|
||||||
self.arguments.iter().collect()
|
&self.arguments
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_scopes(&mut self, symbol_table: &mut SymbolTable, container_scope: usize) {
|
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()
|
self.declared_name_source_range.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_mut(&self) -> bool {
|
||||||
|
self.is_mut
|
||||||
|
}
|
||||||
|
|
||||||
pub fn initializer(&self) -> &Expression {
|
pub fn initializer(&self) -> &Expression {
|
||||||
&self.initializer
|
&self.initializer
|
||||||
}
|
}
|
||||||
|
|||||||
@ -48,6 +48,10 @@ impl Parameter {
|
|||||||
self.declared_name_source_range.clone()
|
self.declared_name_source_range.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn type_use(&self) -> &TypeUse {
|
||||||
|
&self.type_use
|
||||||
|
}
|
||||||
|
|
||||||
pub fn scope_id(&self) -> usize {
|
pub fn scope_id(&self) -> usize {
|
||||||
self.scope_id.unwrap()
|
self.scope_id.unwrap()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,28 @@
|
|||||||
use crate::constants_table::ConstantsTable;
|
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 {
|
pub trait Assemble {
|
||||||
fn assemble(&self, builder: &mut InstructionsBuilder, constants_table: &mut ConstantsTable);
|
fn assemble(&self, builder: &mut InstructionsBuilder, constants_table: &mut ConstantsTable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
pub struct InstructionsBuilder {
|
pub struct InstructionsBuilder {
|
||||||
instructions: Vec<Instruction>,
|
instructions: Vec<Instruction>,
|
||||||
}
|
}
|
||||||
@ -24,3 +42,357 @@ impl InstructionsBuilder {
|
|||||||
std::mem::take(&mut self.instructions)
|
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_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::register_allocation::VrUser;
|
||||||
use crate::ir::util::propagate_spills;
|
use std::collections::HashSet;
|
||||||
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;
|
|
||||||
|
|
||||||
pub struct IrAssign {
|
pub struct IrAssign {
|
||||||
destination: Rc<RefCell<IrVariable>>,
|
destination: IrVariableId,
|
||||||
initializer: Box<IrOperation>,
|
initializer: Box<IrOperation>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IrAssign {
|
impl IrAssign {
|
||||||
pub fn new(destination: Rc<RefCell<IrVariable>>, initializer: IrOperation) -> Self {
|
pub fn new(destination: IrVariableId, initializer: IrOperation) -> Self {
|
||||||
Self {
|
Self {
|
||||||
destination,
|
destination,
|
||||||
initializer: initializer.into(),
|
initializer: initializer.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn destination(&self) -> IrVariableId {
|
||||||
|
self.destination
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn initializer(&self) -> &IrOperation {
|
||||||
|
&self.initializer
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VrUser for IrAssign {
|
impl VrUser for IrAssign {
|
||||||
fn vr_definitions(&self) -> HashSet<IrVrVariableDescriptor> {
|
fn vr_definitions(&self) -> HashSet<IrVariableId> {
|
||||||
self.destination
|
HashSet::from([self.destination])
|
||||||
.borrow()
|
|
||||||
.descriptor()
|
|
||||||
.vr_variable_descriptors()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn vr_uses(&self) -> HashSet<IrVrVariableDescriptor> {
|
fn vr_uses(&self) -> HashSet<IrVariableId> {
|
||||||
self.initializer.vr_uses()
|
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_expression::IrExpression;
|
||||||
use crate::ir::ir_variable::IrVrVariableDescriptor;
|
use crate::ir::ir_variable::IrVariableId;
|
||||||
use crate::ir::register_allocation::VrUser;
|
use crate::ir::register_allocation::VrUser;
|
||||||
use dvm_lib::instruction::{Instruction, Location};
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
|
|
||||||
@ -35,65 +32,16 @@ impl IrBinaryOperation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn assemble_assign(
|
pub fn left(&self) -> &IrExpression {
|
||||||
&self,
|
&self.left
|
||||||
builder: &mut InstructionsBuilder,
|
}
|
||||||
constants_table: &mut ConstantsTable,
|
|
||||||
destination: Location,
|
pub fn right(&self) -> &IrExpression {
|
||||||
) {
|
&self.right
|
||||||
let instruction = match &self.op {
|
}
|
||||||
IrBinaryOperator::Multiply => Instruction::Multiply(
|
|
||||||
self.left.multiply_operand(),
|
pub fn op(&self) -> &IrBinaryOperator {
|
||||||
self.right.multiply_operand(),
|
&self.op
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,7 +83,7 @@ impl Display for IrBinaryOperation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl VrUser 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()]
|
[self.left.as_ref(), self.right.as_ref()]
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|e| e.vr_uses())
|
.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_statement::IrStatement;
|
||||||
use crate::ir::ir_variable::IrVrVariableDescriptor;
|
use crate::ir::ir_variable::IrVariableId;
|
||||||
use crate::ir::register_allocation::{HasVrUsers, VrUser};
|
use crate::ir::register_allocation::{HasVrUsers, RegisterAssignment, VrUser};
|
||||||
use crate::offset_counter::OffsetCounter;
|
use crate::offset_counter::OffsetCounter;
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::fmt::{Display, Formatter};
|
|
||||||
use std::rc::Rc;
|
pub type IrBlockId = usize;
|
||||||
|
|
||||||
pub struct IrBlock {
|
pub struct IrBlock {
|
||||||
id: usize,
|
id: IrBlockId,
|
||||||
debug_label: String,
|
debug_label: String,
|
||||||
predecessors: Vec<Rc<RefCell<IrBlock>>>,
|
|
||||||
successors: Vec<Rc<RefCell<IrBlock>>>,
|
|
||||||
statements: Vec<IrStatement>,
|
statements: Vec<IrStatement>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -22,16 +17,18 @@ impl IrBlock {
|
|||||||
Self {
|
Self {
|
||||||
id,
|
id,
|
||||||
debug_label: debug_label.into(),
|
debug_label: debug_label.into(),
|
||||||
predecessors: vec![],
|
|
||||||
successors: vec![],
|
|
||||||
statements,
|
statements,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn id(&self) -> usize {
|
pub fn id(&self) -> IrBlockId {
|
||||||
self.id
|
self.id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn debug_label(&self) -> &str {
|
||||||
|
&self.debug_label
|
||||||
|
}
|
||||||
|
|
||||||
pub fn statements(&self) -> &[IrStatement] {
|
pub fn statements(&self) -> &[IrStatement] {
|
||||||
&self.statements
|
&self.statements
|
||||||
}
|
}
|
||||||
@ -51,18 +48,18 @@ impl HasVrUsers for IrBlock {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl VrUser for IrBlock {
|
impl VrUser for IrBlock {
|
||||||
fn vr_definitions(&self) -> HashSet<IrVrVariableDescriptor> {
|
fn vr_definitions(&self) -> HashSet<IrVariableId> {
|
||||||
self.statements
|
self.statements
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|s| s.vr_definitions())
|
.flat_map(|s| s.vr_definitions())
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn vr_uses(&self) -> HashSet<IrVrVariableDescriptor> {
|
fn vr_uses(&self) -> HashSet<IrVariableId> {
|
||||||
self.statements.iter().flat_map(|s| s.vr_uses()).collect()
|
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 {
|
for statement in &mut self.statements {
|
||||||
statement.propagate_spills(spills);
|
statement.propagate_spills(spills);
|
||||||
}
|
}
|
||||||
@ -70,7 +67,7 @@ impl VrUser for IrBlock {
|
|||||||
|
|
||||||
fn propagate_register_assignments(
|
fn propagate_register_assignments(
|
||||||
&mut self,
|
&mut self,
|
||||||
assignments: &HashMap<IrVrVariableDescriptor, usize>,
|
assignments: &HashMap<IrVariableId, RegisterAssignment>,
|
||||||
) {
|
) {
|
||||||
for statement in &mut self.statements {
|
for statement in &mut self.statements {
|
||||||
statement.propagate_register_assignments(assignments);
|
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) {
|
// impl Display for IrBlock {
|
||||||
for statement in &self.statements {
|
// fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
statement.assemble(builder, constants_table);
|
// writeln!(f, " {}:", self.debug_label)?;
|
||||||
}
|
//
|
||||||
}
|
// let (live_in, live_out) = self.live_in_live_out();
|
||||||
}
|
//
|
||||||
|
// for (statement_index, statement) in self.statements.iter().enumerate() {
|
||||||
impl Display for IrBlock {
|
// let statement_live_in = live_in.get(&statement_index).unwrap();
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
// let statement_live_out = live_out.get(&statement_index).unwrap();
|
||||||
writeln!(f, " {}:", self.debug_label)?;
|
//
|
||||||
|
// writeln!(
|
||||||
let (live_in, live_out) = self.live_in_live_out();
|
// f,
|
||||||
|
// " {} // live_in: {:?}, live_out: {:?}",
|
||||||
for (statement_index, statement) in self.statements.iter().enumerate() {
|
// statement, statement_live_in, statement_live_out
|
||||||
let statement_live_in = live_in.get(&statement_index).unwrap();
|
// )?;
|
||||||
let statement_live_out = live_out.get(&statement_index).unwrap();
|
// }
|
||||||
|
// writeln!(f, " // ---- {} meta ----", self.debug_label)?;
|
||||||
writeln!(
|
// writeln!(f, " // definitions: {:?}", self.vr_definitions())?;
|
||||||
f,
|
// writeln!(f, " // uses: {:?}", self.vr_uses())?;
|
||||||
" {} // live_in: {:?}, live_out: {:?}",
|
// writeln!(
|
||||||
statement, statement_live_in, statement_live_out
|
// f,
|
||||||
)?;
|
// " // interference graph: {:?}",
|
||||||
}
|
// self.interference_graph()
|
||||||
writeln!(f, " // ---- {} meta ----", self.debug_label)?;
|
// )?;
|
||||||
writeln!(f, " // definitions: {:?}", self.vr_definitions())?;
|
// Ok(())
|
||||||
writeln!(f, " // uses: {:?}", self.vr_uses())?;
|
// }
|
||||||
writeln!(
|
// }
|
||||||
f,
|
|
||||||
" // interference graph: {:?}",
|
|
||||||
self.interference_graph()
|
|
||||||
)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
@ -157,9 +147,9 @@ mod tests {
|
|||||||
.find(|f| f.declared_name() == "main")
|
.find(|f| f.declared_name() == "main")
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let mut main_ir = main.to_ir(&symbol_table, &types_table, None);
|
let main_ir = main.to_ir(&symbol_table, &types_table, None);
|
||||||
let register_assignments = main_ir.assign_registers(2, &mut OffsetCounter::new());
|
let variable_locations = main_ir.assign_registers(2);
|
||||||
assert_eq!(register_assignments.len(), 4);
|
assert_eq!(variable_locations.register_variables_count(), 4);
|
||||||
|
|
||||||
Ok(())
|
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_expression::IrExpression;
|
||||||
use crate::ir::ir_variable::IrVrVariableDescriptor;
|
use crate::ir::ir_variable::IrVariableId;
|
||||||
use crate::ir::register_allocation::VrUser;
|
use crate::ir::register_allocation::VrUser;
|
||||||
use dvm_lib::instruction::Instruction;
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
@ -22,35 +19,30 @@ impl IrCall {
|
|||||||
is_extern,
|
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 {
|
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()
|
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 {
|
impl Display for IrCall {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "{}(", self.function_name)?;
|
write!(f, "{}(", self.function_name)?;
|
||||||
|
|||||||
@ -1,39 +1,45 @@
|
|||||||
use crate::constants_table::ConstantsTable;
|
use crate::constants_table::ConstantsTable;
|
||||||
use crate::ir::ir_parameter::IrParameter;
|
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::ir::register_allocation::VrUser;
|
||||||
use crate::type_info::TypeInfo;
|
use crate::ir::variable_locations::{VariableLocation, VariableLocations};
|
||||||
use dvm_lib::instruction::{
|
use dvm_lib::instruction::{
|
||||||
AddOperand, Location, LocationOrInteger, LocationOrNumber, MoveOperand, MultiplyOperand,
|
AddOperand, Location, LocationOrInteger, LocationOrNumber, MoveOperand, MultiplyOperand,
|
||||||
PushOperand, ReturnOperand, SetFieldOperand, SubtractOperand,
|
PushOperand, ReturnOperand, SetFieldOperand, SubtractOperand,
|
||||||
};
|
};
|
||||||
use std::cell::RefCell;
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::collections::HashSet;
|
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub enum IrExpression {
|
pub enum IrExpression {
|
||||||
Parameter(Rc<IrParameter>),
|
Parameter(Rc<IrParameter>),
|
||||||
Variable(Rc<RefCell<IrVariable>>),
|
Variable(IrVariableId),
|
||||||
Int(i32),
|
Int(i32),
|
||||||
Double(f64),
|
Double(f64),
|
||||||
String(Rc<str>),
|
String(Rc<str>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IrExpression {
|
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 {
|
match self {
|
||||||
IrExpression::Parameter(ir_parameter) => {
|
IrExpression::Parameter(ir_parameter) => {
|
||||||
MoveOperand::Location(Location::StackFrameOffset(ir_parameter.stack_offset()))
|
MoveOperand::Location(Location::StackFrameOffset(ir_parameter.stack_offset()))
|
||||||
}
|
}
|
||||||
IrExpression::Variable(ir_variable) => match ir_variable.borrow().descriptor() {
|
IrExpression::Variable(ir_variable_id) => {
|
||||||
IrVariableDescriptor::VirtualRegister(register_variable) => {
|
match variable_locations.get_variable_location(ir_variable_id) {
|
||||||
MoveOperand::Location(Location::Register(register_variable.assigned_register()))
|
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::Int(i) => MoveOperand::Int(*i),
|
||||||
IrExpression::Double(d) => MoveOperand::Double(*d),
|
IrExpression::Double(d) => MoveOperand::Double(*d),
|
||||||
IrExpression::String(s) => {
|
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 {
|
match self {
|
||||||
IrExpression::Parameter(ir_parameter) => {
|
IrExpression::Parameter(ir_parameter) => {
|
||||||
PushOperand::Location(Location::StackFrameOffset(ir_parameter.stack_offset()))
|
PushOperand::Location(Location::StackFrameOffset(ir_parameter.stack_offset()))
|
||||||
}
|
}
|
||||||
IrExpression::Variable(ir_variable) => match ir_variable.borrow().descriptor() {
|
IrExpression::Variable(ir_variable_id) => {
|
||||||
IrVariableDescriptor::VirtualRegister(register_variable) => {
|
match variable_locations.get_variable_location(ir_variable_id) {
|
||||||
PushOperand::Location(Location::Register(register_variable.assigned_register()))
|
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::Int(i) => PushOperand::Int(*i),
|
||||||
IrExpression::Double(d) => PushOperand::Double(*d),
|
IrExpression::Double(d) => PushOperand::Double(*d),
|
||||||
IrExpression::String(s) => {
|
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 {
|
match self {
|
||||||
IrExpression::Parameter(ir_parameter) => match ir_parameter.type_info() {
|
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()))
|
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() {
|
IrExpression::Variable(ir_variable_id) => {
|
||||||
TypeInfo::Integer | TypeInfo::Double | TypeInfo::String => {
|
let ir_type_info_id = variables_to_type_infos[ir_variable_id];
|
||||||
match ir_variable.borrow().descriptor() {
|
let ir_type_info = &type_infos[ir_type_info_id];
|
||||||
IrVariableDescriptor::VirtualRegister(register_variable) => {
|
match ir_type_info {
|
||||||
AddOperand::Location(Location::Register(
|
IrTypeInfo::Int | IrTypeInfo::Double | IrTypeInfo::String => {
|
||||||
register_variable.assigned_register(),
|
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::Int(i) => AddOperand::Int(*i),
|
||||||
IrExpression::Double(d) => AddOperand::Double(*d),
|
IrExpression::Double(d) => AddOperand::Double(*d),
|
||||||
IrExpression::String(s) => {
|
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 {
|
match self {
|
||||||
IrExpression::Parameter(ir_parameter) => match ir_parameter.type_info() {
|
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()),
|
Location::StackFrameOffset(ir_parameter.stack_offset()),
|
||||||
),
|
),
|
||||||
_ => panic!(
|
_ => panic!(
|
||||||
@ -114,22 +131,26 @@ impl IrExpression {
|
|||||||
ir_parameter.type_info()
|
ir_parameter.type_info()
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
IrExpression::Variable(ir_variable) => match ir_variable.borrow().type_info() {
|
IrExpression::Variable(ir_variable) => {
|
||||||
TypeInfo::Integer | TypeInfo::Double => match ir_variable.borrow().descriptor() {
|
let ir_type_info_id = variables_to_type_infos[ir_variable];
|
||||||
IrVariableDescriptor::VirtualRegister(vr_variable) => {
|
let ir_type_info = &type_infos[ir_type_info_id];
|
||||||
SubtractOperand::Location(Location::Register(
|
match ir_type_info {
|
||||||
vr_variable.assigned_register(),
|
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(
|
VariableLocation::Stack(stack_frame_offset) => SubtractOperand::Location(
|
||||||
Location::StackFrameOffset(stack_variable.offset()),
|
Location::StackFrameOffset(stack_frame_offset),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
_ => panic!(
|
_ => panic!(
|
||||||
"Attempt to subtract with non-integer type (found {})",
|
"Attempt to subtract with non-number type (found {})",
|
||||||
ir_variable.borrow().type_info()
|
ir_type_info
|
||||||
),
|
),
|
||||||
},
|
}
|
||||||
|
}
|
||||||
IrExpression::Int(i) => SubtractOperand::Int(*i),
|
IrExpression::Int(i) => SubtractOperand::Int(*i),
|
||||||
IrExpression::Double(d) => SubtractOperand::Double(*d),
|
IrExpression::Double(d) => SubtractOperand::Double(*d),
|
||||||
IrExpression::String(_) => {
|
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 {
|
match self {
|
||||||
IrExpression::Parameter(ir_parameter) => {
|
IrExpression::Parameter(ir_parameter) => {
|
||||||
MultiplyOperand::Location(Location::StackFrameOffset(ir_parameter.stack_offset()))
|
MultiplyOperand::Location(Location::StackFrameOffset(ir_parameter.stack_offset()))
|
||||||
}
|
}
|
||||||
IrExpression::Variable(ir_variable) => match ir_variable.borrow().descriptor() {
|
IrExpression::Variable(ir_variable_id) => {
|
||||||
IrVariableDescriptor::VirtualRegister(vr_variable) => {
|
let ir_type_info_id = variables_to_type_infos[ir_variable_id];
|
||||||
MultiplyOperand::Location(Location::Register(vr_variable.assigned_register()))
|
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::Int(i) => MultiplyOperand::Int(*i),
|
||||||
IrExpression::Double(d) => MultiplyOperand::Double(*d),
|
IrExpression::Double(d) => MultiplyOperand::Double(*d),
|
||||||
IrExpression::String(_) => {
|
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 {
|
match self {
|
||||||
IrExpression::Parameter(ir_parameter) => {
|
IrExpression::Parameter(ir_parameter) => {
|
||||||
ReturnOperand::Location(Location::StackFrameOffset(ir_parameter.stack_offset()))
|
ReturnOperand::Location(Location::StackFrameOffset(ir_parameter.stack_offset()))
|
||||||
}
|
}
|
||||||
IrExpression::Variable(ir_variable) => match ir_variable.borrow().descriptor() {
|
IrExpression::Variable(ir_variable) => {
|
||||||
IrVariableDescriptor::VirtualRegister(register_variable) => {
|
match variable_locations.get_variable_location(ir_variable) {
|
||||||
ReturnOperand::Location(Location::Register(
|
VariableLocation::Register(register_assignment) => {
|
||||||
register_variable.assigned_register(),
|
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::Int(i) => ReturnOperand::Int(*i),
|
||||||
IrExpression::Double(d) => ReturnOperand::Double(*d),
|
IrExpression::Double(d) => ReturnOperand::Double(*d),
|
||||||
IrExpression::String(s) => {
|
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 {
|
match self {
|
||||||
IrExpression::Parameter(ir_parameter) => {
|
IrExpression::Parameter(ir_parameter) => {
|
||||||
SetFieldOperand::Location(Location::StackFrameOffset(ir_parameter.stack_offset()))
|
SetFieldOperand::Location(Location::StackFrameOffset(ir_parameter.stack_offset()))
|
||||||
}
|
}
|
||||||
IrExpression::Variable(ir_variable) => match ir_variable.borrow().descriptor() {
|
IrExpression::Variable(ir_variable_id) => {
|
||||||
IrVariableDescriptor::VirtualRegister(vr_variable) => {
|
match variable_locations.get_variable_location(ir_variable_id) {
|
||||||
SetFieldOperand::Location(Location::Register(vr_variable.assigned_register()))
|
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::Int(i) => SetFieldOperand::Int(*i),
|
||||||
IrExpression::Double(d) => SetFieldOperand::Double(*d),
|
IrExpression::Double(d) => SetFieldOperand::Double(*d),
|
||||||
IrExpression::String(s) => {
|
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 {
|
match self {
|
||||||
IrExpression::Parameter(ir_parameter) => {
|
IrExpression::Parameter(ir_parameter) => {
|
||||||
LocationOrNumber::Location(ir_parameter.as_location())
|
LocationOrNumber::Location(ir_parameter.as_location())
|
||||||
}
|
}
|
||||||
IrExpression::Variable(ir_variable) => {
|
IrExpression::Variable(ir_variable_id) => LocationOrNumber::Location(
|
||||||
LocationOrNumber::Location(ir_variable.borrow().descriptor().as_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::Int(i) => LocationOrNumber::Int(*i),
|
||||||
IrExpression::Double(d) => LocationOrNumber::Double(*d),
|
IrExpression::Double(d) => LocationOrNumber::Double(*d),
|
||||||
_ => panic!("Attempt to convert {} to a location or number", self),
|
_ => 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 {
|
match self {
|
||||||
IrExpression::Parameter(ir_parameter) => {
|
IrExpression::Parameter(ir_parameter) => {
|
||||||
LocationOrInteger::Location(ir_parameter.as_location())
|
LocationOrInteger::Location(ir_parameter.as_location())
|
||||||
}
|
}
|
||||||
IrExpression::Variable(ir_variable) => {
|
IrExpression::Variable(ir_variable_id) => LocationOrInteger::Location(
|
||||||
LocationOrInteger::Location(ir_variable.borrow().descriptor().as_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),
|
IrExpression::Int(i) => LocationOrInteger::Int(*i),
|
||||||
_ => panic!("Attempt to convert {} to a location or integer", self),
|
_ => panic!("Attempt to convert {} to a location or integer", self),
|
||||||
}
|
}
|
||||||
@ -239,8 +306,8 @@ impl Display for IrExpression {
|
|||||||
IrExpression::Parameter(ir_parameter) => {
|
IrExpression::Parameter(ir_parameter) => {
|
||||||
write!(f, "{}", ir_parameter)
|
write!(f, "{}", ir_parameter)
|
||||||
}
|
}
|
||||||
IrExpression::Variable(ir_variable) => {
|
IrExpression::Variable(ir_variable_id) => {
|
||||||
write!(f, "{}", ir_variable.borrow())
|
write!(f, "{}", ir_variable_id)
|
||||||
}
|
}
|
||||||
IrExpression::Int(i) => {
|
IrExpression::Int(i) => {
|
||||||
write!(f, "{}", i)
|
write!(f, "{}", i)
|
||||||
@ -256,12 +323,10 @@ impl Display for IrExpression {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl VrUser for IrExpression {
|
impl VrUser for IrExpression {
|
||||||
fn vr_uses(&self) -> HashSet<IrVrVariableDescriptor> {
|
fn vr_uses(&self) -> HashSet<IrVariableId> {
|
||||||
match self {
|
match self {
|
||||||
IrExpression::Parameter(_) => HashSet::new(),
|
IrExpression::Parameter(_) => HashSet::new(),
|
||||||
IrExpression::Variable(ir_variable) => {
|
IrExpression::Variable(ir_variable) => HashSet::from([*ir_variable]),
|
||||||
ir_variable.borrow().descriptor().vr_variable_descriptors()
|
|
||||||
}
|
|
||||||
IrExpression::Int(_) => HashSet::new(),
|
IrExpression::Int(_) => HashSet::new(),
|
||||||
IrExpression::Double(_) => HashSet::new(),
|
IrExpression::Double(_) => HashSet::new(),
|
||||||
IrExpression::String(_) => HashSet::new(),
|
IrExpression::String(_) => HashSet::new(),
|
||||||
|
|||||||
@ -1,72 +1,90 @@
|
|||||||
use crate::constants_table::ConstantsTable;
|
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_block::IrBlock;
|
||||||
use crate::ir::ir_parameter::IrParameter;
|
use crate::ir::ir_parameter::IrParameter;
|
||||||
use crate::ir::ir_variable::IrVrVariableDescriptor;
|
use crate::ir::ir_type_info::{IrTypeInfo, IrTypeInfoId};
|
||||||
use crate::ir::register_allocation::HasVrUsers;
|
use crate::ir::ir_variable::{IrVariable, IrVariableId};
|
||||||
use crate::offset_counter::OffsetCounter;
|
use crate::ir::register_allocation::block_assign_registers;
|
||||||
use crate::type_info::TypeInfo;
|
use crate::ir::variable_locations::VariableLocations;
|
||||||
use dvm_lib::vm::function::Function;
|
use dvm_lib::vm::function::Function;
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt::Display;
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub struct IrFunction {
|
pub struct IrFunction {
|
||||||
fqn: Rc<str>,
|
fqn: Rc<str>,
|
||||||
parameters: Vec<Rc<IrParameter>>,
|
parameters: Vec<Rc<IrParameter>>,
|
||||||
return_type_info: TypeInfo,
|
variables: Vec<IrVariable>,
|
||||||
entry: Rc<RefCell<IrBlock>>,
|
return_type_info: Option<IrTypeInfo>,
|
||||||
|
blocks: Vec<IrBlock>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IrFunction {
|
impl IrFunction {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
fqn: Rc<str>,
|
fqn: Rc<str>,
|
||||||
parameters: Vec<Rc<IrParameter>>,
|
parameters: Vec<Rc<IrParameter>>,
|
||||||
return_type_info: &TypeInfo,
|
variables: Vec<IrVariable>,
|
||||||
entry: Rc<RefCell<IrBlock>>,
|
return_type_info: Option<IrTypeInfo>,
|
||||||
|
blocks: Vec<IrBlock>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
fqn,
|
fqn,
|
||||||
parameters: parameters.to_vec(),
|
parameters,
|
||||||
return_type_info: return_type_info.clone(),
|
variables,
|
||||||
entry,
|
return_type_info,
|
||||||
|
blocks,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn assign_registers(
|
pub fn fqn(&self) -> &str {
|
||||||
&mut self,
|
&self.fqn
|
||||||
register_count: usize,
|
|
||||||
offset_counter: &mut OffsetCounter,
|
|
||||||
) -> HashMap<IrVrVariableDescriptor, usize> {
|
|
||||||
self.entry
|
|
||||||
.borrow_mut()
|
|
||||||
.assign_registers(register_count, offset_counter)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn assemble(&self, stack_size: usize, constants_table: &mut ConstantsTable) -> Function {
|
pub fn blocks(&self) -> &[IrBlock] {
|
||||||
let mut builder = InstructionsBuilder::new();
|
&self.blocks
|
||||||
self.entry.borrow().assemble(&mut builder, constants_table);
|
}
|
||||||
let instructions = builder.take_instructions();
|
|
||||||
|
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(
|
Function::new(
|
||||||
self.fqn.clone(),
|
self.fqn.clone(),
|
||||||
self.parameters.len(),
|
self.parameters.len(),
|
||||||
stack_size,
|
variable_locations.stack_size(),
|
||||||
instructions,
|
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::IrVariableId;
|
||||||
use crate::ir::ir_variable::IrVrVariableDescriptor;
|
|
||||||
use crate::ir::register_allocation::VrUser;
|
use crate::ir::register_allocation::VrUser;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
|
|
||||||
pub struct IrGetFieldRef {
|
pub struct IrGetFieldRef {
|
||||||
self_parameter_or_variable: IrParameterOrVariable,
|
self_variable_id: IrVariableId,
|
||||||
field_index: usize,
|
field_index: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IrGetFieldRef {
|
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 {
|
||||||
self_parameter_or_variable,
|
self_variable_id,
|
||||||
field_index,
|
field_index,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn self_parameter_or_variable(&self) -> &IrParameterOrVariable {
|
pub fn self_variable_id(self) -> IrVariableId {
|
||||||
&self.self_parameter_or_variable
|
self.self_variable_id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn field_index(&self) -> usize {
|
pub fn field_index(&self) -> usize {
|
||||||
@ -28,16 +27,12 @@ impl IrGetFieldRef {
|
|||||||
|
|
||||||
impl Display for IrGetFieldRef {
|
impl Display for IrGetFieldRef {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(
|
write!(f, "&{}.{}", self.self_variable_id, self.field_index)
|
||||||
f,
|
|
||||||
"&{}.{}",
|
|
||||||
self.self_parameter_or_variable, self.field_index
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VrUser for IrGetFieldRef {
|
impl VrUser for IrGetFieldRef {
|
||||||
fn vr_uses(&self) -> HashSet<IrVrVariableDescriptor> {
|
fn vr_uses(&self) -> HashSet<IrVariableId> {
|
||||||
self.self_parameter_or_variable().vr_uses()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,24 +1,23 @@
|
|||||||
use crate::ir::ir_parameter_or_variable::IrParameterOrVariable;
|
use crate::ir::ir_variable::IrVariableId;
|
||||||
use crate::ir::ir_variable::IrVrVariableDescriptor;
|
|
||||||
use crate::ir::register_allocation::VrUser;
|
use crate::ir::register_allocation::VrUser;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
|
|
||||||
pub struct IrGetFieldRefMut {
|
pub struct IrGetFieldRefMut {
|
||||||
self_parameter_or_variable: IrParameterOrVariable,
|
self_ir_variable: IrVariableId,
|
||||||
field_index: usize,
|
field_index: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IrGetFieldRefMut {
|
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 {
|
||||||
self_parameter_or_variable,
|
self_ir_variable,
|
||||||
field_index,
|
field_index,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn self_parameter_or_variable(&self) -> &IrParameterOrVariable {
|
pub fn self_ir_variable(&self) -> IrVariableId {
|
||||||
&self.self_parameter_or_variable
|
self.self_ir_variable
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn field_index(&self) -> usize {
|
pub fn field_index(&self) -> usize {
|
||||||
@ -28,17 +27,12 @@ impl IrGetFieldRefMut {
|
|||||||
|
|
||||||
impl Display for IrGetFieldRefMut {
|
impl Display for IrGetFieldRefMut {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(
|
write!(f, "&mut {}.{}", self.self_ir_variable, self.field_index())
|
||||||
f,
|
|
||||||
"&mut {}.{}",
|
|
||||||
self.self_parameter_or_variable,
|
|
||||||
self.field_index()
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VrUser for IrGetFieldRefMut {
|
impl VrUser for IrGetFieldRefMut {
|
||||||
fn vr_uses(&self) -> HashSet<IrVrVariableDescriptor> {
|
fn vr_uses(&self) -> HashSet<IrVariableId> {
|
||||||
self.self_parameter_or_variable.vr_uses()
|
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::IrGetFieldRef;
|
||||||
use crate::ir::ir_get_field_ref_mut::IrGetFieldRefMut;
|
use crate::ir::ir_get_field_ref_mut::IrGetFieldRefMut;
|
||||||
use crate::ir::ir_read_field::IrReadField;
|
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 crate::ir::register_allocation::VrUser;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
@ -49,7 +49,7 @@ impl Display for IrOperation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl VrUser for IrOperation {
|
impl VrUser for IrOperation {
|
||||||
fn vr_uses(&self) -> HashSet<IrVrVariableDescriptor> {
|
fn vr_uses(&self) -> HashSet<IrVariableId> {
|
||||||
match self {
|
match self {
|
||||||
IrOperation::GetFieldRef(ir_get_field_ref) => ir_get_field_ref.vr_uses(),
|
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(),
|
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 dvm_lib::instruction::Location;
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub struct IrParameter {
|
pub struct IrParameter {
|
||||||
name: Rc<str>,
|
name: Rc<str>,
|
||||||
type_info: TypeInfo,
|
type_info: IrTypeInfo,
|
||||||
stack_offset: isize,
|
stack_offset: isize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IrParameter {
|
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 {
|
Self {
|
||||||
name: name.into(),
|
name: name.into(),
|
||||||
type_info,
|
type_info,
|
||||||
@ -18,7 +18,7 @@ impl IrParameter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn type_info(&self) -> &TypeInfo {
|
pub fn type_info(&self) -> &IrTypeInfo {
|
||||||
&self.type_info
|
&self.type_info
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
use crate::ir::ir_parameter::IrParameter;
|
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 dvm_lib::instruction::Location;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
@ -7,6 +7,7 @@ use std::fmt::{Display, Formatter};
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
#[deprecated]
|
||||||
pub enum IrParameterOrVariable {
|
pub enum IrParameterOrVariable {
|
||||||
IrParameter(Rc<IrParameter>),
|
IrParameter(Rc<IrParameter>),
|
||||||
Variable(Rc<RefCell<IrVariable>>),
|
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 IrParameterOrVariable::Variable(ir_variable) = &self {
|
||||||
if let IrVariableDescriptor::VirtualRegister(vr_variable) =
|
if let IrVariableDescriptor::VirtualRegister(vr_variable) =
|
||||||
ir_variable.borrow().descriptor()
|
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 crate::ir::register_allocation::VrUser;
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
pub struct IrReadField {
|
pub struct IrReadField {
|
||||||
field_ref_variable: Rc<RefCell<IrVariable>>,
|
field_ref_variable: IrVariableId,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IrReadField {
|
impl IrReadField {
|
||||||
pub fn new(field_ref_variable: Rc<RefCell<IrVariable>>) -> Self {
|
pub fn new(field_ref_variable: IrVariableId) -> Self {
|
||||||
Self { field_ref_variable }
|
Self { field_ref_variable }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn field_ref_variable(&self) -> &Rc<RefCell<IrVariable>> {
|
pub fn field_ref_variable(&self) -> IrVariableId {
|
||||||
&self.field_ref_variable
|
self.field_ref_variable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VrUser for IrReadField {
|
||||||
|
fn vr_uses(&self) -> HashSet<IrVariableId> {
|
||||||
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for IrReadField {
|
impl Display for IrReadField {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "{}", self.field_ref_variable.borrow(),)
|
write!(f, "IrReadField({})", self.field_ref_variable)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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_expression::IrExpression;
|
||||||
use crate::ir::ir_variable::IrVrVariableDescriptor;
|
use crate::ir::ir_variable::IrVariableId;
|
||||||
use crate::ir::register_allocation::VrUser;
|
use crate::ir::register_allocation::VrUser;
|
||||||
use dvm_lib::instruction::Instruction;
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
|
|
||||||
@ -15,10 +12,14 @@ impl IrReturn {
|
|||||||
pub fn new(value: Option<IrExpression>) -> Self {
|
pub fn new(value: Option<IrExpression>) -> Self {
|
||||||
Self { value }
|
Self { value }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn value(&self) -> Option<&IrExpression> {
|
||||||
|
self.value.as_ref()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VrUser for IrReturn {
|
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() {
|
if let Some(ir_expression) = self.value.as_ref() {
|
||||||
ir_expression.vr_uses()
|
ir_expression.vr_uses()
|
||||||
} else {
|
} 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 {
|
impl Display for IrReturn {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "return")?;
|
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_expression::IrExpression;
|
||||||
use crate::ir::ir_variable::{IrVariable, IrVariableDescriptor, IrVrVariableDescriptor};
|
use crate::ir::ir_variable::{IrVariable, IrVariableId};
|
||||||
use crate::ir::register_allocation::VrUser;
|
use crate::ir::register_allocation::VrUser;
|
||||||
use dvm_lib::instruction::Instruction;
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
@ -24,26 +21,27 @@ impl IrSetField {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl VrUser for IrSetField {
|
impl VrUser for IrSetField {
|
||||||
fn vr_uses(&self) -> HashSet<IrVrVariableDescriptor> {
|
fn vr_uses(&self) -> HashSet<IrVariableId> {
|
||||||
let mut set = HashSet::new();
|
// let mut set = HashSet::new();
|
||||||
match self.field_ref_variable.borrow().descriptor() {
|
// match self.field_ref_variable.borrow().descriptor() {
|
||||||
IrVariableDescriptor::VirtualRegister(vr_variable) => {
|
// IrVariableDescriptor::VirtualRegister(vr_variable) => {
|
||||||
set.insert(vr_variable.clone());
|
// set.insert(vr_variable.clone());
|
||||||
}
|
// }
|
||||||
IrVariableDescriptor::Stack(_) => {}
|
// IrVariableDescriptor::Stack(_) => {}
|
||||||
}
|
// }
|
||||||
set.extend(self.initializer.vr_uses());
|
// set.extend(self.initializer.vr_uses());
|
||||||
set
|
// set
|
||||||
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Assemble for IrSetField {
|
// impl Assemble for IrSetField {
|
||||||
fn assemble(&self, builder: &mut InstructionsBuilder, constants_table: &mut ConstantsTable) {
|
// fn assemble(&self, builder: &mut InstructionsBuilder, constants_table: &mut ConstantsTable) {
|
||||||
let field_ref_location = self.field_ref_variable.borrow().descriptor().as_location();
|
// let field_ref_location = self.field_ref_variable.borrow().descriptor().as_location();
|
||||||
let set_field_operand = self.initializer.set_field_operand(constants_table);
|
// let set_field_operand = self.initializer.set_field_operand(constants_table);
|
||||||
builder.push(Instruction::SetField(field_ref_location, set_field_operand));
|
// builder.push(Instruction::SetField(field_ref_location, set_field_operand));
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
impl Display for IrSetField {
|
impl Display for IrSetField {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
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_assign::IrAssign;
|
||||||
use crate::ir::ir_call::IrCall;
|
use crate::ir::ir_call::IrCall;
|
||||||
use crate::ir::ir_return::IrReturn;
|
use crate::ir::ir_return::IrReturn;
|
||||||
use crate::ir::ir_set_field::IrSetField;
|
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::ir::register_allocation::VrUser;
|
||||||
use crate::offset_counter::OffsetCounter;
|
use std::collections::HashSet;
|
||||||
use std::collections::{HashMap, HashSet};
|
|
||||||
use std::fmt::{Display, Formatter};
|
|
||||||
|
|
||||||
pub enum IrStatement {
|
pub enum IrStatement {
|
||||||
Assign(IrAssign),
|
Assign(IrAssign),
|
||||||
@ -18,7 +14,7 @@ pub enum IrStatement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl VrUser for IrStatement {
|
impl VrUser for IrStatement {
|
||||||
fn vr_definitions(&self) -> HashSet<IrVrVariableDescriptor> {
|
fn vr_definitions(&self) -> HashSet<IrVariableId> {
|
||||||
match self {
|
match self {
|
||||||
IrStatement::Assign(ir_assign) => ir_assign.vr_definitions(),
|
IrStatement::Assign(ir_assign) => ir_assign.vr_definitions(),
|
||||||
IrStatement::Call(ir_call) => ir_call.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 {
|
match self {
|
||||||
IrStatement::Assign(ir_assign) => ir_assign.vr_uses(),
|
IrStatement::Assign(ir_assign) => ir_assign.vr_uses(),
|
||||||
IrStatement::Call(ir_call) => ir_call.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(),
|
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::ir::ir_type_info::IrTypeInfo;
|
||||||
use crate::type_info::TypeInfo;
|
use std::fmt::{Display, Formatter};
|
||||||
use dvm_lib::instruction::Location;
|
|
||||||
use std::collections::HashSet;
|
|
||||||
use std::fmt::{Debug, Display, Formatter};
|
|
||||||
use std::hash::Hash;
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
pub type IrVariableId = usize;
|
||||||
|
|
||||||
pub struct IrVariable {
|
pub struct IrVariable {
|
||||||
descriptor: IrVariableDescriptor,
|
name: Rc<str>,
|
||||||
type_info: TypeInfo,
|
type_info: IrTypeInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IrVariable {
|
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 {
|
Self {
|
||||||
descriptor: IrVariableDescriptor::VirtualRegister(IrVrVariableDescriptor::new(
|
name: name.into(),
|
||||||
name, block_id,
|
type_info,
|
||||||
)),
|
|
||||||
type_info: type_info.clone(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_stack_with_offset(
|
pub fn name(&self) -> &str {
|
||||||
name: Rc<str>,
|
&self.name
|
||||||
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 type_info(&self) -> &TypeInfo {
|
pub fn type_info(&self) -> &IrTypeInfo {
|
||||||
&self.type_info
|
&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 {
|
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 {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "{}", self.name)
|
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 assemble;
|
||||||
|
mod debug_print;
|
||||||
pub mod ir_allocate;
|
pub mod ir_allocate;
|
||||||
pub mod ir_assign;
|
pub mod ir_assign;
|
||||||
pub mod ir_binary_operation;
|
pub mod ir_binary_operation;
|
||||||
@ -16,6 +17,8 @@ pub mod ir_read_field;
|
|||||||
pub mod ir_return;
|
pub mod ir_return;
|
||||||
pub mod ir_set_field;
|
pub mod ir_set_field;
|
||||||
pub mod ir_statement;
|
pub mod ir_statement;
|
||||||
|
pub mod ir_type_info;
|
||||||
pub mod ir_variable;
|
pub mod ir_variable;
|
||||||
mod register_allocation;
|
mod register_allocation;
|
||||||
mod util;
|
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 crate::offset_counter::OffsetCounter;
|
||||||
|
use dvm_lib::instruction::{Register, StackFrameOffset};
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
pub type InterferenceGraph = HashMap<IrVrVariableDescriptor, HashSet<IrVrVariableDescriptor>>;
|
pub type RegisterAssignment = Register;
|
||||||
pub type LivenessMap = HashMap<usize, HashSet<IrVrVariableDescriptor>>;
|
pub type InterferenceGraph = HashMap<IrVariableId, HashSet<IrVariableId>>;
|
||||||
|
pub type LivenessSets = Vec<HashSet<IrVariableId>>;
|
||||||
|
|
||||||
pub trait HasVrUsers {
|
pub fn block_live_in_live_out(ir_block: &IrBlock) -> (LivenessSets, LivenessSets) {
|
||||||
fn vr_users(&self) -> Vec<&dyn VrUser>;
|
// init
|
||||||
fn vr_users_mut(&mut self) -> Vec<&mut dyn VrUser>;
|
let n_statements = ir_block.statements().len();
|
||||||
|
let mut live_in: LivenessSets = vec![HashSet::new(); n_statements];
|
||||||
fn live_in_live_out(&self) -> (LivenessMap, LivenessMap) {
|
let mut live_out: LivenessSets = vec![HashSet::new(); n_statements];
|
||||||
let mut live_in: LivenessMap = HashMap::new();
|
|
||||||
let mut live_out: LivenessMap = HashMap::new();
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let mut did_work = false;
|
let mut did_work = false;
|
||||||
for (block_index, statement) in self.vr_users().iter().enumerate().rev() {
|
// Go backwards for efficiency
|
||||||
// init if necessary
|
for (statement_index, ir_statement) in ir_block.statements().iter().enumerate().rev() {
|
||||||
if !live_in.contains_key(&block_index) {
|
|
||||||
live_in.insert(block_index, HashSet::new());
|
|
||||||
}
|
|
||||||
if !live_out.contains_key(&block_index) {
|
|
||||||
live_out.insert(block_index, HashSet::new());
|
|
||||||
}
|
|
||||||
|
|
||||||
// out (union of successors ins)
|
// out (union of successors ins)
|
||||||
// for now, a statement can only have one successor
|
// for now, a statement can only have one successor
|
||||||
// this will need to be updated when we add jumps
|
// 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 = &mut live_out[statement_index];
|
||||||
let statement_live_out = live_out.get_mut(&block_index).unwrap();
|
let successor_live_in = &live_in[statement_index + 1];
|
||||||
for vr_variable in successor_live_in {
|
for ir_variable_id in successor_live_in {
|
||||||
if statement_live_out.insert(vr_variable.clone()) {
|
if statement_live_out.insert(*ir_variable_id) {
|
||||||
did_work = true;
|
did_work = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// in: use(s) U ( out(s) - def(s) )
|
// in: use(s) U ( out(s) - def(s) )
|
||||||
let mut new_ins = statement
|
let use_s = ir_statement
|
||||||
.vr_uses()
|
.vr_uses()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|u| (*u).clone())
|
.map(|u| *u)
|
||||||
.collect::<HashSet<_>>();
|
.collect::<HashSet<_>>();
|
||||||
let statement_live_out = live_out.get(&block_index).unwrap();
|
let out_s = &live_out[statement_index];
|
||||||
let defs = statement
|
let def_s = ir_statement
|
||||||
.vr_definitions()
|
.vr_definitions()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|d| (*d).clone())
|
.map(|d| *d)
|
||||||
.collect::<HashSet<_>>();
|
.collect::<HashSet<_>>();
|
||||||
let rhs = statement_live_out - &defs;
|
let rhs = out_s - &def_s;
|
||||||
new_ins.extend(rhs);
|
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 {
|
for new_in in new_ins {
|
||||||
if statement_live_in.insert(new_in) {
|
if statement_live_in.insert(new_in) {
|
||||||
|
// if we added a previously unadded variable, we did work
|
||||||
did_work = true;
|
did_work = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// break only if we did nothing!
|
||||||
if !did_work {
|
if !did_work {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -65,35 +62,36 @@ pub trait HasVrUsers {
|
|||||||
(live_in, live_out)
|
(live_in, live_out)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn interference_graph(&self) -> InterferenceGraph {
|
pub fn block_interference_graph(
|
||||||
let mut all_vr_variables: HashSet<IrVrVariableDescriptor> = HashSet::new();
|
ir_block: &IrBlock,
|
||||||
|
spilled: &HashSet<IrVariableId>,
|
||||||
for vr_user in self.vr_users() {
|
) -> InterferenceGraph {
|
||||||
all_vr_variables.extend(vr_user.vr_definitions());
|
// create a set of all variables used in the block
|
||||||
all_vr_variables.extend(vr_user.vr_uses());
|
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();
|
let mut graph: InterferenceGraph = HashMap::new();
|
||||||
for variable in all_vr_variables {
|
for variable in all_vr_variables {
|
||||||
graph.insert(variable, HashSet::new());
|
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() {
|
for (statement_index, ir_statement) in ir_block.statements().iter().enumerate() {
|
||||||
let user_live_in = live_out.get(&index).unwrap();
|
let statement_live_out = &live_out[statement_index];
|
||||||
for definition_vr_variable in vr_user.vr_definitions() {
|
for definition_vr_variable in ir_statement.vr_definitions() {
|
||||||
for live_out_variable in user_live_in {
|
for live_out_variable in statement_live_out {
|
||||||
// we do the following check to avoid adding an edge to itself
|
// we do the following check to avoid adding an edge to itself
|
||||||
if definition_vr_variable.ne(live_out_variable) {
|
if definition_vr_variable != *live_out_variable {
|
||||||
graph
|
// add edges to both sets (two-way)
|
||||||
.get_mut(&definition_vr_variable)
|
let definition_edges = graph.get_mut(&definition_vr_variable).unwrap();
|
||||||
.unwrap()
|
definition_edges.insert(*live_out_variable);
|
||||||
.insert(live_out_variable.clone());
|
let live_out_variable_edges = graph.get_mut(live_out_variable).unwrap();
|
||||||
graph
|
live_out_variable_edges.insert(definition_vr_variable);
|
||||||
.get_mut(live_out_variable)
|
|
||||||
.unwrap()
|
|
||||||
.insert(definition_vr_variable.clone());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -102,57 +100,54 @@ pub trait HasVrUsers {
|
|||||||
graph
|
graph
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assign_registers(
|
pub fn block_assign_registers(ir_block: &IrBlock, register_count: usize) -> VariableLocations {
|
||||||
&mut self,
|
let mut spilled: HashSet<IrVariableId> = HashSet::new();
|
||||||
register_count: usize,
|
|
||||||
offset_counter: &mut OffsetCounter,
|
|
||||||
) -> HashMap<IrVrVariableDescriptor, usize> {
|
|
||||||
let mut spills: HashSet<IrVrVariableDescriptor> = HashSet::new();
|
|
||||||
loop {
|
loop {
|
||||||
let mut interference_graph = self.interference_graph();
|
let mut interference_graph = block_interference_graph(ir_block, &spilled);
|
||||||
let (registers, new_spills) =
|
let (registers, new_spills) = registers_and_spills(&mut interference_graph, register_count);
|
||||||
registers_and_spills(&mut interference_graph, register_count);
|
|
||||||
|
|
||||||
if spills != new_spills {
|
if spilled != new_spills {
|
||||||
spills = new_spills;
|
spilled = new_spills; // todo: figure out if this works algorithmically
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// we've calculated final assignments, so propagate them
|
let mut spills_to_stack_offsets: HashMap<IrVariableId, StackFrameOffset> =
|
||||||
for vr_user in self.vr_users_mut() {
|
HashMap::new();
|
||||||
vr_user.propagate_register_assignments(®isters);
|
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 {
|
pub trait VrUser {
|
||||||
fn vr_definitions(&self) -> HashSet<IrVrVariableDescriptor> {
|
fn vr_definitions(&self) -> HashSet<IrVariableId> {
|
||||||
HashSet::new()
|
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
|
// default no-op
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
fn propagate_register_assignments(
|
fn propagate_register_assignments(
|
||||||
&mut self,
|
&mut self,
|
||||||
_assignments: &HashMap<IrVrVariableDescriptor, usize>,
|
_assignments: &HashMap<IrVariableId, RegisterAssignment>,
|
||||||
) {
|
) {
|
||||||
// default no-op
|
// default no-op
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
fn propagate_stack_offsets(&mut self, _counter: &mut OffsetCounter) {
|
fn propagate_stack_offsets(&mut self, _counter: &mut OffsetCounter) {
|
||||||
// default no-op
|
// default no-op
|
||||||
}
|
}
|
||||||
@ -160,8 +155,8 @@ pub trait VrUser {
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct WorkItem {
|
struct WorkItem {
|
||||||
vr: IrVrVariableDescriptor,
|
vr: IrVariableId,
|
||||||
edges: HashSet<IrVrVariableDescriptor>,
|
edges: HashSet<IrVariableId>,
|
||||||
color: bool,
|
color: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,8 +164,8 @@ pub fn registers_and_spills(
|
|||||||
interference_graph: &mut InterferenceGraph,
|
interference_graph: &mut InterferenceGraph,
|
||||||
k: usize,
|
k: usize,
|
||||||
) -> (
|
) -> (
|
||||||
HashMap<IrVrVariableDescriptor, usize>,
|
HashMap<IrVariableId, RegisterAssignment>,
|
||||||
HashSet<IrVrVariableDescriptor>,
|
HashSet<IrVariableId>,
|
||||||
) {
|
) {
|
||||||
let mut work_stack: Vec<WorkItem> = vec![];
|
let mut work_stack: Vec<WorkItem> = vec![];
|
||||||
|
|
||||||
@ -180,8 +175,8 @@ pub fn registers_and_spills(
|
|||||||
|
|
||||||
// 3. assign colors to registers
|
// 3. assign colors to registers
|
||||||
let mut rebuilt_graph: InterferenceGraph = HashMap::new();
|
let mut rebuilt_graph: InterferenceGraph = HashMap::new();
|
||||||
let mut register_assignments: HashMap<IrVrVariableDescriptor, usize> = HashMap::new();
|
let mut register_assignments: HashMap<IrVariableId, RegisterAssignment> = HashMap::new();
|
||||||
let mut spills: HashSet<IrVrVariableDescriptor> = HashSet::new();
|
let mut spills: HashSet<IrVariableId> = HashSet::new();
|
||||||
|
|
||||||
while let Some(work_item) = work_stack.pop() {
|
while let Some(work_item) = work_stack.pop() {
|
||||||
if work_item.color {
|
if work_item.color {
|
||||||
@ -201,7 +196,7 @@ fn assign_register(
|
|||||||
work_item: &WorkItem,
|
work_item: &WorkItem,
|
||||||
graph: &mut InterferenceGraph,
|
graph: &mut InterferenceGraph,
|
||||||
k: usize,
|
k: usize,
|
||||||
register_assignments: &mut HashMap<IrVrVariableDescriptor, usize>,
|
register_assignments: &mut HashMap<IrVariableId, RegisterAssignment>,
|
||||||
) {
|
) {
|
||||||
rebuild_vr_and_edges(graph, work_item);
|
rebuild_vr_and_edges(graph, work_item);
|
||||||
|
|
||||||
@ -220,21 +215,19 @@ fn assign_register(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_vr_lt_k(
|
fn find_vr_lt_k(interference_graph: &InterferenceGraph, k: usize) -> Option<IrVariableId> {
|
||||||
interference_graph: &InterferenceGraph,
|
|
||||||
k: usize,
|
|
||||||
) -> Option<&IrVrVariableDescriptor> {
|
|
||||||
interference_graph.iter().find_map(
|
interference_graph.iter().find_map(
|
||||||
|(vr, neighbors)| {
|
|(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(
|
fn remove_vr_and_edges(
|
||||||
interference_graph: &mut InterferenceGraph,
|
interference_graph: &mut InterferenceGraph,
|
||||||
vr: &IrVrVariableDescriptor,
|
vr: &IrVariableId,
|
||||||
) -> HashSet<IrVrVariableDescriptor> {
|
) -> HashSet<IrVariableId> {
|
||||||
// first, outgoing
|
// first, outgoing
|
||||||
let outgoing_edges = interference_graph.remove(vr).unwrap();
|
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(
|
fn can_optimistically_color(
|
||||||
work_item: &WorkItem,
|
work_item: &WorkItem,
|
||||||
register_assignments: &HashMap<IrVrVariableDescriptor, usize>,
|
register_assignments: &HashMap<IrVariableId, usize>,
|
||||||
k: usize,
|
k: usize,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
// see if we can optimistically color
|
// see if we can optimistically color
|
||||||
@ -332,38 +325,29 @@ mod tests {
|
|||||||
|
|
||||||
fn line_graph() -> InterferenceGraph {
|
fn line_graph() -> InterferenceGraph {
|
||||||
let mut graph: InterferenceGraph = HashMap::new();
|
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
|
// v1 -- v0 -- v2
|
||||||
graph.insert(v0.clone(), HashSet::from([v1.clone(), v2.clone()]));
|
graph.insert(0, HashSet::from([1, 2]));
|
||||||
graph.insert(v1.clone(), HashSet::from([v0.clone()]));
|
graph.insert(1, HashSet::from([0]));
|
||||||
graph.insert(v2.clone(), HashSet::from([v0.clone()]));
|
graph.insert(2, HashSet::from([0]));
|
||||||
graph
|
graph
|
||||||
}
|
}
|
||||||
|
|
||||||
fn triangle_graph() -> InterferenceGraph {
|
fn triangle_graph() -> InterferenceGraph {
|
||||||
let mut graph: InterferenceGraph = HashMap::new();
|
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
|
// triangle: each has two edges
|
||||||
// v0
|
// v0
|
||||||
// | \
|
// | \
|
||||||
// v1--v2
|
// v1--v2
|
||||||
graph.insert(v0.clone(), HashSet::from([v1.clone(), v2.clone()]));
|
graph.insert(0, HashSet::from([1, 2]));
|
||||||
graph.insert(v1.clone(), HashSet::from([v0.clone(), v2.clone()]));
|
graph.insert(1, HashSet::from([0, 2]));
|
||||||
graph.insert(v2.clone(), HashSet::from([v0.clone(), v1.clone()]));
|
graph.insert(2, HashSet::from([0, 1]));
|
||||||
graph
|
graph
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_vrs(graph: &InterferenceGraph) -> Vec<IrVrVariableDescriptor> {
|
fn get_vrs() -> Vec<IrVariableId> {
|
||||||
let v0 = graph.keys().find(|k| k.name() == "v0").unwrap();
|
vec![0, 1, 2]
|
||||||
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()]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -371,7 +355,7 @@ mod tests {
|
|||||||
let graph = line_graph();
|
let graph = line_graph();
|
||||||
let found = find_vr_lt_k(&graph, 2);
|
let found = find_vr_lt_k(&graph, 2);
|
||||||
assert!(found.is_some());
|
assert!(found.is_some());
|
||||||
assert!(found.unwrap().name() == "v1" || found.unwrap().name() == "v2");
|
assert!(found.unwrap() == 1 || found.unwrap() == 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -384,7 +368,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn remove_edges_v0() {
|
fn remove_edges_v0() {
|
||||||
let mut graph = line_graph();
|
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]);
|
let v0_outgoing = remove_vr_and_edges(&mut graph, &vrs[0]);
|
||||||
assert!(v0_outgoing.contains(&vrs[1]));
|
assert!(v0_outgoing.contains(&vrs[1]));
|
||||||
@ -437,7 +421,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// we should have a triangle graph again
|
// we should have a triangle graph again
|
||||||
let vrs = get_vrs(&rebuilt_graph);
|
let vrs = get_vrs();
|
||||||
for vr in &vrs {
|
for vr in &vrs {
|
||||||
assert!(rebuilt_graph.contains_key(vr));
|
assert!(rebuilt_graph.contains_key(vr));
|
||||||
assert_eq!(rebuilt_graph.get(vr).unwrap().len(), 2);
|
assert_eq!(rebuilt_graph.get(vr).unwrap().len(), 2);
|
||||||
|
|||||||
@ -1,22 +1,15 @@
|
|||||||
use crate::ir::ir_variable::{
|
use crate::ir::ir_variable::IrVariableId;
|
||||||
IrStackVariableDescriptor, IrVariable, IrVariableDescriptor, IrVrVariableDescriptor,
|
|
||||||
};
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
pub fn propagate_spills(
|
pub fn propagate_spills(
|
||||||
ir_variable: &mut Rc<RefCell<IrVariable>>,
|
target_ir_variable: IrVariableId,
|
||||||
spills: &HashSet<IrVrVariableDescriptor>,
|
register_variables: &mut HashSet<IrVariableId>,
|
||||||
|
stack_variables: &mut HashSet<IrVariableId>,
|
||||||
|
new_spills: &HashSet<IrVariableId>,
|
||||||
) {
|
) {
|
||||||
let mut borrowed_ir_variable = ir_variable.borrow_mut();
|
if new_spills.contains(&target_ir_variable) && register_variables.contains(&target_ir_variable)
|
||||||
if let IrVariableDescriptor::VirtualRegister(vr_variable) = borrowed_ir_variable.descriptor() {
|
{
|
||||||
if spills.contains(vr_variable) {
|
register_variables.remove(&target_ir_variable);
|
||||||
let name = vr_variable.name_owned();
|
stack_variables.insert(target_ir_variable);
|
||||||
let block_id = vr_variable.block_id();
|
|
||||||
borrowed_ir_variable.set_descriptor(IrVariableDescriptor::Stack(
|
|
||||||
IrStackVariableDescriptor::new(name, block_id),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
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 intrinsics;
|
||||||
pub mod ir;
|
pub mod ir;
|
||||||
pub mod lexer;
|
pub mod lexer;
|
||||||
|
pub mod lowering;
|
||||||
pub mod offset_counter;
|
pub mod offset_counter;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
pub mod scope;
|
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();
|
let arguments = call.arguments();
|
||||||
assert_eq!(arguments.len(), 1);
|
assert_eq!(arguments.len(), 1);
|
||||||
let first_argument = arguments[0];
|
let first_argument = &arguments[0];
|
||||||
match first_argument {
|
match first_argument {
|
||||||
Expression::String(s) => {
|
Expression::String(s) => {
|
||||||
assert_eq!(s.content(), "Hello, World!");
|
assert_eq!(s.content(), "Hello, World!");
|
||||||
|
|||||||
@ -14,7 +14,12 @@ use crate::ast::statement::Statement;
|
|||||||
use crate::semantic_analysis::scope::{Scope, ScopeId};
|
use crate::semantic_analysis::scope::{Scope, ScopeId};
|
||||||
use std::collections::HashMap;
|
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>,
|
scopes: Vec<Scope>,
|
||||||
current_scope_id: Option<ScopeId>,
|
current_scope_id: Option<ScopeId>,
|
||||||
nodes_to_scopes: HashMap<NodeId, ScopeId>,
|
nodes_to_scopes: HashMap<NodeId, ScopeId>,
|
||||||
@ -36,24 +41,19 @@ impl ScopeCollectionContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn pop_scope(&mut self) {
|
pub fn pop_scope(&mut self) {
|
||||||
let popped_scope = self.scopes.pop().unwrap();
|
self.current_scope_id = self.scopes[self.current_scope_id.unwrap()].parent_id();
|
||||||
self.current_scope_id = popped_scope.parent_id();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn current_scope_id(&self) -> Option<ScopeId> {
|
pub fn current_scope_id(&self) -> Option<ScopeId> {
|
||||||
self.current_scope_id
|
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> {
|
pub fn nodes_to_scopes_mut(&mut self) -> &mut HashMap<NodeId, ScopeId> {
|
||||||
&mut self.nodes_to_scopes
|
&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();
|
let mut ctx = ScopeCollectionContext::new();
|
||||||
ctx.push_scope(Scope::new(None));
|
ctx.push_scope(Scope::new(None));
|
||||||
for function in compilation_unit.functions() {
|
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);
|
collect_scopes_extern_function(extern_function, &mut ctx);
|
||||||
}
|
}
|
||||||
ctx.pop_scope();
|
ctx.pop_scope();
|
||||||
ctx
|
ScopeCollectionResult {
|
||||||
|
scopes: ctx.scopes,
|
||||||
|
nodes_to_scopes: ctx.nodes_to_scopes,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_scopes_function(function: &Function, ctx: &mut ScopeCollectionContext) {
|
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::scope::{Scope, ScopeId};
|
||||||
use crate::semantic_analysis::symbol::{FunctionSymbol, ParameterSymbol, Symbol};
|
use crate::semantic_analysis::symbol::{FunctionSymbol, ParameterSymbol, Symbol};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub struct SymbolCollectionContext {
|
pub struct SymbolCollectionResult {
|
||||||
scopes: Vec<Scope>,
|
pub symbols: Vec<Symbol>,
|
||||||
nodes_to_scopes: HashMap<NodeId, ScopeId>,
|
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>,
|
symbols: Vec<Symbol>,
|
||||||
diagnostics: Diagnostics,
|
diagnostics: Diagnostics,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SymbolCollectionContext {
|
impl<'a> SymbolCollectionContext<'a> {
|
||||||
pub fn new(scopes: Vec<Scope>, nodes_to_scopes: HashMap<NodeId, ScopeId>) -> Self {
|
pub fn new(scopes: &'a mut Vec<Scope>, nodes_to_scopes: &'a HashMap<NodeId, ScopeId>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
scopes,
|
scopes,
|
||||||
nodes_to_scopes,
|
nodes_to_scopes,
|
||||||
|
fqn_context: Vec::new(),
|
||||||
symbols: Vec::new(),
|
symbols: Vec::new(),
|
||||||
diagnostics: Diagnostics::new(),
|
diagnostics: Diagnostics::new(),
|
||||||
}
|
}
|
||||||
@ -45,26 +53,56 @@ impl SymbolCollectionContext {
|
|||||||
scope.symbols_mut().insert(declared_name, symbol_id);
|
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 {
|
pub fn diagnostics_mut(&mut self) -> &mut Diagnostics {
|
||||||
&mut self.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() {
|
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() {
|
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) {
|
fn collect_symbols_function(function: &Function, ctx: &mut SymbolCollectionContext) {
|
||||||
|
let fqn = ctx.resolve_fqn(&function.declared_name_owned()).into();
|
||||||
|
|
||||||
// function itself
|
// function itself
|
||||||
let function_symbol = Symbol::Function(FunctionSymbol::new(
|
let function_symbol = Symbol::Function(FunctionSymbol::new(
|
||||||
function.declared_name_owned(),
|
function.declared_name_owned(),
|
||||||
Some(function.declared_name_source_range()),
|
Some(function.declared_name_source_range()),
|
||||||
|
fqn,
|
||||||
false,
|
false,
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -90,10 +128,15 @@ fn collect_symbols_extern_function(
|
|||||||
extern_function: &ExternFunction,
|
extern_function: &ExternFunction,
|
||||||
ctx: &mut SymbolCollectionContext,
|
ctx: &mut SymbolCollectionContext,
|
||||||
) {
|
) {
|
||||||
|
let fqn = ctx
|
||||||
|
.resolve_fqn(&extern_function.declared_name_owned())
|
||||||
|
.into();
|
||||||
|
|
||||||
// function itself
|
// function itself
|
||||||
let function_symbol = Symbol::Function(FunctionSymbol::new(
|
let function_symbol = Symbol::Function(FunctionSymbol::new(
|
||||||
extern_function.declared_name_owned(),
|
extern_function.declared_name_owned(),
|
||||||
Some(extern_function.declared_name_source_range()),
|
Some(extern_function.declared_name_source_range()),
|
||||||
|
fqn,
|
||||||
true,
|
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_scopes;
|
||||||
mod collect_symbols;
|
mod collect_symbols;
|
||||||
|
mod collect_types;
|
||||||
mod diagnostic_helpers;
|
mod diagnostic_helpers;
|
||||||
mod resolve_names;
|
mod resolve_names;
|
||||||
|
mod resolve_types;
|
||||||
mod scope;
|
mod scope;
|
||||||
mod semantic_context;
|
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 crate::semantic_analysis::symbol::{Symbol, SymbolId, VariableSymbol};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
pub struct NameResolutionContext {
|
pub struct NameResolutionResult {
|
||||||
scopes: Vec<Scope>,
|
pub nodes_to_symbols: HashMap<NodeId, SymbolId>,
|
||||||
symbols: Vec<Symbol>,
|
pub diagnostics: Diagnostics,
|
||||||
nodes_to_scopes: HashMap<NodeId, ScopeId>,
|
}
|
||||||
|
|
||||||
|
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>,
|
nodes_to_symbols: HashMap<NodeId, SymbolId>,
|
||||||
diagnostics: Diagnostics,
|
diagnostics: Diagnostics,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NameResolutionContext {
|
impl<'a> NameResolutionContext<'a> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
scopes: Vec<Scope>,
|
scopes: &'a mut Vec<Scope>,
|
||||||
symbols: Vec<Symbol>,
|
symbols: &'a mut Vec<Symbol>,
|
||||||
nodes_to_scopes: HashMap<NodeId, ScopeId>,
|
nodes_to_scopes: &'a HashMap<NodeId, ScopeId>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
scopes,
|
scopes,
|
||||||
@ -81,9 +86,21 @@ enum ExpressionResolutionPhase {
|
|||||||
Static,
|
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() {
|
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 symbol = Symbol::Variable(VariableSymbol::new(
|
||||||
let_statement.declared_name_owned(),
|
let_statement.declared_name_owned(),
|
||||||
Some(let_statement.declared_name_source_range()),
|
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
|
// the following could be a method, but this is probably the only place in this phase
|
||||||
// where we are pushing symbols still
|
// where we are pushing symbols still
|
||||||
@ -228,7 +246,7 @@ fn resolve_names_identifier(
|
|||||||
) {
|
) {
|
||||||
match phase {
|
match phase {
|
||||||
ExpressionResolutionPhase::Static => {
|
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 maybe_scope = Some(&ctx.scopes()[scope_id]);
|
||||||
let mut found_symbol_id: Option<SymbolId> = None;
|
let mut found_symbol_id: Option<SymbolId> = None;
|
||||||
while let Some(scope) = maybe_scope {
|
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 {
|
pub struct FunctionSymbol {
|
||||||
name: Rc<str>,
|
declared_name: Rc<str>,
|
||||||
source_range: Option<SourceRange>,
|
declared_name_source_range: Option<SourceRange>,
|
||||||
|
fqn: Rc<str>,
|
||||||
is_extern: bool,
|
is_extern: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FunctionSymbol {
|
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 {
|
Self {
|
||||||
name,
|
declared_name,
|
||||||
source_range,
|
declared_name_source_range,
|
||||||
|
fqn,
|
||||||
is_extern,
|
is_extern,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn declared_name(&self) -> &str {
|
pub fn declared_name(&self) -> &str {
|
||||||
&self.name
|
&self.declared_name
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn declared_name_owned(&self) -> Rc<str> {
|
pub fn declared_name_owned(&self) -> Rc<str> {
|
||||||
self.name.clone()
|
self.declared_name.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn source_range(&self) -> Option<&SourceRange> {
|
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 {
|
pub struct VariableSymbol {
|
||||||
name: Rc<str>,
|
name: Rc<str>,
|
||||||
source_range: Option<SourceRange>,
|
source_range: Option<SourceRange>,
|
||||||
|
is_mut: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VariableSymbol {
|
impl VariableSymbol {
|
||||||
pub fn new(name: Rc<str>, source_range: Option<SourceRange>) -> Self {
|
pub fn new(name: Rc<str>, source_range: Option<SourceRange>, is_mut: bool) -> Self {
|
||||||
Self { name, source_range }
|
Self {
|
||||||
|
name,
|
||||||
|
source_range,
|
||||||
|
is_mut,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn declared_name(&self) -> &str {
|
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