]> git.lizzy.rs Git - rust.git/commitdiff
break critical edges only when needed
authorAriel Ben-Yehuda <ariel.byd@gmail.com>
Sun, 5 Jun 2016 06:00:17 +0000 (09:00 +0300)
committerAriel Ben-Yehuda <ariel.byd@gmail.com>
Sun, 5 Jun 2016 06:27:26 +0000 (09:27 +0300)
the *only* place where critical edges need to be broken is on Call
instructions, so only break them there.

src/librustc_borrowck/borrowck/mir/elaborate_drops.rs
src/librustc_driver/driver.rs
src/librustc_mir/transform/add_call_guards.rs [new file with mode: 0644]
src/librustc_mir/transform/break_cleanup_edges.rs [deleted file]
src/librustc_mir/transform/dump_mir.rs [new file with mode: 0644]
src/librustc_mir/transform/mod.rs

index f72e10d99cfd121f14c3e4632984ce023542183c..e783420fa065ce138f68361141b56e03386ce527 100644 (file)
@@ -327,12 +327,30 @@ fn elaborate_replace(
         let data = self.mir.basic_block_data(bb);
         let terminator = data.terminator();
 
-        let unwind = Some(unwind.unwrap_or_else(|| {
-            // we can't use the resume block directly, because we
-            // may want to add a drop flag write.
-            self.jump_to_resume_block(terminator.scope,
-                                      terminator.span)
-        }));
+        let assign = Statement {
+            kind: StatementKind::Assign(location.clone(), Rvalue::Use(value.clone())),
+            span: terminator.span,
+            scope: terminator.scope
+        };
+
+        let unwind = unwind.unwrap_or(self.patch.resume_block());
+        let unwind = self.patch.new_block(BasicBlockData {
+            statements: vec![assign.clone()],
+            terminator: Some(Terminator {
+                kind: TerminatorKind::Goto { target: unwind },
+                ..*terminator
+            }),
+            is_cleanup: true
+        });
+
+        let target = self.patch.new_block(BasicBlockData {
+            statements: vec![assign],
+            terminator: Some(Terminator {
+                kind: TerminatorKind::Goto { target: target },
+                ..*terminator
+            }),
+            is_cleanup: data.is_cleanup,
+        });
 
         if !self.lvalue_is_tracked(location) {
             // drop and replace behind a pointer/array/whatever. The location
@@ -341,7 +359,7 @@ fn elaborate_replace(
             self.patch.patch_terminator(bb, TerminatorKind::Drop {
                 location: location.clone(),
                 target: target,
-                unwind: unwind
+                unwind: Some(unwind)
             });
         } else {
             debug!("elaborate_drop_and_replace({:?}) - tracked", terminator);
@@ -356,24 +374,15 @@ fn elaborate_replace(
                 lvalue: location,
                 path: path,
                 succ: target,
-                unwind: unwind
+                unwind: Some(unwind)
             }, bb);
             on_all_children_bits(self.tcx, self.mir, self.move_data(), path, |child| {
                 self.set_drop_flag(Location { block: target, index: 0 },
                                    child, DropFlagState::Present);
-                if let Some(unwind) = unwind {
-                    self.set_drop_flag(Location { block: unwind, index: 0 },
-                                       child, DropFlagState::Present);
-                }
+                self.set_drop_flag(Location { block: unwind, index: 0 },
+                                   child, DropFlagState::Present);
             });
         }
-
-        self.patch.add_assign(Location { block: target, index: 0 },
-                              location.clone(), Rvalue::Use(value.clone()));
-        if let Some(unwind) = unwind {
-            self.patch.add_assign(Location { block: unwind, index: 0 },
-                                  location.clone(), Rvalue::Use(value.clone()));
-        }
     }
 
     /// This elaborates a single drop instruction, located at `bb`, and
@@ -828,19 +837,6 @@ fn drop_block<'a>(&mut self, c: &DropCtxt<'a, 'tcx>) -> BasicBlock {
         })
     }
 
-    fn jump_to_resume_block<'a>(&mut self, scope: ScopeId, span: Span) -> BasicBlock {
-        let resume_block = self.patch.resume_block();
-        self.patch.new_block(BasicBlockData {
-            statements: vec![],
-            terminator: Some(Terminator {
-                scope: scope, span: span, kind: TerminatorKind::Goto {
-                    target: resume_block
-                }
-            }),
-            is_cleanup: true
-       })
-    }
-
     fn box_free_block<'a>(
         &mut self,
         c: &DropCtxt<'a, 'tcx>,
