]>
Commit | Line | Data |
---|---|---|
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 | ||
11 | use rustc::ty::TyCtxt; | |
12 | use rustc::mir::repr::*; | |
13 | use rustc::mir::transform::{MirPass, MirSource, Pass}; | |
14 | ||
15 | use rustc_data_structures::bitvec::BitVector; | |
16 | ||
17 | use pretty; | |
18 | ||
19 | use traversal; | |
20 | ||
21 | pub 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 | ||
43 | impl<'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 | ||
102 | impl Pass for BreakCleanupEdges {} | |
103 | ||
104 | // Returns true if the terminator is a call that would use an invoke in LLVM. | |
105 | fn 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 | } |