1616//! [gk]: https://en.wikipedia.org/wiki/Data-flow_analysis#Bit_vector_problems
1717//! [#64566]: https://github.com/rust-lang/rust/pull/64566
1818
19+ use std:: borrow:: Borrow ;
1920use std:: cmp:: Ordering ;
20- use std:: ops;
21+ use std:: ffi:: OsString ;
22+ use std:: path:: { Path , PathBuf } ;
23+ use std:: { fs, io, ops} ;
2124
25+ use rustc:: hir:: def_id:: DefId ;
2226use rustc:: mir:: { self , traversal, BasicBlock , Location } ;
27+ use rustc:: ty:: { self , TyCtxt } ;
28+ use rustc_data_structures:: work_queue:: WorkQueue ;
2329use rustc_index:: bit_set:: BitSet ;
2430use rustc_index:: vec:: { Idx , IndexVec } ;
25- use rustc_data_structures :: work_queue :: WorkQueue ;
31+ use syntax :: symbol :: sym ;
2632
2733use crate :: dataflow:: BottomValue ;
2834
35+ mod graphviz;
36+
2937/// A specific kind of dataflow analysis.
3038///
3139/// To run a dataflow analysis, one must set the initial state of the `START_BLOCK` via
@@ -62,6 +70,13 @@ pub trait Analysis<'tcx>: BottomValue {
6270 /// and try to keep it short.
6371 const NAME : & ' static str ;
6472
73+ /// How each element of your dataflow state will be displayed during debugging.
74+ ///
75+ /// By default, this is the `fmt::Debug` representation of `Self::Idx`.
76+ fn pretty_print_idx ( & self , w : & mut impl io:: Write , idx : Self :: Idx ) -> io:: Result < ( ) > {
77+ write ! ( w, "{:?}" , idx)
78+ }
79+
6580 /// The size of each bitvector allocated for each block.
6681 fn bits_per_block ( & self , body : & mir:: Body < ' tcx > ) -> usize ;
6782
@@ -77,7 +92,7 @@ pub trait Analysis<'tcx>: BottomValue {
7792 location : Location ,
7893 ) ;
7994
80- /// Updates the current dataflow state with the effect of evaluating a statement .
95+ /// Updates the current dataflow state with the effect of evaluating a terminator .
8196 ///
8297 /// Note that the effect of a successful return from a `Call` terminator should **not** be
8398 /// acounted for in this function. That should go in `apply_call_return_effect`. For example,
@@ -180,17 +195,20 @@ impl CursorPosition {
180195 }
181196}
182197
198+ type ResultsRefCursor < ' a , ' mir , ' tcx , A > =
199+ ResultsCursor < ' mir , ' tcx , A , & ' a Results < ' tcx , A > > ;
200+
183201/// Inspect the results of dataflow analysis.
184202///
185203/// This cursor has linear performance when visiting statements in a block in order. Visiting
186204/// statements within a block in reverse order is `O(n^2)`, where `n` is the number of statements
187205/// in that block.
188- pub struct ResultsCursor < ' mir , ' tcx , A >
206+ pub struct ResultsCursor < ' mir , ' tcx , A , R = Results < ' tcx , A > >
189207where
190208 A : Analysis < ' tcx > ,
191209{
192210 body : & ' mir mir:: Body < ' tcx > ,
193- results : Results < ' tcx , A > ,
211+ results : R ,
194212 state : BitSet < A :: Idx > ,
195213
196214 pos : CursorPosition ,
@@ -202,24 +220,29 @@ where
202220 is_call_return_effect_applied : bool ,
203221}
204222
205- impl < ' mir , ' tcx , A > ResultsCursor < ' mir , ' tcx , A >
223+ impl < ' mir , ' tcx , A , R > ResultsCursor < ' mir , ' tcx , A , R >
206224where
207225 A : Analysis < ' tcx > ,
226+ R : Borrow < Results < ' tcx , A > > ,
208227{
209228 /// Returns a new cursor for `results` that points to the start of the `START_BLOCK`.
210- pub fn new ( body : & ' mir mir:: Body < ' tcx > , results : Results < ' tcx , A > ) -> Self {
229+ pub fn new ( body : & ' mir mir:: Body < ' tcx > , results : R ) -> Self {
211230 ResultsCursor {
212231 body,
213232 pos : CursorPosition :: AtBlockStart ( mir:: START_BLOCK ) ,
214233 is_call_return_effect_applied : false ,
215- state : results. entry_sets [ mir:: START_BLOCK ] . clone ( ) ,
234+ state : results. borrow ( ) . entry_sets [ mir:: START_BLOCK ] . clone ( ) ,
216235 results,
217236 }
218237 }
219238
239+ pub fn analysis ( & self ) -> & A {
240+ & self . results . borrow ( ) . analysis
241+ }
242+
220243 /// Resets the cursor to the start of the given `block`.
221244 pub fn seek_to_block_start ( & mut self , block : BasicBlock ) {
222- self . state . overwrite ( & self . results . entry_sets [ block] ) ;
245+ self . state . overwrite ( & self . results . borrow ( ) . entry_sets [ block] ) ;
223246 self . pos = CursorPosition :: AtBlockStart ( block) ;
224247 self . is_call_return_effect_applied = false ;
225248 }
@@ -275,7 +298,7 @@ where
275298 } = & term. kind {
276299 if !self . is_call_return_effect_applied {
277300 self . is_call_return_effect_applied = true ;
278- self . results . analysis . apply_call_return_effect (
301+ self . results . borrow ( ) . analysis . apply_call_return_effect (
279302 & mut self . state ,
280303 target. block ,
281304 func,
@@ -316,7 +339,7 @@ where
316339 } ;
317340
318341 let block_data = & self . body . basic_blocks ( ) [ target_block] ;
319- self . results . analysis . apply_partial_block_effect (
342+ self . results . borrow ( ) . analysis . apply_partial_block_effect (
320343 & mut self . state ,
321344 target_block,
322345 block_data,
@@ -349,7 +372,9 @@ where
349372{
350373 analysis : A ,
351374 bits_per_block : usize ,
375+ tcx : TyCtxt < ' tcx > ,
352376 body : & ' a mir:: Body < ' tcx > ,
377+ def_id : DefId ,
353378 dead_unwinds : & ' a BitSet < BasicBlock > ,
354379 entry_sets : IndexVec < BasicBlock , BitSet < A :: Idx > > ,
355380}
@@ -359,7 +384,9 @@ where
359384 A : Analysis < ' tcx > ,
360385{
361386 pub fn new (
387+ tcx : TyCtxt < ' tcx > ,
362388 body : & ' a mir:: Body < ' tcx > ,
389+ def_id : DefId ,
363390 dead_unwinds : & ' a BitSet < BasicBlock > ,
364391 analysis : A ,
365392 ) -> Self {
@@ -377,7 +404,9 @@ where
377404 Engine {
378405 analysis,
379406 bits_per_block,
407+ tcx,
380408 body,
409+ def_id,
381410 dead_unwinds,
382411 entry_sets,
383412 }
@@ -413,10 +442,26 @@ where
413442 ) ;
414443 }
415444
416- Results {
417- analysis : self . analysis ,
418- entry_sets : self . entry_sets ,
445+ let Engine {
446+ tcx,
447+ body,
448+ def_id,
449+ analysis,
450+ entry_sets,
451+ ..
452+ } = self ;
453+
454+ let results = Results { analysis, entry_sets } ;
455+
456+ let attrs = tcx. get_attrs ( def_id) ;
457+ if let Some ( path) = get_dataflow_graphviz_output_path ( tcx, attrs, A :: NAME ) {
458+ let result = write_dataflow_graphviz_results ( body, def_id, & path, & results) ;
459+ if let Err ( e) = result {
460+ warn ! ( "Failed to write dataflow results to {}: {}" , path. display( ) , e) ;
461+ }
419462 }
463+
464+ results
420465 }
421466
422467 fn propagate_bits_into_graph_successors_of (
@@ -510,3 +555,59 @@ where
510555 }
511556 }
512557}
558+
559+ /// Looks for attributes like `#[rustc_mir(borrowck_graphviz_postflow="./path/to/suffix.dot")]` and
560+ /// extracts the path with the given analysis name prepended to the suffix.
561+ ///
562+ /// Returns `None` if no such attribute exists.
563+ fn get_dataflow_graphviz_output_path (
564+ tcx : TyCtxt < ' tcx > ,
565+ attrs : ty:: Attributes < ' tcx > ,
566+ analysis : & str ,
567+ ) -> Option < PathBuf > {
568+ let mut rustc_mir_attrs = attrs
569+ . into_iter ( )
570+ . filter ( |attr| attr. check_name ( sym:: rustc_mir) )
571+ . flat_map ( |attr| attr. meta_item_list ( ) . into_iter ( ) . flat_map ( |v| v. into_iter ( ) ) ) ;
572+
573+ let borrowck_graphviz_postflow = rustc_mir_attrs
574+ . find ( |attr| attr. check_name ( sym:: borrowck_graphviz_postflow) ) ?;
575+
576+ let path_and_suffix = match borrowck_graphviz_postflow. value_str ( ) {
577+ Some ( p) => p,
578+ None => {
579+ tcx. sess . span_err (
580+ borrowck_graphviz_postflow. span ( ) ,
581+ "borrowck_graphviz_postflow requires a path" ,
582+ ) ;
583+
584+ return None ;
585+ }
586+ } ;
587+
588+ // Change "path/suffix.dot" to "path/analysis_name_suffix.dot"
589+ let mut ret = PathBuf :: from ( path_and_suffix. to_string ( ) ) ;
590+ let suffix = ret. file_name ( ) . unwrap ( ) ;
591+
592+ let mut file_name: OsString = analysis. into ( ) ;
593+ file_name. push ( "_" ) ;
594+ file_name. push ( suffix) ;
595+ ret. set_file_name ( file_name) ;
596+
597+ Some ( ret)
598+ }
599+
600+ fn write_dataflow_graphviz_results < A : Analysis < ' tcx > > (
601+ body : & mir:: Body < ' tcx > ,
602+ def_id : DefId ,
603+ path : & Path ,
604+ results : & Results < ' tcx , A >
605+ ) -> io:: Result < ( ) > {
606+ debug ! ( "printing dataflow results for {:?} to {}" , def_id, path. display( ) ) ;
607+
608+ let mut buf = Vec :: new ( ) ;
609+ let graphviz = graphviz:: Formatter :: new ( body, def_id, results) ;
610+
611+ dot:: render ( & graphviz, & mut buf) ?;
612+ fs:: write ( path, buf)
613+ }
0 commit comments