Skip to content

Commit

Permalink
Performance improvements, various fixes
Browse files Browse the repository at this point in the history
* Fix issues with graph simplification pass
* Improve performance of passes and algorithm implementations
* Trait refactoring
  • Loading branch information
hansihe committed May 27, 2020
1 parent 2daf708 commit 1838ac2
Show file tree
Hide file tree
Showing 40 changed files with 671 additions and 238 deletions.
22 changes: 22 additions & 0 deletions libeir_interpreter/src/erl_lib/maps.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use libeir_intern::Symbol;

use crate::vm::VMState;
use crate::module::{NativeModule, NativeReturn};
use crate::process::ProcessContext;

use crate::term::{Term, MapTerm};

use std::rc::Rc;

fn new_0(_vm: &VMState, _proc: &mut ProcessContext, args: &[Rc<Term>]) -> NativeReturn {
assert!(args.len() == 0);
NativeReturn::Return {
term: Term::Map(MapTerm::new()).into(),
}
}

pub fn make_maps() -> NativeModule {
let mut module = NativeModule::new(Symbol::intern("maps"));
module.add_fun(Symbol::intern("new"), 0, Box::new(new_0));
module
}
3 changes: 3 additions & 0 deletions libeir_interpreter/src/erl_lib/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@ pub use self::math::make_math;

//mod file;
//pub use self::file::make_time;

