5
5
6
6
use crate :: info;
7
7
use csv:: WriterBuilder ;
8
+ use graph_cycles:: Cycles ;
9
+ use petgraph:: graph:: Graph ;
8
10
use serde:: { ser:: SerializeStruct , Serialize , Serializer } ;
9
11
use stable_mir:: mir:: mono:: Instance ;
10
12
use stable_mir:: mir:: visit:: { Location , PlaceContext , PlaceRef } ;
11
13
use stable_mir:: mir:: {
12
- Body , MirVisitor , Mutability , ProjectionElem , Safety , Terminator , TerminatorKind ,
14
+ BasicBlock , Body , MirVisitor , Mutability , ProjectionElem , Safety , Terminator , TerminatorKind ,
13
15
} ;
14
16
use stable_mir:: ty:: { AdtDef , AdtKind , FnDef , GenericArgs , MirConst , RigidTy , Ty , TyKind } ;
15
17
use stable_mir:: visitor:: { Visitable , Visitor } ;
@@ -159,6 +161,7 @@ impl OverallStats {
159
161
pub fn loops ( & mut self , filename : PathBuf ) {
160
162
let all_items = stable_mir:: all_local_items ( ) ;
161
163
let ( has_loops, no_loops) = all_items
164
+ . clone ( )
162
165
. into_iter ( )
163
166
. filter_map ( |item| {
164
167
let kind = item. ty ( ) . kind ( ) ;
@@ -168,9 +171,37 @@ impl OverallStats {
168
171
Some ( FnLoops :: new ( item. name ( ) ) . collect ( & item. body ( ) ) )
169
172
} )
170
173
. partition :: < Vec < _ > , _ > ( |props| props. has_loops ( ) ) ;
171
- self . counters
172
- . extend_from_slice ( & [ ( "has_loops" , has_loops. len ( ) ) , ( "no_loops" , no_loops. len ( ) ) ] ) ;
173
- dump_csv ( filename, & has_loops) ;
174
+
175
+ let ( has_iterators, no_iterators) = all_items
176
+ . clone ( )
177
+ . into_iter ( )
178
+ . filter_map ( |item| {
179
+ let kind = item. ty ( ) . kind ( ) ;
180
+ if !kind. is_fn ( ) {
181
+ return None ;
182
+ } ;
183
+ Some ( FnLoops :: new ( item. name ( ) ) . collect ( & item. body ( ) ) )
184
+ } )
185
+ . partition :: < Vec < _ > , _ > ( |props| props. has_iterators ( ) ) ;
186
+
187
+ let ( has_either, _) = all_items
188
+ . into_iter ( )
189
+ . filter_map ( |item| {
190
+ let kind = item. ty ( ) . kind ( ) ;
191
+ if !kind. is_fn ( ) {
192
+ return None ;
193
+ } ;
194
+ Some ( FnLoops :: new ( item. name ( ) ) . collect ( & item. body ( ) ) )
195
+ } )
196
+ . partition :: < Vec < _ > , _ > ( |props| props. has_iterators ( ) || props. has_loops ( ) ) ;
197
+
198
+ self . counters . extend_from_slice ( & [
199
+ ( "has_loops" , has_loops. len ( ) ) ,
200
+ ( "no_loops" , no_loops. len ( ) ) ,
201
+ ( "has_iterators" , has_iterators. len ( ) ) ,
202
+ ( "no_iterators" , no_iterators. len ( ) ) ,
203
+ ] ) ;
204
+ dump_csv ( filename, & has_either) ;
174
205
}
175
206
176
207
/// Create a callgraph for this crate and try to find recursive calls.
@@ -436,21 +467,26 @@ impl<'a> MirVisitor for BodyVisitor<'a> {
436
467
fn_props ! {
437
468
struct FnLoops {
438
469
iterators,
439
- nested_loops,
440
- /// TODO: Collect loops.
441
470
loops,
471
+ // TODO: Collect nested loops.
472
+ nested_loops,
442
473
}
443
474
}
444
475
445
476
impl FnLoops {
446
477
pub fn collect ( self , body : & Body ) -> FnLoops {
447
- let mut visitor = IteratorVisitor { props : self , body } ;
478
+ let mut visitor =
479
+ IteratorVisitor { props : self , body, graph : Vec :: new ( ) , current_bbidx : 0 } ;
448
480
visitor. visit_body ( body) ;
449
481
visitor. props
450
482
}
451
483
452
484
pub fn has_loops ( & self ) -> bool {
453
- ( self . iterators + self . loops + self . nested_loops ) > 0
485
+ ( self . loops + self . nested_loops ) > 0
486
+ }
487
+
488
+ pub fn has_iterators ( & self ) -> bool {
489
+ ( self . iterators ) > 0
454
490
}
455
491
}
456
492
@@ -461,12 +497,36 @@ impl FnLoops {
461
497
struct IteratorVisitor < ' a > {
462
498
props : FnLoops ,
463
499
body : & ' a Body ,
500
+ graph : Vec < ( u32 , u32 ) > ,
501
+ current_bbidx : u32 ,
464
502
}
465
503
466
504
impl < ' a > MirVisitor for IteratorVisitor < ' a > {
505
+ fn visit_body ( & mut self , body : & Body ) {
506
+ // First visit the body to build the control flow graph
507
+ self . super_body ( body) ;
508
+ // Build the petgraph from the adj vec
509
+ let g = Graph :: < ( ) , ( ) > :: from_edges ( self . graph . clone ( ) ) ;
510
+ self . props . loops += g. cycles ( ) . len ( ) ;
511
+ }
512
+
513
+ fn visit_basic_block ( & mut self , bb : & BasicBlock ) {
514
+ self . current_bbidx = self . body . blocks . iter ( ) . position ( |b| * b == * bb) . unwrap ( ) as u32 ;
515
+ self . super_basic_block ( bb) ;
516
+ }
517
+
467
518
fn visit_terminator ( & mut self , term : & Terminator , location : Location ) {
519
+ // Add edges between basic block into the adj table
520
+ let successors = term. kind . successors ( ) ;
521
+ for target in successors {
522
+ self . graph . push ( ( self . current_bbidx , target as u32 ) ) ;
523
+ }
524
+
468
525
if let TerminatorKind :: Call { func, .. } = & term. kind {
469
526
let kind = func. ty ( self . body . locals ( ) ) . unwrap ( ) . kind ( ) ;
527
+ // Check if the target is a visited block.
528
+
529
+ // Check if the call is an iterator function that contains loops.
470
530
if let TyKind :: RigidTy ( RigidTy :: FnDef ( def, _) ) = kind {
471
531
let fullname = def. name ( ) ;
472
532
let names = fullname. split ( "::" ) . collect :: < Vec < _ > > ( ) ;
@@ -505,6 +565,7 @@ impl<'a> MirVisitor for IteratorVisitor<'a> {
505
565
}
506
566
}
507
567
}
568
+
508
569
self . super_terminator ( term, location)
509
570
}
510
571
}
0 commit comments