Skip to content

Commit f18535f

Browse files
Refactor rustc_peek
We now use `DataflowResultsCursor` to get the dataflow state before calls to `rustc_peek`. The original version used a custom implementation that only looked at assignment statements. This also extends `rustc_peek` to take arguments by-value as well as by-reference, and allows *all* dataflow analyses, not just those dependent on `MoveData`, to be inspected.
1 parent 702b45e commit f18535f

File tree

1 file changed

+153
-131
lines changed

1 file changed

+153
-131
lines changed

src/librustc_mir/transform/rustc_peek.rs

+153-131
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,17 @@ use syntax::ast;
33
use syntax::symbol::sym;
44
use syntax_pos::Span;
55

6-
use rustc::ty::{self, TyCtxt};
6+
use rustc::ty::{self, TyCtxt, Ty};
77
use rustc::hir::def_id::DefId;
8-
use rustc::mir::{self, Body, Location};
8+
use rustc::mir::{self, Body, Location, Local};
99
use rustc_index::bit_set::BitSet;
1010
use crate::transform::{MirPass, MirSource};
1111

1212
use crate::dataflow::{do_dataflow, DebugFormatted};
1313
use crate::dataflow::MoveDataParamEnv;
1414
use crate::dataflow::BitDenotation;
1515
use crate::dataflow::DataflowResults;
16+
use crate::dataflow::DataflowResultsCursor;
1617
use crate::dataflow::{
1718
DefinitelyInitializedPlaces, MaybeInitializedPlaces, MaybeUninitializedPlaces
1819
};
@@ -88,151 +89,172 @@ pub fn sanity_check_via_rustc_peek<'tcx, O>(
8889
def_id: DefId,
8990
_attributes: &[ast::Attribute],
9091
results: &DataflowResults<'tcx, O>,
91-
) where
92-
O: BitDenotation<'tcx, Idx = MovePathIndex> + HasMoveData<'tcx>,
93-
{
92+
) where O: RustcPeekAt<'tcx> {
9493
debug!("sanity_check_via_rustc_peek def_id: {:?}", def_id);
95-
// FIXME: this is not DRY. Figure out way to abstract this and
96-
// `dataflow::build_sets`. (But note it is doing non-standard
97-
// stuff, so such generalization may not be realistic.)
9894

99-
for bb in body.basic_blocks().indices() {
100-
each_block(tcx, body, results, bb);
101-
}
102-
}
95+
let mut cursor = DataflowResultsCursor::new(results, body);
10396

