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    // A decimal constant decodes, but the arithmetic a fold does on it is binary and would read the
1555    // coefficient as a significand, so a decimal is never a constant here. Its operations become
1556    // library calls in the back end instead, tamnd/rucc#207.
1557    if format.decimal().is_some() {
1558        return None;
1559    }
1560    Some(Float::from_bits(format, func[at].bits()))
1561}
1562
1563/// Writes what a set of buckets came to over the instruction it was worked out from.
1564///
1565/// In place, which keeps the result value, so every reader of that instruction is already reading
1566/// the one answer and whatever it used to read is left where it was for [`crate::dce`].
1567pub(crate) fn fold_composite(func: &mut Func, inst: Inst, composite: Composite) {
1568    match composite {
1569        Composite::Always(answer) => become_constant(func, inst, answer.into()),
1570        Composite::Pred(flip) => {
1571            let args = func.push_values(&[flip.lhs, flip.rhs]);
1572            let data = &mut func[inst];
1573            data.opcode = flip.opcode;
1574            data.flags = flip.flags;
1575            data.args = args;
1576            data.extra = flip.extra;
1577        }
1578    }
1579}
1580
1581/// Whether this value is a constant with every bit of its type set.
1582fn all_ones(func: &Func, value: Value) -> bool {
1583    let ty = func[value].ty;
1584    let Def::Result { inst, .. } = func[value].def else { return false };
1585    let data = &func[inst];
1586    let Extra::Imm(at) = data.extra else { return false };
1587    if data.opcode != Opcode::IConst {
1588        return false;
1589    }
1590    // Read as signed, because an all ones value of any width is minus one that way and reading
1591    // it unsigned would need the width to build the mask from.
1592    func[at].signed(ty) == -1
1593}
1594
1595#[cfg(test)]
1596mod tests {
1597    use rucc_base::Interner;
1598    use rucc_ir::{
1599        Block, Builder, Extra, Flags, Float, FloatPred, Func, IntPred, Module, Opcode, Signature,
1600        Type, Value,
1601    };
1602    use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
1603
1604    use super::{
1605        CANONICAL, COMPARE, EXPAND, PLANS, SELECT, Shown, TABLES, canonical, compare, identities,
1606        select, strength, width,
1607    };
1608    use crate::rules::Piece;
1609    use crate::stats::Kind;
1610    use crate::{Fuel, Pass, simplify::Simplify};
1611
1612    /// A function with one block, ready to have instructions appended to it.
1613    fn blank() -> (Interner, Func, Block) {
1614        let mut names = Interner::new();
1615        let name = names.intern("f");
1616        let mut func = Func::new(name, Signature::new().with_returns(&[Type::int(1)]));
1617        let block = func.create_block();
1618        (names, func, block)
1619    }
1620
1621    /// The same, at the width the test is about and taking a parameter of it, since every identity
1622    /// below needs an operand that is not itself a constant.
1623    fn one_block(ty: Type) -> (Interner, Func, Block) {
1624        let mut names = Interner::new();
1625        let name = names.intern("f");
1626        let signature = Signature::new().with_params(&[ty]).with_returns(&[ty]);
1627        let mut func = Func::new(name, signature);
1628        let block = func.create_block();
1629        (names, func, block)
1630    }
1631
1632    /// Runs the pass with as much fuel as it wants, and says whether it rewrote anything.
1633    fn simplify(func: &mut Func) -> bool {
1634        Simplify
1635            .run(func, &mut crate::machine::fixtures::analyses(), &mut Fuel::unlimited())
1636            .changed()
1637    }
1638
1639    /// The opcode and the predicate the value now comes from.
1640    fn came_from(func: &Func, value: Value) -> (Opcode, Extra) {
1641        let rucc_ir::Def::Result { inst, .. } = func[value].def else { panic!("not a result") };
1642        (func[inst].opcode, func[inst].extra)
1643    }
1644
1645    /// What the block gives back, which is where every identity test reads its answer. A rule
1646    /// that produces a value is only worth anything if the readers move, so the readers are what
1647    /// the test looks at rather than the instruction that fired.
1648    fn returned(func: &Func, block: Block) -> Value {
1649        let inst = func.terminator(block).expect("the block has a terminator");
1650        func[func[inst].args][0]
1651    }
1652
1653    /// The operands of the instruction a value comes from.
1654    fn operands(func: &Func, value: Value) -> Vec<Value> {
1655        let rucc_ir::Def::Result { inst, .. } = func[value].def else { panic!("not a result") };
1656        func[func[inst].args].to_vec()
1657    }
1658
1659    /// The number a value is, which panics unless it is a constant.
1660    fn number(func: &Func, value: Value) -> i128 {
1661        let rucc_ir::Def::Result { inst, .. } = func[value].def else { panic!("not a result") };
1662        let data = &func[inst];
1663        assert_eq!(data.opcode, Opcode::IConst, "not a constant");
1664        let Extra::Imm(at) = data.extra else { panic!("a constant with no number") };
1665        func[at].signed(func[value].ty)
1666    }
1667
1668    /// Every rule in every table leaves one of the four shapes the pass knows how to apply.
1669    ///
1670    /// A rule that left anything else would be matched, found to be none of them, and skipped, and
1671    /// nothing at run time would say so: the rewrite would simply stop happening. So it is said
1672    /// here instead, once, over every table.
1673    #[test]
1674    fn every_rule_leaves_a_shape_the_pass_knows_what_to_do_with() {
1675        for (table, _) in TABLES {
1676            for rule in table.rules {
1677                let known = matches!(
1678                    rule.replacement,
1679                    [Piece::App { head, arity: 1 }, Piece::Var { .. }]
1680                        if head.starts_with("value.")
1681                ) || matches!(
1682                    rule.replacement,
1683                    [Piece::App { head, arity: 1 }, Piece::Int(_)]
1684                        if head.starts_with("iconst.")
1685                ) || matches!(
1686                    rule.replacement,
1687                    [Piece::App { arity: 2, .. }, ..] if instruction(rule.replacement)
1688                ) || conversion(rule.replacement);
1689                assert!(known, "{} leaves a shape the pass would skip", rule.pattern);
1690            }
1691        }
1692    }
1693
1694    /// The pieces of a replacement that is a conversion, read the way [`super::converted`] reads
1695    /// them, and shape only for the same reason [`instruction`] is: there are no bindings here to
1696    /// resolve the operand against.
1697    fn conversion(pieces: &'static [Piece]) -> bool {
1698        let [Piece::App { head, arity: 1 }, rest @ ..] = pieces else { return false };
1699        let converts =
1700            matches!(super::opcode_of(head), Some(Opcode::SExt | Opcode::ZExt | Opcode::Trunc));
1701        let number = matches!(rest, [Piece::App { head, .. }, ..] if head.starts_with("iconst."));
1702        converts && !number && shape(rest).is_some_and(<[Piece]>::is_empty)
1703    }
1704
1705    /// Every rule in the width table writes a term ending at the width the one it matched ended
1706    /// at.
1707    ///
1708    /// The pass rewrites in place and leaves the result type where it was, so a rule whose
1709    /// replacement converted to some other width would quietly produce a value of the wrong one.
1710    /// `rucc-verify` refuses a replacement narrower than what it replaces and says nothing about a
1711    /// wider one, so this is the half of that pair the solver does not cover.
1712    #[test]
1713    fn a_width_rule_writes_a_term_that_ends_where_the_one_it_matched_ended() {
1714        for rule in width::TABLE.rules {
1715            let [Piece::App { head, .. }, ..] = rule.replacement else {
1716                panic!("{} writes no head", rule.pattern)
1717            };
1718            let wrote = head.rsplit_once('.').expect("a replacement head names a width").1;
1719            let matched = rule
1720                .pattern
1721                .trim_start_matches('(')
1722                .split([' ', ')'])
1723                .next()
1724                .and_then(|head| head.rsplit_once('.'))
1725                .expect("a pattern head names a width")
1726                .1;
1727            assert_eq!(wrote, matched, "{} ends somewhere else", rule.pattern);
1728        }
1729    }
1730
1731    /// The pieces of a replacement that is an instruction, read the way the pass reads them, so
1732    /// that the check above is the pass's own answer rather than a second opinion about it.
1733    ///
1734    /// The bindings are empty, which is why a `value.iN` operand fails to resolve and this only
1735    /// says the shape is one the pass would take rather than that it would take it here.
1736    fn instruction(pieces: &'static [Piece]) -> bool {
1737        let [Piece::App { head, arity: 2 }, rest @ ..] = pieces else { return false };
1738        if super::opcode_of(head).is_none() {
1739            return false;
1740        }
1741        shape(rest).and_then(shape).is_some_and(<[Piece]>::is_empty)
1742    }
1743
1744    /// One operand of a replacement, read the way [`super::operand`] reads it, and the pieces
1745    /// after it. An instruction under the one a rule writes is an operand too, and its own
1746    /// operands are read the same way.
1747    fn shape(pieces: &'static [Piece]) -> Option<&'static [Piece]> {
1748        match pieces {
1749            [Piece::App { head, arity: 1 }, Piece::Var { .. }, rest @ ..]
1750                if head.starts_with("value.") =>
1751            {
1752                Some(rest)
1753            }
1754            [
1755                Piece::App { head, arity: 1 },
1756                Piece::Int(_) | Piece::Var { .. } | Piece::Computed { .. },
1757                rest @ ..,
1758            ] if head.starts_with("iconst.") => Some(rest),
1759            [Piece::App { head, arity }, rest @ ..]
1760                if super::opcode_of(head).is_some_and(|opcode| opcode != Opcode::IConst) =>
1761            {
1762                (0..*arity).try_fold(rest, |rest, _| shape(rest))
1763            }
1764            _ => None,
1765        }
1766    }
1767
1768    /// And each table holds every rule its file writes. The tables are generated, so this is
1769    /// asking whether the generator saw the whole file, which is the one thing about it worth
1770    /// doubting.
1771    #[test]
1772    fn each_table_holds_every_rule_its_file_writes() {
1773        let tier_one = include_str!("../rules/simplify.rules");
1774        let tier_two = include_str!("../rules/strength.rules");
1775        let tier_three = include_str!("../rules/canonical.rules");
1776        let tier_four = include_str!("../rules/width.rules");
1777        let tier_five = include_str!("../rules/compare.rules");
1778        let tier_six = include_str!("../rules/select.rules");
1779        let count = |text: &str| text.matches("(rule (simplify ").count();
1780        assert_eq!(identities::TABLE.rules.len(), count(tier_one));
1781        assert_eq!(strength::TABLE.rules.len(), count(tier_two));
1782        assert_eq!(canonical::TABLE.rules.len(), count(tier_three));
1783        assert_eq!(width::TABLE.rules.len(), count(tier_four));
1784        assert_eq!(compare::TABLE.rules.len(), count(tier_five));
1785        assert_eq!(select::TABLE.rules.len(), count(tier_six));
1786        assert!(
1787            identities::TABLE.rules.len() > 100,
1788            "tier one is about a hundred rules and there are fewer"
1789        );
1790        assert!(
1791            strength::TABLE.rules.len() > 20,
1792            "tier two is the multiplications and the divisions and there are fewer"
1793        );
1794        assert_eq!(
1795            canonical::TABLE.rules.len(),
1796            20,
1797            "tier three is five commutative operators at four widths"
1798        );
1799        assert_eq!(
1800            width::TABLE.rules.len(),
1801            66,
1802            "tier four is the truncation and extension algebra over four widths, and the three \
1803             shapes of it that exist over the one bit a comparison answers in"
1804        );
1805        assert_eq!(
1806            compare::TABLE.rules.len(),
1807            72,
1808            "tier five is four predicates against each of four constants at four widths, and a \
1809             widened boolean against zero under two predicates at the same four"
1810        );
1811        assert_eq!(
1812            select::TABLE.rules.len(),
1813            32,
1814            "tier six is eight shapes of select at the four widths a select comes in"
1815        );
1816    }
1817
1818    /// Three ways of showing an operand and no more, since a fourth would be a plan nothing
1819    /// tries and a rule written for it would never fire.
1820    #[test]
1821    fn a_pattern_is_reached_by_one_of_the_plans() {
1822        assert_eq!(PLANS.len(), 3);
1823    }
1824
1825    /// Tier four is matched with its operand expanded, and none of the shared plans expands one.
1826    ///
1827    /// Every pattern in that tier has an instruction at its second level, so under any of the
1828    /// plans above it every rule in it would fail at the first node and the whole tier would be a
1829    /// file nobody matched with. Asserted rather than left to be read, because that failure is
1830    /// silent.
1831    ///
1832    /// Tier five expands as well, under the second of its own two plans, which is the half of it
1833    /// about a widened boolean compared against zero.
1834    #[test]
1835    fn a_width_rule_is_only_matched_with_its_operand_expanded() {
1836        let (_, plans) = TABLES[2];
1837        assert_eq!(plans.len(), 1);
1838        assert_eq!(plans[0], EXPAND[0]);
1839        assert_eq!(plans[0][0], Shown::Expand);
1840        for plan in PLANS {
1841            assert_ne!(plan, plans[0], "no shared plan expands an operand");
1842        }
1843        assert_ne!(CANONICAL[0], plans[0]);
1844        assert_eq!(COMPARE[1][0], Shown::Expand);
1845        assert_eq!(COMPARE[1][1], Shown::Const);
1846    }
1847
1848    /// Tier three is matched under its own plan and no other.
1849    ///
1850    /// This is what makes the rules terminate rather than swap a pair of constants back and forth
1851    /// until the fuel runs out. It is asserted rather than left to be read, because the cost of
1852    /// somebody adding the shared plans to the tier three row is a pass that does not stop.
1853    #[test]
1854    fn a_canonicalisation_is_only_matched_with_the_right_operand_refused() {
1855        let (_, plans) = TABLES[5];
1856        assert_eq!(plans.len(), 1);
1857        assert_eq!(plans[0], CANONICAL[0]);
1858        assert_eq!(plans[0][1], Shown::Var);
1859        for plan in PLANS {
1860            assert_ne!(plan, plans[0], "a shared plan would let a canonicalisation cycle");
1861        }
1862    }
1863
1864    /// Tier five is matched with the constant on the right and no other way.
1865    ///
1866    /// Every rule in it writes the constant there, so under the plan that shows a constant left
1867    /// operand as a number none of them would match and under the plan that refuses a constant on
1868    /// the right none of them would either. Two plans, differing only in how the left operand is
1869    /// shown, which is what the two halves of the tier are about.
1870    #[test]
1871    fn a_comparison_rule_is_only_matched_with_the_constant_on_the_right() {
1872        let (_, plans) = TABLES[3];
1873        assert_eq!(plans.len(), 2);
1874        assert_eq!(plans, COMPARE);
1875        for plan in plans {
1876            assert_eq!(plan[1], Shown::Const);
1877        }
1878        assert_eq!(plans[0][0], Shown::Reg);
1879        assert_eq!(plans[1][0], Shown::Expand);
1880    }
1881
1882    /// Tier six is matched with the arms as numbers, and then with one arm expanded at a time.
1883    ///
1884    /// The condition is a register under every plan, since every rule in the tier binds it as a
1885    /// value. Expanding both arms at once would match nothing in the tier, because the arm that is
1886    /// not expanded is the value the other was computed from and only two registers can be said to
1887    /// be the same value.
1888    #[test]
1889    fn a_select_rule_is_matched_with_one_arm_expanded_at_a_time() {
1890        let (_, plans) = TABLES[4];
1891        assert_eq!(plans, SELECT);
1892        for plan in plans {
1893            assert_eq!(plan[0], Shown::Reg);
1894            assert!(plan[1] != Shown::Expand || plan[2] != Shown::Expand);
1895        }
1896    }
1897
1898    /// A function of two numbers at a width that compares them with `slt` and hands the answer to
1899    /// `arms`, which builds a select from it and returns what to return.
1900    fn selecting(
1901        width: u32,
1902        arms: impl FnOnce(&mut Builder<'_>, Value, Value) -> Value,
1903    ) -> (Func, Block, Value, Value) {
1904        let ty = Type::int(width);
1905        let (_, mut func, block) = blank();
1906        let x = func.append_param(block, ty);
1907        let y = func.append_param(block, ty);
1908        let mut build = Builder::new(&mut func, block);
1909        let cmp = build.icmp(IntPred::Slt, x, y);
1910        let out = arms(&mut build, cmp, x);
1911        build.ret(&[out]);
1912        (func, block, x, y)
1913    }
1914
1915    /// The comparison a value was widened from, with its predicate and its two operands.
1916    fn widened(func: &Func, value: Value) -> (IntPred, Vec<Value>) {
1917        assert_eq!(came_from(func, value).0, Opcode::ZExt);
1918        let bit = operands(func, value)[0];
1919        let (opcode, extra) = came_from(func, bit);
1920        assert_eq!(opcode, Opcode::ICmp);
1921        let Extra::IntPred(pred) = extra else { panic!("a comparison with no predicate") };
1922        (pred, operands(func, bit))
1923    }
1924
1925    /// `a < b ? 1 : 0` is the comparison widened, at every width, and `a < b ? 0 : 1` is the
1926    /// opposite comparison widened, with no exclusive or left between the two.
1927    #[test]
1928    fn a_select_between_one_and_zero_is_the_comparison_widened() {
1929        for width in [8u32, 16, 32, 64] {
1930            let ty = Type::int(width);
1931            for (then, other, pred) in [(1, 0, IntPred::Slt), (0, 1, IntPred::Sge)] {
1932                let (mut func, block, x, y) = selecting(width, |build, cmp, _| {
1933                    let then = build.iconst(ty, then);
1934                    let other = build.iconst(ty, other);
1935                    build.select(cmp, then, other)
1936                });
1937                assert!(simplify(&mut func), "i{width} {then} {other} was left alone");
1938                let got = returned(&func, block);
1939                assert_eq!(func[got].ty, ty);
1940                assert_eq!(widened(&func, got), (pred, vec![x, y]), "i{width} {then} {other}");
1941            }
1942        }
1943    }
1944
1945    /// A condition that is not a comparison has nothing to flip, and the exclusive or stays.
1946    #[test]
1947    fn a_select_between_zero_and_one_on_a_bit_is_the_bit_negated_and_widened() {
1948        let (_, mut func, block) = blank();
1949        let bit = func.append_param(block, Type::int(1));
1950        let mut build = Builder::new(&mut func, block);
1951        let zero = build.iconst(Type::int(32), 0);
1952        let one = build.iconst(Type::int(32), 1);
1953        let out = build.select(bit, zero, one);
1954        build.ret(&[out]);
1955        assert!(simplify(&mut func));
1956        let got = returned(&func, block);
1957        assert_eq!(came_from(&func, got).0, Opcode::ZExt);
1958        let negated = operands(&func, got)[0];
1959        assert_eq!(came_from(&func, negated).0, Opcode::Xor);
1960        let args = operands(&func, negated);
1961        assert_eq!(args[0], bit);
1962        assert_eq!(func[args[1]].ty, Type::int(1));
1963    }
1964
1965    /// `a < b ? -1 : 0` is nothing less the comparison widened, and `a < b ? 0 : -1` is the
1966    /// comparison widened less one.
1967    #[test]
1968    fn a_select_between_minus_one_and_zero_is_the_comparison_widened_and_moved() {
1969        for width in [8u32, 16, 32, 64] {
1970            let ty = Type::int(width);
1971            for (then, other, opcode) in [(-1, 0, Opcode::Sub), (0, -1, Opcode::Add)] {
1972                let (mut func, block, x, y) = selecting(width, |build, cmp, _| {
1973                    let then = build.iconst(ty, then);
1974                    let other = build.iconst(ty, other);
1975                    build.select(cmp, then, other)
1976                });
1977                assert!(simplify(&mut func), "i{width} {then} {other} was left alone");
1978                let got = returned(&func, block);
1979                assert_eq!(came_from(&func, got).0, opcode, "i{width} {then} {other}");
1980                let args = operands(&func, got);
1981                let (number_at, widened_at) = if opcode == Opcode::Sub { (0, 1) } else { (1, 0) };
1982                assert_eq!(
1983                    number(&func, args[number_at]),
1984                    if opcode == Opcode::Sub { 0 } else { -1 }
1985                );
1986                assert_eq!(func[args[number_at]].ty, ty);
1987                assert_eq!(widened(&func, args[widened_at]), (IntPred::Slt, vec![x, y]));
1988            }
1989        }
1990    }
1991
1992    /// `a < b ? x + 1 : x` is `x` plus the comparison, and `a < b ? x - 1 : x` is `x` less it,
1993    /// and with the arms the other way round the comparison is the opposite one.
1994    #[test]
1995    fn a_select_between_a_value_and_one_step_from_it_is_the_value_moved_by_the_comparison() {
1996        for width in [8u32, 16, 32, 64] {
1997            let ty = Type::int(width);
1998            for step in [Opcode::Add, Opcode::Sub] {
1999                for stepped_first in [true, false] {
2000                    let (mut func, block, x, y) = selecting(width, |build, cmp, x| {
2001                        let one = build.iconst(ty, 1);
2002                        let stepped = build.binary(step, x, one, Flags::NSW);
2003                        if stepped_first {
2004                            build.select(cmp, stepped, x)
2005                        } else {
2006                            build.select(cmp, x, stepped)
2007                        }
2008                    });
2009                    let case = format!("i{width} {step:?} first {stepped_first}");
2010                    assert!(simplify(&mut func), "{case} was left alone");
2011                    let got = returned(&func, block);
2012                    assert_eq!(came_from(&func, got).0, step, "{case}");
2013                    // What the select chose between made no promise the new instruction keeps.
2014                    let rucc_ir::Def::Result { inst, .. } = func[got].def else { panic!() };
2015                    assert_eq!(func[inst].flags, Flags::NONE, "{case}");
2016                    let args = operands(&func, got);
2017                    assert_eq!(args[0], x, "{case}");
2018                    let pred = if stepped_first { IntPred::Slt } else { IntPred::Sge };
2019                    assert_eq!(widened(&func, args[1]), (pred, vec![x, y]), "{case}");
2020                }
2021            }
2022        }
2023    }
2024
2025    /// A step of two is not one step, and the select is left for the back end.
2026    #[test]
2027    fn a_select_between_a_value_and_two_more_is_left_alone() {
2028        let (mut func, block, _, _) = selecting(32, |build, cmp, x| {
2029            let two = build.iconst(Type::int(32), 2);
2030            let stepped = build.binary(Opcode::Add, x, two, Flags::NONE);
2031            build.select(cmp, stepped, x)
2032        });
2033        simplify(&mut func);
2034        let got = returned(&func, block);
2035        assert_eq!(came_from(&func, got).0, Opcode::Select);
2036    }
2037
2038    /// The edge of a type, at each width, read each way.
2039    ///
2040    /// The least and greatest unsigned value and the least and greatest signed one, which are the
2041    /// four constants tier five is written against.
2042    fn edges(width: u32) -> [(i128, bool); 4] {
2043        let signed = 1i128 << (width - 1);
2044        [(0, false), (-1, false), (-signed, true), (signed - 1, true)]
2045    }
2046
2047    /// A comparison that its type has already answered becomes the answer.
2048    ///
2049    /// Nothing unsigned is below zero, everything unsigned is at least zero, and the same pair of
2050    /// sentences holds at each of the other three edges. Thirty two rules, run as one test,
2051    /// because what is being checked is the same sentence at four constants and four widths.
2052    #[test]
2053    fn a_comparison_against_the_edge_of_its_type_folds_to_a_bit() {
2054        for width in [8u32, 16, 32, 64] {
2055            let ty = Type::int(width);
2056            for (edge, signed) in edges(width) {
2057                // Below the edge is false at the bottom and above it is false at the top, and the
2058                // other of each pair is the negation, so one table gives all four.
2059                let below = edge == 0 || edge == -(1i128 << (width - 1));
2060                let (false_pred, true_pred) = match (signed, below) {
2061                    (false, true) => (IntPred::Ult, IntPred::Uge),
2062                    (false, false) => (IntPred::Ugt, IntPred::Ule),
2063                    (true, true) => (IntPred::Slt, IntPred::Sge),
2064                    (true, false) => (IntPred::Sgt, IntPred::Sle),
2065                };
2066                // Minus one for the true bit, because the rule writes `(iconst.i1 1)` and one bit
2067                // holding a one read signed is minus one, which is the same bit pattern and the
2068                // reading everything else in the compiler takes of a true condition.
2069                for (pred, answer) in [(false_pred, 0), (true_pred, -1)] {
2070                    let (_, mut func, block) = blank();
2071                    let x = func.append_param(block, ty);
2072                    let mut build = Builder::new(&mut func, block);
2073                    let bound = build.iconst(ty, edge);
2074                    let cmp = build.icmp(pred, x, bound);
2075                    build.ret(&[cmp]);
2076                    assert!(simplify(&mut func), "i{width} {pred:?} {edge} was left alone");
2077                    let got = returned(&func, block);
2078                    assert_eq!(
2079                        came_from(&func, got).0,
2080                        Opcode::IConst,
2081                        "i{width} {pred:?} {edge} did not fold"
2082                    );
2083                    assert_eq!(number(&func, got), answer, "i{width} {pred:?} {edge}");
2084                    assert_eq!(func[got].ty, Type::int(1), "i{width} {pred:?} {edge} is a bit");
2085                }
2086            }
2087        }
2088    }
2089
2090    /// And one that is true or false for exactly one value becomes the test for that value.
2091    ///
2092    /// The predicate has to come from the rule. Every case here matched an ordering and every one
2093    /// of them has to leave `eq` or `ne`, so a rewriter that took the predicate from the
2094    /// instruction it replaced would leave the ordering in place and this would say so.
2095    #[test]
2096    fn a_comparison_true_for_one_value_becomes_a_test_for_that_value() {
2097        for width in [8u32, 16, 32, 64] {
2098            let ty = Type::int(width);
2099            for (edge, signed) in edges(width) {
2100                let below = edge == 0 || edge == -(1i128 << (width - 1));
2101                // At most the bottom is equality and above it is inequality, and at the top the
2102                // two swap over.
2103                let (eq_pred, ne_pred) = match (signed, below) {
2104                    (false, true) => (IntPred::Ule, IntPred::Ugt),
2105                    (false, false) => (IntPred::Uge, IntPred::Ult),
2106                    (true, true) => (IntPred::Sle, IntPred::Sgt),
2107                    (true, false) => (IntPred::Sge, IntPred::Slt),
2108                };
2109                for (pred, left) in [(eq_pred, IntPred::Eq), (ne_pred, IntPred::Ne)] {
2110                    let (_, mut func, block) = blank();
2111                    let x = func.append_param(block, ty);
2112                    let mut build = Builder::new(&mut func, block);
2113                    let bound = build.iconst(ty, edge);
2114                    let cmp = build.icmp(pred, x, bound);
2115                    build.ret(&[cmp]);
2116                    assert!(simplify(&mut func), "i{width} {pred:?} {edge} was left alone");
2117                    let got = returned(&func, block);
2118                    assert_eq!(
2119                        came_from(&func, got),
2120                        (Opcode::ICmp, Extra::IntPred(left)),
2121                        "i{width} {pred:?} {edge} kept the predicate it matched"
2122                    );
2123                    let args = operands(&func, got);
2124                    assert_eq!(args[0], x, "i{width} {pred:?} {edge} lost its value");
2125                    assert_eq!(number(&func, args[1]), edge, "i{width} {pred:?} {edge}");
2126                    // The width the rule was written at, which is the width of what is being
2127                    // compared and not the width of the answer. A constant built at the result's
2128                    // type would be a one bit zero standing where a wider one was asked for.
2129                    assert_eq!(func[args[1]].ty, ty, "i{width} {pred:?} {edge} narrowed its bound");
2130                }
2131            }
2132        }
2133    }
2134
2135    /// A boolean widened and compared against zero is the boolean.
2136    ///
2137    /// The shape `if (flag)` and `(long)(a == b)` and every `__builtin_expect` arrive in, since
2138    /// each of them widens a comparison and then asks whether the wide value is zero. What the
2139    /// test asserts is that the branch ends up on the comparison itself, at one bit, with the
2140    /// widening left for dead code elimination.
2141    #[test]
2142    fn a_widened_boolean_compared_against_zero_is_the_boolean() {
2143        for width in [8u32, 16, 32, 64] {
2144            let ty = Type::int(width);
2145            let (_, mut func, block) = blank();
2146            let x = func.append_param(block, Type::int(32));
2147            let mut build = Builder::new(&mut func, block);
2148            let seven = build.iconst(Type::int(32), 7);
2149            let flag = build.icmp(IntPred::Eq, x, seven);
2150            let wide = build.unary(Opcode::ZExt, flag, ty);
2151            let zero = build.iconst(ty, 0);
2152            let test = build.icmp(IntPred::Ne, wide, zero);
2153            build.ret(&[test]);
2154            assert!(simplify(&mut func), "i{width} was left alone");
2155            let got = returned(&func, block);
2156            assert_eq!(got, flag, "i{width} did not end up on the comparison");
2157            assert_eq!(func[got].ty, Type::int(1), "i{width} is a bit");
2158        }
2159    }
2160
2161    /// And one compared against zero the other way is that boolean negated.
2162    ///
2163    /// The rule writes an exclusive or with a one bit one, because what is under the widening is
2164    /// whatever produced the bit and there is no predicate to flip in the general case. Where it
2165    /// is a comparison, which is this test, the hand written rewrite above the tables turns that
2166    /// exclusive or into the opposite comparison, and the pair composes into one instruction.
2167    ///
2168    /// Two runs, because the walk visits each instruction once and the hand written rewrite is
2169    /// tried before the tables are: the exclusive or did not exist when this instruction was
2170    /// looked at. Every pipeline above `-O0` names the pass twice, which is where the second run
2171    /// comes from in a real compile.
2172    #[test]
2173    fn a_widened_boolean_that_is_zero_is_the_boolean_negated() {
2174        for width in [8u32, 16, 32, 64] {
2175            let ty = Type::int(width);
2176            let (_, mut func, block) = blank();
2177            let x = func.append_param(block, Type::int(32));
2178            let mut build = Builder::new(&mut func, block);
2179            let seven = build.iconst(Type::int(32), 7);
2180            let flag = build.icmp(IntPred::Eq, x, seven);
2181            let wide = build.unary(Opcode::ZExt, flag, ty);
2182            let zero = build.iconst(ty, 0);
2183            let test = build.icmp(IntPred::Eq, wide, zero);
2184            build.ret(&[test]);
2185            assert!(simplify(&mut func), "i{width} was left alone");
2186            let got = returned(&func, block);
2187            assert_eq!(came_from(&func, got).0, Opcode::Xor, "i{width} is not a negation");
2188            assert!(simplify(&mut func), "i{width} kept the exclusive or");
2189            assert_eq!(
2190                came_from(&func, got),
2191                (Opcode::ICmp, Extra::IntPred(IntPred::Ne)),
2192                "i{width} did not come out as the opposite comparison"
2193            );
2194            let args = operands(&func, got);
2195            assert_eq!(args[0], x, "i{width} lost its value");
2196            assert_eq!(number(&func, args[1]), 7, "i{width} lost its bound");
2197        }
2198    }
2199
2200    /// Every commutative operator tier three writes moves its constant to the right.
2201    ///
2202    /// One test over the five rather than five tests, because what is being checked is the same
2203    /// thing five times and the operator is the only part that differs.
2204    #[test]
2205    fn a_constant_on_the_left_of_a_commutative_operation_moves_to_the_right() {
2206        for opcode in [Opcode::Add, Opcode::Mul, Opcode::And, Opcode::Or, Opcode::Xor] {
2207            for width in [8, 16, 32, 64] {
2208                let ty = Type::int(width);
2209                let (_, mut func, block) = one_block(ty);
2210                let x = func.append_param(block, ty);
2211                let mut build = Builder::new(&mut func, block);
2212                // Three, because it is a number no identity in tier one is about and no strength
2213                // reduction in tier two is about, so the only rule that can fire is the one this
2214                // test is here for.
2215                let three = build.iconst(ty, 3);
2216                let value = build.binary(opcode, three, x, Flags::NONE);
2217                build.ret(&[value]);
2218                assert!(simplify(&mut func), "{opcode:?} at i{width} was left alone");
2219                let args = operands(&func, returned(&func, block));
2220                assert_eq!(came_from(&func, returned(&func, block)).0, opcode);
2221                assert_eq!(args[0], x, "{opcode:?} at i{width} kept the value on the right");
2222                assert_eq!(number(&func, args[1]), 3, "{opcode:?} at i{width} lost its constant");
2223            }
2224        }
2225    }
2226
2227    /// And an operation whose operands are both constants is left where it is.
2228    ///
2229    /// This is the termination argument, run rather than read. Without the plan that refuses a
2230    /// constant on the right, the rule above would match this, swap the two, match the swapped
2231    /// form, and go on doing it until the fuel ran out. Folding is what this instruction is for
2232    /// and `crate::fold` is where it happens.
2233    #[test]
2234    fn an_operation_on_two_constants_is_not_swapped_back_and_forth() {
2235        let i32 = Type::int(32);
2236        let (_, mut func, block) = one_block(i32);
2237        let mut build = Builder::new(&mut func, block);
2238        let three = build.iconst(i32, 3);
2239        let five = build.iconst(i32, 5);
2240        let sum = build.binary(Opcode::Add, three, five, Flags::NONE);
2241        build.ret(&[sum]);
2242        assert!(!simplify(&mut func), "the constants were rearranged rather than left to folding");
2243        let args = operands(&func, returned(&func, block));
2244        assert_eq!(number(&func, args[0]), 3);
2245        assert_eq!(number(&func, args[1]), 5);
2246    }
2247
2248    /// A constant already on the right stays there and nothing fires.
2249    ///
2250    /// The other half of the same argument. A canonicalisation that fired on the shape it produces
2251    /// would be a canonicalisation with no direction, which is what section 13.5 refuses.
2252    #[test]
2253    fn a_constant_already_on_the_right_is_left_alone() {
2254        let i32 = Type::int(32);
2255        let (_, mut func, block) = one_block(i32);
2256        let x = func.append_param(block, i32);
2257        let mut build = Builder::new(&mut func, block);
2258        let three = build.iconst(i32, 3);
2259        let sum = build.binary(Opcode::Add, x, three, Flags::NONE);
2260        build.ret(&[sum]);
2261        assert!(!simplify(&mut func));
2262        let args = operands(&func, returned(&func, block));
2263        assert_eq!(args[0], x);
2264        assert_eq!(number(&func, args[1]), 3);
2265    }
2266
2267    /// A subtraction is not commutative and nothing moves its constant.
2268    ///
2269    /// Turning `c - x` into anything is not what tier three does, and the rules are written per
2270    /// opcode rather than over a set of them, so this is asking whether the wrong opcode found its
2271    /// way into the file.
2272    #[test]
2273    fn a_subtraction_keeps_its_operands_where_they_are() {
2274        let i32 = Type::int(32);
2275        let (_, mut func, block) = one_block(i32);
2276        let x = func.append_param(block, i32);
2277        let mut build = Builder::new(&mut func, block);
2278        let three = build.iconst(i32, 3);
2279        let difference = build.binary(Opcode::Sub, three, x, Flags::NONE);
2280        build.ret(&[difference]);
2281        assert!(!simplify(&mut func));
2282        let args = operands(&func, returned(&func, block));
2283        assert_eq!(number(&func, args[0]), 3);
2284        assert_eq!(args[1], x);
2285    }
2286
2287    /// A block whose parameter and whose result are different widths, which is what every width
2288    /// rule needs and what `one_block` cannot give.
2289    fn narrow_to_wide(takes: Type, gives: Type) -> (Interner, Func, Block) {
2290        let mut names = Interner::new();
2291        let name = names.intern("f");
2292        let signature = Signature::new().with_params(&[takes]).with_returns(&[gives]);
2293        let mut func = Func::new(name, signature);
2294        let block = func.create_block();
2295        (names, func, block)
2296    }
2297
2298    /// A conversion of a conversion of a parameter, which is the shape every width rule matches.
2299    ///
2300    /// The parameter is at `from`, the inner conversion takes it to `through` and the outer one
2301    /// takes that to `to`, and what comes back is the function, the block and the parameter.
2302    fn chain(
2303        inner: Opcode,
2304        outer: Opcode,
2305        from: Type,
2306        through: Type,
2307        to: Type,
2308    ) -> (Func, Block, Value) {
2309        let (_, mut func, block) = narrow_to_wide(from, to);
2310        let x = func.append_param(block, from);
2311        let mut build = Builder::new(&mut func, block);
2312        let middle = build.unary(inner, x, through);
2313        let outside = build.unary(outer, middle, to);
2314        build.ret(&[outside]);
2315        (func, block, x)
2316    }
2317
2318    /// Truncating an extension back to the width it came from is the value that was there.
2319    ///
2320    /// Every pair of widths and both extensions, because the rule file writes all twelve and a
2321    /// test of one of them would say nothing about the other eleven.
2322    #[test]
2323    fn truncating_an_extension_back_to_its_own_width_gives_the_value_back() {
2324        for extend in [Opcode::SExt, Opcode::ZExt] {
2325            for (narrow, wide) in [(8, 16), (8, 32), (8, 64), (16, 32), (16, 64), (32, 64)] {
2326                let (from, through) = (Type::int(narrow), Type::int(wide));
2327                let (mut func, block, x) = chain(extend, Opcode::Trunc, from, through, from);
2328                assert!(simplify(&mut func), "{extend:?} i{narrow} to i{wide} was left alone");
2329                assert_eq!(
2330                    returned(&func, block),
2331                    x,
2332                    "{extend:?} i{narrow} to i{wide} and back did not give the value back"
2333                );
2334            }
2335        }
2336    }
2337
2338    /// Truncating an extension to a width still above the source is the same extension, stopping
2339    /// earlier.
2340    #[test]
2341    fn truncating_an_extension_above_its_source_is_a_shorter_extension() {
2342        let (mut func, block, x) =
2343            chain(Opcode::SExt, Opcode::Trunc, Type::int(8), Type::int(64), Type::int(16));
2344        assert!(simplify(&mut func));
2345        let result = returned(&func, block);
2346        assert_eq!(came_from(&func, result).0, Opcode::SExt);
2347        assert_eq!(operands(&func, result), vec![x]);
2348        assert_eq!(func[result].ty, Type::int(16));
2349    }
2350
2351    /// Truncating an extension to a width below the source is a truncation of the source, and
2352    /// which extension it was never mattered.
2353    #[test]
2354    fn truncating_an_extension_below_its_source_is_a_truncation_of_the_source() {
2355        let (mut func, block, x) =
2356            chain(Opcode::ZExt, Opcode::Trunc, Type::int(16), Type::int(32), Type::int(8));
2357        assert!(simplify(&mut func));
2358        let result = returned(&func, block);
2359        assert_eq!(came_from(&func, result).0, Opcode::Trunc);
2360        assert_eq!(operands(&func, result), vec![x]);
2361        assert_eq!(func[result].ty, Type::int(8));
2362    }
2363
2364    /// An extension of an extension is one extension, and a sign extension of a zero extension is
2365    /// a zero extension rather than a sign extension.
2366    #[test]
2367    fn an_extension_of_an_extension_is_one_extension() {
2368        for (inner, outer, want) in [
2369            (Opcode::ZExt, Opcode::ZExt, Opcode::ZExt),
2370            (Opcode::SExt, Opcode::SExt, Opcode::SExt),
2371            (Opcode::ZExt, Opcode::SExt, Opcode::ZExt),
2372        ] {
2373            let (mut func, block, x) =
2374                chain(inner, outer, Type::int(8), Type::int(16), Type::int(64));
2375            assert!(simplify(&mut func), "{outer:?} of {inner:?} was left alone");
2376            let result = returned(&func, block);
2377            assert_eq!(came_from(&func, result).0, want, "{outer:?} of {inner:?}");
2378            assert_eq!(operands(&func, result), vec![x]);
2379            assert_eq!(func[result].ty, Type::int(64));
2380        }
2381    }
2382
2383    /// A truncation of a truncation is one truncation, straight to the width the outer one asked
2384    /// for.
2385    ///
2386    /// The inner one threw away bits the outer one was going to throw away as well, so the width
2387    /// in the middle was never read and the rule goes to the outer width from the source. Both
2388    /// orderings of the three widths are tried, because a rule that picked the middle width rather
2389    /// than the outer one would still pass a test that only went from sixty four to eight through
2390    /// thirty two.
2391    #[test]
2392    fn a_truncation_of_a_truncation_is_one_truncation() {
2393        for (from, through, to) in [(64u32, 32u32, 16u32), (64, 32, 8), (64, 16, 8), (32, 16, 8)] {
2394            let (mut func, block, x) = chain(
2395                Opcode::Trunc,
2396                Opcode::Trunc,
2397                Type::int(from),
2398                Type::int(through),
2399                Type::int(to),
2400            );
2401            assert!(simplify(&mut func), "i{from} to i{through} to i{to} was left alone");
2402            let result = returned(&func, block);
2403            assert_eq!(came_from(&func, result).0, Opcode::Trunc, "i{from} to i{through} to i{to}");
2404            assert_eq!(operands(&func, result), vec![x]);
2405            assert_eq!(func[result].ty, Type::int(to));
2406        }
2407    }
2408
2409    /// And zero extending a sign extension is not one, because the bits the sign extension copied
2410    /// are bits of the value now and nothing above them is a function of the source alone.
2411    #[test]
2412    fn zero_extending_a_sign_extension_is_left_alone() {
2413        let (mut func, _, _) =
2414            chain(Opcode::SExt, Opcode::ZExt, Type::int(8), Type::int(16), Type::int(64));
2415        assert!(!simplify(&mut func), "a zero extension of a sign extension was rewritten");
2416    }
2417
2418    /// And zero extending a truncation is left alone, which is the rule the tier would be expected
2419    /// to have and does not.
2420    ///
2421    /// It was written and proved and then measured, and the measurement is why it went: the
2422    /// machine has one instruction for the pair already, the `and` with an immediate that replaced
2423    /// it is the longer encoding of the two, and the mask hides the narrowing from
2424    /// [`crate::narrow`]. The rule file says the whole of it. This is here so that somebody adding
2425    /// it back finds a test rather than a silence.
2426    #[test]
2427    fn zero_extending_a_truncation_is_left_alone() {
2428        let (mut func, _, _) =
2429            chain(Opcode::Trunc, Opcode::ZExt, Type::int(64), Type::int(32), Type::int(64));
2430        assert!(!simplify(&mut func), "a zero extension of a truncation became a mask");
2431    }
2432
2433    /// A width rule needs an operand something computed, and a parameter is not one.
2434    ///
2435    /// This is what the plan being an expanding one means at the bottom: there is no instruction
2436    /// under the operand to be the second level of the pattern, so nothing matches and nothing is
2437    /// rewritten. Said out loud because it is the case that would otherwise be a crash rather than
2438    /// a miss.
2439    #[test]
2440    fn a_width_rule_needs_an_operand_an_instruction_computed() {
2441        let (_, mut func, block) = narrow_to_wide(Type::int(64), Type::int(32));
2442        let x = func.append_param(block, Type::int(64));
2443        let mut build = Builder::new(&mut func, block);
2444        let narrowed = build.unary(Opcode::Trunc, x, Type::int(32));
2445        build.ret(&[narrowed]);
2446        assert!(!simplify(&mut func), "a truncation of a parameter was rewritten");
2447    }
2448
2449    #[test]
2450    fn adding_nothing_points_every_reader_at_the_operand() {
2451        let i32 = Type::int(32);
2452        let (_, mut func, block) = one_block(i32);
2453        let x = func.append_param(block, i32);
2454        let mut build = Builder::new(&mut func, block);
2455        let zero = build.iconst(i32, 0);
2456        let sum = build.binary(Opcode::Add, x, zero, Flags::NONE);
2457        build.ret(&[sum]);
2458        assert!(simplify(&mut func));
2459        // The `add` is still there, used by nothing, which is what dead code elimination is for.
2460        assert_eq!(returned(&func, block), x);
2461        assert_eq!(came_from(&func, sum).0, Opcode::Add);
2462    }
2463
2464    /// The constant on either side, since nothing puts it on the right yet and a rule written one
2465    /// way round would fire on half the additions it should.
2466    #[test]
2467    fn the_constant_is_found_on_either_side_of_an_identity() {
2468        for swapped in [false, true] {
2469            let i32 = Type::int(32);
2470            let (_, mut func, block) = one_block(i32);
2471            let x = func.append_param(block, i32);
2472            let mut build = Builder::new(&mut func, block);
2473            let zero = build.iconst(i32, 0);
2474            let (lhs, rhs) = if swapped { (zero, x) } else { (x, zero) };
2475            let sum = build.binary(Opcode::Add, lhs, rhs, Flags::NONE);
2476            build.ret(&[sum]);
2477            assert!(simplify(&mut func), "swapped {swapped}");
2478            assert_eq!(returned(&func, block), x, "swapped {swapped}");
2479        }
2480    }
2481
2482    #[test]
2483    fn multiplying_by_nothing_becomes_the_constant_where_it_stands() {
2484        let i32 = Type::int(32);
2485        let (_, mut func, block) = one_block(i32);
2486        let x = func.append_param(block, i32);
2487        let mut build = Builder::new(&mut func, block);
2488        let zero = build.iconst(i32, 0);
2489        let product = build.binary(Opcode::Mul, x, zero, Flags::NONE);
2490        build.ret(&[product]);
2491        assert!(simplify(&mut func));
2492        // The result value survives, which is the whole reason this half rewrites in place.
2493        assert_eq!(returned(&func, block), product);
2494        assert_eq!(came_from(&func, product).0, Opcode::IConst);
2495        assert_eq!(number(&func, product), 0);
2496    }
2497
2498    /// The two identities a pattern that writes one name twice exists for, at every width they
2499    /// are written at.
2500    #[test]
2501    fn a_value_against_itself() {
2502        for bits in [8, 16, 32, 64] {
2503            let ty = Type::int(bits);
2504            let (_, mut func, block) = one_block(ty);
2505            let x = func.append_param(block, ty);
2506            let mut build = Builder::new(&mut func, block);
2507            let both = build.binary(Opcode::And, x, x, Flags::NONE);
2508            build.ret(&[both]);
2509            assert!(simplify(&mut func), "{bits} bits");
2510            assert_eq!(returned(&func, block), x, "{bits} bits");
2511
2512            let (_, mut func, block) = one_block(ty);
2513            let x = func.append_param(block, ty);
2514            let mut build = Builder::new(&mut func, block);
2515            let nothing = build.binary(Opcode::Sub, x, x, Flags::NONE);
2516            build.ret(&[nothing]);
2517            assert!(simplify(&mut func), "{bits} bits");
2518            assert_eq!(number(&func, nothing), 0, "{bits} bits");
2519        }
2520    }
2521
2522    /// Every predicate with one name in both operands, at every width the rules are written at.
2523    /// Six of the ten are true and four are false, and not one of them had to look at what the
2524    /// operand holds.
2525    #[test]
2526    fn every_comparison_of_a_value_with_itself_is_decided() {
2527        for bits in [8, 16, 32, 64] {
2528            for pred in IntPred::all() {
2529                let mut names = Interner::new();
2530                let name = names.intern("f");
2531                let int = Type::int(bits);
2532                let signature = Signature::new().with_params(&[int]).with_returns(&[Type::int(1)]);
2533                let mut func = Func::new(name, signature);
2534                let block = func.create_block();
2535                let x = func.append_param(block, int);
2536                let mut build = Builder::new(&mut func, block);
2537                let answer = build.icmp(pred, x, x);
2538                build.ret(&[answer]);
2539                assert!(simplify(&mut func), "{pred:?} at {bits} bits");
2540                let said = number(&func, answer);
2541                if matches!(
2542                    pred,
2543                    IntPred::Ne | IntPred::Slt | IntPred::Sgt | IntPred::Ult | IntPred::Ugt
2544                ) {
2545                    assert_eq!(said, 0, "{pred:?} at {bits} bits");
2546                } else {
2547                    assert_ne!(said, 0, "{pred:?} at {bits} bits");
2548                }
2549            }
2550        }
2551    }
2552
2553    /// A remainder by one is nothing, and a division by one is the value. The pair is worth a
2554    /// test of its own because they are the two identities that produce different shapes from the
2555    /// same operands.
2556    #[test]
2557    fn dividing_by_one_and_the_remainder_that_goes_with_it() {
2558        let i32 = Type::int(32);
2559        let (_, mut func, block) = one_block(i32);
2560        let x = func.append_param(block, i32);
2561        let mut build = Builder::new(&mut func, block);
2562        let one = build.iconst(i32, 1);
2563        let quotient = build.binary(Opcode::SDiv, x, one, Flags::NONE);
2564        let rest = build.binary(Opcode::SRem, x, one, Flags::NONE);
2565        let sum = build.binary(Opcode::Add, quotient, rest, Flags::NONE);
2566        build.ret(&[sum]);
2567        assert!(simplify(&mut func));
2568        assert_eq!(number(&func, rest), 0);
2569        // The add reads the value the division was of, which is what the redirection did.
2570        let rucc_ir::Def::Result { inst, .. } = func[sum].def else { panic!("not a result") };
2571        assert_eq!(func[func[inst].args][0], x);
2572    }
2573
2574    /// All ones at one bit is the `1` the rule file writes, and the front end writes it as `-1`.
2575    /// The two are the same bit and the rule has to fire on what the front end wrote.
2576    #[test]
2577    fn all_ones_at_one_bit_is_the_one_the_front_end_writes() {
2578        for written in [-1, 1] {
2579            let bit = Type::int(1);
2580            let (_, mut func, block) = one_block(bit);
2581            let x = func.append_param(block, bit);
2582            let mut build = Builder::new(&mut func, block);
2583            let ones = build.iconst(bit, written);
2584            let kept = build.binary(Opcode::And, x, ones, Flags::NONE);
2585            build.ret(&[kept]);
2586            assert!(simplify(&mut func), "written as {written}");
2587            assert_eq!(returned(&func, block), x, "written as {written}");
2588        }
2589    }
2590
2591    /// One identity feeding another is followed all the way, so the second is worth as much as
2592    /// the first. The redirections are applied once at the end of the run, and this is what says
2593    /// that costs nothing.
2594    #[test]
2595    fn one_identity_feeding_another_is_followed_to_the_end() {
2596        let i32 = Type::int(32);
2597        let (_, mut func, block) = one_block(i32);
2598        let x = func.append_param(block, i32);
2599        let mut build = Builder::new(&mut func, block);
2600        let zero = build.iconst(i32, 0);
2601        let one = build.iconst(i32, 1);
2602        let sum = build.binary(Opcode::Add, x, zero, Flags::NONE);
2603        let product = build.binary(Opcode::Mul, sum, one, Flags::NONE);
2604        let shifted = build.binary(Opcode::Shl, product, zero, Flags::NONE);
2605        build.ret(&[shifted]);
2606        assert!(simplify(&mut func));
2607        assert_eq!(returned(&func, block), x);
2608    }
2609
2610    /// Shifting nothing in any direction, and all ones to the right with the sign bit coming in.
2611    /// The count is a parameter here, so nothing at all is known about it and the identity is the
2612    /// only thing that could decide these.
2613    #[test]
2614    fn shifting_nothing_and_shifting_all_ones_with_the_sign() {
2615        for bits in [8, 16, 32, 64] {
2616            let ty = Type::int(bits);
2617            let cases = [
2618                (Opcode::Shl, 0_i128, 0_i128),
2619                (Opcode::LShr, 0, 0),
2620                (Opcode::AShr, 0, 0),
2621                (Opcode::AShr, -1, -1),
2622            ];
2623            for (opcode, from, expected) in cases {
2624                let (_, mut func, block) = one_block(ty);
2625                let count = func.append_param(block, ty);
2626                let mut build = Builder::new(&mut func, block);
2627                let value = build.iconst(ty, from);
2628                let shifted = build.binary(opcode, value, count, Flags::NONE);
2629                build.ret(&[shifted]);
2630                assert!(simplify(&mut func), "{opcode:?} of {from} at {bits} bits");
2631                let said = number(&func, shifted);
2632                assert_eq!(said, expected, "{opcode:?} of {from} at {bits} bits");
2633            }
2634        }
2635    }
2636
2637    /// All ones shifted right with zeroes coming in is not all ones, and there is no rule saying
2638    /// it is. The pair with the arithmetic shift above is the whole of why the sign matters here.
2639    #[test]
2640    fn all_ones_shifted_right_with_zeroes_coming_in_is_left_alone() {
2641        let i32 = Type::int(32);
2642        let (_, mut func, block) = one_block(i32);
2643        let count = func.append_param(block, i32);
2644        let mut build = Builder::new(&mut func, block);
2645        let ones = build.iconst(i32, -1);
2646        let shifted = build.binary(Opcode::LShr, ones, count, Flags::NONE);
2647        build.ret(&[shifted]);
2648        assert!(!simplify(&mut func));
2649        assert_eq!(came_from(&func, shifted).0, Opcode::LShr);
2650    }
2651
2652    #[test]
2653    fn an_instruction_no_rule_is_about_is_left_alone() {
2654        // Multiplying by three. Two is tier two and is an addition, one and zero are tier one, and
2655        // every power of two is a shift, so three is the smallest constant no tier has anything to
2656        // say about. Turning it into a shift and an add is a sequence rather than a rewrite.
2657        let i32 = Type::int(32);
2658        let (_, mut func, block) = one_block(i32);
2659        let x = func.append_param(block, i32);
2660        let mut build = Builder::new(&mut func, block);
2661        let three = build.iconst(i32, 3);
2662        let tripled = build.binary(Opcode::Mul, x, three, Flags::NONE);
2663        build.ret(&[tripled]);
2664        assert!(!simplify(&mut func), "no rule is about multiplying by three");
2665        assert_eq!(returned(&func, block), tripled);
2666        assert_eq!(came_from(&func, tripled).0, Opcode::Mul);
2667    }
2668
2669    #[test]
2670    fn multiplying_by_two_becomes_an_addition_of_the_value_with_itself() {
2671        let i32 = Type::int(32);
2672        let (_, mut func, block) = one_block(i32);
2673        let x = func.append_param(block, i32);
2674        let mut build = Builder::new(&mut func, block);
2675        let two = build.iconst(i32, 2);
2676        let doubled = build.binary(Opcode::Mul, x, two, Flags::NONE);
2677        build.ret(&[doubled]);
2678        assert!(simplify(&mut func));
2679        // In place, so the value the return reads is the one it always read.
2680        assert_eq!(returned(&func, block), doubled);
2681        assert_eq!(came_from(&func, doubled).0, Opcode::Add);
2682        assert_eq!(operands(&func, doubled), [x, x]);
2683        // And it stays an addition although two is a power of two and the rule below would take
2684        // it. A rule naming its constant is more specific than a rule taking whatever constant is
2685        // there, so the trie tries it first without anything having to sort the two.
2686    }
2687
2688    #[test]
2689    fn multiplying_by_a_power_of_two_becomes_a_shift_by_the_count_of_its_zeros() {
2690        let i32 = Type::int(32);
2691        let (_, mut func, block) = one_block(i32);
2692        let x = func.append_param(block, i32);
2693        let mut build = Builder::new(&mut func, block);
2694        let eight = build.iconst(i32, 8);
2695        let scaled = build.binary(Opcode::Mul, x, eight, Flags::NONE);
2696        build.ret(&[scaled]);
2697        assert!(simplify(&mut func));
2698        assert_eq!(returned(&func, block), scaled);
2699        assert_eq!(came_from(&func, scaled).0, Opcode::Shl);
2700        let args = operands(&func, scaled);
2701        assert_eq!(args[0], x);
2702        assert_eq!(number(&func, args[1]), 3);
2703    }
2704
2705    #[test]
2706    fn the_power_of_two_with_the_sign_bit_set_is_one_of_them() {
2707        // The constant the compiler and the solver would disagree about if either read it at some
2708        // width other than the rule's. At 32 bits this is a power of two and shifts by 31, and in
2709        // the 128 bit integer the pass matches constants into it is a negative number, so a guard
2710        // that forgot to mask would call it no power of two at all.
2711        let i32 = Type::int(32);
2712        let (_, mut func, block) = one_block(i32);
2713        let x = func.append_param(block, i32);
2714        let mut build = Builder::new(&mut func, block);
2715        let top = build.iconst(i32, 0x8000_0000);
2716        let scaled = build.binary(Opcode::Mul, x, top, Flags::NONE);
2717        build.ret(&[scaled]);
2718        assert!(simplify(&mut func));
2719        assert_eq!(came_from(&func, scaled).0, Opcode::Shl);
2720        assert_eq!(number(&func, operands(&func, scaled)[1]), 31);
2721    }
2722
2723    #[test]
2724    fn dividing_an_unsigned_value_by_a_power_of_two_becomes_a_shift() {
2725        let i32 = Type::int(32);
2726        let (_, mut func, block) = one_block(i32);
2727        let x = func.append_param(block, i32);
2728        let mut build = Builder::new(&mut func, block);
2729        let sixteen = build.iconst(i32, 16);
2730        let quotient = build.binary(Opcode::UDiv, x, sixteen, Flags::NONE);
2731        build.ret(&[quotient]);
2732        assert!(simplify(&mut func));
2733        assert_eq!(came_from(&func, quotient).0, Opcode::LShr);
2734        let args = operands(&func, quotient);
2735        assert_eq!(args[0], x);
2736        assert_eq!(number(&func, args[1]), 4);
2737    }
2738
2739    #[test]
2740    fn dividing_a_signed_value_by_a_power_of_two_is_left_alone() {
2741        // Deliberately, and this says so rather than leaving it to be read as an oversight. A
2742        // signed division rounds towards zero and a shift rounds down, so the two agree only on
2743        // values that are not negative. Correcting for that is a bias added before the shift,
2744        // which is a sequence of instructions rather than one term in place of another.
2745        let i32 = Type::int(32);
2746        let (_, mut func, block) = one_block(i32);
2747        let x = func.append_param(block, i32);
2748        let mut build = Builder::new(&mut func, block);
2749        let sixteen = build.iconst(i32, 16);
2750        let quotient = build.binary(Opcode::SDiv, x, sixteen, Flags::NONE);
2751        build.ret(&[quotient]);
2752        assert!(!simplify(&mut func), "no rule turns a signed division into a shift");
2753        assert_eq!(came_from(&func, quotient).0, Opcode::SDiv);
2754    }
2755
2756    #[test]
2757    fn the_unsigned_remainder_of_a_power_of_two_becomes_a_mask() {
2758        let i32 = Type::int(32);
2759        let (_, mut func, block) = one_block(i32);
2760        let x = func.append_param(block, i32);
2761        let mut build = Builder::new(&mut func, block);
2762        let thirty_two = build.iconst(i32, 32);
2763        let rest = build.binary(Opcode::URem, x, thirty_two, Flags::NONE);
2764        build.ret(&[rest]);
2765        assert!(simplify(&mut func));
2766        assert_eq!(came_from(&func, rest).0, Opcode::And);
2767        let args = operands(&func, rest);
2768        assert_eq!(args[0], x);
2769        assert_eq!(number(&func, args[1]), 31);
2770    }
2771
2772    #[test]
2773    fn a_division_by_a_constant_that_is_not_a_power_of_two_is_left_alone() {
2774        let i32 = Type::int(32);
2775        let (_, mut func, block) = one_block(i32);
2776        let x = func.append_param(block, i32);
2777        let mut build = Builder::new(&mut func, block);
2778        let ten = build.iconst(i32, 10);
2779        let quotient = build.binary(Opcode::UDiv, x, ten, Flags::NONE);
2780        build.ret(&[quotient]);
2781        assert!(!simplify(&mut func), "ten is no power of two");
2782        assert_eq!(came_from(&func, quotient).0, Opcode::UDiv);
2783    }
2784
2785    #[test]
2786    fn multiplying_by_minus_one_becomes_a_subtraction_from_a_zero_the_rewrite_defines() {
2787        // The other shape of operand: nothing in the function holds a zero, so the rewrite has to
2788        // put one in front of the instruction it is rewriting.
2789        let i32 = Type::int(32);
2790        let (_, mut func, block) = one_block(i32);
2791        let x = func.append_param(block, i32);
2792        let mut build = Builder::new(&mut func, block);
2793        let minus = build.iconst(i32, -1);
2794        let negated = build.binary(Opcode::Mul, x, minus, Flags::NONE);
2795        build.ret(&[negated]);
2796        assert!(simplify(&mut func));
2797        assert_eq!(returned(&func, block), negated);
2798        assert_eq!(came_from(&func, negated).0, Opcode::Sub);
2799        let args = operands(&func, negated);
2800        assert_eq!(number(&func, args[0]), 0);
2801        assert_eq!(args[1], x);
2802    }
2803
2804    #[test]
2805    fn a_strength_reduction_keeps_a_promise_only_where_it_is_the_same_promise() {
2806        // An `nsw` on a multiplication is a promise about that multiplication, and carrying one
2807        // across a rewrite because it probably still holds is how a wrong one gets made. These are
2808        // the rewrites where it provably holds, and the two places next to them where it does not:
2809        // `nuw` on a multiplication by `-1` is about the largest unsigned number, and a shift by
2810        // thirty one is a multiplication by the most negative `int`.
2811        let i32 = Type::int(32);
2812        let both = Flags::NSW.union(Flags::NUW);
2813        for (by, left, flags, opcode, kept) in [
2814            (2, false, both, Opcode::Add, both),
2815            (-1, false, both, Opcode::Sub, Flags::NSW),
2816            (128, false, Flags::NSW, Opcode::Shl, Flags::NSW),
2817            (128, false, both, Opcode::Shl, both),
2818            (128, true, Flags::NSW, Opcode::Shl, Flags::NSW),
2819            (128, false, Flags::NONE, Opcode::Shl, Flags::NONE),
2820            (i128::from(i32::MIN), false, Flags::NSW, Opcode::Shl, Flags::NONE),
2821        ] {
2822            let (_, mut func, block) = one_block(i32);
2823            let x = func.append_param(block, i32);
2824            let mut build = Builder::new(&mut func, block);
2825            let k = build.iconst(i32, by);
2826            let (lhs, rhs) = if left { (k, x) } else { (x, k) };
2827            let product = build.binary(Opcode::Mul, lhs, rhs, flags);
2828            build.ret(&[product]);
2829            assert!(simplify(&mut func));
2830            let rucc_ir::Def::Result { inst, .. } = func[product].def else {
2831                panic!("not a result")
2832            };
2833            assert_eq!(func[inst].opcode, opcode, "{by}");
2834            assert_eq!(func[inst].flags, kept, "{by}, {flags:?}, constant on the left {left}");
2835        }
2836    }
2837
2838    #[test]
2839    fn a_strength_reduction_leaves_the_verifier_nothing_to_complain_about() {
2840        // The zero the negation needs is defined in front of the instruction that reads it, and
2841        // whether it really is in front of it is a question about the block rather than about the
2842        // instruction, which is what the verifier is for.
2843        let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
2844        let i32 = Type::int(32);
2845        let (mut names, mut func, block) = one_block(i32);
2846        let mut module = Module::new(names.intern("test.c"), &target);
2847        let x = func.append_param(block, i32);
2848        let mut build = Builder::new(&mut func, block);
2849        let minus = build.iconst(i32, -1);
2850        let negated = build.binary(Opcode::Mul, x, minus, Flags::NONE);
2851        let two = build.iconst(i32, 2);
2852        let doubled = build.binary(Opcode::Mul, negated, two, Flags::NONE);
2853        build.ret(&[doubled]);
2854        assert!(simplify(&mut func));
2855        module.add_func(func);
2856        rucc_ir::verify(&module, &names).expect("the pass left the function verifiable");
2857    }
2858
2859    /// The function the pass leaves is still one the verifier accepts. Pointing a reader at a
2860    /// different value and turning an instruction into a constant are both things a rewrite could
2861    /// get wrong in a way none of the tests above would notice, because each of those asks about
2862    /// one instruction and this asks about the function.
2863    #[test]
2864    fn the_pass_leaves_the_verifier_nothing_to_complain_about() {
2865        let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
2866        let i32 = Type::int(32);
2867        let (mut names, mut func, block) = one_block(i32);
2868        let mut module = Module::new(names.intern("test.c"), &target);
2869        let x = func.append_param(block, i32);
2870        let mut build = Builder::new(&mut func, block);
2871        let zero = build.iconst(i32, 0);
2872        let one = build.iconst(i32, 1);
2873        let sum = build.binary(Opcode::Add, x, zero, Flags::NONE);
2874        let product = build.binary(Opcode::Mul, sum, one, Flags::NONE);
2875        let gone = build.binary(Opcode::Sub, product, product, Flags::NONE);
2876        let total = build.binary(Opcode::Add, product, gone, Flags::NONE);
2877        build.ret(&[total]);
2878        assert!(simplify(&mut func));
2879        module.add_func(func);
2880        rucc_ir::verify(&module, &names).expect("the pass left the function verifiable");
2881    }
2882
2883    #[test]
2884    fn fuel_stops_an_identity_and_not_the_walk() {
2885        let i32 = Type::int(32);
2886        let (_, mut func, block) = one_block(i32);
2887        let x = func.append_param(block, i32);
2888        let mut build = Builder::new(&mut func, block);
2889        let zero = build.iconst(i32, 0);
2890        let first = build.binary(Opcode::Add, x, zero, Flags::NONE);
2891        let second = build.binary(Opcode::Sub, x, zero, Flags::NONE);
2892        let sum = build.binary(Opcode::Add, first, second, Flags::NONE);
2893        build.ret(&[sum]);
2894        let stats =
2895            Simplify.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(1));
2896        assert!(stats.changed());
2897        assert_eq!(stats.total(Kind::Optimized), 1);
2898        assert_eq!(stats.count(Kind::Missed, super::NO_FUEL_RULE), 1);
2899        // The first fired and the second did not, and the second is still read by the add.
2900        let rucc_ir::Def::Result { inst, .. } = func[sum].def else { panic!("not a result") };
2901        assert_eq!(func[func[inst].args], [x, second]);
2902    }
2903
2904    #[test]
2905    fn a_negated_float_comparison_becomes_the_opposite_predicate() {
2906        // Every ordered predicate and its opposite, which is the table `!(x < y)` is `x >= y`
2907        // or unordered lives in, and the one place a sign error would hide.
2908        for pred in FloatPred::all() {
2909            let (_, mut func, block) = blank();
2910            let mut build = Builder::new(&mut func, block);
2911            let x = build.iconst(Type::int(64), 0);
2912            let x = build.unary(Opcode::Bitcast, x, Type::float(Float::F64));
2913            // Not `x` against itself, which is equal or unordered and settles most predicates on its own.
2914            let y = build.iconst(Type::int(64), 1);
2915            let y = build.unary(Opcode::Bitcast, y, Type::float(Float::F64));
2916            let cmp = build.fcmp(pred, x, y, Flags::NONE);
2917            let ones = build.iconst(Type::int(1), -1);
2918            let not = build.binary(Opcode::Xor, cmp, ones, Flags::NONE);
2919            build.ret(&[not]);
2920            assert!(simplify(&mut func), "{pred:?}");
2921            assert_eq!(
2922                came_from(&func, not),
2923                (Opcode::FCmp, Extra::FloatPred(pred.inverse())),
2924                "{pred:?}"
2925            );
2926        }
2927    }
2928
2929    #[test]
2930    fn a_negated_integer_comparison_becomes_the_opposite_predicate() {
2931        for pred in IntPred::all() {
2932            let (_, mut func, block) = blank();
2933            let mut build = Builder::new(&mut func, block);
2934            let x = build.iconst(Type::int(32), 3);
2935            let y = build.iconst(Type::int(32), 4);
2936            let cmp = build.icmp(pred, x, y);
2937            let ones = build.iconst(Type::int(1), -1);
2938            let not = build.binary(Opcode::Xor, cmp, ones, Flags::NONE);
2939            build.ret(&[not]);
2940            assert!(simplify(&mut func), "{pred:?}");
2941            assert_eq!(
2942                came_from(&func, not),
2943                (Opcode::ICmp, Extra::IntPred(pred.inverse())),
2944                "{pred:?}"
2945            );
2946        }
2947    }
2948
2949    #[test]
2950    fn the_constant_is_found_on_either_side() {
2951        for swapped in [false, true] {
2952            let (_, mut func, block) = blank();
2953            let mut build = Builder::new(&mut func, block);
2954            let x = build.iconst(Type::int(32), 3);
2955            let y = build.iconst(Type::int(32), 4);
2956            let cmp = build.icmp(IntPred::Slt, x, y);
2957            let ones = build.iconst(Type::int(1), -1);
2958            let (lhs, rhs) = if swapped { (ones, cmp) } else { (cmp, ones) };
2959            let not = build.binary(Opcode::Xor, lhs, rhs, Flags::NONE);
2960            build.ret(&[not]);
2961            assert!(simplify(&mut func), "swapped {swapped}");
2962            assert_eq!(came_from(&func, not).1, Extra::IntPred(IntPred::Sge));
2963        }
2964    }
2965
2966    #[test]
2967    fn an_exclusive_or_of_two_comparisons_is_left_alone() {
2968        let (_, mut func, block) = blank();
2969        let mut build = Builder::new(&mut func, block);
2970        let x = build.iconst(Type::int(32), 3);
2971        let y = build.iconst(Type::int(32), 4);
2972        let a = build.icmp(IntPred::Slt, x, y);
2973        let b = build.icmp(IntPred::Sgt, x, y);
2974        let differ = build.binary(Opcode::Xor, a, b, Flags::NONE);
2975        build.ret(&[differ]);
2976        assert!(!simplify(&mut func));
2977        assert_eq!(came_from(&func, differ).0, Opcode::Xor);
2978    }
2979
2980    #[test]
2981    fn an_exclusive_or_of_something_that_is_not_a_comparison_is_left_alone() {
2982        let (_, mut func, block) = blank();
2983        let mut build = Builder::new(&mut func, block);
2984        let x = build.iconst(Type::int(32), 3);
2985        let narrow = build.unary(Opcode::Trunc, x, Type::int(1));
2986        let ones = build.iconst(Type::int(1), -1);
2987        let not = build.binary(Opcode::Xor, narrow, ones, Flags::NONE);
2988        build.ret(&[not]);
2989        assert!(!simplify(&mut func));
2990        assert_eq!(came_from(&func, not).0, Opcode::Xor);
2991    }
2992
2993    #[test]
2994    fn a_wider_exclusive_or_with_one_is_not_a_negation_and_is_left_alone() {
2995        let (_, mut func, block) = blank();
2996        let mut build = Builder::new(&mut func, block);
2997        let x = build.iconst(Type::int(32), 3);
2998        let y = build.iconst(Type::int(32), 4);
2999        let cmp = build.icmp(IntPred::Slt, x, y);
3000        let wide = build.unary(Opcode::ZExt, cmp, Type::int(32));
3001        let one = build.iconst(Type::int(32), 1);
3002        let flipped = build.binary(Opcode::Xor, wide, one, Flags::NONE);
3003        let narrow = build.unary(Opcode::Trunc, flipped, Type::int(1));
3004        build.ret(&[narrow]);
3005        assert!(!simplify(&mut func), "an i32 xor 1 flips one bit of thirty two");
3006        assert_eq!(came_from(&func, flipped).0, Opcode::Xor);
3007    }
3008
3009    #[test]
3010    fn the_comparisons_flags_travel_with_the_predicate() {
3011        let (_, mut func, block) = blank();
3012        let mut build = Builder::new(&mut func, block);
3013        let x = build.iconst(Type::int(64), 0);
3014        let x = build.unary(Opcode::Bitcast, x, Type::float(Float::F64));
3015        // Not `x` against itself, which is equal or unordered and settles most predicates on its own.
3016        let y = build.iconst(Type::int(64), 1);
3017        let y = build.unary(Opcode::Bitcast, y, Type::float(Float::F64));
3018        let cmp = build.fcmp(FloatPred::Olt, x, y, Flags::FAST);
3019        let ones = build.iconst(Type::int(1), -1);
3020        let not = build.binary(Opcode::Xor, cmp, ones, Flags::NONE);
3021        build.ret(&[not]);
3022        assert!(simplify(&mut func));
3023        let rucc_ir::Def::Result { inst, .. } = func[not].def else { panic!("not a result") };
3024        // The promise the original comparison was made under, not the exclusive or's absence of
3025        // one. Dropping it would be correct and would quietly undo a fast math flag.
3026        assert_eq!(func[inst].flags, Flags::FAST);
3027    }
3028
3029    #[test]
3030    fn fuel_stops_the_transformation_and_not_the_walk() {
3031        let (_, mut func, block) = blank();
3032        let mut build = Builder::new(&mut func, block);
3033        let x = build.iconst(Type::int(32), 3);
3034        let y = build.iconst(Type::int(32), 4);
3035        let a = build.icmp(IntPred::Slt, x, y);
3036        let b = build.icmp(IntPred::Sgt, x, y);
3037        let ones = build.iconst(Type::int(1), -1);
3038        let first = build.binary(Opcode::Xor, a, ones, Flags::NONE);
3039        let second = build.binary(Opcode::Xor, b, ones, Flags::NONE);
3040        let both = build.binary(Opcode::And, first, second, Flags::NONE);
3041        build.ret(&[both]);
3042        let stats =
3043            Simplify.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(1));
3044        assert!(stats.changed());
3045        assert_eq!(stats.count(Kind::Optimized, super::FLIPPED), 1);
3046        assert_eq!(stats.count(Kind::Missed, super::NO_FUEL), 1);
3047        assert_eq!(came_from(&func, first).0, Opcode::ICmp);
3048        assert_eq!(came_from(&func, second).0, Opcode::Xor);
3049    }
3050
3051    /// Two `i32` parameters to compare, which every composite test below is about.
3052    fn a_pair() -> (Func, Block, Value, Value) {
3053        let mut names = Interner::new();
3054        let name = names.intern("f");
3055        let int = Type::int(32);
3056        let signature = Signature::new().with_params(&[int, int]).with_returns(&[Type::int(1)]);
3057        let mut func = Func::new(name, signature);
3058        let block = func.create_block();
3059        let x = func.append_param(block, int);
3060        let y = func.append_param(block, int);
3061        (func, block, x, y)
3062    }
3063
3064    /// The same, of `f64`.
3065    fn a_float_pair() -> (Func, Block, Value, Value) {
3066        let mut names = Interner::new();
3067        let name = names.intern("f");
3068        let float = Type::float(Float::F64);
3069        let signature = Signature::new().with_params(&[float, float]).with_returns(&[Type::int(1)]);
3070        let mut func = Func::new(name, signature);
3071        let block = func.create_block();
3072        let x = func.append_param(block, float);
3073        let y = func.append_param(block, float);
3074        (func, block, x, y)
3075    }
3076
3077    /// Every bucket table agrees with [`FloatPred::inverse`] about what the opposite of a predicate
3078    /// is.
3079    ///
3080    /// The point of the assertion is that the two were written in different crates by different
3081    /// reasoning. `inverse` is a sixteen line table of names, and the buckets are four bits and a
3082    /// complement, so a predicate given the wrong set here disagrees with the name it was given
3083    /// there and this says which one.
3084    #[test]
3085    fn the_opposite_of_a_float_predicate_is_the_buckets_it_leaves_out() {
3086        for pred in FloatPred::all() {
3087            assert_eq!(
3088                super::float_buckets(pred.inverse()),
3089                super::bucket::ALL_FLOAT ^ super::float_buckets(pred),
3090                "{pred:?}"
3091            );
3092        }
3093    }
3094
3095    /// And with [`FloatPred::swapped`] about what reading the operands the other way round does.
3096    ///
3097    /// Which of two values is below the other changes and nothing else does, because equal is equal
3098    /// from both ends and a NaN makes a pair unordered from both ends.
3099    #[test]
3100    fn swapping_a_float_predicates_operands_exchanges_below_and_above() {
3101        for pred in FloatPred::all() {
3102            let want = super::turned(super::float_buckets(pred));
3103            assert_eq!(super::float_buckets(pred.swapped()), want, "{pred:?}");
3104        }
3105    }
3106
3107    /// The sixteen floating point predicates are the sixteen sets, so reading a set back is total
3108    /// and gives the predicate it came from.
3109    #[test]
3110    fn every_set_of_float_buckets_is_a_predicate() {
3111        for pred in FloatPred::all() {
3112            assert_eq!(super::float_pred(super::float_buckets(pred)), Some(pred), "{pred:?}");
3113        }
3114        for buckets in 0..=super::bucket::ALL_FLOAT {
3115            assert!(super::float_pred(buckets).is_some(), "{buckets} spells nothing");
3116        }
3117    }
3118
3119    /// The same two agreements for the integer predicates.
3120    #[test]
3121    fn an_integer_predicate_agrees_with_its_own_opposite_and_its_own_swap() {
3122        use super::bucket::ALL_INT;
3123        for pred in IntPred::all() {
3124            let (before, reading) = super::int_buckets(pred);
3125            let (opposite, other) = super::int_buckets(pred.inverse());
3126            assert_eq!(opposite, ALL_INT ^ before, "the opposite of {pred:?}");
3127            assert_eq!(other, reading, "the opposite of {pred:?} reads the operands differently");
3128            let (swapped, other) = super::int_buckets(pred.swapped());
3129            assert_eq!(swapped, super::turned(before), "the swap of {pred:?}");
3130            assert_eq!(other, reading, "the swap of {pred:?} reads the operands differently");
3131        }
3132    }
3133
3134    /// Reading an integer set back gives the predicate it came from, under the reading that
3135    /// predicate wanted.
3136    #[test]
3137    fn every_integer_predicate_is_read_back_as_itself() {
3138        for pred in IntPred::all() {
3139            let (buckets, reading) = super::int_buckets(pred);
3140            assert_eq!(super::int_pred(buckets, reading), Some(pred), "{pred:?}");
3141        }
3142    }
3143
3144    #[test]
3145    fn two_integer_comparisons_that_agree_about_nothing_are_false() {
3146        let (mut func, block, x, y) = a_pair();
3147        let mut build = Builder::new(&mut func, block);
3148        let same = build.icmp(IntPred::Eq, x, y);
3149        let differ = build.icmp(IntPred::Ne, x, y);
3150        let both = build.binary(Opcode::And, same, differ, Flags::NONE);
3151        build.ret(&[both]);
3152        assert!(simplify(&mut func));
3153        assert_eq!(number(&func, both), 0);
3154    }
3155
3156    #[test]
3157    fn two_integer_comparisons_that_cover_everything_are_true() {
3158        let (mut func, block, x, y) = a_pair();
3159        let mut build = Builder::new(&mut func, block);
3160        let above = build.icmp(IntPred::Sge, x, y);
3161        let below = build.icmp(IntPred::Slt, x, y);
3162        let either = build.binary(Opcode::Or, above, below, Flags::NONE);
3163        build.ret(&[either]);
3164        assert!(simplify(&mut func));
3165        assert_ne!(number(&func, either), 0);
3166    }
3167
3168    /// Below or equal is one comparison, and the saving is what makes the rewrite worth taking on
3169    /// its own rather than only for the two answers that are constants.
3170    #[test]
3171    fn two_integer_comparisons_that_overlap_become_one() {
3172        let (mut func, block, x, y) = a_pair();
3173        let mut build = Builder::new(&mut func, block);
3174        let below = build.icmp(IntPred::Slt, x, y);
3175        let same = build.icmp(IntPred::Eq, x, y);
3176        let either = build.binary(Opcode::Or, below, same, Flags::NONE);
3177        build.ret(&[either]);
3178        assert!(simplify(&mut func));
3179        assert_eq!(came_from(&func, either), (Opcode::ICmp, Extra::IntPred(IntPred::Sle)));
3180        assert_eq!(operands(&func, either), [x, y]);
3181    }
3182
3183    /// `(x<y) && (y<x)`, which is the third test of `gcc.c-torture/execute/compare-3.c` and the one
3184    /// that needs the second comparison turned round before the two are about one pair.
3185    #[test]
3186    fn the_second_comparison_is_read_in_the_first_ones_operand_order() {
3187        let (mut func, block, x, y) = a_pair();
3188        let mut build = Builder::new(&mut func, block);
3189        let below = build.icmp(IntPred::Slt, x, y);
3190        let above = build.icmp(IntPred::Slt, y, x);
3191        let both = build.binary(Opcode::And, below, above, Flags::NONE);
3192        build.ret(&[both]);
3193        assert!(simplify(&mut func));
3194        assert_eq!(number(&func, both), 0);
3195    }
3196
3197    /// An equality says nothing about how the operands are read, so it combines with either
3198    /// ordering and takes the one beside it.
3199    #[test]
3200    fn an_equality_takes_the_ordering_of_the_comparison_beside_it() {
3201        for (ordered, want) in [(IntPred::Ult, IntPred::Ule), (IntPred::Slt, IntPred::Sle)] {
3202            let (mut func, block, x, y) = a_pair();
3203            let mut build = Builder::new(&mut func, block);
3204            let below = build.icmp(ordered, x, y);
3205            let same = build.icmp(IntPred::Eq, x, y);
3206            let either = build.binary(Opcode::Or, below, same, Flags::NONE);
3207            build.ret(&[either]);
3208            assert!(simplify(&mut func), "{ordered:?}");
3209            assert_eq!(came_from(&func, either).1, Extra::IntPred(want), "{ordered:?}");
3210        }
3211    }
3212
3213    /// A signed comparison and an unsigned one are two different questions, and a set built out of
3214    /// one of each would be a set about no reading in particular.
3215    #[test]
3216    fn a_signed_comparison_and_an_unsigned_one_are_left_alone() {
3217        let (mut func, block, x, y) = a_pair();
3218        let mut build = Builder::new(&mut func, block);
3219        let signed = build.icmp(IntPred::Slt, x, y);
3220        let unsigned = build.icmp(IntPred::Ugt, x, y);
3221        let both = build.binary(Opcode::And, signed, unsigned, Flags::NONE);
3222        build.ret(&[both]);
3223        assert!(!simplify(&mut func));
3224        assert_eq!(came_from(&func, both).0, Opcode::And);
3225    }
3226
3227    #[test]
3228    fn two_comparisons_about_different_operands_are_left_alone() {
3229        let (mut func, block, x, y) = a_pair();
3230        let mut build = Builder::new(&mut func, block);
3231        let other = build.iconst(Type::int(32), 7);
3232        let first = build.icmp(IntPred::Slt, x, y);
3233        let second = build.icmp(IntPred::Sgt, x, other);
3234        let both = build.binary(Opcode::And, first, second, Flags::NONE);
3235        build.ret(&[both]);
3236        assert!(!simplify(&mut func));
3237        assert_eq!(came_from(&func, both).0, Opcode::And);
3238    }
3239
3240    /// `x == y && x != y` on floating point, which is false for the same reason it is on integers
3241    /// and is not the same reason a reader might expect: `oeq` and `une` do not overlap because
3242    /// `oeq` refuses a NaN and `une` accepts one, so the pair is empty rather than only unequal.
3243    #[test]
3244    fn two_float_comparisons_that_agree_about_nothing_are_false() {
3245        let (mut func, block, x, y) = a_float_pair();
3246        let mut build = Builder::new(&mut func, block);
3247        let same = build.fcmp(FloatPred::Oeq, x, y, Flags::NONE);
3248        let differ = build.fcmp(FloatPred::Une, x, y, Flags::NONE);
3249        let both = build.binary(Opcode::And, same, differ, Flags::NONE);
3250        build.ret(&[both]);
3251        assert!(simplify(&mut func));
3252        assert_eq!(number(&func, both), 0);
3253    }
3254
3255    /// Unordered, or above or equal, or below, which is the fifth test of
3256    /// `gcc.c-torture/execute/ieee/compare-fp-3.c`. It is three comparisons and two `or`s, and it
3257    /// folds because the walk is forward and the rewrite is in place: the inner pair is one
3258    /// comparison by the time the outer `or` is looked at.
3259    #[test]
3260    fn a_three_way_float_condition_folds_one_pair_at_a_time() {
3261        let (mut func, block, x, y) = a_float_pair();
3262        let mut build = Builder::new(&mut func, block);
3263        let neither = build.fcmp(FloatPred::Uno, x, y, Flags::NONE);
3264        let above = build.fcmp(FloatPred::Oge, x, y, Flags::NONE);
3265        let below = build.fcmp(FloatPred::Olt, x, y, Flags::NONE);
3266        let first = build.binary(Opcode::Or, neither, above, Flags::NONE);
3267        let whole = build.binary(Opcode::Or, first, below, Flags::NONE);
3268        build.ret(&[whole]);
3269        assert!(simplify(&mut func));
3270        assert_eq!(came_from(&func, first).1, Extra::FloatPred(FloatPred::Uge));
3271        assert_ne!(number(&func, whole), 0);
3272    }
3273
3274    /// One `f64` parameter, which every magnitude test below takes the magnitude of.
3275    fn a_float() -> (Func, Block, Value) {
3276        let mut names = Interner::new();
3277        let name = names.intern("f");
3278        let float = Type::float(Float::F64);
3279        let signature = Signature::new().with_params(&[float]).with_returns(&[Type::int(1)]);
3280        let mut func = Func::new(name, signature);
3281        let block = func.create_block();
3282        let x = func.append_param(block, float);
3283        (func, block, x)
3284    }
3285
3286    /// `fabs (x)` as the lowering writes it, which is the sign bit cleared over the bits.
3287    fn magnitude_of(build: &mut Builder<'_>, x: Value) -> Value {
3288        let bits = Type::int(64);
3289        let number = build.unary(Opcode::Bitcast, x, bits);
3290        let mask = build.iconst(bits, i128::from(i64::MAX));
3291        let cleared = build.binary(Opcode::And, number, mask, Flags::NONE);
3292        build.unary(Opcode::Bitcast, cleared, Type::float(Float::F64))
3293    }
3294
3295    /// `fabs (x) < 0.0`, which is what `gcc.c-torture/execute/20020720-1.c` asserts is false by
3296    /// calling a function it never defines.
3297    #[test]
3298    fn a_magnitude_is_never_below_zero() {
3299        let (mut func, block, x) = a_float();
3300        let mut build = Builder::new(&mut func, block);
3301        let p = magnitude_of(&mut build, x);
3302        let zero = build.fconst(Type::float(Float::F64), 0);
3303        let below = build.fcmp(FloatPred::Olt, p, zero, Flags::NONE);
3304        build.ret(&[below]);
3305        assert!(simplify(&mut func));
3306        assert_eq!(number(&func, below), 0);
3307    }
3308
3309    /// The same question with the operands the other way round. `0.0 > fabs (x)` is the same claim
3310    /// and is a different instruction, and the buckets have to be turned round to see it.
3311    #[test]
3312    fn zero_is_never_above_a_magnitude() {
3313        let (mut func, block, x) = a_float();
3314        let mut build = Builder::new(&mut func, block);
3315        let p = magnitude_of(&mut build, x);
3316        let zero = build.fconst(Type::float(Float::F64), 0);
3317        let above = build.fcmp(FloatPred::Ogt, zero, p, Flags::NONE);
3318        build.ret(&[above]);
3319        assert!(simplify(&mut func));
3320        assert_eq!(number(&func, above), 0);
3321    }
3322
3323    /// `fabs (x) <= 0.0` is not a constant and is still shorter than it was: the only way a
3324    /// magnitude is at or below zero is by being zero, so the answer is an equality.
3325    #[test]
3326    fn a_magnitude_at_or_below_zero_is_a_magnitude_equal_to_it() {
3327        let (mut func, block, x) = a_float();
3328        let mut build = Builder::new(&mut func, block);
3329        let p = magnitude_of(&mut build, x);
3330        let zero = build.fconst(Type::float(Float::F64), 0);
3331        let atmost = build.fcmp(FloatPred::Ole, p, zero, Flags::NONE);
3332        build.ret(&[atmost]);
3333        assert!(simplify(&mut func));
3334        assert_eq!(came_from(&func, atmost).1, Extra::FloatPred(FloatPred::Oeq));
3335    }
3336
3337    /// A negative constant takes the equal bucket with it, because every value a magnitude can be
3338    /// is above every negative number, so the comparison is false rather than shorter.
3339    #[test]
3340    fn a_magnitude_is_never_at_or_below_a_negative_number() {
3341        let (mut func, block, x) = a_float();
3342        let mut build = Builder::new(&mut func, block);
3343        let p = magnitude_of(&mut build, x);
3344        let minus_one = build.fconst(Type::float(Float::F64), 0xbff0_0000_0000_0000);
3345        let atmost = build.fcmp(FloatPred::Ole, p, minus_one, Flags::NONE);
3346        build.ret(&[atmost]);
3347        assert!(simplify(&mut func));
3348        assert_eq!(number(&func, atmost), 0);
3349    }
3350
3351    /// `fabs (x) >= 0.0` is left alone, and a reader who expects it to be true is the reason this
3352    /// test is here rather than the reason it fails: a NaN has its sign bit cleared like anything
3353    /// else and is not above, below or equal to anything, so the comparison is false for one.
3354    #[test]
3355    fn a_magnitude_at_or_above_zero_is_still_a_question_about_a_nan() {
3356        let (mut func, block, x) = a_float();
3357        let mut build = Builder::new(&mut func, block);
3358        let p = magnitude_of(&mut build, x);
3359        let zero = build.fconst(Type::float(Float::F64), 0);
3360        let atleast = build.fcmp(FloatPred::Oge, p, zero, Flags::NONE);
3361        build.ret(&[atleast]);
3362        assert!(!simplify(&mut func));
3363        assert_eq!(came_from(&func, atleast).1, Extra::FloatPred(FloatPred::Oge));
3364    }
3365
3366    /// A positive constant narrows nothing, so the comparison stands as it was written.
3367    #[test]
3368    fn a_magnitude_against_a_positive_number_is_left_alone() {
3369        let (mut func, block, x) = a_float();
3370        let mut build = Builder::new(&mut func, block);
3371        let p = magnitude_of(&mut build, x);
3372        let one = build.fconst(Type::float(Float::F64), 0x3ff0_0000_0000_0000);
3373        let below = build.fcmp(FloatPred::Olt, p, one, Flags::NONE);
3374        build.ret(&[below]);
3375        assert!(!simplify(&mut func));
3376        assert_eq!(came_from(&func, below).1, Extra::FloatPred(FloatPred::Olt));
3377    }
3378
3379    /// A mask with its top bit set says nothing about the sign of what comes out of it, so the
3380    /// bitcast under it is not a magnitude and the comparison stands.
3381    #[test]
3382    fn a_mask_that_keeps_the_sign_bit_is_not_a_magnitude() {
3383        let (mut func, block, x) = a_float();
3384        let mut build = Builder::new(&mut func, block);
3385        let bits = Type::int(64);
3386        let number = build.unary(Opcode::Bitcast, x, bits);
3387        let mask = build.iconst(bits, -2);
3388        let cleared = build.binary(Opcode::And, number, mask, Flags::NONE);
3389        let p = build.unary(Opcode::Bitcast, cleared, Type::float(Float::F64));
3390        let zero = build.fconst(Type::float(Float::F64), 0);
3391        let below = build.fcmp(FloatPred::Olt, p, zero, Flags::NONE);
3392        build.ret(&[below]);
3393        assert!(!simplify(&mut func));
3394        assert_eq!(came_from(&func, below).1, Extra::FloatPred(FloatPred::Olt));
3395    }
3396
3397    /// A NaN on the other side settles the comparison on its own, and it is the NaN that does it
3398    /// rather than the magnitude, so the answer is the one any value against a NaN has.
3399    #[test]
3400    fn a_magnitude_against_a_nan_is_settled_by_the_nan() {
3401        let (mut func, block, x) = a_float();
3402        let mut build = Builder::new(&mut func, block);
3403        let p = magnitude_of(&mut build, x);
3404        let nan = build.fconst(Type::float(Float::F64), NAN);
3405        let below = build.fcmp(FloatPred::Olt, p, nan, Flags::NONE);
3406        build.ret(&[below]);
3407        let stats = Simplify.run(
3408            &mut func,
3409            &mut crate::machine::fixtures::analyses(),
3410            &mut Fuel::unlimited(),
3411        );
3412        assert_eq!(stats.count(Kind::Optimized, super::MAGNITUDE), 0);
3413        assert_eq!(stats.count(Kind::Optimized, super::BOUNDED), 1);
3414        assert_eq!(number(&func, below), 0);
3415    }
3416
3417    /// The quiet NaN a `const double` given `1.0/0.0 - 1.0/0.0` holds.
3418    const NAN: u128 = 0x7ff8_0000_0000_0000;
3419
3420    /// A positive infinity.
3421    const INFINITY: u128 = 0x7ff0_0000_0000_0000;
3422
3423    /// Every comparison of `gcc.c-torture/execute/ieee/fp-cmp-6.c`, which is a NaN against a
3424    /// number the program could have changed. The ordered ones and `ueq`'s missing half are false
3425    /// and `une` is true, whatever `x` holds.
3426    #[test]
3427    fn a_nan_is_unordered_against_anything() {
3428        for (pred, answer) in [
3429            (FloatPred::Oeq, false),
3430            (FloatPred::Olt, false),
3431            (FloatPred::Ogt, false),
3432            (FloatPred::Ole, false),
3433            (FloatPred::Oge, false),
3434            (FloatPred::One, false),
3435            (FloatPred::Une, true),
3436            (FloatPred::Ult, true),
3437            (FloatPred::Uno, true),
3438        ] {
3439            let (mut func, block, x) = a_float();
3440            let mut build = Builder::new(&mut func, block);
3441            let nan = build.fconst(Type::float(Float::F64), NAN);
3442            let asked = build.fcmp(pred, nan, x, Flags::NONE);
3443            build.ret(&[asked]);
3444            assert!(simplify(&mut func), "{pred:?}");
3445            assert_eq!(number(&func, asked) != 0, answer, "{pred:?}");
3446        }
3447    }
3448
3449    /// Nothing is above a positive infinity, which is `gcc.c-torture/execute/ieee/fp-cmp-7.c`. At or
3450    /// below one is only a question about a NaN, and it is left as written because what it narrows
3451    /// to is the predicate it already is.
3452    #[test]
3453    fn nothing_is_above_a_positive_infinity() {
3454        let (mut func, block, x) = a_float();
3455        let mut build = Builder::new(&mut func, block);
3456        let infinity = build.fconst(Type::float(Float::F64), INFINITY);
3457        let above = build.fcmp(FloatPred::Ogt, x, infinity, Flags::NONE);
3458        let atmost = build.fcmp(FloatPred::Ole, x, infinity, Flags::NONE);
3459        let below = build.fcmp(FloatPred::Olt, x, infinity, Flags::NONE);
3460        build.ret(&[above, atmost, below]);
3461        assert!(simplify(&mut func));
3462        assert_eq!(number(&func, above), 0);
3463        assert_eq!(came_from(&func, atmost).1, Extra::FloatPred(FloatPred::Ole));
3464        assert_eq!(came_from(&func, below).1, Extra::FloatPred(FloatPred::Olt));
3465    }
3466
3467    /// The same on the left of a negative infinity, turned round.
3468    #[test]
3469    fn a_negative_infinity_is_above_nothing() {
3470        let (mut func, block, x) = a_float();
3471        let mut build = Builder::new(&mut func, block);
3472        let infinity = build.fconst(Type::float(Float::F64), INFINITY | 1 << 63);
3473        let above = build.fcmp(FloatPred::Ogt, infinity, x, Flags::NONE);
3474        build.ret(&[above]);
3475        assert!(simplify(&mut func));
3476        assert_eq!(number(&func, above), 0);
3477    }
3478
3479    /// Two constants are one bucket, so every predicate over them is an answer.
3480    #[test]
3481    fn two_float_constants_are_an_answer() {
3482        let (mut func, block, _) = a_float();
3483        let mut build = Builder::new(&mut func, block);
3484        let one = build.fconst(Type::float(Float::F64), 0x3ff0_0000_0000_0000);
3485        let two = build.fconst(Type::float(Float::F64), 0x4000_0000_0000_0000);
3486        let below = build.fcmp(FloatPred::Olt, one, two, Flags::NONE);
3487        let equal = build.fcmp(FloatPred::Ueq, one, two, Flags::NONE);
3488        build.ret(&[below, equal]);
3489        assert!(simplify(&mut func));
3490        assert_ne!(number(&func, below), 0);
3491        assert_eq!(number(&func, equal), 0);
3492    }
3493
3494    /// A value is equal to itself or is a NaN, so it is never below itself, and `x != x` is the
3495    /// question of whether it is a NaN. `x == x` is the shortest it can be written already.
3496    #[test]
3497    fn a_value_against_itself_is_equal_or_a_nan() {
3498        let (mut func, block, x) = a_float();
3499        let mut build = Builder::new(&mut func, block);
3500        let below = build.fcmp(FloatPred::Olt, x, x, Flags::NONE);
3501        let differs = build.fcmp(FloatPred::Une, x, x, Flags::NONE);
3502        let same = build.fcmp(FloatPred::Oeq, x, x, Flags::NONE);
3503        build.ret(&[below, differs, same]);
3504        assert!(simplify(&mut func));
3505        assert_eq!(number(&func, below), 0);
3506        assert_eq!(came_from(&func, differs).1, Extra::FloatPred(FloatPred::Uno));
3507        assert_eq!(came_from(&func, same).1, Extra::FloatPred(FloatPred::Oeq));
3508    }
3509
3510    /// `isunordered (x, y) || !isunordered (x, y)` kept as two branches, which is the seventh test
3511    /// of `gcc.c-torture/execute/ieee/compare-fp-3.c` at `-O1`, `-Os` and `-Oz`. The second
3512    /// comparison is only reached where the first was false, so the pair is ordered there. On the
3513    /// side where it was true nothing is settled, and `x < y` after `x >= y` was false is `x < y` or
3514    /// unordered, which is shorter only as far as `ult` is.
3515    #[test]
3516    fn a_branch_in_front_settles_the_same_pair() {
3517        let (mut func, entry, x, y) = a_float_pair();
3518        let [then, other, join] = [(); 3].map(|()| func.create_block());
3519        let mut build = Builder::new(&mut func, entry);
3520        let neither = build.fcmp(FloatPred::Uno, x, y, Flags::NONE);
3521        build.br_if(neither, then, &[], other, &[]);
3522        let mut build = Builder::new(&mut func, other);
3523        let ordered = build.fcmp(FloatPred::Ord, x, y, Flags::NONE);
3524        let turned = build.fcmp(FloatPred::Ord, y, x, Flags::NONE);
3525        let above = build.fcmp(FloatPred::Ogt, x, y, Flags::NONE);
3526        build.jump(join, &[]);
3527        let mut build = Builder::new(&mut func, then);
3528        let there = build.fcmp(FloatPred::Ord, x, y, Flags::NONE);
3529        build.jump(join, &[]);
3530        let mut build = Builder::new(&mut func, join);
3531        let both = build.binary(Opcode::And, ordered, turned, Flags::NONE);
3532        let all = build.binary(Opcode::And, both, above, Flags::NONE);
3533        let all = build.binary(Opcode::And, all, there, Flags::NONE);
3534        build.ret(&[all]);
3535        assert!(simplify(&mut func));
3536        assert_ne!(number(&func, ordered), 0);
3537        assert_ne!(number(&func, turned), 0);
3538        assert_eq!(came_from(&func, above).1, Extra::FloatPred(FloatPred::Ogt));
3539        assert_eq!(number(&func, there), 0);
3540    }
3541
3542    /// A join has two ways in, and what one branch said is not what the other did, so nothing is
3543    /// settled past it.
3544    #[test]
3545    fn a_join_settles_nothing() {
3546        let (mut func, entry, x, y) = a_float_pair();
3547        let [then, other, join] = [(); 3].map(|()| func.create_block());
3548        let mut build = Builder::new(&mut func, entry);
3549        let neither = build.fcmp(FloatPred::Uno, x, y, Flags::NONE);
3550        build.br_if(neither, then, &[], other, &[]);
3551        Builder::new(&mut func, then).jump(join, &[]);
3552        Builder::new(&mut func, other).jump(join, &[]);
3553        let mut build = Builder::new(&mut func, join);
3554        let ordered = build.fcmp(FloatPred::Ord, x, y, Flags::NONE);
3555        build.ret(&[ordered]);
3556        assert!(!simplify(&mut func));
3557        assert_eq!(came_from(&func, ordered).1, Extra::FloatPred(FloatPred::Ord));
3558    }
3559
3560    /// A number that is not an infinity says nothing about the other side on its own.
3561    #[test]
3562    fn a_finite_bound_is_left_alone() {
3563        let (mut func, block, x) = a_float();
3564        let mut build = Builder::new(&mut func, block);
3565        let one = build.fconst(Type::float(Float::F64), 0x3ff0_0000_0000_0000);
3566        let below = build.fcmp(FloatPred::Olt, x, one, Flags::NONE);
3567        build.ret(&[below]);
3568        assert!(!simplify(&mut func));
3569        assert_eq!(came_from(&func, below).1, Extra::FloatPred(FloatPred::Olt));
3570    }
3571
3572    /// The fold spends fuel like the other two and stopping it stops the transforming rather than
3573    /// the walking.
3574    #[test]
3575    fn fuel_stops_the_magnitude_fold_and_not_the_walk() {
3576        let (mut func, block, x) = a_float();
3577        let mut build = Builder::new(&mut func, block);
3578        let p = magnitude_of(&mut build, x);
3579        let zero = build.fconst(Type::float(Float::F64), 0);
3580        let below = build.fcmp(FloatPred::Olt, p, zero, Flags::NONE);
3581        let also = build.fcmp(FloatPred::Olt, p, zero, Flags::NONE);
3582        let both = build.binary(Opcode::Or, below, also, Flags::NONE);
3583        build.ret(&[both]);
3584        let stats =
3585            Simplify.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(1));
3586        assert_eq!(stats.count(Kind::Optimized, super::MAGNITUDE), 1);
3587        assert_eq!(stats.count(Kind::Missed, super::NO_FUEL_MAGNITUDE), 1);
3588        assert_eq!(number(&func, below), 0);
3589        assert_eq!(came_from(&func, also).0, Opcode::FCmp);
3590    }
3591
3592    /// A fast math promise is a promise about one comparison, and a set built out of two that were
3593    /// not promised the same thing is a set under no promise in particular.
3594    #[test]
3595    fn two_comparisons_promised_different_things_are_left_alone() {
3596        let (mut func, block, x, y) = a_float_pair();
3597        let mut build = Builder::new(&mut func, block);
3598        let below = build.fcmp(FloatPred::Olt, x, y, Flags::FAST);
3599        let same = build.fcmp(FloatPred::Oeq, x, y, Flags::NONE);
3600        let either = build.binary(Opcode::Or, below, same, Flags::NONE);
3601        build.ret(&[either]);
3602        assert!(!simplify(&mut func));
3603        assert_eq!(came_from(&func, either).0, Opcode::Or);
3604    }
3605
3606    #[test]
3607    fn the_promise_both_comparisons_were_made_under_travels_to_the_one_that_replaces_them() {
3608        let (mut func, block, x, y) = a_float_pair();
3609        let mut build = Builder::new(&mut func, block);
3610        let below = build.fcmp(FloatPred::Olt, x, y, Flags::FAST);
3611        let same = build.fcmp(FloatPred::Oeq, x, y, Flags::FAST);
3612        let either = build.binary(Opcode::Or, below, same, Flags::NONE);
3613        build.ret(&[either]);
3614        assert!(simplify(&mut func));
3615        assert_eq!(came_from(&func, either).1, Extra::FloatPred(FloatPred::Ole));
3616        let rucc_ir::Def::Result { inst, .. } = func[either].def else { panic!("not a result") };
3617        assert_eq!(func[inst].flags, Flags::FAST);
3618    }
3619
3620    /// An `and` of two comparisons at a width that is not one bit is an and of two bits held in
3621    /// something wider, which is a different program.
3622    #[test]
3623    fn a_wider_and_of_two_comparisons_is_left_alone() {
3624        let (mut func, block, x, y) = a_pair();
3625        let mut build = Builder::new(&mut func, block);
3626        let same = build.icmp(IntPred::Eq, x, y);
3627        let differ = build.icmp(IntPred::Ne, x, y);
3628        let first = build.unary(Opcode::ZExt, same, Type::int(32));
3629        let second = build.unary(Opcode::ZExt, differ, Type::int(32));
3630        let both = build.binary(Opcode::And, first, second, Flags::NONE);
3631        let narrow = build.unary(Opcode::Trunc, both, Type::int(1));
3632        build.ret(&[narrow]);
3633        assert!(!simplify(&mut func));
3634        assert_eq!(came_from(&func, both).0, Opcode::And);
3635    }
3636
3637    #[test]
3638    fn fuel_stops_the_composite_fold_and_not_the_walk() {
3639        let (mut func, block, x, y) = a_pair();
3640        let mut build = Builder::new(&mut func, block);
3641        let same = build.icmp(IntPred::Eq, x, y);
3642        let differ = build.icmp(IntPred::Ne, x, y);
3643        let below = build.icmp(IntPred::Slt, x, y);
3644        let above = build.icmp(IntPred::Sgt, x, y);
3645        let first = build.binary(Opcode::And, same, differ, Flags::NONE);
3646        let second = build.binary(Opcode::And, below, above, Flags::NONE);
3647        let both = build.binary(Opcode::Or, first, second, Flags::NONE);
3648        build.ret(&[both]);
3649        let stats =
3650            Simplify.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(1));
3651        assert!(stats.changed());
3652        assert_eq!(stats.count(Kind::Optimized, super::COMPOSITE), 1);
3653        assert_eq!(stats.count(Kind::Missed, super::NO_FUEL_COMPOSITE), 1);
3654        assert_eq!(came_from(&func, first).0, Opcode::IConst);
3655        assert_eq!(came_from(&func, second).0, Opcode::And);
3656    }
3657}