@@ -3,19 +3,21 @@ use syntax::ast;
3
3
use syntax:: symbol:: sym;
4
4
use syntax_pos:: Span ;
5
5
6
- use rustc:: ty:: { self , TyCtxt } ;
6
+ use rustc:: ty:: { self , TyCtxt , Ty } ;
7
7
use rustc:: hir:: def_id:: DefId ;
8
- use rustc:: mir:: { self , Body , Location } ;
8
+ use rustc:: mir:: { self , Body , Location , Local } ;
9
9
use rustc_index:: bit_set:: BitSet ;
10
10
use crate :: transform:: { MirPass , MirSource } ;
11
11
12
12
use crate :: dataflow:: { do_dataflow, DebugFormatted } ;
13
13
use crate :: dataflow:: MoveDataParamEnv ;
14
14
use crate :: dataflow:: BitDenotation ;
15
15
use crate :: dataflow:: DataflowResults ;
16
+ use crate :: dataflow:: DataflowResultsCursor ;
16
17
use crate :: dataflow:: {
17
18
DefinitelyInitializedPlaces , MaybeInitializedPlaces , MaybeUninitializedPlaces
18
19
} ;
20
+ use crate :: dataflow:: IndirectlyMutableLocals ;
19
21
use crate :: dataflow:: move_paths:: { MovePathIndex , LookupResult } ;
20
22
use crate :: dataflow:: move_paths:: { HasMoveData , MoveData } ;
21
23
@@ -50,6 +52,10 @@ impl<'tcx> MirPass<'tcx> for SanityCheck {
50
52
do_dataflow ( tcx, body, def_id, & attributes, & dead_unwinds,
51
53
DefinitelyInitializedPlaces :: new ( tcx, body, & mdpe) ,
52
54
|bd, i| DebugFormatted :: new ( & bd. move_data ( ) . move_paths [ i] ) ) ;
55
+ let flow_indirectly_mut =
56
+ do_dataflow ( tcx, body, def_id, & attributes, & dead_unwinds,
57
+ IndirectlyMutableLocals :: new ( tcx, body, param_env) ,
58
+ |_, i| DebugFormatted :: new ( & i) ) ;
53
59
54
60
if has_rustc_mir_with ( & attributes, sym:: rustc_peek_maybe_init) . is_some ( ) {
55
61
sanity_check_via_rustc_peek ( tcx, body, def_id, & attributes, & flow_inits) ;
@@ -60,6 +66,9 @@ impl<'tcx> MirPass<'tcx> for SanityCheck {
60
66
if has_rustc_mir_with ( & attributes, sym:: rustc_peek_definite_init) . is_some ( ) {
61
67
sanity_check_via_rustc_peek ( tcx, body, def_id, & attributes, & flow_def_inits) ;
62
68
}
69
+ if has_rustc_mir_with ( & attributes, sym:: rustc_peek_indirectly_mutable) . is_some ( ) {
70
+ sanity_check_via_rustc_peek ( tcx, body, def_id, & attributes, & flow_indirectly_mut) ;
71
+ }
63
72
if has_rustc_mir_with ( & attributes, sym:: stop_after_dataflow) . is_some ( ) {
64
73
tcx. sess . fatal ( "stop_after_dataflow ended compilation" ) ;
65
74
}
@@ -88,151 +97,196 @@ pub fn sanity_check_via_rustc_peek<'tcx, O>(
88
97
def_id : DefId ,
89
98
_attributes : & [ ast:: Attribute ] ,
90
99
results : & DataflowResults < ' tcx , O > ,
91
- ) where
92
- O : BitDenotation < ' tcx , Idx = MovePathIndex > + HasMoveData < ' tcx > ,
93
- {
100
+ ) where O : RustcPeekAt < ' tcx > {
94
101
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.)
98
102
99
- for bb in body. basic_blocks ( ) . indices ( ) {
100
- each_block ( tcx, body, results, bb) ;
101
- }
102
- }
103
+ let mut cursor = DataflowResultsCursor :: new ( results, body) ;
103
104
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)
105
+ let peek_calls = body
106
+ . basic_blocks ( )
107
+ . iter_enumerated ( )
108
+ . filter_map ( |( bb, block_data) | {
109
+ PeekCall :: from_terminator ( tcx, block_data. terminator ( ) )
110
+ . map ( |call| ( bb, block_data, call) )
111
+ } ) ;
112
+
113
+ for ( bb, block_data, call) in peek_calls {
114
+ // Look for a sequence like the following to indicate that we should be peeking at `_1`:
115
+ // _2 = &_1;
116
+ // rustc_peek(_2);
117
+ //
118
+ // /* or */
119
+ //
120
+ // _2 = _1;
121
+ // rustc_peek(_2);
122
+ let ( statement_index, peek_rval) = block_data
123
+ . statements
124
+ . iter ( )
125
+ . enumerate ( )
126
+ . filter_map ( |( i, stmt) | value_assigned_to_local ( stmt, call. arg ) . map ( |rval| ( i, rval) ) )
127
+ . next ( )
128
+ . expect ( "call to rustc_peek should be preceded by \
129
+ assignment to temporary holding its argument") ;
130
+
131
+ match ( call. kind , peek_rval) {
132
+ | ( PeekCallKind :: ByRef , mir:: Rvalue :: Ref ( _, _, place) )
133
+ | ( PeekCallKind :: ByVal , mir:: Rvalue :: Use ( mir:: Operand :: Move ( place) ) )
134
+ | ( PeekCallKind :: ByVal , mir:: Rvalue :: Use ( mir:: Operand :: Copy ( place) ) )
135
+ => {
136
+ let loc = Location { block : bb, statement_index } ;
137
+ cursor. seek ( loc) ;
138
+ let state = cursor. get ( ) ;
139
+ results. operator ( ) . peek_at ( tcx, place, state, call) ;
155
140
}
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
141
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.
142
+ _ => {
189
143
let msg = "rustc_peek: argument expression \
190
- must be immediate borrow of form `&expr `";
191
- tcx. sess . span_err ( span, msg) ;
144
+ must be either `place` or `&place `";
145
+ tcx. sess . span_err ( call . span , msg) ;
192
146
}
193
147
}
148
+ }
149
+ }
194
150
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) ;
151
+ /// If `stmt` is an assignment where the LHS is the given local (with no projections), returns the
152
+ /// RHS of the assignment.
153
+ fn value_assigned_to_local < ' a , ' tcx > (
154
+ stmt : & ' a mir:: Statement < ' tcx > ,
155
+ local : Local ,
156
+ ) -> Option < & ' a mir:: Rvalue < ' tcx > > {
157
+ if let mir:: StatementKind :: Assign ( box ( place, rvalue) ) = & stmt. kind {
158
+ if let mir:: Place { base : mir:: PlaceBase :: Local ( l) , projection : box [ ] } = place {
159
+ if local == * l {
160
+ return Some ( & * rvalue) ;
161
+ }
162
+ }
208
163
}
209
164
210
- results. 0 . operator . before_terminator_effect (
211
- & mut trans,
212
- Location { block : bb, statement_index : statements. len ( ) } ) ;
165
+ None
166
+ }
213
167
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`" ) ) ;
168
+ # [ derive ( Clone , Copy , Debug ) ]
169
+ enum PeekCallKind {
170
+ ByVal ,
171
+ ByRef ,
218
172
}
219
173
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 ) ) ;
174
+ impl PeekCallKind {
175
+ fn from_arg_ty ( arg : Ty < ' _ > ) -> Self {
176
+ match arg. kind {
177
+ ty:: Ref ( _, _, _) => PeekCallKind :: ByRef ,
178
+ _ => PeekCallKind :: ByVal ,
179
+ }
180
+ }
181
+ }
182
+
183
+ #[ derive( Clone , Copy , Debug ) ]
184
+ pub struct PeekCall {
185
+ arg : Local ,
186
+ kind : PeekCallKind ,
187
+ span : Span ,
188
+ }
189
+
190
+ impl PeekCall {
191
+ fn from_terminator < ' tcx > (
192
+ tcx : TyCtxt < ' tcx > ,
193
+ terminator : & mir:: Terminator < ' tcx > ,
194
+ ) -> Option < Self > {
195
+ use mir:: { Operand , Place , PlaceBase } ;
196
+
197
+ let span = terminator. source_info . span ;
198
+ if let mir:: TerminatorKind :: Call { func : Operand :: Constant ( func) , args, .. } =
199
+ & terminator. kind
200
+ {
201
+ if let ty:: FnDef ( def_id, substs) = func. literal . ty . kind {
202
+ let sig = tcx. fn_sig ( def_id) ;
203
+ let name = tcx. item_name ( def_id) ;
204
+ if sig. abi ( ) != Abi :: RustIntrinsic || name != sym:: rustc_peek {
205
+ return None ;
206
+ }
207
+
208
+ assert_eq ! ( args. len( ) , 1 ) ;
209
+ let kind = PeekCallKind :: from_arg_ty ( substs. type_at ( 0 ) ) ;
210
+ let arg = match args[ 0 ] {
211
+ | Operand :: Copy ( Place { base : PlaceBase :: Local ( local) , projection : box [ ] } )
212
+ | Operand :: Move ( Place { base : PlaceBase :: Local ( local) , projection : box [ ] } )
213
+ => local,
214
+
215
+ _ => {
216
+ tcx. sess . diagnostic ( ) . span_err (
217
+ span, "dataflow::sanity_check cannot feed a non-temp to rustc_peek." ) ;
218
+ return None ;
232
219
}
220
+ } ;
221
+
222
+ return Some ( PeekCall {
223
+ arg,
224
+ kind,
225
+ span,
226
+ } ) ;
227
+ }
228
+ }
229
+
230
+ None
231
+ }
232
+ }
233
+
234
+ pub trait RustcPeekAt < ' tcx > : BitDenotation < ' tcx > {
235
+ fn peek_at (
236
+ & self ,
237
+ tcx : TyCtxt < ' tcx > ,
238
+ place : & mir:: Place < ' tcx > ,
239
+ flow_state : & BitSet < Self :: Idx > ,
240
+ call : PeekCall ,
241
+ ) ;
242
+ }
243
+
244
+ impl < ' tcx , O > RustcPeekAt < ' tcx > for O
245
+ where O : BitDenotation < ' tcx , Idx = MovePathIndex > + HasMoveData < ' tcx > ,
246
+ {
247
+ fn peek_at (
248
+ & self ,
249
+ tcx : TyCtxt < ' tcx > ,
250
+ place : & mir:: Place < ' tcx > ,
251
+ flow_state : & BitSet < Self :: Idx > ,
252
+ call : PeekCall ,
253
+ ) {
254
+ match self . move_data ( ) . rev_lookup . find ( place. as_ref ( ) ) {
255
+ LookupResult :: Exact ( peek_mpi) => {
256
+ let bit_state = flow_state. contains ( peek_mpi) ;
257
+ debug ! ( "rustc_peek({:?} = &{:?}) bit_state: {}" ,
258
+ call. arg, place, bit_state) ;
259
+ if !bit_state {
260
+ tcx. sess . span_err ( call. span , "rustc_peek: bit not set" ) ;
233
261
}
234
262
}
263
+
264
+ LookupResult :: Parent ( ..) => {
265
+ tcx. sess . span_err ( call. span , "rustc_peek: argument untracked" ) ;
266
+ }
267
+ }
268
+ }
269
+ }
270
+
271
+ impl < ' tcx > RustcPeekAt < ' tcx > for IndirectlyMutableLocals < ' _ , ' tcx > {
272
+ fn peek_at (
273
+ & self ,
274
+ tcx : TyCtxt < ' tcx > ,
275
+ place : & mir:: Place < ' tcx > ,
276
+ flow_state : & BitSet < Local > ,
277
+ call : PeekCall ,
278
+ ) {
279
+ warn ! ( "peek_at: place={:?}" , place) ;
280
+ let local = match place {
281
+ mir:: Place { base : mir:: PlaceBase :: Local ( l) , projection : box [ ] } => * l,
282
+ _ => {
283
+ tcx. sess . span_err ( call. span , "rustc_peek: argument was not a local" ) ;
284
+ return ;
285
+ }
286
+ } ;
287
+
288
+ if !flow_state. contains ( local) {
289
+ tcx. sess . span_err ( call. span , "rustc_peek: bit not set" ) ;
235
290
}
236
291
}
237
- return None ;
238
292
}
0 commit comments