2016-11-30 18:14:17 +01:00
|
|
|
// 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::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};
|
2017-02-26 03:05:02 +02:00
|
|
|
use rustc::mir::{Terminator, TerminatorKind, VisibilityScope, VisibilityScopeData};
|
2016-11-30 18:14:17 +01:00
|
|
|
use rustc::mir::visit as mir_visit;
|
2017-08-04 11:25:13 +03:00
|
|
|
use rustc::ty::{self, ClosureSubsts, TyCtxt};
|
2016-11-30 18:14:17 +01:00
|
|
|
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>,
|
|
|
|
}
|
|
|
|
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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",
|
Add `EndRegion` statement kind to MIR.
* Emit `EndRegion` for every code-extent for which we observe a
borrow. To do this, we needed to thread source info back through
to `fn in_scope`, which makes this commit a bit more painful than
one might have expected.
* There is `end_region` emission in `Builder::pop_scope` and in
`Builder::exit_scope`; the first handles falling out of a scope
normally, the second handles e.g. `break`.
* Remove `EndRegion` statements during the erase_regions mir
transformation.
* Preallocate the terminator block, and throw an `Unreachable` marker
on it from the outset. Then overwrite that Terminator as necessary
on demand.
* Instead of marking the scope as needs_cleanup after seeing a
borrow, just treat every scope in the chain as being part of the
diverge_block (after any *one* of them has separately signalled
that it needs cleanup, e.g. due to having a destructor to run).
* Allow for resume terminators to be patched when looking up drop flags.
(In particular, `MirPatch::new` has an explicit code path,
presumably previously unreachable, that patches up such resume
terminators.)
* Make `Scope` implement `Debug` trait.
* Expanded a stray comment: we do not emit StorageDead on diverging
paths, but that end behavior might not be desirable.
2017-02-17 13:38:42 +01:00
|
|
|
StatementKind::EndRegion(..) => "StatementKind::EndRegion",
|
2017-07-11 14:01:07 -07:00
|
|
|
StatementKind::Validate(..) => "StatementKind::Validate",
|
2016-11-30 18:14:17 +01:00
|
|
|
StatementKind::SetDiscriminant { .. } => "StatementKind::SetDiscriminant",
|
|
|
|
StatementKind::StorageLive(..) => "StatementKind::StorageLive",
|
|
|
|
StatementKind::StorageDead(..) => "StatementKind::StorageDead",
|
2017-02-15 21:21:36 +02:00
|
|
|
StatementKind::InlineAsm { .. } => "StatementKind::InlineAsm",
|
2016-11-30 18:14:17 +01:00
|
|
|
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::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",
|
2016-12-26 14:34:03 +01:00
|
|
|
TerminatorKind::GeneratorDrop => "TerminatorKind::GeneratorDrop",
|
2017-07-10 21:11:31 +02:00
|
|
|
TerminatorKind::Yield { .. } => "TerminatorKind::Yield",
|
2016-11-30 18:14:17 +01:00
|
|
|
}, 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",
|
2017-07-05 14:57:26 -07:00
|
|
|
AssertMessage::GeneratorResumedAfterReturn => {
|
|
|
|
"AssertMessage::GeneratorResumedAfterReturn"
|
|
|
|
}
|
|
|
|
AssertMessage::GeneratorResumedAfterPanic => {
|
|
|
|
"AssertMessage::GeneratorResumedAfterPanic"
|
|
|
|
}
|
2016-11-30 18:14:17 +01:00
|
|
|
}, 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",
|
2017-01-31 01:10:54 +02:00
|
|
|
Rvalue::Discriminant(..) => "Rvalue::Discriminant",
|
2017-05-18 18:43:52 +03:00
|
|
|
Rvalue::NullaryOp(..) => "Rvalue::NullaryOp",
|
2016-11-30 18:14:17 +01:00
|
|
|
Rvalue::Aggregate(ref kind, ref _operands) => {
|
|
|
|
// AggregateKind is not distinguished by visit API, so
|
|
|
|
// record it. (`super_rvalue` handles `_operands`.)
|
2017-05-12 01:38:26 +03:00
|
|
|
self.record(match **kind {
|
2017-02-26 00:32:14 +02:00
|
|
|
AggregateKind::Array(_) => "AggregateKind::Array",
|
2016-11-30 18:14:17 +01:00
|
|
|
AggregateKind::Tuple => "AggregateKind::Tuple",
|
|
|
|
AggregateKind::Adt(..) => "AggregateKind::Adt",
|
|
|
|
AggregateKind::Closure(..) => "AggregateKind::Closure",
|
2016-12-26 14:34:03 +01:00
|
|
|
AggregateKind::Generator(..) => "AggregateKind::Generator",
|
2016-11-30 18:14:17 +01:00
|
|
|
}, kind);
|
|
|
|
|
|
|
|
"Rvalue::Aggregate"
|
|
|
|
}
|
|
|
|
};
|
|
|
|
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::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,
|
2017-07-18 22:31:38 -04:00
|
|
|
substs: &ClosureSubsts<'tcx>,
|
|
|
|
_: Location) {
|
2016-11-30 18:14:17 +01:00
|
|
|
self.record("ClosureSubsts", substs);
|
|
|
|
self.super_closure_substs(substs);
|
|
|
|
}
|
|
|
|
|
2017-08-04 11:25:13 +03:00
|
|
|
fn visit_const(&mut self,
|
|
|
|
constant: &&'tcx ty::Const<'tcx>,
|
|
|
|
_: Location) {
|
|
|
|
self.record("Const", constant);
|
|
|
|
self.super_const(constant);
|
2016-11-30 18:14:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fn visit_const_usize(&mut self,
|
|
|
|
const_usize: &ConstUsize,
|
|
|
|
_: Location) {
|
|
|
|
self.record("ConstUsize", const_usize);
|
|
|
|
self.super_const_usize(const_usize);
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|