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    match cfg.predecessors(block) {
507        [only] if *only == head => {}
508        _ => return None,
509    }
510    let term = func.terminator(block)?;
511    if func[term].opcode != Opcode::Jump {
512        return None;
513    }
514    Some(func.successors(term).next()?.block)
515}
516
517/// Why this diamond is left alone, or `None` when nothing is in the way.
518///
519/// The store plan is passed in because the two stores it names are the one pair of instructions
520/// with effects this pass is allowed to move, and everything else with an effect still refuses.
521fn refused(
522    func: &Func,
523    shape: &Diamond,
524    store: Option<&Stored>,
525    implied: &[Option<usize>],
526) -> Option<&'static str> {
527    // A branch nobody has to take is not a branch worth removing. `simplify-cfg` runs after this
528    // pass and turns a decided branch into a jump, and then the arm that cannot run is deleted
529    // whole. Converting first replaces a branch that costs nothing with a select that costs
530    // something, and the fold that would undo it is a rule the set does not have yet, so the work
531    // in the arm that never ran survives into the machine code. The corpus found this on `if (1)`.
532    //
533    // The question is put to `simplify-cfg` rather than answered again here, for the reason its
534    // own documentation gives: two answers about when a branch is decided would be two compilers.
535    // It matters in this case, because the condition on `if (1)` is not a constant, it is a
536    // comparison of two constants, which `fold` deliberately leaves standing.
537    let term = func.terminator(shape.head).expect("the head of a diamond ends in its branch");
538    if simplify_cfg::taken(func, term, &Bindings::new()).is_some() {
539        return Some(CONDITION_IS_DECIDED);
540    }
541    let moving = store.map(|one| one.insts);
542    for &arm in shape.arms.iter().flatten() {
543        for inst in func.insts(arm) {
544            if func.is_terminator(inst) || moving.is_some_and(|two| two.contains(&inst)) {
545                continue;
546            }
547            if func[inst].opcode == Opcode::Store {
548                // Named separately from the effects below because it is a different answer rather
549                // than a stricter one. A store the other side does not make is section 22.2's
550                // fourth transformation without its proof, and section 22.6 calls it the worst bug
551                // in the document: making it on both paths writes memory the program was not going
552                // to write, which is not a no-op if another thread is writing the same bytes and is
553                // not a no-op if the page is read only. What would license it is knowing the
554                // location is written whatever happens, and nothing here knows that yet.
555                return Some(mismatch(func, shape));
556            }
557            if func[inst].opcode.has_effects() {
558                return Some(ARM_HAS_EFFECTS);
559            }
560            if !speculatable(func, inst) {
561                return Some(ARM_MAY_TRAP);
562            }
563        }
564    }
565    let params = func[shape.join].params.to_vec();
566    for (index, &param) in params.iter().enumerate() {
567        // The two sides agreeing about a parameter is the common case in a triangle, where one
568        // side passes on what it was already holding, and it needs no select at all. Neither does
569        // one the condition settles, which is why this is asked after that question rather than
570        // before it: a value of a type nothing can select between is fine when nothing has to.
571        if agree(func, shape.args[0][index], shape.args[1][index]) {
572            continue;
573        }
574        if implied.get(index).copied().flatten().is_some() {
575            continue;
576        }
577        if !selectable(func[param].ty) {
578            return Some(NO_SELECT_AT_THAT_WIDTH);
579        }
580    }
581    None
582}
583
584/// Whether the two sides hand the join the same thing, so that no `select` is needed for it.
585///
586/// The same value is the easy answer and it is the one a triangle gives, where one side passes on
587/// what it was already holding. The same constant is the answer the corpus asked for. `x ? 7 : 7`
588/// arrives here as two `iconst.i32 7` instructions, one in each arm, which are two values because
589/// nothing has hash consed them into one. Asking only about the value builds a `select` between two
590/// sevens, which costs a compare, a byte and a conditional move to work out that seven is seven.
591/// The module comment says what the general answer would be and why it is not available yet.
592fn agree(func: &Func, then: Value, other: Value) -> bool {
593    if then == other {
594        return true;
595    }
596    let (Some((left, lty)), Some((right, rty))) = (constant(func, then), constant(func, other))
597    else {
598        return false;
599    };
600    lty == rty && left == right
601}
602
603/// Whether doing this on a path that was not going to do it is harmless.
604///
605/// Only division asks anything here, because the caller has already refused everything with an
606/// effect and what is left is arithmetic. Zero is the divisor everybody knows about. Minus one is
607/// the other one: the smallest signed number divided by it is not representable and x86 raises the
608/// same exception it raises for zero.
609pub(crate) fn speculatable(func: &Func, inst: Inst) -> bool {
610    let opcode = func[inst].opcode;
611    if !matches!(opcode, Opcode::SDiv | Opcode::UDiv | Opcode::SRem | Opcode::URem) {
612        return true;
613    }
614    let Some(&divisor) = func[func[inst].args].get(1) else { return false };
615    let Some((imm, ty)) = constant(func, divisor) else { return false };
616    if imm.unsigned() == 0 {
617        return false;
618    }
619    imm.signed(ty) != -1
620}
621
622/// A store both arms make to the same place, which becomes one store below the branch.
623///
624/// Section 22.2's fourth transformation, in the half of it that needs no proof. `if (c) *p = a;
625/// else *p = b;` is `*p = c ? a : b`, and the number of stores is one before and one after, to the
626/// same address, of a value the program was going to write there on one path or the other.
627struct Stored {
628    /// The store each side wrote, which goes when the one copy below replaces both.
629    insts: [Inst; 2],
630    /// What each side wrote, taken in the order the branch names its targets.
631    values: [Value; 2],
632    /// The address, which is one value both sides named.
633    addr: Value,
634    /// The store to write once, whose value operand is replaced by the select above it.
635    data: InstData,
636}
637
638/// Which side's value serves for both, for each join parameter the branch condition settles.
639///
640/// Section 22.2's second transformation, `value_replacement`. `x = (a == b) ? b : a` is `x = a`,
641/// because the only way to arrive carrying `b` is along the edge where `a` and `b` are the same
642/// number. The select goes, and so does whichever arm was only there to work the other value out.
643///
644/// The answer for a parameter is the side whose value is passed on. If the two are known equal on
645/// one side's edge, then that side's value is the other side's value there, and the other side's
646/// value is right on both edges. Availability comes for free: everything both arms worked out is
647/// moved into the head before the jump is written, so a value that came from an arm is defined
648/// above the point it is now read at.
649///
650/// # Why the condition has to be an equality
651///
652/// This is the pass's one expensive question and section 22.7 says so. What answers it is document
653/// 10's relational oracle, which records what a dominating edge established between two values, and
654/// the edge out of a `br_if` establishes something about two values only when the branch is on a
655/// comparison of them. A branch on `x < n` says nothing about whether two other values are equal,
656/// so asking is a query that cannot come back with anything. Gating on the comparison being an
657/// equality is what turns this from a query per diamond into a query per diamond that could
658/// possibly answer, which on the corpus is a small fraction of them. GCC gates the same way, on
659/// `EQ_EXPR` and `NE_EXPR` at `gcc/tree-ssa-phiopt.cc`.
660fn implied(func: &Func, an: &mut Analyses, shape: &Diamond) -> Vec<Option<usize>> {
661    let count = shape.args[0].len();
662    let mut answers = vec![None; count];
663    if !equality(func, shape.cond) {
664        return answers;
665    }
666    let asking: Vec<usize> = (0..count).filter(|&index| worth_asking(func, shape, index)).collect();
667    if asking.is_empty() {
668        return answers;
669    }
670    // Cloned because the two are held at once and the cache hands out one borrow at a time. It is
671    // paid for only by a diamond that got this far, which the two gates above have already made
672    // rare, and section 22.7 is where the cost of this query was budgeted.
673    let cfg = an.cfg(func).clone();
674    let dom = an.dominators(func).clone();
675    let mut ranges = Ranges::new(func, &cfg, &dom);
676    for index in asking {
677        let pair = [shape.args[0][index], shape.args[1][index]];
678        for side in 0..2 {
679            let Some(block) = shape.arms[side] else { continue };
680            if ranges.compare(IntPred::Eq, pair[0], pair[1], block) == Truth::Always {
681                answers[index] = Some(1 - side);
682                break;
683            }
684        }
685    }
686    answers
687}
688
689/// Whether the branch is on a comparison that says two values are the same or are not.
690fn equality(func: &Func, cond: Value) -> bool {
691    let Def::Result { inst, .. } = func[cond].def else { return false };
692    if func[inst].opcode != Opcode::ICmp {
693        return false;
694    }
695    matches!(func[inst].extra, Extra::IntPred(IntPred::Eq | IntPred::Ne))
696}
697
698/// Whether this join parameter is one the oracle could have something to say about.
699///
700/// Two sides that agree need nothing. Two constants that are not the same number are not the same
701/// number on any edge, and asking is a query whose answer is already in hand.
702fn worth_asking(func: &Func, shape: &Diamond, index: usize) -> bool {
703    let pair = [shape.args[0][index], shape.args[1][index]];
704    if agree(func, pair[0], pair[1]) {
705        return false;
706    }
707    constant(func, pair[0]).is_none() || constant(func, pair[1]).is_none()
708}
709
710/// Which of the two store refusals this diamond is, once it is known to be one of them.
711///
712/// The two are worth separating because they say different things about what would fix them. One
713/// path storing is section 22.6's predicate, which is a proof nothing here can do. Both paths
714/// storing and not matching is usually two arms that worked the same address out separately, which
715/// is `a[i] = ...` on both sides, and what fixes that is document 16's value numbering making the
716/// two into one value rather than anything about memory.
717fn mismatch(func: &Func, shape: &Diamond) -> &'static str {
718    let [Some(then), Some(other)] = shape.arms else { return STORE_ON_ONE_PATH };
719    match (stored_in(func, then), stored_in(func, other)) {
720        (Some(_), Some(_)) => STORES_DO_NOT_MATCH,
721        _ => STORE_ON_ONE_PATH,
722    }
723}
724
725/// The store this diamond can move below the branch, if it has one.
726///
727/// Both sides have to have a block, which is what makes this the safe half of the transformation.
728/// A triangle has one side that is the join, and a store in the join already runs whichever way the
729/// branch went, so there is nothing here to move and the shape that reaches this with one arm is
730/// the one where a store happens on one path only. That one is refused above.
731fn storing(func: &Func, shape: &Diamond) -> Option<Stored> {
732    let [Some(then), Some(other)] = shape.arms else { return None };
733    let insts = [stored_in(func, then)?, stored_in(func, other)?];
734    let data = [func[insts[0]], func[insts[1]]];
735    // The flags are what the optimizer is licensed to assume about the access, so one store written
736    // under the union of two sets of assumptions would be claiming on one path something only the
737    // other path established. `volatile` is refused outright rather than by disagreeing, because
738    // section 22.6 says never and because the reason is not the flag matching: both how many
739    // accesses there are and what order they come in are observable, and a value that arrives
740    // through a select is a different program from one that arrives through a branch.
741    if data[0].flags != data[1].flags || data[0].flags.contains(Flags::VOLATILE) {
742        return None;
743    }
744    let (Extra::Mem(one), Extra::Mem(two)) = (data[0].extra, data[1].extra) else { return None };
745    // The alignment, the size, the aliasing node and the `restrict` scope, all of which the one
746    // store carries forward, so two that disagree about any of them have no single answer to carry.
747    if func[one] != func[two] || func[one].order != MemOrder::NotAtomic {
748        return None;
749    }
750    // A store names what it writes and then where, which is the order the builder takes them in.
751    let &[then, addr] = func[data[0].args].first_chunk::<2>()?;
752    let &[other, addr_two] = func[data[1].args].first_chunk::<2>()?;
753    // The same value for the address, which is stronger than the same address and is what can be
754    // checked without an alias analysis. It also settles where that value comes from: neither arm
755    // dominates the other, so a value both of them name is one worked out at or above the head, and
756    // the one store is written in the head where it is available.
757    if addr != addr_two || func[then].ty != func[other].ty {
758        return None;
759    }
760    if !agree(func, then, other) && !selectable(func[then].ty) {
761        return None;
762    }
763    Some(Stored { insts, values: [then, other], addr, data: data[0] })
764}
765
766/// The one store this arm makes, if it makes exactly one and does nothing else that has to happen.
767///
768/// Exactly one, because two stores below one select is two selects and a shape nothing has asked
769/// for. Nothing else with an effect, because everything else with an effect is still refused and
770/// this is the check that says so: an arm that stores and also calls something has a call that only
771/// happens on the path it is on, and no amount of agreement about the store changes that.
772fn stored_in(func: &Func, arm: Block) -> Option<Inst> {
773    let mut store = None;
774    for inst in func.insts(arm) {
775        if func.is_terminator(inst) || !func[inst].opcode.has_effects() {
776            continue;
777        }
778        if func[inst].opcode != Opcode::Store || store.is_some() {
779            return None;
780        }
781        store = Some(inst);
782    }
783    store
784}
785
786/// One join argument both arms worked out the same way, and the one operand they disagreed about.
787///
788/// Section 22.2's third transformation. `cond ? f(a) : f(b)` is `f(cond ? a : b)`, which is one
789/// operation where there were two and one select either way, and it is structural rather than a
790/// rewrite rule because the two `f`s are in different blocks and no pattern spans blocks.
791struct Factored {
792    /// The instruction each side wrote, which goes when the one copy below replaces both.
793    insts: [Inst; 2],
794    /// What each side handed that instruction, taken from the side taken when the condition holds.
795    operands: Vec<Value>,
796    /// The one position the two sides put different values in, and what each of them put there.
797    ///
798    /// `None` when they agree in every position, which is both arms computing the same thing from
799    /// the same operands. Then one copy serves both and there is no select at all.
800    differ: Option<(usize, [Value; 2])>,
801    /// The instruction to write once, whose operand list is replaced by the one above.
802    data: InstData,
803    /// What it produces.
804    ty: Type,
805}
806
807/// What can be factored out of each of the join's parameters, in the order the join takes them.
808///
809/// A triangle factors nothing. One of its sides is the join itself, so there is no block on that
810/// side holding an operation to pair the other one with, and what that side hands the join is a
811/// value worked out before the branch.
812fn factoring(func: &Func, shape: &Diamond, implied: &[Option<usize>]) -> Vec<Option<Factored>> {
813    let count = shape.args[0].len();
814    let [Some(then), Some(other)] = shape.arms else {
815        return (0..count).map(|_| None).collect();
816    };
817    (0..count)
818        .map(|index| {
819            // A value the condition settled is passed on whole, so there is no operation to write
820            // once below and the two that worked the two values out are left for dead code.
821            if implied.get(index).copied().flatten().is_some() {
822                return None;
823            }
824            factored(func, shape, [then, other], index)
825        })
826        .collect()
827}
828
829/// Whether this join argument is the same operation on both sides, and what to write instead.
830fn factored(func: &Func, shape: &Diamond, arms: [Block; 2], index: usize) -> Option<Factored> {
831    let sides = [shape.args[0][index], shape.args[1][index]];
832    // Two sides that agree need no operation written at all, and the caller passes the value on.
833    if agree(func, sides[0], sides[1]) {
834        return None;
835    }
836    let insts = [written_in(func, arms[0], sides[0])?, written_in(func, arms[1], sides[1])?];
837    let data = [func[insts[0]], func[insts[1]]];
838    // Everything about the two has to match except the operands. The flags are what the optimizer
839    // is licensed to assume, so writing one copy under the union of two sets of assumptions would
840    // be claiming on one path something only the other path established. The extra is whatever the
841    // instruction carries that is not an operand, which for a comparison is the predicate, and two
842    // predicates that differ are two different questions.
843    if data[0].opcode != data[1].opcode || data[0].flags != data[1].flags {
844        return None;
845    }
846    if data[0].extra != data[1].extra || func[sides[0]].ty != func[sides[1]].ty {
847        return None;
848    }
849    let operands = [func[data[0].args].to_vec(), func[data[1].args].to_vec()];
850    if operands[0].len() != operands[1].len() {
851        return None;
852    }
853    let mut apart =
854        operands[0].iter().zip(&operands[1]).enumerate().filter(|(_, (one, two))| one != two);
855    let differ = match (apart.next(), apart.next()) {
856        // Two positions apart would need two selects, and two selects and one operation is what
857        // one select and two operations already cost. There is nothing to win, so it is left.
858        (_, Some(_)) => return None,
859        (Some((at, (&one, &two))), None) => {
860            if func[one].ty != func[two].ty || !selectable(func[one].ty) {
861                return None;
862            }
863            Some((at, [one, two]))
864        }
865        (None, None) => None,
866    };
867    let ty = func[sides[0]].ty;
868    Some(Factored { insts, operands: operands[0].clone(), differ, data: data[0], ty })
869}
870
871/// The instruction in this arm that works out this value, if the arm is where it comes from and the
872/// only thing that reads it is the jump to the join.
873///
874/// Both halves are needed. The arm has to be where it is worked out, because an operation to factor
875/// out is one this pass is about to stop writing and it can only stop writing what it can find.
876/// Nothing else can read it, because the one copy that replaces the two is written after the arms
877/// have gone and a second reader in the arm would have been left pointing at an instruction that is
878/// no longer in any block.
879fn written_in(func: &Func, arm: Block, value: Value) -> Option<Inst> {
880    let inst = func
881        .insts(arm)
882        .find(|&inst| func[inst].results == 1 && func[inst].first_result == Some(value))?;
883    let mut seen = 0;
884    for inst in func.insts(arm) {
885        seen += func[func[inst].args].iter().filter(|&&arg| arg == value).count();
886        for call in func.successors(inst) {
887            seen += func[call.args].iter().filter(|&&arg| arg == value).count();
888        }
889    }
890    (seen == 1).then_some(inst)
891}
892
893/// Whether a value of this type is one a `select` can choose.
894///
895/// The four widths `crates/rucc-ir/src/term.rs` names a `select` at. A wider integer, a float, a
896/// pointer, a bit or a vector has no head, so a `select` of one would be a term the rule set has
897/// no lowering for and the failure would be at instruction selection rather than here.
898fn selectable(ty: Type) -> bool {
899    ty.is_scalar() && ty.is_int() && matches!(ty.bits(), 8 | 16 | 32 | 64)
900}
901
902/// How much work an arm does, not counting the jump that is about to go.
903pub(crate) fn length(func: &Func, block: Block) -> u32 {
904    let count = func.insts(block).filter(|&inst| !func.is_terminator(inst)).count();
905    u32::try_from(count).unwrap_or(u32::MAX)
906}
907
908/// Whether the estimate leaves enough doubt about this branch to be worth removing it.
909pub(crate) fn unpredictable(taken: Probability) -> bool {
910    let margin = heuristics::PHIOPT_UNPREDICTABLE_MARGIN_PERCENT * (Probability::SCALE / 100);
911    taken.parts() >= margin && taken.parts() <= Probability::SCALE - margin
912}
913
914/// Moves the arms into the head, builds the selects and takes the branch out.
915///
916/// The order matters and is the reason this is one function. The branch goes first, so that what
917/// the arms were doing can be appended to the head without anything having to be threaded around a
918/// terminator. The selects are built after that work has moved, since they read what it produced.
919/// The jump goes last because it is the terminator.
920fn convert(
921    func: &mut Func,
922    shape: &Diamond,
923    plan: &[Option<Factored>],
924    store: Option<&Stored>,
925    implied: &[Option<usize>],
926) {
927    let term = func.terminator(shape.head).expect("the head of a diamond ends in its branch");
928    let span = func.span(term);
929    func.remove_inst(term);
930    let mut dropped: Vec<Inst> = plan.iter().flatten().flat_map(|one| one.insts).collect();
931    dropped.extend(store.iter().flat_map(|one| one.insts));
932    for &arm in shape.arms.iter().flatten() {
933        for inst in func.insts(arm).collect::<Vec<Inst>>() {
934            if func.is_terminator(inst) {
935                continue;
936            }
937            func.remove_inst(inst);
938            // A factored operation is not moved, it is replaced. One copy of it is written below,
939            // after the selects it reads, and these two are what that copy is instead of.
940            if !dropped.contains(&inst) {
941                func.append_inst(shape.head, inst);
942            }
943        }
944    }
945    let mut build = Builder::new(func, shape.head).at(span);
946    let mut args = Vec::with_capacity(shape.args[0].len());
947    for (index, (&then, &other)) in shape.args[0].iter().zip(&shape.args[1]).enumerate() {
948        // A value the condition settled, passed on as it is. The side named is the one whose value
949        // is right on both edges, which is the side the two were not shown to be equal on.
950        if let Some(side) = implied.get(index).copied().flatten() {
951            args.push(shape.args[side][index]);
952            continue;
953        }
954        if let Some(one) = &plan[index] {
955            let mut operands = one.operands.clone();
956            if let Some((at, sides)) = one.differ {
957                operands[at] = build.select(shape.cond, sides[0], sides[1]);
958            }
959            let list = build.func().push_values(&operands);
960            args.push(build.value(InstData { args: list, ..one.data }, one.ty));
961            continue;
962        }
963        // The condition holds on the first side, which is the side `select` takes when the bit is
964        // one, so the order the branch named its targets in is the order the arguments go in.
965        let same = agree(build.func(), then, other);
966        args.push(if same { then } else { build.select(shape.cond, then, other) });
967    }
968    // After everything the arms were doing has moved, because the value being stored is often one
969    // of the things they were working out, and before the jump because the jump is the terminator.
970    if let Some(one) = store {
971        let [then, other] = one.values;
972        let same = agree(build.func(), then, other);
973        let what = if same { then } else { build.select(shape.cond, then, other) };
974        let list = build.func().push_values(&[what, one.addr]);
975        build.inst(InstData { args: list, ..one.data }, &[]);
976    }
977    build.jump(shape.join, &args);
978    // Nothing arrives at the arms now, and section 6.5 makes taking an unreachable block out the
979    // standing obligation of whichever pass stranded it rather than something the next pass tidies
980    // up. The verifier holds every pass to that.
981    for &arm in shape.arms.iter().flatten() {
982        func.remove_block(arm);
983    }
984}
985
986#[cfg(test)]
987mod tests {
988    use rucc_base::Interner;
989    use rucc_ir::{
990        Block, Builder, Flags, Func, IntPred, MemInfo, MemOrder, Opcode, Restrict, Signature, Type,
991        Value,
992    };
993
994    use super::PhiOpt;
995    use crate::profile::{Probability, Quality};
996    use crate::stats::Kind;
997    use crate::{Analyses, Fuel, Pass, Stats};
998
999    /// Runs the pass with as much fuel as it wants.
1000    fn phiopt(func: &mut Func) -> Stats {
1001        PhiOpt.run(func, &mut Analyses::new(), &mut Fuel::unlimited())
1002    }
1003
1004    /// The blocks the function still has, by number.
1005    fn blocks(func: &Func) -> Vec<usize> {
1006        func.blocks().map(Block::index).collect()
1007    }
1008
1009    /// Where a block's terminator goes, as block numbers.
1010    fn goes_to(func: &Func, block: usize) -> Vec<usize> {
1011        let block = Block::from_usize(block);
1012        let term = func.terminator(block).expect("every block here has one");
1013        func.successors(term).map(|call| call.block.index()).collect()
1014    }
1015
1016    /// The opcodes a block holds, in order.
1017    fn opcodes(func: &Func, block: usize) -> Vec<Opcode> {
1018        let block = Block::from_usize(block);
1019        func.insts(block).map(|inst| func[inst].opcode).collect()
1020    }
1021
1022    /// What a block's terminator carries on its first edge.
1023    fn carries(func: &Func, block: usize) -> Vec<Value> {
1024        let block = Block::from_usize(block);
1025        let term = func.terminator(block).expect("every block here has one");
1026        let call = func.successors(term).next().expect("a terminator here has an edge");
1027        func[call.args].to_vec()
1028    }
1029
1030    /// Four aligned bytes, ordinary, with nothing known about aliasing.
1031    fn plain() -> MemInfo {
1032        MemInfo {
1033            size: 4,
1034            align: 4,
1035            order: MemOrder::NotAtomic,
1036            tbaa: None,
1037            restrict: Restrict::NONE,
1038        }
1039    }
1040
1041    /// A store, which is the instruction used here whenever something has to happen.
1042    fn store_something(build: &mut Builder<'_>) {
1043        let what = build.iconst(Type::int(32), 7);
1044        let address = build.iconst(Type::int(64), 16);
1045        let address = build.unary(Opcode::IntToPtr, address, Type::PTR);
1046        build.store(what, address, plain(), Flags::NONE);
1047    }
1048
1049    /// `if (x < 0) *p = a; else *p = b;`, with both stores told the same thing about the access.
1050    ///
1051    /// The address is a function parameter, so it is one value both arms name, and the two values
1052    /// written are the other two parameters. Block 0 is the head, blocks 1 and 2 are the arms and
1053    /// block 3 is the join, which takes nothing and returns.
1054    fn both_arms_store(info: MemInfo, flags: [Flags; 2], addresses: bool) -> Func {
1055        let mut names = Interner::new();
1056        let ints = [Type::PTR, Type::int(32), Type::int(32), Type::PTR];
1057        let signature = Signature::new().with_params(&ints);
1058        let mut func = Func::new(names.intern("f"), signature);
1059        let head = func.create_block();
1060        let address = func.append_param(head, Type::PTR);
1061        let written =
1062            [func.append_param(head, Type::int(32)), func.append_param(head, Type::int(32))];
1063        let elsewhere = func.append_param(head, Type::PTR);
1064        let arms = [func.create_block(), func.create_block()];
1065        let join = func.create_block();
1066
1067        let mut build = Builder::new(&mut func, head);
1068        let zero = build.iconst(Type::int(32), 0);
1069        let test = build.icmp(IntPred::Slt, written[0], zero);
1070        build.br_if(test, arms[0], &[], arms[1], &[]);
1071        for (index, arm) in arms.iter().enumerate() {
1072            let mut build = Builder::new(&mut func, *arm);
1073            let where_to = if addresses && index == 1 { elsewhere } else { address };
1074            build.store(written[index], where_to, info, flags[index]);
1075            build.jump(join, &[]);
1076        }
1077        let mut build = Builder::new(&mut func, join);
1078        build.ret(&[]);
1079        func
1080    }
1081
1082    /// `x < y ? a : b`, as a diamond whose two arms are empty.
1083    ///
1084    /// Block 0 is the head and takes the two values it compares as function parameters, blocks 1
1085    /// and 2 are the arms and carry one of two constants, and block 3 is the join and returns what
1086    /// it was given.
1087    fn empty_arms() -> Func {
1088        let mut names = Interner::new();
1089        let signature = Signature::new().with_params(&[Type::int(32), Type::int(32)]);
1090        let mut func = Func::new(names.intern("f"), signature);
1091        let head = func.create_block();
1092        let left = func.append_param(head, Type::int(32));
1093        let right = func.append_param(head, Type::int(32));
1094        let arms = [func.create_block(), func.create_block()];
1095        let join = func.create_block();
1096        let param = func.append_param(join, Type::int(32));
1097
1098        let mut build = Builder::new(&mut func, head);
1099        let test = build.icmp(IntPred::Slt, left, right);
1100        build.br_if(test, arms[0], &[], arms[1], &[]);
1101        for (arm, value) in arms.iter().zip([1, 2]) {
1102            let mut build = Builder::new(&mut func, *arm);
1103            let it = build.iconst(Type::int(32), value);
1104            build.jump(join, &[it]);
1105        }
1106        let mut build = Builder::new(&mut func, join);
1107        build.ret(&[param]);
1108        func
1109    }
1110
1111    #[test]
1112    fn a_branch_that_is_already_decided_is_left_for_simplify_cfg() {
1113        // What `if (1)` looks like by the time it gets here. Converting would build a select on a
1114        // constant and keep the arm that cannot run, and the pass that would fold it does not
1115        // exist, so the answer is to leave the branch alone and let the arm be deleted whole.
1116        let mut names = Interner::new();
1117        let mut func = Func::new(names.intern("f"), Signature::new());
1118        let head = func.create_block();
1119        let arms = [func.create_block(), func.create_block()];
1120        let join = func.create_block();
1121        let param = func.append_param(join, Type::int(32));
1122
1123        let mut build = Builder::new(&mut func, head);
1124        // What `if (1)` reaches this pass as. Not a constant, a comparison of two constants, since
1125        // `fold` will not turn an `icmp` into an `i1` that nothing lowers.
1126        let one = build.iconst(Type::int(32), 1);
1127        let zero = build.iconst(Type::int(32), 0);
1128        let test = build.icmp(IntPred::Ne, one, zero);
1129        build.br_if(test, arms[0], &[], arms[1], &[]);
1130        for (arm, value) in arms.iter().zip([1, 2]) {
1131            let mut build = Builder::new(&mut func, *arm);
1132            let it = build.iconst(Type::int(32), value);
1133            build.jump(join, &[it]);
1134        }
1135        let mut build = Builder::new(&mut func, join);
1136        build.ret(&[param]);
1137
1138        let stats = phiopt(&mut func);
1139        assert_eq!(stats.count(Kind::Missed, super::CONDITION_IS_DECIDED), 1);
1140        assert_eq!(blocks(&func), vec![0, 1, 2, 3]);
1141    }
1142
1143    #[test]
1144    fn a_diamond_whose_arms_are_empty_becomes_a_select() {
1145        let mut func = empty_arms();
1146        let stats = phiopt(&mut func);
1147        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 1);
1148        // The two constants moved up with the arms, and the select is what the branch was.
1149        assert_eq!(
1150            opcodes(&func, 0),
1151            vec![Opcode::ICmp, Opcode::IConst, Opcode::IConst, Opcode::Select, Opcode::Jump]
1152        );
1153        assert_eq!(goes_to(&func, 0), vec![3]);
1154        assert_eq!(blocks(&func), vec![0, 3]);
1155    }
1156
1157    #[test]
1158    fn the_side_the_condition_holds_on_is_the_side_the_select_takes_first() {
1159        let mut func = empty_arms();
1160        phiopt(&mut func);
1161        let select = func
1162            .insts(Block::from_usize(0))
1163            .find(|&inst| func[inst].opcode == Opcode::Select)
1164            .expect("the select the pass just built");
1165        let args = func[func[select].args].to_vec();
1166        let one = crate::fold::constant(&func, args[1]).expect("the true arm carried a constant");
1167        let two = crate::fold::constant(&func, args[2]).expect("the false arm carried a constant");
1168        assert_eq!(one.0.unsigned(), 1, "the arm the branch named first");
1169        assert_eq!(two.0.unsigned(), 2, "the arm the branch named second");
1170    }
1171
1172    /// A triangle: one side goes straight to the join carrying what it already had.
1173    #[test]
1174    fn a_triangle_whose_empty_side_goes_straight_to_the_join_is_converted() {
1175        let mut names = Interner::new();
1176        let signature = Signature::new().with_params(&[Type::int(32)]);
1177        let mut func = Func::new(names.intern("f"), signature);
1178        let head = func.create_block();
1179        let outside = func.append_param(head, Type::int(32));
1180        let arm = func.create_block();
1181        let join = func.create_block();
1182        let param = func.append_param(join, Type::int(32));
1183
1184        let mut build = Builder::new(&mut func, head);
1185        let zero = build.iconst(Type::int(32), 0);
1186        let test = build.icmp(IntPred::Slt, outside, zero);
1187        build.br_if(test, arm, &[], join, &[outside]);
1188        let mut build = Builder::new(&mut func, arm);
1189        let it = build.iconst(Type::int(32), 0);
1190        build.jump(join, &[it]);
1191        let mut build = Builder::new(&mut func, join);
1192        build.ret(&[param]);
1193
1194        let stats = phiopt(&mut func);
1195        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 1);
1196        assert_eq!(blocks(&func), vec![0, 2]);
1197        assert_eq!(goes_to(&func, 0), vec![2]);
1198        assert_eq!(opcodes(&func, 0).last(), Some(&Opcode::Jump));
1199    }
1200
1201    #[test]
1202    fn a_parameter_both_sides_agree_about_needs_no_select() {
1203        let mut names = Interner::new();
1204        let signature = Signature::new().with_params(&[Type::int(32)]);
1205        let mut func = Func::new(names.intern("f"), signature);
1206        let head = func.create_block();
1207        let outside = func.append_param(head, Type::int(32));
1208        let arms = [func.create_block(), func.create_block()];
1209        let join = func.create_block();
1210        let param = func.append_param(join, Type::int(32));
1211
1212        let mut build = Builder::new(&mut func, head);
1213        let zero = build.iconst(Type::int(32), 0);
1214        let test = build.icmp(IntPred::Slt, outside, zero);
1215        build.br_if(test, arms[0], &[], arms[1], &[]);
1216        for arm in arms {
1217            let mut build = Builder::new(&mut func, arm);
1218            build.jump(join, &[outside]);
1219        }
1220        let mut build = Builder::new(&mut func, join);
1221        build.ret(&[param]);
1222
1223        let stats = phiopt(&mut func);
1224        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 1);
1225        assert!(!opcodes(&func, 0).contains(&Opcode::Select), "both sides carried the same value");
1226        assert_eq!(carries(&func, 0), vec![outside]);
1227    }
1228
1229    #[test]
1230    fn two_sides_carrying_the_same_number_need_no_select_either() {
1231        // `x ? 7 : 7`, which the corpus has eight of. The two sevens are two values, because
1232        // nothing has hash consed them into one, so asking only whether the values are equal
1233        // builds a select between two sevens and pays a compare and a conditional move for it.
1234        let mut names = Interner::new();
1235        let signature = Signature::new().with_params(&[Type::int(32)]);
1236        let mut func = Func::new(names.intern("f"), signature);
1237        let head = func.create_block();
1238        let outside = func.append_param(head, Type::int(32));
1239        let arms = [func.create_block(), func.create_block()];
1240        let join = func.create_block();
1241        let param = func.append_param(join, Type::int(32));
1242
1243        let mut build = Builder::new(&mut func, head);
1244        let zero = build.iconst(Type::int(32), 0);
1245        let test = build.icmp(IntPred::Slt, outside, zero);
1246        build.br_if(test, arms[0], &[], arms[1], &[]);
1247        for arm in arms {
1248            let mut build = Builder::new(&mut func, arm);
1249            let seven = build.iconst(Type::int(32), 7);
1250            build.jump(join, &[seven]);
1251        }
1252        let mut build = Builder::new(&mut func, join);
1253        build.ret(&[param]);
1254
1255        let stats = phiopt(&mut func);
1256        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 1);
1257        assert!(!opcodes(&func, 0).contains(&Opcode::Select), "both sides carried a seven");
1258    }
1259
1260    #[test]
1261    fn two_sides_carrying_different_numbers_still_get_a_select() {
1262        let mut func = empty_arms();
1263        let stats = phiopt(&mut func);
1264        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 1);
1265        assert!(opcodes(&func, 0).contains(&Opcode::Select), "one and two are not the same number");
1266    }
1267
1268    /// `x = (a == b) ? b : a`, as the diamond it arrives here as.
1269    ///
1270    /// Block 0 is the head and takes the two values, blocks 1 and 2 are the arms and each carries
1271    /// one of them to block 3, which returns what it was given. The comparison's predicate and the
1272    /// type of the two values are what the tests below vary.
1273    fn condition_settles_it(pred: IntPred, ty: Type) -> Func {
1274        let mut names = Interner::new();
1275        let signature = Signature::new().with_params(&[ty, ty]);
1276        let mut func = Func::new(names.intern("f"), signature);
1277        let head = func.create_block();
1278        let left = func.append_param(head, ty);
1279        let right = func.append_param(head, ty);
1280        let arms = [func.create_block(), func.create_block()];
1281        let join = func.create_block();
1282        let param = func.append_param(join, ty);
1283
1284        let mut build = Builder::new(&mut func, head);
1285        let test = build.icmp(pred, left, right);
1286        build.br_if(test, arms[0], &[], arms[1], &[]);
1287        // The side taken when the condition holds carries the right hand value, the other side
1288        // carries the left, which is what makes the two the same number on one edge and not the
1289        // other. Which side that is follows the predicate.
1290        for (arm, value) in arms.iter().zip([right, left]) {
1291            let mut build = Builder::new(&mut func, *arm);
1292            build.jump(join, &[value]);
1293        }
1294        let mut build = Builder::new(&mut func, join);
1295        build.ret(&[param]);
1296        func
1297    }
1298
1299    #[test]
1300    fn a_value_the_condition_says_is_the_other_one_needs_no_select() {
1301        let mut func = condition_settles_it(IntPred::Eq, Type::int(32));
1302        let stats = phiopt(&mut func);
1303        assert_eq!(stats.count(Kind::Optimized, super::VALUE_IMPLIED), 1);
1304        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 1);
1305        assert_eq!(opcodes(&func, 0), vec![Opcode::ICmp, Opcode::Jump]);
1306        // The value passed on is the one carried by the side the two were not shown equal on.
1307        let params = func[Block::from_usize(0)].params.to_vec();
1308        assert_eq!(carries(&func, 0), vec![params[0]]);
1309        assert_eq!(blocks(&func), vec![0, 3]);
1310    }
1311
1312    /// The same with the branch the other way round, where the equal side is the one not taken.
1313    #[test]
1314    fn an_inequality_settles_it_from_the_other_side() {
1315        let mut func = condition_settles_it(IntPred::Ne, Type::int(32));
1316        let stats = phiopt(&mut func);
1317        assert_eq!(stats.count(Kind::Optimized, super::VALUE_IMPLIED), 1);
1318        let params = func[Block::from_usize(0)].params.to_vec();
1319        assert_eq!(carries(&func, 0), vec![params[1]]);
1320    }
1321
1322    /// A pointer has no `select`, and a value nothing has to choose between does not need one.
1323    #[test]
1324    fn a_value_of_a_type_with_no_select_is_still_settled_by_the_condition() {
1325        let mut func = condition_settles_it(IntPred::Eq, Type::PTR);
1326        let stats = phiopt(&mut func);
1327        assert_eq!(stats.count(Kind::Missed, super::NO_SELECT_AT_THAT_WIDTH), 0);
1328        assert_eq!(stats.count(Kind::Optimized, super::VALUE_IMPLIED), 1);
1329        assert_eq!(opcodes(&func, 0), vec![Opcode::ICmp, Opcode::Jump]);
1330    }
1331
1332    /// A branch on anything but an equality is not asked about, and the select is written as usual.
1333    #[test]
1334    fn a_branch_that_is_not_an_equality_gets_its_select() {
1335        let mut func = condition_settles_it(IntPred::Slt, Type::int(32));
1336        let stats = phiopt(&mut func);
1337        assert_eq!(stats.count(Kind::Optimized, super::VALUE_IMPLIED), 0);
1338        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 1);
1339        assert!(opcodes(&func, 0).contains(&Opcode::Select));
1340    }
1341
1342    /// Two constants that are not the same number are not the same number on any edge.
1343    #[test]
1344    fn two_different_constants_are_not_asked_about() {
1345        let mut func = empty_arms();
1346        let stats = phiopt(&mut func);
1347        assert_eq!(stats.count(Kind::Optimized, super::VALUE_IMPLIED), 0);
1348        assert!(opcodes(&func, 0).contains(&Opcode::Select));
1349    }
1350
1351    #[test]
1352    fn a_store_both_arms_make_to_one_place_is_made_once_below_the_branch() {
1353        let mut func = both_arms_store(plain(), [Flags::NONE; 2], false);
1354        let stats = phiopt(&mut func);
1355        assert_eq!(stats.count(Kind::Optimized, super::STORE_REPLACED), 1);
1356        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 1);
1357        // One store, below the select that chooses what it writes, and no branch above either.
1358        assert_eq!(
1359            opcodes(&func, 0),
1360            vec![Opcode::IConst, Opcode::ICmp, Opcode::Select, Opcode::Store, Opcode::Jump]
1361        );
1362        assert_eq!(blocks(&func), vec![0, 3]);
1363        assert_eq!(goes_to(&func, 0), vec![3]);
1364    }
1365
1366    #[test]
1367    fn the_one_store_writes_what_the_side_the_condition_holds_on_was_writing() {
1368        let mut func = both_arms_store(plain(), [Flags::NONE; 2], false);
1369        phiopt(&mut func);
1370        let head = Block::from_usize(0);
1371        let select = func
1372            .insts(head)
1373            .find(|&inst| func[inst].opcode == Opcode::Select)
1374            .expect("the select the pass just built");
1375        let store = func
1376            .insts(head)
1377            .find(|&inst| func[inst].opcode == Opcode::Store)
1378            .expect("the one store that is left");
1379        let chosen = func[func[select].args].to_vec();
1380        let written = func[func[store].args].to_vec();
1381        // The head's parameters in order: the address, then what each side writes.
1382        let params = func[head].params.to_vec();
1383        assert_eq!(chosen[1], params[1], "the arm the branch named first");
1384        assert_eq!(chosen[2], params[2], "the arm the branch named second");
1385        assert_eq!(written[0], func[select].first_result.expect("a select produces one value"));
1386        assert_eq!(written[1], params[0], "the address both arms named");
1387    }
1388
1389    /// Both arms writing the same value needs no select, only the one store.
1390    #[test]
1391    fn two_arms_that_write_the_same_thing_get_a_store_and_no_select() {
1392        let mut func = both_arms_store(plain(), [Flags::NONE; 2], false);
1393        // Point the second arm's store at the first arm's value, which is what the front end
1394        // produces when both branches of a conditional assign the same thing.
1395        let head = Block::from_usize(0);
1396        let params = func[head].params.to_vec();
1397        let store = func
1398            .insts(Block::from_usize(2))
1399            .find(|&inst| func[inst].opcode == Opcode::Store)
1400            .expect("the second arm's store");
1401        let args = func.push_values(&[params[1], params[0]]);
1402        func[store].args = args;
1403
1404        let stats = phiopt(&mut func);
1405        assert_eq!(stats.count(Kind::Optimized, super::STORE_REPLACED), 1);
1406        assert_eq!(
1407            opcodes(&func, 0),
1408            vec![Opcode::IConst, Opcode::ICmp, Opcode::Store, Opcode::Jump]
1409        );
1410    }
1411
1412    /// Two stores to two different places is two writes, and doing both is writing one of them twice.
1413    #[test]
1414    fn two_arms_that_store_to_different_addresses_keep_their_branch() {
1415        let mut func = both_arms_store(plain(), [Flags::NONE; 2], true);
1416        let stats = phiopt(&mut func);
1417        assert_eq!(stats.count(Kind::Optimized, super::STORE_REPLACED), 0);
1418        assert_eq!(stats.count(Kind::Missed, super::STORES_DO_NOT_MATCH), 1);
1419        assert_eq!(goes_to(&func, 0), vec![1, 2]);
1420    }
1421
1422    #[test]
1423    fn a_volatile_store_keeps_its_branch_even_when_both_arms_make_it() {
1424        let mut func = both_arms_store(plain(), [Flags::VOLATILE; 2], false);
1425        let stats = phiopt(&mut func);
1426        assert_eq!(stats.count(Kind::Optimized, super::STORE_REPLACED), 0);
1427        assert_eq!(stats.count(Kind::Missed, super::STORES_DO_NOT_MATCH), 1);
1428        assert_eq!(goes_to(&func, 0), vec![1, 2]);
1429    }
1430
1431    #[test]
1432    fn an_atomic_store_keeps_its_branch_even_when_both_arms_make_it() {
1433        let mut func = both_arms_store(
1434            MemInfo { order: MemOrder::SeqCst, ..plain() },
1435            [Flags::NONE; 2],
1436            false,
1437        );
1438        let stats = phiopt(&mut func);
1439        assert_eq!(stats.count(Kind::Optimized, super::STORE_REPLACED), 0);
1440        assert_eq!(stats.count(Kind::Missed, super::STORES_DO_NOT_MATCH), 1);
1441    }
1442
1443    /// Two stores told different things about the access have no one answer to carry downward.
1444    #[test]
1445    fn two_stores_that_disagree_about_the_access_keep_their_branch() {
1446        let mut func = both_arms_store(plain(), [Flags::NONE; 2], false);
1447        let store = func
1448            .insts(Block::from_usize(2))
1449            .find(|&inst| func[inst].opcode == Opcode::Store)
1450            .expect("the second arm's store");
1451        let mem = func.add_mem(MemInfo { align: 1, ..plain() });
1452        func[store].extra = rucc_ir::Extra::Mem(mem);
1453
1454        let stats = phiopt(&mut func);
1455        assert_eq!(stats.count(Kind::Optimized, super::STORE_REPLACED), 0);
1456        assert_eq!(stats.count(Kind::Missed, super::STORES_DO_NOT_MATCH), 1);
1457    }
1458
1459    /// The store comes off the work count, because one of the two arms was always going to make it.
1460    ///
1461    /// Each arm here holds the store and as much other work as the rule allows, so counting the
1462    /// store as work would put both arms one over the limit and the branch would stay.
1463    #[test]
1464    fn a_store_each_way_does_not_count_against_how_long_the_arms_may_be() {
1465        let mut func = both_arms_store(plain(), [Flags::NONE; 2], false);
1466        let params = func[Block::from_usize(0)].params.to_vec();
1467        for arm in [1, 2] {
1468            let block = Block::from_usize(arm);
1469            let term = func.terminator(block).expect("an arm ends in its jump");
1470            func.remove_inst(term);
1471            let mut build = Builder::new(&mut func, block);
1472            let mut value = params[1];
1473            for _ in 0..rucc_cost::heuristics::PHIOPT_ARM_INSTRUCTIONS {
1474                value = build.binary(Opcode::Add, value, params[2], Flags::NONE);
1475            }
1476            func.append_inst(block, term);
1477        }
1478
1479        let stats = phiopt(&mut func);
1480        assert_eq!(stats.count(Kind::Missed, super::ARMS_TOO_LONG), 0);
1481        assert_eq!(stats.count(Kind::Optimized, super::STORE_REPLACED), 1);
1482    }
1483
1484    /// A store one side makes and the other does not, which is the transformation with no proof.
1485    #[test]
1486    fn an_arm_that_stores_where_the_other_does_not_keeps_its_branch() {
1487        let mut names = Interner::new();
1488        let signature = Signature::new().with_params(&[Type::int(32)]);
1489        let mut func = Func::new(names.intern("f"), signature);
1490        let head = func.create_block();
1491        let outside = func.append_param(head, Type::int(32));
1492        let arms = [func.create_block(), func.create_block()];
1493        let join = func.create_block();
1494        let param = func.append_param(join, Type::int(32));
1495
1496        let mut build = Builder::new(&mut func, head);
1497        let zero = build.iconst(Type::int(32), 0);
1498        let test = build.icmp(IntPred::Slt, outside, zero);
1499        build.br_if(test, arms[0], &[], arms[1], &[]);
1500        let mut build = Builder::new(&mut func, arms[0]);
1501        store_something(&mut build);
1502        let it = build.iconst(Type::int(32), 1);
1503        build.jump(join, &[it]);
1504        let mut build = Builder::new(&mut func, arms[1]);
1505        let it = build.iconst(Type::int(32), 2);
1506        build.jump(join, &[it]);
1507        let mut build = Builder::new(&mut func, join);
1508        build.ret(&[param]);
1509
1510        let stats = phiopt(&mut func);
1511        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 0);
1512        assert_eq!(stats.count(Kind::Missed, super::STORE_ON_ONE_PATH), 1);
1513        assert_eq!(goes_to(&func, 0), vec![1, 2]);
1514    }
1515
1516    /// A load, which is the effect that is not a store and gets the general answer.
1517    #[test]
1518    fn an_arm_that_does_something_else_keeps_its_branch() {
1519        let mut names = Interner::new();
1520        let signature = Signature::new().with_params(&[Type::int(32)]);
1521        let mut func = Func::new(names.intern("f"), signature);
1522        let head = func.create_block();
1523        let outside = func.append_param(head, Type::int(32));
1524        let arms = [func.create_block(), func.create_block()];
1525        let join = func.create_block();
1526        let param = func.append_param(join, Type::int(32));
1527
1528        let mut build = Builder::new(&mut func, head);
1529        let zero = build.iconst(Type::int(32), 0);
1530        let test = build.icmp(IntPred::Slt, outside, zero);
1531        build.br_if(test, arms[0], &[], arms[1], &[]);
1532        let mut build = Builder::new(&mut func, arms[0]);
1533        let address = build.iconst(Type::int(64), 16);
1534        let address = build.unary(Opcode::IntToPtr, address, Type::PTR);
1535        let it = build.load(Type::int(32), address, plain(), Flags::NONE);
1536        build.jump(join, &[it]);
1537        let mut build = Builder::new(&mut func, arms[1]);
1538        let it = build.iconst(Type::int(32), 2);
1539        build.jump(join, &[it]);
1540        let mut build = Builder::new(&mut func, join);
1541        build.ret(&[param]);
1542
1543        let stats = phiopt(&mut func);
1544        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 0);
1545        assert_eq!(stats.count(Kind::Missed, super::ARM_HAS_EFFECTS), 1);
1546        assert_eq!(goes_to(&func, 0), vec![1, 2]);
1547    }
1548
1549    /// A division whose divisor is not known cannot be moved onto the path that skipped it.
1550    #[test]
1551    fn an_arm_that_divides_by_something_unknown_keeps_its_branch() {
1552        let mut names = Interner::new();
1553        let signature = Signature::new().with_params(&[Type::int(32), Type::int(32)]);
1554        let mut func = Func::new(names.intern("f"), signature);
1555        let head = func.create_block();
1556        let left = func.append_param(head, Type::int(32));
1557        let right = func.append_param(head, Type::int(32));
1558        let arms = [func.create_block(), func.create_block()];
1559        let join = func.create_block();
1560        let param = func.append_param(join, Type::int(32));
1561
1562        let mut build = Builder::new(&mut func, head);
1563        let zero = build.iconst(Type::int(32), 0);
1564        let test = build.icmp(IntPred::Ne, right, zero);
1565        build.br_if(test, arms[0], &[], arms[1], &[]);
1566        let mut build = Builder::new(&mut func, arms[0]);
1567        let it = build.binary(Opcode::SDiv, left, right, Flags::NONE);
1568        build.jump(join, &[it]);
1569        let mut build = Builder::new(&mut func, arms[1]);
1570        let it = build.iconst(Type::int(32), 0);
1571        build.jump(join, &[it]);
1572        let mut build = Builder::new(&mut func, join);
1573        build.ret(&[param]);
1574
1575        let stats = phiopt(&mut func);
1576        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 0);
1577        assert_eq!(stats.count(Kind::Missed, super::ARM_MAY_TRAP), 1);
1578        assert_eq!(goes_to(&func, 0), vec![1, 2]);
1579    }
1580
1581    #[test]
1582    fn a_division_by_a_constant_that_is_not_zero_or_minus_one_is_moved() {
1583        let mut names = Interner::new();
1584        let signature = Signature::new().with_params(&[Type::int(32)]);
1585        let mut func = Func::new(names.intern("f"), signature);
1586        let head = func.create_block();
1587        let outside = func.append_param(head, Type::int(32));
1588        let arms = [func.create_block(), func.create_block()];
1589        let join = func.create_block();
1590        let param = func.append_param(join, Type::int(32));
1591
1592        let mut build = Builder::new(&mut func, head);
1593        let zero = build.iconst(Type::int(32), 0);
1594        let test = build.icmp(IntPred::Slt, outside, zero);
1595        build.br_if(test, arms[0], &[], arms[1], &[]);
1596        let mut build = Builder::new(&mut func, arms[0]);
1597        let three = build.iconst(Type::int(32), 3);
1598        let it = build.binary(Opcode::SDiv, outside, three, Flags::NONE);
1599        build.jump(join, &[it]);
1600        let mut build = Builder::new(&mut func, arms[1]);
1601        let it = build.iconst(Type::int(32), 0);
1602        build.jump(join, &[it]);
1603        let mut build = Builder::new(&mut func, join);
1604        build.ret(&[param]);
1605
1606        let stats = phiopt(&mut func);
1607        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 1);
1608        assert!(opcodes(&func, 0).contains(&Opcode::SDiv));
1609    }
1610
1611    /// Nothing chooses between two pointers, so the shape is matched and then left alone.
1612    #[test]
1613    fn a_value_no_select_is_lowered_for_keeps_its_branch() {
1614        let mut names = Interner::new();
1615        let signature = Signature::new().with_params(&[Type::int(32)]);
1616        let mut func = Func::new(names.intern("f"), signature);
1617        let head = func.create_block();
1618        let outside = func.append_param(head, Type::int(32));
1619        let arms = [func.create_block(), func.create_block()];
1620        let join = func.create_block();
1621        func.append_param(join, Type::PTR);
1622
1623        let mut build = Builder::new(&mut func, head);
1624        let zero = build.iconst(Type::int(32), 0);
1625        let test = build.icmp(IntPred::Slt, outside, zero);
1626        build.br_if(test, arms[0], &[], arms[1], &[]);
1627        for (arm, value) in arms.iter().zip([16, 32]) {
1628            let mut build = Builder::new(&mut func, *arm);
1629            let it = build.iconst(Type::int(64), value);
1630            let it = build.unary(Opcode::IntToPtr, it, Type::PTR);
1631            build.jump(join, &[it]);
1632        }
1633        let mut build = Builder::new(&mut func, join);
1634        build.ret(&[]);
1635
1636        let stats = phiopt(&mut func);
1637        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 0);
1638        assert_eq!(stats.count(Kind::Missed, super::NO_SELECT_AT_THAT_WIDTH), 1);
1639    }
1640
1641    #[test]
1642    fn arms_with_more_work_in_them_than_the_budget_keep_their_branch() {
1643        let mut names = Interner::new();
1644        let signature = Signature::new().with_params(&[Type::int(32)]);
1645        let mut func = Func::new(names.intern("f"), signature);
1646        let head = func.create_block();
1647        let outside = func.append_param(head, Type::int(32));
1648        let arms = [func.create_block(), func.create_block()];
1649        let join = func.create_block();
1650        let param = func.append_param(join, Type::int(32));
1651
1652        let mut build = Builder::new(&mut func, head);
1653        let zero = build.iconst(Type::int(32), 0);
1654        let test = build.icmp(IntPred::Slt, outside, zero);
1655        build.br_if(test, arms[0], &[], arms[1], &[]);
1656        let mut build = Builder::new(&mut func, arms[0]);
1657        // Four instructions, which is past the budget however cheap each of them is.
1658        let mut it = outside;
1659        for _ in 0..4 {
1660            it = build.binary(Opcode::Add, it, outside, Flags::NONE);
1661        }
1662        build.jump(join, &[it]);
1663        let mut build = Builder::new(&mut func, arms[1]);
1664        let it = build.iconst(Type::int(32), 0);
1665        build.jump(join, &[it]);
1666        let mut build = Builder::new(&mut func, join);
1667        build.ret(&[param]);
1668
1669        let stats = phiopt(&mut func);
1670        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 0);
1671        assert_eq!(stats.count(Kind::Missed, super::ARMS_TOO_LONG), 1);
1672    }
1673
1674    /// The margin, at the two ends of it and just outside.
1675    ///
1676    /// A pass level test of the refusal it guards is not written, and the module doc says why: a
1677    /// diamond is the one shape none of document 11's one sided predictors can key on, so every
1678    /// branch this pass matches comes back even until `__builtin_expect` is wired through the
1679    /// front end. The arithmetic is what there is to check today.
1680    #[test]
1681    fn the_margin_is_a_quarter_in_from_each_end() {
1682        let guessed = |percent: u32| Probability::percent(percent, Quality::Guessed);
1683        assert!(super::unpredictable(Probability::even()));
1684        assert!(super::unpredictable(guessed(25)));
1685        assert!(super::unpredictable(guessed(75)));
1686        assert!(!super::unpredictable(guessed(24)));
1687        assert!(!super::unpredictable(guessed(76)));
1688        assert!(!super::unpredictable(Probability::always()));
1689        assert!(!super::unpredictable(Probability::never()));
1690    }
1691
1692    #[test]
1693    fn an_arm_that_two_edges_reach_is_not_an_arm() {
1694        let mut names = Interner::new();
1695        let signature = Signature::new().with_params(&[Type::int(32)]);
1696        let mut func = Func::new(names.intern("f"), signature);
1697        let head = func.create_block();
1698        let outside = func.append_param(head, Type::int(32));
1699        let above = func.create_block();
1700        let arms = [func.create_block(), func.create_block()];
1701        let join = func.create_block();
1702        let param = func.append_param(join, Type::int(32));
1703
1704        // The entry reaches the first arm as well as the head does, so moving the arm's work into
1705        // the head would leave the entry's path without it.
1706        let mut build = Builder::new(&mut func, head);
1707        let zero = build.iconst(Type::int(32), 0);
1708        let first = build.icmp(IntPred::Slt, outside, zero);
1709        build.br_if(first, above, &[], arms[0], &[]);
1710        let mut build = Builder::new(&mut func, above);
1711        let one = build.iconst(Type::int(32), 1);
1712        let second = build.icmp(IntPred::Slt, outside, one);
1713        build.br_if(second, arms[0], &[], arms[1], &[]);
1714        for (arm, value) in arms.iter().zip([1, 2]) {
1715            let mut build = Builder::new(&mut func, *arm);
1716            let it = build.iconst(Type::int(32), value);
1717            build.jump(join, &[it]);
1718        }
1719        let mut build = Builder::new(&mut func, join);
1720        build.ret(&[param]);
1721
1722        let stats = phiopt(&mut func);
1723        // Neither branch is a diamond. The head's first side goes to a block that is not the join
1724        // and is not an arm either, since two edges reach it, and the second branch's first side
1725        // is the same block for the same reason.
1726        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 0);
1727        assert_eq!(goes_to(&func, 0), vec![1, 2]);
1728        assert_eq!(goes_to(&func, 1), vec![2, 3]);
1729    }
1730
1731    #[test]
1732    fn fuel_stops_the_conversion_where_it_stands() {
1733        let mut func = empty_arms();
1734        let mut fuel = Fuel::of(0);
1735        let stats = PhiOpt.run(&mut func, &mut Analyses::new(), &mut fuel);
1736        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 0);
1737        assert_eq!(stats.count(Kind::Missed, super::NO_FUEL), 1);
1738        assert_eq!(goes_to(&func, 0), vec![1, 2]);
1739    }
1740
1741    /// `x < y ? f(a, k) : f(b, k)`, as a diamond whose two arms do the same thing to different
1742    /// operands.
1743    ///
1744    /// Block 0 is the head, taking the two values it compares and the two operands and working out
1745    /// the operand both arms share. Blocks 1 and 2 are the arms, each applying every one of
1746    /// `steps` to its own operand and that shared value, and block 3 is the join, taking one
1747    /// parameter for each of them.
1748    fn same_operation(steps: &[Opcode]) -> Func {
1749        let mut names = Interner::new();
1750        let int = Type::int(32);
1751        let signature = Signature::new().with_params(&[int, int, int, int]);
1752        let mut func = Func::new(names.intern("f"), signature);
1753        let head = func.create_block();
1754        let left = func.append_param(head, int);
1755        let right = func.append_param(head, int);
1756        let operands = [func.append_param(head, int), func.append_param(head, int)];
1757        let arms = [func.create_block(), func.create_block()];
1758        let join = func.create_block();
1759        let params: Vec<Value> = steps.iter().map(|_| func.append_param(join, int)).collect();
1760
1761        let mut build = Builder::new(&mut func, head);
1762        // In the head rather than in each arm, so that the two sides share this operand as one
1763        // value. Two arms that each work out their own three are two operations apart, not one.
1764        let shared = build.iconst(int, 3);
1765        let test = build.icmp(IntPred::Slt, left, right);
1766        build.br_if(test, arms[0], &[], arms[1], &[]);
1767        for (&arm, operand) in arms.iter().zip(operands) {
1768            let mut build = Builder::new(&mut func, arm);
1769            let carried: Vec<Value> = steps
1770                .iter()
1771                .map(|&opcode| build.binary(opcode, operand, shared, Flags::default()))
1772                .collect();
1773            build.jump(join, &carried);
1774        }
1775        let mut build = Builder::new(&mut func, join);
1776        build.ret(&params);
1777        func
1778    }
1779
1780    /// The transformation. Two adds become one add of a select, rather than one select of two adds.
1781    #[test]
1782    fn an_operation_both_arms_did_is_done_once_below_the_branch() {
1783        let mut func = same_operation(&[Opcode::Add]);
1784        let stats = phiopt(&mut func);
1785        assert_eq!(stats.count(Kind::Optimized, super::FACTORED), 1);
1786        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 1);
1787        assert_eq!(
1788            opcodes(&func, 0),
1789            vec![Opcode::IConst, Opcode::ICmp, Opcode::Select, Opcode::Add, Opcode::Jump],
1790            "the select chooses the operand and the add happens once"
1791        );
1792        assert_eq!(blocks(&func), vec![0, 3]);
1793    }
1794
1795    /// The select goes under the operation, so what it chooses between is the operands and not the
1796    /// answers. Getting that the wrong way round would be a select of two adds that happens to have
1797    /// the right opcodes in it.
1798    #[test]
1799    fn the_select_chooses_the_operands_and_not_the_answers() {
1800        let mut func = same_operation(&[Opcode::Add]);
1801        phiopt(&mut func);
1802        let head = Block::from_usize(0);
1803        let select = func
1804            .insts(head)
1805            .find(|&inst| func[inst].opcode == Opcode::Select)
1806            .expect("the select the pass just built");
1807        let add = func
1808            .insts(head)
1809            .find(|&inst| func[inst].opcode == Opcode::Add)
1810            .expect("the add the pass just wrote");
1811        let chosen = func[func[select].args].to_vec();
1812        let params = func[head].params.to_vec();
1813        assert_eq!(&chosen[1..], &params[2..], "the two operands the arms differed in");
1814        let added = func[func[add].args].to_vec();
1815        assert_eq!(added[0], func[select].first_result.expect("a select has a result"));
1816        assert_eq!(carries(&func, 0), vec![func[add].first_result.expect("an add has a result")]);
1817    }
1818
1819    /// Nothing is speculated by an operation both arms were doing, so the length rule is about what
1820    /// is left after the factoring rather than about what the arms arrived holding. Three
1821    /// instructions an arm is over the limit, and three instructions that all factor is none.
1822    #[test]
1823    fn arms_that_factor_away_entirely_are_not_too_long() {
1824        let steps = [Opcode::Add, Opcode::Sub, Opcode::Mul];
1825        let mut func = same_operation(&steps);
1826        let stats = phiopt(&mut func);
1827        assert_eq!(stats.count(Kind::Missed, super::ARMS_TOO_LONG), 0);
1828        assert_eq!(stats.count(Kind::Optimized, super::FACTORED), 3);
1829        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 1);
1830        let written = opcodes(&func, 0);
1831        assert_eq!(written.iter().filter(|&&op| op == Opcode::Select).count(), 3);
1832        for step in steps {
1833            assert_eq!(written.iter().filter(|&&op| op == step).count(), 1, "{step:?} once");
1834        }
1835    }
1836
1837    /// Both arms doing the same thing to the same operands is a common subexpression nothing has
1838    /// numbered, and one copy of it serves both sides with no select at all.
1839    #[test]
1840    fn arms_that_agree_in_every_operand_need_no_select() {
1841        let mut names = Interner::new();
1842        let int = Type::int(32);
1843        let signature = Signature::new().with_params(&[int, int, int]);
1844        let mut func = Func::new(names.intern("f"), signature);
1845        let head = func.create_block();
1846        let left = func.append_param(head, int);
1847        let right = func.append_param(head, int);
1848        let operand = func.append_param(head, int);
1849        let arms = [func.create_block(), func.create_block()];
1850        let join = func.create_block();
1851        let param = func.append_param(join, int);
1852
1853        let mut build = Builder::new(&mut func, head);
1854        let shared = build.iconst(int, 3);
1855        let test = build.icmp(IntPred::Slt, left, right);
1856        build.br_if(test, arms[0], &[], arms[1], &[]);
1857        for &arm in &arms {
1858            let mut build = Builder::new(&mut func, arm);
1859            let it = build.binary(Opcode::Add, operand, shared, Flags::default());
1860            build.jump(join, &[it]);
1861        }
1862        let mut build = Builder::new(&mut func, join);
1863        build.ret(&[param]);
1864
1865        let stats = phiopt(&mut func);
1866        assert_eq!(stats.count(Kind::Optimized, super::FACTORED), 1);
1867        assert_eq!(
1868            opcodes(&func, 0),
1869            vec![Opcode::IConst, Opcode::ICmp, Opcode::Add, Opcode::Jump],
1870            "one add and nothing to choose between"
1871        );
1872    }
1873
1874    /// Two different operations are two operations, and the pass falls back to hoisting both and
1875    /// selecting between what they produced.
1876    #[test]
1877    fn arms_that_do_different_things_are_not_factored() {
1878        let mut names = Interner::new();
1879        let int = Type::int(32);
1880        let signature = Signature::new().with_params(&[int, int, int, int]);
1881        let mut func = Func::new(names.intern("f"), signature);
1882        let head = func.create_block();
1883        let left = func.append_param(head, int);
1884        let right = func.append_param(head, int);
1885        let operands = [func.append_param(head, int), func.append_param(head, int)];
1886        let arms = [func.create_block(), func.create_block()];
1887        let join = func.create_block();
1888        let param = func.append_param(join, int);
1889
1890        let mut build = Builder::new(&mut func, head);
1891        let shared = build.iconst(int, 3);
1892        let test = build.icmp(IntPred::Slt, left, right);
1893        build.br_if(test, arms[0], &[], arms[1], &[]);
1894        for ((&arm, operand), opcode) in arms.iter().zip(operands).zip([Opcode::Add, Opcode::Sub]) {
1895            let mut build = Builder::new(&mut func, arm);
1896            let it = build.binary(opcode, operand, shared, Flags::default());
1897            build.jump(join, &[it]);
1898        }
1899        let mut build = Builder::new(&mut func, join);
1900        build.ret(&[param]);
1901
1902        let stats = phiopt(&mut func);
1903        assert_eq!(stats.count(Kind::Optimized, super::FACTORED), 0);
1904        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 1);
1905        assert_eq!(
1906            opcodes(&func, 0),
1907            vec![
1908                Opcode::IConst,
1909                Opcode::ICmp,
1910                Opcode::Add,
1911                Opcode::Sub,
1912                Opcode::Select,
1913                Opcode::Jump
1914            ],
1915            "both operations hoisted and a select between their answers"
1916        );
1917    }
1918
1919    /// Two operand positions apart needs two selects and one operation, which is what one select
1920    /// and two operations already cost, so there is nothing to win and it is left alone.
1921    #[test]
1922    fn arms_that_differ_in_two_operands_are_not_factored() {
1923        let mut names = Interner::new();
1924        let int = Type::int(32);
1925        let signature = Signature::new().with_params(&[int, int, int, int, int, int]);
1926        let mut func = Func::new(names.intern("f"), signature);
1927        let head = func.create_block();
1928        let left = func.append_param(head, int);
1929        let right = func.append_param(head, int);
1930        let first = [func.append_param(head, int), func.append_param(head, int)];
1931        let second = [func.append_param(head, int), func.append_param(head, int)];
1932        let arms = [func.create_block(), func.create_block()];
1933        let join = func.create_block();
1934        let param = func.append_param(join, int);
1935
1936        let mut build = Builder::new(&mut func, head);
1937        let test = build.icmp(IntPred::Slt, left, right);
1938        build.br_if(test, arms[0], &[], arms[1], &[]);
1939        for ((&arm, one), two) in arms.iter().zip(first).zip(second) {
1940            let mut build = Builder::new(&mut func, arm);
1941            let it = build.binary(Opcode::Add, one, two, Flags::default());
1942            build.jump(join, &[it]);
1943        }
1944        let mut build = Builder::new(&mut func, join);
1945        build.ret(&[param]);
1946
1947        let stats = phiopt(&mut func);
1948        assert_eq!(stats.count(Kind::Optimized, super::FACTORED), 0);
1949        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 1);
1950        assert_eq!(opcodes(&func, 0).iter().filter(|&&op| op == Opcode::Add).count(), 2);
1951    }
1952
1953    /// The one copy is written after the arms have gone, so an operation something else in the arm
1954    /// reads cannot be one of the two it replaces. Here each arm hands its answer to the join
1955    /// twice, which is two readers and not one.
1956    #[test]
1957    fn an_operation_read_more_than_once_is_not_factored() {
1958        let mut names = Interner::new();
1959        let int = Type::int(32);
1960        let signature = Signature::new().with_params(&[int, int, int, int]);
1961        let mut func = Func::new(names.intern("f"), signature);
1962        let head = func.create_block();
1963        let left = func.append_param(head, int);
1964        let right = func.append_param(head, int);
1965        let operands = [func.append_param(head, int), func.append_param(head, int)];
1966        let arms = [func.create_block(), func.create_block()];
1967        let join = func.create_block();
1968        let params = [func.append_param(join, int), func.append_param(join, int)];
1969
1970        let mut build = Builder::new(&mut func, head);
1971        let shared = build.iconst(int, 3);
1972        let test = build.icmp(IntPred::Slt, left, right);
1973        build.br_if(test, arms[0], &[], arms[1], &[]);
1974        for (&arm, operand) in arms.iter().zip(operands) {
1975            let mut build = Builder::new(&mut func, arm);
1976            let it = build.binary(Opcode::Add, operand, shared, Flags::default());
1977            build.jump(join, &[it, it]);
1978        }
1979        let mut build = Builder::new(&mut func, join);
1980        build.ret(&params);
1981
1982        let stats = phiopt(&mut func);
1983        assert_eq!(stats.count(Kind::Optimized, super::FACTORED), 0);
1984        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 1);
1985        assert_eq!(opcodes(&func, 0).iter().filter(|&&op| op == Opcode::Add).count(), 2);
1986    }
1987
1988    /// A triangle has a block on one side only, so there is no second operation to pair the first
1989    /// one with and nothing to factor.
1990    #[test]
1991    fn a_triangle_factors_nothing() {
1992        let mut names = Interner::new();
1993        let int = Type::int(32);
1994        let signature = Signature::new().with_params(&[int, int, int]);
1995        let mut func = Func::new(names.intern("f"), signature);
1996        let head = func.create_block();
1997        let left = func.append_param(head, int);
1998        let right = func.append_param(head, int);
1999        let operand = func.append_param(head, int);
2000        let arm = func.create_block();
2001        let join = func.create_block();
2002        let param = func.append_param(join, int);
2003
2004        let mut build = Builder::new(&mut func, head);
2005        let shared = build.iconst(int, 3);
2006        let test = build.icmp(IntPred::Slt, left, right);
2007        build.br_if(test, arm, &[], join, &[operand]);
2008        let mut build = Builder::new(&mut func, arm);
2009        let it = build.binary(Opcode::Add, operand, shared, Flags::default());
2010        build.jump(join, &[it]);
2011        let mut build = Builder::new(&mut func, join);
2012        build.ret(&[param]);
2013
2014        let stats = phiopt(&mut func);
2015        assert_eq!(stats.count(Kind::Optimized, super::FACTORED), 0);
2016        assert_eq!(stats.count(Kind::Optimized, super::CONVERTED), 1);
2017    }
2018}