]>
Commit | Line | Data |
---|---|---|
3157f602 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; | |
c30ab7b3 | 12 | use rustc::mir::*; |
3157f602 | 13 | use rustc_data_structures::indexed_vec::{Idx, IndexVec}; |
abe05a73 | 14 | use transform::{MirPass, MirSource}; |
3157f602 | 15 | |
3b2f2976 XL |
16 | #[derive(PartialEq)] |
17 | pub enum AddCallGuards { | |
18 | AllCallEdges, | |
19 | CriticalCallEdges, | |
20 | } | |
21 | pub use self::AddCallGuards::*; | |
3157f602 XL |
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 | ||
7cac9316 XL |
43 | impl MirPass for AddCallGuards { |
44 | fn run_pass<'a, 'tcx>(&self, | |
45 | _tcx: TyCtxt<'a, 'tcx, 'tcx>, | |
46 | _src: MirSource, | |
47 | mir: &mut Mir<'tcx>) { | |
3b2f2976 | 48 | self.add_call_guards(mir); |
cc61c64b XL |
49 | } |
50 | } | |
51 | ||
3b2f2976 XL |
52 | impl AddCallGuards { |
53 | pub fn add_call_guards(&self, mir: &mut Mir) { | |
54 | let pred_count: IndexVec<_, _> = | |
55 | mir.predecessors().iter().map(|ps| ps.len()).collect(); | |
3157f602 | 56 | |
3b2f2976 XL |
57 | // We need a place to store the new blocks generated |
58 | let mut new_blocks = Vec::new(); | |
3157f602 | 59 | |
3b2f2976 | 60 | let cur_len = mir.basic_blocks().len(); |
3157f602 | 61 | |
3b2f2976 XL |
62 | for block in mir.basic_blocks_mut() { |
63 | match block.terminator { | |
64 | Some(Terminator { | |
65 | kind: TerminatorKind::Call { | |
66 | destination: Some((_, ref mut destination)), | |
67 | cleanup, | |
68 | .. | |
69 | }, source_info | |
70 | }) if pred_count[*destination] > 1 && | |
71 | (cleanup.is_some() || self == &AllCallEdges) => | |
72 | { | |
73 | // It's a critical edge, break it | |
74 | let call_guard = BasicBlockData { | |
75 | statements: vec![], | |
76 | is_cleanup: block.is_cleanup, | |
77 | terminator: Some(Terminator { | |
78 | source_info, | |
79 | kind: TerminatorKind::Goto { target: *destination } | |
80 | }) | |
81 | }; | |
3157f602 | 82 | |
3b2f2976 XL |
83 | // Get the index it will be when inserted into the MIR |
84 | let idx = cur_len + new_blocks.len(); | |
85 | new_blocks.push(call_guard); | |
86 | *destination = BasicBlock::new(idx); | |
87 | } | |
88 | _ => {} | |
3157f602 XL |
89 | } |
90 | } | |
91 | ||
3b2f2976 | 92 | debug!("Broke {} N edges", new_blocks.len()); |
3157f602 | 93 | |
3b2f2976 XL |
94 | mir.basic_blocks_mut().extend(new_blocks); |
95 | } | |
3157f602 | 96 | } |