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, which is why this pass still wants a trip count: what it asks for
53//! is how many bytes the loop was going to read anyway, so the walk in the runtime is bounded by work
54//! the loop is already doing. A count that is too small costs iterations in the slow half and a count
55//! that is too large costs a slightly longer walk, and neither is a wrong answer, which is why the
56//! count is read from any exit that offers one rather than from an exit that runs every time.
57//!
58//! An address that does not move is the same expression with a step of zero, and its offset is zero
59//! on every iteration, so there is nothing to carry and the window question collapses into whether
60//! the one access fits. How far the runtime is asked to look is then just the bytes the access reads.
61//! Hoisting would rather have these, and it takes the ones in loops it is willing to touch. What is
62//! left over is the ones in loops it refused for one of its own reasons, a second way out or a call
63//! inside, and those come back here.
64//!
65//! # A walk that goes the other way
66//!
67//! A loop whose address goes down each time round is the same transformation looked at from the
68//! other end, and it is written here so that it is the same code. The offset the guard carries
69//! counts bytes moved from the first access rather than bytes added to it, so it still goes up by
70//! the step every time round and everything built on it is untouched: the guard block, the block
71//! parameter, the clamp and the test are the ones above, word for word.
72//!
73//! What changes is which end of the object the runtime is asked about. The window has to be room
74//! below the first access rather than above it, so the query is `cap_extent_back` and it is asked at
75//! `first + reach`, the end of the first access rather than its start. The answer is how many bytes
76//! ending there belong to whatever owns them, the window is that less the reach as before, and the
77//! access on iteration `delta` is the `reach` bytes ending at `first + reach - delta`. That is what
78//! `swept.down.sym.i64` in the rule table is written about, and it is asked instead of the ascending
79//! rule rather than derived from it.
80//!
81//! Anchoring at the end is what buys all of that. Anchoring at the lowest address the loop reaches
82//! would need a real trip count, since where the verified range starts would then depend on how far
83//! the loop goes, and this pass takes loops nobody counted and gives them a guess of ten. A guess is
84//! free for an ascending walk, where asking for too little only costs iterations in the slow half.
85//! It is unsound for a descending one, so the query goes the other way instead of the anchor.
86//!
87//! # A walk nobody could follow
88//!
89//! Everything above assumes the pass knows how far the address moves each time round. Most of what
90//! is left on real code is loops where it does not, and they are not exotic: a scanner that steps by
91//! one or by two depending on what it just read, a pointer that comes back round through a join
92//! because the body has a branch in it, a walk whose step is a width the caller passed in. None of
93//! those is an induction variable and scalar evolution has nothing to say about any of them, so they
94//! arrive here as an address that does something unknown.
95//!
96//! The way through is to stop asking how far the address moves and ask instead where it is. If the
97//! check's address is a fixed distance from a pointer the loop's header carries, then the guard can
98//! take where that pointer was on the way in from where it is now, and the difference is the
99//! displacement itself. It is exact rather than an upper bound on it, so the same window and the same
100//! rule apply word for word, and the guard tests it with the same unsigned comparison. It costs a
101//! subtract in the guard and saves the block parameter and the add at the latch, so it is not more
102//! code than counting.
103//!
104//! What has to be established is that the pointer is its own former self plus bytes. `p = p->next` is
105//! the case this is not allowed to take: the difference between two nodes of a list is a number, but
106//! it is not a displacement inside one object and the extent the preheader asked about says nothing
107//! about it. So the value the latch hands back has to reach the parameter through `ptr_add`s, block
108//! parameters inside the loop and `select`, and a load anywhere on the way is a refusal. `measured`
109//! is where that walk is, and it is syntactic because what it has to establish is.
110//!
111//! The trip count is the one thing a measured walk is worse at. How far the runtime is asked to look
112//! is a count times a step and there is no step, so the largest constant step seen on the way round
113//! stands in for it, and a walk with no constant step anywhere falls back on the bytes one access
114//! reads. Asking for too little costs iterations in the slow half and never an answer, which is the
115//! same trade the trip count guess is already making.
116//!
117//! # Why the fast half may drop a check
118//!
119//! `check_bounds` asks whether the bytes an access names lie inside one object. Every address in
120//! `[first, first + extent)` is inside the object that owns `first`, by what the query answers, and
121//! the window is exactly the offsets whose access stays inside that. So no check in the fast half
122//! could have failed.
123//!
124//! `check_live` asks whether anything owns the address right now, and the query answered that too,
125//! since a byte belonging to the owner of `first` is a byte with an owner. Right now is the catch,
126//! and it is why nothing that could free may be in the loop. A call in the body could free the object
127//! between the question and the iteration that reads it, and then the fast half would read freed
128//! storage with nothing to say so.
129//!
130//! That is a question about the callee rather than about calling, and [`crate::nofree`] answers it
131//! before the pipeline starts, so a call carrying [`rucc_ir::Flags::NOFREE`] is one the loop may
132//! keep. Hoisting refuses every call whatever it does, and the reason is not this one: it needs the
133//! loop to reach the end of what its count says, and a call that does not come back leaves it short.
134//! Splitting never claims the loop reaches the end, so a call that might not come back costs it
135//! nothing.
136//!
137//! Two answers of the query carry the weight and both are argued where the query is implemented. An
138//! address no watched region covers gets the whole limit back, so a loop over a local or a global
139//! splits into a fast half that runs the whole way, which is right because no check on such an
140//! address ever fires under this milestone. An address whose granule nobody owns gets zero, so the
141//! limit is zero, the fast half runs no iterations, and the check inside the slow half is what reports
142//! the dangling pointer, at the access rather than at the loop.
143//!
144//! # Which loops
145//!
146//! Innermost, one latch, a preheader, nothing in it that could free, and no value defined inside it
147//! that anything outside reads. Not a count, unlike hoisting, because the count is not something
148//! this rests on: it is spent on how far to ask the runtime to look, and the runtime answers with a
149//! true count of the bytes that belong to the object whatever it was asked for. A loop nobody
150//! counted gets the same guess everything else that has to guess about a loop gets, ten, which is
151//! GCC's `avg-loop-niter` and the number [`crate::scev::Estimate`] already hands out. The last is
152//! loop closed form, which [`crate::canon`] establishes, and it is checked rather than assumed
153//! because the copy would otherwise leave a reader outside the loop seeing whichever half happened
154//! to define the value.
155//!
156//! Canonicalization runs a long way in front of this, and `simplify-cfg` between the two undoes some
157//! of what it did, so on SQLite the closed form condition is what refuses 351 of the checks this
158//! would otherwise have taken out. Running canonicalization again in front of this gets 156 of them
159//! back and costs 17672 bytes of `.text`, which is a bad trade for eleven more checks, so the answer
160//! is for this to repair the exits of the one loop it is splitting rather than for the pipeline to
161//! repair every loop in the function. That is its own piece of work.
162//!
163//! Not every check in the loop has to be one this can size. A check whose address the analysis cannot
164//! follow simply stays in both halves, and the fast half is then a loop with fewer checks in it rather
165//! than none. That is worth having on its own and it is worth having because it is what a real loop
166//! looks like: one sweep the analysis reads and one index that came out of a table.
167//!
168//! # Which level
169//!
170//! `-O2` and `-O3`, alongside `crate::unroll` and for the same reason. The loop body is copied, so
171//! the function grows by about the size of the loop, and buying speed with code is what those levels
172//! are for and what `-Os` and `-Oz` are for declining.
173
174use std::collections::{HashMap, HashSet};
175
176use rucc_cost::heuristics;
177use rucc_ir::{
178    Block, BlockCall, Builder, Def, Extra, Flags, Func, Inst, InstData, IntPred, Opcode, Type,
179    Value,
180};
181
182use crate::canon;
183use crate::cfg::Cfg;
184use crate::copy;
185use crate::discharge::{Question, constant, operand_of, yes};
186use crate::dom::Dominators;
187use crate::loops::{LoopId, Loops};
188use crate::rules::safety;
189use crate::scev::{Anchor, Evolution, Plain, Reading, Scev};
190use crate::trip::{Around, counted, covered, inst_of};
191use crate::{Analyses, Fuel, Pass, Preserved, Stats};
192
193/// What is reported when a loop is split.
194const SPLIT: &str = "loop split, the iterations in front of the first one that could fail a check \
195                     run without them";
196
197/// What is reported when a loop had to be put back into closed form before it could be split.
198const CLOSED_HERE: &str = "loop put back into closed form, a value it defines is read after it and both halves define one";
199
200/// What is reported when the pass ran out of fuel with a loop it was about to split.
201const NO_FUEL: &str = "loop left alone, the pass ran out of fuel";
202
203/// What is reported for a loop with nowhere to work the limit out.
204const NO_PREHEADER: &str = "loop left alone, it has no block in front of it to put a check in";
205
206/// What is reported for a loop with a loop inside it.
207const A_LOOP_INSIDE: &str = "loop left alone, it has another loop inside it";
208
209/// What is reported for a loop with more than one way round.
210const MANY_LATCHES: &str = "loop left alone, it goes back to its header from more than one place";
211
212/// What is reported for a loop with a call in it that could free.
213const A_CALL_INSIDE: &str = "loop left alone, a call in it might free what the loop is reading";
214
215/// What is reported for a loop holding something that ends a lifetime outright.
216const ENDS_A_LIFETIME: &str = "loop left alone, something in it ends a lifetime";
217
218/// What is reported for a loop holding something the copier cannot copy.
219const NOT_COPYABLE: &str = "loop left alone, something in it carries a side table this cannot copy";
220
221/// What is reported for a loop whose values are read after it without going through a parameter.
222const ESCAPES: &str = "loop left alone, a value it defines is read outside it";
223
224/// What is reported for a loop whose two halves would be too much code.
225const TOO_BIG: &str = "loop left alone, the two halves would be more code than the limit allows";
226
227/// What is reported for a check whose address does not walk the loop.
228const NOT_A_SWEEP: &str = "check kept in both halves, its address does not walk the loop by a \
229                           constant";
230
231/// What is reported for a check whose address the analysis has nothing to say about.
232const NOT_FOLLOWED: &str = "check kept in both halves, what its address does round the loop is not \
233                            something the analysis follows";
234
235/// What is reported for a check whose step does not keep its alignment.
236const MISALIGNED: &str =
237    "check kept in both halves, its step is not a whole number of its alignment";
238
239/// What is reported for a check the guard would have to measure, whose access wants an alignment
240/// nothing here can promise.
241const MEASURED_ALIGN: &str = "check kept in both halves, the guard would measure how far its \
242                              address moved and that is no answer about its alignment";
243
244/// What is reported for a check that already covers a range the program worked out.
245const ALREADY_COMPUTED: &str =
246    "check kept in both halves, how many bytes it covers is a number only the program has";
247
248/// What is reported for a check the rule table will not say yes about.
249const NOT_PROVED: &str = "check kept in both halves, no rule in the safety namespace says an offset inside the window is \
250     an access inside the object";
251
252/// The pass.
253#[derive(Debug)]
254pub struct Split;
255
256impl Pass for Split {
257    fn name(&self) -> &'static str {
258        "split"
259    }
260
261    fn describe(&self) -> &'static str {
262        "a loop becomes a run of iterations with no checks in it and the rest of the loop with them"
263    }
264
265    fn preserves(&self) -> Preserved {
266        // Blocks appear and edges move, so nothing built on the graph stands.
267        Preserved::NONE
268    }
269
270    fn run(&self, func: &mut Func, an: &mut Analyses, fuel: &mut Fuel) -> Stats {
271        let mut stats = Stats::new();
272        if func.entry().is_none() {
273            return stats;
274        }
275        let cfg = an.cfg(func).clone();
276        let loops = an.loops(func).clone();
277        if loops.count() == 0 {
278            return stats;
279        }
280
281        // Worked out first and applied afterwards, because scalar evolution reads the function and
282        // the transformation writes it. Every plan is about an innermost loop and no two innermost
283        // loops share a block, so applying one leaves every other one's blocks where they were.
284        let mut plans = planned(func, &cfg, &loops, &mut stats);
285
286        // Closed form put back where it is missing, before anything is copied. The repair adds a
287        // block parameter and rewrites uses, so it moves no edge and creates no block, which is why
288        // the graph and the loop forest above are both still good after it. What it does move is
289        // which value a use inside another loop names, and a plan is a list of values, so a repair
290        // means the plans are worked out again rather than trusted. The stats go with them, or the
291        // first round's reasons would be counted twice.
292        let dom = an.dominators(func).clone();
293        let repairs = repaired(func, &cfg, &dom, &loops, &plans, fuel);
294        if repairs.made > 0 {
295            stats = Stats::new();
296            plans = planned(func, &cfg, &loops, &mut stats);
297            for _ in 0..repairs.worked {
298                stats.optimized(CLOSED_HERE);
299            }
300        }
301        plans.retain(|plan| {
302            if leaving(func, plan) {
303                stats.missed(ESCAPES);
304                return false;
305            }
306            true
307        });
308
309        let mut changed = false;
310        for plan in plans {
311            if !fuel.take() {
312                stats.missed(NO_FUEL);
313                continue;
314            }
315            apply(func, &plan);
316            stats.optimized(SPLIT);
317            changed = true;
318        }
319        if changed {
320            an.clear();
321        }
322        stats
323    }
324}
325
326/// How far past the first access an iteration reads, and who works that out.
327///
328/// Both are the same number and they differ in who does the arithmetic. `By` is a walk the analysis
329/// read, so the guard counts: it carries a byte offset of its own, starts it at zero on the way in
330/// and adds the step every time round. `Of` is a walk the analysis could not read, whose address is
331/// instead a fixed distance from a pointer the loop's header carries, so the guard measures: it
332/// takes where that pointer was on the way in from where it is now, and the difference is the
333/// displacement itself rather than a count standing in for it.
334///
335/// Measuring is what reaches a pointer that moves by an amount nobody wrote down, or by a different
336/// amount down each arm of a branch, or that comes back round through a join. None of those is an
337/// induction variable and there is nothing for scalar evolution to say about any of them, and
338/// between them they are 286 of the checks loop splitting still leaves in place on the SQLite
339/// amalgamation, at 44 sites. See tamnd/rucc#810.
340///
341/// The offset a measured walk produces is exact rather than an upper bound, which is what keeps this
342/// inside the rule table. `swept.sym.i64` is asked about it word for word as it is asked about a
343/// counted one, because `(p + k) - (first + k)` is `p - first` for whatever fixed `k` the check sits
344/// at, so the difference the guard computes is the displacement the rule is written about.
345#[derive(Clone, Copy, Debug, PartialEq, Eq)]
346enum Walk {
347    /// The address moves this many bytes every time round, either way. Zero is an address that does
348    /// not move, which is allowed and puts no limit on the loop. Negative is a walk from high to
349    /// low, and what changes for one is which end of the object the runtime is asked about rather
350    /// than anything about how the two halves are built.
351    By(i128),
352    /// The address is a fixed distance from a parameter of the loop's header, which the loop moves
353    /// on by an amount the analysis did not read.
354    Of {
355        /// Which of the header's parameters it is a distance from.
356        param: usize,
357        /// The largest step seen on the way round, which is a guess. It is spent on how far the
358        /// runtime is asked to look and on nothing else, so it is never the reason an answer is
359        /// wrong.
360        guess: i128,
361    },
362}
363
364impl Walk {
365    /// Whether the address stays where it is, which is a loop that needs no guard at all.
366    fn still(self) -> bool {
367        self == Self::By(0)
368    }
369
370    /// Whether the address walks from high to low, which asks the runtime about the other end of
371    /// the object.
372    ///
373    /// A measured walk never does. The guard's subtraction is read unsigned, so a pointer that went
374    /// below where it started is an enormous displacement and the guard hands the loop to the half
375    /// that kept its checks, which is the answer that end of the object would have given anyway.
376    fn down(self) -> bool {
377        matches!(self, Self::By(step) if step < 0)
378    }
379
380    /// How many bytes one iteration covers, for working out how far to ask the runtime to look.
381    ///
382    /// A magnitude, since how much ground a walk covers does not depend on which way it goes, and a
383    /// guess for a measured walk, where asking for too little costs iterations in the slow half and
384    /// asking for too much costs a slightly longer walk.
385    fn stride(self) -> i128 {
386        match self {
387            Self::By(step) => step.abs(),
388            Self::Of { guess, .. } => guess,
389        }
390    }
391
392    /// Which offset this walk shares with the others in the loop.
393    fn key(self) -> Key {
394        match self {
395            Self::By(step) => Key::Every(step.abs()),
396            Self::Of { param, .. } => Key::From(param),
397        }
398    }
399}
400
401/// Which checks are at the same offset from their own first access on every iteration, and so can
402/// share one offset in the guard and the smaller of their windows.
403#[derive(Clone, Copy, Debug, PartialEq, Eq)]
404enum Key {
405    /// They walk by the same number of bytes each time round, whichever way each of them goes.
406    Every(i128),
407    /// They are measured from the same parameter of the loop's header. Two checks a fixed distance
408    /// from one pointer are the same distance apart on every iteration, whatever the pointer does,
409    /// so one subtraction answers for both.
410    From(usize),
411}
412
413/// One check the fast half will not need, and the walk that says so.
414#[derive(Debug)]
415struct Sweep {
416    /// The check itself, which is removed from the fast half and kept in the copy.
417    check: Inst,
418    /// Where the first iteration's address is computed from. An address rather than a value when
419    /// it is a global, since nothing outside the loop computes one of those. See [`Anchor`].
420    base: Anchor,
421    /// How far past that value the first iteration reads, in bytes. Usually a number, and a value
422    /// and a scale beside it when the loop started its counter at something it was handed. See
423    /// `spare` for how it is built and #810 for what it is worth.
424    apart: Plain,
425    /// What the address does round the loop, and so what the guard has to work out.
426    walk: Walk,
427    /// How many bytes one access covers.
428    reach: i128,
429}
430
431/// One loop to split, worked out before anything is written.
432#[derive(Debug)]
433struct Plan {
434    /// The loop itself, which is read again when its closed form has to be repaired.
435    id: LoopId,
436    /// Where the limit is worked out.
437    preheader: Block,
438    /// The block the guard takes over from.
439    header: Block,
440    /// The block the back edge leaves from, which is where the iteration count goes up.
441    latch: Block,
442    /// Everything that is copied, which is the whole loop.
443    body: Vec<Block>,
444    /// How many times the loop goes round, which is what the runtime is asked to look no further
445    /// than.
446    around: Around,
447    /// The checks the fast half will not need, which is never empty in a plan.
448    sweeps: Vec<Sweep>,
449}
450
451/// Plans a loop, or counts what stopped it.
452///
453/// Nothing is reported for a loop with no check in it, because a loop that does no memory access is
454/// not a missed opportunity and a report for every one of them would bury the loops that are.
455fn sweep(
456    func: &Func,
457    cfg: &Cfg,
458    loops: &Loops,
459    scev: &mut Scev<'_>,
460    id: LoopId,
461    plans: &mut Vec<Plan>,
462    stats: &mut Stats,
463) {
464    let body = loops.blocks(id).to_vec();
465    let checks: Vec<Inst> = body
466        .iter()
467        .flat_map(|&block| func.insts(block).collect::<Vec<Inst>>())
468        .filter(|&inst| matches!(func[inst].opcode, Opcode::CheckBounds | Opcode::CheckLive))
469        .collect();
470    if checks.is_empty() {
471        return;
472    }
473
474    let (preheader, latch) = match shaped(func, cfg, loops, id, &body) {
475        Ok(shape) => shape,
476        Err(why) => {
477            stats.missed(why);
478            return;
479        }
480    };
481    // Not a refusal when there is no count, unlike in hoisting, because the count is not something
482    // this rests on. It is spent on how far to ask the runtime to look, and the runtime answers with
483    // a true count of the bytes that belong to the object whatever it was asked for. A guess that is
484    // too small costs iterations in the half that keeps its checks and a guess that is too large
485    // costs a slightly longer walk, so a loop nobody counted gets the same guess everything else
486    // that has to guess about a loop gets.
487    let around =
488        counted(scev, id).unwrap_or(Around::Number(i128::from(crate::scev::ASSUMED_ITERATIONS)));
489
490    // What the preheader hands the header, which is where a measured walk starts from. Read once,
491    // because every check in the loop that the guard has to measure is measured from one of these.
492    let term = func.terminator(preheader).expect("a preheader ends in a jump to the header");
493    let entering = copy::edge_args(func, term, loops.header(id));
494
495    let mut sweeps = Vec::new();
496    for check in checks {
497        match walked(func, cfg, loops, scev, id, latch, &entering, check) {
498            Ok(sweep) => sweeps.push(sweep),
499            Err(why) => stats.missed(why),
500        }
501    }
502    if sweeps.is_empty() {
503        return;
504    }
505    plans.push(Plan { id, preheader, header: loops.header(id), latch, body, around, sweeps });
506}
507
508/// The preheader and the latch of a loop this pass may copy, or why there is not one.
509///
510/// The conditions are the module comment's. The one worth restating is freeing, because it is the
511/// only one that is about what the fast half is allowed to leave out rather than about whether the
512/// copy can be made at all: the extent is asked once before the loop and believed for the whole of
513/// the fast half, so anything that could hand the storage back in the middle would make the answer
514/// stale, and the fast half has nothing left in it to notice.
515///
516/// Which is a question about the callee and not about calling, so it is asked of the callee.
517/// [`crate::nofree`] settles it before the pipeline starts and writes the answer onto the call site,
518/// and a call carrying it reaches nothing that ends a lifetime. Note that this is a weaker
519/// requirement than [`crate::hoist`]'s, which refuses every call whatever it does, because hoisting
520/// needs the loop to reach the end of what its count says and a call that does not come back leaves
521/// it short. Splitting never claims that, so coming back is not something it needs.
522fn shaped(
523    func: &Func,
524    cfg: &Cfg,
525    loops: &Loops,
526    id: LoopId,
527    body: &[Block],
528) -> Result<(Block, Block), &'static str> {
529    let Some(preheader) = loops.preheader(cfg, id) else {
530        return Err(NO_PREHEADER);
531    };
532    let [latch] = loops.latches(id) else {
533        return Err(MANY_LATCHES);
534    };
535    for &block in body {
536        if loops.innermost(block) != Some(id) {
537            return Err(A_LOOP_INSIDE);
538        }
539        for inst in func.insts(block) {
540            match func[inst].opcode {
541                Opcode::Call | Opcode::CallIndirect | Opcode::TailCall
542                    if !func[inst].flags.contains(Flags::NOFREE) =>
543                {
544                    return Err(A_CALL_INSIDE);
545                }
546                // Assembly could do anything and the two meta instructions end a lifetime by
547                // definition, which is the same answer `crate::nofree` gives for all three.
548                Opcode::InlineAsm | Opcode::MetaEnd | Opcode::MetaTransfer => {
549                    return Err(ENDS_A_LIFETIME);
550                }
551                _ => {}
552            }
553            if !copy::copyable(func, inst) {
554                return Err(NOT_COPYABLE);
555            }
556        }
557    }
558    let size = body.iter().map(|&block| func.insts(block).count()).sum::<usize>();
559    if size > heuristics::SPLIT_MAX_INSNS as usize {
560        return Err(TOO_BIG);
561    }
562    Ok((preheader, *latch))
563}
564
565/// Every loop in the function that is worth copying, and why each of the others is not.
566fn planned(func: &Func, cfg: &Cfg, loops: &Loops, stats: &mut Stats) -> Vec<Plan> {
567    let mut plans = Vec::new();
568    let mut scev = Scev::new(func, cfg, loops);
569    for id in loops.all() {
570        sweep(func, cfg, loops, &mut scev, id, &mut plans, stats);
571    }
572    plans
573}
574
575/// How many loops the closed form repair touched, and how many of those it finished.
576///
577/// Two numbers rather than one because they answer different questions. Anything touched at all is
578/// why the plans have to be worked out again, and only the ones it finished are loops that can now
579/// be copied and so are what gets reported.
580struct Repairs {
581    /// Loops the repair wrote something into.
582    made: usize,
583    /// Loops that are in closed form afterwards.
584    worked: usize,
585}
586
587/// Puts the loops that need it back into closed form, before anything is copied.
588///
589/// [`crate::canon`] establishes closed form a long way in front of this pass and `simplify-cfg`
590/// between the two undoes some of what it did. Running the whole of canonicalization again was
591/// measured and it costs 17672 bytes of `.text` on the SQLite amalgamation, because it repairs every
592/// loop in the function rather than the ones about to be copied. This repairs those, which costs
593/// nothing on a function with no loop to split.
594///
595/// Not every loop can be repaired this way. A value read past a join that no single exit dominates
596/// needs a parameter at the join as well as at each exit, and the repair adds one at the exits only,
597/// so the count of what worked is a second look rather than an assumption that the first one did.
598fn repaired(
599    func: &mut Func,
600    cfg: &Cfg,
601    dom: &Dominators,
602    loops: &Loops,
603    plans: &[Plan],
604    fuel: &mut Fuel,
605) -> Repairs {
606    let mut repairs = Repairs { made: 0, worked: 0 };
607    for plan in plans {
608        if !leaving(func, plan) {
609            continue;
610        }
611        let mut wrote = false;
612        while let Some(job) = canon::leaked(func, cfg, dom, loops, plan.id) {
613            if !fuel.take() {
614                break;
615            }
616            canon::close(func, &job);
617            wrote = true;
618        }
619        if !wrote {
620            continue;
621        }
622        repairs.made += 1;
623        if !leaving(func, plan) {
624            repairs.worked += 1;
625        }
626    }
627    repairs
628}
629
630/// Whether anything after this loop reads a value its body defines.
631fn leaving(func: &Func, plan: &Plan) -> bool {
632    let inside: HashSet<Block> = plan.body.iter().copied().collect();
633    escapes(func, &plan.body, &inside)
634}
635
636/// Whether anything outside the loop reads a value defined inside it.
637///
638/// Where there is one, the two halves would leave it reading whichever of them happened to define
639/// it. Closed form is what makes it not one: the use names a parameter of the block the loop leaves
640/// to, and each half fills that parameter in on its own way out.
641fn escapes(func: &Func, body: &[Block], inside: &HashSet<Block>) -> bool {
642    let mut defined: HashSet<Value> = HashSet::new();
643    for &block in body {
644        defined.extend(func[block].params.iter().copied());
645        for inst in func.insts(block) {
646            defined.extend(func[inst].results());
647        }
648    }
649    for block in func.blocks() {
650        if inside.contains(&block) {
651            continue;
652        }
653        for inst in func.insts(block) {
654            if func[func[inst].args].iter().any(|value| defined.contains(value)) {
655                return true;
656            }
657            for call in func.successors(inst) {
658                if func[call.args].iter().any(|value| defined.contains(value)) {
659                    return true;
660                }
661            }
662        }
663    }
664    false
665}
666
667/// What one check's address does round the loop, or why the pass cannot say.
668///
669/// The counted walk is asked for first and the measured one takes what it could not. That order is
670/// the cheaper answer first: a counted walk costs the guard an add on a value it already carries,
671/// and a measured one costs it a subtraction of two pointers every time round. It is also the more
672/// exact answer first, since a counted walk knows the step and so knows the alignment, which a
673/// measured one never does.
674#[allow(clippy::too_many_arguments)]
675fn walked(
676    func: &Func,
677    cfg: &Cfg,
678    loops: &Loops,
679    scev: &mut Scev<'_>,
680    id: LoopId,
681    latch: Block,
682    entering: &[Value],
683    check: Inst,
684) -> Result<Sweep, &'static str> {
685    let args = &func[func[check].args];
686    // A check that already carries its own extent is one hoisting put somewhere, and how many bytes
687    // it covers is not a number this pass can divide by a step.
688    if args.len() > 2 {
689        return Err(ALREADY_COMPUTED);
690    }
691    let (Some(&capability), Some(&pointer)) = (args.first(), args.get(1)) else {
692        return Err(NOT_A_SWEEP);
693    };
694    if operand_of(func, capability, Opcode::CapOf, 0) != Some(pointer) {
695        return Err(NOT_A_SWEEP);
696    }
697    // A liveness check reads no bytes, so the window it needs is the one byte its address is in.
698    // A bounds check carries how many it reads in its payload.
699    let (reach, align) = match func[check].extra {
700        Extra::Mem(held) => (i128::from(func[held].size), i128::from(func[held].align)),
701        _ => (1, 1),
702    };
703
704    let (base, apart, walk) = match following(func, scev, id, pointer) {
705        Ok((base, apart, step)) => (base, apart, Walk::By(step)),
706        // The reason the counted walk gave is what gets reported when the measured one cannot take
707        // the check either, so that the census keeps saying what the analysis made of the address
708        // rather than collapsing every one of them into this fallback missing.
709        Err(why) => match measured(func, cfg, loops, id, latch, entering, pointer, reach) {
710            Some(found) => found,
711            None => return Err(why),
712        },
713    };
714    match walk {
715        // An address a whole number of steps along from an aligned one is aligned, which is the
716        // whole of what this condition is. It is [`crate::hoist`]'s and it is here for the reason it
717        // is there, that a bounds check carries an alignment as well as a byte count.
718        Walk::By(step) if step != 0 && step % align != 0 => return Err(MISALIGNED),
719        // A measured walk moves by an amount nobody wrote down, so there is no such number to divide
720        // and nothing here can say the second access is as aligned as the first. Refusing on the
721        // access wanting any alignment at all is the conservative reading, and it is its own line in
722        // the census so that what it costs is a number rather than a guess.
723        Walk::Of { .. } if align > 1 => return Err(MEASURED_ALIGN),
724        _ => {}
725    }
726    // Whether an offset inside the window means an access inside the object, which is what dropping
727    // this check rests on and is not something this file decides. The direction goes with it,
728    // because a walk from high to low is a different claim about addresses and has its own rule.
729    if !windowed(reach, walk.down()) {
730        return Err(NOT_PROVED);
731    }
732    Ok(Sweep { check, base, apart, walk, reach })
733}
734
735/// The walk scalar evolution read, as a base to measure from and a step in bytes.
736///
737/// An address that does not move is a sweep with a step of zero, and the arithmetic downstream takes
738/// it without a special case anywhere. Hoisting would rather have these, but hoisting only gets the
739/// ones in loops it is willing to touch at all, and a loop it refused for one of its own reasons
740/// leaves the check where it is. Splitting is willing to touch more loops, so the same check comes
741/// back here and there is no reason to hand it back.
742fn following(
743    func: &Func,
744    scev: &mut Scev<'_>,
745    id: LoopId,
746    pointer: Value,
747) -> Result<(Anchor, Plain, i128), &'static str> {
748    let (start, step) = match scev.evolution(id, pointer) {
749        Evolution::Affine(chrec) => {
750            let Some(step) = chrec.step.as_number() else {
751                return Err(NOT_A_SWEEP);
752            };
753            (chrec.base, step)
754        }
755        Evolution::Invariant(base) => (base, 0),
756        _ => return Err(NOT_FOLLOWED),
757    };
758    // Scale one because the base is an address. Anything else is a multiple of a pointer, which is
759    // not a thing the loop computed, so it is a shape this reads rather than a case to handle.
760    //
761    // The second arm is `a + 8 * start`, an address the loop reached before it began, which is what
762    // a counter the caller handed in looks like once the front end has multiplied the element size
763    // through it. The pointer is the side the whole thing is measured from and the index is what is
764    // scaled beside it, so anything else with two values in it is refused here rather than turned
765    // into an address off whichever value came first.
766    match (start.plain(), start.on()) {
767        (Some(at @ Plain { value: Some(base), read: None, scale: 1, .. }), _) => Ok((
768            Anchor::Value(base),
769            Plain { value: None, read: None, scale: 0, offset: at.offset },
770            step,
771        )),
772        (_, Some((base, apart))) if walks(func, base, apart) => Ok((base, apart, step)),
773        _ => Err(NOT_A_SWEEP),
774    }
775}
776
777/// The walk the guard can measure, for an address that is a fixed distance from a pointer the loop's
778/// header carries.
779///
780/// A syntactic walk rather than an analysis, because what it has to establish is syntactic. The
781/// address is peeled of the constant `ptr_add`s on the front of it and what is under them has to be
782/// a parameter of the header, which is a pointer the loop hands itself round the back edge. The
783/// first access is then that parameter's value on the way in, `k` bytes along, and the displacement
784/// on any later iteration is the parameter's value now less the value on the way in. That is a
785/// subtraction the guard can do, whatever the loop did to the pointer in between.
786///
787/// # What the back edge has to look like
788///
789/// The value the latch hands the parameter has to be that same parameter moved: through `ptr_add`s,
790/// through parameters of blocks inside the loop, and through a `select`, which is what a branch that
791/// moves the pointer differently down each arm turns into. Anything else is refused.
792///
793/// The refusal is the point of the walk. A list is `p = p->next`, where the value on the back edge is
794/// a load, and measuring how far that got from where it started measures nothing: the next node is
795/// wherever it happens to be, not inside the object the first one is in, and the extent the preheader
796/// asked about says nothing about it. Requiring the pointer to be its own former self plus bytes is
797/// what makes the difference a displacement inside one object rather than the distance between two
798/// unrelated addresses.
799///
800/// The largest constant step seen on the way is carried out as a guess. It is spent on how far the
801/// runtime is asked to look and nowhere else, so a walk with no constant step anywhere in it falls
802/// back on the bytes one access reads and is a smaller ask rather than a wrong one.
803#[allow(clippy::too_many_arguments)]
804fn measured(
805    func: &Func,
806    cfg: &Cfg,
807    loops: &Loops,
808    id: LoopId,
809    latch: Block,
810    entering: &[Value],
811    pointer: Value,
812    reach: i128,
813) -> Option<(Anchor, Plain, Walk)> {
814    let header = loops.header(id);
815    let (at, offset) = peeled(func, pointer);
816    let Def::Param { block, index } = func[at].def else { return None };
817    if block != header || !func[at].ty.is_ptr() {
818        return None;
819    }
820    let param = index as usize;
821    let &first = entering.get(param)?;
822    let term = func.terminator(latch)?;
823    let round = copy::edge_args(func, term, header);
824    let &next = round.get(param)?;
825    let mut seen = HashSet::new();
826    let far = moving(func, cfg, loops, id, at, next, &mut seen)?;
827    let guess = if far == 0 { reach } else { far };
828    let apart = Plain { value: None, read: None, scale: 0, offset };
829    Some((Anchor::Value(first), apart, Walk::Of { param, guess }))
830}
831
832/// A pointer with the constant `ptr_add`s on the front of it taken off, and how many bytes they came
833/// to between them.
834fn peeled(func: &Func, pointer: Value) -> (Value, i128) {
835    let mut at = pointer;
836    let mut offset = 0;
837    while let Some(by) = operand_of(func, at, Opcode::PtrAdd, 1) {
838        let (Some(step), Some(of)) = (constant(func, by), operand_of(func, at, Opcode::PtrAdd, 0))
839        else {
840            break;
841        };
842        offset += step;
843        at = of;
844    }
845    (at, offset)
846}
847
848/// Whether a value is a header parameter moved by some number of bytes, and the largest step in it.
849///
850/// The conditions are [`measured`]'s and the walk is the obvious one. What is worth saying is what
851/// each answer means. `None` is a value that is not the parameter moved, which is a refusal.
852/// `Some(0)` is the parameter moved by amounts none of which is written down, which is allowed and
853/// leaves the caller to fall back on the bytes an access reads. Anything else is the largest step
854/// this found, which is the best guess available at how far the loop is going to get.
855fn moving(
856    func: &Func,
857    cfg: &Cfg,
858    loops: &Loops,
859    id: LoopId,
860    param: Value,
861    value: Value,
862    seen: &mut HashSet<Value>,
863) -> Option<i128> {
864    if value == param {
865        return Some(0);
866    }
867    // A value already on the way back is one whose steps are counted, and coming back round to it is
868    // what a walk through a join looks like. Zero rather than a refusal, because this path adds no
869    // step that has not been seen.
870    if !seen.insert(value) {
871        return Some(0);
872    }
873    let at = match func[value].def {
874        Def::Result { inst, .. } => func.block_of(inst)?,
875        Def::Param { block, .. } => block,
876    };
877    // Anything defined outside the loop is something the loop was handed rather than the parameter
878    // moved, and it is where the walk stops as well as what it refuses.
879    if loops.innermost(at) != Some(id) {
880        return None;
881    }
882    match func[value].def {
883        Def::Result { inst, .. } => {
884            let args = &func[func[inst].args];
885            match func[inst].opcode {
886                Opcode::PtrAdd => {
887                    let (&of, &by) = (args.first()?, args.get(1)?);
888                    let far = moving(func, cfg, loops, id, param, of, seen)?;
889                    Some(far.max(constant(func, by).map_or(0, i128::abs)))
890                }
891                // Both arms have to be the parameter moved, since either of them may be the one
892                // taken. The condition is not looked at, because how the loop chose is not something
893                // the displacement depends on.
894                Opcode::Select => {
895                    let (&one, &two) = (args.get(1)?, args.get(2)?);
896                    let one = moving(func, cfg, loops, id, param, one, seen)?;
897                    let two = moving(func, cfg, loops, id, param, two, seen)?;
898                    Some(one.max(two))
899                }
900                _ => None,
901            }
902        }
903        // A parameter of a block inside the loop is a join, and every way into it has to be the
904        // parameter moved. The header is not one of them: its other parameters are other values and
905        // the parameter itself was the base case above.
906        Def::Param { block, index } => {
907            if block == loops.header(id) {
908                return None;
909            }
910            let mut far = 0;
911            for &pred in cfg.predecessors(block) {
912                let term = func.terminator(pred)?;
913                let args = copy::edge_args(func, term, block);
914                let &came = args.get(index as usize)?;
915                far = far.max(moving(func, cfg, loops, id, param, came, seen)?);
916            }
917            Some(far)
918        }
919    }
920}
921
922/// Whether a pointer and a byte displacement beside it are the two the address is really built out
923/// of, rather than two values an expression happened to end up holding.
924///
925/// The displacement has to end up as wide as the arithmetic, because what is built from it here is
926/// a `ptr_add` in a preheader. It gets there one of three ways: it is a plain number, or it is
927/// already sixty four bits, or it is narrower and the invariant says which extension it is read
928/// through, which is what an index the caller handed in looks like in C, where the index is an
929/// `int`.
930fn walks(func: &Func, base: Anchor, apart: Plain) -> bool {
931    let word = Type::int(64);
932    if !base.value().is_none_or(|base| func[base].ty.is_ptr()) {
933        return false;
934    }
935    // A global with nothing but a number beside it, which is what a walk over a file scope array
936    // from a fixed place in it looks like. A number is as wide as it needs to be.
937    let Some(value) = apart.value.filter(|_| apart.scale != 0) else { return true };
938    match apart.read {
939        None => func[value].ty == word,
940        Some(read) => read.to == word && func[value].ty.is_int() && func[value].ty.bits() < 64,
941    }
942}
943
944/// Makes the two halves and the block that chooses between them.
945///
946/// The order matters in two places. The copy is made before anything is rewired, so the copy's back
947/// edge is remapped to the copy's own header rather than to a guard that did not exist yet. The
948/// checks come out of the fast half last, so the copy still has them.
949fn apply(func: &mut Func, plan: &Plan) {
950    // The slow half, which is the loop as it stands, under a substitution that renames everything it
951    // defines. Nothing is seeded, so its header gets parameters of its own, which is what a copy
952    // reached from a block that also reaches the original needs.
953    let mut renamed: HashMap<Value, Value> = HashMap::new();
954    let copies = copy::blocks(func, &plan.body, &mut renamed);
955    let slow = copies[&plan.header];
956
957    let Choice { ok, windows } = limited(func, plan);
958
959    // Nothing in the loop moves, so which half runs is settled in the preheader and settled for
960    // good. There is no guard block and nothing carried round: the way into the loop is the choice.
961    if windows.is_empty() {
962        let term = func.terminator(plan.preheader).expect("a preheader ends in a jump");
963        let args = copy::edge_args(func, term, plan.header);
964        func.remove_inst(term);
965        Builder::new(func, plan.preheader).br_if(ok, plan.header, &args, slow, &args);
966        take(func, plan);
967        return;
968    }
969
970    // The guard, which takes over the header's place: the preheader arrives here, the back edge
971    // comes back to here, and the header is reached from here and nowhere else. Its first
972    // parameters are offsets of its own, one per distinct step, because where the loop's own
973    // pointers are is not something this pass has to find and a loop with several ways out may
974    // have nothing that walks in step with what its checks are about.
975    //
976    // A measured offset gets no parameter and nothing carried round. Where its pointer is now is
977    // already among the parameters below, since it is one the header carries, and the guard works
978    // the displacement out from that.
979    let word = Type::int(64);
980    let counting: Vec<i128> =
981        windows.iter().filter(|window| window.from.is_none()).map(|w| stepped(w.key)).collect();
982    let types: Vec<Type> = func[plan.header].params.iter().map(|&param| func[param].ty).collect();
983    let guard = func.create_block();
984    let offsets: Vec<Value> = counting.iter().map(|_| func.append_param(guard, word)).collect();
985    let carried: Vec<Value> = types.iter().map(|&ty| func.append_param(guard, ty)).collect();
986
987    // Unsigned, because the window is a byte count and so is the offset, and because unsigned is
988    // what the rule the removal rests on is written in. That is what makes the subtraction below
989    // safe as well: a pointer that went under where it started comes out as a displacement no
990    // window is ever going to hold, so the loop goes to the half that kept its checks.
991    let mut build = Builder::new(func, guard);
992    let mut inside: Option<Value> = None;
993    let mut counted = 0;
994    for window in &windows {
995        let offset = match window.from {
996            None => {
997                let offset = offsets[counted];
998                counted += 1;
999                offset
1000            }
1001            Some(from) => {
1002                let Key::From(param) = window.key else {
1003                    unreachable!("only a measured window holds where its pointer began")
1004                };
1005                let now = build.unary(Opcode::PtrToInt, carried[param], word);
1006                build.binary(Opcode::Sub, now, from, Flags::NONE)
1007            }
1008        };
1009        let under = build.icmp(IntPred::Ule, offset, window.bound);
1010        inside = Some(match inside {
1011            None => under,
1012            Some(so_far) => build.binary(Opcode::And, so_far, under, Flags::NONE),
1013        });
1014    }
1015    let inside = inside.expect("a plan with a window has at least one of them");
1016    build.br_if(inside, plan.header, &carried, slow, &carried);
1017
1018    // The way in, which tests whether the fast half may run at all and starts every offset at the
1019    // first access. A loop nothing fits in never reaches the guard.
1020    let term = func.terminator(plan.preheader).expect("a preheader ends in a jump to the header");
1021    let args = copy::edge_args(func, term, plan.header);
1022    func.remove_inst(term);
1023    let mut build = Builder::new(func, plan.preheader);
1024    let zero = build.iconst(word, 0);
1025    let mut into: Vec<Value> = offsets.iter().map(|_| zero).collect();
1026    into.extend_from_slice(&args);
1027    build.br_if(ok, guard, &into, slow, &args);
1028
1029    // The way round, which walks each counted offset on by its step. The offsets are the guard's
1030    // parameters and the guard dominates every block in the fast half, so the latch may read them.
1031    // `nuw` rather than `nsw` because [`bounded`] held the window short of where this could wrap,
1032    // and it held it there in unsigned terms. A measured offset has nothing here: the guard reads
1033    // the pointer the loop already hands round.
1034    let term = func.terminator(plan.latch).expect("a latch ends in a branch back to the header");
1035    let mut build = Builder::new(func, plan.latch);
1036    let mut made = Vec::new();
1037    let mut next = Vec::new();
1038    for (&offset, &step) in offsets.iter().zip(&counting) {
1039        let by = build.iconst(word, step);
1040        made.push(by);
1041        let walked = build.binary(Opcode::Add, offset, by, Flags::NUW);
1042        made.push(walked);
1043        next.push(walked);
1044    }
1045    for value in made {
1046        let inst = inst_of(func, value);
1047        func.remove_inst(inst);
1048        func.insert_before(inst, term);
1049    }
1050    route(func, term, plan.header, guard, &next);
1051    take(func, plan);
1052}
1053
1054/// Takes the checks the fast half does not need out of it.
1055///
1056/// The `cap_of` each one was reading is left where it is, for `dce` after this pass to take away,
1057/// which is the arrangement [`crate::hoist`] and [`crate::discharge`] are both in.
1058fn take(func: &mut Func, plan: &Plan) {
1059    for sweep in &plan.sweeps {
1060        func.remove_inst(sweep.check);
1061    }
1062}
1063
1064/// Sends every edge this terminator has to `from` to `to` instead, with more arguments in front.
1065fn route(func: &mut Func, term: Inst, from: Block, to: Block, first: &[Value]) {
1066    for at in func.target_list(term).iter() {
1067        let call = func[at];
1068        if call.block != from {
1069            continue;
1070        }
1071        let mut args = first.to_vec();
1072        args.extend_from_slice(&func[call.args]);
1073        let args = func.push_values(&args);
1074        func.set_block_call(at, BlockCall { block: to, args });
1075    }
1076}
1077
1078/// One offset the guard works out every time round, and how far it may get.
1079struct Window {
1080    /// Which checks share it, which for a counted offset is how far the address moves each time
1081    /// round. That is a magnitude, because the offset counts bytes from the first access and counts
1082    /// them the same way whichever direction the address walks.
1083    key: Key,
1084    /// The highest offset an access may start at and still be inside what the extent covers.
1085    bound: Value,
1086    /// Where the pointer was on the way into the loop, as an integer, for an offset the guard
1087    /// measures. `None` for one it counts, which starts at zero and needs nothing to measure from.
1088    from: Option<Value>,
1089}
1090
1091/// How the two halves are chosen between, which depends on whether any address in the loop moves.
1092struct Choice {
1093    /// Whether every check in the loop fits at all, which the preheader tests before it enters the
1094    /// fast half. It is false for a dangling pointer or an object smaller than the thing being read
1095    /// out of it, and then the fast half runs no iterations and the check in the slow half reports
1096    /// the fault at the access rather than at the loop.
1097    ok: Value,
1098    /// One per distinct step, and empty when no address in the loop moves. A loop like that needs
1099    /// no guard block and nothing carried round it, because `ok` is the whole answer and it does
1100    /// not change while the loop runs.
1101    windows: Vec<Window>,
1102}
1103
1104/// Builds what the preheader has to work out before either half can run.
1105///
1106/// One `cap_extent` per check and what it leaves room for, all of it in the preheader in front of
1107/// the jump into the loop. A builder appends to the end of a block, which in a block that already
1108/// has its terminator is after it, so everything is built first and then moved in front of the
1109/// terminator in the order it was built.
1110///
1111/// # Why the window is bytes and not iterations
1112///
1113/// This used to work out how many iterations a check allows, which is `(extent - reach) / step + 1`
1114/// clamped at zero, and count iterations against it. The claim that has to hold for the fast half
1115/// to be allowed to drop its checks was then that `i * step + reach <= extent` for every `i` below
1116/// that limit, which has a symbolic multiply and a symbolic divide in it at sixty four bits, and
1117/// z3 does not finish on it in two and a half minutes in any of three formulations. So the whole
1118/// transformation sat outside the rule table that `spec/safe-memory/07-check-elimination.md`
1119/// section 7.7 asks every elimination to be inside, and it sat there for a solver reason rather
1120/// than a design one, which is the worst kind.
1121///
1122/// Counting bytes instead of iterations takes the arithmetic out. The offset the loop is at moves
1123/// by `step` each time round exactly as the address does, the window is `extent - reach`, and the
1124/// claim is that an offset at or below that plus the reach is inside the extent. No multiply and no
1125/// divide, and it is the claim `swept.sym.i64` in `crates/rucc-opt/rules/safety.rules` already
1126/// makes, which [`windowed`] asks. The pass earns that rule's hypotheses rather than assuming them:
1127/// `ok` is where `extent` is held to be at least `reach`, so the window cannot have wrapped, and
1128/// [`bounded`] is where the offset is held short of where adding one more step would.
1129///
1130/// It is also less code. A loop with one step in it loses a divide from its preheader and carries
1131/// the same one value round that it did before.
1132///
1133/// # Why one window per step and not one per check
1134///
1135/// Two checks that walk by the same amount are at the same offset on every iteration, so they can
1136/// share the offset and the smaller of their two windows. On SQLite 127 of the 268 loops this
1137/// splits have one distinct step and five have two, so this is one value round the loop almost
1138/// always and two occasionally.
1139fn limited(func: &mut Func, plan: &Plan) -> Choice {
1140    let word = Type::int(64);
1141    let term = func.terminator(plan.preheader).expect("a preheader ends in a jump to the header");
1142    // What the preheader hands the header, which is where a measured offset is measured from. Read
1143    // before the builder exists, because reading it borrows the function.
1144    let entering = copy::edge_args(func, term, plan.header);
1145    let mut made = Vec::new();
1146    let mut build = Builder::new(func, plan.preheader);
1147
1148    let mut ok: Option<Value> = None;
1149    let mut windows: Vec<Window> = Vec::new();
1150    for sweep in &plan.sweeps {
1151        // How far the runtime is asked to look is how many bytes the loop reads from this address
1152        // on, and an address that does not move reads the same bytes however many times the loop
1153        // goes round, so the count the loop was going to run does not come into it.
1154        let around = if sweep.walk.still() { Around::Number(0) } else { plan.around };
1155        let (window, zero) = spare(&mut build, &mut made, sweep, around);
1156        // Every check has to fit for the fast half to be the one that runs, and this is where the
1157        // hypothesis the rule is asked under is earned: a window worked out from an extent smaller
1158        // than the reach is one that wrapped, and none of what follows would mean anything.
1159        let fits = build.icmp(IntPred::Sge, window, zero);
1160        made.push(fits);
1161        ok = Some(match ok {
1162            None => fits,
1163            Some(so_far) => {
1164                let both = build.binary(Opcode::And, so_far, fits, Flags::NONE);
1165                made.push(both);
1166                both
1167            }
1168        });
1169        if sweep.walk.still() {
1170            continue;
1171        }
1172        // Two checks that walk by the same amount are at the same offset on every iteration, so
1173        // they share the offset and the smaller of their two windows. The amount is a magnitude,
1174        // which is what lets a walk up and a walk down by eight share one offset: the offset counts
1175        // bytes from the first access and both of them are eight bytes further along each time
1176        // round. Which way they went is in the window each of them worked out, and taking the
1177        // smaller of two windows is no different for being about two directions.
1178        //
1179        // Two checks the guard measures share for the same reason and by the other key. A fixed
1180        // distance from one pointer is a fixed distance from it on every iteration, so both of them
1181        // moved by whatever that pointer moved by and one subtraction answers for the pair.
1182        let key = sweep.walk.key();
1183        match windows.iter().position(|held| held.key == key) {
1184            Some(at) => {
1185                let bound = windows[at].bound;
1186                let smaller = build.icmp(IntPred::Ult, window, bound);
1187                made.push(smaller);
1188                let least = build.select(smaller, window, bound);
1189                made.push(least);
1190                windows[at].bound = least;
1191            }
1192            None => {
1193                // Where a measured offset is measured from, worked out once in the preheader
1194                // because it is the same address on every iteration by definition.
1195                let from = match key {
1196                    Key::Every(_) => None,
1197                    Key::From(param) => {
1198                        let at = *entering
1199                            .get(param)
1200                            .expect("the header takes the parameter the plan measured from");
1201                        let from = build.unary(Opcode::PtrToInt, at, word);
1202                        made.push(from);
1203                        Some(from)
1204                    }
1205                };
1206                windows.push(Window { key, bound: window, from });
1207            }
1208        }
1209    }
1210    let ok = ok.expect("a plan holds at least one check");
1211
1212    for window in &mut windows {
1213        window.bound = bounded(&mut build, &mut made, stepped(window.key), window.bound);
1214    }
1215
1216    for value in made {
1217        let inst = inst_of(func, value);
1218        func.remove_inst(inst);
1219        func.insert_before(inst, term);
1220    }
1221    Choice { ok, windows }
1222}
1223
1224/// How much the offset goes up by between one test and the next, which is nothing for one the guard
1225/// measures.
1226///
1227/// A measured offset is worked out from the pointer every time round rather than added to, so it is
1228/// never one step past anything and there is no step to leave room for. What it can be is enormous,
1229/// when the pointer went below where it started and the subtraction came out as a huge unsigned
1230/// number, and that is the answer wanted: the guard is meant to hand a loop like that to the half
1231/// that kept its checks.
1232fn stepped(key: Key) -> i128 {
1233    match key {
1234        Key::Every(step) => step,
1235        Key::From(_) => 0,
1236    }
1237}
1238
1239/// Holds a window short of where one more step would take the offset out of sixty four bits.
1240///
1241/// The offset goes up by the step every time round and is tested afterwards, so it reaches one step
1242/// past the window before the guard sends the loop to the other half. Nothing else here bounds the
1243/// window: `cap_extent` answers with no more than it was asked for, and what it was asked for is a
1244/// trip count times a step, which saturates rather than refusing. An offset that wrapped would come
1245/// back small, the guard would let it through, and the fast half would read past the end of the
1246/// object with nothing left in it to say so.
1247///
1248/// One comparison and one select in the preheader, and the value it clamps to is so far past any
1249/// object a program allocates that this never fires. It is here because the failure it stops is
1250/// silent.
1251fn bounded(build: &mut Builder<'_>, made: &mut Vec<Value>, step: i128, bound: Value) -> Value {
1252    let word = Type::int(64);
1253    let room = build.iconst(word, i128::from(i64::MAX) - step);
1254    made.push(room);
1255    let over = build.icmp(IntPred::Ugt, bound, room);
1256    made.push(over);
1257    let held = build.select(over, room, bound);
1258    made.push(held);
1259    held
1260}
1261
1262/// Whether one offset at or below the window is one whose access is inside the extent.
1263///
1264/// This function decides nothing. It builds the term `swept.sym.i64` is written about and asks the
1265/// table, which is section 7.7's split: the pass established the window and carries the offset, and
1266/// whether an offset inside the window means an access inside the object is somebody's proof rather
1267/// than this file's opinion. It is the same rule [`crate::hoist`] asks about a loop whose extent the
1268/// program works out, and it is the same question, since a window is a hoisted check's far end under
1269/// another name.
1270///
1271/// Four of its five arguments are opaque. The address, the extent and the window are values the pass
1272/// does not have as numbers, and the offset is whichever iteration the reader cares about, which is
1273/// how one question comes to be about all of them. The rule's three hypotheses about that pair are
1274/// what [`limited`] and [`bounded`] earn.
1275///
1276/// A walk from high to low asks `swept.down.sym.i64` instead, which is the same claim written about
1277/// addresses that go the other way. Asking the ascending rule and subtracting somewhere in the pass
1278/// would be arithmetic on the thing being proved, which is what section 7.7 exists to stop, so the
1279/// direction picks a term and the table answers about that term or does not.
1280fn windowed(reach: i128, down: bool) -> bool {
1281    let mut question = Question::default();
1282    let at = question.opaque();
1283    let at = question.app("value.i64", &[at]);
1284    let span = question.opaque();
1285    let span = question.app("value.i64", &[span]);
1286    let far = question.opaque();
1287    let far = question.app("value.i64", &[far]);
1288    let reach = question.number(reach);
1289    let reach = question.app("iconst.i64", &[reach]);
1290    let delta = question.opaque();
1291    let delta = question.app("value.i64", &[delta]);
1292    let head = if down { "swept.down.sym.i64" } else { "swept.sym.i64" };
1293    let term = question.app(head, &[at, span, far, reach, delta]);
1294    match safety::TABLE.find(&question, term) {
1295        Some(found) => yes(&safety::TABLE, found.rule),
1296        None => false,
1297    }
1298}
1299
1300/// The base as a value here, writing the address of a global out again when that is what it is.
1301///
1302/// One instruction, and the same one the loop has inside it. Working it out again is why
1303/// [`crate::licm`] leaves the one in the loop alone, and it is why the address can be described
1304/// rather than named in the first place.
1305fn anchored(build: &mut Builder<'_>, made: &mut Vec<Value>, base: Anchor) -> Value {
1306    match base {
1307        Anchor::Value(value) => value,
1308        Anchor::Address(symbol) => {
1309            let extra = Extra::Symbol(symbol);
1310            let at =
1311                build.value(InstData { extra, ..InstData::new(Opcode::GlobalAddr) }, Type::PTR);
1312            made.push(at);
1313            at
1314        }
1315    }
1316}
1317
1318/// How far the first access sits past the base, as a value, or `None` when it sits on it.
1319///
1320/// Wrapping arithmetic throughout, because this is the address the loop was going to compute
1321/// anyway. The flags a `nsw` would put on it would be a promise about the caller's index, and what
1322/// this is building is a question for the runtime rather than an address anything reads.
1323fn displacement(build: &mut Builder<'_>, made: &mut Vec<Value>, apart: Plain) -> Option<Value> {
1324    let word = Type::int(64);
1325    let mut sum = match apart.value.filter(|_| apart.scale != 0) {
1326        None => {
1327            return (apart.offset != 0).then(|| {
1328                let by = build.iconst(word, apart.offset);
1329                made.push(by);
1330                by
1331            });
1332        }
1333        Some(value) => value,
1334    };
1335    // The extension the invariant describes, emitted before anything is done with the value. It
1336    // comes first because everything after it is arithmetic at the wide type and the value is not
1337    // that width yet.
1338    if let Some(read) = apart.read {
1339        let widen = match read.reading {
1340            Reading::Signed => Opcode::SExt,
1341            Reading::Unsigned => Opcode::ZExt,
1342        };
1343        sum = build.unary(widen, sum, read.to);
1344        made.push(sum);
1345    }
1346    if apart.scale != 1 {
1347        let by = build.iconst(word, apart.scale);
1348        made.push(by);
1349        sum = build.binary(Opcode::Mul, sum, by, Flags::NONE);
1350        made.push(sum);
1351    }
1352    if apart.offset != 0 {
1353        let by = build.iconst(word, apart.offset);
1354        made.push(by);
1355        sum = build.binary(Opcode::Add, sum, by, Flags::NONE);
1356        made.push(sum);
1357    }
1358    Some(sum)
1359}
1360
1361/// How many bytes past the first access belong to whatever owns it, and a zero to compare that with.
1362///
1363/// The question both callers rest on. `extent - reach` is negative when the first access does not
1364/// fit at all, zero when exactly one fits, and how much room there is for further ones otherwise.
1365///
1366/// # A walk from high to low
1367///
1368/// The offset the guard carries is a magnitude, so a loop whose address goes down is a loop whose
1369/// offset goes up in exactly the same way and everything built around the offset is untouched. What
1370/// changes is which end of the object is asked about. An ascending walk starts at the first access
1371/// and runs off the top of it, so `cap_extent` at the first address is the question. A descending
1372/// one starts at the first access and runs off the bottom, so the question is `cap_extent_back` at
1373/// the end of the first access, which is `first + reach`.
1374///
1375/// Anchoring at the end rather than at `first` is what makes the two the same shape. The answer is
1376/// then how many bytes below the end of the first access belong to the same thing, the window is
1377/// that less the reach exactly as above, and the access on iteration `delta` is the `reach` bytes
1378/// ending at `first + reach - delta`. That is the claim `swept.down.sym.i64` is written about, with
1379/// `at` being the end of the first access, and it is a claim about every iteration for the same
1380/// reason the ascending one is.
1381fn spare(
1382    build: &mut Builder<'_>,
1383    made: &mut Vec<Value>,
1384    sweep: &Sweep,
1385    around: Around,
1386) -> (Value, Value) {
1387    let word = Type::int(64);
1388    let base = anchored(build, made, sweep.base);
1389    let first = match displacement(build, made, sweep.apart) {
1390        None => base,
1391        Some(by) => {
1392            let args = build.func().push_values(&[base, by]);
1393            let sum = build.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
1394            made.push(sum);
1395            sum
1396        }
1397    };
1398    // How many bytes the loop was going to read, which is how far the runtime is asked to look and
1399    // nothing more. An answer short of the truth costs iterations in the slow half and is never
1400    // wrong, so a count that saturates rather than one that refuses is the right thing here. The
1401    // step goes in as a magnitude, since how many bytes a walk covers does not depend on which way
1402    // it goes.
1403    let stride = sweep.walk.stride();
1404    let want = match around {
1405        Around::Number(times) => {
1406            let far = times.saturating_mul(stride).saturating_add(sweep.reach);
1407            let far = i64::try_from(far).unwrap_or(i64::MAX);
1408            let bytes = build.iconst(word, i128::from(far));
1409            made.push(bytes);
1410            bytes
1411        }
1412        Around::Computed(count, reading) => {
1413            covered(build, made, count, stride, sweep.reach, reading, Flags::NONE)
1414        }
1415    };
1416
1417    // Where the question is asked from, which for a walk that goes down is the end of the first
1418    // access rather than its start. The arithmetic wraps, in the way [`displacement`] wraps and for
1419    // the same reason: this is an address the loop was going to reach anyway and the value is a
1420    // question for the runtime rather than something anything reads through.
1421    let (asked, at) = if sweep.walk.down() {
1422        let by = build.iconst(word, sweep.reach);
1423        made.push(by);
1424        let args = build.func().push_values(&[first, by]);
1425        let end = build.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
1426        made.push(end);
1427        (Opcode::CapExtentBack, end)
1428    } else {
1429        (Opcode::CapExtent, first)
1430    };
1431
1432    let args = build.func().push_values(&[at]);
1433    let capability = build.value(InstData { args, ..InstData::new(Opcode::CapOf) }, Type::CAP);
1434    made.push(capability);
1435    let args = build.func().push_values(&[capability, at, want]);
1436    let extent = build.value(InstData { args, ..InstData::new(asked) }, word);
1437    made.push(extent);
1438
1439    let reach = build.iconst(word, sweep.reach);
1440    made.push(reach);
1441    let left = build.binary(Opcode::Sub, extent, reach, Flags::NSW);
1442    made.push(left);
1443    let zero = build.iconst(word, 0);
1444    made.push(zero);
1445    (left, zero)
1446}
1447
1448#[cfg(test)]
1449mod tests {
1450    use rucc_base::Interner;
1451    use rucc_ir::{
1452        Block, Builder, Extra, Flags, Func, Inst, InstData, IntPred, MemInfo, MemOrder, Module,
1453        Opcode, Restrict, Signature, Type, Value, verify_func,
1454    };
1455    use rucc_target::{TargetInfo, Triple};
1456
1457    use super::{SPLIT, Split};
1458    use crate::canon::Canon;
1459    use crate::stats::Kind;
1460    use crate::{Fuel, Pass, Stats};
1461
1462    /// How many times the loop goes round, and how wide each element of the walk is.
1463    const TRIPS: i128 = 16;
1464    const WIDTH: i128 = 4;
1465
1466    /// A counted loop that reads one element each time round and can stop on what it read.
1467    ///
1468    /// ```text
1469    /// entry(a): jump head(0)
1470    /// head(i):  p = a + i*4; check_bounds cap_of(p), p; v = load p
1471    ///           br v == 0 -> done, more
1472    /// more:     next = i + 1; br next < 16 -> head(next), done
1473    /// done:     ret
1474    /// ```
1475    ///
1476    /// The second way out is the point. Hoisting refuses this loop, because a loop that can stop in
1477    /// the middle reads fewer bytes than its count says and one check in front of it for all of them
1478    /// would refuse a program that was right. Splitting does not care, because the count it reads is
1479    /// only ever an upper limit on how far to look.
1480    fn leaving() -> (Interner, Func, Vec<Block>) {
1481        walking(Some(TRIPS), Flags::NSW)
1482    }
1483
1484    /// The same loop, with how many times it goes round handed in rather than written down.
1485    ///
1486    /// What this reaches is the other half of [`crate::trip::covered`], the one that builds the
1487    /// count out of something the loop does not change. It is worth its own test because that
1488    /// arithmetic promises not to wrap for hoisting and promises nothing for this pass, and the two
1489    /// callers now ask for different things from the same code.
1490    fn counting() -> (Interner, Func, Vec<Block>) {
1491        walking(None, Flags::NSW)
1492    }
1493
1494    /// The same loop again, with an increment that promises nothing, so nobody counts it.
1495    ///
1496    /// What `-fwrapv` produces, and the shape a great deal of real code is in. Hoisting refuses it,
1497    /// because a count that rests on the counter not wrapping is not a count it may size a check
1498    /// with. This pass does not size anything with it, so it guesses.
1499    fn uncounted() -> (Interner, Func, Vec<Block>) {
1500        walking(Some(TRIPS), Flags::NONE)
1501    }
1502
1503    /// The same loop, reading from an index the caller handed in rather than from zero.
1504    ///
1505    /// `a[start + i]`, whose first address is `a + 4 * start`: a pointer and a displacement, with a
1506    /// number for neither of them. This is the shape the pass used to give up on, and it is a
1507    /// common one, because a loop over part of an array is written this way and so is every walk
1508    /// that begins where the last one stopped. See #810.
1509    fn from_an_index() -> (Interner, Func, Vec<Block>) {
1510        let mut names = Interner::new();
1511        let params = [Type::PTR, Type::int(64)];
1512        let mut func = Func::new(names.intern("f"), Signature::new().with_params(&params));
1513        let entry = func.create_block();
1514        let head = func.create_block();
1515        let more = func.create_block();
1516        let done = func.create_block();
1517        let array = func.append_param(entry, Type::PTR);
1518        let start = func.append_param(entry, Type::int(64));
1519        let counter = func.append_param(head, Type::int(64));
1520
1521        let zero = Builder::new(&mut func, entry).iconst(Type::int(64), 0);
1522        Builder::new(&mut func, entry).jump(head, &[zero]);
1523
1524        let mut build = Builder::new(&mut func, head);
1525        let index = build.binary(Opcode::Add, counter, start, Flags::NSW);
1526        let by = build.iconst(Type::int(64), WIDTH);
1527        let scaled = build.binary(Opcode::Mul, index, by, Flags::NSW);
1528        let args = build.func().push_values(&[array, scaled]);
1529        let pointer = build.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
1530        check(&mut build, pointer);
1531        let read = build.load(Type::int(32), pointer, mem(), Flags::NONE);
1532        let nothing = build.iconst(Type::int(32), 0);
1533        let stop = build.icmp(IntPred::Eq, read, nothing);
1534        build.br_if(stop, done, &[], more, &[]);
1535
1536        let mut build = Builder::new(&mut func, more);
1537        let one = build.iconst(Type::int(64), 1);
1538        let next = build.binary(Opcode::Add, counter, one, Flags::NSW);
1539        let limit = build.iconst(Type::int(64), TRIPS);
1540        let again = build.icmp(IntPred::Slt, next, limit);
1541        build.br_if(again, head, &[next], done, &[]);
1542        Builder::new(&mut func, done).ret(&[]);
1543        (names, func, vec![entry, head, more, done])
1544    }
1545
1546    /// The same loop over a file scope array, with the `global_addr` inside the loop.
1547    ///
1548    /// Which is where one sits, because working the address out again costs a single instruction
1549    /// and `crate::licm` would rather do that than hold it in a register the whole way round. So
1550    /// the address of the array is not a value defined outside the loop and never will be, and the
1551    /// pass has to take it from where it is or not at all. See #810.
1552    fn over_a_global() -> (Interner, Func, Vec<Block>) {
1553        let mut names = Interner::new();
1554        let tab = names.intern("tab");
1555        let mut func = Func::new(names.intern("f"), Signature::new());
1556        let entry = func.create_block();
1557        let head = func.create_block();
1558        let more = func.create_block();
1559        let done = func.create_block();
1560        let counter = func.append_param(head, Type::int(64));
1561
1562        let zero = Builder::new(&mut func, entry).iconst(Type::int(64), 0);
1563        Builder::new(&mut func, entry).jump(head, &[zero]);
1564
1565        let mut build = Builder::new(&mut func, head);
1566        let by = build.iconst(Type::int(64), WIDTH);
1567        let scaled = build.binary(Opcode::Mul, counter, by, Flags::NSW);
1568        let extra = Extra::Symbol(tab);
1569        let array = build.value(InstData { extra, ..InstData::new(Opcode::GlobalAddr) }, Type::PTR);
1570        let args = build.func().push_values(&[array, scaled]);
1571        let pointer = build.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
1572        check(&mut build, pointer);
1573        let read = build.load(Type::int(32), pointer, mem(), Flags::NONE);
1574        let nothing = build.iconst(Type::int(32), 0);
1575        let stop = build.icmp(IntPred::Eq, read, nothing);
1576        build.br_if(stop, done, &[], more, &[]);
1577
1578        let mut build = Builder::new(&mut func, more);
1579        let one = build.iconst(Type::int(64), 1);
1580        let next = build.binary(Opcode::Add, counter, one, Flags::NSW);
1581        let limit = build.iconst(Type::int(64), TRIPS);
1582        let again = build.icmp(IntPred::Slt, next, limit);
1583        build.br_if(again, head, &[next], done, &[]);
1584        Builder::new(&mut func, done).ret(&[]);
1585        (names, func, vec![entry, head, more, done])
1586    }
1587
1588    /// The same loop again, with the index in `int` and sign extended, which is what C gives.
1589    ///
1590    /// `a[start + i]` with `start` and `i` both `int`. The front end adds them at thirty two bits
1591    /// and sign extends the sum before scaling it, so the first thing scalar evolution meets is the
1592    /// extension of a chrec whose base is a value rather than a number. Splitting takes it because
1593    /// the widened base is described rather than named, and this pass emits the extension in the
1594    /// preheader. See #810.
1595    fn from_a_narrow_index() -> (Interner, Func, Vec<Block>) {
1596        let mut names = Interner::new();
1597        let params = [Type::PTR, Type::int(32)];
1598        let mut func = Func::new(names.intern("f"), Signature::new().with_params(&params));
1599        let entry = func.create_block();
1600        let head = func.create_block();
1601        let more = func.create_block();
1602        let done = func.create_block();
1603        let array = func.append_param(entry, Type::PTR);
1604        let start = func.append_param(entry, Type::int(32));
1605        let counter = func.append_param(head, Type::int(32));
1606
1607        let zero = Builder::new(&mut func, entry).iconst(Type::int(32), 0);
1608        Builder::new(&mut func, entry).jump(head, &[zero]);
1609
1610        let mut build = Builder::new(&mut func, head);
1611        let index = build.binary(Opcode::Add, counter, start, Flags::NSW);
1612        let wide = build.unary(Opcode::SExt, index, Type::int(64));
1613        let by = build.iconst(Type::int(64), WIDTH);
1614        let scaled = build.binary(Opcode::Mul, wide, by, Flags::NSW);
1615        let args = build.func().push_values(&[array, scaled]);
1616        let pointer = build.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
1617        check(&mut build, pointer);
1618        let read = build.load(Type::int(32), pointer, mem(), Flags::NONE);
1619        let nothing = build.iconst(Type::int(32), 0);
1620        let stop = build.icmp(IntPred::Eq, read, nothing);
1621        build.br_if(stop, done, &[], more, &[]);
1622
1623        let mut build = Builder::new(&mut func, more);
1624        let one = build.iconst(Type::int(32), 1);
1625        let next = build.binary(Opcode::Add, counter, one, Flags::NSW);
1626        let limit = build.iconst(Type::int(32), TRIPS);
1627        let again = build.icmp(IntPred::Slt, next, limit);
1628        build.br_if(again, head, &[next], done, &[]);
1629        Builder::new(&mut func, done).ret(&[]);
1630        (names, func, vec![entry, head, more, done])
1631    }
1632
1633    /// The same loop again, walking from the end of the array down to the start of it.
1634    ///
1635    /// ```text
1636    /// entry(a): jump head(15)
1637    /// head(i):  p = a + i*4; check_bounds cap_of(p), p; v = load p
1638    ///           br v == 0 -> done, more
1639    /// more:     next = i - 1; br next >= 0 -> head(next), done
1640    /// done:     ret
1641    /// ```
1642    ///
1643    /// The step is minus four, so the first access is the highest address the loop touches and every
1644    /// later one is below it. What the pass has to ask about is room under the first access rather
1645    /// than over it, which is `cap_extent_back` at the end of that access. See #680.
1646    fn downwards() -> (Interner, Func, Vec<Block>) {
1647        let mut names = Interner::new();
1648        let mut func = Func::new(names.intern("f"), Signature::new().with_params(&[Type::PTR]));
1649        let entry = func.create_block();
1650        let head = func.create_block();
1651        let more = func.create_block();
1652        let done = func.create_block();
1653        let array = func.append_param(entry, Type::PTR);
1654        let counter = func.append_param(head, Type::int(64));
1655
1656        let last = Builder::new(&mut func, entry).iconst(Type::int(64), TRIPS - 1);
1657        Builder::new(&mut func, entry).jump(head, &[last]);
1658
1659        let mut build = Builder::new(&mut func, head);
1660        let by = build.iconst(Type::int(64), WIDTH);
1661        let scaled = build.binary(Opcode::Mul, counter, by, Flags::NSW);
1662        let args = build.func().push_values(&[array, scaled]);
1663        let pointer = build.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
1664        check(&mut build, pointer);
1665        let read = build.load(Type::int(32), pointer, mem(), Flags::NONE);
1666        let nothing = build.iconst(Type::int(32), 0);
1667        let stop = build.icmp(IntPred::Eq, read, nothing);
1668        build.br_if(stop, done, &[], more, &[]);
1669
1670        let mut build = Builder::new(&mut func, more);
1671        let one = build.iconst(Type::int(64), 1);
1672        let next = build.binary(Opcode::Sub, counter, one, Flags::NSW);
1673        let floor = build.iconst(Type::int(64), 0);
1674        let again = build.icmp(IntPred::Sge, next, floor);
1675        build.br_if(again, head, &[next], done, &[]);
1676        Builder::new(&mut func, done).ret(&[]);
1677        (names, func, vec![entry, head, more, done])
1678    }
1679
1680    /// A scanner whose pointer moves by one byte or by two, depending on what it just read.
1681    ///
1682    /// ```text
1683    /// entry(a): jump head(a)
1684    /// head(p):  check_bounds cap_of(p), p; v = load p
1685    ///           br v == 0 -> done, more
1686    /// more:     br v < 0 -> two, one
1687    /// one:      jump back(p + 1)
1688    /// two:      jump back(p + 2)
1689    /// back(q):  jump head(q)
1690    /// done:     ret
1691    /// ```
1692    ///
1693    /// What a UTF-8 walk looks like, and what half of SQLite's text handling looks like. There is no
1694    /// step to speak of, so scalar evolution says nothing and the guard has to measure how far the
1695    /// pointer got rather than count how far it should have got. See #810.
1696    fn by_what_it_read() -> (Interner, Func, Vec<Block>) {
1697        let mut names = Interner::new();
1698        let mut func = Func::new(names.intern("f"), Signature::new().with_params(&[Type::PTR]));
1699        let entry = func.create_block();
1700        let head = func.create_block();
1701        let more = func.create_block();
1702        let one = func.create_block();
1703        let two = func.create_block();
1704        let back = func.create_block();
1705        let done = func.create_block();
1706        let text = func.append_param(entry, Type::PTR);
1707        let at = func.append_param(head, Type::PTR);
1708        let next = func.append_param(back, Type::PTR);
1709
1710        Builder::new(&mut func, entry).jump(head, &[text]);
1711
1712        let mut build = Builder::new(&mut func, head);
1713        checking(&mut build, at, byte());
1714        let read = build.load(Type::int(8), at, byte(), Flags::NONE);
1715        let nothing = build.iconst(Type::int(8), 0);
1716        let stop = build.icmp(IntPred::Eq, read, nothing);
1717        build.br_if(stop, done, &[], more, &[]);
1718
1719        let mut build = Builder::new(&mut func, more);
1720        let wide = build.icmp(IntPred::Slt, read, nothing);
1721        build.br_if(wide, two, &[], one, &[]);
1722
1723        for (block, step) in [(one, 1), (two, 2)] {
1724            let mut build = Builder::new(&mut func, block);
1725            let by = build.iconst(Type::int(64), step);
1726            let args = build.func().push_values(&[at, by]);
1727            let far = build.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
1728            build.jump(back, &[far]);
1729        }
1730
1731        Builder::new(&mut func, back).jump(head, &[next]);
1732        Builder::new(&mut func, done).ret(&[]);
1733        (names, func, vec![entry, head, more, one, two, back, done])
1734    }
1735
1736    /// A walk down a linked list, where the next pointer is read out of the current node.
1737    ///
1738    /// ```text
1739    /// entry(a): jump head(a)
1740    /// head(p):  check_bounds cap_of(p), p; v = load p
1741    ///           br v == 0 -> done, more
1742    /// more:     q = load p + 8; jump head(q)
1743    /// done:     ret
1744    /// ```
1745    ///
1746    /// The case measuring is not allowed to take. How far the second node is from the first is a
1747    /// number, but it is not a displacement inside one object, and the extent the preheader asked
1748    /// about at the first node says nothing whatever about the second. See #810.
1749    fn down_a_list() -> (Interner, Func, Vec<Block>) {
1750        let mut names = Interner::new();
1751        let mut func = Func::new(names.intern("f"), Signature::new().with_params(&[Type::PTR]));
1752        let entry = func.create_block();
1753        let head = func.create_block();
1754        let more = func.create_block();
1755        let done = func.create_block();
1756        let list = func.append_param(entry, Type::PTR);
1757        let at = func.append_param(head, Type::PTR);
1758
1759        Builder::new(&mut func, entry).jump(head, &[list]);
1760
1761        let mut build = Builder::new(&mut func, head);
1762        checking(&mut build, at, byte());
1763        let read = build.load(Type::int(8), at, byte(), Flags::NONE);
1764        let nothing = build.iconst(Type::int(8), 0);
1765        let stop = build.icmp(IntPred::Eq, read, nothing);
1766        build.br_if(stop, done, &[], more, &[]);
1767
1768        let mut build = Builder::new(&mut func, more);
1769        let by = build.iconst(Type::int(64), 8);
1770        let args = build.func().push_values(&[at, by]);
1771        let field = build.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
1772        let next = build.load(Type::PTR, field, mem(), Flags::NONE);
1773        build.jump(head, &[next]);
1774        Builder::new(&mut func, done).ret(&[]);
1775        (names, func, vec![entry, head, more, done])
1776    }
1777
1778    /// Builds the loop, with the exit test against a number or against a second parameter.
1779    fn walking(times: Option<i128>, flags: Flags) -> (Interner, Func, Vec<Block>) {
1780        let mut names = Interner::new();
1781        let mut params = vec![Type::PTR];
1782        params.extend(times.is_none().then_some(Type::int(64)));
1783        let mut func = Func::new(names.intern("f"), Signature::new().with_params(&params));
1784        let entry = func.create_block();
1785        let head = func.create_block();
1786        let more = func.create_block();
1787        let done = func.create_block();
1788        let array = func.append_param(entry, Type::PTR);
1789        let handed = times.is_none().then(|| func.append_param(entry, Type::int(64)));
1790        let counter = func.append_param(head, Type::int(64));
1791
1792        let zero = Builder::new(&mut func, entry).iconst(Type::int(64), 0);
1793        Builder::new(&mut func, entry).jump(head, &[zero]);
1794
1795        let mut build = Builder::new(&mut func, head);
1796        let by = build.iconst(Type::int(64), WIDTH);
1797        let scaled = build.binary(Opcode::Mul, counter, by, Flags::NSW);
1798        let args = build.func().push_values(&[array, scaled]);
1799        let pointer = build.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
1800        check(&mut build, pointer);
1801        let read = build.load(Type::int(32), pointer, mem(), Flags::NONE);
1802        let nothing = build.iconst(Type::int(32), 0);
1803        let stop = build.icmp(IntPred::Eq, read, nothing);
1804        build.br_if(stop, done, &[], more, &[]);
1805
1806        let mut build = Builder::new(&mut func, more);
1807        let one = build.iconst(Type::int(64), 1);
1808        let next = build.binary(Opcode::Add, counter, one, flags);
1809        let limit = match (times, handed) {
1810            (Some(times), _) => build.iconst(Type::int(64), times),
1811            (None, handed) => handed.expect("a loop with no number for a limit was handed one"),
1812        };
1813        let again = build.icmp(IntPred::Slt, next, limit);
1814        build.br_if(again, head, &[next], done, &[]);
1815        Builder::new(&mut func, done).ret(&[]);
1816        (names, func, vec![entry, head, more, done])
1817    }
1818
1819    /// What one access in the loop covers.
1820    fn mem() -> MemInfo {
1821        MemInfo {
1822            size: WIDTH as u64,
1823            align: WIDTH as u32,
1824            order: MemOrder::NotAtomic,
1825            tbaa: None,
1826            restrict: Restrict::NONE,
1827        }
1828    }
1829
1830    /// What one access covers in a loop that walks a byte at a time.
1831    ///
1832    /// A walk the guard has to measure has to be over something wanting no alignment, because a step
1833    /// nobody wrote down is a step nothing can divide by the alignment. Which is what the loops this
1834    /// reaches look like anyway: they are scanners over text.
1835    fn byte() -> MemInfo {
1836        MemInfo {
1837            size: 1,
1838            align: 1,
1839            order: MemOrder::NotAtomic,
1840            tbaa: None,
1841            restrict: Restrict::NONE,
1842        }
1843    }
1844
1845    /// Puts `cap_of` and a `check_bounds` at `pointer` into a block.
1846    ///
1847    /// The shape `rucc-safety` emits, written out here rather than reached for, because `rucc-opt`
1848    /// is rank 9 alongside `rucc-safety` and cannot depend on it.
1849    fn check(build: &mut Builder<'_>, pointer: Value) {
1850        checking(build, pointer, mem());
1851    }
1852
1853    /// The same, for an access of some other width.
1854    fn checking(build: &mut Builder<'_>, pointer: Value, info: MemInfo) {
1855        let args = build.func().push_values(&[pointer]);
1856        let capability = build.value(InstData { args, ..InstData::new(Opcode::CapOf) }, Type::CAP);
1857        let args = build.func().push_values(&[capability, pointer]);
1858        let extra = Extra::Mem(build.func().add_mem(info));
1859        build.inst(InstData { args, extra, ..InstData::new(Opcode::CheckBounds) }, &[]);
1860    }
1861
1862    /// Canonicalizes and then splits, with as much fuel as both want.
1863    ///
1864    /// Both, because the pass is written against the shape [`Canon`] leaves, and it is
1865    /// canonicalization that gives the loop the preheader the limit is worked out in.
1866    fn split_up(func: &mut Func) -> Stats {
1867        let mut an = crate::machine::fixtures::analyses();
1868        Canon.run(func, &mut an, &mut Fuel::unlimited());
1869        Split.run(func, &mut an, &mut Fuel::unlimited())
1870    }
1871
1872    #[test]
1873    fn a_loop_whose_result_is_read_after_it_is_put_back_into_closed_form_first() {
1874        // Canonicalization runs a long way in front of this pass and `simplify-cfg` between the two
1875        // undoes some of what it did, which is why the loop here is canonicalized and then broken.
1876        // Both halves would define the value the code after the loop reads, so the pass repairs the
1877        // one loop it is about to copy rather than refusing it or running canonicalization again.
1878        let (mut names, mut func, blocks) = leaving();
1879        let mut an = crate::machine::fixtures::analyses();
1880        Canon.run(&mut func, &mut an, &mut Fuel::unlimited());
1881
1882        let (head, done) = (blocks[1], blocks[3]);
1883        let read = func
1884            .insts(head)
1885            .find(|&inst| func[inst].opcode == Opcode::Load)
1886            .and_then(|inst| func[inst].results().next())
1887            .expect("the loop loads what it walks over");
1888        let term = func.terminator(done).expect("the block after the loop returns");
1889        let sum = Builder::new(&mut func, done).binary(Opcode::Add, read, read, Flags::NONE);
1890        let inst = super::inst_of(&func, sum);
1891        func.remove_inst(inst);
1892        func.insert_before(inst, term);
1893        an.clear();
1894
1895        let stats = Split.run(&mut func, &mut an, &mut Fuel::unlimited());
1896        assert_eq!(stats.count(Kind::Optimized, super::CLOSED_HERE), 1);
1897        assert_eq!(stats.count(Kind::Optimized, SPLIT), 1);
1898        assert_eq!(func[done].params.len(), 1, "the block after the loop took the value in");
1899        assert_eq!(all(&func, Opcode::CheckBounds).len(), 1, "the fast half lost its check");
1900        sound(&func, &mut names);
1901    }
1902
1903    /// Every instruction in the function with this opcode, and the block it is in.
1904    fn all(func: &Func, opcode: Opcode) -> Vec<(Block, Inst)> {
1905        func.blocks()
1906            .flat_map(|block| func.insts(block).map(move |inst| (block, inst)).collect::<Vec<_>>())
1907            .filter(|&(_, inst)| func[inst].opcode == opcode)
1908            .collect()
1909    }
1910
1911    /// Insists the function is one the rest of the compiler may believe.
1912    ///
1913    /// This is what the tests here rest on. The pass makes a second copy of a loop, gives a new
1914    /// block parameters that stand for the old header's, and moves a preheader's worth of
1915    /// arithmetic in front of a terminator that was already there, so whether every value is in
1916    /// scope where it is read is not something reading the code settles.
1917    fn sound(func: &Func, names: &mut Interner) {
1918        let target = TargetInfo::new("x86_64-unknown-linux-gnu".parse::<Triple>().unwrap());
1919        let module = Module::new(names.intern("t.c"), &target);
1920        if let Err(errors) = verify_func(&module, func, names) {
1921            panic!("{errors:#?}");
1922        }
1923    }
1924
1925    #[test]
1926    fn a_loop_that_can_stop_early_is_split_even_though_hoisting_will_not_touch_it() {
1927        // The census row this pass was written for. Of the checks SQLite still carries at -O2, the
1928        // largest group by far is in loops with a second way out, which is exactly the loop here.
1929        let (mut names, mut func, _) = leaving();
1930        let mut an = crate::machine::fixtures::analyses();
1931        Canon.run(&mut func, &mut an, &mut Fuel::unlimited());
1932        let refused = crate::hoist::Hoist.run(&mut func, &mut an, &mut Fuel::unlimited());
1933        assert!(!refused.changed(), "hoisting has nothing to say about this loop");
1934
1935        let stats = Split.run(&mut func, &mut an, &mut Fuel::unlimited());
1936        assert_eq!(stats.count(Kind::Optimized, SPLIT), 1);
1937        sound(&func, &mut names);
1938    }
1939
1940    #[test]
1941    fn the_half_the_loop_runs_first_has_no_check_in_it_and_the_other_one_keeps_it() {
1942        // One check went in and one check came out, and the one that came out is in the copy. That
1943        // is the whole transformation: the same work, with the checking half reached only once the
1944        // guard says the run of safe iterations is over.
1945        let (mut names, mut func, blocks) = leaving();
1946        let head = blocks[1];
1947        split_up(&mut func);
1948
1949        let left = all(&func, Opcode::CheckBounds);
1950        assert_eq!(left.len(), 1, "one check, and it is the one the slow half kept");
1951        assert_ne!(left[0].0, head, "and it is not in the block the loop started in");
1952        sound(&func, &mut names);
1953    }
1954
1955    #[test]
1956    fn how_far_the_runtime_is_asked_to_look_is_settled_in_front_of_the_loop() {
1957        // The one thing a compiler cannot work out here is how many bytes belong to the object, so
1958        // it is asked, once, before the loop starts. Once is what makes this worth doing: a query
1959        // per loop in place of a check per iteration.
1960        let (mut names, mut func, _) = leaving();
1961        split_up(&mut func);
1962
1963        let asked = all(&func, Opcode::CapExtent);
1964        assert_eq!(asked.len(), 1, "one question for the one check that was sized");
1965        let cfg = crate::Cfg::new(&func);
1966        let doms = crate::Dominators::new(&cfg);
1967        let loops = crate::Loops::new(&cfg, &doms);
1968        assert!(
1969            loops.all().all(|id| !loops.contains(id, asked[0].0)),
1970            "and it is outside the loop"
1971        );
1972        sound(&func, &mut names);
1973    }
1974
1975    #[test]
1976    fn a_walk_that_starts_at_an_index_the_caller_handed_in_is_split() {
1977        // #810. The first address is `a + 4 * start` and the question has to be put about that
1978        // address rather than about the array, because an extent measured from the array covers
1979        // bytes in front of where the loop begins and would say the walk fits when it does not.
1980        let (mut names, mut func, blocks) = from_an_index();
1981        let stats = split_up(&mut func);
1982        assert_eq!(stats.count(Kind::Optimized, SPLIT), 1);
1983        assert_eq!(all(&func, Opcode::CheckBounds).len(), 1, "the fast half lost its check");
1984
1985        let asked = all(&func, Opcode::CapExtent);
1986        assert_eq!(asked.len(), 1, "one question for the one check that was sized");
1987        let at = func[func[asked[0].1].args][1];
1988        let inst = super::inst_of(&func, at);
1989        assert_eq!(func[inst].opcode, Opcode::PtrAdd, "the question is asked about a displacement");
1990        assert_eq!(func[func[inst].args][0], func[blocks[0]].params[0], "off the array");
1991        sound(&func, &mut names);
1992    }
1993
1994    #[test]
1995    fn a_walk_over_a_file_scope_array_is_split_and_the_address_is_written_out_again() {
1996        // #810. The address of a global is a link time constant, so it does not change inside a
1997        // loop wherever the instruction that works it out happens to sit. The question in front of
1998        // the loop gets a `global_addr` of its own rather than reading the one inside, which is one
1999        // instruction and is the same trade `crate::licm` already makes for these.
2000        let (mut names, mut func, _) = over_a_global();
2001        let stats = split_up(&mut func);
2002        assert_eq!(stats.count(Kind::Optimized, SPLIT), 1);
2003        assert_eq!(all(&func, Opcode::CheckBounds).len(), 1, "the fast half lost its check");
2004
2005        let asked = all(&func, Opcode::CapExtent);
2006        assert_eq!(asked.len(), 1, "one question for the one check that was sized");
2007        let at = func[func[asked[0].1].args][1];
2008        let inst = super::inst_of(&func, at);
2009        assert_eq!(func[inst].opcode, Opcode::GlobalAddr, "asked about the array itself");
2010
2011        let cfg = crate::Cfg::new(&func);
2012        let doms = crate::Dominators::new(&cfg);
2013        let loops = crate::Loops::new(&cfg, &doms);
2014        let addresses = all(&func, Opcode::GlobalAddr);
2015        assert_eq!(addresses.len(), 3, "one in each half of the loop and one in front of them");
2016        assert_eq!(
2017            addresses
2018                .iter()
2019                .filter(|&&(block, _)| loops.all().all(|id| !loops.contains(id, block)))
2020                .count(),
2021            1,
2022            "and the one in front is outside every loop, which is where the question is asked",
2023        );
2024        sound(&func, &mut names);
2025    }
2026
2027    #[test]
2028    fn a_walk_whose_step_is_not_a_number_is_split_and_the_guard_measures_how_far_it_got() {
2029        // #810. The pointer moves by one or by two and nothing knows which, so there is no step to
2030        // carry and no count to keep. What the guard can do instead is subtract: where the pointer
2031        // is now, less where it was on the way in, is the displacement itself rather than a number
2032        // standing in for it, so the same window and the same rule apply unchanged.
2033        let (mut names, mut func, blocks) = by_what_it_read();
2034        let stats = split_up(&mut func);
2035        assert_eq!(stats.count(Kind::Optimized, SPLIT), 1);
2036        assert_eq!(all(&func, Opcode::CheckBounds).len(), 1, "the fast half lost its check");
2037
2038        let asked = all(&func, Opcode::CapExtent);
2039        assert_eq!(asked.len(), 1, "one question for the one check that was sized");
2040        assert_eq!(
2041            func[func[asked[0].1].args][1], func[blocks[0]].params[0],
2042            "asked about the pointer the loop was handed, which is where the walk begins",
2043        );
2044
2045        let measured = all(&func, Opcode::PtrToInt);
2046        assert_eq!(measured.len(), 2, "where the pointer began and where it is now");
2047        sound(&func, &mut names);
2048    }
2049
2050    #[test]
2051    fn a_guard_that_measures_carries_nothing_round_the_loop() {
2052        // The measured offset costs less than the counted one rather than more. It is worked out
2053        // from a pointer the loop already hands itself, so the guard needs no parameter for it and
2054        // the latch needs no add, and what is left is one subtraction where there was a block
2055        // parameter and an increment.
2056        let (mut names, mut func, _) = by_what_it_read();
2057        split_up(&mut func);
2058
2059        let cfg = crate::Cfg::new(&func);
2060        let doms = crate::Dominators::new(&cfg);
2061        let loops = crate::Loops::new(&cfg, &doms);
2062        let guard = loops
2063            .all()
2064            .map(|id| loops.header(id))
2065            .find(|&block| func.insts(block).any(|inst| func[inst].opcode == Opcode::PtrToInt))
2066            .expect("the guard is the header of the loop it took over");
2067        assert_eq!(func[guard].params.len(), 1, "the pointer the header carried, and nothing else");
2068        sound(&func, &mut names);
2069    }
2070
2071    #[test]
2072    fn a_walk_down_a_linked_list_is_left_alone() {
2073        // The measured window is not a licence to subtract any two pointers. The next node of a
2074        // list is not inside the object the current one is in, so how far apart they are is a
2075        // number about nothing, and the extent asked about at the head of the list would be
2076        // believed for an address that has no relation to it. What stops it is the walk over the
2077        // back edge, which insists the pointer is its own former self plus bytes, and a load is not.
2078        let (mut names, mut func, _) = down_a_list();
2079        let stats = split_up(&mut func);
2080        assert_eq!(stats.count(Kind::Optimized, SPLIT), 0, "the loop is left alone");
2081        assert_eq!(stats.count(Kind::Missed, super::NOT_FOLLOWED), 1);
2082        assert_eq!(all(&func, Opcode::CheckBounds).len(), 1, "and the check stays where it was");
2083        assert!(all(&func, Opcode::CapExtent).is_empty(), "with nothing asked in front of it");
2084        sound(&func, &mut names);
2085    }
2086
2087    #[test]
2088    fn a_walk_the_guard_would_measure_is_left_alone_when_its_access_wants_alignment() {
2089        // A step nobody wrote down is a step nothing can divide by the alignment, so a measured walk
2090        // has no answer about whether the second access is as aligned as the first. Refusing is the
2091        // conservative reading and it has its own line in the census, so what it costs is a number.
2092        let (mut names, mut func, _) = by_what_it_read();
2093        for (_, inst) in all(&func, Opcode::CheckBounds) {
2094            let extra = Extra::Mem(func.add_mem(mem()));
2095            func[inst].extra = extra;
2096        }
2097        let stats = split_up(&mut func);
2098        assert_eq!(stats.count(Kind::Optimized, SPLIT), 0, "the loop is left alone");
2099        assert_eq!(stats.count(Kind::Missed, super::MEASURED_ALIGN), 1);
2100        sound(&func, &mut names);
2101    }
2102
2103    #[test]
2104    fn a_walk_from_an_index_in_int_is_split_and_the_extension_is_emitted_in_front() {
2105        // #810, and the shape that is actually in C rather than the one that is convenient to
2106        // build. The chrec of `start + i` is in `int` and its base is `start`, so widening it to
2107        // pointer width wants `sext(start)`, which nothing in the function computes. The invariant
2108        // describes the extension instead and this pass emits it, once, in the preheader.
2109        let (mut names, mut func, blocks) = from_a_narrow_index();
2110        let stats = split_up(&mut func);
2111        assert_eq!(stats.count(Kind::Optimized, SPLIT), 1);
2112        assert_eq!(all(&func, Opcode::CheckBounds).len(), 1, "the fast half lost its check");
2113
2114        let asked = all(&func, Opcode::CapExtent);
2115        assert_eq!(asked.len(), 1, "one question for the one check that was sized");
2116        let at = func[func[asked[0].1].args][1];
2117        let sum = super::inst_of(&func, at);
2118        assert_eq!(func[sum].opcode, Opcode::PtrAdd, "the question is asked about a displacement");
2119        assert_eq!(func[func[sum].args][0], func[blocks[0]].params[0], "off the array");
2120        let widened = all(&func, Opcode::SExt);
2121        assert_eq!(widened.len(), 3, "one extension in each half of the loop and one in front");
2122        let start = func[blocks[0]].params[1];
2123        assert_eq!(
2124            widened.iter().filter(|&&(_, inst)| func[func[inst].args][0] == start).count(),
2125            1,
2126            "and the one in front is of the index the caller handed in, which the halves never take",
2127        );
2128        sound(&func, &mut names);
2129    }
2130
2131    #[test]
2132    fn a_walk_from_high_to_low_is_split_and_the_question_goes_the_other_way() {
2133        // #680. The offset the guard carries counts bytes moved rather than bytes added, so it goes
2134        // up here exactly as it does in an ascending loop and the guard is the same guard. The one
2135        // thing that turns over is which end of the object the runtime is asked about, and it is
2136        // asked at the end of the first access rather than at its start so that the window is room
2137        // below and the rule the pass asks is the mirror of the one it asks going up.
2138        let (mut names, mut func, blocks) = downwards();
2139        let stats = split_up(&mut func);
2140        assert_eq!(stats.count(Kind::Optimized, SPLIT), 1);
2141        assert_eq!(all(&func, Opcode::CheckBounds).len(), 1, "the fast half lost its check");
2142
2143        assert!(all(&func, Opcode::CapExtent).is_empty(), "nothing asked about the bytes above");
2144        let asked = all(&func, Opcode::CapExtentBack);
2145        assert_eq!(asked.len(), 1, "one question for the one check that was sized");
2146        let at = func[func[asked[0].1].args][1];
2147        let end = super::inst_of(&func, at);
2148        assert_eq!(func[end].opcode, Opcode::PtrAdd, "asked at the end of the first access");
2149        let from = func[func[end].args][0];
2150        let first = super::inst_of(&func, from);
2151        assert_eq!(
2152            func[first].opcode,
2153            Opcode::PtrAdd,
2154            "past a first access that is a displacement"
2155        );
2156        assert_eq!(func[func[first].args][0], func[blocks[0]].params[0], "off the array");
2157        sound(&func, &mut names);
2158    }
2159
2160    #[test]
2161    fn a_loop_with_a_call_in_it_that_might_free_is_left_alone() {
2162        // The extent is asked once and believed for the whole of the fast half, so anything that
2163        // could hand the storage back in the middle makes the answer stale and the fast half has
2164        // nothing left in it to notice.
2165        let (_, mut func, _) = calling(Flags::NONE);
2166        let stats = split_up(&mut func);
2167        assert!(!stats.changed());
2168        assert_eq!(stats.count(Kind::Missed, super::A_CALL_INSIDE), 1);
2169    }
2170
2171    #[test]
2172    fn a_loop_with_a_call_in_it_that_cannot_free_is_split() {
2173        // Whether the storage can be handed back is a question about the callee, and `crate::nofree`
2174        // answers it before the pipeline starts. This is the largest row of the census by a long way,
2175        // and it is also the row where this pass and hoisting come apart the furthest: hoisting
2176        // refuses a call whatever it does, because it needs the loop to reach the end of what its
2177        // count says, and this never claims that.
2178        let (mut names, mut func, _) = calling(Flags::NOFREE);
2179        let stats = split_up(&mut func);
2180        assert_eq!(stats.count(Kind::Optimized, SPLIT), 1);
2181        assert_eq!(all(&func, Opcode::CheckBounds).len(), 1, "the fast half lost its check");
2182        assert_eq!(all(&func, Opcode::Call).len(), 2, "and both halves kept the call");
2183        sound(&func, &mut names);
2184    }
2185
2186    /// The loop with a call added to its latch, carrying whatever the caller says about it.
2187    fn calling(flags: Flags) -> (Interner, Func, Vec<Block>) {
2188        let (mut names, mut func, blocks) = leaving();
2189        let more = blocks[2];
2190        let term = func.terminator(more).expect("the latch branches");
2191        let callee = names.intern("somewhere");
2192        let signature = func.add_signature(Signature::new());
2193        let call = Builder::new(&mut func, more).call(callee, signature, &[]);
2194        func[call].flags |= flags;
2195        func.remove_inst(call);
2196        func.insert_before(call, term);
2197        (names, func, blocks)
2198    }
2199
2200    #[test]
2201    fn a_check_whose_address_does_not_move_is_taken_too() {
2202        // One check on the array itself, every time round, alongside the one that walks. Hoisting
2203        // would rather have the still one, but this loop has a second way out, so hoisting will not
2204        // touch it and the check is still here to be taken. A step of zero is what carries it: the
2205        // access fits on the first iteration or on none of them, so it puts no limit on the loop.
2206        let (mut names, mut func, _) = standing(false);
2207        let stats = split_up(&mut func);
2208        assert_eq!(stats.count(Kind::Optimized, SPLIT), 1);
2209        assert_eq!(all(&func, Opcode::CapExtent).len(), 2, "both addresses were sized in front");
2210        assert_eq!(
2211            all(&func, Opcode::CheckBounds).len(),
2212            2,
2213            "the fast half lost both checks and the slow half kept both"
2214        );
2215        sound(&func, &mut names);
2216    }
2217
2218    #[test]
2219    fn the_window_is_worked_out_without_dividing_by_anything() {
2220        // The reason it counts bytes rather than iterations. Iterations came out of a division by
2221        // the step, which is a step of zero on a check whose address does not move, and on x86 that
2222        // is a fault rather than a wrong number, so tamnd/rucc#818 was a program dying on the way
2223        // into a loop it was never going to fail in. It is also why the claim could not be a rule:
2224        // the divide and the multiply that went with it are what z3 would not finish on. The plan
2225        // here has one check of each kind, which is the shape fifty six of SQLite's two hundred and
2226        // sixty eight split loops have.
2227        let (mut names, mut func, _) = standing(false);
2228        split_up(&mut func);
2229        for opcode in [Opcode::SDiv, Opcode::UDiv] {
2230            assert!(all(&func, opcode).is_empty(), "{opcode:?} is left in the window arithmetic");
2231        }
2232        sound(&func, &mut names);
2233    }
2234
2235    #[test]
2236    fn two_checks_that_walk_by_the_same_amount_share_one_offset() {
2237        // One value round the loop rather than one per check, which is what the common shape wants:
2238        // a loop that reads one array and writes another walks both by the same step, so they are
2239        // at the same offset on every iteration and the window is the smaller of the two.
2240        let (mut names, mut func, blocks) = twinned();
2241        let stats = split_up(&mut func);
2242        assert_eq!(stats.count(Kind::Optimized, SPLIT), 1);
2243        assert_eq!(all(&func, Opcode::CapExtent).len(), 2, "both addresses were sized in front");
2244
2245        let head = blocks[1];
2246        let cfg = crate::Cfg::new(&func);
2247        let into = cfg.predecessors(head);
2248        assert_eq!(into.len(), 1, "the guard is the only way into the header now");
2249        let guard = into[0];
2250        assert_eq!(
2251            func[guard].params.len(),
2252            func[head].params.len() + 1,
2253            "one offset, not one per check"
2254        );
2255        sound(&func, &mut names);
2256    }
2257
2258    /// The loop with a second walking check in it, on the element after the one it reads.
2259    ///
2260    /// Two checks that move by the same amount, which is what a loop that reads one array and writes
2261    /// another is, and what a loop that looks one element ahead is. The window arithmetic keeps one
2262    /// offset for the pair of them rather than one each, and this is the fixture that says so.
2263    fn twinned() -> (Interner, Func, Vec<Block>) {
2264        let (names, mut func, blocks) = leaving();
2265        let (entry, head) = (blocks[0], blocks[1]);
2266        let array = func[entry].params[0];
2267        let counter = func[head].params[0];
2268        let term = func.terminator(head).expect("the header branches");
2269        let mut build = Builder::new(&mut func, head);
2270        let by = build.iconst(Type::int(64), WIDTH);
2271        let scaled = build.binary(Opcode::Mul, counter, by, Flags::NSW);
2272        let ahead = build.binary(Opcode::Add, scaled, by, Flags::NSW);
2273        let args = build.func().push_values(&[array, ahead]);
2274        let pointer = build.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
2275        check(&mut build, pointer);
2276        let made: Vec<Inst> = func.insts(head).skip_while(|&inst| inst != term).skip(1).collect();
2277        for inst in made {
2278            func.remove_inst(inst);
2279            func.insert_before(inst, term);
2280        }
2281        (names, func, blocks)
2282    }
2283
2284    #[test]
2285    fn a_loop_where_nothing_moves_picks_its_half_once_and_counts_nothing() {
2286        // Half the loops this takes on SQLite are like this, and they need none of the machinery the
2287        // rest of them do. Which half runs is decided by the answer to a question asked in the
2288        // preheader, the answer does not change while the loop runs, so the way into the loop is
2289        // where the two halves are chosen between and there is no counter and no guard block.
2290        let (mut names, mut func, blocks) = standing(true);
2291        let stats = split_up(&mut func);
2292        assert_eq!(stats.count(Kind::Optimized, SPLIT), 1);
2293        assert_eq!(all(&func, Opcode::CapExtent).len(), 1);
2294        assert_eq!(all(&func, Opcode::CheckBounds).len(), 1, "the fast half lost its check");
2295
2296        let (entry, head) = (blocks[0], blocks[1]);
2297        let term = func.terminator(entry).expect("the preheader still ends in something");
2298        assert_eq!(func[term].opcode, Opcode::BrIf, "the way in is the choice");
2299        assert_eq!(func[head].params.len(), 1, "and the header took on no counter");
2300        sound(&func, &mut names);
2301    }
2302
2303    /// The loop with a check on the array itself added to its header, every time round.
2304    ///
2305    /// Hoisting would rather have that check, and it takes the ones in loops it is willing to touch.
2306    /// This loop has a second way out, so hoisting will not touch it and the check is still here.
2307    /// `alone` takes the walking check away, which leaves a loop where nothing moves at all.
2308    fn standing(alone: bool) -> (Interner, Func, Vec<Block>) {
2309        let (names, mut func, blocks) = leaving();
2310        let (entry, head) = (blocks[0], blocks[1]);
2311        let array = func[entry].params[0];
2312        let walking = all(&func, Opcode::CheckBounds);
2313        let term = func.terminator(head).expect("the header branches");
2314        let mut build = Builder::new(&mut func, head);
2315        check(&mut build, array);
2316        let made: Vec<Inst> = func.insts(head).skip_while(|&inst| inst != term).skip(1).collect();
2317        for inst in made {
2318            func.remove_inst(inst);
2319            func.insert_before(inst, term);
2320        }
2321        if alone {
2322            for (_, inst) in walking {
2323                func.remove_inst(inst);
2324            }
2325        }
2326        (names, func, blocks)
2327    }
2328
2329    #[test]
2330    fn a_loop_whose_count_is_an_expression_is_split_on_what_that_expression_says() {
2331        // How far to look is worked out in the preheader rather than written down, out of a value
2332        // the loop does not change. Nothing here promises the arithmetic stays inside sixty four
2333        // bits, and it does not have to: a limit that wrapped is still answered with a true count
2334        // of the bytes that belong to the object.
2335        let (mut names, mut func, _) = counting();
2336        let stats = split_up(&mut func);
2337        assert_eq!(stats.count(Kind::Optimized, SPLIT), 1);
2338        assert_eq!(all(&func, Opcode::CapExtent).len(), 1);
2339        assert_eq!(all(&func, Opcode::CheckBounds).len(), 1, "the fast half lost its check");
2340        sound(&func, &mut names);
2341    }
2342
2343    #[test]
2344    fn a_loop_nobody_counted_is_split_on_a_guess() {
2345        // The difference from hoisting in one test. Hoisting refuses this loop, because the count
2346        // is what it sizes the check it writes with and a count nobody settled is not one it may
2347        // write a check from. Nothing here rests on the count: it is spent on how far to ask the
2348        // runtime to look, and the runtime answers with a true count of the bytes that belong to the
2349        // object whatever it was asked for, so a guess is as safe as a proof and only less useful.
2350        let (mut names, mut func, _) = uncounted();
2351        let mut an = crate::machine::fixtures::analyses();
2352        Canon.run(&mut func, &mut an, &mut Fuel::unlimited());
2353        let refused = crate::hoist::Hoist.run(&mut func, &mut an, &mut Fuel::unlimited());
2354        assert!(!refused.changed(), "hoisting will not size a check from a count nobody settled");
2355
2356        let stats = Split.run(&mut func, &mut an, &mut Fuel::unlimited());
2357        assert_eq!(stats.count(Kind::Optimized, SPLIT), 1);
2358        assert_eq!(all(&func, Opcode::CheckBounds).len(), 1, "the fast half lost its check");
2359        sound(&func, &mut names);
2360    }
2361
2362    #[test]
2363    fn the_pass_stops_when_the_fuel_runs_out() {
2364        // What `-fopt-fuel` is for, and the reason every transformation here goes through the
2365        // counter rather than round it.
2366        let (_, mut func, _) = leaving();
2367        let mut an = crate::machine::fixtures::analyses();
2368        Canon.run(&mut func, &mut an, &mut Fuel::unlimited());
2369        let stats = Split.run(&mut func, &mut an, &mut Fuel::of(0));
2370        assert!(!stats.changed());
2371        assert_eq!(stats.count(Kind::Missed, super::NO_FUEL), 1);
2372    }
2373}