Skip to content

Commit 9d1a5d2

Browse files
committed
rustc_mir: add a borrow dataflow analysis for "local paths".
1 parent 8f800f1 commit 9d1a5d2

File tree

3 files changed

+171
-2
lines changed

3 files changed

+171
-2
lines changed

src/librustc_mir/analysis/dataflow/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -513,7 +513,7 @@ pub struct BlockSets<'a, E: Idx> {
513513
}
514514

515515
impl<'a, E:Idx> BlockSets<'a, E> {
516-
fn gen(&mut self, e: &E) {
516+
pub(crate) fn gen(&mut self, e: &E) {
517517
self.gen_set.add(e);
518518
self.kill_set.remove(e);
519519
}
@@ -538,7 +538,7 @@ impl<'a, E:Idx> BlockSets<'a, E> {
538538
}
539539
}
540540

541-
fn kill(&mut self, e: &E) {
541+
pub(crate) fn kill(&mut self, e: &E) {
542542
self.gen_set.remove(e);
543543
self.kill_set.add(e);
544544
}
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
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::IdxSet;
12+
use rustc_data_structures::bitslice::BitwiseOperator;
13+
use rustc::mir::*;
14+
use rustc::mir::visit::Visitor;
15+
use std::iter;
16+
use analysis::dataflow::{BitDenotation, BlockSets, InitialFlow};
17+
use analysis::local_paths::{LocalPaths, PathId};
18+
19+
#[derive(Copy, Clone)]
20+
pub struct MaybeBorrowed<'a, 'tcx: 'a> {
21+
mir: &'a Mir<'tcx>,
22+
local_paths: &'a LocalPaths<'tcx>
23+
}
24+
25+
impl<'a, 'tcx: 'a> MaybeBorrowed<'a, 'tcx> {
26+
pub fn new(mir: &'a Mir<'tcx>, local_paths: &'a LocalPaths<'tcx>) -> Self {
27+
MaybeBorrowed { mir, local_paths }
28+
}
29+
}
30+
31+
impl<'a, 'tcx> BitDenotation for MaybeBorrowed<'a, 'tcx> {
32+
type Idx = PathId;
33+
fn name() -> &'static str { "maybe_borrowed" }
34+
fn bits_per_block(&self) -> usize {
35+
self.local_paths.total_count()
36+
}
37+
38+
fn start_block_effect(&self, _sets: &mut IdxSet<PathId>) {
39+
// Nothing is borrowed on function entry
40+
}
41+
42+
fn statement_effect(&self,
43+
sets: &mut BlockSets<PathId>,
44+
location: Location) {
45+
{
46+
let mut moves = MoveCollector {
47+
local_paths: self.local_paths,
48+
sets
49+
};
50+
moves.visit_location(self.mir, location);
51+
}
52+
53+
match self.mir[location.block].statements[location.statement_index].kind {
54+
StatementKind::Assign(_, Rvalue::Ref(.., ref place)) => {
55+
// Ignore `place`s based on a dereference, when `gen`-ing borrows,
56+
// as the resulting reference can't actually point to a local path
57+
// that isn't already borrowed, and definitely not the base reference.
58+
{
59+
let mut place = place;
60+
while let Place::Projection(ref proj) = *place {
61+
if let ProjectionElem::Deref = proj.elem {
62+
return;
63+
}
64+
place = &proj.base;
65+
}
66+
}
67+
68+
match self.local_paths.place_path_acessed_prefix(place) {
69+
Ok(path) | Err(Some(path)) => {
70+
sets.gen(&path);
71+
}
72+
Err(None) => {}
73+
}
74+
}
75+
StatementKind::StorageDead(local) => {
76+
// FIXME(eddyb) use ranges here for performance.
77+
let path = self.local_paths.locals[local];
78+
for path in iter::once(path).chain(self.local_paths.descendants(path)) {
79+
sets.kill(&path);
80+
}
81+
}
82+
// FIXME(eddyb) cancel all borrows on `yield` (unless the generator is immovable).
83+
_ => {}
84+
}
85+
}
86+
87+
fn terminator_effect(&self,
88+
sets: &mut BlockSets<PathId>,
89+
location: Location) {
90+
let mut moves = MoveCollector {
91+
local_paths: self.local_paths,
92+
sets
93+
};
94+
moves.visit_location(self.mir, location);
95+
}
96+
97+
fn propagate_call_return(&self,
98+
_in_out: &mut IdxSet<PathId>,
99+
_call_bb: BasicBlock,
100+
_dest_bb: BasicBlock,
101+
_dest_place: &Place) {
102+
// Nothing to do when a call returns successfully
103+
}
104+
}
105+
106+
impl<'a, 'tcx> BitwiseOperator for MaybeBorrowed<'a, 'tcx> {
107+
#[inline]
108+
fn join(&self, pred1: usize, pred2: usize) -> usize {
109+
pred1 | pred2 // "maybe" means we union effects of both preds
110+
}
111+
}
112+
113+
impl<'a, 'tcx> InitialFlow for MaybeBorrowed<'a, 'tcx> {
114+
#[inline]
115+
fn bottom_value() -> bool {
116+
false // bottom = unborrowed
117+
}
118+
}
119+
120+
struct MoveCollector<'a, 'b: 'a, 'tcx: 'a> {
121+
local_paths: &'a LocalPaths<'tcx>,
122+
sets: &'a mut BlockSets<'b, PathId>
123+
}
124+
125+
impl<'a, 'b, 'tcx> Visitor<'tcx> for MoveCollector<'a, 'b, 'tcx> {
126+
fn visit_operand(&mut self, operand: &Operand, _: Location) {
127+
if let Operand::Move(ref place) = *operand {
128+
if let Ok(path) = self.local_paths.place_path_acessed_prefix(place) {
129+
self.sets.kill(&path);
130+
}
131+
}
132+
}
133+
}

