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