about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorMarijn Haverbeke <marijnh@gmail.com>2011-11-17 16:42:17 +0100
committerMarijn Haverbeke <marijnh@gmail.com>2011-11-18 12:49:01 +0100
commit8f8ebb550cf7e641d7dedd56e08efd4f0e15afab (patch)
treefce19d94b7df2f56541a51898ca6d0c2048ddef5 /src
parent0c97fcbf6689d8d4f466cdea80369ae057a4523e (diff)
downloadrust-8f8ebb550cf7e641d7dedd56e08efd4f0e15afab.tar.gz
rust-8f8ebb550cf7e641d7dedd56e08efd4f0e15afab.zip
Implement a last-use-of-local finding algorithm
Issue #925
Diffstat (limited to 'src')
-rw-r--r--src/comp/driver/rustc.rs8
-rw-r--r--src/comp/middle/kind.rs7
-rw-r--r--src/comp/middle/last_use.rs219
-rw-r--r--src/comp/rustc.rc1
-rw-r--r--src/lib/list.rs17
-rw-r--r--src/lib/vec.rs4
6 files changed, 250 insertions, 6 deletions
diff --git a/src/comp/driver/rustc.rs b/src/comp/driver/rustc.rs
index 05fe635b242..518a6251702 100644
--- a/src/comp/driver/rustc.rs
+++ b/src/comp/driver/rustc.rs
@@ -5,7 +5,8 @@ import metadata::{creader, cstore};
 import syntax::parse::{parser};
 import syntax::{ast, codemap};
 import front::attr;
-import middle::{trans, resolve, freevars, kind, ty, typeck, fn_usage};
+import middle::{trans, resolve, freevars, kind, ty, typeck, fn_usage,
+                last_use};
 import syntax::print::{pp, pprust};
 import util::{ppaux, filesearch};
 import back::link;
@@ -138,6 +139,8 @@ fn compile_input(sess: session::session, cfg: ast::crate_cfg, input: str,
              bind freevars::annotate_freevars(def_map, crate));
     let ty_cx = ty::mk_ctxt(sess, def_map, ext_map, ast_map, freevars);
     time(time_passes, "typechecking", bind typeck::check_crate(ty_cx, crate));
