Commit db9e7d2f authored by qinsoon's avatar qinsoon

reg alloc works fine

(for callee saved regs, we insert push/pop before reg alloc. and
after allocation, remove unnecessary ones)
parent ed646119
......@@ -443,6 +443,56 @@ impl MachineCode for ASMCode {
self.code.remove(index);
self.code.insert(index, ASMInst::nop());
}
fn remove_unnecessary_callee_saved(&mut self, used_callee_saved: Vec<MuID>) -> Vec<MuID> {
// we always save rbp
let rbp = x86_64::RBP.extract_ssa_id().unwrap();
// every push/pop will use/define rsp
let rsp = x86_64::RSP.extract_ssa_id().unwrap();
let find_op_other_than_rsp = |inst: &ASMInst| -> Option<MuID> {
for id in inst.defines.keys() {
if *id != rsp && *id != rbp {
return Some(*id);
}
}
for id in inst.uses.keys() {
if *id != rsp && *id != rbp {
return Some(*id);
}
}
None
};
let mut inst_to_remove = vec![];
let mut regs_to_remove = vec![];
for i in 0..self.number_of_insts() {
let ref inst = self.code[i];
if inst.code.contains("push") || inst.code.contains("pop") {
match find_op_other_than_rsp(inst) {
Some(op) => {
// if this push/pop instruction is about a callee saved register
// and the register is not used, we set the instruction as nop
if x86_64::is_callee_saved(op) && !used_callee_saved.contains(&op) {
trace!("removing instruction {:?} for save/restore unnecessary callee saved regs", inst);
regs_to_remove.push(op);
inst_to_remove.push(i);
}
}
None => {}
}
}
}
for i in inst_to_remove {
self.set_inst_nop(i);
}
regs_to_remove
}
fn emit(&self) -> Vec<u8> {
let mut ret = vec![];
......
......@@ -65,7 +65,7 @@ pub trait CodeGenerator {
fn emit_call_near_mem64(&mut self, callsite: String, func: &P<Value>) -> ValueLocation;
fn emit_ret(&mut self);
fn emit_push_r64(&mut self, src: &P<Value>);
fn emit_push_imm32(&mut self, src: i32);
fn emit_pop_r64(&mut self, dest: &P<Value>);
......
......@@ -14,6 +14,7 @@ use runtime::entrypoints::RuntimeEntrypoint;
use compiler::CompilerPass;
use compiler::backend;
use compiler::backend::PROLOGUE_BLOCK_NAME;
use compiler::backend::x86_64;
use compiler::backend::x86_64::CodeGenerator;
use compiler::backend::x86_64::ASMCodeGen;
......@@ -898,7 +899,7 @@ impl <'a> InstructionSelection {
}
fn emit_common_prologue(&mut self, args: &Vec<P<Value>>, vm: &VM) {
let block_name = "prologue".to_string();
let block_name = PROLOGUE_BLOCK_NAME.to_string();
self.backend.start_block(block_name.clone());
// no livein
......@@ -914,10 +915,12 @@ impl <'a> InstructionSelection {
// push all callee-saved registers
{
let frame = self.current_frame.as_mut().unwrap();
let rbp = x86_64::RBP.extract_ssa_id().unwrap();
for i in 0..x86_64::CALLEE_SAVED_GPRs.len() {
let ref reg = x86_64::CALLEE_SAVED_GPRs[i];
// not pushing rbp (as we have done taht)
if reg.extract_ssa_id().unwrap() != x86_64::RBP.extract_ssa_id().unwrap() {
// not pushing rbp (as we have done that)
if reg.extract_ssa_id().unwrap() != rbp {
trace!("allocate frame slot for reg {}", reg);
self.backend.emit_push_r64(&reg);
frame.alloc_slot_for_callee_saved_reg(reg.clone(), vm);
}
......
......@@ -11,6 +11,10 @@ pub const WORD_SIZE : ByteSize = 8;
pub const AOT_EMIT_DIR : &'static str = "emit";
pub const AOT_EMIT_CONTEXT_FILE : &'static str = "context.s";
// this is not full name, but pro/epilogue name is generated from this
pub const PROLOGUE_BLOCK_NAME: &'static str = "prologue";
pub const EPILOGUE_BLOCK_NAME: &'static str = "epilogue";
// X86_64
#[cfg(target_arch = "x86_64")]
......
......@@ -9,6 +9,7 @@ pub use compiler::backend::reg_alloc::graph_coloring::coloring::GraphColoring;
use ast::ir::*;
use vm::VM;
use compiler::CompilerPass;
use compiler::backend::is_callee_saved;
use compiler::backend::init_machine_regs_for_func;
use std::any::Any;
......@@ -58,6 +59,24 @@ impl RegisterAllocation {
}
}
// find out what callee saved registers are used
{
use std::collections::HashSet;
let used_callee_saved: HashSet<MuID> =
coloring.cf.temps.values()
.map(|x| *x)
.filter(|x| is_callee_saved(*x))
.collect();
let used_callee_saved: Vec<MuID> = used_callee_saved.into_iter().collect();
let removed_callee_saved = coloring.cf.mc_mut().remove_unnecessary_callee_saved(used_callee_saved);
for reg in removed_callee_saved {
coloring.cf.frame.remove_record_for_callee_saved_reg(reg);
}
}
coloring.cf.mc().trace_mc();
}
}
......
......@@ -60,6 +60,10 @@ impl Frame {
let slot = self.alloc_slot(&reg, vm);
slot.make_memory_op(reg.ty.clone(), vm)
}
pub fn remove_record_for_callee_saved_reg(&mut self, reg: MuID) {
self.allocated.remove(&reg);
}
pub fn alloc_slot_for_spilling(&mut self, reg: P<Value>, vm: &VM) -> P<Value> {
let slot = self.alloc_slot(&reg, vm);
......
......@@ -10,7 +10,9 @@ use rustc_serialize::{Encodable, Encoder, Decodable, Decoder};
pub struct CompiledFunction {
pub func_id: MuID,
pub func_ver_id: MuID,
pub temps: HashMap<MuID, MuID>, // assumes one temporary maps to one register
// assumes one temporary maps to one register
pub temps: HashMap<MuID, MuID>,
// not emitting this
pub mc: Option<Box<MachineCode + Send + Sync>>,
......@@ -122,10 +124,15 @@ pub trait MachineCode {
// functions for rewrite
/// replace a temp with a machine register (to_reg must be a machine register)
fn replace_reg(&mut self, from: MuID, to: MuID);
/// replace a temp with another temp
/// replace a temp that is defined in the inst with another temp
fn replace_define_tmp_for_inst(&mut self, from: MuID, to: MuID, inst: usize);
/// replace a temp that is used in the inst with another temp
fn replace_use_tmp_for_inst(&mut self, from: MuID, to: MuID, inst: usize);
/// set an instruction as nop
fn set_inst_nop(&mut self, index: usize);
/// remove unnecessary push/pop if the callee saved register is not used
/// returns what registers push/pop have been deleted
fn remove_unnecessary_callee_saved(&mut self, used_callee_saved: Vec<MuID>) -> Vec<MuID>;
fn as_any(&self) -> &Any;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment