1 // The visitors in this module collect sizes and counts of the most important
2 // pieces of AST and HIR. The resulting numbers are good approximations but not
3 // completely accurate (some things might be counted twice, others missed).
5 use rustc_ast
::visit
as ast_visit
;
6 use rustc_ast
::visit
::BoundKind
;
7 use rustc_ast
::{self as ast, AttrId, NodeId}
;
8 use rustc_data_structures
::fx
::{FxHashMap, FxHashSet}
;
10 use rustc_hir
::intravisit
as hir_visit
;
12 use rustc_middle
::hir
::map
::Map
;
13 use rustc_middle
::ty
::TyCtxt
;
14 use rustc_middle
::util
::common
::to_readable_str
;
17 #[derive(Copy, Clone, PartialEq, Eq, Hash)]
30 fn new() -> NodeStats
{
31 NodeStats { count: 0, size: 0 }
37 subnodes
: FxHashMap
<&'
static str, NodeStats
>,
42 Node { stats: NodeStats::new(), subnodes: FxHashMap::default() }
46 /// This type measures the size of AST and HIR nodes, by implementing the AST
47 /// and HIR `Visitor` traits. But we don't measure every visited type because
48 /// that could cause double counting.
50 /// For example, `ast::Visitor` has `visit_ident`, but `Ident`s are always
51 /// stored inline within other AST nodes, so we don't implement `visit_ident`
52 /// here. In contrast, we do implement `visit_expr` because `ast::Expr` is
53 /// always stored as `P<ast::Expr>`, and every such expression should be
54 /// measured separately.
56 /// In general, a `visit_foo` method should be implemented here if the
57 /// corresponding `Foo` type is always stored on its own, e.g.: `P<Foo>`,
58 /// `Box<Foo>`, `Vec<Foo>`, `Box<[Foo]>`.
60 /// There are some types in the AST and HIR tree that the visitors do not have
61 /// a `visit_*` method for, and so we cannot measure these, which is
63 struct StatCollector
<'k
> {
64 krate
: Option
<Map
<'k
>>,
65 nodes
: FxHashMap
<&'
static str, Node
>,
69 pub fn print_hir_stats(tcx
: TyCtxt
<'_
>) {
70 let mut collector
= StatCollector
{
71 krate
: Some(tcx
.hir()),
72 nodes
: FxHashMap
::default(),
73 seen
: FxHashSet
::default(),
75 tcx
.hir().walk_toplevel_module(&mut collector
);
76 tcx
.hir().walk_attributes(&mut collector
);
77 collector
.print("HIR STATS", "hir-stats");
80 pub fn print_ast_stats(krate
: &ast
::Crate
, title
: &str, prefix
: &str) {
81 use rustc_ast
::visit
::Visitor
;
84 StatCollector { krate: None, nodes: FxHashMap::default(), seen: FxHashSet::default() }
;
85 collector
.visit_crate(krate
);
86 collector
.print(title
, prefix
);
89 impl<'k
> StatCollector
<'k
> {
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
);
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
);
102 label1
: &'
static str,
103 label2
: Option
<&'
static str>,
107 if id
!= Id
::None
&& !self.seen
.insert(id
) {
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
);
115 if let Some(label2
) = label2
{
116 let subnode
= node
.subnodes
.entry(label2
).or_insert(NodeStats
::new());
118 subnode
.size
= std
::mem
::size_of_val(val
);
122 fn print(&self, title
: &str, prefix
: &str) {
123 let mut nodes
: Vec
<_
> = self.nodes
.iter().collect();
124 nodes
.sort_by_key(|&(_
, ref node
)| node
.stats
.count
* node
.stats
.size
);
126 let total_size
= nodes
.iter().map(|(_
, node
)| node
.stats
.count
* node
.stats
.size
).sum();
128 eprintln
!("{} {}", prefix
, title
);
130 "{} {:<18}{:>18}{:>14}{:>14}",
131 prefix
, "Name", "Accumulated Size", "Count", "Item Size"
133 eprintln
!("{} ----------------------------------------------------------------", prefix
);
135 let percent
= |m
, n
| (m
* 100) as f64 / n
as f64;
137 for (label
, node
) in nodes
{
138 let size
= node
.stats
.count
* node
.stats
.size
;
140 "{} {:<18}{:>10} ({:4.1}%){:>14}{:>14}",
143 to_readable_str(size
),
144 percent(size
, total_size
),
145 to_readable_str(node
.stats
.count
),
146 to_readable_str(node
.stats
.size
)
148 if !node
.subnodes
.is_empty() {
149 let mut subnodes
: Vec
<_
> = node
.subnodes
.iter().collect();
150 subnodes
.sort_by_key(|&(_
, ref subnode
)| subnode
.count
* subnode
.size
);
152 for (label
, subnode
) in subnodes
{
153 let size
= subnode
.count
* subnode
.size
;
155 "{} - {:<18}{:>10} ({:4.1}%){:>14}",
158 to_readable_str(size
),
159 percent(size
, total_size
),
160 to_readable_str(subnode
.count
),
165 eprintln
!("{} ----------------------------------------------------------------", prefix
);
166 eprintln
!("{} {:<18}{:>10}", prefix
, "Total", to_readable_str(total_size
));
167 eprintln
!("{}", prefix
);
171 // Used to avoid boilerplate for types with many variants.
172 macro_rules
! record_variants
{
174 ($
self:ident
, $val
:expr
, $kind
:expr
, $id
:expr
, $
mod:ident
, $ty
:ty
, $tykind
:ident
),
175 [$
($variant
:ident
),*]
179 $
mod::$tykind
::$variant { .. }
=> {
180 $
self.record_variant(stringify
!($ty
), stringify
!($variant
), $id
, $val
)
187 impl<'v
> hir_visit
::Visitor
<'v
> for StatCollector
<'v
> {
188 fn visit_param(&mut self, param
: &'v hir
::Param
<'v
>) {
189 self.record("Param", Id
::Node(param
.hir_id
), param
);
190 hir_visit
::walk_param(self, param
)
193 fn visit_nested_item(&mut self, id
: hir
::ItemId
) {
194 let nested_item
= self.krate
.unwrap().item(id
);
195 self.visit_item(nested_item
)
198 fn visit_nested_trait_item(&mut self, trait_item_id
: hir
::TraitItemId
) {
199 let nested_trait_item
= self.krate
.unwrap().trait_item(trait_item_id
);
200 self.visit_trait_item(nested_trait_item
)
203 fn visit_nested_impl_item(&mut self, impl_item_id
: hir
::ImplItemId
) {
204 let nested_impl_item
= self.krate
.unwrap().impl_item(impl_item_id
);
205 self.visit_impl_item(nested_impl_item
)
208 fn visit_nested_foreign_item(&mut self, id
: hir
::ForeignItemId
) {
209 let nested_foreign_item
= self.krate
.unwrap().foreign_item(id
);
210 self.visit_foreign_item(nested_foreign_item
);
213 fn visit_nested_body(&mut self, body_id
: hir
::BodyId
) {
214 let nested_body
= self.krate
.unwrap().body(body_id
);
215 self.visit_body(nested_body
)
218 fn visit_item(&mut self, i
: &'v hir
::Item
<'v
>) {
220 (self, i
, i
.kind
, Id
::Node(i
.hir_id()), hir
, Item
, ItemKind
),
241 hir_visit
::walk_item(self, i
)
244 fn visit_body(&mut self, b
: &'v hir
::Body
<'v
>) {
245 self.record("Body", Id
::None
, b
);
246 hir_visit
::walk_body(self, b
);
249 fn visit_mod(&mut self, m
: &'v hir
::Mod
<'v
>, _s
: Span
, n
: HirId
) {
250 self.record("Mod", Id
::None
, m
);
251 hir_visit
::walk_mod(self, m
, n
)
254 fn visit_foreign_item(&mut self, i
: &'v hir
::ForeignItem
<'v
>) {
256 (self, i
, i
.kind
, Id
::Node(i
.hir_id()), hir
, ForeignItem
, ForeignItemKind
),
259 hir_visit
::walk_foreign_item(self, i
)
262 fn visit_local(&mut self, l
: &'v hir
::Local
<'v
>) {
263 self.record("Local", Id
::Node(l
.hir_id
), l
);
264 hir_visit
::walk_local(self, l
)
267 fn visit_block(&mut self, b
: &'v hir
::Block
<'v
>) {
268 self.record("Block", Id
::Node(b
.hir_id
), b
);
269 hir_visit
::walk_block(self, b
)
272 fn visit_stmt(&mut self, s
: &'v hir
::Stmt
<'v
>) {
274 (self, s
, s
.kind
, Id
::Node(s
.hir_id
), hir
, Stmt
, StmtKind
),
275 [Local
, Item
, Expr
, Semi
]
277 hir_visit
::walk_stmt(self, s
)
280 fn visit_arm(&mut self, a
: &'v hir
::Arm
<'v
>) {
281 self.record("Arm", Id
::Node(a
.hir_id
), a
);
282 hir_visit
::walk_arm(self, a
)
285 fn visit_pat(&mut self, p
: &'v hir
::Pat
<'v
>) {
287 (self, p
, p
.kind
, Id
::Node(p
.hir_id
), hir
, Pat
, PatKind
),
288 [Wild
, Binding
, Struct
, TupleStruct
, Or
, Path
, Tuple
, Box
, Ref
, Lit
, Range
, Slice
]
290 hir_visit
::walk_pat(self, p
)
293 fn visit_pat_field(&mut self, f
: &'v hir
::PatField
<'v
>) {
294 self.record("PatField", Id
::Node(f
.hir_id
), f
);
295 hir_visit
::walk_pat_field(self, f
)
298 fn visit_expr(&mut self, e
: &'v hir
::Expr
<'v
>) {
300 (self, e
, e
.kind
, Id
::Node(e
.hir_id
), hir
, Expr
, ExprKind
),
302 Box
, ConstBlock
, Array
, Call
, MethodCall
, Tup
, Binary
, Unary
, Lit
, Cast
, Type
,
303 DropTemps
, Let
, If
, Loop
, Match
, Closure
, Block
, Assign
, AssignOp
, Field
, Index
,
304 Path
, AddrOf
, Break
, Continue
, Ret
, InlineAsm
, Struct
, Repeat
, Yield
, Err
307 hir_visit
::walk_expr(self, e
)
310 fn visit_let_expr(&mut self, lex
: &'v hir
::Let
<'v
>) {
311 self.record("Let", Id
::Node(lex
.hir_id
), lex
);
312 hir_visit
::walk_let_expr(self, lex
)
315 fn visit_expr_field(&mut self, f
: &'v hir
::ExprField
<'v
>) {
316 self.record("ExprField", Id
::Node(f
.hir_id
), f
);
317 hir_visit
::walk_expr_field(self, f
)
320 fn visit_ty(&mut self, t
: &'v hir
::Ty
<'v
>) {
322 (self, t
, t
.kind
, Id
::Node(t
.hir_id
), hir
, Ty
, TyKind
),
339 hir_visit
::walk_ty(self, t
)
342 fn visit_generic_param(&mut self, p
: &'v hir
::GenericParam
<'v
>) {
343 self.record("GenericParam", Id
::Node(p
.hir_id
), p
);
344 hir_visit
::walk_generic_param(self, p
)
347 fn visit_generics(&mut self, g
: &'v hir
::Generics
<'v
>) {
348 self.record("Generics", Id
::None
, g
);
349 hir_visit
::walk_generics(self, g
)
352 fn visit_where_predicate(&mut self, p
: &'v hir
::WherePredicate
<'v
>) {
354 (self, p
, p
, Id
::None
, hir
, WherePredicate
, WherePredicate
),
355 [BoundPredicate
, RegionPredicate
, EqPredicate
]
357 hir_visit
::walk_where_predicate(self, p
)
362 fk
: hir_visit
::FnKind
<'v
>,
363 fd
: &'v hir
::FnDecl
<'v
>,
368 self.record("FnDecl", Id
::None
, fd
);
369 hir_visit
::walk_fn(self, fk
, fd
, b
, id
)
372 fn visit_use(&mut self, p
: &'v hir
::Path
<'v
>, hir_id
: hir
::HirId
) {
373 // This is `visit_use`, but the type is `Path` so record it that way.
374 self.record("Path", Id
::None
, p
);
375 hir_visit
::walk_use(self, p
, hir_id
)
378 fn visit_trait_item(&mut self, ti
: &'v hir
::TraitItem
<'v
>) {
380 (self, ti
, ti
.kind
, Id
::Node(ti
.hir_id()), hir
, TraitItem
, TraitItemKind
),
383 hir_visit
::walk_trait_item(self, ti
)
386 fn visit_trait_item_ref(&mut self, ti
: &'v hir
::TraitItemRef
) {
387 self.record("TraitItemRef", Id
::Node(ti
.id
.hir_id()), ti
);
388 hir_visit
::walk_trait_item_ref(self, ti
)
391 fn visit_impl_item(&mut self, ii
: &'v hir
::ImplItem
<'v
>) {
393 (self, ii
, ii
.kind
, Id
::Node(ii
.hir_id()), hir
, ImplItem
, ImplItemKind
),
396 hir_visit
::walk_impl_item(self, ii
)
399 fn visit_foreign_item_ref(&mut self, fi
: &'v hir
::ForeignItemRef
) {
400 self.record("ForeignItemRef", Id
::Node(fi
.id
.hir_id()), fi
);
401 hir_visit
::walk_foreign_item_ref(self, fi
)
404 fn visit_impl_item_ref(&mut self, ii
: &'v hir
::ImplItemRef
) {
405 self.record("ImplItemRef", Id
::Node(ii
.id
.hir_id()), ii
);
406 hir_visit
::walk_impl_item_ref(self, ii
)
409 fn visit_param_bound(&mut self, b
: &'v hir
::GenericBound
<'v
>) {
411 (self, b
, b
, Id
::None
, hir
, GenericBound
, GenericBound
),
412 [Trait
, LangItemTrait
, Outlives
]
414 hir_visit
::walk_param_bound(self, b
)
417 fn visit_field_def(&mut self, s
: &'v hir
::FieldDef
<'v
>) {
418 self.record("FieldDef", Id
::Node(s
.hir_id
), s
);
419 hir_visit
::walk_field_def(self, s
)
422 fn visit_variant(&mut self, v
: &'v hir
::Variant
<'v
>) {
423 self.record("Variant", Id
::None
, v
);
424 hir_visit
::walk_variant(self, v
)
427 fn visit_generic_arg(&mut self, ga
: &'v hir
::GenericArg
<'v
>) {
429 (self, ga
, ga
, Id
::Node(ga
.hir_id()), hir
, GenericArg
, GenericArg
),
430 [Lifetime
, Type
, Const
, Infer
]
433 hir
::GenericArg
::Lifetime(lt
) => self.visit_lifetime(lt
),
434 hir
::GenericArg
::Type(ty
) => self.visit_ty(ty
),
435 hir
::GenericArg
::Const(ct
) => self.visit_anon_const(&ct
.value
),
436 hir
::GenericArg
::Infer(inf
) => self.visit_infer(inf
),
440 fn visit_lifetime(&mut self, lifetime
: &'v hir
::Lifetime
) {
441 self.record("Lifetime", Id
::Node(lifetime
.hir_id
), lifetime
);
442 hir_visit
::walk_lifetime(self, lifetime
)
445 fn visit_path(&mut self, path
: &'v hir
::Path
<'v
>, _id
: hir
::HirId
) {
446 self.record("Path", Id
::None
, path
);
447 hir_visit
::walk_path(self, path
)
450 fn visit_path_segment(&mut self, path_segment
: &'v hir
::PathSegment
<'v
>) {
451 self.record("PathSegment", Id
::None
, path_segment
);
452 hir_visit
::walk_path_segment(self, path_segment
)
455 fn visit_generic_args(&mut self, ga
: &'v hir
::GenericArgs
<'v
>) {
456 self.record("GenericArgs", Id
::None
, ga
);
457 hir_visit
::walk_generic_args(self, ga
)
460 fn visit_assoc_type_binding(&mut self, type_binding
: &'v hir
::TypeBinding
<'v
>) {
461 self.record("TypeBinding", Id
::Node(type_binding
.hir_id
), type_binding
);
462 hir_visit
::walk_assoc_type_binding(self, type_binding
)
465 fn visit_attribute(&mut self, attr
: &'v ast
::Attribute
) {
466 self.record("Attribute", Id
::Attr(attr
.id
), attr
);
469 fn visit_inline_asm(&mut self, asm
: &'v hir
::InlineAsm
<'v
>, id
: HirId
) {
470 self.record("InlineAsm", Id
::None
, asm
);
471 hir_visit
::walk_inline_asm(self, asm
, id
);
475 impl<'v
> ast_visit
::Visitor
<'v
> for StatCollector
<'v
> {
476 fn visit_foreign_item(&mut self, i
: &'v ast
::ForeignItem
) {
478 (self, i
, i
.kind
, Id
::None
, ast
, ForeignItem
, ForeignItemKind
),
479 [Static
, Fn
, TyAlias
, MacCall
]
481 ast_visit
::walk_foreign_item(self, i
)
484 fn visit_item(&mut self, i
: &'v ast
::Item
) {
486 (self, i
, i
.kind
, Id
::None
, ast
, Item
, ItemKind
),
507 ast_visit
::walk_item(self, i
)
510 fn visit_local(&mut self, l
: &'v ast
::Local
) {
511 self.record("Local", Id
::None
, l
);
512 ast_visit
::walk_local(self, l
)
515 fn visit_block(&mut self, b
: &'v ast
::Block
) {
516 self.record("Block", Id
::None
, b
);
517 ast_visit
::walk_block(self, b
)
520 fn visit_stmt(&mut self, s
: &'v ast
::Stmt
) {
522 (self, s
, s
.kind
, Id
::None
, ast
, Stmt
, StmtKind
),
523 [Local
, Item
, Expr
, Semi
, Empty
, MacCall
]
525 ast_visit
::walk_stmt(self, s
)
528 fn visit_param(&mut self, p
: &'v ast
::Param
) {
529 self.record("Param", Id
::None
, p
);
530 ast_visit
::walk_param(self, p
)
533 fn visit_arm(&mut self, a
: &'v ast
::Arm
) {
534 self.record("Arm", Id
::None
, a
);
535 ast_visit
::walk_arm(self, a
)
538 fn visit_pat(&mut self, p
: &'v ast
::Pat
) {
540 (self, p
, p
.kind
, Id
::None
, ast
, Pat
, PatKind
),
559 ast_visit
::walk_pat(self, p
)
562 fn visit_expr(&mut self, e
: &'v ast
::Expr
) {
564 (self, e
, e
.kind
, Id
::None
, ast
, Expr
, ExprKind
),
566 Box
, Array
, ConstBlock
, Call
, MethodCall
, Tup
, Binary
, Unary
, Lit
, Cast
, Type
, Let
,
567 If
, While
, ForLoop
, Loop
, Match
, Closure
, Block
, Async
, Await
, TryBlock
, Assign
,
568 AssignOp
, Field
, Index
, Range
, Underscore
, Path
, AddrOf
, Break
, Continue
, Ret
,
569 InlineAsm
, MacCall
, Struct
, Repeat
, Paren
, Try
, Yield
, Yeet
, Err
572 ast_visit
::walk_expr(self, e
)
575 fn visit_ty(&mut self, t
: &'v ast
::Ty
) {
577 (self, t
, t
.kind
, Id
::None
, ast
, Ty
, TyKind
),
599 ast_visit
::walk_ty(self, t
)
602 fn visit_generic_param(&mut self, g
: &'v ast
::GenericParam
) {
603 self.record("GenericParam", Id
::None
, g
);
604 ast_visit
::walk_generic_param(self, g
)
607 fn visit_where_predicate(&mut self, p
: &'v ast
::WherePredicate
) {
609 (self, p
, p
, Id
::None
, ast
, WherePredicate
, WherePredicate
),
610 [BoundPredicate
, RegionPredicate
, EqPredicate
]
612 ast_visit
::walk_where_predicate(self, p
)
615 fn visit_fn(&mut self, fk
: ast_visit
::FnKind
<'v
>, _
: Span
, _
: NodeId
) {
616 self.record("FnDecl", Id
::None
, fk
.decl());
617 ast_visit
::walk_fn(self, fk
)
620 fn visit_assoc_item(&mut self, i
: &'v ast
::AssocItem
, ctxt
: ast_visit
::AssocCtxt
) {
622 (self, i
, i
.kind
, Id
::None
, ast
, AssocItem
, AssocItemKind
),
623 [Const
, Fn
, Type
, MacCall
]
625 ast_visit
::walk_assoc_item(self, i
, ctxt
);
628 fn visit_param_bound(&mut self, b
: &'v ast
::GenericBound
, _ctxt
: BoundKind
) {
630 (self, b
, b
, Id
::None
, ast
, GenericBound
, GenericBound
),
633 ast_visit
::walk_param_bound(self, b
)
636 fn visit_field_def(&mut self, s
: &'v ast
::FieldDef
) {
637 self.record("FieldDef", Id
::None
, s
);
638 ast_visit
::walk_field_def(self, s
)
641 fn visit_variant(&mut self, v
: &'v ast
::Variant
) {
642 self.record("Variant", Id
::None
, v
);
643 ast_visit
::walk_variant(self, v
)
646 // `UseTree` has one inline use (in `ast::ItemKind::Use`) and one
647 // non-inline use (in `ast::UseTreeKind::Nested). The former case is more
648 // common, so we don't implement `visit_use_tree` and tolerate the missed
649 // coverage in the latter case.
651 // `PathSegment` has one inline use (in `ast::ExprKind::MethodCall`) and
652 // one non-inline use (in `ast::Path::segments`). The latter case is more
653 // common than the former case, so we implement this visitor and tolerate
654 // the double counting in the former case.
655 fn visit_path_segment(&mut self, path_segment
: &'v ast
::PathSegment
) {
656 self.record("PathSegment", Id
::None
, path_segment
);
657 ast_visit
::walk_path_segment(self, path_segment
)
660 // `GenericArgs` has one inline use (in `ast::AssocConstraint::gen_args`) and one
661 // non-inline use (in `ast::PathSegment::args`). The latter case is more
662 // common, so we implement `visit_generic_args` and tolerate the double
663 // counting in the former case.
664 fn visit_generic_args(&mut self, g
: &'v ast
::GenericArgs
) {
666 (self, g
, g
, Id
::None
, ast
, GenericArgs
, GenericArgs
),
667 [AngleBracketed
, Parenthesized
]
669 ast_visit
::walk_generic_args(self, g
)
672 fn visit_attribute(&mut self, attr
: &'v ast
::Attribute
) {
674 (self, attr
, attr
.kind
, Id
::None
, ast
, Attribute
, AttrKind
),
677 ast_visit
::walk_attribute(self, attr
)
680 fn visit_expr_field(&mut self, f
: &'v ast
::ExprField
) {
681 self.record("ExprField", Id
::None
, f
);
682 ast_visit
::walk_expr_field(self, f
)
685 fn visit_crate(&mut self, krate
: &'v ast
::Crate
) {
686 self.record("Crate", Id
::None
, krate
);
687 ast_visit
::walk_crate(self, krate
)
690 fn visit_inline_asm(&mut self, asm
: &'v ast
::InlineAsm
) {
691 self.record("InlineAsm", Id
::None
, asm
);
692 ast_visit
::walk_inline_asm(self, asm
)