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