Skip to main content

rucc_opt/
phiopt.rs

1//! If-conversion, the part of it that turns a diamond into a select.
2//!
3//! Design: `spec/optimizer/22-phiopt-and-if-conversion.md`. A block ends in a two way branch, each
4//! arm works out a value and does nothing else, and the two arms meet again at a block that takes
5//! that value as a parameter. The branch is not deciding what the program does, it is deciding
6//! which of two numbers to keep, and `select` says that directly. Section 22.2 asks for the shape
7//! matcher and five transformations built on it, and the shape matcher plus the first, the second,
8//! the third and half of the fourth of them is what is here.
9//!
10//! This is the highest variance transformation in the compiler and the document says so in its
11//! third paragraph. Removing a mispredicted branch is worth about twenty cycles. Removing a
12//! perfectly predicted one costs whatever the arm that is no longer skipped costs, and no static
13//! analysis tells the two apart reliably. So the cost rule below is written to be argued with
14//! rather than to be right, and section 42's measurement of the pass on and off at `-O2` is the
15//! only honest evaluation there is.
16//!
17//! # The shape
18//!
19//! A head block ending in `br_if`, and a join block both arms reach. Each side of the branch is
20//! either a block of its own that does nothing but work out values and jump to the join, or the
21//! join itself. That gives three shapes and the pass takes all three: the diamond where both sides
22//! have a block, and the two triangles where one side goes straight to the join because the arm
23//! was empty and `simplify-cfg` already took it out.
24//!
25//! What replaces it is one block. Everything the arms worked out moves into the head, a `select`
26//! is built for each of the join's parameters the two sides disagree about, and the head jumps to
27//! the join carrying them. The arms are then unreachable and go, and the join is left for
28//! `simplify-cfg` to merge upward when nothing else arrives at it.
29//!
30//! # The value the condition already settled
31//!
32//! Section 22.2's second transformation, `value_replacement`. `x = (a == b) ? b : a` is `x = a`,
33//! because the only way to arrive at the join carrying `b` is along the edge where `a` and `b` are
34//! the same number. No select is written, the branch goes with the rest of them, and whichever arm
35//! was only there to work the other value out is left with nothing in it.
36//!
37//! What answers this is document 10's relational oracle, which is what section 22.2 says it needs
38//! and this is its first caller in the compiler. The question is put as `Ranges::compare` at the
39//! arm rather than as a range lookup, because the fact wanted is about two values rather than about
40//! either one of them, and section 10.3 is where that distinction is made.
41//!
42//! The branch has to be on an equality, and that gate is the difference between a cheap pass and an
43//! expensive one. Section 22.7 already names this query as the expensive part of the pass. What the
44//! oracle records is what a dominating edge established between two values, and the edge out of a
45//! `br_if` establishes something about two values only when the branch is on a comparison of them,
46//! so a branch on `x < n` cannot answer whether two other values are equal and asking is a query
47//! with nothing at the end of it. GCC gates the same way, on `EQ_EXPR` and `NE_EXPR`.
48//!
49//! One thing this does that the select cannot is a value whose type has no `select` at all. A
50//! pointer is the case: `p == q ? q : p` used to keep its branch, because a `select` of two
51//! pointers is a term nothing lowers, and it is now one move, because nothing has to be chosen.
52//! The width refusal below is therefore asked after this rather than before it.
53//!
54//! It is one deep, in the same sense the factoring below is. What the join is handed is what gets
55//! asked about, so `a == b ? b + c : a + c` factors to one add over a select and the select stays,
56//! because what the oracle would have to know is that `b + c` and `a + c` are equal rather than
57//! that `a` and `b` are. Asking about the factored operand instead is the change that would take
58//! it, and it is left for when something measures a use for it.
59//!
60//! # The operation both arms did
61//!
62//! Section 22.2's third transformation, `factor_out_conditional_operation`. When the two sides
63//! worked out their answers the same way from different operands, `cond ? f(a) : f(b)`, the select
64//! goes under the operation rather than over it and the answer is `f(cond ? a : b)`. One operation
65//! where there were two, and the same one select either way.
66//!
67//! It is structural and not a rewrite rule for the reason section 22.2 gives about all five: the
68//! two `f`s are in different blocks and no pattern spans blocks. By the time they are in one block
69//! the arms have already been hoisted and the select already written, and undoing that is a larger
70//! rewrite than never writing it.
71//!
72//! The two operations have to match in everything but one operand. The opcode and the operand count
73//! obviously. The flags, because those are what the optimizer is licensed to assume and one copy
74//! written under the union of two sets of assumptions would be claiming on one path something only
75//! the other path established. Whatever else the instruction carries, which for a comparison is the
76//! predicate, since two predicates are two different questions. And exactly one operand position
77//! apart, because two positions apart needs two selects and one operation, which is what one select
78//! and two operations already cost.
79//!
80//! Agreeing in every position is allowed and is the case where no select is written at all. Both
81//! arms working out the same thing from the same operands is what a common subexpression that
82//! nothing has numbered looks like from here, and one copy of it serves both sides.
83//!
84//! The operation has to be worked out in the arm and read only by the arm's jump to the join. The
85//! first because an operation to stop writing is one this has to be able to find. The second
86//! because the one copy that replaces the two is written after the arms have gone, and a second
87//! reader inside the arm would have been left pointing at an instruction that is no longer in any
88//! block.
89//!
90//! Only the value the join takes is asked about, so a chain both arms share is factored one deep.
91//! `total += (long long)(i * 2)` against `total += (long long)(i + 1)` has three operations in each
92//! arm, the outermost pair factors, the sign extensions under them are the same operation on
93//! different operands and would factor too, and they are not looked at because nothing hands them
94//! to the join. Doing it to a depth would mean factoring what the select then reads, which is the
95//! same function called on what it just produced, and it is left for when something asks for it.
96//!
97//! A constant operand is not refused and the reason is that it was measured and it goes both ways.
98//! An operation with a constant in it takes that constant as an immediate, so factoring turns two
99//! free immediates into a select between two values that have to be in registers, and on
100//! `product + 2` against `product + 1` outside a loop that costs five bytes. On `total += 1`
101//! against `total += 1000` inside one it saves fourteen, because the constants were being
102//! rematerialized every iteration anyway. Over the corpus, refusing every constant operand trades
103//! thirty two bytes of win for twenty two bytes of loss, which is ten bytes across 1453 programs
104//! and is not worth a rule.
105//!
106//! # The store both arms made
107//!
108//! Section 22.2's fourth transformation, conditional store replacement, in the half of it that
109//! needs no proof. When both arms store to the same place, `if (c) *p = a; else *p = b;` becomes
110//! `*p = c ? a : b`, and the branch goes with the rest of them.
111//!
112//! Half, and which half is the whole point. Section 22.6 calls the other half the worst bug in the
113//! document, because a store made on a path that was not going to make one writes memory the
114//! program was not going to write. The load modify store form GCC uses, reading the location and
115//! writing back what it read on the path that had no store, is not a no-op: it is a write, so it
116//! races with another thread writing the same bytes, and it faults if the page is read only. What
117//! would license it is knowing the location is written whatever happens, which is the predicate
118//! section 22.6 asks for and which nothing here can answer yet.
119//!
120//! When both arms store to the same address, that predicate is discharged by the shape itself and
121//! nothing has to be proved. One store before and one store after, to the same address, of a value
122//! the program was going to write there on one path or the other. Nothing new is written, nothing
123//! is written twice, and the order of that store against everything else in the function is where
124//! it was. So this is the case that goes in with nothing more to ask. The one armed case goes in
125//! only with the proof the next section gives, and is refused by name when that proof is missing,
126//! so that `-fopt-info-all` says which of the two it was.
127//!
128//! What has to match beyond the address is the access itself: the flags, and the alignment, size,
129//! aliasing node and `restrict` scope that a store carries alongside them, because the one store
130//! written below carries one of each and two that disagree have no single answer to carry. The
131//! address has to be the same value rather than a provably equal one, which is the strong form of
132//! the question and is the only form available without an alias analysis. It also settles where the
133//! address comes from: neither arm dominates the other, so a value both of them name is worked out
134//! at or above the head, and the one store is written where it is available.
135//!
136//! The same value rather than the same address is also where most of what this does not catch
137//! goes, so the two refusals are counted separately and say which. `if (x > 128) q[i] = 128; else
138//! q[i] = x;` works `q + i` out twice, once in each arm, and two instructions that compute the same
139//! address are two values, so this walks away from a diamond whose two stores go to the same place
140//! by any reading a person would give it. What fixes that is document 16's value numbering turning
141//! the two into one, not anything about memory, and hoisting the address by hand into `int *p =
142//! &q[i];` is enough to get the fold today.
143//!
144//! `volatile` and atomic are refused. `volatile` because section 22.6 says never, and the reason is
145//! not that the flags fail to match: how many accesses there are and what order they come in are
146//! both observable, and a value that arrives through a select is a different program from one that
147//! arrives through a branch. Atomic for the ordering rather than the access, since a store with an
148//! order on it is a fence as much as a write.
149//!
150//! # The store one arm made
151//!
152//! The other half of section 22.2's fourth transformation, for the one kind of location where the
153//! proof section 22.6 asks for can be given. `if (v > best[k]) best[k] = v;` becomes a load of
154//! `best[k]`, a select between `v` and what the load found, and a store of the select, on both
155//! paths. GCC does the same thing to the same loop and under the same two conditions.
156//!
157//! The location is a local whose address never leaves the function. That answers the race, since
158//! another thread can only write bytes it can name and nothing outside this function can name
159//! these, and it answers the read only page, since a local is on the stack. Where the address
160//! points is `alias::origin` and whether anything else was ever handed it is `alias::Escapes`. A
161//! function that gives stack back part way through, which is what a variable length array going
162//! out of scope does, is refused whole.
163//!
164//! The head read or wrote the same address at the same width, with nothing after that access that
165//! could have given the memory back. That answers the fault. `best[k]` with a `k` the branch was
166//! guarding is an address the other path never reaches, and a local is only in the frame for the
167//! offsets it has. An access the head already made there means the other path reached it anyway.
168//! The same address is a structural question, two chains of the same pure operations over the same
169//! values, because nothing before value numbering has made the arm's copy of a subscript and the
170//! head's copy one value.
171//!
172//! A load in an arm is taken on the second condition alone. An arm that reads an address the head
173//! already touched cannot fault on the path that did not read it, and a plain load is not something
174//! another thread can see. That is what lets `if (v[i] > best[k]) best[k] = v[i];` go, where the arm
175//! reads `v[i]` a second time. The loads an arm makes have to come before its store, since the one
176//! store is written below everything the arms did.
177//!
178//! The store is counted as work, because it now happens on a path that did not make one, so the
179//! predictability half of the cost rule still has its say about it. The load that finds the old
180//! value is not counted, since the head has just touched the same bytes.
181//!
182//! # What a select is built for
183//!
184//! Two sides disagree about a parameter when they hand the join different values, and also when
185//! they hand it different values that are the same number. The second half is there because the
186//! corpus has eight diamonds whose two arms both work out the same constant, in separate
187//! instructions that nothing has hash consed into one, and the tier six rule `select(c, x, x) -> x`
188//! does not reach them for exactly the same reason: two operands that are not one value do not
189//! match a pattern that writes one name twice. What would reach them is document 12.1's hash
190//! consing or document 16's value numbering, and until one of those exists the cheap question is
191//! worth asking here, where the alternative is a `select` this pass wrote itself between two sevens.
192//!
193//! # Why moving an arm's work into the head is safe
194//!
195//! Because the arm has exactly one predecessor, which is the head. That is checked, and it is the
196//! whole of the argument in both directions.
197//!
198//! Downward: an instruction in the arm reads values that dominate the arm, and the head dominates
199//! the arm too, so every one of them is available where the instruction is going. Upward: nothing
200//! outside the arm can read what the arm defines except by the arm's own jump, since the arm
201//! dominates only itself, and that jump's arguments are exactly what the selects are built out of.
202//! An arm with two predecessors would break both halves at once, which is why the check is on the
203//! predecessor count and not on the shape of the graph around it.
204//!
205//! The loop rules that `spec/optimizer/23-jump-threading.md` needs are not needed here, and the
206//! reason is worth writing down rather than leaving as an absence. No edge is added, so no loop
207//! gains a second way in and no loop can become irreducible. An arm cannot be a loop header, since
208//! a header has a back edge and this arm has one predecessor and it is not itself. An arm can be a
209//! latch, and then the head becomes the latch instead, which keeps the single latch property
210//! document 07.3 wants rather than spoiling it. The one shape that would matter is a join that
211//! only its own arms reach, which is a region unreachable from the entry, and the pass asks
212//! whether the head is reachable before it looks at anything.
213//!
214//! # What it refuses, and every one of them is section 22.6
215//!
216//! An arm that does something. The predicate is [`rucc_ir::Opcode::has_effects`], which is what
217//! dead code elimination deletes an instruction under, so an arm this pass will hoist is an arm
218//! whose instructions could have been deleted outright had nothing read them. A call, a `volatile`
219//! access and a load are all effects by that answer, which closes the second and sixth failures in
220//! section 22.6 with one question. The exceptions are the stores and the loads the sections above
221//! give a proof for, which are the effects this pass moves, and each of them only with its proof.
222//!
223//! A store the other side does not match, to a location the section above cannot prove anything
224//! about. That is the first failure in section 22.6 and it gets a reason of its own rather than the
225//! general one, because it is a different answer rather than a stricter one: the transformation
226//! exists, it is section 22.2's fourth, and what is missing is the proof.
227//!
228//! An arm that divides. Division is not an effect, because nothing observes it and dead code
229//! elimination is right to delete one, but it traps, and a trap on a path that did not have one is
230//! section 22.6's third failure. The exception is a divisor that is a constant which is neither
231//! zero nor minus one, which cannot trap and is most of the divisions real code contains.
232//!
233//! A value the two sides disagree about whose type has no `select`. The IR names a `select` at
234//! eight, sixteen, thirty two and sixty four bit integers and at nothing else, so producing one of
235//! any other type would build a term the back end has no rule for. That is an invisible gap rather
236//! than a wrong answer, and the producer is the side that has to avoid it. It is asked after the
237//! condition has had its say, because a value nothing has to choose between needs no select and so
238//! does not need one that can be lowered.
239//!
240//! A branch that is already decided. Section 22.6 does not list this one and the corpus found it,
241//! on a program whose source says `if (1)`. `simplify-cfg` runs after this pass and turns a decided
242//! branch into a jump, and then the arm that cannot run is deleted whole and its work with it.
243//! Converting first replaces a branch that costs nothing at run time with a select that costs
244//! something, and it keeps alive the work in the arm that never ran, because the fold that would
245//! undo it is `select(1, a, b) -> a` and that rule does not exist yet. The case cost twenty eight
246//! bytes of `.text` and a multiply that could not happen.
247//!
248//! The question is put to `simplify_cfg::taken` rather than answered again here, for the
249//! reason that function's own documentation gives: two answers about when a branch is decided
250//! would be two compilers. It matters in this case rather than being tidiness. The condition on
251//! `if (1)` is not a constant, it is `icmp ne 1, 0`, and `fold` leaves that standing on purpose,
252//! because nothing lowers an `i1` by itself and folding one would turn working code into code that
253//! does not build, which is issue 352. `taken` reads the answer off without leaving anything
254//! standing, since the branch that was the comparison's only reader goes at the same time.
255//!
256//! # The cost rule
257//!
258//! Section 22.2 states it and this implements it without softening it.
259//!
260//! Both arms empty of instructions: convert, always. The select replaces a branch with one
261//! operation that reads two values which already exist, and there is no machine where that is
262//! worse. Nothing about predictability enters, because there is nothing being speculated.
263//!
264//! What is factored does not count as work. Both arms did the operation, one of them was always
265//! going to do it, and afterwards one copy of it runs whichever way the branch would have gone, so
266//! nothing is being speculated. A diamond whose arms factor away entirely converts on the same
267//! terms as a diamond with empty arms, and one that factors down to two instructions is judged on
268//! the two rather than on what it started as.
269//!
270//! What the head already worked out does not count either. The arm of `if (v[i] > best[k])` works
271//! out the address of `best[k]` a second time, because lowering a subscript does not know it has
272//! lowered it before, and once that copy is moved into the head it sits in the same block as the
273//! first one and `number` makes the two one value. Counting it would refuse the diamond for work
274//! nobody ends up doing.
275//!
276//! Arms with work left in them: up to [`heuristics::PHIOPT_ARM_INSTRUCTIONS`] instructions each,
277//! and only when the branch probability is within
278//! [`heuristics::PHIOPT_UNPREDICTABLE_MARGIN_PERCENT`] of even by document 11's estimate. A branch
279//! the estimate calls one sided keeps its branch, because if the estimate is right the branch is
280//! free and the arm is not.
281//!
282//! The estimate is usually a guess and the guess is often wrong, which section 22.6 lists as the
283//! failure with no defence. Note where that leaves an unpredicted branch: document 11 answers even
284//! and says it is guessing, even is inside the margin, so a branch nothing is known about is
285//! treated as unpredictable and converted. That is the aggressive reading and it is deliberate,
286//! since the alternative is a pass that fires on almost nothing and measures nothing.
287//!
288//! It is also, today, the only reading, and that is worth saying rather than leaving to be
289//! discovered. Every static predictor in document 11 that gives a one sided answer keys on
290//! something one arm of the branch does and the other does not: one arm never comes back, one arm
291//! calls something cold, one arm leaves the loop, one arm returns a negative number. A diamond has
292//! neither of those, because both of its arms fall through to the same block, so the predictors
293//! that could refuse a conversion here are exactly the ones a diamond cannot trip. What is left is
294//! the branch condition itself, which is `__builtin_expect` at ninety percent and the pointer
295//! heuristic at seventy, and only the first of those is outside the margin. `__builtin_expect` is
296//! dropped in the front end today, so until it is wired the probability half of the rule refuses
297//! nothing at all. The check is here rather than deferred because leaving it out would mean the
298//! measurement never showed that, and because the day the hint is wired is the day it starts
299//! mattering.
300//!
301//! # Which level, and how many times
302//!
303//! Every level that optimizes, which is section 22.2's `-O1` and above.
304//!
305//! Once. Section 22.7 asks for two instances at `-O2`, one before the loop pipeline and one after,
306//! because the loop passes make diamonds. There is no loop pipeline yet, so the second instance
307//! would be a second walk over every function to find the shapes the first one already took, and
308//! it belongs in the change that adds the passes it exists to clean up after.
309//!
310//! Section 22.2 also wants a peephole run after this one, so that the rule set can answer what the
311//! `select` becomes. Those rules are tier six of `spec/optimizer/13-rewrite-rules.md`, in
312//! `rules/select.rules`, and the run that fires them is the `simplify` every level already has
313//! after this pass. `select(c, 1, 0)` is `zext(c)` there, and a select between a value and one
314//! step from it is the value moved by the condition.
315
316use rucc_cost::heuristics;
317use rucc_ir::{
318    Block, Builder, Def, Extra, Flags, Func, Inst, InstData, IntPred, MemOrder, Opcode, Type, Value,
319};
320
321use crate::alias::{self, Escapes, Origin};
322use crate::cfg::Cfg;
323use crate::fold::constant;
324use crate::profile::Probability;
325use crate::range::ops::Truth;
326use crate::range::query::Ranges;
327use crate::simplify_cfg::{self, Bindings};
328use crate::{Analyses, Fuel, Pass, Preserved, Stats};
329
330/// Recorded once for each diamond that became a select.
331const CONVERTED: &str =
332    "branch whose two arms only work out a value replaced by the value and no branch";
333
334/// Recorded once for each operation both arms did that ended up being done once.
335const FACTORED: &str = "operation both arms did to different operands done once below the branch";
336
337/// Recorded once for each value the branch condition settled, so that no select was written for it.
338const VALUE_IMPLIED: &str =
339    "value the two arms disagreed about settled by the condition rather than by a select";
340
341/// Recorded once for each pair of stores to one place that became one store below the branch.
342const STORE_REPLACED: &str = "store both arms made to the same place made once below the branch";
343
344/// Recorded once for each store only one arm made, to a local the head had already touched.
345const STORE_GUARDED: &str =
346    "store one path made to a local the branch had already touched made on both paths";
347
348/// Recorded once for each load an arm made of an address the head had already touched.
349const LOAD_SPECULATED: &str =
350    "load one path made of an address the branch had already touched made on both paths";
351
352/// Recorded for a diamond one of whose arms does something that has to happen.
353const ARM_HAS_EFFECTS: &str =
354    "branch kept, an arm does something that only happens on the path it is on";
355
356/// Recorded for a diamond where only one of the two paths stores at all.
357const STORE_ON_ONE_PATH: &str =
358    "branch kept, a store only one path makes would have to be made on the other path too";
359
360/// Recorded for a diamond where both paths store but not the same store to the same place.
361const STORES_DO_NOT_MATCH: &str =
362    "branch kept, both paths store but not to one address the two of them name the same way";
363
364/// Recorded for a diamond one of whose arms divides by something that could be zero.
365const ARM_MAY_TRAP: &str = "branch kept, an arm divides and doing it on both paths could trap";
366
367/// Recorded for a diamond whose two arms disagree about a value nothing can choose between.
368const NO_SELECT_AT_THAT_WIDTH: &str =
369    "branch kept, the value the arms disagree about is not a width a select is lowered at";
370
371/// Recorded for a diamond whose arms are more work than the branch is worth.
372const ARMS_TOO_LONG: &str = "branch kept, its arms are more work than doing both of them is worth";
373
374/// Recorded for a diamond whose branch the estimate says the machine will get right.
375const BRANCH_IS_PREDICTED: &str =
376    "branch kept, it goes one way often enough that the machine will predict it";
377
378/// Recorded for a diamond that would have been converted if there had been fuel for it.
379const CONDITION_IS_DECIDED: &str =
380    "branch kept, its condition is already known and the arm that cannot run is better deleted";
381const NO_FUEL: &str = "branch kept, the pass ran out of fuel";
382
383/// The pass.
384#[derive(Debug, Clone, Copy, PartialEq, Eq)]
385pub struct PhiOpt;
386
387impl Pass for PhiOpt {
388    fn name(&self) -> &'static str {
389        "phiopt"
390    }
391
392    fn describe(&self) -> &'static str {
393        "a branch whose two arms only work out a value becomes a select, and the branch goes"
394    }
395
396    fn preserves(&self) -> Preserved {
397        // Nothing. Blocks stop existing and an edge stops existing with them, so every analysis
398        // built on the graph was built on a different graph.
399        Preserved::NONE
400    }
401
402    fn run(&self, func: &mut Func, an: &mut Analyses, fuel: &mut Fuel) -> Stats {
403        let mut stats = Stats::new();
404        if func.entry().is_none() {
405            return stats;
406        }
407        for head in func.blocks().collect::<Vec<Block>>() {
408            let cfg = an.cfg(func);
409            if !cfg.reaches(head) {
410                continue;
411            }
412            let Some(shape) = diamond(func, cfg, head) else { continue };
413            let store = storing(func, &shape);
414            // Before the refusals rather than after, because one of them is about a value having a
415            // width a select is lowered at, and a value the condition settles gets no select at all.
416            // The waste that costs is a range query on a diamond that then turns out to have an
417            // effect in it, and the equality gate below is what keeps that from being every diamond.
418            let implied = implied(func, an, &shape);
419            if let Some(reason) = refused(func, &shape, store.as_ref(), &implied) {
420                stats.missed(reason);
421                continue;
422            }
423            let plan = factoring(func, &shape, &implied);
424            // What is factored is not speculated. Both arms did the operation, one of them was
425            // always going to do it, and after this one copy of it runs whichever way the branch
426            // would have gone. So it comes off the count the cost rule is about, and a diamond
427            // whose arms factor away entirely converts on the same terms as a diamond with empty
428            // arms: always, because there is nothing being done that was not being done before.
429            // The store both arms made comes off the count for the same reason a factored operation
430            // does. One of the two was always going to run, and afterwards one copy of it runs
431            // whichever way the branch would have gone, so nothing about memory is being speculated.
432            // A store only one arm made stays on it, since the other path now makes it too.
433            let paired = store.as_ref().is_some_and(|one| one.insts.len() == 2);
434            let replaced = plan.iter().flatten().count() + usize::from(paired);
435            let saved = u32::try_from(replaced).unwrap_or(u32::MAX);
436            let work = shape
437                .arms
438                .map(|arm| arm.map_or(0, |block| work(func, head, block)).saturating_sub(saved));
439            if work.iter().any(|&count| count > 0) {
440                if work.iter().any(|&count| count > heuristics::PHIOPT_ARM_INSTRUCTIONS) {
441                    stats.missed(ARMS_TOO_LONG);
442                    continue;
443                }
444                // The first edge out of the head, which is the arm taken when the condition holds,
445                // because `Cfg::successors` is in the order the terminator names its targets. Which
446                // of the two is asked about does not matter, since the question is whether the
447                // number is near even and the other edge is its complement.
448                if !unpredictable(an.frequencies(func).taken(head, 0)) {
449                    stats.missed(BRANCH_IS_PREDICTED);
450                    continue;
451                }
452            }
453            if !fuel.take() {
454                // Where the pass stops rather than where it starts skipping, for the reason jump
455                // threading gives: a budget that has reached zero will not have anything in it at
456                // the next block either, and the refusals above are the counts worth being true.
457                stats.missed(NO_FUEL);
458                break;
459            }
460            let loads = shape
461                .arms
462                .iter()
463                .flatten()
464                .flat_map(|&arm| func.insts(arm))
465                .filter(|&inst| func[inst].opcode == Opcode::Load)
466                .count();
467            convert(func, &shape, &plan, store.as_ref(), &implied);
468            // The graph was about the function as it was a moment ago, and the manager clears the
469            // cache after the pass returns, which is too late for the next block.
470            an.clear();
471            for _ in plan.iter().flatten() {
472                stats.optimized(FACTORED);
473            }
474            for _ in implied.iter().flatten() {
475                stats.optimized(VALUE_IMPLIED);
476            }
477            match store.as_ref().map(|one| one.insts.len()) {
478                Some(2) => stats.optimized(STORE_REPLACED),
479                Some(_) => stats.optimized(STORE_GUARDED),
480                None => {}
481            }
482            for _ in 0..loads {
483                stats.optimized(LOAD_SPECULATED);
484            }
485            stats.optimized(CONVERTED);
486        }
487        stats
488    }
489}
490
491/// A branch whose two arms meet again, and what each of them hands the block they meet at.
492pub(crate) struct Diamond {
493    /// The block the branch is in.
494    pub(crate) head: Block,
495    /// The bit the branch is on, which is the bit the selects are on.
496    pub(crate) cond: Value,
497    /// The block both arms reach.
498    pub(crate) join: Block,
499    /// The block on each side, when that side is a block of its own rather than the join.
500    ///
501    /// Index zero is the side taken when the condition holds, which is the side `select` calls
502    /// `then`, and the order is the order the terminator names its targets in.
503    pub(crate) arms: [Option<Block>; 2],
504    /// What each side hands the join, in the order the join takes its parameters.
505    pub(crate) args: [Vec<Value>; 2],
506}
507
508/// The diamond this block is the head of, if it is the head of one.
509pub(crate) fn diamond(func: &Func, cfg: &Cfg, head: Block) -> Option<Diamond> {
510    let entry = cfg.entry()?;
511    let term = func.terminator(head)?;
512    if func[term].opcode != Opcode::BrIf {
513        return None;
514    }
515    let cond = *func[func[term].args].first()?;
516    let mut targets = func.successors(term);
517    let sides = [targets.next()?, targets.next()?];
518    // Both arms at the same block is a branch that goes to one place carrying two argument lists.
519    // It is convertible and it is rare enough not to be worth a second shape, and `simplify-cfg`
520    // takes the case where the two lists agree.
521    if sides[0].block == sides[1].block {
522        return None;
523    }
524    let through = [
525        passes_through(func, cfg, head, sides[0].block),
526        passes_through(func, cfg, head, sides[1].block),
527    ];
528    // The diamond, then the two triangles. A side that is not the join has to be a block that
529    // reaches it, which is what makes the arm below a side that has one.
530    let join = match through {
531        [Some(left), Some(right)] if left == right => left,
532        [Some(left), _] if left == sides[1].block => left,
533        [_, Some(right)] if right == sides[0].block => right,
534        _ => return None,
535    };
536    // A join that is the head is a loop with nothing outside it, and one that is the entry is a
537    // block control arrives at rather than one it reaches.
538    if join == head || join == entry {
539        return None;
540    }
541    let arms = [
542        (sides[0].block != join).then_some(sides[0].block),
543        (sides[1].block != join).then_some(sides[1].block),
544    ];
545    let mut args = [Vec::new(), Vec::new()];
546    for (index, side) in sides.iter().enumerate() {
547        let carried = match arms[index] {
548            // The arm's own jump is what tells the join what this side worked out.
549            Some(arm) => func.successors(func.terminator(arm)?).next()?.args,
550            None => side.args,
551        };
552        args[index] = func[carried].to_vec();
553    }
554    Some(Diamond { head, cond, join, arms, args })
555}
556
557/// Where this side of the branch ends up, when it is a block whose only job is to get there.
558///
559/// Everything this asks is needed. Parameters, because a block that takes them is being told
560/// something on the edge and there would be nothing to tell it once the edge is gone. One
561/// predecessor and it being the head, because that is the whole argument for moving the block's
562/// work upward and it is also what makes removing the block afterwards legal. A jump, because an
563/// arm that branches is a second decision and this pass is about one.
564fn passes_through(func: &Func, cfg: &Cfg, head: Block, block: Block) -> Option<Block> {
565    if !func[block].params.is_empty() {
566        return None;
567    }
568    // A block an image holds the address of has a way in the graph does not show: what arrives
569    // there is a `goto *p` that can be in another function, so the one predecessor below is not the
570    // only one and the block is not one this pass may take out.
571    if func.block_name(block).is_some() {
572        return None;
573    }
574    match cfg.predecessors(block) {
575        [only] if *only == head => {}
576        _ => return None,
577    }
578    let term = func.terminator(block)?;
579    if func[term].opcode != Opcode::Jump {
580        return None;
581    }
582    Some(func.successors(term).next()?.block)
583}
584
585/// Why this diamond is left alone, or `None` when nothing is in the way.
586///
587/// The store plan is passed in because the two stores it names are the one pair of instructions
588/// with effects this pass is allowed to move, and everything else with an effect still refuses.
589fn refused(
590    func: &Func,
591    shape: &Diamond,
592    store: Option<&Stored>,
593    implied: &[Option<usize>],
594) -> Option<&'static str> {
595    // A branch nobody has to take is not a branch worth removing. `simplify-cfg` runs after this
596    // pass and turns a decided branch into a jump, and then the arm that cannot run is deleted
597    // whole. Converting first replaces a branch that costs nothing with a select that costs
598    // something, and the fold that would undo it is a rule the set does not have yet, so the work
599    // in the arm that never ran survives into the machine code. The corpus found this on `if (1)`.
600    //
601    // The question is put to `simplify-cfg` rather than answered again here, for the reason its
602    // own documentation gives: two answers about when a branch is decided would be two compilers.
603    // It matters in this case, because the condition on `if (1)` is not a constant, it is a
604    // comparison of two constants, which `fold` deliberately leaves standing.
605    let term = func.terminator(shape.head).expect("the head of a diamond ends in its branch");
606    if simplify_cfg::taken(func, term, &Bindings::new()).is_some() {
607        return Some(CONDITION_IS_DECIDED);
608    }
609    let moving: &[Inst] = store.map_or(&[], |one| &one.insts);
610    for &arm in shape.arms.iter().flatten() {
611        for inst in func.insts(arm) {
612            if func.is_terminator(inst) || moving.contains(&inst) {
613                continue;
614            }
615            if readable(func, shape.head, inst) {
616                continue;
617            }
618            if func[inst].opcode == Opcode::Store {
619                // Named separately from the effects below because it is a different answer rather
620                // than a stricter one. A store the other side does not make is section 22.2's
621                // fourth transformation without its proof, and section 22.6 calls it the worst bug
622                // in the document: making it on both paths writes memory the program was not going
623                // to write, which is not a no-op if another thread is writing the same bytes and is
624                // not a no-op if the page is read only. What licenses it is a local nothing else
625                // can name that the head already touched, and a store that got here had neither.
626                return Some(mismatch(func, shape));
627            }
628            if func[inst].opcode.has_effects() {
629                return Some(ARM_HAS_EFFECTS);
630            }
631            if !speculatable(func, inst) {
632                return Some(ARM_MAY_TRAP);
633            }
634        }
635    }
636    let params = func[shape.join].params.to_vec();
637    for (index, &param) in params.iter().enumerate() {
638        // The two sides agreeing about a parameter is the common case in a triangle, where one
639        // side passes on what it was already holding, and it needs no select at all. Neither does
640        // one the condition settles, which is why this is asked after that question rather than
641        // before it: a value of a type nothing can select between is fine when nothing has to.
642        if agree(func, shape.args[0][index], shape.args[1][index]) {
643            continue;
644        }
645        if implied.get(index).copied().flatten().is_some() {
646            continue;
647        }
648        if !selectable(func[param].ty) {
649            return Some(NO_SELECT_AT_THAT_WIDTH);
650        }
651    }
652    None
653}
654
655/// Whether the two sides hand the join the same thing, so that no `select` is needed for it.
656///
657/// The same value is the easy answer and it is the one a triangle gives, where one side passes on
658/// what it was already holding. The same constant is the answer the corpus asked for. `x ? 7 : 7`
659/// arrives here as two `iconst.i32 7` instructions, one in each arm, which are two values because
660/// nothing has hash consed them into one. Asking only about the value builds a `select` between two
661/// sevens, which costs a compare, a byte and a conditional move to work out that seven is seven.
662/// The module comment says what the general answer would be and why it is not available yet.
663fn agree(func: &Func, then: Value, other: Value) -> bool {
664    if then == other {
665        return true;
666    }
667    let (Some((left, lty)), Some((right, rty))) = (constant(func, then), constant(func, other))
668    else {
669        return false;
670    };
671    lty == rty && left == right
672}
673
674/// Whether doing this on a path that was not going to do it is harmless.
675///
676/// Only division asks anything here, because the caller has already refused everything with an
677/// effect and what is left is arithmetic. Zero is the divisor everybody knows about. Minus one is
678/// the other one: the smallest signed number divided by it is not representable and x86 raises the
679/// same exception it raises for zero.
680pub(crate) fn speculatable(func: &Func, inst: Inst) -> bool {
681    let opcode = func[inst].opcode;
682    if !matches!(opcode, Opcode::SDiv | Opcode::UDiv | Opcode::SRem | Opcode::URem) {
683        return true;
684    }
685    let Some(&divisor) = func[func[inst].args].get(1) else { return false };
686    let Some((imm, ty)) = constant(func, divisor) else { return false };
687    if imm.unsigned() == 0 {
688        return false;
689    }
690    imm.signed(ty) != -1
691}
692
693/// A store one or both arms make, which becomes one store below the branch.
694///
695/// Section 22.2's fourth transformation. `if (c) *p = a; else *p = b;` is `*p = c ? a : b`, and
696/// the number of stores is one before and one after, to the same address, of a value the program
697/// was going to write there on one path or the other. With one arm storing, the other side writes
698/// back what it finds, and that is only done where the module comment says it can be.
699struct Stored {
700    /// The store each side wrote, which goes when the one copy below replaces them.
701    insts: Vec<Inst>,
702    /// What each side wrote, taken in the order the branch names its targets, and `None` on a side
703    /// that wrote nothing and so writes back what is already there.
704    values: [Option<Value>; 2],
705    /// The address, which is one value both sides named.
706    addr: Value,
707    /// The store to write once, whose value operand is replaced by the select above it.
708    data: InstData,
709    /// What is stored.
710    ty: Type,
711}
712
713/// Which side's value serves for both, for each join parameter the branch condition settles.
714///
715/// Section 22.2's second transformation, `value_replacement`. `x = (a == b) ? b : a` is `x = a`,
716/// because the only way to arrive carrying `b` is along the edge where `a` and `b` are the same
717/// number. The select goes, and so does whichever arm was only there to work the other value out.
718///
719/// The answer for a parameter is the side whose value is passed on. If the two are known equal on
720/// one side's edge, then that side's value is the other side's value there, and the other side's
721/// value is right on both edges. Availability comes for free: everything both arms worked out is
722/// moved into the head before the jump is written, so a value that came from an arm is defined
723/// above the point it is now read at.
724///
725/// # Why the condition has to be an equality
726///
727/// This is the pass's one expensive question and section 22.7 says so. What answers it is document
728/// 10's relational oracle, which records what a dominating edge established between two values, and
729/// the edge out of a `br_if` establishes something about two values only when the branch is on a
730/// comparison of them. A branch on `x < n` says nothing about whether two other values are equal,
731/// so asking is a query that cannot come back with anything. Gating on the comparison being an
732/// equality is what turns this from a query per diamond into a query per diamond that could
733/// possibly answer, which on the corpus is a small fraction of them. GCC gates the same way, on
734/// `EQ_EXPR` and `NE_EXPR` at `gcc/tree-ssa-phiopt.cc`.
735fn implied(func: &Func, an: &mut Analyses, shape: &Diamond) -> Vec<Option<usize>> {
736    let count = shape.args[0].len();
737    let mut answers = vec![None; count];
738    if !equality(func, shape.cond) {
739        return answers;
740    }
741    let asking: Vec<usize> = (0..count).filter(|&index| worth_asking(func, shape, index)).collect();
742    if asking.is_empty() {
743        return answers;
744    }
745    // Cloned because the two are held at once and the cache hands out one borrow at a time. It is
746    // paid for only by a diamond that got this far, which the two gates above have already made
747    // rare, and section 22.7 is where the cost of this query was budgeted.
748    let cfg = an.cfg(func);
749    let dom = an.dominators(func);
750    let mut ranges = Ranges::new(func, cfg, dom);
751    for index in asking {
752        let pair = [shape.args[0][index], shape.args[1][index]];
753        for side in 0..2 {
754            let Some(block) = shape.arms[side] else { continue };
755            if ranges.compare(IntPred::Eq, pair[0], pair[1], block) == Truth::Always {
756                answers[index] = Some(1 - side);
757                break;
758            }
759        }
760    }
761    answers
762}
763
764/// Whether the branch is on a comparison that says two values are the same or are not.
765fn equality(func: &Func, cond: Value) -> bool {
766    let Def::Result { inst, .. } = func[cond].def else { return false };
767    if func[inst].opcode != Opcode::ICmp {
768        return false;
769    }
770    matches!(func[inst].extra, Extra::IntPred(IntPred::Eq | IntPred::Ne))
771}
772
773/// Whether this join parameter is one the oracle could have something to say about.
774///
775/// Two sides that agree need nothing. Two constants that are not the same number are not the same
776/// number on any edge, and asking is a query whose answer is already in hand.
777fn worth_asking(func: &Func, shape: &Diamond, index: usize) -> bool {
778    let pair = [shape.args[0][index], shape.args[1][index]];
779    if agree(func, pair[0], pair[1]) {
780        return false;
781    }
782    constant(func, pair[0]).is_none() || constant(func, pair[1]).is_none()
783}
784
785/// Which of the two store refusals this diamond is, once it is known to be one of them.
786///
787/// The two are worth separating because they say different things about what would fix them. One
788/// path storing is section 22.6's predicate, which is a proof nothing here can do. Both paths
789/// storing and not matching is usually two arms that worked the same address out separately, which
790/// is `a[i] = ...` on both sides, and what fixes that is document 16's value numbering making the
791/// two into one value rather than anything about memory.
792fn mismatch(func: &Func, shape: &Diamond) -> &'static str {
793    let [Some(then), Some(other)] = shape.arms else { return STORE_ON_ONE_PATH };
794    match (stored_in(func, then), stored_in(func, other)) {
795        (Some(_), Some(_)) => STORES_DO_NOT_MATCH,
796        _ => STORE_ON_ONE_PATH,
797    }
798}
799
800/// The store this diamond can move below the branch, if it has one.
801///
802/// Two stores to the same place are the half of the transformation that needs no proof. One store
803/// is the half that does, and [`alone`] is where it is asked for.
804fn storing(func: &Func, shape: &Diamond) -> Option<Stored> {
805    let found = shape.arms.map(|arm| arm.and_then(|block| stored_in(func, block)));
806    match found {
807        [Some(then), Some(other)] => both(func, [then, other]),
808        [Some(one), None] => alone(func, shape, one, 0),
809        [None, Some(one)] => alone(func, shape, one, 1),
810        [None, None] => None,
811    }
812}
813
814/// The one store two stores to the same place become.
815fn both(func: &Func, insts: [Inst; 2]) -> Option<Stored> {
816    let data = [func[insts[0]], func[insts[1]]];
817    // The flags are what the optimizer is licensed to assume about the access, so one store written
818    // under the union of two sets of assumptions would be claiming on one path something only the
819    // other path established. `volatile` is refused outright rather than by disagreeing, because
820    // section 22.6 says never and because the reason is not the flag matching: both how many
821    // accesses there are and what order they come in are observable, and a value that arrives
822    // through a select is a different program from one that arrives through a branch.
823    if data[0].flags != data[1].flags || data[0].flags.contains(Flags::VOLATILE) {
824        return None;
825    }
826    let (Extra::Mem(one), Extra::Mem(two)) = (data[0].extra, data[1].extra) else { return None };
827    // The alignment, the size, the aliasing node and the `restrict` scope, all of which the one
828    // store carries forward, so two that disagree about any of them have no single answer to carry.
829    if func[one] != func[two] || func[one].order != MemOrder::NotAtomic {
830        return None;
831    }
832    // A store names what it writes and then where, which is the order the builder takes them in.
833    let &[then, addr] = func[data[0].args].first_chunk::<2>()?;
834    let &[other, addr_two] = func[data[1].args].first_chunk::<2>()?;
835    // The same value for the address, which is stronger than the same address and is what can be
836    // checked without an alias analysis. It also settles where that value comes from: neither arm
837    // dominates the other, so a value both of them name is one worked out at or above the head, and
838    // the one store is written in the head where it is available.
839    if addr != addr_two || func[then].ty != func[other].ty {
840        return None;
841    }
842    if !agree(func, then, other) && !selectable(func[then].ty) {
843        return None;
844    }
845    let ty = func[then].ty;
846    Some(Stored {
847        insts: insts.to_vec(),
848        values: [Some(then), Some(other)],
849        addr,
850        data: data[0],
851        ty,
852    })
853}
854
855/// The store only one side makes, when the module comment's proof for it is there.
856///
857/// The side is the one that stores. The other side stores too once this is done, and what it
858/// stores is what it would have found had it looked.
859fn alone(func: &Func, shape: &Diamond, inst: Inst, side: usize) -> Option<Stored> {
860    let data = func[inst];
861    if data.flags.contains(Flags::VOLATILE) {
862        return None;
863    }
864    let Extra::Mem(mem) = data.extra else { return None };
865    if func[mem].order != MemOrder::NotAtomic {
866        return None;
867    }
868    let &[value, addr] = func[data.args].first_chunk::<2>()?;
869    let ty = func[value].ty;
870    if !selectable(ty) || !touched(func, shape.head, addr, ty) {
871        return None;
872    }
873    let (Origin::Local(slot), _) = alias::origin(func, addr) else { return None };
874    let gives_back = func
875        .blocks()
876        .flat_map(|block| func.insts(block))
877        .any(|one| func[one].opcode == Opcode::StackRestore);
878    if gives_back || Escapes::of(func).escaped(slot) {
879        return None;
880    }
881    let mut values = [None, None];
882    values[side] = Some(value);
883    Some(Stored { insts: vec![inst], values, addr, data, ty })
884}
885
886/// Whether the head reads or writes this address at this type, with nothing after that access that
887/// could have given the memory back.
888///
889/// Walked from the branch upward, and the walk stops at the first thing with an effect that is not
890/// a load or a store, because a call is what frees memory and everything above it proves nothing
891/// about the memory below it.
892fn touched(func: &Func, head: Block, addr: Value, ty: Type) -> bool {
893    let insts: Vec<Inst> = func.insts(head).collect();
894    for &inst in insts.iter().rev() {
895        let data = func[inst];
896        if func.is_terminator(inst) || !data.opcode.has_effects() {
897            continue;
898        }
899        let access = match data.opcode {
900            Opcode::Load => func[data.args].first().copied().zip(data.first_result),
901            Opcode::Store => func[data.args].first_chunk::<2>().map(|&[value, at]| (at, value)),
902            _ => return false,
903        };
904        let Some((at, value)) = access else { return false };
905        if plain(func, data) && func[value].ty == ty && same(func, at, addr, 0) {
906            return true;
907        }
908    }
909    false
910}
911
912/// Whether this load in an arm is one the head has already shown can be made on both paths.
913fn readable(func: &Func, head: Block, inst: Inst) -> bool {
914    let data = func[inst];
915    if data.opcode != Opcode::Load || !plain(func, data) {
916        return false;
917    }
918    let (Some(&addr), Some(value)) = (func[data.args].first(), data.first_result) else {
919        return false;
920    };
921    touched(func, head, addr, func[value].ty)
922}
923
924/// Whether this access is neither `volatile` nor atomic.
925fn plain(func: &Func, data: InstData) -> bool {
926    let Extra::Mem(mem) = data.extra else { return false };
927    !data.flags.contains(Flags::VOLATILE) && func[mem].order == MemOrder::NotAtomic
928}
929
930/// How deep [`same`] follows two chains of operations before it gives up on them.
931///
932/// A subscript is a sign extension, a shift and an add, and a field of an element of a local
933/// array is one more add, so this is that with room to spare.
934const SAME_DEPTH: usize = 6;
935
936/// Whether two values are worked out the same way from the same things.
937///
938/// The operations are the pure ones an address is made of, compared on everything
939/// `number` compares them on, so that two values this calls the same are two values that pass
940/// would make one.
941fn same(func: &Func, one: Value, two: Value, depth: usize) -> bool {
942    if agree(func, one, two) {
943        return true;
944    }
945    if depth == SAME_DEPTH || func[one].ty != func[two].ty {
946        return false;
947    }
948    let (Def::Result { inst: left, .. }, Def::Result { inst: right, .. }) =
949        (func[one].def, func[two].def)
950    else {
951        return false;
952    };
953    let (left, right) = (func[left], func[right]);
954    if left.opcode != right.opcode || left.flags != right.flags || left.extra != right.extra {
955        return false;
956    }
957    if left.results != 1 || right.results != 1 || !addressing(left.opcode) {
958        return false;
959    }
960    let (left, right) = (&func[left.args], &func[right.args]);
961    left.len() == right.len()
962        && left.iter().zip(right).all(|(&one, &two)| same(func, one, two, depth + 1))
963}
964
965/// Whether this is one of the operations [`same`] follows.
966fn addressing(opcode: Opcode) -> bool {
967    matches!(
968        opcode,
969        Opcode::PtrAdd
970            | Opcode::Add
971            | Opcode::Sub
972            | Opcode::Mul
973            | Opcode::Shl
974            | Opcode::And
975            | Opcode::Or
976            | Opcode::Xor
977            | Opcode::SExt
978            | Opcode::ZExt
979            | Opcode::Trunc
980            | Opcode::Bitcast
981            | Opcode::GlobalAddr
982    )
983}
984
985/// The one store this arm makes, if it makes exactly one and does nothing else that has to happen.
986///
987/// Exactly one, because two stores below one select is two selects and a shape nothing has asked
988/// for. Nothing else with an effect, because everything else with an effect is still refused and
989/// this is the check that says so: an arm that stores and also calls something has a call that only
990/// happens on the path it is on, and no amount of agreement about the store changes that.
991fn stored_in(func: &Func, arm: Block) -> Option<Inst> {
992    let mut store = None;
993    for inst in func.insts(arm) {
994        if func.is_terminator(inst) || !func[inst].opcode.has_effects() {
995            continue;
996        }
997        // A load ahead of the store is judged on its own by the refusals. One after it is not
998        // allowed, because the one store is written below everything the arms did, and a load that
999        // came after it would then read memory from before it.
1000        if func[inst].opcode == Opcode::Load && store.is_none() {
1001            continue;
1002        }
1003        if func[inst].opcode != Opcode::Store || store.is_some() {
1004            return None;
1005        }
1006        store = Some(inst);
1007    }
1008    store
1009}
1010
1011/// One join argument both arms worked out the same way, and the one operand they disagreed about.
1012///
1013/// Section 22.2's third transformation. `cond ? f(a) : f(b)` is `f(cond ? a : b)`, which is one
1014/// operation where there were two and one select either way, and it is structural rather than a
1015/// rewrite rule because the two `f`s are in different blocks and no pattern spans blocks.
1016struct Factored {
1017    /// The instruction each side wrote, which goes when the one copy below replaces both.
1018    insts: [Inst; 2],
1019    /// What each side handed that instruction, taken from the side taken when the condition holds.
1020    operands: Vec<Value>,
1021    /// The one position the two sides put different values in, and what each of them put there.
1022    ///
1023    /// `None` when they agree in every position, which is both arms computing the same thing from
1024    /// the same operands. Then one copy serves both and there is no select at all.
1025    differ: Option<(usize, [Value; 2])>,
1026    /// The instruction to write once, whose operand list is replaced by the one above.
1027    data: InstData,
1028    /// What it produces.
1029    ty: Type,
1030}
1031
1032/// What can be factored out of each of the join's parameters, in the order the join takes them.
1033///
1034/// A triangle factors nothing. One of its sides is the join itself, so there is no block on that
1035/// side holding an operation to pair the other one with, and what that side hands the join is a
1036/// value worked out before the branch.
1037fn factoring(func: &Func, shape: &Diamond, implied: &[Option<usize>]) -> Vec<Option<Factored>> {
1038    let count = shape.args[0].len();
1039    let [Some(then), Some(other)] = shape.arms else {
1040        return (0..count).map(|_| None).collect();
1041    };
1042    (0..count)
1043        .map(|index| {
1044            // A value the condition settled is passed on whole, so there is no operation to write
1045            // once below and the two that worked the two values out are left for dead code.
1046            if implied.get(index).copied().flatten().is_some() {
1047                return None;
1048            }
1049            factored(func, shape, [then, other], index)
1050        })
1051        .collect()
1052}
1053
1054/// Whether this join argument is the same operation on both sides, and what to write instead.
1055fn factored(func: &Func, shape: &Diamond, arms: [Block; 2], index: usize) -> Option<Factored> {
1056    let sides = [shape.args[0][index], shape.args[1][index]];
1057    // Two sides that agree need no operation written at all, and the caller passes the value on.
1058    if agree(func, sides[0], sides[1]) {
1059        return None;
1060    }
1061    let insts = [written_in(func, arms[0], sides[0])?, written_in(func, arms[1], sides[1])?];
1062    let data = [func[insts[0]], func[insts[1]]];
1063    // Everything about the two has to match except the operands. The flags are what the optimizer
1064    // is licensed to assume, so writing one copy under the union of two sets of assumptions would
1065    // be claiming on one path something only the other path established. The extra is whatever the
1066    // instruction carries that is not an operand, which for a comparison is the predicate, and two
1067    // predicates that differ are two different questions.
1068    if data[0].opcode != data[1].opcode || data[0].flags != data[1].flags {
1069        return None;
1070    }
1071    if data[0].extra != data[1].extra || func[sides[0]].ty != func[sides[1]].ty {
1072        return None;
1073    }
1074    let operands = [func[data[0].args].to_vec(), func[data[1].args].to_vec()];
1075    if operands[0].len() != operands[1].len() {
1076        return None;
1077    }
1078    let mut apart =
1079        operands[0].iter().zip(&operands[1]).enumerate().filter(|(_, (one, two))| one != two);
1080    let differ = match (apart.next(), apart.next()) {
1081        // Two positions apart would need two selects, and two selects and one operation is what
1082        // one select and two operations already cost. There is nothing to win, so it is left.
1083        (_, Some(_)) => return None,
1084        (Some((at, (&one, &two))), None) => {
1085            if func[one].ty != func[two].ty || !selectable(func[one].ty) {
1086                return None;
1087            }
1088            Some((at, [one, two]))
1089        }
1090        (None, None) => None,
1091    };
1092    let ty = func[sides[0]].ty;
1093    Some(Factored { insts, operands: operands[0].clone(), differ, data: data[0], ty })
1094}
1095
1096/// The instruction in this arm that works out this value, if the arm is where it comes from and the
1097/// only thing that reads it is the jump to the join.
1098///
1099/// Both halves are needed. The arm has to be where it is worked out, because an operation to factor
1100/// out is one this pass is about to stop writing and it can only stop writing what it can find.
1101/// Nothing else can read it, because the one copy that replaces the two is written after the arms
1102/// have gone and a second reader in the arm would have been left pointing at an instruction that is
1103/// no longer in any block.
1104fn written_in(func: &Func, arm: Block, value: Value) -> Option<Inst> {
1105    let inst = func
1106        .insts(arm)
1107        .find(|&inst| func[inst].results == 1 && func[inst].first_result == Some(value))?;
1108    let mut seen = 0;
1109    for inst in func.insts(arm) {
1110        seen += func[func[inst].args].iter().filter(|&&arg| arg == value).count();
1111        for call in func.successors(inst) {
1112            seen += func[call.args].iter().filter(|&&arg| arg == value).count();
1113        }
1114    }
1115    (seen == 1).then_some(inst)
1116}
1117
1118/// Whether a value of this type is one a `select` can choose.
1119///
1120/// The four widths `crates/rucc-ir/src/term.rs` names a `select` at. A wider integer, a float, a
1121/// pointer, a bit or a vector has no head, so a `select` of one would be a term the rule set has
1122/// no lowering for and the failure would be at instruction selection rather than here.
1123///
1124/// This function is also the whole answer to whether a `select` at any of those types can exist at
1125/// all, since this pass is the only one that turns a choice into one and every other writer of one
1126/// in the tree is choosing between integers it built itself. `crates/rucc-codegen/src/quad.rs`
1127/// leans on that where it says a conditional expression over two `_Float128`s stays a branch.
1128fn selectable(ty: Type) -> bool {
1129    ty.is_scalar() && ty.is_int() && matches!(ty.bits(), 8 | 16 | 32 | 64)
1130}
1131
1132/// How much work an arm does, not counting the jump that is about to go.
1133pub(crate) fn length(func: &Func, block: Block) -> u32 {
1134    let count = func.insts(block).filter(|&inst| !func.is_terminator(inst)).count();
1135    u32::try_from(count).unwrap_or(u32::MAX)
1136}
1137
1138/// How much work an arm does that the head is not already doing, not counting the jump.
1139///
1140/// An arm longer than this bothers to look through is counted whole, since it is refused as too
1141/// long either way and the question is a walk over the head for each of its instructions.
1142///
1143/// An integer constant is not counted. On the machine it is an immediate in the instruction that
1144/// reads it, or at worst a move nothing waits on, so it is not work the other path would be paying
1145/// for. Counting it made the arm of `acc = c ? acc + 1 : acc` two instructions when `acc` is an
1146/// `int` and three when it is anything else, since the front end writes the `1` as an `int` and
1147/// converts it, and that one extra constant was the difference between a select and a branch.
1148fn work(func: &Func, head: Block, arm: Block) -> u32 {
1149    let whole = length(func, arm);
1150    if whole > heuristics::PHIOPT_ARM_SCAN_INSTRUCTIONS {
1151        return whole;
1152    }
1153    let done: Vec<Value> = func
1154        .insts(head)
1155        .filter(|&inst| func[inst].results == 1 && !func[inst].opcode.has_effects())
1156        .filter_map(|inst| func[inst].first_result)
1157        .collect();
1158    let repeated = |inst: Inst| {
1159        let data = func[inst];
1160        if data.results != 1 || data.opcode.has_effects() {
1161            return false;
1162        }
1163        let Some(value) = data.first_result else { return false };
1164        done.iter().any(|&there| same(func, there, value, 0))
1165    };
1166    let free = |inst: Inst| func[inst].opcode == Opcode::IConst;
1167    let count = func
1168        .insts(arm)
1169        .filter(|&inst| !func.is_terminator(inst) && !repeated(inst) && !free(inst))
1170        .count();
1171    u32::try_from(count).unwrap_or(u32::MAX)
1172}
1173
1174/// Whether the estimate leaves enough doubt about this branch to be worth removing it.
1175pub(crate) fn unpredictable(taken: Probability) -> bool {
1176    let margin = heuristics::PHIOPT_UNPREDICTABLE_MARGIN_PERCENT * (Probability::SCALE / 100);
1177    taken.parts() >= margin && taken.parts() <= Probability::SCALE - margin
1178}
1179
1180/// Moves the arms into the head, builds the selects and takes the branch out.
1181///
1182/// The order matters and is the reason this is one function. The branch goes first, so that what
1183/// the arms were doing can be appended to the head without anything having to be threaded around a
1184/// terminator. The selects are built after that work has moved, since they read what it produced.
1185/// The jump goes last because it is the terminator.
1186fn convert(
1187    func: &mut Func,
1188    shape: &Diamond,
1189    plan: &[Option<Factored>],
1190    store: Option<&Stored>,
1191    implied: &[Option<usize>],
1192) {
1193    let term = func.terminator(shape.head).expect("the head of a diamond ends in its branch");
1194    let span = func.span(term);
1195    func.remove_inst(term);
1196    let mut dropped: Vec<Inst> = plan.iter().flatten().flat_map(|one| one.insts).collect();
1197    dropped.extend(store.iter().flat_map(|one| one.insts.iter().copied()));
1198    for &arm in shape.arms.iter().flatten() {
1199        for inst in func.insts(arm).collect::<Vec<Inst>>() {
1200            if func.is_terminator(inst) {
1201                continue;
1202            }
1203            func.remove_inst(inst);
1204            // A factored operation is not moved, it is replaced. One copy of it is written below,
1205            // after the selects it reads, and these two are what that copy is instead of.
1206            if !dropped.contains(&inst) {
1207                func.append_inst(shape.head, inst);
1208            }
1209        }
1210    }
1211    let mut build = Builder::new(func, shape.head).at(span);
1212    let mut args = Vec::with_capacity(shape.args[0].len());
1213    for (index, (&then, &other)) in shape.args[0].iter().zip(&shape.args[1]).enumerate() {
1214        // A value the condition settled, passed on as it is. The side named is the one whose value
1215        // is right on both edges, which is the side the two were not shown to be equal on.
1216        if let Some(side) = implied.get(index).copied().flatten() {
1217            args.push(shape.args[side][index]);
1218            continue;
1219        }
1220        if let Some(one) = &plan[index] {
1221            let mut operands = one.operands.clone();
1222            if let Some((at, sides)) = one.differ {
1223                operands[at] = build.select(shape.cond, sides[0], sides[1]);
1224            }
1225            let list = build.func().push_values(&operands);
1226            args.push(build.value(InstData { args: list, ..one.data }, one.ty));
1227            continue;
1228        }
1229        // The condition holds on the first side, which is the side `select` takes when the bit is
1230        // one, so the order the branch named its targets in is the order the arguments go in.
1231        let same = agree(build.func(), then, other);
1232        args.push(if same { then } else { build.select(shape.cond, then, other) });
1233    }
1234    // After everything the arms were doing has moved, because the value being stored is often one
1235    // of the things they were working out, and before the jump because the jump is the terminator.
1236    if let Some(one) = store {
1237        // The side that made no store writes back what it finds, and it finds it here, after
1238        // everything the arms did and before the one store, which is where the side that stored
1239        // had not yet done it.
1240        let old = one.values.contains(&None).then(|| {
1241            let Extra::Mem(mem) = one.data.extra else { unreachable!("a store says what it is") };
1242            let info = build.func()[mem];
1243            build.load(one.ty, one.addr, info, one.data.flags)
1244        });
1245        let [then, other] = one
1246            .values
1247            .map(|value| value.or(old).expect("a side that stored nothing reads what is there"));
1248        let same = agree(build.func(), then, other);
1249        let what = if same { then } else { build.select(shape.cond, then, other) };
1250        let list = build.func().push_values(&[what, one.addr]);
1251        build.inst(InstData { args: list, ..one.data }, &[]);
1252    }
1253    build.jump(shape.join, &args);
1254    // Nothing arrives at the arms now, and section 6.5 makes taking an unreachable block out the
1255    // standing obligation of whichever pass stranded it rather than something the next pass tidies
1256    // up. The verifier holds every pass to that.
1257    for &arm in shape.arms.iter().flatten() {
1258        func.remove_block(arm);
1259    }
1260}
1261
1262#[cfg(test)]
1263mod tests {
1264    use rucc_base::Interner;
1265    use rucc_ir::{
1266        Block, Builder, Extra, Flags, Float, Func, InstData, IntPred, MemInfo, MemOrder, Opcode,
1267        Restrict, Signature, Type, Value,
1268    };
1269
1270    use super::PhiOpt;
1271    use crate::profile::{Probability, Quality};
1272    use crate::stats::Kind;
1273    use crate::{Fuel, Pass, Stats};
1274
1275    /// Runs the pass with as much fuel as it wants.
1276    fn phiopt(func: &mut Func) -> Stats {
1277        PhiOpt.run(func, &mut crate::machine::fixtures::analyses(), &mut Fuel::unlimited())
1278    }
1279
1280    /// The blocks the function still has, by number.
1281    fn blocks(func: &Func) -> Vec<usize> {
1282        func.blocks().map(Block::index).collect()
1283    }
1284
1285    /// Where a block's terminator goes, as block numbers.
1286    fn goes_to(func: &Func, block: usize) -> Vec<usize> {
1287        let block = Block::from_usize(block);
1288        let term = func.terminator(block).expect("every block here has one");
1289        func.successors(term).map(|call| call.block.index()).collect()
1290    }
1291
1292    /// The opcodes a block holds, in order.
1293    fn opcodes(func: &Func, block: usize) -> Vec<Opcode> {
1294        let block = Block::from_usize(block);
1295        func.insts(block).map(|inst| func[inst].opcode).collect()
1296    }
1297
1298    /// What a block's terminator carries on its first edge.
1299    fn carries(func: &Func, block: usize) -> Vec<Value> {
1300        let block = Block::from_usize(block);
1301        let term = func.terminator(block).expect("every block here has one");
1302        let call = func.successors(term).next().expect("a terminator here has an edge");
1303        func[call.args].to_vec()
1304    }
1305
1306    /// Four aligned bytes, ordinary, with nothing known about aliasing.
1307    fn plain() -> MemInfo {
1308        MemInfo {
1309            size: 4,
1310            align: 4,
1311            order: MemOrder::NotAtomic,
1312            tbaa: None,
1313            owns: 0,
1314            restrict: Restrict::NONE,
1315        }
1316    }
1317
1318    /// A store, which is the instruction used here whenever something has to happen.
1319    fn store_something(build: &mut Builder<'_>) {
1320        let what = build.iconst(Type::int(32), 7);
1321        let address = build.iconst(Type::int(64), 16);
1322        let address = build.unary(Opcode::IntToPtr, address, Type::PTR);
1323        build.store(what, address, plain(), Flags::NONE);
1324    }
1325
1326    /// `if (x < 0) *p = a; else *p = b;`, with both stores told the same thing about the access.
1327    ///
1328    /// The address is a function parameter, so it is one value both arms name, and the two values
1329    /// written are the other two parameters. Block 0 is the head, blocks 1 and 2 are the arms and
1330    /// block 3 is the join, which takes nothing and returns.
1331    fn both_arms_store(info: MemInfo, flags: [Flags; 2], addresses: bool) -> Func {
1332        let mut names = Interner::new();
1333        let ints = [Type::PTR, Type::int(32), Type::int(32), Type::PTR];
1334        let signature = Signature::new().with_params(&ints);
1335        let mut func = Func::new(names.intern("f"), signature);
1336        let head = func.create_block();
1337        let address = func.append_param(head, Type::PTR);
1338        let written =
1339            [func.append_param(head, Type::int(32)), func.append_param(head, Type::int(32))];
1340        let elsewhere = func.append_param(head, Type::PTR);
1341        let arms = [func.create_block(), func.create_block()];
1342        let join = func.create_block();
1343
1344        let mut build = Builder::new(&mut func, head);
1345        let zero = build.iconst(Type::int(32), 0);
1346        let test = build.icmp(IntPred::Slt, written[0], zero);
1347        build.br_if(test, arms[0], &[], arms[1], &[]);
1348        for (index, arm) in arms.iter().enumerate() {
1349            let mut build = Builder::new(&mut func, *arm);
1350            let where_to = if addresses && index == 1 { elsewhere } else { address };
1351            build.store(written[index], where_to, info, flags[index]);
1352            build.jump(join, &[]);
1353        }
1354        let mut build = Builder::new(&mut func, join);
1355        build.ret(&[]);
1356        func
1357    }
1358
1359    /// `x < y ? a : b`, as a diamond whose two arms are empty.
1360    ///
1361    /// Block 0 is the head and takes the two values it compares as function parameters, blocks 1
1362    /// and 2 are the arms and carry one of two constants, and block 3 is the join and returns what
1363    /// it was given.
1364    fn empty_arms() -> Func {
1365        let mut names = Interner::new();
1366        let signature = Signature::new().with_params(&[Type::int(32), Type::int(32)]);
1367        let mut func = Func::new(names.intern("f"), signature);
1368        let head = func.create_block();
1369        let left = func.append_param(head, Type::int(32));
1370        let right = func.append_param(head, Type::int(32));
1371        let arms = [func.create_block(), func.create_block()];
1372        let join = func.create_block();
1373        let param = func.append_param(join, Type::int(32));
1374
1375        let mut build = Builder::new(&mut func, head);
1376        let test = build.icmp(IntPred::Slt, left, right);
1377        build.br_if(test, arms[0], &[], arms[1], &[]);
1378        for (arm, value) in arms.iter().zip([1, 2]) {
1379            let mut build = Builder::new(&mut func, *arm);
1380            let it = build.iconst(Type::int(32), value);
1381            build.jump(join, &[it]);
1382        }
1383        let mut build = Builder::new(&mut func, join);
1384        build.ret(&[param]);
1385        func
1386    }
1387
1388    #[test]
1389    fn a_branch_that_is_already_decided_is_left_for_simplify_cfg() {
1390        // What `if (1)` looks like by the time it gets here. Converting would build a select on a
1391        // constant and keep the arm that cannot run, and the pass that would fold it does not
1392        // exist, so the answer is to leave the branch alone and let the arm be deleted whole.
1393        let mut names = Interner::new();
1394        let mut func = Func::new(names.intern("f"), Signature::new());
1395        let head = func.create_block();
1396        let arms = [func.create_block(), func.create_block()];
1397        let join = func.create_block();
1398        let param = func.append_param(join, Type::int(32));
1399
1400        let mut build = Builder::new(&mut func, head);
1401        // What `if (1)` reaches this pass as. Not a constant, a comparison of two constants, since
1402        // `fold` will not turn an `icmp` into an `i1` that nothing lowers.
1403        let one = build.iconst(Type::int(32), 1);
1404        let zero = build.iconst(Type::int(32), 0);
1405        let test = build.icmp(IntPred::Ne, one, zero);
1406        build.br_if(test, arms[0], &[], arms[1], &[]);
1407        for (arm, value) in arms.iter().zip([1, 2]) {
1408            let mut build = Builder::new(&mut func, *arm);
1409            let it = build.iconst(Type::int(32), value);
1410            build.jump(join, &[it]);
1411        }
1412        let mut build = Builder::new(&mut func, join);
1413        build.ret(&[param]);
1414
1415        let stats = phiopt(&mut func);
1416        assert_eq!(stats.count(Kind::Missed, super::CONDITION_IS_DECIDED), 1);
1417        assert_eq!(blocks(&func), vec![0, 1, 2, 3]);
1418    }
1419
1420    #[test]
1421    fn a_diamond_whose_arms_are_empty_becomes_a_select() {
1422        let mut func = empty_arms();
1423        let stats = phiopt(&mut func);
1424        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 1);
1425        // The two constants moved up with the arms, and the select is what the branch was.
1426        assert_eq!(
1427            opcodes(&func, 0),
1428            vec![Opcode::ICmp, Opcode::IConst, Opcode::IConst, Opcode::Select, Opcode::Jump]
1429        );
1430        assert_eq!(goes_to(&func, 0), vec![3]);
1431        assert_eq!(blocks(&func), vec![0, 3]);
1432    }
1433
1434    #[test]
1435    fn the_side_the_condition_holds_on_is_the_side_the_select_takes_first() {
1436        let mut func = empty_arms();
1437        phiopt(&mut func);
1438        let select = func
1439            .insts(Block::from_usize(0))
1440            .find(|&inst| func[inst].opcode == Opcode::Select)
1441            .expect("the select the pass just built");
1442        let args = func[func[select].args].to_vec();
1443        let one = crate::fold::constant(&func, args[1]).expect("the true arm carried a constant");
1444        let two = crate::fold::constant(&func, args[2]).expect("the false arm carried a constant");
1445        assert_eq!(one.0.unsigned(), 1, "the arm the branch named first");
1446        assert_eq!(two.0.unsigned(), 2, "the arm the branch named second");
1447    }
1448
1449    /// A triangle: one side goes straight to the join carrying what it already had.
1450    #[test]
1451    fn a_triangle_whose_empty_side_goes_straight_to_the_join_is_converted() {
1452        let mut names = Interner::new();
1453        let signature = Signature::new().with_params(&[Type::int(32)]);
1454        let mut func = Func::new(names.intern("f"), signature);
1455        let head = func.create_block();
1456        let outside = func.append_param(head, Type::int(32));
1457        let arm = func.create_block();
1458        let join = func.create_block();
1459        let param = func.append_param(join, Type::int(32));
1460
1461        let mut build = Builder::new(&mut func, head);
1462        let zero = build.iconst(Type::int(32), 0);
1463        let test = build.icmp(IntPred::Slt, outside, zero);
1464        build.br_if(test, arm, &[], join, &[outside]);
1465        let mut build = Builder::new(&mut func, arm);
1466        let it = build.iconst(Type::int(32), 0);
1467        build.jump(join, &[it]);
1468        let mut build = Builder::new(&mut func, join);
1469        build.ret(&[param]);
1470
1471        let stats = phiopt(&mut func);
1472        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 1);
1473        assert_eq!(blocks(&func), vec![0, 2]);
1474        assert_eq!(goes_to(&func, 0), vec![2]);
1475        assert_eq!(opcodes(&func, 0).last(), Some(&Opcode::Jump));
1476    }
1477
1478    #[test]
1479    fn a_parameter_both_sides_agree_about_needs_no_select() {
1480        let mut names = Interner::new();
1481        let signature = Signature::new().with_params(&[Type::int(32)]);
1482        let mut func = Func::new(names.intern("f"), signature);
1483        let head = func.create_block();
1484        let outside = func.append_param(head, Type::int(32));
1485        let arms = [func.create_block(), func.create_block()];
1486        let join = func.create_block();
1487        let param = func.append_param(join, Type::int(32));
1488
1489        let mut build = Builder::new(&mut func, head);
1490        let zero = build.iconst(Type::int(32), 0);
1491        let test = build.icmp(IntPred::Slt, outside, zero);
1492        build.br_if(test, arms[0], &[], arms[1], &[]);
1493        for arm in arms {
1494            let mut build = Builder::new(&mut func, arm);
1495            build.jump(join, &[outside]);
1496        }
1497        let mut build = Builder::new(&mut func, join);
1498        build.ret(&[param]);
1499
1500        let stats = phiopt(&mut func);
1501        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 1);
1502        assert!(!opcodes(&func, 0).contains(&Opcode::Select), "both sides carried the same value");
1503        assert_eq!(carries(&func, 0), vec![outside]);
1504    }
1505
1506    #[test]
1507    fn two_sides_carrying_the_same_number_need_no_select_either() {
1508        // `x ? 7 : 7`, which the corpus has eight of. The two sevens are two values, because
1509        // nothing has hash consed them into one, so asking only whether the values are equal
1510        // builds a select between two sevens and pays a compare and a conditional move for it.
1511        let mut names = Interner::new();
1512        let signature = Signature::new().with_params(&[Type::int(32)]);
1513        let mut func = Func::new(names.intern("f"), signature);
1514        let head = func.create_block();
1515        let outside = func.append_param(head, Type::int(32));
1516        let arms = [func.create_block(), func.create_block()];
1517        let join = func.create_block();
1518        let param = func.append_param(join, Type::int(32));
1519
1520        let mut build = Builder::new(&mut func, head);
1521        let zero = build.iconst(Type::int(32), 0);
1522        let test = build.icmp(IntPred::Slt, outside, zero);
1523        build.br_if(test, arms[0], &[], arms[1], &[]);
1524        for arm in arms {
1525            let mut build = Builder::new(&mut func, arm);
1526            let seven = build.iconst(Type::int(32), 7);
1527            build.jump(join, &[seven]);
1528        }
1529        let mut build = Builder::new(&mut func, join);
1530        build.ret(&[param]);
1531
1532        let stats = phiopt(&mut func);
1533        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 1);
1534        assert!(!opcodes(&func, 0).contains(&Opcode::Select), "both sides carried a seven");
1535    }
1536
1537    #[test]
1538    fn two_sides_carrying_different_numbers_still_get_a_select() {
1539        let mut func = empty_arms();
1540        let stats = phiopt(&mut func);
1541        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 1);
1542        assert!(opcodes(&func, 0).contains(&Opcode::Select), "one and two are not the same number");
1543    }
1544
1545    /// `x = (a == b) ? b : a`, as the diamond it arrives here as.
1546    ///
1547    /// Block 0 is the head and takes the two values, blocks 1 and 2 are the arms and each carries
1548    /// one of them to block 3, which returns what it was given. The comparison's predicate and the
1549    /// type of the two values are what the tests below vary.
1550    fn condition_settles_it(pred: IntPred, ty: Type) -> Func {
1551        let mut names = Interner::new();
1552        let signature = Signature::new().with_params(&[ty, ty]);
1553        let mut func = Func::new(names.intern("f"), signature);
1554        let head = func.create_block();
1555        let left = func.append_param(head, ty);
1556        let right = func.append_param(head, ty);
1557        let arms = [func.create_block(), func.create_block()];
1558        let join = func.create_block();
1559        let param = func.append_param(join, ty);
1560
1561        let mut build = Builder::new(&mut func, head);
1562        let test = build.icmp(pred, left, right);
1563        build.br_if(test, arms[0], &[], arms[1], &[]);
1564        // The side taken when the condition holds carries the right hand value, the other side
1565        // carries the left, which is what makes the two the same number on one edge and not the
1566        // other. Which side that is follows the predicate.
1567        for (arm, value) in arms.iter().zip([right, left]) {
1568            let mut build = Builder::new(&mut func, *arm);
1569            build.jump(join, &[value]);
1570        }
1571        let mut build = Builder::new(&mut func, join);
1572        build.ret(&[param]);
1573        func
1574    }
1575
1576    #[test]
1577    fn a_value_the_condition_says_is_the_other_one_needs_no_select() {
1578        let mut func = condition_settles_it(IntPred::Eq, Type::int(32));
1579        let stats = phiopt(&mut func);
1580        assert_eq!(stats.count(Kind::Optimized, super::VALUE_IMPLIED), 1);
1581        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 1);
1582        assert_eq!(opcodes(&func, 0), vec![Opcode::ICmp, Opcode::Jump]);
1583        // The value passed on is the one carried by the side the two were not shown equal on.
1584        let params = func[Block::from_usize(0)].params.to_vec();
1585        assert_eq!(carries(&func, 0), vec![params[0]]);
1586        assert_eq!(blocks(&func), vec![0, 3]);
1587    }
1588
1589    /// The same with the branch the other way round, where the equal side is the one not taken.
1590    #[test]
1591    fn an_inequality_settles_it_from_the_other_side() {
1592        let mut func = condition_settles_it(IntPred::Ne, Type::int(32));
1593        let stats = phiopt(&mut func);
1594        assert_eq!(stats.count(Kind::Optimized, super::VALUE_IMPLIED), 1);
1595        let params = func[Block::from_usize(0)].params.to_vec();
1596        assert_eq!(carries(&func, 0), vec![params[1]]);
1597    }
1598
1599    /// A pointer has no `select`, and a value nothing has to choose between does not need one.
1600    #[test]
1601    fn a_value_of_a_type_with_no_select_is_still_settled_by_the_condition() {
1602        let mut func = condition_settles_it(IntPred::Eq, Type::PTR);
1603        let stats = phiopt(&mut func);
1604        assert_eq!(stats.count(Kind::Missed, super::NO_SELECT_AT_THAT_WIDTH), 0);
1605        assert_eq!(stats.count(Kind::Optimized, super::VALUE_IMPLIED), 1);
1606        assert_eq!(opcodes(&func, 0), vec![Opcode::ICmp, Opcode::Jump]);
1607    }
1608
1609    /// A branch on anything but an equality is not asked about, and the select is written as usual.
1610    #[test]
1611    fn a_branch_that_is_not_an_equality_gets_its_select() {
1612        let mut func = condition_settles_it(IntPred::Slt, Type::int(32));
1613        let stats = phiopt(&mut func);
1614        assert_eq!(stats.count(Kind::Optimized, super::VALUE_IMPLIED), 0);
1615        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 1);
1616        assert!(opcodes(&func, 0).contains(&Opcode::Select));
1617    }
1618
1619    /// Two constants that are not the same number are not the same number on any edge.
1620    #[test]
1621    fn two_different_constants_are_not_asked_about() {
1622        let mut func = empty_arms();
1623        let stats = phiopt(&mut func);
1624        assert_eq!(stats.count(Kind::Optimized, super::VALUE_IMPLIED), 0);
1625        assert!(opcodes(&func, 0).contains(&Opcode::Select));
1626    }
1627
1628    #[test]
1629    fn a_store_both_arms_make_to_one_place_is_made_once_below_the_branch() {
1630        let mut func = both_arms_store(plain(), [Flags::NONE; 2], false);
1631        let stats = phiopt(&mut func);
1632        assert_eq!(stats.count(Kind::Optimized, super::STORE_REPLACED), 1);
1633        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 1);
1634        // One store, below the select that chooses what it writes, and no branch above either.
1635        assert_eq!(
1636            opcodes(&func, 0),
1637            vec![Opcode::IConst, Opcode::ICmp, Opcode::Select, Opcode::Store, Opcode::Jump]
1638        );
1639        assert_eq!(blocks(&func), vec![0, 3]);
1640        assert_eq!(goes_to(&func, 0), vec![3]);
1641    }
1642
1643    #[test]
1644    fn the_one_store_writes_what_the_side_the_condition_holds_on_was_writing() {
1645        let mut func = both_arms_store(plain(), [Flags::NONE; 2], false);
1646        phiopt(&mut func);
1647        let head = Block::from_usize(0);
1648        let select = func
1649            .insts(head)
1650            .find(|&inst| func[inst].opcode == Opcode::Select)
1651            .expect("the select the pass just built");
1652        let store = func
1653            .insts(head)
1654            .find(|&inst| func[inst].opcode == Opcode::Store)
1655            .expect("the one store that is left");
1656        let chosen = func[func[select].args].to_vec();
1657        let written = func[func[store].args].to_vec();
1658        // The head's parameters in order: the address, then what each side writes.
1659        let params = func[head].params.to_vec();
1660        assert_eq!(chosen[1], params[1], "the arm the branch named first");
1661        assert_eq!(chosen[2], params[2], "the arm the branch named second");
1662        assert_eq!(written[0], func[select].first_result.expect("a select produces one value"));
1663        assert_eq!(written[1], params[0], "the address both arms named");
1664    }
1665
1666    /// Both arms writing the same value needs no select, only the one store.
1667    #[test]
1668    fn two_arms_that_write_the_same_thing_get_a_store_and_no_select() {
1669        let mut func = both_arms_store(plain(), [Flags::NONE; 2], false);
1670        // Point the second arm's store at the first arm's value, which is what the front end
1671        // produces when both branches of a conditional assign the same thing.
1672        let head = Block::from_usize(0);
1673        let params = func[head].params.to_vec();
1674        let store = func
1675            .insts(Block::from_usize(2))
1676            .find(|&inst| func[inst].opcode == Opcode::Store)
1677            .expect("the second arm's store");
1678        let args = func.push_values(&[params[1], params[0]]);
1679        func[store].args = args;
1680
1681        let stats = phiopt(&mut func);
1682        assert_eq!(stats.count(Kind::Optimized, super::STORE_REPLACED), 1);
1683        assert_eq!(
1684            opcodes(&func, 0),
1685            vec![Opcode::IConst, Opcode::ICmp, Opcode::Store, Opcode::Jump]
1686        );
1687    }
1688
1689    /// Two stores to two different places is two writes, and doing both is writing one of them twice.
1690    #[test]
1691    fn two_arms_that_store_to_different_addresses_keep_their_branch() {
1692        let mut func = both_arms_store(plain(), [Flags::NONE; 2], true);
1693        let stats = phiopt(&mut func);
1694        assert_eq!(stats.count(Kind::Optimized, super::STORE_REPLACED), 0);
1695        assert_eq!(stats.count(Kind::Missed, super::STORES_DO_NOT_MATCH), 1);
1696        assert_eq!(goes_to(&func, 0), vec![1, 2]);
1697    }
1698
1699    #[test]
1700    fn a_volatile_store_keeps_its_branch_even_when_both_arms_make_it() {
1701        let mut func = both_arms_store(plain(), [Flags::VOLATILE; 2], false);
1702        let stats = phiopt(&mut func);
1703        assert_eq!(stats.count(Kind::Optimized, super::STORE_REPLACED), 0);
1704        assert_eq!(stats.count(Kind::Missed, super::STORES_DO_NOT_MATCH), 1);
1705        assert_eq!(goes_to(&func, 0), vec![1, 2]);
1706    }
1707
1708    #[test]
1709    fn an_atomic_store_keeps_its_branch_even_when_both_arms_make_it() {
1710        let mut func = both_arms_store(
1711            MemInfo { order: MemOrder::SeqCst, ..plain() },
1712            [Flags::NONE; 2],
1713            false,
1714        );
1715        let stats = phiopt(&mut func);
1716        assert_eq!(stats.count(Kind::Optimized, super::STORE_REPLACED), 0);
1717        assert_eq!(stats.count(Kind::Missed, super::STORES_DO_NOT_MATCH), 1);
1718    }
1719
1720    /// Two stores told different things about the access have no one answer to carry downward.
1721    #[test]
1722    fn two_stores_that_disagree_about_the_access_keep_their_branch() {
1723        let mut func = both_arms_store(plain(), [Flags::NONE; 2], false);
1724        let store = func
1725            .insts(Block::from_usize(2))
1726            .find(|&inst| func[inst].opcode == Opcode::Store)
1727            .expect("the second arm's store");
1728        let mem = func.add_mem(MemInfo { align: 1, ..plain() });
1729        func[store].extra = Extra::Mem(mem);
1730
1731        let stats = phiopt(&mut func);
1732        assert_eq!(stats.count(Kind::Optimized, super::STORE_REPLACED), 0);
1733        assert_eq!(stats.count(Kind::Missed, super::STORES_DO_NOT_MATCH), 1);
1734    }
1735
1736    /// The store comes off the work count, because one of the two arms was always going to make it.
1737    ///
1738    /// Each arm here holds the store and as much other work as the rule allows, so counting the
1739    /// store as work would put both arms one over the limit and the branch would stay.
1740    #[test]
1741    fn a_store_each_way_does_not_count_against_how_long_the_arms_may_be() {
1742        let mut func = both_arms_store(plain(), [Flags::NONE; 2], false);
1743        let params = func[Block::from_usize(0)].params.to_vec();
1744        for arm in [1, 2] {
1745            let block = Block::from_usize(arm);
1746            let term = func.terminator(block).expect("an arm ends in its jump");
1747            func.remove_inst(term);
1748            let mut build = Builder::new(&mut func, block);
1749            let mut value = params[1];
1750            for _ in 0..rucc_cost::heuristics::PHIOPT_ARM_INSTRUCTIONS {
1751                value = build.binary(Opcode::Add, value, params[2], Flags::NONE);
1752            }
1753            func.append_inst(block, term);
1754        }
1755
1756        let stats = phiopt(&mut func);
1757        assert_eq!(stats.count(Kind::Missed, super::ARMS_TOO_LONG), 0);
1758        assert_eq!(stats.count(Kind::Optimized, super::STORE_REPLACED), 1);
1759    }
1760
1761    /// A store one side makes and the other does not, which is the transformation with no proof.
1762    #[test]
1763    fn an_arm_that_stores_where_the_other_does_not_keeps_its_branch() {
1764        let mut names = Interner::new();
1765        let signature = Signature::new().with_params(&[Type::int(32)]);
1766        let mut func = Func::new(names.intern("f"), signature);
1767        let head = func.create_block();
1768        let outside = func.append_param(head, Type::int(32));
1769        let arms = [func.create_block(), func.create_block()];
1770        let join = func.create_block();
1771        let param = func.append_param(join, Type::int(32));
1772
1773        let mut build = Builder::new(&mut func, head);
1774        let zero = build.iconst(Type::int(32), 0);
1775        let test = build.icmp(IntPred::Slt, outside, zero);
1776        build.br_if(test, arms[0], &[], arms[1], &[]);
1777        let mut build = Builder::new(&mut func, arms[0]);
1778        store_something(&mut build);
1779        let it = build.iconst(Type::int(32), 1);
1780        build.jump(join, &[it]);
1781        let mut build = Builder::new(&mut func, arms[1]);
1782        let it = build.iconst(Type::int(32), 2);
1783        build.jump(join, &[it]);
1784        let mut build = Builder::new(&mut func, join);
1785        build.ret(&[param]);
1786
1787        let stats = phiopt(&mut func);
1788        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 0);
1789        assert_eq!(stats.count(Kind::Missed, super::STORE_ON_ONE_PATH), 1);
1790        assert_eq!(goes_to(&func, 0), vec![1, 2]);
1791    }
1792
1793    /// `if (x < a[2]) a[2] = x;` on a local `int a[8]`, which is the one armed store section 22.6
1794    /// needs a proof for.
1795    ///
1796    /// Block 0 is the head, which makes the local and branches. Block 1 is the arm, which works the
1797    /// slot's address out a second time, the way a subscript is lowered, and stores to it. Block 2
1798    /// is the join. `escape` hands the local's address to memory first, and `read` says whether the
1799    /// head reads the slot or compares against zero instead.
1800    fn one_arm_stores(escape: bool, read: bool) -> Func {
1801        let mut names = Interner::new();
1802        let signature = Signature::new().with_params(&[Type::int(32)]);
1803        let mut func = Func::new(names.intern("f"), signature);
1804        let head = func.create_block();
1805        let written = func.append_param(head, Type::int(32));
1806        let arm = func.create_block();
1807        let join = func.create_block();
1808
1809        let mut build = Builder::new(&mut func, head);
1810        let mem = build.func().add_mem(MemInfo { size: 32, align: 16, ..plain() });
1811        let alloca = InstData { extra: Extra::Mem(mem), ..InstData::new(Opcode::Alloca) };
1812        let local = build.value(alloca, Type::PTR);
1813        if escape {
1814            let somewhere = build.iconst(Type::int(64), 16);
1815            let somewhere = build.unary(Opcode::IntToPtr, somewhere, Type::PTR);
1816            build.store(local, somewhere, MemInfo { size: 8, align: 8, ..plain() }, Flags::NONE);
1817        }
1818        let eight = build.iconst(Type::int(64), 8);
1819        let slot = build.binary(Opcode::PtrAdd, local, eight, Flags::NONE);
1820        let against = if read {
1821            build.load(Type::int(32), slot, plain(), Flags::NONE)
1822        } else {
1823            build.iconst(Type::int(32), 0)
1824        };
1825        let test = build.icmp(IntPred::Slt, written, against);
1826        build.br_if(test, arm, &[], join, &[]);
1827        let mut build = Builder::new(&mut func, arm);
1828        let eight = build.iconst(Type::int(64), 8);
1829        let slot = build.binary(Opcode::PtrAdd, local, eight, Flags::NONE);
1830        build.store(written, slot, plain(), Flags::NONE);
1831        build.jump(join, &[]);
1832        let mut build = Builder::new(&mut func, join);
1833        build.ret(&[]);
1834        func
1835    }
1836
1837    /// The fold GCC makes too, and the second address the arm works out costs nothing, since it is
1838    /// the head's address worked out again and would otherwise have put the arm over the limit.
1839    #[test]
1840    fn a_store_one_arm_makes_to_a_local_the_head_read_is_made_on_both_paths() {
1841        let mut func = one_arm_stores(false, true);
1842        let stats = phiopt(&mut func);
1843        assert_eq!(stats.count(Kind::Optimized, super::STORE_GUARDED), 1);
1844        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 1);
1845        assert_eq!(blocks(&func), vec![0, 2]);
1846        let ops = opcodes(&func, 0);
1847        assert_eq!(ops.iter().filter(|&&op| op == Opcode::Load).count(), 2);
1848        assert_eq!(ops[ops.len() - 3..], [Opcode::Select, Opcode::Store, Opcode::Jump]);
1849    }
1850
1851    /// A local whose address has left the function is one another thread could be writing.
1852    #[test]
1853    fn a_store_one_arm_makes_to_a_local_that_escaped_keeps_its_branch() {
1854        let mut func = one_arm_stores(true, true);
1855        let stats = phiopt(&mut func);
1856        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 0);
1857        assert_eq!(stats.count(Kind::Missed, super::STORE_ON_ONE_PATH), 1);
1858        assert_eq!(goes_to(&func, 0), vec![1, 2]);
1859    }
1860
1861    /// Without the head's read nothing says the other path ever reached the slot, and `a[k]` with a
1862    /// `k` the branch was guarding is the case where it did not.
1863    #[test]
1864    fn a_store_one_arm_makes_to_a_slot_the_head_never_touched_keeps_its_branch() {
1865        let mut func = one_arm_stores(false, false);
1866        let stats = phiopt(&mut func);
1867        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 0);
1868        assert_eq!(stats.count(Kind::Missed, super::STORE_ON_ONE_PATH), 1);
1869        assert_eq!(goes_to(&func, 0), vec![1, 2]);
1870    }
1871
1872    /// `x = *p; if (x < 0) x = *p;`, with the arm's load told `flags`.
1873    fn arm_reads(flags: Flags) -> Func {
1874        let mut names = Interner::new();
1875        let signature = Signature::new().with_params(&[Type::PTR]);
1876        let mut func = Func::new(names.intern("f"), signature);
1877        let head = func.create_block();
1878        let address = func.append_param(head, Type::PTR);
1879        let arm = func.create_block();
1880        let join = func.create_block();
1881        let param = func.append_param(join, Type::int(32));
1882
1883        let mut build = Builder::new(&mut func, head);
1884        let first = build.load(Type::int(32), address, plain(), Flags::NONE);
1885        let zero = build.iconst(Type::int(32), 0);
1886        let test = build.icmp(IntPred::Slt, first, zero);
1887        build.br_if(test, arm, &[], join, &[zero]);
1888        let mut build = Builder::new(&mut func, arm);
1889        let again = build.load(Type::int(32), address, plain(), flags);
1890        build.jump(join, &[again]);
1891        let mut build = Builder::new(&mut func, join);
1892        build.ret(&[param]);
1893        func
1894    }
1895
1896    #[test]
1897    fn a_load_in_an_arm_of_an_address_the_head_read_is_made_on_both_paths() {
1898        let mut func = arm_reads(Flags::NONE);
1899        let stats = phiopt(&mut func);
1900        assert_eq!(stats.count(Kind::Optimized, super::LOAD_SPECULATED), 1);
1901        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 1);
1902        assert_eq!(blocks(&func), vec![0, 2]);
1903    }
1904
1905    /// How many reads there are is what `volatile` makes observable, so the head's read proves
1906    /// nothing about a second one.
1907    #[test]
1908    fn a_volatile_load_in_an_arm_keeps_its_branch() {
1909        let mut func = arm_reads(Flags::VOLATILE);
1910        let stats = phiopt(&mut func);
1911        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 0);
1912        assert_eq!(stats.count(Kind::Missed, super::ARM_HAS_EFFECTS), 1);
1913        assert_eq!(goes_to(&func, 0), vec![1, 2]);
1914    }
1915
1916    /// A load, which is the effect that is not a store and gets the general answer.
1917    #[test]
1918    fn an_arm_that_does_something_else_keeps_its_branch() {
1919        let mut names = Interner::new();
1920        let signature = Signature::new().with_params(&[Type::int(32)]);
1921        let mut func = Func::new(names.intern("f"), signature);
1922        let head = func.create_block();
1923        let outside = func.append_param(head, Type::int(32));
1924        let arms = [func.create_block(), func.create_block()];
1925        let join = func.create_block();
1926        let param = func.append_param(join, Type::int(32));
1927
1928        let mut build = Builder::new(&mut func, head);
1929        let zero = build.iconst(Type::int(32), 0);
1930        let test = build.icmp(IntPred::Slt, outside, zero);
1931        build.br_if(test, arms[0], &[], arms[1], &[]);
1932        let mut build = Builder::new(&mut func, arms[0]);
1933        let address = build.iconst(Type::int(64), 16);
1934        let address = build.unary(Opcode::IntToPtr, address, Type::PTR);
1935        let it = build.load(Type::int(32), address, plain(), Flags::NONE);
1936        build.jump(join, &[it]);
1937        let mut build = Builder::new(&mut func, arms[1]);
1938        let it = build.iconst(Type::int(32), 2);
1939        build.jump(join, &[it]);
1940        let mut build = Builder::new(&mut func, join);
1941        build.ret(&[param]);
1942
1943        let stats = phiopt(&mut func);
1944        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 0);
1945        assert_eq!(stats.count(Kind::Missed, super::ARM_HAS_EFFECTS), 1);
1946        assert_eq!(goes_to(&func, 0), vec![1, 2]);
1947    }
1948
1949    /// A division whose divisor is not known cannot be moved onto the path that skipped it.
1950    #[test]
1951    fn an_arm_that_divides_by_something_unknown_keeps_its_branch() {
1952        let mut names = Interner::new();
1953        let signature = Signature::new().with_params(&[Type::int(32), Type::int(32)]);
1954        let mut func = Func::new(names.intern("f"), signature);
1955        let head = func.create_block();
1956        let left = func.append_param(head, Type::int(32));
1957        let right = func.append_param(head, Type::int(32));
1958        let arms = [func.create_block(), func.create_block()];
1959        let join = func.create_block();
1960        let param = func.append_param(join, Type::int(32));
1961
1962        let mut build = Builder::new(&mut func, head);
1963        let zero = build.iconst(Type::int(32), 0);
1964        let test = build.icmp(IntPred::Ne, right, zero);
1965        build.br_if(test, arms[0], &[], arms[1], &[]);
1966        let mut build = Builder::new(&mut func, arms[0]);
1967        let it = build.binary(Opcode::SDiv, left, right, Flags::NONE);
1968        build.jump(join, &[it]);
1969        let mut build = Builder::new(&mut func, arms[1]);
1970        let it = build.iconst(Type::int(32), 0);
1971        build.jump(join, &[it]);
1972        let mut build = Builder::new(&mut func, join);
1973        build.ret(&[param]);
1974
1975        let stats = phiopt(&mut func);
1976        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 0);
1977        assert_eq!(stats.count(Kind::Missed, super::ARM_MAY_TRAP), 1);
1978        assert_eq!(goes_to(&func, 0), vec![1, 2]);
1979    }
1980
1981    #[test]
1982    fn a_division_by_a_constant_that_is_not_zero_or_minus_one_is_moved() {
1983        let mut names = Interner::new();
1984        let signature = Signature::new().with_params(&[Type::int(32)]);
1985        let mut func = Func::new(names.intern("f"), signature);
1986        let head = func.create_block();
1987        let outside = func.append_param(head, Type::int(32));
1988        let arms = [func.create_block(), func.create_block()];
1989        let join = func.create_block();
1990        let param = func.append_param(join, Type::int(32));
1991
1992        let mut build = Builder::new(&mut func, head);
1993        let zero = build.iconst(Type::int(32), 0);
1994        let test = build.icmp(IntPred::Slt, outside, zero);
1995        build.br_if(test, arms[0], &[], arms[1], &[]);
1996        let mut build = Builder::new(&mut func, arms[0]);
1997        let three = build.iconst(Type::int(32), 3);
1998        let it = build.binary(Opcode::SDiv, outside, three, Flags::NONE);
1999        build.jump(join, &[it]);
2000        let mut build = Builder::new(&mut func, arms[1]);
2001        let it = build.iconst(Type::int(32), 0);
2002        build.jump(join, &[it]);
2003        let mut build = Builder::new(&mut func, join);
2004        build.ret(&[param]);
2005
2006        let stats = phiopt(&mut func);
2007        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 1);
2008        assert!(opcodes(&func, 0).contains(&Opcode::SDiv));
2009    }
2010
2011    /// Nothing chooses between two pointers, so the shape is matched and then left alone.
2012    #[test]
2013    fn a_value_no_select_is_lowered_for_keeps_its_branch() {
2014        let mut names = Interner::new();
2015        let signature = Signature::new().with_params(&[Type::int(32)]);
2016        let mut func = Func::new(names.intern("f"), signature);
2017        let head = func.create_block();
2018        let outside = func.append_param(head, Type::int(32));
2019        let arms = [func.create_block(), func.create_block()];
2020        let join = func.create_block();
2021        func.append_param(join, Type::PTR);
2022
2023        let mut build = Builder::new(&mut func, head);
2024        let zero = build.iconst(Type::int(32), 0);
2025        let test = build.icmp(IntPred::Slt, outside, zero);
2026        build.br_if(test, arms[0], &[], arms[1], &[]);
2027        for (arm, value) in arms.iter().zip([16, 32]) {
2028            let mut build = Builder::new(&mut func, *arm);
2029            let it = build.iconst(Type::int(64), value);
2030            let it = build.unary(Opcode::IntToPtr, it, Type::PTR);
2031            build.jump(join, &[it]);
2032        }
2033        let mut build = Builder::new(&mut func, join);
2034        build.ret(&[]);
2035
2036        let stats = phiopt(&mut func);
2037        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 0);
2038        assert_eq!(stats.count(Kind::Missed, super::NO_SELECT_AT_THAT_WIDTH), 1);
2039    }
2040
2041    /// And nothing chooses between two floats either, at any format.
2042    ///
2043    /// Worth its own test although the answer is the pointer one's, because this is the pass that
2044    /// decides it for every float in the language and `crates/rucc-codegen/src/quad.rs` says so in
2045    /// its own documentation: a conditional expression over two quads is a branch and a phi and
2046    /// stays one, so the back end never sees a `select` at that format and needs no lowering for
2047    /// one. A quad here rather than a `double` since the quad is the format with no register of its
2048    /// own arithmetic, which makes it the one a later change is most likely to reach for.
2049    #[test]
2050    fn a_choice_between_two_quads_keeps_its_branch_as_well() {
2051        let mut names = Interner::new();
2052        let quad = Type::float(Float::F128);
2053        let signature = Signature::new().with_params(&[Type::int(32)]);
2054        let mut func = Func::new(names.intern("f"), signature);
2055        let head = func.create_block();
2056        let outside = func.append_param(head, Type::int(32));
2057        let arms = [func.create_block(), func.create_block()];
2058        let join = func.create_block();
2059        func.append_param(join, quad);
2060
2061        let mut build = Builder::new(&mut func, head);
2062        let zero = build.iconst(Type::int(32), 0);
2063        let test = build.icmp(IntPred::Slt, outside, zero);
2064        build.br_if(test, arms[0], &[], arms[1], &[]);
2065        for (arm, bits) in arms.iter().zip([0x3fff_u128 << 112, 0x4000_u128 << 112]) {
2066            let mut build = Builder::new(&mut func, *arm);
2067            let it = build.fconst(quad, bits);
2068            build.jump(join, &[it]);
2069        }
2070        let mut build = Builder::new(&mut func, join);
2071        build.ret(&[]);
2072
2073        let stats = phiopt(&mut func);
2074        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 0);
2075        assert_eq!(stats.count(Kind::Missed, super::NO_SELECT_AT_THAT_WIDTH), 1);
2076    }
2077
2078    #[test]
2079    fn arms_with_more_work_in_them_than_the_budget_keep_their_branch() {
2080        let mut names = Interner::new();
2081        let signature = Signature::new().with_params(&[Type::int(32)]);
2082        let mut func = Func::new(names.intern("f"), signature);
2083        let head = func.create_block();
2084        let outside = func.append_param(head, Type::int(32));
2085        let arms = [func.create_block(), func.create_block()];
2086        let join = func.create_block();
2087        let param = func.append_param(join, Type::int(32));
2088
2089        let mut build = Builder::new(&mut func, head);
2090        let zero = build.iconst(Type::int(32), 0);
2091        let test = build.icmp(IntPred::Slt, outside, zero);
2092        build.br_if(test, arms[0], &[], arms[1], &[]);
2093        let mut build = Builder::new(&mut func, arms[0]);
2094        // Four instructions, which is past the budget however cheap each of them is.
2095        let mut it = outside;
2096        for _ in 0..4 {
2097            it = build.binary(Opcode::Add, it, outside, Flags::NONE);
2098        }
2099        build.jump(join, &[it]);
2100        let mut build = Builder::new(&mut func, arms[1]);
2101        let it = build.iconst(Type::int(32), 0);
2102        build.jump(join, &[it]);
2103        let mut build = Builder::new(&mut func, join);
2104        build.ret(&[param]);
2105
2106        let stats = phiopt(&mut func);
2107        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 0);
2108        assert_eq!(stats.count(Kind::Missed, super::ARMS_TOO_LONG), 1);
2109    }
2110
2111    /// An arm whose work is one addition and two constants, one of them left over.
2112    ///
2113    /// This is `acc = v < k ? acc + 1 : acc` with `acc` an `unsigned long long`, as the front end
2114    /// hands it over: the `1` written as an `int`, the same `1` as the add reads it, and the add.
2115    /// Only the add is work, so the arm fits and the branch goes.
2116    #[test]
2117    fn constants_in_an_arm_are_not_counted_as_work() {
2118        let mut names = Interner::new();
2119        let signature = Signature::new().with_params(&[Type::int(32), Type::int(64)]);
2120        let mut func = Func::new(names.intern("f"), signature);
2121        let head = func.create_block();
2122        let outside = func.append_param(head, Type::int(32));
2123        let acc = func.append_param(head, Type::int(64));
2124        let arms = [func.create_block(), func.create_block()];
2125        let join = func.create_block();
2126        let param = func.append_param(join, Type::int(64));
2127
2128        let mut build = Builder::new(&mut func, head);
2129        let zero = build.iconst(Type::int(32), 0);
2130        let test = build.icmp(IntPred::Slt, outside, zero);
2131        build.br_if(test, arms[0], &[], arms[1], &[]);
2132        let mut build = Builder::new(&mut func, arms[0]);
2133        build.iconst(Type::int(32), 1);
2134        let one = build.iconst(Type::int(64), 1);
2135        let it = build.binary(Opcode::Add, acc, one, Flags::NONE);
2136        build.jump(join, &[it]);
2137        let mut build = Builder::new(&mut func, arms[1]);
2138        build.jump(join, &[acc]);
2139        let mut build = Builder::new(&mut func, join);
2140        build.ret(&[param]);
2141
2142        let stats = phiopt(&mut func);
2143        assert_eq!(stats.count(Kind::Missed, super::ARMS_TOO_LONG), 0);
2144        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 1);
2145    }
2146
2147    /// The same arm with a third operation in it is still too long, constants or not.
2148    #[test]
2149    fn an_arm_of_three_operations_is_too_long_with_its_constants_free() {
2150        let mut names = Interner::new();
2151        let signature = Signature::new().with_params(&[Type::int(32), Type::int(64)]);
2152        let mut func = Func::new(names.intern("f"), signature);
2153        let head = func.create_block();
2154        let outside = func.append_param(head, Type::int(32));
2155        let acc = func.append_param(head, Type::int(64));
2156        let arms = [func.create_block(), func.create_block()];
2157        let join = func.create_block();
2158        let param = func.append_param(join, Type::int(64));
2159
2160        let mut build = Builder::new(&mut func, head);
2161        let zero = build.iconst(Type::int(32), 0);
2162        let test = build.icmp(IntPred::Slt, outside, zero);
2163        build.br_if(test, arms[0], &[], arms[1], &[]);
2164        let mut build = Builder::new(&mut func, arms[0]);
2165        let mut it = acc;
2166        for step in 1..=3 {
2167            let by = build.iconst(Type::int(64), step);
2168            it = build.binary(Opcode::Mul, it, by, Flags::NONE);
2169        }
2170        build.jump(join, &[it]);
2171        let mut build = Builder::new(&mut func, arms[1]);
2172        build.jump(join, &[acc]);
2173        let mut build = Builder::new(&mut func, join);
2174        build.ret(&[param]);
2175
2176        let stats = phiopt(&mut func);
2177        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 0);
2178        assert_eq!(stats.count(Kind::Missed, super::ARMS_TOO_LONG), 1);
2179    }
2180
2181    /// The margin, at the two ends of it and just outside.
2182    ///
2183    /// A pass level test of the refusal it guards is not written, and the module doc says why: a
2184    /// diamond is the one shape none of document 11's one sided predictors can key on, so every
2185    /// branch this pass matches comes back even until `__builtin_expect` is wired through the
2186    /// front end. The arithmetic is what there is to check today.
2187    #[test]
2188    fn the_margin_is_a_quarter_in_from_each_end() {
2189        let guessed = |percent: u32| Probability::percent(percent, Quality::Guessed);
2190        assert!(super::unpredictable(Probability::even()));
2191        assert!(super::unpredictable(guessed(25)));
2192        assert!(super::unpredictable(guessed(75)));
2193        assert!(!super::unpredictable(guessed(24)));
2194        assert!(!super::unpredictable(guessed(76)));
2195        assert!(!super::unpredictable(Probability::always()));
2196        assert!(!super::unpredictable(Probability::never()));
2197    }
2198
2199    #[test]
2200    fn an_arm_that_two_edges_reach_is_not_an_arm() {
2201        let mut names = Interner::new();
2202        let signature = Signature::new().with_params(&[Type::int(32)]);
2203        let mut func = Func::new(names.intern("f"), signature);
2204        let head = func.create_block();
2205        let outside = func.append_param(head, Type::int(32));
2206        let above = func.create_block();
2207        let arms = [func.create_block(), func.create_block()];
2208        let join = func.create_block();
2209        let param = func.append_param(join, Type::int(32));
2210
2211        // The entry reaches the first arm as well as the head does, so moving the arm's work into
2212        // the head would leave the entry's path without it.
2213        let mut build = Builder::new(&mut func, head);
2214        let zero = build.iconst(Type::int(32), 0);
2215        let first = build.icmp(IntPred::Slt, outside, zero);
2216        build.br_if(first, above, &[], arms[0], &[]);
2217        let mut build = Builder::new(&mut func, above);
2218        let one = build.iconst(Type::int(32), 1);
2219        let second = build.icmp(IntPred::Slt, outside, one);
2220        build.br_if(second, arms[0], &[], arms[1], &[]);
2221        for (arm, value) in arms.iter().zip([1, 2]) {
2222            let mut build = Builder::new(&mut func, *arm);
2223            let it = build.iconst(Type::int(32), value);
2224            build.jump(join, &[it]);
2225        }
2226        let mut build = Builder::new(&mut func, join);
2227        build.ret(&[param]);
2228
2229        let stats = phiopt(&mut func);
2230        // Neither branch is a diamond. The head's first side goes to a block that is not the join
2231        // and is not an arm either, since two edges reach it, and the second branch's first side
2232        // is the same block for the same reason.
2233        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 0);
2234        assert_eq!(goes_to(&func, 0), vec![1, 2]);
2235        assert_eq!(goes_to(&func, 1), vec![2, 3]);
2236    }
2237
2238    #[test]
2239    fn fuel_stops_the_conversion_where_it_stands() {
2240        let mut func = empty_arms();
2241        let mut fuel = Fuel::of(0);
2242        let stats = PhiOpt.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut fuel);
2243        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 0);
2244        assert_eq!(stats.count(Kind::Missed, super::NO_FUEL), 1);
2245        assert_eq!(goes_to(&func, 0), vec![1, 2]);
2246    }
2247
2248    /// `x < y ? f(a, k) : f(b, k)`, as a diamond whose two arms do the same thing to different
2249    /// operands.
2250    ///
2251    /// Block 0 is the head, taking the two values it compares and the two operands and working out
2252    /// the operand both arms share. Blocks 1 and 2 are the arms, each applying every one of
2253    /// `steps` to its own operand and that shared value, and block 3 is the join, taking one
2254    /// parameter for each of them.
2255    fn same_operation(steps: &[Opcode]) -> Func {
2256        let mut names = Interner::new();
2257        let int = Type::int(32);
2258        let signature = Signature::new().with_params(&[int, int, int, int]);
2259        let mut func = Func::new(names.intern("f"), signature);
2260        let head = func.create_block();
2261        let left = func.append_param(head, int);
2262        let right = func.append_param(head, int);
2263        let operands = [func.append_param(head, int), func.append_param(head, int)];
2264        let arms = [func.create_block(), func.create_block()];
2265        let join = func.create_block();
2266        let params: Vec<Value> = steps.iter().map(|_| func.append_param(join, int)).collect();
2267
2268        let mut build = Builder::new(&mut func, head);
2269        // In the head rather than in each arm, so that the two sides share this operand as one
2270        // value. Two arms that each work out their own three are two operations apart, not one.
2271        let shared = build.iconst(int, 3);
2272        let test = build.icmp(IntPred::Slt, left, right);
2273        build.br_if(test, arms[0], &[], arms[1], &[]);
2274        for (&arm, operand) in arms.iter().zip(operands) {
2275            let mut build = Builder::new(&mut func, arm);
2276            let carried: Vec<Value> = steps
2277                .iter()
2278                .map(|&opcode| build.binary(opcode, operand, shared, Flags::default()))
2279                .collect();
2280            build.jump(join, &carried);
2281        }
2282        let mut build = Builder::new(&mut func, join);
2283        build.ret(&params);
2284        func
2285    }
2286
2287    /// The transformation. Two adds become one add of a select, rather than one select of two adds.
2288    #[test]
2289    fn an_operation_both_arms_did_is_done_once_below_the_branch() {
2290        let mut func = same_operation(&[Opcode::Add]);
2291        let stats = phiopt(&mut func);
2292        assert_eq!(stats.count(Kind::Optimized, super::FACTORED), 1);
2293        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 1);
2294        assert_eq!(
2295            opcodes(&func, 0),
2296            vec![Opcode::IConst, Opcode::ICmp, Opcode::Select, Opcode::Add, Opcode::Jump],
2297            "the select chooses the operand and the add happens once"
2298        );
2299        assert_eq!(blocks(&func), vec![0, 3]);
2300    }
2301
2302    /// The select goes under the operation, so what it chooses between is the operands and not the
2303    /// answers. Getting that the wrong way round would be a select of two adds that happens to have
2304    /// the right opcodes in it.
2305    #[test]
2306    fn the_select_chooses_the_operands_and_not_the_answers() {
2307        let mut func = same_operation(&[Opcode::Add]);
2308        phiopt(&mut func);
2309        let head = Block::from_usize(0);
2310        let select = func
2311            .insts(head)
2312            .find(|&inst| func[inst].opcode == Opcode::Select)
2313            .expect("the select the pass just built");
2314        let add = func
2315            .insts(head)
2316            .find(|&inst| func[inst].opcode == Opcode::Add)
2317            .expect("the add the pass just wrote");
2318        let chosen = func[func[select].args].to_vec();
2319        let params = func[head].params.to_vec();
2320        assert_eq!(&chosen[1..], &params[2..], "the two operands the arms differed in");
2321        let added = func[func[add].args].to_vec();
2322        assert_eq!(added[0], func[select].first_result.expect("a select has a result"));
2323        assert_eq!(carries(&func, 0), vec![func[add].first_result.expect("an add has a result")]);
2324    }
2325
2326    /// Nothing is speculated by an operation both arms were doing, so the length rule is about what
2327    /// is left after the factoring rather than about what the arms arrived holding. Three
2328    /// instructions an arm is over the limit, and three instructions that all factor is none.
2329    #[test]
2330    fn arms_that_factor_away_entirely_are_not_too_long() {
2331        let steps = [Opcode::Add, Opcode::Sub, Opcode::Mul];
2332        let mut func = same_operation(&steps);
2333        let stats = phiopt(&mut func);
2334        assert_eq!(stats.count(Kind::Missed, super::ARMS_TOO_LONG), 0);
2335        assert_eq!(stats.count(Kind::Optimized, super::FACTORED), 3);
2336        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 1);
2337        let written = opcodes(&func, 0);
2338        assert_eq!(written.iter().filter(|&&op| op == Opcode::Select).count(), 3);
2339        for step in steps {
2340            assert_eq!(written.iter().filter(|&&op| op == step).count(), 1, "{step:?} once");
2341        }
2342    }
2343
2344    /// Both arms doing the same thing to the same operands is a common subexpression nothing has
2345    /// numbered, and one copy of it serves both sides with no select at all.
2346    #[test]
2347    fn arms_that_agree_in_every_operand_need_no_select() {
2348        let mut names = Interner::new();
2349        let int = Type::int(32);
2350        let signature = Signature::new().with_params(&[int, int, int]);
2351        let mut func = Func::new(names.intern("f"), signature);
2352        let head = func.create_block();
2353        let left = func.append_param(head, int);
2354        let right = func.append_param(head, int);
2355        let operand = func.append_param(head, int);
2356        let arms = [func.create_block(), func.create_block()];
2357        let join = func.create_block();
2358        let param = func.append_param(join, int);
2359
2360        let mut build = Builder::new(&mut func, head);
2361        let shared = build.iconst(int, 3);
2362        let test = build.icmp(IntPred::Slt, left, right);
2363        build.br_if(test, arms[0], &[], arms[1], &[]);
2364        for &arm in &arms {
2365            let mut build = Builder::new(&mut func, arm);
2366            let it = build.binary(Opcode::Add, operand, shared, Flags::default());
2367            build.jump(join, &[it]);
2368        }
2369        let mut build = Builder::new(&mut func, join);
2370        build.ret(&[param]);
2371
2372        let stats = phiopt(&mut func);
2373        assert_eq!(stats.count(Kind::Optimized, super::FACTORED), 1);
2374        assert_eq!(
2375            opcodes(&func, 0),
2376            vec![Opcode::IConst, Opcode::ICmp, Opcode::Add, Opcode::Jump],
2377            "one add and nothing to choose between"
2378        );
2379    }
2380
2381    /// Two different operations are two operations, and the pass falls back to hoisting both and
2382    /// selecting between what they produced.
2383    #[test]
2384    fn arms_that_do_different_things_are_not_factored() {
2385        let mut names = Interner::new();
2386        let int = Type::int(32);
2387        let signature = Signature::new().with_params(&[int, int, int, int]);
2388        let mut func = Func::new(names.intern("f"), signature);
2389        let head = func.create_block();
2390        let left = func.append_param(head, int);
2391        let right = func.append_param(head, int);
2392        let operands = [func.append_param(head, int), func.append_param(head, int)];
2393        let arms = [func.create_block(), func.create_block()];
2394        let join = func.create_block();
2395        let param = func.append_param(join, int);
2396
2397        let mut build = Builder::new(&mut func, head);
2398        let shared = build.iconst(int, 3);
2399        let test = build.icmp(IntPred::Slt, left, right);
2400        build.br_if(test, arms[0], &[], arms[1], &[]);
2401        for ((&arm, operand), opcode) in arms.iter().zip(operands).zip([Opcode::Add, Opcode::Sub]) {
2402            let mut build = Builder::new(&mut func, arm);
2403            let it = build.binary(opcode, operand, shared, Flags::default());
2404            build.jump(join, &[it]);
2405        }
2406        let mut build = Builder::new(&mut func, join);
2407        build.ret(&[param]);
2408
2409        let stats = phiopt(&mut func);
2410        assert_eq!(stats.count(Kind::Optimized, super::FACTORED), 0);
2411        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 1);
2412        assert_eq!(
2413            opcodes(&func, 0),
2414            vec![
2415                Opcode::IConst,
2416                Opcode::ICmp,
2417                Opcode::Add,
2418                Opcode::Sub,
2419                Opcode::Select,
2420                Opcode::Jump
2421            ],
2422            "both operations hoisted and a select between their answers"
2423        );
2424    }
2425
2426    /// Two operand positions apart needs two selects and one operation, which is what one select
2427    /// and two operations already cost, so there is nothing to win and it is left alone.
2428    #[test]
2429    fn arms_that_differ_in_two_operands_are_not_factored() {
2430        let mut names = Interner::new();
2431        let int = Type::int(32);
2432        let signature = Signature::new().with_params(&[int, int, int, int, int, int]);
2433        let mut func = Func::new(names.intern("f"), signature);
2434        let head = func.create_block();
2435        let left = func.append_param(head, int);
2436        let right = func.append_param(head, int);
2437        let first = [func.append_param(head, int), func.append_param(head, int)];
2438        let second = [func.append_param(head, int), func.append_param(head, int)];
2439        let arms = [func.create_block(), func.create_block()];
2440        let join = func.create_block();
2441        let param = func.append_param(join, int);
2442
2443        let mut build = Builder::new(&mut func, head);
2444        let test = build.icmp(IntPred::Slt, left, right);
2445        build.br_if(test, arms[0], &[], arms[1], &[]);
2446        for ((&arm, one), two) in arms.iter().zip(first).zip(second) {
2447            let mut build = Builder::new(&mut func, arm);
2448            let it = build.binary(Opcode::Add, one, two, Flags::default());
2449            build.jump(join, &[it]);
2450        }
2451        let mut build = Builder::new(&mut func, join);
2452        build.ret(&[param]);
2453
2454        let stats = phiopt(&mut func);
2455        assert_eq!(stats.count(Kind::Optimized, super::FACTORED), 0);
2456        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 1);
2457        assert_eq!(opcodes(&func, 0).iter().filter(|&&op| op == Opcode::Add).count(), 2);
2458    }
2459
2460    /// The one copy is written after the arms have gone, so an operation something else in the arm
2461    /// reads cannot be one of the two it replaces. Here each arm hands its answer to the join
2462    /// twice, which is two readers and not one.
2463    #[test]
2464    fn an_operation_read_more_than_once_is_not_factored() {
2465        let mut names = Interner::new();
2466        let int = Type::int(32);
2467        let signature = Signature::new().with_params(&[int, int, int, int]);
2468        let mut func = Func::new(names.intern("f"), signature);
2469        let head = func.create_block();
2470        let left = func.append_param(head, int);
2471        let right = func.append_param(head, int);
2472        let operands = [func.append_param(head, int), func.append_param(head, int)];
2473        let arms = [func.create_block(), func.create_block()];
2474        let join = func.create_block();
2475        let params = [func.append_param(join, int), func.append_param(join, int)];
2476
2477        let mut build = Builder::new(&mut func, head);
2478        let shared = build.iconst(int, 3);
2479        let test = build.icmp(IntPred::Slt, left, right);
2480        build.br_if(test, arms[0], &[], arms[1], &[]);
2481        for (&arm, operand) in arms.iter().zip(operands) {
2482            let mut build = Builder::new(&mut func, arm);
2483            let it = build.binary(Opcode::Add, operand, shared, Flags::default());
2484            build.jump(join, &[it, it]);
2485        }
2486        let mut build = Builder::new(&mut func, join);
2487        build.ret(&params);
2488
2489        let stats = phiopt(&mut func);
2490        assert_eq!(stats.count(Kind::Optimized, super::FACTORED), 0);
2491        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 1);
2492        assert_eq!(opcodes(&func, 0).iter().filter(|&&op| op == Opcode::Add).count(), 2);
2493    }
2494
2495    /// A triangle has a block on one side only, so there is no second operation to pair the first
2496    /// one with and nothing to factor.
2497    #[test]
2498    fn a_triangle_factors_nothing() {
2499        let mut names = Interner::new();
2500        let int = Type::int(32);
2501        let signature = Signature::new().with_params(&[int, int, int]);
2502        let mut func = Func::new(names.intern("f"), signature);
2503        let head = func.create_block();
2504        let left = func.append_param(head, int);
2505        let right = func.append_param(head, int);
2506        let operand = func.append_param(head, int);
2507        let arm = func.create_block();
2508        let join = func.create_block();
2509        let param = func.append_param(join, int);
2510
2511        let mut build = Builder::new(&mut func, head);
2512        let shared = build.iconst(int, 3);
2513        let test = build.icmp(IntPred::Slt, left, right);
2514        build.br_if(test, arm, &[], join, &[operand]);
2515        let mut build = Builder::new(&mut func, arm);
2516        let it = build.binary(Opcode::Add, operand, shared, Flags::default());
2517        build.jump(join, &[it]);
2518        let mut build = Builder::new(&mut func, join);
2519        build.ret(&[param]);
2520
2521        let stats = phiopt(&mut func);
2522        assert_eq!(stats.count(Kind::Optimized, super::FACTORED), 0);
2523        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 1);
2524    }
2525}