diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index ebd18630e4e4c..b252409a92ada 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 16d2ac262d004..583d605ab18be 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_attr as attr; @@ -483,6 +484,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>, } impl<'tcx> TypeckResults<'tcx> { @@ -509,6 +514,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(), } } @@ -753,6 +759,7 @@ impl<'a, 'tcx> HashStable> 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| { @@ -779,6 +786,7 @@ impl<'a, 'tcx> HashStable> 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 859a940a62526..69fa99993bb40 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -175,6 +175,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; diff --git a/compiler/rustc_mir/src/monomorphize/collector.rs b/compiler/rustc_mir/src/monomorphize/collector.rs index 0ab9aa8e1bfd4..4bd431dcc6a3c 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> { 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 9ca4b6687f1b5..57d2723cf9cfd 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 0000000000000..799b4e18c240f --- /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 e144c8f168e79..4c40d0c367eca 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 = (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 7b5b14ae6c831..41b4172781cf8 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 032cc7ee2334a..589570f1cb7ca 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> = 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(),