]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_mir/src/transform/rustc_peek.rs
New upstream version 1.49.0~beta.4+dfsg1
[rustc.git] / compiler / rustc_mir / src / transform / rustc_peek.rs
CommitLineData
1b1a35ee
XL
1use std::borrow::Borrow;
2
3use rustc_ast::ast;
dfeec247
XL
4use rustc_span::symbol::sym;
5use rustc_span::Span;
6use rustc_target::spec::abi::Abi;
3157f602 7
29967ef6 8use crate::transform::MirPass;
dfeec247 9use rustc_index::bit_set::BitSet;
f9f354fc 10use rustc_middle::mir::{self, Body, Local, Location};
ba9703b0 11use rustc_middle::ty::{self, Ty, TyCtxt};
3157f602 12
f9f354fc
XL
13use crate::dataflow::impls::{
14 DefinitelyInitializedPlaces, MaybeInitializedPlaces, MaybeLiveLocals, MaybeMutBorrowedLocals,
15 MaybeUninitializedPlaces,
16};
dfeec247
XL
17use crate::dataflow::move_paths::{HasMoveData, MoveData};
18use crate::dataflow::move_paths::{LookupResult, MovePathIndex};
dfeec247 19use crate::dataflow::MoveDataParamEnv;
1b1a35ee 20use crate::dataflow::{Analysis, JoinSemiLattice, Results, ResultsCursor};
041b39d2 21
041b39d2
XL
22pub struct SanityCheck;
23
e1599b0c 24impl<'tcx> MirPass<'tcx> for SanityCheck {
29967ef6 25 fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
74b04a01 26 use crate::dataflow::has_rustc_mir_with;
29967ef6 27 let def_id = body.source.def_id();
48663c56 28 if !tcx.has_attr(def_id, sym::rustc_mir) {
532ac7d7 29 debug!("skipping rustc_peek::SanityCheck on {}", tcx.def_path_str(def_id));
041b39d2
XL
30 return;
31 } else {
532ac7d7 32 debug!("running rustc_peek::SanityCheck on {}", tcx.def_path_str(def_id));
041b39d2
XL
33 }
34
35 let attributes = tcx.get_attrs(def_id);
36 let param_env = tcx.param_env(def_id);
60c5eb7d 37 let move_data = MoveData::gather_moves(body, tcx, param_env).unwrap();
74b04a01 38 let mdpe = MoveDataParamEnv { move_data, param_env };
3dfed10e 39 let sess = &tcx.sess;
74b04a01 40
3dfed10e 41 if has_rustc_mir_with(sess, &attributes, sym::rustc_peek_maybe_init).is_some() {
f9f354fc 42 let flow_inits = MaybeInitializedPlaces::new(tcx, body, &mdpe)
29967ef6 43 .into_engine(tcx, body)
f9f354fc
XL
44 .iterate_to_fixpoint();
45
29967ef6 46 sanity_check_via_rustc_peek(tcx, body, &attributes, &flow_inits);
041b39d2 47 }
f9f354fc 48
3dfed10e 49 if has_rustc_mir_with(sess, &attributes, sym::rustc_peek_maybe_uninit).is_some() {
f9f354fc 50 let flow_uninits = MaybeUninitializedPlaces::new(tcx, body, &mdpe)
29967ef6 51 .into_engine(tcx, body)
f9f354fc
XL
52 .iterate_to_fixpoint();
53
29967ef6 54 sanity_check_via_rustc_peek(tcx, body, &attributes, &flow_uninits);
041b39d2 55 }
f9f354fc 56
3dfed10e 57 if has_rustc_mir_with(sess, &attributes, sym::rustc_peek_definite_init).is_some() {
f9f354fc 58 let flow_def_inits = DefinitelyInitializedPlaces::new(tcx, body, &mdpe)
29967ef6 59 .into_engine(tcx, body)
f9f354fc
XL
60 .iterate_to_fixpoint();
61
29967ef6 62 sanity_check_via_rustc_peek(tcx, body, &attributes, &flow_def_inits);
041b39d2 63 }
f9f354fc 64
3dfed10e 65 if has_rustc_mir_with(sess, &attributes, sym::rustc_peek_indirectly_mutable).is_some() {
f9f354fc 66 let flow_mut_borrowed = MaybeMutBorrowedLocals::mut_borrows_only(tcx, body, param_env)
29967ef6 67 .into_engine(tcx, body)
f9f354fc
XL
68 .iterate_to_fixpoint();
69
29967ef6 70 sanity_check_via_rustc_peek(tcx, body, &attributes, &flow_mut_borrowed);
e74abb32 71 }
f9f354fc 72
3dfed10e 73 if has_rustc_mir_with(sess, &attributes, sym::rustc_peek_liveness).is_some() {
29967ef6 74 let flow_liveness = MaybeLiveLocals.into_engine(tcx, body).iterate_to_fixpoint();
f9f354fc 75
29967ef6 76 sanity_check_via_rustc_peek(tcx, body, &attributes, &flow_liveness);
f9f354fc
XL
77 }
78
3dfed10e 79 if has_rustc_mir_with(sess, &attributes, sym::stop_after_dataflow).is_some() {
041b39d2
XL
80 tcx.sess.fatal("stop_after_dataflow ended compilation");
81 }
82 }
83}
3157f602
XL
84
85/// This function scans `mir` for all calls to the intrinsic
86/// `rustc_peek` that have the expression form `rustc_peek(&expr)`.
87///
88/// For each such call, determines what the dataflow bit-state is for
89/// the L-value corresponding to `expr`; if the bit-state is a 1, then
90/// that call to `rustc_peek` is ignored by the sanity check. If the
91/// bit-state is a 0, then this pass emits a error message saying
92/// "rustc_peek: bit not set".
93///
94/// The intention is that one can write unit tests for dataflow by
95/// putting code into a compile-fail test and using `rustc_peek` to
96/// make observations about the results of dataflow static analyses.
97///
98/// (If there are any calls to `rustc_peek` that do not match the
99/// expression form above, then that emits an error as well, but those
100/// errors are not intended to be used for unit tests.)
74b04a01 101pub fn sanity_check_via_rustc_peek<'tcx, A>(
dc9dc135
XL
102 tcx: TyCtxt<'tcx>,
103 body: &Body<'tcx>,
dc9dc135 104 _attributes: &[ast::Attribute],
74b04a01 105 results: &Results<'tcx, A>,
dfeec247 106) where
74b04a01 107 A: RustcPeekAt<'tcx>,
dfeec247 108{
29967ef6 109 let def_id = body.source.def_id();
532ac7d7 110 debug!("sanity_check_via_rustc_peek def_id: {:?}", def_id);
3157f602 111
74b04a01 112 let mut cursor = ResultsCursor::new(body, results);
e74abb32 113
dfeec247
XL
114 let peek_calls = body.basic_blocks().iter_enumerated().filter_map(|(bb, block_data)| {
115 PeekCall::from_terminator(tcx, block_data.terminator()).map(|call| (bb, block_data, call))
116 });
e74abb32
XL
117
118 for (bb, block_data, call) in peek_calls {
119 // Look for a sequence like the following to indicate that we should be peeking at `_1`:
120 // _2 = &_1;
121 // rustc_peek(_2);
122 //
123 // /* or */
124 //
125 // _2 = _1;
126 // rustc_peek(_2);
127 let (statement_index, peek_rval) = block_data
128 .statements
129 .iter()
130 .enumerate()
f9f354fc 131 .find_map(|(i, stmt)| value_assigned_to_local(stmt, call.arg).map(|rval| (i, rval)))
dfeec247
XL
132 .expect(
133 "call to rustc_peek should be preceded by \
134 assignment to temporary holding its argument",
135 );
e74abb32
XL
136
137 match (call.kind, peek_rval) {
dfeec247 138 (PeekCallKind::ByRef, mir::Rvalue::Ref(_, _, place))
ba9703b0
XL
139 | (
140 PeekCallKind::ByVal,
141 mir::Rvalue::Use(mir::Operand::Move(place) | mir::Operand::Copy(place)),
142 ) => {
e74abb32 143 let loc = Location { block: bb, statement_index };
f9f354fc 144 cursor.seek_before_primary_effect(loc);
e74abb32 145 let state = cursor.get();
ba9703b0 146 results.analysis.peek_at(tcx, *place, state, call);
e74abb32
XL
147 }
148
149 _ => {
150 let msg = "rustc_peek: argument expression \
151 must be either `place` or `&place`";
152 tcx.sess.span_err(call.span, msg);
153 }
154 }
3157f602
XL
155 }
156}
157
e74abb32
XL
158/// If `stmt` is an assignment where the LHS is the given local (with no projections), returns the
159/// RHS of the assignment.
160fn value_assigned_to_local<'a, 'tcx>(
161 stmt: &'a mir::Statement<'tcx>,
162 local: Local,
163) -> Option<&'a mir::Rvalue<'tcx>> {
164 if let mir::StatementKind::Assign(box (place, rvalue)) = &stmt.kind {
165 if let Some(l) = place.as_local() {
166 if local == l {
167 return Some(&*rvalue);
3157f602 168 }
e74abb32
XL
169 }
170 }
3157f602 171
e74abb32
XL
172 None
173}
174
175#[derive(Clone, Copy, Debug)]
176enum PeekCallKind {
177 ByVal,
178 ByRef,
179}
180
181impl PeekCallKind {
182 fn from_arg_ty(arg: Ty<'_>) -> Self {
1b1a35ee 183 match arg.kind() {
e74abb32
XL
184 ty::Ref(_, _, _) => PeekCallKind::ByRef,
185 _ => PeekCallKind::ByVal,
186 }
187 }
188}
189
190#[derive(Clone, Copy, Debug)]
191pub struct PeekCall {
192 arg: Local,
193 kind: PeekCallKind,
194 span: Span,
195}
196
197impl PeekCall {
198 fn from_terminator<'tcx>(
199 tcx: TyCtxt<'tcx>,
200 terminator: &mir::Terminator<'tcx>,
201 ) -> Option<Self> {
202 use mir::Operand;
203
204 let span = terminator.source_info.span;
205 if let mir::TerminatorKind::Call { func: Operand::Constant(func), args, .. } =
206 &terminator.kind
207 {
1b1a35ee 208 if let ty::FnDef(def_id, substs) = *func.literal.ty.kind() {
e74abb32
XL
209 let sig = tcx.fn_sig(def_id);
210 let name = tcx.item_name(def_id);
211 if sig.abi() != Abi::RustIntrinsic || name != sym::rustc_peek {
212 return None;
213 }
214
215 assert_eq!(args.len(), 1);
216 let kind = PeekCallKind::from_arg_ty(substs.type_at(0));
217 let arg = match &args[0] {
218 Operand::Copy(place) | Operand::Move(place) => {
219 if let Some(local) = place.as_local() {
220 local
221 } else {
222 tcx.sess.diagnostic().span_err(
223 span,
224 "dataflow::sanity_check cannot feed a non-temp to rustc_peek.",
225 );
226 return None;
9e0c209e
SL
227 }
228 }
e74abb32
XL
229 _ => {
230 tcx.sess.diagnostic().span_err(
231 span,
232 "dataflow::sanity_check cannot feed a non-temp to rustc_peek.",
233 );
234 return None;
9e0c209e 235 }
e74abb32
XL
236 };
237
dfeec247 238 return Some(PeekCall { arg, kind, span });
3157f602
XL
239 }
240 }
241
e74abb32 242 None
3157f602 243 }
e74abb32 244}
3157f602 245
74b04a01 246pub trait RustcPeekAt<'tcx>: Analysis<'tcx> {
e74abb32
XL
247 fn peek_at(
248 &self,
249 tcx: TyCtxt<'tcx>,
ba9703b0 250 place: mir::Place<'tcx>,
1b1a35ee 251 flow_state: &Self::Domain,
e74abb32
XL
252 call: PeekCall,
253 );
3157f602
XL
254}
255
1b1a35ee 256impl<'tcx, A, D> RustcPeekAt<'tcx> for A
dfeec247 257where
1b1a35ee
XL
258 A: Analysis<'tcx, Domain = D> + HasMoveData<'tcx>,
259 D: JoinSemiLattice + Clone + Borrow<BitSet<MovePathIndex>>,
e74abb32
XL
260{
261 fn peek_at(
262 &self,
263 tcx: TyCtxt<'tcx>,
ba9703b0 264 place: mir::Place<'tcx>,
1b1a35ee 265 flow_state: &Self::Domain,
e74abb32
XL
266 call: PeekCall,
267 ) {
268 match self.move_data().rev_lookup.find(place.as_ref()) {
269 LookupResult::Exact(peek_mpi) => {
1b1a35ee 270 let bit_state = flow_state.borrow().contains(peek_mpi);
dfeec247 271 debug!("rustc_peek({:?} = &{:?}) bit_state: {}", call.arg, place, bit_state);
e74abb32
XL
272 if !bit_state {
273 tcx.sess.span_err(call.span, "rustc_peek: bit not set");
3157f602
XL
274 }
275 }
e74abb32
XL
276
277 LookupResult::Parent(..) => {
278 tcx.sess.span_err(call.span, "rustc_peek: argument untracked");
279 }
280 }
281 }
282}
283
74b04a01 284impl<'tcx> RustcPeekAt<'tcx> for MaybeMutBorrowedLocals<'_, 'tcx> {
e74abb32
XL
285 fn peek_at(
286 &self,
287 tcx: TyCtxt<'tcx>,
ba9703b0 288 place: mir::Place<'tcx>,
e74abb32
XL
289 flow_state: &BitSet<Local>,
290 call: PeekCall,
291 ) {
292 warn!("peek_at: place={:?}", place);
293 let local = if let Some(l) = place.as_local() {
294 l
295 } else {
296 tcx.sess.span_err(call.span, "rustc_peek: argument was not a local");
297 return;
298 };
299
300 if !flow_state.contains(local) {
301 tcx.sess.span_err(call.span, "rustc_peek: bit not set");
3157f602
XL
302 }
303 }
3157f602 304}
f9f354fc
XL
305
306impl<'tcx> RustcPeekAt<'tcx> for MaybeLiveLocals {
307 fn peek_at(
308 &self,
309 tcx: TyCtxt<'tcx>,
310 place: mir::Place<'tcx>,
311 flow_state: &BitSet<Local>,
312 call: PeekCall,
313 ) {
314 warn!("peek_at: place={:?}", place);
315 let local = if let Some(l) = place.as_local() {
316 l
317 } else {
318 tcx.sess.span_err(call.span, "rustc_peek: argument was not a local");
319 return;
320 };
321
322 if !flow_state.contains(local) {
323 tcx.sess.span_err(call.span, "rustc_peek: bit not set");
324 }
325 }
326}