use crate::asm::{ AsmBinaryOperator, AsmFunction, AsmInstruction, AsmOperand, ControlUnit, ControlUnitId, VirtualRegisterId, }; use crate::ir::{ IrAssign, IrBinaryOperation, IrBinaryOperator, IrCall, IrCallType, IrExpression, IrFunction, IrLiteral, IrReturn, IrStatement, }; use std::collections::HashMap; use std::rc::Rc; struct AssemblyContext { control_unit_counter: ControlUnitId, control_units: Vec, virtual_register_counter: VirtualRegisterId, virtual_registers: HashMap, VirtualRegisterId>, } impl AssemblyContext { pub fn new() -> Self { Self { control_unit_counter: 0, control_units: Vec::new(), virtual_register_counter: 0, virtual_registers: HashMap::new(), } } fn next_control_unit_id(&mut self) -> ControlUnitId { let id = self.control_unit_counter; self.control_unit_counter += 1; id } pub fn new_control_unit(&mut self) -> ControlUnitId { let new_id = self.next_control_unit_id(); self.control_units.push(ControlUnit::new(new_id)); new_id } pub fn current_control_unit(&mut self) -> &ControlUnit { if self.control_units.is_empty() { self.new_control_unit(); } self.control_units.last().unwrap() } pub fn current_control_unit_mut(&mut self) -> &mut ControlUnit { if self.control_units.is_empty() { self.new_control_unit(); } self.control_units.last_mut().unwrap() } pub fn into_control_units(self) -> Vec { self.control_units } pub fn get_virtual_register(&self, name: &str) -> VirtualRegisterId { *self .virtual_registers .get(name) .expect(&format!("Did not set a virtual register for name {}", name)) } pub fn next_virtual_register(&mut self, name: &str) -> VirtualRegisterId { if self.virtual_registers.contains_key(name) { panic!( "Should not already have a virtual register with name {}", name ); } let virtual_register_id = self.virtual_register_counter; self.virtual_register_counter += 1; self.virtual_registers .insert(name.into(), virtual_register_id); virtual_register_id } } pub fn assemble_ir_function(ir_function: &IrFunction) -> AsmFunction { let mut context = AssemblyContext::new(); // body for statement in ir_function.statements() { assemble_ir_statement(statement, &mut context); } AsmFunction::new(ir_function.fqn().into(), context.into_control_units()) } fn assemble_ir_statement(ir_statement: &IrStatement, context: &mut AssemblyContext) { match ir_statement { IrStatement::Allocate(_) => { todo!() } IrStatement::Call(ir_call) => { assemble_ir_call(ir_call, context); } IrStatement::Return(ir_return) => { assemble_ir_return(ir_return, context); } IrStatement::Assign(ir_assign) => { assemble_ir_assign(ir_assign, context); } IrStatement::MakeClosure(_) => { todo!() } IrStatement::BinaryOperation(ir_binary_operation) => { assemble_ir_binary_operation(ir_binary_operation, context) } } } fn assemble_ir_assign(ir_assign: &IrAssign, context: &mut AssemblyContext) { let source_operand = match ir_assign.rhs() { IrExpression::Variable(ir_variable) => { let virtual_register_id = context.get_virtual_register(ir_variable.name()); AsmOperand::VirtualRegister(virtual_register_id) } IrExpression::Literal(ir_literal) => ir_literal_to_asm_operand(ir_literal), }; let destination = context.next_virtual_register(ir_assign.lhs().name()); context .current_control_unit_mut() .append_instruction(AsmInstruction::Move { source: source_operand, destination, }) } fn ir_literal_to_asm_operand(ir_literal: &IrLiteral) -> AsmOperand { match ir_literal { IrLiteral::I8(i) => AsmOperand::I8(*i), IrLiteral::I16(i) => AsmOperand::I16(*i), IrLiteral::I32(i) => AsmOperand::I32(*i), IrLiteral::I64(i) => AsmOperand::I64(*i), IrLiteral::I128(i) => AsmOperand::I128(*i), IrLiteral::ISize(i) => AsmOperand::ISize(*i), IrLiteral::U8(u) => AsmOperand::U8(*u), IrLiteral::U16(u) => AsmOperand::U16(*u), IrLiteral::U32(u) => AsmOperand::U32(*u), IrLiteral::U64(u) => AsmOperand::U64(*u), IrLiteral::U128(u) => AsmOperand::U128(*u), IrLiteral::USize(u) => AsmOperand::USize(*u), IrLiteral::Boolean(b) => AsmOperand::Boolean(*b), IrLiteral::Float(f) => AsmOperand::Float(*f), IrLiteral::Double(d) => AsmOperand::Double(*d), IrLiteral::String(s) => AsmOperand::String(s.clone()), } } fn assemble_ir_call(ir_call: &IrCall, context: &mut AssemblyContext) { // push args for argument in ir_call.arguments() { match argument { IrExpression::Variable(ir_variable) => { let variable_register_id = context.get_virtual_register(ir_variable.name()); context .current_control_unit_mut() .append_instruction(AsmInstruction::Push { source: AsmOperand::VirtualRegister(variable_register_id), }) } IrExpression::Literal(ir_literal) => context .current_control_unit_mut() .append_instruction(AsmInstruction::Push { source: ir_literal_to_asm_operand(ir_literal), }), } } // issue call match ir_call.call_type() { IrCallType::Static => { context .current_control_unit_mut() .append_instruction(AsmInstruction::InvokeStatic { fqn: ir_call.function_name_owned(), }); } IrCallType::Object => { context .current_control_unit_mut() .append_instruction(AsmInstruction::InvokeObject { fqn: ir_call.function_name_owned(), }) } IrCallType::PlatformStatic => {} IrCallType::PlatformObject => {} } } fn assemble_ir_return(ir_return: &IrReturn, context: &mut AssemblyContext) { let operand = ir_return.expression().map(|expression| match expression { IrExpression::Variable(ir_variable) => { let variable_virtual_register_id = context.get_virtual_register(ir_variable.name()); AsmOperand::VirtualRegister(variable_virtual_register_id) } IrExpression::Literal(ir_literal) => ir_literal_to_asm_operand(ir_literal), }); context .current_control_unit_mut() .append_instruction(AsmInstruction::Return { operand }); } fn assemble_ir_binary_operation( ir_binary_operation: &IrBinaryOperation, context: &mut AssemblyContext, ) { let left = match ir_binary_operation.left() { IrExpression::Variable(ir_variable) => { AsmOperand::VirtualRegister(context.get_virtual_register(ir_variable.name())) } IrExpression::Literal(ir_literal) => ir_literal_to_asm_operand(ir_literal), }; let right = match ir_binary_operation.right() { IrExpression::Variable(ir_variable) => { AsmOperand::VirtualRegister(context.get_virtual_register(ir_variable.name())) } IrExpression::Literal(ir_literal) => ir_literal_to_asm_operand(ir_literal), }; let destination = context.next_virtual_register(ir_binary_operation.destination().name()); let operator = match ir_binary_operation.operator() { IrBinaryOperator::Add => AsmBinaryOperator::Add, IrBinaryOperator::Subtract => AsmBinaryOperator::Subtract, IrBinaryOperator::Multiply => AsmBinaryOperator::Multiply, IrBinaryOperator::Divide => AsmBinaryOperator::Divide, IrBinaryOperator::Exponent => { todo!() } }; let instruction = AsmInstruction::BinaryOperation { destination, left, right, operator, }; context .current_control_unit_mut() .append_instruction(instruction); }