]> git.lizzy.rs Git - rust.git/commitdiff
Add Resume Terminator which corresponds to resume
authorSimonas Kazlauskas <git@kazlauskas.me>
Tue, 15 Dec 2015 18:46:39 +0000 (20:46 +0200)
committerSimonas Kazlauskas <git@kazlauskas.me>
Wed, 6 Jan 2016 11:57:51 +0000 (13:57 +0200)
Diverge should eventually go away

src/librustc/mir/repr.rs
src/librustc/mir/visit.rs
src/librustc_mir/build/expr/into.rs
src/librustc_mir/build/scope.rs
src/librustc_mir/transform/erase_regions.rs
src/librustc_trans/trans/mir/block.rs

index 42c9b7b0f39abd112554f9b14ba7518dc095112a..8cdb804c599ea2c4895321e80a5bd2defb4879ab 100644 (file)
@@ -251,6 +251,10 @@ pub enum Terminator<'tcx> {
     /// well-known diverging block actually diverges.
     Diverge,
 
+    /// Indicates that the landing pad is finished and unwinding should
+    /// continue. Emitted by build::scope::diverge_cleanup.
+    Resume,
+
     /// Indicates a normal return. The ReturnPointer lvalue should
     /// have been filled in by now. This should only occur in the
     /// `END_BLOCK`.
