diff --git a/src/backend/generator.rs b/src/backend/generator.rs index 3457a96..6e41240 100644 --- a/src/backend/generator.rs +++ b/src/backend/generator.rs @@ -1,6 +1,6 @@ use std::collections::BTreeMap; -use crate::{backend::{arm_instr::{ARMInstr, AddInstr, BInstr, BlInstr, CmpInstr, ConditionCode, LoadInstr, LoadPseudoInstr, MoveInstr, MulInstr, PopInstr, PushInstr, RegisterOrImm, RsbInstr, SDivInstr, StoreInstr, SubInstr}, register_allocator::{REG_FP, REG_R0, REG_R1, REG_R2, REG_R3, Register, RegisterAlloc, RegisterAllocator}, types::ARMAsmVar}, ir::types::{Function, IRInstr, MoveRValue, Variable, VariableOrIntLit, VariableType}}; +use crate::{backend::{arm_instr::{ARMInstr, AddInstr, BInstr, BlInstr, CmpInstr, ConditionCode, LoadInstr, LoadPseudoInstr, MoveInstr, MulInstr, PopInstr, PushInstr, RegisterOrImm, RsbInstr, SDivInstr, StoreInstr, SubInstr}, register_allocator::{REG_FP, REG_R0, REG_R1, REG_R2, REG_R3, REG_SP, Register, RegisterAlloc, RegisterAllocator}, types::ARMAsmVar}, ir::types::{Function, IRInstr, MoveRValue, Variable, VariableOrIntLit, VariableType}}; use crate::ir::types::BinaryOp as IRBinaryOp; use crate::ir::types::CmpOp as IRCmpOp; use crate::ir::types::UnaryOp as IRUnaryOp; @@ -16,7 +16,79 @@ pub struct Generator { const DEFAULT_VAR_ALIGN: usize = 4; const ARG_REGS: [Register; 4] = [REG_R0, REG_R1, REG_R2, REG_R3]; +const ARM_DATA_IMM_CHUNK: i32 = 255; +const ARM_LOAD_STORE_MAX_OFFSET: i32 = 4095; + +fn emit_sub_imm(dest: Register, left: Register, imm: i32, instrs: &mut Vec) { + if imm == 0 { + if dest != left { + instrs.push(MoveInstr::new_uncond(dest, RegisterOrImm::Reg(left))); + } + return; + } + let mut remaining = imm; + let first_chunk = remaining.min(ARM_DATA_IMM_CHUNK); + instrs.push(SubInstr::new(dest, left, RegisterOrImm::Imm(first_chunk))); + remaining -= first_chunk; + while remaining > 0 { + let chunk = remaining.min(ARM_DATA_IMM_CHUNK); + instrs.push(SubInstr::new(dest, dest, RegisterOrImm::Imm(chunk))); + remaining -= chunk; + } +} + +fn emit_add_sp_imm(imm: i32, instrs: &mut Vec) { + let mut remaining = imm; + while remaining > 0 { + let chunk = remaining.min(ARM_DATA_IMM_CHUNK); + instrs.push(AddInstr::new_sp(chunk)); + remaining -= chunk; + } +} + +fn emit_sub_sp_imm(imm: i32, instrs: &mut Vec) { + let mut remaining = imm; + while remaining > 0 { + let chunk = remaining.min(ARM_DATA_IMM_CHUNK); + instrs.push(SubInstr::new_sp(chunk)); + remaining -= chunk; + } +} + +fn emit_load_stack(dest: Register, offset: i32, reg_allocator: &mut RegisterAllocator, instrs: &mut Vec) { + if offset <= ARM_LOAD_STORE_MAX_OFFSET { + instrs.push(LoadInstr::new_stack(dest, offset)); + } else { + let addr_alloc = reg_allocator.alloc_any().expect("Ran out of registers"); + emit_sub_imm(addr_alloc.reg, REG_FP, offset, instrs); + instrs.push(LoadInstr::new(dest, addr_alloc.reg, None)); + } +} + +fn emit_store_stack(src: Register, offset: i32, reg_allocator: &mut RegisterAllocator, instrs: &mut Vec) { + if offset <= ARM_LOAD_STORE_MAX_OFFSET { + instrs.push(StoreInstr::new_stack(src, offset)); + } else { + let addr_alloc = reg_allocator.alloc_any().expect("Ran out of registers"); + emit_sub_imm(addr_alloc.reg, REG_FP, offset, instrs); + instrs.push(StoreInstr::new(src, addr_alloc.reg, None)); + } +} + fn load_variable(variable: Variable, reg_allocator: &mut RegisterAllocator, var_index_to_stack_offset: &BTreeMap, instrs: &mut Vec) -> RegisterAlloc { + if variable.data_type.is_array() { + let var_alloc = reg_allocator.alloc(variable.clone()).expect("Ran out of registers"); + match variable.var_type { + VariableType::Global => { + instrs.push(LoadPseudoInstr::new(var_alloc.reg, format!("global_var_{}", variable.index))); + } + _ => { + let stack_offset = var_index_to_stack_offset.get(&variable.index).expect("Variable not declared"); + emit_sub_imm(var_alloc.reg, REG_FP, *stack_offset as i32, instrs); + } + } + return var_alloc; + } match variable.var_type { VariableType::Global => { let var_alloc = reg_allocator.alloc(variable.clone()).expect("Ran out of registers"); @@ -34,14 +106,17 @@ fn load_variable(variable: Variable, reg_allocator: &mut RegisterAllocator, var_ let var_alloc = reg_allocator.alloc_reg(reg).expect("Ran out of registers"); var_alloc } else { - todo!("More than 4 parameters not supported yet"); + let var_alloc = reg_allocator.alloc(variable.clone()).expect("Ran out of registers"); + let stack_arg_offset = 8 + ((param_index - ARG_REGS.len()) * 4); + instrs.push(LoadInstr::new(var_alloc.reg, REG_FP, Some(RegisterOrImm::Imm(stack_arg_offset as i32)))); + var_alloc } }, _ => { let stack_offset = var_index_to_stack_offset.get(&variable.index).expect("Variable not declared"); let var_alloc = reg_allocator.alloc(variable.clone()).expect("Ran out of registers"); // if !var_alloc.is_reused { - instrs.push(LoadInstr::new_stack(var_alloc.reg, *stack_offset as i32)); + emit_load_stack(var_alloc.reg, *stack_offset as i32, reg_allocator, instrs); // } var_alloc } @@ -60,7 +135,7 @@ fn save_variable(variable: Variable, reg: Register, reg_allocator: &mut Register }, _ => { let stack_offset = var_index_to_stack_offset.get(&variable.index).expect("Variable not declared"); - instrs.push(StoreInstr::new_stack(reg, *stack_offset as i32)); + emit_store_stack(reg, *stack_offset as i32, reg_allocator, instrs); } } } @@ -136,8 +211,6 @@ impl Generator { IRInstr::Move(dest, src) => self.emit_move(dest, src, &var_index_to_stack_offset), IRInstr::Load(dest, addr) => self.emit_load(dest, addr, &var_index_to_stack_offset), IRInstr::Store(addr, value) => self.emit_store(addr, value, &var_index_to_stack_offset), - IRInstr::GetAddr(dest, var) => self.emit_get_addr(dest, var, &var_index_to_stack_offset), - IRInstr::GetElementPtr(dest, base, index, elem_size) => self.emit_get_element_ptr(dest, base, index, elem_size, &var_index_to_stack_offset), IRInstr::Declare(variable) => { assert!(!encounter_entry, "Variable declarations must come before entry instruction"); let size = variable.data_type.size_in_bytes(); @@ -147,7 +220,7 @@ impl Generator { IRInstr::Entry => { assert!(!encounter_entry, "Multiple entry instructions are not allowed"); encounter_entry = true; - self.instrs.push(SubInstr::new_sp(stack_size_needed as i32)); + emit_sub_sp_imm(stack_size_needed as i32, &mut self.instrs); }, IRInstr::DefineFunc(_, _, _) => unreachable!(), IRInstr::Cmp(variable, left, cmp_op, right) => self.emit_cmp(variable, left, cmp_op, right, &var_index_to_stack_offset), @@ -207,20 +280,33 @@ impl Generator { fn emit_func_call(&mut self, func: Function, args: Vec, ret: Option, var_index_to_stack_offset: &BTreeMap) { self.instrs.push(PushInstr::new_push_caller_save()); - if args.len() > 4 { - todo!("More than 4 arguments not supported yet"); + let stack_arg_count = args.len().saturating_sub(ARG_REGS.len()); + let stack_arg_size = stack_arg_count * 4; + let caller_save_size = 5 * 4; + let stack_padding = if (caller_save_size + stack_arg_size) % ARM_STACK_ALIGNMENT == 0 { 0 } else { 4 }; + let extra_stack_size = stack_arg_size + stack_padding; + if extra_stack_size > 0 { + emit_sub_sp_imm(extra_stack_size as i32, &mut self.instrs); } let mut arg_reg_allocs = Vec::new(); for (i, arg) in args.into_iter().enumerate() { - arg_reg_allocs.push(self.register_allocator.alloc_reg(ARG_REGS[i]).expect("Ran out of registers")); let arg_alloc = load_variable(arg, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs); - self.instrs.push(MoveInstr::new_uncond(ARG_REGS[i], RegisterOrImm::Reg(arg_alloc.reg))); + if i < ARG_REGS.len() { + arg_reg_allocs.push(self.register_allocator.alloc_reg(ARG_REGS[i]).expect("Ran out of registers")); + self.instrs.push(MoveInstr::new_uncond(ARG_REGS[i], RegisterOrImm::Reg(arg_alloc.reg))); + } else { + let stack_offset = ((i - ARG_REGS.len()) * 4) as i32; + self.instrs.push(StoreInstr::new(arg_alloc.reg, REG_SP, Some(RegisterOrImm::Imm(stack_offset)))); + } } self.instrs.push(BlInstr::new(func.name.clone())); if let Some(ret) = ret { save_variable(ret, REG_R0, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs); } + if extra_stack_size > 0 { + emit_add_sp_imm(extra_stack_size as i32, &mut self.instrs); + } self.instrs.push(PopInstr::new_pop_caller_save()); } @@ -237,31 +323,6 @@ impl Generator { save_variable(dest, dest_alloc.reg, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs); } - fn emit_get_addr(&mut self, dest: Variable, var: Variable, var_index_to_stack_offset: &BTreeMap) { - let dest_alloc = self.register_allocator.alloc(dest.clone()).expect("Ran out of registers"); - match var.var_type { - VariableType::Global => { - self.instrs.push(LoadPseudoInstr::new(dest_alloc.reg, format!("global_var_{}", var.index))); - } - _ => { - let stack_offset = var_index_to_stack_offset.get(&var.index).expect("Variable not declared"); - self.instrs.push(SubInstr::new(dest_alloc.reg, REG_FP, RegisterOrImm::Imm(*stack_offset as i32))); - } - } - save_variable(dest, dest_alloc.reg, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs); - } - - fn emit_get_element_ptr(&mut self, dest: Variable, base: Variable, index: Variable, elem_size: usize, var_index_to_stack_offset: &BTreeMap) { - let base_alloc = load_variable(base, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs); - let index_alloc = load_variable(index, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs); - let scaled_alloc = self.register_allocator.alloc_any().expect("Ran out of registers"); - self.instrs.push(MoveInstr::new_uncond(scaled_alloc.reg, RegisterOrImm::Imm(elem_size as i32))); - self.instrs.push(MulInstr::new(scaled_alloc.reg, index_alloc.reg, RegisterOrImm::Reg(scaled_alloc.reg))); - let dest_alloc = self.register_allocator.alloc(dest.clone()).expect("Ran out of registers"); - self.instrs.push(AddInstr::new(dest_alloc.reg, base_alloc.reg, RegisterOrImm::Reg(scaled_alloc.reg))); - save_variable(dest, dest_alloc.reg, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs); - } - fn emit_load(&mut self, dest: Variable, addr: Variable, var_index_to_stack_offset: &BTreeMap) { let addr_alloc = load_variable(addr, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs); let dest_alloc = self.register_allocator.alloc(dest.clone()).expect("Ran out of registers"); diff --git a/src/ir/generator.rs b/src/ir/generator.rs index d5eee51..b829ff4 100644 --- a/src/ir/generator.rs +++ b/src/ir/generator.rs @@ -276,7 +276,7 @@ impl<'a> Generator<'a> { if var.data_type.is_array() { let ptr_ty = var.data_type.decay_to_ptr().unwrap(); let dest = self.var_manager.declare_temp(ptr_ty); - (vec![IRInstr::GetAddr(dest.clone(), var)], Some(dest)) + (vec![IRInstr::Move(dest.clone(), MoveRValue::Var(var))], Some(dest)) } else { (vec![], Some(var)) } @@ -565,7 +565,7 @@ impl<'a> Generator<'a> { let var = self.var_manager.get_symbol(symbol).unwrap(); if var.data_type.is_array() { let ptr = self.var_manager.declare_temp(var.data_type.decay_to_ptr().unwrap()); - (vec![IRInstr::GetAddr(ptr.clone(), var)], ptr) + (vec![IRInstr::Move(ptr.clone(), MoveRValue::Var(var))], ptr) } else { (vec![], var) } @@ -579,7 +579,11 @@ impl<'a> Generator<'a> { instrs.extend(index_instrs); let addr_ty = IRType::Ptr(Box::new(elem_ty.into())); let dest = self.var_manager.declare_temp(addr_ty); - instrs.push(IRInstr::GetElementPtr(dest.clone(), base, index_var.unwrap(), elem_size)); + let elem_size_var = self.var_manager.declare_temp(IRType::I32); + let offset = self.var_manager.declare_temp(IRType::I32); + instrs.push(IRInstr::Move(elem_size_var.clone(), MoveRValue::ConstInt(elem_size as i32))); + instrs.push(IRInstr::Binary(offset.clone(), index_var.unwrap(), AstBinaryOp::Mul.into(), elem_size_var)); + instrs.push(IRInstr::Binary(dest.clone(), base, AstBinaryOp::Add.into(), offset)); Some((instrs, dest)) } } diff --git a/src/ir/types.rs b/src/ir/types.rs index 4f9e382..3e4bd44 100644 --- a/src/ir/types.rs +++ b/src/ir/types.rs @@ -32,8 +32,6 @@ pub enum IRInstr { Move(Variable, MoveRValue), Load(Variable, Variable), Store(Variable, Variable), - GetAddr(Variable, Variable), - GetElementPtr(Variable, Variable, Variable, usize), } impl Display for IRInstr { @@ -53,8 +51,6 @@ impl Display for IRInstr { IRInstr::Move(dest, src) => write!(f, "{} = {}", dest, src), IRInstr::Load(dest, addr) => write!(f, "{} = *{}", dest, addr), IRInstr::Store(addr, value) => write!(f, "*{} = {}", addr, value), - IRInstr::GetAddr(dest, var) => write!(f, "{} = getaddr {}", dest, var), - IRInstr::GetElementPtr(dest, base, index, elem_size) => write!(f, "{} = gep {}, {}, {}", dest, base, index, elem_size), IRInstr::Declare(var) => write!(f, "declare {}", var.to_decl_string()), IRInstr::DefineFunc(func, args, body) => { let body_str = body.iter().map(|instr| format!(" {}", instr)).collect::>().join("\n");