Skip to content

Commit de70e59

Browse files
committed
Refactor AddCallGuards in two loops.
1 parent 4a54b26 commit de70e59

File tree

1 file changed

+82
-56
lines changed

1 file changed

+82
-56
lines changed

compiler/rustc_mir_transform/src/add_call_guards.rs

Lines changed: 82 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,20 @@
1+
//! Breaks outgoing critical edges for call terminators in the MIR.
2+
//!
3+
//! Critical edges are edges that are neither the only edge leaving a
4+
//! block, nor the only edge entering one.
5+
//!
6+
//! When you want something to happen "along" an edge, you can either
7+
//! do at the end of the predecessor block, or at the start of the
8+
//! successor block. Critical edges have to be broken in order to prevent
9+
//! "edge actions" from affecting other edges. We need this for calls that are
10+
//! codegened to LLVM invoke instructions, because invoke is a block terminator
11+
//! in LLVM so we can't insert any code to handle the call's result into the
12+
//! block that performs the call.
13+
//!
14+
//! This function will break those edges by inserting new blocks along them.
15+
//!
16+
//! NOTE: Simplify CFG will happily undo most of the work this pass does.
17+
118
use rustc_index::{Idx, IndexVec};
219
use rustc_middle::mir::*;
320
use rustc_middle::ty::TyCtxt;
@@ -10,80 +27,48 @@ pub(super) enum AddCallGuards {
1027
}
1128
pub(super) use self::AddCallGuards::*;
1229

13-
/**
14-
* Breaks outgoing critical edges for call terminators in the MIR.
15-
*
16-
* Critical edges are edges that are neither the only edge leaving a
17-
* block, nor the only edge entering one.
18-
*
19-
* When you want something to happen "along" an edge, you can either
20-
* do at the end of the predecessor block, or at the start of the
21-
* successor block. Critical edges have to be broken in order to prevent
22-
* "edge actions" from affecting other edges. We need this for calls that are
23-
* codegened to LLVM invoke instructions, because invoke is a block terminator
24-
* in LLVM so we can't insert any code to handle the call's result into the
25-
* block that performs the call.
26-
*
27-
* This function will break those edges by inserting new blocks along them.
28-
*
29-
* NOTE: Simplify CFG will happily undo most of the work this pass does.
30-
*
31-
*/
32-
3330
impl<'tcx> crate::MirPass<'tcx> for AddCallGuards {
3431
fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
3532
let mut pred_count = IndexVec::from_elem(0u8, &body.basic_blocks);
33+
pred_count[START_BLOCK] = 1; // Add entry edge.
3634
for (_, data) in body.basic_blocks.iter_enumerated() {
3735
for succ in data.terminator().successors() {
3836
pred_count[succ] = pred_count[succ].saturating_add(1);
3937
}
4038
}
4139

42-
// We need a place to store the new blocks generated
43-
let mut new_blocks = Vec::new();
44-
45-
let cur_len = body.basic_blocks.len();
46-
let mut new_block = |source_info: SourceInfo, is_cleanup: bool, target: BasicBlock| {
47-
let block = BasicBlockData::new(
48-
Some(Terminator { source_info, kind: TerminatorKind::Goto { target } }),
49-
is_cleanup,
50-
);
51-
let idx = cur_len + new_blocks.len();
52-
new_blocks.push(block);
53-
BasicBlock::new(idx)
54-
};
40+
enum Action {
41+
Call,
42+
Asm { target_index: usize },
43+
}
5544

56-
for block in body.basic_blocks_mut() {
57-
match block.terminator {
58-
Some(Terminator {
59-
kind: TerminatorKind::Call { target: Some(ref mut destination), unwind, .. },
60-
source_info,
61-
}) if pred_count[*destination] > 1
62-
&& (generates_invoke(unwind) || self == &AllCallEdges) =>
45+
let mut work = Vec::with_capacity(body.basic_blocks.len());
46+
for (bb, block) in body.basic_blocks.iter_enumerated() {
47+
let term = block.terminator();
48+
match term.kind {
49+
TerminatorKind::Call { target: Some(destination), unwind, .. }
50+
if pred_count[destination] > 1
51+
&& (generates_invoke(unwind) || self == &AllCallEdges) =>
6352
{
6453
// It's a critical edge, break it
65-
*destination = new_block(source_info, block.is_cleanup, *destination);
54+
work.push((bb, Action::Call));
6655
}
67-
Some(Terminator {
68-
kind:
69-
TerminatorKind::InlineAsm {
70-
asm_macro: InlineAsmMacro::Asm,
71-
ref mut targets,
72-
ref operands,
73-
unwind,
74-
..
75-
},
76-
source_info,
77-
}) if self == &CriticalCallEdges => {
56+
TerminatorKind::InlineAsm {
57+
asm_macro: InlineAsmMacro::Asm,
58+
ref targets,
59+
ref operands,
60+
unwind,
61+
..
62+
} if self == &CriticalCallEdges => {
7863
let has_outputs = operands.iter().any(|op| {
7964
matches!(op, InlineAsmOperand::InOut { .. } | InlineAsmOperand::Out { .. })
8065
});
8166
let has_labels =
8267
operands.iter().any(|op| matches!(op, InlineAsmOperand::Label { .. }));
8368
if has_outputs && (has_labels || generates_invoke(unwind)) {
84-
for target in targets.iter_mut() {
69+
for (target_index, target) in targets.iter().enumerate() {
8570
if pred_count[*target] > 1 {
86-
*target = new_block(source_info, block.is_cleanup, *target);
71+
work.push((bb, Action::Asm { target_index }));
8772
}
8873
}
8974
}
@@ -92,9 +77,50 @@ impl<'tcx> crate::MirPass<'tcx> for AddCallGuards {
9277
}
9378
}
9479

95-
debug!("Broke {} N edges", new_blocks.len());
80+
if work.is_empty() {
81+
return;
82+
}
83+
84+
// We need a place to store the new blocks generated
85+
let mut new_blocks = Vec::new();
9686

97-
body.basic_blocks_mut().extend(new_blocks);
87+
let cur_len = body.basic_blocks.len();
88+
let mut new_block = |source_info: SourceInfo, is_cleanup: bool, target: BasicBlock| {
89+
let block = BasicBlockData::new(
90+
Some(Terminator { source_info, kind: TerminatorKind::Goto { target } }),
91+
is_cleanup,
92+
);
93+
let idx = cur_len + new_blocks.len();
94+
new_blocks.push(block);
95+
BasicBlock::new(idx)
96+
};
97+
98+
let basic_blocks = body.basic_blocks.as_mut();
99+
for (source, action) in work {
100+
let block = &mut basic_blocks[source];
101+
let is_cleanup = block.is_cleanup;
102+
let term = block.terminator_mut();
103+
let source_info = term.source_info;
104+
match action {
105+
Action::Call => {
106+
let TerminatorKind::Call { target: Some(ref mut destination), .. } = term.kind
107+
else {
108+
unreachable!()
109+
};
110+
*destination = new_block(source_info, is_cleanup, *destination);
111+
}
112+
Action::Asm { target_index } => {
113+
let TerminatorKind::InlineAsm { ref mut targets, .. } = term.kind else {
114+
unreachable!()
115+
};
116+
targets[target_index] =
117+
new_block(source_info, is_cleanup, targets[target_index]);
118+
}
119+
}
120+
}
121+
122+
debug!("Broke {} N edges", new_blocks.len());
123+
basic_blocks.extend(new_blocks);
98124
}
99125

100126
fn is_required(&self) -> bool {

0 commit comments

Comments
 (0)