Skip to main content

rucc_codegen/
holding.rs

1//! Which of its values a declaration holds on the way into each block.
2//!
3//! Design: `spec/11-asm-objects-debug.md` section 11.4.
4//!
5//! A local the program writes more than once is behind a value for each write, and two of them can
6//! both be live into one block: the value from the last trip round a loop is still read after the
7//! one computed on this trip has been written, and both of them are the local. Their stretches then
8//! start at the same address and say different things, and nothing in the stretches says which of
9//! the two the local is. What does is the order the assignments ran in, which is a question about
10//! the program rather than about the registers, so it is asked of the IR before selection.
11//!
12//! The answer is the value the last assignment on every path into the block gave the declaration.
13//! An assignment is where a named value was computed, a block parameter a declaration is the value
14//! of, or a start [`rucc_ir::Func::start_place`] says is still somewhere. A block whose paths in
15//! disagree, or one no path reaches, has no answer, and the back end reads that as nothing to
16//! choose by rather than as a choice.
17
18use std::collections::{HashMap, VecDeque};
19
20use rucc_ir::{Block, Def, Func, Inst, Value};
21
22/// The value each declaration that has more than one of them holds at the top of each block, where
23/// every path in agrees, as the declaration, the block and the value, sorted.
24///
25/// Only a declaration with more than one value is asked about, since a declaration with one cannot
26/// be holding the wrong one of them, and the list would otherwise be every local times every block.
27#[must_use]
28pub fn on_entry(func: &Func) -> Vec<(u32, Block, Value)> {
29    let mut assigned: HashMap<u32, Vec<(Block, Option<Inst>, Value)>> = HashMap::new();
30    for value in func.values() {
31        let place = match func[value].def {
32            Def::Result { inst, .. } => func.block_of(inst).map(|block| (block, Some(inst))),
33            Def::Param { block, index } => {
34                let param = usize::try_from(index)
35                    .ok()
36                    .and_then(|index| func[block].params.get(index).copied());
37                (func.is_placed(block) && param == Some(value)).then_some((block, None))
38            }
39        };
40        if let Some((block, after)) = place {
41            for decl in func.value_decls(value) {
42                assigned.entry(decl).or_default().push((block, after, value));
43            }
44        }
45        for start in func.value_starts(value) {
46            if let Some((block, after)) = func.start_place(start) {
47                assigned.entry(start.decl).or_default().push((block, after, value));
48            }
49        }
50    }
51    assigned.retain(|_, all| all.iter().any(|&(_, _, value)| value != all[0].2));
52    if assigned.is_empty() {
53        return Vec::new();
54    }
55
56    let blocks: Vec<Block> = func.blocks().collect();
57    let count = func.counts().blocks;
58    // Where each instruction is in its block, one past the top so that the top itself is nought.
59    let mut position = vec![0usize; func.counts().insts];
60    let mut succs: Vec<Vec<Block>> = vec![Vec::new(); count];
61    for &block in &blocks {
62        for (at, inst) in func.insts(block).enumerate() {
63            position[inst.index()] = at + 1;
64        }
65        if let Some(terminator) = func.terminator(block) {
66            succs[block.index()].extend(func.successors(terminator).map(|call| call.block));
67        }
68    }
69    let entry = func.entry();
70
71    let mut decls: Vec<u32> = assigned.keys().copied().collect();
72    decls.sort_unstable();
73    let mut out = Vec::new();
74    for decl in decls {
75        // What the last assignment in each block gave the declaration, and the ones at the top of
76        // it, the parameters and a start before everything in it. Two at the same place giving it
77        // different values are two assignments this cannot put in order, and the answer is none.
78        let mut last: Vec<Option<(usize, Held)>> = vec![None; count];
79        let mut top: Vec<Option<Held>> = vec![None; count];
80        for &(block, after, value) in &assigned[&decl] {
81            let at = after.map_or(0, |inst| position[inst.index()]);
82            let slot = &mut last[block.index()];
83            *slot = match *slot {
84                Some((have, _)) if have > at => *slot,
85                Some((have, held)) if have == at => Some((at, held.meet(Held::Known(value)))),
86                _ => Some((at, Held::Known(value))),
87            };
88            if at == 0 {
89                let slot = &mut top[block.index()];
90                *slot = Some(slot.map_or(Held::Known(value), |held| held.meet(Held::Known(value))));
91            }
92        }
93        // Forward to a fixed point. Every block starts unvisited, the entry starts with nothing
94        // said, and a block's way in is what all of its predecessors agree on at their way out.
95        //
96        // A list of blocks whose way out changed rather than sweeps over every block until none
97        // does, since a sweep in an order that runs against the edges moves an answer one block
98        // at a time, and jtckdint's test is 22000 blocks of that. A way in only ever goes from
99        // unvisited to one value to none, so meeting it with each way out as it arrives is the
100        // same as meeting all of them at once, and each block goes on the list at most twice.
101        let mut into: Vec<Held> = vec![Held::Unvisited; count];
102        let mut queued = vec![false; count];
103        let mut waiting = VecDeque::new();
104        if let Some(entry) = entry {
105            into[entry.index()] = Held::Unknown;
106            queued[entry.index()] = true;
107            waiting.push_back(entry);
108        }
109        for &(block, _, _) in &assigned[&decl] {
110            if !queued[block.index()] {
111                queued[block.index()] = true;
112                waiting.push_back(block);
113            }
114        }
115        while let Some(block) = waiting.pop_front() {
116            queued[block.index()] = false;
117            let out = last[block.index()].map_or(into[block.index()], |(_, held)| held);
118            for &succ in &succs[block.index()] {
119                if Some(succ) == entry {
120                    continue;
121                }
122                let now = into[succ.index()].meet(out);
123                if now != into[succ.index()] {
124                    into[succ.index()] = now;
125                    if !queued[succ.index()] {
126                        queued[succ.index()] = true;
127                        waiting.push_back(succ);
128                    }
129                }
130            }
131        }
132        for &block in &blocks {
133            let held = top[block.index()].unwrap_or(into[block.index()]);
134            if let Held::Known(value) = held {
135                out.push((decl, block, value));
136            }
137        }
138    }
139    out
140}
141
142/// What a declaration is known to hold at one place.
143#[derive(Clone, Copy, Debug, PartialEq, Eq)]
144enum Held {
145    /// Nothing has reached here yet.
146    Unvisited,
147    /// This value, on every path that has.
148    Known(Value),
149    /// Different values on different paths, or nothing said at all.
150    Unknown,
151}
152
153impl Held {
154    /// What two paths that meet agree on.
155    fn meet(self, other: Held) -> Held {
156        match (self, other) {
157            (Held::Unvisited, held) | (held, Held::Unvisited) => held,
158            (Held::Known(one), Held::Known(two)) if one == two => self,
159            _ => Held::Unknown,
160        }
161    }
162}
163
164#[cfg(test)]
165mod tests {
166    use rucc_base::Symbol;
167    use rucc_ir::{Builder, Signature, Start, Type};
168
169    use super::*;
170
171    /// A loop the way the lowering leaves one: the header takes the declaration's value as a
172    /// parameter, the body computes the next one, and a block after the body reads both.
173    ///
174    /// `entry -> head(i) -> body -> after -> head`, with `after` also leaving. The body is where
175    /// `next` is computed and named, so in `after` the declaration is `next` even though `i` is
176    /// still live there, and in `head` it is the parameter.
177    #[test]
178    fn a_value_computed_on_this_trip_is_what_the_blocks_after_it_hold() {
179        let mut func = Func::new(Symbol::from_raw(0), Signature::new());
180        let entry = func.create_block();
181        let head = func.create_block();
182        let body = func.create_block();
183        let after = func.create_block();
184        let exit = func.create_block();
185        let i = func.append_param(head, Type::int(32));
186        let mut build = Builder::new(&mut func, entry);
187        let zero = build.iconst(Type::int(32), 0);
188        build.jump(head, &[zero]);
189        Builder::new(&mut func, head).jump(body, &[]);
190        let mut build = Builder::new(&mut func, body);
191        let one = build.iconst(Type::int(32), 1);
192        let next = build.binary(rucc_ir::Opcode::Add, i, one, rucc_ir::Flags::NONE);
193        build.jump(after, &[]);
194        let mut build = Builder::new(&mut func, after);
195        let done = build.icmp(rucc_ir::IntPred::Eq, i, next);
196        build.br_if(done, exit, &[], head, &[next]);
197        Builder::new(&mut func, exit).ret(&[]);
198        for value in [zero, i, next] {
199            func.declare_value(value, 3);
200        }
201
202        let held = on_entry(&func);
203        assert!(held.contains(&(3, head, i)), "{held:?}");
204        assert!(held.contains(&(3, body, i)), "{held:?}");
205        assert!(held.contains(&(3, after, next)), "{held:?}");
206        assert!(held.contains(&(3, exit, next)), "{held:?}");
207        assert!(!held.iter().any(|&(_, block, _)| block == entry), "{held:?}");
208    }
209
210    /// Two arms that give a declaration different values leave the block they meet in with no
211    /// answer, and a start that gives it one of them again on the way in is what it holds.
212    #[test]
213    fn arms_that_disagree_say_nothing_and_a_start_says_what_it_gave() {
214        let mut func = Func::new(Symbol::from_raw(0), Signature::new());
215        let entry = func.create_block();
216        let left = func.create_block();
217        let right = func.create_block();
218        let join = func.create_block();
219        let flag = func.append_param(entry, Type::I1);
220        let mut build = Builder::new(&mut func, entry);
221        let first = build.iconst(Type::int(32), 1);
222        build.br_if(flag, left, &[], right, &[]);
223        let mut build = Builder::new(&mut func, left);
224        let second = build.iconst(Type::int(32), 2);
225        let saved = build.iconst(Type::int(32), 3);
226        build.jump(join, &[]);
227        Builder::new(&mut func, right).jump(join, &[]);
228        Builder::new(&mut func, join).ret(&[]);
229        func.declare_value(first, 5);
230        func.declare_value(second, 5);
231
232        let held = on_entry(&func);
233        assert!(held.contains(&(5, left, first)), "{held:?}");
234        assert!(held.contains(&(5, right, first)), "{held:?}");
235        assert!(!held.iter().any(|&(_, block, _)| block == join), "{held:?}");
236
237        // `x = saved;` at the end of the left arm, where saved is the first value again, so both
238        // arms hand the join the first. A declaration with the one value is not asked about.
239        let Def::Result { inst: loaded, .. } = func[saved].def else { unreachable!() };
240        func.declare_value_from(first, Start { decl: 5, block: left, after: Some(loaded) });
241        func.declare_value_from(second, Start { decl: 6, block: left, after: None });
242        let held = on_entry(&func);
243        assert!(held.contains(&(5, join, first)), "{held:?}");
244        assert!(held.contains(&(5, left, first)), "{held:?}");
245        assert!(!held.iter().any(|&(decl, _, _)| decl == 6), "{held:?}");
246    }
247}