feat(backend): Support cmp/label/bc/br ir instr

This commit is contained in:
2026-05-15 11:04:05 +08:00
parent ace1170646
commit ef11ae9d96
4 changed files with 217 additions and 72 deletions
+128 -61
View File
@@ -1,7 +1,10 @@
use std::collections::BTreeMap;
use crate::{backend::{arm_instr::{ARMInstr, AddInstr, BlInstr, LoadInstr, LoadPseudoInstr, MoveInstr, MulInstr, PopInstr, PushInstr, RegisterOrImm, SDivInstr, StoreInstr, SubInstr}, register_allocator::{REG_R0, REG_R1, REG_R2, REG_R3, Register, RegisterAlloc, RegisterAllocator}, types::ARMAsmVar}, ir::types::{Function, IRInstr, MoveRValue, Variable, 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_R0, REG_R1, REG_R2, REG_R3, 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;
pub const ARM_STACK_ALIGNMENT: usize = 8;
pub struct Generator {
instrs: Vec<ARMInstr>,
@@ -11,6 +14,49 @@ pub struct Generator {
}
const DEFAULT_VAR_ALIGN: usize = 4;
fn load_variable(variable: Variable, reg_allocator: &mut RegisterAllocator, var_index_to_stack_offset: &BTreeMap<usize, usize>, instrs: &mut Vec<ARMInstr>) -> RegisterAlloc {
match variable.var_type {
VariableType::Global => {
let var_alloc = reg_allocator.alloc(variable).expect("Ran out of registers");
let var_reg = var_alloc.reg;
// if !var_alloc.is_reused {
let address_alloc = reg_allocator.alloc_any().expect("Ran out of registers");
instrs.push(LoadPseudoInstr::new(address_alloc.reg, format!("global_var_{}", variable.index)));
instrs.push(LoadInstr::new(var_reg, address_alloc.reg, None));
// }
var_alloc
},
VariableType::ParamTemp => {
todo!()
},
_ => {
let stack_offset = var_index_to_stack_offset.get(&variable.index).expect("Variable not declared");
let var_alloc = reg_allocator.alloc(variable).expect("Ran out of registers");
// if !var_alloc.is_reused {
instrs.push(LoadInstr::new_stack(var_alloc.reg, *stack_offset as i32));
// }
var_alloc
}
}
}
fn save_variable(variable: Variable, reg: Register, reg_allocator: &mut RegisterAllocator, var_index_to_stack_offset: &BTreeMap<usize, usize>, instrs: &mut Vec<ARMInstr>) {
match variable.var_type {
VariableType::Global => {
let address_alloc = reg_allocator.alloc_any().expect("Ran out of registers");
instrs.push(LoadPseudoInstr::new(address_alloc.reg, format!("global_var_{}", variable.index)));
instrs.push(StoreInstr::new(reg, address_alloc.reg, None));
},
VariableType::ParamTemp => {
todo!()
},
_ => {
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));
}
}
}
impl Generator {
pub fn new() -> Self {
Self {
@@ -59,10 +105,10 @@ impl Generator {
self.instrs.push(ARMInstr::FunctionHead(func.name.clone(), 4)); // Assuming 4-byte alignment for simplicity
self.instrs.push(PushInstr::new_push_fp_lr());
self.instrs.push(MoveInstr::new_sp_to_fp());
self.emit_func(body);
self.emit_func(&func.name, body);
}
fn emit_func(&mut self, instrs: Vec<IRInstr>) {
fn emit_func(&mut self, func_name: &str, instrs: Vec<IRInstr>) {
let mut encounter_entry = false;
let mut stack_size_needed = 0;
let mut var_index_to_stack_offset = BTreeMap::new();
@@ -73,13 +119,8 @@ impl Generator {
if let Some(v) = v {
let ret_alloc = self.register_allocator.alloc_reg(REG_R0).expect("Ran out of registers");
let ret_reg = ret_alloc.reg;
let v_alloc = self.register_allocator.alloc(v).expect("Ran out of registers");
let v_reg = v_alloc.reg;
if !v_alloc.is_reused {
let v_stack_offset = var_index_to_stack_offset.get(&v.index).expect("Variable not declared");
self.instrs.push(LoadInstr::new_stack(v_reg, *v_stack_offset as i32));
}
self.instrs.push(MoveInstr::new_uncond(ret_reg, RegisterOrImm::Reg(v_reg)));
let v_alloc = load_variable(v, &mut self.register_allocator, &var_index_to_stack_offset, &mut self.instrs);
self.instrs.push(MoveInstr::new_uncond(ret_reg, RegisterOrImm::Reg(v_alloc.reg)));
}
self.instrs.push(MoveInstr::new_fp_to_sp());
self.instrs.push(PopInstr::new_pop_fp_pc());
@@ -98,10 +139,60 @@ impl Generator {
self.instrs.push(SubInstr::new_sp(stack_size_needed as i32));
},
IRInstr::DefineFunc(_, _, _) => unreachable!(),
_ => unimplemented!(),
IRInstr::Cmp(variable, left, cmp_op, right) => self.emit_cmp(variable, left, cmp_op, right, &var_index_to_stack_offset),
IRInstr::Unary(variable, unary_op, variable1) => self.emit_unary(variable, unary_op, variable1, &var_index_to_stack_offset),
IRInstr::Goto(index) => self.instrs.push(BInstr::new(format!("label_{}_{}", func_name, index))),
IRInstr::CondGoto(variable, true_label_index, false_label_index) => {
let variable_alloc = load_variable(variable, &mut self.register_allocator, &var_index_to_stack_offset, &mut self.instrs);
let variable_reg = variable_alloc.reg;
self.instrs.push(CmpInstr::new(variable_reg, RegisterOrImm::Imm(0)));
self.instrs.push(BInstr::new_cond(ConditionCode::Ne, format!("label_{}_{}", func_name, true_label_index)));
self.instrs.push(BInstr::new(format!("label_{}_{}", func_name, false_label_index)));
},
IRInstr::Label(index) => self.instrs.push(ARMInstr::Label(format!("label_{}_{}", func_name, index))),
}
}
}
fn emit_unary(&mut self, dest: Variable, unary_op: IRUnaryOp, variable: Variable, var_index_to_stack_offset: &BTreeMap<usize, usize>) {
let variable_alloc = load_variable(variable, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
let dest_alloc = self.register_allocator.alloc(dest).expect("Ran out of registers");
match unary_op {
IRUnaryOp::Neg => {
self.instrs.push(RsbInstr::new(dest_alloc.reg, variable_alloc.reg, RegisterOrImm::Imm(0)));
},
}
save_variable(dest, dest_alloc.reg, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
}
fn emit_cmp(&mut self, variable: Variable, left: VariableOrIntLit, cmp_op: IRCmpOp, right: VariableOrIntLit, var_index_to_stack_offset: &BTreeMap<usize, usize>) {
let left_alloc = match left {
VariableOrIntLit::Var(var) => {
load_variable(var, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs)
},
VariableOrIntLit::IntLit(lit) => {
let alloc = self.register_allocator.alloc_any().expect("Ran out of registers");
self.instrs.push(MoveInstr::new_uncond(alloc.reg, RegisterOrImm::Imm(lit)));
alloc
},
};
// TODO: check left == right
let right_alloc = match right {
VariableOrIntLit::Var(var) => {
load_variable(var, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs)
},
VariableOrIntLit::IntLit(lit) => {
let alloc = self.register_allocator.alloc_any().expect("Ran out of registers");
self.instrs.push(MoveInstr::new_uncond(alloc.reg, RegisterOrImm::Imm(lit)));
alloc
},
};
let variable_alloc = self.register_allocator.alloc(variable).expect("Ran out of registers");
let variable_reg = variable_alloc.reg;
self.instrs.push(CmpInstr::new(left_alloc.reg, RegisterOrImm::Reg(right_alloc.reg)));
self.instrs.push(MoveInstr::new_uncond(variable_reg, RegisterOrImm::Imm(0)));
self.instrs.push(MoveInstr::new_cond(cmp_op.into(), variable_reg, RegisterOrImm::Imm(1)));
save_variable(variable, variable_alloc.reg, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
}
fn emit_func_call(&mut self, func: Function, args: Vec<Variable>, ret: Option<Variable>, var_index_to_stack_offset: &BTreeMap<usize, usize>) {
self.instrs.push(PushInstr::new_push_caller_save());
@@ -109,20 +200,15 @@ impl Generator {
todo!("More than 4 arguments not supported yet");
}
const ARG_REGS: [Register; 4] = [REG_R0, REG_R1, REG_R2, REG_R3];
let mut arg_reg_allocs = Vec::new();
for (i, arg) in args.into_iter().enumerate() {
let arg_alloc = self.register_allocator.alloc(arg).expect("Ran out of registers");
let arg_reg = arg_alloc.reg;
if !arg_alloc.is_reused {
let arg_stack_offset = var_index_to_stack_offset.get(&arg.index).expect("Variable not declared");
self.instrs.push(LoadInstr::new_stack(arg_reg, *arg_stack_offset as i32));
}
self.instrs.push(MoveInstr::new_uncond(ARG_REGS[i], RegisterOrImm::Reg(arg_reg)));
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)));
}
self.instrs.push(BlInstr::new(func.name.clone()));
if let Some(ret) = ret {
let ret_alloc = self.register_allocator.alloc(ret).expect("Ran out of registers");
let ret_reg = ret_alloc.reg;
self.instrs.push(MoveInstr::new_uncond(ret_reg, RegisterOrImm::Reg(REG_R0)));
save_variable(ret, REG_R0, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
}
@@ -133,66 +219,47 @@ impl Generator {
let dest_register = dest_alloc.reg;
match src {
MoveRValue::Var(variable) => {
if !dest_alloc.is_reused {
let var_stack_offset = var_index_to_stack_offset.get(&variable.index).expect("Variable not found");
self.instrs.push(LoadInstr::new_stack(dest_register, *var_stack_offset as i32));
}
let src_alloc = load_variable(variable, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
self.instrs.push(MoveInstr::new_uncond(dest_register, RegisterOrImm::Reg(src_alloc.reg)));
},
MoveRValue::ConstInt(literal_int) => self.instrs.push(MoveInstr::new_uncond(dest_register, RegisterOrImm::Imm(literal_int))),
};
match dest.var_type {
VariableType::Global => {
let address_reg = self.register_allocator.alloc_any().expect("Ran out of registers");
self.instrs.push(LoadPseudoInstr::new(address_reg, format!("global_var_{}", dest.index)));
self.instrs.push(StoreInstr::new(dest_register, address_reg, None));
},
VariableType::ParamTemp => {
todo!()
},
_ => {
let offset = *var_index_to_stack_offset.get(&dest.index).expect("Variable not declared");
self.instrs.push(StoreInstr::new_stack(dest_register, offset as i32));
}
}
save_variable(dest, dest_alloc.reg, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
}
fn emit_binary(&mut self, dest: Variable, left: Variable, op: IRBinaryOp, right: Variable, var_index_to_stack_offset: &BTreeMap<usize, usize>) {
let left_alloc = self.register_allocator.alloc(left).expect("Ran out of registers");
let right_alloc = self.register_allocator.alloc(right).expect("Ran out of registers");
let dest_alloc = self.register_allocator.alloc(dest).expect("Ran out of registers");
let left_reg = left_alloc.reg;
let right_reg = right_alloc.reg;
let dest_reg = dest_alloc.reg;
if !left_alloc.is_reused {
let left_offset = var_index_to_stack_offset.get(&left.index).expect("Variable not declared");
self.instrs.push(LoadInstr::new_stack(left_reg, *left_offset as i32));
}
if !right_alloc.is_reused {
let right_offset = var_index_to_stack_offset.get(&right.index).expect("Variable not declared");
self.instrs.push(LoadInstr::new_stack(right_reg, *right_offset as i32));
}
// should consider left == right
let left_alloc = load_variable(left, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
let (_right_alloc, right_reg) = if left != right {
let right_alloc = load_variable(right, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
let right_reg = right_alloc.reg;
(Some(right_alloc), right_reg)
} else {
(None, left_alloc.reg)
};
match op {
IRBinaryOp::Add => {
self.instrs.push(AddInstr::new(dest_reg, left_reg, RegisterOrImm::Reg(right_reg)));
self.instrs.push(AddInstr::new(dest_reg, left_alloc.reg, RegisterOrImm::Reg(right_reg)));
},
IRBinaryOp::Sub => {
self.instrs.push(SubInstr::new(dest_reg, left_reg, RegisterOrImm::Reg(right_reg)));
self.instrs.push(SubInstr::new(dest_reg, left_alloc.reg, RegisterOrImm::Reg(right_reg)));
},
IRBinaryOp::Mul => {
self.instrs.push(MulInstr::new(dest_reg, left_reg, RegisterOrImm::Reg(right_reg)));
self.instrs.push(MulInstr::new(dest_reg, left_alloc.reg, RegisterOrImm::Reg(right_reg)));
},
IRBinaryOp::Div => {
self.instrs.push(SDivInstr::new(dest_reg, left_reg, RegisterOrImm::Reg(right_reg)));
self.instrs.push(SDivInstr::new(dest_reg, left_alloc.reg, RegisterOrImm::Reg(right_reg)));
},
IRBinaryOp::Mod => {
let temp_reg = self.register_allocator.alloc_any().expect("Ran out of registers");
self.instrs.push(SDivInstr::new(temp_reg, left_reg, RegisterOrImm::Reg(right_reg)));
let temp_alloc = self.register_allocator.alloc_any().expect("Ran out of registers");
let temp_reg = temp_alloc.reg;
self.instrs.push(SDivInstr::new(temp_reg, left_alloc.reg, RegisterOrImm::Reg(right_reg)));
self.instrs.push(MulInstr::new(temp_reg, temp_reg, RegisterOrImm::Reg(right_reg)));
self.instrs.push(SubInstr::new(dest_reg, left_reg, RegisterOrImm::Reg(temp_reg)));
self.instrs.push(SubInstr::new(dest_reg, left_alloc.reg, RegisterOrImm::Reg(temp_reg)));
},
}
let dest_stack_offset = var_index_to_stack_offset.get(&dest.index).expect("Variable not declared");
self.instrs.push(StoreInstr::new_stack(dest_reg, *dest_stack_offset as i32));
save_variable(dest, dest_alloc.reg, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
}
}