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
@@ -357,7 +372,9 @@ where
357372{
358373 analysis : A ,
359374 bits_per_block : usize ,
375+ tcx : TyCtxt < ' tcx > ,
360376 body : & ' a mir:: Body < ' tcx > ,
377+ def_id : DefId ,
361378 dead_unwinds : & ' a BitSet < BasicBlock > ,
362379 entry_sets : IndexVec < BasicBlock , BitSet < A :: Idx > > ,
363380}
@@ -367,7 +384,9 @@ where
367384 A : Analysis < ' tcx > ,
368385{
369386 pub fn new (
387+ tcx : TyCtxt < ' tcx > ,
370388 body : & ' a mir:: Body < ' tcx > ,
389+ def_id : DefId ,
371390 dead_unwinds : & ' a BitSet < BasicBlock > ,
372391 analysis : A ,
373392 ) -> Self {
@@ -385,7 +404,9 @@ where
385404 Engine {
386405 analysis,
387406 bits_per_block,
407+ tcx,
388408 body,
409+ def_id,
389410 dead_unwinds,
390411 entry_sets,
391412 }
@@ -421,10 +442,26 @@ where
421442 ) ;
422443 }
423444
424- Results {
425- analysis : self . analysis ,
426- 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+ }
427462 }
463+
464+ results
428465 }
429466
430467 fn propagate_bits_into_graph_successors_of (
@@ -518,3 +555,59 @@ where
518555 }
519556 }
520557}
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