feat(backend): Support cmp/label/bc/br ir instr
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
use std::{fmt::Display, ops::Add};
|
use std::{fmt::Display, ops::Add};
|
||||||
|
|
||||||
use crate::backend::register_allocator::{REG_FP, REG_LR, REG_PC, REG_R0, REG_R1, REG_R2, REG_R3, REG_R12, REG_SP, Register};
|
use crate::{backend::register_allocator::{REG_FP, REG_LR, REG_PC, REG_R0, REG_R1, REG_R2, REG_R3, REG_R12, REG_SP, Register}, ir::types::CmpOp as IRCmpOp};
|
||||||
pub enum ARMInstr{
|
pub enum ARMInstr{
|
||||||
Move(MoveInstr),
|
Move(MoveInstr),
|
||||||
Load(LoadInstr),
|
Load(LoadInstr),
|
||||||
@@ -10,11 +10,14 @@ pub enum ARMInstr{
|
|||||||
SDiv(SDivInstr),
|
SDiv(SDivInstr),
|
||||||
Add(AddInstr),
|
Add(AddInstr),
|
||||||
Sub(SubInstr),
|
Sub(SubInstr),
|
||||||
|
Rsb(RsbInstr),
|
||||||
Cmp(CmpInstr),
|
Cmp(CmpInstr),
|
||||||
Push(PushInstr),
|
Push(PushInstr),
|
||||||
Pop(PopInstr),
|
Pop(PopInstr),
|
||||||
FunctionHead(String, usize),
|
FunctionHead(String, usize),
|
||||||
Bl(BlInstr),
|
Bl(BlInstr),
|
||||||
|
B(BInstr),
|
||||||
|
Label(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for ARMInstr {
|
impl Display for ARMInstr {
|
||||||
@@ -28,11 +31,14 @@ impl Display for ARMInstr {
|
|||||||
ARMInstr::SDiv(instr) => write!(f, "{}", instr),
|
ARMInstr::SDiv(instr) => write!(f, "{}", instr),
|
||||||
ARMInstr::Add(instr) => write!(f, "{}", instr),
|
ARMInstr::Add(instr) => write!(f, "{}", instr),
|
||||||
ARMInstr::Sub(instr) => write!(f, "{}", instr),
|
ARMInstr::Sub(instr) => write!(f, "{}", instr),
|
||||||
|
ARMInstr::Rsb(instr) => write!(f, "{}", instr),
|
||||||
ARMInstr::Cmp(instr) => write!(f, "{}", instr),
|
ARMInstr::Cmp(instr) => write!(f, "{}", instr),
|
||||||
ARMInstr::Push(instr) => write!(f, "{}", instr),
|
ARMInstr::Push(instr) => write!(f, "{}", instr),
|
||||||
ARMInstr::Pop(instr) => write!(f, "{}", instr),
|
ARMInstr::Pop(instr) => write!(f, "{}", instr),
|
||||||
ARMInstr::Bl(instr) => write!(f, "{}", instr),
|
ARMInstr::Bl(instr) => write!(f, "{}", instr),
|
||||||
|
ARMInstr::B(instr) => write!(f, "{}", instr),
|
||||||
ARMInstr::FunctionHead(name, align_size) => write!(f, ".align {}\n.global {}\n.type {}, %function\n{}:", align_size, name, name, name),
|
ARMInstr::FunctionHead(name, align_size) => write!(f, ".align {}\n.global {}\n.type {}, %function\n{}:", align_size, name, name, name),
|
||||||
|
ARMInstr::Label(name) => write!(f, "{}:", name),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -56,6 +62,18 @@ pub enum ConditionCode {
|
|||||||
Gt,
|
Gt,
|
||||||
Ge,
|
Ge,
|
||||||
}
|
}
|
||||||
|
impl From<IRCmpOp> for ConditionCode {
|
||||||
|
fn from(cmp_op: IRCmpOp) -> Self {
|
||||||
|
match cmp_op {
|
||||||
|
IRCmpOp::Eq => ConditionCode::Eq,
|
||||||
|
IRCmpOp::Ne => ConditionCode::Ne,
|
||||||
|
IRCmpOp::Lt => ConditionCode::Lt,
|
||||||
|
IRCmpOp::Le => ConditionCode::Le,
|
||||||
|
IRCmpOp::Gt => ConditionCode::Gt,
|
||||||
|
IRCmpOp::Ge => ConditionCode::Ge,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
impl Display for ConditionCode {
|
impl Display for ConditionCode {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
let code_str = match self {
|
let code_str = match self {
|
||||||
@@ -89,6 +107,9 @@ impl MoveInstr {
|
|||||||
pub fn new_uncond(dest: Register, src: RegisterOrImm) -> ARMInstr {
|
pub fn new_uncond(dest: Register, src: RegisterOrImm) -> ARMInstr {
|
||||||
ARMInstr::Move(MoveInstr(None, dest, src))
|
ARMInstr::Move(MoveInstr(None, dest, src))
|
||||||
}
|
}
|
||||||
|
pub fn new_cond(condition: ConditionCode, dest: Register, src: RegisterOrImm) -> ARMInstr {
|
||||||
|
ARMInstr::Move(MoveInstr(Some(condition), dest, src))
|
||||||
|
}
|
||||||
pub fn new_sp_to_fp() -> ARMInstr {
|
pub fn new_sp_to_fp() -> ARMInstr {
|
||||||
ARMInstr::Move(MoveInstr(None, REG_FP, RegisterOrImm::Reg(REG_SP)))
|
ARMInstr::Move(MoveInstr(None, REG_FP, RegisterOrImm::Reg(REG_SP)))
|
||||||
}
|
}
|
||||||
@@ -205,20 +226,37 @@ impl SubInstr {
|
|||||||
ARMInstr::Sub(SubInstr(REG_SP, REG_SP, RegisterOrImm::Imm(offset)))
|
ARMInstr::Sub(SubInstr(REG_SP, REG_SP, RegisterOrImm::Imm(offset)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for SubInstr {
|
impl Display for SubInstr {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
let SubInstr(dest, left, right) = self;
|
let SubInstr(dest, left, right) = self;
|
||||||
write!(f, "sub {}, {}, {}", dest, left, right)
|
write!(f, "sub {}, {}, {}", dest, left, right)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub struct CmpInstr(Register, Register);
|
pub struct RsbInstr(Register, Register, RegisterOrImm);
|
||||||
|
impl RsbInstr {
|
||||||
|
pub fn new(dest: Register, left: Register, right: RegisterOrImm) -> ARMInstr {
|
||||||
|
ARMInstr::Rsb(RsbInstr(dest, left, right))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Display for RsbInstr {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let RsbInstr(dest, left, right) = self;
|
||||||
|
write!(f, "rsb {}, {}, {}", dest, left, right)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub struct CmpInstr(Register, RegisterOrImm);
|
||||||
impl Display for CmpInstr {
|
impl Display for CmpInstr {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
let CmpInstr(left, right) = self;
|
let CmpInstr(left, right) = self;
|
||||||
write!(f, "cmp {}, {}", left, right)
|
write!(f, "cmp {}, {}", left, right)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl CmpInstr {
|
||||||
|
pub fn new(left: Register, right: RegisterOrImm) -> ARMInstr {
|
||||||
|
ARMInstr::Cmp(CmpInstr(left, right))
|
||||||
|
}
|
||||||
|
}
|
||||||
pub struct PushInstr(Vec<Register>);
|
pub struct PushInstr(Vec<Register>);
|
||||||
impl PushInstr {
|
impl PushInstr {
|
||||||
pub fn new_push_fp_lr() -> ARMInstr {
|
pub fn new_push_fp_lr() -> ARMInstr {
|
||||||
@@ -272,16 +310,41 @@ impl Display for PopInstr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct BlInstr(String);
|
pub struct BlInstr(Option<ConditionCode>, String);
|
||||||
impl Display for BlInstr {
|
impl Display for BlInstr {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
let BlInstr(func_name) = self;
|
match &self.0 {
|
||||||
write!(f, "bl {}", func_name)
|
Some(condition) => write!(f, "bl{} {}", condition, self.1),
|
||||||
|
None => write!(f, "bl {}", self.1),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BlInstr {
|
impl BlInstr {
|
||||||
pub fn new(func_name: String) -> ARMInstr {
|
pub fn new(label_name: String) -> ARMInstr {
|
||||||
ARMInstr::Bl(BlInstr(func_name))
|
ARMInstr::Bl(BlInstr(None, label_name))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_cond(condition: ConditionCode, label_name: String) -> ARMInstr {
|
||||||
|
ARMInstr::Bl(BlInstr(Some(condition), label_name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub struct BInstr(Option<ConditionCode>, String);
|
||||||
|
impl Display for BInstr {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match &self.0 {
|
||||||
|
Some(condition) => write!(f, "b{} {}", condition, self.1),
|
||||||
|
None => write!(f, "b {}", self.1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BInstr {
|
||||||
|
pub fn new(label_name: String) -> ARMInstr {
|
||||||
|
ARMInstr::B(BInstr(None, label_name))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_cond(condition: ConditionCode, label_name: String) -> ARMInstr {
|
||||||
|
ARMInstr::B(BInstr(Some(condition), label_name))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+128
-61
@@ -1,7 +1,10 @@
|
|||||||
use std::collections::BTreeMap;
|
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::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 const ARM_STACK_ALIGNMENT: usize = 8;
|
||||||
pub struct Generator {
|
pub struct Generator {
|
||||||
instrs: Vec<ARMInstr>,
|
instrs: Vec<ARMInstr>,
|
||||||
@@ -11,6 +14,49 @@ pub struct Generator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_VAR_ALIGN: usize = 4;
|
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 {
|
impl Generator {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
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(ARMInstr::FunctionHead(func.name.clone(), 4)); // Assuming 4-byte alignment for simplicity
|
||||||
self.instrs.push(PushInstr::new_push_fp_lr());
|
self.instrs.push(PushInstr::new_push_fp_lr());
|
||||||
self.instrs.push(MoveInstr::new_sp_to_fp());
|
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 encounter_entry = false;
|
||||||
let mut stack_size_needed = 0;
|
let mut stack_size_needed = 0;
|
||||||
let mut var_index_to_stack_offset = BTreeMap::new();
|
let mut var_index_to_stack_offset = BTreeMap::new();
|
||||||
@@ -73,13 +119,8 @@ impl Generator {
|
|||||||
if let Some(v) = v {
|
if let Some(v) = v {
|
||||||
let ret_alloc = self.register_allocator.alloc_reg(REG_R0).expect("Ran out of registers");
|
let ret_alloc = self.register_allocator.alloc_reg(REG_R0).expect("Ran out of registers");
|
||||||
let ret_reg = ret_alloc.reg;
|
let ret_reg = ret_alloc.reg;
|
||||||
let v_alloc = self.register_allocator.alloc(v).expect("Ran out of registers");
|
let v_alloc = load_variable(v, &mut self.register_allocator, &var_index_to_stack_offset, &mut self.instrs);
|
||||||
let v_reg = v_alloc.reg;
|
self.instrs.push(MoveInstr::new_uncond(ret_reg, RegisterOrImm::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)));
|
|
||||||
}
|
}
|
||||||
self.instrs.push(MoveInstr::new_fp_to_sp());
|
self.instrs.push(MoveInstr::new_fp_to_sp());
|
||||||
self.instrs.push(PopInstr::new_pop_fp_pc());
|
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));
|
self.instrs.push(SubInstr::new_sp(stack_size_needed as i32));
|
||||||
},
|
},
|
||||||
IRInstr::DefineFunc(_, _, _) => unreachable!(),
|
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>) {
|
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());
|
self.instrs.push(PushInstr::new_push_caller_save());
|
||||||
@@ -109,20 +200,15 @@ impl Generator {
|
|||||||
todo!("More than 4 arguments not supported yet");
|
todo!("More than 4 arguments not supported yet");
|
||||||
}
|
}
|
||||||
const ARG_REGS: [Register; 4] = [REG_R0, REG_R1, REG_R2, REG_R3];
|
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() {
|
for (i, arg) in args.into_iter().enumerate() {
|
||||||
let arg_alloc = self.register_allocator.alloc(arg).expect("Ran out of registers");
|
arg_reg_allocs.push(self.register_allocator.alloc_reg(ARG_REGS[i]).expect("Ran out of registers"));
|
||||||
let arg_reg = arg_alloc.reg;
|
let arg_alloc = load_variable(arg, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
|
||||||
if !arg_alloc.is_reused {
|
self.instrs.push(MoveInstr::new_uncond(ARG_REGS[i], RegisterOrImm::Reg(arg_alloc.reg)));
|
||||||
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)));
|
|
||||||
}
|
}
|
||||||
self.instrs.push(BlInstr::new(func.name.clone()));
|
self.instrs.push(BlInstr::new(func.name.clone()));
|
||||||
if let Some(ret) = ret {
|
if let Some(ret) = ret {
|
||||||
let ret_alloc = self.register_allocator.alloc(ret).expect("Ran out of registers");
|
save_variable(ret, REG_R0, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
|
||||||
let ret_reg = ret_alloc.reg;
|
|
||||||
self.instrs.push(MoveInstr::new_uncond(ret_reg, RegisterOrImm::Reg(REG_R0)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -133,66 +219,47 @@ impl Generator {
|
|||||||
let dest_register = dest_alloc.reg;
|
let dest_register = dest_alloc.reg;
|
||||||
match src {
|
match src {
|
||||||
MoveRValue::Var(variable) => {
|
MoveRValue::Var(variable) => {
|
||||||
if !dest_alloc.is_reused {
|
let src_alloc = load_variable(variable, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
|
||||||
let var_stack_offset = var_index_to_stack_offset.get(&variable.index).expect("Variable not found");
|
self.instrs.push(MoveInstr::new_uncond(dest_register, RegisterOrImm::Reg(src_alloc.reg)));
|
||||||
self.instrs.push(LoadInstr::new_stack(dest_register, *var_stack_offset as i32));
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
MoveRValue::ConstInt(literal_int) => self.instrs.push(MoveInstr::new_uncond(dest_register, RegisterOrImm::Imm(literal_int))),
|
MoveRValue::ConstInt(literal_int) => self.instrs.push(MoveInstr::new_uncond(dest_register, RegisterOrImm::Imm(literal_int))),
|
||||||
};
|
};
|
||||||
match dest.var_type {
|
save_variable(dest, dest_alloc.reg, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
fn emit_binary(&mut self, dest: Variable, left: Variable, op: IRBinaryOp, right: Variable, var_index_to_stack_offset: &BTreeMap<usize, usize>) {
|
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 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;
|
let dest_reg = dest_alloc.reg;
|
||||||
if !left_alloc.is_reused {
|
// should consider left == right
|
||||||
let left_offset = var_index_to_stack_offset.get(&left.index).expect("Variable not declared");
|
let left_alloc = load_variable(left, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
|
||||||
self.instrs.push(LoadInstr::new_stack(left_reg, *left_offset as i32));
|
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;
|
||||||
if !right_alloc.is_reused {
|
(Some(right_alloc), right_reg)
|
||||||
let right_offset = var_index_to_stack_offset.get(&right.index).expect("Variable not declared");
|
} else {
|
||||||
self.instrs.push(LoadInstr::new_stack(right_reg, *right_offset as i32));
|
(None, left_alloc.reg)
|
||||||
}
|
};
|
||||||
match op {
|
match op {
|
||||||
IRBinaryOp::Add => {
|
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 => {
|
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 => {
|
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 => {
|
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 => {
|
IRBinaryOp::Mod => {
|
||||||
let temp_reg = self.register_allocator.alloc_any().expect("Ran out of registers");
|
let temp_alloc = 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_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(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");
|
save_variable(dest, dest_alloc.reg, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
|
||||||
self.instrs.push(StoreInstr::new_stack(dest_reg, *dest_stack_offset as i32));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -74,4 +74,8 @@ mod tests {
|
|||||||
test_case("0-3,14-25");
|
test_case("0-3,14-25");
|
||||||
// test_case("0-3,14-25");
|
// test_case("0-3,14-25");
|
||||||
}
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_if_while() {
|
||||||
|
test_case("26-32,34-41,46-51,57");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -109,6 +109,9 @@ impl RegisterAllocator {
|
|||||||
// Variable already has a register allocated
|
// Variable already has a register allocated
|
||||||
let use_kind = inner.register_map.get_mut(®).expect("Inconsistent state: variable has a register but it's not in the register map");
|
let use_kind = inner.register_map.get_mut(®).expect("Inconsistent state: variable has a register but it's not in the register map");
|
||||||
assert!(matches!(use_kind, RegisterUseKind::UsedByVariable(v) | RegisterUseKind::AllocatedToVariable(v) if *v == var));
|
assert!(matches!(use_kind, RegisterUseKind::UsedByVariable(v) | RegisterUseKind::AllocatedToVariable(v) if *v == var));
|
||||||
|
if matches!(use_kind, RegisterUseKind::UsedByVariable(_)) {
|
||||||
|
panic!("variable already actively borrowed");
|
||||||
|
}
|
||||||
*use_kind = RegisterUseKind::UsedByVariable(var);
|
*use_kind = RegisterUseKind::UsedByVariable(var);
|
||||||
return Some(RegisterAlloc {
|
return Some(RegisterAlloc {
|
||||||
allocator: Rc::downgrade(&self.inner),
|
allocator: Rc::downgrade(&self.inner),
|
||||||
@@ -179,13 +182,17 @@ impl RegisterAllocator {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn alloc_any(&mut self) -> Option<Register> {
|
pub fn alloc_any(&mut self) -> Option<RegisterAlloc> {
|
||||||
let mut inner = self.inner.borrow_mut();
|
let mut inner = self.inner.borrow_mut();
|
||||||
|
|
||||||
for (®, use_kind) in inner.register_map.iter_mut() {
|
for (®, use_kind) in inner.register_map.iter_mut() {
|
||||||
if let RegisterUseKind::Free = use_kind {
|
if let RegisterUseKind::Free = use_kind {
|
||||||
*use_kind = RegisterUseKind::Designated;
|
*use_kind = RegisterUseKind::Designated;
|
||||||
return Some(reg);
|
return Some(RegisterAlloc {
|
||||||
|
allocator: Rc::downgrade(&self.inner),
|
||||||
|
reg,
|
||||||
|
is_reused: false,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let RegisterAllocatorInner{register_map, variable_to_register} = &mut *inner;
|
let RegisterAllocatorInner{register_map, variable_to_register} = &mut *inner;
|
||||||
@@ -193,7 +200,11 @@ impl RegisterAllocator {
|
|||||||
if let RegisterUseKind::AllocatedToVariable(ori_var) = use_kind {
|
if let RegisterUseKind::AllocatedToVariable(ori_var) = use_kind {
|
||||||
variable_to_register.remove(&ori_var);
|
variable_to_register.remove(&ori_var);
|
||||||
*use_kind = RegisterUseKind::Designated;
|
*use_kind = RegisterUseKind::Designated;
|
||||||
return Some(reg);
|
return Some(RegisterAlloc {
|
||||||
|
allocator: Rc::downgrade(&self.inner),
|
||||||
|
reg,
|
||||||
|
is_reused: false,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user