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