1use alloc::vec;
10use alloc::vec::Vec;
11
12use super::entity::Block;
13use super::function::Function;
14use super::operand::Operand;
15use super::verify::MirError;
16use super::visit::Access;
17use crate::target::InstKind;
18
19pub(crate) const NONE: u32 = u32::MAX;
20
21#[derive(Clone, Debug, Default)]
23pub(crate) struct Cfg {
24 succ_start: Vec<u32>,
25 succs: Vec<u32>,
26 pred_start: Vec<u32>,
27 preds: Vec<u32>,
28 pub(crate) rpo: Vec<u32>,
30 pub(crate) rpo_index: Vec<u32>,
32 pub(crate) idom: Vec<u32>,
34 pre: Vec<u32>,
35 post: Vec<u32>,
36}
37
38pub(crate) fn for_each_succ(f: &Function, block: Block, mut visit: impl FnMut(Block)) {
41 let insts = f.insts(block);
42 if let Some(&last) = insts.last() {
43 let kind = f
44 .opcode(last)
45 .and_then(|o| f.target().inst(o))
46 .map(|d| d.kind());
47 if kind.is_some_and(InstKind::is_terminator) {
48 for op in f.operands(last) {
49 if let Operand::Block(call) = op {
50 visit(call.block());
51 }
52 }
53 }
54 }
55 if !f.sites.is_empty() {
56 for &inst in insts {
57 if let Some(pad) = f.call_site(inst).and_then(|s| s.landing_pad()) {
58 visit(pad);
59 }
60 }
61 }
62}
63
64impl Cfg {
65 pub(crate) fn new(f: &Function) -> Cfg {
67 let n = f.block_count();
68 let mut cfg = Cfg {
69 succ_start: Vec::with_capacity(n + 1),
70 ..Cfg::default()
71 };
72 for b in f.blocks() {
73 cfg.succ_start.push(cfg.succs.len() as u32);
74 for_each_succ(f, b, |s| {
75 if s.index() < n {
76 cfg.succs.push(s.index() as u32);
77 }
78 });
79 }
80 cfg.succ_start.push(cfg.succs.len() as u32);
81
82 let mut count = vec![0u32; n + 1];
84 for &s in &cfg.succs {
85 count[s as usize + 1] += 1;
86 }
87 for i in 0..n {
88 count[i + 1] += count[i];
89 }
90 cfg.pred_start = count.clone();
91 let mut preds = vec![0; cfg.succs.len()];
92 for b in 0..n {
93 for &s in cfg.succ_slice(b) {
94 let at = &mut count[s as usize];
95 preds[*at as usize] = b as u32;
96 *at += 1;
97 }
98 }
99 cfg.preds = preds;
100
101 cfg.rpo_index = vec![NONE; n];
103 if n > 0 {
104 let mut post = Vec::with_capacity(n);
105 let mut seen = vec![false; n];
106 let mut stack: Vec<(u32, u32)> = vec![(0, 0)];
107 seen[0] = true;
108 while let Some(top) = stack.last_mut() {
109 let (b, k) = *top;
110 let succs = cfg.succ_slice(b as usize);
111 if let Some(&s) = succs.get(k as usize) {
112 top.1 += 1;
113 if !seen[s as usize] {
114 seen[s as usize] = true;
115 stack.push((s, 0));
116 }
117 } else {
118 post.push(b);
119 let _ = stack.pop();
120 }
121 }
122 post.reverse();
123 for (i, &b) in post.iter().enumerate() {
124 cfg.rpo_index[b as usize] = i as u32;
125 }
126 cfg.rpo = post;
127 }
128 cfg.dominators();
129 cfg
130 }
131
132 fn succ_slice(&self, b: usize) -> &[u32] {
133 let (s, e) = (self.succ_start[b] as usize, self.succ_start[b + 1] as usize);
134 self.succs.get(s..e).unwrap_or(&[])
135 }
136
137 pub(crate) fn succs(&self, b: usize) -> &[u32] {
139 if b + 1 < self.succ_start.len() {
140 self.succ_slice(b)
141 } else {
142 &[]
143 }
144 }
145
146 pub(crate) fn preds(&self, b: usize) -> &[u32] {
148 match (self.pred_start.get(b), self.pred_start.get(b + 1)) {
149 (Some(&s), Some(&e)) => self.preds.get(s as usize..e as usize).unwrap_or(&[]),
150 _ => &[],
151 }
152 }
153
154 pub(crate) fn reachable(&self, b: usize) -> bool {
156 self.rpo_index.get(b).is_some_and(|&i| i != NONE)
157 }
158
159 fn dominators(&mut self) {
160 let n = self.rpo_index.len();
161 self.idom = vec![NONE; n];
162 let Some(&entry) = self.rpo.first() else {
163 return;
164 };
165 self.lengauer_tarjan(entry);
166 let mut child_start = vec![0u32; n + 1];
169 for &b in &self.rpo {
170 let d = self.idom[b as usize];
171 if d != b && d != NONE {
172 child_start[d as usize + 1] += 1;
173 }
174 }
175 for i in 0..n {
176 child_start[i + 1] += child_start[i];
177 }
178 let mut fill = child_start.clone();
179 let mut children = vec![0u32; child_start[n] as usize];
180 for &b in &self.rpo {
181 let d = self.idom[b as usize];
182 if d != b && d != NONE {
183 children[fill[d as usize] as usize] = b;
184 fill[d as usize] += 1;
185 }
186 }
187 self.pre = vec![NONE; n];
188 self.post = vec![NONE; n];
189 let (mut pre, mut post) = (0u32, 0u32);
190 let mut stack: Vec<(u32, u32)> = vec![(entry, child_start[entry as usize])];
191 self.pre[entry as usize] = pre;
192 pre += 1;
193 while let Some(top) = stack.last_mut() {
194 let (b, k) = *top;
195 if k < child_start[b as usize + 1] {
196 top.1 += 1;
197 let c = children[k as usize];
198 self.pre[c as usize] = pre;
199 pre += 1;
200 stack.push((c, child_start[c as usize]));
201 } else {
202 self.post[b as usize] = post;
203 post += 1;
204 let _ = stack.pop();
205 }
206 }
207 }
208
209 fn lengauer_tarjan(&mut self, entry: u32) {
215 let n = self.rpo_index.len();
216 let mut dfn = vec![0u32; n];
218 let mut vertex: Vec<u32> = Vec::with_capacity(n + 1);
219 vertex.push(NONE);
220 let mut parent = vec![0u32; n + 1];
221 let mut stack: Vec<(u32, u32)> = vec![(entry, 0)];
222 dfn[entry as usize] = 1;
223 vertex.push(entry);
224 while let Some(top) = stack.last_mut() {
225 let (b, k) = *top;
226 let succs = self.succ_slice(b as usize);
227 if let Some(&s) = succs.get(k as usize) {
228 top.1 += 1;
229 if dfn[s as usize] == 0 {
230 let num = vertex.len() as u32;
231 dfn[s as usize] = num;
232 vertex.push(s);
233 parent[num as usize] = dfn[b as usize];
234 stack.push((s, 0));
235 }
236 } else {
237 let _ = stack.pop();
238 }
239 }
240 let count = vertex.len() - 1;
241 let mut semi: Vec<u32> = (0..=count as u32).collect();
243 let mut label: Vec<u32> = (0..=count as u32).collect();
244 let mut ancestor = vec![0u32; count + 1];
245 let mut idom = vec![0u32; count + 1];
246 let mut bucket_head = vec![0u32; count + 1];
247 let mut bucket_next = vec![0u32; count + 1];
248 let mut path: Vec<u32> = Vec::new();
249 let mut eval =
252 |v: u32, ancestor: &mut Vec<u32>, label: &mut Vec<u32>, semi: &Vec<u32>| -> u32 {
253 if ancestor[v as usize] == 0 {
254 return v;
255 }
256 path.clear();
257 let mut x = v;
258 while ancestor[ancestor[x as usize] as usize] != 0 {
259 path.push(x);
260 x = ancestor[x as usize];
261 }
262 while let Some(y) = path.pop() {
264 let a = ancestor[y as usize];
265 if semi[label[a as usize] as usize] < semi[label[y as usize] as usize] {
266 label[y as usize] = label[a as usize];
267 }
268 ancestor[y as usize] = ancestor[a as usize];
269 }
270 label[v as usize]
271 };
272 for w in (2..=count as u32).rev() {
273 let block = vertex[w as usize];
274 for &p in self.preds(block as usize) {
275 let v = dfn[p as usize];
276 if v == 0 {
277 continue;
278 }
279 let u = eval(v, &mut ancestor, &mut label, &semi);
280 if semi[u as usize] < semi[w as usize] {
281 semi[w as usize] = semi[u as usize];
282 }
283 }
284 let sd = semi[w as usize] as usize;
286 bucket_next[w as usize] = bucket_head[sd];
287 bucket_head[sd] = w;
288 let pw = parent[w as usize];
289 ancestor[w as usize] = pw;
290 let mut v = bucket_head[pw as usize];
291 bucket_head[pw as usize] = 0;
292 while v != 0 {
293 let next = bucket_next[v as usize];
294 let u = eval(v, &mut ancestor, &mut label, &semi);
295 idom[v as usize] = if semi[u as usize] < semi[v as usize] {
296 u
297 } else {
298 pw
299 };
300 v = next;
301 }
302 }
303 for w in 2..=count {
304 if idom[w] != semi[w] {
305 idom[w] = idom[idom[w] as usize];
306 }
307 }
308 self.idom[entry as usize] = entry;
309 for w in 2..=count {
310 self.idom[vertex[w] as usize] = vertex[idom[w] as usize];
311 }
312 }
313
314 pub(crate) fn dominates(&self, a: usize, b: usize) -> bool {
316 match (
317 self.pre.get(a),
318 self.pre.get(b),
319 self.post.get(a),
320 self.post.get(b),
321 ) {
322 (Some(&pa), Some(&pb), Some(&qa), Some(&qb)) => {
323 pa != NONE && pb != NONE && pa <= pb && qb <= qa
324 }
325 _ => false,
326 }
327 }
328}
329
330impl Function {
331 pub fn verify_ssa(&self) -> Result<(), MirError> {
361 self.verify()?;
362 let cfg = Cfg::new(self);
363 self.check_dominance(&cfg)
364 }
365
366 pub(crate) fn check_dominance(&self, cfg: &Cfg) -> Result<(), MirError> {
367 let mut def_block = vec![NONE; self.vreg_count()];
370 let mut def_pos = vec![0u32; self.vreg_count()];
371 for b in self.blocks() {
372 for p in self.params(b) {
373 if let Some(v) = p.reg().and_then(crate::Reg::as_virt) {
374 if let Some(d) = def_block.get_mut(v.index()) {
375 *d = b.index() as u32;
376 }
377 }
378 }
379 for (k, &inst) in self.insts(b).iter().enumerate() {
380 self.visit_regs(inst, |_, r, access| {
381 if let (Access::Def, Some(v)) = (access, r.as_virt()) {
382 if let Some(d) = def_block.get_mut(v.index()) {
383 *d = b.index() as u32;
384 def_pos[v.index()] = k as u32 + 1;
385 }
386 }
387 });
388 }
389 }
390 for &b in &cfg.rpo {
391 let block = Block::from_index(b as usize);
392 for (k, &inst) in self.insts(block).iter().enumerate() {
393 let mut err = None;
394 self.visit_regs(inst, |_, r, access| {
395 let Some(v) = r.as_virt() else { return };
396 if access != Access::Use || err.is_some() {
397 return;
398 }
399 let d = def_block.get(v.index()).copied().unwrap_or(NONE);
400 if d == NONE {
401 err = Some(MirError::UndefinedVReg { vreg: v });
402 } else if d == b {
403 if def_pos[v.index()] > k as u32 {
404 err = Some(MirError::NotDominated { vreg: v, inst });
405 }
406 } else if !cfg.dominates(d as usize, b as usize) {
407 err = Some(MirError::NotDominated { vreg: v, inst });
408 }
409 });
410 if let Some(e) = err {
411 return Err(e);
412 }
413 }
414 }
415 Ok(())
416 }
417}
418
419#[cfg(test)]
420#[allow(
421 clippy::needless_range_loop,
422 reason = "the reference compares dominator sets by block index"
423)]
424mod tests {
425 use alloc::vec;
426 use alloc::vec::Vec;
427
428 use proptest::prelude::*;
429
430 use super::*;
431 use crate::mir::Operand;
432 use crate::x86_64::{self, SYSV, op};
433
434 fn graph(succs: &[Vec<usize>]) -> Function {
437 let mut f = Function::new("g", &SYSV);
438 let blocks: Vec<Block> = succs
439 .iter()
440 .map(|_| f.block().unwrap_or(Block::new(0)))
441 .collect();
442 for (i, s) in succs.iter().enumerate() {
443 let b = blocks[i];
444 let call = |f: &mut Function, t: usize| f.block_call(blocks[t], &[]).map(Operand::from);
445 let r = match s.len() {
446 0 => f.push(b, op::RET, &[]),
447 1 => call(&mut f, s[0]).and_then(|c| f.push(b, op::JMP, &[c])),
448 2 => {
449 let (t, e) = (call(&mut f, s[0]), call(&mut f, s[1]));
450 match (t, e) {
451 (Ok(t), Ok(e)) => f.push(
452 b,
453 op::JCC,
454 &[x86_64::cond(isa_lang::x86_64::Cond::E).into(), t, e],
455 ),
456 (Err(e), _) | (_, Err(e)) => Err(e),
457 }
458 }
459 _ => {
460 let t = f.vreg(x86_64::GPR).and_then(|t| {
461 let i = f.vreg(x86_64::GPR)?;
462 Ok((t, i))
463 });
464 match t {
465 Ok((t, i)) => {
466 let mut ops = vec![Operand::from(t), Operand::from(i)];
467 for &k in s {
468 if let Ok(c) = call(&mut f, k) {
469 ops.push(c);
470 }
471 }
472 f.push(b, op::JMPTBL, &ops)
473 }
474 Err(e) => Err(e),
475 }
476 }
477 };
478 assert!(r.is_ok(), "{r:?}");
479 }
480 f
481 }
482
483 fn reference(cfg: &Cfg, n: usize) -> Vec<Option<Vec<bool>>> {
485 let mut dom: Vec<Option<Vec<bool>>> = vec![None; n];
486 dom[0] = Some({
487 let mut v = vec![false; n];
488 v[0] = true;
489 v
490 });
491 let mut changed = true;
492 while changed {
493 changed = false;
494 for b in 1..n {
495 if !cfg.reachable(b) {
496 continue;
497 }
498 let mut acc: Option<Vec<bool>> = None;
499 for &p in cfg.preds(b) {
500 if let Some(d) = &dom[p as usize] {
501 acc = Some(match acc {
502 None => d.clone(),
503 Some(a) => a.iter().zip(d).map(|(x, y)| *x && *y).collect(),
504 });
505 }
506 }
507 if let Some(mut a) = acc {
508 a[b] = true;
509 if dom[b].as_ref() != Some(&a) {
510 dom[b] = Some(a);
511 changed = true;
512 }
513 }
514 }
515 }
516 dom
517 }
518
519 proptest! {
520 #![proptest_config(ProptestConfig::with_cases(400))]
521
522 #[test]
523 fn prop_dominators_match_the_dataflow_reference(
524 edges in prop::collection::vec(prop::collection::vec(0usize..24, 0..4), 1..24)
525 ) {
526 let n = edges.len();
527 let succs: Vec<Vec<usize>> = edges
528 .iter()
529 .map(|s| s.iter().map(|&t| t % n).collect::<Vec<_>>())
530 .map(|s: Vec<usize>| if s.len() == 2 && s[0] == s[1] { vec![s[0]] } else { s })
531 .collect();
532 let f = graph(&succs);
533 let cfg = Cfg::new(&f);
534 let dom = reference(&cfg, n);
535 for b in 0..n {
536 prop_assert_eq!(cfg.reachable(b), dom[b].is_some());
537 let Some(d) = &dom[b] else { continue };
538 for a in 0..n {
539 prop_assert_eq!(cfg.dominates(a, b), d[a], "{} dom {}", a, b);
540 }
541 if b != 0 {
542 let idom = cfg.idom[b] as usize;
545 prop_assert!(d[idom] && idom != b);
546 for a in 0..n {
547 if d[a] && a != b {
548 prop_assert!(cfg.dominates(a, idom));
549 }
550 }
551 }
552 }
553 }
554 }
555
556 #[test]
557 fn test_many_predecessors_deep_in_the_tree_stay_linear() {
558 let n = 20_000;
561 let mut succs: Vec<Vec<usize>> = (0..n).map(|i| vec![i + 1, n]).collect();
562 succs.push(vec![]); succs[n - 1] = vec![n];
564 let f = graph(&succs);
565 let cfg = Cfg::new(&f);
566 assert_eq!(cfg.idom[n] as usize, 0);
567 assert!(cfg.dominates(0, n) && cfg.dominates(n - 1, n - 1));
568 }
569}