Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/cranelift/codegen/src/flowgraph.rs
1693 views
1
//! A control flow graph represented as mappings of basic blocks to their predecessors
2
//! and successors.
3
//!
4
//! Successors are represented as basic blocks while predecessors are represented by basic
5
//! blocks. Basic blocks are denoted by tuples of block and branch/jump instructions. Each
6
//! predecessor tuple corresponds to the end of a basic block.
7
//!
8
//! ```c
9
//! Block0:
10
//! ... ; beginning of basic block
11
//!
12
//! ...
13
//!
14
//! brif vx, Block1, Block2 ; end of basic block
15
//!
16
//! Block1:
17
//! jump block3
18
//! ```
19
//!
20
//! Here `Block1` and `Block2` would each have a single predecessor denoted as `(Block0, brif)`,
21
//! while `Block3` would have a single predecessor denoted as `(Block1, jump block3)`.
22
23
use crate::bforest;
24
use crate::entity::SecondaryMap;
25
use crate::inst_predicates;
26
use crate::ir::{Block, Function, Inst};
27
use crate::timing;
28
use core::mem;
29
30
/// A basic block denoted by its enclosing Block and last instruction.
31
#[derive(Debug, PartialEq, Eq)]
32
pub struct BlockPredecessor {
33
/// Enclosing Block key.
34
pub block: Block,
35
/// Last instruction in the basic block.
36
pub inst: Inst,
37
}
38
39
impl BlockPredecessor {
40
/// Convenient method to construct new BlockPredecessor.
41
pub fn new(block: Block, inst: Inst) -> Self {
42
Self { block, inst }
43
}
44
}
45
46
/// A container for the successors and predecessors of some Block.
47
#[derive(Clone, Default)]
48
struct CFGNode {
49
/// Instructions that can branch or jump to this block.
50
///
51
/// This maps branch instruction -> predecessor block which is redundant since the block containing
52
/// the branch instruction is available from the `layout.inst_block()` method. We store the
53
/// redundant information because:
54
///
55
/// 1. Many `pred_iter()` consumers want the block anyway, so it is handily available.
56
/// 2. The `invalidate_block_successors()` may be called *after* branches have been removed from
57
/// their block, but we still need to remove them form the old block predecessor map.
58
///
59
/// The redundant block stored here is always consistent with the CFG successor lists, even after
60
/// the IR has been edited.
61
pub predecessors: bforest::Map<Inst, Block>,
62
63
/// Set of blocks that are the targets of branches and jumps in this block.
64
/// The set is ordered by block number, indicated by the `()` comparator type.
65
pub successors: bforest::Set<Block>,
66
}
67
68
/// The Control Flow Graph maintains a mapping of blocks to their predecessors
69
/// and successors where predecessors are basic blocks and successors are
70
/// basic blocks.
71
pub struct ControlFlowGraph {
72
data: SecondaryMap<Block, CFGNode>,
73
pred_forest: bforest::MapForest<Inst, Block>,
74
succ_forest: bforest::SetForest<Block>,
75
valid: bool,
76
}
77
78
impl ControlFlowGraph {
79
/// Allocate a new blank control flow graph.
80
pub fn new() -> Self {
81
Self {
82
data: SecondaryMap::new(),
83
valid: false,
84
pred_forest: bforest::MapForest::new(),
85
succ_forest: bforest::SetForest::new(),
86
}
87
}
88
89
/// Clear all data structures in this control flow graph.
90
pub fn clear(&mut self) {
91
self.data.clear();
92
self.pred_forest.clear();
93
self.succ_forest.clear();
94
self.valid = false;
95
}
96
97
/// Allocate and compute the control flow graph for `func`.
98
pub fn with_function(func: &Function) -> Self {
99
let mut cfg = Self::new();
100
cfg.compute(func);
101
cfg
102
}
103
104
/// Compute the control flow graph of `func`.
105
///
106
/// This will clear and overwrite any information already stored in this data structure.
107
pub fn compute(&mut self, func: &Function) {
108
let _tt = timing::flowgraph();
109
self.clear();
110
self.data.resize(func.dfg.num_blocks());
111
112
for block in &func.layout {
113
self.compute_block(func, block);
114
}
115
116
self.valid = true;
117
}
118
119
fn compute_block(&mut self, func: &Function, block: Block) {
120
inst_predicates::visit_block_succs(func, block, |inst, dest, _| {
121
self.add_edge(block, inst, dest);
122
});
123
}
124
125
fn invalidate_block_successors(&mut self, block: Block) {
126
// Temporarily take ownership because we need mutable access to self.data inside the loop.
127
// Unfortunately borrowck cannot see that our mut accesses to predecessors don't alias
128
// our iteration over successors.
129
let mut successors = mem::replace(&mut self.data[block].successors, Default::default());
130
for succ in successors.iter(&self.succ_forest) {
131
self.data[succ]
132
.predecessors
133
.retain(&mut self.pred_forest, |_, &mut e| e != block);
134
}
135
successors.clear(&mut self.succ_forest);
136
}
137
138
/// Recompute the control flow graph of `block`.
139
///
140
/// This is for use after modifying instructions within a specific block. It recomputes all edges
141
/// from `block` while leaving edges to `block` intact. Its functionality a subset of that of the
142
/// more expensive `compute`, and should be used when we know we don't need to recompute the CFG
143
/// from scratch, but rather that our changes have been restricted to specific blocks.
144
pub fn recompute_block(&mut self, func: &Function, block: Block) {
145
debug_assert!(self.is_valid());
146
self.invalidate_block_successors(block);
147
self.compute_block(func, block);
148
}
149
150
fn add_edge(&mut self, from: Block, from_inst: Inst, to: Block) {
151
self.data[from]
152
.successors
153
.insert(to, &mut self.succ_forest, &());
154
self.data[to]
155
.predecessors
156
.insert(from_inst, from, &mut self.pred_forest, &());
157
}
158
159
/// Get an iterator over the CFG predecessors to `block`.
160
pub fn pred_iter(&self, block: Block) -> PredIter<'_> {
161
PredIter(self.data[block].predecessors.iter(&self.pred_forest))
162
}
163
164
/// Get an iterator over the CFG successors to `block`.
165
pub fn succ_iter(&self, block: Block) -> SuccIter<'_> {
166
debug_assert!(self.is_valid());
167
self.data[block].successors.iter(&self.succ_forest)
168
}
169
170
/// Check if the CFG is in a valid state.
171
///
172
/// Note that this doesn't perform any kind of validity checks. It simply checks if the
173
/// `compute()` method has been called since the last `clear()`. It does not check that the
174
/// CFG is consistent with the function.
175
pub fn is_valid(&self) -> bool {
176
self.valid
177
}
178
}
179
180
/// An iterator over block predecessors. The iterator type is `BlockPredecessor`.
181
///
182
/// Each predecessor is an instruction that branches to the block.
183
pub struct PredIter<'a>(bforest::MapIter<'a, Inst, Block>);
184
185
impl<'a> Iterator for PredIter<'a> {
186
type Item = BlockPredecessor;
187
188
fn next(&mut self) -> Option<BlockPredecessor> {
189
self.0.next().map(|(i, e)| BlockPredecessor::new(e, i))
190
}
191
}
192
193
/// An iterator over block successors. The iterator type is `Block`.
194
pub type SuccIter<'a> = bforest::SetIter<'a, Block>;
195
196
#[cfg(test)]
197
mod tests {
198
use super::*;
199
use crate::cursor::{Cursor, FuncCursor};
200
use crate::ir::{InstBuilder, types};
201
use alloc::vec::Vec;
202
203
#[test]
204
fn empty() {
205
let func = Function::new();
206
ControlFlowGraph::with_function(&func);
207
}
208
209
#[test]
210
fn no_predecessors() {
211
let mut func = Function::new();
212
let block0 = func.dfg.make_block();
213
let block1 = func.dfg.make_block();
214
let block2 = func.dfg.make_block();
215
func.layout.append_block(block0);
216
func.layout.append_block(block1);
217
func.layout.append_block(block2);
218
219
let cfg = ControlFlowGraph::with_function(&func);
220
221
let mut fun_blocks = func.layout.blocks();
222
for block in func.layout.blocks() {
223
assert_eq!(block, fun_blocks.next().unwrap());
224
assert_eq!(cfg.pred_iter(block).count(), 0);
225
assert_eq!(cfg.succ_iter(block).count(), 0);
226
}
227
}
228
229
#[test]
230
fn branches_and_jumps() {
231
let mut func = Function::new();
232
let block0 = func.dfg.make_block();
233
let cond = func.dfg.append_block_param(block0, types::I32);
234
let block1 = func.dfg.make_block();
235
let block2 = func.dfg.make_block();
236
237
let br_block0_block2_block1;
238
let br_block1_block1_block2;
239
240
{
241
let mut cur = FuncCursor::new(&mut func);
242
243
cur.insert_block(block0);
244
br_block0_block2_block1 = cur.ins().brif(cond, block2, &[], block1, &[]);
245
246
cur.insert_block(block1);
247
br_block1_block1_block2 = cur.ins().brif(cond, block1, &[], block2, &[]);
248
249
cur.insert_block(block2);
250
}
251
252
let mut cfg = ControlFlowGraph::with_function(&func);
253
254
{
255
let block0_predecessors = cfg.pred_iter(block0).collect::<Vec<_>>();
256
let block1_predecessors = cfg.pred_iter(block1).collect::<Vec<_>>();
257
let block2_predecessors = cfg.pred_iter(block2).collect::<Vec<_>>();
258
259
let block0_successors = cfg.succ_iter(block0).collect::<Vec<_>>();
260
let block1_successors = cfg.succ_iter(block1).collect::<Vec<_>>();
261
let block2_successors = cfg.succ_iter(block2).collect::<Vec<_>>();
262
263
assert_eq!(block0_predecessors.len(), 0);
264
assert_eq!(block1_predecessors.len(), 2);
265
assert_eq!(block2_predecessors.len(), 2);
266
267
assert_eq!(
268
block1_predecessors
269
.contains(&BlockPredecessor::new(block0, br_block0_block2_block1)),
270
true
271
);
272
assert_eq!(
273
block1_predecessors
274
.contains(&BlockPredecessor::new(block1, br_block1_block1_block2)),
275
true
276
);
277
assert_eq!(
278
block2_predecessors
279
.contains(&BlockPredecessor::new(block0, br_block0_block2_block1)),
280
true
281
);
282
assert_eq!(
283
block2_predecessors
284
.contains(&BlockPredecessor::new(block1, br_block1_block1_block2)),
285
true
286
);
287
288
assert_eq!(block0_successors, [block1, block2]);
289
assert_eq!(block1_successors, [block1, block2]);
290
assert_eq!(block2_successors, []);
291
}
292
293
// Add a new block to hold a return instruction
294
let ret_block = func.dfg.make_block();
295
296
{
297
let mut cur = FuncCursor::new(&mut func);
298
cur.insert_block(ret_block);
299
cur.ins().return_(&[]);
300
}
301
302
// Change some instructions and recompute block0 and ret_block
303
func.dfg
304
.replace(br_block0_block2_block1)
305
.brif(cond, block1, &[], ret_block, &[]);
306
cfg.recompute_block(&func, block0);
307
cfg.recompute_block(&func, ret_block);
308
let br_block0_block1_ret_block = br_block0_block2_block1;
309
310
{
311
let block0_predecessors = cfg.pred_iter(block0).collect::<Vec<_>>();
312
let block1_predecessors = cfg.pred_iter(block1).collect::<Vec<_>>();
313
let block2_predecessors = cfg.pred_iter(block2).collect::<Vec<_>>();
314
315
let block0_successors = cfg.succ_iter(block0);
316
let block1_successors = cfg.succ_iter(block1);
317
let block2_successors = cfg.succ_iter(block2);
318
319
assert_eq!(block0_predecessors.len(), 0);
320
assert_eq!(block1_predecessors.len(), 2);
321
assert_eq!(block2_predecessors.len(), 1);
322
323
assert_eq!(
324
block1_predecessors
325
.contains(&BlockPredecessor::new(block0, br_block0_block1_ret_block)),
326
true
327
);
328
assert_eq!(
329
block1_predecessors
330
.contains(&BlockPredecessor::new(block1, br_block1_block1_block2)),
331
true
332
);
333
assert_eq!(
334
block2_predecessors
335
.contains(&BlockPredecessor::new(block0, br_block0_block1_ret_block)),
336
false
337
);
338
assert_eq!(
339
block2_predecessors
340
.contains(&BlockPredecessor::new(block1, br_block1_block1_block2)),
341
true
342
);
343
344
assert_eq!(block0_successors.collect::<Vec<_>>(), [block1, ret_block]);
345
assert_eq!(block1_successors.collect::<Vec<_>>(), [block1, block2]);
346
assert_eq!(block2_successors.collect::<Vec<_>>(), []);
347
}
348
}
349
}
350
351