Skip to main content

probl_sema/
liveness.rs

1//! Which variables may still be read after each statement.
2//!
3//! The engine merges worlds whose *live* variables are equal, and clears dead
4//! ones, so that worlds differing only in values nobody reads again collapse
5//! into one.
6
7use crate::ir::*;
8
9/// A fixed-size set of slot ids.
10#[derive(Clone, Debug, PartialEq, Eq, Default)]
11pub struct SlotSet {
12    words: Vec<u64>,
13}
14
15impl SlotSet {
16    pub fn with_capacity(n: usize) -> SlotSet {
17        SlotSet {
18            words: vec![0; n.div_ceil(64)],
19        }
20    }
21
22    pub fn insert(&mut self, slot: SlotId) {
23        let (w, b) = (slot as usize / 64, slot as usize % 64);
24        if w >= self.words.len() {
25            self.words.resize(w + 1, 0);
26        }
27        self.words[w] |= 1 << b;
28    }
29
30    pub fn remove(&mut self, slot: SlotId) {
31        let (w, b) = (slot as usize / 64, slot as usize % 64);
32        if w < self.words.len() {
33            self.words[w] &= !(1 << b);
34        }
35    }
36
37    pub fn contains(&self, slot: SlotId) -> bool {
38        let (w, b) = (slot as usize / 64, slot as usize % 64);
39        w < self.words.len() && self.words[w] & (1 << b) != 0
40    }
41
42    pub fn union_with(&mut self, other: &SlotSet) {
43        if other.words.len() > self.words.len() {
44            self.words.resize(other.words.len(), 0);
45        }
46        for (a, b) in self.words.iter_mut().zip(&other.words) {
47            *a |= b;
48        }
49    }
50
51    pub fn iter(&self) -> impl Iterator<Item = SlotId> + '_ {
52        self.words
53            .iter()
54            .enumerate()
55            .flat_map(|(w, &bits)| Bits(bits).map(move |b| (w * 64) as SlotId + b))
56    }
57
58    /// The slots below `n` that aren't in the set.
59    pub fn iter_missing(&self, n: usize) -> impl Iterator<Item = SlotId> + '_ {
60        (0..n.div_ceil(64)).flat_map(move |w| {
61            let present = self.words.get(w).copied().unwrap_or(0);
62            let below = if n - w * 64 >= 64 {
63                u64::MAX
64            } else {
65                (1 << (n - w * 64)) - 1
66            };
67            Bits(!present & below).map(move |b| (w * 64) as SlotId + b)
68        })
69    }
70
71    fn same(&self, other: &SlotSet) -> bool {
72        let n = self.words.len().max(other.words.len());
73        (0..n).all(|i| self.words.get(i).copied().unwrap_or(0) == other.words.get(i).copied().unwrap_or(0))
74    }
75}
76
77/// The positions of the bits set in a word, lowest first.
78struct Bits(u64);
79
80impl Iterator for Bits {
81    type Item = SlotId;
82
83    fn next(&mut self) -> Option<SlotId> {
84        if self.0 == 0 {
85            return None;
86        }
87        let b = self.0.trailing_zeros();
88        self.0 &= self.0 - 1;
89        Some(b)
90    }
91}
92
93/// Liveness for every statement of a program.
94#[derive(Debug)]
95pub struct Liveness {
96    /// Slots that may be read after each statement, indexed by statement id.
97    pub after: Vec<SlotSet>,
98    /// For `Loop` statements: slots live at the start of each iteration.
99    pub loop_head: Vec<SlotSet>,
100    /// The slots that die in each statement: live before it or written by
101    /// it, and not live after it. A slot can't die anywhere else, so
102    /// clearing these after each statement frees every value nobody will
103    /// read, except in worlds that jump out with `break` or `continue`.
104    pub dies: Vec<Vec<SlotId>>,
105    /// For each assignment: whether the live values after it can say less
106    /// about a world than before, so that worlds which differed may now be
107    /// the same. It loses more values than it makes: a value read for the
108    /// last time, or the old value of the slot it overwrites, against the
109    /// new one. `f = f - $5` narrows (`$5` and the old `f`, for the new `f`);
110    /// `t = s + r` with `s` still live doesn't (`r`, for `t`). The last
111    /// statement of a block never does: what follows it merges anyway.
112    pub narrows: Vec<bool>,
113}
114
115pub fn analyze(program: &Program) -> Liveness {
116    let n = program.stmt_count as usize;
117    let mut liveness = Liveness {
118        after: vec![SlotSet::default(); n],
119        loop_head: vec![SlotSet::default(); n],
120        dies: vec![Vec::new(); n],
121        narrows: vec![false; n],
122    };
123    for func in &program.functions {
124        let size = func.n_slots();
125        let exit = SlotSet::with_capacity(size);
126        let mut pass = Pass {
127            live: &mut liveness,
128            size,
129            caught: SlotSet::with_capacity(size),
130        };
131        let no_loop = LoopCtx {
132            after: exit.clone(),
133            head: exit.clone(),
134        };
135        pass.block(&func.body, exit, &no_loop);
136    }
137    liveness
138}
139
140struct LoopCtx {
141    /// Live after the loop: where `break` goes.
142    after: SlotSet,
143    /// Live at the loop head: where `continue` goes.
144    head: SlotSet,
145}
146
147struct Pass<'a> {
148    live: &'a mut Liveness,
149    size: usize,
150    /// Inside a `try` body: what its catches read. A fault can leave for a
151    /// catch at any statement, so these stay live throughout the body.
152    caught: SlotSet,
153}
154
155impl Pass<'_> {
156    fn block(&mut self, block: &Block, mut live: SlotSet, lc: &LoopCtx) -> SlotSet {
157        live.union_with(&self.caught);
158        for (i, stmt) in block.stmts.iter().enumerate().rev() {
159            let mut before = self.stmt(stmt, live.clone(), lc);
160            before.union_with(&self.caught);
161            let mut touched = before.clone();
162            if let Some(slot) = written(stmt) {
163                touched.insert(slot);
164            }
165            let id = stmt.id as usize;
166            self.live.dies[id] = touched.iter().filter(|&s| !live.contains(s)).collect();
167            // At the end of a block, whatever comes next merges anyway: the
168            // `if` that joins its branches, the loop at its next iteration,
169            // or the function's return.
170            let last = i + 1 == block.stmts.len();
171            if let (StmtKind::Set { place, .. }, false) = (&stmt.kind, last) {
172                let overwritten = before.contains(place.slot) && live.contains(place.slot);
173                let lost = before.iter().filter(|&s| !live.contains(s)).count() + usize::from(overwritten);
174                self.live.narrows[id] = lost > usize::from(live.contains(place.slot));
175            }
176            self.live.after[id] = live;
177            live = before;
178        }
179        live
180    }
181
182    fn stmt(&mut self, stmt: &Stmt, out: SlotSet, lc: &LoopCtx) -> SlotSet {
183        match &stmt.kind {
184            StmtKind::Set { place, value } => {
185                let mut live = out;
186                define(place, &mut live);
187                uses(value, &mut live);
188                live
189            }
190            StmtKind::Draw { place, dist } => {
191                let mut live = out;
192                define(place, &mut live);
193                uses(dist, &mut live);
194                live
195            }
196            StmtKind::Take { place, bag } => {
197                let mut live = out;
198                define(place, &mut live);
199                live.insert(bag.slot);
200                place_uses(bag, &mut live);
201                live
202            }
203            StmtKind::Call { dest, callee, args } => {
204                let mut live = out;
205                define(dest, &mut live);
206                match callee {
207                    Callee::Fn { capture_args, .. } => capture_args.iter().for_each(|&s| live.insert(s)),
208                    Callee::Value(e, _) => uses(e, &mut live),
209                }
210                args.iter().for_each(|a| uses(a, &mut live));
211                live
212            }
213            StmtKind::If { cond, then, otherwise } => {
214                let mut live = self.block(then, out.clone(), lc);
215                live.union_with(&self.block(otherwise, out, lc));
216                uses(cond, &mut live);
217                live
218            }
219            StmtKind::Chance {
220                arms,
221                otherwise,
222                exhaustive,
223            } => {
224                let mut live = match otherwise {
225                    Some(b) => self.block(b, out.clone(), lc),
226                    None if !exhaustive => out.clone(),
227                    None => SlotSet::with_capacity(self.size),
228                };
229                for (weight, body) in arms {
230                    live.union_with(&self.block(body, out.clone(), lc));
231                    uses(weight, &mut live);
232                }
233                live
234            }
235            StmtKind::Loop { body, .. } => {
236                // An enclosing loop analyzes this one again on each of its
237                // own rounds, with live sets that only grow. Starting from
238                // the head found last time, which is below the new one, still
239                // reaches it, and takes one round when nothing changed:
240                // starting afresh would double the work at every level of
241                // nesting.
242                let mut head = std::mem::take(&mut self.live.loop_head[stmt.id as usize]);
243                loop {
244                    let inner = LoopCtx {
245                        after: out.clone(),
246                        head: head.clone(),
247                    };
248                    let mut entry = self.block(body, head.clone(), &inner);
249                    entry.union_with(&head);
250                    if entry.same(&head) {
251                        break;
252                    }
253                    head = entry;
254                }
255                self.live.loop_head[stmt.id as usize] = head.clone();
256                head
257            }
258            StmtKind::Break => lc.after.clone(),
259            StmtKind::Continue => lc.head.clone(),
260            StmtKind::Return(value) => {
261                let mut live = SlotSet::with_capacity(self.size);
262                uses(value, &mut live);
263                live
264            }
265            StmtKind::Observe { value, from } => {
266                let mut live = out;
267                uses(value, &mut live);
268                if let Some(d) = from {
269                    uses(d, &mut live);
270                }
271                live
272            }
273            StmtKind::Report { value, key, .. } => {
274                let mut live = out;
275                uses(value, &mut live);
276                if let Some(k) = key {
277                    uses(k, &mut live);
278                }
279                live
280            }
281            StmtKind::Fail { .. } => SlotSet::with_capacity(self.size),
282            StmtKind::Check { slot, .. } => {
283                let mut live = out;
284                live.insert(*slot);
285                live
286            }
287            StmtKind::Try { body, catches } => {
288                let mut caught = SlotSet::with_capacity(self.size);
289                for c in catches {
290                    caught.union_with(&self.block(&c.body, out.clone(), lc));
291                }
292                let outer = std::mem::replace(&mut self.caught, caught.clone());
293                self.caught.union_with(&outer);
294                let mut live = self.block(body, out, lc);
295                self.caught = outer;
296                live.union_with(&caught);
297                live
298            }
299        }
300    }
301}
302
303/// The variable a statement assigns, if any.
304fn written(stmt: &Stmt) -> Option<SlotId> {
305    match &stmt.kind {
306        StmtKind::Set { place, .. } | StmtKind::Draw { place, .. } | StmtKind::Take { place, .. } => Some(place.slot),
307        StmtKind::Call { dest, .. } => Some(dest.slot),
308        _ => None,
309    }
310}
311
312/// Writing to a whole variable kills it; writing into part of it doesn't.
313fn define(place: &Place, live: &mut SlotSet) {
314    if place.path.is_empty() {
315        live.remove(place.slot);
316    } else {
317        live.insert(place.slot);
318        place_uses(place, live);
319    }
320}
321
322fn place_uses(place: &Place, live: &mut SlotSet) {
323    for elem in &place.path {
324        if let PathElem::Index(e) = elem {
325            uses(e, live);
326        }
327    }
328}
329
330fn uses(expr: &Expr, live: &mut SlotSet) {
331    match &expr.kind {
332        ExprKind::Lit(_) | ExprKind::Input(_) => {}
333        ExprKind::Slot(s) => live.insert(*s),
334        ExprKind::Unary(_, e) | ExprKind::Field(e, _) => uses(e, live),
335        ExprKind::Binary(_, a, b) | ExprKind::Index(a, b) => {
336            uses(a, live);
337            uses(b, live);
338        }
339        ExprKind::List(items) => items.iter().for_each(|e| uses(e, live)),
340        ExprKind::Map(entries) => entries.iter().for_each(|(k, v)| {
341            uses(k, live);
342            uses(v, live);
343        }),
344        ExprKind::Record { fields, .. } => fields.iter().for_each(|(_, e)| uses(e, live)),
345        ExprKind::With(base, fields) => {
346            uses(base, live);
347            fields.iter().for_each(|(_, e)| uses(e, live));
348        }
349        ExprKind::Builtin { args, named, .. } => {
350            args.iter().for_each(|e| uses(e, live));
351            named.iter().for_each(|(_, e)| uses(e, live));
352        }
353        ExprKind::Closure { capture_args, .. } | ExprKind::Simulate { capture_args, .. } => {
354            capture_args.iter().for_each(|&s| live.insert(s))
355        }
356        ExprKind::Interp(parts) => parts.iter().for_each(|p| {
357            if let InterpPart::Expr(e) = p {
358                uses(e, live)
359            }
360        }),
361    }
362}
363
364#[cfg(test)]
365mod tests {
366    use super::*;
367
368    #[test]
369    fn sets_list_their_members_and_the_rest() {
370        let mut set = SlotSet::with_capacity(130);
371        for s in [0, 3, 63, 64, 129] {
372            set.insert(s);
373        }
374        assert_eq!(set.iter().collect::<Vec<_>>(), [0, 3, 63, 64, 129]);
375        let missing: Vec<SlotId> = set.iter_missing(131).collect();
376        assert_eq!(missing.len(), 131 - 5);
377        assert!(missing.iter().all(|&s| !set.contains(s)));
378        assert_eq!(missing.last(), Some(&130));
379        assert_eq!(set.iter_missing(3).collect::<Vec<_>>(), [1, 2]);
380        assert_eq!(SlotSet::default().iter_missing(2).collect::<Vec<_>>(), [0, 1]);
381        assert_eq!(set.iter_missing(0).count(), 0);
382    }
383}