Skip to content

Commit 8f800f1

Browse files
committed
rustc_mir: add a "local (interior) path" collector analysis pass.
1 parent 8d281d2 commit 8f800f1

File tree

4 files changed

+293
-0
lines changed

4 files changed

+293
-0
lines changed
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
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_vec::{IndexVec, Idx};
12+
use rustc_data_structures::fx::FxHashMap;
13+
use rustc::mir::*;
14+
use rustc::mir::visit::{PlaceContext, Visitor};
15+
use rustc::ty::Ty;
16+
use analysis::local_paths::{LocalPaths, PathData, PathId};
17+
18+
impl<'tcx> LocalPaths<'tcx> {
19+
pub fn collect(mir: &Mir<'tcx>) -> Self {
20+
let mut collector = LocalPathCollector {
21+
locals: mir.local_decls.iter().map(|decl| {
22+
PathTree::new(decl.ty)
23+
})
24+
.collect()
25+
};
26+
// All arguments have been accessed prior to the call to this function.
27+
for arg in mir.args_iter() {
28+
collector.locals[arg].accessed = true;
29+
}
30+
collector.visit_mir(mir);
31+
let mut fields = FxHashMap::default();
32+
let mut variants = FxHashMap::default();
33+
let mut data = IndexVec::new();
34+
LocalPaths {
35+
locals: collector.locals.iter().map(|tree| {
36+
tree.flatten(&mut fields, &mut variants, &mut data)
37+
}).collect(),
38+
fields,
39+
variants,
40+
data
41+
}
42+
}
43+
}
44+
45+
struct PathTree<'tcx> {
46+
ty: Ty<'tcx>,
47+
accessed: bool,
48+
fields: FxHashMap<Field, PathTree<'tcx>>,
49+
variants: FxHashMap<usize, PathTree<'tcx>>,
50+
}
51+
52+
impl<'tcx> PathTree<'tcx> {
53+
fn new(ty: Ty<'tcx>) -> Self {
54+
PathTree {
55+
ty,
56+
accessed: false,
57+
fields: FxHashMap::default(),
58+
variants: FxHashMap::default()
59+
}
60+
}
61+
62+
fn project(&mut self, elem: &PlaceElem<'tcx>) -> Option<&mut Self> {
63+
if let Some(adt) = self.ty.ty_adt_def() {
64+
// Packed types have additional restrictions
65+
// and it's easier to just not look into them.
66+
if adt.repr.packed() {
67+
return None;
68+
}
69+
70+
// Enums and unions have overlapping members, so every access
71+
// of any member must be treated as an access of any other.
72+
if adt.is_union() || adt.is_enum() {
73+
self.accessed = true;
74+
}
75+
}
76+
77+
match *elem {
78+
ProjectionElem::Field(f, ty) => {
79+
Some(self.fields.entry(f).or_insert(PathTree::new(ty)))
80+
}
81+
ProjectionElem::Downcast(_, v) => {
82+
Some(self.variants.entry(v).or_insert(PathTree::new(self.ty)))
83+
}
84+
// Could support indexing by constants in the future.
85+
ProjectionElem::ConstantIndex { .. } |
86+
ProjectionElem::Subslice { .. } => None,
87+
// Can't support without alias analysis.
88+
ProjectionElem::Index(_) |
89+
ProjectionElem::Deref => None
90+
}
91+
}
92+
93+
fn flatten(&self,
94+
fields: &mut FxHashMap<(PathId, Field), PathId>,
95+
variants: &mut FxHashMap<(PathId, usize), PathId>,
96+
data: &mut IndexVec<PathId, PathData<'tcx>>)
97+
-> PathId {
98+
let root = data.push(PathData {
99+
ty: self.ty,
100+
last_descendant: PathId::new(0),
101+
accessed: self.accessed
102+
});
103+
for (&f, child) in &self.fields {
104+
let child = child.flatten(fields, variants, data);
105+
fields.insert((root, f), child);
106+
}
107+
for (&v, child) in &self.variants {
108+
let child = child.flatten(fields, variants, data);
109+
variants.insert((root, v), child);
110+
}
111+
data[root].last_descendant = data.last().unwrap();
112+
root
113+
}
114+
}
115+
116+
struct LocalPathCollector<'tcx> {
117+
locals: IndexVec<Local, PathTree<'tcx>>
118+
}
119+
120+
impl<'tcx> LocalPathCollector<'tcx> {
121+
fn place_path(&mut self, place: &Place<'tcx>) -> Option<&mut PathTree<'tcx>> {
122+
match *place {
123+
Place::Local(local) => Some(&mut self.locals[local]),
124+
Place::Static(_) => None,
125+
Place::Projection(ref proj) => {
126+
let base = self.place_path(&proj.base)?;
127+
base.project(&proj.elem)
128+
}
129+
}
130+
}
131+
}
132+
133+
impl<'tcx> Visitor<'tcx> for LocalPathCollector<'tcx> {
134+
fn visit_place(&mut self,
135+
place: &Place<'tcx>,
136+
context: PlaceContext,
137+
location: Location) {
138+
if let Some(path) = self.place_path(place) {
139+
if context.is_use() {
140+
path.accessed = true;
141+
}
142+
}
143+
144+
// Traverse the projections in `place`.
145+
let context = if context.is_mutating_use() {
146+
PlaceContext::Projection(Mutability::Mut)
147+
} else {
148+
PlaceContext::Projection(Mutability::Not)
149+
};
150+
let mut place = place;
151+
while let Place::Projection(ref proj) = *place {
152+
self.visit_projection_elem(&proj.elem, context, location);
153+
place = &proj.base;
154+
}
155+
}
156+
157+
// Handle the locals used in indexing projections.
158+
fn visit_local(&mut self,
159+
&local: &Local,
160+
context: PlaceContext,
161+
_: Location) {
162+
if context.is_use() {
163+
self.locals[local].accessed = true;
164+
}
165+
}
166+
167+
fn visit_terminator_kind(&mut self,
168+
block: BasicBlock,
169+
kind: &TerminatorKind<'tcx>,
170+
location: Location) {
171+
if let TerminatorKind::Return = *kind {
172+
self.visit_local(&RETURN_PLACE, PlaceContext::Move, location);
173+
}
174+
self.super_terminator_kind(block, kind, location);
175+
}
176+
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
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_vec::{IndexVec, Idx};
12+
use rustc_data_structures::fx::FxHashMap;
13+
use rustc::mir::*;
14+
use rustc::ty::Ty;
15+
use std::iter::Step;
16+
use std::ops::Range;
17+
18+
pub mod collect;
19+
20+
newtype_index!(PathId { DEBUG_FORMAT = "PathId({})" });
21+
22+
impl Step for PathId {
23+
fn steps_between(start: &Self, end: &Self) -> Option<usize> {
24+
Step::steps_between(&start.index(), &end.index())
25+
}
26+
fn replace_one(&mut self) -> Self {
27+
*self = PathId::new(self.index().replace_one());
28+
*self
29+
}
30+
fn replace_zero(&mut self) -> Self {
31+
*self = PathId::new(self.index().replace_zero());
32+
*self
33+
}
34+
fn add_one(&self) -> Self {
35+
PathId::new(self.index().add_one())
36+
}
37+
fn sub_one(&self) -> Self {
38+
PathId::new(self.index().sub_one())
39+
}
40+
fn add_usize(&self, n: usize) -> Option<Self> {
41+
self.index().add_usize(n).map(PathId::new)
42+
}
43+
}
44+
45+
pub struct PathData<'tcx> {
46+
pub ty: Ty<'tcx>,
47+
pub last_descendant: PathId,
48+
49+
/// Whether this path is ever directly accessed,
50+
/// instead of being just a parent of a path that is.
51+
// FIXME(eddyb) have a separate notion of "access path",
52+
// to keep the sets working on it small.
53+
pub accessed: bool
54+
}
55+
56+
/// A forest of `Place` interior paths into `Local` roots, flattened in
57+
/// pre-order, with each node immediatelly followed by its descendants.
58+
///
59+
/// Paths into dereferences aren't tracked, as they count as distinct
60+
/// "interior" roots, which aren't meaningful without alias analysis.
61+
/// As such, users must handle indirect accesses themselves.
62+
///
63+
/// Paths into array elements aren't currently supported but they could be.
64+
pub struct LocalPaths<'tcx> {
65+
pub data: IndexVec<PathId, PathData<'tcx>>,
66+
pub locals: IndexVec<Local, PathId>,
67+
pub fields: FxHashMap<(PathId, Field), PathId>,
68+
pub variants: FxHashMap<(PathId, usize), PathId>
69+
}
70+
71+
impl<'tcx> LocalPaths<'tcx> {
72+
pub fn total_count(&self) -> usize {
73+
self.data.len()
74+
}
75+
76+
pub fn descendants(&self, path: PathId) -> Range<PathId> {
77+
path.add_one()..self.data[path].last_descendant.add_one()
78+
}
79+
80+
pub fn children<'a>(&'a self, path: PathId) -> Children<'a, 'tcx> {
81+
Children {
82+
local_paths: self,
83+
descendants: self.descendants(path)
84+
}
85+
}
86+
87+
/// Obtain the `PathId` for the `elem` component of `base`, if it is tracked.
88+
pub fn project<V, T>(&self, base: PathId, elem: &ProjectionElem<V, T>) -> Option<PathId> {
89+
match *elem {
90+
ProjectionElem::Field(f, _) => self.fields.get(&(base, f)).cloned(),
91+
ProjectionElem::Downcast(_, v) => self.variants.get(&(base, v)).cloned(),
92+
// Could support indexing by constants in the future.
93+
ProjectionElem::ConstantIndex { .. } |
94+
ProjectionElem::Subslice { .. } => None,
95+
// Can't support without alias analysis.
96+
ProjectionElem::Index(_) |
97+
ProjectionElem::Deref => None
98+
}
99+
}
100+
}
101+
102+
pub struct Children<'a, 'tcx: 'a> {
103+
local_paths: &'a LocalPaths<'tcx>,
104+
descendants: Range<PathId>
105+
}
106+
107+
impl<'a, 'tcx> Iterator for Children<'a, 'tcx> {
108+
type Item = PathId;
109+
fn next(&mut self) -> Option<PathId> {
110+
self.descendants.next().map(|child| {
111+
self.descendants.start = self.local_paths.descendants(child).end;
112+
child
113+
})
114+
}
115+
}

src/librustc_mir/analysis/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ pub mod alignment;
1212
pub mod dataflow;
1313
pub mod def_use;
1414
pub mod liveness;
15+
pub mod local_paths;

src/librustc_mir/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment!
3434
#![feature(range_contains)]
3535
#![feature(rustc_diagnostic_macros)]
3636
#![feature(placement_in_syntax)]
37+
#![feature(step_trait)]
3738
#![feature(collection_placement)]
3839
#![feature(nonzero)]
3940
#![feature(underscore_lifetimes)]

0 commit comments

Comments
 (0)