Skip to main content

rucc_lower/
ssa.rs

1//! SSA construction, by the algorithm of Braun and others.
2//!
3//! Design: `spec/08-ir.md` section 8.5.
4//!
5//! The classical way to get SSA out of a C front end is to give every local variable a stack
6//! slot, emit a load for every read and a store for every write, and then run a pass that
7//! builds dominance frontiers and deletes almost all of it again. That pass is the only one
8//! `-O0` runs, and everything it deletes was allocated first. So we do not build it: a local
9//! whose address is never taken never gets a slot, and the value it holds is worked out here
10//! while the tree is being walked.
11//!
12//! The algorithm is Braun, Buchwald, Hack, Leissa, Mallon and Zwinkau, "Simple and Efficient
13//! Construction of Static Single Assignment Form" (CC 2013). Writing a variable records the
14//! value it now holds in the block doing the writing. Reading one in a block that wrote it is
15//! a lookup. Reading one in a block that did not is a question for the predecessors, and the
16//! answer is either the one value they all agree on or a new block parameter that collects
17//! what each of them has.
18//!
19//! # Sealing
20//!
21//! The one thing the caller has to get right. A block is sealed when it will get no further
22//! predecessors, and reading a variable in an unsealed block cannot ask the predecessors
23//! because they are not all there yet. It gets a block parameter instead, which is filled in
24//! when the block is sealed. That is what a loop header needs and is the whole reason the
25//! algorithm handles loops without a dominance computation: the header is created, left
26//! unsealed while its body is walked, and sealed when the back edge has been emitted.
27//!
28//! A block with no back edge into it can be sealed as soon as it is created, and the walk
29//! seals almost everything immediately.
30//!
31//! # Block parameters, not phi nodes
32//!
33//! A phi in the paper is a block parameter here, and adding an operand to one is appending an
34//! argument to the branch in each predecessor. The paper's removal of trivial phis is done in
35//! two halves: a parameter found to stand for one value is recorded as standing for it, and
36//! nothing is deleted until [`Ssa::finish`], which resolves every operand in the function once
37//! and then drops the parameters and the arguments that went with them. One pass over the
38//! function rather than one walk per removal, and no use lists to keep in step.
39
40use std::collections::{HashMap, HashSet};
41
42use rucc_base::Idx;
43use rucc_diag::Span;
44use rucc_ir::{Block, BlockCall, Extra, Func, Imm, Inst, InstData, Opcode, Type, Value};
45
46/// A variable, which is somewhere in the source that can be written more than once.
47///
48/// What it names is the caller's business. The walk over the typed tree makes one of these per
49/// local whose address is never taken, and nothing here looks inside it.
50#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
51pub struct Var(u32);
52
53impl Var {
54    /// The variable with that number.
55    #[must_use]
56    pub const fn new(raw: u32) -> Var {
57        Var(raw)
58    }
59
60    /// Its number.
61    #[must_use]
62    pub const fn raw(self) -> u32 {
63        self.0
64    }
65}
66
67/// One edge into a block: where it comes from, and the branch target that carries its
68/// arguments.
69///
70/// The target is named by its place in the function's table rather than by the block it goes
71/// to, because an edge that has to grow an argument later needs to be found again, and two
72/// edges to the same block are the same block.
73#[derive(Clone, Copy, Debug, PartialEq, Eq)]
74struct Edge {
75    from: Block,
76    call: Idx<BlockCall>,
77}
78
79/// A block parameter that stands for a variable, which is what the paper calls a phi.
80#[derive(Clone, Copy, Debug, PartialEq, Eq)]
81struct Phi {
82    block: Block,
83    var: Var,
84}
85
86/// The state of an SSA construction over one function.
87#[derive(Debug)]
88pub struct Ssa {
89    /// The integer type a pointer has the width of, for the one value this has to invent.
90    address: Type,
91    /// What each variable holds at the end of each block.
92    defs: HashMap<(Var, Block), Value>,
93    /// Whether each block will get more predecessors.
94    sealed: Vec<bool>,
95    /// The parameters of each unsealed block that are waiting for its predecessors.
96    incomplete: Vec<Vec<(Var, Value)>>,
97    /// The edges into each block.
98    preds: Vec<Vec<Edge>>,
99    /// Which block parameters are ours, and what they stand for.
100    phis: HashMap<Value, Phi>,
101    /// For each value, the parameters of ours that read it. The use list the paper needs,
102    /// restricted to the uses it actually walks.
103    users: HashMap<Value, Vec<Value>>,
104    /// What each parameter that turned out to be redundant stands for instead.
105    subst: HashMap<Value, Value>,
106    /// The value a read of something never written gives back, one per type.
107    zero: Vec<(Type, Value)>,
108    /// Which declaration each variable the caller named is, for the ones it named.
109    named: HashMap<Var, u32>,
110    /// Every value a named variable was given, in the order they were recorded.
111    holds: Vec<(Value, u32)>,
112    /// Which values a named variable has already been given, so that the second variable to be
113    /// written the same value is not given it again. See [`Ssa::write`].
114    owned: HashSet<Value>,
115}
116
117impl Ssa {
118    /// A construction over a function whose pointers are as wide as that integer type.
119    ///
120    /// The width is here because of one case: reading a variable that nothing has written, in
121    /// a block nothing branches to. C says the value is indeterminate and
122    /// `spec/08-ir.md` section 8.4 says it is unspecified but stable, so this hands back a
123    /// zero, and a zero of pointer type is an integer zero cast to one.
124    #[must_use]
125    pub fn new(address: Type) -> Ssa {
126        Ssa {
127            address,
128            defs: HashMap::new(),
129            sealed: Vec::new(),
130            incomplete: Vec::new(),
131            preds: Vec::new(),
132            phis: HashMap::new(),
133            users: HashMap::new(),
134            subst: HashMap::new(),
135            zero: Vec::new(),
136            named: HashMap::new(),
137            holds: Vec::new(),
138            owned: HashSet::new(),
139        }
140    }
141
142    /// Says that a variable is a declaration the program wrote, so that the values it turns into
143    /// come out of here knowing which one.
144    ///
145    /// The number is whatever the caller counts declarations by and means nothing here, which is
146    /// the same arrangement [`rucc_ir::Func::declare_mem`] makes for a local that got a slot
147    /// instead. A variable nothing said this about is a temporary, and its values are nobody's.
148    pub fn stands_for(&mut self, var: Var, decl: u32) {
149        self.named.insert(var, decl);
150    }
151
152    /// Records that a variable holds a value from here to the end of the block.
153    ///
154    /// The name goes on the value only the first time a named variable is written it, which is the
155    /// rule that keeps `int m = a;` from making the whole of `a` answer to `m` as well. An
156    /// assignment from something already live writes no new value, so both names would be behind
157    /// the one value and everything downstream would have to say the two variables are in the same
158    /// register everywhere either of them is, which is wrong wherever the program has since written
159    /// one of them. The value belongs to the name that was written it first, and the copy says
160    /// nothing rather than something wrong. It gets an answer again at the next assignment that
161    /// computes anything, which is where a value of its own comes from.
162    pub fn write(&mut self, var: Var, block: Block, value: Value) {
163        if let Some(&decl) = self.named.get(&var) {
164            if self.owned.insert(value) {
165                self.holds.push((value, decl));
166            }
167        }
168        self.defs.insert((var, block), value);
169    }
170
171    /// The value a variable holds at this point in a block, which is the whole algorithm.
172    ///
173    /// The type is what a parameter would be given if one has to be made. It is passed in
174    /// rather than remembered per variable because the caller has it in hand and a variable
175    /// whose type this had to store would be a variable this had to be told about first. A
176    /// variable read at two types is a variable read wrong, and what comes back is whatever
177    /// the first read decided.
178    ///
179    /// What comes back may be a parameter that [`Ssa::finish`] later takes out. Putting it
180    /// into the function is safe, because finish rewrites everything the function holds.
181    /// Remembering it on the side and comparing it to something afterwards is not.
182    ///
183    /// # Panics
184    ///
185    /// Panics if the function has no entry block, which can only happen when nothing has been
186    /// built into it yet.
187    pub fn read(&mut self, func: &mut Func, var: Var, block: Block, ty: Type) -> Value {
188        // A run of blocks with one predecessor each is walked rather than recursed through.
189        // It is the shape a sequence of `if (c) return;` leaves behind, there can be thousands
190        // of them in one function, and the recursion the paper is written with would be that
191        // deep.
192        let mut chain = Vec::new();
193        let mut at = block;
194        let value = loop {
195            if let Some(&value) = self.defs.get(&(var, at)) {
196                break self.resolve(value);
197            }
198            self.reserve(at);
199            if !self.sealed[at.index()] {
200                break self.pending(func, var, at, ty);
201            }
202            match self.preds[at.index()].len() {
203                // Nothing reaches here, so nothing wrote it on the way.
204                0 => break self.undefined(func, ty),
205                // One predecessor is not a choice, so it needs no parameter to record one.
206                1 => {
207                    chain.push(at);
208                    at = self.preds[at.index()][0].from;
209                }
210                _ => break self.phi(func, var, at, ty),
211            }
212        };
213        for at in chain {
214            self.write(var, at, value);
215        }
216        self.write(var, block, value);
217        value
218    }
219
220    /// Records the edges a terminator makes, which is what tells this the shape of the CFG.
221    ///
222    /// Every terminator has to be handed over, and before the block it goes to is sealed. A
223    /// branch this was not told about is a predecessor that will be missed, and the parameter
224    /// that should have collected a value from it will be short an argument, which is
225    /// something the verifier says out loud rather than something that goes quiet.
226    ///
227    /// # Panics
228    ///
229    /// Panics if the instruction is not in a block.
230    pub fn branch(&mut self, func: &Func, inst: Inst) {
231        let from = func.block_of(inst).expect("a terminator in a block");
232        for call in func.target_list(inst).iter() {
233            let to = func[call].block;
234            self.reserve(to);
235            self.preds[to.index()].push(Edge { from, call });
236        }
237    }
238
239    /// Says that a block has all the predecessors it is going to have.
240    ///
241    /// # Panics
242    ///
243    /// Panics if the block has already been sealed.
244    pub fn seal(&mut self, func: &mut Func, block: Block) {
245        self.reserve(block);
246        assert!(!self.sealed[block.index()], "a block is sealed once");
247        self.sealed[block.index()] = true;
248        // Taken rather than iterated, because filling one of these in reads variables, which
249        // can leave a parameter waiting in another block but never in this one.
250        let waiting = std::mem::take(&mut self.incomplete[block.index()]);
251        for (var, phi) in waiting {
252            let value = self.operands(func, var, phi);
253            // Only when the parameter is still what the block holds. Between the read that made
254            // it and this, the block may have written the variable again, and that write is what
255            // the block holds now: the parameter is what it held at the top. A `switch` case is
256            // where this happens, since it is read from, written to, and sealed only when the
257            // whole body has been walked.
258            if self.defs.get(&(var, block)) == Some(&phi) {
259                self.write(var, block, value);
260            }
261        }
262    }
263
264    /// Whether a block has been told it has all its predecessors.
265    #[must_use]
266    pub fn is_sealed(&self, block: Block) -> bool {
267        self.sealed.get(block.index()).copied().unwrap_or(false)
268    }
269
270    /// Applies everything that was worked out and drops what turned out to be redundant.
271    ///
272    /// Until this runs the function is correct but wordy: a parameter that stands for one
273    /// value is still a parameter, and the branches still pass it. This resolves every operand
274    /// of every instruction and every argument of every branch once, and then takes the
275    /// parameters out along with the arguments that fed them.
276    pub fn finish(mut self, func: &mut Func) {
277        self.names(func);
278        if self.subst.is_empty() {
279            return;
280        }
281
282        let blocks: Vec<Block> = func.blocks().collect();
283        for &block in &blocks {
284            let insts: Vec<Inst> = func.insts(block).collect();
285            for inst in insts {
286                let args = func[inst].args;
287                func.rewrite(args, |value| self.resolve(value));
288                for call in func.target_list(inst).iter() {
289                    let args = func[call].args;
290                    func.rewrite(args, |value| self.resolve(value));
291                }
292            }
293        }
294
295        // Which positions each block is losing. Read off the function rather than off the
296        // edges this was told about, so that a branch nobody mentioned still comes out with
297        // arguments that match the block it goes to.
298        let mut dropped: Vec<Vec<usize>> = vec![Vec::new(); func.counts().blocks];
299        for &block in &blocks {
300            for (index, &param) in func[block].params.iter().enumerate() {
301                if self.subst.contains_key(&param) {
302                    dropped[block.index()].push(index);
303                }
304            }
305        }
306
307        for &block in &blocks {
308            let insts: Vec<Inst> = func.insts(block).collect();
309            for inst in insts {
310                for at in func.target_list(inst).iter() {
311                    let mut call = func[at];
312                    let going = &dropped[call.block.index()];
313                    if going.is_empty() {
314                        continue;
315                    }
316                    let kept: Vec<Value> = func[call.args]
317                        .iter()
318                        .copied()
319                        .enumerate()
320                        .filter(|(index, _)| !going.contains(index))
321                        .map(|(_, value)| value)
322                        .collect();
323                    call.args = func.push_values(&kept);
324                    func.set_block_call(at, call);
325                }
326            }
327        }
328
329        for &block in &blocks {
330            if !dropped[block.index()].is_empty() {
331                func.retain_params(block, |param| !self.subst.contains_key(&param));
332            }
333        }
334    }
335
336    /// Hands the function which declaration each of its values is the value of.
337    ///
338    /// Resolved on the way out rather than as it was recorded, because a parameter that stands for
339    /// one value is not known to until the blocks that feed it have been walked, and a name put on
340    /// one before then would be a name on a value the function is about to lose. What is left after
341    /// resolving is a value that is still there, which is the only kind worth naming.
342    fn names(&mut self, func: &mut Func) {
343        for (value, decl) in std::mem::take(&mut self.holds) {
344            let value = self.resolve(value);
345            func.declare_value(value, decl);
346        }
347    }
348
349    // The parts of the algorithm.
350
351    /// A parameter for a block that does not know its predecessors yet.
352    fn pending(&mut self, func: &mut Func, var: Var, block: Block, ty: Type) -> Value {
353        let phi = func.append_param(block, ty);
354        self.phis.insert(phi, Phi { block, var });
355        self.incomplete[block.index()].push((var, phi));
356        self.write(var, block, phi);
357        phi
358    }
359
360    /// A parameter for a block that has more than one predecessor, filled in at once.
361    fn phi(&mut self, func: &mut Func, var: Var, block: Block, ty: Type) -> Value {
362        let phi = func.append_param(block, ty);
363        self.phis.insert(phi, Phi { block, var });
364        // Written before the operands are read, because reading them can come back here, and
365        // this is what stops a loop going round for ever.
366        self.write(var, block, phi);
367        self.operands(func, var, phi)
368    }
369
370    /// Gives a parameter one argument in each predecessor, and asks whether it was worth it.
371    fn operands(&mut self, func: &mut Func, var: Var, phi: Value) -> Value {
372        let block = self.phis[&phi].block;
373        let ty = func[phi].ty;
374        // By index, because reading a variable in a predecessor can add edges elsewhere. Not
375        // here: the edges of a sealed block are all in, and an unsealed one is not filling
376        // anything in yet.
377        for index in 0..self.preds[block.index()].len() {
378            let edge = self.preds[block.index()][index];
379            let value = self.read(func, var, edge.from, ty);
380            let mut call = func[edge.call];
381            call.args = func.append_arg(call.args, value);
382            func.set_block_call(edge.call, call);
383            self.users.entry(value).or_default().push(phi);
384        }
385        self.trivial(func, phi)
386    }
387
388    /// Records a parameter as standing for one value, when that is all it ever collected.
389    ///
390    /// A parameter whose arguments are all one value, ignoring the ones that are the parameter
391    /// itself coming round a loop, is that value written at a distance. The paper deletes it
392    /// here. This records it and lets [`Ssa::finish`] do the deleting, which is what turns one
393    /// walk of the function per removal into one walk of the function.
394    fn trivial(&mut self, func: &mut Func, phi: Value) -> Value {
395        let block = self.phis[&phi].block;
396        let Some(at) = func[block].params.iter().position(|&param| param == phi) else {
397            return phi;
398        };
399
400        let mut same: Option<Value> = None;
401        for index in 0..self.preds[block.index()].len() {
402            let edge = self.preds[block.index()][index];
403            let arg = self.resolve(func[func[edge.call].args][at]);
404            if arg == phi || same == Some(arg) {
405                continue;
406            }
407            if same.is_some() {
408                // Two values reach here, so the parameter is what says which.
409                return phi;
410            }
411            same = Some(arg);
412        }
413
414        let same = match same {
415            Some(value) => value,
416            // No arguments at all, so nothing wrote the variable on any path that reaches
417            // here, and this is the same case as reading it in a block with no predecessors.
418            None => self.undefined(func, func[phi].ty),
419        };
420        self.subst.insert(phi, same);
421
422        // Whoever read this parameter now reads what it stands for, and one of them may have
423        // been holding on for this one value.
424        let users = self.users.remove(&phi).unwrap_or_default();
425        let inherited: Vec<Value> = users.iter().copied().filter(|&user| user != phi).collect();
426        self.users.entry(same).or_default().extend(inherited.iter().copied());
427        for user in inherited {
428            if !self.subst.contains_key(&user) {
429                self.trivial(func, user);
430            }
431        }
432        self.resolve(same)
433    }
434
435    /// What a value stands for, after every parameter along the way has been resolved.
436    ///
437    /// The walk terminates because a parameter is recorded as standing for something exactly
438    /// once and what it stands for was already resolved when it was recorded, so the chains
439    /// grow at the far end and never close on themselves.
440    fn resolve(&mut self, value: Value) -> Value {
441        let mut at = value;
442        while let Some(&next) = self.subst.get(&at) {
443            at = next;
444        }
445        if at != value {
446            self.subst.insert(value, at);
447        }
448        at
449    }
450
451    /// The value of a variable nothing wrote, which is a zero at the top of the entry block.
452    ///
453    /// One per type, so that two reads of the same uninitialized variable give the same
454    /// answer, which is what `spec/08-ir.md` means by unspecified but stable.
455    fn undefined(&mut self, func: &mut Func, ty: Type) -> Value {
456        if let Some(&(_, value)) = self.zero.iter().find(|&&(at, _)| at == ty) {
457            return value;
458        }
459
460        let entry = func.entry().expect("a function with a block in it");
461        let first = func.insts(entry).next();
462        let value = if ty.is_ptr() {
463            let int = self.constant(func, entry, first, self.address);
464            let args = func.push_values(&[int]);
465            let cast = func.create_inst(
466                InstData { args, ..InstData::new(Opcode::IntToPtr) },
467                &[ty],
468                Span::DUMMY,
469            );
470            place(func, entry, first, cast);
471            func[cast].first_result.expect("one result")
472        } else {
473            self.constant(func, entry, first, ty)
474        };
475
476        self.zero.push((ty, value));
477        value
478    }
479
480    /// A zero of an arithmetic type, at the top of the entry block.
481    fn constant(&mut self, func: &mut Func, entry: Block, first: Option<Inst>, ty: Type) -> Value {
482        let imm = if ty.lane().is_float() { Imm::from_bits(0) } else { Imm::int(0, ty.lane()) };
483        let imm = func.add_imm(imm);
484        let opcode = if ty.lane().is_float() { Opcode::FConst } else { Opcode::IConst };
485        let inst = func.create_inst(
486            InstData { extra: Extra::Imm(imm), ..InstData::new(opcode) },
487            &[ty],
488            Span::DUMMY,
489        );
490        place(func, entry, first, inst);
491        func[inst].first_result.expect("one result")
492    }
493
494    /// Makes room for a block this has not been told about before.
495    fn reserve(&mut self, block: Block) {
496        let wanted = block.index() + 1;
497        if self.sealed.len() < wanted {
498            self.sealed.resize(wanted, false);
499            self.incomplete.resize_with(wanted, Vec::new);
500            self.preds.resize_with(wanted, Vec::new);
501        }
502    }
503}
504
505/// Puts an instruction at the top of the entry block, before whatever was first.
506fn place(func: &mut Func, entry: Block, first: Option<Inst>, inst: Inst) {
507    match first {
508        Some(first) => func.insert_before(inst, first),
509        None => func.append_inst(entry, inst),
510    }
511}
512
513#[cfg(test)]
514mod tests {
515    use rucc_base::Interner;
516    use rucc_ir::{Builder, Flags, IntPred, Module, Signature, print_func, verify_func};
517    use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
518
519    use super::*;
520
521    const I32: Type = Type::int(32);
522    const BOOL: Type = Type::int(1);
523
524    fn target() -> TargetInfo {
525        TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
526    }
527
528    /// The function as text, after the verifier has agreed that it is one.
529    ///
530    /// Both halves matter and neither says what the other says. The verifier says the result is
531    /// a function the rest of the compiler may believe, and the text says which values the
532    /// algorithm decided on, which is the part a person has to read to know it did the right
533    /// thing rather than merely a consistent one.
534    fn checked(func: Func, names: &mut Interner) -> String {
535        let mut module = Module::new(names.intern("t.c"), &target());
536        let id = module.add_func(func);
537        if let Err(errors) = verify_func(&module, &module[id], names) {
538            let listed: Vec<String> = errors.iter().map(ToString::to_string).collect();
539            panic!("{}", listed.join("\n"));
540        }
541        print_func(&module, &module[id], names)
542    }
543
544    /// A function taking one condition and returning an `i32`, with its entry block sealed.
545    fn start(names: &mut Interner) -> (Func, Ssa, Block, Value) {
546        let signature = Signature::new().with_params(&[BOOL]).with_returns(&[I32]);
547        let mut func = Func::new(names.intern("f"), signature);
548        let entry = func.create_block();
549        let cond = func.append_param(entry, BOOL);
550        let mut ssa = Ssa::new(Type::int(64));
551        ssa.seal(&mut func, entry);
552        (func, ssa, entry, cond)
553    }
554
555    #[test]
556    fn a_variable_read_where_it_was_written_is_the_value_it_was_written() {
557        let mut names = Interner::new();
558        let (mut func, mut ssa, entry, _) = start(&mut names);
559        let x = Var::new(0);
560
561        let one = Builder::new(&mut func, entry).iconst(I32, 1);
562        ssa.write(x, entry, one);
563        let read = ssa.read(&mut func, x, entry, I32);
564        assert_eq!(read, one);
565
566        Builder::new(&mut func, entry).ret(&[read]);
567        ssa.finish(&mut func);
568        assert!(func[entry].params.len() == 1, "no parameter was needed");
569    }
570
571    #[test]
572    fn a_variable_written_on_both_arms_arrives_as_a_block_parameter() {
573        let mut names = Interner::new();
574        let (mut func, mut ssa, entry, cond) = start(&mut names);
575        let x = Var::new(0);
576
577        let then = func.create_block();
578        let otherwise = func.create_block();
579        let join = func.create_block();
580
581        let branch = Builder::new(&mut func, entry).br_if(cond, then, &[], otherwise, &[]);
582        ssa.branch(&func, branch);
583        ssa.seal(&mut func, then);
584        ssa.seal(&mut func, otherwise);
585
586        let one = Builder::new(&mut func, then).iconst(I32, 1);
587        ssa.write(x, then, one);
588        let jump = Builder::new(&mut func, then).jump(join, &[]);
589        ssa.branch(&func, jump);
590
591        let two = Builder::new(&mut func, otherwise).iconst(I32, 2);
592        ssa.write(x, otherwise, two);
593        let jump = Builder::new(&mut func, otherwise).jump(join, &[]);
594        ssa.branch(&func, jump);
595
596        ssa.seal(&mut func, join);
597        let read = ssa.read(&mut func, x, join, I32);
598        Builder::new(&mut func, join).ret(&[read]);
599        ssa.finish(&mut func);
600
601        assert_eq!(checked(func, &mut names), DIAMOND);
602    }
603
604    #[test]
605    fn a_variable_both_arms_agree_about_needs_no_block_parameter() {
606        let mut names = Interner::new();
607        let (mut func, mut ssa, entry, cond) = start(&mut names);
608        let x = Var::new(0);
609
610        let one = Builder::new(&mut func, entry).iconst(I32, 1);
611        ssa.write(x, entry, one);
612
613        let then = func.create_block();
614        let otherwise = func.create_block();
615        let join = func.create_block();
616
617        let branch = Builder::new(&mut func, entry).br_if(cond, then, &[], otherwise, &[]);
618        ssa.branch(&func, branch);
619        ssa.seal(&mut func, then);
620        ssa.seal(&mut func, otherwise);
621
622        for block in [then, otherwise] {
623            let jump = Builder::new(&mut func, block).jump(join, &[]);
624            ssa.branch(&func, jump);
625        }
626
627        ssa.seal(&mut func, join);
628        let read = ssa.read(&mut func, x, join, I32);
629        assert_eq!(read, one, "the parameter stood for the one value both arms had");
630        Builder::new(&mut func, join).ret(&[read]);
631        ssa.finish(&mut func);
632
633        assert!(func[join].params.is_empty(), "the parameter was taken out again");
634        assert_eq!(checked(func, &mut names), AGREED);
635    }
636
637    /// Every value in the function a declaration is behind, by its number, and what is behind it.
638    fn named(func: &Func) -> Vec<(usize, Vec<u32>)> {
639        (0..func.counts().values)
640            .map(|at| (at, func.value_decls(Idx::from_usize(at)).collect::<Vec<u32>>()))
641            .filter(|(_, decls)| !decls.is_empty())
642            .collect()
643    }
644
645    /// A variable the caller named leaves every value it turned into knowing which declaration it
646    /// is, the parameter that collects two of them included.
647    ///
648    /// Three values for one variable is the point. A debugger asking where the variable is at an
649    /// address has to be told which of the three was the one in hand there, and that is a question
650    /// about the code that came out rather than about this.
651    #[test]
652    fn a_named_variable_leaves_every_value_it_turned_into_knowing_which_it_is() {
653        let mut names = Interner::new();
654        let (mut func, mut ssa, entry, cond) = start(&mut names);
655        let x = Var::new(0);
656        ssa.stands_for(x, 41);
657
658        let then = func.create_block();
659        let otherwise = func.create_block();
660        let join = func.create_block();
661
662        let branch = Builder::new(&mut func, entry).br_if(cond, then, &[], otherwise, &[]);
663        ssa.branch(&func, branch);
664        ssa.seal(&mut func, then);
665        ssa.seal(&mut func, otherwise);
666
667        let one = Builder::new(&mut func, then).iconst(I32, 1);
668        ssa.write(x, then, one);
669        let jump = Builder::new(&mut func, then).jump(join, &[]);
670        ssa.branch(&func, jump);
671
672        let two = Builder::new(&mut func, otherwise).iconst(I32, 2);
673        ssa.write(x, otherwise, two);
674        let jump = Builder::new(&mut func, otherwise).jump(join, &[]);
675        ssa.branch(&func, jump);
676
677        ssa.seal(&mut func, join);
678        let read = ssa.read(&mut func, x, join, I32);
679        Builder::new(&mut func, join).ret(&[read]);
680        ssa.finish(&mut func);
681
682        let held = vec![(one.index(), vec![41]), (two.index(), vec![41]), (read.index(), vec![41])];
683        assert_eq!(named(&func), held);
684    }
685
686    /// A parameter that turned out to stand for one value takes no name with it when it goes.
687    ///
688    /// The name was recorded against the parameter while the arms were being walked, because
689    /// nothing knew yet that both of them would agree. What comes out is a name on the value the
690    /// parameter stood for, and nothing on a value the function no longer has.
691    #[test]
692    fn a_name_recorded_against_a_parameter_follows_it_to_what_it_stood_for() {
693        let mut names = Interner::new();
694        let (mut func, mut ssa, entry, cond) = start(&mut names);
695        let x = Var::new(0);
696        ssa.stands_for(x, 41);
697
698        let one = Builder::new(&mut func, entry).iconst(I32, 1);
699        ssa.write(x, entry, one);
700
701        let then = func.create_block();
702        let otherwise = func.create_block();
703        let join = func.create_block();
704
705        let branch = Builder::new(&mut func, entry).br_if(cond, then, &[], otherwise, &[]);
706        ssa.branch(&func, branch);
707        ssa.seal(&mut func, then);
708        ssa.seal(&mut func, otherwise);
709
710        for block in [then, otherwise] {
711            let jump = Builder::new(&mut func, block).jump(join, &[]);
712            ssa.branch(&func, jump);
713        }
714
715        ssa.seal(&mut func, join);
716        let read = ssa.read(&mut func, x, join, I32);
717        Builder::new(&mut func, join).ret(&[read]);
718        ssa.finish(&mut func);
719
720        assert_eq!(named(&func), vec![(one.index(), vec![41])]);
721    }
722
723    /// A second variable written a value the first one already holds takes no name from it.
724    ///
725    /// What `int m = a;` looks like from here. The assignment writes no new value, so without this
726    /// both names would be behind the one value and everything downstream would have to say the
727    /// two are in the same register everywhere either of them is, which is wrong the moment the
728    /// program writes one of them again. The value belongs to the name it was written into first.
729    #[test]
730    fn a_variable_written_a_value_another_one_already_holds_takes_no_name_from_it() {
731        let mut names = Interner::new();
732        let (mut func, mut ssa, entry, _) = start(&mut names);
733        let (a, m) = (Var::new(0), Var::new(1));
734        ssa.stands_for(a, 41);
735        ssa.stands_for(m, 42);
736
737        let one = Builder::new(&mut func, entry).iconst(I32, 1);
738        ssa.write(a, entry, one);
739        let read = ssa.read(&mut func, a, entry, I32);
740        ssa.write(m, entry, read);
741        Builder::new(&mut func, entry).ret(&[read]);
742        ssa.finish(&mut func);
743
744        assert_eq!(named(&func), vec![(one.index(), vec![41])]);
745    }
746
747    /// A variable nothing named leaves nothing behind, which is every temporary an expression
748    /// needed somewhere to put.
749    #[test]
750    fn a_variable_nothing_named_leaves_no_names_at_all() {
751        let mut names = Interner::new();
752        let (mut func, mut ssa, entry, _) = start(&mut names);
753        let x = Var::new(0);
754
755        let one = Builder::new(&mut func, entry).iconst(I32, 1);
756        ssa.write(x, entry, one);
757        let read = ssa.read(&mut func, x, entry, I32);
758        Builder::new(&mut func, entry).ret(&[read]);
759        ssa.finish(&mut func);
760
761        assert!(named(&func).is_empty());
762    }
763
764    #[test]
765    fn a_variable_a_loop_changes_is_carried_by_the_headers_parameter() {
766        let mut names = Interner::new();
767        let (mut func, mut ssa, entry, _) = start(&mut names);
768        let x = Var::new(0);
769
770        let zero = Builder::new(&mut func, entry).iconst(I32, 0);
771        ssa.write(x, entry, zero);
772
773        let header = func.create_block();
774        let body = func.create_block();
775        let exit = func.create_block();
776
777        let jump = Builder::new(&mut func, entry).jump(header, &[]);
778        ssa.branch(&func, jump);
779
780        // The header is left unsealed, which is the whole point: the back edge has not been
781        // emitted yet and reading the variable here cannot ask the predecessors.
782        let counter = ssa.read(&mut func, x, header, I32);
783        let mut build = Builder::new(&mut func, header);
784        let ten = build.iconst(I32, 10);
785        let test = build.icmp(IntPred::Slt, counter, ten);
786        let branch = build.br_if(test, body, &[], exit, &[]);
787        ssa.branch(&func, branch);
788        ssa.seal(&mut func, body);
789        ssa.seal(&mut func, exit);
790
791        let carried = ssa.read(&mut func, x, body, I32);
792        let mut build = Builder::new(&mut func, body);
793        let one = build.iconst(I32, 1);
794        let next = build.binary(Opcode::Add, carried, one, Flags::NONE);
795        let jump = build.jump(header, &[]);
796        ssa.write(x, body, next);
797        ssa.branch(&func, jump);
798        ssa.seal(&mut func, header);
799
800        let result = ssa.read(&mut func, x, exit, I32);
801        Builder::new(&mut func, exit).ret(&[result]);
802        ssa.finish(&mut func);
803
804        assert_eq!(checked(func, &mut names), LOOP);
805    }
806
807    #[test]
808    fn a_variable_a_loop_does_not_change_is_not_carried_at_all() {
809        let mut names = Interner::new();
810        let (mut func, mut ssa, entry, cond) = start(&mut names);
811        let x = Var::new(0);
812
813        let seven = Builder::new(&mut func, entry).iconst(I32, 7);
814        ssa.write(x, entry, seven);
815
816        let header = func.create_block();
817        let body = func.create_block();
818        let exit = func.create_block();
819
820        let jump = Builder::new(&mut func, entry).jump(header, &[]);
821        ssa.branch(&func, jump);
822
823        let branch = Builder::new(&mut func, header).br_if(cond, body, &[], exit, &[]);
824        ssa.branch(&func, branch);
825        ssa.seal(&mut func, body);
826        ssa.seal(&mut func, exit);
827
828        // Read in the body, which is what makes the header need a parameter before the back
829        // edge says the parameter is only ever the one value.
830        let inside = ssa.read(&mut func, x, body, I32);
831        let mut build = Builder::new(&mut func, body);
832        build.binary(Opcode::Add, inside, inside, Flags::NONE);
833        let jump = build.jump(header, &[]);
834        ssa.branch(&func, jump);
835        ssa.seal(&mut func, header);
836
837        let result = ssa.read(&mut func, x, exit, I32);
838        Builder::new(&mut func, exit).ret(&[result]);
839        ssa.finish(&mut func);
840
841        assert!(func[header].params.is_empty(), "the parameter went, and the addition reads %1");
842        assert_eq!(checked(func, &mut names), UNCHANGED);
843    }
844
845    #[test]
846    fn a_variable_two_nested_loops_do_not_change_is_carried_by_neither() {
847        // The case the paper's recursive removal is for. The inner header's parameter looks
848        // like it collects two values until the outer header's parameter turns out to stand
849        // for one, and nothing but redoing the inner one finds that out.
850        let mut names = Interner::new();
851        let (mut func, mut ssa, entry, cond) = start(&mut names);
852        let x = Var::new(0);
853
854        let seven = Builder::new(&mut func, entry).iconst(I32, 7);
855        ssa.write(x, entry, seven);
856
857        let outer = func.create_block();
858        let inner = func.create_block();
859        let latch = func.create_block();
860        let exit = func.create_block();
861
862        let jump = Builder::new(&mut func, entry).jump(outer, &[]);
863        ssa.branch(&func, jump);
864
865        let jump = Builder::new(&mut func, outer).jump(inner, &[]);
866        ssa.branch(&func, jump);
867
868        let read = ssa.read(&mut func, x, inner, I32);
869        let mut build = Builder::new(&mut func, inner);
870        build.binary(Opcode::Add, read, read, Flags::NONE);
871        let branch = build.br_if(cond, inner, &[], latch, &[]);
872        ssa.branch(&func, branch);
873        ssa.seal(&mut func, inner);
874        ssa.seal(&mut func, latch);
875
876        let branch = Builder::new(&mut func, latch).br_if(cond, outer, &[], exit, &[]);
877        ssa.branch(&func, branch);
878        ssa.seal(&mut func, outer);
879        ssa.seal(&mut func, exit);
880
881        let result = ssa.read(&mut func, x, exit, I32);
882        Builder::new(&mut func, exit).ret(&[result]);
883        ssa.finish(&mut func);
884
885        assert!(func[outer].params.is_empty() && func[inner].params.is_empty());
886        assert_eq!(checked(func, &mut names), NESTED);
887    }
888
889    #[test]
890    fn a_write_after_the_read_that_made_a_parameter_is_what_the_block_holds() {
891        // The shape a `switch` case has. The case block is left unsealed while the rest of the
892        // body is walked, so a read in it makes a parameter, and a write after that read is
893        // what the block holds from then on. Sealing must not put the parameter back.
894        let mut names = Interner::new();
895        let (mut func, mut ssa, entry, cond) = start(&mut names);
896        let x = Var::new(0);
897
898        let one = Builder::new(&mut func, entry).iconst(I32, 1);
899        ssa.write(x, entry, one);
900
901        let case = func.create_block();
902        let other = func.create_block();
903        let branch = Builder::new(&mut func, entry).br_if(cond, case, &[], other, &[]);
904        ssa.branch(&func, branch);
905        ssa.seal(&mut func, other);
906
907        // The case, reached before it is known what else reaches it.
908        let read = ssa.read(&mut func, x, case, I32);
909        let sum = Builder::new(&mut func, case).binary(Opcode::Add, read, read, Flags::NONE);
910        ssa.write(x, case, sum);
911
912        // The other edge into it, which is what a `case` falling into the next one is.
913        let mut build = Builder::new(&mut func, other);
914        let two = build.iconst(I32, 2);
915        let jump = build.jump(case, &[]);
916        ssa.write(x, other, two);
917        ssa.branch(&func, jump);
918        ssa.seal(&mut func, case);
919
920        let after = ssa.read(&mut func, x, case, I32);
921        assert_eq!(after, sum, "the block holds what it wrote, not the parameter it started at");
922        Builder::new(&mut func, case).ret(&[after]);
923        ssa.finish(&mut func);
924
925        assert_eq!(checked(func, &mut names), WRITTEN_AFTER);
926    }
927
928    #[test]
929    fn a_variable_nothing_wrote_reads_as_the_same_zero_every_time() {
930        let mut names = Interner::new();
931        let (mut func, mut ssa, entry, _) = start(&mut names);
932        let x = Var::new(0);
933        let y = Var::new(1);
934        let z = Var::new(2);
935
936        let first = ssa.read(&mut func, x, entry, I32);
937        let second = ssa.read(&mut func, y, entry, I32);
938        let pointer = ssa.read(&mut func, z, entry, Type::PTR);
939        assert_eq!(first, second, "unspecified, and the same both times");
940        assert_ne!(first, pointer);
941
942        Builder::new(&mut func, entry).ret(&[first]);
943        ssa.finish(&mut func);
944        assert_eq!(checked(func, &mut names), UNWRITTEN);
945    }
946
947    /// Two arms with different values, so the block below them takes a parameter.
948    const DIAMOND: &str = "\
949func @f(i1) -> i32, linkage(external) {
950block0(%0: i1):
951    br_if %0, block1, block2
952
953block1:
954    %1 = iconst.i32 1
955    jump block3(%1)
956
957block2:
958    %2 = iconst.i32 2
959    jump block3(%2)
960
961block3(%3: i32):
962    return %3
963}
964";
965
966    /// Two arms with one value between them, so it does not.
967    const AGREED: &str = "\
968func @f(i1) -> i32, linkage(external) {
969block0(%0: i1):
970    %1 = iconst.i32 1
971    br_if %0, block1, block2
972
973block1:
974    jump block3
975
976block2:
977    jump block3
978
979block3:
980    return %1
981}
982";
983
984    /// A counter, which the header's parameter carries round and the body's addition adds
985    /// to. The parameter is what a phi node would have been.
986    const LOOP: &str = "\
987func @f(i1) -> i32, linkage(external) {
988block0(%0: i1):
989    %1 = iconst.i32 0
990    jump block1(%1)
991
992block1(%2: i32):
993    %3 = iconst.i32 10
994    %4 = icmp slt %2, %3
995    br_if %4, block2, block3
996
997block2:
998    %5 = iconst.i32 1
999    %6 = add %2, %5
1000    jump block1(%6)
1001
1002block3:
1003    return %2
1004}
1005";
1006
1007    /// The same loop over a variable nothing in it writes, where the header's parameter is
1008    /// made, filled in from both edges, and then found to be the one value it started with.
1009    const UNCHANGED: &str = "\
1010func @f(i1) -> i32, linkage(external) {
1011block0(%0: i1):
1012    %1 = iconst.i32 7
1013    jump block1
1014
1015block1:
1016    br_if %0, block2, block3
1017
1018block2:
1019    %2 = add %1, %1
1020    jump block1
1021
1022block3:
1023    return %1
1024}
1025";
1026
1027    /// Two nested loops over a variable neither writes. The inner header's parameter is only
1028    /// found to be redundant after the outer one is, which is the recursion in the paper.
1029    const NESTED: &str = "\
1030func @f(i1) -> i32, linkage(external) {
1031block0(%0: i1):
1032    %1 = iconst.i32 7
1033    jump block1
1034
1035block1:
1036    jump block2
1037
1038block2:
1039    %2 = add %1, %1
1040    br_if %0, block2, block3
1041
1042block3:
1043    br_if %0, block1, block4
1044
1045block4:
1046    return %1
1047}
1048";
1049
1050    /// A block whose parameter carries what the variable held on the way in, and whose own
1051    /// write is what it holds on the way out.
1052    const WRITTEN_AFTER: &str = "\
1053func @f(i1) -> i32, linkage(external) {
1054block0(%0: i1):
1055    %1 = iconst.i32 1
1056    br_if %0, block1(%1), block2
1057
1058block1(%2: i32):
1059    %3 = add %2, %2
1060    return %3
1061
1062block2:
1063    %4 = iconst.i32 2
1064    jump block1(%4)
1065}
1066";
1067
1068    /// A read of something nothing wrote, twice for one type and once for a pointer, which is
1069    /// two constants at the top of the entry block and a cast for the pointer.
1070    const UNWRITTEN: &str = "\
1071func @f(i1) -> i32, linkage(external) {
1072block0(%0: i1):
1073    %1 = iconst.i64 0
1074    %2 = inttoptr.ptr %1
1075    %3 = iconst.i32 0
1076    return %3
1077}
1078";
1079}