320 lines
12 KiB
Rust
320 lines
12 KiB
Rust
|
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||
|
// file at the top-level directory of this distribution and at
|
||
|
// http://rust-lang.org/COPYRIGHT.
|
||
|
//
|
||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||
|
// option. This file may not be copied, modified, or distributed
|
||
|
// except according to those terms.
|
||
|
|
||
|
// The visitors in this module collect sizes and counts of the most important
|
||
|
// pieces of MIR. The resulting numbers are good approximations but not
|
||
|
// completely accurate (some things might be counted twice, others missed).
|
||
|
|
||
|
use rustc_const_math::{ConstUsize};
|
||
|
use rustc::middle::const_val::{ConstVal};
|
||
|
use rustc::mir::{AggregateKind, AssertMessage, BasicBlock, BasicBlockData};
|
||
|
use rustc::mir::{Constant, Literal, Location, LocalDecl};
|
||
|
use rustc::mir::{Lvalue, LvalueElem, LvalueProjection};
|
||
|
use rustc::mir::{Mir, Operand, ProjectionElem};
|
||
|
use rustc::mir::{Rvalue, SourceInfo, Statement, StatementKind};
|
||
|
use rustc::mir::{Terminator, TerminatorKind, TypedConstVal, VisibilityScope, VisibilityScopeData};
|
||
|
use rustc::mir::visit as mir_visit;
|
||
|
use rustc::mir::visit::Visitor;
|
||
|
use rustc::ty::{ClosureSubsts, TyCtxt};
|
||
|
use rustc::util::common::to_readable_str;
|
||
|
use rustc::util::nodemap::{FxHashMap};
|
||
|
|
||
|
struct NodeData {
|
||
|
count: usize,
|
||
|
size: usize,
|
||
|
}
|
||
|
|
||
|
struct StatCollector<'a, 'tcx: 'a> {
|
||
|
_tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||
|
data: FxHashMap<&'static str, NodeData>,
|
||
|
}
|
||
|
|
||
|
pub fn print_mir_stats<'tcx, 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>, title: &str) {
|
||
|
let mut collector = StatCollector {
|
||
|
_tcx: tcx,
|
||
|
data: FxHashMap(),
|
||
|
};
|
||
|
// For debugging instrumentation like this, we don't need to worry
|
||
|
// about maintaining the dep graph.
|
||
|
let _ignore = tcx.dep_graph.in_ignore();
|
||
|
let mir_map = tcx.mir_map.borrow();
|
||
|
for def_id in mir_map.keys() {
|
||
|
let mir = mir_map.get(&def_id).unwrap();
|
||
|
collector.visit_mir(&mir.borrow());
|
||
|
}
|
||
|
collector.print(title);
|
||
|
}
|
||
|
|
||
|
impl<'a, 'tcx> StatCollector<'a, 'tcx> {
|
||
|
|
||
|
fn record_with_size(&mut self, label: &'static str, node_size: usize) {
|
||
|
let entry = self.data.entry(label).or_insert(NodeData {
|
||
|
count: 0,
|
||
|
size: 0,
|
||
|
});
|
||
|
|
||
|
entry.count += 1;
|
||
|
entry.size = node_size;
|
||
|
}
|
||
|
|
||
|
fn record<T>(&mut self, label: &'static str, node: &T) {
|
||
|
self.record_with_size(label, ::std::mem::size_of_val(node));
|
||
|
}
|
||
|
|
||
|
fn print(&self, title: &str) {
|
||
|
let mut stats: Vec<_> = self.data.iter().collect();
|
||
|
|
||
|
stats.sort_by_key(|&(_, ref d)| d.count * d.size);
|
||
|
|
||
|
println!("\n{}\n", title);
|
||
|
|
||
|
println!("{:<32}{:>18}{:>14}{:>14}",
|
||
|
"Name", "Accumulated Size", "Count", "Item Size");
|
||
|
println!("------------------------------------------------------------------------------");
|
||
|
|
||
|
for (label, data) in stats {
|
||
|
println!("{:<32}{:>18}{:>14}{:>14}",
|
||
|
label,
|
||
|
to_readable_str(data.count * data.size),
|
||
|
to_readable_str(data.count),
|
||
|
to_readable_str(data.size));
|
||
|
}
|
||
|
println!("------------------------------------------------------------------------------");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl<'a, 'tcx> mir_visit::Visitor<'tcx> for StatCollector<'a, 'tcx> {
|
||
|
fn visit_mir(&mut self, mir: &Mir<'tcx>) {
|
||
|
self.record("Mir", mir);
|
||
|
|
||
|
// since the `super_mir` method does not traverse the MIR of
|
||
|
// promoted rvalues, (but we still want to gather statistics
|
||
|
// on the structures represented there) we manually traverse
|
||
|
// the promoted rvalues here.
|
||
|
for promoted_mir in &mir.promoted {
|
||
|
self.visit_mir(promoted_mir);
|
||
|
}
|
||
|
|
||
|
self.super_mir(mir);
|
||
|
}
|
||
|
|
||
|
fn visit_basic_block_data(&mut self,
|
||
|
block: BasicBlock,
|
||
|
data: &BasicBlockData<'tcx>) {
|
||
|
self.record("BasicBlockData", data);
|
||
|
self.super_basic_block_data(block, data);
|
||
|
}
|
||
|
|
||
|
fn visit_visibility_scope_data(&mut self,
|
||
|
scope_data: &VisibilityScopeData) {
|
||
|
self.record("VisibilityScopeData", scope_data);
|
||
|
self.super_visibility_scope_data(scope_data);
|
||
|
}
|
||
|
|
||
|
fn visit_statement(&mut self,
|
||
|
block: BasicBlock,
|
||
|
statement: &Statement<'tcx>,
|
||
|
location: Location) {
|
||
|
self.record("Statement", statement);
|
||
|
self.record(match statement.kind {
|
||
|
StatementKind::Assign(..) => "StatementKind::Assign",
|
||
|
StatementKind::SetDiscriminant { .. } => "StatementKind::SetDiscriminant",
|
||
|
StatementKind::StorageLive(..) => "StatementKind::StorageLive",
|
||
|
StatementKind::StorageDead(..) => "StatementKind::StorageDead",
|
||
|
StatementKind::Nop => "StatementKind::Nop",
|
||
|
}, &statement.kind);
|
||
|
self.super_statement(block, statement, location);
|
||
|
}
|
||
|
|
||
|
fn visit_terminator(&mut self,
|
||
|
block: BasicBlock,
|
||
|
terminator: &Terminator<'tcx>,
|
||
|
location: Location) {
|
||
|
self.record("Terminator", terminator);
|
||
|
self.super_terminator(block, terminator, location);
|
||
|
}
|
||
|
|
||
|
fn visit_terminator_kind(&mut self,
|
||
|
block: BasicBlock,
|
||
|
kind: &TerminatorKind<'tcx>,
|
||
|
location: Location) {
|
||
|
self.record("TerminatorKind", kind);
|
||
|
self.record(match *kind {
|
||
|
TerminatorKind::Goto { .. } => "TerminatorKind::Goto",
|
||
|
TerminatorKind::If { .. } => "TerminatorKind::If",
|
||
|
TerminatorKind::Switch { .. } => "TerminatorKind::Switch",
|
||
|
TerminatorKind::SwitchInt { .. } => "TerminatorKind::SwitchInt",
|
||
|
TerminatorKind::Resume => "TerminatorKind::Resume",
|
||
|
TerminatorKind::Return => "TerminatorKind::Return",
|
||
|
TerminatorKind::Unreachable => "TerminatorKind::Unreachable",
|
||
|
TerminatorKind::Drop { .. } => "TerminatorKind::Drop",
|
||
|
TerminatorKind::DropAndReplace { .. } => "TerminatorKind::DropAndReplace",
|
||
|
TerminatorKind::Call { .. } => "TerminatorKind::Call",
|
||
|
TerminatorKind::Assert { .. } => "TerminatorKind::Assert",
|
||
|
}, kind);
|
||
|
self.super_terminator_kind(block, kind, location);
|
||
|
}
|
||
|
|
||
|
fn visit_assert_message(&mut self,
|
||
|
msg: &AssertMessage<'tcx>,
|
||
|
location: Location) {
|
||
|
self.record("AssertMessage", msg);
|
||
|
self.record(match *msg {
|
||
|
AssertMessage::BoundsCheck { .. } => "AssertMessage::BoundsCheck",
|
||
|
AssertMessage::Math(..) => "AssertMessage::Math",
|
||
|
}, msg);
|
||
|
self.super_assert_message(msg, location);
|
||
|
}
|
||
|
|
||
|
fn visit_rvalue(&mut self,
|
||
|
rvalue: &Rvalue<'tcx>,
|
||
|
location: Location) {
|
||
|
self.record("Rvalue", rvalue);
|
||
|
let rvalue_kind = match *rvalue {
|
||
|
Rvalue::Use(..) => "Rvalue::Use",
|
||
|
Rvalue::Repeat(..) => "Rvalue::Repeat",
|
||
|
Rvalue::Ref(..) => "Rvalue::Ref",
|
||
|
Rvalue::Len(..) => "Rvalue::Len",
|
||
|
Rvalue::Cast(..) => "Rvalue::Cast",
|
||
|
Rvalue::BinaryOp(..) => "Rvalue::BinaryOp",
|
||
|
Rvalue::CheckedBinaryOp(..) => "Rvalue::CheckedBinaryOp",
|
||
|
Rvalue::UnaryOp(..) => "Rvalue::UnaryOp",
|
||
|
Rvalue::Box(..) => "Rvalue::Box",
|
||
|
Rvalue::Aggregate(ref kind, ref _operands) => {
|
||
|
// AggregateKind is not distinguished by visit API, so
|
||
|
// record it. (`super_rvalue` handles `_operands`.)
|
||
|
self.record(match *kind {
|
||
|
AggregateKind::Array => "AggregateKind::Array",
|
||
|
AggregateKind::Tuple => "AggregateKind::Tuple",
|
||
|
AggregateKind::Adt(..) => "AggregateKind::Adt",
|
||
|
AggregateKind::Closure(..) => "AggregateKind::Closure",
|
||
|
}, kind);
|
||
|
|
||
|
"Rvalue::Aggregate"
|
||
|
}
|
||
|
Rvalue::InlineAsm { .. } => "Rvalue::InlineAsm",
|
||
|
};
|
||
|
self.record(rvalue_kind, rvalue);
|
||
|
self.super_rvalue(rvalue, location);
|
||
|
}
|
||
|
|
||
|
fn visit_operand(&mut self,
|
||
|
operand: &Operand<'tcx>,
|
||
|
location: Location) {
|
||
|
self.record("Operand", operand);
|
||
|
self.record(match *operand {
|
||
|
Operand::Consume(..) => "Operand::Consume",
|
||
|
Operand::Constant(..) => "Operand::Constant",
|
||
|
}, operand);
|
||
|
self.super_operand(operand, location);
|
||
|
}
|
||
|
|
||
|
fn visit_lvalue(&mut self,
|
||
|
lvalue: &Lvalue<'tcx>,
|
||
|
context: mir_visit::LvalueContext<'tcx>,
|
||
|
location: Location) {
|
||
|
self.record("Lvalue", lvalue);
|
||
|
self.record(match *lvalue {
|
||
|
Lvalue::Local(..) => "Lvalue::Local",
|
||
|
Lvalue::Static(..) => "Lvalue::Static",
|
||
|
Lvalue::Projection(..) => "Lvalue::Projection",
|
||
|
}, lvalue);
|
||
|
self.super_lvalue(lvalue, context, location);
|
||
|
}
|
||
|
|
||
|
fn visit_projection(&mut self,
|
||
|
lvalue: &LvalueProjection<'tcx>,
|
||
|
context: mir_visit::LvalueContext<'tcx>,
|
||
|
location: Location) {
|
||
|
self.record("LvalueProjection", lvalue);
|
||
|
self.super_projection(lvalue, context, location);
|
||
|
}
|
||
|
|
||
|
fn visit_projection_elem(&mut self,
|
||
|
lvalue: &LvalueElem<'tcx>,
|
||
|
context: mir_visit::LvalueContext<'tcx>,
|
||
|
location: Location) {
|
||
|
self.record("LvalueElem", lvalue);
|
||
|
self.record(match *lvalue {
|
||
|
ProjectionElem::Deref => "LvalueElem::Deref",
|
||
|
ProjectionElem::Subslice { .. } => "LvalueElem::Subslice",
|
||
|
ProjectionElem::Field(..) => "LvalueElem::Field",
|
||
|
ProjectionElem::Index(..) => "LvalueElem::Index",
|
||
|
ProjectionElem::ConstantIndex { .. } => "LvalueElem::ConstantIndex",
|
||
|
ProjectionElem::Downcast(..) => "LvalueElem::Downcast",
|
||
|
}, lvalue);
|
||
|
self.super_projection_elem(lvalue, context, location);
|
||
|
}
|
||
|
|
||
|
fn visit_constant(&mut self,
|
||
|
constant: &Constant<'tcx>,
|
||
|
location: Location) {
|
||
|
self.record("Constant", constant);
|
||
|
self.super_constant(constant, location);
|
||
|
}
|
||
|
|
||
|
fn visit_literal(&mut self,
|
||
|
literal: &Literal<'tcx>,
|
||
|
location: Location) {
|
||
|
self.record("Literal", literal);
|
||
|
self.record(match *literal {
|
||
|
Literal::Item { .. } => "Literal::Item",
|
||
|
Literal::Value { .. } => "Literal::Value",
|
||
|
Literal::Promoted { .. } => "Literal::Promoted",
|
||
|
}, literal);
|
||
|
self.super_literal(literal, location);
|
||
|
}
|
||
|
|
||
|
fn visit_source_info(&mut self,
|
||
|
source_info: &SourceInfo) {
|
||
|
self.record("SourceInfo", source_info);
|
||
|
self.super_source_info(source_info);
|
||
|
}
|
||
|
|
||
|
fn visit_closure_substs(&mut self,
|
||
|
substs: &ClosureSubsts<'tcx>) {
|
||
|
self.record("ClosureSubsts", substs);
|
||
|
self.super_closure_substs(substs);
|
||
|
}
|
||
|
|
||
|
fn visit_const_val(&mut self,
|
||
|
const_val: &ConstVal,
|
||
|
_: Location) {
|
||
|
self.record("ConstVal", const_val);
|
||
|
self.super_const_val(const_val);
|
||
|
}
|
||
|
|
||
|
fn visit_const_usize(&mut self,
|
||
|
const_usize: &ConstUsize,
|
||
|
_: Location) {
|
||
|
self.record("ConstUsize", const_usize);
|
||
|
self.super_const_usize(const_usize);
|
||
|
}
|
||
|
|
||
|
fn visit_typed_const_val(&mut self,
|
||
|
val: &TypedConstVal<'tcx>,
|
||
|
location: Location) {
|
||
|
self.record("TypedConstVal", val);
|
||
|
self.super_typed_const_val(val, location);
|
||
|
}
|
||
|
|
||
|
fn visit_local_decl(&mut self,
|
||
|
local_decl: &LocalDecl<'tcx>) {
|
||
|
self.record("LocalDecl", local_decl);
|
||
|
self.super_local_decl(local_decl);
|
||
|
}
|
||
|
|
||
|
fn visit_visibility_scope(&mut self,
|
||
|
scope: &VisibilityScope) {
|
||
|
self.record("VisiblityScope", scope);
|
||
|
self.super_visibility_scope(scope);
|
||
|
}
|
||
|
}
|