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