Skip to main content

rucc_opt/
split.rs

1//! Splits a loop into a run of iterations that needs no checks and the rest of it, which keeps them.
2//!
3//! Design: `spec/safe-memory/07-check-elimination.md` section 7.4, which names this and says what it
4//! is for: "Loop splitting is the general form. The checked part and the unchecked part are divided
5//! at `min(n, extent / sizeof(T))`."
6//!
7//! [`crate::hoist`] is the pass next door and it answers a different question. It puts one check in
8//! front of a loop that covers every access the loop makes, which needs the loop to make every one
9//! of them: an exact count, one way out, and a check every iteration reaches. Most loops in real code
10//! are not like that. The census on tamnd/rucc#782 says that of the roughly fifteen hundred checks
11//! SQLite still carries at `-O2`, six hundred and ninety two are in loops with a second way out and
12//! sixty four are checks an iteration can finish without reaching. Neither is a loop hoisting can say
13//! anything about, and both are loops this one can, because it never has to claim the loop reaches
14//! the end of what it might read. It only has to know a prefix that is safe.
15//!
16//! # What the two halves are
17//!
18//! The loop is copied. The original becomes the fast half and loses its checks, the copy becomes the
19//! slow half and keeps them, and a new block in front of the original decides which one runs. That
20//! block carries an offset in bytes from the first access, walks it on by the step every time round,
21//! and hands over to the slow half once the offset passes a window worked out in the preheader.
22//!
23//! The offset is a new value rather than one of the loop's own pointers, and the test is against
24//! the window rather than against anything the loop compares. That is what makes this work on a loop
25//! with several ways out: the fast half keeps every exit the loop had, so leaving early still leaves
26//! early, and the extra test is only ever the reason the fast half stops early and never the reason
27//! it runs longer.
28//!
29//! A loop where no address moves gets neither the block nor the offset. Which half runs is settled
30//! by an answer that does not change while the loop runs, so the way into the loop is where the two
31//! halves are chosen between and there is nothing to carry. Half the loops this takes on SQLite are
32//! that shape.
33//!
34//! # Where the window comes from
35//!
36//! For a check whose address is `first + delta` reading `reach` bytes each time, every offset with
37//! `delta + reach <= extent` is one the check cannot fail on, where `extent` is how many bytes from
38//! `first` on belong to whatever owns `first`. So the window is `extent - reach`, and where a loop
39//! has several checks that walk by the same amount the window is the smallest of theirs.
40//!
41//! Bytes rather than iterations, and that is the whole of the arithmetic. An earlier version of this
42//! counted iterations against `(extent - reach) / step + 1`, which is the same transformation and a
43//! much harder claim: it has a symbolic multiply and a symbolic divide in it at sixty four bits, and
44//! z3 does not finish on it in two and a half minutes in any of three formulations, so the pass sat
45//! outside the rule table that `spec/safe-memory/07-check-elimination.md` section 7.7 asks every
46//! elimination to be inside. In bytes it is `swept.sym.i64`, which is already in that table and
47//! already proved, and the pass asks it rather than deciding. `limited` below is where that
48//! happens.
49//!
50//! The extent is the half of that a compiler cannot work out, so it is asked at run time, through the
51//! `cap_extent` query that tamnd/rucc#792 added. The query takes a limit on how far to look and the
52//! answer is never more than that limit and never more than the truth, so what this pass asks for is
53//! as much as the arithmetic carries. That used to be a trip count times a step, on the grounds that
54//! what the query walked was work the loop was about to do anyway. tamnd/rucc#861 stopped it walking
55//! and tamnd/rucc#871 took the bound off: the query probes the far end of what it was asked for and
56//! halves, so the price does not turn on the number, and a limit smaller than the object is a smaller
57//! window and so fewer iterations in the half with no checks in it.
58//!
59//! An address that does not move is the same expression with a step of zero, and its offset is zero
60//! on every iteration, so there is nothing to carry and the window question collapses into whether
61//! the one access fits.
62//! Hoisting would rather have these, and it takes the ones in loops it is willing to touch. What is
63//! left over is the ones in loops it refused for one of its own reasons, a second way out or a call
64//! inside, and those come back here.
65//!
66//! # A walk that goes the other way
67//!
68//! A loop whose address goes down each time round is the same transformation looked at from the
69//! other end, and it is written here so that it is the same code. The offset the guard carries
70//! counts bytes moved from the first access rather than bytes added to it, so it still goes up by
71//! the step every time round and everything built on it is untouched: the guard block, the block
72//! parameter, the clamp and the test are the ones above, word for word.
73//!
74//! What changes is which end of the object the runtime is asked about. The window has to be room
75//! below the first access rather than above it, so the query is `cap_extent_back` and it is asked at
76//! `first + reach`, the end of the first access rather than its start. The answer is how many bytes
77//! ending there belong to whatever owns them, the window is that less the reach as before, and the
78//! access on iteration `delta` is the `reach` bytes ending at `first + reach - delta`. That is what
79//! `swept.down.sym.i64` in the rule table is written about, and it is asked instead of the ascending
80//! rule rather than derived from it.
81//!
82//! Anchoring at the end is what buys all of that. Anchoring at the lowest address the loop reaches
83//! would need a real trip count, since where the verified range starts would then depend on how far
84//! the loop goes, and this pass takes loops nobody counted. Not knowing is free for an ascending
85//! walk, where asking for too little only costs iterations in the slow half. It is unsound for a
86//! descending one, so the query goes the other way instead of the anchor.
87//!
88//! # A walk nobody could follow
89//!
90//! Everything above assumes the pass knows how far the address moves each time round. Most of what
91//! is left on real code is loops where it does not, and they are not exotic: a scanner that steps by
92//! one or by two depending on what it just read, a pointer that comes back round through a join
93//! because the body has a branch in it, a walk whose step is a width the caller passed in. None of
94//! those is an induction variable and scalar evolution has nothing to say about any of them, so they
95//! arrive here as an address that does something unknown.
96//!
97//! The way through is to stop asking how far the address moves and ask instead where it is. If the
98//! check's address is a fixed distance from a pointer the loop's header carries, then the guard can
99//! take where that pointer was on the way in from where it is now, and the difference is the
100//! displacement itself. It is exact rather than an upper bound on it, so the same window and the same
101//! rule apply word for word, and the guard tests it with the same unsigned comparison. It costs a
102//! subtract in the guard and saves the block parameter and the add at the latch, so it is not more
103//! code than counting.
104//!
105//! What has to be established is that the pointer is its own former self plus bytes, and the reason
106//! is money rather than soundness. The guard compares the difference against the window at run time,
107//! so `p = p->next` is safe to measure: a node that landed inside the first one's object passes the
108//! comparison and one that did not takes the slow half, and either way the answer is right. It is
109//! that a list never passes. The next node of a heap allocated list is its own object, so the guard
110//! fails on the second iteration and every one after it, and the split bought a second copy of the
111//! loop with every check still in both halves. Letting lists through on SQLite splits 73 more loops,
112//! puts 220 more calls to `check_bounds` in the object and adds 139 kilobytes, for 5 liveness checks.
113//! So the value the latch hands back has to reach the parameter through `ptr_add`s, block parameters
114//! inside the loop and `select`, and a load anywhere on the way is a refusal. `measured` is where
115//! that walk is, and it is syntactic because what it is buying is.
116//!
117//! A fixed distance from a pointer the header carries is not the only address the guard can find its
118//! way to, and on real code it is not even the commonest. The one above it is that pointer plus a
119//! variable, which is an address that is still a function of what the header carries and of what the
120//! loop was handed, and both the guard and the preheader hold every one of those. So the guard writes
121//! the arithmetic out again from its own parameters, the preheader writes it out again from the
122//! values it passes, and the subtraction between the two is the same subtraction. That is
123//! rematerialization rather than measurement, `writable` is where it is decided and `remade` is where
124//! it is written, and the fixed distance case is the instance of it that costs nothing to write.
125//!
126//! What may be written again is a list of opcodes rather than a question about effects, because two
127//! things have to hold and neither is what an effect flag answers. The copy has to compute the same
128//! number somewhere else, which is what rules out reading memory, and it has to be harmless in the
129//! preheader of a loop that turns out to run no iterations, which is what rules out a divide.
130//!
131//! # Why the fast half may drop a check
132//!
133//! `check_bounds` asks whether the bytes an access names lie inside one object. Every address in
134//! `[first, first + extent)` is inside the object that owns `first`, by what the query answers, and
135//! the window is exactly the offsets whose access stays inside that. So no check in the fast half
136//! could have failed.
137//!
138//! `check_live` asks whether anything owns the address right now, and the query answered that too,
139//! since a byte belonging to the owner of `first` is a byte with an owner. Right now is the catch,
140//! and it is why nothing that could free may be in the loop. A call in the body could free the object
141//! between the question and the iteration that reads it, and then the fast half would read freed
142//! storage with nothing to say so.
143//!
144//! That is a question about the callee rather than about calling, and [`crate::nofree`] answers it
145//! before the pipeline starts, so a call carrying [`rucc_ir::Flags::NOFREE`] is one the loop may
146//! keep. Hoisting refuses every call whatever it does, and the reason is not this one: it needs the
147//! loop to reach the end of what its count says, and a call that does not come back leaves it short.
148//! Splitting never claims the loop reaches the end, so a call that might not come back costs it
149//! nothing.
150//!
151//! `check_deriv` asks whether a pointer computed from another one stayed inside the capability the
152//! first one had, and that is the same containment written about a pointer rather than about the
153//! bytes under it. It is the narrower question of the two, since the window document 03 section 3.1
154//! allows a derivation runs a stride below the object and up to its end, and the fast half is only
155//! ever claiming the address is inside. So a loop whose bounds check the window covers has a
156//! derivation check the same window covers, and on the two benchmarks where an index walks a byte
157//! at a time that check was all the fast half had left in it.
158//!
159//! What it needs beyond a walk is that the extent was asked about the object the check names. The
160//! query goes to the first iteration's address, so an address a little way along from the pointer
161//! the check is about is a question about whatever owns that instead, which past the end of one
162//! object is the next object rather than nothing. Two shapes give the right object and `started` and
163//! `paired` are the two. Either the walk starts on the pointer the check names, or that pointer
164//! walks the loop alongside the new one, in which case the two are a fixed distance apart on every
165//! iteration and a window that wide holds the pair: the lower end being inside the object says the
166//! capability is that object and the upper end being inside it says the derivation stayed there.
167//!
168//! The second is the commoner by a long way, because `p = p + k` is what most pointer arithmetic in
169//! a loop is, and it is what `bench/safety/a-string-scan` does.
170//!
171//! Two answers of the query carry the weight and both are argued where the query is implemented. An
172//! address no watched region covers gets the whole limit back, so a loop over a local or a global
173//! splits into a fast half that runs the whole way, which is right because no check on such an
174//! address ever fires under this milestone. An address whose granule nobody owns gets zero, so the
175//! limit is zero, the fast half runs no iterations, and the check inside the slow half is what reports
176//! the dangling pointer, at the access rather than at the loop.
177//!
178//! # Which loops
179//!
180//! One latch, a preheader, nothing in it that could free, and no value defined inside it that
181//! anything outside reads. Not a count, unlike hoisting, and not even a step: the count was spent on
182//! how far to ask the runtime to look and nothing asks for less than everything any more, and the
183//! step was spent on the same thing. The last is loop closed form, which [`crate::canon`]
184//! establishes, and it is checked rather than assumed because the copy would otherwise leave a reader
185//! outside the loop seeing whichever half happened to define the value.
186//!
187//! Canonicalization runs a long way in front of this, and `simplify-cfg` between the two undoes some
188//! of what it did, so on SQLite the closed form condition once refused 351 of the checks this would
189//! otherwise have taken out. Running canonicalization again in front of this gets 156 of them back
190//! and costs 17672 bytes of `.text`, which is a bad trade for eleven more checks, so the answer is
191//! that this repairs the one loop it is splitting rather than the pipeline repairing every loop in
192//! the function. `repaired` is that, and with the repair reaching the joins the exits meet at as
193//! well as the exits themselves the condition now refuses none of them.
194//!
195//! What the repair cannot help with is a name the pass is about to write and has not written yet. A
196//! guard is worked out from values the loop was handed, and where the loop before it is one this is
197//! also splitting, a value that loop defines stops being one value the moment it has two halves.
198//! Those loops are refused, and there are five of them on SQLite against the two hundred and fifty
199//! the repair finishes.
200//!
201//! A loop with a loop inside it is not refused, and there is nothing about an inner loop that would
202//! make the copy wrong: the copier takes any set of blocks and the guard goes in front of the outer
203//! header either way. What the outer guard cannot speak for is a check inside the inner loop, since
204//! it measures where the outer walk has got to at the top of an outer iteration and the inner loop
205//! runs its whole way inside that iteration. Those checks stay in both halves and the inner loop's
206//! own split is what takes them, so what an outer split is worth is the checks in the outer loop's
207//! own blocks. On SQLite that is most of what is there: of the 169 nests the pass used to refuse
208//! outright, 162 have a check in the outer loop's own blocks and 113 have more than six.
209//!
210//! Where a nest plans twice the inner plan wins, because the two plans name blocks in common and
211//! applying either moves them. The outer one comes back on the next run of the pipeline. The size
212//! limit is the one limit, counted over the whole nest, which is what `heuristics::SPLIT_MAX_INSNS`
213//! already counts since a loop's block list holds the blocks of the loops inside it. A second and
214//! smaller limit was the obvious guess and the measurement says it is not needed: the outer loop's
215//! own blocks are over fifty instructions in 115 of those 169, so a nest that fits inside the limit
216//! is mostly the outer loop rather than mostly the inner one, and the limit is already pricing the
217//! part that pays.
218//!
219//! Not every check in the loop has to be one this can size. A check whose address the analysis cannot
220//! follow simply stays in both halves, and the fast half is then a loop with fewer checks in it rather
221//! than none. That is worth having on its own and it is worth having because it is what a real loop
222//! looks like: one sweep the analysis reads and one index that came out of a table.
223//!
224//! # Which level
225//!
226//! `-O2` and `-O3`, alongside `crate::unroll` and for the same reason. The loop body is copied, so
227//! the function grows by about the size of the loop, and buying speed with code is what those levels
228//! are for and what `-Os` and `-Oz` are for declining.
229
230use std::collections::{HashMap, HashSet};
231
232use rucc_cost::heuristics;
233use rucc_ir::{
234    Block, BlockCall, Builder, Def, Extra, Flags, Func, Inst, InstData, IntPred, Opcode, Type,
235    Value,
236};
237
238use crate::canon;
239use crate::cfg::Cfg;
240use crate::copy;
241use crate::discharge::{Question, constant, operand_of, yes};
242use crate::dom::Dominators;
243use crate::frontier::Frontiers;
244use crate::loops::{LoopId, Loops};
245use crate::rules::safety;
246use crate::scev::{Anchor, Evolution, Plain, Reading, Scev};
247use crate::trip::inst_of;
248use crate::{Analyses, Fuel, Pass, Preserved, Stats};
249
250/// What is reported when a loop is split.
251const SPLIT: &str = "loop split, the iterations in front of the first one that could fail a check \
252                     run without them";
253
254/// What is reported when a loop had to be put back into closed form before it could be split.
255const CLOSED_HERE: &str = "loop put back into closed form, a value it defines is read after it and both halves define one";
256
257/// What is reported when the pass ran out of fuel with a loop it was about to split.
258const NO_FUEL: &str = "loop left alone, the pass ran out of fuel";
259
260/// What is reported for a loop with nowhere to work the limit out.
261const NO_PREHEADER: &str = "loop left alone, it has no block in front of it to put a check in";
262
263/// What is reported for a loop whose blocks another loop being split here has already taken.
264const NESTED_WITH_ONE: &str = "loop left alone, a loop inside it is being split here instead";
265
266/// What is reported for a check that is not in the blocks of the loop being split.
267const INSIDE_A_LOOP: &str =
268    "check kept in both halves, it is in a loop inside the one being split and moves with that one";
269
270/// What is reported for a loop with more than one way round.
271const MANY_LATCHES: &str = "loop left alone, it goes back to its header from more than one place";
272
273/// What is reported for a loop with a call in it that could free.
274const A_CALL_INSIDE: &str = "loop left alone, a call in it might free what the loop is reading";
275
276/// What is reported for a loop holding something that ends a lifetime outright.
277const ENDS_A_LIFETIME: &str = "loop left alone, something in it ends a lifetime";
278
279/// What is reported for a loop holding something the copier cannot copy.
280const NOT_COPYABLE: &str = "loop left alone, something in it carries a side table this cannot copy";
281
282/// What is reported for a loop whose values are read after it without going through a parameter.
283const ESCAPES: &str = "loop left alone, a value it defines is read outside it";
284
285/// What is reported for a loop another loop's guard is about to name a value of.
286const WANTED_ELSEWHERE: &str =
287    "loop left alone, the guard of another loop being split here names a value it defines";
288
289/// What is reported for a loop whose two halves would be too much code.
290const TOO_BIG: &str = "loop left alone, the two halves would be more code than the limit allows";
291
292/// What is reported for a check whose address does not walk the loop.
293const NOT_A_SWEEP: &str = "check kept in both halves, its address does not walk the loop by a \
294                           constant";
295
296/// What is reported for a check whose address the analysis has nothing to say about.
297///
298/// The last of the four below rather than the only one, and what is left once [`stopped`] has had a
299/// look at the address. Anything that reaches here is an address built some way none of the three
300/// named shapes covers, so the row is the remainder of the census rather than the whole of it.
301const NOT_FOLLOWED: &str = "check kept in both halves, what its address does round the loop is not \
302                            something the analysis follows";
303
304/// What is reported for a check on a pointer the loop carries and reads back out of memory.
305const WALKS_A_STRUCTURE: &str = "check kept in both halves, the pointer it is about comes back \
306                                 round the loop out of memory, which is a walk over a linked \
307                                 structure";
308
309/// What is reported for a check whose address was itself read out of memory inside the loop.
310const ADDRESS_FROM_MEMORY: &str =
311    "check kept in both halves, the pointer it is about was read out of memory inside the loop";
312
313/// What is reported for a check whose address came back from a call inside the loop.
314const ADDRESS_FROM_A_CALL: &str =
315    "check kept in both halves, the pointer it is about came back from a call inside the loop";
316
317/// What is reported for a check whose address is one of two the loop chose between.
318const ADDRESS_FROM_A_CHOICE: &str =
319    "check kept in both halves, the pointer it is about is one of two the loop chose between";
320
321/// What is reported for a check on a pointer the pass cannot fault, moved by a displacement it can.
322const STEP_NOT_FOLLOWED: &str = "check kept in both halves, its address is a displacement off a \
323                                 pointer and the displacement is not something the analysis follows";
324
325/// What is reported for a check whose step does not keep its alignment.
326const MISALIGNED: &str =
327    "check kept in both halves, its step is not a whole number of its alignment";
328
329/// What is reported for a check the guard would have to measure, whose access wants an alignment
330/// nothing here can promise.
331const MEASURED_ALIGN: &str = "check kept in both halves, the guard would measure how far its \
332                              address moved and that is no answer about its alignment";
333
334/// What is reported for a check that already covers a range the program worked out.
335const ALREADY_COMPUTED: &str =
336    "check kept in both halves, how many bytes it covers is a number only the program has";
337
338/// What is reported for a derivation check whose walk does not start on the pointer it is about.
339const NOT_FROM_THE_START: &str = "derivation check kept in both halves, the walk starts along from \
340                                  the pointer the check is about rather than on it";
341
342/// What is reported for a check the rule table will not say yes about.
343const NOT_PROVED: &str = "check kept in both halves, no rule in the safety namespace says an offset inside the window is \
344     an access inside the object";
345
346/// The pass.
347#[derive(Debug)]
348pub struct Split;
349
350impl Pass for Split {
351    fn name(&self) -> &'static str {
352        "split"
353    }
354
355    fn describe(&self) -> &'static str {
356        "a loop becomes a run of iterations with no checks in it and the rest of the loop with them"
357    }
358
359    fn preserves(&self) -> Preserved {
360        // Blocks appear and edges move, so nothing built on the graph stands.
361        Preserved::NONE
362    }
363
364    fn run(&self, func: &mut Func, an: &mut Analyses, fuel: &mut Fuel) -> Stats {
365        let mut stats = Stats::new();
366        if func.entry().is_none() {
367            return stats;
368        }
369        let cfg = an.cfg(func);
370        let loops = an.loops(func);
371        if loops.count() == 0 {
372            return stats;
373        }
374
375        // Worked out first and applied afterwards, because scalar evolution reads the function and
376        // the transformation writes it. No two plans share a block, which `planned` sees to, so
377        // applying one leaves every other one's blocks where they were.
378        let mut plans = planned(func, cfg, loops, &mut stats);
379
380        // Closed form put back where it is missing, before anything is copied. The repair adds a
381        // block parameter and rewrites uses, so it moves no edge and creates no block, which is why
382        // the graph and the loop forest above are both still good after it. What it does move is
383        // which value a use inside another loop names, and a plan is a list of values, so a repair
384        // means the plans are worked out again rather than trusted. The stats go with them, or the
385        // first round's reasons would be counted twice.
386        let dom = an.dominators(func);
387        let fronts = an.frontiers(func);
388        let repairs = repaired(func, dom, fronts, loops, &plans, fuel);
389        if repairs.made > 0 {
390            stats = Stats::new();
391            plans = planned(func, cfg, loops, &mut stats);
392            for _ in 0..repairs.worked {
393                stats.optimized(CLOSED_HERE);
394            }
395        }
396        // A guard names values, and until it is written those uses are in the plans rather than in
397        // the function, so the walk that looks for a value read outside the loop cannot see them.
398        // They are collected here and the loops they belong to are refused, because a loop that is
399        // split stops having one value where another loop's guard expects to find one.
400        let named: Vec<(LoopId, Value)> = plans
401            .iter()
402            .flat_map(|plan| mentions(func, plan).into_iter().map(move |value| (plan.id, value)))
403            .collect();
404        plans.retain(|plan| {
405            if leaving(func, plan) {
406                stats.missed(ESCAPES);
407                return false;
408            }
409            if elsewhere(func, plan, &named) {
410                stats.missed(WANTED_ELSEWHERE);
411                return false;
412            }
413            true
414        });
415
416        let mut changed = false;
417        for plan in plans {
418            if !fuel.take() {
419                stats.missed(NO_FUEL);
420                continue;
421            }
422            apply(func, &plan);
423            stats.optimized(SPLIT);
424            changed = true;
425        }
426        if changed {
427            an.clear();
428        }
429        stats
430    }
431}
432
433/// How far past the first access an iteration reads, and who works that out.
434///
435/// Both are the same number and they differ in who does the arithmetic. `By` is a walk the analysis
436/// read, so the guard counts: it carries a byte offset of its own, starts it at zero on the way in
437/// and adds the step every time round. `Of` is a walk the analysis could not read, whose address is
438/// instead a fixed distance from a pointer the loop's header carries, so the guard measures: it
439/// takes where that pointer was on the way in from where it is now, and the difference is the
440/// displacement itself rather than a count standing in for it.
441///
442/// Measuring is what reaches a pointer that moves by an amount nobody wrote down, or by a different
443/// amount down each arm of a branch, or that comes back round through a join. None of those is an
444/// induction variable and there is nothing for scalar evolution to say about any of them, and
445/// between them they are 286 of the checks loop splitting still leaves in place on the SQLite
446/// amalgamation, at 44 sites. See tamnd/rucc#810.
447///
448/// The offset a measured walk produces is exact rather than an upper bound, which is what keeps this
449/// inside the rule table. `swept.sym.i64` is asked about it word for word as it is asked about a
450/// counted one, because `(p + k) - (first + k)` is `p - first` for whatever fixed `k` the check sits
451/// at, so the difference the guard computes is the displacement the rule is written about.
452#[derive(Clone, Copy, Debug, PartialEq, Eq)]
453enum Walk {
454    /// The address moves this many bytes every time round, either way. Zero is an address that does
455    /// not move, which is allowed and puts no limit on the loop. Negative is a walk from high to
456    /// low, and what changes for one is which end of the object the runtime is asked about rather
457    /// than anything about how the two halves are built.
458    By(i128),
459    /// The address is a fixed distance from a value the guard can work out for itself, out of the
460    /// parameters the header carries and the values the loop was handed. The loop moves it on by an
461    /// amount the analysis did not read, so where it is gets measured rather than counted.
462    Again {
463        /// The value to work out again, which is the check's address with the constant `ptr_add`s
464        /// on the front of it taken off. A parameter of the header is the commonest one and costs
465        /// nothing to work out, since the guard already carries it.
466        at: Value,
467    },
468}
469
470impl Walk {
471    /// Whether the address stays where it is, which is a loop that needs no guard at all.
472    fn still(self) -> bool {
473        self == Self::By(0)
474    }
475
476    /// Whether the address walks from high to low, which asks the runtime about the other end of
477    /// the object.
478    ///
479    /// A measured walk never does. The guard's subtraction is read unsigned, so a pointer that went
480    /// below where it started is an enormous displacement and the guard hands the loop to the half
481    /// that kept its checks, which is the answer that end of the object would have given anyway.
482    fn down(self) -> bool {
483        matches!(self, Self::By(step) if step < 0)
484    }
485
486    /// Which offset this walk shares with the others in the loop.
487    fn key(self) -> Key {
488        match self {
489            Self::By(step) => Key::Every(step.abs()),
490            Self::Again { at, .. } => Key::From(at),
491        }
492    }
493}
494
495/// Which checks are at the same offset from their own first access on every iteration, and so can
496/// share one offset in the guard and the smaller of their windows.
497#[derive(Clone, Copy, Debug, PartialEq, Eq)]
498enum Key {
499    /// They walk by the same number of bytes each time round, whichever way each of them goes.
500    Every(i128),
501    /// They are measured from the same value, which the guard works out again for itself. Two
502    /// checks a fixed distance from one pointer are the same distance apart on every iteration,
503    /// whatever the pointer does, so one subtraction answers for both, and one copy of whatever
504    /// arithmetic the pointer took answers for both as well.
505    From(Value),
506}
507
508/// One check the fast half will not need, and the walk that says so.
509#[derive(Debug)]
510struct Sweep {
511    /// The check itself, which is removed from the fast half and kept in the copy.
512    check: Inst,
513    /// Where the first iteration's address is computed from. An address rather than a value when
514    /// it is a global, since nothing outside the loop computes one of those. See [`Anchor`].
515    base: Anchor,
516    /// How far past that value the first iteration reads, in bytes. Usually a number, and a value
517    /// and a scale beside it when the loop started its counter at something it was handed. See
518    /// `spare` for how it is built and #810 for what it is worth.
519    apart: Plain,
520    /// What the address does round the loop, and so what the guard has to work out.
521    walk: Walk,
522    /// Everything inside the loop that has to be written again for the guard to have the address,
523    /// operands before uses. Empty for a counted walk and for a measured one off a parameter the
524    /// header already carries, which is most of them. See [`writable`].
525    rebuild: Vec<Value>,
526    /// How many bytes one access covers.
527    reach: i128,
528    /// How far past the window's first byte the walk's first access sits, when that is a distance
529    /// the loop works out rather than one written here. Nothing on almost every sweep, because the
530    /// two are the same address. See [`trailing`].
531    ahead: Option<Plain>,
532}
533
534/// One loop to split, worked out before anything is written.
535#[derive(Debug)]
536struct Plan {
537    /// The loop itself, which is read again when its closed form has to be repaired.
538    id: LoopId,
539    /// Where the limit is worked out.
540    preheader: Block,
541    /// The block the guard takes over from.
542    header: Block,
543    /// The block the back edge leaves from, which is where the iteration count goes up.
544    latch: Block,
545    /// Everything that is copied, which is the whole loop.
546    body: Vec<Block>,
547    /// The checks the fast half will not need, which is never empty in a plan.
548    sweeps: Vec<Sweep>,
549}
550
551/// Plans a loop, or counts what stopped it.
552///
553/// Nothing is reported for a loop with no check in it, because a loop that does no memory access is
554/// not a missed opportunity and a report for every one of them would bury the loops that are.
555fn sweep(
556    func: &Func,
557    cfg: &Cfg,
558    loops: &Loops,
559    scev: &mut Scev<'_>,
560    id: LoopId,
561    plans: &mut Vec<Plan>,
562    stats: &mut Stats,
563) {
564    let body = loops.blocks(id).to_vec();
565    let checks: Vec<Inst> = body
566        .iter()
567        .flat_map(|&block| func.insts(block).collect::<Vec<Inst>>())
568        .filter(|&inst| {
569            matches!(
570                func[inst].opcode,
571                Opcode::CheckBounds | Opcode::CheckLive | Opcode::CheckDeriv
572            )
573        })
574        .collect();
575    if checks.is_empty() {
576        return;
577    }
578
579    let (preheader, latch) = match shaped(func, cfg, loops, id, &body) {
580        Ok(shape) => shape,
581        Err(why) => {
582            stats.missed(why);
583            return;
584        }
585    };
586    let mut sweeps = Vec::new();
587    for check in checks {
588        // A check in a loop inside this one runs many times for each time round this one, at an
589        // address that moves with the inner loop rather than with this one. The guard here measures
590        // where this loop's walk has got to at the top of an iteration, and that says nothing about
591        // how far the inner loop goes before the iteration is over, so the check stays in both
592        // halves. What takes it is the inner loop's own split, which is a plan of its own.
593        if func.block_of(check).is_none_or(|block| loops.innermost(block) != Some(id)) {
594            stats.missed(INSIDE_A_LOOP);
595            continue;
596        }
597        match walked(func, cfg, loops, scev, id, latch, check) {
598            Ok(sweep) => sweeps.push(sweep),
599            Err(why) => stats.missed(why),
600        }
601    }
602    if sweeps.is_empty() {
603        return;
604    }
605    plans.push(Plan { id, preheader, header: loops.header(id), latch, body, sweeps });
606}
607
608/// The preheader and the latch of a loop this pass may copy, or why there is not one.
609///
610/// The conditions are the module comment's. The one worth restating is freeing, because it is the
611/// only one that is about what the fast half is allowed to leave out rather than about whether the
612/// copy can be made at all: the extent is asked once before the loop and believed for the whole of
613/// the fast half, so anything that could hand the storage back in the middle would make the answer
614/// stale, and the fast half has nothing left in it to notice.
615///
616/// Which is a question about the callee and not about calling, so it is asked of the callee.
617/// [`crate::nofree`] settles it before the pipeline starts and writes the answer onto the call site,
618/// and a call carrying it reaches nothing that ends a lifetime. Note that this is a weaker
619/// requirement than [`crate::hoist`]'s, which refuses every call whatever it does, because hoisting
620/// needs the loop to reach the end of what its count says and a call that does not come back leaves
621/// it short. Splitting never claims that, so coming back is not something it needs.
622fn shaped(
623    func: &Func,
624    cfg: &Cfg,
625    loops: &Loops,
626    id: LoopId,
627    body: &[Block],
628) -> Result<(Block, Block), &'static str> {
629    let Some(preheader) = loops.preheader(cfg, id) else {
630        return Err(NO_PREHEADER);
631    };
632    let [latch] = loops.latches(id) else {
633        return Err(MANY_LATCHES);
634    };
635    for &block in body {
636        for inst in func.insts(block) {
637            match func[inst].opcode {
638                Opcode::Call | Opcode::CallIndirect | Opcode::TailCall
639                    if !func[inst].flags.contains(Flags::NOFREE) =>
640                {
641                    return Err(A_CALL_INSIDE);
642                }
643                // Assembly could do anything and the two meta instructions end a lifetime by
644                // definition, which is the same answer `crate::nofree` gives for all three.
645                Opcode::InlineAsm | Opcode::MetaEnd | Opcode::MetaTransfer => {
646                    return Err(ENDS_A_LIFETIME);
647                }
648                _ => {}
649            }
650            if !copy::copyable(func, inst) {
651                return Err(NOT_COPYABLE);
652            }
653        }
654    }
655    let size = body.iter().map(|&block| func.insts(block).count()).sum::<usize>();
656    if size > heuristics::SPLIT_MAX_INSNS as usize {
657        return Err(TOO_BIG);
658    }
659    Ok((preheader, *latch))
660}
661
662/// Every loop in the function that is worth copying, and why each of the others is not.
663///
664/// A nest can plan twice, once for the inner loop and once for the outer one, and the two plans name
665/// blocks in common. Applying either of them moves those blocks, so only one may run, and the one
666/// kept is the inner one. That is not a coin toss: the outer plan takes checks out of the outer
667/// loop's own blocks, which run once per outer iteration, while the inner plan takes checks out of
668/// blocks that run once per inner iteration, and the inner loop is also the smaller thing to copy.
669/// The outer loop is left for the next run of the pipeline, when the inner one is already split.
670fn planned(func: &Func, cfg: &Cfg, loops: &Loops, stats: &mut Stats) -> Vec<Plan> {
671    let mut plans = Vec::new();
672    let mut scev = Scev::new(func, cfg, loops);
673    for id in loops.all() {
674        sweep(func, cfg, loops, &mut scev, id, &mut plans, stats);
675    }
676    plans.sort_by_key(|plan| std::cmp::Reverse(loops.depth(plan.id)));
677    let mut taken: HashSet<Block> = HashSet::new();
678    plans.retain(|plan| {
679        if plan.body.iter().any(|block| taken.contains(block)) {
680            stats.missed(NESTED_WITH_ONE);
681            return false;
682        }
683        taken.extend(plan.body.iter().copied());
684        true
685    });
686    plans
687}
688
689/// How many loops the closed form repair touched, and how many of those it finished.
690///
691/// Two numbers rather than one because they answer different questions. Anything touched at all is
692/// why the plans have to be worked out again, and only the ones it finished are loops that can now
693/// be copied and so are what gets reported.
694struct Repairs {
695    /// Loops the repair wrote something into.
696    made: usize,
697    /// Loops that are in closed form afterwards.
698    worked: usize,
699}
700
701/// Puts the loops that need it back into closed form, before anything is copied.
702///
703/// [`crate::canon`] establishes closed form a long way in front of this pass and `simplify-cfg`
704/// between the two undoes some of what it did. Running the whole of canonicalization again was
705/// measured and it costs 17672 bytes of `.text` on the SQLite amalgamation, because it repairs every
706/// loop in the function rather than the ones about to be copied. This repairs those, which costs
707/// nothing on a function with no loop to split.
708///
709/// A value read past a join that no single exit dominates gets a parameter at the join as well as
710/// at each exit, which is what the iterated dominance frontier in [`canon::leaked`] is for. What is
711/// still not repaired is a use the placements do not dominate at all, so the count of what worked
712/// is a second look rather than an assumption that the first one did.
713fn repaired(
714    func: &mut Func,
715    dom: &Dominators,
716    fronts: &Frontiers,
717    loops: &Loops,
718    plans: &[Plan],
719    fuel: &mut Fuel,
720) -> Repairs {
721    let mut repairs = Repairs { made: 0, worked: 0 };
722    for plan in plans {
723        if !leaving(func, plan) {
724            continue;
725        }
726        let mut wrote = false;
727        while let Some(job) = canon::leaked(func, dom, fronts, loops, plan.id) {
728            if !fuel.take() {
729                break;
730            }
731            canon::close(func, dom, loops, &job);
732            wrote = true;
733        }
734        if !wrote {
735            continue;
736        }
737        repairs.made += 1;
738        if !leaving(func, plan) {
739            repairs.worked += 1;
740        }
741    }
742    repairs
743}
744
745/// Whether anything after this loop reads a value its body defines.
746fn leaving(func: &Func, plan: &Plan) -> bool {
747    let inside: HashSet<Block> = plan.body.iter().copied().collect();
748    escapes(func, &plan.body, &inside)
749}
750
751/// Every value a plan's guard will name, which is a use that is not in the function yet.
752///
753/// The guard runs in front of the loop and works out where its first access is, so what it names is
754/// whatever those addresses were built on. Where a check's address has to
755/// be written again there is arithmetic to copy as well, and the values that arithmetic rests on are
756/// the operands of the instructions being copied, since [`remade`] rewrites the header's parameters
757/// and leaves everything else naming what it named inside the loop.
758///
759/// The gap a sweep leaves in front of its first access counts too. [`spare`] subtracts it off the
760/// room it has, and it is worked out from a value of its own, so a guard names it just as surely as
761/// it names the address the sweep starts from.
762fn mentions(func: &Func, plan: &Plan) -> Vec<Value> {
763    let mut found = Vec::new();
764    for sweep in &plan.sweeps {
765        found.extend(sweep.base.value());
766        found.extend(sweep.apart.value);
767        found.extend(sweep.ahead.and_then(|ahead| ahead.value));
768        if let Walk::Again { at, .. } = sweep.walk {
769            found.push(at);
770        }
771        for &value in &sweep.rebuild {
772            found.push(value);
773            if let Def::Result { inst, .. } = func[value].def {
774                found.extend(func[func[inst].args].iter().copied());
775            }
776        }
777    }
778    found
779}
780
781/// Whether some other loop being split here has a guard that names a value this one's body defines.
782///
783/// Splitting a loop is what makes such a name wrong. Before it, the value is defined on the one path
784/// out of the loop and so is there to be read in front of the next one. After it, there are two
785/// paths out and the value on each belongs to its own half, which is the same thing loop closed form
786/// is about and is why the repair in front of this pass exists. The repair cannot help here, because
787/// the use it would point at a parameter is one the pass has not written down yet.
788fn elsewhere(func: &Func, plan: &Plan, named: &[(LoopId, Value)]) -> bool {
789    let defined = defines(func, &plan.body);
790    named.iter().any(|&(id, value)| id != plan.id && defined.contains(&value))
791}
792
793/// Every value the blocks of a loop define, parameters and results alike.
794fn defines(func: &Func, body: &[Block]) -> HashSet<Value> {
795    let mut defined: HashSet<Value> = HashSet::new();
796    for &block in body {
797        defined.extend(func[block].params.iter().copied());
798        for inst in func.insts(block) {
799            defined.extend(func[inst].results());
800        }
801    }
802    defined
803}
804
805/// Whether anything outside the loop reads a value defined inside it.
806///
807/// Where there is one, the two halves would leave it reading whichever of them happened to define
808/// it. Closed form is what makes it not one: the use names a parameter of the block the loop leaves
809/// to, and each half fills that parameter in on its own way out.
810fn escapes(func: &Func, body: &[Block], inside: &HashSet<Block>) -> bool {
811    let defined = defines(func, body);
812    for block in func.blocks() {
813        if inside.contains(&block) {
814            continue;
815        }
816        for inst in func.insts(block) {
817            if func[func[inst].args].iter().any(|value| defined.contains(value)) {
818                return true;
819            }
820            for call in func.successors(inst) {
821                if func[call.args].iter().any(|value| defined.contains(value)) {
822                    return true;
823                }
824            }
825        }
826    }
827    false
828}
829
830/// What one check's address does round the loop, or why the pass cannot say.
831///
832/// The counted walk is asked for first and the measured one takes what it could not. That order is
833/// the cheaper answer first: a counted walk costs the guard an add on a value it already carries,
834/// and a measured one costs it a subtraction of two pointers every time round. It is also the more
835/// exact answer first, since a counted walk knows the step and so knows the alignment, which a
836/// measured one never does.
837#[allow(clippy::too_many_arguments)]
838fn walked(
839    func: &Func,
840    cfg: &Cfg,
841    loops: &Loops,
842    scev: &mut Scev<'_>,
843    id: LoopId,
844    latch: Block,
845    check: Inst,
846) -> Result<Sweep, &'static str> {
847    // A derivation check names four operands and the pointer that walks is the third of them, since
848    // the capability it carries is the old pointer's rather than the new one's. Everything below is
849    // written about the address that moves, so the two are pulled apart here and what the shape
850    // needs beyond a walk is asked once the walk is known.
851    let (capability, source, pointer) = match (func[check].opcode, &func[func[check].args]) {
852        (Opcode::CheckDeriv, &[capability, from, to, _stride]) => (capability, Some(from), to),
853        (Opcode::CheckDeriv, _) => return Err(NOT_A_SWEEP),
854        // A check that already carries its own extent is one hoisting put somewhere, and how many
855        // bytes it covers is not a number this pass can divide by a step.
856        (_, args) if args.len() > 2 => return Err(ALREADY_COMPUTED),
857        (_, &[capability, pointer]) => (capability, None, pointer),
858        _ => return Err(NOT_A_SWEEP),
859    };
860    let Some(named) = operand_of(func, capability, Opcode::CapOf, 0) else {
861        return Err(NOT_A_SWEEP);
862    };
863    // Which object the check is about, when that is not the address it names. A derivation check
864    // writes it down and the two have to agree. Every other check names one address and leaves it to
865    // the capability, which is about that address when the capability was taken there and about
866    // something the address was derived from when it was taken once at the pointer the object came
867    // from and shared down the walk. The second is what `rucc_safety::origin` produces, and it asks
868    // the same question a derivation check asks, so it is answered by the same three rules below
869    // rather than by a fourth written for it.
870    let source = match source {
871        Some(from) if named != from => return Err(NOT_A_SWEEP),
872        Some(from) => Some(from),
873        None => (named != pointer).then_some(named),
874    };
875    // A liveness check reads no bytes, so the window it needs is the one byte its address is in.
876    // A bounds check carries how many it reads in its payload. A derivation check reads no bytes
877    // either, and the byte its address is in is the narrower of the two windows document 03 section
878    // 3.1 allows it, so asking for that one is a smaller claim than the judgement needs.
879    let (reach, align) = match func[check].extra {
880        Extra::Mem(held) => (i128::from(func[held].size), i128::from(func[held].align)),
881        _ => (1, 1),
882    };
883
884    let (base, apart, walk, rebuild) = match following(func, scev, id, pointer) {
885        Ok((base, apart, step)) => (base, apart, Walk::By(step), Vec::new()),
886        // The reason the counted walk gave is what gets reported when the measured one cannot take
887        // the check either, so that the census keeps saying what the analysis made of the address
888        // rather than collapsing every one of them into this fallback missing.
889        Err(why) => match measured(func, cfg, loops, id, latch, pointer) {
890            Some(found) => found,
891            // The one reason on that list that names no shape. Every other one says what the
892            // address was and why that is not enough, and this one says the analysis had nothing to
893            // say at all, so what it stopped on is worked out here rather than left as one row.
894            None if why == NOT_FOLLOWED => return Err(stopped(func, loops, id, latch, pointer)),
895            None => return Err(why),
896        },
897    };
898    match walk {
899        // An address a whole number of steps along from an aligned one is aligned, which is the
900        // whole of what this condition is. It is [`crate::hoist`]'s and it is here for the reason it
901        // is there, that a bounds check carries an alignment as well as a byte count.
902        Walk::By(step) if step != 0 && step % align != 0 => return Err(MISALIGNED),
903        // A measured walk moves by an amount nobody wrote down, so there is no such number to divide
904        // and nothing here can say the second access is as aligned as the first. Refusing on the
905        // access wanting any alignment at all is the conservative reading, and it is its own line in
906        // the census so that what it costs is a number rather than a guess.
907        //
908        // Refusing looks like bookkeeping about a payload, because `__rucc_check_bounds` takes an
909        // address, a size and a descriptor and the alignment never reaches it. It is not. The
910        // alignment is a conjunct of J1 in `spec/safe-memory/04-safety-model.md`, it is bug class S7
911        // in document 03, and `tests/safety` has three programs for it that are marked as gaps
912        // closing on `tamnd/rucc#431`. What that means here is that the field is going to start
913        // being read, and a pass that had quietly stopped preserving it in the meantime would be
914        // the reason it could not. So the refusal stays and the sixty odd checks it costs are the
915        // price of a claim that is still open rather than a mistake to be tidied away.
916        Walk::Again { .. } if align > 1 => return Err(MEASURED_ALIGN),
917        _ => {}
918    }
919    // A derivation check asks about the old pointer's capability, and the window is worked out from
920    // the extent of whatever owns the first iteration's address, so those two have to be the same
921    // object. Either the walk starts on the old pointer, which [`started`] is, or the old pointer
922    // walks the loop alongside the new one and one window holds the pair, which [`paired`] is.
923    let (apart, reach, ahead) = match source {
924        None => (apart, reach, None),
925        Some(from) if started(base, apart, from) => (apart, reach, None),
926        Some(from) => match paired(func, scev, id, base, Cover { apart, reach }, walk, from) {
927            Some(cover) => (cover.apart, cover.reach, None),
928            None => match trailing(func, scev, id, base, apart, walk, from) {
929                Some((apart, ahead)) => (apart, reach, Some(ahead)),
930                None => return Err(NOT_FROM_THE_START),
931            },
932        },
933    };
934    // Whether an offset inside the window means an access inside the object, which is what dropping
935    // this check rests on and is not something this file decides. The direction goes with it,
936    // because a walk from high to low is a different claim about addresses and has its own rule.
937    if !windowed(reach, walk.down()) {
938        return Err(NOT_PROVED);
939    }
940    Ok(Sweep { check, base, apart, walk, rebuild, reach, ahead })
941}
942
943/// Whether the first iteration's address is a given pointer rather than somewhere along from it.
944///
945/// [`spare`] asks the runtime about the first iteration's address, which is the base plus however
946/// far the first access sits past it, so a window says what it is meant to say about a derivation
947/// check only when those two are the same address. The condition is the base being the pointer the
948/// check names and the displacement being nothing, which together say the walk starts on it.
949///
950/// A walk that starts a little way along is not rescued by the guard refusing. An address past the
951/// end of one object can be inside the next one, and then the extent comes back positive, the
952/// window is real, and what it is about is the wrong object. The one thing that does hold is a
953/// walk starting on an address nobody owns, which answers zero and sends every iteration to the
954/// slow half, and that is not enough on its own.
955///
956/// [`paired`] is the other way this can hold, and between the two of them they are most of what a
957/// derivation check in a loop looks like.
958fn started(base: Anchor, apart: Plain, from: Value) -> bool {
959    base == Anchor::Value(from) && flat(apart) == Some(0)
960}
961
962/// Where the first iteration's window starts, and how many bytes of it the guard has to ask for.
963///
964/// The two travel together because [`paired`] moves the one and widens the other in the same
965/// breath, and because putting the window somewhere else without saying how wide it now is would
966/// be the mistake that function's doc comment warns about.
967#[derive(Clone, Copy, Debug)]
968struct Cover {
969    /// How far past the anchor the window begins.
970    apart: Plain,
971    /// How many bytes past that the query has to cover.
972    reach: i128,
973}
974
975/// One window that holds the pointer a derivation check is about and where its walk begins.
976///
977/// `p = p + k` is the commonest derivation there is and [`started`] refuses every one of them,
978/// because the pointer the check names is the one moving and the walk therefore starts wherever the
979/// loop was handed rather than on it. On SQLite that refusal is 1546 checks against the 146
980/// [`started`] takes, and `bench/safety/a-string-scan` is the shape: a cursor stepped a byte at a
981/// time, with the derivation check the only thing the fast half still had in it.
982///
983/// The way through is to stop asking about one address and ask about both. Both have to follow one
984/// anchor, so that the distance between them is a number this can work out, and then a window
985/// measured from whichever of them is lower and wide enough to cover the gap holds the pair on the
986/// first iteration. That is the same claim [`windowed`] already asks about an access that many
987/// bytes wide, written about two pointers instead of about the bytes under one, and the pass asks
988/// it in exactly that form rather than inventing a second one.
989///
990/// How wide the access is comes into it, which it did not while this was only ever asked about a
991/// derivation check. A derivation check reads nothing and the byte its address is in is the whole of
992/// what it wants, so the window was the gap and one byte on the end of it. A bounds check carries a
993/// count, and a capability taken where the object came from rather than at the address being checked
994/// brings one here, so the far end is whichever is further of the byte the other pointer is in and
995/// the end of the access. Getting that wrong is a window narrower than the bytes the loop reads,
996/// which is the one mistake in this file that a guard cannot catch.
997///
998/// What it earns is what a derivation check wants. The lower end is inside the object the query was
999/// about, so the capability the check names is that object, and the upper end is inside it too, so
1000/// the pointer computed from it did not leave. Which of the two is the old pointer does not come
1001/// into it, which is why a step down needs nothing said separately: `p = p - 1` is the same pair a
1002/// byte apart with the ends the other way round.
1003///
1004/// # The two steps this takes
1005///
1006/// The old pointer moving by the same step as the new one is the first, and there the distance
1007/// between the two is the same number on every iteration, so the one window holds the pair wherever
1008/// the walk has got to.
1009///
1010/// The old pointer not moving at all is the second, and there the pair comes apart as the walk goes
1011/// on. It is still taken, and what makes it sound is that a pointer which does not move only has to
1012/// be placed once. The first iteration's window holds it, the first iteration is in the fast half
1013/// whenever anything is, and a window on a later iteration says where the walk has reached. So the
1014/// two things a derivation check asks are answered by two askings of the one rule rather than by
1015/// one, and neither of them is arithmetic this file did quietly. On SQLite these are 370 of the
1016/// refusals against the 55 where the old pointer moves at a step of its own, and that last case is
1017/// the one that stays refused: a pointer running away at its own rate is not placed by either
1018/// window.
1019///
1020/// The displacements have to be numbers here, since putting the window on the lower of the two and
1021/// making it as wide as the gap is arithmetic there is no reason to do at run time when the answer
1022/// is already known. A gap the loop works out is [`trailing`], which puts the window somewhere else
1023/// and hands the guard the subtraction.
1024fn paired(
1025    func: &Func,
1026    scev: &mut Scev<'_>,
1027    id: LoopId,
1028    base: Anchor,
1029    cover: Cover,
1030    walk: Walk,
1031    from: Value,
1032) -> Option<Cover> {
1033    let Walk::By(step) = walk else { return None };
1034    let (anchor, behind, along) = following(func, scev, id, from).ok()?;
1035    if anchor != base || (along != step && along != 0) {
1036        return None;
1037    }
1038    let (near, far) = (flat(behind)?, flat(cover.apart)?);
1039    let low = near.min(far);
1040    let high = near.checked_add(1)?.max(far.checked_add(cover.reach)?);
1041    let apart = Plain { value: None, read: None, scale: 0, offset: low };
1042    Some(Cover { apart, reach: high.checked_sub(low)? })
1043}
1044
1045/// The same window when the gap between the two pointers is a distance the loop works out.
1046///
1047/// [`paired`] needs both displacements to be numbers, because it puts the window on the lower of
1048/// the two and makes it as wide as the difference, and neither of those is arithmetic worth doing
1049/// where the answer is already known. A subscript computed in an outer loop is not a number. On
1050/// SQLite that is 103 of the refusals and `bench/safety/a-strided-column-sum.c` is the shape:
1051/// `grid[row * COLS + col]` walked down the rows, where the pointer the check names is the
1052/// allocation itself and the walk begins `col` elements into it.
1053///
1054/// What is done instead is to put the window on the pointer the check names, which is the object
1055/// the check is about and so the object the query has to be about, and hand the guard the gap to
1056/// take off the window it measured. The preheader of the loop being split is where that happens, it
1057/// is a multiply and a subtract, and the value being multiplied is one the outer loop already
1058/// worked out.
1059///
1060/// Two things have to hold and both are asked rather than assumed. The pointer the check names has
1061/// to stand still, for the reason [`paired`] gives. And the gap has to come out at or above zero,
1062/// since a walk beginning below the pointer the window was measured from is a walk into bytes the
1063/// extent said nothing about. That second one is not a range the analysis reads, it is a comparison
1064/// the guard makes, and it is the one extra instruction this costs over [`paired`].
1065///
1066/// A walk that goes down is left alone. Its window is measured backwards from the end of the first
1067/// access, so the gap would be a claim about bytes on the other side of the pointer and it is a
1068/// different argument rather than this one with a sign changed.
1069fn trailing(
1070    func: &Func,
1071    scev: &mut Scev<'_>,
1072    id: LoopId,
1073    base: Anchor,
1074    apart: Plain,
1075    walk: Walk,
1076    from: Value,
1077) -> Option<(Plain, Plain)> {
1078    if !matches!(walk, Walk::By(_)) || walk.down() {
1079        return None;
1080    }
1081    let (anchor, behind, along) = following(func, scev, id, from).ok()?;
1082    if anchor != base || along != 0 {
1083        return None;
1084    }
1085    let near = flat(behind)?;
1086    // Nothing to hand the guard when the walk's own displacement is a number as well, since that is
1087    // the case [`paired`] took and this would be a worse answer to it.
1088    apart.value.filter(|_| apart.scale != 0)?;
1089    let ahead = Plain { offset: apart.offset.checked_sub(near)?, ..apart };
1090    Some((Plain { value: None, read: None, scale: 0, offset: near }, ahead))
1091}
1092
1093/// The displacement as a number, when it is nothing but one.
1094///
1095/// [`displacement`]'s test written the other way round: nothing to add is a value that is not there
1096/// or is not counted, and no number on top of it.
1097fn flat(apart: Plain) -> Option<i128> {
1098    apart.value.filter(|_| apart.scale != 0).is_none().then_some(apart.offset)
1099}
1100
1101/// The walk scalar evolution read, as a base to measure from and a step in bytes.
1102///
1103/// An address that does not move is a sweep with a step of zero, and the arithmetic downstream takes
1104/// it without a special case anywhere. Hoisting would rather have these, but hoisting only gets the
1105/// ones in loops it is willing to touch at all, and a loop it refused for one of its own reasons
1106/// leaves the check where it is. Splitting is willing to touch more loops, so the same check comes
1107/// back here and there is no reason to hand it back.
1108fn following(
1109    func: &Func,
1110    scev: &mut Scev<'_>,
1111    id: LoopId,
1112    pointer: Value,
1113) -> Result<(Anchor, Plain, i128), &'static str> {
1114    let (start, step) = match scev.evolution(id, pointer) {
1115        Evolution::Affine(chrec) => {
1116            let Some(step) = chrec.step.as_number() else {
1117                return Err(NOT_A_SWEEP);
1118            };
1119            (chrec.base, step)
1120        }
1121        Evolution::Invariant(base) => (base, 0),
1122        _ => return Err(NOT_FOLLOWED),
1123    };
1124    // Scale one because the base is an address. Anything else is a multiple of a pointer, which is
1125    // not a thing the loop computed, so it is a shape this reads rather than a case to handle.
1126    //
1127    // The second arm is `a + 8 * start`, an address the loop reached before it began, which is what
1128    // a counter the caller handed in looks like once the front end has multiplied the element size
1129    // through it. The pointer is the side the whole thing is measured from and the index is what is
1130    // scaled beside it, so anything else with two values in it is refused here rather than turned
1131    // into an address off whichever value came first.
1132    match (start.plain(), start.on()) {
1133        (Some(at @ Plain { value: Some(base), read: None, scale: 1, .. }), _) => Ok((
1134            Anchor::Value(base),
1135            Plain { value: None, read: None, scale: 0, offset: at.offset },
1136            step,
1137        )),
1138        (_, Some((base, apart))) if walks(func, base, apart) => Ok((base, apart, step)),
1139        _ => Err(NOT_A_SWEEP),
1140    }
1141}
1142
1143/// The walk the guard can measure, for an address the guard can work out for itself.
1144///
1145/// A syntactic walk rather than an analysis, because what it has to establish is syntactic. The
1146/// address is peeled of the constant `ptr_add`s on the front of it, and what is under them has to be
1147/// something the guard could write again out of the parameters the header hands it and the values
1148/// the loop was handed from outside. The first access is then the same expression written in the
1149/// preheader out of the values the preheader passes, `k` bytes along, and the displacement on any
1150/// later iteration is the one less the other. That is a subtraction the guard can do, whatever the
1151/// loop did to the pointer in between.
1152///
1153/// The commonest shape by far is the address being a parameter of the header outright, and that
1154/// costs nothing to write again: the guard already carries the parameter and the preheader already
1155/// passes it. Everything past that is [`writable`] and [`remade`], which are what make `p + x` for
1156/// a variable `x` reachable, and `x` is a variable in a third of what is left here.
1157///
1158/// # What the back edge has to look like
1159///
1160/// The value the latch hands the parameter has to be that same parameter moved: through `ptr_add`s,
1161/// through parameters of blocks inside the loop, and through a `select`, which is what a branch that
1162/// moves the pointer differently down each arm turns into. Anything else is refused.
1163///
1164/// That question is asked of every pointer the address is built on that the header carries. One the
1165/// loop was handed from outside does not move at all and so has nothing to answer.
1166///
1167/// The refusal is the point of the walk, and not for the reason it looks like. The subtraction is
1168/// sound whatever the pointer did, because the guard compares the difference against the window at
1169/// run time: a pointer that landed inside the first one's object passes and one that did not takes
1170/// the slow half. What the refusal is about is profit. A list is `p = p->next`, where the value on
1171/// the back edge is a load, and the next node of a heap allocated list is its own object, so the
1172/// guard fails on the second iteration and every one after it and both halves keep every check.
1173/// Measured on SQLite, taking lists as well splits 73 more loops, puts 220 more calls to
1174/// `check_bounds` in the object and adds 139 kilobytes, and removes 5 liveness checks.
1175fn measured(
1176    func: &Func,
1177    cfg: &Cfg,
1178    loops: &Loops,
1179    id: LoopId,
1180    latch: Block,
1181    pointer: Value,
1182) -> Option<(Anchor, Plain, Walk, Vec<Value>)> {
1183    let (at, offset) = peeled(func, pointer);
1184    if !func[at].ty.is_ptr() {
1185        return None;
1186    }
1187    let mut rebuild = Vec::new();
1188    let mut leaves = Vec::new();
1189    let mut seen = HashSet::new();
1190    if !writable(func, loops, id, at, &mut rebuild, &mut leaves, &mut seen) {
1191        return None;
1192    }
1193    if rebuild.len() > heuristics::SPLIT_REMADE_INSNS {
1194        return None;
1195    }
1196    if !leaves.iter().all(|&leaf| carried(func, cfg, loops, id, latch, leaf)) {
1197        return None;
1198    }
1199    // The base is where the first access is measured from, and it is written in the preheader by
1200    // `limited` rather than named here, since for anything but a bare parameter no such value exists
1201    // yet. `Anchor::Value(at)` says which expression to write, and `limited` is where it is written.
1202    let apart = Plain { value: None, read: None, scale: 0, offset };
1203    Some((Anchor::Value(at), apart, Walk::Again { at }, rebuild))
1204}
1205
1206/// Whether the guard could write the expression that works this address out somewhere else, and in
1207/// what order.
1208///
1209/// The two places it would be written are the guard, out of the parameters the header carries, and
1210/// the preheader, out of the values the preheader passes the header. So a value stops the walk when
1211/// both of those already have it, and there are two ways that happens. A value defined outside the
1212/// loop is the same number wherever it is read, so it is written again by being read again. A
1213/// parameter of the header is carried by the guard and passed by the preheader, so each of them has
1214/// its own in hand. Both kinds are leaves, and a pointer leaf is reported to the caller because
1215/// whether the address is worth measuring turns on what the loop does to it.
1216///
1217/// Everything else in the loop has to be an instruction this may write a second copy of. A parameter
1218/// of a block inside the loop is not: it is a join, and which value arrived depends on which way the
1219/// iteration went, which neither the guard nor the preheader is in a position to know. Nor is
1220/// anything that reads memory, because the second copy would read it at a different moment.
1221///
1222/// The order is a post order, so operands come out in front of the uses that want them, which is
1223/// what [`remade`] needs to write them in one pass. It may hold junk when this refuses, and the
1224/// caller throws it away.
1225fn writable(
1226    func: &Func,
1227    loops: &Loops,
1228    id: LoopId,
1229    value: Value,
1230    order: &mut Vec<Value>,
1231    leaves: &mut Vec<Value>,
1232    seen: &mut HashSet<Value>,
1233) -> bool {
1234    // A value reached twice is written once, and its place in the order is the first one, which is
1235    // in front of both uses. Returning true here is safe because a refusal anywhere refuses the
1236    // whole address, so a value already seen is one already accepted.
1237    if !seen.insert(value) {
1238        return true;
1239    }
1240    let at = match func[value].def {
1241        Def::Result { inst, .. } => func.block_of(inst),
1242        Def::Param { block, .. } => Some(block),
1243    };
1244    if at.is_none_or(|at| !loops.contains(id, at)) {
1245        if func[value].ty.is_ptr() {
1246            leaves.push(value);
1247        }
1248        return true;
1249    }
1250    // A value defined in a loop inside this one is neither of those. It is not the same number
1251    // wherever it is read, so reading it again in the preheader is not writing it again, and it is
1252    // not a parameter of the header, so neither block has it in hand. The two questions look alike
1253    // and the answers are opposite, which is why this arm is separate from the one above rather
1254    // than folded into it as "not in this loop's own blocks".
1255    if at.is_some_and(|at| loops.innermost(at) != Some(id)) {
1256        return false;
1257    }
1258    match func[value].def {
1259        Def::Param { block, .. } => {
1260            if block != loops.header(id) {
1261                return false;
1262            }
1263            if func[value].ty.is_ptr() {
1264                leaves.push(value);
1265            }
1266            true
1267        }
1268        Def::Result { inst, index } => {
1269            if index != 0 || !plain(func[inst].opcode) {
1270                return false;
1271            }
1272            let args = func[func[inst].args].to_vec();
1273            if !args.iter().all(|&arg| writable(func, loops, id, arg, order, leaves, seen)) {
1274                return false;
1275            }
1276            order.push(value);
1277            true
1278        }
1279    }
1280}
1281
1282/// Whether an instruction is one the guard may write a second copy of.
1283///
1284/// A list rather than a question about effects, and deliberately. What has to hold is that a second
1285/// copy in another block computes the same number, which rules out anything that reads memory and
1286/// anything that depends on where it is, and that writing it in the preheader is harmless on a loop
1287/// that turns out to run no iterations at all, which rules out anything that can fault. A division
1288/// is the one that catches people out: it has no effects to speak of and it traps on a zero the
1289/// first iteration would never have reached. Naming what is allowed makes an opcode added later
1290/// refused until somebody looks at it, which is the right way round for this.
1291fn plain(opcode: Opcode) -> bool {
1292    matches!(
1293        opcode,
1294        Opcode::IConst
1295            | Opcode::Add
1296            | Opcode::Sub
1297            | Opcode::Mul
1298            | Opcode::Shl
1299            | Opcode::LShr
1300            | Opcode::AShr
1301            | Opcode::And
1302            | Opcode::Or
1303            | Opcode::Xor
1304            | Opcode::SExt
1305            | Opcode::ZExt
1306            | Opcode::Trunc
1307            | Opcode::ICmp
1308            | Opcode::Select
1309            | Opcode::PtrAdd
1310            | Opcode::GlobalAddr
1311    )
1312}
1313
1314/// Writes the expression that works an address out into the block a builder is on, with the header's
1315/// parameters replaced by whatever that block has in their place.
1316///
1317/// The order is [`writable`]'s, so every operand has been written by the time the use of it is
1318/// reached and one pass over the list is enough. A value not in the map is one from outside the loop,
1319/// which is itself wherever it is read.
1320///
1321/// Flags come off. `nsw` on an add in the loop is a promise about an address the loop was going to
1322/// compute, and the copy in the preheader is computed whether the loop runs or not, so a promise that
1323/// held there does not obviously hold here. Dropping it costs nothing, since what is built is a
1324/// question for the runtime rather than an address anything reads through.
1325fn remade(
1326    build: &mut Builder<'_>,
1327    made: &mut Vec<Value>,
1328    order: &[Value],
1329    at: Value,
1330    swap: &HashMap<Value, Value>,
1331) -> Value {
1332    let mut swap = swap.clone();
1333    for &value in order {
1334        let Def::Result { inst, .. } = build.func()[value].def else {
1335            unreachable!("the order holds nothing but instruction results")
1336        };
1337        let data = build.func()[inst];
1338        let args: Vec<Value> = build.func()[data.args]
1339            .iter()
1340            .map(|arg| swap.get(arg).copied().unwrap_or(*arg))
1341            .collect();
1342        let args = build.func().push_values(&args);
1343        let ty = build.func()[value].ty;
1344        let copy =
1345            build.value(InstData { args, extra: data.extra, ..InstData::new(data.opcode) }, ty);
1346        made.push(copy);
1347        swap.insert(value, copy);
1348    }
1349    swap.get(&at).copied().unwrap_or(at)
1350}
1351
1352/// Whether the loop moves a pointer it carries in a way this is willing to measure.
1353///
1354/// A pointer the loop was handed from outside does not move at all and is nothing to refuse. One the
1355/// header carries is handed back round the latch, and what comes back has to be that same pointer
1356/// moved, which is [`moving`] and is where the linked list refusal lives.
1357fn carried(func: &Func, cfg: &Cfg, loops: &Loops, id: LoopId, latch: Block, leaf: Value) -> bool {
1358    let header = loops.header(id);
1359    let Def::Param { block, index } = func[leaf].def else { return true };
1360    if block != header {
1361        return true;
1362    }
1363    let Some(term) = func.terminator(latch) else { return false };
1364    let round = copy::edge_args(func, term, header);
1365    let Some(&next) = round.get(index as usize) else { return false };
1366    let mut seen = HashSet::new();
1367    moving(func, cfg, loops, id, leaf, next, &mut seen)
1368}
1369
1370/// A pointer with the constant `ptr_add`s on the front of it taken off, and how many bytes they came
1371/// to between them.
1372fn peeled(func: &Func, pointer: Value) -> (Value, i128) {
1373    let mut at = pointer;
1374    let mut offset = 0;
1375    while let Some(by) = operand_of(func, at, Opcode::PtrAdd, 1) {
1376        let (Some(step), Some(of)) = (constant(func, by), operand_of(func, at, Opcode::PtrAdd, 0))
1377        else {
1378            break;
1379        };
1380        offset += step;
1381        at = of;
1382    }
1383    (at, offset)
1384}
1385
1386/// A pointer with every `ptr_add` on the front of it taken off, and whether any of them moved it by
1387/// an amount that is not a number.
1388///
1389/// [`peeled`] stops at a step that is not a number, because what it is working out is a fixed
1390/// distance and a step nobody wrote down is not one. This does not stop, because what it is working
1391/// out is where the address came from, and a step nobody wrote down is still a step off something.
1392/// That the step was there at all is the second thing it hands back, since a displacement the
1393/// program computed is one of the ways an address stops being something the analysis follows.
1394fn beneath(func: &Func, pointer: Value) -> (Value, bool) {
1395    let mut at = pointer;
1396    let mut worked = false;
1397    while let Some(of) = operand_of(func, at, Opcode::PtrAdd, 0) {
1398        let by = operand_of(func, at, Opcode::PtrAdd, 1);
1399        worked = worked || by.is_some_and(|by| constant(func, by).is_none());
1400        at = of;
1401    }
1402    (at, worked)
1403}
1404
1405/// What the analysis stopped on, for an address it had nothing to say about.
1406///
1407/// [`NOT_FOLLOWED`] used to be one row and it is the largest in the census, which made it the least
1408/// useful thing in there: a number that big is a list of different problems, and the row said which
1409/// pass gave up rather than what it gave up on. So the address is taken apart once more here, at the
1410/// point the reason is finally reported, and what is underneath it is what gets named.
1411///
1412/// The header parameter is looked at first and looked through, because a pointer the loop carries is
1413/// the interesting case and what it is depends on what comes back round the latch rather than on the
1414/// parameter. A load there is `p = p->next`, which is the walk over a linked structure the census
1415/// wants counted on its own: nothing in this pass will ever split one, since the guard tests a
1416/// distance and the next node of a list is its own object.
1417fn stopped(func: &Func, loops: &Loops, id: LoopId, latch: Block, pointer: Value) -> &'static str {
1418    let (base, worked) = beneath(func, pointer);
1419    // Only the load is named over the back edge. What else can come back is an address the loop
1420    // worked out some other way, and where that was worked out is the question the rest of this
1421    // answers, so naming it here as well would be the same answer written in two places.
1422    if let Some(next) = round(func, loops, id, latch, base) {
1423        if shape(func, beneath(func, next).0) == ADDRESS_FROM_MEMORY {
1424            return WALKS_A_STRUCTURE;
1425        }
1426    }
1427    let named = shape(func, base);
1428    if named != NOT_FOLLOWED {
1429        return named;
1430    }
1431    // Nothing to say about the pointer the address is built on, so what is left to say is how far
1432    // along it the address is. That is worth its own row because it is a different thing to fix:
1433    // the pointer is fine and the subscript is what nothing here can count.
1434    if worked { STEP_NOT_FOLLOWED } else { NOT_FOLLOWED }
1435}
1436
1437/// The value a header parameter is handed on the way back round, if the pointer is one.
1438///
1439/// [`carried`] does this walk to decide whether to refuse and this one does it to decide what to
1440/// say, which is why neither calls the other: that one wants to know if what comes back is the
1441/// parameter moved, and this one wants the value itself.
1442fn round(func: &Func, loops: &Loops, id: LoopId, latch: Block, pointer: Value) -> Option<Value> {
1443    let Def::Param { block, index } = func[pointer].def else { return None };
1444    if block != loops.header(id) {
1445        return None;
1446    }
1447    let term = func.terminator(latch)?;
1448    copy::edge_args(func, term, block).get(index as usize).copied()
1449}
1450
1451/// Where a pointer with nothing on the front of it came from, said as one of the census rows.
1452fn shape(func: &Func, base: Value) -> &'static str {
1453    let Def::Result { inst, .. } = func[base].def else { return NOT_FOLLOWED };
1454    match func[inst].opcode {
1455        Opcode::Load => ADDRESS_FROM_MEMORY,
1456        Opcode::Call | Opcode::CallIndirect => ADDRESS_FROM_A_CALL,
1457        Opcode::Select => ADDRESS_FROM_A_CHOICE,
1458        _ => NOT_FOLLOWED,
1459    }
1460}
1461
1462/// Whether a value is a header parameter moved by some number of bytes.
1463///
1464/// The conditions are [`measured`]'s and the walk is the obvious one. False is a value that is not
1465/// the parameter moved, which is a refusal, and true is the parameter moved by amounts this does not
1466/// need to know. It used to hand back the largest step it saw, which sized how far the runtime was
1467/// asked to look, and nothing is sized by a step any more.
1468fn moving(
1469    func: &Func,
1470    cfg: &Cfg,
1471    loops: &Loops,
1472    id: LoopId,
1473    param: Value,
1474    value: Value,
1475    seen: &mut HashSet<Value>,
1476) -> bool {
1477    if value == param {
1478        return true;
1479    }
1480    // A value already on the way back is one this has been through, and coming back round to it is
1481    // what a walk through a join looks like. Not a refusal, because this path holds nothing that has
1482    // not been looked at.
1483    if !seen.insert(value) {
1484        return true;
1485    }
1486    let at = match func[value].def {
1487        Def::Result { inst, .. } => match func.block_of(inst) {
1488            Some(block) => block,
1489            None => return false,
1490        },
1491        Def::Param { block, .. } => block,
1492    };
1493    // Anything defined outside the loop is something the loop was handed rather than the parameter
1494    // moved, and it is where the walk stops as well as what it refuses. A value defined in a loop
1495    // inside this one is refused by the same test and it is refused for a stronger reason: what it
1496    // does is a question about the inner loop's iterations rather than about this one's.
1497    if loops.innermost(at) != Some(id) {
1498        return false;
1499    }
1500    match func[value].def {
1501        Def::Result { inst, .. } => {
1502            let args = &func[func[inst].args];
1503            match func[inst].opcode {
1504                Opcode::PtrAdd => match (args.first(), args.get(1)) {
1505                    (Some(&of), Some(_)) => moving(func, cfg, loops, id, param, of, seen),
1506                    _ => false,
1507                },
1508                // Both arms have to be the parameter moved, since either of them may be the one
1509                // taken. The condition is not looked at, because how the loop chose is not something
1510                // the displacement depends on.
1511                Opcode::Select => match (args.get(1), args.get(2)) {
1512                    (Some(&one), Some(&two)) => {
1513                        moving(func, cfg, loops, id, param, one, seen)
1514                            && moving(func, cfg, loops, id, param, two, seen)
1515                    }
1516                    _ => false,
1517                },
1518                _ => false,
1519            }
1520        }
1521        // A parameter of a block inside the loop is a join, and every way into it has to be the
1522        // parameter moved. The header is not one of them: its other parameters are other values and
1523        // the parameter itself was the base case above.
1524        Def::Param { block, index } => {
1525            if block == loops.header(id) {
1526                return false;
1527            }
1528            let mut moved = true;
1529            for &pred in cfg.predecessors(block) {
1530                let Some(term) = func.terminator(pred) else { return false };
1531                let args = copy::edge_args(func, term, block);
1532                let Some(&came) = args.get(index as usize) else { return false };
1533                moved = moved && moving(func, cfg, loops, id, param, came, seen);
1534            }
1535            moved
1536        }
1537    }
1538}
1539
1540/// Whether a pointer and a byte displacement beside it are the two the address is really built out
1541/// of, rather than two values an expression happened to end up holding.
1542///
1543/// The displacement has to end up as wide as the arithmetic, because what is built from it here is
1544/// a `ptr_add` in a preheader. It gets there one of three ways: it is a plain number, or it is
1545/// already sixty four bits, or it is narrower and the invariant says which extension it is read
1546/// through, which is what an index the caller handed in looks like in C, where the index is an
1547/// `int`.
1548fn walks(func: &Func, base: Anchor, apart: Plain) -> bool {
1549    let word = Type::int(64);
1550    if !base.value().is_none_or(|base| func[base].ty.is_ptr()) {
1551        return false;
1552    }
1553    // A global with nothing but a number beside it, which is what a walk over a file scope array
1554    // from a fixed place in it looks like. A number is as wide as it needs to be.
1555    let Some(value) = apart.value.filter(|_| apart.scale != 0) else { return true };
1556    match apart.read {
1557        None => func[value].ty == word,
1558        Some(read) => read.to == word && func[value].ty.is_int() && func[value].ty.bits() < 64,
1559    }
1560}
1561
1562/// Makes the two halves and the block that chooses between them.
1563///
1564/// The order matters in two places. The copy is made before anything is rewired, so the copy's back
1565/// edge is remapped to the copy's own header rather than to a guard that did not exist yet. The
1566/// checks come out of the fast half last, so the copy still has them.
1567fn apply(func: &mut Func, plan: &Plan) {
1568    // The slow half, which is the loop as it stands, under a substitution that renames everything it
1569    // defines. Nothing is seeded, so its header gets parameters of its own, which is what a copy
1570    // reached from a block that also reaches the original needs.
1571    let mut renamed: HashMap<Value, Value> = HashMap::new();
1572    let copies = copy::blocks(func, &plan.body, &mut renamed);
1573    let slow = copies[&plan.header];
1574
1575    let Choice { ok, windows } = limited(func, plan);
1576
1577    // Nothing in the loop moves, so which half runs is settled in the preheader and settled for
1578    // good. There is no guard block and nothing carried round: the way into the loop is the choice.
1579    if windows.is_empty() {
1580        let term = func.terminator(plan.preheader).expect("a preheader ends in a jump");
1581        let args = copy::edge_args(func, term, plan.header);
1582        func.remove_inst(term);
1583        Builder::new(func, plan.preheader).br_if(ok, plan.header, &args, slow, &args);
1584        take(func, plan);
1585        return;
1586    }
1587
1588    // The guard, which takes over the header's place: the preheader arrives here, the back edge
1589    // comes back to here, and the header is reached from here and nowhere else. Its first
1590    // parameters are offsets of its own, one per distinct step, because where the loop's own
1591    // pointers are is not something this pass has to find and a loop with several ways out may
1592    // have nothing that walks in step with what its checks are about.
1593    //
1594    // A measured offset gets no parameter and nothing carried round. Where its pointer is now is
1595    // worked out from the parameters below, which are the ones the header carries, either by being
1596    // one of them outright or by the guard writing the arithmetic out again.
1597    let word = Type::int(64);
1598    let counting: Vec<i128> =
1599        windows.iter().filter(|window| window.from.is_none()).map(|w| stepped(w.key)).collect();
1600    let types: Vec<Type> = func[plan.header].params.iter().map(|&param| func[param].ty).collect();
1601    let guard = func.create_block();
1602    let offsets: Vec<Value> = counting.iter().map(|_| func.append_param(guard, word)).collect();
1603    let carried: Vec<Value> = types.iter().map(|&ty| func.append_param(guard, ty)).collect();
1604
1605    // Unsigned, because the window is a byte count and so is the offset, and because unsigned is
1606    // what the rule the removal rests on is written in. That is what makes the subtraction below
1607    // safe as well: a pointer that went under where it started comes out as a displacement no
1608    // window is ever going to hold, so the loop goes to the half that kept its checks.
1609    let held: HashMap<Value, Value> =
1610        func[plan.header].params.iter().copied().zip(carried.iter().copied()).collect();
1611    // Nothing built here has to be moved afterwards, unlike in the preheader: the guard is a block
1612    // this pass just made and it has no terminator yet, so appending puts things in the order they
1613    // were built and the branch at the end goes on last. `spent` is where the builder drops what it
1614    // made and nothing reads it back.
1615    let mut build = Builder::new(func, guard);
1616    let mut spent = Vec::new();
1617    let mut inside: Option<Value> = None;
1618    let mut counted = 0;
1619    for window in &windows {
1620        let offset = match window.from {
1621            None => {
1622                let offset = offsets[counted];
1623                counted += 1;
1624                offset
1625            }
1626            Some(from) => {
1627                let Key::From(at) = window.key else {
1628                    unreachable!("only a measured window holds where its pointer began")
1629                };
1630                let here = remade(&mut build, &mut spent, &window.rebuild, at, &held);
1631                let now = build.unary(Opcode::PtrToInt, here, word);
1632                build.binary(Opcode::Sub, now, from, Flags::NONE)
1633            }
1634        };
1635        let under = build.icmp(IntPred::Ule, offset, window.bound);
1636        inside = Some(match inside {
1637            None => under,
1638            Some(so_far) => build.binary(Opcode::And, so_far, under, Flags::NONE),
1639        });
1640    }
1641    let inside = inside.expect("a plan with a window has at least one of them");
1642    build.br_if(inside, plan.header, &carried, slow, &carried);
1643
1644    // The way in, which tests whether the fast half may run at all and starts every offset at the
1645    // first access. A loop nothing fits in never reaches the guard.
1646    let term = func.terminator(plan.preheader).expect("a preheader ends in a jump to the header");
1647    let args = copy::edge_args(func, term, plan.header);
1648    func.remove_inst(term);
1649    let mut build = Builder::new(func, plan.preheader);
1650    let zero = build.iconst(word, 0);
1651    let mut into: Vec<Value> = offsets.iter().map(|_| zero).collect();
1652    into.extend_from_slice(&args);
1653    build.br_if(ok, guard, &into, slow, &args);
1654
1655    // The way round, which walks each counted offset on by its step. The offsets are the guard's
1656    // parameters and the guard dominates every block in the fast half, so the latch may read them.
1657    // `nuw` rather than `nsw` because [`bounded`] held the window short of where this could wrap,
1658    // and it held it there in unsigned terms. A measured offset has nothing here: the guard reads
1659    // the pointer the loop already hands round.
1660    let term = func.terminator(plan.latch).expect("a latch ends in a branch back to the header");
1661    let mut build = Builder::new(func, plan.latch);
1662    let mut made = Vec::new();
1663    let mut next = Vec::new();
1664    for (&offset, &step) in offsets.iter().zip(&counting) {
1665        let by = build.iconst(word, step);
1666        made.push(by);
1667        let walked = build.binary(Opcode::Add, offset, by, Flags::NUW);
1668        made.push(walked);
1669        next.push(walked);
1670    }
1671    for value in made {
1672        let inst = inst_of(func, value);
1673        func.remove_inst(inst);
1674        func.insert_before(inst, term);
1675    }
1676    route(func, term, plan.header, guard, &next);
1677    take(func, plan);
1678}
1679
1680/// Takes the checks the fast half does not need out of it.
1681///
1682/// The `cap_of` each one was reading is left where it is, for `dce` after this pass to take away,
1683/// which is the arrangement [`crate::hoist`] and [`crate::discharge`] are both in.
1684fn take(func: &mut Func, plan: &Plan) {
1685    for sweep in &plan.sweeps {
1686        func.remove_inst(sweep.check);
1687    }
1688}
1689
1690/// Sends every edge this terminator has to `from` to `to` instead, with more arguments in front.
1691fn route(func: &mut Func, term: Inst, from: Block, to: Block, first: &[Value]) {
1692    for at in func.target_list(term).iter() {
1693        let call = func[at];
1694        if call.block != from {
1695            continue;
1696        }
1697        let mut args = first.to_vec();
1698        args.extend_from_slice(&func[call.args]);
1699        let args = func.push_values(&args);
1700        func.set_block_call(at, BlockCall { block: to, args, ..call });
1701    }
1702}
1703
1704/// One offset the guard works out every time round, and how far it may get.
1705struct Window {
1706    /// Which checks share it, which for a counted offset is how far the address moves each time
1707    /// round. That is a magnitude, because the offset counts bytes from the first access and counts
1708    /// them the same way whichever direction the address walks.
1709    key: Key,
1710    /// The highest offset an access may start at and still be inside what the extent covers.
1711    bound: Value,
1712    /// Where the pointer was on the way into the loop, as an integer, for an offset the guard
1713    /// measures. `None` for one it counts, which starts at zero and needs nothing to measure from.
1714    from: Option<Value>,
1715    /// What the guard writes again to know where the pointer is now, operands before uses. Empty
1716    /// for a counted offset, and empty for a measured one off a parameter the header carries, since
1717    /// the guard carries that parameter itself. See [`writable`].
1718    rebuild: Vec<Value>,
1719}
1720
1721/// How the two halves are chosen between, which depends on whether any address in the loop moves.
1722struct Choice {
1723    /// Whether every check in the loop fits at all, which the preheader tests before it enters the
1724    /// fast half. It is false for a dangling pointer or an object smaller than the thing being read
1725    /// out of it, and then the fast half runs no iterations and the check in the slow half reports
1726    /// the fault at the access rather than at the loop.
1727    ok: Value,
1728    /// One per distinct step, and empty when no address in the loop moves. A loop like that needs
1729    /// no guard block and nothing carried round it, because `ok` is the whole answer and it does
1730    /// not change while the loop runs.
1731    windows: Vec<Window>,
1732}
1733
1734/// Builds what the preheader has to work out before either half can run.
1735///
1736/// One `cap_extent` per check and what it leaves room for, all of it in the preheader in front of
1737/// the jump into the loop. A builder appends to the end of a block, which in a block that already
1738/// has its terminator is after it, so everything is built first and then moved in front of the
1739/// terminator in the order it was built.
1740///
1741/// # Why the window is bytes and not iterations
1742///
1743/// This used to work out how many iterations a check allows, which is `(extent - reach) / step + 1`
1744/// clamped at zero, and count iterations against it. The claim that has to hold for the fast half
1745/// to be allowed to drop its checks was then that `i * step + reach <= extent` for every `i` below
1746/// that limit, which has a symbolic multiply and a symbolic divide in it at sixty four bits, and
1747/// z3 does not finish on it in two and a half minutes in any of three formulations. So the whole
1748/// transformation sat outside the rule table that `spec/safe-memory/07-check-elimination.md`
1749/// section 7.7 asks every elimination to be inside, and it sat there for a solver reason rather
1750/// than a design one, which is the worst kind.
1751///
1752/// Counting bytes instead of iterations takes the arithmetic out. The offset the loop is at moves
1753/// by `step` each time round exactly as the address does, the window is `extent - reach`, and the
1754/// claim is that an offset at or below that plus the reach is inside the extent. No multiply and no
1755/// divide, and it is the claim `swept.sym.i64` in `crates/rucc-opt/rules/safety.rules` already
1756/// makes, which [`windowed`] asks. The pass earns that rule's hypotheses rather than assuming them:
1757/// `ok` is where `extent` is held to be at least `reach`, so the window cannot have wrapped, and
1758/// [`bounded`] is where the offset is held short of where adding one more step would.
1759///
1760/// It is also less code. A loop with one step in it loses a divide from its preheader and carries
1761/// the same one value round that it did before.
1762///
1763/// # Why one window per step and not one per check
1764///
1765/// Two checks that walk by the same amount are at the same offset on every iteration, so they can
1766/// share the offset and the smaller of their two windows. On SQLite 127 of the 268 loops this
1767/// splits have one distinct step and five have two, so this is one value round the loop almost
1768/// always and two occasionally.
1769fn limited(func: &mut Func, plan: &Plan) -> Choice {
1770    let word = Type::int(64);
1771    let term = func.terminator(plan.preheader).expect("a preheader ends in a jump to the header");
1772    // What the preheader hands the header, which is where a measured offset is measured from. Read
1773    // before the builder exists, because reading it borrows the function.
1774    let entering = copy::edge_args(func, term, plan.header);
1775    // What the preheader has in place of each parameter the header carries, which is what a measured
1776    // address is written again out of to get the first iteration's.
1777    let swap: HashMap<Value, Value> =
1778        func[plan.header].params.iter().copied().zip(entering.iter().copied()).collect();
1779    let mut made = Vec::new();
1780    let mut build = Builder::new(func, plan.preheader);
1781
1782    let mut ok: Option<Value> = None;
1783    let mut windows: Vec<Window> = Vec::new();
1784    // Every measured address written once, since the same expression under the same substitution is
1785    // the same value and two checks off one pointer are the commonest thing here.
1786    let mut begun: HashMap<Value, Value> = HashMap::new();
1787    // Every question asked once, for the same reason. See [`Asked`].
1788    let mut asked = Asked::default();
1789    for sweep in &plan.sweeps {
1790        let base = match sweep.walk {
1791            Walk::By(_) => anchored(&mut build, &mut made, sweep.base),
1792            Walk::Again { at, .. } => match begun.get(&at) {
1793                Some(&had) => had,
1794                None => {
1795                    let first = remade(&mut build, &mut made, &sweep.rebuild, at, &swap);
1796                    begun.insert(at, first);
1797                    first
1798                }
1799            },
1800        };
1801        let (window, zero, also) = spare(&mut build, &mut made, sweep, base, &mut asked);
1802        // Every check has to fit for the fast half to be the one that runs, and this is where the
1803        // hypothesis the rule is asked under is earned: a window worked out from an extent smaller
1804        // than the reach is one that wrapped, and none of what follows would mean anything.
1805        let fits = build.icmp(IntPred::Sge, window, zero);
1806        made.push(fits);
1807        // What the sweep asked for beside that, which is nothing on all but the ones [`trailing`]
1808        // took and is the gap being at or above zero on those.
1809        let fits = match also {
1810            None => fits,
1811            Some(more) => {
1812                let both = build.binary(Opcode::And, fits, more, Flags::NONE);
1813                made.push(both);
1814                both
1815            }
1816        };
1817        ok = Some(match ok {
1818            None => fits,
1819            Some(so_far) => {
1820                let both = build.binary(Opcode::And, so_far, fits, Flags::NONE);
1821                made.push(both);
1822                both
1823            }
1824        });
1825        if sweep.walk.still() {
1826            continue;
1827        }
1828        // Two checks that walk by the same amount are at the same offset on every iteration, so
1829        // they share the offset and the smaller of their two windows. The amount is a magnitude,
1830        // which is what lets a walk up and a walk down by eight share one offset: the offset counts
1831        // bytes from the first access and both of them are eight bytes further along each time
1832        // round. Which way they went is in the window each of them worked out, and taking the
1833        // smaller of two windows is no different for being about two directions.
1834        //
1835        // Two checks the guard measures share for the same reason and by the other key. A fixed
1836        // distance from one pointer is a fixed distance from it on every iteration, so both of them
1837        // moved by whatever that pointer moved by and one subtraction answers for the pair.
1838        let key = sweep.walk.key();
1839        match windows.iter().position(|held| held.key == key) {
1840            Some(at) => {
1841                let bound = windows[at].bound;
1842                let smaller = build.icmp(IntPred::Ult, window, bound);
1843                made.push(smaller);
1844                let least = build.select(smaller, window, bound);
1845                made.push(least);
1846                windows[at].bound = least;
1847            }
1848            None => {
1849                // Where a measured offset is measured from, worked out once in the preheader
1850                // because it is the same address on every iteration by definition.
1851                let from = match key {
1852                    Key::Every(_) => None,
1853                    Key::From(_) => {
1854                        let from = build.unary(Opcode::PtrToInt, base, word);
1855                        made.push(from);
1856                        Some(from)
1857                    }
1858                };
1859                let rebuild = if from.is_some() { sweep.rebuild.clone() } else { Vec::new() };
1860                windows.push(Window { key, bound: window, from, rebuild });
1861            }
1862        }
1863    }
1864    let ok = ok.expect("a plan holds at least one check");
1865
1866    for window in &mut windows {
1867        window.bound = bounded(&mut build, &mut made, stepped(window.key), window.bound);
1868    }
1869
1870    for value in made {
1871        let inst = inst_of(func, value);
1872        func.remove_inst(inst);
1873        func.insert_before(inst, term);
1874    }
1875    Choice { ok, windows }
1876}
1877
1878/// How much the offset goes up by between one test and the next, which is nothing for one the guard
1879/// measures.
1880///
1881/// A measured offset is worked out from the pointer every time round rather than added to, so it is
1882/// never one step past anything and there is no step to leave room for. What it can be is enormous,
1883/// when the pointer went below where it started and the subtraction came out as a huge unsigned
1884/// number, and that is the answer wanted: the guard is meant to hand a loop like that to the half
1885/// that kept its checks.
1886fn stepped(key: Key) -> i128 {
1887    match key {
1888        Key::Every(step) => step,
1889        Key::From(_) => 0,
1890    }
1891}
1892
1893/// Holds a window short of where one more step would take the offset out of sixty four bits.
1894///
1895/// The offset goes up by the step every time round and is tested afterwards, so it reaches one step
1896/// past the window before the guard sends the loop to the other half. Nothing else here bounds the
1897/// window: `cap_extent` answers with no more than it was asked for, and what it was asked for is a
1898/// trip count times a step, which saturates rather than refusing. An offset that wrapped would come
1899/// back small, the guard would let it through, and the fast half would read past the end of the
1900/// object with nothing left in it to say so.
1901///
1902/// One comparison and one select in the preheader, and the value it clamps to is so far past any
1903/// object a program allocates that this never fires. It is here because the failure it stops is
1904/// silent.
1905fn bounded(build: &mut Builder<'_>, made: &mut Vec<Value>, step: i128, bound: Value) -> Value {
1906    let word = Type::int(64);
1907    let room = build.iconst(word, i128::from(i64::MAX) - step);
1908    made.push(room);
1909    let over = build.icmp(IntPred::Ugt, bound, room);
1910    made.push(over);
1911    let held = build.select(over, room, bound);
1912    made.push(held);
1913    held
1914}
1915
1916/// Whether one offset at or below the window is one whose access is inside the extent.
1917///
1918/// This function decides nothing. It builds the term `swept.sym.i64` is written about and asks the
1919/// table, which is section 7.7's split: the pass established the window and carries the offset, and
1920/// whether an offset inside the window means an access inside the object is somebody's proof rather
1921/// than this file's opinion. It is the same rule [`crate::hoist`] asks about a loop whose extent the
1922/// program works out, and it is the same question, since a window is a hoisted check's far end under
1923/// another name.
1924///
1925/// Four of its five arguments are opaque. The address, the extent and the window are values the pass
1926/// does not have as numbers, and the offset is whichever iteration the reader cares about, which is
1927/// how one question comes to be about all of them. The rule's three hypotheses about that pair are
1928/// what [`limited`] and [`bounded`] earn.
1929///
1930/// A walk from high to low asks `swept.down.sym.i64` instead, which is the same claim written about
1931/// addresses that go the other way. Asking the ascending rule and subtracting somewhere in the pass
1932/// would be arithmetic on the thing being proved, which is what section 7.7 exists to stop, so the
1933/// direction picks a term and the table answers about that term or does not.
1934fn windowed(reach: i128, down: bool) -> bool {
1935    let mut question = Question::default();
1936    let at = question.opaque();
1937    let at = question.app("value.i64", &[at]);
1938    let span = question.opaque();
1939    let span = question.app("value.i64", &[span]);
1940    let far = question.opaque();
1941    let far = question.app("value.i64", &[far]);
1942    let reach = question.number(reach);
1943    let reach = question.app("iconst.i64", &[reach]);
1944    let delta = question.opaque();
1945    let delta = question.app("value.i64", &[delta]);
1946    let head = if down { "swept.down.sym.i64" } else { "swept.sym.i64" };
1947    let term = question.app(head, &[at, span, far, reach, delta]);
1948    match safety::TABLE.find(&question, term) {
1949        Some(found) => yes(&safety::TABLE, found.rule),
1950        None => false,
1951    }
1952}
1953
1954/// The base as a value here, writing the address of a global out again when that is what it is.
1955///
1956/// One instruction, and the same one the loop has inside it. Working it out again is why
1957/// [`crate::licm`] leaves the one in the loop alone, and it is why the address can be described
1958/// rather than named in the first place.
1959fn anchored(build: &mut Builder<'_>, made: &mut Vec<Value>, base: Anchor) -> Value {
1960    match base {
1961        Anchor::Value(value) => value,
1962        Anchor::Address(symbol) => {
1963            let extra = Extra::Symbol(symbol);
1964            let at =
1965                build.value(InstData { extra, ..InstData::new(Opcode::GlobalAddr) }, Type::PTR);
1966            made.push(at);
1967            at
1968        }
1969    }
1970}
1971
1972/// How far the first access sits past the base, as a value, or `None` when it sits on it.
1973///
1974/// Wrapping arithmetic throughout, because this is the address the loop was going to compute
1975/// anyway. The flags a `nsw` would put on it would be a promise about the caller's index, and what
1976/// this is building is a question for the runtime rather than an address anything reads.
1977fn displacement(build: &mut Builder<'_>, made: &mut Vec<Value>, apart: Plain) -> Option<Value> {
1978    let word = Type::int(64);
1979    let mut sum = match apart.value.filter(|_| apart.scale != 0) {
1980        None => {
1981            return (apart.offset != 0).then(|| {
1982                let by = build.iconst(word, apart.offset);
1983                made.push(by);
1984                by
1985            });
1986        }
1987        Some(value) => value,
1988    };
1989    // The extension the invariant describes, emitted before anything is done with the value. It
1990    // comes first because everything after it is arithmetic at the wide type and the value is not
1991    // that width yet.
1992    if let Some(read) = apart.read {
1993        let widen = match read.reading {
1994            Reading::Signed => Opcode::SExt,
1995            Reading::Unsigned => Opcode::ZExt,
1996        };
1997        sum = build.unary(widen, sum, read.to);
1998        made.push(sum);
1999    }
2000    if apart.scale != 1 {
2001        let by = build.iconst(word, apart.scale);
2002        made.push(by);
2003        sum = build.binary(Opcode::Mul, sum, by, Flags::NONE);
2004        made.push(sum);
2005    }
2006    if apart.offset != 0 {
2007        let by = build.iconst(word, apart.offset);
2008        made.push(by);
2009        sum = build.binary(Opcode::Add, sum, by, Flags::NONE);
2010        made.push(sum);
2011    }
2012    Some(sum)
2013}
2014
2015/// How many bytes past the first access belong to whatever owns it, and a zero to compare that with.
2016///
2017/// The question both callers rest on. `extent - reach` is negative when the first access does not
2018/// fit at all, zero when exactly one fits, and how much room there is for further ones otherwise.
2019///
2020/// # A walk from high to low
2021///
2022/// The offset the guard carries is a magnitude, so a loop whose address goes down is a loop whose
2023/// offset goes up in exactly the same way and everything built around the offset is untouched. What
2024/// changes is which end of the object is asked about. An ascending walk starts at the first access
2025/// and runs off the top of it, so `cap_extent` at the first address is the question. A descending
2026/// one starts at the first access and runs off the bottom, so the question is `cap_extent_back` at
2027/// the end of the first access, which is `first + reach`.
2028///
2029/// Anchoring at the end rather than at `first` is what makes the two the same shape. The answer is
2030/// then how many bytes below the end of the first access belong to the same thing, the window is
2031/// that less the reach exactly as above, and the access on iteration `delta` is the `reach` bytes
2032/// ending at `first + reach - delta`. That is the claim `swept.down.sym.i64` is written about, with
2033/// `at` being the end of the first access, and it is a claim about every iteration for the same
2034/// reason the ascending one is.
2035///
2036/// # Asking once
2037///
2038/// [`spare`] runs once per sweep, and a loop that walks one pointer has a bounds check, a liveness
2039/// check and a derivation check on it, so the same question about the same address used to be asked
2040/// three times over and after tamnd/rucc#869 more often than that. On SQLite that came to 4184 calls
2041/// to the runtime for 591 split loops, which is seven per loop, and `a-string-scan` had five in one
2042/// preheader at one address. [`Asked`] is what makes it one. Two questions built out of the same
2043/// pieces are the same question here, because the whole of what this builds sits in one block that
2044/// has no call in it, so nothing between two of them can change what the second one would answer.
2045///
2046/// [`crate::number`] would say the same thing about the arithmetic and cannot say it about the query,
2047/// which has effects, and in any case it runs before this pass rather than after it, so there is
2048/// nothing behind this that would tidy up after it.
2049fn spare(
2050    build: &mut Builder<'_>,
2051    made: &mut Vec<Value>,
2052    sweep: &Sweep,
2053    base: Value,
2054    asked: &mut Asked,
2055) -> (Value, Value, Option<Value>) {
2056    let word = Type::int(64);
2057    let first = match asked.first(base, sweep.apart) {
2058        Some(had) => had,
2059        None => {
2060            let first = match displacement(build, made, sweep.apart) {
2061                None => base,
2062                Some(by) => {
2063                    let args = build.func().push_values(&[base, by]);
2064                    let data = InstData::new(Opcode::PtrAdd);
2065                    let sum = build.value(InstData { args, ..data }, Type::PTR);
2066                    made.push(sum);
2067                    sum
2068                }
2069            };
2070            asked.firsts.push(((base, sweep.apart), first));
2071            first
2072        }
2073    };
2074    // How far the runtime is asked to look, which is as far as the arithmetic carries. The answer
2075    // is a true count of the bytes that belong to the object, never more than the truth and never
2076    // more than what was asked for, so a smaller ask is a smaller window and a smaller window is
2077    // fewer iterations in the half that has no checks in it. There is nothing on the other side of
2078    // that trade any more. The query probes the far end of what it was asked for and halves rather
2079    // than walking, about twenty five reads of the plane whatever the number is, so the price does
2080    // not turn on the number and the largest ask is the right one.
2081    let want = asked.number(build, made, i128::from(i64::MAX));
2082
2083    // Where the question is asked from, which for a walk that goes down is the end of the first
2084    // access rather than its start. The arithmetic wraps, in the way [`displacement`] wraps and for
2085    // the same reason: this is an address the loop was going to reach anyway and the value is a
2086    // question for the runtime rather than something anything reads through.
2087    let (query, at) = if sweep.walk.down() {
2088        let end = match asked.end(first, sweep.reach) {
2089            Some(had) => had,
2090            None => {
2091                let by = asked.number(build, made, sweep.reach);
2092                let args = build.func().push_values(&[first, by]);
2093                let data = InstData::new(Opcode::PtrAdd);
2094                let end = build.value(InstData { args, ..data }, Type::PTR);
2095                made.push(end);
2096                asked.ends.push(((first, sweep.reach), end));
2097                end
2098            }
2099        };
2100        (Opcode::CapExtentBack, end)
2101    } else {
2102        (Opcode::CapExtent, first)
2103    };
2104
2105    let extent = match asked.extent(query, at, want) {
2106        Some(had) => had,
2107        None => {
2108            let args = build.func().push_values(&[at]);
2109            let data = InstData::new(Opcode::CapOf);
2110            let capability = build.value(InstData { args, ..data }, Type::CAP);
2111            made.push(capability);
2112            let args = build.func().push_values(&[capability, at, want]);
2113            let extent = build.value(InstData { args, ..InstData::new(query) }, word);
2114            made.push(extent);
2115            asked.extents.push(((query, at, want), extent));
2116            extent
2117        }
2118    };
2119
2120    let reach = asked.number(build, made, sweep.reach);
2121    let left = build.binary(Opcode::Sub, extent, reach, Flags::NSW);
2122    made.push(left);
2123    let zero = asked.number(build, made, 0);
2124
2125    // The gap [`trailing`] left for the guard to work out, which is how far into the window the
2126    // walk's first access sits. Taking it off the window is what makes the window one about the
2127    // walk again, and asking it to be at or above zero is what says the walk begins inside the
2128    // object the window was measured in rather than somewhere below it.
2129    let Some(ahead) = sweep.ahead.and_then(|ahead| displacement(build, made, ahead)) else {
2130        return (left, zero, None);
2131    };
2132    let short = build.binary(Opcode::Sub, left, ahead, Flags::NSW);
2133    made.push(short);
2134    let above = build.icmp(IntPred::Sge, ahead, zero);
2135    made.push(above);
2136    (short, zero, Some(above))
2137}
2138
2139/// What the preheader has worked out already, so that one question is asked once.
2140///
2141/// Association lists rather than maps, because a plan holds a handful of sweeps and the keys are
2142/// what scalar evolution hands out, which is `Eq` and not `Hash`. Looking a key up walks the list,
2143/// and the longest list on SQLite is a dozen entries.
2144#[derive(Default)]
2145struct Asked {
2146    /// Numbers written down, by the number.
2147    numbers: Vec<(i128, Value)>,
2148    /// Where the first access is, by the base it is measured from and how far past it it sits.
2149    firsts: Vec<((Value, Plain), Value)>,
2150    /// The end of a first access, by where it starts and how many bytes it is.
2151    ends: Vec<((Value, i128), Value)>,
2152    /// What the runtime answered, by which end was asked, about which address and how far.
2153    extents: Vec<((Opcode, Value, Value), Value)>,
2154}
2155
2156impl Asked {
2157    /// A number written down in the preheader, once per number.
2158    fn number(&mut self, build: &mut Builder<'_>, made: &mut Vec<Value>, imm: i128) -> Value {
2159        if let Some(&(_, had)) = self.numbers.iter().find(|&&(seen, _)| seen == imm) {
2160            return had;
2161        }
2162        let value = build.iconst(Type::int(64), imm);
2163        made.push(value);
2164        self.numbers.push((imm, value));
2165        value
2166    }
2167
2168    /// The first access off this base and this far past it, if it has been worked out.
2169    fn first(&self, base: Value, apart: Plain) -> Option<Value> {
2170        self.firsts.iter().find(|&&(key, _)| key == (base, apart)).map(|&(_, had)| had)
2171    }
2172
2173    /// The end of this first access, if it has been worked out.
2174    fn end(&self, first: Value, reach: i128) -> Option<Value> {
2175        self.ends.iter().find(|&&(key, _)| key == (first, reach)).map(|&(_, had)| had)
2176    }
2177
2178    /// What the runtime said about this address, if it has been asked.
2179    fn extent(&self, query: Opcode, at: Value, want: Value) -> Option<Value> {
2180        self.extents.iter().find(|&&(key, _)| key == (query, at, want)).map(|&(_, had)| had)
2181    }
2182}
2183
2184#[cfg(test)]
2185mod tests {
2186    use rucc_base::Interner;
2187    use rucc_ir::{
2188        Block, Builder, Extra, Flags, Func, Inst, InstData, IntPred, MemInfo, MemOrder, Module,
2189        Opcode, Restrict, Signature, Type, Value, verify_func,
2190    };
2191    use rucc_target::{TargetInfo, Triple};
2192
2193    use super::{SPLIT, Split};
2194    use crate::canon::Canon;
2195    use crate::stats::Kind;
2196    use crate::{Fuel, Pass, Stats};
2197
2198    /// How many times the loop goes round, and how wide each element of the walk is.
2199    const TRIPS: i128 = 16;
2200    const WIDTH: i128 = 4;
2201
2202    /// A counted loop that reads one element each time round and can stop on what it read.
2203    ///
2204    /// ```text
2205    /// entry(a): jump head(0)
2206    /// head(i):  p = a + i*4; check_bounds cap_of(p), p; v = load p
2207    ///           br v == 0 -> done, more
2208    /// more:     next = i + 1; br next < 16 -> head(next), done
2209    /// done:     ret
2210    /// ```
2211    ///
2212    /// The second way out is the point. Hoisting refuses this loop, because a loop that can stop in
2213    /// the middle reads fewer bytes than its count says and one check in front of it for all of them
2214    /// would refuse a program that was right. Splitting does not care, because the count it reads is
2215    /// only ever an upper limit on how far to look.
2216    fn leaving() -> (Interner, Func, Vec<Block>) {
2217        walking(Some(TRIPS), Flags::NSW)
2218    }
2219
2220    /// The same loop, with how many times it goes round handed in rather than written down.
2221    ///
2222    /// A loop whose count is an expression rather than a number, which this pass no longer reads and
2223    /// which is still worth a test of its own: the shape has to split like any other and the guard
2224    /// has to come out the same as the one a written down count gets.
2225    fn counting() -> (Interner, Func, Vec<Block>) {
2226        walking(None, Flags::NSW)
2227    }
2228
2229    /// The same loop again, with an increment that promises nothing, so nobody counts it.
2230    ///
2231    /// What `-fwrapv` produces, and the shape a great deal of real code is in. Hoisting refuses it,
2232    /// because a count that rests on the counter not wrapping is not a count it may size a check
2233    /// with. This pass sizes nothing with a count, so it takes it.
2234    fn uncounted() -> (Interner, Func, Vec<Block>) {
2235        walking(Some(TRIPS), Flags::NONE)
2236    }
2237
2238    /// The same loop, reading from an index the caller handed in rather than from zero.
2239    ///
2240    /// `a[start + i]`, whose first address is `a + 4 * start`: a pointer and a displacement, with a
2241    /// number for neither of them. This is the shape the pass used to give up on, and it is a
2242    /// common one, because a loop over part of an array is written this way and so is every walk
2243    /// that begins where the last one stopped. See #810.
2244    fn from_an_index() -> (Interner, Func, Vec<Block>) {
2245        let mut names = Interner::new();
2246        let params = [Type::PTR, Type::int(64)];
2247        let mut func = Func::new(names.intern("f"), Signature::new().with_params(&params));
2248        let entry = func.create_block();
2249        let head = func.create_block();
2250        let more = func.create_block();
2251        let done = func.create_block();
2252        let array = func.append_param(entry, Type::PTR);
2253        let start = func.append_param(entry, Type::int(64));
2254        let counter = func.append_param(head, Type::int(64));
2255
2256        let zero = Builder::new(&mut func, entry).iconst(Type::int(64), 0);
2257        Builder::new(&mut func, entry).jump(head, &[zero]);
2258
2259        let mut build = Builder::new(&mut func, head);
2260        let index = build.binary(Opcode::Add, counter, start, Flags::NSW);
2261        let by = build.iconst(Type::int(64), WIDTH);
2262        let scaled = build.binary(Opcode::Mul, index, by, Flags::NSW);
2263        let args = build.func().push_values(&[array, scaled]);
2264        let pointer = build.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
2265        check(&mut build, pointer);
2266        let read = build.load(Type::int(32), pointer, mem(), Flags::NONE);
2267        let nothing = build.iconst(Type::int(32), 0);
2268        let stop = build.icmp(IntPred::Eq, read, nothing);
2269        build.br_if(stop, done, &[], more, &[]);
2270
2271        let mut build = Builder::new(&mut func, more);
2272        let one = build.iconst(Type::int(64), 1);
2273        let next = build.binary(Opcode::Add, counter, one, Flags::NSW);
2274        let limit = build.iconst(Type::int(64), TRIPS);
2275        let again = build.icmp(IntPred::Slt, next, limit);
2276        build.br_if(again, head, &[next], done, &[]);
2277        Builder::new(&mut func, done).ret(&[]);
2278        (names, func, vec![entry, head, more, done])
2279    }
2280
2281    /// The same loop over a file scope array, with the `global_addr` inside the loop.
2282    ///
2283    /// Which is where one sits, because working the address out again costs a single instruction
2284    /// and `crate::licm` would rather do that than hold it in a register the whole way round. So
2285    /// the address of the array is not a value defined outside the loop and never will be, and the
2286    /// pass has to take it from where it is or not at all. See #810.
2287    fn over_a_global() -> (Interner, Func, Vec<Block>) {
2288        let mut names = Interner::new();
2289        let tab = names.intern("tab");
2290        let mut func = Func::new(names.intern("f"), Signature::new());
2291        let entry = func.create_block();
2292        let head = func.create_block();
2293        let more = func.create_block();
2294        let done = func.create_block();
2295        let counter = func.append_param(head, Type::int(64));
2296
2297        let zero = Builder::new(&mut func, entry).iconst(Type::int(64), 0);
2298        Builder::new(&mut func, entry).jump(head, &[zero]);
2299
2300        let mut build = Builder::new(&mut func, head);
2301        let by = build.iconst(Type::int(64), WIDTH);
2302        let scaled = build.binary(Opcode::Mul, counter, by, Flags::NSW);
2303        let extra = Extra::Symbol(tab);
2304        let array = build.value(InstData { extra, ..InstData::new(Opcode::GlobalAddr) }, Type::PTR);
2305        let args = build.func().push_values(&[array, scaled]);
2306        let pointer = build.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
2307        check(&mut build, pointer);
2308        let read = build.load(Type::int(32), pointer, mem(), Flags::NONE);
2309        let nothing = build.iconst(Type::int(32), 0);
2310        let stop = build.icmp(IntPred::Eq, read, nothing);
2311        build.br_if(stop, done, &[], more, &[]);
2312
2313        let mut build = Builder::new(&mut func, more);
2314        let one = build.iconst(Type::int(64), 1);
2315        let next = build.binary(Opcode::Add, counter, one, Flags::NSW);
2316        let limit = build.iconst(Type::int(64), TRIPS);
2317        let again = build.icmp(IntPred::Slt, next, limit);
2318        build.br_if(again, head, &[next], done, &[]);
2319        Builder::new(&mut func, done).ret(&[]);
2320        (names, func, vec![entry, head, more, done])
2321    }
2322
2323    /// The same loop again, with the index in `int` and sign extended, which is what C gives.
2324    ///
2325    /// `a[start + i]` with `start` and `i` both `int`. The front end adds them at thirty two bits
2326    /// and sign extends the sum before scaling it, so the first thing scalar evolution meets is the
2327    /// extension of a chrec whose base is a value rather than a number. Splitting takes it because
2328    /// the widened base is described rather than named, and this pass emits the extension in the
2329    /// preheader. See #810.
2330    fn from_a_narrow_index() -> (Interner, Func, Vec<Block>) {
2331        let mut names = Interner::new();
2332        let params = [Type::PTR, Type::int(32)];
2333        let mut func = Func::new(names.intern("f"), Signature::new().with_params(&params));
2334        let entry = func.create_block();
2335        let head = func.create_block();
2336        let more = func.create_block();
2337        let done = func.create_block();
2338        let array = func.append_param(entry, Type::PTR);
2339        let start = func.append_param(entry, Type::int(32));
2340        let counter = func.append_param(head, Type::int(32));
2341
2342        let zero = Builder::new(&mut func, entry).iconst(Type::int(32), 0);
2343        Builder::new(&mut func, entry).jump(head, &[zero]);
2344
2345        let mut build = Builder::new(&mut func, head);
2346        let index = build.binary(Opcode::Add, counter, start, Flags::NSW);
2347        let wide = build.unary(Opcode::SExt, index, Type::int(64));
2348        let by = build.iconst(Type::int(64), WIDTH);
2349        let scaled = build.binary(Opcode::Mul, wide, by, Flags::NSW);
2350        let args = build.func().push_values(&[array, scaled]);
2351        let pointer = build.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
2352        check(&mut build, pointer);
2353        let read = build.load(Type::int(32), pointer, mem(), Flags::NONE);
2354        let nothing = build.iconst(Type::int(32), 0);
2355        let stop = build.icmp(IntPred::Eq, read, nothing);
2356        build.br_if(stop, done, &[], more, &[]);
2357
2358        let mut build = Builder::new(&mut func, more);
2359        let one = build.iconst(Type::int(32), 1);
2360        let next = build.binary(Opcode::Add, counter, one, Flags::NSW);
2361        let limit = build.iconst(Type::int(32), TRIPS);
2362        let again = build.icmp(IntPred::Slt, next, limit);
2363        build.br_if(again, head, &[next], done, &[]);
2364        Builder::new(&mut func, done).ret(&[]);
2365        (names, func, vec![entry, head, more, done])
2366    }
2367
2368    /// The same loop again, walking from the end of the array down to the start of it.
2369    ///
2370    /// ```text
2371    /// entry(a): jump head(15)
2372    /// head(i):  p = a + i*4; check_bounds cap_of(p), p; v = load p
2373    ///           br v == 0 -> done, more
2374    /// more:     next = i - 1; br next >= 0 -> head(next), done
2375    /// done:     ret
2376    /// ```
2377    ///
2378    /// The step is minus four, so the first access is the highest address the loop touches and every
2379    /// later one is below it. What the pass has to ask about is room under the first access rather
2380    /// than over it, which is `cap_extent_back` at the end of that access. See #680.
2381    fn downwards() -> (Interner, Func, Vec<Block>) {
2382        let mut names = Interner::new();
2383        let mut func = Func::new(names.intern("f"), Signature::new().with_params(&[Type::PTR]));
2384        let entry = func.create_block();
2385        let head = func.create_block();
2386        let more = func.create_block();
2387        let done = func.create_block();
2388        let array = func.append_param(entry, Type::PTR);
2389        let counter = func.append_param(head, Type::int(64));
2390
2391        let last = Builder::new(&mut func, entry).iconst(Type::int(64), TRIPS - 1);
2392        Builder::new(&mut func, entry).jump(head, &[last]);
2393
2394        let mut build = Builder::new(&mut func, head);
2395        let by = build.iconst(Type::int(64), WIDTH);
2396        let scaled = build.binary(Opcode::Mul, counter, by, Flags::NSW);
2397        let args = build.func().push_values(&[array, scaled]);
2398        let pointer = build.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
2399        check(&mut build, pointer);
2400        let read = build.load(Type::int(32), pointer, mem(), Flags::NONE);
2401        let nothing = build.iconst(Type::int(32), 0);
2402        let stop = build.icmp(IntPred::Eq, read, nothing);
2403        build.br_if(stop, done, &[], more, &[]);
2404
2405        let mut build = Builder::new(&mut func, more);
2406        let one = build.iconst(Type::int(64), 1);
2407        let next = build.binary(Opcode::Sub, counter, one, Flags::NSW);
2408        let floor = build.iconst(Type::int(64), 0);
2409        let again = build.icmp(IntPred::Sge, next, floor);
2410        build.br_if(again, head, &[next], done, &[]);
2411        Builder::new(&mut func, done).ret(&[]);
2412        (names, func, vec![entry, head, more, done])
2413    }
2414
2415    /// A scanner whose pointer moves by one byte or by two, depending on what it just read.
2416    ///
2417    /// ```text
2418    /// entry(a): jump head(a)
2419    /// head(p):  check_bounds cap_of(p), p; v = load p
2420    ///           br v == 0 -> done, more
2421    /// more:     br v < 0 -> two, one
2422    /// one:      jump back(p + 1)
2423    /// two:      jump back(p + 2)
2424    /// back(q):  jump head(q)
2425    /// done:     ret
2426    /// ```
2427    ///
2428    /// What a UTF-8 walk looks like, and what half of SQLite's text handling looks like. There is no
2429    /// step to speak of, so scalar evolution says nothing and the guard has to measure how far the
2430    /// pointer got rather than count how far it should have got. See #810.
2431    fn by_what_it_read() -> (Interner, Func, Vec<Block>) {
2432        let mut names = Interner::new();
2433        let mut func = Func::new(names.intern("f"), Signature::new().with_params(&[Type::PTR]));
2434        let entry = func.create_block();
2435        let head = func.create_block();
2436        let more = func.create_block();
2437        let one = func.create_block();
2438        let two = func.create_block();
2439        let back = func.create_block();
2440        let done = func.create_block();
2441        let text = func.append_param(entry, Type::PTR);
2442        let at = func.append_param(head, Type::PTR);
2443        let next = func.append_param(back, Type::PTR);
2444
2445        Builder::new(&mut func, entry).jump(head, &[text]);
2446
2447        let mut build = Builder::new(&mut func, head);
2448        checking(&mut build, at, byte());
2449        let read = build.load(Type::int(8), at, byte(), Flags::NONE);
2450        let nothing = build.iconst(Type::int(8), 0);
2451        let stop = build.icmp(IntPred::Eq, read, nothing);
2452        build.br_if(stop, done, &[], more, &[]);
2453
2454        let mut build = Builder::new(&mut func, more);
2455        let wide = build.icmp(IntPred::Slt, read, nothing);
2456        build.br_if(wide, two, &[], one, &[]);
2457
2458        for (block, step) in [(one, 1), (two, 2)] {
2459            let mut build = Builder::new(&mut func, block);
2460            let by = build.iconst(Type::int(64), step);
2461            let args = build.func().push_values(&[at, by]);
2462            let far = build.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
2463            build.jump(back, &[far]);
2464        }
2465
2466        Builder::new(&mut func, back).jump(head, &[next]);
2467        Builder::new(&mut func, done).ret(&[]);
2468        (names, func, vec![entry, head, more, one, two, back, done])
2469    }
2470
2471    /// A walk whose address is a pointer the header carries plus an index it also carries.
2472    ///
2473    /// ```text
2474    /// entry(a, n): jump head(a, 0)
2475    /// head(p, i):  x = i & 7; q = p + x
2476    ///              check_bounds cap_of(q), q
2477    ///              j = i + 1; f = p + 8
2478    ///              br j < n -> head(f, j), done
2479    /// done:        ret
2480    /// ```
2481    ///
2482    /// The `and` is what stops scalar evolution: `i` walks by one and `i & 7` does not walk by
2483    /// anything, so the address is not an induction variable and nothing counts it. It is still a
2484    /// function of what the header carries, so the guard can write the two instructions out again
2485    /// from its own parameters and the preheader can write them out again from what it passes. See
2486    /// #810.
2487    ///
2488    /// A `load` in place of the `and` is the same fixture with the answer the other way, which is
2489    /// `x_came_out_of_memory` below.
2490    fn from_what_it_carries(reading: bool) -> (Interner, Func, Vec<Block>) {
2491        let word = Type::int(64);
2492        let mut names = Interner::new();
2493        let mut func =
2494            Func::new(names.intern("f"), Signature::new().with_params(&[Type::PTR, word]));
2495        let entry = func.create_block();
2496        let head = func.create_block();
2497        let done = func.create_block();
2498        let text = func.append_param(entry, Type::PTR);
2499        let count = func.append_param(entry, word);
2500        let at = func.append_param(head, Type::PTR);
2501        let index = func.append_param(head, word);
2502
2503        let mut build = Builder::new(&mut func, entry);
2504        let zero = build.iconst(word, 0);
2505        build.jump(head, &[text, zero]);
2506
2507        let mut build = Builder::new(&mut func, head);
2508        let spread = if reading {
2509            build.load(word, at, mem(), Flags::NONE)
2510        } else {
2511            let mask = build.iconst(word, 7);
2512            build.binary(Opcode::And, index, mask, Flags::NONE)
2513        };
2514        let args = build.func().push_values(&[at, spread]);
2515        let pointer = build.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
2516        checking(&mut build, pointer, byte());
2517        let one = build.iconst(word, 1);
2518        let next = build.binary(Opcode::Add, index, one, Flags::NSW);
2519        let by = build.iconst(word, 8);
2520        let args = build.func().push_values(&[at, by]);
2521        let far = build.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
2522        let again = build.icmp(IntPred::Slt, next, count);
2523        build.br_if(again, head, &[far, next], done, &[]);
2524        Builder::new(&mut func, done).ret(&[]);
2525        (names, func, vec![entry, head, done])
2526    }
2527
2528    /// A walk down a linked list, where the next pointer is read out of the current node.
2529    ///
2530    /// ```text
2531    /// entry(a): jump head(a)
2532    /// head(p):  check_bounds cap_of(p), p; v = load p
2533    ///           br v == 0 -> done, more
2534    /// more:     q = load p + 8; jump head(q)
2535    /// done:     ret
2536    /// ```
2537    ///
2538    /// The case measuring does not take. Not because subtracting the two nodes would be wrong, but
2539    /// because the second one is its own object, so the guard would send every iteration after the
2540    /// first to the slow half and the split would be two copies of the loop for nothing. See #810.
2541    fn down_a_list() -> (Interner, Func, Vec<Block>) {
2542        let mut names = Interner::new();
2543        let mut func = Func::new(names.intern("f"), Signature::new().with_params(&[Type::PTR]));
2544        let entry = func.create_block();
2545        let head = func.create_block();
2546        let more = func.create_block();
2547        let done = func.create_block();
2548        let list = func.append_param(entry, Type::PTR);
2549        let at = func.append_param(head, Type::PTR);
2550
2551        Builder::new(&mut func, entry).jump(head, &[list]);
2552
2553        let mut build = Builder::new(&mut func, head);
2554        checking(&mut build, at, byte());
2555        let read = build.load(Type::int(8), at, byte(), Flags::NONE);
2556        let nothing = build.iconst(Type::int(8), 0);
2557        let stop = build.icmp(IntPred::Eq, read, nothing);
2558        build.br_if(stop, done, &[], more, &[]);
2559
2560        let mut build = Builder::new(&mut func, more);
2561        let by = build.iconst(Type::int(64), 8);
2562        let args = build.func().push_values(&[at, by]);
2563        let field = build.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
2564        let next = build.load(Type::PTR, field, mem(), Flags::NONE);
2565        build.jump(head, &[next]);
2566        Builder::new(&mut func, done).ret(&[]);
2567        (names, func, vec![entry, head, more, done])
2568    }
2569
2570    /// Builds the loop, with the exit test against a number or against a second parameter.
2571    /// The same loop as [`walking`], with the counter starting at a number the caller handed in.
2572    ///
2573    /// `a[start + i]` for `i` from nothing up to `TRIPS`, which is the shape an inner loop over a
2574    /// row of a matrix has once the outer loop's subscript is folded into the start. What it gives
2575    /// the pass is a walk whose displacement off the array is a value rather than a number.
2576    fn offsetting(flags: Flags) -> (Interner, Func, Vec<Block>) {
2577        let mut names = Interner::new();
2578        let word = Type::int(64);
2579        let params = vec![Type::PTR, word];
2580        let mut func = Func::new(names.intern("f"), Signature::new().with_params(&params));
2581        let entry = func.create_block();
2582        let head = func.create_block();
2583        let more = func.create_block();
2584        let done = func.create_block();
2585        let array = func.append_param(entry, Type::PTR);
2586        let start = func.append_param(entry, word);
2587        let counter = func.append_param(head, word);
2588
2589        Builder::new(&mut func, entry).jump(head, &[start]);
2590
2591        let mut build = Builder::new(&mut func, head);
2592        let by = build.iconst(word, WIDTH);
2593        let scaled = build.binary(Opcode::Mul, counter, by, Flags::NSW);
2594        let args = build.func().push_values(&[array, scaled]);
2595        let pointer = build.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
2596        check(&mut build, pointer);
2597        let read = build.load(Type::int(32), pointer, mem(), Flags::NONE);
2598        let nothing = build.iconst(Type::int(32), 0);
2599        let stop = build.icmp(IntPred::Eq, read, nothing);
2600        build.br_if(stop, done, &[], more, &[]);
2601
2602        let mut build = Builder::new(&mut func, more);
2603        let one = build.iconst(word, 1);
2604        let next = build.binary(Opcode::Add, counter, one, flags);
2605        let times = build.iconst(word, TRIPS);
2606        let limit = build.binary(Opcode::Add, start, times, Flags::NSW);
2607        let again = build.icmp(IntPred::Slt, next, limit);
2608        build.br_if(again, head, &[next], done, &[]);
2609        Builder::new(&mut func, done).ret(&[]);
2610        (names, func, vec![entry, head, more, done])
2611    }
2612
2613    fn walking(times: Option<i128>, flags: Flags) -> (Interner, Func, Vec<Block>) {
2614        let mut names = Interner::new();
2615        let mut params = vec![Type::PTR];
2616        params.extend(times.is_none().then_some(Type::int(64)));
2617        let mut func = Func::new(names.intern("f"), Signature::new().with_params(&params));
2618        let entry = func.create_block();
2619        let head = func.create_block();
2620        let more = func.create_block();
2621        let done = func.create_block();
2622        let array = func.append_param(entry, Type::PTR);
2623        let handed = times.is_none().then(|| func.append_param(entry, Type::int(64)));
2624        let counter = func.append_param(head, Type::int(64));
2625
2626        let zero = Builder::new(&mut func, entry).iconst(Type::int(64), 0);
2627        Builder::new(&mut func, entry).jump(head, &[zero]);
2628
2629        let mut build = Builder::new(&mut func, head);
2630        let by = build.iconst(Type::int(64), WIDTH);
2631        let scaled = build.binary(Opcode::Mul, counter, by, Flags::NSW);
2632        let args = build.func().push_values(&[array, scaled]);
2633        let pointer = build.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
2634        check(&mut build, pointer);
2635        let read = build.load(Type::int(32), pointer, mem(), Flags::NONE);
2636        let nothing = build.iconst(Type::int(32), 0);
2637        let stop = build.icmp(IntPred::Eq, read, nothing);
2638        build.br_if(stop, done, &[], more, &[]);
2639
2640        let mut build = Builder::new(&mut func, more);
2641        let one = build.iconst(Type::int(64), 1);
2642        let next = build.binary(Opcode::Add, counter, one, flags);
2643        let limit = match (times, handed) {
2644            (Some(times), _) => build.iconst(Type::int(64), times),
2645            (None, handed) => handed.expect("a loop with no number for a limit was handed one"),
2646        };
2647        let again = build.icmp(IntPred::Slt, next, limit);
2648        build.br_if(again, head, &[next], done, &[]);
2649        Builder::new(&mut func, done).ret(&[]);
2650        (names, func, vec![entry, head, more, done])
2651    }
2652
2653    /// Builds a loop with two ways out that meet again, so neither way out dominates the meeting.
2654    ///
2655    /// A parameter at each exit is what section 26.4 asks for and it does not reach this on its own.
2656    /// Both exits grow one and a use at the join still names the value the loop defined, because a
2657    /// parameter is only a name where its block dominates.
2658    fn joining() -> (Interner, Func, Vec<Block>) {
2659        let mut names = Interner::new();
2660        let params = [Type::PTR, Type::int(64)];
2661        let mut func = Func::new(names.intern("f"), Signature::new().with_params(&params));
2662        let entry = func.create_block();
2663        let head = func.create_block();
2664        let more = func.create_block();
2665        let left = func.create_block();
2666        let right = func.create_block();
2667        let join = func.create_block();
2668        let array = func.append_param(entry, Type::PTR);
2669        let handed = func.append_param(entry, Type::int(64));
2670        let counter = func.append_param(head, Type::int(64));
2671
2672        let zero = Builder::new(&mut func, entry).iconst(Type::int(64), 0);
2673        Builder::new(&mut func, entry).jump(head, &[zero]);
2674
2675        let mut build = Builder::new(&mut func, head);
2676        let by = build.iconst(Type::int(64), WIDTH);
2677        let scaled = build.binary(Opcode::Mul, counter, by, Flags::NSW);
2678        let args = build.func().push_values(&[array, scaled]);
2679        let pointer = build.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
2680        check(&mut build, pointer);
2681        let read = build.load(Type::int(32), pointer, mem(), Flags::NONE);
2682        let nothing = build.iconst(Type::int(32), 0);
2683        let stop = build.icmp(IntPred::Eq, read, nothing);
2684        build.br_if(stop, left, &[], more, &[]);
2685
2686        let mut build = Builder::new(&mut func, more);
2687        let one = build.iconst(Type::int(64), 1);
2688        let next = build.binary(Opcode::Add, counter, one, Flags::NSW);
2689        let again = build.icmp(IntPred::Slt, next, handed);
2690        build.br_if(again, head, &[next], right, &[]);
2691
2692        Builder::new(&mut func, left).jump(join, &[]);
2693        Builder::new(&mut func, right).jump(join, &[]);
2694        Builder::new(&mut func, join).ret(&[]);
2695        (names, func, vec![entry, head, more, left, right, join])
2696    }
2697
2698    /// Builds two loops one after the other, the second starting from where the first stopped.
2699    ///
2700    /// The guard the second one gets is worked out from where its walk starts, which is a value the
2701    /// first loop defines, and splitting the first loop is what stops that being one value.
2702    fn one_after_another() -> (Interner, Func, Vec<Block>) {
2703        let mut names = Interner::new();
2704        let params = [Type::PTR, Type::int(64)];
2705        let mut func = Func::new(names.intern("f"), Signature::new().with_params(&params));
2706        let entry = func.create_block();
2707        let head = func.create_block();
2708        let more = func.create_block();
2709        let over = func.create_block();
2710        let next = func.create_block();
2711        let again = func.create_block();
2712        let done = func.create_block();
2713        let array = func.append_param(entry, Type::PTR);
2714        let limit = func.append_param(entry, Type::int(64));
2715        let first = func.append_param(head, Type::int(64));
2716        let second = func.append_param(next, Type::int(64));
2717
2718        let zero = Builder::new(&mut func, entry).iconst(Type::int(64), 0);
2719        Builder::new(&mut func, entry).jump(head, &[zero]);
2720
2721        let mut build = Builder::new(&mut func, head);
2722        let args = build.func().push_values(&[array, first]);
2723        let at = build.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
2724        checking(&mut build, at, byte());
2725        let read = build.load(Type::int(8), at, byte(), Flags::NONE);
2726        let nothing = build.iconst(Type::int(8), 0);
2727        let stop = build.icmp(IntPred::Eq, read, nothing);
2728        build.br_if(stop, over, &[], more, &[]);
2729
2730        let mut build = Builder::new(&mut func, more);
2731        let one = build.iconst(Type::int(64), 1);
2732        let step = build.binary(Opcode::Add, first, one, Flags::NSW);
2733        build.jump(head, &[step]);
2734
2735        Builder::new(&mut func, over).jump(next, &[first]);
2736
2737        let mut build = Builder::new(&mut func, next);
2738        let args = build.func().push_values(&[array, second]);
2739        let here = build.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
2740        checking(&mut build, here, byte());
2741        let seen = build.load(Type::int(8), here, byte(), Flags::NONE);
2742        let blank = build.iconst(Type::int(8), 32);
2743        let over_too = build.icmp(IntPred::Eq, seen, blank);
2744        build.br_if(over_too, done, &[], again, &[]);
2745
2746        let mut build = Builder::new(&mut func, again);
2747        let one = build.iconst(Type::int(64), 1);
2748        let onward = build.binary(Opcode::Add, second, one, Flags::NSW);
2749        let go = build.icmp(IntPred::Slt, onward, limit);
2750        build.br_if(go, next, &[onward], done, &[]);
2751
2752        Builder::new(&mut func, done).ret(&[]);
2753        (names, func, vec![entry, head, more, over, next, again, done])
2754    }
2755
2756    /// The same two loops, with the second one carrying a derivation check rather than a bounds
2757    /// check.
2758    ///
2759    /// The check names the array, which stands still, so the window goes on the array and the guard
2760    /// subtracts how far along the walk begins. That gap is where the first loop stopped, which is
2761    /// the first loop's own counter, and it is the one way a plan names a value without the value
2762    /// being either the address the sweep starts from or the displacement it carries. `trailing` is
2763    /// where that happens and `bench/safety/a-strided-column-sum.c` is the shape it was written for.
2764    fn one_after_another_with_a_gap() -> (Interner, Func, Vec<Block>) {
2765        let mut names = Interner::new();
2766        let params = [Type::PTR, Type::int(64)];
2767        let mut func = Func::new(names.intern("f"), Signature::new().with_params(&params));
2768        let entry = func.create_block();
2769        let head = func.create_block();
2770        let more = func.create_block();
2771        let over = func.create_block();
2772        let next = func.create_block();
2773        let again = func.create_block();
2774        let done = func.create_block();
2775        let array = func.append_param(entry, Type::PTR);
2776        let limit = func.append_param(entry, Type::int(64));
2777        let first = func.append_param(head, Type::int(64));
2778        let second = func.append_param(next, Type::int(64));
2779
2780        let zero = Builder::new(&mut func, entry).iconst(Type::int(64), 0);
2781        Builder::new(&mut func, entry).jump(head, &[zero]);
2782
2783        let mut build = Builder::new(&mut func, head);
2784        let args = build.func().push_values(&[array, first]);
2785        let at = build.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
2786        checking(&mut build, at, byte());
2787        let read = build.load(Type::int(8), at, byte(), Flags::NONE);
2788        let nothing = build.iconst(Type::int(8), 0);
2789        let stop = build.icmp(IntPred::Eq, read, nothing);
2790        build.br_if(stop, over, &[], more, &[]);
2791
2792        let mut build = Builder::new(&mut func, more);
2793        let one = build.iconst(Type::int(64), 1);
2794        let step = build.binary(Opcode::Add, first, one, Flags::NSW);
2795        build.jump(head, &[step]);
2796
2797        Builder::new(&mut func, over).jump(next, &[first]);
2798
2799        let mut build = Builder::new(&mut func, next);
2800        let args = build.func().push_values(&[array, second]);
2801        let here = build.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
2802        let seen = build.load(Type::int(8), here, byte(), Flags::NONE);
2803        let blank = build.iconst(Type::int(8), 32);
2804        let over_too = build.icmp(IntPred::Eq, seen, blank);
2805        build.br_if(over_too, done, &[], again, &[]);
2806        deriving(&mut func, next, array, here);
2807
2808        let mut build = Builder::new(&mut func, again);
2809        let one = build.iconst(Type::int(64), 1);
2810        let onward = build.binary(Opcode::Add, second, one, Flags::NSW);
2811        let go = build.icmp(IntPred::Slt, onward, limit);
2812        build.br_if(go, next, &[onward], done, &[]);
2813
2814        Builder::new(&mut func, done).ret(&[]);
2815        (names, func, vec![entry, head, more, over, next, again, done])
2816    }
2817
2818    /// Builds a loop with a loop inside it, each of them reading the array it was handed.
2819    ///
2820    /// The outer loop reads one element per outer iteration, which is a check in its own blocks. The
2821    /// inner loop reads one per inner iteration, and whether that one is checked is the argument, so
2822    /// that the same nest can be a nest whose inner loop is worth splitting and one whose is not.
2823    fn nested(inner_reads: bool) -> (Interner, Func, Vec<Block>) {
2824        let mut names = Interner::new();
2825        let params = [Type::PTR, Type::int(64), Type::int(64)];
2826        let mut func = Func::new(names.intern("f"), Signature::new().with_params(&params));
2827        let entry = func.create_block();
2828        let outer = func.create_block();
2829        let inner = func.create_block();
2830        let round = func.create_block();
2831        let after = func.create_block();
2832        let done = func.create_block();
2833        let array = func.append_param(entry, Type::PTR);
2834        let rows = func.append_param(entry, Type::int(64));
2835        let columns = func.append_param(entry, Type::int(64));
2836        let row = func.append_param(outer, Type::int(64));
2837        let column = func.append_param(inner, Type::int(64));
2838
2839        let zero = Builder::new(&mut func, entry).iconst(Type::int(64), 0);
2840        Builder::new(&mut func, entry).jump(outer, &[zero]);
2841
2842        let mut build = Builder::new(&mut func, outer);
2843        let by = build.iconst(Type::int(64), WIDTH);
2844        let scaled = build.binary(Opcode::Mul, row, by, Flags::NSW);
2845        let args = build.func().push_values(&[array, scaled]);
2846        let at = build.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
2847        check(&mut build, at);
2848        build.load(Type::int(32), at, mem(), Flags::NONE);
2849        let start = build.iconst(Type::int(64), 0);
2850        build.jump(inner, &[start]);
2851
2852        let mut build = Builder::new(&mut func, inner);
2853        let wide = build.iconst(Type::int(64), WIDTH);
2854        let along = build.binary(Opcode::Mul, column, wide, Flags::NSW);
2855        let args = build.func().push_values(&[array, along]);
2856        let here = build.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
2857        if inner_reads {
2858            check(&mut build, here);
2859            build.load(Type::int(32), here, mem(), Flags::NONE);
2860        }
2861        build.jump(round, &[]);
2862
2863        let mut build = Builder::new(&mut func, round);
2864        let one = build.iconst(Type::int(64), 1);
2865        let onward = build.binary(Opcode::Add, column, one, Flags::NSW);
2866        let more = build.icmp(IntPred::Slt, onward, columns);
2867        build.br_if(more, inner, &[onward], after, &[]);
2868
2869        let mut build = Builder::new(&mut func, after);
2870        let one = build.iconst(Type::int(64), 1);
2871        let next = build.binary(Opcode::Add, row, one, Flags::NSW);
2872        let again = build.icmp(IntPred::Slt, next, rows);
2873        build.br_if(again, outer, &[next], done, &[]);
2874
2875        Builder::new(&mut func, done).ret(&[]);
2876        (names, func, vec![entry, outer, inner, round, after, done])
2877    }
2878
2879    /// Builds a nest whose outer loop reads at an address the inner loop worked out.
2880    ///
2881    /// The check is in the outer loop's own blocks, so it is one the outer guard would speak for,
2882    /// but the offset it reads at is defined inside the inner loop. That value is not the same
2883    /// number wherever it is read and it is not a parameter of the outer header, so neither the
2884    /// guard nor the preheader has it in hand, and naming it in either of them names something that
2885    /// does not reach there.
2886    fn reading_what_the_inner_loop_found() -> (Interner, Func, Vec<Block>) {
2887        let mut names = Interner::new();
2888        let params = [Type::PTR, Type::int(64), Type::int(64)];
2889        let mut func = Func::new(names.intern("f"), Signature::new().with_params(&params));
2890        let entry = func.create_block();
2891        let outer = func.create_block();
2892        let inner = func.create_block();
2893        let after = func.create_block();
2894        let done = func.create_block();
2895        let array = func.append_param(entry, Type::PTR);
2896        let rows = func.append_param(entry, Type::int(64));
2897        let columns = func.append_param(entry, Type::int(64));
2898        let row = func.append_param(outer, Type::int(64));
2899        let column = func.append_param(inner, Type::int(64));
2900
2901        let zero = Builder::new(&mut func, entry).iconst(Type::int(64), 0);
2902        Builder::new(&mut func, entry).jump(outer, &[zero]);
2903
2904        let start = Builder::new(&mut func, outer).iconst(Type::int(64), 0);
2905        Builder::new(&mut func, outer).jump(inner, &[start]);
2906
2907        let mut build = Builder::new(&mut func, inner);
2908        let one = build.iconst(Type::int(64), 1);
2909        let onward = build.binary(Opcode::Add, column, one, Flags::NSW);
2910        let more = build.icmp(IntPred::Slt, onward, columns);
2911        build.br_if(more, inner, &[onward], after, &[]);
2912
2913        let mut build = Builder::new(&mut func, after);
2914        let args = build.func().push_values(&[array, onward]);
2915        let at = build.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
2916        checking(&mut build, at, byte());
2917        build.load(Type::int(8), at, byte(), Flags::NONE);
2918        let one = build.iconst(Type::int(64), 1);
2919        let next = build.binary(Opcode::Add, row, one, Flags::NSW);
2920        let again = build.icmp(IntPred::Slt, next, rows);
2921        build.br_if(again, outer, &[next], done, &[]);
2922
2923        Builder::new(&mut func, done).ret(&[]);
2924        (names, func, vec![entry, outer, inner, after, done])
2925    }
2926
2927    /// What one access in the loop covers.
2928    fn mem() -> MemInfo {
2929        MemInfo {
2930            size: WIDTH as u64,
2931            align: WIDTH as u32,
2932            order: MemOrder::NotAtomic,
2933            tbaa: None,
2934            owns: 0,
2935            restrict: Restrict::NONE,
2936        }
2937    }
2938
2939    /// What one access covers in a loop that walks a byte at a time.
2940    ///
2941    /// A walk the guard has to measure has to be over something wanting no alignment, because a step
2942    /// nobody wrote down is a step nothing can divide by the alignment. Which is what the loops this
2943    /// reaches look like anyway: they are scanners over text.
2944    fn byte() -> MemInfo {
2945        MemInfo {
2946            size: 1,
2947            align: 1,
2948            order: MemOrder::NotAtomic,
2949            tbaa: None,
2950            owns: 0,
2951            restrict: Restrict::NONE,
2952        }
2953    }
2954
2955    /// Puts `cap_of` and a `check_bounds` at `pointer` into a block.
2956    ///
2957    /// The shape `rucc-safety` emits, written out here rather than reached for, because `rucc-opt`
2958    /// is rank 9 alongside `rucc-safety` and cannot depend on it.
2959    fn check(build: &mut Builder<'_>, pointer: Value) {
2960        checking(build, pointer, mem());
2961    }
2962
2963    /// The same, for an access of some other width.
2964    fn checking(build: &mut Builder<'_>, pointer: Value, info: MemInfo) {
2965        let args = build.func().push_values(&[pointer]);
2966        let capability = build.value(InstData { args, ..InstData::new(Opcode::CapOf) }, Type::CAP);
2967        let args = build.func().push_values(&[capability, pointer]);
2968        let extra = Extra::Mem(build.func().add_mem(info));
2969        build.inst(InstData { args, extra, ..InstData::new(Opcode::CheckBounds) }, &[]);
2970    }
2971
2972    /// Puts the `cap_of` and the `check_deriv` `rucc-safety` writes behind pointer arithmetic into
2973    /// a block, naming `from` as the pointer the arithmetic started from.
2974    ///
2975    /// Built at the end of the block and then moved in front of the terminator, which is what the
2976    /// builder makes easy and is where a check on an address the block works out belongs anyway.
2977    fn deriving(func: &mut Func, block: Block, from: Value, derived: Value) {
2978        let term = func.terminator(block).expect("the block ends in a branch");
2979        let held: Vec<Inst> = func.insts(block).collect();
2980        let mut build = Builder::new(func, block);
2981        let args = build.func().push_values(&[from]);
2982        let capability = build.value(InstData { args, ..InstData::new(Opcode::CapOf) }, Type::CAP);
2983        let stride = build.iconst(Type::int(64), WIDTH);
2984        let args = build.func().push_values(&[capability, from, derived, stride]);
2985        build.inst(InstData { args, ..InstData::new(Opcode::CheckDeriv) }, &[]);
2986        let added: Vec<Inst> = func.insts(block).filter(|inst| !held.contains(inst)).collect();
2987        for inst in added {
2988            func.remove_inst(inst);
2989            func.insert_before(inst, term);
2990        }
2991    }
2992
2993    /// A second walk off the same pointer, stepping by `step` bytes a time round.
2994    ///
2995    /// The counter the header carries scaled by something other than the stride the loop already
2996    /// walks by, which is a pointer following the same anchor at a rate of its own.
2997    fn beside(func: &mut Func, block: Block, from: Value, step: i128) -> Value {
2998        let term = func.terminator(block).expect("the block ends in a branch");
2999        let mul = func
3000            .insts(block)
3001            .find(|&inst| func[inst].opcode == Opcode::Mul)
3002            .expect("the loop scales its counter");
3003        let counter = func[func[mul].args][0];
3004        let mut build = Builder::new(func, block);
3005        let by = build.iconst(Type::int(64), step);
3006        let scaled = build.binary(Opcode::Mul, counter, by, Flags::NSW);
3007        let args = build.func().push_values(&[from, scaled]);
3008        let along = build.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
3009        for value in [by, scaled, along] {
3010            let inst = super::inst_of(func, value);
3011            func.remove_inst(inst);
3012            func.insert_before(inst, term);
3013        }
3014        along
3015    }
3016
3017    /// One stride past a pointer, worked out in front of the block's terminator.
3018    fn stepped(func: &mut Func, block: Block, from: Value) -> Value {
3019        let term = func.terminator(block).expect("the block ends in a branch");
3020        let mut build = Builder::new(func, block);
3021        let by = build.iconst(Type::int(64), WIDTH);
3022        let args = build.func().push_values(&[from, by]);
3023        let along = build.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
3024        for value in [by, along] {
3025            let inst = super::inst_of(func, value);
3026            func.remove_inst(inst);
3027            func.insert_before(inst, term);
3028        }
3029        along
3030    }
3031
3032    /// Points the capability the bounds check in `block` names at `from` rather than at the address
3033    /// being checked.
3034    ///
3035    /// The shape `rucc_safety::origin` produces, where a capability is taken once at the pointer the
3036    /// object came from and every address derived from it shares that one. The `cap_of` moves with
3037    /// it, since a capability about a pointer defined outside the loop is one the optimizer hoists
3038    /// out of the loop anyway and leaving it inside would be testing a shape nothing emits.
3039    fn taken_at(func: &mut Func, block: Block, from: Value) {
3040        let check = func
3041            .insts(block)
3042            .find(|&inst| func[inst].opcode == Opcode::CheckBounds)
3043            .expect("the loop checks the address it works out");
3044        let held = func[func[check].args][0];
3045        let made = super::inst_of(func, held);
3046        func[made].args = func.push_values(&[from]);
3047    }
3048
3049    /// Moves the walk one stride along, so the address is `&a[i + 1]` rather than `&a[i]`.
3050    fn shifted(func: &mut Func, block: Block) {
3051        let add = func
3052            .insts(block)
3053            .find(|&inst| func[inst].opcode == Opcode::PtrAdd)
3054            .expect("the loop works out an address");
3055        let (array, scaled) = (func[func[add].args][0], func[func[add].args][1]);
3056        let mut build = Builder::new(func, block);
3057        let by = build.iconst(Type::int(64), WIDTH);
3058        let along = build.binary(Opcode::Add, scaled, by, Flags::NSW);
3059        for value in [by, along] {
3060            let inst = super::inst_of(func, value);
3061            func.remove_inst(inst);
3062            func.insert_before(inst, add);
3063        }
3064        func[add].args = func.push_values(&[array, along]);
3065    }
3066
3067    /// The address the loop works out and the pointer it started from.
3068    fn arithmetic(func: &Func, block: Block) -> (Value, Value) {
3069        let add = func
3070            .insts(block)
3071            .find(|&inst| func[inst].opcode == Opcode::PtrAdd)
3072            .expect("the loop works out an address");
3073        let from = func[func[add].args][0];
3074        let derived = func[add].results().next().expect("a ptr_add gives one pointer");
3075        (from, derived)
3076    }
3077
3078    /// Canonicalizes and then splits, with as much fuel as both want.
3079    ///
3080    /// Both, because the pass is written against the shape [`Canon`] leaves, and it is
3081    /// canonicalization that gives the loop the preheader the limit is worked out in.
3082    fn split_up(func: &mut Func) -> Stats {
3083        let mut an = crate::machine::fixtures::analyses();
3084        Canon.run(func, &mut an, &mut Fuel::unlimited());
3085        Split.run(func, &mut an, &mut Fuel::unlimited())
3086    }
3087
3088    #[test]
3089    fn a_loop_whose_result_is_read_after_it_is_put_back_into_closed_form_first() {
3090        // Canonicalization runs a long way in front of this pass and `simplify-cfg` between the two
3091        // undoes some of what it did, which is why the loop here is canonicalized and then broken.
3092        // Both halves would define the value the code after the loop reads, so the pass repairs the
3093        // one loop it is about to copy rather than refusing it or running canonicalization again.
3094        let (mut names, mut func, blocks) = leaving();
3095        let mut an = crate::machine::fixtures::analyses();
3096        Canon.run(&mut func, &mut an, &mut Fuel::unlimited());
3097
3098        let (head, done) = (blocks[1], blocks[3]);
3099        let read = func
3100            .insts(head)
3101            .find(|&inst| func[inst].opcode == Opcode::Load)
3102            .and_then(|inst| func[inst].results().next())
3103            .expect("the loop loads what it walks over");
3104        let term = func.terminator(done).expect("the block after the loop returns");
3105        let sum = Builder::new(&mut func, done).binary(Opcode::Add, read, read, Flags::NONE);
3106        let inst = super::inst_of(&func, sum);
3107        func.remove_inst(inst);
3108        func.insert_before(inst, term);
3109        an.clear();
3110
3111        let stats = Split.run(&mut func, &mut an, &mut Fuel::unlimited());
3112        assert_eq!(stats.count(Kind::Optimized, super::CLOSED_HERE), 1);
3113        assert_eq!(stats.count(Kind::Optimized, SPLIT), 1);
3114        assert_eq!(func[done].params.len(), 1, "the block after the loop took the value in");
3115        assert_eq!(all(&func, Opcode::CheckBounds).len(), 1, "the fast half lost its check");
3116        sound(&func, &mut names);
3117    }
3118
3119    #[test]
3120    fn a_value_read_past_a_join_that_neither_way_out_dominates_is_handed_over_there_as_well() {
3121        // Two ways out of the loop and they meet again, so a parameter at each of them is a name
3122        // the code at the meeting cannot say. The repair puts one there too, which is where the
3123        // iterated dominance frontier comes in, and both halves then hand their own value along.
3124        let (mut names, mut func, blocks) = joining();
3125        let mut an = crate::machine::fixtures::analyses();
3126        Canon.run(&mut func, &mut an, &mut Fuel::unlimited());
3127
3128        let (head, join) = (blocks[1], blocks[5]);
3129        let read = func
3130            .insts(head)
3131            .find(|&inst| func[inst].opcode == Opcode::Load)
3132            .and_then(|inst| func[inst].results().next())
3133            .expect("the loop loads what it walks over");
3134        let term = func.terminator(join).expect("the block the two ways out meet at returns");
3135        let sum = Builder::new(&mut func, join).binary(Opcode::Add, read, read, Flags::NONE);
3136        let inst = super::inst_of(&func, sum);
3137        func.remove_inst(inst);
3138        func.insert_before(inst, term);
3139        an.clear();
3140
3141        let stats = Split.run(&mut func, &mut an, &mut Fuel::unlimited());
3142        assert_eq!(stats.count(Kind::Optimized, super::CLOSED_HERE), 1);
3143        assert_eq!(stats.count(Kind::Optimized, SPLIT), 1);
3144        assert_eq!(stats.count(Kind::Missed, super::ESCAPES), 0);
3145        assert_eq!(func[join].params.len(), 1, "the meeting took the value in as well");
3146        assert_eq!(all(&func, Opcode::CheckBounds).len(), 1, "the fast half lost its check");
3147        sound(&func, &mut names);
3148    }
3149
3150    #[test]
3151    fn a_loop_whose_value_the_next_loops_guard_names_is_left_alone() {
3152        // The second loop starts where the first one stopped, so its guard names a value the first
3153        // loop's body defines. Splitting the first loop would leave that value with one definition
3154        // per half and the guard naming neither, and the repair cannot help because the guard is
3155        // not written down yet. Without the refusal the verifier reports the guard's address as a
3156        // value that arrives at a block and does not reach the use, which is what SQLite hit.
3157        let (mut names, mut func, _) = one_after_another();
3158        let mut an = crate::machine::fixtures::analyses();
3159        Canon.run(&mut func, &mut an, &mut Fuel::unlimited());
3160
3161        let stats = Split.run(&mut func, &mut an, &mut Fuel::unlimited());
3162        sound(&func, &mut names);
3163        assert_eq!(stats.count(Kind::Missed, super::WANTED_ELSEWHERE), 1);
3164        assert_eq!(stats.count(Kind::Optimized, SPLIT), 1);
3165    }
3166
3167    #[test]
3168    fn a_loop_whose_value_the_next_loops_guard_subtracts_is_left_alone_as_well() {
3169        // The same refusal reached through the gap rather than through the address. The second
3170        // loop's window goes on the array and its guard takes off how far along the walk begins,
3171        // which is the first loop's counter, and nothing else in the plan names that value. Missing
3172        // it was tamnd/rucc#1248: the first loop was split, its counter came out as one value per
3173        // half, and the guard that had not been written yet named the one that does not reach it.
3174        let (mut names, mut func, _) = one_after_another_with_a_gap();
3175        let mut an = crate::machine::fixtures::analyses();
3176        Canon.run(&mut func, &mut an, &mut Fuel::unlimited());
3177
3178        let stats = Split.run(&mut func, &mut an, &mut Fuel::unlimited());
3179        sound(&func, &mut names);
3180        assert_eq!(stats.count(Kind::Missed, super::WANTED_ELSEWHERE), 1);
3181        assert_eq!(stats.count(Kind::Optimized, SPLIT), 1);
3182    }
3183
3184    #[test]
3185    fn a_loop_with_a_loop_inside_it_is_split() {
3186        // Nothing about an inner loop makes the copy wrong. The whole nest is copied, the guard goes
3187        // in front of the outer header, and the check in the outer loop's own blocks comes out of
3188        // the fast half. The inner loop reads nothing here, so it plans nothing and does not compete
3189        // with the outer one for the blocks they have in common.
3190        let (mut names, mut func, _) = nested(false);
3191        let stats = split_up(&mut func);
3192        assert_eq!(stats.count(Kind::Optimized, SPLIT), 1);
3193        assert_eq!(stats.count(Kind::Missed, super::NESTED_WITH_ONE), 0);
3194        assert_eq!(all(&func, Opcode::CheckBounds).len(), 1, "the fast half lost its check");
3195        sound(&func, &mut names);
3196    }
3197
3198    #[test]
3199    fn the_inner_loop_is_the_one_split_when_both_of_them_could_be() {
3200        // Both loops plan, and the two plans name the inner loop's blocks between them, so only one
3201        // of them may run. The inner one is kept: its checks run once per inner iteration rather
3202        // than once per outer one, and it is the smaller thing to copy. The outer one is left for
3203        // the next run of the pipeline.
3204        let (mut names, mut func, _) = nested(true);
3205        let stats = split_up(&mut func);
3206        assert_eq!(stats.count(Kind::Optimized, SPLIT), 1);
3207        assert_eq!(stats.count(Kind::Missed, super::NESTED_WITH_ONE), 1);
3208        assert_eq!(stats.count(Kind::Missed, super::INSIDE_A_LOOP), 1);
3209        sound(&func, &mut names);
3210    }
3211
3212    #[test]
3213    fn a_value_the_inner_loop_defined_is_not_one_the_guard_may_write_again() {
3214        // The check is in the outer loop's own blocks, so the guard would speak for it, and the
3215        // offset it reads at came out of the inner loop. Reading that value again in the preheader
3216        // is not writing it again, because it is not the same number wherever it is read, and it is
3217        // not a parameter of the outer header either, so it is neither of the two things the walk
3218        // stops at. Treating it as the first of them puts a name in the guard that does not reach
3219        // there, which the verifier catches, so the address is refused and the check stays.
3220        //
3221        // Canonicalization is what would otherwise hide this, since the repair gives the block after
3222        // the inner loop a parameter for the value and the address then names that instead. It is
3223        // left out here for that reason, and the loop has its preheader written into the fixture.
3224        let (mut names, mut func, _) = reading_what_the_inner_loop_found();
3225        let mut an = crate::machine::fixtures::analyses();
3226        let stats = Split.run(&mut func, &mut an, &mut Fuel::unlimited());
3227        // Soundness first, because it is the stronger of the two: without the refusal the guard and
3228        // the preheader both name the inner loop's value and the verifier says so at each of them.
3229        sound(&func, &mut names);
3230        assert_eq!(stats.count(Kind::Optimized, SPLIT), 0);
3231    }
3232
3233    /// What a value is, when it is a number written down.
3234    fn number(func: &Func, value: Value) -> Option<i128> {
3235        let inst = crate::trip::inst_of(func, value);
3236        if func[inst].opcode != Opcode::IConst {
3237            return None;
3238        }
3239        let Extra::Imm(imm) = func[inst].extra else { return None };
3240        Some(func[imm].signed(func[value].ty))
3241    }
3242
3243    /// Every instruction in the function with this opcode, and the block it is in.
3244    fn all(func: &Func, opcode: Opcode) -> Vec<(Block, Inst)> {
3245        func.blocks()
3246            .flat_map(|block| func.insts(block).map(move |inst| (block, inst)).collect::<Vec<_>>())
3247            .filter(|&(_, inst)| func[inst].opcode == opcode)
3248            .collect()
3249    }
3250
3251    /// Insists the function is one the rest of the compiler may believe.
3252    ///
3253    /// This is what the tests here rest on. The pass makes a second copy of a loop, gives a new
3254    /// block parameters that stand for the old header's, and moves a preheader's worth of
3255    /// arithmetic in front of a terminator that was already there, so whether every value is in
3256    /// scope where it is read is not something reading the code settles.
3257    fn sound(func: &Func, names: &mut Interner) {
3258        let target = TargetInfo::new("x86_64-unknown-linux-gnu".parse::<Triple>().unwrap());
3259        let module = Module::new(names.intern("t.c"), &target);
3260        if let Err(errors) = verify_func(&module, func, names) {
3261            panic!("{errors:#?}");
3262        }
3263    }
3264
3265    #[test]
3266    fn a_loop_that_can_stop_early_is_split_even_though_hoisting_will_not_touch_it() {
3267        // The census row this pass was written for. Of the checks SQLite still carries at -O2, the
3268        // largest group by far is in loops with a second way out, which is exactly the loop here.
3269        let (mut names, mut func, _) = leaving();
3270        let mut an = crate::machine::fixtures::analyses();
3271        Canon.run(&mut func, &mut an, &mut Fuel::unlimited());
3272        let refused = crate::hoist::Hoist.run(&mut func, &mut an, &mut Fuel::unlimited());
3273        assert!(!refused.changed(), "hoisting has nothing to say about this loop");
3274
3275        let stats = Split.run(&mut func, &mut an, &mut Fuel::unlimited());
3276        assert_eq!(stats.count(Kind::Optimized, SPLIT), 1);
3277        sound(&func, &mut names);
3278    }
3279
3280    #[test]
3281    fn the_half_the_loop_runs_first_has_no_check_in_it_and_the_other_one_keeps_it() {
3282        // One check went in and one check came out, and the one that came out is in the copy. That
3283        // is the whole transformation: the same work, with the checking half reached only once the
3284        // guard says the run of safe iterations is over.
3285        let (mut names, mut func, blocks) = leaving();
3286        let head = blocks[1];
3287        split_up(&mut func);
3288
3289        let left = all(&func, Opcode::CheckBounds);
3290        assert_eq!(left.len(), 1, "one check, and it is the one the slow half kept");
3291        assert_ne!(left[0].0, head, "and it is not in the block the loop started in");
3292        sound(&func, &mut names);
3293    }
3294
3295    #[test]
3296    fn the_derivation_check_on_an_index_that_walks_goes_the_way_the_bounds_check_beside_it_goes() {
3297        // `a[i]` is two judgements, one about the arithmetic and one about the access, and the
3298        // window covers both. It covers the arithmetic more easily than the access, since a
3299        // derivation is allowed to land anywhere the access is allowed to and a stride short of
3300        // that as well. Until the guard spoke for it this was the whole of what the fast half of a
3301        // byte at a time loop still had in it.
3302        let (mut names, mut func, blocks) = walking(Some(TRIPS), Flags::NSW);
3303        let (from, derived) = arithmetic(&func, blocks[1]);
3304        deriving(&mut func, blocks[1], from, derived);
3305
3306        let stats = split_up(&mut func);
3307        assert_eq!(stats.count(Kind::Optimized, SPLIT), 1);
3308        assert_eq!(all(&func, Opcode::CheckBounds).len(), 1, "the fast half lost its check");
3309        assert_eq!(all(&func, Opcode::CheckDeriv).len(), 1, "and the derivation check with it");
3310        sound(&func, &mut names);
3311    }
3312
3313    #[test]
3314    fn two_checks_on_one_address_ask_the_runtime_one_question() {
3315        // tamnd/rucc#871. `a[i]` carries a bounds check and a derivation check and the guard sizes
3316        // both of them from the same address, so the preheader called the runtime twice about it.
3317        // What made the two calls different was how many bytes each one said the loop was going to
3318        // read, and that stopped meaning anything when the query stopped walking, so both ask for
3319        // everything now and the second is a value the preheader already has. On `a-string-scan` it
3320        // was five calls at one address.
3321        let (mut names, mut func, blocks) = walking(Some(TRIPS), Flags::NSW);
3322        let (from, derived) = arithmetic(&func, blocks[1]);
3323        deriving(&mut func, blocks[1], from, derived);
3324
3325        let stats = split_up(&mut func);
3326        assert_eq!(stats.count(Kind::Optimized, SPLIT), 1);
3327        assert_eq!(all(&func, Opcode::CapExtent).len(), 1, "one question for the two checks");
3328        sound(&func, &mut names);
3329    }
3330
3331    #[test]
3332    fn a_derivation_check_whose_walk_starts_along_from_the_pointer_it_is_about_is_taken() {
3333        // `&a[i] + 1` walks from a stride past `a`, so a window measured where the walk begins is a
3334        // window about whoever owns that address rather than about whoever owns `a`. Measuring from
3335        // `a` instead and widening the window by the stride answers both: `a` is in it on the first
3336        // iteration and the walk is in it on every one.
3337        let (mut names, mut func, blocks) = walking(Some(TRIPS), Flags::NSW);
3338        let head = blocks[1];
3339        let (from, walked) = arithmetic(&func, head);
3340        let past = stepped(&mut func, head, walked);
3341        deriving(&mut func, head, from, past);
3342
3343        let stats = split_up(&mut func);
3344        assert_eq!(stats.count(Kind::Optimized, SPLIT), 1);
3345        assert_eq!(stats.count(Kind::Missed, super::NOT_FROM_THE_START), 0);
3346        assert_eq!(all(&func, Opcode::CheckDeriv).len(), 1, "the fast half lost the check");
3347        sound(&func, &mut names);
3348    }
3349
3350    #[test]
3351    fn a_derivation_check_whose_pointer_sits_above_the_walk_is_taken() {
3352        // The same thing the other way round. The pointer the check names is a stride past `a` and
3353        // the walk starts on `a`, so the lower of the two is where the walk begins and the window
3354        // is as wide as the gap. Which of the pair is the one that moves does not come into it.
3355        let (mut names, mut func, blocks) = walking(Some(TRIPS), Flags::NSW);
3356        let head = blocks[1];
3357        let (array, walked) = arithmetic(&func, head);
3358        let above = stepped(&mut func, head, array);
3359        deriving(&mut func, head, above, walked);
3360
3361        let stats = split_up(&mut func);
3362        assert_eq!(stats.count(Kind::Optimized, SPLIT), 1);
3363        assert_eq!(stats.count(Kind::Missed, super::NOT_FROM_THE_START), 0);
3364        assert_eq!(all(&func, Opcode::CheckDeriv).len(), 1, "the fast half lost the check");
3365        sound(&func, &mut names);
3366    }
3367
3368    #[test]
3369    fn a_derivation_check_whose_walk_begins_a_handed_distance_into_the_object_is_taken() {
3370        // `a[start + i]`, where the check is about `a` and the walk begins `start` elements in. The
3371        // window goes on `a`, which is the object the check is about, and the guard takes the gap
3372        // off what it measured there and asks for the gap to be at or above zero.
3373        let (mut names, mut func, blocks) = offsetting(Flags::NSW);
3374        let head = blocks[1];
3375        let (array, walked) = arithmetic(&func, head);
3376        deriving(&mut func, head, array, walked);
3377
3378        let stats = split_up(&mut func);
3379        assert_eq!(stats.count(Kind::Optimized, SPLIT), 1);
3380        assert_eq!(stats.count(Kind::Missed, super::NOT_FROM_THE_START), 0);
3381        assert_eq!(all(&func, Opcode::CheckDeriv).len(), 1, "the fast half lost the check");
3382        sound(&func, &mut names);
3383    }
3384
3385    #[test]
3386    fn a_derivation_check_whose_pointer_moves_and_whose_walk_begins_a_handed_distance_in_stays() {
3387        // Both pointers walk and the distance off the array is not a number, so neither window is
3388        // available: the pair cannot be measured against each other and the one that would go on
3389        // the pointer the check names needs that pointer to stand still. It is the gap left over.
3390        let (mut names, mut func, blocks) = offsetting(Flags::NSW);
3391        let head = blocks[1];
3392        let (_, walked) = arithmetic(&func, head);
3393        let past = stepped(&mut func, head, walked);
3394        deriving(&mut func, head, walked, past);
3395
3396        let stats = split_up(&mut func);
3397        assert_eq!(stats.count(Kind::Optimized, SPLIT), 1);
3398        assert_eq!(stats.count(Kind::Missed, super::NOT_FROM_THE_START), 1);
3399        assert_eq!(all(&func, Opcode::CheckDeriv).len(), 2, "the check is in both halves");
3400        sound(&func, &mut names);
3401    }
3402
3403    #[test]
3404    fn a_derivation_check_whose_pointer_walks_at_a_step_of_its_own_stays() {
3405        // The case neither window speaks for. The pointer the check names runs away at twice the
3406        // rate the walk does, so the distance between the two is a different number every time
3407        // round and no window a number of bytes wide holds the pair for more than one iteration.
3408        let (mut names, mut func, blocks) = walking(Some(TRIPS), Flags::NSW);
3409        let head = blocks[1];
3410        let (array, walked) = arithmetic(&func, head);
3411        let faster = beside(&mut func, head, array, 2 * WIDTH);
3412        deriving(&mut func, head, faster, walked);
3413
3414        let stats = split_up(&mut func);
3415        assert_eq!(stats.count(Kind::Optimized, SPLIT), 1);
3416        assert_eq!(stats.count(Kind::Missed, super::NOT_FROM_THE_START), 1);
3417        assert_eq!(all(&func, Opcode::CheckDeriv).len(), 2, "the check is in both halves");
3418        sound(&func, &mut names);
3419    }
3420
3421    #[test]
3422    fn a_derivation_check_whose_own_pointer_walks_beside_the_new_one_is_taken_by_one_window() {
3423        // `p = p + k`, where the pointer the check names is the one that moves, so no window
3424        // measured from a single address speaks for it. One measured from the lower of the two and
3425        // a step and a byte wide holds the pair wherever the walk has got to, and that says the old
3426        // pointer is inside the object and the new one did not leave it. This is `a-string-scan`,
3427        // where the derivation check was the whole of what the fast half still had.
3428        let (mut names, mut func, blocks) = walking(Some(TRIPS), Flags::NSW);
3429        let head = blocks[1];
3430        let (_, walked) = arithmetic(&func, head);
3431        let past = stepped(&mut func, head, walked);
3432        deriving(&mut func, head, walked, past);
3433
3434        let stats = split_up(&mut func);
3435        assert_eq!(stats.count(Kind::Optimized, SPLIT), 1);
3436        assert_eq!(stats.count(Kind::Missed, super::NOT_FROM_THE_START), 0);
3437        assert_eq!(all(&func, Opcode::CheckDeriv).len(), 1, "the fast half lost it");
3438        sound(&func, &mut names);
3439    }
3440
3441    #[test]
3442    fn a_bounds_check_whose_capability_was_taken_where_the_object_came_from_is_taken() {
3443        // `rucc_safety::origin` takes one capability at `a` and every address off it shares that
3444        // one, so the check names `a` where it used to name the address being checked. The walk
3445        // begins on `a` here, which is the first of the three rules a derivation check already
3446        // goes through, and the window is the same window it always was.
3447        let (mut names, mut func, blocks) = walking(Some(TRIPS), Flags::NSW);
3448        let head = blocks[1];
3449        let (array, _) = arithmetic(&func, head);
3450        taken_at(&mut func, head, array);
3451
3452        let stats = split_up(&mut func);
3453        assert_eq!(stats.count(Kind::Optimized, SPLIT), 1);
3454        assert_eq!(stats.count(Kind::Missed, super::NOT_FROM_THE_START), 0);
3455        assert_eq!(all(&func, Opcode::CheckBounds).len(), 1, "the fast half lost its check");
3456        sound(&func, &mut names);
3457    }
3458
3459    #[test]
3460    fn a_bounds_check_whose_capability_was_taken_where_a_handed_walk_started_is_taken_too() {
3461        // `a[start + i]` with the capability on `a`. The window goes on `a`, which is the object
3462        // the check is about, and the guard takes off how far in the walk begins. Which is the
3463        // third of the three rules, reached from a bounds check rather than from a derivation
3464        // check, and it is the commonest shape a subscript in a loop has.
3465        let (mut names, mut func, blocks) = offsetting(Flags::NSW);
3466        let head = blocks[1];
3467        let (array, _) = arithmetic(&func, head);
3468        taken_at(&mut func, head, array);
3469
3470        let stats = split_up(&mut func);
3471        assert_eq!(stats.count(Kind::Optimized, SPLIT), 1);
3472        assert_eq!(stats.count(Kind::Missed, super::NOT_FROM_THE_START), 0);
3473        assert_eq!(all(&func, Opcode::CheckBounds).len(), 1, "the fast half lost its check");
3474        sound(&func, &mut names);
3475    }
3476
3477    #[test]
3478    fn a_window_that_holds_a_capability_and_an_access_is_as_wide_as_the_access() {
3479        // `a[i + 1]` with the capability on `a`, so the pair is a whole access apart and the window
3480        // has to reach from `a` to the end of the first read. The rule it goes through was written
3481        // for a derivation check, which reads nothing, and it made the window the gap and one byte
3482        // on the end. That is four bytes short here, and a window short of what the loop reads is
3483        // the one mistake in this file the guard cannot catch, since the guard sends every
3484        // iteration inside the window down the half that does not check.
3485        let (mut names, mut func, blocks) = walking(Some(TRIPS), Flags::NSW);
3486        let head = blocks[1];
3487        let (array, _) = arithmetic(&func, head);
3488        shifted(&mut func, head);
3489        taken_at(&mut func, head, array);
3490
3491        let stats = split_up(&mut func);
3492        assert_eq!(stats.count(Kind::Optimized, SPLIT), 1);
3493        assert_eq!(all(&func, Opcode::CheckBounds).len(), 1, "the fast half lost its check");
3494
3495        let (_, asked) = *all(&func, Opcode::CapExtent).first().expect("the walk was sized");
3496        let extent = asked_of(&func, asked);
3497        let window = all(&func, Opcode::Sub)
3498            .into_iter()
3499            .find(|&(_, inst)| func[func[inst].args][0] == extent)
3500            .map(|(_, inst)| func[func[inst].args][1])
3501            .and_then(|value| number(&func, value))
3502            .expect("the guard takes the window off what it measured");
3503        assert_eq!(window, WIDTH * 2, "the window holds the pointer and the whole first access");
3504        sound(&func, &mut names);
3505    }
3506
3507    /// What the runtime answered, out of the instruction that asked it.
3508    fn asked_of(func: &Func, inst: Inst) -> Value {
3509        func[inst].results().next().expect("the query gives one number")
3510    }
3511
3512    #[test]
3513    fn how_far_the_runtime_is_asked_to_look_is_settled_in_front_of_the_loop() {
3514        // The one thing a compiler cannot work out here is how many bytes belong to the object, so
3515        // it is asked, once, before the loop starts. Once is what makes this worth doing: a query
3516        // per loop in place of a check per iteration.
3517        let (mut names, mut func, _) = leaving();
3518        split_up(&mut func);
3519
3520        let asked = all(&func, Opcode::CapExtent);
3521        assert_eq!(asked.len(), 1, "one question for the one check that was sized");
3522        let cfg = crate::Cfg::new(&func);
3523        let doms = crate::Dominators::new(&cfg);
3524        let loops = crate::Loops::new(&cfg, &doms);
3525        assert!(
3526            loops.all().all(|id| !loops.contains(id, asked[0].0)),
3527            "and it is outside the loop"
3528        );
3529        sound(&func, &mut names);
3530    }
3531
3532    #[test]
3533    fn a_walk_that_starts_at_an_index_the_caller_handed_in_is_split() {
3534        // #810. The first address is `a + 4 * start` and the question has to be put about that
3535        // address rather than about the array, because an extent measured from the array covers
3536        // bytes in front of where the loop begins and would say the walk fits when it does not.
3537        let (mut names, mut func, blocks) = from_an_index();
3538        let stats = split_up(&mut func);
3539        assert_eq!(stats.count(Kind::Optimized, SPLIT), 1);
3540        assert_eq!(all(&func, Opcode::CheckBounds).len(), 1, "the fast half lost its check");
3541
3542        let asked = all(&func, Opcode::CapExtent);
3543        assert_eq!(asked.len(), 1, "one question for the one check that was sized");
3544        let at = func[func[asked[0].1].args][1];
3545        let inst = super::inst_of(&func, at);
3546        assert_eq!(func[inst].opcode, Opcode::PtrAdd, "the question is asked about a displacement");
3547        assert_eq!(func[func[inst].args][0], func[blocks[0]].params[0], "off the array");
3548        sound(&func, &mut names);
3549    }
3550
3551    #[test]
3552    fn a_walk_over_a_file_scope_array_is_split_and_the_address_is_written_out_again() {
3553        // #810. The address of a global is a link time constant, so it does not change inside a
3554        // loop wherever the instruction that works it out happens to sit. The question in front of
3555        // the loop gets a `global_addr` of its own rather than reading the one inside, which is one
3556        // instruction and is the same trade `crate::licm` already makes for these.
3557        let (mut names, mut func, _) = over_a_global();
3558        let stats = split_up(&mut func);
3559        assert_eq!(stats.count(Kind::Optimized, SPLIT), 1);
3560        assert_eq!(all(&func, Opcode::CheckBounds).len(), 1, "the fast half lost its check");
3561
3562        let asked = all(&func, Opcode::CapExtent);
3563        assert_eq!(asked.len(), 1, "one question for the one check that was sized");
3564        let at = func[func[asked[0].1].args][1];
3565        let inst = super::inst_of(&func, at);
3566        assert_eq!(func[inst].opcode, Opcode::GlobalAddr, "asked about the array itself");
3567
3568        let cfg = crate::Cfg::new(&func);
3569        let doms = crate::Dominators::new(&cfg);
3570        let loops = crate::Loops::new(&cfg, &doms);
3571        let addresses = all(&func, Opcode::GlobalAddr);
3572        assert_eq!(addresses.len(), 3, "one in each half of the loop and one in front of them");
3573        assert_eq!(
3574            addresses
3575                .iter()
3576                .filter(|&&(block, _)| loops.all().all(|id| !loops.contains(id, block)))
3577                .count(),
3578            1,
3579            "and the one in front is outside every loop, which is where the question is asked",
3580        );
3581        sound(&func, &mut names);
3582    }
3583
3584    #[test]
3585    fn a_walk_whose_step_is_not_a_number_is_split_and_the_guard_measures_how_far_it_got() {
3586        // #810. The pointer moves by one or by two and nothing knows which, so there is no step to
3587        // carry and no count to keep. What the guard can do instead is subtract: where the pointer
3588        // is now, less where it was on the way in, is the displacement itself rather than a number
3589        // standing in for it, so the same window and the same rule apply unchanged.
3590        let (mut names, mut func, blocks) = by_what_it_read();
3591        let stats = split_up(&mut func);
3592        assert_eq!(stats.count(Kind::Optimized, SPLIT), 1);
3593        assert_eq!(all(&func, Opcode::CheckBounds).len(), 1, "the fast half lost its check");
3594
3595        let asked = all(&func, Opcode::CapExtent);
3596        assert_eq!(asked.len(), 1, "one question for the one check that was sized");
3597        assert_eq!(
3598            func[func[asked[0].1].args][1], func[blocks[0]].params[0],
3599            "asked about the pointer the loop was handed, which is where the walk begins",
3600        );
3601
3602        let measured = all(&func, Opcode::PtrToInt);
3603        assert_eq!(measured.len(), 2, "where the pointer began and where it is now");
3604        sound(&func, &mut names);
3605    }
3606
3607    #[test]
3608    fn a_guard_that_measures_carries_nothing_round_the_loop() {
3609        // The measured offset costs less than the counted one rather than more. It is worked out
3610        // from a pointer the loop already hands itself, so the guard needs no parameter for it and
3611        // the latch needs no add, and what is left is one subtraction where there was a block
3612        // parameter and an increment.
3613        let (mut names, mut func, _) = by_what_it_read();
3614        split_up(&mut func);
3615
3616        let cfg = crate::Cfg::new(&func);
3617        let doms = crate::Dominators::new(&cfg);
3618        let loops = crate::Loops::new(&cfg, &doms);
3619        let guard = loops
3620            .all()
3621            .map(|id| loops.header(id))
3622            .find(|&block| func.insts(block).any(|inst| func[inst].opcode == Opcode::PtrToInt))
3623            .expect("the guard is the header of the loop it took over");
3624        assert_eq!(func[guard].params.len(), 1, "the pointer the header carried, and nothing else");
3625        sound(&func, &mut names);
3626    }
3627
3628    #[test]
3629    fn an_address_built_out_of_what_the_header_carries_is_written_again_in_the_guard() {
3630        // #810. `p + (i & 7)` is not an induction variable and scalar evolution has nothing to say
3631        // about it, and it is not a fixed distance from a pointer either, so measuring where the
3632        // pointer went does not reach it. It is still a function of the two parameters the header
3633        // carries, so both the guard and the preheader can write the two instructions out again
3634        // from what each of them already has, and then the subtraction is the one that was already
3635        // here.
3636        let (mut names, mut func, blocks) = from_what_it_carries(false);
3637        let stats = split_up(&mut func);
3638        assert_eq!(stats.count(Kind::Optimized, SPLIT), 1);
3639        assert_eq!(all(&func, Opcode::CheckBounds).len(), 1, "the fast half lost its check");
3640
3641        let masks = all(&func, Opcode::And);
3642        assert_eq!(masks.len(), 4, "one per half, one in the guard and one in the preheader");
3643        let inside: Vec<Block> = masks.iter().map(|&(block, _)| block).collect();
3644        assert!(inside.contains(&blocks[0]), "the preheader works the first address out");
3645
3646        let asked = all(&func, Opcode::CapExtent);
3647        assert_eq!(asked.len(), 1, "one question, in front of the loop");
3648        assert_eq!(asked[0].0, blocks[0], "asked in the preheader about the first address");
3649        let measured = all(&func, Opcode::PtrToInt);
3650        assert_eq!(measured.len(), 2, "where the address began and where it is now");
3651        sound(&func, &mut names);
3652    }
3653
3654    #[test]
3655    fn an_address_built_on_something_read_out_of_memory_is_left_alone() {
3656        // The same loop with a load where the mask was. A second copy of a load in the guard is a
3657        // second read at another moment, which is not the same number, and a copy of it in the
3658        // preheader is a read on a loop that may run no iterations at all. So the address stops
3659        // being something either block could work out and the check stays in both halves.
3660        let (mut names, mut func, _) = from_what_it_carries(true);
3661        let stats = split_up(&mut func);
3662        assert_eq!(stats.count(Kind::Optimized, SPLIT), 0, "the loop is left alone");
3663        assert_eq!(stats.count(Kind::Missed, super::STEP_NOT_FOLLOWED), 1, "and says which half");
3664        assert_eq!(all(&func, Opcode::CheckBounds).len(), 1, "and the check stays where it was");
3665        assert!(all(&func, Opcode::CapExtent).is_empty(), "with nothing asked in front of it");
3666        sound(&func, &mut names);
3667    }
3668
3669    #[test]
3670    fn a_walk_down_a_linked_list_is_left_alone() {
3671        // Splitting a list would be sound and would not pay. The guard tests the difference at run
3672        // time, so a second node that landed inside the first one's object would pass it, but the
3673        // next node of a heap allocated list is its own object and the guard fails from the second
3674        // iteration on, leaving two copies of the loop with every check in both. What stops it is
3675        // the walk over the back edge, which insists the pointer is its own former self plus bytes,
3676        // and a load is not.
3677        let (mut names, mut func, _) = down_a_list();
3678        let stats = split_up(&mut func);
3679        assert_eq!(stats.count(Kind::Optimized, SPLIT), 0, "the loop is left alone");
3680        assert_eq!(stats.count(Kind::Missed, super::WALKS_A_STRUCTURE), 1, "and says it is a list");
3681        assert_eq!(all(&func, Opcode::CheckBounds).len(), 1, "and the check stays where it was");
3682        assert!(all(&func, Opcode::CapExtent).is_empty(), "with nothing asked in front of it");
3683        sound(&func, &mut names);
3684    }
3685
3686    /// Where the address the loop checks came from, for [`made_in_the_loop`].
3687    enum Made {
3688        /// Read out of memory.
3689        Read,
3690        /// Handed back by a call that cannot free.
3691        Returned,
3692        /// One of two, chosen every iteration.
3693        Chosen,
3694        /// Worked out from the counter as a number and then used as an address.
3695        Cast,
3696    }
3697
3698    /// A loop whose checked address is made in the body, in one of the ways the census names.
3699    ///
3700    /// ```text
3701    /// entry(a, n): jump head(0)
3702    /// head(i):     p = <made here>; check_bounds cap_of(p), p
3703    ///              j = i + 1; br j < n -> head(j), done
3704    /// done:        ret
3705    /// ```
3706    ///
3707    /// The same loop every time, because what these rows differ in is where the pointer came from
3708    /// and that is the only thing varied here. None of the four is an address scalar evolution can
3709    /// evolve and none is one the guard could write again, so all of them reach the same refusal.
3710    /// What each one is for is that the refusal now says which of them it was.
3711    fn made_in_the_loop(how: Made) -> (Interner, Func, Vec<Block>) {
3712        let word = Type::int(64);
3713        let mut names = Interner::new();
3714        let mut func =
3715            Func::new(names.intern("f"), Signature::new().with_params(&[Type::PTR, word]));
3716        let entry = func.create_block();
3717        let head = func.create_block();
3718        let done = func.create_block();
3719        let text = func.append_param(entry, Type::PTR);
3720        let count = func.append_param(entry, word);
3721        let index = func.append_param(head, word);
3722
3723        let mut build = Builder::new(&mut func, entry);
3724        let zero = build.iconst(word, 0);
3725        build.jump(head, &[zero]);
3726
3727        let mut build = Builder::new(&mut func, head);
3728        let args = build.func().push_values(&[text, index]);
3729        let along = build.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
3730        let pointer = match how {
3731            Made::Read => build.load(Type::PTR, along, mem(), Flags::NONE),
3732            Made::Returned => {
3733                let callee = names.intern("somewhere");
3734                let returns = Signature::new().with_returns(&[Type::PTR]);
3735                let signature = build.func().add_signature(returns);
3736                let call = build.call(callee, signature, &[]);
3737                // Without this the loop is refused for the call before any check is looked at, and
3738                // the row would be one no build ever reports. A callee that cannot free is the only
3739                // way a call gets to be in a loop this pass is still willing to split.
3740                build.func()[call].flags |= Flags::NOFREE;
3741                build.func()[call].results().next().expect("the callee hands back a pointer")
3742            }
3743            // One arm reads memory, which is what keeps the guard from writing the choice out
3744            // again. Two arms it could write are a choice it takes rather than refuses.
3745            Made::Chosen => {
3746                let other = build.load(Type::PTR, along, mem(), Flags::NONE);
3747                let odd = build.iconst(word, 1);
3748                let which = build.binary(Opcode::And, index, odd, Flags::NONE);
3749                let none = build.iconst(word, 0);
3750                let taken = build.icmp(IntPred::Eq, which, none);
3751                build.select(taken, text, other)
3752            }
3753            Made::Cast => build.unary(Opcode::IntToPtr, index, Type::PTR),
3754        };
3755        checking(&mut build, pointer, byte());
3756        let one = build.iconst(word, 1);
3757        let next = build.binary(Opcode::Add, index, one, Flags::NSW);
3758        let again = build.icmp(IntPred::Slt, next, count);
3759        build.br_if(again, head, &[next], done, &[]);
3760        Builder::new(&mut func, done).ret(&[]);
3761        (names, func, vec![entry, head, done])
3762    }
3763
3764    /// What the census says about a loop built by [`made_in_the_loop`].
3765    fn refusal(how: Made) -> (Interner, Func, Stats) {
3766        let (names, mut func, _) = made_in_the_loop(how);
3767        let stats = split_up(&mut func);
3768        assert_eq!(stats.count(Kind::Optimized, SPLIT), 0, "the loop is left alone");
3769        (names, func, stats)
3770    }
3771
3772    #[test]
3773    fn an_address_read_out_of_memory_says_so() {
3774        // A second copy of the load is a second read at another moment, so neither the guard nor
3775        // the preheader can work the address out, and the check stays in both halves. What this is
3776        // about is the row it lands in: the pointer came out of memory, which is a different thing
3777        // to do something about than a subscript nothing can count.
3778        let (mut names, func, stats) = refusal(Made::Read);
3779        assert_eq!(stats.count(Kind::Missed, super::ADDRESS_FROM_MEMORY), 1);
3780        sound(&func, &mut names);
3781    }
3782
3783    #[test]
3784    fn an_address_handed_back_by_a_call_says_so() {
3785        // The loop is still one this pass would split, since the callee cannot free, so the check
3786        // is looked at and refused on its own account rather than the loop being dropped first.
3787        let (mut names, func, stats) = refusal(Made::Returned);
3788        assert_eq!(stats.count(Kind::Missed, super::ADDRESS_FROM_A_CALL), 1);
3789        sound(&func, &mut names);
3790    }
3791
3792    #[test]
3793    fn an_address_the_loop_chose_between_says_so() {
3794        // Named by what the address is rather than by what is under the arm that reads memory. The
3795        // choice is the outer thing and it is the thing anybody reading the census would go and
3796        // look at, since which arm was taken is what the guard would have to know.
3797        let (mut names, func, stats) = refusal(Made::Chosen);
3798        assert_eq!(stats.count(Kind::Missed, super::ADDRESS_FROM_A_CHOICE), 1);
3799        sound(&func, &mut names);
3800    }
3801
3802    #[test]
3803    fn an_address_none_of_the_rows_fits_is_still_counted() {
3804        // The remainder, which is what the old single row has become. Keeping it is the point: a
3805        // census that named three shapes and dropped everything else would be a census of what
3806        // somebody thought to look for rather than of what the build does.
3807        let (mut names, func, stats) = refusal(Made::Cast);
3808        assert_eq!(stats.count(Kind::Missed, super::NOT_FOLLOWED), 1);
3809        sound(&func, &mut names);
3810    }
3811
3812    #[test]
3813    fn a_walk_the_guard_would_measure_is_left_alone_when_its_access_wants_alignment() {
3814        // A step nobody wrote down is a step nothing can divide by the alignment, so a measured walk
3815        // has no answer about whether the second access is as aligned as the first. Refusing is the
3816        // conservative reading and it has its own line in the census, so what it costs is a number.
3817        let (mut names, mut func, _) = by_what_it_read();
3818        for (_, inst) in all(&func, Opcode::CheckBounds) {
3819            let extra = Extra::Mem(func.add_mem(mem()));
3820            func[inst].extra = extra;
3821        }
3822        let stats = split_up(&mut func);
3823        assert_eq!(stats.count(Kind::Optimized, SPLIT), 0, "the loop is left alone");
3824        assert_eq!(stats.count(Kind::Missed, super::MEASURED_ALIGN), 1);
3825        sound(&func, &mut names);
3826    }
3827
3828    #[test]
3829    fn a_walk_from_an_index_in_int_is_split_and_the_extension_is_emitted_in_front() {
3830        // #810, and the shape that is actually in C rather than the one that is convenient to
3831        // build. The chrec of `start + i` is in `int` and its base is `start`, so widening it to
3832        // pointer width wants `sext(start)`, which nothing in the function computes. The invariant
3833        // describes the extension instead and this pass emits it, once, in the preheader.
3834        let (mut names, mut func, blocks) = from_a_narrow_index();
3835        let stats = split_up(&mut func);
3836        assert_eq!(stats.count(Kind::Optimized, SPLIT), 1);
3837        assert_eq!(all(&func, Opcode::CheckBounds).len(), 1, "the fast half lost its check");
3838
3839        let asked = all(&func, Opcode::CapExtent);
3840        assert_eq!(asked.len(), 1, "one question for the one check that was sized");
3841        let at = func[func[asked[0].1].args][1];
3842        let sum = super::inst_of(&func, at);
3843        assert_eq!(func[sum].opcode, Opcode::PtrAdd, "the question is asked about a displacement");
3844        assert_eq!(func[func[sum].args][0], func[blocks[0]].params[0], "off the array");
3845        let widened = all(&func, Opcode::SExt);
3846        assert_eq!(widened.len(), 3, "one extension in each half of the loop and one in front");
3847        let start = func[blocks[0]].params[1];
3848        assert_eq!(
3849            widened.iter().filter(|&&(_, inst)| func[func[inst].args][0] == start).count(),
3850            1,
3851            "and the one in front is of the index the caller handed in, which the halves never take",
3852        );
3853        sound(&func, &mut names);
3854    }
3855
3856    #[test]
3857    fn a_walk_from_high_to_low_is_split_and_the_question_goes_the_other_way() {
3858        // #680. The offset the guard carries counts bytes moved rather than bytes added, so it goes
3859        // up here exactly as it does in an ascending loop and the guard is the same guard. The one
3860        // thing that turns over is which end of the object the runtime is asked about, and it is
3861        // asked at the end of the first access rather than at its start so that the window is room
3862        // below and the rule the pass asks is the mirror of the one it asks going up.
3863        let (mut names, mut func, blocks) = downwards();
3864        let stats = split_up(&mut func);
3865        assert_eq!(stats.count(Kind::Optimized, SPLIT), 1);
3866        assert_eq!(all(&func, Opcode::CheckBounds).len(), 1, "the fast half lost its check");
3867
3868        assert!(all(&func, Opcode::CapExtent).is_empty(), "nothing asked about the bytes above");
3869        let asked = all(&func, Opcode::CapExtentBack);
3870        assert_eq!(asked.len(), 1, "one question for the one check that was sized");
3871        let at = func[func[asked[0].1].args][1];
3872        let end = super::inst_of(&func, at);
3873        assert_eq!(func[end].opcode, Opcode::PtrAdd, "asked at the end of the first access");
3874        let from = func[func[end].args][0];
3875        let first = super::inst_of(&func, from);
3876        assert_eq!(
3877            func[first].opcode,
3878            Opcode::PtrAdd,
3879            "past a first access that is a displacement"
3880        );
3881        assert_eq!(func[func[first].args][0], func[blocks[0]].params[0], "off the array");
3882        sound(&func, &mut names);
3883    }
3884
3885    #[test]
3886    fn a_loop_with_a_call_in_it_that_might_free_is_left_alone() {
3887        // The extent is asked once and believed for the whole of the fast half, so anything that
3888        // could hand the storage back in the middle makes the answer stale and the fast half has
3889        // nothing left in it to notice.
3890        let (_, mut func, _) = calling(Flags::NONE);
3891        let stats = split_up(&mut func);
3892        assert!(!stats.changed());
3893        assert_eq!(stats.count(Kind::Missed, super::A_CALL_INSIDE), 1);
3894    }
3895
3896    #[test]
3897    fn a_loop_with_a_call_in_it_that_cannot_free_is_split() {
3898        // Whether the storage can be handed back is a question about the callee, and `crate::nofree`
3899        // answers it before the pipeline starts. This is the largest row of the census by a long way,
3900        // and it is also the row where this pass and hoisting come apart the furthest: hoisting
3901        // refuses a call whatever it does, because it needs the loop to reach the end of what its
3902        // count says, and this never claims that.
3903        let (mut names, mut func, _) = calling(Flags::NOFREE);
3904        let stats = split_up(&mut func);
3905        assert_eq!(stats.count(Kind::Optimized, SPLIT), 1);
3906        assert_eq!(all(&func, Opcode::CheckBounds).len(), 1, "the fast half lost its check");
3907        assert_eq!(all(&func, Opcode::Call).len(), 2, "and both halves kept the call");
3908        sound(&func, &mut names);
3909    }
3910
3911    /// The loop with a call added to its latch, carrying whatever the caller says about it.
3912    fn calling(flags: Flags) -> (Interner, Func, Vec<Block>) {
3913        let (mut names, mut func, blocks) = leaving();
3914        let more = blocks[2];
3915        let term = func.terminator(more).expect("the latch branches");
3916        let callee = names.intern("somewhere");
3917        let signature = func.add_signature(Signature::new());
3918        let call = Builder::new(&mut func, more).call(callee, signature, &[]);
3919        func[call].flags |= flags;
3920        func.remove_inst(call);
3921        func.insert_before(call, term);
3922        (names, func, blocks)
3923    }
3924
3925    #[test]
3926    fn a_check_whose_address_does_not_move_is_taken_too() {
3927        // One check on the array itself, every time round, alongside the one that walks. Hoisting
3928        // would rather have the still one, but this loop has a second way out, so hoisting will not
3929        // touch it and the check is still here to be taken. A step of zero is what carries it: the
3930        // access fits on the first iteration or on none of them, so it puts no limit on the loop.
3931        let (mut names, mut func, _) = standing(false);
3932        let stats = split_up(&mut func);
3933        assert_eq!(stats.count(Kind::Optimized, SPLIT), 1);
3934        assert_eq!(all(&func, Opcode::CapExtent).len(), 1, "and both were sized by one question");
3935        assert_eq!(
3936            all(&func, Opcode::CheckBounds).len(),
3937            2,
3938            "the fast half lost both checks and the slow half kept both"
3939        );
3940        sound(&func, &mut names);
3941    }
3942
3943    #[test]
3944    fn the_window_is_worked_out_without_dividing_by_anything() {
3945        // The reason it counts bytes rather than iterations. Iterations came out of a division by
3946        // the step, which is a step of zero on a check whose address does not move, and on x86 that
3947        // is a fault rather than a wrong number, so tamnd/rucc#818 was a program dying on the way
3948        // into a loop it was never going to fail in. It is also why the claim could not be a rule:
3949        // the divide and the multiply that went with it are what z3 would not finish on. The plan
3950        // here has one check of each kind, which is the shape fifty six of SQLite's two hundred and
3951        // sixty eight split loops have.
3952        let (mut names, mut func, _) = standing(false);
3953        split_up(&mut func);
3954        for opcode in [Opcode::SDiv, Opcode::UDiv] {
3955            assert!(all(&func, opcode).is_empty(), "{opcode:?} is left in the window arithmetic");
3956        }
3957        sound(&func, &mut names);
3958    }
3959
3960    #[test]
3961    fn two_checks_that_walk_by_the_same_amount_share_one_offset() {
3962        // One value round the loop rather than one per check, which is what the common shape wants:
3963        // a loop that reads one array and writes another walks both by the same step, so they are
3964        // at the same offset on every iteration and the window is the smaller of the two.
3965        let (mut names, mut func, blocks) = twinned();
3966        let stats = split_up(&mut func);
3967        assert_eq!(stats.count(Kind::Optimized, SPLIT), 1);
3968        assert_eq!(all(&func, Opcode::CapExtent).len(), 2, "both addresses were sized in front");
3969
3970        let head = blocks[1];
3971        let cfg = crate::Cfg::new(&func);
3972        let into = cfg.predecessors(head);
3973        assert_eq!(into.len(), 1, "the guard is the only way into the header now");
3974        let guard = into[0];
3975        assert_eq!(
3976            func[guard].params.len(),
3977            func[head].params.len() + 1,
3978            "one offset, not one per check"
3979        );
3980        sound(&func, &mut names);
3981    }
3982
3983    /// The loop with a second walking check in it, on the element after the one it reads.
3984    ///
3985    /// Two checks that move by the same amount, which is what a loop that reads one array and writes
3986    /// another is, and what a loop that looks one element ahead is. The window arithmetic keeps one
3987    /// offset for the pair of them rather than one each, and this is the fixture that says so.
3988    fn twinned() -> (Interner, Func, Vec<Block>) {
3989        let (names, mut func, blocks) = leaving();
3990        let (entry, head) = (blocks[0], blocks[1]);
3991        let array = func[entry].params[0];
3992        let counter = func[head].params[0];
3993        let term = func.terminator(head).expect("the header branches");
3994        let mut build = Builder::new(&mut func, head);
3995        let by = build.iconst(Type::int(64), WIDTH);
3996        let scaled = build.binary(Opcode::Mul, counter, by, Flags::NSW);
3997        let ahead = build.binary(Opcode::Add, scaled, by, Flags::NSW);
3998        let args = build.func().push_values(&[array, ahead]);
3999        let pointer = build.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
4000        check(&mut build, pointer);
4001        let made: Vec<Inst> = func.insts(head).skip_while(|&inst| inst != term).skip(1).collect();
4002        for inst in made {
4003            func.remove_inst(inst);
4004            func.insert_before(inst, term);
4005        }
4006        (names, func, blocks)
4007    }
4008
4009    #[test]
4010    fn a_loop_where_nothing_moves_picks_its_half_once_and_counts_nothing() {
4011        // Half the loops this takes on SQLite are like this, and they need none of the machinery the
4012        // rest of them do. Which half runs is decided by the answer to a question asked in the
4013        // preheader, the answer does not change while the loop runs, so the way into the loop is
4014        // where the two halves are chosen between and there is no counter and no guard block.
4015        let (mut names, mut func, blocks) = standing(true);
4016        let stats = split_up(&mut func);
4017        assert_eq!(stats.count(Kind::Optimized, SPLIT), 1);
4018        assert_eq!(all(&func, Opcode::CapExtent).len(), 1);
4019        assert_eq!(all(&func, Opcode::CheckBounds).len(), 1, "the fast half lost its check");
4020
4021        let (entry, head) = (blocks[0], blocks[1]);
4022        let term = func.terminator(entry).expect("the preheader still ends in something");
4023        assert_eq!(func[term].opcode, Opcode::BrIf, "the way in is the choice");
4024        assert_eq!(func[head].params.len(), 1, "and the header took on no counter");
4025        sound(&func, &mut names);
4026    }
4027
4028    /// The loop with a check on the array itself added to its header, every time round.
4029    ///
4030    /// Hoisting would rather have that check, and it takes the ones in loops it is willing to touch.
4031    /// This loop has a second way out, so hoisting will not touch it and the check is still here.
4032    /// `alone` takes the walking check away, which leaves a loop where nothing moves at all.
4033    fn standing(alone: bool) -> (Interner, Func, Vec<Block>) {
4034        let (names, mut func, blocks) = leaving();
4035        let (entry, head) = (blocks[0], blocks[1]);
4036        let array = func[entry].params[0];
4037        let walking = all(&func, Opcode::CheckBounds);
4038        let term = func.terminator(head).expect("the header branches");
4039        let mut build = Builder::new(&mut func, head);
4040        check(&mut build, array);
4041        let made: Vec<Inst> = func.insts(head).skip_while(|&inst| inst != term).skip(1).collect();
4042        for inst in made {
4043            func.remove_inst(inst);
4044            func.insert_before(inst, term);
4045        }
4046        if alone {
4047            for (_, inst) in walking {
4048                func.remove_inst(inst);
4049            }
4050        }
4051        (names, func, blocks)
4052    }
4053
4054    #[test]
4055    fn a_loop_whose_count_is_an_expression_is_split_on_what_that_expression_says() {
4056        // How far to look is worked out in the preheader rather than written down, out of a value
4057        // the loop does not change. Nothing here promises the arithmetic stays inside sixty four
4058        // bits, and it does not have to: a limit that wrapped is still answered with a true count
4059        // of the bytes that belong to the object.
4060        let (mut names, mut func, _) = counting();
4061        let stats = split_up(&mut func);
4062        assert_eq!(stats.count(Kind::Optimized, SPLIT), 1);
4063        assert_eq!(all(&func, Opcode::CapExtent).len(), 1);
4064        assert_eq!(all(&func, Opcode::CheckBounds).len(), 1, "the fast half lost its check");
4065        sound(&func, &mut names);
4066    }
4067
4068    #[test]
4069    fn a_loop_nobody_counted_is_split_and_asks_for_as_much_as_the_arithmetic_carries() {
4070        // The difference from hoisting in one test. Hoisting refuses this loop, because the count
4071        // is what it sizes the check it writes with and a count nobody settled is not one it may
4072        // write a check from. Nothing here rests on the count: it is spent on how far to ask the
4073        // runtime to look, and the runtime answers with a true count of the bytes that belong to the
4074        // object whatever it was asked for.
4075        //
4076        // tamnd/rucc#871. What the ask used to be worked out from was a guess of ten iterations, and
4077        // that was a bound on how far the runtime would walk rather than anything the guard wanted.
4078        // tamnd/rucc#861 stopped it walking, so a loop nobody counted asks for everything and gets
4079        // the extent of the object at the same price a small ask would have cost.
4080        let (mut names, mut func, _) = uncounted();
4081        let mut an = crate::machine::fixtures::analyses();
4082        Canon.run(&mut func, &mut an, &mut Fuel::unlimited());
4083        let refused = crate::hoist::Hoist.run(&mut func, &mut an, &mut Fuel::unlimited());
4084        assert!(!refused.changed(), "hoisting will not size a check from a count nobody settled");
4085
4086        let stats = Split.run(&mut func, &mut an, &mut Fuel::unlimited());
4087        assert_eq!(stats.count(Kind::Optimized, SPLIT), 1);
4088        assert_eq!(all(&func, Opcode::CheckBounds).len(), 1, "the fast half lost its check");
4089
4090        let asked = all(&func, Opcode::CapExtent);
4091        assert_eq!(asked.len(), 1, "one question for the one check that was sized");
4092        let want = func[func[asked[0].1].args][2];
4093        assert_eq!(number(&func, want), Some(i128::from(i64::MAX)), "and it asked for everything");
4094        sound(&func, &mut names);
4095    }
4096
4097    #[test]
4098    fn the_pass_stops_when_the_fuel_runs_out() {
4099        // What `-fopt-fuel` is for, and the reason every transformation here goes through the
4100        // counter rather than round it.
4101        let (_, mut func, _) = leaving();
4102        let mut an = crate::machine::fixtures::analyses();
4103        Canon.run(&mut func, &mut an, &mut Fuel::unlimited());
4104        let stats = Split.run(&mut func, &mut an, &mut Fuel::of(0));
4105        assert!(!stats.changed());
4106        assert_eq!(stats.count(Kind::Missed, super::NO_FUEL), 1);
4107    }
4108}