Skip to main content

rucc_opt/
phiopt.rs

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