WARNING! Access to this system is limited to authorised users only.
Unauthorised users may be subject to prosecution.
Unauthorised access to this system is a criminal offence under Australian law (Federal Crimes Act 1914 Part VIA)
It is a criminal offence to:
(1) Obtain access to data without authority. -Penalty 2 years imprisonment.
(2) Damage, delete, alter or insert data without authority. -Penalty 10 years imprisonment.
User activity is monitored and recorded. Anyone using this system expressly consents to such monitoring and recording.

To protect your data, the CISO officer has suggested users to enable 2FA as soon as possible.
Currently 2.7% of users enabled 2FA.

Commit 1461b6f1 authored by qinsoon's avatar qinsoon
Browse files

[wip] considering branches - need to prune alive entries based on

liveins
parent bdd7e194
......@@ -31,6 +31,7 @@ struct ASMCode {
name: MuName,
code: Vec<ASMInst>,
entry: MuName,
blocks: LinkedHashMap<MuName, ASMBlock>,
frame_size_patchpoints: Vec<ASMLocation>
......@@ -116,6 +117,7 @@ impl ASMCode {
trace!("insert spilling code");
let mut ret = ASMCode {
name: self.name.clone(),
entry: self.entry.clone(),
code: vec![],
blocks: linked_hashmap!{},
frame_size_patchpoints: vec![]
......@@ -483,7 +485,7 @@ impl MachineCode for ASMCode {
Some(inst) if inst.code.starts_with("jmp") => {
let split : Vec<&str> = inst.code.split(' ').collect();
Some(String::from(split[1]))
Some(ASMCodeGen::unmangle_block_label(self.name.clone(), String::from(split[1])))
}
_ => None
}
......@@ -495,7 +497,7 @@ impl MachineCode for ASMCode {
Some(inst) if inst.code.ends_with(':') => {
let split : Vec<&str> = inst.code.split(':').collect();
Some(String::from(split[0]))
Some(ASMCodeGen::unmangle_block_label(self.name.clone(), String::from(split[0])))
}
_ => None
}
......@@ -732,6 +734,10 @@ impl MachineCode for ASMCode {
fn get_all_blocks(&self) -> Vec<MuName> {
self.blocks.keys().map(|x| x.clone()).collect()
}
fn get_entry_block(&self) -> MuName {
self.entry.clone()
}
fn get_block_range(&self, block: &str) -> Option<ops::Range<usize>> {
match self.blocks.get(block) {
......@@ -1221,6 +1227,13 @@ impl ASMCodeGen {
format!("{}_{}", self.cur().name, label)
}
fn unmangle_block_label(fn_name: MuName, label: String) -> MuName {
// input: _fn_name_BLOCK_NAME
// return BLOCK_NAME
let split : Vec<&str> = label.splitn(2, &(fn_name + "_")).collect();
String::from(split[1])
}
fn finish_code_sequence_asm(&mut self) -> Box<ASMCode> {
self.cur.take().unwrap()
}
......@@ -1770,9 +1783,10 @@ fn op_postfix(op_len: usize) -> &'static str {
}
impl CodeGenerator for ASMCodeGen {
fn start_code(&mut self, func_name: MuName) -> ValueLocation {
fn start_code(&mut self, func_name: MuName, entry: MuName) -> ValueLocation {
self.cur = Some(Box::new(ASMCode {
name: func_name.clone(),
entry: entry,
code: vec![],
blocks: linked_hashmap! {},
frame_size_patchpoints: vec![]
......@@ -1806,6 +1820,7 @@ impl CodeGenerator for ASMCodeGen {
fn start_code_sequence(&mut self) {
self.cur = Some(Box::new(ASMCode {
name: "snippet".to_string(),
entry: "none".to_string(),
code: vec![],
blocks: linked_hashmap! {},
frame_size_patchpoints: vec![]
......
......@@ -6,7 +6,7 @@ use compiler::machine_code::MachineCode;
use compiler::backend::{Reg, Mem};
pub trait CodeGenerator {
fn start_code(&mut self, func_name: MuName) -> ValueLocation;
fn start_code(&mut self, func_name: MuName, entry: MuName) -> ValueLocation;
fn finish_code(&mut self, func_name: MuName) -> (Box<MachineCode + Sync + Send>, ValueLocation);
// generate unnamed sequence of linear code (no branch)
......
......@@ -3564,12 +3564,14 @@ impl CompilerPass for InstructionSelection {
fn start_function(&mut self, vm: &VM, func_ver: &mut MuFunctionVersion) {
debug!("{}", self.name());
let entry_block = func_ver.content.as_ref().unwrap().get_entry_block();
self.current_fv_id = func_ver.id();
self.current_frame = Some(Frame::new(func_ver.id()));
self.current_func_start = Some({
let funcs = vm.funcs().read().unwrap();
let func = funcs.get(&func_ver.func_id).unwrap().read().unwrap();
self.backend.start_code(func.name().unwrap())
self.backend.start_code(func.name().unwrap(), entry_block.name().unwrap())
});
self.current_callsite_id = 0;
self.current_exn_callsites.clear();
......@@ -3578,8 +3580,7 @@ impl CompilerPass for InstructionSelection {
self.current_constants.clear();
self.current_constants_locs.clear();
// prologue (get arguments from entry block first)
let entry_block = func_ver.content.as_ref().unwrap().get_entry_block();
// prologue (get arguments from entry block first)
let ref args = entry_block.content.as_ref().unwrap().args;
self.emit_common_prologue(args, vm);
}
......
......@@ -8,6 +8,7 @@ use std::fmt;
type EntryID = usize;
#[derive(Clone)]
pub struct AliveEntries {
index: EntryID,
......@@ -232,8 +233,40 @@ impl AliveEntries {
panic!("Temp{} has more than one entry in AliveEntries");
}
}
pub fn intersect(&mut self, another: &Self) -> bool {
let mut entries_to_delete : Vec<EntryID> = vec![];
let mut changed = false;
for (index, entry) in self.inner.iter_mut() {
if entry.has_temp() {
let temp = entry.get_temp().unwrap();
// find entry with the same temp in the other set, and do intersect
for another_entry in another.find_entries_for_temp(temp) {
if entry.intersect(another_entry) {
changed = true;
}
}
} else {
// find entry without a temp in the other set and do intersect
for another_entry in another.inner.values() {
if !another_entry.has_temp() {
if entry.intersect(another_entry) {
changed = true;
}
}
}
}
}
changed
}
}
#[derive(Clone)]
pub struct RegisterEntry {
temp : Option<MuID>,
real : Vec<MuID>,
......@@ -301,6 +334,28 @@ impl RegisterEntry {
self.stack.push(mem)
}
}
// two entries can intersect only when they have the same temp, or they do not have temps
pub fn intersect(&mut self, another: &Self) -> bool {
assert!(
(!self.has_temp() && !another.has_temp()
|| (self.has_temp() && another.has_temp() && self.get_temp().unwrap() == another.get_temp().unwrap()))
);
let mut changed = false;
// intersect real registers
if vec_utils::intersect(&mut self.real, &another.real) {
changed = true;
}
// intersect memory
if vec_utils::intersect(&mut self.stack, &another.stack) {
changed = true;
}
changed
}
}
impl fmt::Display for RegisterEntry {
......
......@@ -3,6 +3,7 @@ use ast::ir::*;
use ast::ptr::*;
use compiler::machine_code::CompiledFunction;
use compiler::backend::get_color_for_precolored as alias;
use compiler::backend::PROLOGUE_BLOCK_NAME;
mod alive_entry;
use compiler::backend::reg_alloc::validate::alive_entry::*;
......@@ -41,82 +42,143 @@ pub fn validate_regalloc(cf: &CompiledFunction,
alive.new_alive_reg(alias(reg.id()));
}
debug!("alive entries in the beginning");
debug!("---alive entries in the beginning---");
debug!("{}", alive);
let mc = cf.mc();
for i in 0..mc.number_of_insts() {
mc.trace_inst(i);
let mut work_queue : LinkedHashMap<MuName, AliveEntries> = LinkedHashMap::new();
let mut visited : LinkedHashMap<MuName, AliveEntries> = LinkedHashMap::new();
// push entry block
work_queue.insert(PROLOGUE_BLOCK_NAME.to_string(), alive.clone());
if mc.is_jmp(i).is_some() {
// we need to do flow-sensitive analysis
unimplemented!();
}
while !work_queue.is_empty() {
// fetch next block
let (block, mut alive) = work_queue.pop_front().unwrap();
// validate spill
if let Some(spill_loc) = mc.is_spill_load(i) {
// spill load is a move from spill location (mem) to temp
// its define is the scratch temp
let scratch_temp = mc.get_inst_reg_defines(i)[0];
let source_temp = get_source_temp_for_scratch(scratch_temp, &spill_scratch_regs);
// we check if source_temp are alive, and if it is alive in the designated location
validate_spill_load(scratch_temp, source_temp, spill_loc, &reg_spilled, &mut alive);
} else if let Some(spill_loc) = mc.is_spill_store(i) {
// spill store is a move from scratch temp to mem
// it uses scratch temp as well as stack pointer (to refer to mem)
// we try to find the scratch temp
let scratch_temp = {
let uses = mc.get_inst_reg_uses(i);
let mut use_temps = vec![];
for reg in uses {
if reg >= MACHINE_ID_END {
use_temps.push(reg)
}
debug!("---working on block {}---", block);
debug!("{}", alive);
// check inst sequentially
let range = match mc.get_block_range(&block) {
Some(range) => range,
None => panic!("cannot find range for block {}", block)
};
let last_inst = range.end - 1;
for i in range {
mc.trace_inst(i);
// validate spill
if let Some(spill_loc) = mc.is_spill_load(i) {
// spill load is a move from spill location (mem) to temp
// its define is the scratch temp
let scratch_temp = mc.get_inst_reg_defines(i)[0];
let source_temp = get_source_temp_for_scratch(scratch_temp, &spill_scratch_regs);
// we check if source_temp are alive, and if it is alive in the designated location
validate_spill_load(scratch_temp, source_temp, spill_loc, &reg_spilled, &mut alive);
} else if let Some(spill_loc) = mc.is_spill_store(i) {
// spill store is a move from scratch temp to mem
// it uses scratch temp as well as stack pointer (to refer to mem)
// we try to find the scratch temp
let scratch_temp = {
let uses = mc.get_inst_reg_uses(i);
let mut use_temps = vec![];
for reg in uses {
if reg >= MACHINE_ID_END {
use_temps.push(reg)
}
};
assert!(use_temps.len() == 1);
use_temps[0]
};
let source_temp = get_source_temp_for_scratch(scratch_temp, &spill_scratch_regs);
assert!(use_temps.len() == 1);
// we add both scratch_temp, and source_temp as alive
add_spill_store(scratch_temp, source_temp, spill_loc, &reg_spilled, &mut alive);
}
use_temps[0]
};
let source_temp = get_source_temp_for_scratch(scratch_temp, &spill_scratch_regs);
// validate uses of registers
for reg_use in mc.get_inst_reg_uses(i) {
validate_use(reg_use, &reg_assigned, &alive);
}
// we add both scratch_temp, and source_temp as alive
add_spill_store(scratch_temp, source_temp, spill_loc, &reg_spilled, &mut alive);
}
// remove registers that die at this instruction from alive entries
if let Some(kills) = liveness.get_kills(i) {
for reg in kills.iter() {
kill_reg(*reg, &reg_assigned, &mut alive);
}
}
for reg_def in mc.get_inst_reg_defines(i) {
let liveout = liveness.get_liveout(i).unwrap();
// if reg is in the liveout set, we add a define to it
if liveout.contains(&reg_def) {
// add a define
// modify existing alive entries (e.g. kill existing registers)
add_def(reg_def, &reg_assigned, mc.is_move(i), &mut alive);
} else {
// we need to kill the reg, so that other temps cannot use it
// (its value has been defined)
kill_reg(reg_def, &reg_assigned, &mut alive);
}
}
// validate uses of registers
for reg_use in mc.get_inst_reg_uses(i) {
validate_use(reg_use, &reg_assigned, &alive);
debug!("{}", alive);
trace!("---");
}
// remove registers that die at this instruction from alive entries
if let Some(kills) = liveness.get_kills(i) {
for reg in kills.iter() {
kill_reg(*reg, &reg_assigned, &mut alive);
// find succeeding blocks
let succeeding_blocks : Vec<MuName> = mc.get_succs(last_inst).iter()
.map(|x| match mc.is_label(*x - 1) {
Some(label) => label,
None => panic!("cannot find label for inst {}", *x - 1)
}).collect();
// 1) if we have visited this block before, we need to merge (intersect alive entries)
// alive entries is changed, we need to push successors
// 2) if this is our first time visit the block, we push successors
let mut should_push_successors = false;
if visited.contains_key(&block) {
// if current block exists in visited, intersect with current
let mut old = visited.get_mut(&block).unwrap();
let changed = old.intersect(&alive);
if changed {
debug!("we have visted this block before, but intersection made changes. we need to push its sucessors again. ");
should_push_successors = true;
}
} else {
debug!("first time we visited this block, push its successors");
visited.insert(block.clone(), alive.clone());
should_push_successors = true;
}
for reg_def in mc.get_inst_reg_defines(i) {
let liveout = liveness.get_liveout(i).unwrap();
// if reg is in the liveout set, we add a define to it
if liveout.contains(&reg_def) {
// add a define
// modify existing alive entries (e.g. kill existing registers)
add_def(reg_def, &reg_assigned, mc.is_move(i), &mut alive);
} else {
// we need to kill the reg, so that other temps cannot use it
// (its value has been defined)
kill_reg(reg_def, &reg_assigned, &mut alive);
// push successors to work list
if should_push_successors {
if succeeding_blocks.len() == 1 {
// nothing special, just push next block to work list
work_queue.insert(succeeding_blocks[0].clone(), alive.clone());
debug!("push block {} to work list", succeeding_blocks[0]);
} else if succeeding_blocks.len() == 2 {
// conditional branch
// FIXME: need to prune alive entries based on live in
// it is possible that a variable is alive at the end of a BB, and used
// only in one of its successors
work_queue.insert(succeeding_blocks[0].clone(), alive.clone());
work_queue.insert(succeeding_blocks[1].clone(), alive.clone());
debug!("push block {} to work list", succeeding_blocks[0]);
debug!("push block {} to work list", succeeding_blocks[1]);
}
}
debug!("{}", alive);
trace!("---");
//
}
}
......@@ -253,7 +315,7 @@ fn validate_spill_load(scratch_temp: MuID, source_temp: MuID, spill_loc: P<Value
alive: &mut AliveEntries) {
// verify its correct: the source temp should be alive with the mem location
if alive.has_entries_for_temp(source_temp) {
alive.find_entries_for_temp(source_temp).iter().inspect(|entry| {
for entry in alive.find_entries_for_temp(source_temp).iter() {
if entry.match_stack_loc(spill_loc.clone()) {
// valid
} else {
......@@ -262,7 +324,7 @@ fn validate_spill_load(scratch_temp: MuID, source_temp: MuID, spill_loc: P<Value
panic!("validation failed: load a register from a spilled location that is incorrect");
}
});
}
} else {
error!("SourceTemp{} is not alive, loading it from {} as ScratchTemp{} is not valid", scratch_temp, spill_loc, scratch_temp);
......
......@@ -178,6 +178,7 @@ pub trait MachineCode {
fn set_ir_block_liveout(&mut self, block: &str, set: Vec<MuID>);
fn get_all_blocks(&self) -> Vec<MuName>;
fn get_entry_block(&self) -> MuName;
// returns [start_inst, end_inst) // end_inst not included
fn get_block_range(&self, block: &str) -> Option<ops::Range<usize>>;
......
......@@ -84,6 +84,22 @@ pub fn find_value<T: PartialEq> (vec: &Vec<T>, val: T) -> Option<usize> {
None
}
pub fn intersect<T: PartialEq + Clone> (vec: &mut Vec<T>, vec2: &Vec<T>) -> bool {
let mut indices_to_delete = vec![];
for i in 0..vec.len() {
if find_value(vec2, vec[0].clone()).is_none() {
indices_to_delete.push(i);
}
}
for i in indices_to_delete.iter() {
vec.remove(*i);
}
indices_to_delete.len() != 0
}
pub fn remove_value<T: PartialEq> (vec: &mut Vec<T>, val: T) {
match find_value(vec, val) {
Some(index) => {vec.remove(index);},
......
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