borrowck: skip CFG construction when there is nothing to propagate

CFG construction takes a large amount of time and memory, especially for
large constants. If such a constant contains no actions on lvalues, it
can't have borrowck problems and can be ignored by it.

This removes the 4.9GB borrowck peak from #36799. It seems that HIR had
grown by 300MB and MIR had grown by 500MB from the last massif
collection and that remains to be investigated, but this at least shaves
the borrowck peak.
This commit is contained in:
Ariel Ben-Yehuda 2017-07-29 20:30:44 +03:00
parent 91aff5775d
commit 83eb264273
2 changed files with 46 additions and 16 deletions

View File

@ -112,19 +112,28 @@ fn borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, owner_def_id: DefId) {
// is not yet stolen.
tcx.mir_validated(owner_def_id).borrow();
let cfg = cfg::CFG::new(bccx.tcx, &body);
let AnalysisData { all_loans,
loans: loan_dfcx,
move_data: flowed_moves } =
build_borrowck_dataflow_data(bccx, &cfg, body_id);
check_loans::check_loans(bccx, &loan_dfcx, &flowed_moves, &all_loans, body);
// option dance because you can't capture an uninitialized variable
// by mut-ref.
let mut cfg = None;
if let Some(AnalysisData { all_loans,
loans: loan_dfcx,
move_data: flowed_moves }) =
build_borrowck_dataflow_data(bccx, false, body_id,
|bccx| {
cfg = Some(cfg::CFG::new(bccx.tcx, &body));
cfg.as_mut().unwrap()
})
{
check_loans::check_loans(bccx, &loan_dfcx, &flowed_moves, &all_loans, body);
}
}
fn build_borrowck_dataflow_data<'a, 'tcx>(this: &mut BorrowckCtxt<'a, 'tcx>,
cfg: &cfg::CFG,
body_id: hir::BodyId)
-> AnalysisData<'a, 'tcx>
fn build_borrowck_dataflow_data<'a, 'c, 'tcx, F>(this: &mut BorrowckCtxt<'a, 'tcx>,
force_analysis: bool,
body_id: hir::BodyId,
get_cfg: F)
-> Option<AnalysisData<'a, 'tcx>>
where F: FnOnce(&mut BorrowckCtxt<'a, 'tcx>) -> &'c cfg::CFG
{
// Check the body of fn items.
let tcx = this.tcx;
@ -137,6 +146,18 @@ fn build_borrowck_dataflow_data<'a, 'tcx>(this: &mut BorrowckCtxt<'a, 'tcx>,
let (all_loans, move_data) =
gather_loans::gather_loans_in_fn(this, body_id);
if !force_analysis && move_data.is_empty() && all_loans.is_empty() {
// large arrays of data inserted as constants can take a lot of
// time and memory to borrow-check - see issue #36799. However,
// they don't have lvalues, so no borrow-check is actually needed.
// Recognize that case and skip borrow-checking.
debug!("skipping loan propagation for {:?} because of no loans", body_id);
return None;
} else {
debug!("propagating loans in {:?}", body_id);
}
let cfg = get_cfg(this);
let mut loan_dfcx =
DataFlowContext::new(this.tcx,
"borrowck",
@ -159,9 +180,9 @@ fn build_borrowck_dataflow_data<'a, 'tcx>(this: &mut BorrowckCtxt<'a, 'tcx>,
id_range,
body);
AnalysisData { all_loans: all_loans,
loans: loan_dfcx,
move_data:flowed_moves }
Some(AnalysisData { all_loans: all_loans,
loans: loan_dfcx,
move_data:flowed_moves })
}
/// Accessor for introspective clients inspecting `AnalysisData` and
@ -178,8 +199,8 @@ pub fn build_borrowck_dataflow_data_for_fn<'a, 'tcx>(
let region_maps = tcx.region_maps(owner_def_id);
let mut bccx = BorrowckCtxt { tcx, tables, region_maps, owner_def_id };
let dataflow_data = build_borrowck_dataflow_data(&mut bccx, cfg, body_id);
(bccx, dataflow_data)
let dataflow_data = build_borrowck_dataflow_data(&mut bccx, true, body_id, |_| cfg);
(bccx, dataflow_data.unwrap())
}
// ----------------------------------------------------------------------

View File

@ -220,6 +220,15 @@ pub fn new() -> MoveData<'tcx> {
}
}
/// return true if there are no trackable assignments or moves
/// in this move data - that means that there is nothing that
/// could cause a borrow error.
pub fn is_empty(&self) -> bool {
self.moves.borrow().is_empty() &&
self.path_assignments.borrow().is_empty() &&
self.var_assignments.borrow().is_empty()
}
pub fn path_loan_path(&self, index: MovePathIndex) -> Rc<LoanPath<'tcx>> {
(*self.paths.borrow())[index.get()].loan_path.clone()
}