16
16
//! [gk]: https://en.wikipedia.org/wiki/Data-flow_analysis#Bit_vector_problems
17
17
//! [#64566]: https://github.com/rust-lang/rust/pull/64566
18
18
19
+ use std:: borrow:: Borrow ;
19
20
use std:: cmp:: Ordering ;
20
- use std:: ops;
21
+ use std:: ffi:: OsString ;
22
+ use std:: path:: { Path , PathBuf } ;
23
+ use std:: { fs, io, ops} ;
21
24
25
+ use rustc:: hir:: def_id:: DefId ;
22
26
use rustc:: mir:: { self , traversal, BasicBlock , Location } ;
27
+ use rustc:: ty:: { self , TyCtxt } ;
28
+ use rustc_data_structures:: work_queue:: WorkQueue ;
23
29
use rustc_index:: bit_set:: BitSet ;
24
30
use rustc_index:: vec:: { Idx , IndexVec } ;
25
- use rustc_data_structures :: work_queue :: WorkQueue ;
31
+ use syntax :: symbol :: sym ;
26
32
27
33
use crate :: dataflow:: BottomValue ;
28
34
35
+ mod graphviz;
36
+
29
37
/// A specific kind of dataflow analysis.
30
38
///
31
39
/// 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 {
62
70
/// and try to keep it short.
63
71
const NAME : & ' static str ;
64
72
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
+
65
80
/// The size of each bitvector allocated for each block.
66
81
fn bits_per_block ( & self , body : & mir:: Body < ' tcx > ) -> usize ;
67
82
@@ -357,7 +372,9 @@ where
357
372
{
358
373
analysis : A ,
359
374
bits_per_block : usize ,
375
+ tcx : TyCtxt < ' tcx > ,
360
376
body : & ' a mir:: Body < ' tcx > ,
377
+ def_id : DefId ,
361
378
dead_unwinds : & ' a BitSet < BasicBlock > ,
362
379
entry_sets : IndexVec < BasicBlock , BitSet < A :: Idx > > ,
363
380
}
@@ -367,7 +384,9 @@ where
367
384
A : Analysis < ' tcx > ,
368
385
{
369
386
pub fn new (
387
+ tcx : TyCtxt < ' tcx > ,
370
388
body : & ' a mir:: Body < ' tcx > ,
389
+ def_id : DefId ,
371
390
dead_unwinds : & ' a BitSet < BasicBlock > ,
372
391
analysis : A ,
373
392
) -> Self {
@@ -385,7 +404,9 @@ where
385
404
Engine {
386
405
analysis,
387
406
bits_per_block,
407
+ tcx,
388
408
body,
409
+ def_id,
389
410
dead_unwinds,
390
411
entry_sets,
391
412
}
@@ -421,10 +442,26 @@ where
421
442
) ;
422
443
}
423
444
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
+ }
427
462
}
463
+
464
+ results
428
465
}
429
466
430
467
fn propagate_bits_into_graph_successors_of (
@@ -518,3 +555,59 @@ where
518
555
}
519
556
}
520
557
}
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