replace last_use with liveness info
This commit is contained in:
parent
62fe587f81
commit
9e6a068034
10 changed files with 122 additions and 481 deletions
|
|
@ -4,8 +4,7 @@ import session::session;
|
|||
import syntax::parse;
|
||||
import syntax::{ast, codemap};
|
||||
import syntax::attr;
|
||||
import middle::{trans, resolve, freevars, kind, ty, typeck,
|
||||
last_use, lint};
|
||||
import middle::{trans, resolve, freevars, kind, ty, typeck, lint};
|
||||
import syntax::print::{pp, pprust};
|
||||
import util::{ppaux, filesearch};
|
||||
import back::link;
|
||||
|
|
@ -192,7 +191,7 @@ fn compile_upto(sess: session, cfg: ast::crate_cfg,
|
|||
bind middle::check_loop::check_crate(ty_cx, crate));
|
||||
time(time_passes, "alt checking",
|
||||
bind middle::check_alt::check_crate(ty_cx, crate));
|
||||
let _last_use_map =
|
||||
let (last_use_map, spill_map) =
|
||||
time(time_passes, "liveness checking",
|
||||
bind middle::liveness::check_crate(ty_cx, method_map, crate));
|
||||
time(time_passes, "typestate checking",
|
||||
|
|
@ -200,13 +199,11 @@ fn compile_upto(sess: session, cfg: ast::crate_cfg,
|
|||
let (root_map, mutbl_map) = time(
|
||||
time_passes, "borrow checking",
|
||||
bind middle::borrowck::check_crate(ty_cx, method_map, crate));
|
||||
let (copy_map, ref_map) =
|
||||
let (copy_map, _ref_map) =
|
||||
time(time_passes, "alias checking",
|
||||
bind middle::alias::check_crate(ty_cx, crate));
|
||||
let (last_uses, spill_map) = time(time_passes, "last use finding",
|
||||
bind last_use::find_last_uses(crate, def_map, ref_map, ty_cx));
|
||||
time(time_passes, "kind checking",
|
||||
bind kind::check_crate(ty_cx, method_map, last_uses, crate));
|
||||
bind kind::check_crate(ty_cx, method_map, last_use_map, crate));
|
||||
|
||||
lint::check_crate(ty_cx, crate, sess.opts.lint_opts, time_passes);
|
||||
|
||||
|
|
@ -214,7 +211,7 @@ fn compile_upto(sess: session, cfg: ast::crate_cfg,
|
|||
let outputs = option::get(outputs);
|
||||
|
||||
let maps = {mutbl_map: mutbl_map, root_map: root_map,
|
||||
copy_map: copy_map, last_uses: last_uses,
|
||||
copy_map: copy_map, last_use_map: last_use_map,
|
||||
impl_map: impl_map, method_map: method_map,
|
||||
vtable_map: vtable_map, spill_map: spill_map};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import util::ppaux::ty_to_str;
|
||||
|
||||
import dvec::extensions;
|
||||
import syntax::ast;
|
||||
import syntax::fold;
|
||||
import syntax::visit;
|
||||
|
|
@ -18,7 +19,7 @@ import std::serialization::serializer_helpers;
|
|||
import std::serialization::deserializer_helpers;
|
||||
import std::prettyprint::serializer;
|
||||
import std::smallintmap::map;
|
||||
import middle::{ty, typeck, last_use};
|
||||
import middle::{ty, typeck};
|
||||
import middle::typeck::{method_origin,
|
||||
serialize_method_origin,
|
||||
deserialize_method_origin,
|
||||
|
|
@ -52,11 +53,11 @@ type maps = {
|
|||
mutbl_map: middle::borrowck::mutbl_map,
|
||||
root_map: middle::borrowck::root_map,
|
||||
copy_map: middle::alias::copy_map,
|
||||
last_uses: middle::last_use::last_uses,
|
||||
last_use_map: middle::liveness::last_use_map,
|
||||
impl_map: middle::resolve::impl_map,
|
||||
method_map: middle::typeck::method_map,
|
||||
vtable_map: middle::typeck::vtable_map,
|
||||
spill_map: middle::last_use::spill_map
|
||||
spill_map: middle::liveness::spill_map
|
||||
};
|
||||
|
||||
type decode_ctxt = @{
|
||||
|
|
@ -539,10 +540,6 @@ impl helper for ebml::ebml_deserializer {
|
|||
let fv = deserialize_method_origin(self);
|
||||
fv.tr(xcx)
|
||||
}
|
||||
fn read_is_last_use(xcx: extended_decode_ctxt) -> last_use::is_last_use {
|
||||
let lu = last_use::deserialize_is_last_use(self);
|
||||
lu.tr(xcx)
|
||||
}
|
||||
}
|
||||
|
||||
impl of tr for method_origin {
|
||||
|
|
@ -561,17 +558,6 @@ impl of tr for method_origin {
|
|||
}
|
||||
}
|
||||
|
||||
impl of tr for last_use::is_last_use {
|
||||
fn tr(xcx: extended_decode_ctxt) -> last_use::is_last_use {
|
||||
alt self {
|
||||
last_use::is_last_use { self }
|
||||
last_use::closes_over(ids) {
|
||||
last_use::closes_over(vec::map(ids, {|id| xcx.tr_id(id)}))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ______________________________________________________________________
|
||||
// Encoding and decoding vtable_res
|
||||
|
||||
|
|
@ -844,11 +830,13 @@ fn encode_side_tables_for_id(ecx: @e::encode_ctxt,
|
|||
}
|
||||
}
|
||||
|
||||
option::iter(maps.last_uses.find(id)) {|m|
|
||||
option::iter(maps.last_use_map.find(id)) {|m|
|
||||
ebml_w.tag(c::tag_table_last_use) {||
|
||||
ebml_w.id(id);
|
||||
ebml_w.tag(c::tag_table_val) {||
|
||||
last_use::serialize_is_last_use(ebml_w, m)
|
||||
ebml_w.emit_from_vec((*m).get()) {|id|
|
||||
ebml_w.emit_int(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -975,7 +963,11 @@ fn decode_side_tables(xcx: extended_decode_ctxt,
|
|||
let bounds = val_dsr.read_bounds(xcx);
|
||||
dcx.tcx.ty_param_bounds.insert(id, bounds);
|
||||
} else if tag == (c::tag_table_last_use as uint) {
|
||||
dcx.maps.last_uses.insert(id, val_dsr.read_is_last_use(xcx));
|
||||
let ids = val_dsr.read_to_vec {||
|
||||
xcx.tr_id(val_dsr.read_int())
|
||||
};
|
||||
let dvec = @dvec::from_vec(vec::to_mut(ids));
|
||||
dcx.maps.last_use_map.insert(id, dvec);
|
||||
} else if tag == (c::tag_table_method_map as uint) {
|
||||
dcx.maps.method_map.insert(id,
|
||||
val_dsr.read_method_origin(xcx));
|
||||
|
|
|
|||
|
|
@ -138,10 +138,10 @@ enum loan_path {
|
|||
// a complete record of a loan that was granted
|
||||
type loan = {lp: @loan_path, cmt: cmt, mutbl: ast::mutability};
|
||||
|
||||
fn save_and_restore<T:copy,U>(&t: T, f: fn() -> U) -> U {
|
||||
let old_t = t;
|
||||
fn save_and_restore<T:copy,U>(&save_and_restore_t: T, f: fn() -> U) -> U {
|
||||
let old_save_and_restore_t = save_and_restore_t;
|
||||
let u <- f();
|
||||
t = old_t;
|
||||
save_and_restore_t = old_save_and_restore_t;
|
||||
ret u;
|
||||
}
|
||||
|
||||
|
|
@ -888,6 +888,7 @@ fn check_loans_in_fn(fk: visit::fn_kind, decl: ast::fn_decl, body: ast::blk,
|
|||
sp: span, id: ast::node_id, &&self: check_loan_ctxt,
|
||||
visitor: visit::vt<check_loan_ctxt>) {
|
||||
|
||||
#debug["purity on entry=%?", self.declared_purity];
|
||||
save_and_restore(self.in_ctor) {||
|
||||
save_and_restore(self.declared_purity) {||
|
||||
// In principle, we could consider fk_anon(*) or
|
||||
|
|
@ -909,6 +910,7 @@ fn check_loans_in_fn(fk: visit::fn_kind, decl: ast::fn_decl, body: ast::blk,
|
|||
visit::visit_fn(fk, decl, body, sp, id, self, visitor);
|
||||
}
|
||||
}
|
||||
#debug["purity on exit=%?", self.declared_purity];
|
||||
}
|
||||
|
||||
fn check_loans_in_expr(expr: @ast::expr,
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import std::map::hashmap;
|
|||
import util::ppaux::{ty_to_str, tys_to_str};
|
||||
import syntax::print::pprust::expr_to_str;
|
||||
import freevars::freevar_entry;
|
||||
import dvec::extensions;
|
||||
|
||||
// Kind analysis pass.
|
||||
//
|
||||
|
|
@ -56,15 +57,15 @@ type rval_map = std::map::hashmap<node_id, ()>;
|
|||
type ctx = {tcx: ty::ctxt,
|
||||
rval_map: rval_map,
|
||||
method_map: typeck::method_map,
|
||||
last_uses: last_use::last_uses};
|
||||
last_use_map: liveness::last_use_map};
|
||||
|
||||
fn check_crate(tcx: ty::ctxt, method_map: typeck::method_map,
|
||||
last_uses: last_use::last_uses, crate: @crate)
|
||||
last_use_map: liveness::last_use_map, crate: @crate)
|
||||
-> rval_map {
|
||||
let ctx = {tcx: tcx,
|
||||
rval_map: std::map::int_hash(),
|
||||
method_map: method_map,
|
||||
last_uses: last_uses};
|
||||
last_use_map: last_use_map};
|
||||
let visit = visit::mk_vt(@{
|
||||
visit_expr: check_expr,
|
||||
visit_stmt: check_stmt,
|
||||
|
|
@ -177,11 +178,10 @@ fn check_fn(fk: visit::fn_kind, decl: fn_decl, body: blk, sp: span,
|
|||
// if this is the last use of the variable, then it will be
|
||||
// a move and not a copy
|
||||
let is_move = {
|
||||
let last_uses = alt check cx.last_uses.find(fn_id) {
|
||||
some(last_use::closes_over(vars)) { vars }
|
||||
none { [] }
|
||||
};
|
||||
last_uses.contains(id)
|
||||
alt check cx.last_use_map.find(fn_id) {
|
||||
some(vars) {(*vars).contains(id)}
|
||||
none {false}
|
||||
}
|
||||
};
|
||||
|
||||
let ty = ty::node_id_to_type(cx.tcx, id);
|
||||
|
|
@ -367,7 +367,7 @@ fn is_nullary_variant(cx: ctx, ex: @expr) -> bool {
|
|||
|
||||
fn check_copy_ex(cx: ctx, ex: @expr, _warn: bool) {
|
||||
if ty::expr_is_lval(cx.method_map, ex) &&
|
||||
!cx.last_uses.contains_key(ex.id) &&
|
||||
!cx.last_use_map.contains_key(ex.id) &&
|
||||
!is_nullary_variant(cx, ex) {
|
||||
let ty = ty::expr_ty(cx.tcx, ex);
|
||||
check_copy(cx, ty, ex.span);
|
||||
|
|
|
|||
|
|
@ -1,393 +0,0 @@
|
|||
import syntax::{visit, ast_util};
|
||||
import syntax::ast::*;
|
||||
import syntax::codemap::span;
|
||||
import std::list::{is_not_empty, list, nil, cons, tail};
|
||||
import core::unreachable;
|
||||
import std::list;
|
||||
import std::map::hashmap;
|
||||
|
||||
// Last use analysis pass.
|
||||
//
|
||||
// Finds the last read of each value stored in a local variable or
|
||||
// callee-owned argument (arguments with by-move or by-copy passing
|
||||
// style). This is a limited form of liveness analysis, peformed
|
||||
// (perhaps foolishly) directly on the AST.
|
||||
//
|
||||
// The algorithm walks the AST, keeping a set of (def, last_use)
|
||||
// pairs. When the function is exited, or the local is overwritten,
|
||||
// the current set of last uses is marked with 'true' in a table.
|
||||
// Other branches may later overwrite them with 'false' again, since
|
||||
// they may find a use coming after them. (Marking an expression as a
|
||||
// last use is only done if it has not already been marked with
|
||||
// 'false'.)
|
||||
//
|
||||
// Some complexity is added to deal with joining control flow branches
|
||||
// (by `break` or conditionals), and for handling loops.
|
||||
|
||||
// Marks expr_paths that are last uses.
|
||||
#[auto_serialize]
|
||||
enum is_last_use {
|
||||
is_last_use,
|
||||
closes_over([node_id]),
|
||||
}
|
||||
type last_uses = std::map::hashmap<node_id, is_last_use>;
|
||||
type spill_map = std::map::hashmap<node_id, ()>;
|
||||
|
||||
enum seen { unset, seen(node_id), }
|
||||
enum block_type { func, lp, }
|
||||
|
||||
enum use { var_use(node_id), close_over(node_id), }
|
||||
type set = [{def: node_id, uses: @list<use>}];
|
||||
type bl = @{type: block_type, mut second: bool, mut exits: [set]};
|
||||
|
||||
enum use_id { path(node_id), close(node_id, node_id) }
|
||||
fn hash_use_id(id: use_id) -> uint {
|
||||
(alt id { path(i) { i } close(i, j) { (i << 10) + j } }) as uint
|
||||
}
|
||||
|
||||
type ctx = {last_uses: std::map::hashmap<use_id, bool>,
|
||||
spill_map: std::map::hashmap<node_id, ()>,
|
||||
def_map: resolve::def_map,
|
||||
ref_map: alias::ref_map,
|
||||
tcx: ty::ctxt,
|
||||
// The current set of local last uses
|
||||
mut current: set,
|
||||
mut blocks: @list<bl>};
|
||||
|
||||
fn find_last_uses(c: @crate, def_map: resolve::def_map,
|
||||
ref_map: alias::ref_map, tcx: ty::ctxt)
|
||||
-> (last_uses, spill_map) {
|
||||
let v = visit::mk_vt(@{visit_expr: visit_expr,
|
||||
visit_stmt: visit_stmt,
|
||||
visit_fn: visit_fn
|
||||
with *visit::default_visitor()});
|
||||
let cx = {last_uses: std::map::hashmap(hash_use_id, {|a, b| a == b}),
|
||||
spill_map: std::map::int_hash(),
|
||||
def_map: def_map,
|
||||
ref_map: ref_map,
|
||||
tcx: tcx,
|
||||
mut current: [],
|
||||
mut blocks: @nil};
|
||||
visit::visit_crate(*c, cx, v);
|
||||
let mini_table = std::map::int_hash();
|
||||
for cx.last_uses.each {|key, val|
|
||||
if val {
|
||||
alt key {
|
||||
path(id) {
|
||||
mini_table.insert(id, is_last_use);
|
||||
let def_node = ast_util::def_id_of_def(def_map.get(id)).node;
|
||||
cx.spill_map.insert(def_node, ());
|
||||
}
|
||||
close(fn_id, local_id) {
|
||||
cx.spill_map.insert(local_id, ());
|
||||
let known = alt check mini_table.find(fn_id) {
|
||||
some(closes_over(ids)) { ids }
|
||||
none { [] }
|
||||
};
|
||||
mini_table.insert(fn_id, closes_over(known + [local_id]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ret (mini_table, cx.spill_map);
|
||||
}
|
||||
|
||||
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, lp); }
|
||||
expr_while(_, _) | expr_loop(_) {
|
||||
visit_block(lp, cx) {|| visit::visit_expr(ex, cx, v);}
|
||||
}
|
||||
expr_alt(input, arms, _) {
|
||||
v.visit_expr(input, cx, v);
|
||||
let before = cx.current;
|
||||
let mut sets = [];
|
||||
for arms.each {|arm|
|
||||
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 mut 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(_) {
|
||||
let my_def = cx.def_map.get(ex.id);
|
||||
let my_def_id = ast_util::def_id_of_def(my_def).node;
|
||||
alt cx.ref_map.find(my_def_id) {
|
||||
option::some(root_id) {
|
||||
clear_in_current(cx, root_id, false);
|
||||
}
|
||||
_ {
|
||||
option::iter(def_is_owned_local(cx, my_def)) {|nid|
|
||||
clear_in_current(cx, nid, false);
|
||||
cx.current += [{def: nid,
|
||||
uses: @cons(var_use(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_fn_block(_, _, cap_clause) |
|
||||
expr_fn(_, _, _, cap_clause) {
|
||||
// n.b.: safe to ignore copies, as if they are unused
|
||||
// then they are ignored, otherwise they will show up
|
||||
// as freevars in the body.
|
||||
for (*cap_clause).each { |ci|
|
||||
if ci.is_move {
|
||||
clear_def_if_local(cx, cx.def_map.get(ci.id), false);
|
||||
}
|
||||
}
|
||||
visit::visit_expr(ex, cx, v);
|
||||
}
|
||||
expr_call(f, args, _) {
|
||||
v.visit_expr(f, cx, v);
|
||||
let mut fns = [];
|
||||
let arg_ts = ty::ty_fn_args(ty::expr_ty(cx.tcx, f));
|
||||
vec::iter2(args, arg_ts) {|arg, arg_t|
|
||||
alt arg.node {
|
||||
expr_fn(*) | expr_fn_block(*)
|
||||
if is_blockish(ty::ty_fn_proto(arg_t.ty)) {
|
||||
fns += [arg];
|
||||
}
|
||||
_ {
|
||||
alt ty::arg_mode(cx.tcx, arg_t) {
|
||||
by_mutbl_ref { clear_if_path(cx, arg, v, false); }
|
||||
_ { v.visit_expr(arg, cx, v); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for fns.each {|f| v.visit_expr(f, cx, v); }
|
||||
vec::iter2(args, arg_ts) {|arg, arg_t|
|
||||
alt arg.node {
|
||||
expr_path(_) {
|
||||
alt ty::arg_mode(cx.tcx, arg_t) {
|
||||
by_ref | by_val | by_mutbl_ref {
|
||||
let def = cx.def_map.get(arg.id);
|
||||
option::iter(def_is_owned_local(cx, def)) {|id|
|
||||
clear_in_current(cx, id, false);
|
||||
cx.spill_map.insert(id, ());
|
||||
}
|
||||
}
|
||||
_ {}
|
||||
}
|
||||
}
|
||||
_ {}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ { visit::visit_expr(ex, cx, v); }
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_stmt(s: @stmt, cx: ctx, v: visit::vt<ctx>) {
|
||||
alt s.node {
|
||||
stmt_decl(@{node: decl_local(ls), _}, _) {
|
||||
shadow_in_current(cx, {|id|
|
||||
let mut rslt = false;
|
||||
for ls.each {|local|
|
||||
let mut found = false;
|
||||
pat_util::pat_bindings(cx.tcx.def_map, local.node.pat,
|
||||
{|pid, _a, _b|
|
||||
if pid == id { found = true; }
|
||||
});
|
||||
if found { rslt = true; break; }
|
||||
}
|
||||
rslt
|
||||
});
|
||||
}
|
||||
_ {}
|
||||
}
|
||||
visit::visit_stmt(s, cx, v);
|
||||
}
|
||||
|
||||
fn visit_fn(fk: visit::fn_kind, decl: fn_decl, body: blk,
|
||||
sp: span, id: node_id,
|
||||
cx: ctx, v: visit::vt<ctx>) {
|
||||
let fty = ty::node_id_to_type(cx.tcx, id);
|
||||
let proto = ty::ty_fn_proto(fty);
|
||||
alt proto {
|
||||
proto_any | proto_block {
|
||||
visit_block(func, cx, {||
|
||||
shadow_in_current(cx, {|id|
|
||||
vec::any(decl.inputs, {|arg| arg.id == id})
|
||||
});
|
||||
visit::visit_fn(fk, decl, body, sp, id, cx, v);
|
||||
});
|
||||
}
|
||||
proto_box | proto_uniq | proto_bare {
|
||||
alt cx.tcx.freevars.find(id) {
|
||||
some(vars) {
|
||||
for vec::each(*vars) {|v|
|
||||
option::iter(def_is_owned_local(cx, v.def)) {|nid|
|
||||
clear_in_current(cx, nid, false);
|
||||
cx.current += [{def: nid,
|
||||
uses: @cons(close_over(id), @nil)}];
|
||||
}
|
||||
}
|
||||
}
|
||||
_ {}
|
||||
}
|
||||
let mut old_cur = [], old_blocks = @nil;
|
||||
cx.blocks <-> old_blocks;
|
||||
cx.current <-> old_cur;
|
||||
visit::visit_fn(fk, decl, body, sp, id, cx, v);
|
||||
cx.blocks <-> old_blocks;
|
||||
leave_fn(cx);
|
||||
cx.current <-> old_cur;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_block(tp: block_type, cx: ctx, visit: fn()) {
|
||||
let local = @{type: tp, mut second: false, mut exits: []};
|
||||
cx.blocks = @cons(local, cx.blocks);
|
||||
visit();
|
||||
local.second = true;
|
||||
local.exits = [];
|
||||
visit();
|
||||
let cx_blocks = cx.blocks;
|
||||
cx.blocks = tail(cx_blocks);
|
||||
local.exits += [cx.current];
|
||||
cx.current = join_branches(local.exits);
|
||||
}
|
||||
|
||||
fn add_block_exit(cx: ctx, tp: block_type) -> bool {
|
||||
let mut cur = cx.blocks;
|
||||
loop {
|
||||
alt *cur {
|
||||
cons(b, tail) {
|
||||
if (b.type == tp) {
|
||||
if !b.second { b.exits += [cx.current]; }
|
||||
ret true;
|
||||
}
|
||||
cur = tail;
|
||||
}
|
||||
nil {
|
||||
ret false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn join_branches(branches: [set]) -> set {
|
||||
let mut found: set = [], i = 0u;
|
||||
let l = vec::len(branches);
|
||||
for branches.each {|set|
|
||||
i += 1u;
|
||||
for set.each {|elt|
|
||||
if !vec::any(found, {|v| v.def == elt.def}) {
|
||||
let mut j = i, nne = elt.uses;
|
||||
while j < l {
|
||||
for vec::each(branches[j]) {|elt2|
|
||||
if elt2.def == elt.def {
|
||||
list::iter(elt2.uses) {|e|
|
||||
if !list::has(nne, e) { nne = @cons(e, nne); }
|
||||
}
|
||||
}
|
||||
}
|
||||
j += 1u;
|
||||
}
|
||||
found += [{def: elt.def, uses: nne}];
|
||||
}
|
||||
}
|
||||
}
|
||||
ret found;
|
||||
}
|
||||
|
||||
fn leave_fn(cx: ctx) {
|
||||
for cx.current.each {|elt|
|
||||
list::iter(elt.uses) {|use|
|
||||
let key = alt use {
|
||||
var_use(pth_id) { path(pth_id) }
|
||||
close_over(fn_id) { close(fn_id, elt.def) }
|
||||
};
|
||||
if !cx.last_uses.contains_key(key) {
|
||||
cx.last_uses.insert(key, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn shadow_in_current(cx: ctx, p: fn(node_id) -> bool) {
|
||||
let mut out = [];
|
||||
cx.current <-> out;
|
||||
for out.each {|e| if !p(e.def) { cx.current += [e]; } }
|
||||
}
|
||||
|
||||
fn clear_in_current(cx: ctx, my_def: node_id, to: bool) {
|
||||
for cx.current.each {|elt|
|
||||
if elt.def == my_def {
|
||||
list::iter(elt.uses) {|use|
|
||||
let key = alt use {
|
||||
var_use(pth_id) { path(pth_id) }
|
||||
close_over(fn_id) { close(fn_id, elt.def) }
|
||||
};
|
||||
if !to || !cx.last_uses.contains_key(key) {
|
||||
cx.last_uses.insert(key, to);
|
||||
}
|
||||
}
|
||||
cx.current = vec::filter(copy cx.current, {|x| x.def != my_def});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn def_is_owned_local(cx: ctx, d: def) -> option<node_id> {
|
||||
alt d {
|
||||
def_local(id, _) { some(id) }
|
||||
def_arg(id, m) {
|
||||
alt ty::resolved_mode(cx.tcx, m) {
|
||||
by_copy | by_move { some(id) }
|
||||
by_ref | by_val | by_mutbl_ref { none }
|
||||
}
|
||||
}
|
||||
def_upvar(_, d, fn_id) {
|
||||
if is_blockish(ty::ty_fn_proto(ty::node_id_to_type(cx.tcx, fn_id))) {
|
||||
def_is_owned_local(cx, *d)
|
||||
} else { none }
|
||||
}
|
||||
_ { none }
|
||||
}
|
||||
}
|
||||
|
||||
fn clear_def_if_local(cx: ctx, d: def, to: bool) {
|
||||
alt def_is_owned_local(cx, d) {
|
||||
some(nid) { clear_in_current(cx, nid, to); }
|
||||
_ {}
|
||||
}
|
||||
}
|
||||
|
||||
fn clear_if_path(cx: ctx, ex: @expr, v: visit::vt<ctx>, to: bool) {
|
||||
alt ex.node {
|
||||
expr_path(_) { clear_def_if_local(cx, cx.def_map.get(ex.id), to); }
|
||||
_ { v.visit_expr(ex, cx, v); }
|
||||
}
|
||||
}
|
||||
|
|
@ -57,6 +57,7 @@ import capture::{cap_move, cap_drop, cap_copy, cap_ref};
|
|||
|
||||
export check_crate;
|
||||
export last_use_map;
|
||||
export spill_map;
|
||||
|
||||
// Maps from an expr id to a list of variable ids for which this expr
|
||||
// is the last use. Typically, the expr is a path and the node id is
|
||||
|
|
@ -84,7 +85,7 @@ enum live_node_kind {
|
|||
|
||||
fn check_crate(tcx: ty::ctxt,
|
||||
method_map: typeck::method_map,
|
||||
crate: @crate) -> last_use_map {
|
||||
crate: @crate) -> (last_use_map, spill_map) {
|
||||
let visitor = visit::mk_vt(@{
|
||||
visit_fn: visit_fn,
|
||||
visit_local: visit_local,
|
||||
|
|
@ -98,7 +99,7 @@ fn check_crate(tcx: ty::ctxt,
|
|||
last_use_map, spill_map);
|
||||
visit::visit_crate(*crate, initial_maps, visitor);
|
||||
tcx.sess.abort_if_errors();
|
||||
ret last_use_map;
|
||||
ret (last_use_map, spill_map);
|
||||
}
|
||||
|
||||
impl of to_str::to_str for live_node {
|
||||
|
|
@ -141,7 +142,13 @@ enum relevant_def { rdef_var(node_id), rdef_self }
|
|||
|
||||
type capture_info = {ln: live_node, is_move: bool, rv: relevant_def};
|
||||
|
||||
type var_info = {id: node_id, name: str};
|
||||
enum var_kind {
|
||||
vk_arg(node_id, str, rmode),
|
||||
vk_local(node_id, str),
|
||||
vk_field(str),
|
||||
vk_self,
|
||||
vk_implicit_ret
|
||||
}
|
||||
|
||||
fn relevant_def(def: def) -> option<relevant_def> {
|
||||
alt def {
|
||||
|
|
@ -163,7 +170,7 @@ class ir_maps {
|
|||
let variable_map: hashmap<node_id, variable>;
|
||||
let field_map: hashmap<str, variable>;
|
||||
let capture_map: hashmap<node_id, @[capture_info]>;
|
||||
let mut var_infos: [var_info];
|
||||
let mut var_kinds: [var_kind];
|
||||
let mut lnks: [live_node_kind];
|
||||
|
||||
new(tcx: ty::ctxt, method_map: typeck::method_map,
|
||||
|
|
@ -179,7 +186,7 @@ class ir_maps {
|
|||
self.variable_map = int_hash();
|
||||
self.capture_map = int_hash();
|
||||
self.field_map = str_hash();
|
||||
self.var_infos = [];
|
||||
self.var_kinds = [];
|
||||
self.lnks = [];
|
||||
}
|
||||
|
||||
|
|
@ -200,13 +207,23 @@ class ir_maps {
|
|||
#debug["%s is node %d", ln.to_str(), node_id];
|
||||
}
|
||||
|
||||
fn add_variable(node_id: node_id, name: str) -> variable {
|
||||
fn add_variable(vk: var_kind) -> variable {
|
||||
let v = variable(self.num_vars);
|
||||
self.variable_map.insert(node_id, v);
|
||||
self.var_infos += [{id:node_id, name:name}];
|
||||
self.var_kinds += [vk];
|
||||
self.num_vars += 1u;
|
||||
|
||||
#debug["%s is node %d", v.to_str(), node_id];
|
||||
alt vk {
|
||||
vk_local(node_id, _) | vk_arg(node_id, _, _) {
|
||||
self.variable_map.insert(node_id, v);
|
||||
}
|
||||
vk_field(name) {
|
||||
self.field_map.insert(name, v);
|
||||
}
|
||||
vk_self | vk_implicit_ret {
|
||||
}
|
||||
}
|
||||
|
||||
#debug["%s is %?", v.to_str(), vk];
|
||||
|
||||
v
|
||||
}
|
||||
|
|
@ -221,6 +238,15 @@ class ir_maps {
|
|||
}
|
||||
}
|
||||
|
||||
fn variable_name(var: variable) -> str {
|
||||
alt self.var_kinds[*var] {
|
||||
vk_local(_, name) | vk_arg(_, name, _) {name}
|
||||
vk_field(name) {"self." + name}
|
||||
vk_self {"self"}
|
||||
vk_implicit_ret {"<implicit-ret>"}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_captures(node_id: node_id, +cs: [capture_info]) {
|
||||
self.capture_map.insert(node_id, @cs);
|
||||
}
|
||||
|
|
@ -238,24 +264,40 @@ class ir_maps {
|
|||
self.lnks[*ln]
|
||||
}
|
||||
|
||||
fn add_last_use(expr_id: node_id, var: variable) {
|
||||
let v = alt self.last_use_map.find(expr_id) {
|
||||
some(v) { v }
|
||||
none {
|
||||
let v = @dvec();
|
||||
self.last_use_map.insert(expr_id, v);
|
||||
v
|
||||
fn add_spill(var: variable) {
|
||||
let vk = self.var_kinds[*var];
|
||||
alt vk {
|
||||
vk_local(id, _) | vk_arg(id, _, by_val) {
|
||||
#debug["adding spill for %?", vk];
|
||||
self.spill_map.insert(id, ());
|
||||
}
|
||||
};
|
||||
let {id, name} = self.var_infos[*var];
|
||||
#debug["Node %d is a last use of variable %d / %s",
|
||||
expr_id, id, name];
|
||||
(*v).push(id);
|
||||
vk_arg(*) | vk_field(_) | vk_self | vk_implicit_ret {}
|
||||
}
|
||||
}
|
||||
|
||||
fn add_spill(var: variable) {
|
||||
let id = self.var_infos[*var].id;
|
||||
if id != 0 { self.spill_map.insert(id, ()); }
|
||||
fn add_last_use(expr_id: node_id, var: variable) {
|
||||
let vk = self.var_kinds[*var];
|
||||
#debug["Node %d is a last use of variable %?", expr_id, vk];
|
||||
alt vk {
|
||||
vk_arg(id, name, by_move) |
|
||||
vk_arg(id, name, by_copy) |
|
||||
vk_local(id, name) {
|
||||
let v = alt self.last_use_map.find(expr_id) {
|
||||
some(v) { v }
|
||||
none {
|
||||
let v = @dvec();
|
||||
self.last_use_map.insert(expr_id, v);
|
||||
v
|
||||
}
|
||||
};
|
||||
|
||||
(*v).push(id);
|
||||
}
|
||||
vk_arg(_, _, by_ref) | vk_arg(_, _, by_mutbl_ref) |
|
||||
vk_arg(_, _, by_val) | vk_self | vk_field(_) | vk_implicit_ret {
|
||||
#debug["--but it is not owned"];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -272,7 +314,8 @@ fn visit_fn(fk: visit::fn_kind, decl: fn_decl, body: blk,
|
|||
|
||||
for decl.inputs.each { |arg|
|
||||
#debug["adding argument %d", arg.id];
|
||||
(*fn_maps).add_variable(arg.id, arg.ident);
|
||||
let mode = ty::resolved_mode(self.tcx, arg.mode);
|
||||
(*fn_maps).add_variable(vk_arg(arg.id, arg.ident, mode));
|
||||
};
|
||||
|
||||
// gather up the various local variables, significant expressions,
|
||||
|
|
@ -293,8 +336,8 @@ fn visit_fn(fk: visit::fn_kind, decl: fn_decl, body: blk,
|
|||
let specials = {
|
||||
exit_ln: (*fn_maps).add_live_node(lnk_exit),
|
||||
fallthrough_ln: (*fn_maps).add_live_node(lnk_exit),
|
||||
no_ret_var: (*fn_maps).add_variable(0, "<no_ret>"),
|
||||
self_var: (*fn_maps).add_variable(0, "self")
|
||||
no_ret_var: (*fn_maps).add_variable(vk_implicit_ret),
|
||||
self_var: (*fn_maps).add_variable(vk_self)
|
||||
};
|
||||
|
||||
// compute liveness
|
||||
|
|
@ -317,8 +360,7 @@ fn visit_fn(fk: visit::fn_kind, decl: fn_decl, body: blk,
|
|||
fn add_class_fields(self: @ir_maps, did: def_id) {
|
||||
for ty::lookup_class_fields(self.tcx, did).each { |field_ty|
|
||||
assert field_ty.id.crate == local_crate;
|
||||
let var = (*self).add_variable(
|
||||
field_ty.id.node, #fmt["self.%s", field_ty.ident]);
|
||||
let var = (*self).add_variable(vk_field(field_ty.ident));
|
||||
self.field_map.insert(field_ty.ident, var);
|
||||
}
|
||||
}
|
||||
|
|
@ -329,7 +371,7 @@ fn visit_local(local: @local, &&self: @ir_maps, vt: vt<@ir_maps>) {
|
|||
#debug["adding local variable %d", p_id];
|
||||
let name = ast_util::path_to_ident(path);
|
||||
(*self).add_live_node_for_node(p_id, lnk_vdef(sp));
|
||||
(*self).add_variable(p_id, name);
|
||||
(*self).add_variable(vk_local(p_id, name));
|
||||
}
|
||||
visit::visit_local(local, self, vt);
|
||||
}
|
||||
|
|
@ -730,6 +772,9 @@ class liveness {
|
|||
}
|
||||
}
|
||||
|
||||
// as above, the "self" variable is a non-owned variable
|
||||
self.acc(self.s.exit_ln, self.s.self_var, ACC_READ);
|
||||
|
||||
// in a ctor, there is an implicit use of self.f for all fields f:
|
||||
for self.ir.field_map.each_value { |var|
|
||||
self.acc(self.s.exit_ln, var, ACC_READ|ACC_USE);
|
||||
|
|
@ -938,8 +983,8 @@ class liveness {
|
|||
// see comment on lvalues in
|
||||
// propagate_through_lvalue_components()
|
||||
let succ = self.write_lvalue(l, succ, ACC_WRITE);
|
||||
let succ = self.propagate_through_expr(r, succ);
|
||||
self.propagate_through_lvalue_components(l, succ)
|
||||
let succ = self.propagate_through_lvalue_components(l, succ);
|
||||
self.propagate_through_expr(r, succ)
|
||||
}
|
||||
|
||||
expr_swap(l, r) {
|
||||
|
|
@ -1063,14 +1108,11 @@ class liveness {
|
|||
// ----------------------++-----------------------
|
||||
// ||
|
||||
// | || |
|
||||
// | || v
|
||||
// | || (lvalue components)
|
||||
// | || |
|
||||
// v || v
|
||||
// (rvalue) || (rvalue)
|
||||
// | || |
|
||||
// v || |
|
||||
// (write of lvalue) || |
|
||||
// v || v
|
||||
// (write of lvalue) || (lvalue components)
|
||||
// | || |
|
||||
// v || v
|
||||
// (succ) || (succ)
|
||||
|
|
@ -1604,7 +1646,7 @@ impl check_methods for @liveness {
|
|||
possibly_uninitialized_field {"possibly uninitialized field"}
|
||||
moved_variable {"moved variable"}
|
||||
};
|
||||
let name = self.ir.var_infos[*var].name;
|
||||
let name = (*self.ir).variable_name(var);
|
||||
alt lnk {
|
||||
lnk_freevar(span) {
|
||||
self.tcx.sess.span_err(
|
||||
|
|
@ -1625,7 +1667,7 @@ impl check_methods for @liveness {
|
|||
}
|
||||
|
||||
fn should_warn(var: variable) -> option<str> {
|
||||
let name = self.ir.var_infos[*var].name;
|
||||
let name = (*self.ir).variable_name(var);
|
||||
if name[0] == ('_' as u8) {none} else {some(name)}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2832,7 +2832,7 @@ fn trans_arg_expr(cx: block, arg: ty::arg, lldestty: TypeRef, e: @ast::expr,
|
|||
} else if arg_mode == ast::by_copy || arg_mode == ast::by_move {
|
||||
let alloc = alloc_ty(bcx, arg.ty);
|
||||
let move_out = arg_mode == ast::by_move ||
|
||||
ccx.maps.last_uses.contains_key(e.id);
|
||||
ccx.maps.last_use_map.contains_key(e.id);
|
||||
if lv.kind == temporary { revoke_clean(bcx, val); }
|
||||
if lv.kind == owned || !ty::type_is_immediate(arg.ty) {
|
||||
memmove_ty(bcx, alloc, val, arg.ty);
|
||||
|
|
@ -3561,7 +3561,8 @@ fn trans_expr(bcx: block, e: @ast::expr, dest: dest) -> block {
|
|||
let src_r = trans_temp_lval(bcx, src);
|
||||
let {bcx, val: addr, kind} = trans_lval(src_r.bcx, dst);
|
||||
assert kind == owned;
|
||||
let is_last_use = bcx.ccx().maps.last_uses.contains_key(src.id);
|
||||
let is_last_use =
|
||||
bcx.ccx().maps.last_use_map.contains_key(src.id);
|
||||
ret store_temp_expr(bcx, DROP_EXISTING, addr, src_r,
|
||||
expr_ty(bcx, src), is_last_use);
|
||||
}
|
||||
|
|
@ -3639,10 +3640,10 @@ fn trans_expr(bcx: block, e: @ast::expr, dest: dest) -> block {
|
|||
}
|
||||
|
||||
fn lval_to_dps(bcx: block, e: @ast::expr, dest: dest) -> block {
|
||||
let last_uses = bcx.ccx().maps.last_uses;
|
||||
let last_use_map = bcx.ccx().maps.last_use_map;
|
||||
let ty = expr_ty(bcx, e);
|
||||
let lv = trans_lval(bcx, e);
|
||||
let last_use = (lv.kind == owned && last_uses.contains_key(e.id));
|
||||
let last_use = (lv.kind == owned && last_use_map.contains_key(e.id));
|
||||
lval_result_to_dps(lv, ty, last_use, dest)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import util::ppaux::ty_to_str;
|
|||
import syntax::ast_map::{path, path_mod, path_name};
|
||||
import driver::session::session;
|
||||
import std::map::hashmap;
|
||||
import dvec::extensions;
|
||||
|
||||
// ___Good to know (tm)__________________________________________________
|
||||
//
|
||||
|
|
@ -307,9 +308,9 @@ fn build_closure(bcx0: block,
|
|||
env_vals += [env_ref(lv.val, ty, lv.kind)];
|
||||
}
|
||||
capture::cap_copy {
|
||||
let mv = alt check ccx.maps.last_uses.find(id) {
|
||||
let mv = alt check ccx.maps.last_use_map.find(id) {
|
||||
none { false }
|
||||
some(last_use::closes_over(vars)) { vec::contains(vars, nid) }
|
||||
some(vars) { (*vars).contains(nid) }
|
||||
};
|
||||
if mv { env_vals += [env_move(lv.val, ty, lv.kind)]; }
|
||||
else { env_vals += [env_copy(lv.val, ty, lv.kind)]; }
|
||||
|
|
|
|||
|
|
@ -71,7 +71,6 @@ mod middle {
|
|||
mod lint;
|
||||
mod borrowck;
|
||||
mod alias;
|
||||
mod last_use;
|
||||
mod liveness;
|
||||
mod block_use;
|
||||
mod kind;
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ fn start(+token: int) {
|
|||
let mut ch = comm::chan(p);
|
||||
int::range(2, n_threads + 1) { |i|
|
||||
let id = n_threads + 2 - i;
|
||||
let to_child = task::spawn_listener::<int> {|p|
|
||||
let to_child = task::spawn_listener::<int> {|p, copy ch|
|
||||
roundtrip(id, p, ch)
|
||||
};
|
||||
ch = to_child;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue