replace last_use with liveness info

This commit is contained in:
Niko Matsakis 2012-05-25 00:14:40 -07:00
parent 62fe587f81
commit 9e6a068034
10 changed files with 122 additions and 481 deletions

View file

@ -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};

View file

@ -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));

View file

@ -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,

View file

@ -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);

View file

@ -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); }
}
}

View file

@ -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)}
}

View file

@ -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)
}

View file

@ -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)]; }

View file

@ -71,7 +71,6 @@ mod middle {
mod lint;
mod borrowck;
mod alias;
mod last_use;
mod liveness;
mod block_use;
mod kind;

View file

@ -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;