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