about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAriel Ben-Yehuda <ariel.byd@gmail.com>2017-07-29 20:30:44 +0300
committerAriel Ben-Yehuda <ariel.byd@gmail.com>2017-07-31 15:17:30 +0300
commit83eb264273fe7ace01b2100c116daa36f06920b8 (patch)
tree3f1e737e28b060271cd68025c55fdf38e9877c1a
parent91aff5775d3b4a95e2b0c2fe50785f3d28fa3dd8 (diff)
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.
-rw-r--r--src/librustc_borrowck/borrowck/mod.rs53
-rw-r--r--src/librustc_borrowck/borrowck/move_data.rs9
2 files changed, 46 insertions, 16 deletions
diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs
index 1bfc5805bc8..16ecfce49d0 100644
--- a/src/librustc_borrowck/borrowck/mod.rs
+++ b/src/librustc_borrowck/borrowck/mod.rs
@@ -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())
 }
 
 // ----------------------------------------------------------------------
diff --git a/src/librustc_borrowck/borrowck/move_data.rs b/src/librustc_borrowck/borrowck/move_data.rs
index 0a31905c792..fd80e8320d6 100644
--- a/src/librustc_borrowck/borrowck/move_data.rs
+++ b/src/librustc_borrowck/borrowck/move_data.rs
@@ -220,6 +220,15 @@ impl<'a, 'tcx> 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()
     }