mod maps;
pub use self::maps::make_maps;
2 changes: 1 addition & 1 deletion libeir_interpreter/src/process/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ impl CallExecutor {
for (v, t) in live.iter().zip(env.iter()) {
self.binds.insert(v, t.clone());
}
assert!(live.size() == env.len());
assert!(live.iter().count() == env.len());

block
} else {
Expand Down
21 changes: 19 additions & 2 deletions libeir_interpreter/src/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,27 @@ impl VMState {

pub fn add_erlang_module(&mut self, module: Module) {
let erl_mod = ErlangModule::from_eir(module);
self.modules.insert(erl_mod.name, ModuleType::Erlang(erl_mod, None));
match self.modules.remove(&erl_mod.name) {
None => {
self.modules.insert(erl_mod.name, ModuleType::Erlang(erl_mod, None));
},
Some(ModuleType::Native(native)) => {
self.modules.insert(erl_mod.name, ModuleType::Erlang(erl_mod, Some(native)));
},
_ => panic!(),
}
}

pub fn add_native_module(&mut self, module: NativeModule) {
self.modules.insert(module.name, ModuleType::Native(module));
match self.modules.remove(&module.name) {
None => {
self.modules.insert(module.name, ModuleType::Native(module));
},
Some(ModuleType::Erlang(erl, None)) => {
self.modules.insert(module.name, ModuleType::Erlang(erl, Some(module)));
},
_ => panic!(),
}
}

pub fn add_nif_overlay(&mut self, module: NativeModule) {
Expand All @@ -78,6 +94,7 @@ impl VMState {
self.add_native_module(crate::erl_lib::make_erlang());
self.add_native_module(crate::erl_lib::make_lists());
self.add_native_module(crate::erl_lib::make_math());
self.add_native_module(crate::erl_lib::make_maps());
}

pub fn call(&mut self, fun: &FunctionIdent, args: &[Term]) -> Result<Rc<Term>, (Rc<Term>, Rc<Term>, Rc<Term>)> {
Expand Down
5 changes: 5 additions & 0 deletions libeir_ir/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ lazy_static = "1.2.0"
itertools = "0.8.0"

cranelift-entity = "0.56.0"
cranelift-bforest = { path = "../../wasmtime/cranelift/bforest" }

bumpalo = { git = "https://github.com/hansihe/bumpalo", branch = "nightly_alloc", features = ["nightly", "collections"] }

petgraph = "0.4"
Expand All @@ -40,6 +42,9 @@ lalrpop-util = "0.17"

snafu = "0.5"

fnv = "1.0.3"
hashbrown = { git = "https://github.com/hansihe/hashbrown.git", features = ["raw", "nightly"] }

[dev-dependencies]
pretty_assertions = "0.6"

Expand Down
2 changes: 0 additions & 2 deletions libeir_ir/src/algo/func_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,6 @@ impl Function {
functions,
};

println!("{:?}", tree);
assert!(tree.validate_no_cycles(tree.root_fun, 0, tree.functions.len()).is_some());

tree
Expand All @@ -228,7 +227,6 @@ impl Function {
impl FunctionTree {

fn validate_no_cycles(&self, entry: Block, mut curr: usize, limit: usize) -> Option<usize> {
println!("curr: {} limit: {}", curr, limit);
if curr > limit {
return None;
}
Expand Down
97 changes: 58 additions & 39 deletions libeir_ir/src/algo/live.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
use std::collections::HashMap;

use crate::{ Function };
use crate::{ Block, Value };
use crate::Function;
use crate::{Block, Value};

use libeir_util_datastructures::pooled_entity_set::{
EntitySetPool, EntitySet,
BoundEntitySet,
};
use libeir_util_datastructures::aux_traits::{HasAux, AuxDebug, AuxImpl};
use cranelift_bforest::{Set, SetForest, BoundSet};

impl Function {

Expand All @@ -27,41 +25,62 @@ impl Function {
/// For CFGs that are acyclic, this algorithm will complete in a single
/// iteration. For cyclic CFGs, this should take (around) 1 extra iteration
/// for every additional nested cycle.
#[derive(Debug, Clone)]
#[derive(Clone)]
pub struct LiveValues {
/// Values that need to exist at every block.
/// Before block arguments.
live_at: HashMap<Block, EntitySet<Value>>,
live_at: HashMap<Block, Set<Value>>,
/// Values that need to exist within every block.
/// After block arguments, before operation.
live_in: HashMap<Block, EntitySet<Value>>,
live_in: HashMap<Block, Set<Value>>,
/// The pool where `ebb_live` and `flow_live` is allocated.
pool: EntitySetPool<Value>,
forest: SetForest<Value>,
}

impl HasAux<SetForest<Value>> for LiveValues {
fn get_aux(&self) -> &SetForest<Value> {
&self.forest
}
}

impl<C: HasAux<SetForest<Value>>> AuxDebug<C> for LiveValues {
fn aux_fmt(&self, f: &mut std::fmt::Formatter<'_>, _aux: &C) -> std::fmt::Result {
let mut b = f.debug_struct("LiveValues");
b.field("live_at", &AuxImpl(&self.live_at, self));
b.field("live_in", &AuxImpl(&self.live_in, self));
b.finish()
}
}

impl std::fmt::Debug for LiveValues {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
self.aux_fmt(f, self)
}
}

impl LiveValues {

pub fn live_at<'a>(&'a self, block: Block) -> BoundEntitySet<'a, Value> {
self.live_at[&block].bind(&self.pool)
pub fn live_at<'a>(&'a self, block: Block) -> BoundSet<'a, Value, ()> {
self.live_at[&block].bind(&self.forest, &())
}
pub fn live_in<'a>(&'a self, block: Block) -> BoundEntitySet<'a, Value> {
self.live_in[&block].bind(&self.pool)
pub fn live_in<'a>(&'a self, block: Block) -> BoundSet<'a, Value, ()> {
self.live_in[&block].bind(&self.forest, &())
}

pub fn is_live_at(&self, block: Block, value: Value) -> bool {
self.live_at[&block].contains(value, &self.pool)
self.live_at[&block].contains(value, &self.forest, &())
}
pub fn is_live_in(&self, block: Block, value: Value) -> bool {
self.live_in[&block].contains(value, &self.pool)
self.live_in[&block].contains(value, &self.forest, &())
}

}

fn dataflow_pass(
fun: &Function,
pool: &mut EntitySetPool<Value>,
live: &mut HashMap<Block, EntitySet<Value>>,
live_in: &mut HashMap<Block, EntitySet<Value>>,
pool: &mut SetForest<Value>,
live: &mut HashMap<Block, Set<Value>>,
live_in: &mut HashMap<Block, Set<Value>>,
) -> bool {

let graph = fun.block_graph();
Expand All @@ -72,12 +91,12 @@ fn dataflow_pass(
// For each Op node in the cfg
while let Some(block) = visitor.next(&graph) {

let mut set: EntitySet<Value> = EntitySet::new();
let mut set: Set<Value> = Set::new();

// For each of the outgoing branches, add its live values to the current set
for branch in graph.outgoing(block) {
if let Some(vals) = live.get(&branch) {
set.union(vals, pool);
set.union_from(vals, pool, &());
}
}

Expand All @@ -86,27 +105,27 @@ fn dataflow_pass(
// Only insert if it actually is a variable, not a block or constant
fun.value_walk_nested_values::<_, ()>(*read, &mut |v| {
if fun.value_argument(v).is_some() {
set.insert(v, pool);
set.insert(v, pool, &());
}
Ok(())
}).unwrap();
}

// Update the live_after values
if !live_in.contains_key(&block) {
live_in.insert(block, EntitySet::new());
live_in.insert(block, Set::new());
}
live_in.get_mut(&block).unwrap().union(&set, pool);
live_in.get_mut(&block).unwrap().union_from(&set, pool, &());

// Remove the block arguments from the current set
for arg in fun.block_args(block) {
set.remove(*arg, pool);
set.remove(*arg, pool, &());
}

// If we don't have a previous live calculation for this block, or if
// the live set has changed, we are not yet stable.
if let Some(mut old_set) = live.remove(&block) {
if !old_set.eq(&set, pool) {
if !old_set.iter(pool).eq(set.iter(pool)) {
stable = false;
}
old_set.clear(pool);
Expand All @@ -123,16 +142,16 @@ fn dataflow_pass(
}

pub fn calculate_live_values(fun: &Function) -> LiveValues {
let mut pool = EntitySetPool::new();
let mut forest = SetForest::new();

let mut live_at: HashMap<Block, EntitySet<Value>> = HashMap::new();
let mut live_in: HashMap<Block, EntitySet<Value>> = HashMap::new();
let mut live_at: HashMap<Block, Set<Value>> = HashMap::new();
let mut live_in: HashMap<Block, Set<Value>> = HashMap::new();

// Iterate dataflow until all dependencies have been resolved
loop {
let res = dataflow_pass(
fun,
&mut pool,
&mut forest,
&mut live_at,
&mut live_in,
);
Expand All @@ -142,11 +161,11 @@ pub fn calculate_live_values(fun: &Function) -> LiveValues {
// Validate that the live set at entry is empty
{
let entry = fun.block_entry();
assert!(live_at[&entry].size(&pool) == 0, "{:?}", live_at[&entry].bind(&pool));
assert!(live_at[&entry].iter(&forest).count() == 0, "{:?}", live_at[&entry].bind(&forest, &()));
}

LiveValues {
pool,
forest,
live_at,
live_in,
}
Expand Down Expand Up @@ -178,14 +197,14 @@ a'foo':a'bar'/1 {
let live = ir.live_values();

let b1_live = live.live_at(b1);
assert!(b1_live.size() == 0);
assert!(b1_live.iter().count() == 0);

let b2_live = live.live_at(b2);
assert!(b2_live.size() == 1);
assert!(b2_live.iter().count() == 1);
assert!(b2_live.contains(b1_ret));

let b3_live = live.live_at(b3);
assert!(b3_live.size() == 1);
assert!(b3_live.iter().count() == 1);
assert!(b3_live.contains(b1_ret));
}

Expand Down Expand Up @@ -220,19 +239,19 @@ a'foo':a'bar'/1 {
let live = ir.live_values();

let b1_live = live.live_at(b1);
assert!(b1_live.size() == 0);
assert!(b1_live.iter().count() == 0);

let b3_live = live.live_at(b3);
assert!(b3_live.size() == 2);
assert!(b3_live.iter().count() == 2);
assert!(b3_live.contains(b1_ret));
assert!(b3_live.contains(b2_c));

let b5_live = live.live_at(b5);
assert!(b5_live.size() == 1);
assert!(b5_live.iter().count() == 1);
assert!(b5_live.contains(b1_ret));

let b6_live = live.live_at(b6);
assert!(b6_live.size() == 1);
assert!(b6_live.iter().count() == 1);
assert!(b6_live.contains(b1_ret));
}

Expand Down
5 changes: 0 additions & 5 deletions libeir_ir/src/algo/mangle/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,6 @@ impl Mangler {

/// Runs lambda mangling on a single function container
pub fn run(&mut self, fun: &mut FunctionBuilder) -> Block {
println!("==================================");
println!("==================================");
println!("======== Mangle!!! ");
println!("==================================");
println!("==================================");
let mut recv = receiver::SingleMangleReceiver {
fun,
};
Expand Down
Loading

0 comments on commit 1838ac2

Please sign in to comment.