Skip to content

Commit

Permalink
perf(vm): Eliminate redundant match expressions
Browse files Browse the repository at this point in the history
  • Loading branch information
Marwes committed Oct 1, 2019
1 parent ab1b1b8 commit 945fb83
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 7 deletions.
106 changes: 100 additions & 6 deletions vm/src/core/dead_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,60 @@ use base::{

use crate::core::{
self,
optimize::{walk_expr, walk_expr_alloc, SameLifetime, Visitor},
Allocator, CExpr, Expr, LetBinding,
optimize::{walk_expr, walk_expr_alloc, DifferentLifetime, SameLifetime, Visitor},
Allocator, CExpr, Expr, LetBinding, Named, Pattern,
};

fn is_pure_simple(expr: CExpr) -> bool {
pub struct SimplePure(bool);

impl<'l, 'expr> Visitor<'l, 'expr> for SimplePure {
type Producer = DifferentLifetime<'l, 'expr>;

fn visit_expr(&mut self, expr: CExpr<'expr>) -> Option<CExpr<'l>> {
if !self.0 {
return None;
}
match *expr {
Expr::Call(..) => {
self.0 = false;
None
}
Expr::Let(ref bind, expr) => {
match bind.expr {
// Creating a group of closures is always pure (though calling them may not be)
Named::Recursive(_) => (),
Named::Expr(expr) => {
self.visit_expr(expr);
}
}
self.visit_expr(expr)
}
_ => walk_expr_alloc(self, expr),
}
}
fn detach_allocator(&self) -> Option<&'l Allocator<'l>> {
None
}
}

let mut visitor = SimplePure(true);
visitor.visit_expr(expr);
visitor.0
}

pub fn dead_code_elimination<'a>(allocator: &'a Allocator<'a>, expr: CExpr<'a>) -> CExpr<'a> {
struct FreeVars<'a> {
struct DeadCodeEliminator<'a> {
allocator: &'a Allocator<'a>,
used_bindings: FnvSet<&'a SymbolRef>,
}
impl FreeVars<'_> {
impl DeadCodeEliminator<'_> {
fn is_used(&self, s: &Symbol) -> bool {
self.used_bindings.contains(&**s)
}
}

impl<'e> Visitor<'e, 'e> for FreeVars<'e> {
impl<'e> Visitor<'e, 'e> for DeadCodeEliminator<'e> {
type Producer = SameLifetime<'e>;

fn visit_expr(&mut self, expr: CExpr<'e>) -> Option<CExpr<'e>> {
Expand Down Expand Up @@ -69,6 +107,26 @@ pub fn dead_code_elimination<'a>(allocator: &'a Allocator<'a>, expr: CExpr<'a>)
&*self.allocator.arena.alloc(Expr::Let(bind, body))
})
}

Expr::Match(scrutinee, alts) if alts.len() == 1 => match &alts[0].pattern {
Pattern::Record(fields) => {
if !is_pure_simple(scrutinee)
|| fields
.iter()
.map(|(x, y)| y.as_ref().unwrap_or(&x.name))
.any(|field_bind| self.is_used(&field_bind))
{
walk_expr_alloc(self, expr)
} else {
Some(
self.visit_expr(alts[0].expr)
.unwrap_or_else(|| alts[0].expr),
)
}
}
_ => walk_expr_alloc(self, expr),
},

_ => walk_expr_alloc(self, expr),
}
}
Expand All @@ -77,7 +135,7 @@ pub fn dead_code_elimination<'a>(allocator: &'a Allocator<'a>, expr: CExpr<'a>)
}
}

let mut free_vars = FreeVars {
let mut free_vars = DeadCodeEliminator {
allocator,
used_bindings: DepGraph::default().used_bindings(expr),
};
Expand Down Expand Up @@ -224,4 +282,40 @@ mod tests {
"#;
check_optimization(initial_str, expected_str, dead_code_elimination);
}

#[test]
fn eliminate_redundant_match() {
let initial_str = r#"
match { x = 1 } with
| { x } -> 1
end
"#;
let expected_str = r#"
1
"#;
check_optimization(initial_str, expected_str, dead_code_elimination);
}

#[test]
fn dont_eliminate_used_match() {
let initial_str = r#"
rec let f y = y
in
let x = f 123
in
match { x } with
| { x } -> x
end
"#;
let expected_str = r#"
rec let f y = y
in
let x = f 123
in
match { x } with
| { x } -> x
end
"#;
check_optimization(initial_str, expected_str, dead_code_elimination);
}
}
23 changes: 22 additions & 1 deletion vm/src/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1910,6 +1910,14 @@ mod tests {
}

fn expr_eq(map: &mut HashMap<Symbol, Symbol>, l: &Expr, r: &Expr) -> bool {
let b = expr_eq_(map, l, r);
if !b {
eprintln!("{} != {}", l, r);
}
b
}

fn expr_eq_(map: &mut HashMap<Symbol, Symbol>, l: &Expr, r: &Expr) -> bool {
match (l, r) {
(&Expr::Match(_, l_alts), &Expr::Match(_, r_alts)) => {
for (l, r) in l_alts.iter().zip(r_alts) {
Expand Down Expand Up @@ -1952,7 +1960,20 @@ mod tests {
(&Expr::Ident(ref l, _), &Expr::Ident(ref r, _)) => check(map, &l.name, &r.name),
(&Expr::Let(ref lb, l), &Expr::Let(ref rb, r)) => {
let b = match (&lb.expr, &rb.expr) {
(&Named::Expr(le), &Named::Expr(re)) => expr_eq(map, le, re),
(Named::Expr(le), Named::Expr(re)) => expr_eq(map, le, re),
(Named::Recursive(lc), Named::Recursive(rc)) => {
lc.len() == rc.len()
&& lc.iter().zip(rc).all(|(lc, rc)| {
check(map, &lc.name.name, &rc.name.name)
&& lc.args.len() == rc.args.len()
&& lc
.args
.iter()
.zip(&rc.args)
.all(|(l, r)| check(map, &l.name, &r.name))
&& expr_eq(map, lc.expr, rc.expr)
})
}
_ => false,
};
check(map, &lb.name.name, &rb.name.name) && b && expr_eq(map, l, r)
Expand Down

0 comments on commit 945fb83

Please sign in to comment.