Skip to content

Commit 4f8a170

Browse files
committed
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.
1 parent 85c7492 commit 4f8a170

File tree

3 files changed

+246
-36
lines changed

3 files changed

+246
-36
lines changed

compiler/rustc_passes/src/hir_stats.rs

Lines changed: 184 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
4663
struct 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

5269
pub 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

7289
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+
) {
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+
271339
impl<'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

src/test/ui/stats/hir-stats.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
// compile-flags: -Zhir-stats
33
// only-x86_64
44

5-
// The aim here is to include at least one of every different type of AST/HIR
6-
// node reported by `-Zhir-stats`.
5+
// The aim here is to include at least one of every different type of top-level
6+
// AST/HIR node reported by `-Zhir-stats`.
77

88
#![allow(dead_code)]
99

0 commit comments

Comments
 (0)