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
@@ -77,7 +92,7 @@ pub trait Analysis<'tcx>: BottomValue {
77
92
location : Location ,
78
93
) ;
79
94
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 .
81
96
///
82
97
/// Note that the effect of a successful return from a `Call` terminator should **not** be
83
98
/// acounted for in this function. That should go in `apply_call_return_effect`. For example,
@@ -180,17 +195,20 @@ impl CursorPosition {
180
195
}
181
196
}
182
197
198
+ type ResultsRefCursor < ' a , ' mir , ' tcx , A > =
199
+ ResultsCursor < ' mir , ' tcx , A , & ' a Results < ' tcx , A > > ;
200
+
183
201
/// Inspect the results of dataflow analysis.
184
202
///
185
203
/// This cursor has linear performance when visiting statements in a block in order. Visiting
186
204
/// statements within a block in reverse order is `O(n^2)`, where `n` is the number of statements
187
205
/// in that block.
188
- pub struct ResultsCursor < ' mir , ' tcx , A >
206
+ pub struct ResultsCursor < ' mir , ' tcx , A , R = Results < ' tcx , A > >
189
207
where
190
208
A : Analysis < ' tcx > ,
191
209
{
192
210
body : & ' mir mir:: Body < ' tcx > ,
193
- results : Results < ' tcx , A > ,
211
+ results : R ,
194
212
state : BitSet < A :: Idx > ,
195
213
196
214
pos : CursorPosition ,
@@ -202,24 +220,29 @@ where
202
220
is_call_return_effect_applied : bool ,
203
221
}
204
222
205
- impl < ' mir , ' tcx , A > ResultsCursor < ' mir , ' tcx , A >
223
+ impl < ' mir , ' tcx , A , R > ResultsCursor < ' mir , ' tcx , A , R >
206
224
where
207
225
A : Analysis < ' tcx > ,
226
+ R : Borrow < Results < ' tcx , A > > ,
208
227
{
209
228
/// 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 {
211
230
ResultsCursor {
212
231
body,
213
232
pos : CursorPosition :: AtBlockStart ( mir:: START_BLOCK ) ,
214
233
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 ( ) ,
216
235
results,
217
236
}
218
237
}
219
238
239
+ pub fn analysis ( & self ) -> & A {
240
+ & self . results . borrow ( ) . analysis
241
+ }
242
+
220
243
/// Resets the cursor to the start of the given `block`.
221
244
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] ) ;
223
246
self . pos = CursorPosition :: AtBlockStart ( block) ;
224
247
self . is_call_return_effect_applied = false ;
225
248
}
@@ -275,7 +298,7 @@ where
275
298
} = & term. kind {
276
299
if !self . is_call_return_effect_applied {
277
300
self . is_call_return_effect_applied = true ;
278
- self . results . analysis . apply_call_return_effect (
301
+ self . results . borrow ( ) . analysis . apply_call_return_effect (
279
302
& mut self . state ,
280
303
target. block ,
281
304
func,
@@ -316,7 +339,7 @@ where
316
339
} ;
317
340
318
341
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 (
320
343
& mut self . state ,
321
344
target_block,
322
345
block_data,
@@ -349,7 +372,9 @@ where
349
372
{
350
373
analysis : A ,
351
374
bits_per_block : usize ,
375
+ tcx : TyCtxt < ' tcx > ,
352
376
body : & ' a mir:: Body < ' tcx > ,
377
+ def_id : DefId ,
353
378
dead_unwinds : & ' a BitSet < BasicBlock > ,
354
379
entry_sets : IndexVec < BasicBlock , BitSet < A :: Idx > > ,
355
380
}
@@ -359,7 +384,9 @@ where
359
384
A : Analysis < ' tcx > ,
360
385
{
361
386
pub fn new (
387
+ tcx : TyCtxt < ' tcx > ,
362
388
body : & ' a mir:: Body < ' tcx > ,
389
+ def_id : DefId ,
363
390
dead_unwinds : & ' a BitSet < BasicBlock > ,
364
391
analysis : A ,
365
392
) -> Self {
@@ -377,7 +404,9 @@ where
377
404
Engine {
378
405
analysis,
379
406
bits_per_block,
407
+ tcx,
380
408
body,
409
+ def_id,
381
410
dead_unwinds,
382
411
entry_sets,
383
412
}
@@ -413,10 +442,26 @@ where
413
442
) ;
414
443
}
415
444
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
+ }
419
462
}
463
+
464
+ results
420
465
}
421
466
422
467
fn propagate_bits_into_graph_successors_of (
@@ -510,3 +555,59 @@ where
510
555
}
511
556
}
512
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