about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAman Arora <me@aman-arora.com>2021-05-05 15:57:08 -0400
committerAman Arora <me@aman-arora.com>2021-06-28 14:21:55 -0400
commitfc273e9bf2ab4594e8dcdb737b63bffb3f6b08c9 (patch)
tree5d4c81800d048385b8b3d24b8f70bad4ad03bdae
parent3e9d7ecf784e5ecaf7437d04be3992ad23fa7cb6 (diff)
downloadrust-fc273e9bf2ab4594e8dcdb737b63bffb3f6b08c9.tar.gz
rust-fc273e9bf2ab4594e8dcdb737b63bffb3f6b08c9.zip
Introduce -Zprofile-closures to evaluate the impact of 2229
This creates a CSV with name "closure_profile_XXXXX.csv", where the
variable part is the process id of the compiler.

To profile a cargo project you can run one of the following depending on
if you're compiling a library or a binary:

```
cargo +stage1 rustc --lib -- -Zprofile-closures
cargo +stage1 rustc --bin -- -Zprofile-closures
```
-rw-r--r--compiler/rustc_interface/src/tests.rs1
-rw-r--r--compiler/rustc_middle/src/ty/context.rs18
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs19
-rw-r--r--compiler/rustc_mir/src/monomorphize/collector.rs7
-rw-r--r--compiler/rustc_mir/src/monomorphize/mod.rs1
-rw-r--r--compiler/rustc_mir/src/monomorphize/util.rs73
-rw-r--r--compiler/rustc_session/src/options.rs2
-rw-r--r--compiler/rustc_typeck/src/check/upvar.rs18
-rw-r--r--compiler/rustc_typeck/src/check/writeback.rs16
9 files changed, 148 insertions, 7 deletions
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index ebd18630e4e..b252409a92a 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -654,6 +654,7 @@ fn test_debugging_options_tracking_hash() {
     untracked!(perf_stats, true);
     // `pre_link_arg` is omitted because it just forwards to `pre_link_args`.
     untracked!(pre_link_args, vec![String::from("abc"), String::from("def")]);
+    untracked!(profile_closures, true);
     untracked!(print_link_args, true);
     untracked!(print_llvm_passes, true);
     untracked!(print_mono_items, Some(String::from("abc")));
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 77f955e93b9..6e910d0087f 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -19,11 +19,12 @@ use crate::ty::query::{self, OnDiskCache, TyCtxtAt};
 use crate::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, Subst, SubstsRef, UserSubsts};
 use crate::ty::TyKind::*;
 use crate::ty::{
-    self, AdtDef, AdtKind, Binder, BindingMode, BoundVar, CanonicalPolyFnSig, Const, ConstVid,
-    DefIdTree, ExistentialPredicate, FloatTy, FloatVar, FloatVid, GenericParamDefKind, InferConst,
-    InferTy, IntTy, IntVar, IntVid, List, MainDefinition, ParamConst, ParamTy, PolyFnSig,
-    Predicate, PredicateInner, PredicateKind, ProjectionTy, Region, RegionKind, ReprOptions,
-    TraitObjectVisitor, Ty, TyKind, TyS, TyVar, TyVid, TypeAndMut, UintTy, Visibility,
+    self, AdtDef, AdtKind, Binder, BindingMode, BoundVar, CanonicalPolyFnSig,
+    ClosureSizeProfileData, Const, ConstVid, DefIdTree, ExistentialPredicate, FloatTy, FloatVar,
+    FloatVid, GenericParamDefKind, InferConst, InferTy, IntTy, IntVar, IntVid, List,
+    MainDefinition, ParamConst, ParamTy, PolyFnSig, Predicate, PredicateInner, PredicateKind,
+    ProjectionTy, Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, TyS, TyVar,
+    TyVid, TypeAndMut, UintTy, Visibility,
 };
 use rustc_ast as ast;
 use rustc_ast::expand::allocator::AllocatorKind;
