1 import syntax::{visit, ast_util};
3 import syntax::codemap::span;
4 import std::list::{is_not_empty, list, nil, cons, tail};
5 import core::{vec, option};
8 // Last use analysis pass.
10 // Finds the last read of each value stored in a local variable or
11 // callee-owned argument (arguments with by-move or by-copy passing
12 // style). This is a limited form of liveness analysis, peformed
13 // (perhaps foolishly) directly on the AST.
15 // The algorithm walks the AST, keeping a set of (def, last_use)
16 // pairs. When the function is exited, or the local is overwritten,
17 // the current set of last uses is marked with 'true' in a table.
18 // Other branches may later overwrite them with 'false' again, since
19 // they may find a use coming after them. (Marking an expression as a
20 // last use is only done if it has not already been marked with
23 // Some complexity is added to deal with joining control flow branches
24 // (by `break` or conditionals), and for handling loops.
26 // Marks expr_paths that are last uses.
27 type last_uses = std::map::hashmap<node_id, ()>;
29 tag seen { unset; seen(node_id); }
30 tag block_type { func; loop; }
32 type set = [{def: node_id, exprs: list<node_id>}];
33 type bl = @{type: block_type, mutable second: bool, mutable exits: [set]};
35 type ctx = {last_uses: std::map::hashmap<node_id, bool>,
36 def_map: resolve::def_map,
37 ref_map: alias::ref_map,
39 // The current set of local last uses
41 mutable blocks: list<bl>};
43 fn find_last_uses(c: @crate, def_map: resolve::def_map,
44 ref_map: alias::ref_map, tcx: ty::ctxt) -> last_uses {
45 let v = visit::mk_vt(@{visit_expr: visit_expr,
47 with *visit::default_visitor()});
48 let cx = {last_uses: std::map::new_int_hash(),
54 visit::visit_crate(*c, cx, v);
55 let mini_table = std::map::new_int_hash();
56 cx.last_uses.items {|key, val|
58 mini_table.insert(key, ());
59 let def_node = ast_util::def_id_of_def(def_map.get(key)).node;
60 mini_table.insert(def_node, ());
66 fn is_block(cx: ctx, id: node_id) -> bool {
67 alt ty::struct(cx.tcx, ty::node_id_to_monotype(cx.tcx, id)) {
68 ty::ty_fn({proto: proto_block., _}) { true }
73 fn visit_expr(ex: @expr, cx: ctx, v: visit::vt<ctx>) {
76 visit::visit_expr_opt(oexpr, cx, v);
77 if !add_block_exit(cx, func) { leave_fn(cx); }
80 visit::visit_expr_opt(oexpr, cx, v);
83 expr_break. { add_block_exit(cx, loop); }
84 expr_while(_, _) | expr_do_while(_, _) {
85 visit_block(loop, cx) {|| visit::visit_expr(ex, cx, v);}
87 expr_for(_, coll, blk) {
88 v.visit_expr(coll, cx, v);
89 visit_block(loop, cx) {|| visit::visit_block(blk, cx, v);}
91 expr_ternary(_, _, _) {
92 v.visit_expr(ast_util::ternary_to_if(ex), cx, v);
94 expr_alt(input, arms) {
95 v.visit_expr(input, cx, v);
96 let before = cx.current, sets = [];
99 v.visit_arm(arm, cx, v);
100 sets += [cx.current];
102 cx.current = join_branches(sets);
104 expr_if(cond, then, els) {
105 v.visit_expr(cond, cx, v);
106 let cur = cx.current;
107 visit::visit_block(then, cx, v);
109 visit::visit_expr_opt(els, cx, v);
110 cx.current = join_branches([cur, cx.current]);
113 let my_def = ast_util::def_id_of_def(cx.def_map.get(ex.id)).node;
114 alt cx.ref_map.find(my_def) {
115 option::some(root_id) { clear_in_current(cx, root_id, false); }
117 alt clear_if_path(cx, ex, v, false) {
118 option::some(my_def) {
119 cx.current += [{def: my_def, exprs: cons(ex.id, @nil)}];
126 expr_swap(lhs, rhs) {
127 clear_if_path(cx, lhs, v, false);
128 clear_if_path(cx, rhs, v, false);
130 expr_move(dest, src) | expr_assign(dest, src) {
131 v.visit_expr(src, cx, v);
132 clear_if_path(cx, dest, v, true);
134 expr_assign_op(_, dest, src) {
135 v.visit_expr(src, cx, v);
136 v.visit_expr(dest, cx, v);
137 clear_if_path(cx, dest, v, true);
139 expr_call(f, args, _) {
140 v.visit_expr(f, cx, v);
141 let i = 0u, fns = [];
142 let arg_ts = ty::ty_fn_args(cx.tcx, ty::expr_ty(cx.tcx, f));
145 expr_fn(proto_block., _, _, _) { fns += [arg]; }
146 expr_fn_block(_, _) when is_block(cx, arg.id) { fns += [arg]; }
149 by_mut_ref. { clear_if_path(cx, arg, v, false); }
150 _ { v.visit_expr(arg, cx, v); }
156 for f in fns { v.visit_expr(f, cx, v); }
158 _ { visit::visit_expr(ex, cx, v); }
162 fn visit_fn(fk: visit::fn_kind, decl: fn_decl, body: blk,
163 sp: span, id: node_id,
164 cx: ctx, v: visit::vt<ctx>) {
165 let fty = ty::node_id_to_type(cx.tcx, id);
166 let proto = ty::ty_fn_proto(cx.tcx, fty);
167 if proto == proto_block {
168 visit_block(func, cx, {||
169 visit::visit_fn(fk, decl, body, sp, id, cx, v);
172 alt cx.tcx.freevars.find(id) {
175 clear_in_current(cx, ast_util::def_id_of_def(v.def).node,
183 visit::visit_fn(fk, decl, body, sp, id, cx, v);
189 fn visit_block(tp: block_type, cx: ctx, visit: block()) {
190 let local = @{type: tp, mutable second: false, mutable exits: []};
191 cx.blocks = cons(local, @cx.blocks);
195 let cx_blocks = cx.blocks;
196 check is_not_empty(cx_blocks);
197 cx.blocks = tail(cx_blocks);
198 cx.current = join_branches(local.exits);
201 fn add_block_exit(cx: ctx, tp: block_type) -> bool {
207 if !b.second { b.exits += [cx.current]; }
217 fn join_branches(branches: [set]) -> set {
218 let found: set = [], i = 0u, l = vec::len(branches);
219 for set in branches {
221 for {def, exprs} in set {
222 if !vec::any(found, {|v| v.def == def}) {
223 let j = i, nne = exprs;
225 for {def: d2, exprs} in branches[j] {
227 list::iter(exprs) {|e|
228 if !list::has(nne, e) { nne = cons(e, @nne); }
234 found += [{def: def, exprs: nne}];
241 fn leave_fn(cx: ctx) {
242 for {def, exprs} in cx.current {
243 list::iter(exprs) {|ex_id|
244 if !cx.last_uses.contains_key(ex_id) {
245 cx.last_uses.insert(ex_id, true);
251 fn clear_in_current(cx: ctx, my_def: node_id, to: bool) {
252 for {def, exprs} in cx.current {
254 list::iter(exprs) {|expr|
255 if !to || !cx.last_uses.contains_key(expr) {
256 cx.last_uses.insert(expr, to);
259 cx.current = vec::filter(copy cx.current,
260 {|x| x.def != my_def});
266 fn clear_if_path(cx: ctx, ex: @expr, v: visit::vt<ctx>, to: bool)
267 -> option::t<node_id> {
270 alt cx.def_map.get(ex.id) {
271 def_local(def_id, let_copy.) | def_arg(def_id, by_copy.) |
272 def_arg(def_id, by_move.) {
273 clear_in_current(cx, def_id.node, to);
274 ret option::some(def_id.node);
279 _ { v.visit_expr(ex, cx, v); }