@@ -21,11 +21,28 @@ enum Id {
2121 None ,
2222}
2323
24- struct NodeData {
24+ struct NodeStats {
2525 count : usize ,
2626 size : usize ,
2727}
2828
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+
2946/// This type measures the size of AST and HIR nodes, by implementing the AST
3047/// and HIR `Visitor` traits. But we don't measure every visited type because
3148/// that could cause double counting.
@@ -45,14 +62,14 @@ struct NodeData {
4562/// unfortunate.
4663struct StatCollector < ' k > {
4764 krate : Option < Map < ' k > > ,
48- data : FxHashMap < & ' static str , NodeData > ,
65+ nodes : FxHashMap < & ' static str , Node > ,
4966 seen : FxHashSet < Id > ,
5067}
5168
5269pub fn print_hir_stats ( tcx : TyCtxt < ' _ > ) {
5370 let mut collector = StatCollector {
5471 krate : Some ( tcx. hir ( ) ) ,
55- data : FxHashMap :: default ( ) ,
72+ nodes : FxHashMap :: default ( ) ,
5673 seen : FxHashSet :: default ( ) ,
5774 } ;
5875 tcx. hir ( ) . walk_toplevel_module ( & mut collector) ;
@@ -64,47 +81,82 @@ pub fn print_ast_stats(krate: &ast::Crate, title: &str) {
6481 use rustc_ast:: visit:: Visitor ;
6582
6683 let mut collector =
67- StatCollector { krate : None , data : FxHashMap :: default ( ) , seen : FxHashSet :: default ( ) } ;
84+ StatCollector { krate : None , nodes : FxHashMap :: default ( ) , seen : FxHashSet :: default ( ) } ;
6885 collector. visit_crate ( krate) ;
6986 collector. print ( title) ;
7087}
7188
7289impl < ' 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+ ) {
74107 if id != Id :: None && !self . seen . insert ( id) {
75108 return ;
76109 }
77110
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) ;
79114
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+ }
82120 }
83121
84122 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 ) ;
88125
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 ( ) ;
90127
91128 eprintln ! ( "\n {}\n " , title) ;
92129
93130 eprintln ! ( "{:<18}{:>18}{:>14}{:>14}" , "Name" , "Accumulated Size" , "Count" , "Item Size" ) ;
94131 eprintln ! ( "----------------------------------------------------------------" ) ;
95132
96- let percent = |m, n| { ( m * 100 ) as f64 / n as f64 } ;
133+ let percent = |m, n| ( m * 100 ) as f64 / n as f64 ;
97134
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 ;
100137 eprintln ! (
101138 "{:<18}{:>10} ({:4.1}%){:>14}{:>14}" ,
102139 label,
103140 to_readable_str( size) ,
104141 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)
107144 ) ;
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+ }
108160 }
109161 eprintln ! ( "----------------------------------------------------------------" ) ;
110162 eprintln ! ( "{:<18}{:>10}\n " , "Total" , to_readable_str( total_size) ) ;
@@ -268,14 +320,54 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
268320 }
269321}
270322
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+
271339impl < ' v > ast_visit:: Visitor < ' v > for StatCollector < ' v > {
272340 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+ ) ;
274345 ast_visit:: walk_foreign_item ( self , i)
275346 }
276347
277348 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+ ) ;
279371 ast_visit:: walk_item ( self , i)
280372 }
281373
@@ -290,7 +382,10 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
290382 }
291383
292384 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+ ) ;
294389 ast_visit:: walk_stmt ( self , s)
295390 }
296391
@@ -305,17 +400,66 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
305400 }
306401
307402 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+ ) ;
309423 ast_visit:: walk_pat ( self , p)
310424 }
311425
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)
315437 }
316438
317439 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+
319463 ast_visit:: walk_ty ( self , t)
320464 }
321465
@@ -325,7 +469,10 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
325469 }
326470
327471 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+ ) ;
329476 ast_visit:: walk_where_predicate ( self , p)
330477 }
331478
@@ -334,14 +481,17 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
334481 ast_visit:: walk_fn ( self , fk, s)
335482 }
336483
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) ;
340490 }
341491
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 )
345495 }
346496
347497 fn visit_field_def ( & mut self , s : & ' v ast:: FieldDef ) {
@@ -369,12 +519,12 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
369519 // common, so we implement `visit_generic_args` and tolerate the double
370520 // counting in the former case.
371521 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 ] ) ;
373523 ast_visit:: walk_generic_args ( self , sp, g)
374524 }
375525
376526 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 ] ) ;
378528 ast_visit:: walk_attribute ( self , attr)
379529 }
380530
0 commit comments