]> git.lizzy.rs Git - rust.git/commitdiff
rewrite scope drop to be iterative
authorNiko Matsakis <niko@alum.mit.edu>
Thu, 24 Mar 2016 00:46:38 +0000 (20:46 -0400)
committerNiko Matsakis <niko@alum.mit.edu>
Thu, 24 Mar 2016 00:46:38 +0000 (20:46 -0400)
while I'm at it, remove the "extra caching" that I was doing for no good
reason except laziness. Basically before I was caching at each scope in
the chain, but there's not really a reason to do that, since the cached
entry point at level N is always equal to the last cached exit point
from level N-1.

src/librustc_mir/build/mod.rs
src/librustc_mir/build/scope.rs

index a88b138a44ddea3d67f7b04e53822f659d64c639..b79f492179fb9363cbdd583f4a472ea75bf911b8 100644 (file)
@@ -47,6 +47,10 @@ pub struct Builder<'a, 'tcx: 'a> {
     var_indices: FnvHashMap<ast::NodeId, u32>,
     temp_decls: Vec<TempDecl<'tcx>>,
     unit_temp: Option<Lvalue<'tcx>>,
+
+    // cached block with a RESUME terminator; we create this at the
+    // first panic
+    cached_resume_block: Option<BasicBlock>,
 }
 
 struct CFG<'tcx> {
@@ -175,6 +179,7 @@ pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>,
         var_decls: vec![],
         var_indices: FnvHashMap(),
         unit_temp: None,
+        cached_resume_block: None,
     };
 
     assert_eq!(builder.cfg.start_new_block(), START_BLOCK);
index acca2637c82c193def7a28f4acea41605e1bbe92..f5f6f409eaba4d6ab2725685f8b26ede4a1d4422 100644 (file)
@@ -456,21 +456,41 @@ pub fn schedule_box_free(&mut self,
     /// See module comment for more details. None indicates there’s no
     /// cleanup to do at this point.
     pub fn diverge_cleanup(&mut self) -> Option<BasicBlock> {
-        if self.scopes.is_empty() {
+        if self.scopes.iter().all(|scope| scope.drops.is_empty() && scope.free.is_none()) {
             return None;
         }
+        assert!(!self.scopes.is_empty()); // or `all` above would be true
+
         let unit_temp = self.get_unit_temp();
-        let Builder { ref mut hir, ref mut cfg, ref mut scopes, .. } = *self;
-
-        // Given an array of scopes, we generate these from the outermost scope to the innermost
-        // one. Thus for array [S0, S1, S2] with corresponding cleanup blocks [B0, B1, B2], we will
-        // generate B0 <- B1 <- B2 in left-to-right order. Control flow of the generated blocks
-        // always ends up at a block with the Resume terminator.
-        if scopes.iter().any(|scope| !scope.drops.is_empty() || scope.free.is_some()) {
-            Some(build_diverge_scope(hir.tcx(), self.fn_span, cfg, &unit_temp, scopes))
+        let Builder { ref mut hir, ref mut cfg, ref mut scopes,
+                      ref mut cached_resume_block, .. } = *self;
+
+        // Build up the drops in **reverse** order. The end result will
+        // look like:
+        //
+        //    scopes[n] -> scopes[n-1] -> ... -> scopes[0]
+        //
+        // However, we build this in **reverse order**. That is, we
+        // process scopes[0], then scopes[1], etc, pointing each one at
+        // the result generates from the one before. Along the way, we
+        // store caches. If everything is cached, we'll just walk right
+        // to left reading the cached results but never created anything.
+
+        // To start, create the resume terminator.
+        let mut target = if let Some(target) = *cached_resume_block {
+            target
         } else {
-            None
+            let resumeblk = cfg.start_new_cleanup_block();
+            cfg.terminate(resumeblk, scopes[0].id, self.fn_span, TerminatorKind::Resume);
+            *cached_resume_block = Some(resumeblk);
+            resumeblk
+        };
+
+        for scope in scopes {
+            target = build_diverge_scope(hir.tcx(), cfg, &unit_temp, scope, target);
         }
+
+        Some(target)
     }
 
     /// Utility function for *non*-scope code to build their own drops
@@ -640,43 +660,25 @@ fn build_scope_drops<'tcx>(cfg: &mut CFG<'tcx>,
 }
 
 fn build_diverge_scope<'tcx>(tcx: &TyCtxt<'tcx>,
-                             fn_span: Span,
                              cfg: &mut CFG<'tcx>,
                              unit_temp: &Lvalue<'tcx>,
-                             scopes: &mut [Scope<'tcx>])
+                             scope: &mut Scope<'tcx>,
+                             mut target: BasicBlock)
                              -> BasicBlock
 {
-    assert!(scopes.len() >= 1);
-
     // Build up the drops in **reverse** order. The end result will
     // look like:
     //
-    //    [drops[n]] -...-> [drops[0]] -> [Free] -> [scopes[..n-1]]
+    //    [drops[n]] -...-> [drops[0]] -> [Free] -> [target]
     //    |                                    |
     //    +------------------------------------+
-    //     code for scopes[n]
+    //     code for scope
     //
     // The code in this function reads from right to left. At each
     // point, we check for cached blocks representing the
     // remainder. If everything is cached, we'll just walk right to
     // left reading the cached results but never created anything.
 
-    // To start, translate scopes[1..].
-    let (scope, earlier_scopes) = scopes.split_last_mut().unwrap();
-    let mut target = if let Some(cached_block) = scope.cached_block {
-        cached_block
-    } else if earlier_scopes.is_empty() {
-        // Diverging from the root scope creates a RESUME terminator.
-        // FIXME what span to use here?
-        let resumeblk = cfg.start_new_cleanup_block();
-        cfg.terminate(resumeblk, scope.id, fn_span, TerminatorKind::Resume);
-        resumeblk
-    } else {
-        // Diverging from any other scope chains up to the previous scope.
-        build_diverge_scope(tcx, fn_span, cfg, unit_temp, earlier_scopes)
-    };
-    scope.cached_block = Some(target);
-
     // Next, build up any free.
     if let Some(ref mut free_data) = scope.free {
         target = if let Some(cached_block) = free_data.cached_block {