diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 79c0ad0d242..130f598d7f4 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -926,6 +926,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "print some performance-related statistics"), hir_stats: bool = (false, parse_bool, [UNTRACKED], "print some statistics about AST and HIR"), + mir_stats: bool = (false, parse_bool, [UNTRACKED], + "print some statistics about MIR"), } pub fn default_lib_output() -> CrateType { diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 069f0a89bef..8d995e97b95 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -37,7 +37,7 @@ use rustc_privacy; use rustc_plugin::registry::Registry; use rustc_plugin as plugin; use rustc_passes::{ast_validation, no_asm, loops, consts, rvalues, - static_recursion, hir_stats}; + static_recursion, hir_stats, mir_stats}; use rustc_const_eval::check_match; use super::Compilation; @@ -932,6 +932,10 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, "MIR dump", || mir::mir_map::build_mir_for_crate(tcx)); + if sess.opts.debugging_opts.mir_stats { + mir_stats::print_mir_stats(tcx, "PRE CLEANUP MIR STATS"); + } + time(time_passes, "MIR cleanup and validation", || { let mut passes = sess.mir_passes.borrow_mut(); // Push all the built-in validation passes. @@ -1000,6 +1004,10 @@ pub fn phase_4_translate_to_llvm<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, "resolving dependency formats", || dependency_format::calculate(&tcx.sess)); + if tcx.sess.opts.debugging_opts.mir_stats { + mir_stats::print_mir_stats(tcx, "PRE OPTIMISATION MIR STATS"); + } + // Run the passes that transform the MIR into a more suitable form for translation to LLVM // code. time(time_passes, "MIR optimisations", || { @@ -1028,6 +1036,10 @@ pub fn phase_4_translate_to_llvm<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, passes.run_passes(tcx); }); + if tcx.sess.opts.debugging_opts.mir_stats { + mir_stats::print_mir_stats(tcx, "POST OPTIMISATION MIR STATS"); + } + let translation = time(time_passes, "translation", diff --git a/src/librustc_passes/lib.rs b/src/librustc_passes/lib.rs index 525d49ddd82..670ef426c2d 100644 --- a/src/librustc_passes/lib.rs +++ b/src/librustc_passes/lib.rs @@ -46,6 +46,7 @@ pub mod ast_validation; pub mod consts; pub mod hir_stats; pub mod loops; +pub mod mir_stats; pub mod no_asm; pub mod rvalues; pub mod static_recursion; diff --git a/src/librustc_passes/mir_stats.rs b/src/librustc_passes/mir_stats.rs new file mode 100644 index 00000000000..cec1c20519b --- /dev/null +++ b/src/librustc_passes/mir_stats.rs @@ -0,0 +1,319 @@ +// 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 or the MIT license +// , 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(&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); + } +}