@@ -484,6 +485,10 @@ pub struct TypeckResults<'tcx> {
     /// This hashset records all instances where we behave
     /// like this to allow `const_to_pat` to reliably handle this situation.
     pub treat_byte_string_as_slice: ItemLocalSet,
+
+    /// Contains the data for evaluating the effect of feature `capture_disjoint_fields`
+    /// on closure size.
+    pub closure_size_eval: FxHashMap<DefId, ClosureSizeProfileData<'tcx>>,
 }
 
 impl<'tcx> TypeckResults<'tcx> {
@@ -510,6 +515,7 @@ impl<'tcx> TypeckResults<'tcx> {
             closure_fake_reads: Default::default(),
             generator_interior_types: ty::Binder::dummy(Default::default()),
             treat_byte_string_as_slice: Default::default(),
+            closure_size_eval: Default::default(),
         }
     }
 
@@ -754,6 +760,7 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for TypeckResults<'tcx> {
             ref closure_fake_reads,
             ref generator_interior_types,
             ref treat_byte_string_as_slice,
+            ref closure_size_eval,
         } = *self;
 
         hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| {
@@ -780,6 +787,7 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for TypeckResults<'tcx> {
             closure_fake_reads.hash_stable(hcx, hasher);
             generator_interior_types.hash_stable(hcx, hasher);
             treat_byte_string_as_slice.hash_stable(hcx, hasher);
+            closure_size_eval.hash_stable(hcx, hasher);
         })
     }
 }
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index a2abbec7492..dc5687de896 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -173,6 +173,25 @@ pub enum Visibility {
     Invisible,
 }
 
+#[derive(
+    Clone,
+    Debug,
+    PartialEq,
+    Eq,
+    Copy,
+    Hash,
+    TyEncodable,
+    TyDecodable,
+    HashStable,
+    TypeFoldable
+)]
+pub struct ClosureSizeProfileData<'tcx> {
+    /// Tuple containing the types of closure captures before the feature `capture_disjoint_fields`
+    pub before_feature_tys: Ty<'tcx>,
+    /// Tuple containing the types of closure captures after the feature `capture_disjoint_fields`
+    pub after_feature_tys: Ty<'tcx>,
+}
+
 pub trait DefIdTree: Copy {
     fn parent(self, id: DefId) -> Option<DefId>;
 
diff --git a/compiler/rustc_mir/src/monomorphize/collector.rs b/compiler/rustc_mir/src/monomorphize/collector.rs
index 0ab9aa8e1bf..4bd431dcc6a 100644
--- a/compiler/rustc_mir/src/monomorphize/collector.rs
+++ b/compiler/rustc_mir/src/monomorphize/collector.rs
@@ -1071,6 +1071,13 @@ fn create_fn_mono_item<'tcx>(
     source: Span,
 ) -> Spanned<MonoItem<'tcx>> {
     debug!("create_fn_mono_item(instance={})", instance);
+
+    let def_id = instance.def_id();
+    if tcx.sess.opts.debugging_opts.profile_closures && def_id.is_local() && tcx.is_closure(def_id)
+    {
+        monomorphize::util::dump_closure_profile(tcx, instance);
+    }
+
     respan(source, MonoItem::Fn(instance.polymorphize(tcx)))
 }
 
diff --git a/compiler/rustc_mir/src/monomorphize/mod.rs b/compiler/rustc_mir/src/monomorphize/mod.rs
index 9ca4b6687f1..57d2723cf9c 100644
--- a/compiler/rustc_mir/src/monomorphize/mod.rs
+++ b/compiler/rustc_mir/src/monomorphize/mod.rs
@@ -7,6 +7,7 @@ use rustc_hir::lang_items::LangItem;
 pub mod collector;
 pub mod partitioning;
 pub mod polymorphize;
+pub mod util;
 
 fn custom_coerce_unsize_info<'tcx>(
     tcx: TyCtxt<'tcx>,
