@@ -21,11 +21,28 @@ enum Id {
21
21
None ,
22
22
}
23
23
24
- struct NodeData {
24
+ struct NodeStats {
25
25
count : usize ,
26
26
size : usize ,
27
27
}
28
28
29
+ impl NodeStats {
30
+ fn new ( ) -> NodeStats {
31
+ NodeStats { count : 0 , size : 0 }
32
+ }
33
+ }
34
+
35
+ struct Node {
36
+ stats : NodeStats ,
37
+ subnodes : FxHashMap < & ' static str , NodeStats > ,
38
+ }
39
+
40
+ impl Node {
41
+ fn new ( ) -> Node {
42
+ Node { stats : NodeStats :: new ( ) , subnodes : FxHashMap :: default ( ) }
43
+ }
44
+ }
45
+
29
46
/// This type measures the size of AST and HIR nodes, by implementing the AST
30
47
/// and HIR `Visitor` traits. But we don't measure every visited type because
31
48
/// that could cause double counting.
@@ -45,14 +62,14 @@ struct NodeData {
45
62
/// unfortunate.
46
63
struct StatCollector < ' k > {
47
64
krate : Option < Map < ' k > > ,
48
- data : FxHashMap < & ' static str , NodeData > ,
65
+ nodes : FxHashMap < & ' static str , Node > ,
49
66
seen : FxHashSet < Id > ,
50
67
}
51
68
52
69
pub fn print_hir_stats ( tcx : TyCtxt < ' _ > ) {
53
70
let mut collector = StatCollector {
54
71
krate : Some ( tcx. hir ( ) ) ,
55
- data : FxHashMap :: default ( ) ,
72
+ nodes : FxHashMap :: default ( ) ,
56
73
seen : FxHashSet :: default ( ) ,
57
74
} ;
58
75
tcx. hir ( ) . walk_toplevel_module ( & mut collector) ;
@@ -64,47 +81,82 @@ pub fn print_ast_stats(krate: &ast::Crate, title: &str) {
64
81
use rustc_ast:: visit:: Visitor ;
65
82
66
83
let mut collector =
67
- StatCollector { krate : None , data : FxHashMap :: default ( ) , seen : FxHashSet :: default ( ) } ;
84
+ StatCollector { krate : None , nodes : FxHashMap :: default ( ) , seen : FxHashSet :: default ( ) } ;
68
85
collector. visit_crate ( krate) ;
69
86
collector. print ( title) ;
70
87
}
71
88
72
89
impl < ' k > StatCollector < ' k > {
73
- fn record < T > ( & mut self , label : & ' static str , id : Id , node : & T ) {
90
+ // Record a top-level node.
91
+ fn record < T > ( & mut self , label : & ' static str , id : Id , val : & T ) {
92
+ self . record_inner ( label, None , id, val) ;
93
+ }
94
+
95
+ // Record a two-level entry, with a top-level enum type and a variant.
96
+ fn record_variant < T > ( & mut self , label1 : & ' static str , label2 : & ' static str , id : Id , val : & T ) {
97
+ self . record_inner ( label1, Some ( label2) , id, val) ;
98
+ }
99
+
100
+ fn record_inner < T > (
101
+ & mut self ,
102
+ label1 : & ' static str ,
103
+ label2 : Option < & ' static str > ,
104
+ id : Id ,
105
+ val : & T ,
106
+ ) {
74
107
if id != Id :: None && !self . seen . insert ( id) {
75
108
return ;
76
109
}
77
110
78
- let entry = self . data . entry ( label) . or_insert ( NodeData { count : 0 , size : 0 } ) ;
111
+ let node = self . nodes . entry ( label1) . or_insert ( Node :: new ( ) ) ;
112
+ node. stats . count += 1 ;
113
+ node. stats . size = std:: mem:: size_of_val ( val) ;
79
114
80
- entry. count += 1 ;
81
- entry. size = std:: mem:: size_of_val ( node) ;
115
+ if let Some ( label2) = label2 {
116
+ let subnode = node. subnodes . entry ( label2) . or_insert ( NodeStats :: new ( ) ) ;
117
+ subnode. count += 1 ;
118
+ subnode. size = std:: mem:: size_of_val ( val) ;
119
+ }
82
120
}
83
121
84
122
fn print ( & self , title : & str ) {
85
- let mut stats: Vec < _ > = self . data . iter ( ) . collect ( ) ;
86
-
87
- stats. sort_by_key ( |& ( _, ref d) | d. count * d. size ) ;
123
+ let mut nodes: Vec < _ > = self . nodes . iter ( ) . collect ( ) ;
124
+ nodes. sort_by_key ( |& ( _, ref node) | node. stats . count * node. stats . size ) ;
88
125
89
- let total_size = stats . iter ( ) . map ( |( _, data ) | data . count * data . size ) . sum ( ) ;
126
+ let total_size = nodes . iter ( ) . map ( |( _, node ) | node . stats . count * node . stats . size ) . sum ( ) ;
90
127
91
128
eprintln ! ( "\n {}\n " , title) ;
92
129
93
130
eprintln ! ( "{:<18}{:>18}{:>14}{:>14}" , "Name" , "Accumulated Size" , "Count" , "Item Size" ) ;
94
131
eprintln ! ( "----------------------------------------------------------------" ) ;
95
132
96
- let percent = |m, n| { ( m * 100 ) as f64 / n as f64 } ;
133
+ let percent = |m, n| ( m * 100 ) as f64 / n as f64 ;
97
134
98
- for ( label, data ) in stats {
99
- let size = data . count * data . size ;
135
+ for ( label, node ) in nodes {
136
+ let size = node . stats . count * node . stats . size ;
100
137
eprintln ! (
101
138
"{:<18}{:>10} ({:4.1}%){:>14}{:>14}" ,
102
139
label,
103
140
to_readable_str( size) ,
104
141
percent( size, total_size) ,
105
- to_readable_str( data . count) ,
106
- to_readable_str( data . size)
142
+ to_readable_str( node . stats . count) ,
143
+ to_readable_str( node . stats . size)
107
144
) ;
145
+ if !node. subnodes . is_empty ( ) {
146
+ let mut subnodes: Vec < _ > = node. subnodes . iter ( ) . collect ( ) ;
147
+ subnodes. sort_by_key ( |& ( _, ref subnode) | subnode. count * subnode. size ) ;
148
+
149
+ for ( label, subnode) in subnodes {
150
+ let size = subnode. count * subnode. size ;
151
+ eprintln ! (
152
+ "- {:<18}{:>10} ({:4.1}%){:>14}" ,
153
+ label,
154
+ to_readable_str( size) ,
155
+ percent( size, total_size) ,
156
+ to_readable_str( subnode. count) ,
157
+ ) ;
158
+ }
159
+ }
108
160
}
109
161
eprintln ! ( "----------------------------------------------------------------" ) ;
110
162
eprintln ! ( "{:<18}{:>10}\n " , "Total" , to_readable_str( total_size) ) ;
@@ -268,14 +320,54 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
268
320
}
269
321
}
270
322
323
+ // Used to avoid boilerplate for types with many variants.
324
+ macro_rules! record_variants {
325
+ (
326
+ ( $self: ident, $val: expr, $kind: expr, $ty: ty, $tykind: ident) , // mandatory pieces
327
+ [ $( $variant: ident) ,* ]
328
+ ) => {
329
+ match $kind {
330
+ $(
331
+ ast:: $tykind:: $variant { .. } => {
332
+ $self. record_variant( stringify!( $ty) , stringify!( $variant) , Id :: None , $val)
333
+ }
334
+ ) *
335
+ }
336
+ } ;
337
+ }
338
+
271
339
impl < ' v > ast_visit:: Visitor < ' v > for StatCollector < ' v > {
272
340
fn visit_foreign_item ( & mut self , i : & ' v ast:: ForeignItem ) {
273
- self . record ( "ForeignItem" , Id :: None , i) ;
341
+ record_variants ! (
342
+ ( self , i, i. kind, ForeignItem , ForeignItemKind ) ,
343
+ [ Static , Fn , TyAlias , MacCall ]
344
+ ) ;
274
345
ast_visit:: walk_foreign_item ( self , i)
275
346
}
276
347
277
348
fn visit_item ( & mut self , i : & ' v ast:: Item ) {
278
- self . record ( "Item" , Id :: None , i) ;
349
+ record_variants ! (
350
+ ( self , i, i. kind, Item , ItemKind ) ,
351
+ [
352
+ ExternCrate ,
353
+ Use ,
354
+ Static ,
355
+ Const ,
356
+ Fn ,
357
+ Mod ,
358
+ ForeignMod ,
359
+ GlobalAsm ,
360
+ TyAlias ,
361
+ Enum ,
362
+ Struct ,
363
+ Union ,
364
+ Trait ,
365
+ TraitAlias ,
366
+ Impl ,
367
+ MacCall ,
368
+ MacroDef
369
+ ]
370
+ ) ;
279
371
ast_visit:: walk_item ( self , i)
280
372
}
281
373
@@ -290,7 +382,10 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
290
382
}
291
383
292
384
fn visit_stmt ( & mut self , s : & ' v ast:: Stmt ) {
293
- self . record ( "Stmt" , Id :: None , s) ;
385
+ record_variants ! (
386
+ ( self , s, s. kind, Stmt , StmtKind ) ,
387
+ [ Local , Item , Expr , Semi , Empty , MacCall ]
388
+ ) ;
294
389
ast_visit:: walk_stmt ( self , s)
295
390
}
296
391
@@ -305,17 +400,66 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
305
400
}
306
401
307
402
fn visit_pat ( & mut self , p : & ' v ast:: Pat ) {
308
- self . record ( "Pat" , Id :: None , p) ;
403
+ record_variants ! (
404
+ ( self , p, p. kind, Pat , PatKind ) ,
405
+ [
406
+ Wild ,
407
+ Ident ,
408
+ Struct ,
409
+ TupleStruct ,
410
+ Or ,
411
+ Path ,
412
+ Tuple ,
413
+ Box ,
414
+ Ref ,
415
+ Lit ,
416
+ Range ,
417
+ Slice ,
418
+ Rest ,
419
+ Paren ,
420
+ MacCall
421
+ ]
422
+ ) ;
309
423
ast_visit:: walk_pat ( self , p)
310
424
}
311
425
312
- fn visit_expr ( & mut self , ex : & ' v ast:: Expr ) {
313
- self . record ( "Expr" , Id :: None , ex) ;
314
- ast_visit:: walk_expr ( self , ex)
426
+ fn visit_expr ( & mut self , e : & ' v ast:: Expr ) {
427
+ record_variants ! (
428
+ ( self , e, e. kind, Expr , ExprKind ) ,
429
+ [
430
+ Box , Array , ConstBlock , Call , MethodCall , Tup , Binary , Unary , Lit , Cast , Type , Let ,
431
+ If , While , ForLoop , Loop , Match , Closure , Block , Async , Await , TryBlock , Assign ,
432
+ AssignOp , Field , Index , Range , Underscore , Path , AddrOf , Break , Continue , Ret ,
433
+ InlineAsm , MacCall , Struct , Repeat , Paren , Try , Yield , Yeet , Err
434
+ ]
435
+ ) ;
436
+ ast_visit:: walk_expr ( self , e)
315
437
}
316
438
317
439
fn visit_ty ( & mut self , t : & ' v ast:: Ty ) {
318
- self . record ( "Ty" , Id :: None , t) ;
440
+ record_variants ! (
441
+ ( self , t, t. kind, Ty , TyKind ) ,
442
+ [
443
+ Slice ,
444
+ Array ,
445
+ Ptr ,
446
+ Rptr ,
447
+ BareFn ,
448
+ Never ,
449
+ Tup ,
450
+ Path ,
451
+ TraitObject ,
452
+ ImplTrait ,
453
+ Paren ,
454
+ Typeof ,
455
+ Infer ,
456
+ ImplicitSelf ,
457
+ MacCall ,
458
+ Err ,
459
+ CVarArgs
460
+ ]
461
+ ) ;
462
+
319
463
ast_visit:: walk_ty ( self , t)
320
464
}
321
465
@@ -325,7 +469,10 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
325
469
}
326
470
327
471
fn visit_where_predicate ( & mut self , p : & ' v ast:: WherePredicate ) {
328
- self . record ( "WherePredicate" , Id :: None , p) ;
472
+ record_variants ! (
473
+ ( self , p, p, WherePredicate , WherePredicate ) ,
474
+ [ BoundPredicate , RegionPredicate , EqPredicate ]
475
+ ) ;
329
476
ast_visit:: walk_where_predicate ( self , p)
330
477
}
331
478
@@ -334,14 +481,17 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
334
481
ast_visit:: walk_fn ( self , fk, s)
335
482
}
336
483
337
- fn visit_assoc_item ( & mut self , item : & ' v ast:: AssocItem , ctxt : ast_visit:: AssocCtxt ) {
338
- self . record ( "AssocItem" , Id :: None , item) ;
339
- ast_visit:: walk_assoc_item ( self , item, ctxt) ;
484
+ fn visit_assoc_item ( & mut self , i : & ' v ast:: AssocItem , ctxt : ast_visit:: AssocCtxt ) {
485
+ record_variants ! (
486
+ ( self , i, i. kind, AssocItem , AssocItemKind ) ,
487
+ [ Const , Fn , TyAlias , MacCall ]
488
+ ) ;
489
+ ast_visit:: walk_assoc_item ( self , i, ctxt) ;
340
490
}
341
491
342
- fn visit_param_bound ( & mut self , bounds : & ' v ast:: GenericBound , _ctxt : BoundKind ) {
343
- self . record ( " GenericBound" , Id :: None , bounds ) ;
344
- ast_visit:: walk_param_bound ( self , bounds )
492
+ fn visit_param_bound ( & mut self , b : & ' v ast:: GenericBound , _ctxt : BoundKind ) {
493
+ record_variants ! ( ( self , b , b , GenericBound , GenericBound ) , [ Trait , Outlives ] ) ;
494
+ ast_visit:: walk_param_bound ( self , b )
345
495
}
346
496
347
497
fn visit_field_def ( & mut self , s : & ' v ast:: FieldDef ) {
@@ -369,12 +519,12 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
369
519
// common, so we implement `visit_generic_args` and tolerate the double
370
520
// counting in the former case.
371
521
fn visit_generic_args ( & mut self , sp : Span , g : & ' v ast:: GenericArgs ) {
372
- self . record ( "GenericArgs" , Id :: None , g) ;
522
+ record_variants ! ( ( self , g , g, GenericArgs , GenericArgs ) , [ AngleBracketed , Parenthesized ] ) ;
373
523
ast_visit:: walk_generic_args ( self , sp, g)
374
524
}
375
525
376
526
fn visit_attribute ( & mut self , attr : & ' v ast:: Attribute ) {
377
- self . record ( " Attribute" , Id :: None , attr ) ;
527
+ record_variants ! ( ( self , attr , attr . kind , Attribute , AttrKind ) , [ Normal , DocComment ] ) ;
378
528
ast_visit:: walk_attribute ( self , attr)
379
529
}
380
530
0 commit comments