deimos-lang/dmc-lib/src/ir/assemble.rs
2026-07-03 17:37:08 -05:00

395 lines
15 KiB
Rust

use crate::constants_table::ConstantsTable;
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_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;
use crate::ir::variable_locations::{VariableLocation, VariableLocations};
use dvm_lib::instruction::{
AddOperand, Instruction, Location, LocationOrInteger, LocationOrNumber, MoveOperand,
MultiplyOperand, PushOperand, ReturnOperand, SubtractOperand,
};
struct FunctionAssemblyContext<'a> {
variable_locations: &'a VariableLocations,
parameters: &'a [IrParameter],
variables: &'a [IrVariable],
constants_table: &'a mut ConstantsTable,
instructions: Vec<Instruction>,
}
pub fn assemble_ir_function(
ir_function: &IrFunction,
variable_locations: &VariableLocations,
constants_table: &mut ConstantsTable,
) -> Vec<Instruction> {
let mut ctx = FunctionAssemblyContext {
variable_locations,
constants_table,
parameters: ir_function.parameters(),
variables: ir_function.variables(),
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(_) => {
todo!()
}
IrOperation::GetFieldRefMut(_) => {
todo!()
}
IrOperation::ReadField(_) => {
todo!()
}
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(ir_call) => {
// pop top of stack after call into destination
assemble_ir_call(ir_call, ctx);
ctx.instructions
.push(Instruction::Pop(Some(destination_location)));
}
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_id) => MoveOperand::Location(
Location::StackFrameOffset(ctx.parameters[*ir_parameter_id].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_id) => MultiplyOperand::Location(
Location::StackFrameOffset(ctx.parameters[*ir_parameter_id].stack_offset()),
),
IrExpression::Variable(ir_variable_id) => {
let ir_variable = &ctx.variables[*ir_variable_id];
match ir_variable.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_variable.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_id) => {
let ir_parameter = &ctx.parameters[*ir_parameter_id];
match ir_parameter.type_info() {
IrTypeInfo::Int | IrTypeInfo::Double | IrTypeInfo::String => {
AddOperand::Location(Location::StackFrameOffset(ir_parameter.stack_offset()))
}
_ => panic!(
"Attempt to add with non-integer type (found {})",
ir_parameter.type_info()
),
}
}
IrExpression::Variable(ir_variable_id) => {
let ir_variable = &ctx.variables[*ir_variable_id];
match ir_variable.type_info() {
IrTypeInfo::Int | IrTypeInfo::Double | IrTypeInfo::String => AddOperand::Location(
to_location(ctx.variable_locations.get_variable_location(ir_variable_id)),
),
_ => panic!(
"Attempt to add with non-integer type (found {})",
ir_variable.type_info()
),
}
}
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_id) => {
let ir_parameter = &ctx.parameters[*ir_parameter_id];
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_id) => {
let ir_variable = &ctx.variables[*ir_variable_id];
match ir_variable.type_info() {
IrTypeInfo::Int | IrTypeInfo::Double => SubtractOperand::Location(to_location(
ctx.variable_locations.get_variable_location(ir_variable_id),
)),
_ => panic!(
"Attempt to subtract with non-number type (found {})",
ir_variable.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_id) => {
let ir_parameter = &ctx.parameters[*ir_parameter_id];
LocationOrNumber::Location(Location::StackFrameOffset(ir_parameter.stack_offset()))
}
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_id) => PushOperand::Location(
Location::StackFrameOffset(ctx.parameters[*ir_parameter_id].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_id) => ReturnOperand::Location(
Location::StackFrameOffset(ctx.parameters[*ir_parameter_id].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_id) => LocationOrInteger::Location(
Location::StackFrameOffset(ctx.parameters[*ir_parameter_id].stack_offset()),
),
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)
}
}
}