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.

Commit 9be20de9 authored by qinsoon's avatar qinsoon
Browse files

implemented loop analysis

parent eb657c64
......@@ -16,19 +16,21 @@ use compiler::CompilerPass;
use ast::ir::*;
use vm::VM;
use compiler::machine_code::*;
use compiler::backend;
use std::any::Any;
use utils::LinkedHashMap;
use utils::LinkedHashSet;
use utils::LinkedMultiMap;
use utils::LinkedRepeatableMultiMap;
use utils::Tree;
use std::any::Any;
const TRACE_DOMTREE: bool = true;
const TRACE_LOOPANALYSIS: bool = true;
pub struct MCDomTree {
pub struct MCLoopAnalysis {
name: &'static str
}
impl CompilerPass for MCDomTree {
impl CompilerPass for MCLoopAnalysis {
fn name(&self) -> &'static str {
self.name
}
......@@ -41,21 +43,59 @@ impl CompilerPass for MCDomTree {
let compiled_funcs = vm.compiled_funcs().read().unwrap();
let mut cf = compiled_funcs.get(&func.id()).unwrap().write().unwrap();
let cfg = cf.mc().build_cfg();
let prologue = cf.mc().get_prologue_block();
let dominators = compute_dominators(&cf, &cfg);
trace!("dominators:");
trace!("---dominators---");
trace!("{:?}", dominators);
let idoms = compute_immediate_dominators(&dominators);
trace!("immediate dominators:");
trace!("---immediate dominators---");
trace!("{:?}", idoms);
let domtree = compute_domtree(prologue.clone(), &idoms);
trace!("---domtree---");
trace!("{:?}", domtree);
let loops = compute_loops(&domtree, &cfg);
trace!("---loops---");
trace!("{:?}", loops);
let merged_loops = compute_merged_loop(&loops);
trace!("---merged loops---");
trace!("{:?}", merged_loops);
let loop_nest_tree = compute_loop_nest_tree(prologue.clone(), &merged_loops);
trace!("---loop-nest tree---");
trace!("{:?}", loop_nest_tree);
let loop_depth = compute_loop_depth(&loop_nest_tree, &merged_loops);
trace!("---loop depth---");
trace!("{:?}", loop_depth);
let result = Box::new(MCLoopAnalysisResult {
domtree,
loops,
loop_nest_tree,
loop_depth
});
cf.loop_analysis = Some(result);
}
}
impl MCDomTree {
pub fn new() -> MCDomTree {
MCDomTree {
name: "Compute Machine Code Dom Tree"
#[allow(dead_code)]
pub struct MCLoopAnalysisResult {
domtree: MCDomTree,
loops: LinkedRepeatableMultiMap<MuName, MCNaturalLoop>,
loop_nest_tree: MCLoopNestTree,
loop_depth: LinkedHashMap<MuName, usize>
}
impl MCLoopAnalysis {
pub fn new() -> MCLoopAnalysis {
MCLoopAnalysis {
name: "Machine Code Loop Analysis"
}
}
}
......@@ -126,24 +166,29 @@ fn compute_immediate_dominators(dominators: &LinkedMultiMap<MuName, MuName>)
let mut immediate_doms: LinkedHashMap<MuName, MuName> = LinkedHashMap::new();
for (n, doms) in dominators.iter() {
trace_if!(TRACE_DOMTREE, "compute idom(n={:?})", n);
trace_if!(TRACE_LOOPANALYSIS, "compute idom(n={:?})", n);
// check which dominator idom from doms is the immediate dominator
// 1. idom is not n
// 2. idom dominates n (for sure if we find idom from n's dominators)
// 3. idom does not dominate any other dominator of n
for candidate in doms.iter() {
trace_if!(TRACE_DOMTREE, " check candidate {:?}", candidate);
trace_if!(TRACE_LOOPANALYSIS, " check candidate {:?}", candidate);
// idom is not n
if candidate != n {
let mut candidate_is_idom = true;
// candidate does not dominate any other dominator of n
for d in doms.iter() {
trace_if!(TRACE_DOMTREE, " check if {:?} doms d={:?}", candidate, d);
trace_if!(
TRACE_LOOPANALYSIS,
" check if {:?} doms d={:?}",
candidate,
d
);
if d != candidate && d != n {
if is_dom(candidate, d, &dominators) {
trace_if!(
TRACE_DOMTREE,
TRACE_LOOPANALYSIS,
" failed, as {:?} dominates other dominator {:?}",
candidate,
d
......@@ -151,18 +196,23 @@ fn compute_immediate_dominators(dominators: &LinkedMultiMap<MuName, MuName>)
candidate_is_idom = false;
}
} else {
trace_if!(TRACE_DOMTREE, " skip, as d==candidate or d==n");
trace_if!(TRACE_LOOPANALYSIS, " skip, as d==candidate or d==n");
}
}
if candidate_is_idom {
assert!(!immediate_doms.contains_key(n));
trace_if!(TRACE_DOMTREE, " add idom({:?}) = {:?}", n, candidate);
trace_if!(
TRACE_LOOPANALYSIS,
" add idom({:?}) = {:?}",
n,
candidate
);
immediate_doms.insert(n.clone(), candidate.clone());
break;
}
} else {
trace_if!(TRACE_DOMTREE, " skip, candidate is n");
trace_if!(TRACE_LOOPANALYSIS, " skip, candidate is n");
}
}
}
......@@ -175,3 +225,197 @@ fn compute_immediate_dominators(dominators: &LinkedMultiMap<MuName, MuName>)
fn is_dom(a: &MuName, b: &MuName, dominators: &LinkedMultiMap<MuName, MuName>) -> bool {
dominators.contains_key_val(b, a)
}
pub type MCDomTree = Tree<MuName>;
fn compute_domtree(entry: MuName, idoms: &LinkedHashMap<MuName, MuName>) -> MCDomTree {
let mut domtree = MCDomTree::new(entry);
// for every other node, there is an edge from
for (x, idom_x) in idoms.iter() {
domtree.insert(idom_x.clone(), x.clone());
}
domtree
}
#[derive(Debug)]
struct MCNaturalLoop {
header: MuName,
backedge: MuName,
blocks: LinkedHashSet<MuName>
}
/// returns a set of lists, which contains blocks in the loop
/// the first element in the list is the block header
fn compute_loops(
domtree: &MCDomTree,
cfg: &MachineCFG
) -> LinkedRepeatableMultiMap<MuName, MCNaturalLoop> {
let mut ret = LinkedRepeatableMultiMap::new();
let mut work_list = vec![domtree.root()];
while !work_list.is_empty() {
let cur = work_list.pop().unwrap();
if let Some(loops) = identify_loop(cur, domtree, cfg) {
ret.insert_vec(cur.clone(), loops);
}
if domtree.has_children(cur) {
for child in domtree.get_children(cur).iter() {
work_list.push(child);
}
}
}
ret
}
fn identify_loop(
header: &MuName,
domtree: &MCDomTree,
cfg: &MachineCFG
) -> Option<Vec<MCNaturalLoop>> {
trace_if!(TRACE_LOOPANALYSIS, "find loop with header {}", header);
let descendants = domtree.get_all_descendants(header);
trace_if!(TRACE_LOOPANALYSIS, "descendants: {:?}", descendants);
let mut ret = None;
for n in descendants.iter() {
if cfg.has_edge(n, header) {
// n -> header is a backedge
let lp = identify_single_loop(header, n, &descendants, cfg);
if ret.is_none() {
ret = Some(vec![lp]);
} else {
ret.as_mut().unwrap().push(lp);
}
}
}
ret
}
fn identify_single_loop(
header: &MuName,
backedge: &MuName,
nodes: &LinkedHashSet<MuName>,
cfg: &MachineCFG
) -> MCNaturalLoop {
trace_if!(
TRACE_LOOPANALYSIS,
"find loop with header {} and backedge {}",
header,
backedge
);
// we want to find all nodes x in 'nodes'
// where there is a path from x to backedge not containing header
let mut loop_blocks = LinkedHashSet::new();
for x in nodes.iter() {
if x == header || x == backedge {
loop_blocks.insert(x.clone());
} else if cfg.has_path_with_node_excluded(x, backedge, header) {
loop_blocks.insert(x.clone());
}
}
MCNaturalLoop {
header: header.clone(),
backedge: backedge.clone(),
blocks: loop_blocks
}
}
#[derive(Debug)]
struct MCMergedLoop {
header: MuName,
backedges: LinkedHashSet<MuName>,
blocks: LinkedHashSet<MuName>
}
fn compute_merged_loop(loops: &LinkedRepeatableMultiMap<MuName, MCNaturalLoop>)
-> LinkedHashMap<MuName, MCMergedLoop> {
let mut merged_loops = LinkedHashMap::new();
for (header, natural_loops) in loops.iter() {
let mut merged_loop = MCMergedLoop {
header: header.clone(),
backedges: LinkedHashSet::new(),
blocks: LinkedHashSet::new()
};
for l in natural_loops.iter() {
merged_loop.backedges.insert(l.backedge.clone());
merged_loop.blocks.add_all(l.blocks.clone());
}
merged_loops.insert(header.clone(), merged_loop);
}
merged_loops
}
type MCLoopNestTree = Tree<MuName>;
fn compute_loop_nest_tree(
root: MuName,
merged_loops: &LinkedHashMap<MuName, MCMergedLoop>
) -> MCLoopNestTree {
trace_if!(TRACE_LOOPANALYSIS, "compute loop-nest tree");
let mut loop_nest_tree = Tree::new(root.clone());
for header in merged_loops.keys() {
trace_if!(TRACE_LOOPANALYSIS, "check loop: {}", header);
// if header appears in other merged loop, then it is a nested loop
// we need to find the most recent outer loop (determined by loop size - number of blocks)
let mut outer_loop_candidate = None;
let mut outer_loop_size = {
use std::usize;
usize::MAX
};
for (outer_header, outer_merged_loop) in merged_loops.iter() {
// nested loop - add an edge from outer loop header to this loop header
if header != outer_header && outer_merged_loop.blocks.contains(header) {
let loop_size = outer_merged_loop.blocks.len();
if loop_size < outer_loop_size {
outer_loop_candidate = Some(outer_header);
outer_loop_size = loop_size;
}
}
}
if let Some(outer_loop) = outer_loop_candidate {
loop_nest_tree.insert(outer_loop.clone(), header.clone());
} else {
// this header is not a nested loop - add an edge from root to this loop header
loop_nest_tree.insert(root.clone(), header.clone());
}
}
loop_nest_tree
}
fn compute_loop_depth(
tree: &MCLoopNestTree,
merged_loops: &LinkedHashMap<MuName, MCMergedLoop>
) -> LinkedHashMap<MuName, usize> {
trace_if!(TRACE_LOOPANALYSIS, "compute loop depth");
let mut ret = LinkedHashMap::new();
record_depth(0, tree.root(), tree, merged_loops, &mut ret);
ret
}
fn record_depth(
depth: usize,
node: &MuName,
tree: &MCLoopNestTree,
merged_loops: &LinkedHashMap<MuName, MCMergedLoop>,
map: &mut LinkedHashMap<MuName, usize>
) {
// insert the header with the deapth
trace_if!(TRACE_LOOPANALYSIS, "Header {} = Depth {}", node, depth);
map.insert(node.clone(), depth);
// also find all the blocks that belong to the header and are not inner loop header
// and insert them with the same depth
if let Some(merged_loop) = merged_loops.get(node) {
for b in merged_loop.blocks.iter() {
if !merged_loops.contains_key(b) {
map.insert(b.clone(), depth);
trace_if!(TRACE_LOOPANALYSIS, "{} = Depth {}", b, depth);
}
}
}
if tree.has_children(node) {
for c in tree.get_children(node).iter() {
record_depth(depth + 1, c, tree, merged_loops, map);
}
}
}
......@@ -15,7 +15,7 @@
/// A instruction selection pass. Uses simple tree pattern matching.
pub mod inst_sel;
/// A Dominator Tree pass for machine code.
pub mod mc_domtree;
pub mod mc_loopanalysis;
/// A register allocation pass. Graph coloring.
pub mod reg_alloc;
/// A peephole optimization pass after register allocation.
......
......@@ -16,13 +16,13 @@ use ast::ir::*;
use ast::ptr::*;
use compiler;
use compiler::frame::*;
use compiler::backend::mc_loopanalysis::MCLoopAnalysisResult;
use runtime::ValueLocation;
use rodal;
use utils::Address;
use utils::LinkedHashMap;
use std::sync::Arc;
use utils::{LinkedHashMap, LinkedHashSet};
use runtime::resolve_symbol;
use rodal;
use std::sync::Arc;
use std;
use std::ops;
use std::collections::HashMap;
......@@ -54,7 +54,10 @@ pub struct CompiledFunction {
/// start location of this compiled function
pub start: ValueLocation,
/// end location of this compiled function
pub end: ValueLocation
pub end: ValueLocation,
/// results of machine code loop analysis
pub loop_analysis: Option<Box<MCLoopAnalysisResult>>
}
rodal_named!(CompiledFunction);
unsafe impl rodal::Dump for CompiledFunction {
......@@ -92,7 +95,8 @@ impl CompiledFunction {
mc: Some(mc),
frame: frame,
start: start_loc,
end: end_loc
end: end_loc,
loop_analysis: None
}
}
......@@ -332,7 +336,7 @@ pub trait MachineCode {
succs: succs
};
trace!("CFGNode {:?}", node);
trace!("{:?}", node);
ret.inner.insert(block.clone(), node);
}
......@@ -362,6 +366,66 @@ impl MachineCFG {
pub fn get_succs(&self, block: &MuName) -> &Vec<MuName> {
&self.inner.get(block).unwrap().succs
}
pub fn has_edge(&self, from: &MuName, to: &MuName) -> bool {
if self.inner.contains_key(from) {
let ref node = self.inner.get(from).unwrap();
for succ in node.succs.iter() {
if succ == to {
return true;
}
}
}
false
}
/// checks if there exists a path between from and to, without excluded node
pub fn has_path_with_node_excluded(
&self,
from: &MuName,
to: &MuName,
exclude_node: &MuName
) -> bool {
// we cannot exclude start and end of the path
assert!(exclude_node != from && exclude_node != to);
if from == to {
true
} else {
// we are doing BFS
// visited nodes
let mut visited: LinkedHashSet<&MuName> = LinkedHashSet::new();
// work queue
let mut work_list: Vec<&MuName> = vec![];
// initialize visited nodes, and work queue
visited.insert(from);
work_list.push(from);
while !work_list.is_empty() {
let n = work_list.pop().unwrap();
for succ in self.get_succs(n) {
if succ == exclude_node {
// we are not going to follow a path with the excluded node
continue;
} else {
// if we are reaching destination, return true
if succ == to {
return true;
}
// push succ to work list so we will traverse them later
if !visited.contains(succ) {
visited.insert(succ);
work_list.push(succ);
}
}
}
}
false
}
}
}
/// MachineCFGNode represents a block in machine code control flow graph
......
......@@ -111,7 +111,7 @@ impl Default for CompilerPolicy {
// compilation
passes.push(Box::new(backend::inst_sel::InstructionSelection::new()));
passes.push(Box::new(backend::mc_domtree::MCDomTree::new()));
passes.push(Box::new(backend::mc_loopanalysis::MCLoopAnalysis::new()));
passes.push(Box::new(backend::reg_alloc::RegisterAllocation::new()));
// machine code level passes
......
......@@ -62,12 +62,16 @@ mod linked_hashmap;
mod linked_hashset;
/// linked multi-key hashmap implementation
mod linked_multimap;
/// tree implementation
mod tree;
// re-export these data structures
pub use linked_hashmap::LinkedHashMap;
pub use linked_hashset::LinkedHashSet;
pub use linked_multimap::LinkedMultiMap;
pub use linked_multimap::LinkedRepeatableMultiMap;
pub use tree::Tree;
pub use self::doubly::DoublyLinkedList;
/// mem module:
......
......@@ -157,6 +157,25 @@ impl<K: Hash + Eq, S: BuildHasher> LinkedHashSet<K, S> {
}
}
impl<K: Hash + Eq> PartialEq for LinkedHashSet<K> {
fn eq(&self, other: &Self) -> bool {
if self.len() != other.len() {
return false;
}
for ele in self.iter() {
if !other.contains(ele) {
return false;
}
}
true
}
fn ne(&self, other: &Self) -> bool {
!self.eq(other)
}
}
impl<K: Hash + Eq> Eq for LinkedHashSet<K> {}
impl<K: Hash + Eq + Clone> Clone for LinkedHashSet<K> {
fn clone(&self) -> Self {
LinkedHashSet(self.0.clone())
......
......@@ -80,8 +80,71 @@ impl<K: Hash + Eq, V: Hash + Eq> LinkedMultiMap<K, V> {
impl<K: Hash + Eq + Debug, V: Hash + Eq + Debug> Debug for LinkedMultiMap<K, V> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "MultiMap").unwrap();
for (k, v) in self.iter() {
write!(f, "{:?} -> {:?}\n", k, v).unwrap();
writeln!(f, "{:?} -> {:?}", k, v).unwrap();
}
Ok(())
}
}
pub struct LinkedRepeatableMultiMap<K, V> {
inner: LinkedHashMap<K, Vec<V>>
}
impl<K: Hash + Eq, V> LinkedRepeatableMultiMap<K, V> {
pub fn new() -> LinkedRepeatableMultiMap<K, V> {
LinkedRepeatableMultiMap {
inner: LinkedHashMap::new()
}
}
pub fn insert(&mut self, k: K, v: V) {
if self.inner.contains_key(&k) {
self.inner.get_mut(&k).unwrap().push(v);
} else {
self.inner.insert(k, vec![v]);
}
}
pub fn insert_vec(&mut self, k: K, mut vec: Vec<V>) {
if self.inner.contains_key(&k) {
self.inner.get_mut(&k).unwrap().append(&mut vec);
} else {
self.inner.insert(k, vec);
}
}
pub fn replace_set(&mut self, k: K, vec: Vec<V>) {
self.inner.insert(k, vec);
}
pub fn get(&self, k: &K) -> Option<&Vec<V>> {
self.inner.get(k)
}
pub fn contains_key(&self, k: &K) -> bool {
self.inner.contains_key(k)
}
pub fn len(&self) -> usize {
self.inner.len()
}
pub fn iter(&self) -> Iter<K, Vec<V>> {
self.inner.iter()
}
pub fn keys(&self) -> Keys<K, Vec<V>> {
self.inner.keys()
}
}
impl<K: Hash + Eq + Debug, V: Debug> Debug for LinkedRepeatableMultiMap<K, V> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "RepeatableMultiMap").unwrap();
for (k, v) in self.iter() {
writeln!(f, "{:?} -> {:?}", k, v).unwrap();
}
Ok(())
}
......
// Copyright 2017 The Australian National University
//