Skip to content

Commit cc15d83

Browse files
committed
rustc_mir: add a local path access analysis.
1 parent 6882b97 commit cc15d83

File tree

3 files changed

+194
-9
lines changed

3 files changed

+194
-9
lines changed

src/librustc_mir/analysis/dataflow/mod.rs

+9-9
Original file line numberDiff line numberDiff line change
@@ -121,14 +121,14 @@ pub struct MoveDataParamEnv<'gcx, 'tcx> {
121121
pub(crate) param_env: ty::ParamEnv<'gcx>,
122122
}
123123

124-
pub(crate) fn do_dataflow<'a, 'gcx, 'tcx, BD, P>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
125-
mir: &'a Mir<'tcx>,
126-
node_id: ast::NodeId,
127-
attributes: &[ast::Attribute],
128-
dead_unwinds: &IdxSet<BasicBlock>,
129-
bd: BD,
130-
p: P)
131-
-> DataflowResults<BD>
124+
pub(crate) fn do_dataflow<BD, P>(tcx: TyCtxt,
125+
mir: &Mir,
126+
node_id: ast::NodeId,
127+
attributes: &[ast::Attribute],
128+
dead_unwinds: &IdxSet<BasicBlock>,
129+
bd: BD,
130+
p: P)
131+
-> DataflowResults<BD>
132132
where BD: BitDenotation + InitialFlow,
133133
P: Fn(&BD, BD::Idx) -> DebugFormatted
134134
{
@@ -139,7 +139,7 @@ pub(crate) fn do_dataflow<'a, 'gcx, 'tcx, BD, P>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
139139
impl<'a, 'gcx: 'tcx, 'tcx: 'a, BD> DataflowAnalysis<'a, 'tcx, BD> where BD: BitDenotation
140140
{
141141
pub(crate) fn run<P>(self,
142-
tcx: TyCtxt<'a, 'gcx, 'tcx>,
142+
tcx: TyCtxt,
143143
node_id: ast::NodeId,
144144
attributes: &[ast::Attribute],
145145
p: P) -> DataflowResults<BD>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use rustc_data_structures::indexed_set::IdxSetBuf;
12+
use rustc_data_structures::indexed_vec::Idx;
13+
use rustc::mir::*;
14+
use rustc::mir::visit::{PlaceContext, Visitor};
15+
use rustc::ty;
16+
use syntax::ast;
17+
use analysis::dataflow::{do_dataflow, BitDenotation, BlockSets, DebugFormatted};
18+
use analysis::eventflow::{Backward, Events, EventFlowResults, Forward, PastAndFuture};
19+
use analysis::local_paths::{LocalPaths, PathId};
20+
use analysis::local_paths::borrows::MaybeBorrowed;
21+
use analysis::locations::FlatLocations;
22+
23+
pub struct Accesses<'a> {
24+
pub results: PastAndFuture<EventFlowResults<'a, Forward, PathId>,
25+
EventFlowResults<'a, Backward, PathId>>
26+
}
27+
28+
impl<'a> Accesses<'a> {
29+
pub fn collect(mir: &Mir,
30+
local_paths: &LocalPaths,
31+
flat_locations: &'a FlatLocations)
32+
-> Self {
33+
let borrows = ty::tls::with(|tcx| {
34+
do_dataflow(tcx, mir, ast::DUMMY_NODE_ID, &[],
35+
&IdxSetBuf::new_empty(mir.basic_blocks().len()),
36+
MaybeBorrowed::new(mir, local_paths),
37+
|_, path| DebugFormatted::new(&path))
38+
});
39+
40+
let mut collector = AccessPathCollector {
41+
local_paths,
42+
location: Location {
43+
block: START_BLOCK,
44+
statement_index: !0
45+
},
46+
accesses: Events::new(mir, flat_locations, local_paths.total_count()),
47+
maybe_borrowed: IdxSetBuf::new_empty(0)
48+
};
49+
50+
// FIXME(eddyb) introduce a seeker for this (like in eventflow),
51+
// maybe reusing `dataflow::at_location(::FlowAtLocation)`.
52+
// That would remove the need for asserting the location.
53+
54+
for (block, data) in mir.basic_blocks().iter_enumerated() {
55+
collector.location.block = block;
56+
collector.maybe_borrowed = borrows.sets().on_entry_set_for(block.index()).to_owned();
57+
58+
let on_entry = &mut collector.maybe_borrowed.clone();
59+
let kill_set = &mut collector.maybe_borrowed.clone();
60+
for (i, statement) in data.statements.iter().enumerate() {
61+
collector.location.statement_index = i;
62+
borrows.operator().before_statement_effect(&mut BlockSets {
63+
on_entry,
64+
kill_set,
65+
gen_set: &mut collector.maybe_borrowed,
66+
}, collector.location);
67+
// FIXME(eddyb) get rid of temporary with NLL/2phi.
68+
let location = collector.location;
69+
collector.visit_statement(block, statement, location);
70+
borrows.operator().statement_effect(&mut BlockSets {
71+
on_entry,
72+
kill_set,
73+
gen_set: &mut collector.maybe_borrowed,
74+
}, collector.location);
75+
}
76+
77+
if let Some(ref terminator) = data.terminator {
78+
collector.location.statement_index = data.statements.len();
79+
borrows.operator().before_terminator_effect(&mut BlockSets {
80+
on_entry,
81+
kill_set,
82+
gen_set: &mut collector.maybe_borrowed,
83+
}, collector.location);
84+
// FIXME(eddyb) get rid of temporary with NLL/2phi.
85+
let location = collector.location;
86+
collector.visit_terminator(block, terminator, location);
87+
}
88+
}
89+
let results = collector.accesses.flow(mir.args_iter().map(|arg| {
90+
// All arguments have been accessed prior to the call to this function.
91+
local_paths.locals[arg]
92+
// FIXME(eddyb) this should work with just `arg`, not also its descendants.
93+
}));
94+
Accesses { results }
95+
}
96+
}
97+
98+
struct AccessPathCollector<'a, 'b, 'tcx: 'a> {
99+
local_paths: &'a LocalPaths<'tcx>,
100+
accesses: Events<'a, 'b, 'tcx, PathId>,
101+
location: Location,
102+
maybe_borrowed: IdxSetBuf<PathId>
103+
}
104+
105+
impl<'a, 'b, 'tcx> AccessPathCollector<'a, 'b, 'tcx> {
106+
fn access_anything_borrowed(&mut self, location: Location) {
107+
// FIXME(eddyb) OR `maybe_borrowed` into the accesses for performance.
108+
for path in self.maybe_borrowed.iter() {
109+
self.accesses.insert_at(path, location);
110+
}
111+
}
112+
}
113+
114+
impl<'a, 'b, 'tcx> Visitor<'tcx> for AccessPathCollector<'a, 'b, 'tcx> {
115+
fn visit_place(&mut self,
116+
place: &Place<'tcx>,
117+
context: PlaceContext<'tcx>,
118+
location: Location) {
119+
assert_eq!(self.location, location);
120+
121+
if context.is_use() {
122+
match self.local_paths.place_path_acessed_prefix(place) {
123+
Ok(path) | Err(Some(path)) => {
124+
self.accesses.insert_at(path, location);
125+
}
126+
Err(None) => {}
127+
}
128+
}
129+
130+
// Traverse the projections in `place`.
131+
let context = if context.is_mutating_use() {
132+
PlaceContext::Projection(Mutability::Mut)
133+
} else {
134+
PlaceContext::Projection(Mutability::Not)
135+
};
136+
let mut place = place;
137+
while let Place::Projection(ref proj) = *place {
138+
self.visit_projection_elem(&proj.elem, context, location);
139+
place = &proj.base;
140+
}
141+
}
142+
143+
// Handle the locals used in indexing projections.
144+
fn visit_local(&mut self,
145+
&local: &Local,
146+
context: PlaceContext,
147+
location: Location) {
148+
assert_eq!(self.location, location);
149+
150+
if context.is_use() {
151+
self.accesses.insert_at(self.local_paths.locals[local], location);
152+
}
153+
}
154+
155+
fn visit_projection_elem(&mut self,
156+
elem: &PlaceElem<'tcx>,
157+
context: PlaceContext<'tcx>,
158+
location: Location) {
159+
assert_eq!(self.location, location);
160+
161+
if let ProjectionElem::Deref = *elem {
162+
self.access_anything_borrowed(location);
163+
}
164+
self.super_projection_elem(elem, context, location);
165+
}
166+
167+
fn visit_terminator_kind(&mut self,
168+
block: BasicBlock,
169+
kind: &TerminatorKind<'tcx>,
170+
location: Location) {
171+
assert_eq!(self.location, location);
172+
173+
match *kind {
174+
TerminatorKind::Call { .. } => {
175+
self.access_anything_borrowed(location);
176+
}
177+
TerminatorKind::Return => {
178+
self.visit_local(&RETURN_PLACE, PlaceContext::Move, location);
179+
}
180+
_ => {}
181+
}
182+
self.super_terminator_kind(block, kind, location);
183+
}
184+
}

src/librustc_mir/analysis/local_paths/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use rustc::ty::Ty;
1515
use std::iter::Step;
1616
use std::ops::Range;
1717

18+
pub mod accesses;
1819
pub mod borrows;
1920
pub mod collect;
2021

0 commit comments

Comments
 (0)