Skip to main content

rucc_opt/
live.rs

1//! Which values are live where, which is what the pressure model counts and what a scheduler
2//! has to know before it moves anything.
3//!
4//! Design: section 40.6 of `spec/optimizer/40-cost-models.md`, which needs this before it can
5//! count anything, and document 39.5, which is where the count becomes meaningful.
6//!
7//! # Live means used later, and in this IR that is exact
8//!
9//! A value is live at a point when some path from that point reaches a use of it. The IR is in
10//! SSA with block parameters rather than phi nodes, so the awkward case other compilers have here
11//! does not arise: a phi's operand is used in the predecessor and not in the block holding the
12//! phi, which every liveness implementation over phi nodes has to special case and half of them
13//! get wrong. Here the argument travels on the branch, the branch is an instruction in the
14//! predecessor, and the ordinary rule that an instruction uses its operands already says the right
15//! thing.
16//!
17//! # The fixpoint
18//!
19//! Backwards, over the reverse of reverse postorder, until nothing changes. A block's live-in is
20//! what is live at its first instruction with its own parameters taken out, since a parameter is
21//! defined by arriving. Its live-out is the union of the live-ins of its successors. Postorder
22//! means a block is visited after the blocks it branches to wherever the graph allows, so the
23//! usual function settles in one round and a loop costs one more.
24//!
25//! What a block adds to the set passing through it and what it takes out are the same every round,
26//! so they are worked out once rather than by walking its instructions each time. A round only
27//! looks again at a block when the live-in of something it branches to changed in the round
28//! before, since otherwise it would get the same answer. A function of thirty thousand blocks and
29//! two thousand loops took seconds when every block was redone every round.
30//!
31//! A value passed as a branch argument is live at the branch and not on the edge, because what
32//! crosses the edge is the parameter it becomes. [`Liveness::through`] is where a caller sees it,
33//! and it is the walk the pressure model counts along, so the argument is counted where it is
34//! actually held.
35//!
36//! # What is not counted
37//!
38//! Values of type `mem` are the memory dependence chain and are not data. They are live in the
39//! same sense as anything else and [`Liveness`] reports them, because a pass asking whether a
40//! store is still needed wants them. The pressure model is what drops them, because memory is not
41//! held in a register, and that decision belongs where the registers are being counted rather than
42//! here.
43
44use std::cmp::Ordering;
45
46use rucc_ir::{Block, Func, Inst, Value};
47
48use crate::cfg::Cfg;
49
50/// A set of values, kept as the words of a bitmap that have something in them.
51///
52/// A bitmap because the fixpoint unions one of these per edge per round, and a union of two
53/// bitmaps is a loop over words. Only the words with a bit set are kept, because what is live at
54/// one place is a few runs of neighbouring values out of the whole function. On jtckdint's `main`,
55/// with 200000 values and 30000 blocks, a whole bitmap per block was 1.3 GB for the live-ins and
56/// live-outs together, and fewer than one word in a hundred had anything in it. Allocating that,
57/// clearing it and copying it round the fixpoint was most of what working out liveness cost.
58#[derive(Debug, Clone, Default, PartialEq, Eq)]
59struct Set {
60    /// Which word of the bitmap each is, and the word. In order and never zero, so two sets with
61    /// the same values in them are the same list.
62    words: Vec<(u32, u64)>,
63}
64
65impl Set {
66    /// Where the word holding that value is, or where it would go.
67    fn find(&self, value: Value) -> (Result<usize, usize>, u64) {
68        let at = value.index();
69        let word = u32::try_from(at / 64).expect("a value number fits in 32 bits");
70        (self.words.binary_search_by_key(&word, |&(word, _)| word), 1 << (at % 64))
71    }
72
73    fn contains(&self, value: Value) -> bool {
74        match self.find(value) {
75            (Ok(at), bit) => self.words[at].1 & bit != 0,
76            (Err(_), _) => false,
77        }
78    }
79
80    /// Puts it in, and answers whether it was not already there.
81    fn insert(&mut self, value: Value) -> bool {
82        match self.find(value) {
83            (Ok(at), bit) => {
84                let word = &mut self.words[at].1;
85                let had = *word & bit != 0;
86                *word |= bit;
87                !had
88            }
89            (Err(at), bit) => {
90                let word = u32::try_from(value.index() / 64).expect("checked by find");
91                self.words.insert(at, (word, bit));
92                true
93            }
94        }
95    }
96
97    /// Takes it out, and answers whether it was there.
98    fn remove(&mut self, value: Value) -> bool {
99        let (Ok(at), bit) = self.find(value) else {
100            return false;
101        };
102        let word = &mut self.words[at].1;
103        let had = *word & bit != 0;
104        *word &= !bit;
105        if *word == 0 {
106            self.words.remove(at);
107        }
108        had
109    }
110
111    /// Adds everything in the other.
112    fn union_with(&mut self, other: &Self) {
113        if other.words.is_empty() {
114            return;
115        }
116        if self.words.is_empty() {
117            self.words.clone_from(&other.words);
118            return;
119        }
120        let (mine, theirs) = (&self.words, &other.words);
121        let mut both = Vec::with_capacity(mine.len() + theirs.len());
122        let (mut left, mut right) = (0, 0);
123        while left < mine.len() && right < theirs.len() {
124            let ((at, word), (other_at, other_word)) = (mine[left], theirs[right]);
125            match at.cmp(&other_at) {
126                Ordering::Less => {
127                    both.push((at, word));
128                    left += 1;
129                }
130                Ordering::Greater => {
131                    both.push((other_at, other_word));
132                    right += 1;
133                }
134                Ordering::Equal => {
135                    both.push((at, word | other_word));
136                    left += 1;
137                    right += 1;
138                }
139            }
140        }
141        both.extend_from_slice(&mine[left..]);
142        both.extend_from_slice(&theirs[right..]);
143        self.words = both;
144    }
145
146    /// Takes everything out.
147    fn clear(&mut self) {
148        self.words.clear();
149    }
150
151    fn len(&self) -> usize {
152        self.words.iter().map(|&(_, word)| word.count_ones() as usize).sum()
153    }
154
155    /// Them, in order.
156    ///
157    /// The set bits of each word are taken one at a time rather than by testing all sixty four.
158    fn iter(&self) -> impl Iterator<Item = Value> + use<'_> {
159        self.words
160            .iter()
161            .flat_map(|&(at, word)| Bits(word).map(move |bit| Value::new(at * 64 + bit)))
162    }
163}
164
165/// The set bits of one word, lowest first.
166///
167/// `trailing_zeros` finds the next one and clearing the lowest set bit moves past it, so the work
168/// is one step per bit that is there rather than one per bit there could be.
169struct Bits(u64);
170
171impl Iterator for Bits {
172    type Item = u32;
173
174    fn next(&mut self) -> Option<u32> {
175        if self.0 == 0 {
176            return None;
177        }
178        let bit = self.0.trailing_zeros();
179        self.0 &= self.0 - 1;
180        Some(bit)
181    }
182}
183
184/// Which values fall in each of a few groups, as one bitmap per group laid out by word.
185///
186/// Counting a group in a live set is then a mask and a population count for each word the set has
187/// rather than a look at every value in it. The pressure model counts what is live at the edges of
188/// every block, and reading the type of each of those values one at a time was most of what it
189/// cost on a function with thousands of blocks.
190#[derive(Debug)]
191pub struct Groups<const N: usize> {
192    words: Vec<[u64; N]>,
193}
194
195impl<const N: usize> Groups<N> {
196    /// Puts each value of the function in the group `group` names, or in none.
197    ///
198    /// # Panics
199    ///
200    /// Panics if `group` names a group past the last of the `N`.
201    #[must_use]
202    pub fn of(func: &Func, group: impl Fn(Value) -> Option<usize>) -> Self {
203        let mut words = vec![[0; N]; func.values().count().div_ceil(64)];
204        for value in func.values() {
205            if let Some(group) = group(value) {
206                let at = value.index();
207                words[at / 64][group] |= 1 << (at % 64);
208            }
209        }
210        Self { words }
211    }
212
213    /// How many of the set fall in each group.
214    fn count(&self, set: &Set) -> [u32; N] {
215        let mut counts = [0; N];
216        for &(at, word) in &set.words {
217            let Some(masks) = self.words.get(at as usize) else { continue };
218            for (count, mask) in counts.iter_mut().zip(masks) {
219                *count += (word & mask).count_ones();
220            }
221        }
222        counts
223    }
224}
225
226/// What is live at the edges of every block.
227///
228/// Per block rather than per instruction, because the sets inside a block are recoverable from the
229/// live-out by walking the block backwards and nothing wants to pay for storing them.
230/// [`Liveness::through`] is that walk, and the pressure model is its first caller.
231#[derive(Debug, Clone, PartialEq, Eq)]
232pub struct Liveness {
233    live_in: Vec<Set>,
234    live_out: Vec<Set>,
235}
236
237impl Liveness {
238    /// Works out what is live where.
239    #[must_use]
240    pub fn of(func: &Func, cfg: &Cfg) -> Self {
241        let blocks = cfg.capacity();
242        let mut live_in = vec![Set::default(); blocks];
243        let mut live_out = vec![Set::default(); blocks];
244
245        // What each block reads before it writes, and what it writes, parameters included. Live in
246        // is then live out with the second taken out and the first put in, which is what walking
247        // the block backwards gives without walking it.
248        let order: Vec<Block> = cfg.postorder().to_vec();
249        let mut reads: Vec<Vec<Value>> = vec![Vec::new(); blocks];
250        let mut writes: Vec<Vec<Value>> = vec![Vec::new(); blocks];
251        let mut defined = Set::default();
252        let mut read = Set::default();
253        for &block in &order {
254            let at = block.index();
255            for &param in &func[block].params {
256                defined.insert(param);
257                writes[at].push(param);
258            }
259            for inst in func.insts(block) {
260                let data = &func[inst];
261                let branches = func.successors(inst).flat_map(|call| &func[call.args]);
262                for &arg in func[data.args].iter().chain(branches) {
263                    if !defined.contains(arg) && read.insert(arg) {
264                        reads[at].push(arg);
265                    }
266                }
267                for result in data.results() {
268                    defined.insert(result);
269                    writes[at].push(result);
270                }
271            }
272            for &value in &writes[at] {
273                defined.remove(value);
274            }
275            for &value in &reads[at] {
276                read.remove(value);
277            }
278        }
279
280        // Postorder, so a block is reached after the blocks it branches to wherever the graph
281        // allows one order to do that. A loop is what makes a second round necessary, and the
282        // second round is only the blocks something changed under.
283        let mut stale = vec![true; blocks];
284        let mut set = Set::default();
285        let mut again = true;
286        while again {
287            again = false;
288            for &block in &order {
289                let at = block.index();
290                if !std::mem::take(&mut stale[at]) {
291                    continue;
292                }
293                set.clear();
294                for &successor in cfg.successors(block) {
295                    set.union_with(&live_in[successor.index()]);
296                }
297                live_out[at].clone_from(&set);
298                for &value in &writes[at] {
299                    set.remove(value);
300                }
301                for &value in &reads[at] {
302                    set.insert(value);
303                }
304                if live_in[at] != set {
305                    live_in[at].clone_from(&set);
306                    for &pred in cfg.predecessors(block) {
307                        stale[pred.index()] = true;
308                        again = true;
309                    }
310                }
311            }
312        }
313
314        Self { live_in, live_out }
315    }
316
317    /// How many of what is live when control arrives at the block fall in each group.
318    #[must_use]
319    pub fn grouped_in<const N: usize>(&self, block: Block, groups: &Groups<N>) -> [u32; N] {
320        groups.count(&self.live_in[block.index()])
321    }
322
323    /// How many of what is live when control leaves the block fall in each group.
324    #[must_use]
325    pub fn grouped_out<const N: usize>(&self, block: Block, groups: &Groups<N>) -> [u32; N] {
326        groups.count(&self.live_out[block.index()])
327    }
328
329    /// What is live when control arrives at the block, which excludes its own parameters.
330    pub fn live_in(&self, block: Block) -> impl Iterator<Item = Value> + use<'_> {
331        self.live_in[block.index()].iter()
332    }
333
334    /// What is live when control leaves it.
335    pub fn live_out(&self, block: Block) -> impl Iterator<Item = Value> + use<'_> {
336        self.live_out[block.index()].iter()
337    }
338
339    /// Whether that value is live on the way in.
340    #[must_use]
341    pub fn is_live_in(&self, block: Block, value: Value) -> bool {
342        self.live_in[block.index()].contains(value)
343    }
344
345    /// Whether that value is live on the way out.
346    #[must_use]
347    pub fn is_live_out(&self, block: Block, value: Value) -> bool {
348        self.live_out[block.index()].contains(value)
349    }
350
351    /// How many values are live on the way in.
352    #[must_use]
353    pub fn count_in(&self, block: Block) -> usize {
354        self.live_in[block.index()].len()
355    }
356
357    /// How many are live on the way out.
358    #[must_use]
359    pub fn count_out(&self, block: Block) -> usize {
360        self.live_out[block.index()].len()
361    }
362
363    /// Walks the block backwards from its live-out, calling `at` before each instruction with what
364    /// is live there.
365    ///
366    /// This is where the per instruction sets come from, for the callers that want them. The set
367    /// handed to `at` is what is live just before that instruction runs, so it holds the
368    /// instruction's operands and not its results.
369    pub fn through(&self, func: &Func, block: Block, mut at: impl FnMut(Inst, &LiveHere<'_>)) {
370        let mut set = self.live_out[block.index()].clone();
371        walk(func, block, &mut set, |inst, set, _| at(inst, &LiveHere { set }));
372    }
373
374    /// The same walk, reporting what each instruction changes rather than what is live.
375    ///
376    /// [`Liveness::through`] hands out the whole set at every instruction, and a caller that only
377    /// wants to count what is in it pays the size of the set per instruction. In a function of a
378    /// hundred and ninety thousand instructions the set is thousands of values wide and that is
379    /// quadratic. What actually changes at an instruction is its results and its operands, so a
380    /// caller keeping a running count can be handed those instead and stay linear.
381    /// tamnd/rucc#1015.
382    pub fn changes(&self, func: &Func, block: Block, mut at: impl FnMut(Inst, &Change)) {
383        let mut set = self.live_out[block.index()].clone();
384        walk(func, block, &mut set, |inst, _, change| at(inst, change));
385    }
386}
387
388/// What one instruction does to the live set, seen walking the block backwards.
389///
390/// Both lists hold each value once, because they record the bits that moved rather than the names
391/// the instruction wrote: a value an instruction names twice is one bit and arrives once.
392#[derive(Debug, Default)]
393pub struct Change {
394    /// Values the instruction defines, which are live after it and not before it.
395    pub gone: Vec<Value>,
396    /// Values it names, which are live before it and were not after it.
397    pub arrived: Vec<Value>,
398}
399
400/// What is live at one point inside a block.
401///
402/// A borrowed view rather than a set the caller keeps, because the walk reuses one set and handing
403/// out a copy per instruction is the whole cost of the walk.
404#[derive(Debug)]
405pub struct LiveHere<'a> {
406    set: &'a Set,
407}
408
409impl LiveHere<'_> {
410    /// Whether that value is live here.
411    #[must_use]
412    pub fn contains(&self, value: Value) -> bool {
413        self.set.contains(value)
414    }
415
416    /// How many values are live here.
417    #[must_use]
418    pub fn len(&self) -> usize {
419        self.set.len()
420    }
421
422    /// Whether nothing is.
423    #[must_use]
424    pub fn is_empty(&self) -> bool {
425        self.len() == 0
426    }
427
428    /// Them, in order.
429    pub fn iter(&self) -> impl Iterator<Item = Value> + use<'_> {
430        self.set.iter()
431    }
432}
433
434/// Walks one block backwards, taking out what each instruction defines and putting in what it
435/// uses, and calling `at` with the set as it stands before each instruction.
436///
437/// The order matters and is the reason this is one function rather than two loops at each caller.
438/// The results go out before the operands come in, so an instruction whose operand is also its
439/// result leaves the value live, which is what a use before a redefinition means.
440fn walk(func: &Func, block: Block, set: &mut Set, mut at: impl FnMut(Inst, &Set, &Change)) {
441    let mut change = Change::default();
442    for this in func.insts_backwards(block) {
443        change.gone.clear();
444        change.arrived.clear();
445        let data = &func[this];
446        for result in data.results() {
447            if set.remove(result) {
448                change.gone.push(result);
449            }
450        }
451        for &arg in &func[data.args] {
452            if set.insert(arg) {
453                change.arrived.push(arg);
454            }
455        }
456        // A branch's arguments are used by the branch, in the block holding it, which is the whole
457        // reason block parameters are easier to be right about than phi nodes.
458        for call in func.successors(this) {
459            for &arg in &func[call.args] {
460                if set.insert(arg) {
461                    change.arrived.push(arg);
462                }
463            }
464        }
465        at(this, set, &change);
466    }
467}
468
469#[cfg(test)]
470mod tests {
471    use rucc_base::Interner;
472    use rucc_ir::{Block, Builder, Flags, Func, Opcode, Signature, Type, Value};
473
474    use super::{Groups, Liveness, Set};
475    use crate::cfg::Cfg;
476
477    const I32: Type = Type::int(32);
478
479    fn blank(count: usize) -> (Func, Vec<Block>) {
480        let mut names = Interner::new();
481        let mut func = Func::new(names.intern("f"), Signature::new());
482        let blocks: Vec<Block> = (0..count).map(|_| func.create_block()).collect();
483        (func, blocks)
484    }
485
486    fn liveness(func: &Func) -> (Cfg, Liveness) {
487        let cfg = Cfg::new(func);
488        let live = Liveness::of(func, &cfg);
489        (cfg, live)
490    }
491
492    #[test]
493    fn a_set_keeps_only_the_words_with_something_in_them() {
494        let value = Value::new;
495        let mut first = Set::default();
496        assert!(first.insert(value(3)));
497        assert!(first.insert(value(200)));
498        assert!(!first.insert(value(3)), "it was already there");
499        assert!(first.insert(value(70)));
500        assert!(first.remove(value(70)));
501        assert!(!first.remove(value(70)), "it went the first time");
502        assert!(!first.remove(value(5000)), "nothing was ever near it");
503        assert_eq!(first.words.len(), 2, "the word 70 was in went with it");
504
505        let mut second = Set::default();
506        second.insert(value(64));
507        second.insert(value(200));
508        second.insert(value(201));
509        second.insert(value(9000));
510        first.union_with(&second);
511        let all: Vec<u32> = first.iter().map(|value| value.raw()).collect();
512        assert_eq!(all, [3, 64, 200, 201, 9000]);
513        assert_eq!(first.len(), 5);
514        assert!(first.contains(value(201)) && !first.contains(value(202)));
515
516        // Put in the other way round and taken out again, it is the same list, which is what the
517        // fixpoint compares to know it is done.
518        let mut again = Set::default();
519        for number in [9000, 201, 5, 200, 64, 3] {
520            again.insert(value(number));
521        }
522        again.remove(value(5));
523        assert_eq!(again, first);
524    }
525
526    #[test]
527    fn a_value_made_and_read_in_one_block_never_crosses_an_edge() {
528        let (mut func, blocks) = blank(1);
529        let mut build = Builder::new(&mut func, blocks[0]);
530        let one = build.iconst(I32, 1);
531        let two = build.iconst(I32, 2);
532        let sum = build.binary(Opcode::Add, one, two, Flags::NONE);
533        build.ret(&[sum]);
534
535        let (_, live) = liveness(&func);
536        assert_eq!(live.count_in(blocks[0]), 0);
537        assert_eq!(live.count_out(blocks[0]), 0);
538    }
539
540    #[test]
541    fn a_value_read_in_a_later_block_is_live_on_the_edge_between_them() {
542        let (mut func, blocks) = blank(2);
543        let mut build = Builder::new(&mut func, blocks[0]);
544        let kept = build.iconst(I32, 7);
545        build.jump(blocks[1], &[]);
546        let mut build = Builder::new(&mut func, blocks[1]);
547        build.ret(&[kept]);
548
549        let (_, live) = liveness(&func);
550        assert!(live.is_live_out(blocks[0], kept), "it is read after the branch");
551        assert!(live.is_live_in(blocks[1], kept), "and it has to arrive there to be read");
552        assert!(!live.is_live_in(blocks[0], kept), "it does not exist before it is made");
553    }
554
555    #[test]
556    fn a_group_counts_what_counting_one_value_at_a_time_counts() {
557        // Enough values that the live set runs over more than one word of the bitmap.
558        let (mut func, blocks) = blank(2);
559        let mut build = Builder::new(&mut func, blocks[0]);
560        let kept: Vec<Value> = (0..150).map(|number| build.iconst(I32, number)).collect();
561        build.jump(blocks[1], &[]);
562        let mut build = Builder::new(&mut func, blocks[1]);
563        build.ret(&kept[..]);
564
565        // Every third value is in no group, and the rest go by whether their number is even.
566        let group = |value: Value| (value.index() % 3 != 0).then_some(value.index() % 2);
567        let groups = Groups::<2>::of(&func, group);
568        let (_, live) = liveness(&func);
569        for &block in &blocks {
570            for (grouped, values) in [
571                (live.grouped_in(block, &groups), live.live_in(block).collect::<Vec<_>>()),
572                (live.grouped_out(block, &groups), live.live_out(block).collect()),
573            ] {
574                let mut counted = [0; 2];
575                for value in values {
576                    if let Some(group) = group(value) {
577                        counted[group] += 1;
578                    }
579                }
580                assert_eq!(grouped, counted);
581            }
582        }
583        assert_eq!(live.grouped_in(blocks[1], &groups), [50, 50]);
584    }
585
586    #[test]
587    fn a_value_passed_on_the_branch_is_used_by_the_branch_and_not_by_the_block_it_arrives_at() {
588        // The whole reason block parameters are easier to be right about than phi nodes. The
589        // argument is live in the predecessor, and the parameter it becomes is defined by
590        // arriving, so it is not live-in of the block that holds it.
591        let (mut func, blocks) = blank(2);
592        let param = func.append_param(blocks[1], I32);
593        let mut build = Builder::new(&mut func, blocks[0]);
594        let sent = build.iconst(I32, 7);
595        build.jump(blocks[1], &[sent]);
596        let mut build = Builder::new(&mut func, blocks[1]);
597        build.ret(&[param]);
598
599        let (_, live) = liveness(&func);
600        // It is live at the branch and dead on the edge, which is the point. Live-out is what
601        // survives the edge, and what the argument becomes on the other side is the parameter.
602        let mut at_the_jump = false;
603        live.through(&func, blocks[0], |inst, here| {
604            if func[inst].opcode == Opcode::Jump {
605                at_the_jump = here.contains(sent);
606            }
607        });
608        assert!(at_the_jump, "the branch uses it");
609        assert!(!live.is_live_out(blocks[0], sent), "and it does not survive the edge");
610        assert!(!live.is_live_in(blocks[1], param), "a parameter is defined by arriving");
611        assert!(!live.is_live_in(blocks[1], sent), "nor does it arrive under its own name");
612        assert_eq!(live.count_in(blocks[1]), 0);
613    }
614
615    #[test]
616    fn a_value_read_on_one_arm_only_is_live_on_that_arm_and_not_the_other() {
617        let (mut func, blocks) = blank(4);
618        let mut build = Builder::new(&mut func, blocks[0]);
619        let kept = build.iconst(I32, 7);
620        let cond = build.iconst(Type::I1, 1);
621        build.br_if(cond, blocks[1], &[], blocks[2], &[]);
622        let mut build = Builder::new(&mut func, blocks[1]);
623        build.jump(blocks[3], &[]);
624        let mut build = Builder::new(&mut func, blocks[2]);
625        build.ret(&[kept]);
626        let mut build = Builder::new(&mut func, blocks[3]);
627        build.ret(&[]);
628
629        let (_, live) = liveness(&func);
630        assert!(live.is_live_out(blocks[0], kept), "one arm reads it, so it survives the branch");
631        assert!(live.is_live_in(blocks[2], kept));
632        assert!(!live.is_live_in(blocks[1], kept), "this arm never mentions it");
633    }
634
635    #[test]
636    fn a_value_read_after_the_loop_stays_live_all_the_way_round_it() {
637        // Block 0 makes it, block 1 is the loop and does not touch it, block 2 reads it. The
638        // fixpoint is what gets this right: one backwards pass over the blocks in postorder puts
639        // it live-in of the loop, and the second round is what carries that back to the latch.
640        let (mut func, blocks) = blank(3);
641        let mut build = Builder::new(&mut func, blocks[0]);
642        let kept = build.iconst(I32, 7);
643        let cond = build.iconst(Type::I1, 1);
644        build.jump(blocks[1], &[]);
645        let mut build = Builder::new(&mut func, blocks[1]);
646        build.br_if(cond, blocks[1], &[], blocks[2], &[]);
647        let mut build = Builder::new(&mut func, blocks[2]);
648        build.ret(&[kept]);
649
650        let (_, live) = liveness(&func);
651        assert!(live.is_live_in(blocks[1], kept), "it has to survive the loop to be read after it");
652        assert!(live.is_live_out(blocks[1], kept), "including round the back edge");
653        assert!(live.is_live_in(blocks[2], kept));
654    }
655
656    #[test]
657    fn nothing_is_live_in_a_block_control_never_reaches() {
658        let (mut func, blocks) = blank(2);
659        let mut build = Builder::new(&mut func, blocks[0]);
660        let kept = build.iconst(I32, 7);
661        build.ret(&[kept]);
662        let mut build = Builder::new(&mut func, blocks[1]);
663        build.ret(&[]);
664
665        let (cfg, live) = liveness(&func);
666        assert!(!cfg.reaches(blocks[1]));
667        assert_eq!(live.count_in(blocks[1]), 0);
668        assert_eq!(live.count_out(blocks[1]), 0);
669    }
670
671    #[test]
672    fn the_walk_through_a_block_says_what_is_live_before_each_instruction() {
673        let (mut func, blocks) = blank(2);
674        let mut build = Builder::new(&mut func, blocks[0]);
675        let one = build.iconst(I32, 1);
676        let two = build.iconst(I32, 2);
677        let sum = build.binary(Opcode::Add, one, two, Flags::NONE);
678        let jump = build.jump(blocks[1], &[sum]);
679        let param = func.append_param(blocks[1], I32);
680        let mut build = Builder::new(&mut func, blocks[1]);
681        build.ret(&[param]);
682
683        let (_, live) = liveness(&func);
684        let mut counts = Vec::new();
685        live.through(&func, blocks[0], |inst, here| counts.push((inst, here.len())));
686        // Backwards: before the jump only the sum is live, before the add both operands are,
687        // before the second constant only the first is, and before the first nothing is.
688        assert_eq!(counts.len(), 4);
689        assert_eq!(counts[0], (jump, 1));
690        assert_eq!(counts[1].1, 2, "the add's two operands");
691        assert_eq!(counts[2].1, 1);
692        assert_eq!(counts[3].1, 0);
693        assert!(counts[0].1 <= counts[1].1, "the sum replaces the two it was made from");
694    }
695
696    #[test]
697    fn a_value_that_is_its_own_operand_stays_live_across_the_instruction_that_redefines_nothing() {
698        // Results go out before operands come in, which is what makes a use of a value the
699        // instruction also produces read as a use rather than as a definition.
700        let (mut func, blocks) = blank(1);
701        let mut build = Builder::new(&mut func, blocks[0]);
702        let start = build.iconst(I32, 1);
703        let doubled = build.binary(Opcode::Add, start, start, Flags::NONE);
704        build.ret(&[doubled]);
705
706        let (_, live) = liveness(&func);
707        let mut most = 0;
708        live.through(&func, blocks[0], |_, here| most = most.max(here.len()));
709        assert_eq!(most, 1, "one value used twice is one value");
710    }
711}