104-
fn each_block<'tcx, O>(
105-
tcx: TyCtxt<'tcx>,
106-
body: &Body<'tcx>,
107-
results: &DataflowResults<'tcx, O>,
108-
bb: mir::BasicBlock,
109-
) where
110-
O: BitDenotation<'tcx, Idx = MovePathIndex> + HasMoveData<'tcx>,
111-
{
112-
let move_data = results.0.operator.move_data();
113-
let mir::BasicBlockData { ref statements, ref terminator, is_cleanup: _ } = body[bb];
114-
115-
let (args, span) = match is_rustc_peek(tcx, terminator) {
116-
Some(args_and_span) => args_and_span,
117-
None => return,
118-
};
119-
assert!(args.len() == 1);
120-
let peek_arg_place = match args[0] {
121-
mir::Operand::Copy(ref place @ mir::Place {
122-
base: mir::PlaceBase::Local(_),
123-
projection: box [],
124-
}) |
125-
mir::Operand::Move(ref place @ mir::Place {
126-
base: mir::PlaceBase::Local(_),
127-
projection: box [],
128-
}) => Some(place),
129-
_ => None,
130-
};
131-
132-
let peek_arg_place = match peek_arg_place {
133-
Some(arg) => arg,
134-
None => {
135-
tcx.sess.diagnostic().span_err(
136-
span, "dataflow::sanity_check cannot feed a non-temp to rustc_peek.");
137-
return;
138-
}
139-
};
140-
141-
let mut on_entry = results.0.sets.entry_set_for(bb.index()).to_owned();
142-
let mut trans = results.0.sets.trans_for(bb.index()).clone();
143-
144-
// Emulate effect of all statements in the block up to (but not
145-
// including) the borrow within `peek_arg_place`. Do *not* include
146-
// call to `peek_arg_place` itself (since we are peeking the state
147-
// of the argument at time immediate preceding Call to
148-
// `rustc_peek`).
149-
150-
for (j, stmt) in statements.iter().enumerate() {
151-
debug!("rustc_peek: ({:?},{}) {:?}", bb, j, stmt);
152-
let (place, rvalue) = match stmt.kind {
153-
mir::StatementKind::Assign(box(ref place, ref rvalue)) => {
154-
(place, rvalue)
97+
let peek_calls = body
98+
.basic_blocks()
99+
.iter_enumerated()
100+
.filter_map(|(bb, block_data)| {
101+
PeekCall::from_terminator(tcx, block_data.terminator())
102+
.map(|call| (bb, block_data, call))
103+
});
104+
105+
for (bb, block_data, call) in peek_calls {
106+
// Look for a sequence like the following to indicate that we should be peeking at `_1`:
107+
// _2 = &_1;
108+
// rustc_peek(_2);
109+
//
110+
// /* or */
111+
//
112+
// _2 = _1;
113+
// rustc_peek(_2);
114+
let (statement_index, peek_rval) = block_data
115+
.statements
116+
.iter()
117+
.enumerate()
118+
.filter_map(|(i, stmt)| value_assigned_to_local(stmt, call.arg).map(|rval| (i, rval)))
119+
.next()
120+
.expect("call to rustc_peek should be preceded by \
121+
assignment to temporary holding its argument");
122+
123+
match (call.kind, peek_rval) {
124+
| (PeekCallKind::ByRef, mir::Rvalue::Ref(_, _, place))
125+
| (PeekCallKind::ByVal, mir::Rvalue::Use(mir::Operand::Move(place)))
126+
| (PeekCallKind::ByVal, mir::Rvalue::Use(mir::Operand::Copy(place)))
127+
=> {
128+
let loc = Location { block: bb, statement_index };
129+
cursor.seek(loc);
130+
let state = cursor.get();
131+
results.operator().peek_at(tcx, place, state, call);
155132
}
156-
mir::StatementKind::FakeRead(..) |
157-
mir::StatementKind::StorageLive(_) |
158-
mir::StatementKind::StorageDead(_) |
159-
mir::StatementKind::InlineAsm { .. } |
160-
mir::StatementKind::Retag { .. } |
161-
mir::StatementKind::AscribeUserType(..) |
162-
mir::StatementKind::Nop => continue,
163-
mir::StatementKind::SetDiscriminant{ .. } =>
164-
span_bug!(stmt.source_info.span,
165-
"sanity_check should run before Deaggregator inserts SetDiscriminant"),
166-
};
167-
168-
if place == peek_arg_place {
169-
if let mir::Rvalue::Ref(_, mir::BorrowKind::Shared, ref peeking_at_place) = *rvalue {
170-
// Okay, our search is over.
171-
match move_data.rev_lookup.find(peeking_at_place.as_ref()) {
172-
LookupResult::Exact(peek_mpi) => {
173-
let bit_state = on_entry.contains(peek_mpi);
174-
debug!("rustc_peek({:?} = &{:?}) bit_state: {}",
175-
place, peeking_at_place, bit_state);
176-
if !bit_state {
177-
tcx.sess.span_err(span, "rustc_peek: bit not set");
178-
}
179-
}
180-
LookupResult::Parent(..) => {
181-
tcx.sess.span_err(span, "rustc_peek: argument untracked");
182-
}
183-
}
184-
return;
185-
} else {
186-
// Our search should have been over, but the input
187-
// does not match expectations of `rustc_peek` for
188-
// this sanity_check.
133+
134+
_ => {
189135
let msg = "rustc_peek: argument expression \
190-
must be immediate borrow of form `&expr`";
191-
tcx.sess.span_err(span, msg);
136+
must be either `place` or `&place`";
137+
tcx.sess.span_err(call.span, msg);
192138
}
193139
}
140+
}
141+
}
194142

195-
let lhs_mpi = move_data.rev_lookup.find(place.as_ref());
196-
197-
debug!("rustc_peek: computing effect on place: {:?} ({:?}) in stmt: {:?}",
198-
place, lhs_mpi, stmt);
199-
// reset GEN and KILL sets before emulating their effect.
200-
trans.clear();
201-
results.0.operator.before_statement_effect(
202-
&mut trans,
203-
Location { block: bb, statement_index: j });
204-
results.0.operator.statement_effect(
205-
&mut trans,
206-
Location { block: bb, statement_index: j });
207-
trans.apply(&mut on_entry);
143+
/// If `stmt` is an assignment where the LHS is the given local (with no projections), returns the
144+
/// RHS of the assignment.
145+
fn value_assigned_to_local<'a, 'tcx>(
146+
stmt: &'a mir::Statement<'tcx>,
147+
local: Local,
148+
) -> Option<&'a mir::Rvalue<'tcx>> {
149+
if let mir::StatementKind::Assign(box (place, rvalue)) = &stmt.kind {
150+
if let mir::Place { base: mir::PlaceBase::Local(l), projection: box [] } = place {
151+
if local == *l {
152+
return Some(&*rvalue);
153+
}
154+
}
208155
}
209156

210-
results.0.operator.before_terminator_effect(
211-
&mut trans,
212-
Location { block: bb, statement_index: statements.len() });
157+
None
158+
}
213159

214-
tcx.sess.span_err(span, &format!("rustc_peek: MIR did not match \
215-
anticipated pattern; note that \
216-
rustc_peek expects input of \
217-
form `&expr`"));
160+
#[derive(Clone, Copy, Debug)]
161+
enum PeekCallKind {
162+
ByVal,
163+
ByRef,
218164
}
219165

220-
fn is_rustc_peek<'a, 'tcx>(
221-
tcx: TyCtxt<'tcx>,
222-
terminator: &'a Option<mir::Terminator<'tcx>>,
223-
) -> Option<(&'a [mir::Operand<'tcx>], Span)> {
224-
if let Some(mir::Terminator { ref kind, source_info, .. }) = *terminator {
225-
if let mir::TerminatorKind::Call { func: ref oper, ref args, .. } = *kind {
226-
if let mir::Operand::Constant(ref func) = *oper {
227-
if let ty::FnDef(def_id, _) = func.literal.ty.kind {
228-
let abi = tcx.fn_sig(def_id).abi();
229-
let name = tcx.item_name(def_id);
230-
if abi == Abi::RustIntrinsic && name == sym::rustc_peek {
231-
return Some((args, source_info.span));
166+
impl PeekCallKind {
167+
fn from_arg_ty(arg: Ty<'_>) -> Self {
168+
match arg.kind {
169+
ty::Ref(_, _, _) => PeekCallKind::ByRef,
170+
_ => PeekCallKind::ByVal,
171+
}
172+
}
173+
}
174+
175+
#[derive(Clone, Copy, Debug)]
176+
pub struct PeekCall {
177+
arg: Local,
178+
kind: PeekCallKind,
179+
span: Span,
180+
}
181+
182+
impl PeekCall {
183+
fn from_terminator<'tcx>(
184+
tcx: TyCtxt<'tcx>,
185+
terminator: &mir::Terminator<'tcx>,
186+
) -> Option<Self> {
187+
use mir::{Operand, Place, PlaceBase};
188+
189+
let span = terminator.source_info.span;
190+
if let mir::TerminatorKind::Call { func: Operand::Constant(func), args, .. } =
191+
&terminator.kind
192+
{
193+
if let ty::FnDef(def_id, substs) = func.literal.ty.kind {
194+
let sig = tcx.fn_sig(def_id);
195+
let name = tcx.item_name(def_id);
196+
if sig.abi() != Abi::RustIntrinsic || name != sym::rustc_peek {
197+
return None;
198+
}
199+
200+
assert_eq!(args.len(), 1);
201+
let kind = PeekCallKind::from_arg_ty(substs.type_at(0));
202+
let arg = match args[0] {
203+
| Operand::Copy(Place { base: PlaceBase::Local(local), projection: box [] })
204+
| Operand::Move(Place { base: PlaceBase::Local(local), projection: box [] })
205+
=> local,
206+
207+
_ => {
208+
tcx.sess.diagnostic().span_err(
209+
span, "dataflow::sanity_check cannot feed a non-temp to rustc_peek.");
210+
return None;
232211
}
212+
};
213+
214+
return Some(PeekCall {
215+
arg,
216+
kind,
217+
span,
218+
});
219+
}
220+
}
221+
222+
None
223+
}
224+
}
225+
226+
pub trait RustcPeekAt<'tcx>: BitDenotation<'tcx> {
227+
fn peek_at(
228+
&self,
229+
tcx: TyCtxt<'tcx>,
230+
place: &mir::Place<'tcx>,
231+
flow_state: &BitSet<Self::Idx>,
232+
call: PeekCall,
233+
);
234+
}
235+
236+
impl<'tcx, O> RustcPeekAt<'tcx> for O
237+
where O: BitDenotation<'tcx, Idx = MovePathIndex> + HasMoveData<'tcx>,
238+
{
239+
fn peek_at(
240+
&self,
241+
tcx: TyCtxt<'tcx>,
242+
place: &mir::Place<'tcx>,
243+
flow_state: &BitSet<Self::Idx>,
244+
call: PeekCall,
245+
) {
246+
match self.move_data().rev_lookup.find(place.as_ref()) {
247+
LookupResult::Exact(peek_mpi) => {
248+
let bit_state = flow_state.contains(peek_mpi);
249+
debug!("rustc_peek({:?} = &{:?}) bit_state: {}",
250+
call.arg, place, bit_state);
251+
if !bit_state {
252+
tcx.sess.span_err(call.span, "rustc_peek: bit not set");
233253
}
234254
}
255+
LookupResult::Parent(..) => {
256+
tcx.sess.span_err(call.span, "rustc_peek: argument untracked");
257+
}
235258
}
236259
}
237-
return None;
238260
}

0 commit comments

Comments
 (0)