index bfad281702fd0bba9d2ee59394a4943b1543fe78..7c859d5e508bc588315da10d7ec7f29163a7e42d 100644 (file)
@@ -1032,11 +1032,12 @@ pub fn phase_4_translate_to_llvm<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         passes.push_pass(box mir::transform::no_landing_pads::NoLandingPads);
         passes.push_pass(box mir::transform::remove_dead_blocks::RemoveDeadBlocks);
         passes.push_pass(box mir::transform::erase_regions::EraseRegions);
-        passes.push_pass(box mir::transform::break_cleanup_edges::BreakCleanupEdges);
+        passes.push_pass(box mir::transform::add_call_guards::AddCallGuards);
         passes.push_pass(box borrowck::ElaborateDrops);
         passes.push_pass(box mir::transform::no_landing_pads::NoLandingPads);
         passes.push_pass(box mir::transform::simplify_cfg::SimplifyCfg);
-        passes.push_pass(box mir::transform::break_cleanup_edges::BreakCleanupEdges);
+        passes.push_pass(box mir::transform::add_call_guards::AddCallGuards);
+        passes.push_pass(box mir::transform::dump_mir::DumpMir("pre_trans"));
         passes.run_passes(tcx, &mut mir_map);
     });
 
diff --git a/src/librustc_mir/transform/add_call_guards.rs b/src/librustc_mir/transform/add_call_guards.rs
new file mode 100644 (file)
index 0000000..bcdd62c
--- /dev/null
@@ -0,0 +1,98 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use rustc::ty::TyCtxt;
+use rustc::mir::repr::*;
+use rustc::mir::transform::{MirPass, MirSource, Pass};
+
+use pretty;
+
+use traversal;
+
+pub struct AddCallGuards;
+
+/**
+ * Breaks outgoing critical edges for call terminators in the MIR.
+ *
+ * Critical edges are edges that are neither the only edge leaving a
+ * block, nor the only edge entering one.
+ *
+ * When you want something to happen "along" an edge, you can either
+ * do at the end of the predecessor block, or at the start of the
+ * successor block. Critical edges have to be broken in order to prevent
+ * "edge actions" from affecting other edges. We need this for calls that are
+ * translated to LLVM invoke instructions, because invoke is a block terminator
+ * in LLVM so we can't insert any code to handle the call's result into the
+ * block that performs the call.
+ *
+ * This function will break those edges by inserting new blocks along them.
+ *
+ * NOTE: Simplify CFG will happily undo most of the work this pass does.
+ *
+ */
+
+impl<'tcx> MirPass<'tcx> for AddCallGuards {
+    fn run_pass<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource, mir: &mut Mir<'tcx>) {
+        let mut pred_count = vec![0u32; mir.basic_blocks.len()];
+
+        // Build the precedecessor map for the MIR
+        for (_, data) in traversal::preorder(mir) {
+            if let Some(ref term) = data.terminator {
+                for &tgt in term.successors().iter() {
+                    pred_count[tgt.index()] += 1;
+                }
+            }
+        }
+
+        // We need a place to store the new blocks generated
+        let mut new_blocks = Vec::new();
+
+        let bbs = mir.all_basic_blocks();
+        let cur_len = mir.basic_blocks.len();
+
+        for &bb in &bbs {
+            let data = mir.basic_block_data_mut(bb);
+
+            match data.terminator {
+                Some(Terminator {
+                    kind: TerminatorKind::Call {
+                        destination: Some((_, ref mut destination)),
+                        cleanup: Some(_),
+                        ..
+                    }, span, scope
+                }) if pred_count[destination.index()] > 1 => {
+                    // It's a critical edge, break it
+                    let call_guard = BasicBlockData {
+                        statements: vec![],
+                        is_cleanup: data.is_cleanup,
+                        terminator: Some(Terminator {
+                            span: span,
+                            scope: scope,
+                            kind: TerminatorKind::Goto { target: *destination }
+                        })
+                    };
+
+                    // Get the index it will be when inserted into the MIR
+                    let idx = cur_len + new_blocks.len();
+                    new_blocks.push(call_guard);
+                    *destination = BasicBlock::new(idx);
+                }
+                _ => {}
+            }
+        }
+
+        pretty::dump_mir(tcx, "break_cleanup_edges", &0, src, mir, None);
+        debug!("Broke {} N edges", new_blocks.len());
+
+        mir.basic_blocks.extend_from_slice(&new_blocks);
+    }
+}
+
+impl Pass for AddCallGuards {}
diff --git a/src/librustc_mir/transform/break_cleanup_edges.rs b/src/librustc_mir/transform/break_cleanup_edges.rs
deleted file mode 100644 (file)
index 4902d31..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-use rustc::ty::TyCtxt;
-use rustc::mir::repr::*;
-use rustc::mir::transform::{MirPass, MirSource, Pass};
-
-use rustc_data_structures::bitvec::BitVector;
-
-use pretty;
-
-use traversal;
-
-pub struct BreakCleanupEdges;
-
-/**
- * Breaks outgoing critical edges for call terminators in the MIR.
- *
- * Critical edges are edges that are neither the only edge leaving a
- * block, nor the only edge entering one.
- *
- * When you want something to happen "along" an edge, you can either
- * do at the end of the predecessor block, or at the start of the
- * successor block. Critical edges have to be broken in order to prevent
- * "edge actions" from affecting other edges. We need this for calls that are
- * translated to LLVM invoke instructions, because invoke is a block terminator
- * in LLVM so we can't insert any code to handle the call's result into the
- * block that performs the call.
- *
- * This function will break those edges by inserting new blocks along them.
- *
- * NOTE: Simplify CFG will happily undo most of the work this pass does.
- *
- */
-
-impl<'tcx> MirPass<'tcx> for BreakCleanupEdges {
-    fn run_pass<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource, mir: &mut Mir<'tcx>) {
-        let mut pred_count = vec![0u32; mir.basic_blocks.len()];
-
-        // Build the precedecessor map for the MIR
-        for (_, data) in traversal::preorder(mir) {
-            if let Some(ref term) = data.terminator {
-                for &tgt in term.successors().iter() {
-                    pred_count[tgt.index()] += 1;
-                }
-            }
-        }
-
-        let cleanup_map : BitVector = mir.basic_blocks
-            .iter().map(|bb| bb.is_cleanup).collect();
-
-        // We need a place to store the new blocks generated
-        let mut new_blocks = Vec::new();
-
-        let bbs = mir.all_basic_blocks();
-        let cur_len = mir.basic_blocks.len();
-
-        for &bb in &bbs {
-            let data = mir.basic_block_data_mut(bb);
-
-            if let Some(ref mut term) = data.terminator {
-                if term_is_invoke(term) {
-                    let term_span = term.span;
-                    let term_scope = term.scope;
-                    let succs = term.successors_mut();
-                    for tgt in succs {
-                        let num_preds = pred_count[tgt.index()];
-                        if num_preds > 1 {
-                            // It's a critical edge, break it
-                            let goto = Terminator {
-                                span: term_span,
-                                scope: term_scope,
-                                kind: TerminatorKind::Goto { target: *tgt }
-                            };
-                            let mut data = BasicBlockData::new(Some(goto));
-                            data.is_cleanup = cleanup_map.contains(tgt.index());
-
-                            // Get the index it will be when inserted into the MIR
-                            let idx = cur_len + new_blocks.len();
-                            new_blocks.push(data);
-                            *tgt = BasicBlock::new(idx);
-                        }
-                    }
-                }
-            }
-        }
-
-        pretty::dump_mir(tcx, "break_cleanup_edges", &0, src, mir, None);
-        debug!("Broke {} N edges", new_blocks.len());
-
-        mir.basic_blocks.extend_from_slice(&new_blocks);
-    }
-}
-
-impl Pass for BreakCleanupEdges {}
-
-// Returns true if the terminator is a call that would use an invoke in LLVM.
-fn term_is_invoke(term: &Terminator) -> bool {
-    match term.kind {
-        TerminatorKind::Call { cleanup: Some(_), .. } |
-        // FIXME: not sure whether we need this one
-        TerminatorKind::Drop { unwind: Some(_), .. } |
-        TerminatorKind::DropAndReplace { .. } => true,
-        _ => false
-    }
-}
diff --git a/src/librustc_mir/transform/dump_mir.rs b/src/librustc_mir/transform/dump_mir.rs
new file mode 100644 (file)
index 0000000..fb49f95
--- /dev/null
@@ -0,0 +1,27 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! This pass just dumps MIR at a specified point.
+
+use rustc::ty::TyCtxt;
+use rustc::mir::repr::*;
+use rustc::mir::transform::{Pass, MirPass, MirSource};
+use pretty;
+
+pub struct DumpMir<'a>(pub &'a str);
+
+impl<'b, 'tcx> MirPass<'tcx> for DumpMir<'b> {
+    fn run_pass<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                    src: MirSource, mir: &mut Mir<'tcx>) {
+        pretty::dump_mir(tcx, self.0, &0, src, mir, None);
+    }
+}
+
+impl<'b> Pass for DumpMir<'b> {}
index 0dcb7ef84d01df4bca25c8bf469c2589a93dc4f0..339dcdec0608071f57f344012a7c177442c79911 100644 (file)
@@ -13,6 +13,7 @@
 pub mod erase_regions;
 pub mod no_landing_pads;
 pub mod type_check;
-pub mod break_cleanup_edges;
+pub mod add_call_guards;
 pub mod promote_consts;
 pub mod qualify_consts;
+pub mod dump_mir;