Skip to main content

rucc_opt/
simplify.rs

1//! Peephole rewrites: a small pattern of instructions becomes a smaller one.
2//!
3//! The third pass, and the one that will eventually not exist. Section 9.3 of
4//! `spec/09-optimizer.md` says the value level optimizer is an acyclic e-graph, and that an
5//! e-graph replaces what would otherwise be a folding pass, a peephole pass, a GVN pass, a
6//! reassociation pass and an instcombine pass, all with a pass ordering problem between them.
7//! This is the peephole pass, written now because the e-graph is a milestone away and because
8//! there is a rewrite that unblocks twelve lowering rules today.
9//!
10//! Every rewrite here has to survive being moved into the rule set later, so each one is stated
11//! as a pattern and a replacement in its own function and nothing shares state with anything.
12//!
13//! # The rewrites
14//!
15//! Two kinds. The rules of `rules/`, one file per tier, which are matched against every
16//! instruction and are where anything new goes, and three rewrites written out by hand below them.
17//!
18//! ## The rules
19//!
20//! Four tiers of `spec/optimizer/13-rewrite-rules.md` section 13.4 so far.
21//!
22//! Tier one is the identities. Adding nothing, multiplying by one, and'ing a value with itself.
23//! None of them needs anything known about the operands and each leaves a term strictly smaller
24//! than the one it replaced.
25//!
26//! Tier two is the strength reductions, which swap an operation for a cheaper one rather than
27//! taking one away: multiplying by two is an addition, and multiplying or dividing by minus one is
28//! a subtraction from nothing. Tier one is tried first because losing an operation beats swapping
29//! one.
30//!
31//! Tier four is the width rules, the algebra of truncation and extension. Truncating an extension
32//! back to the width it came from is the value that was there before either of them, and an
33//! extension of an extension is one extension. This is the tier
34//! the specification says pays on real C, and the reason is C rather than anything about this
35//! compiler: the integer promotions widen nearly every operand of nearly every expression, and
36//! most of those widenings compute something the instruction after them throws away. The widest
37//! of those promotions starts at one bit, because a comparison answers in one and everything done
38//! with the answer is done at the width of an `int` or wider, so the tier is written over that
39//! source as well as over the four a machine computes in.
40//!
41//! Tier three is the canonicalisations, which put the constant of a commutative operation on the
42//! right. They make nothing smaller and nothing faster. What they do is halve how many ways a term
43//! can be written, so that every rule above them needs one variant where it needs two today, and
44//! so that hash consing can see two spellings of one expression as one. They are tried last rather
45//! than third, because rearranging a term is only worth doing when no rule that improves it fires.
46//!
47//! Every rule in all four has been proved against `crates/rucc-ir/rules/ir.model` by
48//! `rucc-verify` before it may be used.
49//!
50//! Which plans a tier is matched under belongs to the tier. Tiers one and two are matched with
51//! either operand offered as a number, since a rule about a constant should fire whichever side it
52//! was written on. Tier three is matched with the left operand offered as a number and the right
53//! one refused if it is one, which is what makes a rule that moves the constant across fire once
54//! rather than forever. Tier four is matched with the operand expanded into the instruction that
55//! computed it, which is what a rule about two instructions at once needs and what none of the
56//! others wants.
57//!
58//! What a rule leaves behind is one of four things. `(value.iN x)` means the result is a value
59//! the function already has, so every use of the result is pointed at that value and the
60//! instruction is left for [`crate::dce`]. `(iconst.iN k)` means the result is a constant, and the
61//! instruction becomes that constant where it stands, which keeps the result value and is why
62//! nothing else has to be rewritten for that half. An instruction means this one becomes that one
63//! where it stands, which keeps the result value for the same reason, and an operand of it the
64//! rule wrote as a number gets an `iconst` in front of the instruction to hold it. A conversion is
65//! that same rewrite in place with one operand instead of two, and it is its own case because a
66//! conversion is the one instruction whose operand is not the width of its result.
67//!
68//! ## The three written by hand
69//!
70//! All three are about comparisons, and all three are here rather than in `rules/` for the same
71//! reason: what each one is, is one statement quantified over the predicates, and the rule language
72//! has no way to say that, so writing any of them as rules would mean writing out every predicate,
73//! every operand order and every width by hand and keeping the enumeration in step with the two
74//! predicate sets forever.
75//!
76//! ### A negation of a comparison
77//!
78//! An exclusive or of a comparison with an `i1` of all ones is that comparison with the opposite
79//! predicate. That is issue 379, and it is worth more than the instruction it saves.
80//!
81//! C spells eight of the sixteen floating point predicates. The six relational and equality
82//! operators give the six ordered ones, `!=` gives `une`, and `__builtin_isunordered` gives `uno`.
83//! The other eight are what the negation of one of those means, and the front end writes a
84//! negation as an exclusive or rather than as a flipped predicate, so `!(x < y)` lowers to an
85//! `fcmp olt` and an `xor` where the machine has an `fcmp uge`. Twelve rules in the x86-64 rule
86//! set are written on those predicates and none of them has ever fired, over the whole torture
87//! suite at every optimization level, because no IR that reaches selection contains one.
88//!
89//! The integer case comes with it. `!(a < b)` on integers is the same shape, the same rewrite and
90//! the same saving, and leaving it out because the coverage report did not complain about it would
91//! be picking the rewrite by what measures it rather than by what it does.
92//!
93//! ### Two comparisons over one pair of operands
94//!
95//! An `and` or an `or` of two comparisons about the same two values is one comparison, or it is a
96//! constant. `(x == y) && (x != y)` is false whatever `x` and `y` are, `(x >= y) || (x < y)` is
97//! true, and `(x < y) || (x == y)` is `x <= y`, which is one instruction where there were three.
98//!
99//! The way to see all of that at once is to stop reading a predicate as a question and read it as
100//! the set of answers it accepts. Two values are below, equal to or above one another, and two
101//! floating point values can also be neither, so there are four cases, exactly one of them holds,
102//! and a predicate is the subset it says yes to. `&&` is then the intersection of two subsets and
103//! `||` is the union, an empty result is false, a full one is true, and anything else is whichever
104//! predicate spells that subset. That is the whole rewrite, and the reason it is a paragraph
105//! rather than a table is that the sixteen floating point predicates are the sixteen subsets of
106//! the four cases, so the map back from a subset is total and has nothing to special case.
107//!
108//! Integers have three cases rather than four, and a complication the floating point side does not
109//! have: `<` is two different questions depending on whether the operands are read signed or
110//! unsigned, and a subset built out of one of each would be a subset about no reading in
111//! particular. So each integer predicate carries which reading it wants, two that disagree refuse
112//! to combine, and `==` and `!=` want neither and go with whatever the other one wanted.
113//!
114//! Nesting falls out of rewriting in place. A three way condition arrives as an `or` of an `or` and
115//! a comparison, the walk reaches the inner one first and leaves a single comparison where it was,
116//! and by the time the outer one is looked at it has a pair of comparisons under it rather than an
117//! `or` and a comparison. That is what `gcc.c-torture/execute/ieee/compare-fp-3.c` needs and it
118//! costs nothing to get.
119//!
120//! ### A comparison one operand's sign bit settles
121//!
122//! `fabs (x) < 0.0` is false whatever `x` holds. That is `gcc.c-torture/execute/20020720-1.c`, and
123//! it asserts it the way the two above do, by calling a function it never defines.
124//!
125//! The same buckets answer it. A magnitude is a positive zero, a positive number, a positive
126//! infinity or a NaN, so a pair made of one and a constant that is not positive is never in the
127//! bucket where the magnitude is below, and against a negative constant it is never in the one
128//! where the two are equal either. Narrow the predicate's set by the buckets the pair can be in and
129//! read the answer back: nothing left is false, and anything left is a shorter question than the
130//! one that was asked. `fabs (x) <= 0.0` comes out as `fabs (x) == 0.0` that way, which is not a
131//! constant and is still worth having.
132//!
133//! What it does not come out as is true. The narrowing only ever takes buckets away and always
134//! takes at least one, so `fabs (x) >= 0.0` is left exactly as it was written, which is the right
135//! answer rather than a missed one: a NaN has its sign bit cleared like anything else and is not
136//! above, below or equal to anything at all.
137//!
138//! `fabs` is not a call by the time this runs. The front end knows the plain library name as well
139//! as the prefixed one and lowers both to the bits, because the magnitude of a value is that value
140//! with its sign bit cleared and there is nothing to call. So what the pattern looks for is a
141//! bitcast of an `and` against a mask whose top bit is clear, which is what that lowering leaves.
142//!
143//! # Why it needs dead code elimination after it
144//!
145//! The rewrite turns the `xor` into the comparison and leaves the original comparison where it
146//! was, used by nothing when the negation was its only reader. Rewriting in place keeps the
147//! result value, so every use of it is already correct and there is nothing to rewrite, and what
148//! is left over is exactly what [`crate::dce`] takes out. That is why the pipeline runs the two in
149//! this order, and it is why the pass before the dead code eliminator was written first.
150//!
151//! An identity that produces a value leaves the same kind of litter for the same reason. The
152//! instruction it fired on reads what it always read and nothing reads it, so it is dead, and
153//! taking it out here would mean deciding whether its operands are still read by anything, which
154//! is the question the dead code eliminator answers for the whole function at once.
155//!
156//! The composite rewrite leaves two of them rather than one, and in the case that comes out
157//! constant it leaves both comparisons and computes nothing at all. The sign rewrite leaves the
158//! four instructions the magnitude was built out of. Same litter, same reason, same pass takes it
159//! out.
160
161use std::cmp::Ordering;
162use std::collections::HashMap;
163use std::sync::OnceLock;
164
165use rucc_base::float::Float;
166use rucc_ir::term::{PLAIN, Plan, Shown, Term, Terms};
167use rucc_ir::{
168    Block, Def, Extra, Flags, FloatPred, Func, Imm, Inst, InstData, IntPred, Opcode, Type, Value,
169};
170
171use crate::rules::{Match, Piece, Subject, Table, canonical, compare, identities, strength, width};
172use crate::uses::{count, substitute};
173use crate::{Analyses, Analysis, Fuel, Pass, Preserved, Stats};
174
175/// Recorded once for each negation folded into the comparison under it.
176const FLIPPED: &str = "comparison negated by an exclusive or rewritten as the opposite comparison";
177
178/// Recorded for a negation that would have folded if there had been fuel for it.
179const NO_FUEL: &str = "negated comparison left alone, the pass ran out of fuel";
180
181/// Recorded once for each pair of comparisons over one operand pair folded into one answer.
182const COMPOSITE: &str = "two comparisons over the same operands combined into one";
183
184/// Recorded for a pair that would have folded if there had been fuel for it.
185const NO_FUEL_COMPOSITE: &str = "pair of comparisons left alone, the pass ran out of fuel";
186
187/// Recorded once for each comparison the sign of one operand settles on its own.
188const MAGNITUDE: &str = "comparison against a value whose sign bit is clear settled by the sign";
189
190/// Recorded for one of those that would have folded if there had been fuel for it.
191const NO_FUEL_MAGNITUDE: &str =
192    "comparison against a magnitude left alone, the pass ran out of fuel";
193
194/// Recorded for a rule that would have fired if there had been fuel for it.
195const NO_FUEL_RULE: &str = "rewrite left alone, the pass ran out of fuel";
196
197/// How each operand of an instruction is shown to the matcher, and in what order the ways are
198/// tried.
199///
200/// The two with a constant come first, because a rule about a number is the more specific one and
201/// an operand that is not a constant declines it at the first node of the trie. Nothing here
202/// expands an operand into the instruction that computed it, since no tier one identity is about
203/// two instructions at once.
204const PLANS: [Plan; 3] =
205    [[Shown::Reg, Shown::Const, Shown::Reg], [Shown::Const, Shown::Reg, Shown::Reg], PLAIN];
206
207/// How the operands are shown to a canonicalisation, which is the one plan tier three is matched
208/// under.
209///
210/// A canonicalisation moves the constant to the right, so the left operand has to be the number
211/// and the right one has to be something that is not, or the rule swaps a pair of constants back
212/// and forth until the pass runs out of fuel. [`Shown::Var`] is what says the right one is not a
213/// number. The plans above cannot be reused here for exactly that reason: the second of them
214/// shows a constant left operand as a number and a constant right operand as a register, which is
215/// the cycling match.
216const CANONICAL: [Plan; 1] = [[Shown::Const, Shown::Var, Shown::Reg]];
217
218/// How the operands are shown to a width rule, which is the one plan tier four is matched under.
219///
220/// Every rule in that tier is about two instructions at once, a conversion and the conversion or
221/// value under it, so the operand it is about has to be shown as the instruction that computed it
222/// rather than as a register holding the answer. That is [`Shown::Expand`], and it is the first
223/// plan here to use it.
224///
225/// One operand, because every instruction the tier matches has one. The other two entries are
226/// never read and say [`Shown::Reg`] because that is what an operand nobody asks about is.
227const EXPAND: [Plan; 1] = [[Shown::Expand, Shown::Reg, Shown::Reg]];
228
229/// How the operands are shown to a comparison rule, which is the two plans tier five is matched
230/// under.
231///
232/// Every rule in that tier compares something against a constant, and writes the constant on the
233/// right, so the right operand is shown as a number in both. What differs is the left one. Most of
234/// the tier is about the value itself and shows it as a register, which is the first of [`PLANS`]
235/// spelled again rather than borrowed, because the other two of those would be tried for nothing:
236/// a comparison with the constant on the left matches no rule here, and neither does one with no
237/// constant at all.
238///
239/// The rest of the tier is about a widened boolean compared against zero, which is two
240/// instructions at once, so the left operand is shown as the instruction that computed it the way
241/// tier four shows its one operand. That is the second plan, and it is a plan of its own rather
242/// than a rule in tier four because the instruction that matched is a comparison: the predicate is
243/// not part of the opcode, which is what makes a tier a separate file here.
244///
245/// The constant on the left is not the missing half of the tier. A comparison is not commutative,
246/// so `0 < x` is not `x < 0` with the operands swapped, it is `x > 0`, and turning the first into
247/// the second is a canonicalisation that belongs in tier three rather than four more rules here.
248const COMPARE: [Plan; 2] =
249    [[Shown::Reg, Shown::Const, Shown::Reg], [Shown::Expand, Shown::Const, Shown::Reg]];
250
251/// The rule tables, one per tier, in the order they are tried, each with the plans it is matched
252/// under.
253///
254/// Tier one first, because an identity takes an operation away and a strength reduction swaps one
255/// for another, so a term both have something to say about is better off losing the operation.
256/// Tier four after those two and tier three last, because a canonicalisation only makes a term
257/// easier for another rule to be about and there is no reason to reach for it while a rule that
258/// improves the code still fires. Nothing turns on the order of those last two anyway: tier three
259/// is about a commutative operation with a constant in it and tier four is about a conversion, so
260/// no instruction is one both have something to say about.
261///
262/// The plans belong to the table rather than to the loop because a tier is written against them.
263/// Tier three is only correct under the one plan that refuses a constant on the right, and a
264/// table matched under a plan it was not written for is a table whose rules mean something else.
265/// Tier four is the other way round: its rules mean nothing at all under a plan that does not
266/// expand, since the second level of every one of its patterns is an instruction.
267///
268/// Tier five sits where it does because nothing turns on it either. It is the only table about a
269/// comparison and no other table mentions one, so there is no instruction two of them have
270/// something to say about and no order in which one of them gets there first.
271const TABLES: [(&Table, &[Plan]); 5] = [
272    (&identities::TABLE, &PLANS),
273    (&strength::TABLE, &PLANS),
274    (&width::TABLE, &EXPAND),
275    (&compare::TABLE, &COMPARE),
276    (&canonical::TABLE, &CANONICAL),
277];
278
279/// The pass. It holds nothing, because a peephole needs to know nothing beyond the pattern.
280#[derive(Debug, Clone, Copy, PartialEq, Eq)]
281pub struct Simplify;
282
283impl Pass for Simplify {
284    fn name(&self) -> &'static str {
285        "simplify"
286    }
287
288    fn describe(&self) -> &'static str {
289        "the identities, the strength reductions, the canonicalisations, and the three comparison \
290         rewrites written by hand"
291    }
292
293    fn preserves(&self) -> Preserved {
294        // Everything about the shape of the function. No block is added, none is removed and no
295        // edge moves, so the graph and everything built out of it stand.
296        //
297        // The liveness does not, and that is the whole of the difference. An identity that
298        // produces a value points every reader of one value at another, which is one more place
299        // the second is live and one fewer the first is, and the same is true of the negation
300        // below, which reads the comparison's operands where it used to read its result.
301        //
302        // A rule that writes an instruction with a constant in it puts one in the block, and that
303        // is still the same answer. It adds a value nothing else mentions, in the block it is
304        // read in, and it ends every path it starts on, so nothing about the shape of the
305        // function moves and the only analysis with something new to say about it is the one
306        // already given up.
307        Preserved::ALL.without(Analysis::Liveness)
308    }
309
310    fn run(&self, func: &mut Func, _an: &mut Analyses, fuel: &mut Fuel) -> Stats {
311        let mut stats = Stats::new();
312        // What a rule that produced a value decided, applied to the whole function at the end.
313        // Rewriting each one where it is found would be a walk over every instruction for every
314        // rewrite, and there is nothing to be gained by it: what a pattern asks about is the
315        // instruction and its operands, and neither changes under a redirection.
316        let mut forward: HashMap<Value, Value> = HashMap::new();
317        // Who reads what, so that an instruction nothing reads is left alone. A rule that fires
318        // on one changes no program, because what it does is point the readers somewhere else and
319        // there are none, and it would still spend fuel and still report having optimized
320        // something. That matters here more than it would in a pass that runs once: this pass is
321        // named twice in every pipeline above `-O0`, an identity it takes stays in the function
322        // until dead code elimination removes it, and without this the second run would rewrite
323        // everything the first run did all over again and say so.
324        //
325        // Stale by design. It is what the function looked like when this run started, and a
326        // rewrite below only ever removes readers, so a value this says nothing reads is a value
327        // nothing reads.
328        let uses = count(func);
329        let dead = |func: &Func, inst: Inst| match func[inst].first_result {
330            Some(result) => uses[result.index()] == 0,
331            None => false,
332        };
333        for block in func.blocks().collect::<Vec<Block>>() {
334            for inst in func.insts(block).collect::<Vec<Inst>>() {
335                if dead(func, inst) {
336                    continue;
337                }
338                if let Some(flip) = negated_comparison(func, inst) {
339                    if !fuel.take() {
340                        // Out of fuel, which stops the transforming rather than the looking, the
341                        // same way the other two passes treat it. The walk is the same walk at
342                        // every fuel setting, which is what makes bisecting over it monotonic.
343                        stats.missed(NO_FUEL);
344                        continue;
345                    }
346                    let args = func.push_values(&[flip.lhs, flip.rhs]);
347                    let data = &mut func[inst];
348                    data.opcode = flip.opcode;
349                    data.flags = flip.flags;
350                    data.args = args;
351                    data.extra = flip.extra;
352                    stats.optimized(FLIPPED);
353                    continue;
354                }
355                if let Some(composite) = composite_comparison(func, inst) {
356                    if !fuel.take() {
357                        stats.missed(NO_FUEL_COMPOSITE);
358                        continue;
359                    }
360                    fold_composite(func, inst, composite);
361                    stats.optimized(COMPOSITE);
362                    continue;
363                }
364                if let Some(settled) = magnitude_comparison(func, inst) {
365                    if !fuel.take() {
366                        stats.missed(NO_FUEL_MAGNITUDE);
367                        continue;
368                    }
369                    fold_composite(func, inst, settled);
370                    stats.optimized(MAGNITUDE);
371                    continue;
372                }
373                let Some((rewrite, pattern)) = identity(func, inst) else { continue };
374                if !fuel.take() {
375                    stats.missed(NO_FUEL_RULE);
376                    continue;
377                }
378                match rewrite {
379                    Rewrite::Value(value) => {
380                        let result = func[inst].first_result.expect("the rule matched a result");
381                        forward.insert(result, value);
382                    }
383                    Rewrite::Constant(number) => become_constant(func, inst, number),
384                    Rewrite::Built { opcode, pred, lhs, rhs } => {
385                        become_instruction(func, inst, opcode, pred, lhs, rhs);
386                    }
387                    Rewrite::Converted { opcode, from } => {
388                        become_conversion(func, inst, opcode, from);
389                    }
390                }
391                stats.optimized(pattern);
392            }
393        }
394        if !forward.is_empty() {
395            substitute(func, &forward);
396        }
397        stats
398    }
399}
400
401/// What a rule says an instruction's result is instead.
402#[derive(Clone, Copy, Debug, PartialEq, Eq)]
403enum Rewrite {
404    /// A value the function already has, which every reader of the result is pointed at.
405    Value(Value),
406    /// A number, which the instruction becomes where it stands.
407    Constant(i128),
408    /// Another instruction, which this one becomes where it stands.
409    Built {
410        /// What it is.
411        opcode: Opcode,
412        /// Which comparison it is, when it is one.
413        ///
414        /// The predicate is not part of the opcode. Every one of the ten integer comparisons is
415        /// `ICmp` and the predicate is beside it, so an opcode on its own does not say what a
416        /// rule asked for, and a rule that wrote `icmp_sge` and got the predicate of the
417        /// instruction it replaced would compute the opposite rather than something else.
418        pred: Option<IntPred>,
419        /// Its left operand.
420        lhs: Operand,
421        /// Its right operand.
422        rhs: Operand,
423    },
424    /// A conversion, which this one becomes where it stands.
425    ///
426    /// Separate from [`Rewrite::Built`] rather than one variant with a list of operands, because a
427    /// conversion is the one instruction a rule writes whose operand is not the width of its
428    /// result. That is what makes it the one whose operand cannot be a number the rule wrote:
429    /// there would be no width to give the constant, and every rule that writes one of these
430    /// writes a value the pattern bound.
431    Converted {
432        /// Which of the three it is.
433        opcode: Opcode,
434        /// What it converts, which is always a value the pattern bound.
435        from: Value,
436    },
437}
438
439/// One operand of an instruction a rule writes.
440#[derive(Clone, Copy, Debug, PartialEq, Eq)]
441enum Operand {
442    /// A value the pattern bound.
443    Value(Value),
444    /// A number the rule wrote, which needs an `iconst` in front of the instruction before it is
445    /// an operand at all, because an operand in this IR is a value and a number is not one until
446    /// something defines it.
447    Constant {
448        /// The number.
449        number: i128,
450        /// How wide it is, which is the width the `iconst.iN` head named.
451        ///
452        /// Taken from the rule rather than from the instruction's result, because the two are
453        /// the same width for everything above and are not for a comparison: the result of one
454        /// is a single bit and its operands are as wide as what was compared. A constant built
455        /// at the result's width would be a one bit zero standing where a thirty two bit one
456        /// was asked for.
457        bits: u32,
458    },
459}
460
461/// The rule that fires on this instruction, and the pattern it came from.
462///
463/// The plans are tried in order and the first that matches wins. A plan is how the operands are
464/// shown rather than what they are, so trying three of them is three walks over a trie, each of
465/// which fails in its first node or two when the instruction is not one any rule is about.
466fn identity(func: &Func, inst: Inst) -> Option<(Rewrite, &'static str)> {
467    let result = func[inst].first_result?;
468    for (table, plan) in
469        TABLES.into_iter().flat_map(|(table, plans)| plans.iter().map(move |&plan| (table, plan)))
470    {
471        let terms = Terms::new(func, inst, plan);
472        let Some(found) = table.find(&terms, Term::Root) else { continue };
473        let rule = table.rule(&found);
474        let rewrite = match rule.replacement {
475            // A value the pattern bound, which is a register because that is the only thing a
476            // `value.iN` binds.
477            [Piece::App { head, arity: 1 }, Piece::Var { index, .. }]
478                if head.starts_with("value.") =>
479            {
480                match found.bindings.get(*index) {
481                    Some(&Term::Reg(value)) => Rewrite::Value(value),
482                    _ => continue,
483                }
484            }
485            // A constant written in the rule. Only at a width the instruction's result has, which
486            // it always does: an `iconst.iN` names an integer width and a rule is proved at the
487            // width it is written at.
488            [Piece::App { head, arity: 1 }, Piece::Int(number)]
489                if head.starts_with("iconst.") && func[result].ty.is_int() =>
490            {
491                Rewrite::Constant(*number)
492            }
493            // An instruction the rule writes, which this one becomes. That is the third shape and
494            // the last one: a replacement deeper than one instruction would need somewhere to put
495            // the ones under it, and a rule that wanted it can be written as two rules that each
496            // leave one.
497            pieces => match built(pieces, &found, &matched(&terms, &found)) {
498                Some(rewrite) => rewrite,
499                // Any other shape, which no rule in the file has. A test below says so, because a
500                // rule that fell through here would be a rule that never fires and nothing would
501                // say it had stopped.
502                None => continue,
503            },
504        };
505        return Some((rewrite, rule.pattern));
506    }
507    None
508}
509
510/// The instruction a rule writes, out of the pieces its replacement flattened into.
511///
512/// Two operands under a head that names an opcode, each of them either a value the pattern bound
513/// or a number the rule wrote. Anything else is nothing this pass can build, and the answer to
514/// one is that the rule does not fire, which the test over the whole table turns into a failure
515/// rather than a silence.
516fn built(
517    pieces: &'static [Piece],
518    found: &Match<Term>,
519    matched: &[Option<i128>],
520) -> Option<Rewrite> {
521    if let Some(rewrite) = converted(pieces, found) {
522        return Some(rewrite);
523    }
524    let [Piece::App { head, arity: 2 }, rest @ ..] = pieces else { return None };
525    let opcode = opcode_of(head)?;
526    // The predicate comes from the same head the opcode did, so a rule whose replacement this
527    // pass can build is a rule written in the vocabulary it matched with, predicate and all.
528    let pred = rucc_ir::term::int_pred(head);
529    if (opcode == Opcode::ICmp) != pred.is_some() {
530        // A comparison whose head names no predicate, or a predicate on something that is not a
531        // comparison. Neither is a head the vocabulary produces, so neither is a rule anybody
532        // wrote, and building the instruction anyway would mean guessing at one of the two.
533        return None;
534    }
535    let (lhs, rest) = operand(rest, found, matched)?;
536    let (rhs, rest) = operand(rest, found, matched)?;
537    rest.is_empty().then_some(Rewrite::Built { opcode, pred, lhs, rhs })
538}
539
540/// The constants the pattern matched, one entry per binding, in the order it bound them.
541///
542/// The same list a guard is handed and worked out the same way, which is what lets a computed
543/// piece be written in the names the pattern bound. It is collected here rather than kept from
544/// the match because most rules have no computation and no guard and would pay for it every time.
545fn matched(terms: &Terms<'_>, found: &Match<Term>) -> Vec<Option<i128>> {
546    found.bindings.iter().map(|&node| terms.int(node)).collect()
547}
548
549/// The conversion a rule writes, if it wrote one.
550///
551/// Three heads rather than any head of one operand, because the width rules are the only tier that
552/// writes an instruction with one, and being specific is what keeps this from claiming a
553/// replacement it cannot build. A `value.iN` or an `iconst.iN` is also a head of one operand and
554/// neither is an instruction, and [`identity`] has already dealt with both by the time anything
555/// gets here, so a test would not catch the day one slipped past.
556///
557/// The operand is a value the pattern bound, and nothing else. A number would need a width to be
558/// written at and the result's width is the wrong one for a conversion, which is the whole reason
559/// this is separate from [`built`].
560fn converted(pieces: &'static [Piece], found: &Match<Term>) -> Option<Rewrite> {
561    let [Piece::App { head, arity: 1 }, rest @ ..] = pieces else { return None };
562    let opcode = match opcode_of(head)? {
563        opcode @ (Opcode::SExt | Opcode::ZExt | Opcode::Trunc) => opcode,
564        _ => return None,
565    };
566    let [Piece::App { head: inner, arity: 1 }, Piece::Var { index, .. }] = rest else {
567        return None;
568    };
569    if !inner.starts_with("value.") {
570        return None;
571    }
572    match found.bindings.get(*index) {
573        Some(&Term::Reg(from)) => Some(Rewrite::Converted { opcode, from }),
574        _ => None,
575    }
576}
577
578/// One operand of that instruction, and the pieces after it.
579fn operand(
580    pieces: &'static [Piece],
581    found: &Match<Term>,
582    matched: &[Option<i128>],
583) -> Option<(Operand, &'static [Piece])> {
584    match pieces {
585        [Piece::App { head, arity: 1 }, Piece::Var { index, .. }, rest @ ..]
586            if head.starts_with("value.") =>
587        {
588            match found.bindings.get(*index) {
589                Some(&Term::Reg(value)) => Some((Operand::Value(value), rest)),
590                _ => None,
591            }
592        }
593        [Piece::App { head, arity: 1 }, Piece::Int(number), rest @ ..]
594            if head.starts_with("iconst.") =>
595        {
596            Some((Operand::Constant { number: *number, bits: bits_of(head)? }, rest))
597        }
598        // A number the rule works out of the ones it matched, which is how a rule about every
599        // power of two is written once rather than once per power. The computation gives nothing
600        // back when a binding it reads is not a constant, and the answer to that is the same as a
601        // guard that does not hold: the rule does not fire.
602        [Piece::App { head, arity: 1 }, Piece::Computed { work, .. }, rest @ ..]
603            if head.starts_with("iconst.") =>
604        {
605            let number = work(matched)?;
606            Some((Operand::Constant { number, bits: bits_of(head)? }, rest))
607        }
608        // A number the pattern bound rather than one the rule wrote. This is what a
609        // canonicalisation needs: it moves the operand it matched to the other side, and what it
610        // matched was whatever number happened to be there.
611        [Piece::App { head, arity: 1 }, Piece::Var { index, .. }, rest @ ..]
612            if head.starts_with("iconst.") =>
613        {
614            match found.bindings.get(*index) {
615                Some(&Term::Num(number)) => {
616                    Some((Operand::Constant { number, bits: bits_of(head)? }, rest))
617                }
618                _ => None,
619            }
620        }
621        _ => None,
622    }
623}
624
625/// The width a head names, out of the `iN` after its last dot.
626///
627/// Every head that takes a width ends in one, and reading it off the name is what keeps the width
628/// a rule was written at attached to the rule rather than inferred from whatever the instruction
629/// being replaced happened to be. A head with no width, or one whose width is not a number, is a
630/// head this cannot build an operand for, and the answer to that is that the rule does not fire.
631fn bits_of(head: &str) -> Option<u32> {
632    head.rsplit_once('.')?.1.strip_prefix('i')?.parse().ok()
633}
634
635/// The opcode a replacement head names, or nothing if the rules have no instruction by that name.
636///
637/// Built the once out of [`rucc_ir::term::heads`], which is where the name of the instruction a
638/// pattern matched comes from as well, so a rule whose replacement this pass can build is a rule
639/// written in the vocabulary it matched with. A table here would be a second vocabulary and the
640/// two would drift.
641///
642/// A name two opcodes answer to belongs to the first of them, which is the general one:
643/// `ptr_add` is an add at the address width and is named as one, and a rule that writes `add` is
644/// asking for the add.
645fn opcode_of(head: &str) -> Option<Opcode> {
646    static NAMES: OnceLock<HashMap<&'static str, Opcode>> = OnceLock::new();
647    let names = NAMES.get_or_init(|| {
648        let mut names = HashMap::new();
649        for (opcode, name) in rucc_ir::term::heads() {
650            names.entry(name).or_insert(opcode);
651        }
652        names
653    });
654    names.get(head).copied()
655}
656
657/// Turns an instruction into the one a rule says computes the same thing.
658///
659/// In place, like the constant below and for the same reason: the result value survives, so every
660/// reader of it is already right and there is nothing to redirect.
661fn become_instruction(
662    func: &mut Func,
663    inst: Inst,
664    opcode: Opcode,
665    pred: Option<IntPred>,
666    lhs: Operand,
667    rhs: Operand,
668) {
669    let result = func[inst].first_result.expect("the rule matched a result");
670    let ty = func[result].ty;
671    let lhs = defined(func, inst, ty, lhs);
672    let rhs = defined(func, inst, ty, rhs);
673    let args = func.push_values(&[lhs, rhs]);
674    let data = &mut func[inst];
675    data.opcode = opcode;
676    data.args = args;
677    // The predicate the rule named, and nothing else a rule writes carries an extra. What was
678    // there belonged to the instruction that is gone, which is the case that matters: a rule
679    // rewriting a comparison into an addition that left the predicate behind would leave an
680    // addition claiming to be `slt`, and one rewriting a comparison into another comparison that
681    // kept the old predicate would compute the opposite of what it said.
682    data.extra = match pred {
683        Some(pred) => Extra::IntPred(pred),
684        None => Extra::None,
685    };
686    // The flags go with the instruction that had them, the same as for a constant. An `nsw` on a
687    // multiplication is a promise about that multiplication, and the addition that replaces it is
688    // a different instruction. The promise may well still hold, and carrying one across a rewrite
689    // because it probably still holds is how a wrong one gets made. Dropping it costs a later
690    // pass an assumption and costs no program its meaning.
691    data.flags = Flags::NONE;
692}
693
694/// Turns an instruction into the conversion a rule says computes the same thing.
695///
696/// In place, for the same reason as the two above: the result value survives, so every reader of
697/// it is already right.
698///
699/// The result keeps the type it had, which is the type the rule wrote. A replacement head names
700/// both widths it converts between, `rucc-verify` refuses a replacement narrower than the pattern
701/// and the rules are written with the two the same, so the width the head names on the way out is
702/// the width the instruction already produces.
703fn become_conversion(func: &mut Func, inst: Inst, opcode: Opcode, from: Value) {
704    let args = func.push_values(&[from]);
705    let data = &mut func[inst];
706    data.opcode = opcode;
707    data.args = args;
708    // Nothing a rule writes carries an extra, and the flags belonged to the instruction that is
709    // gone. Both for the reasons `become_instruction` gives.
710    data.extra = Extra::None;
711    data.flags = Flags::NONE;
712}
713
714/// An operand as a value, defining it in front of the instruction if the rule wrote a number.
715///
716/// `ty` is the type of the instruction's result, which is the width the constant is built at for
717/// everything whose operands are as wide as what it produces. A comparison is the exception and
718/// the reason the rule's own width is carried this far: its result is one bit and its operands are
719/// as wide as what was compared, so the width comes from the `iconst.iN` the rule wrote and the
720/// result's type is used only for its shape.
721fn defined(func: &mut Func, before: Inst, ty: Type, operand: Operand) -> Value {
722    match operand {
723        Operand::Value(value) => value,
724        Operand::Constant { number, bits } => {
725            let ty = if ty.lane() == Type::int(bits) { ty } else { Type::int(bits) };
726            let at = func.add_imm(Imm::int(number, ty.lane()));
727            let data = InstData { extra: Extra::Imm(at), ..InstData::new(Opcode::IConst) };
728            let span = func.span(before);
729            let iconst = func.create_inst(data, &[ty], span);
730            func.insert_before(iconst, before);
731            func[iconst].first_result.expect("one result was asked for")
732        }
733    }
734}
735
736/// Turns an instruction into the constant a rule says its result is.
737///
738/// In place, so the result value survives and every reader of it is already right. That is what
739/// makes this the half of the pass with nothing to redirect.
740fn become_constant(func: &mut Func, inst: Inst, number: i128) {
741    let result = func[inst].first_result.expect("the rule matched a result");
742    let ty = func[result].ty;
743    let imm = func.add_imm(Imm::int(number, ty.lane()));
744    let args = func.push_values(&[]);
745    let data = &mut func[inst];
746    data.opcode = Opcode::IConst;
747    data.args = args;
748    data.extra = Extra::Imm(imm);
749    // The flags go with the instruction that had them. An `nsw` on an add is a promise about an
750    // addition, and a constant makes no promise because it performs nothing.
751    data.flags = Flags::NONE;
752}
753
754/// What an instruction should become, when it is a comparison written as a negation.
755pub(crate) struct Flip {
756    /// `ICmp` or `FCmp`, whichever the comparison underneath was.
757    opcode: Opcode,
758    /// The flags of the comparison, which is where a fast math promise lives.
759    flags: Flags,
760    /// The opposite predicate.
761    extra: Extra,
762    /// The comparison's left operand.
763    lhs: Value,
764    /// Its right operand.
765    rhs: Value,
766}
767
768/// Whether this instruction is `xor (cmp p a b), true`, and what it becomes if it is.
769///
770/// The exclusive or is commutative, so the constant is looked for on both sides. Nothing else
771/// about the shape is negotiable: the result has to be an `i1`, because an exclusive or with one
772/// is a negation only at that width, and the constant has to be all ones, because the front end
773/// writes it as `iconst.i1 -1` and a reader who assumed the literal 1 would match nothing.
774fn negated_comparison(func: &Func, inst: Inst) -> Option<Flip> {
775    let data = &func[inst];
776    if data.opcode != Opcode::Xor {
777        return None;
778    }
779    let args = &func[data.args];
780    let (&first, &second) = (args.first()?, args.get(1)?);
781    if func[first].ty != Type::int(1) {
782        return None;
783    }
784    let cmp = match (all_ones(func, first), all_ones(func, second)) {
785        (true, false) => second,
786        (false, true) => first,
787        // Both, which folding would have turned into a constant, or neither, which is an
788        // exclusive or of two comparisons and is not this pattern.
789        _ => return None,
790    };
791    let Def::Result { inst: cmp, .. } = func[cmp].def else { return None };
792    let data = &func[cmp];
793    let extra = match (data.opcode, data.extra) {
794        (Opcode::ICmp, Extra::IntPred(pred)) => Extra::IntPred(pred.inverse()),
795        (Opcode::FCmp, Extra::FloatPred(pred)) => Extra::FloatPred(pred.inverse()),
796        _ => return None,
797    };
798    let args = &func[data.args];
799    Some(Flip {
800        opcode: data.opcode,
801        flags: data.flags,
802        extra,
803        lhs: *args.first()?,
804        rhs: *args.get(1)?,
805    })
806}
807
808/// Where a pair of operands can stand in relation to each other, as one bit each.
809///
810/// Every comparison either of the IR's two families can make is a set of these and nothing else,
811/// which is the whole idea. Two values are below, equal to or above one another, and two floating
812/// point values can also be neither, so a predicate is a question about which of four buckets the
813/// pair falls in and the answer is the subset it accepts. `olt` accepts one bucket, `ole` accepts
814/// two, `une` accepts three and `uno` accepts the fourth on its own.
815///
816/// Once a predicate is a set, `&&` of two of them over the same pair of operands is the
817/// intersection and `||` is the union, because the buckets do not overlap and exactly one of them
818/// is the case. An empty answer is a combination nothing satisfies and a full one is a combination
819/// everything does, which is what the two torture cases this is for are asking about.
820mod bucket {
821    /// The left operand is below the right one.
822    pub(super) const LT: u8 = 1;
823    /// The two are equal.
824    pub(super) const EQ: u8 = 2;
825    /// The left operand is above the right one.
826    pub(super) const GT: u8 = 4;
827    /// Neither, which only a floating point pair can be and only when one of them is a NaN.
828    pub(super) const UN: u8 = 8;
829    /// Every bucket an integer pair can be in, which is the answer no integer comparison can fail.
830    pub(super) const ALL_INT: u8 = LT | EQ | GT;
831    /// Every bucket a floating point pair can be in.
832    pub(super) const ALL_FLOAT: u8 = LT | EQ | GT | UN;
833}
834
835/// Which ordering an integer predicate reads its operands under.
836///
837/// Equality is under neither, and that is not a technicality: `x == y` and `x < y` have an answer
838/// in common whichever way the second one reads its operands, so an equality can be combined with
839/// a signed comparison and with an unsigned one. Two orderings that disagree cannot be combined at
840/// all, because `slt` and `ult` are not the same question and a set that mixed them would be a set
841/// about no ordering in particular.
842#[derive(Clone, Copy, Debug, PartialEq, Eq)]
843enum Reading {
844    /// The predicate compares signed.
845    Signed,
846    /// The predicate compares unsigned.
847    Unsigned,
848    /// The predicate is an equality and says nothing about an ordering.
849    Neither,
850}
851
852impl Reading {
853    /// The reading two predicates have in common, if they have one.
854    const fn shared(self, other: Self) -> Option<Self> {
855        match (self, other) {
856            (Self::Neither, same) | (same, Self::Neither) => Some(same),
857            (Self::Signed, Self::Signed) => Some(Self::Signed),
858            (Self::Unsigned, Self::Unsigned) => Some(Self::Unsigned),
859            (Self::Signed, Self::Unsigned) | (Self::Unsigned, Self::Signed) => None,
860        }
861    }
862}
863
864/// The buckets an integer predicate accepts, and the ordering it read them under.
865const fn int_buckets(pred: IntPred) -> (u8, Reading) {
866    use bucket::{EQ, GT, LT};
867    match pred {
868        IntPred::Eq => (EQ, Reading::Neither),
869        IntPred::Ne => (LT | GT, Reading::Neither),
870        IntPred::Slt => (LT, Reading::Signed),
871        IntPred::Sle => (LT | EQ, Reading::Signed),
872        IntPred::Sgt => (GT, Reading::Signed),
873        IntPred::Sge => (GT | EQ, Reading::Signed),
874        IntPred::Ult => (LT, Reading::Unsigned),
875        IntPred::Ule => (LT | EQ, Reading::Unsigned),
876        IntPred::Ugt => (GT, Reading::Unsigned),
877        IntPred::Uge => (GT | EQ, Reading::Unsigned),
878    }
879}
880
881/// The integer predicate that accepts exactly this set of buckets under this ordering.
882///
883/// Nothing for the empty set or the full one, which are the two answers that are not a comparison
884/// at all and are dealt with before this is asked. Nothing either for a set that wants an ordering
885/// from a pair that had none, which is a set neither `eq` nor `ne` can spell: two equalities
886/// combine into an equality or into one of those two extremes and never into an ordering, so the
887/// case does not arise and answering it would mean choosing an ordering out of nowhere.
888const fn int_pred(buckets: u8, reading: Reading) -> Option<IntPred> {
889    use bucket::{EQ, GT, LT};
890    match (buckets, reading) {
891        (EQ, _) => Some(IntPred::Eq),
892        (b, _) if b == LT | GT => Some(IntPred::Ne),
893        (LT, Reading::Signed) => Some(IntPred::Slt),
894        (GT, Reading::Signed) => Some(IntPred::Sgt),
895        (b, Reading::Signed) if b == LT | EQ => Some(IntPred::Sle),
896        (b, Reading::Signed) if b == GT | EQ => Some(IntPred::Sge),
897        (LT, Reading::Unsigned) => Some(IntPred::Ult),
898        (GT, Reading::Unsigned) => Some(IntPred::Ugt),
899        (b, Reading::Unsigned) if b == LT | EQ => Some(IntPred::Ule),
900        (b, Reading::Unsigned) if b == GT | EQ => Some(IntPred::Uge),
901        _ => None,
902    }
903}
904
905/// The buckets a floating point predicate accepts.
906///
907/// The sixteen predicates are the sixteen subsets, which is why the IR has `false` and `true` among
908/// them and why this direction and the one below are both total.
909const fn float_buckets(pred: FloatPred) -> u8 {
910    use bucket::{ALL_FLOAT, EQ, GT, LT, UN};
911    match pred {
912        FloatPred::False => 0,
913        FloatPred::Oeq => EQ,
914        FloatPred::Ogt => GT,
915        FloatPred::Oge => GT | EQ,
916        FloatPred::Olt => LT,
917        FloatPred::Ole => LT | EQ,
918        FloatPred::One => LT | GT,
919        FloatPred::Ord => LT | EQ | GT,
920        FloatPred::Uno => UN,
921        FloatPred::Ueq => EQ | UN,
922        FloatPred::Ugt => GT | UN,
923        FloatPred::Uge => GT | EQ | UN,
924        FloatPred::Ult => LT | UN,
925        FloatPred::Ule => LT | EQ | UN,
926        FloatPred::Une => LT | GT | UN,
927        FloatPred::True => ALL_FLOAT,
928    }
929}
930
931/// The floating point predicate that accepts exactly this set of buckets.
932fn float_pred(buckets: u8) -> Option<FloatPred> {
933    FloatPred::all().find(|pred| float_buckets(*pred) == buckets)
934}
935
936/// One of the two comparisons under an `and` or an `or`, read as a set of buckets.
937struct Side {
938    /// `ICmp` or `FCmp`, which both sides have to be the same of.
939    opcode: Opcode,
940    /// The flags, which both sides have to carry the same of. A fast math promise is a promise
941    /// about one comparison, and a set built out of two comparisons that were not promised the
942    /// same thing is a set under no promise in particular.
943    flags: Flags,
944    /// The buckets the predicate accepts, already turned round if the operands were.
945    buckets: u8,
946    /// Which ordering it read, for an integer comparison. Always [`Reading::Neither`] for a
947    /// floating point one, where there is only the one ordering and nothing to agree about.
948    reading: Reading,
949    /// The left operand.
950    lhs: Value,
951    /// The right operand.
952    rhs: Value,
953}
954
955/// The comparison a value holds the result of, if that is what it is.
956fn side(func: &Func, value: Value) -> Option<Side> {
957    let Def::Result { inst, .. } = func[value].def else { return None };
958    let data = &func[inst];
959    let (buckets, reading) = match (data.opcode, data.extra) {
960        (Opcode::ICmp, Extra::IntPred(pred)) => int_buckets(pred),
961        (Opcode::FCmp, Extra::FloatPred(pred)) => (float_buckets(pred), Reading::Neither),
962        _ => return None,
963    };
964    let args = &func[data.args];
965    Some(Side {
966        opcode: data.opcode,
967        flags: data.flags,
968        buckets,
969        reading,
970        lhs: *args.first()?,
971        rhs: *args.get(1)?,
972    })
973}
974
975/// The same set of buckets read with the operands the other way round.
976///
977/// Which of the two is below the other changes places and nothing else moves: equal is equal from
978/// both ends, and a NaN makes a pair unordered from both ends.
979const fn turned(buckets: u8) -> u8 {
980    use bucket::{GT, LT};
981    let mut out = buckets & !(LT | GT);
982    if buckets & LT != 0 {
983        out |= GT;
984    }
985    if buckets & GT != 0 {
986        out |= LT;
987    }
988    out
989}
990
991/// The second side read as though its operands were written in the first side's order.
992///
993/// A comparison is not commutative, so `y < x` is not `x < y`, it is `x > y`. Turning the second
994/// side round is what lets `(x<y) && (y<x)` be a pair about one ordered pair of operands rather
995/// than two unrelated comparisons, and it is the only case in either torture program that needs it.
996fn aligned(first: &Side, second: Side) -> Option<Side> {
997    if first.lhs == second.lhs && first.rhs == second.rhs {
998        return Some(second);
999    }
1000    if first.lhs != second.rhs || first.rhs != second.lhs {
1001        return None;
1002    }
1003    let buckets = turned(second.buckets);
1004    Some(Side { buckets, lhs: first.lhs, rhs: first.rhs, ..second })
1005}
1006
1007/// What an `and` or an `or` of two comparisons over one pair of operands comes to.
1008pub(crate) enum Composite {
1009    /// Nothing the operands could hold makes it come out the other way.
1010    Always(bool),
1011    /// One comparison over the same pair says the same thing as the two together.
1012    Pred(Flip),
1013}
1014
1015/// Whether this instruction is `and` or `or` of two comparisons over the same pair of operands,
1016/// and what it becomes if it is.
1017///
1018/// This is `(x==y) && (x!=y)`, which is false, and `(x>=y) || (x<y)`, which is true, and the four
1019/// other shapes `gcc.c-torture/execute/compare-3.c` is built out of. Neither program is contrived:
1020/// a composite condition written out of macros, or one arm of it produced by inlining, arrives
1021/// looking exactly like this, and the front end has already flattened the `&&` into an `and` by the
1022/// time anything here runs, so what would otherwise be a question about two blocks is a question
1023/// about one instruction and its two operands.
1024///
1025/// Both sides have to be the same family of comparison, carry the same flags and be about the same
1026/// two values. Past that the arithmetic is [`bucket`]: intersect for an `and`, union for an `or`,
1027/// and read the answer back as a predicate. An answer of no buckets is false, an answer of every
1028/// bucket is true, and anything between the two is one comparison where there were two, which is
1029/// worth taking on its own and is also what lets the three way condition in
1030/// `gcc.c-torture/execute/ieee/compare-fp-3.c` fold: the inner `or` becomes a single `uge` and the
1031/// outer one then has a pair to work on rather than an `or` and a comparison.
1032fn composite_comparison(func: &Func, inst: Inst) -> Option<Composite> {
1033    let data = &func[inst];
1034    if func[data.first_result?].ty != Type::int(1) {
1035        return None;
1036    }
1037    let args = &func[data.args];
1038    composite(func, data.opcode, *args.first()?, *args.get(1)?)
1039}
1040
1041/// The same question asked about an `and` or an `or` that is not there yet.
1042///
1043/// [`crate::short_circuit`] asks it before it writes one, because whether the collapse it is
1044/// looking at is worth making is the question of whether what it writes survives this pass, and a
1045/// collapse that leaves one comparison where there were two and a branch is worth making at every
1046/// optimization level rather than only where speculating work is.
1047pub(crate) fn composite(func: &Func, opcode: Opcode, lhs: Value, rhs: Value) -> Option<Composite> {
1048    let intersect = match opcode {
1049        Opcode::And => true,
1050        Opcode::Or => false,
1051        _ => return None,
1052    };
1053    let first = side(func, lhs)?;
1054    let second = aligned(&first, side(func, rhs)?)?;
1055    if first.opcode != second.opcode || first.flags != second.flags {
1056        return None;
1057    }
1058    let reading = first.reading.shared(second.reading)?;
1059    let buckets = match intersect {
1060        true => first.buckets & second.buckets,
1061        false => first.buckets | second.buckets,
1062    };
1063    let whole = match first.opcode {
1064        Opcode::ICmp => bucket::ALL_INT,
1065        _ => bucket::ALL_FLOAT,
1066    };
1067    if buckets == 0 {
1068        return Some(Composite::Always(false));
1069    }
1070    if buckets == whole {
1071        return Some(Composite::Always(true));
1072    }
1073    let extra = match first.opcode {
1074        Opcode::ICmp => Extra::IntPred(int_pred(buckets, reading)?),
1075        _ => Extra::FloatPred(float_pred(buckets)?),
1076    };
1077    Some(Composite::Pred(Flip {
1078        opcode: first.opcode,
1079        flags: first.flags,
1080        extra,
1081        lhs: first.lhs,
1082        rhs: first.rhs,
1083    }))
1084}
1085
1086/// Whether the sign bit of this value is known to be clear, which is what `fabs` leaves behind.
1087///
1088/// `fabs` is not a call by the time anything here runs. The front end knows the plain library name
1089/// as well as the prefixed one and lowers both to the bits, because there is nothing to call: the
1090/// magnitude of a value is that value with its sign bit cleared, payload and all for a NaN and sign
1091/// and all for a negative zero, and a rewriting into `x < 0 ? -x : x` would be wrong for both. So
1092/// what reaches this pass is a bitcast of an `and` of a bitcast, and the `and` is against a mask
1093/// whose top bit is clear.
1094///
1095/// Any such mask and not the one `fabs` writes. A constant with its top bit clear leaves the top
1096/// bit of the answer clear whatever the rest of it does, the top bit of an integer as wide as a
1097/// floating point value is that value's sign bit in every format the compiler has, and asking for
1098/// the exact mask would mean this stopped working the day a rule ahead of it narrowed one.
1099fn magnitude(func: &Func, value: Value) -> bool {
1100    let Def::Result { inst, .. } = func[value].def else { return false };
1101    let data = &func[inst];
1102    if data.opcode != Opcode::Bitcast {
1103        return false;
1104    }
1105    let Some(&bits) = func[data.args].first() else { return false };
1106    let Def::Result { inst: masked, .. } = func[bits].def else { return false };
1107    let data = &func[masked];
1108    if data.opcode != Opcode::And {
1109        return false;
1110    }
1111    func[data.args].iter().any(|&arg| clears_the_sign(func, arg))
1112}
1113
1114/// Whether this value is an integer constant whose top bit is clear.
1115fn clears_the_sign(func: &Func, value: Value) -> bool {
1116    let ty = func[value].ty;
1117    let Def::Result { inst, .. } = func[value].def else { return false };
1118    let data = &func[inst];
1119    let Extra::Imm(at) = data.extra else { return false };
1120    data.opcode == Opcode::IConst && ty.is_int() && func[at].signed(ty) >= 0
1121}
1122
1123/// The buckets a pair made of a magnitude on the left and this constant on the right can fall in.
1124///
1125/// A magnitude is a positive zero, a positive number, a positive infinity or a NaN, so against a
1126/// constant that is not positive it is never the one below. Against a negative constant it is never
1127/// the one equal either, since every value a magnitude can be is above every negative number.
1128///
1129/// Nothing for a constant that is positive, where the answer is every bucket and there would be
1130/// nothing to narrow, and nothing for a NaN, where [`Float::compare`] has no ordering to report and
1131/// the pair is unordered whatever the other side holds. That second case folds already, as a
1132/// comparison of two constants when both sides are or as nothing at all when only one is, and
1133/// answering it here would be a second opinion about it.
1134fn against(func: &Func, value: Value) -> Option<u8> {
1135    use bucket::{EQ, GT, UN};
1136    let Def::Result { inst, .. } = func[value].def else { return None };
1137    let data = &func[inst];
1138    if data.opcode != Opcode::FConst {
1139        return None;
1140    }
1141    let Extra::Imm(at) = data.extra else { return None };
1142    let format = func[value].ty.format()?.encoding();
1143    let number = Float::from_bits(format, func[at].bits());
1144    match number.compare(Float::zero(format, false))? {
1145        Ordering::Less => Some(GT | UN),
1146        Ordering::Equal => Some(GT | EQ | UN),
1147        Ordering::Greater => None,
1148    }
1149}
1150
1151/// Whether this comparison is one the sign bit of an operand settles, and what it becomes if it is.
1152///
1153/// `fabs (x) < 0.0` is false whatever `x` holds, including a NaN, and that is what
1154/// `gcc.c-torture/execute/20020720-1.c` asserts by calling a function it never defines. The
1155/// arithmetic is [`bucket`] again: a magnitude compared against a constant that is not positive
1156/// cannot be the one below, so the buckets the predicate accepts are narrowed by the ones the pair
1157/// can be in, and what is left is false, or is a shorter question than the one that was asked.
1158///
1159/// There is no answer of every bucket here, which is why this has no case for one. The narrowing
1160/// only ever takes buckets away and it always takes at least the one below, so a set that survives
1161/// it is never the full one and a comparison this fires on is never true.
1162///
1163/// A predicate the narrowing leaves alone is declined rather than rewritten, or the pass would
1164/// report having optimized `fabs (x) >= 0.0` into itself once per run until the fuel ran out.
1165fn magnitude_comparison(func: &Func, inst: Inst) -> Option<Composite> {
1166    let data = &func[inst];
1167    let Extra::FloatPred(pred) = data.extra else { return None };
1168    if data.opcode != Opcode::FCmp {
1169        return None;
1170    }
1171    let args = &func[data.args];
1172    let lhs = *args.first()?;
1173    let rhs = *args.get(1)?;
1174    let possible = if magnitude(func, lhs) {
1175        against(func, rhs)?
1176    } else if magnitude(func, rhs) {
1177        turned(against(func, lhs)?)
1178    } else {
1179        return None;
1180    };
1181    let asked = float_buckets(pred);
1182    let buckets = asked & possible;
1183    if buckets == asked {
1184        return None;
1185    }
1186    if buckets == 0 {
1187        return Some(Composite::Always(false));
1188    }
1189    Some(Composite::Pred(Flip {
1190        opcode: Opcode::FCmp,
1191        flags: data.flags,
1192        extra: Extra::FloatPred(float_pred(buckets)?),
1193        lhs,
1194        rhs,
1195    }))
1196}
1197
1198/// Writes what a set of buckets came to over the instruction it was worked out from.
1199///
1200/// In place, which keeps the result value, so every reader of that instruction is already reading
1201/// the one answer and whatever it used to read is left where it was for [`crate::dce`].
1202pub(crate) fn fold_composite(func: &mut Func, inst: Inst, composite: Composite) {
1203    match composite {
1204        Composite::Always(answer) => become_constant(func, inst, answer.into()),
1205        Composite::Pred(flip) => {
1206            let args = func.push_values(&[flip.lhs, flip.rhs]);
1207            let data = &mut func[inst];
1208            data.opcode = flip.opcode;
1209            data.flags = flip.flags;
1210            data.args = args;
1211            data.extra = flip.extra;
1212        }
1213    }
1214}
1215
1216/// Whether this value is a constant with every bit of its type set.
1217fn all_ones(func: &Func, value: Value) -> bool {
1218    let ty = func[value].ty;
1219    let Def::Result { inst, .. } = func[value].def else { return false };
1220    let data = &func[inst];
1221    let Extra::Imm(at) = data.extra else { return false };
1222    if data.opcode != Opcode::IConst {
1223        return false;
1224    }
1225    // Read as signed, because an all ones value of any width is minus one that way and reading
1226    // it unsigned would need the width to build the mask from.
1227    func[at].signed(ty) == -1
1228}
1229
1230#[cfg(test)]
1231mod tests {
1232    use rucc_base::Interner;
1233    use rucc_ir::{
1234        Block, Builder, Extra, Flags, Float, FloatPred, Func, IntPred, Module, Opcode, Signature,
1235        Type, Value,
1236    };
1237    use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
1238
1239    use super::{
1240        CANONICAL, COMPARE, EXPAND, PLANS, Shown, TABLES, canonical, compare, identities, strength,
1241        width,
1242    };
1243    use crate::rules::Piece;
1244    use crate::stats::Kind;
1245    use crate::{Fuel, Pass, simplify::Simplify};
1246
1247    /// A function with one block, ready to have instructions appended to it.
1248    fn blank() -> (Interner, Func, Block) {
1249        let mut names = Interner::new();
1250        let name = names.intern("f");
1251        let mut func = Func::new(name, Signature::new().with_returns(&[Type::int(1)]));
1252        let block = func.create_block();
1253        (names, func, block)
1254    }
1255
1256    /// The same, at the width the test is about and taking a parameter of it, since every identity
1257    /// below needs an operand that is not itself a constant.
1258    fn one_block(ty: Type) -> (Interner, Func, Block) {
1259        let mut names = Interner::new();
1260        let name = names.intern("f");
1261        let signature = Signature::new().with_params(&[ty]).with_returns(&[ty]);
1262        let mut func = Func::new(name, signature);
1263        let block = func.create_block();
1264        (names, func, block)
1265    }
1266
1267    /// Runs the pass with as much fuel as it wants, and says whether it rewrote anything.
1268    fn simplify(func: &mut Func) -> bool {
1269        Simplify
1270            .run(func, &mut crate::machine::fixtures::analyses(), &mut Fuel::unlimited())
1271            .changed()
1272    }
1273
1274    /// The opcode and the predicate the value now comes from.
1275    fn came_from(func: &Func, value: Value) -> (Opcode, Extra) {
1276        let rucc_ir::Def::Result { inst, .. } = func[value].def else { panic!("not a result") };
1277        (func[inst].opcode, func[inst].extra)
1278    }
1279
1280    /// What the block gives back, which is where every identity test reads its answer. A rule
1281    /// that produces a value is only worth anything if the readers move, so the readers are what
1282    /// the test looks at rather than the instruction that fired.
1283    fn returned(func: &Func, block: Block) -> Value {
1284        let inst = func.terminator(block).expect("the block has a terminator");
1285        func[func[inst].args][0]
1286    }
1287
1288    /// The operands of the instruction a value comes from.
1289    fn operands(func: &Func, value: Value) -> Vec<Value> {
1290        let rucc_ir::Def::Result { inst, .. } = func[value].def else { panic!("not a result") };
1291        func[func[inst].args].to_vec()
1292    }
1293
1294    /// The number a value is, which panics unless it is a constant.
1295    fn number(func: &Func, value: Value) -> i128 {
1296        let rucc_ir::Def::Result { inst, .. } = func[value].def else { panic!("not a result") };
1297        let data = &func[inst];
1298        assert_eq!(data.opcode, Opcode::IConst, "not a constant");
1299        let Extra::Imm(at) = data.extra else { panic!("a constant with no number") };
1300        func[at].signed(func[value].ty)
1301    }
1302
1303    /// Every rule in every table leaves one of the four shapes the pass knows how to apply.
1304    ///
1305    /// A rule that left anything else would be matched, found to be none of them, and skipped, and
1306    /// nothing at run time would say so: the rewrite would simply stop happening. So it is said
1307    /// here instead, once, over every table.
1308    #[test]
1309    fn every_rule_leaves_a_shape_the_pass_knows_what_to_do_with() {
1310        for (table, _) in TABLES {
1311            for rule in table.rules {
1312                let known = matches!(
1313                    rule.replacement,
1314                    [Piece::App { head, arity: 1 }, Piece::Var { .. }]
1315                        if head.starts_with("value.")
1316                ) || matches!(
1317                    rule.replacement,
1318                    [Piece::App { head, arity: 1 }, Piece::Int(_)]
1319                        if head.starts_with("iconst.")
1320                ) || matches!(
1321                    rule.replacement,
1322                    [Piece::App { arity: 2, .. }, ..] if instruction(rule.replacement)
1323                ) || conversion(rule.replacement);
1324                assert!(known, "{} leaves a shape the pass would skip", rule.pattern);
1325            }
1326        }
1327    }
1328
1329    /// The pieces of a replacement that is a conversion, read the way [`super::converted`] reads
1330    /// them, and shape only for the same reason [`instruction`] is: there are no bindings here to
1331    /// resolve the operand against.
1332    fn conversion(pieces: &'static [Piece]) -> bool {
1333        let [Piece::App { head, arity: 1 }, rest @ ..] = pieces else { return false };
1334        let converts =
1335            matches!(super::opcode_of(head), Some(Opcode::SExt | Opcode::ZExt | Opcode::Trunc));
1336        converts
1337            && matches!(
1338                rest,
1339                [Piece::App { head, arity: 1 }, Piece::Var { .. }] if head.starts_with("value.")
1340            )
1341    }
1342
1343    /// Every rule in the width table writes a term ending at the width the one it matched ended
1344    /// at.
1345    ///
1346    /// The pass rewrites in place and leaves the result type where it was, so a rule whose
1347    /// replacement converted to some other width would quietly produce a value of the wrong one.
1348    /// `rucc-verify` refuses a replacement narrower than what it replaces and says nothing about a
1349    /// wider one, so this is the half of that pair the solver does not cover.
1350    #[test]
1351    fn a_width_rule_writes_a_term_that_ends_where_the_one_it_matched_ended() {
1352        for rule in width::TABLE.rules {
1353            let [Piece::App { head, .. }, ..] = rule.replacement else {
1354                panic!("{} writes no head", rule.pattern)
1355            };
1356            let wrote = head.rsplit_once('.').expect("a replacement head names a width").1;
1357            let matched = rule
1358                .pattern
1359                .trim_start_matches('(')
1360                .split([' ', ')'])
1361                .next()
1362                .and_then(|head| head.rsplit_once('.'))
1363                .expect("a pattern head names a width")
1364                .1;
1365            assert_eq!(wrote, matched, "{} ends somewhere else", rule.pattern);
1366        }
1367    }
1368
1369    /// The pieces of a replacement that is an instruction, read the way the pass reads them, so
1370    /// that the check above is the pass's own answer rather than a second opinion about it.
1371    ///
1372    /// The bindings are empty, which is why a `value.iN` operand fails to resolve and this only
1373    /// says the shape is one the pass would take rather than that it would take it here.
1374    fn instruction(pieces: &'static [Piece]) -> bool {
1375        let [Piece::App { head, arity: 2 }, rest @ ..] = pieces else { return false };
1376        if super::opcode_of(head).is_none() {
1377            return false;
1378        }
1379        let operand = |pieces: &'static [Piece]| match pieces {
1380            [Piece::App { head, arity: 1 }, Piece::Var { .. }, rest @ ..]
1381                if head.starts_with("value.") =>
1382            {
1383                Some(rest)
1384            }
1385            [Piece::App { head, arity: 1 }, Piece::Int(_), rest @ ..]
1386                if head.starts_with("iconst.") =>
1387            {
1388                Some(rest)
1389            }
1390            [Piece::App { head, arity: 1 }, Piece::Var { .. }, rest @ ..]
1391                if head.starts_with("iconst.") =>
1392            {
1393                Some(rest)
1394            }
1395            [Piece::App { head, arity: 1 }, Piece::Computed { .. }, rest @ ..]
1396                if head.starts_with("iconst.") =>
1397            {
1398                Some(rest)
1399            }
1400            _ => None,
1401        };
1402        operand(rest).and_then(operand).is_some_and(<[Piece]>::is_empty)
1403    }
1404
1405    /// And each table holds every rule its file writes. The tables are generated, so this is
1406    /// asking whether the generator saw the whole file, which is the one thing about it worth
1407    /// doubting.
1408    #[test]
1409    fn each_table_holds_every_rule_its_file_writes() {
1410        let tier_one = include_str!("../rules/simplify.rules");
1411        let tier_two = include_str!("../rules/strength.rules");
1412        let tier_three = include_str!("../rules/canonical.rules");
1413        let tier_four = include_str!("../rules/width.rules");
1414        let tier_five = include_str!("../rules/compare.rules");
1415        let count = |text: &str| text.matches("(rule (simplify ").count();
1416        assert_eq!(identities::TABLE.rules.len(), count(tier_one));
1417        assert_eq!(strength::TABLE.rules.len(), count(tier_two));
1418        assert_eq!(canonical::TABLE.rules.len(), count(tier_three));
1419        assert_eq!(width::TABLE.rules.len(), count(tier_four));
1420        assert_eq!(compare::TABLE.rules.len(), count(tier_five));
1421        assert!(
1422            identities::TABLE.rules.len() > 100,
1423            "tier one is about a hundred rules and there are fewer"
1424        );
1425        assert!(
1426            strength::TABLE.rules.len() > 20,
1427            "tier two is the multiplications and the divisions and there are fewer"
1428        );
1429        assert_eq!(
1430            canonical::TABLE.rules.len(),
1431            20,
1432            "tier three is five commutative operators at four widths"
1433        );
1434        assert_eq!(
1435            width::TABLE.rules.len(),
1436            66,
1437            "tier four is the truncation and extension algebra over four widths, and the three \
1438             shapes of it that exist over the one bit a comparison answers in"
1439        );
1440        assert_eq!(
1441            compare::TABLE.rules.len(),
1442            72,
1443            "tier five is four predicates against each of four constants at four widths, and a \
1444             widened boolean against zero under two predicates at the same four"
1445        );
1446    }
1447
1448    /// Three ways of showing an operand and no more, since a fourth would be a plan nothing
1449    /// tries and a rule written for it would never fire.
1450    #[test]
1451    fn a_pattern_is_reached_by_one_of_the_plans() {
1452        assert_eq!(PLANS.len(), 3);
1453    }
1454
1455    /// Tier four is matched with its operand expanded, and none of the shared plans expands one.
1456    ///
1457    /// Every pattern in that tier has an instruction at its second level, so under any of the
1458    /// plans above it every rule in it would fail at the first node and the whole tier would be a
1459    /// file nobody matched with. Asserted rather than left to be read, because that failure is
1460    /// silent.
1461    ///
1462    /// Tier five expands as well, under the second of its own two plans, which is the half of it
1463    /// about a widened boolean compared against zero.
1464    #[test]
1465    fn a_width_rule_is_only_matched_with_its_operand_expanded() {
1466        let (_, plans) = TABLES[2];
1467        assert_eq!(plans.len(), 1);
1468        assert_eq!(plans[0], EXPAND[0]);
1469        assert_eq!(plans[0][0], Shown::Expand);
1470        for plan in PLANS {
1471            assert_ne!(plan, plans[0], "no shared plan expands an operand");
1472        }
1473        assert_ne!(CANONICAL[0], plans[0]);
1474        assert_eq!(COMPARE[1][0], Shown::Expand);
1475        assert_eq!(COMPARE[1][1], Shown::Const);
1476    }
1477
1478    /// Tier three is matched under its own plan and no other.
1479    ///
1480    /// This is what makes the rules terminate rather than swap a pair of constants back and forth
1481    /// until the fuel runs out. It is asserted rather than left to be read, because the cost of
1482    /// somebody adding the shared plans to the tier three row is a pass that does not stop.
1483    #[test]
1484    fn a_canonicalisation_is_only_matched_with_the_right_operand_refused() {
1485        let (_, plans) = TABLES[4];
1486        assert_eq!(plans.len(), 1);
1487        assert_eq!(plans[0], CANONICAL[0]);
1488        assert_eq!(plans[0][1], Shown::Var);
1489        for plan in PLANS {
1490            assert_ne!(plan, plans[0], "a shared plan would let a canonicalisation cycle");
1491        }
1492    }
1493
1494    /// Tier five is matched with the constant on the right and no other way.
1495    ///
1496    /// Every rule in it writes the constant there, so under the plan that shows a constant left
1497    /// operand as a number none of them would match and under the plan that refuses a constant on
1498    /// the right none of them would either. Two plans, differing only in how the left operand is
1499    /// shown, which is what the two halves of the tier are about.
1500    #[test]
1501    fn a_comparison_rule_is_only_matched_with_the_constant_on_the_right() {
1502        let (_, plans) = TABLES[3];
1503        assert_eq!(plans.len(), 2);
1504        assert_eq!(plans, COMPARE);
1505        for plan in plans {
1506            assert_eq!(plan[1], Shown::Const);
1507        }
1508        assert_eq!(plans[0][0], Shown::Reg);
1509        assert_eq!(plans[1][0], Shown::Expand);
1510    }
1511
1512    /// The edge of a type, at each width, read each way.
1513    ///
1514    /// The least and greatest unsigned value and the least and greatest signed one, which are the
1515    /// four constants tier five is written against.
1516    fn edges(width: u32) -> [(i128, bool); 4] {
1517        let signed = 1i128 << (width - 1);
1518        [(0, false), (-1, false), (-signed, true), (signed - 1, true)]
1519    }
1520
1521    /// A comparison that its type has already answered becomes the answer.
1522    ///
1523    /// Nothing unsigned is below zero, everything unsigned is at least zero, and the same pair of
1524    /// sentences holds at each of the other three edges. Thirty two rules, run as one test,
1525    /// because what is being checked is the same sentence at four constants and four widths.
1526    #[test]
1527    fn a_comparison_against_the_edge_of_its_type_folds_to_a_bit() {
1528        for width in [8u32, 16, 32, 64] {
1529            let ty = Type::int(width);
1530            for (edge, signed) in edges(width) {
1531                // Below the edge is false at the bottom and above it is false at the top, and the
1532                // other of each pair is the negation, so one table gives all four.
1533                let below = edge == 0 || edge == -(1i128 << (width - 1));
1534                let (false_pred, true_pred) = match (signed, below) {
1535                    (false, true) => (IntPred::Ult, IntPred::Uge),
1536                    (false, false) => (IntPred::Ugt, IntPred::Ule),
1537                    (true, true) => (IntPred::Slt, IntPred::Sge),
1538                    (true, false) => (IntPred::Sgt, IntPred::Sle),
1539                };
1540                // Minus one for the true bit, because the rule writes `(iconst.i1 1)` and one bit
1541                // holding a one read signed is minus one, which is the same bit pattern and the
1542                // reading everything else in the compiler takes of a true condition.
1543                for (pred, answer) in [(false_pred, 0), (true_pred, -1)] {
1544                    let (_, mut func, block) = blank();
1545                    let x = func.append_param(block, ty);
1546                    let mut build = Builder::new(&mut func, block);
1547                    let bound = build.iconst(ty, edge);
1548                    let cmp = build.icmp(pred, x, bound);
1549                    build.ret(&[cmp]);
1550                    assert!(simplify(&mut func), "i{width} {pred:?} {edge} was left alone");
1551                    let got = returned(&func, block);
1552                    assert_eq!(
1553                        came_from(&func, got).0,
1554                        Opcode::IConst,
1555                        "i{width} {pred:?} {edge} did not fold"
1556                    );
1557                    assert_eq!(number(&func, got), answer, "i{width} {pred:?} {edge}");
1558                    assert_eq!(func[got].ty, Type::int(1), "i{width} {pred:?} {edge} is a bit");
1559                }
1560            }
1561        }
1562    }
1563
1564    /// And one that is true or false for exactly one value becomes the test for that value.
1565    ///
1566    /// The predicate has to come from the rule. Every case here matched an ordering and every one
1567    /// of them has to leave `eq` or `ne`, so a rewriter that took the predicate from the
1568    /// instruction it replaced would leave the ordering in place and this would say so.
1569    #[test]
1570    fn a_comparison_true_for_one_value_becomes_a_test_for_that_value() {
1571        for width in [8u32, 16, 32, 64] {
1572            let ty = Type::int(width);
1573            for (edge, signed) in edges(width) {
1574                let below = edge == 0 || edge == -(1i128 << (width - 1));
1575                // At most the bottom is equality and above it is inequality, and at the top the
1576                // two swap over.
1577                let (eq_pred, ne_pred) = match (signed, below) {
1578                    (false, true) => (IntPred::Ule, IntPred::Ugt),
1579                    (false, false) => (IntPred::Uge, IntPred::Ult),
1580                    (true, true) => (IntPred::Sle, IntPred::Sgt),
1581                    (true, false) => (IntPred::Sge, IntPred::Slt),
1582                };
1583                for (pred, left) in [(eq_pred, IntPred::Eq), (ne_pred, IntPred::Ne)] {
1584                    let (_, mut func, block) = blank();
1585                    let x = func.append_param(block, ty);
1586                    let mut build = Builder::new(&mut func, block);
1587                    let bound = build.iconst(ty, edge);
1588                    let cmp = build.icmp(pred, x, bound);
1589                    build.ret(&[cmp]);
1590                    assert!(simplify(&mut func), "i{width} {pred:?} {edge} was left alone");
1591                    let got = returned(&func, block);
1592                    assert_eq!(
1593                        came_from(&func, got),
1594                        (Opcode::ICmp, Extra::IntPred(left)),
1595                        "i{width} {pred:?} {edge} kept the predicate it matched"
1596                    );
1597                    let args = operands(&func, got);
1598                    assert_eq!(args[0], x, "i{width} {pred:?} {edge} lost its value");
1599                    assert_eq!(number(&func, args[1]), edge, "i{width} {pred:?} {edge}");
1600                    // The width the rule was written at, which is the width of what is being
1601                    // compared and not the width of the answer. A constant built at the result's
1602                    // type would be a one bit zero standing where a wider one was asked for.
1603                    assert_eq!(func[args[1]].ty, ty, "i{width} {pred:?} {edge} narrowed its bound");
1604                }
1605            }
1606        }
1607    }
1608
1609    /// A boolean widened and compared against zero is the boolean.
1610    ///
1611    /// The shape `if (flag)` and `(long)(a == b)` and every `__builtin_expect` arrive in, since
1612    /// each of them widens a comparison and then asks whether the wide value is zero. What the
1613    /// test asserts is that the branch ends up on the comparison itself, at one bit, with the
1614    /// widening left for dead code elimination.
1615    #[test]
1616    fn a_widened_boolean_compared_against_zero_is_the_boolean() {
1617        for width in [8u32, 16, 32, 64] {
1618            let ty = Type::int(width);
1619            let (_, mut func, block) = blank();
1620            let x = func.append_param(block, Type::int(32));
1621            let mut build = Builder::new(&mut func, block);
1622            let seven = build.iconst(Type::int(32), 7);
1623            let flag = build.icmp(IntPred::Eq, x, seven);
1624            let wide = build.unary(Opcode::ZExt, flag, ty);
1625            let zero = build.iconst(ty, 0);
1626            let test = build.icmp(IntPred::Ne, wide, zero);
1627            build.ret(&[test]);
1628            assert!(simplify(&mut func), "i{width} was left alone");
1629            let got = returned(&func, block);
1630            assert_eq!(got, flag, "i{width} did not end up on the comparison");
1631            assert_eq!(func[got].ty, Type::int(1), "i{width} is a bit");
1632        }
1633    }
1634
1635    /// And one compared against zero the other way is that boolean negated.
1636    ///
1637    /// The rule writes an exclusive or with a one bit one, because what is under the widening is
1638    /// whatever produced the bit and there is no predicate to flip in the general case. Where it
1639    /// is a comparison, which is this test, the hand written rewrite above the tables turns that
1640    /// exclusive or into the opposite comparison, and the pair composes into one instruction.
1641    ///
1642    /// Two runs, because the walk visits each instruction once and the hand written rewrite is
1643    /// tried before the tables are: the exclusive or did not exist when this instruction was
1644    /// looked at. Every pipeline above `-O0` names the pass twice, which is where the second run
1645    /// comes from in a real compile.
1646    #[test]
1647    fn a_widened_boolean_that_is_zero_is_the_boolean_negated() {
1648        for width in [8u32, 16, 32, 64] {
1649            let ty = Type::int(width);
1650            let (_, mut func, block) = blank();
1651            let x = func.append_param(block, Type::int(32));
1652            let mut build = Builder::new(&mut func, block);
1653            let seven = build.iconst(Type::int(32), 7);
1654            let flag = build.icmp(IntPred::Eq, x, seven);
1655            let wide = build.unary(Opcode::ZExt, flag, ty);
1656            let zero = build.iconst(ty, 0);
1657            let test = build.icmp(IntPred::Eq, wide, zero);
1658            build.ret(&[test]);
1659            assert!(simplify(&mut func), "i{width} was left alone");
1660            let got = returned(&func, block);
1661            assert_eq!(came_from(&func, got).0, Opcode::Xor, "i{width} is not a negation");
1662            assert!(simplify(&mut func), "i{width} kept the exclusive or");
1663            assert_eq!(
1664                came_from(&func, got),
1665                (Opcode::ICmp, Extra::IntPred(IntPred::Ne)),
1666                "i{width} did not come out as the opposite comparison"
1667            );
1668            let args = operands(&func, got);
1669            assert_eq!(args[0], x, "i{width} lost its value");
1670            assert_eq!(number(&func, args[1]), 7, "i{width} lost its bound");
1671        }
1672    }
1673
1674    /// Every commutative operator tier three writes moves its constant to the right.
1675    ///
1676    /// One test over the five rather than five tests, because what is being checked is the same
1677    /// thing five times and the operator is the only part that differs.
1678    #[test]
1679    fn a_constant_on_the_left_of_a_commutative_operation_moves_to_the_right() {
1680        for opcode in [Opcode::Add, Opcode::Mul, Opcode::And, Opcode::Or, Opcode::Xor] {
1681            for width in [8, 16, 32, 64] {
1682                let ty = Type::int(width);
1683                let (_, mut func, block) = one_block(ty);
1684                let x = func.append_param(block, ty);
1685                let mut build = Builder::new(&mut func, block);
1686                // Three, because it is a number no identity in tier one is about and no strength
1687                // reduction in tier two is about, so the only rule that can fire is the one this
1688                // test is here for.
1689                let three = build.iconst(ty, 3);
1690                let value = build.binary(opcode, three, x, Flags::NONE);
1691                build.ret(&[value]);
1692                assert!(simplify(&mut func), "{opcode:?} at i{width} was left alone");
1693                let args = operands(&func, returned(&func, block));
1694                assert_eq!(came_from(&func, returned(&func, block)).0, opcode);
1695                assert_eq!(args[0], x, "{opcode:?} at i{width} kept the value on the right");
1696                assert_eq!(number(&func, args[1]), 3, "{opcode:?} at i{width} lost its constant");
1697            }
1698        }
1699    }
1700
1701    /// And an operation whose operands are both constants is left where it is.
1702    ///
1703    /// This is the termination argument, run rather than read. Without the plan that refuses a
1704    /// constant on the right, the rule above would match this, swap the two, match the swapped
1705    /// form, and go on doing it until the fuel ran out. Folding is what this instruction is for
1706    /// and `crate::fold` is where it happens.
1707    #[test]
1708    fn an_operation_on_two_constants_is_not_swapped_back_and_forth() {
1709        let i32 = Type::int(32);
1710        let (_, mut func, block) = one_block(i32);
1711        let mut build = Builder::new(&mut func, block);
1712        let three = build.iconst(i32, 3);
1713        let five = build.iconst(i32, 5);
1714        let sum = build.binary(Opcode::Add, three, five, Flags::NONE);
1715        build.ret(&[sum]);
1716        assert!(!simplify(&mut func), "the constants were rearranged rather than left to folding");
1717        let args = operands(&func, returned(&func, block));
1718        assert_eq!(number(&func, args[0]), 3);
1719        assert_eq!(number(&func, args[1]), 5);
1720    }
1721
1722    /// A constant already on the right stays there and nothing fires.
1723    ///
1724    /// The other half of the same argument. A canonicalisation that fired on the shape it produces
1725    /// would be a canonicalisation with no direction, which is what section 13.5 refuses.
1726    #[test]
1727    fn a_constant_already_on_the_right_is_left_alone() {
1728        let i32 = Type::int(32);
1729        let (_, mut func, block) = one_block(i32);
1730        let x = func.append_param(block, i32);
1731        let mut build = Builder::new(&mut func, block);
1732        let three = build.iconst(i32, 3);
1733        let sum = build.binary(Opcode::Add, x, three, Flags::NONE);
1734        build.ret(&[sum]);
1735        assert!(!simplify(&mut func));
1736        let args = operands(&func, returned(&func, block));
1737        assert_eq!(args[0], x);
1738        assert_eq!(number(&func, args[1]), 3);
1739    }
1740
1741    /// A subtraction is not commutative and nothing moves its constant.
1742    ///
1743    /// Turning `c - x` into anything is not what tier three does, and the rules are written per
1744    /// opcode rather than over a set of them, so this is asking whether the wrong opcode found its
1745    /// way into the file.
1746    #[test]
1747    fn a_subtraction_keeps_its_operands_where_they_are() {
1748        let i32 = Type::int(32);
1749        let (_, mut func, block) = one_block(i32);
1750        let x = func.append_param(block, i32);
1751        let mut build = Builder::new(&mut func, block);
1752        let three = build.iconst(i32, 3);
1753        let difference = build.binary(Opcode::Sub, three, x, Flags::NONE);
1754        build.ret(&[difference]);
1755        assert!(!simplify(&mut func));
1756        let args = operands(&func, returned(&func, block));
1757        assert_eq!(number(&func, args[0]), 3);
1758        assert_eq!(args[1], x);
1759    }
1760
1761    /// A block whose parameter and whose result are different widths, which is what every width
1762    /// rule needs and what `one_block` cannot give.
1763    fn narrow_to_wide(takes: Type, gives: Type) -> (Interner, Func, Block) {
1764        let mut names = Interner::new();
1765        let name = names.intern("f");
1766        let signature = Signature::new().with_params(&[takes]).with_returns(&[gives]);
1767        let mut func = Func::new(name, signature);
1768        let block = func.create_block();
1769        (names, func, block)
1770    }
1771
1772    /// A conversion of a conversion of a parameter, which is the shape every width rule matches.
1773    ///
1774    /// The parameter is at `from`, the inner conversion takes it to `through` and the outer one
1775    /// takes that to `to`, and what comes back is the function, the block and the parameter.
1776    fn chain(
1777        inner: Opcode,
1778        outer: Opcode,
1779        from: Type,
1780        through: Type,
1781        to: Type,
1782    ) -> (Func, Block, Value) {
1783        let (_, mut func, block) = narrow_to_wide(from, to);
1784        let x = func.append_param(block, from);
1785        let mut build = Builder::new(&mut func, block);
1786        let middle = build.unary(inner, x, through);
1787        let outside = build.unary(outer, middle, to);
1788        build.ret(&[outside]);
1789        (func, block, x)
1790    }
1791
1792    /// Truncating an extension back to the width it came from is the value that was there.
1793    ///
1794    /// Every pair of widths and both extensions, because the rule file writes all twelve and a
1795    /// test of one of them would say nothing about the other eleven.
1796    #[test]
1797    fn truncating_an_extension_back_to_its_own_width_gives_the_value_back() {
1798        for extend in [Opcode::SExt, Opcode::ZExt] {
1799            for (narrow, wide) in [(8, 16), (8, 32), (8, 64), (16, 32), (16, 64), (32, 64)] {
1800                let (from, through) = (Type::int(narrow), Type::int(wide));
1801                let (mut func, block, x) = chain(extend, Opcode::Trunc, from, through, from);
1802                assert!(simplify(&mut func), "{extend:?} i{narrow} to i{wide} was left alone");
1803                assert_eq!(
1804                    returned(&func, block),
1805                    x,
1806                    "{extend:?} i{narrow} to i{wide} and back did not give the value back"
1807                );
1808            }
1809        }
1810    }
1811
1812    /// Truncating an extension to a width still above the source is the same extension, stopping
1813    /// earlier.
1814    #[test]
1815    fn truncating_an_extension_above_its_source_is_a_shorter_extension() {
1816        let (mut func, block, x) =
1817            chain(Opcode::SExt, Opcode::Trunc, Type::int(8), Type::int(64), Type::int(16));
1818        assert!(simplify(&mut func));
1819        let result = returned(&func, block);
1820        assert_eq!(came_from(&func, result).0, Opcode::SExt);
1821        assert_eq!(operands(&func, result), vec![x]);
1822        assert_eq!(func[result].ty, Type::int(16));
1823    }
1824
1825    /// Truncating an extension to a width below the source is a truncation of the source, and
1826    /// which extension it was never mattered.
1827    #[test]
1828    fn truncating_an_extension_below_its_source_is_a_truncation_of_the_source() {
1829        let (mut func, block, x) =
1830            chain(Opcode::ZExt, Opcode::Trunc, Type::int(16), Type::int(32), Type::int(8));
1831        assert!(simplify(&mut func));
1832        let result = returned(&func, block);
1833        assert_eq!(came_from(&func, result).0, Opcode::Trunc);
1834        assert_eq!(operands(&func, result), vec![x]);
1835        assert_eq!(func[result].ty, Type::int(8));
1836    }
1837
1838    /// An extension of an extension is one extension, and a sign extension of a zero extension is
1839    /// a zero extension rather than a sign extension.
1840    #[test]
1841    fn an_extension_of_an_extension_is_one_extension() {
1842        for (inner, outer, want) in [
1843            (Opcode::ZExt, Opcode::ZExt, Opcode::ZExt),
1844            (Opcode::SExt, Opcode::SExt, Opcode::SExt),
1845            (Opcode::ZExt, Opcode::SExt, Opcode::ZExt),
1846        ] {
1847            let (mut func, block, x) =
1848                chain(inner, outer, Type::int(8), Type::int(16), Type::int(64));
1849            assert!(simplify(&mut func), "{outer:?} of {inner:?} was left alone");
1850            let result = returned(&func, block);
1851            assert_eq!(came_from(&func, result).0, want, "{outer:?} of {inner:?}");
1852            assert_eq!(operands(&func, result), vec![x]);
1853            assert_eq!(func[result].ty, Type::int(64));
1854        }
1855    }
1856
1857    /// A truncation of a truncation is one truncation, straight to the width the outer one asked
1858    /// for.
1859    ///
1860    /// The inner one threw away bits the outer one was going to throw away as well, so the width
1861    /// in the middle was never read and the rule goes to the outer width from the source. Both
1862    /// orderings of the three widths are tried, because a rule that picked the middle width rather
1863    /// than the outer one would still pass a test that only went from sixty four to eight through
1864    /// thirty two.
1865    #[test]
1866    fn a_truncation_of_a_truncation_is_one_truncation() {
1867        for (from, through, to) in [(64u32, 32u32, 16u32), (64, 32, 8), (64, 16, 8), (32, 16, 8)] {
1868            let (mut func, block, x) = chain(
1869                Opcode::Trunc,
1870                Opcode::Trunc,
1871                Type::int(from),
1872                Type::int(through),
1873                Type::int(to),
1874            );
1875            assert!(simplify(&mut func), "i{from} to i{through} to i{to} was left alone");
1876            let result = returned(&func, block);
1877            assert_eq!(came_from(&func, result).0, Opcode::Trunc, "i{from} to i{through} to i{to}");
1878            assert_eq!(operands(&func, result), vec![x]);
1879            assert_eq!(func[result].ty, Type::int(to));
1880        }
1881    }
1882
1883    /// And zero extending a sign extension is not one, because the bits the sign extension copied
1884    /// are bits of the value now and nothing above them is a function of the source alone.
1885    #[test]
1886    fn zero_extending_a_sign_extension_is_left_alone() {
1887        let (mut func, _, _) =
1888            chain(Opcode::SExt, Opcode::ZExt, Type::int(8), Type::int(16), Type::int(64));
1889        assert!(!simplify(&mut func), "a zero extension of a sign extension was rewritten");
1890    }
1891
1892    /// And zero extending a truncation is left alone, which is the rule the tier would be expected
1893    /// to have and does not.
1894    ///
1895    /// It was written and proved and then measured, and the measurement is why it went: the
1896    /// machine has one instruction for the pair already, the `and` with an immediate that replaced
1897    /// it is the longer encoding of the two, and the mask hides the narrowing from
1898    /// [`crate::narrow`]. The rule file says the whole of it. This is here so that somebody adding
1899    /// it back finds a test rather than a silence.
1900    #[test]
1901    fn zero_extending_a_truncation_is_left_alone() {
1902        let (mut func, _, _) =
1903            chain(Opcode::Trunc, Opcode::ZExt, Type::int(64), Type::int(32), Type::int(64));
1904        assert!(!simplify(&mut func), "a zero extension of a truncation became a mask");
1905    }
1906
1907    /// A width rule needs an operand something computed, and a parameter is not one.
1908    ///
1909    /// This is what the plan being an expanding one means at the bottom: there is no instruction
1910    /// under the operand to be the second level of the pattern, so nothing matches and nothing is
1911    /// rewritten. Said out loud because it is the case that would otherwise be a crash rather than
1912    /// a miss.
1913    #[test]
1914    fn a_width_rule_needs_an_operand_an_instruction_computed() {
1915        let (_, mut func, block) = narrow_to_wide(Type::int(64), Type::int(32));
1916        let x = func.append_param(block, Type::int(64));
1917        let mut build = Builder::new(&mut func, block);
1918        let narrowed = build.unary(Opcode::Trunc, x, Type::int(32));
1919        build.ret(&[narrowed]);
1920        assert!(!simplify(&mut func), "a truncation of a parameter was rewritten");
1921    }
1922
1923    #[test]
1924    fn adding_nothing_points_every_reader_at_the_operand() {
1925        let i32 = Type::int(32);
1926        let (_, mut func, block) = one_block(i32);
1927        let x = func.append_param(block, i32);
1928        let mut build = Builder::new(&mut func, block);
1929        let zero = build.iconst(i32, 0);
1930        let sum = build.binary(Opcode::Add, x, zero, Flags::NONE);
1931        build.ret(&[sum]);
1932        assert!(simplify(&mut func));
1933        // The `add` is still there, used by nothing, which is what dead code elimination is for.
1934        assert_eq!(returned(&func, block), x);
1935        assert_eq!(came_from(&func, sum).0, Opcode::Add);
1936    }
1937
1938    /// The constant on either side, since nothing puts it on the right yet and a rule written one
1939    /// way round would fire on half the additions it should.
1940    #[test]
1941    fn the_constant_is_found_on_either_side_of_an_identity() {
1942        for swapped in [false, true] {
1943            let i32 = Type::int(32);
1944            let (_, mut func, block) = one_block(i32);
1945            let x = func.append_param(block, i32);
1946            let mut build = Builder::new(&mut func, block);
1947            let zero = build.iconst(i32, 0);
1948            let (lhs, rhs) = if swapped { (zero, x) } else { (x, zero) };
1949            let sum = build.binary(Opcode::Add, lhs, rhs, Flags::NONE);
1950            build.ret(&[sum]);
1951            assert!(simplify(&mut func), "swapped {swapped}");
1952            assert_eq!(returned(&func, block), x, "swapped {swapped}");
1953        }
1954    }
1955
1956    #[test]
1957    fn multiplying_by_nothing_becomes_the_constant_where_it_stands() {
1958        let i32 = Type::int(32);
1959        let (_, mut func, block) = one_block(i32);
1960        let x = func.append_param(block, i32);
1961        let mut build = Builder::new(&mut func, block);
1962        let zero = build.iconst(i32, 0);
1963        let product = build.binary(Opcode::Mul, x, zero, Flags::NONE);
1964        build.ret(&[product]);
1965        assert!(simplify(&mut func));
1966        // The result value survives, which is the whole reason this half rewrites in place.
1967        assert_eq!(returned(&func, block), product);
1968        assert_eq!(came_from(&func, product).0, Opcode::IConst);
1969        assert_eq!(number(&func, product), 0);
1970    }
1971
1972    /// The two identities a pattern that writes one name twice exists for, at every width they
1973    /// are written at.
1974    #[test]
1975    fn a_value_against_itself() {
1976        for bits in [8, 16, 32, 64] {
1977            let ty = Type::int(bits);
1978            let (_, mut func, block) = one_block(ty);
1979            let x = func.append_param(block, ty);
1980            let mut build = Builder::new(&mut func, block);
1981            let both = build.binary(Opcode::And, x, x, Flags::NONE);
1982            build.ret(&[both]);
1983            assert!(simplify(&mut func), "{bits} bits");
1984            assert_eq!(returned(&func, block), x, "{bits} bits");
1985
1986            let (_, mut func, block) = one_block(ty);
1987            let x = func.append_param(block, ty);
1988            let mut build = Builder::new(&mut func, block);
1989            let nothing = build.binary(Opcode::Sub, x, x, Flags::NONE);
1990            build.ret(&[nothing]);
1991            assert!(simplify(&mut func), "{bits} bits");
1992            assert_eq!(number(&func, nothing), 0, "{bits} bits");
1993        }
1994    }
1995
1996    /// Every predicate with one name in both operands, at every width the rules are written at.
1997    /// Six of the ten are true and four are false, and not one of them had to look at what the
1998    /// operand holds.
1999    #[test]
2000    fn every_comparison_of_a_value_with_itself_is_decided() {
2001        for bits in [8, 16, 32, 64] {
2002            for pred in IntPred::all() {
2003                let mut names = Interner::new();
2004                let name = names.intern("f");
2005                let int = Type::int(bits);
2006                let signature = Signature::new().with_params(&[int]).with_returns(&[Type::int(1)]);
2007                let mut func = Func::new(name, signature);
2008                let block = func.create_block();
2009                let x = func.append_param(block, int);
2010                let mut build = Builder::new(&mut func, block);
2011                let answer = build.icmp(pred, x, x);
2012                build.ret(&[answer]);
2013                assert!(simplify(&mut func), "{pred:?} at {bits} bits");
2014                let said = number(&func, answer);
2015                if matches!(
2016                    pred,
2017                    IntPred::Ne | IntPred::Slt | IntPred::Sgt | IntPred::Ult | IntPred::Ugt
2018                ) {
2019                    assert_eq!(said, 0, "{pred:?} at {bits} bits");
2020                } else {
2021                    assert_ne!(said, 0, "{pred:?} at {bits} bits");
2022                }
2023            }
2024        }
2025    }
2026
2027    /// A remainder by one is nothing, and a division by one is the value. The pair is worth a
2028    /// test of its own because they are the two identities that produce different shapes from the
2029    /// same operands.
2030    #[test]
2031    fn dividing_by_one_and_the_remainder_that_goes_with_it() {
2032        let i32 = Type::int(32);
2033        let (_, mut func, block) = one_block(i32);
2034        let x = func.append_param(block, i32);
2035        let mut build = Builder::new(&mut func, block);
2036        let one = build.iconst(i32, 1);
2037        let quotient = build.binary(Opcode::SDiv, x, one, Flags::NONE);
2038        let rest = build.binary(Opcode::SRem, x, one, Flags::NONE);
2039        let sum = build.binary(Opcode::Add, quotient, rest, Flags::NONE);
2040        build.ret(&[sum]);
2041        assert!(simplify(&mut func));
2042        assert_eq!(number(&func, rest), 0);
2043        // The add reads the value the division was of, which is what the redirection did.
2044        let rucc_ir::Def::Result { inst, .. } = func[sum].def else { panic!("not a result") };
2045        assert_eq!(func[func[inst].args][0], x);
2046    }
2047
2048    /// All ones at one bit is the `1` the rule file writes, and the front end writes it as `-1`.
2049    /// The two are the same bit and the rule has to fire on what the front end wrote.
2050    #[test]
2051    fn all_ones_at_one_bit_is_the_one_the_front_end_writes() {
2052        for written in [-1, 1] {
2053            let bit = Type::int(1);
2054            let (_, mut func, block) = one_block(bit);
2055            let x = func.append_param(block, bit);
2056            let mut build = Builder::new(&mut func, block);
2057            let ones = build.iconst(bit, written);
2058            let kept = build.binary(Opcode::And, x, ones, Flags::NONE);
2059            build.ret(&[kept]);
2060            assert!(simplify(&mut func), "written as {written}");
2061            assert_eq!(returned(&func, block), x, "written as {written}");
2062        }
2063    }
2064
2065    /// One identity feeding another is followed all the way, so the second is worth as much as
2066    /// the first. The redirections are applied once at the end of the run, and this is what says
2067    /// that costs nothing.
2068    #[test]
2069    fn one_identity_feeding_another_is_followed_to_the_end() {
2070        let i32 = Type::int(32);
2071        let (_, mut func, block) = one_block(i32);
2072        let x = func.append_param(block, i32);
2073        let mut build = Builder::new(&mut func, block);
2074        let zero = build.iconst(i32, 0);
2075        let one = build.iconst(i32, 1);
2076        let sum = build.binary(Opcode::Add, x, zero, Flags::NONE);
2077        let product = build.binary(Opcode::Mul, sum, one, Flags::NONE);
2078        let shifted = build.binary(Opcode::Shl, product, zero, Flags::NONE);
2079        build.ret(&[shifted]);
2080        assert!(simplify(&mut func));
2081        assert_eq!(returned(&func, block), x);
2082    }
2083
2084    /// Shifting nothing in any direction, and all ones to the right with the sign bit coming in.
2085    /// The count is a parameter here, so nothing at all is known about it and the identity is the
2086    /// only thing that could decide these.
2087    #[test]
2088    fn shifting_nothing_and_shifting_all_ones_with_the_sign() {
2089        for bits in [8, 16, 32, 64] {
2090            let ty = Type::int(bits);
2091            let cases = [
2092                (Opcode::Shl, 0_i128, 0_i128),
2093                (Opcode::LShr, 0, 0),
2094                (Opcode::AShr, 0, 0),
2095                (Opcode::AShr, -1, -1),
2096            ];
2097            for (opcode, from, expected) in cases {
2098                let (_, mut func, block) = one_block(ty);
2099                let count = func.append_param(block, ty);
2100                let mut build = Builder::new(&mut func, block);
2101                let value = build.iconst(ty, from);
2102                let shifted = build.binary(opcode, value, count, Flags::NONE);
2103                build.ret(&[shifted]);
2104                assert!(simplify(&mut func), "{opcode:?} of {from} at {bits} bits");
2105                let said = number(&func, shifted);
2106                assert_eq!(said, expected, "{opcode:?} of {from} at {bits} bits");
2107            }
2108        }
2109    }
2110
2111    /// All ones shifted right with zeroes coming in is not all ones, and there is no rule saying
2112    /// it is. The pair with the arithmetic shift above is the whole of why the sign matters here.
2113    #[test]
2114    fn all_ones_shifted_right_with_zeroes_coming_in_is_left_alone() {
2115        let i32 = Type::int(32);
2116        let (_, mut func, block) = one_block(i32);
2117        let count = func.append_param(block, i32);
2118        let mut build = Builder::new(&mut func, block);
2119        let ones = build.iconst(i32, -1);
2120        let shifted = build.binary(Opcode::LShr, ones, count, Flags::NONE);
2121        build.ret(&[shifted]);
2122        assert!(!simplify(&mut func));
2123        assert_eq!(came_from(&func, shifted).0, Opcode::LShr);
2124    }
2125
2126    #[test]
2127    fn an_instruction_no_rule_is_about_is_left_alone() {
2128        // Multiplying by three. Two is tier two and is an addition, one and zero are tier one, and
2129        // every power of two is a shift, so three is the smallest constant no tier has anything to
2130        // say about. Turning it into a shift and an add is a sequence rather than a rewrite.
2131        let i32 = Type::int(32);
2132        let (_, mut func, block) = one_block(i32);
2133        let x = func.append_param(block, i32);
2134        let mut build = Builder::new(&mut func, block);
2135        let three = build.iconst(i32, 3);
2136        let tripled = build.binary(Opcode::Mul, x, three, Flags::NONE);
2137        build.ret(&[tripled]);
2138        assert!(!simplify(&mut func), "no rule is about multiplying by three");
2139        assert_eq!(returned(&func, block), tripled);
2140        assert_eq!(came_from(&func, tripled).0, Opcode::Mul);
2141    }
2142
2143    #[test]
2144    fn multiplying_by_two_becomes_an_addition_of_the_value_with_itself() {
2145        let i32 = Type::int(32);
2146        let (_, mut func, block) = one_block(i32);
2147        let x = func.append_param(block, i32);
2148        let mut build = Builder::new(&mut func, block);
2149        let two = build.iconst(i32, 2);
2150        let doubled = build.binary(Opcode::Mul, x, two, Flags::NONE);
2151        build.ret(&[doubled]);
2152        assert!(simplify(&mut func));
2153        // In place, so the value the return reads is the one it always read.
2154        assert_eq!(returned(&func, block), doubled);
2155        assert_eq!(came_from(&func, doubled).0, Opcode::Add);
2156        assert_eq!(operands(&func, doubled), [x, x]);
2157        // And it stays an addition although two is a power of two and the rule below would take
2158        // it. A rule naming its constant is more specific than a rule taking whatever constant is
2159        // there, so the trie tries it first without anything having to sort the two.
2160    }
2161
2162    #[test]
2163    fn multiplying_by_a_power_of_two_becomes_a_shift_by_the_count_of_its_zeros() {
2164        let i32 = Type::int(32);
2165        let (_, mut func, block) = one_block(i32);
2166        let x = func.append_param(block, i32);
2167        let mut build = Builder::new(&mut func, block);
2168        let eight = build.iconst(i32, 8);
2169        let scaled = build.binary(Opcode::Mul, x, eight, Flags::NONE);
2170        build.ret(&[scaled]);
2171        assert!(simplify(&mut func));
2172        assert_eq!(returned(&func, block), scaled);
2173        assert_eq!(came_from(&func, scaled).0, Opcode::Shl);
2174        let args = operands(&func, scaled);
2175        assert_eq!(args[0], x);
2176        assert_eq!(number(&func, args[1]), 3);
2177    }
2178
2179    #[test]
2180    fn the_power_of_two_with_the_sign_bit_set_is_one_of_them() {
2181        // The constant the compiler and the solver would disagree about if either read it at some
2182        // width other than the rule's. At 32 bits this is a power of two and shifts by 31, and in
2183        // the 128 bit integer the pass matches constants into it is a negative number, so a guard
2184        // that forgot to mask would call it no power of two at all.
2185        let i32 = Type::int(32);
2186        let (_, mut func, block) = one_block(i32);
2187        let x = func.append_param(block, i32);
2188        let mut build = Builder::new(&mut func, block);
2189        let top = build.iconst(i32, 0x8000_0000);
2190        let scaled = build.binary(Opcode::Mul, x, top, Flags::NONE);
2191        build.ret(&[scaled]);
2192        assert!(simplify(&mut func));
2193        assert_eq!(came_from(&func, scaled).0, Opcode::Shl);
2194        assert_eq!(number(&func, operands(&func, scaled)[1]), 31);
2195    }
2196
2197    #[test]
2198    fn dividing_an_unsigned_value_by_a_power_of_two_becomes_a_shift() {
2199        let i32 = Type::int(32);
2200        let (_, mut func, block) = one_block(i32);
2201        let x = func.append_param(block, i32);
2202        let mut build = Builder::new(&mut func, block);
2203        let sixteen = build.iconst(i32, 16);
2204        let quotient = build.binary(Opcode::UDiv, x, sixteen, Flags::NONE);
2205        build.ret(&[quotient]);
2206        assert!(simplify(&mut func));
2207        assert_eq!(came_from(&func, quotient).0, Opcode::LShr);
2208        let args = operands(&func, quotient);
2209        assert_eq!(args[0], x);
2210        assert_eq!(number(&func, args[1]), 4);
2211    }
2212
2213    #[test]
2214    fn dividing_a_signed_value_by_a_power_of_two_is_left_alone() {
2215        // Deliberately, and this says so rather than leaving it to be read as an oversight. A
2216        // signed division rounds towards zero and a shift rounds down, so the two agree only on
2217        // values that are not negative. Correcting for that is a bias added before the shift,
2218        // which is a sequence of instructions rather than one term in place of another.
2219        let i32 = Type::int(32);
2220        let (_, mut func, block) = one_block(i32);
2221        let x = func.append_param(block, i32);
2222        let mut build = Builder::new(&mut func, block);
2223        let sixteen = build.iconst(i32, 16);
2224        let quotient = build.binary(Opcode::SDiv, x, sixteen, Flags::NONE);
2225        build.ret(&[quotient]);
2226        assert!(!simplify(&mut func), "no rule turns a signed division into a shift");
2227        assert_eq!(came_from(&func, quotient).0, Opcode::SDiv);
2228    }
2229
2230    #[test]
2231    fn the_unsigned_remainder_of_a_power_of_two_becomes_a_mask() {
2232        let i32 = Type::int(32);
2233        let (_, mut func, block) = one_block(i32);
2234        let x = func.append_param(block, i32);
2235        let mut build = Builder::new(&mut func, block);
2236        let thirty_two = build.iconst(i32, 32);
2237        let rest = build.binary(Opcode::URem, x, thirty_two, Flags::NONE);
2238        build.ret(&[rest]);
2239        assert!(simplify(&mut func));
2240        assert_eq!(came_from(&func, rest).0, Opcode::And);
2241        let args = operands(&func, rest);
2242        assert_eq!(args[0], x);
2243        assert_eq!(number(&func, args[1]), 31);
2244    }
2245
2246    #[test]
2247    fn a_division_by_a_constant_that_is_not_a_power_of_two_is_left_alone() {
2248        let i32 = Type::int(32);
2249        let (_, mut func, block) = one_block(i32);
2250        let x = func.append_param(block, i32);
2251        let mut build = Builder::new(&mut func, block);
2252        let ten = build.iconst(i32, 10);
2253        let quotient = build.binary(Opcode::UDiv, x, ten, Flags::NONE);
2254        build.ret(&[quotient]);
2255        assert!(!simplify(&mut func), "ten is no power of two");
2256        assert_eq!(came_from(&func, quotient).0, Opcode::UDiv);
2257    }
2258
2259    #[test]
2260    fn multiplying_by_minus_one_becomes_a_subtraction_from_a_zero_the_rewrite_defines() {
2261        // The other shape of operand: nothing in the function holds a zero, so the rewrite has to
2262        // put one in front of the instruction it is rewriting.
2263        let i32 = Type::int(32);
2264        let (_, mut func, block) = one_block(i32);
2265        let x = func.append_param(block, i32);
2266        let mut build = Builder::new(&mut func, block);
2267        let minus = build.iconst(i32, -1);
2268        let negated = build.binary(Opcode::Mul, x, minus, Flags::NONE);
2269        build.ret(&[negated]);
2270        assert!(simplify(&mut func));
2271        assert_eq!(returned(&func, block), negated);
2272        assert_eq!(came_from(&func, negated).0, Opcode::Sub);
2273        let args = operands(&func, negated);
2274        assert_eq!(number(&func, args[0]), 0);
2275        assert_eq!(args[1], x);
2276    }
2277
2278    #[test]
2279    fn the_flags_of_the_instruction_a_strength_reduction_replaces_do_not_come_with_it() {
2280        // An `nsw` on a multiplication is a promise about that multiplication. The addition below
2281        // may well keep it, and a promise carried across a rewrite because it probably still holds
2282        // is how a wrong one gets made.
2283        let i32 = Type::int(32);
2284        let (_, mut func, block) = one_block(i32);
2285        let x = func.append_param(block, i32);
2286        let mut build = Builder::new(&mut func, block);
2287        let two = build.iconst(i32, 2);
2288        let doubled = build.binary(Opcode::Mul, x, two, Flags::NSW);
2289        build.ret(&[doubled]);
2290        assert!(simplify(&mut func));
2291        let rucc_ir::Def::Result { inst, .. } = func[doubled].def else { panic!("not a result") };
2292        assert_eq!(func[inst].flags, Flags::NONE);
2293    }
2294
2295    #[test]
2296    fn a_strength_reduction_leaves_the_verifier_nothing_to_complain_about() {
2297        // The zero the negation needs is defined in front of the instruction that reads it, and
2298        // whether it really is in front of it is a question about the block rather than about the
2299        // instruction, which is what the verifier is for.
2300        let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
2301        let i32 = Type::int(32);
2302        let (mut names, mut func, block) = one_block(i32);
2303        let mut module = Module::new(names.intern("test.c"), &target);
2304        let x = func.append_param(block, i32);
2305        let mut build = Builder::new(&mut func, block);
2306        let minus = build.iconst(i32, -1);
2307        let negated = build.binary(Opcode::Mul, x, minus, Flags::NONE);
2308        let two = build.iconst(i32, 2);
2309        let doubled = build.binary(Opcode::Mul, negated, two, Flags::NONE);
2310        build.ret(&[doubled]);
2311        assert!(simplify(&mut func));
2312        module.add_func(func);
2313        rucc_ir::verify(&module, &names).expect("the pass left the function verifiable");
2314    }
2315
2316    /// The function the pass leaves is still one the verifier accepts. Pointing a reader at a
2317    /// different value and turning an instruction into a constant are both things a rewrite could
2318    /// get wrong in a way none of the tests above would notice, because each of those asks about
2319    /// one instruction and this asks about the function.
2320    #[test]
2321    fn the_pass_leaves_the_verifier_nothing_to_complain_about() {
2322        let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
2323        let i32 = Type::int(32);
2324        let (mut names, mut func, block) = one_block(i32);
2325        let mut module = Module::new(names.intern("test.c"), &target);
2326        let x = func.append_param(block, i32);
2327        let mut build = Builder::new(&mut func, block);
2328        let zero = build.iconst(i32, 0);
2329        let one = build.iconst(i32, 1);
2330        let sum = build.binary(Opcode::Add, x, zero, Flags::NONE);
2331        let product = build.binary(Opcode::Mul, sum, one, Flags::NONE);
2332        let gone = build.binary(Opcode::Sub, product, product, Flags::NONE);
2333        let total = build.binary(Opcode::Add, product, gone, Flags::NONE);
2334        build.ret(&[total]);
2335        assert!(simplify(&mut func));
2336        module.add_func(func);
2337        rucc_ir::verify(&module, &names).expect("the pass left the function verifiable");
2338    }
2339
2340    #[test]
2341    fn fuel_stops_an_identity_and_not_the_walk() {
2342        let i32 = Type::int(32);
2343        let (_, mut func, block) = one_block(i32);
2344        let x = func.append_param(block, i32);
2345        let mut build = Builder::new(&mut func, block);
2346        let zero = build.iconst(i32, 0);
2347        let first = build.binary(Opcode::Add, x, zero, Flags::NONE);
2348        let second = build.binary(Opcode::Sub, x, zero, Flags::NONE);
2349        let sum = build.binary(Opcode::Add, first, second, Flags::NONE);
2350        build.ret(&[sum]);
2351        let stats =
2352            Simplify.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(1));
2353        assert!(stats.changed());
2354        assert_eq!(stats.total(Kind::Optimized), 1);
2355        assert_eq!(stats.count(Kind::Missed, super::NO_FUEL_RULE), 1);
2356        // The first fired and the second did not, and the second is still read by the add.
2357        let rucc_ir::Def::Result { inst, .. } = func[sum].def else { panic!("not a result") };
2358        assert_eq!(func[func[inst].args], [x, second]);
2359    }
2360
2361    #[test]
2362    fn a_negated_float_comparison_becomes_the_opposite_predicate() {
2363        // Every ordered predicate and its opposite, which is the table `!(x < y)` is `x >= y`
2364        // or unordered lives in, and the one place a sign error would hide.
2365        for pred in FloatPred::all() {
2366            let (_, mut func, block) = blank();
2367            let mut build = Builder::new(&mut func, block);
2368            let x = build.iconst(Type::int(64), 0);
2369            let x = build.unary(Opcode::Bitcast, x, Type::float(Float::F64));
2370            let cmp = build.fcmp(pred, x, x, Flags::NONE);
2371            let ones = build.iconst(Type::int(1), -1);
2372            let not = build.binary(Opcode::Xor, cmp, ones, Flags::NONE);
2373            build.ret(&[not]);
2374            assert!(simplify(&mut func), "{pred:?}");
2375            assert_eq!(
2376                came_from(&func, not),
2377                (Opcode::FCmp, Extra::FloatPred(pred.inverse())),
2378                "{pred:?}"
2379            );
2380        }
2381    }
2382
2383    #[test]
2384    fn a_negated_integer_comparison_becomes_the_opposite_predicate() {
2385        for pred in IntPred::all() {
2386            let (_, mut func, block) = blank();
2387            let mut build = Builder::new(&mut func, block);
2388            let x = build.iconst(Type::int(32), 3);
2389            let y = build.iconst(Type::int(32), 4);
2390            let cmp = build.icmp(pred, x, y);
2391            let ones = build.iconst(Type::int(1), -1);
2392            let not = build.binary(Opcode::Xor, cmp, ones, Flags::NONE);
2393            build.ret(&[not]);
2394            assert!(simplify(&mut func), "{pred:?}");
2395            assert_eq!(
2396                came_from(&func, not),
2397                (Opcode::ICmp, Extra::IntPred(pred.inverse())),
2398                "{pred:?}"
2399            );
2400        }
2401    }
2402
2403    #[test]
2404    fn the_constant_is_found_on_either_side() {
2405        for swapped in [false, true] {
2406            let (_, mut func, block) = blank();
2407            let mut build = Builder::new(&mut func, block);
2408            let x = build.iconst(Type::int(32), 3);
2409            let y = build.iconst(Type::int(32), 4);
2410            let cmp = build.icmp(IntPred::Slt, x, y);
2411            let ones = build.iconst(Type::int(1), -1);
2412            let (lhs, rhs) = if swapped { (ones, cmp) } else { (cmp, ones) };
2413            let not = build.binary(Opcode::Xor, lhs, rhs, Flags::NONE);
2414            build.ret(&[not]);
2415            assert!(simplify(&mut func), "swapped {swapped}");
2416            assert_eq!(came_from(&func, not).1, Extra::IntPred(IntPred::Sge));
2417        }
2418    }
2419
2420    #[test]
2421    fn an_exclusive_or_of_two_comparisons_is_left_alone() {
2422        let (_, mut func, block) = blank();
2423        let mut build = Builder::new(&mut func, block);
2424        let x = build.iconst(Type::int(32), 3);
2425        let y = build.iconst(Type::int(32), 4);
2426        let a = build.icmp(IntPred::Slt, x, y);
2427        let b = build.icmp(IntPred::Sgt, x, y);
2428        let differ = build.binary(Opcode::Xor, a, b, Flags::NONE);
2429        build.ret(&[differ]);
2430        assert!(!simplify(&mut func));
2431        assert_eq!(came_from(&func, differ).0, Opcode::Xor);
2432    }
2433
2434    #[test]
2435    fn an_exclusive_or_of_something_that_is_not_a_comparison_is_left_alone() {
2436        let (_, mut func, block) = blank();
2437        let mut build = Builder::new(&mut func, block);
2438        let x = build.iconst(Type::int(32), 3);
2439        let narrow = build.unary(Opcode::Trunc, x, Type::int(1));
2440        let ones = build.iconst(Type::int(1), -1);
2441        let not = build.binary(Opcode::Xor, narrow, ones, Flags::NONE);
2442        build.ret(&[not]);
2443        assert!(!simplify(&mut func));
2444        assert_eq!(came_from(&func, not).0, Opcode::Xor);
2445    }
2446
2447    #[test]
2448    fn a_wider_exclusive_or_with_one_is_not_a_negation_and_is_left_alone() {
2449        let (_, mut func, block) = blank();
2450        let mut build = Builder::new(&mut func, block);
2451        let x = build.iconst(Type::int(32), 3);
2452        let y = build.iconst(Type::int(32), 4);
2453        let cmp = build.icmp(IntPred::Slt, x, y);
2454        let wide = build.unary(Opcode::ZExt, cmp, Type::int(32));
2455        let one = build.iconst(Type::int(32), 1);
2456        let flipped = build.binary(Opcode::Xor, wide, one, Flags::NONE);
2457        let narrow = build.unary(Opcode::Trunc, flipped, Type::int(1));
2458        build.ret(&[narrow]);
2459        assert!(!simplify(&mut func), "an i32 xor 1 flips one bit of thirty two");
2460        assert_eq!(came_from(&func, flipped).0, Opcode::Xor);
2461    }
2462
2463    #[test]
2464    fn the_comparisons_flags_travel_with_the_predicate() {
2465        let (_, mut func, block) = blank();
2466        let mut build = Builder::new(&mut func, block);
2467        let x = build.iconst(Type::int(64), 0);
2468        let x = build.unary(Opcode::Bitcast, x, Type::float(Float::F64));
2469        let cmp = build.fcmp(FloatPred::Olt, x, x, Flags::FAST);
2470        let ones = build.iconst(Type::int(1), -1);
2471        let not = build.binary(Opcode::Xor, cmp, ones, Flags::NONE);
2472        build.ret(&[not]);
2473        assert!(simplify(&mut func));
2474        let rucc_ir::Def::Result { inst, .. } = func[not].def else { panic!("not a result") };
2475        // The promise the original comparison was made under, not the exclusive or's absence of
2476        // one. Dropping it would be correct and would quietly undo a fast math flag.
2477        assert_eq!(func[inst].flags, Flags::FAST);
2478    }
2479
2480    #[test]
2481    fn fuel_stops_the_transformation_and_not_the_walk() {
2482        let (_, mut func, block) = blank();
2483        let mut build = Builder::new(&mut func, block);
2484        let x = build.iconst(Type::int(32), 3);
2485        let y = build.iconst(Type::int(32), 4);
2486        let a = build.icmp(IntPred::Slt, x, y);
2487        let b = build.icmp(IntPred::Sgt, x, y);
2488        let ones = build.iconst(Type::int(1), -1);
2489        let first = build.binary(Opcode::Xor, a, ones, Flags::NONE);
2490        let second = build.binary(Opcode::Xor, b, ones, Flags::NONE);
2491        let both = build.binary(Opcode::And, first, second, Flags::NONE);
2492        build.ret(&[both]);
2493        let stats =
2494            Simplify.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(1));
2495        assert!(stats.changed());
2496        assert_eq!(stats.count(Kind::Optimized, super::FLIPPED), 1);
2497        assert_eq!(stats.count(Kind::Missed, super::NO_FUEL), 1);
2498        assert_eq!(came_from(&func, first).0, Opcode::ICmp);
2499        assert_eq!(came_from(&func, second).0, Opcode::Xor);
2500    }
2501
2502    /// Two `i32` parameters to compare, which every composite test below is about.
2503    fn a_pair() -> (Func, Block, Value, Value) {
2504        let mut names = Interner::new();
2505        let name = names.intern("f");
2506        let int = Type::int(32);
2507        let signature = Signature::new().with_params(&[int, int]).with_returns(&[Type::int(1)]);
2508        let mut func = Func::new(name, signature);
2509        let block = func.create_block();
2510        let x = func.append_param(block, int);
2511        let y = func.append_param(block, int);
2512        (func, block, x, y)
2513    }
2514
2515    /// The same, of `f64`.
2516    fn a_float_pair() -> (Func, Block, Value, Value) {
2517        let mut names = Interner::new();
2518        let name = names.intern("f");
2519        let float = Type::float(Float::F64);
2520        let signature = Signature::new().with_params(&[float, float]).with_returns(&[Type::int(1)]);
2521        let mut func = Func::new(name, signature);
2522        let block = func.create_block();
2523        let x = func.append_param(block, float);
2524        let y = func.append_param(block, float);
2525        (func, block, x, y)
2526    }
2527
2528    /// Every bucket table agrees with [`FloatPred::inverse`] about what the opposite of a predicate
2529    /// is.
2530    ///
2531    /// The point of the assertion is that the two were written in different crates by different
2532    /// reasoning. `inverse` is a sixteen line table of names, and the buckets are four bits and a
2533    /// complement, so a predicate given the wrong set here disagrees with the name it was given
2534    /// there and this says which one.
2535    #[test]
2536    fn the_opposite_of_a_float_predicate_is_the_buckets_it_leaves_out() {
2537        for pred in FloatPred::all() {
2538            assert_eq!(
2539                super::float_buckets(pred.inverse()),
2540                super::bucket::ALL_FLOAT ^ super::float_buckets(pred),
2541                "{pred:?}"
2542            );
2543        }
2544    }
2545
2546    /// And with [`FloatPred::swapped`] about what reading the operands the other way round does.
2547    ///
2548    /// Which of two values is below the other changes and nothing else does, because equal is equal
2549    /// from both ends and a NaN makes a pair unordered from both ends.
2550    #[test]
2551    fn swapping_a_float_predicates_operands_exchanges_below_and_above() {
2552        for pred in FloatPred::all() {
2553            let want = super::turned(super::float_buckets(pred));
2554            assert_eq!(super::float_buckets(pred.swapped()), want, "{pred:?}");
2555        }
2556    }
2557
2558    /// The sixteen floating point predicates are the sixteen sets, so reading a set back is total
2559    /// and gives the predicate it came from.
2560    #[test]
2561    fn every_set_of_float_buckets_is_a_predicate() {
2562        for pred in FloatPred::all() {
2563            assert_eq!(super::float_pred(super::float_buckets(pred)), Some(pred), "{pred:?}");
2564        }
2565        for buckets in 0..=super::bucket::ALL_FLOAT {
2566            assert!(super::float_pred(buckets).is_some(), "{buckets} spells nothing");
2567        }
2568    }
2569
2570    /// The same two agreements for the integer predicates.
2571    #[test]
2572    fn an_integer_predicate_agrees_with_its_own_opposite_and_its_own_swap() {
2573        use super::bucket::ALL_INT;
2574        for pred in IntPred::all() {
2575            let (before, reading) = super::int_buckets(pred);
2576            let (opposite, other) = super::int_buckets(pred.inverse());
2577            assert_eq!(opposite, ALL_INT ^ before, "the opposite of {pred:?}");
2578            assert_eq!(other, reading, "the opposite of {pred:?} reads the operands differently");
2579            let (swapped, other) = super::int_buckets(pred.swapped());
2580            assert_eq!(swapped, super::turned(before), "the swap of {pred:?}");
2581            assert_eq!(other, reading, "the swap of {pred:?} reads the operands differently");
2582        }
2583    }
2584
2585    /// Reading an integer set back gives the predicate it came from, under the reading that
2586    /// predicate wanted.
2587    #[test]
2588    fn every_integer_predicate_is_read_back_as_itself() {
2589        for pred in IntPred::all() {
2590            let (buckets, reading) = super::int_buckets(pred);
2591            assert_eq!(super::int_pred(buckets, reading), Some(pred), "{pred:?}");
2592        }
2593    }
2594
2595    #[test]
2596    fn two_integer_comparisons_that_agree_about_nothing_are_false() {
2597        let (mut func, block, x, y) = a_pair();
2598        let mut build = Builder::new(&mut func, block);
2599        let same = build.icmp(IntPred::Eq, x, y);
2600        let differ = build.icmp(IntPred::Ne, x, y);
2601        let both = build.binary(Opcode::And, same, differ, Flags::NONE);
2602        build.ret(&[both]);
2603        assert!(simplify(&mut func));
2604        assert_eq!(number(&func, both), 0);
2605    }
2606
2607    #[test]
2608    fn two_integer_comparisons_that_cover_everything_are_true() {
2609        let (mut func, block, x, y) = a_pair();
2610        let mut build = Builder::new(&mut func, block);
2611        let above = build.icmp(IntPred::Sge, x, y);
2612        let below = build.icmp(IntPred::Slt, x, y);
2613        let either = build.binary(Opcode::Or, above, below, Flags::NONE);
2614        build.ret(&[either]);
2615        assert!(simplify(&mut func));
2616        assert_ne!(number(&func, either), 0);
2617    }
2618
2619    /// Below or equal is one comparison, and the saving is what makes the rewrite worth taking on
2620    /// its own rather than only for the two answers that are constants.
2621    #[test]
2622    fn two_integer_comparisons_that_overlap_become_one() {
2623        let (mut func, block, x, y) = a_pair();
2624        let mut build = Builder::new(&mut func, block);
2625        let below = build.icmp(IntPred::Slt, x, y);
2626        let same = build.icmp(IntPred::Eq, x, y);
2627        let either = build.binary(Opcode::Or, below, same, Flags::NONE);
2628        build.ret(&[either]);
2629        assert!(simplify(&mut func));
2630        assert_eq!(came_from(&func, either), (Opcode::ICmp, Extra::IntPred(IntPred::Sle)));
2631        assert_eq!(operands(&func, either), [x, y]);
2632    }
2633
2634    /// `(x<y) && (y<x)`, which is the third test of `gcc.c-torture/execute/compare-3.c` and the one
2635    /// that needs the second comparison turned round before the two are about one pair.
2636    #[test]
2637    fn the_second_comparison_is_read_in_the_first_ones_operand_order() {
2638        let (mut func, block, x, y) = a_pair();
2639        let mut build = Builder::new(&mut func, block);
2640        let below = build.icmp(IntPred::Slt, x, y);
2641        let above = build.icmp(IntPred::Slt, y, x);
2642        let both = build.binary(Opcode::And, below, above, Flags::NONE);
2643        build.ret(&[both]);
2644        assert!(simplify(&mut func));
2645        assert_eq!(number(&func, both), 0);
2646    }
2647
2648    /// An equality says nothing about how the operands are read, so it combines with either
2649    /// ordering and takes the one beside it.
2650    #[test]
2651    fn an_equality_takes_the_ordering_of_the_comparison_beside_it() {
2652        for (ordered, want) in [(IntPred::Ult, IntPred::Ule), (IntPred::Slt, IntPred::Sle)] {
2653            let (mut func, block, x, y) = a_pair();
2654            let mut build = Builder::new(&mut func, block);
2655            let below = build.icmp(ordered, x, y);
2656            let same = build.icmp(IntPred::Eq, x, y);
2657            let either = build.binary(Opcode::Or, below, same, Flags::NONE);
2658            build.ret(&[either]);
2659            assert!(simplify(&mut func), "{ordered:?}");
2660            assert_eq!(came_from(&func, either).1, Extra::IntPred(want), "{ordered:?}");
2661        }
2662    }
2663
2664    /// A signed comparison and an unsigned one are two different questions, and a set built out of
2665    /// one of each would be a set about no reading in particular.
2666    #[test]
2667    fn a_signed_comparison_and_an_unsigned_one_are_left_alone() {
2668        let (mut func, block, x, y) = a_pair();
2669        let mut build = Builder::new(&mut func, block);
2670        let signed = build.icmp(IntPred::Slt, x, y);
2671        let unsigned = build.icmp(IntPred::Ugt, x, y);
2672        let both = build.binary(Opcode::And, signed, unsigned, Flags::NONE);
2673        build.ret(&[both]);
2674        assert!(!simplify(&mut func));
2675        assert_eq!(came_from(&func, both).0, Opcode::And);
2676    }
2677
2678    #[test]
2679    fn two_comparisons_about_different_operands_are_left_alone() {
2680        let (mut func, block, x, y) = a_pair();
2681        let mut build = Builder::new(&mut func, block);
2682        let other = build.iconst(Type::int(32), 7);
2683        let first = build.icmp(IntPred::Slt, x, y);
2684        let second = build.icmp(IntPred::Sgt, x, other);
2685        let both = build.binary(Opcode::And, first, second, Flags::NONE);
2686        build.ret(&[both]);
2687        assert!(!simplify(&mut func));
2688        assert_eq!(came_from(&func, both).0, Opcode::And);
2689    }
2690
2691    /// `x == y && x != y` on floating point, which is false for the same reason it is on integers
2692    /// and is not the same reason a reader might expect: `oeq` and `une` do not overlap because
2693    /// `oeq` refuses a NaN and `une` accepts one, so the pair is empty rather than only unequal.
2694    #[test]
2695    fn two_float_comparisons_that_agree_about_nothing_are_false() {
2696        let (mut func, block, x, y) = a_float_pair();
2697        let mut build = Builder::new(&mut func, block);
2698        let same = build.fcmp(FloatPred::Oeq, x, y, Flags::NONE);
2699        let differ = build.fcmp(FloatPred::Une, x, y, Flags::NONE);
2700        let both = build.binary(Opcode::And, same, differ, Flags::NONE);
2701        build.ret(&[both]);
2702        assert!(simplify(&mut func));
2703        assert_eq!(number(&func, both), 0);
2704    }
2705
2706    /// Unordered, or above or equal, or below, which is the fifth test of
2707    /// `gcc.c-torture/execute/ieee/compare-fp-3.c`. It is three comparisons and two `or`s, and it
2708    /// folds because the walk is forward and the rewrite is in place: the inner pair is one
2709    /// comparison by the time the outer `or` is looked at.
2710    #[test]
2711    fn a_three_way_float_condition_folds_one_pair_at_a_time() {
2712        let (mut func, block, x, y) = a_float_pair();
2713        let mut build = Builder::new(&mut func, block);
2714        let neither = build.fcmp(FloatPred::Uno, x, y, Flags::NONE);
2715        let above = build.fcmp(FloatPred::Oge, x, y, Flags::NONE);
2716        let below = build.fcmp(FloatPred::Olt, x, y, Flags::NONE);
2717        let first = build.binary(Opcode::Or, neither, above, Flags::NONE);
2718        let whole = build.binary(Opcode::Or, first, below, Flags::NONE);
2719        build.ret(&[whole]);
2720        assert!(simplify(&mut func));
2721        assert_eq!(came_from(&func, first).1, Extra::FloatPred(FloatPred::Uge));
2722        assert_ne!(number(&func, whole), 0);
2723    }
2724
2725    /// One `f64` parameter, which every magnitude test below takes the magnitude of.
2726    fn a_float() -> (Func, Block, Value) {
2727        let mut names = Interner::new();
2728        let name = names.intern("f");
2729        let float = Type::float(Float::F64);
2730        let signature = Signature::new().with_params(&[float]).with_returns(&[Type::int(1)]);
2731        let mut func = Func::new(name, signature);
2732        let block = func.create_block();
2733        let x = func.append_param(block, float);
2734        (func, block, x)
2735    }
2736
2737    /// `fabs (x)` as the lowering writes it, which is the sign bit cleared over the bits.
2738    fn magnitude_of(build: &mut Builder<'_>, x: Value) -> Value {
2739        let bits = Type::int(64);
2740        let number = build.unary(Opcode::Bitcast, x, bits);
2741        let mask = build.iconst(bits, i128::from(i64::MAX));
2742        let cleared = build.binary(Opcode::And, number, mask, Flags::NONE);
2743        build.unary(Opcode::Bitcast, cleared, Type::float(Float::F64))
2744    }
2745
2746    /// `fabs (x) < 0.0`, which is what `gcc.c-torture/execute/20020720-1.c` asserts is false by
2747    /// calling a function it never defines.
2748    #[test]
2749    fn a_magnitude_is_never_below_zero() {
2750        let (mut func, block, x) = a_float();
2751        let mut build = Builder::new(&mut func, block);
2752        let p = magnitude_of(&mut build, x);
2753        let zero = build.fconst(Type::float(Float::F64), 0);
2754        let below = build.fcmp(FloatPred::Olt, p, zero, Flags::NONE);
2755        build.ret(&[below]);
2756        assert!(simplify(&mut func));
2757        assert_eq!(number(&func, below), 0);
2758    }
2759
2760    /// The same question with the operands the other way round. `0.0 > fabs (x)` is the same claim
2761    /// and is a different instruction, and the buckets have to be turned round to see it.
2762    #[test]
2763    fn zero_is_never_above_a_magnitude() {
2764        let (mut func, block, x) = a_float();
2765        let mut build = Builder::new(&mut func, block);
2766        let p = magnitude_of(&mut build, x);
2767        let zero = build.fconst(Type::float(Float::F64), 0);
2768        let above = build.fcmp(FloatPred::Ogt, zero, p, Flags::NONE);
2769        build.ret(&[above]);
2770        assert!(simplify(&mut func));
2771        assert_eq!(number(&func, above), 0);
2772    }
2773
2774    /// `fabs (x) <= 0.0` is not a constant and is still shorter than it was: the only way a
2775    /// magnitude is at or below zero is by being zero, so the answer is an equality.
2776    #[test]
2777    fn a_magnitude_at_or_below_zero_is_a_magnitude_equal_to_it() {
2778        let (mut func, block, x) = a_float();
2779        let mut build = Builder::new(&mut func, block);
2780        let p = magnitude_of(&mut build, x);
2781        let zero = build.fconst(Type::float(Float::F64), 0);
2782        let atmost = build.fcmp(FloatPred::Ole, p, zero, Flags::NONE);
2783        build.ret(&[atmost]);
2784        assert!(simplify(&mut func));
2785        assert_eq!(came_from(&func, atmost).1, Extra::FloatPred(FloatPred::Oeq));
2786    }
2787
2788    /// A negative constant takes the equal bucket with it, because every value a magnitude can be
2789    /// is above every negative number, so the comparison is false rather than shorter.
2790    #[test]
2791    fn a_magnitude_is_never_at_or_below_a_negative_number() {
2792        let (mut func, block, x) = a_float();
2793        let mut build = Builder::new(&mut func, block);
2794        let p = magnitude_of(&mut build, x);
2795        let minus_one = build.fconst(Type::float(Float::F64), 0xbff0_0000_0000_0000);
2796        let atmost = build.fcmp(FloatPred::Ole, p, minus_one, Flags::NONE);
2797        build.ret(&[atmost]);
2798        assert!(simplify(&mut func));
2799        assert_eq!(number(&func, atmost), 0);
2800    }
2801
2802    /// `fabs (x) >= 0.0` is left alone, and a reader who expects it to be true is the reason this
2803    /// test is here rather than the reason it fails: a NaN has its sign bit cleared like anything
2804    /// else and is not above, below or equal to anything, so the comparison is false for one.
2805    #[test]
2806    fn a_magnitude_at_or_above_zero_is_still_a_question_about_a_nan() {
2807        let (mut func, block, x) = a_float();
2808        let mut build = Builder::new(&mut func, block);
2809        let p = magnitude_of(&mut build, x);
2810        let zero = build.fconst(Type::float(Float::F64), 0);
2811        let atleast = build.fcmp(FloatPred::Oge, p, zero, Flags::NONE);
2812        build.ret(&[atleast]);
2813        assert!(!simplify(&mut func));
2814        assert_eq!(came_from(&func, atleast).1, Extra::FloatPred(FloatPred::Oge));
2815    }
2816
2817    /// A positive constant narrows nothing, so the comparison stands as it was written.
2818    #[test]
2819    fn a_magnitude_against_a_positive_number_is_left_alone() {
2820        let (mut func, block, x) = a_float();
2821        let mut build = Builder::new(&mut func, block);
2822        let p = magnitude_of(&mut build, x);
2823        let one = build.fconst(Type::float(Float::F64), 0x3ff0_0000_0000_0000);
2824        let below = build.fcmp(FloatPred::Olt, p, one, Flags::NONE);
2825        build.ret(&[below]);
2826        assert!(!simplify(&mut func));
2827        assert_eq!(came_from(&func, below).1, Extra::FloatPred(FloatPred::Olt));
2828    }
2829
2830    /// A mask with its top bit set says nothing about the sign of what comes out of it, so the
2831    /// bitcast under it is not a magnitude and the comparison stands.
2832    #[test]
2833    fn a_mask_that_keeps_the_sign_bit_is_not_a_magnitude() {
2834        let (mut func, block, x) = a_float();
2835        let mut build = Builder::new(&mut func, block);
2836        let bits = Type::int(64);
2837        let number = build.unary(Opcode::Bitcast, x, bits);
2838        let mask = build.iconst(bits, -2);
2839        let cleared = build.binary(Opcode::And, number, mask, Flags::NONE);
2840        let p = build.unary(Opcode::Bitcast, cleared, Type::float(Float::F64));
2841        let zero = build.fconst(Type::float(Float::F64), 0);
2842        let below = build.fcmp(FloatPred::Olt, p, zero, Flags::NONE);
2843        build.ret(&[below]);
2844        assert!(!simplify(&mut func));
2845        assert_eq!(came_from(&func, below).1, Extra::FloatPred(FloatPred::Olt));
2846    }
2847
2848    /// A NaN on the other side is left to the comparison folder, which has an answer for it that
2849    /// does not depend on either operand being a magnitude.
2850    #[test]
2851    fn a_magnitude_against_a_nan_is_left_alone() {
2852        let (mut func, block, x) = a_float();
2853        let mut build = Builder::new(&mut func, block);
2854        let p = magnitude_of(&mut build, x);
2855        let nan = build.fconst(Type::float(Float::F64), 0x7ff8_0000_0000_0000);
2856        let below = build.fcmp(FloatPred::Olt, p, nan, Flags::NONE);
2857        build.ret(&[below]);
2858        assert!(!simplify(&mut func));
2859        assert_eq!(came_from(&func, below).1, Extra::FloatPred(FloatPred::Olt));
2860    }
2861
2862    /// The fold spends fuel like the other two and stopping it stops the transforming rather than
2863    /// the walking.
2864    #[test]
2865    fn fuel_stops_the_magnitude_fold_and_not_the_walk() {
2866        let (mut func, block, x) = a_float();
2867        let mut build = Builder::new(&mut func, block);
2868        let p = magnitude_of(&mut build, x);
2869        let zero = build.fconst(Type::float(Float::F64), 0);
2870        let below = build.fcmp(FloatPred::Olt, p, zero, Flags::NONE);
2871        let also = build.fcmp(FloatPred::Olt, p, zero, Flags::NONE);
2872        let both = build.binary(Opcode::Or, below, also, Flags::NONE);
2873        build.ret(&[both]);
2874        let stats =
2875            Simplify.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(1));
2876        assert_eq!(stats.count(Kind::Optimized, super::MAGNITUDE), 1);
2877        assert_eq!(stats.count(Kind::Missed, super::NO_FUEL_MAGNITUDE), 1);
2878        assert_eq!(number(&func, below), 0);
2879        assert_eq!(came_from(&func, also).0, Opcode::FCmp);
2880    }
2881
2882    /// A fast math promise is a promise about one comparison, and a set built out of two that were
2883    /// not promised the same thing is a set under no promise in particular.
2884    #[test]
2885    fn two_comparisons_promised_different_things_are_left_alone() {
2886        let (mut func, block, x, y) = a_float_pair();
2887        let mut build = Builder::new(&mut func, block);
2888        let below = build.fcmp(FloatPred::Olt, x, y, Flags::FAST);
2889        let same = build.fcmp(FloatPred::Oeq, x, y, Flags::NONE);
2890        let either = build.binary(Opcode::Or, below, same, Flags::NONE);
2891        build.ret(&[either]);
2892        assert!(!simplify(&mut func));
2893        assert_eq!(came_from(&func, either).0, Opcode::Or);
2894    }
2895
2896    #[test]
2897    fn the_promise_both_comparisons_were_made_under_travels_to_the_one_that_replaces_them() {
2898        let (mut func, block, x, y) = a_float_pair();
2899        let mut build = Builder::new(&mut func, block);
2900        let below = build.fcmp(FloatPred::Olt, x, y, Flags::FAST);
2901        let same = build.fcmp(FloatPred::Oeq, x, y, Flags::FAST);
2902        let either = build.binary(Opcode::Or, below, same, Flags::NONE);
2903        build.ret(&[either]);
2904        assert!(simplify(&mut func));
2905        assert_eq!(came_from(&func, either).1, Extra::FloatPred(FloatPred::Ole));
2906        let rucc_ir::Def::Result { inst, .. } = func[either].def else { panic!("not a result") };
2907        assert_eq!(func[inst].flags, Flags::FAST);
2908    }
2909
2910    /// An `and` of two comparisons at a width that is not one bit is an and of two bits held in
2911    /// something wider, which is a different program.
2912    #[test]
2913    fn a_wider_and_of_two_comparisons_is_left_alone() {
2914        let (mut func, block, x, y) = a_pair();
2915        let mut build = Builder::new(&mut func, block);
2916        let same = build.icmp(IntPred::Eq, x, y);
2917        let differ = build.icmp(IntPred::Ne, x, y);
2918        let first = build.unary(Opcode::ZExt, same, Type::int(32));
2919        let second = build.unary(Opcode::ZExt, differ, Type::int(32));
2920        let both = build.binary(Opcode::And, first, second, Flags::NONE);
2921        let narrow = build.unary(Opcode::Trunc, both, Type::int(1));
2922        build.ret(&[narrow]);
2923        assert!(!simplify(&mut func));
2924        assert_eq!(came_from(&func, both).0, Opcode::And);
2925    }
2926
2927    #[test]
2928    fn fuel_stops_the_composite_fold_and_not_the_walk() {
2929        let (mut func, block, x, y) = a_pair();
2930        let mut build = Builder::new(&mut func, block);
2931        let same = build.icmp(IntPred::Eq, x, y);
2932        let differ = build.icmp(IntPred::Ne, x, y);
2933        let below = build.icmp(IntPred::Slt, x, y);
2934        let above = build.icmp(IntPred::Sgt, x, y);
2935        let first = build.binary(Opcode::And, same, differ, Flags::NONE);
2936        let second = build.binary(Opcode::And, below, above, Flags::NONE);
2937        let both = build.binary(Opcode::Or, first, second, Flags::NONE);
2938        build.ret(&[both]);
2939        let stats =
2940            Simplify.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(1));
2941        assert!(stats.changed());
2942        assert_eq!(stats.count(Kind::Optimized, super::COMPOSITE), 1);
2943        assert_eq!(stats.count(Kind::Missed, super::NO_FUEL_COMPOSITE), 1);
2944        assert_eq!(came_from(&func, first).0, Opcode::IConst);
2945        assert_eq!(came_from(&func, second).0, Opcode::And);
2946    }
2947}