]> git.proxmox.com Git - rustc.git/blame - src/librustc_mir/transform/break_cleanup_edges.rs
Imported Upstream version 1.10.0+dfsg1
[rustc.git] / src / librustc_mir / transform / break_cleanup_edges.rs
CommitLineData
a7813a04
XL
1// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2// file at the top-level directory of this distribution and at
3// http://rust-lang.org/COPYRIGHT.
4//
5// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8// option. This file may not be copied, modified, or distributed
9// except according to those terms.
10
11use rustc::ty::TyCtxt;
12use rustc::mir::repr::*;
13use rustc::mir::transform::{MirPass, MirSource, Pass};
14
15use rustc_data_structures::bitvec::BitVector;
16
17use pretty;
18
19use traversal;
20
21pub struct BreakCleanupEdges;
22
23/**
24 * Breaks outgoing critical edges for call terminators in the MIR.
25 *
26 * Critical edges are edges that are neither the only edge leaving a
27 * block, nor the only edge entering one.
28 *
29 * When you want something to happen "along" an edge, you can either
30 * do at the end of the predecessor block, or at the start of the
31 * successor block. Critical edges have to be broken in order to prevent
32 * "edge actions" from affecting other edges. We need this for calls that are
33 * translated to LLVM invoke instructions, because invoke is a block terminator
34 * in LLVM so we can't insert any code to handle the call's result into the
35 * block that performs the call.
36 *
37 * This function will break those edges by inserting new blocks along them.
38 *
39 * NOTE: Simplify CFG will happily undo most of the work this pass does.
40 *
41 */
42
43impl<'tcx> MirPass<'tcx> for BreakCleanupEdges {
44 fn run_pass<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource, mir: &mut Mir<'tcx>) {
45 let mut pred_count = vec![0u32; mir.basic_blocks.len()];
46
47 // Build the precedecessor map for the MIR
48 for (_, data) in traversal::preorder(mir) {
49 if let Some(ref term) = data.terminator {
50 for &tgt in term.successors().iter() {
51 pred_count[tgt.index()] += 1;
52 }
53 }
54 }
55
56 let cleanup_map : BitVector = mir.basic_blocks
57 .iter().map(|bb| bb.is_cleanup).collect();
58
59 // We need a place to store the new blocks generated
60 let mut new_blocks = Vec::new();
61
62 let bbs = mir.all_basic_blocks();
63 let cur_len = mir.basic_blocks.len();
64
65 for &bb in &bbs {
66 let data = mir.basic_block_data_mut(bb);
67
68 if let Some(ref mut term) = data.terminator {
69 if term_is_invoke(term) {
70 let term_span = term.span;
71 let term_scope = term.scope;
72 let succs = term.successors_mut();
73 for tgt in succs {
74 let num_preds = pred_count[tgt.index()];
75 if num_preds > 1 {
76 // It's a critical edge, break it
77 let goto = Terminator {
78 span: term_span,
79 scope: term_scope,
80 kind: TerminatorKind::Goto { target: *tgt }
81 };
82 let mut data = BasicBlockData::new(Some(goto));
83 data.is_cleanup = cleanup_map.contains(tgt.index());
84
85 // Get the index it will be when inserted into the MIR
86 let idx = cur_len + new_blocks.len();
87 new_blocks.push(data);
88 *tgt = BasicBlock::new(idx);
89 }
90 }
91 }
92 }
93 }
94
95 pretty::dump_mir(tcx, "break_cleanup_edges", &0, src, mir, None);
96 debug!("Broke {} N edges", new_blocks.len());
97
98 mir.basic_blocks.extend_from_slice(&new_blocks);
99 }
100}
101
102impl Pass for BreakCleanupEdges {}
103
104// Returns true if the terminator is a call that would use an invoke in LLVM.
105fn term_is_invoke(term: &Terminator) -> bool {
106 match term.kind {
107 TerminatorKind::Call { cleanup: Some(_), .. } |
108 TerminatorKind::Drop { unwind: Some(_), .. } => true,
109 _ => false
110 }
111}