diff --git a/compiler/rustc_mir/src/monomorphize/util.rs b/compiler/rustc_mir/src/monomorphize/util.rs
new file mode 100644
index 00000000000..799b4e18c24
--- /dev/null
+++ b/compiler/rustc_mir/src/monomorphize/util.rs
@@ -0,0 +1,73 @@
+use rustc_middle::ty::{self, ClosureSizeProfileData, Instance, TyCtxt};
+use std::fs::OpenOptions;
+use std::io::prelude::*;
+
+/// For a given closure, writes out the data for the profiling the impact of RFC 2229 on
+/// closure size into a CSV.
+///
+/// During the same compile all closures dump the information in the same file
+/// "closure_profile_XXXXX.csv", which is created in the directory where the compiler is invoked.
+crate fn dump_closure_profile(tcx: TyCtxt<'tcx>, closure_instance: Instance<'tcx>) {
+    let mut file = if let Ok(file) = OpenOptions::new()
+        .create(true)
+        .append(true)
+        .open(&format!("closure_profile_{}.csv", std::process::id()))
+    {
+        file
+    } else {
+        eprintln!("Cound't open file for writing closure profile");
+        return;
+    };
+
+    let closure_def_id = closure_instance.def_id();
+    let typeck_results = tcx.typeck(closure_def_id.expect_local());
+
+    if typeck_results.closure_size_eval.contains_key(&closure_def_id) {
+        let param_env = ty::ParamEnv::reveal_all();
+
+        let ClosureSizeProfileData { before_feature_tys, after_feature_tys } =
+            typeck_results.closure_size_eval[&closure_def_id];
+
+        let before_feature_tys = tcx.subst_and_normalize_erasing_regions(
+            closure_instance.substs,
+            param_env,
+            before_feature_tys,
+        );
+        let after_feature_tys = tcx.subst_and_normalize_erasing_regions(
+            closure_instance.substs,
+            param_env,
+            after_feature_tys,
+        );
+
+        let new_size = tcx
+            .layout_of(param_env.and(after_feature_tys))
+            .map(|l| format!("{:?}", l.size.bytes()))
+            .unwrap_or_else(|e| format!("Failed {:?}", e));
+
+        let old_size = tcx
+            .layout_of(param_env.and(before_feature_tys))
+            .map(|l| format!("{:?}", l.size.bytes()))
+            .unwrap_or_else(|e| format!("Failed {:?}", e));
+
+        let closure_hir_id = tcx.hir().local_def_id_to_hir_id(closure_def_id.expect_local());
+        let closure_span = tcx.hir().span(closure_hir_id);
+        let src_file = tcx.sess.source_map().span_to_filename(closure_span);
+        let line_nos = tcx
+            .sess
+            .source_map()
+            .span_to_lines(closure_span)
+            .map(|l| format!("{:?} {:?}", l.lines.first(), l.lines.last()))
+            .unwrap_or_else(|e| format!("{:?}", e));
+
+        if let Err(e) = writeln!(
+            file,
+            "{}, {}, {}, {:?}",
+            old_size,
+            new_size,
+            src_file.prefer_local(),
+            line_nos
+        ) {
+            eprintln!("Error writting to file {}", e.to_string())
+        }
+    }
+}
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index e144c8f168e..4c40d0c367e 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -1209,6 +1209,8 @@ options! {
          "show backtraces for panics during proc-macro execution (default: no)"),
     profile: bool = (false, parse_bool, [TRACKED],
         "insert profiling code (default: no)"),
+    profile_closures: bool = (false, parse_no_flag, [UNTRACKED],
+        "profile size of closures"),
     profile_emit: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED],
         "file path to emit profiling data at runtime when using 'profile' \
         (default based on relative source path)"),
diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs
index 581aa087be9..7845d673236 100644
--- a/compiler/rustc_typeck/src/check/upvar.rs
+++ b/compiler/rustc_typeck/src/check/upvar.rs
@@ -42,7 +42,9 @@ use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
 use rustc_infer::infer::UpvarRegion;
 use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, Projection, ProjectionKind};
 use rustc_middle::mir::FakeReadCause;
