@@ -3,16 +3,17 @@ use syntax::ast;
33use syntax:: symbol:: sym;
44use syntax_pos:: Span ;
55
6- use rustc:: ty:: { self , TyCtxt } ;
6+ use rustc:: ty:: { self , TyCtxt , Ty } ;
77use rustc:: hir:: def_id:: DefId ;
8- use rustc:: mir:: { self , Body , Location } ;
8+ use rustc:: mir:: { self , Body , Location , Local } ;
99use rustc_index:: bit_set:: BitSet ;
1010use crate :: transform:: { MirPass , MirSource } ;
1111
1212use crate :: dataflow:: { do_dataflow, DebugFormatted } ;
1313use crate :: dataflow:: MoveDataParamEnv ;
1414use crate :: dataflow:: BitDenotation ;
1515use crate :: dataflow:: DataflowResults ;
16+ use crate :: dataflow:: DataflowResultsCursor ;
1617use 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