@@ -288,6 +292,14 @@ pub enum CallTargets {
 }
 
 impl CallTargets {
+    pub fn new(ret: BasicBlock, cleanup: Option<BasicBlock>) -> CallTargets {
+        if let Some(c) = cleanup {
+            CallTargets::WithCleanup((ret, c))
+        } else {
+            CallTargets::Return(ret)
+        }
+    }
+
     pub fn as_slice(&self) -> &[BasicBlock] {
         match *self {
             CallTargets::Return(ref b) => slice::ref_slice(b),
@@ -313,6 +325,7 @@ pub fn successors(&self) -> &[BasicBlock] {
             Switch { targets: ref b, .. } => b,
             SwitchInt { targets: ref b, .. } => b,
             Diverge => &[],
+            Resume => &[],
             Return => &[],
             Call { targets: ref b, .. } => b.as_slice(),
             DivergingCall { cleanup: ref b, .. } => if let Some(b) = b.as_ref() {
@@ -332,6 +345,7 @@ pub fn successors_mut(&mut self) -> &mut [BasicBlock] {
             Switch { targets: ref mut b, .. } => b,
             SwitchInt { targets: ref mut b, .. } => b,
             Diverge => &mut [],
+            Resume => &mut [],
             Return => &mut [],
             Call { targets: ref mut b, .. } => b.as_mut_slice(),
             DivergingCall { cleanup: ref mut b, .. } => if let Some(b) = b.as_mut() {
@@ -393,6 +407,7 @@ pub fn fmt_head<W: Write>(&self, fmt: &mut W) -> fmt::Result {
             SwitchInt { discr: ref lv, .. } => write!(fmt, "switchInt({:?})", lv),
             Diverge => write!(fmt, "diverge"),
             Return => write!(fmt, "return"),
+            Resume => write!(fmt, "resume"),
             Call { .. } => {
                 // the author didn’t bother rebasing this
                 unimplemented!()
@@ -408,7 +423,7 @@ pub fn fmt_head<W: Write>(&self, fmt: &mut W) -> fmt::Result {
     pub fn fmt_successor_labels(&self) -> Vec<Cow<'static, str>> {
         use self::Terminator::*;
         match *self {
-            Diverge | Return => vec![],
+            Diverge | Return | Resume => vec![],
             Goto { .. } | Panic { .. } => vec!["".into_cow()],
             If { .. } => vec!["true".into_cow(), "false".into_cow()],
             Call { .. } => vec!["return".into_cow(), "unwind".into_cow()],
index c3a335fdfaac3781c60778167c159383df91431b..41fd505682378c69220d00705fb003616f304d89 100644 (file)
@@ -134,6 +134,7 @@ fn super_terminator(&mut self, block: BasicBlock, terminator: &Terminator<'tcx>)
             }
 
             Terminator::Diverge |
+            Terminator::Resume |
             Terminator::Return => {
             }
 
@@ -431,6 +432,7 @@ fn super_terminator(&mut self,
             }
 
             Terminator::Diverge |
+            Terminator::Resume |
             Terminator::Return => {
             }
 
index a173018b74fea1a4f1f4081f11fb4e8203ec7cb6..e23b5517cadf3a663d11de875043a54c1a15137c 100644 (file)
@@ -225,13 +225,13 @@ pub fn into_expr(&mut self,
                 let success = this.cfg.start_new_block();
                 let cleanup = this.diverge_cleanup();
                 let term = if diverges {
-                    Terminator::DivergingCall { func: fun, args: args, cleanup: Some(cleanup) }
+                    Terminator::DivergingCall { func: fun, args: args, cleanup: cleanup }
                 } else {
                     Terminator::Call {
                         func: fun,
                         args: args,
                         destination: destination.clone(),
-                        targets: CallTargets::WithCleanup((success, cleanup))
+                        targets: CallTargets::new(success, cleanup)
                     }
                 };
                 this.cfg.terminate(block, term);
index b5e029ed94bd12f1defb33a2b8e897e576e26f11..e7f3794c043b0e2dbcd78014ec33423a64863e52 100644 (file)
@@ -86,7 +86,7 @@
 
 */
 
-use build::{BlockAnd, BlockAndExtension, Builder, CFG};
+use build::{BlockAnd, BlockAndExtension, Builder};
 use rustc::middle::region::CodeExtent;
 use rustc::middle::ty::Ty;
 use rustc::mir::repr::*;
@@ -227,16 +227,44 @@ pub fn exit_scope(&mut self,
         self.cfg.terminate(block, Terminator::Goto { target: target });
     }
 
-    /// Creates a path that performs all required cleanup for
-    /// unwinding. This path terminates in DIVERGE. Returns the start
-    /// of the path. See module comment for more details.
-    pub fn diverge_cleanup(&mut self) -> BasicBlock {
-        diverge_cleanup_helper(&mut self.cfg, &mut self.scopes)
+    /// Creates a path that performs all required cleanup for unwinding.
+    ///
+    /// This path terminates in Resume. Returns the start of the path.
+    /// 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() {
+            return None;
+        }
+
+        let mut terminator = Terminator::Resume;
+        // 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. The outermost scope (B0) will always
+        // terminate with a Resume terminator.
+        for scope in self.scopes.iter_mut().filter(|s| !s.drops.is_empty()) {
+            if let Some(b) = scope.cached_block {
+                terminator = Terminator::Goto { target: b };
+                continue;
+            } else {
+                let new_block = self.cfg.start_new_block();
+                self.cfg.terminate(new_block, terminator);
+                terminator = Terminator::Goto { target: new_block };
+                for &(kind, span, ref lvalue) in scope.drops.iter().rev() {
+                    self.cfg.push_drop(new_block, span, kind, lvalue);
+                }
+                scope.cached_block = Some(new_block);
+            }
+        }
+        // Return the innermost cached block, most likely the one we just generated.
+        // Note that if there are no cleanups in scope we return None.
+        self.scopes.iter().rev().flat_map(|b| b.cached_block).next()
     }
 
     /// Create diverge cleanup and branch to it from `block`.
     pub fn panic(&mut self, block: BasicBlock) {
-        let cleanup = self.diverge_cleanup();
+        // FIXME: panic terminator should also have conditional cleanup?
+        let cleanup = self.diverge_cleanup().unwrap_or(DIVERGE_BLOCK);
         self.cfg.terminate(block, Terminator::Panic { target: cleanup });
     }
 
@@ -249,14 +277,18 @@ pub fn schedule_drop(&mut self,
                          lvalue: &Lvalue<'tcx>,
                          lvalue_ty: Ty<'tcx>) {
         if self.hir.needs_drop(lvalue_ty) {
-            match self.scopes.iter_mut().rev().find(|s| s.extent == extent) {
-                Some(scope) => {
+            for scope in self.scopes.iter_mut().rev() {
+                // We must invalidate all the cached_blocks leading up to the scope we’re looking
+                // for, because otherwise some/most of the blocks in the chain might become
+                // incorrect (i.e. they still are pointing at old cached_block).
+                scope.cached_block = None;
+                if scope.extent == extent {
                     scope.drops.push((kind, span, lvalue.clone()));
-                    scope.cached_block = None;
+                    return;
                 }
-                None => self.hir.span_bug(span, &format!("extent {:?} not in scope to drop {:?}",
-                                                         extent, lvalue)),
             }
+            self.hir.span_bug(span,
+                              &format!("extent {:?} not in scope to drop {:?}", extent, lvalue));
         }
     }
 
@@ -268,28 +300,3 @@ pub fn extent_of_outermost_scope(&self) -> CodeExtent {
         self.scopes.first().map(|scope| scope.extent).unwrap()
     }
 }
-
-fn diverge_cleanup_helper<'tcx>(cfg: &mut CFG<'tcx>, scopes: &mut [Scope<'tcx>]) -> BasicBlock {
-    let len = scopes.len();
-
-    if len == 0 {
-        return DIVERGE_BLOCK;
-    }
-
-    let (remaining, scope) = scopes.split_at_mut(len - 1);
-    let scope = &mut scope[0];
-
-    if let Some(b) = scope.cached_block {
-        return b;
-    }
-
-    let block = cfg.start_new_block();
-    for &(kind, span, ref lvalue) in &scope.drops {
-        cfg.push_drop(block, span, kind, lvalue);
-    }
-    scope.cached_block = Some(block);
-
-    let remaining_cleanup_block = diverge_cleanup_helper(cfg, remaining);
-    cfg.terminate(block, Terminator::Goto { target: remaining_cleanup_block });
-    block
-}
index 1e7bb305cdbd2e2f79a7d803b61cd6afebd4b8db..3f5e18b7e79689bcdd52ced4774c2660f2dada00 100644 (file)
@@ -80,6 +80,7 @@ fn erase_regions_terminator(&mut self,
         match *terminator {
             Terminator::Goto { .. } |
             Terminator::Diverge |
+            Terminator::Resume |
             Terminator::Return |
             Terminator::Panic { .. } => {
                 /* nothing to do */
index 00508ed21e58e768b53a5b12ce0df4f94352f0e1..ddd3ed34e8f93f0218a71b1c356d4329ee9e7dcf 100644 (file)
@@ -87,16 +87,16 @@ pub fn trans_block(&mut self, bb: mir::BasicBlock) {
             }
 
             mir::Terminator::Diverge => {
+                build::Unreachable(bcx);
+            }
+
+            mir::Terminator::Resume => {
                 if let Some(llpersonalityslot) = self.llpersonalityslot {
                     let lp = build::Load(bcx, llpersonalityslot);
                     // FIXME(lifetime) base::call_lifetime_end(bcx, self.personality);
                     build::Resume(bcx, lp);
                 } else {
-                    // This fn never encountered anything fallible, so
-                    // a Diverge cannot actually happen. Note that we
-                    // do a total hack to ensure that we visit the
-                    // DIVERGE block last.
-                    build::Unreachable(bcx);
+                    panic!("resume terminator without personality slot")
                 }
             }