From bf20777d1310431178c50b7b8dad0537d5e4d4f7 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 11 Aug 2022 10:11:13 +1000 Subject: [PATCH 1/5] Add a test for `-Zhir-stats` output. This will be very useful in subsequent commits where I will improve the output. --- src/test/ui/stats/hir-stats.rs | 41 +++++++++++++++ src/test/ui/stats/hir-stats.stderr | 83 ++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 src/test/ui/stats/hir-stats.rs create mode 100644 src/test/ui/stats/hir-stats.stderr diff --git a/src/test/ui/stats/hir-stats.rs b/src/test/ui/stats/hir-stats.rs new file mode 100644 index 0000000000000..3c59ee22f24c5 --- /dev/null +++ b/src/test/ui/stats/hir-stats.rs @@ -0,0 +1,41 @@ +// check-pass +// compile-flags: -Zhir-stats +// only-x86_64 + +// The aim here is to include at least one of every different type of AST/HIR +// node reported by `-Zhir-stats`. + +#![allow(dead_code)] + +use std::arch::asm; +use std::fmt::Debug; +use std::ffi::c_void; + +extern "C" { fn f(p: *mut c_void); } + +/// An enum. +enum E<'a, T: Copy> { A { t: T }, B(&'a u32) } + +trait Go { + type G: Debug; + fn go(self) -> u32; +} + +impl<'a, T: Copy> Go for E<'a, T> { + type G = bool; + fn go(self) -> u32 { + 99 + } +} + +fn f2(t: T) where T: Debug {} + +fn main() { + let x = E::A { t: 3 }; + match x { + E::A { .. } => {} + _ => {} + } + + unsafe { asm!("mov rdi, 1"); } +} diff --git a/src/test/ui/stats/hir-stats.stderr b/src/test/ui/stats/hir-stats.stderr new file mode 100644 index 0000000000000..a0c1c80022f2b --- /dev/null +++ b/src/test/ui/stats/hir-stats.stderr @@ -0,0 +1,83 @@ + +PRE EXPANSION AST STATS + +Name Accumulated Size Count Item Size +---------------------------------------------------------------- +Lifetime 32 2 16 +MacCall 64 1 64 +Local 72 1 72 +Arm 96 2 48 +FieldDef 160 2 80 +ForeignItem 160 1 160 +Stmt 160 5 32 +FnDecl 200 5 40 +Variant 240 2 120 +Block 288 6 48 +Attribute 304 2 152 +ImplItem 320 2 160 +TraitItem 320 2 160 +GenericBound 352 4 88 +PathSegment 720 30 24 +Expr 832 8 104 +Pat 840 7 120 +Ty 1_344 14 96 +Item 1_800 9 200 +---------------------------------------------------------------- +Total 8_304 + + +POST EXPANSION AST STATS + +Name Accumulated Size Count Item Size +---------------------------------------------------------------- +Lifetime 32 2 16 +Local 72 1 72 +Arm 96 2 48 +FieldDef 160 2 80 +ForeignItem 160 1 160 +Stmt 160 5 32 +FnDecl 200 5 40 +Variant 240 2 120 +Block 288 6 48 +ImplItem 320 2 160 +TraitItem 320 2 160 +GenericBound 352 4 88 +Attribute 608 4 152 +PathSegment 792 33 24 +Pat 840 7 120 +Expr 936 9 104 +Ty 1_344 14 96 +Item 2_200 11 200 +---------------------------------------------------------------- +Total 9_120 + + +HIR STATS + +Name Accumulated Size Count Item Size +---------------------------------------------------------------- +Param 64 2 32 +Local 64 1 64 +ForeignItem 72 1 72 +FieldDef 96 2 48 +Arm 96 2 48 +Stmt 96 3 32 +FnDecl 120 3 40 +Lifetime 128 4 32 +Variant 160 2 80 +ImplItem 176 2 88 +GenericBound 192 4 48 +TraitItem 192 2 96 +WherePredicate 216 3 72 +Block 288 6 48 +QPath 408 17 24 +Pat 440 5 88 +Attribute 608 4 152 +Expr 672 12 56 +Item 960 12 80 +Ty 1_152 16 72 +Path 1_296 27 48 +PathSegment 2_240 40 56 +---------------------------------------------------------------- +Total 9_736 + From 288b6672be6560fe45f8bb033d581379ec38b4a0 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 11 Aug 2022 12:18:21 +1000 Subject: [PATCH 2/5] Improve AST stat collector. This commit: - Adds a comment explaining which `visit_*` methods should be implemented. - Adds and removes some `visit_*` methods accordingly, improving coverage, and avoiding some double counting. --- compiler/rustc_passes/src/hir_stats.rs | 79 +++++++++++++++++++++----- src/test/ui/stats/hir-stats.stderr | 20 +++++-- 2 files changed, 81 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs index a3be827a7ccec..080c7df47a0e9 100644 --- a/compiler/rustc_passes/src/hir_stats.rs +++ b/compiler/rustc_passes/src/hir_stats.rs @@ -26,6 +26,23 @@ struct NodeData { size: usize, } +/// This type measures the size of AST and HIR nodes, by implementing the AST +/// and HIR `Visitor` traits. But we don't measure every visited type because +/// that could cause double counting. +/// +/// For example, `ast::Visitor` has `visit_ident`, but `Ident`s are always +/// stored inline within other AST nodes, so we don't implement `visit_ident` +/// here. In constrast, we do implement `visit_expr` because `ast::Expr` is +/// always stored as `P`, and every such expression should be +/// measured separately. +/// +/// In general, a `visit_foo` method should be implemented here if the +/// corresponding `Foo` type is always stored on its own, e.g.: `P`, +/// `Box`, `Vec`, `Box<[Foo]>`. +/// +/// There are some types in the AST and HIR tree that the visitors do not have +/// a `visit_*` method for, and so we cannot measure these, which is +/// unfortunate. struct StatCollector<'k> { krate: Option>, data: FxHashMap<&'static str, NodeData>, @@ -44,9 +61,11 @@ pub fn print_hir_stats(tcx: TyCtxt<'_>) { } pub fn print_ast_stats(krate: &ast::Crate, title: &str) { + use rustc_ast::visit::Visitor; + let mut collector = StatCollector { krate: None, data: FxHashMap::default(), seen: FxHashSet::default() }; - ast_visit::walk_crate(&mut collector, krate); + collector.visit_crate(krate); collector.print(title); } @@ -228,6 +247,10 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { hir_visit::walk_path(self, path) } + // `PathSegment` has one inline use (in `ast::ExprKind::MethodCall`) and + // one non-inline use (in `Path::segments`). The latter case is more common + // than the former case, so we implement this visitor and tolerate the + // double counting in the former case. fn visit_path_segment(&mut self, path_span: Span, path_segment: &'v hir::PathSegment<'v>) { self.record("PathSegment", Id::None, path_segment); hir_visit::walk_path_segment(self, path_span, path_segment) @@ -269,6 +292,11 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { ast_visit::walk_stmt(self, s) } + fn visit_param(&mut self, p: &'v ast::Param) { + self.record("Param", Id::None, p); + ast_visit::walk_param(self, p) + } + fn visit_arm(&mut self, a: &'v ast::Arm) { self.record("Arm", Id::None, a); ast_visit::walk_arm(self, a) @@ -289,6 +317,16 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { ast_visit::walk_ty(self, t) } + fn visit_generic_param(&mut self, g: &'v ast::GenericParam) { + self.record("GenericParam", Id::None, g); + ast_visit::walk_generic_param(self, g) + } + + fn visit_where_predicate(&mut self, p: &'v ast::WherePredicate) { + self.record("WherePredicate", Id::None, p); + ast_visit::walk_where_predicate(self, p) + } + fn visit_fn(&mut self, fk: ast_visit::FnKind<'v>, s: Span, _: NodeId) { self.record("FnDecl", Id::None, fk.decl()); ast_visit::walk_fn(self, fk, s) @@ -318,27 +356,42 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { ast_visit::walk_variant(self, v) } - fn visit_lifetime(&mut self, lifetime: &'v ast::Lifetime, _: ast_visit::LifetimeCtxt) { - self.record("Lifetime", Id::None, lifetime); - ast_visit::walk_lifetime(self, lifetime) - } - - fn visit_mac_call(&mut self, mac: &'v ast::MacCall) { - self.record("MacCall", Id::None, mac); - ast_visit::walk_mac(self, mac) - } + // `UseTree` has one inline use (in `ast::ItemKind::Use`) and one + // non-inline use (in `ast::UseTreeKind::Nested). The former case is more + // common, so we don't implement `visit_use_tree` and tolerate the missed + // coverage in the latter case. fn visit_path_segment(&mut self, path_span: Span, path_segment: &'v ast::PathSegment) { self.record("PathSegment", Id::None, path_segment); ast_visit::walk_path_segment(self, path_span, path_segment) } - fn visit_assoc_constraint(&mut self, constraint: &'v ast::AssocConstraint) { - self.record("AssocConstraint", Id::None, constraint); - ast_visit::walk_assoc_constraint(self, constraint) + // `GenericArgs` has one inline use (in `ast::AssocConstraint::gen_args`) and one + // non-inline use (in `ast::PathSegment::args`). The latter case is more + // common, so we implement `visit_generic_args` and tolerate the double + // counting in the former case. + fn visit_generic_args(&mut self, sp: Span, g: &'v ast::GenericArgs) { + self.record("GenericArgs", Id::None, g); + ast_visit::walk_generic_args(self, sp, g) } fn visit_attribute(&mut self, attr: &'v ast::Attribute) { self.record("Attribute", Id::None, attr); + ast_visit::walk_attribute(self, attr) + } + + fn visit_expr_field(&mut self, f: &'v ast::ExprField) { + self.record("ExprField", Id::None, f); + ast_visit::walk_expr_field(self, f) + } + + fn visit_crate(&mut self, krate: &'v ast::Crate) { + self.record("Crate", Id::None, krate); + ast_visit::walk_crate(self, krate) + } + + fn visit_inline_asm(&mut self, asm: &'v ast::InlineAsm) { + self.record("InlineAsm", Id::None, asm); + ast_visit::walk_inline_asm(self, asm) } } diff --git a/src/test/ui/stats/hir-stats.stderr b/src/test/ui/stats/hir-stats.stderr index a0c1c80022f2b..8e2daebcf0ad0 100644 --- a/src/test/ui/stats/hir-stats.stderr +++ b/src/test/ui/stats/hir-stats.stderr @@ -3,13 +3,16 @@ PRE EXPANSION AST STATS Name Accumulated Size Count Item Size ---------------------------------------------------------------- -Lifetime 32 2 16 -MacCall 64 1 64 +ExprField 48 1 48 +GenericArgs 64 1 64 Local 72 1 72 +WherePredicate 72 1 72 +Crate 72 1 72 Arm 96 2 48 FieldDef 160 2 80 ForeignItem 160 1 160 Stmt 160 5 32 +Param 160 4 40 FnDecl 200 5 40 Variant 240 2 120 Block 288 6 48 @@ -17,31 +20,38 @@ Attribute 304 2 152 ImplItem 320 2 160 TraitItem 320 2 160 GenericBound 352 4 88 +GenericParam 520 5 104 PathSegment 720 30 24 Expr 832 8 104 Pat 840 7 120 Ty 1_344 14 96 Item 1_800 9 200 ---------------------------------------------------------------- -Total 8_304 +Total 9_144 POST EXPANSION AST STATS Name Accumulated Size Count Item Size ---------------------------------------------------------------- -Lifetime 32 2 16 +ExprField 48 1 48 +GenericArgs 64 1 64 Local 72 1 72 +WherePredicate 72 1 72 +Crate 72 1 72 Arm 96 2 48 +InlineAsm 120 1 120 FieldDef 160 2 80 ForeignItem 160 1 160 Stmt 160 5 32 +Param 160 4 40 FnDecl 200 5 40 Variant 240 2 120 Block 288 6 48 ImplItem 320 2 160 TraitItem 320 2 160 GenericBound 352 4 88 +GenericParam 520 5 104 Attribute 608 4 152 PathSegment 792 33 24 Pat 840 7 120 @@ -49,7 +59,7 @@ Expr 936 9 104 Ty 1_344 14 96 Item 2_200 11 200 ---------------------------------------------------------------- -Total 9_120 +Total 10_144 HIR STATS From 6a3c663cbb65b08c7a7356cc3745eb9b75493bc6 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 11 Aug 2022 12:34:52 +1000 Subject: [PATCH 3/5] Change how `AssocItem` is reported. Currently it's reported as either `TraitItem` or `ImplItem`. This commit changes it to `AssocItem`, because having the report match the type name is (a) consistent with other types, and (b) the trait/impl split isn't that important here. --- compiler/rustc_passes/src/hir_stats.rs | 6 +----- src/test/ui/stats/hir-stats.stderr | 6 ++---- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs index 080c7df47a0e9..9d3663240921a 100644 --- a/compiler/rustc_passes/src/hir_stats.rs +++ b/compiler/rustc_passes/src/hir_stats.rs @@ -333,11 +333,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { } fn visit_assoc_item(&mut self, item: &'v ast::AssocItem, ctxt: ast_visit::AssocCtxt) { - let label = match ctxt { - ast_visit::AssocCtxt::Trait => "TraitItem", - ast_visit::AssocCtxt::Impl => "ImplItem", - }; - self.record(label, Id::None, item); + self.record("AssocItem", Id::None, item); ast_visit::walk_assoc_item(self, item, ctxt); } diff --git a/src/test/ui/stats/hir-stats.stderr b/src/test/ui/stats/hir-stats.stderr index 8e2daebcf0ad0..1b2eafc68db2b 100644 --- a/src/test/ui/stats/hir-stats.stderr +++ b/src/test/ui/stats/hir-stats.stderr @@ -17,10 +17,9 @@ FnDecl 200 5 40 Variant 240 2 120 Block 288 6 48 Attribute 304 2 152 -ImplItem 320 2 160 -TraitItem 320 2 160 GenericBound 352 4 88 GenericParam 520 5 104 +AssocItem 640 4 160 PathSegment 720 30 24 Expr 832 8 104 Pat 840 7 120 @@ -48,11 +47,10 @@ Param 160 4 40 FnDecl 200 5 40 Variant 240 2 120 Block 288 6 48 -ImplItem 320 2 160 -TraitItem 320 2 160 GenericBound 352 4 88 GenericParam 520 5 104 Attribute 608 4 152 +AssocItem 640 4 160 PathSegment 792 33 24 Pat 840 7 120 Expr 936 9 104 From 85c749266d1e9a38a9fd9c45ef417d0d464ef634 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 11 Aug 2022 13:46:26 +1000 Subject: [PATCH 4/5] Add percentages to `-Zhir-stats` output. --- compiler/rustc_passes/src/hir_stats.rs | 14 +-- src/test/ui/stats/hir-stats.stderr | 140 ++++++++++++------------- 2 files changed, 78 insertions(+), 76 deletions(-) diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs index 9d3663240921a..0c6c1d1aa2b8e 100644 --- a/compiler/rustc_passes/src/hir_stats.rs +++ b/compiler/rustc_passes/src/hir_stats.rs @@ -86,26 +86,28 @@ impl<'k> StatCollector<'k> { stats.sort_by_key(|&(_, ref d)| d.count * d.size); - let mut total_size = 0; + let total_size = stats.iter().map(|(_, data)| data.count * data.size).sum(); eprintln!("\n{}\n", title); eprintln!("{:<18}{:>18}{:>14}{:>14}", "Name", "Accumulated Size", "Count", "Item Size"); eprintln!("----------------------------------------------------------------"); + let percent = |m, n| { (m * 100) as f64 / n as f64 }; + for (label, data) in stats { + let size = data.count * data.size; eprintln!( - "{:<18}{:>18}{:>14}{:>14}", + "{:<18}{:>10} ({:4.1}%){:>14}{:>14}", label, - to_readable_str(data.count * data.size), + to_readable_str(size), + percent(size, total_size), to_readable_str(data.count), to_readable_str(data.size) ); - - total_size += data.count * data.size; } eprintln!("----------------------------------------------------------------"); - eprintln!("{:<18}{:>18}\n", "Total", to_readable_str(total_size)); + eprintln!("{:<18}{:>10}\n", "Total", to_readable_str(total_size)); } } diff --git a/src/test/ui/stats/hir-stats.stderr b/src/test/ui/stats/hir-stats.stderr index 1b2eafc68db2b..132fff1972f4f 100644 --- a/src/test/ui/stats/hir-stats.stderr +++ b/src/test/ui/stats/hir-stats.stderr @@ -3,89 +3,89 @@ PRE EXPANSION AST STATS Name Accumulated Size Count Item Size ---------------------------------------------------------------- -ExprField 48 1 48 -GenericArgs 64 1 64 -Local 72 1 72 -WherePredicate 72 1 72 -Crate 72 1 72 -Arm 96 2 48 -FieldDef 160 2 80 -ForeignItem 160 1 160 -Stmt 160 5 32 -Param 160 4 40 -FnDecl 200 5 40 -Variant 240 2 120 -Block 288 6 48 -Attribute 304 2 152 -GenericBound 352 4 88 -GenericParam 520 5 104 -AssocItem 640 4 160 -PathSegment 720 30 24 -Expr 832 8 104 -Pat 840 7 120 -Ty 1_344 14 96 -Item 1_800 9 200 +ExprField 48 ( 0.5%) 1 48 +GenericArgs 64 ( 0.7%) 1 64 +Local 72 ( 0.8%) 1 72 +WherePredicate 72 ( 0.8%) 1 72 +Crate 72 ( 0.8%) 1 72 +Arm 96 ( 1.0%) 2 48 +FieldDef 160 ( 1.7%) 2 80 +ForeignItem 160 ( 1.7%) 1 160 +Stmt 160 ( 1.7%) 5 32 +Param 160 ( 1.7%) 4 40 +FnDecl 200 ( 2.2%) 5 40 +Variant 240 ( 2.6%) 2 120 +Block 288 ( 3.1%) 6 48 +Attribute 304 ( 3.3%) 2 152 +GenericBound 352 ( 3.8%) 4 88 +GenericParam 520 ( 5.7%) 5 104 +AssocItem 640 ( 7.0%) 4 160 +PathSegment 720 ( 7.9%) 30 24 +Expr 832 ( 9.1%) 8 104 +Pat 840 ( 9.2%) 7 120 +Ty 1_344 (14.7%) 14 96 +Item 1_800 (19.7%) 9 200 ---------------------------------------------------------------- -Total 9_144 +Total 9_144 POST EXPANSION AST STATS Name Accumulated Size Count Item Size ---------------------------------------------------------------- -ExprField 48 1 48 -GenericArgs 64 1 64 -Local 72 1 72 -WherePredicate 72 1 72 -Crate 72 1 72 -Arm 96 2 48 -InlineAsm 120 1 120 -FieldDef 160 2 80 -ForeignItem 160 1 160 -Stmt 160 5 32 -Param 160 4 40 -FnDecl 200 5 40 -Variant 240 2 120 -Block 288 6 48 -GenericBound 352 4 88 -GenericParam 520 5 104 -Attribute 608 4 152 -AssocItem 640 4 160 -PathSegment 792 33 24 -Pat 840 7 120 -Expr 936 9 104 -Ty 1_344 14 96 -Item 2_200 11 200 +ExprField 48 ( 0.5%) 1 48 +GenericArgs 64 ( 0.6%) 1 64 +Local 72 ( 0.7%) 1 72 +WherePredicate 72 ( 0.7%) 1 72 +Crate 72 ( 0.7%) 1 72 +Arm 96 ( 0.9%) 2 48 +InlineAsm 120 ( 1.2%) 1 120 +FieldDef 160 ( 1.6%) 2 80 +ForeignItem 160 ( 1.6%) 1 160 +Stmt 160 ( 1.6%) 5 32 +Param 160 ( 1.6%) 4 40 +FnDecl 200 ( 2.0%) 5 40 +Variant 240 ( 2.4%) 2 120 +Block 288 ( 2.8%) 6 48 +GenericBound 352 ( 3.5%) 4 88 +GenericParam 520 ( 5.1%) 5 104 +Attribute 608 ( 6.0%) 4 152 +AssocItem 640 ( 6.3%) 4 160 +PathSegment 792 ( 7.8%) 33 24 +Pat 840 ( 8.3%) 7 120 +Expr 936 ( 9.2%) 9 104 +Ty 1_344 (13.2%) 14 96 +Item 2_200 (21.7%) 11 200 ---------------------------------------------------------------- -Total 10_144 +Total 10_144 HIR STATS Name Accumulated Size Count Item Size ---------------------------------------------------------------- -Param 64 2 32 -Local 64 1 64 -ForeignItem 72 1 72 -FieldDef 96 2 48 -Arm 96 2 48 -Stmt 96 3 32 -FnDecl 120 3 40 -Lifetime 128 4 32 -Variant 160 2 80 -ImplItem 176 2 88 -GenericBound 192 4 48 -TraitItem 192 2 96 -WherePredicate 216 3 72 -Block 288 6 48 -QPath 408 17 24 -Pat 440 5 88 -Attribute 608 4 152 -Expr 672 12 56 -Item 960 12 80 -Ty 1_152 16 72 -Path 1_296 27 48 -PathSegment 2_240 40 56 +Param 64 ( 0.7%) 2 32 +Local 64 ( 0.7%) 1 64 +ForeignItem 72 ( 0.7%) 1 72 +FieldDef 96 ( 1.0%) 2 48 +Arm 96 ( 1.0%) 2 48 +Stmt 96 ( 1.0%) 3 32 +FnDecl 120 ( 1.2%) 3 40 +Lifetime 128 ( 1.3%) 4 32 +Variant 160 ( 1.6%) 2 80 +ImplItem 176 ( 1.8%) 2 88 +GenericBound 192 ( 2.0%) 4 48 +TraitItem 192 ( 2.0%) 2 96 +WherePredicate 216 ( 2.2%) 3 72 +Block 288 ( 3.0%) 6 48 +QPath 408 ( 4.2%) 17 24 +Pat 440 ( 4.5%) 5 88 +Attribute 608 ( 6.2%) 4 152 +Expr 672 ( 6.9%) 12 56 +Item 960 ( 9.9%) 12 80 +Ty 1_152 (11.8%) 16 72 +Path 1_296 (13.3%) 27 48 +PathSegment 2_240 (23.0%) 40 56 ---------------------------------------------------------------- -Total 9_736 +Total 9_736 From 4f8a1702bab38730bf2386e2e3df57b63a768077 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 11 Aug 2022 14:37:21 +1000 Subject: [PATCH 5/5] Add a second level to the AST size reporting. This tells you which variants of the enums are most common, which is very useful. I've only done it for the AST for now, HIR can be done later. --- compiler/rustc_passes/src/hir_stats.rs | 218 +++++++++++++++++++++---- src/test/ui/stats/hir-stats.rs | 4 +- src/test/ui/stats/hir-stats.stderr | 60 +++++++ 3 files changed, 246 insertions(+), 36 deletions(-) diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs index 0c6c1d1aa2b8e..ec070e6a9c5c6 100644 --- a/compiler/rustc_passes/src/hir_stats.rs +++ b/compiler/rustc_passes/src/hir_stats.rs @@ -21,11 +21,28 @@ enum Id { None, } -struct NodeData { +struct NodeStats { count: usize, size: usize, } +impl NodeStats { + fn new() -> NodeStats { + NodeStats { count: 0, size: 0 } + } +} + +struct Node { + stats: NodeStats, + subnodes: FxHashMap<&'static str, NodeStats>, +} + +impl Node { + fn new() -> Node { + Node { stats: NodeStats::new(), subnodes: FxHashMap::default() } + } +} + /// This type measures the size of AST and HIR nodes, by implementing the AST /// and HIR `Visitor` traits. But we don't measure every visited type because /// that could cause double counting. @@ -45,14 +62,14 @@ struct NodeData { /// unfortunate. struct StatCollector<'k> { krate: Option>, - data: FxHashMap<&'static str, NodeData>, + nodes: FxHashMap<&'static str, Node>, seen: FxHashSet, } pub fn print_hir_stats(tcx: TyCtxt<'_>) { let mut collector = StatCollector { krate: Some(tcx.hir()), - data: FxHashMap::default(), + nodes: FxHashMap::default(), seen: FxHashSet::default(), }; tcx.hir().walk_toplevel_module(&mut collector); @@ -64,47 +81,82 @@ pub fn print_ast_stats(krate: &ast::Crate, title: &str) { use rustc_ast::visit::Visitor; let mut collector = - StatCollector { krate: None, data: FxHashMap::default(), seen: FxHashSet::default() }; + StatCollector { krate: None, nodes: FxHashMap::default(), seen: FxHashSet::default() }; collector.visit_crate(krate); collector.print(title); } impl<'k> StatCollector<'k> { - fn record(&mut self, label: &'static str, id: Id, node: &T) { + // Record a top-level node. + fn record(&mut self, label: &'static str, id: Id, val: &T) { + self.record_inner(label, None, id, val); + } + + // Record a two-level entry, with a top-level enum type and a variant. + fn record_variant(&mut self, label1: &'static str, label2: &'static str, id: Id, val: &T) { + self.record_inner(label1, Some(label2), id, val); + } + + fn record_inner( + &mut self, + label1: &'static str, + label2: Option<&'static str>, + id: Id, + val: &T, + ) { if id != Id::None && !self.seen.insert(id) { return; } - let entry = self.data.entry(label).or_insert(NodeData { count: 0, size: 0 }); + let node = self.nodes.entry(label1).or_insert(Node::new()); + node.stats.count += 1; + node.stats.size = std::mem::size_of_val(val); - entry.count += 1; - entry.size = std::mem::size_of_val(node); + if let Some(label2) = label2 { + let subnode = node.subnodes.entry(label2).or_insert(NodeStats::new()); + subnode.count += 1; + subnode.size = std::mem::size_of_val(val); + } } fn print(&self, title: &str) { - let mut stats: Vec<_> = self.data.iter().collect(); - - stats.sort_by_key(|&(_, ref d)| d.count * d.size); + let mut nodes: Vec<_> = self.nodes.iter().collect(); + nodes.sort_by_key(|&(_, ref node)| node.stats.count * node.stats.size); - let total_size = stats.iter().map(|(_, data)| data.count * data.size).sum(); + let total_size = nodes.iter().map(|(_, node)| node.stats.count * node.stats.size).sum(); eprintln!("\n{}\n", title); eprintln!("{:<18}{:>18}{:>14}{:>14}", "Name", "Accumulated Size", "Count", "Item Size"); eprintln!("----------------------------------------------------------------"); - let percent = |m, n| { (m * 100) as f64 / n as f64 }; + let percent = |m, n| (m * 100) as f64 / n as f64; - for (label, data) in stats { - let size = data.count * data.size; + for (label, node) in nodes { + let size = node.stats.count * node.stats.size; eprintln!( "{:<18}{:>10} ({:4.1}%){:>14}{:>14}", label, to_readable_str(size), percent(size, total_size), - to_readable_str(data.count), - to_readable_str(data.size) + to_readable_str(node.stats.count), + to_readable_str(node.stats.size) ); + if !node.subnodes.is_empty() { + let mut subnodes: Vec<_> = node.subnodes.iter().collect(); + subnodes.sort_by_key(|&(_, ref subnode)| subnode.count * subnode.size); + + for (label, subnode) in subnodes { + let size = subnode.count * subnode.size; + eprintln!( + "- {:<18}{:>10} ({:4.1}%){:>14}", + label, + to_readable_str(size), + percent(size, total_size), + to_readable_str(subnode.count), + ); + } + } } eprintln!("----------------------------------------------------------------"); eprintln!("{:<18}{:>10}\n", "Total", to_readable_str(total_size)); @@ -268,14 +320,54 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { } } +// Used to avoid boilerplate for types with many variants. +macro_rules! record_variants { + ( + ($self:ident, $val:expr, $kind:expr, $ty:ty, $tykind:ident), // mandatory pieces + [$($variant:ident),*] + ) => { + match $kind { + $( + ast::$tykind::$variant { .. } => { + $self.record_variant(stringify!($ty), stringify!($variant), Id::None, $val) + } + )* + } + }; +} + impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { fn visit_foreign_item(&mut self, i: &'v ast::ForeignItem) { - self.record("ForeignItem", Id::None, i); + record_variants!( + (self, i, i.kind, ForeignItem, ForeignItemKind), + [Static, Fn, TyAlias, MacCall] + ); ast_visit::walk_foreign_item(self, i) } fn visit_item(&mut self, i: &'v ast::Item) { - self.record("Item", Id::None, i); + record_variants!( + (self, i, i.kind, Item, ItemKind), + [ + ExternCrate, + Use, + Static, + Const, + Fn, + Mod, + ForeignMod, + GlobalAsm, + TyAlias, + Enum, + Struct, + Union, + Trait, + TraitAlias, + Impl, + MacCall, + MacroDef + ] + ); ast_visit::walk_item(self, i) } @@ -290,7 +382,10 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { } fn visit_stmt(&mut self, s: &'v ast::Stmt) { - self.record("Stmt", Id::None, s); + record_variants!( + (self, s, s.kind, Stmt, StmtKind), + [Local, Item, Expr, Semi, Empty, MacCall] + ); ast_visit::walk_stmt(self, s) } @@ -305,17 +400,66 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { } fn visit_pat(&mut self, p: &'v ast::Pat) { - self.record("Pat", Id::None, p); + record_variants!( + (self, p, p.kind, Pat, PatKind), + [ + Wild, + Ident, + Struct, + TupleStruct, + Or, + Path, + Tuple, + Box, + Ref, + Lit, + Range, + Slice, + Rest, + Paren, + MacCall + ] + ); ast_visit::walk_pat(self, p) } - fn visit_expr(&mut self, ex: &'v ast::Expr) { - self.record("Expr", Id::None, ex); - ast_visit::walk_expr(self, ex) + fn visit_expr(&mut self, e: &'v ast::Expr) { + record_variants!( + (self, e, e.kind, Expr, ExprKind), + [ + Box, Array, ConstBlock, Call, MethodCall, Tup, Binary, Unary, Lit, Cast, Type, Let, + If, While, ForLoop, Loop, Match, Closure, Block, Async, Await, TryBlock, Assign, + AssignOp, Field, Index, Range, Underscore, Path, AddrOf, Break, Continue, Ret, + InlineAsm, MacCall, Struct, Repeat, Paren, Try, Yield, Yeet, Err + ] + ); + ast_visit::walk_expr(self, e) } fn visit_ty(&mut self, t: &'v ast::Ty) { - self.record("Ty", Id::None, t); + record_variants!( + (self, t, t.kind, Ty, TyKind), + [ + Slice, + Array, + Ptr, + Rptr, + BareFn, + Never, + Tup, + Path, + TraitObject, + ImplTrait, + Paren, + Typeof, + Infer, + ImplicitSelf, + MacCall, + Err, + CVarArgs + ] + ); + ast_visit::walk_ty(self, t) } @@ -325,7 +469,10 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { } fn visit_where_predicate(&mut self, p: &'v ast::WherePredicate) { - self.record("WherePredicate", Id::None, p); + record_variants!( + (self, p, p, WherePredicate, WherePredicate), + [BoundPredicate, RegionPredicate, EqPredicate] + ); ast_visit::walk_where_predicate(self, p) } @@ -334,14 +481,17 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { ast_visit::walk_fn(self, fk, s) } - fn visit_assoc_item(&mut self, item: &'v ast::AssocItem, ctxt: ast_visit::AssocCtxt) { - self.record("AssocItem", Id::None, item); - ast_visit::walk_assoc_item(self, item, ctxt); + fn visit_assoc_item(&mut self, i: &'v ast::AssocItem, ctxt: ast_visit::AssocCtxt) { + record_variants!( + (self, i, i.kind, AssocItem, AssocItemKind), + [Const, Fn, TyAlias, MacCall] + ); + ast_visit::walk_assoc_item(self, i, ctxt); } - fn visit_param_bound(&mut self, bounds: &'v ast::GenericBound, _ctxt: BoundKind) { - self.record("GenericBound", Id::None, bounds); - ast_visit::walk_param_bound(self, bounds) + fn visit_param_bound(&mut self, b: &'v ast::GenericBound, _ctxt: BoundKind) { + record_variants!((self, b, b, GenericBound, GenericBound), [Trait, Outlives]); + ast_visit::walk_param_bound(self, b) } fn visit_field_def(&mut self, s: &'v ast::FieldDef) { @@ -369,12 +519,12 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { // common, so we implement `visit_generic_args` and tolerate the double // counting in the former case. fn visit_generic_args(&mut self, sp: Span, g: &'v ast::GenericArgs) { - self.record("GenericArgs", Id::None, g); + record_variants!((self, g, g, GenericArgs, GenericArgs), [AngleBracketed, Parenthesized]); ast_visit::walk_generic_args(self, sp, g) } fn visit_attribute(&mut self, attr: &'v ast::Attribute) { - self.record("Attribute", Id::None, attr); + record_variants!((self, attr, attr.kind, Attribute, AttrKind), [Normal, DocComment]); ast_visit::walk_attribute(self, attr) } diff --git a/src/test/ui/stats/hir-stats.rs b/src/test/ui/stats/hir-stats.rs index 3c59ee22f24c5..a24b3ada57e59 100644 --- a/src/test/ui/stats/hir-stats.rs +++ b/src/test/ui/stats/hir-stats.rs @@ -2,8 +2,8 @@ // compile-flags: -Zhir-stats // only-x86_64 -// The aim here is to include at least one of every different type of AST/HIR -// node reported by `-Zhir-stats`. +// The aim here is to include at least one of every different type of top-level +// AST/HIR node reported by `-Zhir-stats`. #![allow(dead_code)] diff --git a/src/test/ui/stats/hir-stats.stderr b/src/test/ui/stats/hir-stats.stderr index 132fff1972f4f..f4874408c9094 100644 --- a/src/test/ui/stats/hir-stats.stderr +++ b/src/test/ui/stats/hir-stats.stderr @@ -5,26 +5,55 @@ Name Accumulated Size Count Item Size ---------------------------------------------------------------- ExprField 48 ( 0.5%) 1 48 GenericArgs 64 ( 0.7%) 1 64 +- AngleBracketed 64 ( 0.7%) 1 Local 72 ( 0.8%) 1 72 WherePredicate 72 ( 0.8%) 1 72 +- BoundPredicate 72 ( 0.8%) 1 Crate 72 ( 0.8%) 1 72 Arm 96 ( 1.0%) 2 48 FieldDef 160 ( 1.7%) 2 80 ForeignItem 160 ( 1.7%) 1 160 +- Fn 160 ( 1.7%) 1 Stmt 160 ( 1.7%) 5 32 +- Local 32 ( 0.3%) 1 +- MacCall 32 ( 0.3%) 1 +- Expr 96 ( 1.0%) 3 Param 160 ( 1.7%) 4 40 FnDecl 200 ( 2.2%) 5 40 Variant 240 ( 2.6%) 2 120 Block 288 ( 3.1%) 6 48 Attribute 304 ( 3.3%) 2 152 +- Normal 152 ( 1.7%) 1 +- DocComment 152 ( 1.7%) 1 GenericBound 352 ( 3.8%) 4 88 +- Trait 352 ( 3.8%) 4 GenericParam 520 ( 5.7%) 5 104 AssocItem 640 ( 7.0%) 4 160 +- TyAlias 320 ( 3.5%) 2 +- Fn 320 ( 3.5%) 2 PathSegment 720 ( 7.9%) 30 24 Expr 832 ( 9.1%) 8 104 +- Path 104 ( 1.1%) 1 +- Match 104 ( 1.1%) 1 +- Struct 104 ( 1.1%) 1 +- Lit 208 ( 2.3%) 2 +- Block 312 ( 3.4%) 3 Pat 840 ( 9.2%) 7 120 +- Struct 120 ( 1.3%) 1 +- Wild 120 ( 1.3%) 1 +- Ident 600 ( 6.6%) 5 Ty 1_344 (14.7%) 14 96 +- Rptr 96 ( 1.0%) 1 +- Ptr 96 ( 1.0%) 1 +- ImplicitSelf 192 ( 2.1%) 2 +- Path 960 (10.5%) 10 Item 1_800 (19.7%) 9 200 +- Trait 200 ( 2.2%) 1 +- Enum 200 ( 2.2%) 1 +- ForeignMod 200 ( 2.2%) 1 +- Impl 200 ( 2.2%) 1 +- Fn 400 ( 4.4%) 2 +- Use 600 ( 6.6%) 3 ---------------------------------------------------------------- Total 9_144 @@ -35,27 +64,58 @@ Name Accumulated Size Count Item Size ---------------------------------------------------------------- ExprField 48 ( 0.5%) 1 48 GenericArgs 64 ( 0.6%) 1 64 +- AngleBracketed 64 ( 0.6%) 1 Local 72 ( 0.7%) 1 72 WherePredicate 72 ( 0.7%) 1 72 +- BoundPredicate 72 ( 0.7%) 1 Crate 72 ( 0.7%) 1 72 Arm 96 ( 0.9%) 2 48 InlineAsm 120 ( 1.2%) 1 120 FieldDef 160 ( 1.6%) 2 80 ForeignItem 160 ( 1.6%) 1 160 +- Fn 160 ( 1.6%) 1 Stmt 160 ( 1.6%) 5 32 +- Local 32 ( 0.3%) 1 +- Semi 32 ( 0.3%) 1 +- Expr 96 ( 0.9%) 3 Param 160 ( 1.6%) 4 40 FnDecl 200 ( 2.0%) 5 40 Variant 240 ( 2.4%) 2 120 Block 288 ( 2.8%) 6 48 GenericBound 352 ( 3.5%) 4 88 +- Trait 352 ( 3.5%) 4 GenericParam 520 ( 5.1%) 5 104 Attribute 608 ( 6.0%) 4 152 +- DocComment 152 ( 1.5%) 1 +- Normal 456 ( 4.5%) 3 AssocItem 640 ( 6.3%) 4 160 +- TyAlias 320 ( 3.2%) 2 +- Fn 320 ( 3.2%) 2 PathSegment 792 ( 7.8%) 33 24 Pat 840 ( 8.3%) 7 120 +- Struct 120 ( 1.2%) 1 +- Wild 120 ( 1.2%) 1 +- Ident 600 ( 5.9%) 5 Expr 936 ( 9.2%) 9 104 +- Path 104 ( 1.0%) 1 +- Match 104 ( 1.0%) 1 +- Struct 104 ( 1.0%) 1 +- InlineAsm 104 ( 1.0%) 1 +- Lit 208 ( 2.1%) 2 +- Block 312 ( 3.1%) 3 Ty 1_344 (13.2%) 14 96 +- Rptr 96 ( 0.9%) 1 +- Ptr 96 ( 0.9%) 1 +- ImplicitSelf 192 ( 1.9%) 2 +- Path 960 ( 9.5%) 10 Item 2_200 (21.7%) 11 200 +- Trait 200 ( 2.0%) 1 +- Enum 200 ( 2.0%) 1 +- ExternCrate 200 ( 2.0%) 1 +- ForeignMod 200 ( 2.0%) 1 +- Impl 200 ( 2.0%) 1 +- Fn 400 ( 3.9%) 2 +- Use 800 ( 7.9%) 4 ---------------------------------------------------------------- Total 10_144