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