celox_analysis/
cfg_order.rs1use std::hash::Hash;
2
3use fxhash::FxHashMap;
4
5pub fn dominance_order<K, I, F>(entry: K, blocks: I, mut successors: F) -> Vec<K>
11where
12 K: Copy + Eq + Hash + Ord,
13 I: IntoIterator<Item = K>,
14 F: FnMut(K) -> Vec<K>,
15{
16 let mut ids = blocks.into_iter().collect::<Vec<_>>();
17 ids.sort_unstable();
18 ids.dedup();
19
20 if ids.is_empty() {
21 return ids;
22 }
23
24 let Some(entry_index) = ids.iter().position(|&id| id == entry) else {
25 return ids;
26 };
27 let index = ids
28 .iter()
29 .enumerate()
30 .map(|(index, &id)| (id, index))
31 .collect::<FxHashMap<_, _>>();
32
33 let successors = ids
34 .iter()
35 .map(|&id| {
36 let mut successors = successors(id)
37 .into_iter()
38 .filter_map(|successor| index.get(&successor).copied())
39 .collect::<Vec<_>>();
40 successors.dedup();
41 successors
42 })
43 .collect::<Vec<_>>();
44
45 let mut discovered = vec![false; ids.len()];
48 let mut discovery_order = Vec::new();
49 let mut postorder = Vec::new();
50 discovered[entry_index] = true;
51 discovery_order.push(entry_index);
52 let mut stack = vec![(entry_index, false)];
53 while let Some((block, expanded)) = stack.pop() {
54 if expanded {
55 postorder.push(block);
56 continue;
57 }
58
59 if block != entry_index {
60 discovery_order.push(block);
61 }
62 stack.push((block, true));
63 for &successor in successors[block].iter().rev() {
64 if discovered[successor] {
65 continue;
66 }
67 discovered[successor] = true;
68 stack.push((successor, false));
69 }
70 }
71 postorder.reverse();
72
73 let mut predecessors = vec![Vec::new(); ids.len()];
74 for (block, successors) in successors.iter().enumerate() {
75 if !discovered[block] {
76 continue;
77 }
78 for &successor in successors {
79 if discovered[successor] {
80 predecessors[successor].push(block);
81 }
82 }
83 }
84
85 let mut rpo_number = vec![usize::MAX; ids.len()];
88 for (number, &block) in postorder.iter().enumerate() {
89 rpo_number[block] = number;
90 }
91 let mut idom = vec![None; ids.len()];
92 idom[entry_index] = Some(entry_index);
93
94 let intersect = |mut left: usize, mut right: usize, idom: &[Option<usize>]| {
95 while left != right {
96 while rpo_number[left] > rpo_number[right] {
97 left = idom[left].expect("processed dominator must have an idom");
98 }
99 while rpo_number[right] > rpo_number[left] {
100 right = idom[right].expect("processed dominator must have an idom");
101 }
102 }
103 left
104 };
105
106 loop {
107 let mut changed = false;
108 for &block in postorder.iter().skip(1) {
109 let mut processed = predecessors[block]
110 .iter()
111 .copied()
112 .filter(|&predecessor| idom[predecessor].is_some());
113 let Some(first) = processed.next() else {
114 continue;
115 };
116 let next = processed.fold(first, |current, predecessor| {
117 intersect(current, predecessor, &idom)
118 });
119 if idom[block] != Some(next) {
120 idom[block] = Some(next);
121 changed = true;
122 }
123 }
124 if !changed {
125 break;
126 }
127 }
128
129 let mut discovery_position = vec![usize::MAX; ids.len()];
130 for (position, &block) in discovery_order.iter().enumerate() {
131 discovery_position[block] = position;
132 }
133
134 let is_merge = (0..ids.len())
139 .map(|block| {
140 predecessors[block].len() > 1
141 && !predecessors[block]
142 .iter()
143 .copied()
144 .any(|predecessor| dominates_index(block, predecessor, &idom))
145 })
146 .collect::<Vec<_>>();
147
148 let mut children = vec![Vec::new(); ids.len()];
149 for &block in &discovery_order {
150 if block == entry_index {
151 continue;
152 }
153 if let Some(parent) = idom[block] {
154 children[parent].push(block);
155 }
156 }
157 for siblings in &mut children {
158 siblings.sort_unstable_by_key(|&block| (is_merge[block], discovery_position[block]));
159 }
160
161 let mut order = Vec::with_capacity(ids.len());
162 let mut tree_stack = vec![entry_index];
163 while let Some(block) = tree_stack.pop() {
164 order.push(block);
165 tree_stack.extend(children[block].iter().rev().copied());
166 }
167
168 order.extend(
170 ids.iter()
171 .enumerate()
172 .filter(|(index, _)| !discovered[*index])
173 .map(|(index, _)| index),
174 );
175 order.into_iter().map(|index| ids[index]).collect()
176}
177
178fn dominates_index(dominator: usize, mut block: usize, idom: &[Option<usize>]) -> bool {
179 loop {
180 if dominator == block {
181 return true;
182 }
183 let Some(parent) = idom[block] else {
184 return false;
185 };
186 if parent == block {
187 return false;
188 }
189 block = parent;
190 }
191}
192
193#[cfg(test)]
194mod tests {
195 use super::dominance_order;
196
197 #[test]
198 fn emits_dominator_tree_preorder_instead_of_numeric_order() {
199 let order = dominance_order(0, 0..6, |block| match block {
200 0 => vec![4, 1],
201 1 => vec![2],
202 2 => vec![3],
203 4 => vec![5],
204 _ => Vec::new(),
205 });
206
207 assert_eq!(order, vec![0, 4, 5, 1, 2, 3]);
208 }
209
210 #[test]
211 fn appends_unreachable_blocks_deterministically() {
212 let order = dominance_order(0, [4, 0, 3, 2, 1], |block| match block {
213 0 => vec![2],
214 _ => Vec::new(),
215 });
216
217 assert_eq!(order, vec![0, 2, 1, 3, 4]);
218 }
219
220 #[test]
221 fn keeps_a_join_in_dominator_tree_preorder() {
222 let order = dominance_order(0, 0..5, |block| match block {
223 0 => vec![3, 1],
224 1 => vec![2],
225 2 => vec![4],
226 3 => vec![2],
227 _ => Vec::new(),
228 });
229
230 assert_eq!(order, vec![0, 3, 1, 2, 4]);
231 }
232
233 #[test]
234 fn keeps_loop_headers_before_their_backedge_body() {
235 let order = dominance_order(0, 0..4, |block| match block {
236 0 => vec![1],
237 1 => vec![2, 3],
238 2 => vec![1],
239 _ => Vec::new(),
240 });
241
242 assert_eq!(order, vec![0, 1, 2, 3]);
243 }
244}