fix(backend): Imm value exceeds range

This commit is contained in:
2026-06-06 22:58:47 +08:00
parent 669c415bd7
commit 52f9b67018
3 changed files with 104 additions and 43 deletions
+97 -36
View File
@@ -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<ARMInstr>) {
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<ARMInstr>) {
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<ARMInstr>) {
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<ARMInstr>) {
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<ARMInstr>) {
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<usize, usize>, instrs: &mut Vec<ARMInstr>) -> 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<Variable>, ret: Option<Variable>, var_index_to_stack_offset: &BTreeMap<usize, usize>) {
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<usize, usize>) {
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<usize, usize>) {
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<usize, usize>) {
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");
+7 -3
View File
@@ -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))
}
}
-4
View File
@@ -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::<Vec<_>>().join("\n");