+    let last_uses = time(time_passes, "last use finding",
+        bind last_use::find_last_uses(crate, def_map, ty_cx));
     time(time_passes, "function usage",
          bind fn_usage::check_crate_fn_usage(ty_cx, crate));
     time(time_passes, "alt checking",
@@ -150,7 +153,8 @@ fn compile_input(sess: session::session, cfg: ast::crate_cfg, input: str,
     let copy_map =
         time(time_passes, "alias checking",
              bind middle::alias::check_crate(ty_cx, crate));
-    time(time_passes, "kind checking", bind kind::check_crate(ty_cx, crate));
+    time(time_passes, "kind checking",
+         bind kind::check_crate(ty_cx, last_uses, crate));
     time(time_passes, "const checking",
          bind middle::check_const::check_crate(ty_cx, crate));
     if sess.get_opts().no_trans { ret; }
diff --git a/src/comp/middle/kind.rs b/src/comp/middle/kind.rs
index 6437e00f99b..3618a17ff2c 100644
--- a/src/comp/middle/kind.rs
+++ b/src/comp/middle/kind.rs
@@ -15,11 +15,14 @@ type rval_map = std::map::hashmap<node_id, ()>;
 
 type ctx = {tcx: ty::ctxt,
             rval_map: rval_map,
+            last_uses: last_use::last_uses,
             mutable ret_by_ref: bool};
 
-fn check_crate(tcx: ty::ctxt, crate: @crate) -> rval_map {
+fn check_crate(tcx: ty::ctxt, last_uses: last_use::last_uses,
+               crate: @crate) -> rval_map {
     let ctx = {tcx: tcx,
                rval_map: std::map::new_int_hash(),
+               last_uses: last_uses,
                mutable ret_by_ref: false};
     let visit = visit::mk_vt(@{
         visit_expr: check_expr,
@@ -118,7 +121,7 @@ fn maybe_copy(cx: ctx, ex: @expr) {
 }
 
 fn check_copy_ex(cx: ctx, ex: @expr, _warn: bool) {
-    if ty::expr_is_lval(cx.tcx, ex) {
+    if ty::expr_is_lval(cx.tcx, ex) && !cx.last_uses.contains_key(ex.id) {
         let ty = ty::expr_ty(cx.tcx, ex);
         check_copy(cx, ty, ex.span);
         // FIXME turn this on again once vector types are no longer unique.
diff --git a/src/comp/middle/last_use.rs b/src/comp/middle/last_use.rs
new file mode 100644
index 00000000000..5d8aa2493d2
--- /dev/null
+++ b/src/comp/middle/last_use.rs
@@ -0,0 +1,219 @@
+import syntax::{visit, ast_util};
+import syntax::ast::*;
+import std::list::{list, nil, cons, tail};
+import std::{vec, list, option};
+
+// Marks expr_paths that are last uses.
+type last_uses = std::map::hashmap<node_id, ()>;
+
+tag seen { unset; seen(node_id); }
+tag block_type { func; loop; }
+
+type set = [{def: node_id, exprs: list<node_id>}];
+type bl = @{type: block_type, mutable second: bool, mutable exits: [set]};
+
+type ctx = {last_uses: std::map::hashmap<node_id, bool>,
+            def_map: resolve::def_map,
+            tcx: ty::ctxt,
+            // The current set of local last uses
+            mutable current: set,
+            mutable blocks: list<bl>};
+
+fn find_last_uses(c: @crate, def_map: resolve::def_map, tcx: ty::ctxt)
+    -> last_uses {
+    let v = visit::mk_vt(@{visit_expr: visit_expr,
+                           visit_fn: visit_fn
+                           with *visit::default_visitor()});
+    let cx = {last_uses: std::map::new_int_hash(),
+              def_map: def_map,
+              tcx: tcx,
+              mutable current: [],
+              mutable blocks: nil};
+    visit::visit_crate(*c, cx, v);
+    let mini_table = std::map::new_int_hash();
+    cx.last_uses.items {|key, val| if val { mini_table.insert(key, ()); }}
+    ret mini_table;
+}
+
+fn visit_expr(ex: @expr, cx: ctx, v: visit::vt<ctx>) {
+    alt ex.node {
+      expr_ret(oexpr) {
+        visit::visit_expr_opt(oexpr, cx, v);
+        if !add_block_exit(cx, func) { leave_fn(cx); }
+      }
+      expr_fail(oexpr) {
+        visit::visit_expr_opt(oexpr, cx, v);
+        leave_fn(cx);
+      }
+      expr_break. { add_block_exit(cx, loop); }
+      expr_while(_, _) | expr_do_while(_, _) {
+        visit_block(loop, cx) {|| visit::visit_expr(ex, cx, v);}
+      }
+      expr_for(_, coll, blk) {
+        v.visit_expr(coll, cx, v);
+        visit_block(loop, cx) {|| visit::visit_block(blk, cx, v);}
+      }
+      expr_ternary(_, _, _) {
+        v.visit_expr(ast_util::ternary_to_if(ex), cx, v);
+      }
+      expr_alt(input, arms) {
+        v.visit_expr(input, cx, v);
+        let before = cx.current, sets = [];
+        for arm in arms {
+            cx.current = before;
+            v.visit_arm(arm, cx, v);
+            sets += [cx.current];
+        }
+        cx.current = join_branches(sets);
+      }
+      expr_if(cond, then, els) {
+        v.visit_expr(cond, cx, v);
+        let cur = cx.current;
+        visit::visit_block(then, cx, v);
+        cx.current <-> cur;
+        visit::visit_expr_opt(els, cx, v);
+        cx.current = join_branches([cur, cx.current]);
+      }
+      expr_path(_) {
+        alt clear_if_path(cx, ex, v, false) {
+          option::some(my_def) {
+            cx.current += [{def: my_def, exprs: cons(ex.id, @nil)}];
+          }
+          _ {}
+        }
+      }
+      expr_swap(lhs, rhs) {
+        clear_if_path(cx, lhs, v, false);
+        clear_if_path(cx, rhs, v, false);
+      }
+      expr_move(dest, src) | expr_assign(dest, src) {
+        v.visit_expr(src, cx, v);
+        clear_if_path(cx, dest, v, true);
+      }
+      expr_assign_op(_, dest, src) {
+        v.visit_expr(src, cx, v);
+        v.visit_expr(dest, cx, v);
+        clear_if_path(cx, dest, v, true);
+      }
+      expr_call(f, args, _) {
+        v.visit_expr(f, cx, v);
+        let i = 0u;
+        for arg_t in ty::ty_fn_args(cx.tcx, ty::expr_ty(cx.tcx, f)) {
+            alt arg_t.mode {
+              by_mut_ref. { clear_if_path(cx, args[i], v, false); }
+              _ { v.visit_expr(args[i], cx, v); }
+            }
+            i += 1u;
+        }
+      }
+      _ { visit::visit_expr(ex, cx, v); }
+    }
+}
+
+fn visit_fn(f: _fn, tps: [ty_param], sp: syntax::codemap::span,
+            ident: fn_ident, id: node_id, cx: ctx, v: visit::vt<ctx>) {
+    if f.proto == proto_block {
+        visit_block(func, cx, {||
+            visit::visit_fn(f, tps, sp, ident, id, cx, v);
+        });
+    } else {
+        let old = nil;
+        cx.blocks <-> old;
+        visit::visit_fn(f, tps, sp, ident, id, cx, v);
+        cx.blocks <-> old;
+        leave_fn(cx);
+    }
+}
+
+fn visit_block(tp: block_type, cx: ctx, visit: block()) {
+    let local = @{type: tp, mutable second: false, mutable exits: []};
+    cx.blocks = cons(local, @cx.blocks);
+    visit();
+    local.second = true;
+    visit();
+    cx.blocks = tail(cx.blocks);
+    cx.current = join_branches(local.exits);
+}
+
+fn add_block_exit(cx: ctx, tp: block_type) -> bool {
+    let cur = cx.blocks;
+    while cur != nil {
+        alt cur {
+          cons(b, tail) {
+            if (b.type == tp) {
+                if !b.second { b.exits += [cx.current]; }
+                ret true;
+            }
+            cur = *tail;
+          }
+        }
+    }
+    ret false;
+}
+
+fn join_branches(branches: [set]) -> set {
+    let found: set = [], i = 0u, l = vec::len(branches);
+    for set in branches {
+        i += 1u;
+        for {def, exprs} in set {
+            if !vec::any({|v| v.def == def}, found) {
+                let j = i, ne = exprs;
+                while j < l {
+                    for {def: d2, exprs} in branches[j] {
+                        if d2 == def {
+                            list::iter(exprs) {|e|
+                                if !list::has(ne, e) { ne = cons(e, @ne); }
+                            }
+                        }
+                    }
+                    j += 1u;
+                }
+                found += [{def: def, exprs: ne}];
+            }
+        }
+    }
+    ret found;
+}
+
+fn leave_fn(cx: ctx) {
+    for {def, exprs} in cx.current {
+        list::iter(exprs) {|ex_id|
+            if !cx.last_uses.contains_key(ex_id) {
+                cx.last_uses.insert(ex_id, true);
+            }
+        }
+    }
+}
+
+fn clear_in_current(cx: ctx, my_def: node_id, to: bool) {
+    for {def, exprs} in cx.current {
+        if def == my_def {
+            list::iter(exprs) {|expr|
+                if !to || !cx.last_uses.contains_key(expr) {
+                     cx.last_uses.insert(expr, to);
+                }
+            }
+            cx.current = vec::filter({|x| x.def != my_def},
+                                     copy cx.current);
+            break;
+        }
+    }
+}
+
+fn clear_if_path(cx: ctx, ex: @expr, v: visit::vt<ctx>, to: bool)
+    -> option::t<node_id> {
+    alt ex.node {
+      expr_path(_) {
+        alt cx.def_map.get(ex.id) {
+          def_local(def_id, let_copy.) | def_arg(def_id, by_copy.) |
+          def_arg(def_id, by_move.) {
+            clear_in_current(cx, def_id.node, to);
+            ret option::some(def_id.node);
+          }
+          _ {}
+        }
+      }
+      _ { v.visit_expr(ex, cx, v); }
+    }
+    ret option::none;
+}
diff --git a/src/comp/rustc.rc b/src/comp/rustc.rc
index 9215cbcb9c1..af6369b5bce 100644
--- a/src/comp/rustc.rc
+++ b/src/comp/rustc.rc
@@ -30,6 +30,7 @@ mod middle {
     mod check_const;
     mod mut;
     mod alias;
+    mod last_use;
     mod kind;
     mod freevars;
     mod shape;
diff --git a/src/lib/list.rs b/src/lib/list.rs
index 60e8e4821fe..300a5c72a67 100644
--- a/src/lib/list.rs
+++ b/src/lib/list.rs
@@ -134,6 +134,23 @@ fn append<T>(l: list<T>, m: list<T>) -> list<T> {
     }
 }
 
+/*
+Function: iter
+
+Iterate over a list
+*/
+fn iter<copy T>(l: list<T>, f: block(T)) {
+    let cur = l;
+    while cur != nil {
+        alt cur {
+          cons(hd, tl) {
+            f(hd);
+            cur = *tl;
+          }
+        }
+    }
+}
+
 // Local Variables:
 // mode: rust;
 // fill-column: 78;
diff --git a/src/lib/vec.rs b/src/lib/vec.rs
index 35e416c121d..2ae7f2247b1 100644
--- a/src/lib/vec.rs
+++ b/src/lib/vec.rs
@@ -703,8 +703,8 @@ Iterates over vector `v` and, for each element, calls function `f` with the
 element's value and index.
 */
 fn iter2<T>(v: [const T], f: block(uint, T)) {
-    let i = 0u;
-    for x in v { f(i, x); i += 1u; }
+    let i = 0u, l = len(v);
+    while i < l { f(i, v[i]); i += 1u; }
 }
 
 /*