Commit 7f5165b0 authored by qinsoon's avatar qinsoon

[wip] regalloc check for sequential code (not considering stack slots)

parent 37e86924
#![allow(dead_code)]
use utils::LinkedHashMap;
use utils::vec_utils;
use ast::ir::*;
use ast::ptr::*;
use compiler::machine_code::CompiledFunction;
use std::fmt;
pub fn validate_regalloc(cf: &CompiledFunction,
func: &MuFunctionVersion,
reg_assigned: LinkedHashMap<MuID, MuID>,
reg_spilled: LinkedHashMap<MuID, P<Value>>)
{
debug!("---Validating register allocation results---");
let mut alive = AliveEntries::new();
debug!("initializing alive entries for arguments...");
// start with arguments with real locations
let ref frame = cf.frame;
for (temp, reg) in frame.argument_by_reg.iter() {
alive.new_alive_reg(reg.id());
}
for (temp, stack) in frame.argument_by_stack.iter() {
alive.new_alive_mem(stack.clone());
}
debug!("alive entries in the beginning");
debug!("{}", alive);
}
type EntryID = usize;
struct AliveEntries {
pub struct AliveEntries {
index: EntryID,
inner: LinkedHashMap<EntryID, RegisterEntry>
......@@ -40,7 +16,7 @@ struct AliveEntries {
impl fmt::Display for AliveEntries {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "").unwrap();
writeln!(f, "{} entries", self.inner.len()).unwrap();
writeln!(f, "| {:20} | {:20} | {:20} |", "ssa", "registers", "stack slots").unwrap();
for entry in self.inner.values() {
writeln!(f, "{}", entry).unwrap()
......@@ -51,7 +27,7 @@ impl fmt::Display for AliveEntries {
}
impl AliveEntries {
fn new() -> AliveEntries {
pub fn new() -> AliveEntries {
AliveEntries {
index: 0,
inner: LinkedHashMap::new()
......@@ -65,7 +41,27 @@ impl AliveEntries {
ret
}
fn find_entry_for_reg(&self, reg: MuID) -> Option<&RegisterEntry> {
pub fn find_entry_for_temp(&self, temp: MuID) -> Option<&RegisterEntry> {
for entry in self.inner.values() {
if entry.match_temp(temp) {
return Some(entry);
}
}
None
}
pub fn find_entry_for_temp_mut(&mut self, temp: MuID) -> Option<&mut RegisterEntry> {
for entry in self.inner.values_mut() {
if entry.match_temp(temp) {
return Some(entry)
}
}
None
}
pub fn find_entry_for_reg(&self, reg: MuID) -> Option<&RegisterEntry> {
for entry in self.inner.values() {
if entry.match_reg(reg) {
return Some(entry);
......@@ -75,7 +71,7 @@ impl AliveEntries {
None
}
fn find_entry_for_reg_mut(&mut self, reg: MuID) -> Option<&mut RegisterEntry> {
pub fn find_entry_for_reg_mut(&mut self, reg: MuID) -> Option<&mut RegisterEntry> {
for entry in self.inner.values_mut() {
if entry.match_reg(reg) {
return Some(entry);
......@@ -85,7 +81,7 @@ impl AliveEntries {
None
}
fn new_alive_reg(&mut self, reg: MuID) {
pub fn new_alive_reg(&mut self, reg: MuID) {
debug!("adding alive reg: {}", reg);
let id = self.new_index();
......@@ -98,7 +94,7 @@ impl AliveEntries {
self.inner.insert(id, entry);
}
fn new_alive_mem(&mut self, mem: P<Value>) {
pub fn new_alive_mem(&mut self, mem: P<Value>) {
debug!("adding alive mem: {}", mem);
let id = self.new_index();
......@@ -110,18 +106,85 @@ impl AliveEntries {
self.inner.insert(id, entry);
}
pub fn add_temp_in_reg(&mut self, temp: MuID, reg: MuID) {
debug!("adding alive temp in reg: {} in {}", temp, reg);
let entry_exists = self.find_entry_for_temp(temp).is_some();
if entry_exists {
let mut entry = self.find_entry_for_temp_mut(temp).unwrap();
entry.add_real_reg(reg);
} else {
let id = self.new_index();
let entry = RegisterEntry {
temp: Some(temp),
real: vec![reg],
stack: vec![]
};
self.inner.insert(id, entry);
}
}
pub fn remove_reg(&mut self, reg: MuID) {
debug!("removing alive reg: {}", reg);
let mut indices = vec![];
for (index, entry) in self.inner.iter() {
if entry.match_reg(reg) {
indices.push(*index);
}
}
for index in indices {
self.inner.remove(&index);
}
}
pub fn remove_temp(&mut self, reg: MuID) {
debug!("removing alive temp: {}", reg);
let index = {
let mut ret = 0;
for (index, entry) in self.inner.iter() {
if entry.match_temp(reg) {
ret = *index;
}
}
ret
};
self.inner.remove(&index);
}
}
struct RegisterEntry {
pub struct RegisterEntry {
temp : Option<MuID>,
real : Vec<MuID>,
stack : Vec<P<Value>>
}
impl RegisterEntry {
fn match_reg(&self, reg: MuID) -> bool {
pub fn match_temp(&self, temp: MuID) -> bool {
if self.temp.is_some() && self.temp.unwrap() == temp {
true
} else {
false
}
}
pub fn match_reg(&self, reg: MuID) -> bool {
vec_utils::find_value(&self.real, reg).is_some()
}
pub fn add_real_reg(&mut self, reg: MuID) {
if vec_utils::find_value(&mut self.real, reg).is_none() {
self.real.push(reg);
}
}
}
impl fmt::Display for RegisterEntry {
......
use utils::LinkedHashMap;
use utils::LinkedHashSet;
use ast::ir::*;
use compiler::machine_code::CompiledFunction;
pub struct ExactLiveness {
livein : LinkedHashMap<usize, LinkedHashSet<MuID>>,
liveout: LinkedHashMap<usize, LinkedHashSet<MuID>>,
kill : LinkedHashMap<usize, LinkedHashSet<MuID>>
}
impl ExactLiveness {
pub fn new(cf: &CompiledFunction) -> ExactLiveness {
let mut ret = ExactLiveness {
livein: LinkedHashMap::new(),
liveout: LinkedHashMap::new(),
kill: LinkedHashMap::new()
};
ret.liveness_analysis(cf);
ret
}
fn liveness_analysis(&mut self, cf: &CompiledFunction) {
let mc = cf.mc();
for block in mc.get_all_blocks().iter() {
let range = mc.get_block_range(block).unwrap();
let mut liveout : LinkedHashSet<MuID> = LinkedHashSet::from_vec(mc.get_ir_block_liveout(block).unwrap().clone());
for i in range.rev() {
// set liveout
self.liveout.insert(i, liveout.clone());
// compute livein: in[n] <- use[n] + (out[n] - def[n])
for reg_def in mc.get_inst_reg_defines(i) {
liveout.remove(&reg_def);
}
for reg_use in mc.get_inst_reg_uses(i) {
liveout.insert(reg_use);
}
// liveout is livein now
self.livein.insert(i, liveout.clone());
// liveout for prev inst is livein for current inst
}
}
// liveness analysis done
// compute 'kill': if a reg is in livein of an inst, but not liveout, it kills in the inst
for i in self.livein.keys() {
let mut kill : LinkedHashSet<MuID> = LinkedHashSet::new();
let livein = self.livein.get(i).unwrap();
let liveout = self.liveout.get(i).unwrap();
for reg in livein.iter() {
if !liveout.contains(reg) {
kill.insert(*reg);
}
}
self.kill.insert(*i, kill);
}
}
pub fn get_kills(&self, index: usize) -> Option<LinkedHashSet<MuID>> {
match self.kill.get(&index) {
Some(s) => Some(s.clone()),
None => None
}
}
pub fn trace(&self, index: usize) {
if self.livein.contains_key(&index) {
trace!("in : {:?}", self.livein.get(&index).unwrap());
trace!("out : {:?}", self.liveout.get(&index).unwrap());
trace!("kill: {:?}", self.kill.get(&index).unwrap());
}
}
}
\ No newline at end of file
use utils::LinkedHashMap;
use ast::ir::*;
use ast::ptr::*;
use compiler::machine_code::CompiledFunction;
use compiler::backend::get_color_for_precolored as alias;
mod alive_entry;
use compiler::backend::reg_alloc::validate::alive_entry::*;
mod exact_liveness;
use compiler::backend::reg_alloc::validate::exact_liveness::*;
pub fn validate_regalloc(cf: &CompiledFunction,
func: &MuFunctionVersion,
reg_assigned: LinkedHashMap<MuID, MuID>,
reg_spilled: LinkedHashMap<MuID, P<Value>>)
{
debug!("---Validating register allocation results---");
debug!("liveness analysis...");
let liveness = ExactLiveness::new(cf);
for i in 0..cf.mc().number_of_insts() {
cf.mc().trace_inst(i);
liveness.trace(i);
}
let mut alive = AliveEntries::new();
debug!("initializing alive entries for arguments...");
// set up initial states
// machine specific regs, such as program counter, stack pointer, etc
add_machine_specific_regs_at_func_start(&mut alive);
// arguments with real locations
let ref frame = cf.frame;
for (_, reg) in frame.argument_by_reg.iter() {
alive.new_alive_reg(alias(reg.id()));
}
for (_, stack) in frame.argument_by_stack.iter() {
alive.new_alive_mem(stack.clone());
}
debug!("alive entries in the beginning");
debug!("{}", alive);
let mc = cf.mc();
for i in 0..mc.number_of_insts() {
mc.trace_inst(i);
if mc.is_jump(i) {
// we need to flow-sensitive analysis
unimplemented!();
}
for reg_use in mc.get_inst_reg_uses(i) {
validate_use(reg_use, &reg_assigned, &alive);
}
// remove kills in the inst from alive entries
if let Some(kills) = liveness.get_kills(i) {
for reg in kills.iter() {
remove_kill(*reg, &reg_assigned, &mut alive);
}
}
// add defines to alive entries
for reg_def in mc.get_inst_reg_defines(i) {
add_def(reg_def, &reg_assigned, &mut alive);
}
debug!("{}", alive);
trace!("---");
}
}
fn get_machine_reg(reg: MuID, reg_assigned: &LinkedHashMap<MuID, MuID>) -> MuID {
// find machine regs
if reg < MACHINE_ID_END {
reg
} else {
match reg_assigned.get(&reg) {
Some(reg) => *reg,
None => panic!("Temp {} is not assigned to any machine register", reg)
}
}
}
fn validate_use(reg: MuID, reg_assigned: &LinkedHashMap<MuID, MuID>, alive: &AliveEntries) {
if reg < MACHINE_ID_END {
// machine register
// instruction selector choose to use machine registers
// it is not about the correctness of register allocation, we do not verify it here
} else {
let machine_reg = get_machine_reg(reg, reg_assigned);
let temp = reg;
// ensure temp is assigned to the same machine reg in alive entries
if let Some(entry) = alive.find_entry_for_temp(temp) {
if !entry.match_reg(machine_reg) {
error!("Temp{}/MachineReg{} does not match at this point. ", temp, machine_reg);
error!("Temp{} is assigned as {}", temp, entry);
panic!("validation failed: temp-reg pair doesnt match")
}
} else {
error!("Temp{} is not alive at this point. ", temp);
panic!("validation failed: use a temp that is not alive");
}
}
}
fn remove_kill(reg: MuID, reg_assigned: &LinkedHashMap<MuID, MuID>, alive: &mut AliveEntries) {
if reg < MACHINE_ID_END {
if alive.find_entry_for_reg(reg).is_some() {
alive.remove_reg(reg);
}
} else {
let temp = reg;
alive.remove_temp(temp);
}
}
fn add_def(reg: MuID, reg_assigned: &LinkedHashMap<MuID, MuID>, alive: &mut AliveEntries) {
if reg < MACHINE_ID_END {
if alive.find_entry_for_reg(reg).is_none() {
// add new machine register
alive.new_alive_reg(reg);
}
} else {
let machine_reg = get_machine_reg(reg, reg_assigned);
let temp = reg;
alive.add_temp_in_reg(temp, machine_reg);
}
}
#[cfg(target_arch = "x86_64")]
fn add_machine_specific_regs_at_func_start(alive: &mut AliveEntries) {
use compiler::backend::x86_64;
// RIP, RSP, RBP always have valid values
alive.new_alive_reg(x86_64::RIP.id());
alive.new_alive_reg(x86_64::RSP.id());
alive.new_alive_reg(x86_64::RBP.id());
// callee saved regs are alive
alive.new_alive_reg(x86_64::RBX.id());
alive.new_alive_reg(x86_64::R12.id());
alive.new_alive_reg(x86_64::R13.id());
alive.new_alive_reg(x86_64::R14.id());
alive.new_alive_reg(x86_64::R15.id());
}
\ No newline at end of file
......@@ -40,6 +40,24 @@ macro_rules! linked_hashmap {
};
}
#[macro_exprt]
macro_rules! linked_hashset {
(@single $($x:tt)*) => (());
(@count $($rest:expr),*) => (<[()]>::len(&[$(linked_hashset!(@single $rest)),*]));
($($value:expr,)+) => { linked_hashset!($($value),+) };
($($value:expr),*) => {
{
let _cap = linked_hashset!(@count $($key),*);
let mut _map = LinkedHashSet::with_capacity(_cap);
$(
_map.insert($value);
)*
_map
}
};
}
mod address;
pub use address::Address;
pub use address::ObjectReference;
......
......@@ -11,6 +11,12 @@ impl<K: Hash + Eq> LinkedHashSet<K> {
pub fn new() -> Self {
LinkedHashSet(LinkedHashMap::new())
}
pub fn new1(val: K) -> Self {
let mut ret = LinkedHashSet::new();
ret.insert(val);
ret
}
pub fn from_vec(from: Vec<K>) -> Self {
let mut ret = LinkedHashSet::new();
......
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