-use rustc_middle::ty::{self, TraitRef, Ty, TyCtxt, TypeckResults, UpvarSubsts};
+use rustc_middle::ty::{
+    self, ClosureSizeProfileData, TraitRef, Ty, TyCtxt, TypeckResults, UpvarSubsts,
+};
 use rustc_session::lint;
 use rustc_span::sym;
 use rustc_span::{MultiSpan, Span, Symbol};
@@ -175,6 +177,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             self.perform_2229_migration_anaysis(closure_def_id, body_id, capture_clause, span);
         }
 
+        let after_feature_tys = self.final_upvar_tys(closure_def_id);
+
         // We now fake capture information for all variables that are mentioned within the closure
         // We do this after handling migrations so that min_captures computes before
         if !enable_precise_capture(self.tcx, span) {
@@ -203,6 +207,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             self.compute_min_captures(closure_def_id, capture_clause, capture_information);
         }
 
+        let before_feature_tys = self.final_upvar_tys(closure_def_id);
+
         if let Some(closure_substs) = infer_kind {
             // Unify the (as yet unbound) type variable in the closure
             // substs with the kind we inferred.
@@ -258,6 +264,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             .collect();
         self.typeck_results.borrow_mut().closure_fake_reads.insert(closure_def_id, fake_reads);
 
+        if self.tcx.sess.opts.debugging_opts.profile_closures {
+            self.typeck_results.borrow_mut().closure_size_eval.insert(
+                closure_def_id,
+                ClosureSizeProfileData {
+                    before_feature_tys: self.tcx.mk_tup(before_feature_tys.into_iter()),
+                    after_feature_tys: self.tcx.mk_tup(after_feature_tys.into_iter()),
+                },
+            );
+        }
+
         // If we are also inferred the closure kind here,
         // process any deferred resolutions.
         let deferred_call_resolutions = self.remove_deferred_call_resolutions(closure_def_id);
diff --git a/compiler/rustc_typeck/src/check/writeback.rs b/compiler/rustc_typeck/src/check/writeback.rs
index 032cc7ee233..589570f1cb7 100644
--- a/compiler/rustc_typeck/src/check/writeback.rs
+++ b/compiler/rustc_typeck/src/check/writeback.rs
@@ -15,7 +15,7 @@ use rustc_middle::hir::place::Place as HirPlace;
 use rustc_middle::mir::FakeReadCause;
 use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCast};
 use rustc_middle::ty::fold::{TypeFoldable, TypeFolder};
-use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::ty::{self, ClosureSizeProfileData, Ty, TyCtxt};
 use rustc_span::symbol::sym;
 use rustc_span::Span;
 use rustc_trait_selection::opaque_types::InferCtxtExt;
@@ -60,6 +60,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
         wbcx.visit_body(body);
         wbcx.visit_min_capture_map();
+        wbcx.eval_closure_size();
         wbcx.visit_fake_reads_map();
         wbcx.visit_closures();
         wbcx.visit_liberated_fn_sigs();
@@ -333,6 +334,19 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> {
 }
 
 impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
+    fn eval_closure_size(&mut self) {
+        let mut res: FxHashMap<DefId, ClosureSizeProfileData<'tcx>> = Default::default();
+        for (closure_def_id, data) in self.fcx.typeck_results.borrow().closure_size_eval.iter() {
+            let closure_hir_id =
+                self.tcx().hir().local_def_id_to_hir_id(closure_def_id.expect_local());
+
+            let data = self.resolve(*data, &closure_hir_id);
+
+            res.insert(*closure_def_id, data);
+        }
+
+        self.typeck_results.closure_size_eval = res;
+    }
     fn visit_min_capture_map(&mut self) {
         let mut min_captures_wb = ty::MinCaptureInformationMap::with_capacity_and_hasher(
             self.fcx.typeck_results.borrow().closure_min_captures.len(),