| // 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); |
| } |
| } |