src/librustc_mir/analysis/local_paths/mod.rs

Lines changed: 36 additions & 0 deletions
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 borrows;
1819
pub mod collect;
1920

2021
newtype_index!(PathId { DEBUG_FORMAT = "PathId({})" });
@@ -97,6 +98,41 @@ impl<'tcx> LocalPaths<'tcx> {
9798
ProjectionElem::Deref => None
9899
}
99100
}
101+
102+
/// If possible, obtain a `PathId` for the complete `Place` (as `Ok(_)`),
103+
/// otherwise, give the longest `PathId` prefix (as `Err(Some(_))`).
104+
pub fn place_path(&self, place: &Place) -> Result<PathId, Option<PathId>> {
105+
match *place {
106+
Place::Local(local) => Ok(self.locals[local]),
107+
Place::Static(_) => Err(None),
108+
Place::Projection(ref proj) => {
109+
let base = self.place_path(&proj.base)?;
110+
match self.project(base, &proj.elem) {
111+
Some(child) => Ok(child),
112+
None => Err(Some(base))
113+
}
114+
}
115+
}
116+
}
117+
118+
/// Like `place_path`, but for the shortest accessed path prefix of the `place`.
119+
/// If the path doesn't refer to the complete `place`, it's returned in `Err`.
120+
pub fn place_path_acessed_prefix(&self, place: &Place) -> Result<PathId, Option<PathId>> {
121+
match *place {
122+
Place::Local(local) => Ok(self.locals[local]),
123+
Place::Static(_) => Err(None),
124+
Place::Projection(ref proj) => {
125+
let base = self.place_path_acessed_prefix(&proj.base)?;
126+
if self.data[base].accessed {
127+
return Err(Some(base));
128+
}
129+
match self.project(base, &proj.elem) {
130+
Some(child) => Ok(child),
131+
None => Err(Some(base))
132+
}
133+
}
134+
}
135+
}
100136
}
101137

102138
pub struct Children<'a, 'tcx: 'a> {

0 commit comments

Comments
 (0)