yz_basic_block/arena/
check.rs

1use super::*;
2
3fn check_finish(mut offending: Vec<(BbId, BbId)>) -> Result<(), OffendingIds> {
4    if offending.is_empty() {
5        Ok(())
6    } else {
7        offending.sort();
8        offending.dedup();
9        offending.shrink_to_fit();
10        Err(OffendingIds(offending))
11    }
12}
13
14impl<S, C> Arena<S, C>
15where
16    ABB<S, C>: ForeachTarget<JumpTarget = BbId>,
17{
18    fn check_intern(&self, bbid: BbId, bb: &ABB<S, C>, offending: &mut Vec<(BbId, BbId)>) {
19        bb.foreach_target(|&t| {
20            if t != bbid && self.bbs.get(&t).is_none() {
21                offending.push((bbid, t));
22            }
23        });
24    }
25
26    fn check_bbs(&self) -> Vec<(BbId, BbId)> {
27        let mut errs = Vec::new();
28        for (&n, i) in self.bbs.iter() {
29            self.check_intern(n, i, &mut errs);
30        }
31        errs
32    }
33
34    /// Use this method to re-check all references in the `Arena` after
35    /// modifications via [`Arena::bbs_mut`].
36    pub fn check(&self) -> Result<(), OffendingIds> {
37        let mut errs = self.check_bbs();
38        // all labels should point to a valid BbId
39        errs.extend(self.labels.values().filter_map(|&i| {
40            if self.bbs.get(&i).is_none() {
41                Some((i, i))
42            } else {
43                None
44            }
45        }));
46        // all placeholders should have label(s)
47        for (&n, i) in self.bbs.iter() {
48            if i.inner.is_placeholder() && self.labels_of_bb(n).next().is_none() {
49                errs.push((n, n));
50            }
51        }
52        check_finish(errs)
53    }
54
55    fn find_first_free(&self) -> Option<usize> {
56        for i in self.cache_ins_start..usize::MAX {
57            if self.bbs.get(&i).is_none() {
58                return Some(i);
59            }
60        }
61        None
62    }
63
64    /// Returns the ID of the newly appended BB if successful,
65    /// or $bb & the invalid BbIds.
66    pub fn push(&mut self, bb: ABB<S, C>) -> Result<usize, (ABB<S, C>, OffendingIds)> {
67        let ret = match self.find_first_free() {
68            Some(n) => n,
69            None => return Err((bb, OffendingIds(Vec::new()))),
70        };
71        let mut errs = Vec::new();
72        self.check_intern(ret, &bb, &mut errs);
73        match check_finish(errs) {
74            Ok(()) => {
75                self.bbs.insert(ret, bb);
76                self.cache_ins_start = ret.saturating_add(1);
77                Ok(ret)
78            }
79            Err(errs) => Err((bb, errs)),
80        }
81    }
82
83    /// Removes a BB, fails if any references to it exist.
84    /// If successful, returns the removed BB and all labels which referenced it.
85    /// Otherwise, returns the offending BBs (which still reference it)
86    pub fn remove(&mut self, bbid: BbId) -> Option<Result<(ABB<S, C>, Vec<String>), OffendingIds>> {
87        let x = self.bbs.remove(&bbid)?;
88        let offending = self.check_bbs();
89        Some(if offending.is_empty() {
90            let (labelrt, rlabels) = take(&mut self.labels)
91                .into_iter()
92                .partition(|&(_, v)| v == bbid);
93            self.labels = rlabels;
94            if bbid < self.cache_ins_start {
95                self.cache_ins_start = bbid;
96            }
97            Ok((x, labelrt.into_iter().map(|x| x.0).collect()))
98        } else {
99            self.bbs.insert(bbid, x);
100            Err(OffendingIds(offending))
101        })
102    }
103}