Skip to main content

rucc_codegen/
compare.rs

1//! Taking out a comparison the machine has already made.
2//!
3//! Design: `spec/optimizer/37-machine-level-optimization.md` section 37.4.
4//!
5//! A comparison produces no value. It sets a few bits nobody named and the instruction behind it
6//! reads them, so a comparison that sets the bits that are already there is one nothing could tell
7//! had run. There are two ways for that to happen, and both of them are about an instruction a
8//! little way in front rather than about a dataflow the whole function takes part in.
9//!
10//! The same comparison twice. `if (x == y) ... else if (x != y)` and every expression that asks a
11//! question and then asks its negation come out as two comparisons of the same two registers with
12//! nothing between them but the bytes each one kept. The second asks what the first asked and the
13//! answer has not moved.
14//!
15//! A comparison against zero of something arithmetic has just worked out. `if (a & MASK)` is an
16//! `and` and then a comparison of its result against zero, and the `and` set the bits that
17//! comparison would have set on its way past. This is the common one by a long way: at `-O2` over
18//! the SQLite amalgamation there are 2250 of these and 0 of the other shape.
19//!
20//! # Why it runs after the layout rather than before
21//!
22//! Because this is the second pass to work on a pair of instructions whose middle has to stay
23//! empty, and the first is the block layout. A branch on a comparison is written there as the
24//! comparison with its byte taken off and a jump that reads the condition state, and what is
25//! between those two is live and is not a register, so anything that ran afterwards and put an
26//! instruction between them would be wrong. Running last is the whole of what makes this safe,
27//! which is the sentence section 37.4 uses about the layout itself.
28//!
29//! It also makes the two shapes one shape. A comparison the layout folded a branch into is a
30//! comparison that keeps nothing, one whose byte something else wanted is a comparison that keeps
31//! a byte, and after the layout both are sitting in a block to be looked at the same way. Before
32//! the layout the first kind does not exist yet, so a pass that ran earlier would have to either
33//! leave every branch alone or undo the fusion to get at one.
34//!
35//! # How the rewrite is made
36//!
37//! Through [`crate::changes`], one comparison at a time, because one comparison is all a change
38//! here is: a comparison that is already made is already made whatever happened to the one in
39//! front of it, so there is nothing to be all of or none of.
40//!
41//! What the framework is for here is the other half of it, which is the shape. What is left of a
42//! comparison is a different instruction with a different name and one operand rather than three,
43//! and that it is an instruction this machine has is now asked rather than believed. The condition
44//! state is the half nothing can check, because it is not a register and is in no operand vector,
45//! and the argument that the bits are already the bits stays the walk's own.
46//!
47//! # What a block boundary is
48//!
49//! The end of everything this knows. The state a comparison leaves is not a register and nothing
50//! in this back end carries one from a block to its successors: the layout writes the jump that
51//! reads a comparison into the same block as the comparison, which is the only place one is read
52//! at all. So the walk starts each block knowing nothing, which is what makes it a walk rather
53//! than a dataflow.
54//!
55//! # What it will not do
56//!
57//! A comparison with anything between it and the instruction that already made it that writes the
58//! condition state. The target says which instructions those are and says it about every name it
59//! does not recognise, so an opcode added to a rule set and not to that description makes this
60//! find less rather than making it wrong.
61//!
62//! A comparison of a register something wrote in between. The bits are still the bits the earlier
63//! instruction left, but they are about what the register held then and the comparison is about
64//! what it holds now. Every definition between the two is checked against the registers the
65//! earlier one was about, which are physical by the time this runs and so are the ones the machine
66//! will really read.
67//!
68//! A comparison against zero after arithmetic whose condition reads a part of the condition state
69//! the arithmetic did not leave the way a comparison would have. `subl` says whether its answer
70//! was zero and a comparison of that answer against zero would agree, and it says whether the
71//! subtraction overflowed where the comparison would have said it did not, so a signed `<` after
72//! one reads a sign and an overflow that no longer belong together. [`rucc_target::Zeroing`] is
73//! where each instruction says which conditions it is good for, and every condition that ends up
74//! reading what the arithmetic left has to be one of them, including the ones behind the
75//! comparison rather than on it.
76//!
77//! A comparison against zero after arithmetic that wrote a different number of bits. `andl` leaves
78//! a statement about thirty two bits and `cmpq $0` asks about sixty four, and on this machine the
79//! upper half is then zero and the two disagree about the sign.
80//!
81//! A comparison against zero after arithmetic whose condition state nothing is found to read. That
82//! is a comparison that is dead rather than redundant, and taking a dead one out is a different
83//! question: it needs no earlier instruction at all, so answering it here would mean answering it
84//! only where an earlier instruction happened to be.
85
86use rucc_base::Interner;
87use rucc_base::hash::Map;
88use rucc_mir::{self as mir, Role};
89use rucc_target::{Compare, FlagInsts, MachineInsts, Reads, RegClass, Zeroing};
90
91use crate::changes::{self, Changes, Plan};
92
93/// A register, and the file it is drawn from.
94///
95/// The class as well as the number, because the two files number from zero and `xmm0` is not
96/// `rax`. The width is deliberately not here: `%al` and `%eax` are one register, so a write of
97/// either is a write of the other and a statement about what the other held is a statement about
98/// a value that has moved.
99type Place = (RegClass, mir::Reg);
100
101/// Takes out every comparison whose condition state the instruction in front of it already left.
102///
103/// Gives back how many went, which the tests read and nothing else does.
104pub fn redundant(
105    func: &mut mir::Func,
106    insts: &FlagInsts,
107    machine: &MachineInsts,
108    names: &mut Interner,
109) -> usize {
110    // Every name the rewrite could want, before the walk rather than inside it. The walk holds a
111    // name it read out of the interner while it edits the function, and interning a new one there
112    // would be the same interner borrowed twice.
113    let opcodes: Map<&str, mir::Opcode> = insts
114        .compares
115        .iter()
116        .filter_map(|entry| entry.kept)
117        .map(|kept| (kept, mir::Opcode::new(names.intern(&format!("{}{kept}", insts.prefix)))))
118        .collect();
119    let names = &*names;
120    let mut counts = changes::Reads::of(func);
121    let mut gone = 0;
122    let mut seen = Map::default();
123    for block in func.blocks().collect::<Vec<_>>() {
124        let sequence: Vec<mir::Inst> = func.insts(block).collect();
125        let mut left: Option<Left> = None;
126        for at in 0..sequence.len() {
127            let inst = sequence[at];
128            let does = *seen
129                .entry(func[inst].opcode)
130                .or_insert_with(|| Does::of(insts, opcode(func, insts, names, inst)));
131            left = match does {
132                Does::Unknown => None,
133                Does::Compares(entry) => {
134                    let already = left
135                        .as_ref()
136                        .is_some_and(|had| had.answers(func, insts, names, &sequence, at, entry));
137                    // What the earlier instruction left comes to this one's answer, and it is
138                    // worked out here rather than after the rewrite because one of the answers to
139                    // what is left of an instruction is that there is nothing left of it.
140                    let after = stale(func, inst, left);
141                    if already && took(func, &opcodes, &mut counts, machine, names, inst, entry) {
142                        gone += 1;
143                        after
144                    } else {
145                        // Either the comparison is one nothing has made yet or it is one the
146                        // target would not have what is left of, and both of those are a
147                        // comparison that runs and leaves its own answer behind.
148                        stale(func, inst, Some(Left::made(func, entry, inst)))
149                    }
150                }
151                Does::Zeroes(name, zeroing) => Left::zeroed(func, insts, name, zeroing, inst),
152                Does::Writes => None,
153                Does::Neither => stale(func, inst, left),
154            };
155        }
156    }
157    gone
158}
159
160/// What the description says an opcode does to the condition state.
161///
162/// Asked once for each opcode a function has rather than once for each instruction, since each
163/// question is a walk down one of the target's tables comparing names and nearly every answer is
164/// no. A function has a few hundred opcodes at most, so the walk asks about each the first time it
165/// turns up and reads the answer back after that.
166#[derive(Debug, Clone, Copy)]
167enum Does<'a> {
168    /// The description does not cover it, so it may have done anything.
169    Unknown,
170    /// It makes a comparison.
171    Compares(&'static Compare),
172    /// It is arithmetic that leaves the comparison of what it wrote against zero. Asked before
173    /// whether it writes the condition state, because every one of these does and this is what it
174    /// wrote there.
175    Zeroes(&'a str, &'static Zeroing),
176    /// It writes the condition state with nothing this pass can use.
177    Writes,
178    /// It leaves the condition state alone.
179    Neither,
180}
181
182impl<'a> Does<'a> {
183    fn of(insts: &FlagInsts, name: Option<&'a str>) -> Self {
184        let Some(name) = name else { return Self::Unknown };
185        if let Some(entry) = insts.compare(name) {
186            Self::Compares(entry)
187        } else if let Some(zeroing) = insts.zeroed(name) {
188            Self::Zeroes(name, zeroing)
189        } else if (insts.writes)(name) {
190            Self::Writes
191        } else {
192            Self::Neither
193        }
194    }
195}
196
197/// What the condition state holds, and which registers it is a statement about.
198struct Left {
199    /// Which of the two ways it got there.
200    how: How,
201    /// The registers the statement is about, which anything writing one of makes it stale.
202    about: Vec<Place>,
203}
204
205/// The two ways the condition state comes to hold something this pass can use.
206enum How {
207    /// A comparison made it, and this is the question it asked.
208    Made {
209        /// The name of the comparison that keeps nothing, which is what says two are the same.
210        asks: &'static str,
211        /// What it compared, in the order it read them.
212        read: Vec<Place>,
213        /// The constant it compared against, if it compared against one.
214        imm: Option<i64>,
215    },
216    /// Arithmetic left it, and this is what a comparison against zero has to look like to be one
217    /// the arithmetic already made.
218    Zeroed {
219        /// How wide the value it wrote is.
220        width: u32,
221        /// Which conditions may read what it left.
222        covers: Zeroing,
223    },
224}
225
226impl Left {
227    /// What a comparison leaves behind.
228    fn made(func: &mir::Func, entry: &Compare, inst: mir::Inst) -> Self {
229        let read: Vec<Place> = reads(func, inst).into_iter().map(|(_, place)| place).collect();
230        Self {
231            how: How::Made {
232                asks: entry.asks,
233                read: read.clone(),
234                imm: func[inst].imm.map(|at| func[at].0),
235            },
236            about: read,
237        }
238    }
239
240    /// What arithmetic leaves behind, when what it wrote is one register of a width the
241    /// description names.
242    ///
243    /// The statement is about the register it wrote rather than about the ones it read, which is
244    /// what makes its own definition not something that makes it stale: what it wrote is the value
245    /// the comparison it stands in for is about.
246    fn zeroed(
247        func: &mir::Func,
248        insts: &FlagInsts,
249        name: &str,
250        zeroing: &Zeroing,
251        inst: mir::Inst,
252    ) -> Option<Self> {
253        let written = writes(func, inst);
254        let [(at, def)] = written[..] else { return None };
255        let width = (insts.width)(name, at)?;
256        Some(Self { how: How::Zeroed { width, covers: *zeroing }, about: vec![def] })
257    }
258
259    /// Whether this comparison is one the condition state already answers.
260    fn answers(
261        &self,
262        func: &mir::Func,
263        insts: &FlagInsts,
264        names: &Interner,
265        sequence: &[mir::Inst],
266        at: usize,
267        entry: &Compare,
268    ) -> bool {
269        let inst = sequence[at];
270        let asked: Vec<Place> = reads(func, inst).into_iter().map(|(_, place)| place).collect();
271        let against = func[inst].imm.map(|at| func[at].0);
272        match &self.how {
273            // The same question about the same values, so every bit of the answer is the bit that
274            // is already there and what reads it is not something anyone has to ask.
275            How::Made { asks, read, imm } => {
276                *asks == entry.asks && *read == asked && *imm == against
277            }
278            How::Zeroed { width, covers } => {
279                if against != Some(0) || self.about != asked {
280                    return false;
281                }
282                let [(index, _)] = reads(func, inst)[..] else { return false };
283                let Some(name) = opcode(func, insts, names, inst) else { return false };
284                if (insts.width)(name, index) != Some(*width) {
285                    return false;
286                }
287                let conditions = conditions(func, insts, names, sequence, at);
288                !conditions.is_empty() && conditions.iter().all(|&reads| covers.covers(reads))
289            }
290        }
291    }
292}
293
294/// The conditions that read what an instruction leaves in the condition state.
295///
296/// Its own first, which is where a comparison that keeps a byte carries the condition it is about,
297/// and then the ones behind it as far as whatever writes the condition state next. Both halves
298/// matter and for one reason: the rewrite leaves the readers where they are and takes the
299/// comparison out from under them, so each of them ends up reading what the instruction further
300/// back left instead.
301fn conditions(
302    func: &mir::Func,
303    insts: &FlagInsts,
304    names: &Interner,
305    sequence: &[mir::Inst],
306    at: usize,
307) -> Vec<Reads> {
308    let mut found = Vec::new();
309    let Some(name) = opcode(func, insts, names, sequence[at]) else { return found };
310    found.extend(insts.reads(name));
311    for &inst in &sequence[at + 1..] {
312        let Some(name) = opcode(func, insts, names, inst) else { break };
313        // What it reads before whether it writes, because an instruction can do both and the read
314        // it does is a read of what is there now. An add with carry is the one that does, and
315        // asking the questions the other way round would count it as the end of the walk and never
316        // count the carry it took off the comparison this is about to remove.
317        found.extend(insts.reads(name));
318        if (insts.writes)(name) {
319            break;
320        }
321    }
322    found
323}
324
325/// The same state, unless the instruction wrote a register it was a statement about.
326fn stale(func: &mir::Func, inst: mir::Inst, left: Option<Left>) -> Option<Left> {
327    let left = left?;
328    let touched = writes(func, inst).iter().any(|&(_, place)| left.about.contains(&place));
329    (!touched).then_some(left)
330}
331
332/// The registers an instruction reads, each with the index it reads it at.
333fn reads(func: &mir::Func, inst: mir::Inst) -> Vec<(u8, Place)> {
334    picked(func, inst, Role::Use)
335}
336
337/// The registers an instruction writes, each with the index it writes it at.
338fn writes(func: &mir::Func, inst: mir::Inst) -> Vec<(u8, Place)> {
339    let mut found = picked(func, inst, Role::Def);
340    found.extend(picked(func, inst, Role::EarlyDef));
341    found
342}
343
344/// The operands in that role, each with the index it is at.
345fn picked(func: &mir::Func, inst: mir::Inst, role: Role) -> Vec<(u8, Place)> {
346    func[func[inst].operands]
347        .iter()
348        .enumerate()
349        .filter(|(_, operand)| operand.role == role)
350        .filter_map(|(at, operand)| Some((u8::try_from(at).ok()?, (operand.class, operand.reg))))
351        .collect()
352}
353
354/// Turns a comparison into what is left of it, which is a byte or nothing at all, and says whether
355/// that was a change the target had.
356///
357/// The byte keeps the register it was going to and the constant goes, because what the constant
358/// was for was the comparison and the comparison is the part that is not happening. Nothing else
359/// about the instruction moves, which is what keeps this a rewrite of one instruction rather than
360/// a rewrite of the block around it.
361///
362/// One change is one set, since a comparison that is already made is already made whatever the one
363/// before it came to. What the set is for here is the other half of [`crate::changes`], which is
364/// the shape: the instruction the byte is left as is one this target has to have, and this is where
365/// that is asked rather than believed.
366fn took(
367    func: &mut mir::Func,
368    opcodes: &Map<&str, mir::Opcode>,
369    counts: &mut changes::Reads,
370    machine: &MachineInsts,
371    names: &Interner,
372    inst: mir::Inst,
373    entry: &Compare,
374) -> bool {
375    let mut set = Changes::new();
376    match entry.kept {
377        None => set.remove(inst),
378        Some(kept) => {
379            let Some(&opcode) = opcodes.get(kept) else { return false };
380            let byte: Vec<mir::Operand> = func[func[inst].operands]
381                .iter()
382                .filter(|operand| operand.role != Role::Use)
383                .copied()
384                .collect();
385            set.rewrite(inst, Plan { opcode, operands: byte, imm: None, ..Plan::of(func, inst) });
386        }
387    }
388    set.commit(func, counts, names, machine).is_ok()
389}
390
391/// The name this target knows an instruction by, for an instruction that is one of this target's.
392///
393/// The opcode in machine IR carries the target's prefix, because a function in the middle of being
394/// compiled holds instructions of one machine and the prefix is what says which. Anything without
395/// it is not something this description covers, and the rest of the pass treats that as knowing
396/// nothing rather than as knowing it is safe.
397fn opcode<'a>(
398    func: &mir::Func,
399    insts: &FlagInsts,
400    names: &'a Interner,
401    inst: mir::Inst,
402) -> Option<&'a str> {
403    names.resolve(func[inst].opcode.name()).strip_prefix(insts.prefix)
404}
405
406#[cfg(test)]
407mod tests {
408    use rucc_target::x86_64::{FLAGS, GPR, MACHINE};
409
410    use super::*;
411
412    /// A function with one block, and the names it was built with.
413    fn empty() -> (Interner, mir::Func, mir::Block) {
414        let mut names = Interner::new();
415        let mut func = mir::Func::new(names.intern("f"));
416        let block = func.create_block();
417        (names, func, block)
418    }
419
420    /// The opcode of that name on this target.
421    fn op(names: &mut Interner, name: &str) -> mir::Opcode {
422        mir::Opcode::new(names.intern(&format!("{}{name}", FLAGS.prefix)))
423    }
424
425    /// The pass, over the machine this crate has a backend for.
426    fn takes(func: &mut mir::Func, names: &mut Interner) -> usize {
427        redundant(func, &FLAGS, &MACHINE, names)
428    }
429
430    /// What every instruction in a block came to, as opcodes with the target's prefix taken off.
431    fn shape(func: &mir::Func, names: &Interner, block: mir::Block) -> Vec<String> {
432        func.insts(block)
433            .map(|inst| {
434                names
435                    .resolve(func[inst].opcode.name())
436                    .strip_prefix(FLAGS.prefix)
437                    .unwrap_or("")
438                    .to_owned()
439            })
440            .collect()
441    }
442
443    /// The shape the issue is named after: the same comparison made twice with nothing between the
444    /// two but the byte the first one kept. The second asks what the first asked, so what is left
445    /// of it is the byte alone.
446    #[test]
447    fn the_same_comparison_twice_leaves_one_comparison_and_two_bytes() {
448        let (mut names, mut func, block) = empty();
449        let value = func.new_vreg(GPR);
450        let first = func.new_vreg(GPR);
451        let second = func.new_vreg(GPR);
452        let ne = op(&mut names, "cmp_set_ne_ri_32");
453        let e = op(&mut names, "cmp_set_e_ri_32");
454        func.build(block, ne).def(first, GPR).uses(value, GPR).imm(0).finish();
455        func.build(block, e).def(second, GPR).uses(value, GPR).imm(0).finish();
456
457        assert_eq!(takes(&mut func, &mut names), 1);
458        assert_eq!(shape(&func, &names, block), ["cmp_set_ne_ri_32", "set_e"]);
459    }
460
461    /// The same two comparisons with something writing the compared register in between. The bits
462    /// are the bits the first one left and they are about a value that has moved on.
463    #[test]
464    fn a_comparison_of_a_register_something_wrote_in_between_stays() {
465        let (mut names, mut func, block) = empty();
466        let value = func.new_vreg(GPR);
467        let other = func.new_vreg(GPR);
468        let first = func.new_vreg(GPR);
469        let second = func.new_vreg(GPR);
470        let ne = op(&mut names, "cmp_set_ne_ri_32");
471        let e = op(&mut names, "cmp_set_e_ri_32");
472        let copy = op(&mut names, "mov_rr_64");
473        func.build(block, ne).def(first, GPR).uses(value, GPR).imm(0).finish();
474        func.build(block, copy).def(value, GPR).uses(other, GPR).finish();
475        func.build(block, e).def(second, GPR).uses(value, GPR).imm(0).finish();
476
477        assert_eq!(takes(&mut func, &mut names), 0);
478        assert_eq!(shape(&func, &names, block).len(), 3);
479    }
480
481    /// The common shape, which is `if (a & MASK)`. The `and` clears the carry and the overflow and
482    /// sets the zero and the sign from what it wrote, which is every bit the comparison would have
483    /// set and the same values, so every condition may read it.
484    #[test]
485    fn a_comparison_against_zero_after_a_bitwise_operation_goes() {
486        for condition in ["e", "l", "b"] {
487            let (mut names, mut func, block) = empty();
488            let value = func.new_vreg(GPR);
489            let byte = func.new_vreg(GPR);
490            let and = op(&mut names, "and_ri_32");
491            let cmp = op(&mut names, &format!("cmp_set_{condition}_ri_32"));
492            func.build(block, and).def(value, GPR).uses(value, GPR).imm(255).finish();
493            func.build(block, cmp).def(byte, GPR).uses(value, GPR).imm(0).finish();
494
495            assert_eq!(takes(&mut func, &mut names), 1, "set{condition}");
496            assert_eq!(
497                shape(&func, &names, block),
498                ["and_ri_32".to_owned(), format!("set_{condition}")]
499            );
500        }
501    }
502
503    /// The same after a subtraction, which is the one that is only half true. The zero bit is what
504    /// a comparison of the answer against zero would have set it to, and the overflow is not, so
505    /// the conditions built out of the sign and the overflow together have to stay.
506    #[test]
507    fn a_comparison_against_zero_after_a_subtraction_goes_only_for_the_zero_conditions() {
508        for (condition, left) in [("e", 1), ("ne", 1), ("l", 0), ("ge", 0), ("a", 0)] {
509            let (mut names, mut func, block) = empty();
510            let value = func.new_vreg(GPR);
511            let other = func.new_vreg(GPR);
512            let byte = func.new_vreg(GPR);
513            let sub = op(&mut names, "sub_rr_32");
514            let cmp = op(&mut names, &format!("cmp_set_{condition}_ri_32"));
515            func.build(block, sub).def(value, GPR).uses(value, GPR).uses(other, GPR).finish();
516            func.build(block, cmp).def(byte, GPR).uses(value, GPR).imm(0).finish();
517
518            assert_eq!(takes(&mut func, &mut names), left, "set{condition}");
519        }
520    }
521
522    /// A comparison the layout already folded a branch into, which keeps no byte at all. There is
523    /// nothing left of one of those, and the jump behind it reads what the `and` left.
524    #[test]
525    fn a_comparison_that_keeps_nothing_is_taken_out_and_the_jump_reads_what_is_there() {
526        let (mut names, mut func, block) = empty();
527        let value = func.new_vreg(GPR);
528        let and = op(&mut names, "and_ri_32");
529        let cmp = op(&mut names, "cmp_ri_32");
530        let jump = op(&mut names, "jcc_l");
531        func.build(block, and).def(value, GPR).uses(value, GPR).imm(255).finish();
532        func.build(block, cmp).uses(value, GPR).imm(0).finish();
533        func.build(block, jump).finish();
534
535        assert_eq!(takes(&mut func, &mut names), 1);
536        assert_eq!(shape(&func, &names, block), ["and_ri_32", "jcc_l"]);
537    }
538
539    /// The same three instructions with a subtraction in front. The condition is behind the
540    /// comparison rather than on it, so finding it means looking at what reads what the comparison
541    /// would have left, and a signed `<` is not something a subtraction answers.
542    #[test]
543    fn a_comparison_that_keeps_nothing_is_refused_on_the_condition_behind_it() {
544        let (mut names, mut func, block) = empty();
545        let value = func.new_vreg(GPR);
546        let other = func.new_vreg(GPR);
547        let sub = op(&mut names, "sub_rr_32");
548        let cmp = op(&mut names, "cmp_ri_32");
549        let jump = op(&mut names, "jcc_l");
550        func.build(block, sub).def(value, GPR).uses(value, GPR).uses(other, GPR).finish();
551        func.build(block, cmp).uses(value, GPR).imm(0).finish();
552        func.build(block, jump).finish();
553
554        assert_eq!(takes(&mut func, &mut names), 0);
555        assert_eq!(shape(&func, &names, block).len(), 3);
556    }
557
558    /// Arithmetic that wrote half of what the comparison is asking about. The upper half is zero
559    /// because this machine writes it that way, so the two agree about whether the value is zero
560    /// and disagree about its sign, and the description has no way to say half of one condition.
561    #[test]
562    fn a_comparison_wider_than_the_arithmetic_in_front_of_it_stays() {
563        let (mut names, mut func, block) = empty();
564        let value = func.new_vreg(GPR);
565        let byte = func.new_vreg(GPR);
566        let and = op(&mut names, "and_ri_32");
567        let cmp = op(&mut names, "cmp_set_e_ri_64");
568        func.build(block, and).def(value, GPR).uses(value, GPR).imm(255).finish();
569        func.build(block, cmp).def(byte, GPR).uses(value, GPR).imm(0).finish();
570
571        assert_eq!(takes(&mut func, &mut names), 0);
572        assert_eq!(shape(&func, &names, block).len(), 2);
573    }
574
575    /// Something between the two that writes the condition state. A multiply is not in the
576    /// description's list because this machine leaves the zero bit undefined after one, so what it
577    /// left is not something to read and not something to reason from either.
578    #[test]
579    fn anything_that_writes_the_condition_state_in_between_makes_the_comparison_stay() {
580        let (mut names, mut func, block) = empty();
581        let value = func.new_vreg(GPR);
582        let other = func.new_vreg(GPR);
583        let byte = func.new_vreg(GPR);
584        let and = op(&mut names, "and_ri_32");
585        let mul = op(&mut names, "imul_rr_32");
586        let cmp = op(&mut names, "cmp_set_e_ri_32");
587        func.build(block, and).def(value, GPR).uses(value, GPR).imm(255).finish();
588        func.build(block, mul).def(other, GPR).uses(other, GPR).uses(other, GPR).finish();
589        func.build(block, cmp).def(byte, GPR).uses(value, GPR).imm(0).finish();
590
591        assert_eq!(takes(&mut func, &mut names), 0);
592        assert_eq!(shape(&func, &names, block).len(), 3);
593    }
594
595    /// A comparison that keeps nothing and whose condition state nothing is found to read. It is
596    /// dead rather than redundant, and this pass is not the one that answers that.
597    #[test]
598    fn a_comparison_nothing_is_found_to_read_stays() {
599        let (mut names, mut func, block) = empty();
600        let value = func.new_vreg(GPR);
601        let and = op(&mut names, "and_ri_32");
602        let cmp = op(&mut names, "cmp_ri_32");
603        func.build(block, and).def(value, GPR).uses(value, GPR).imm(255).finish();
604        func.build(block, cmp).uses(value, GPR).imm(0).finish();
605
606        assert_eq!(takes(&mut func, &mut names), 0);
607        assert_eq!(shape(&func, &names, block).len(), 2);
608    }
609
610    /// The condition state does not cross a block boundary, and neither does this.
611    #[test]
612    fn a_comparison_in_another_block_is_not_one_the_arithmetic_answers() {
613        let (mut names, mut func, block) = empty();
614        let next = func.create_block();
615        let value = func.new_vreg(GPR);
616        let byte = func.new_vreg(GPR);
617        let and = op(&mut names, "and_ri_32");
618        let cmp = op(&mut names, "cmp_set_e_ri_32");
619        func.build(block, and).def(value, GPR).uses(value, GPR).imm(255).finish();
620        *func.succs_mut(block) = vec![mir::BlockCall::to(next)];
621        func.build(next, cmp).def(byte, GPR).uses(value, GPR).imm(0).finish();
622
623        assert_eq!(takes(&mut func, &mut names), 0);
624        assert_eq!(shape(&func, &names, next).len(), 1);
625    }
626}