Skip to main content

rucc_opt/
discharge.rs

1//! Taking out a safety check whose answer is already known.
2//!
3//! Design: `spec/safe-memory/07-check-elimination.md` section 7.3, which is the first half of the
4//! Tier E budget. `rucc-safety` puts a bounds check and a lifetime check in front of every access
5//! and does not try to be clever about it, on purpose: a walk that inserts everything is a walk
6//! anybody can read, and every check that is not needed is meant to be taken out here instead.
7//! This pass takes them out, and it does the case document 07 expects to be worth the most and to
8//! be the easiest to get right, which is a second access to bytes an earlier access already had
9//! checked. All three kinds `rucc-safety` emits are the pass's business, the bounds check and the
10//! lifetime check in front of an access and the derivation check after a walk, because they are
11//! emitted together and taking out one of three is a third of a saving.
12//!
13//! # The two halves
14//!
15//! Section 7.7 asks for the pass and the condition to be separate things, and they are. What is in
16//! this file is a walk: which check runs before which, which pointer was computed from which, and
17//! how far apart two addresses are. Nothing here decides whether that is enough. The condition
18//! under which a check may go is a rule in `rules/safety.rules`, a solver has to agree with it
19//! before this crate finishes building, and `crate::rules::safety` is the table it compiles into.
20//!
21//! The split is worth the trouble because the two halves fail differently. A walk that gets the
22//! context wrong is a bug of the ordinary kind, and section 14.3's differential check accounting,
23//! which runs the instrumented program with every check and again with the discharged ones gone,
24//! is what looks for it. A removal condition that is wrong is arithmetic that is off at the ends of
25//! the type. It gives the right answer on every test anybody writes and lets one access through in
26//! the one case nobody thought of, and nothing observes that until somebody exploits it.
27//!
28//! # What it establishes and what it asks
29//!
30//! Walking the dominator tree from the entry, the pass carries a set of facts. A `check_bounds`
31//! that stays is a fact, because a check that passes says the bytes it was about lie inside one
32//! storage instance, and a check that fails does not return. A fact is remembered as the pointer's
33//! base and the constant offset from it, which is what a chain of `ptr_add` over constants comes
34//! to, plus how many bytes the access covers.
35//!
36//! At the next `check_bounds`, the pointer is normalized the same way. When a fact shares its base,
37//! the distance between the two accesses is the difference of the two offsets, and that is a number
38//! this pass has rather than a claim it makes: both addresses are the same value plus a constant.
39//! The question of whether the later bytes are inside the earlier ones is then handed to the table,
40//! which answers it in sixty four bit arithmetic rather than in the offsets, and the check goes
41//! only if the answer is yes.
42//!
43//! A check whose extent is an operand is left out of all of this, in both directions. Section 7.4's
44//! hoisted check covers as many bytes as its loop runs times, and every range compared here is a
45//! pair of numbers, so such a check is neither read as a fact nor asked about. Reading its payload
46//! would be worse than skipping it, since the size there is one element of the walk rather than the
47//! range the check is about, and a fact recorded from it would be smaller than the truth in one
48//! direction and a question asked from it smaller in the other.
49//!
50//! The capability operand has to be the `cap_of` of the check's own pointer, which is the shape
51//! `rucc-safety` emits and the shape the argument needs. The check being removed asks whether its
52//! bytes are inside the instance that owns its own pointer, its pointer is inside the range the
53//! earlier check established, and that range is inside one instance, so the answer is yes. A check
54//! whose capability came from somewhere else is asking about a different instance and is left
55//! alone. Nothing is required of the earlier check's capability, because all that is used of it is
56//! that the check passed, and a check that passed put its bytes inside one instance whatever
57//! capability it named.
58//!
59//! # The conjunct that is not about bytes
60//!
61//! A `check_bounds` tests two things, because document 06 section 6.3 put the access alignment on
62//! it rather than in a check of its own: that the bytes are inside one instance, and that the
63//! address starts where an access of that alignment may start. Everything above is about the
64//! first. A check that goes takes the second away with it, so nothing goes until something has
65//! answered it, which is `aligned` below and which reads the object the address came from and the
66//! steps taken from it. An access that assumes nothing about where it starts has nothing to
67//! answer, and a member of a packed record is exactly that.
68//!
69//! A global is settled elsewhere and arrives as [`Flags::ALIGNED`], for the reason
70//! [`Flags::STATIC`] beside it exists: how aligned a global is lives on the module and a pass is
71//! given a function. Without that the gate would cost seventeen times what it costs, which is the
72//! measurement in the changelog and is what says the flag earns its bit.
73//!
74//! What is left is a pointer this function was handed, one it loaded out of memory, and one a call
75//! gave back, and for those the answer is the same one the bytes get: a check that stays is a check
76//! that runs, and a check that runs tests the alignment and refuses when it does not hold. So the
77//! first access through a pointer somebody handed in proves for nothing what every later access
78//! through the same value needs, and `Scope::aligns` carries it.
79//!
80//! That fact is easier to carry than a range and it is worth saying why, because the section below
81//! spends a page arguing about what a call does to a range. A range is about storage and storage
82//! can be freed and handed back out smaller. An alignment is about the number in the value, and
83//! nothing in a function changes the number an SSA value holds, so it crosses a call, it crosses
84//! inline assembly and it crosses a `meta_end`. Dominance is the only thing that bounds it.
85//!
86//! What is still not answered is counted rather than argued about, the same as everything else
87//! here, so `-fopt-info-missed` says what the rest of the `!aligned` fact of section 6.2.4 would be
88//! worth. On the SQLite amalgamation that row is 6046 checks at 1178 sites, down from 10729 at 1413
89//! once a check's own answer is carried, and the checks in the assembly went from 28473 to 23790.
90//!
91//! # The fact nobody had to check for
92//!
93//! Section 7.2 lists four sources of a discharge and puts the frontend first, because the majority
94//! of accesses in real C are to a local or a global at a constant offset and the bounds of either
95//! are not something anybody has to find out. An `alloca` of a fixed size makes one storage
96//! instance of that many bytes and says so in its payload, so the range from its address to that
97//! many further along is inside one instance for exactly the reason a passing `check_bounds` says
98//! its own range is. When the address a check is about normalizes to such an `alloca`, that range
99//! is the fact, and the question put to the table is the same question with the same rule
100//! answering it.
101//!
102//! Two things make it worth more than a fact a check established. It is there before anything has
103//! run, so the first access to a local is discharged rather than only the second. And no call takes
104//! it away: a callee cannot free a frame slot, whatever it does to whatever the slot points at, so
105//! this fact is asked separately rather than kept in the set the walk throws away at the first call
106//! it cannot see through.
107//!
108//! Only the fixed size form. A variable length array is an `alloca` with an operand and a payload
109//! whose size field reads zero, and reading it anyway would discharge every check in the array.
110//!
111//! A global is the same fact about the other half of section 7.2's sentence, and it arrives here
112//! differently for one reason: how big a global is lives on the module and this pass is given one
113//! function. So `crate::extents` works it out over the module before the pipeline starts, asks the
114//! same rule, and writes the answer onto the check as [`Flags::STATIC`], which is what
115//! `crate::nofree` does with what a call reaches and for the same reason. What is read here is what
116//! the IR says, the same way the pass reads an opcode.
117//!
118//! It answers a lifetime check as well as a bounds check, which a local does not. What a local
119//! gives is an extent, and how long it stays alive is the block it was declared in, which is a
120//! question this pass has nothing to say about. A global has static storage duration and is alive
121//! wherever the question is asked.
122//!
123//! # The walk that stops at a step it cannot read
124//!
125//! Everything above needs the address to be a base and a constant, and an array index is not a
126//! constant. The walk stops at the first `ptr_add` whose step is a value, and what comes out is a
127//! fact about a base whose size nobody knows, which answers nothing.
128//!
129//! Section 7.2's third source is what gets past it. Document 10's ranges know something about the
130//! step even though it is not a number: an index the program has already tested against a length,
131//! or one whose low bits are all that is used, is bounded. So the walk carries on, adding the low
132//! end of the step's range to the offset and the width of the range to the size, and what it ends
133//! up with is the range of addresses the access can land in.
134//!
135//! Whether an object holding all of that range holds the one address the access actually uses is
136//! its own rule, `reached.i64`, which leaves the distance opaque so that one answer covers every
137//! value the step could take. It is a rule of its own rather than the containment rule asked about
138//! the far end of the range, and the reason is section 7.7's: turning a range of addresses into one
139//! containment question is arithmetic on the thing being proved, and a pass doing that quietly is
140//! what the split between the walk and the rule exists to stop.
141//!
142//! What the range is asked of is the list above and not a shorter one: the local an `alloca`
143//! declares, the object an allocator made where the program has tested it, and the ranges checks
144//! that already ran established. The allocation was missing from that list until tamnd/rucc#880,
145//! which is what left a loop walking an index into its own `malloc` with every check it started
146//! with however plainly the call said how many bytes it made.
147//!
148//! The range is only ever asked with and never recorded. What a check proves when it runs is that
149//! the address the program used was inside the object, and nothing at all about the rest of a
150//! range this pass made up around it. So a check discharged this way records the narrow fact, the
151//! bytes the access really wanted, which is the thing that was proved and is what a second check
152//! of the same bytes is answered by.
153//!
154//! The ranges are built only for a function that has a walk by a value in it, because they cost a
155//! copy of the control flow graph and a function without one would never ask them anything.
156//!
157//! # The lifetime half, and what it borrows from the other one
158//!
159//! A `check_live` that stays is a fact too, and a smaller one than it looks: it says the storage
160//! instance holding its own address is alive, and it says nothing about the address four bytes
161//! along, because that address might be in a different instance. On its own that fact discharges
162//! only a second lifetime check of the very same address, and the shape `rucc-safety` emits is a
163//! lifetime check per field rather than per object, so on its own it would almost never fire.
164//!
165//! What makes it fire is the bounds fact sitting next to it. A `check_bounds` that passed put its
166//! whole range inside one instance, so if the lifetime check's address is in that range, the
167//! instance that was found alive is the instance the whole range is in, and the whole range is
168//! alive. So a lifetime fact is recorded as the widest checked range containing its address, and a
169//! later lifetime check is asked about as a single byte. The question of whether that byte is in
170//! that range is the same question the bounds half asks, put to the same rule.
171//!
172//! The order the two arrive in is what makes this work rather than a coincidence to be careful
173//! about: `rucc-safety` emits the bounds check first and the lifetime check second, so the range is
174//! established by the time there is a lifetime fact to widen. A lifetime check that arrives with no
175//! range around it keeps the narrow fact, which is correct and worth little.
176//!
177//! # The derivation half, which is one question rather than two
178//!
179//! `rucc-safety` puts a `check_deriv` after every `ptr_add` off a pointer, and what it asks is not
180//! about a range at all: it asks whether the pointer that came out is still in the storage instance
181//! the pointer that went in belongs to. The runtime has some slack in it for a pointer that walked
182//! exactly off either end, and none of that slack is used here, because the case this pass answers
183//! is the one where both ends are plainly inside something.
184//!
185//! What answers it is one fact holding both ends. A `check_bounds` that passed put its whole range
186//! inside one instance, so if the address that went in and the address that came out are both in
187//! that range, the second is in the instance the first belongs to, which is the question. It has to
188//! be one fact and not one for each end: two facts saying two addresses are each inside some
189//! instance say nothing about whether it is the same instance, and that is the only thing being
190//! asked. A local is a fact of exactly this shape and is asked the same way.
191//!
192//! Both ends are asked about as a single byte, the way a lifetime check is, and for the same reason.
193//! Nothing here is claiming anything about how many bytes are readable at either address.
194//!
195//! A `check_deriv` that stays leaves no fact behind. What it establishes is that two addresses share
196//! an instance, which is not a range of bytes and does not fit in what this walk carries, and the
197//! `covered.i64` rule has nothing to say about it. Recording it would mean a second kind of fact and
198//! a second rule, and the pointer it is about nearly always gets a `check_bounds` of its own a few
199//! instructions later that establishes the range properly.
200//!
201//! # What a call throws away, and which calls throw away nothing
202//!
203//! Section 7.3 says nothing kills a bounds fact except a redefinition of the capability, which in
204//! SSA is never. This pass is stricter than that about the lifetime half and not about the bounds
205//! half: a call, or anything else this pass cannot see through, drops every lifetime fact it is
206//! carrying, and a call keeps the bounds facts and marks them as having had one run over them.
207//!
208//! The case a call is about is a `free` and then an allocation of something smaller at the same
209//! address. The range established before the call is no longer inside one instance after it, and
210//! what document 07 leaves that to is the lifetime judgement rather than this one. The next section
211//! is the argument that the lifetime judgement is now enough, and what a mark on a fact buys.
212//!
213//! A `meta_end` and a `meta_transfer` drop both halves, and so does inline assembly. Nothing emits
214//! either of the first two yet, so most of this costs nothing today and is the difference between
215//! conservative and wrong on the day the instrumentation starts ending lifetimes. `crate::nofree`
216//! treats them the same way. Assembly is with them rather than with the calls because the argument
217//! below rests on the runtime owning the planes, and a block of assembly can write over one without
218//! the runtime having been asked.
219//!
220//! The two facts nobody had to check for go across a call untouched, and neither is an exception to
221//! the paragraph above because neither is in the set being thrown away. A callee cannot free a
222//! frame slot and cannot free a global, so a check the declaration answers is answered on the far
223//! side of any call at all.
224//!
225//! A call that says it reaches nothing which can free is the exception, and it is not this pass
226//! being trusting. `crate::nofree` works the answer out over the whole module before the pipeline
227//! starts and writes it onto the call site as [`Flags::NOFREE`], because the fact belongs to the
228//! callee and a pass is given one function. Reading it here is reading what the IR says, the same
229//! way the pass reads an opcode. Nothing else about a call is believed: the lifetime facts still go
230//! across an unmarked call, a call through an address, and inline assembly.
231//!
232//! What the strictness still costs is measured rather than guessed. A check that a fact would have
233//! covered if a call had not intervened is counted, so `-fopt-info-missed` says per function what
234//! is left to win. On the SQLite amalgamation 3.53.4 at `-O2 -fsafety=detect` that is 3978 bounds
235//! checks at 605 sites, 2679 lifetime checks at 596 sites and 2538 derivation checks at 534 sites,
236//! against 28473 bounds checks and 20423 lifetime checks that survive the whole pipeline.
237//!
238//! # Why keeping the bounds half is allowed
239//!
240//! The eighth box of tamnd/rucc#1241 asked this pass to stop throwing the bounds facts away, on the
241//! grounds that the lifetime check at the access compares a version and will refuse the case the
242//! paragraph above is about. It is done and the reason belongs here rather than in the issue,
243//! because what it turns on is what this file does.
244//!
245//! The claim is that a bounds fact may cross a call when every access that then uses it is guarded
246//! by a lifetime check that refuses once the instance has changed. Three things have to hold for
247//! that. The first two hold since the runtime started reading the version a recovery found and
248//! started moving the aux with the bytes at a copy, and the third is what `Known::since` is for.
249//!
250//! The first holds. `rucc_safety::check` emits the two checks as a pair off one capability, so the
251//! only question is whether this pass took the lifetime half out again, and there are five ways it
252//! does. `REMOVED_LIVE_STATIC` is a global, `REMOVED_LIVE_LOCAL` is a frame slot of this
253//! function, and `REMOVED_LIVE_HANDED` is an object every caller hands in, which `crate::params`
254//! only ever says of a caller's frame slot or of a global this module vouches for. None of those
255//! three can be freed by anybody, so a bounds fact about one does not go stale in the first place.
256//! `REMOVED_LIVE` comes out of `Scope::alive`, which is thrown away at the call, so it cannot
257//! fire on the far side of one. `REMOVED_LIVE_RANGE` is the frame slot rule or `Scope::alive`
258//! widened, so it is those two again. On the far side of a call the lifetime check is therefore
259//! either still standing or about an object no callee can end.
260//!
261//! The second holds and did not when this section was first written. A lifetime check that is still
262//! standing refuses a changed instance only when the version the capability carries is about the
263//! pointer the access went through, and `rucc_safe_rt::check`'s `stale` used to take the weaker
264//! reading for every recovered capability, which is every pointer a `cap_of` could not trace back to
265//! an allocator call. A recovery that walked the planes answers now, so a pointer a function was
266//! handed is covered. A pointer it loaded out of memory is covered too, and that took a second
267//! thing: the capability for one of those comes out of the aux slot beside the word, a slot holds a
268//! displacement from the pointer it was written beside rather than an address, and a `memcpy` used
269//! to move the word and leave the slot. `rucc_safe_rt::check::relocate` moves the aux across at
270//! every copy a wrapper interposes, which is what tamnd/rucc#1148 wanted. What is left uncovered is
271//! a pointer stored by code this compiler did not build, and an object a foreign writer has touched
272//! is the case `rucc_safe_rt::layout::Meta::HANDED` already stands apart.
273//!
274//! The third is a hazard the relaxation introduces rather than one it inherits, and it is what the
275//! mark is for. A lifetime fact is widened by `widened` out of the bounds facts standing at the
276//! time, so bounds facts that survive a call would otherwise widen lifetime facts established after
277//! it. A bounds fact saying a range is inside one instance, taken before a `free` and an allocation
278//! of something smaller at the same address, would then widen a lifetime check that passed on the
279//! new instance into a claim that the whole of the old range is alive, and the far end of that
280//! range is storage the new instance does not own. So `Known::since` marks where the facts a call
281//! has run over end, `widened` reads only the ones after it, and the marked ones answer a bounds
282//! check and nothing else.
283//!
284//! The derivation rule reads only the unmarked ones too, and that one is caution rather than
285//! necessity. What a `check_deriv` asks is whether two addresses share an instance, and a marked
286//! fact answers the question it was established for rather than that one. Letting it read them
287//! would still refuse every case that matters, because the access through a pointer it wrongly let
288//! through has a lifetime check of its own that the version compare refuses, but the report would
289//! arrive at the access as judgement J1 instead of at the derivation as J2, and a derivation
290//! nothing is ever read through would go unreported. So it reads the unmarked ones and the cost is
291//! counted in the `PAST_A_CALL_DERIV` row.
292//!
293//! What it comes to, on the SQLite amalgamation 3.53.4 at `-O2 -fsafety=detect`, before against
294//! after. 414 bounds checks go, which is 28887 down to 28473, and the assembly shrinks by 107
295//! kilobytes. 131 derivation checks arrive, 22754 up to 22885, and they are the other half of the
296//! paragraph above: a bounds check that is removed establishes nothing, so a check the marked fact
297//! answered no longer pushes a fact of its own, and the derivation rule was reading that. Lifetime
298//! checks do not move at all, which is the point. Net it is 283 fewer checks in the object.
299//!
300//! Why it is only 414 is worth reading, because it says where the next piece of work is and it is
301//! not here. A bounds check has to pass the alignment guard before any rule may take it out, and
302//! the guard is only asked once a rule has answered, so the row counting what it costs only counts
303//! checks something was ready to remove. That row goes from 8464 checks to 10729. Those 2265 are
304//! bounds checks a fact that crossed a call now answers and the alignment guard then keeps anyway,
305//! and they are five times the number that got out. The alignment question is `settles` and
306//! `aligned` in this file, and it is the binding constraint on the bounds half now rather than the
307//! call is.
308
309use std::collections::{HashMap, HashSet};
310
311use rucc_ir::{Block, Def, Extra, Flags, Func, Inst, Opcode, Type, Value};
312
313use crate::range::query::Ranges;
314use crate::rules::{Piece, Subject, Table, safety};
315use crate::{Analyses, Analysis, Cfg, Fuel, Pass, Preserved, Stats, copy, heap};
316
317/// Recorded once for each bounds check taken out.
318const REMOVED: &str = "bounds check removed, a dominating check covers the same bytes";
319
320/// Recorded once for each bounds check taken out because it was inside a local.
321const REMOVED_LOCAL: &str = "bounds check removed, its bytes are inside a local this function \
322                             declares";
323
324/// Recorded once for each bounds check taken out because it was inside a global.
325const REMOVED_STATIC: &str = "bounds check removed, its bytes are inside an object of static \
326                              storage duration";
327
328/// Recorded once for each bounds check taken out because every caller hands in the object.
329const REMOVED_HANDED: &str = "bounds check removed, its bytes are inside an object every call to \
330                              this function hands it";
331
332/// Recorded once for each bounds check taken out because an allocator made the object.
333const REMOVED_MADE: &str = "bounds check removed, its bytes are inside an object an allocator made \
334                            and this function has tested";
335
336/// Recorded once for each bounds check taken out because a range answered the step it walked by.
337const REMOVED_RANGE: &str = "bounds check removed, every address the walk can reach is inside the \
338                             object it started from";
339
340/// Recorded for a bounds check removed by carrying its walk past the step the constant reader
341/// stopped at, all the way back to the pointer its capability names.
342const REMOVED_MIDWAY: &str = "bounds check removed, its walk was carried on to the pointer its \
343                              capability names and every address it can reach is held";
344
345/// Recorded once for each lifetime check taken out.
346const REMOVED_LIVE: &str = "lifetime check removed, a dominating check covers the same storage";
347
348/// Recorded once for each lifetime check taken out because it was inside a global.
349const REMOVED_LIVE_STATIC: &str =
350    "lifetime check removed, its storage lives as long as the program does";
351
352/// Recorded once for each lifetime check taken out because every caller hands in the object.
353const REMOVED_LIVE_HANDED: &str = "lifetime check removed, its storage is an object every call to \
354                                   this function hands it";
355
356/// Recorded once for each lifetime check taken out because it was inside a frame slot.
357const REMOVED_LIVE_LOCAL: &str =
358    "lifetime check removed, its storage is a frame slot of this function";
359
360/// Recorded once for each lifetime check taken out because a range answered the step it walked by.
361const REMOVED_LIVE_RANGE: &str = "lifetime check removed, every address the walk can reach is in \
362                                  storage a check found alive";
363
364/// The same as [`REMOVED_MIDWAY`], for a lifetime check.
365const REMOVED_MIDWAY_LIVE: &str = "lifetime check removed, its walk was carried on to the pointer \
366                                   its capability names and every address it can reach is alive";
367
368/// Recorded for a bounds check that would have gone if there had been fuel for it.
369const NO_FUEL: &str = "bounds check kept, the pass ran out of fuel";
370
371/// Recorded for a lifetime check that would have gone if there had been fuel for it.
372const NO_FUEL_LIVE: &str = "lifetime check kept, the pass ran out of fuel";
373
374/// Recorded once for each derivation check taken out because a range answered the step it walked by.
375const REMOVED_DERIV_RANGE: &str = "derivation check removed, every address either end can reach is \
376                                   inside one checked range";
377
378/// Recorded for a bounds check a call cost, which is the honest price of the paragraph above.
379///
380/// This one is worth reading rather than skipping. It is the number of checks that are still being
381/// paid for because `crate::nofree` could not vouch for a call, so it says per function what the
382/// rest of section 7.5's summary work would be worth before anybody writes it.
383const PAST_A_CALL: &str =
384    "bounds check kept, a call between it and the check that covers it might free";
385
386/// The same, for a lifetime check. Section 8.8 is about this number rather than the one above.
387const PAST_A_CALL_LIVE: &str =
388    "lifetime check kept, a call between it and the check that covers it might free";
389
390/// Recorded for a bounds check kept because nothing here says where the access starts.
391///
392/// The alignment conjunct of judgement J1 rides on `check_bounds`, so taking the check out takes
393/// the alignment test with it. Recorded only for a check a rule had already answered the bytes of,
394/// so the number is what the gate costs rather than how many checks have an alignment, which makes
395/// it what the `!aligned` fact of `spec/safe-memory/06-instrumentation.md` section 6.2.4 would be
396/// worth.
397///
398/// What is left in this row is the pointers no check has run on yet, since one that has is answered
399/// by [`Scope::proved`]. So it is now a count of first accesses rather than of all of them, and
400/// what would take it down further is the front end saying what a pointer is aligned to at the
401/// point it makes one.
402const UNKNOWN_ALIGNMENT: &str =
403    "bounds check kept, nothing here says the address is aligned to what the access assumes";
404
405/// Recorded for a bounds check whose address reached something saying an alignment that was short.
406///
407/// The other half of the row above, and it is separate because the two are worth different things.
408/// That one is a value nothing here says anything about, and a fact from somewhere else could take
409/// it. This one already has an answer and the answer is no, so a fact about where a pointer starts
410/// would change nothing.
411///
412/// Two shapes end up here and they are not the same, which is worth knowing before anybody reads
413/// the number as a target. One is `(int *)(p + 1)`, where the object is known and the step is a
414/// constant that lands a byte into it. That is row S7 and the check has to stay. The other is a
415/// step nobody can read, where [`divides`] answers one because every number divides by one, so the
416/// walk comes back saying a byte and means it does not know. Telling those apart is what a better
417/// [`divides`] would do and this row is where the work would show up.
418const LOST_ALIGNMENT: &str =
419    "bounds check kept, what the address was computed from says less alignment than it assumes";
420
421/// Recorded for a bounds check whose operands this pass cannot read.
422const UNKNOWN_SHAPE: &str = "bounds check left alone, its pointer is not a base and a constant";
423
424/// Recorded for a bounds check whose capability names neither its address nor the base of it.
425///
426/// The other way [`about`] gives up, and it is a different thing entirely from the row above. The
427/// capability here is readable and it names a value the address really was walked off, just not one
428/// of the two [`its_own`] accepts: `rucc_safety::origin` shares one capability down a whole
429/// derivation chain, and [`normal`] stops walking at the first step it cannot read, so a chain with
430/// a step like `i * 4` in it leaves the capability naming something further back than the base.
431/// Splitting this row into the three below it is what #1390 asked for, and the three sit in the
432/// order the pass fails at them. This one is the walk not getting back to the named pointer at all,
433/// which on the amalgamation is nothing, and the other two are the range rules refusing the walk it
434/// did get back.
435const MIDWAY_CAPABILITY: &str =
436    "bounds check left alone, its capability names a pointer further back than its base";
437
438/// Recorded for a midway bounds check where nothing at all is known about the pointer named.
439///
440/// The one that matters. [`beyond`] answered a range of addresses off the pointer the capability
441/// names, every rule was asked, and not one of them has ever heard of that pointer: it is not a
442/// local this function declared and no bounds check standing here is about it. So the range is not
443/// too wide and the walk is not wrong, there is simply no extent for the thing the capability was
444/// taken at, and no arrangement of the rules already here will produce one.
445const MIDWAY_NO_EXTENT: &str =
446    "bounds check left alone, nothing here says how far the object its capability names runs";
447
448/// Recorded for a midway bounds check whose walk reaches outside what is known about the pointer.
449///
450/// The honest refusals. Something is known about the pointer the capability names and the addresses
451/// the walk can reach are not all inside it, which is either a range that could be tighter or an
452/// access that really can go out of the object.
453const MIDWAY_OVER: &str =
454    "bounds check left alone, its walk can reach outside what is known about the object";
455
456/// Recorded for a bounds check about a range the program worked out.
457const COMPUTED_EXTENT: &str =
458    "bounds check left alone, how many bytes it covers is a number only the program has";
459
460/// Recorded for a lifetime check whose operands this pass cannot read.
461const UNKNOWN_SHAPE_LIVE: &str =
462    "lifetime check left alone, its pointer is not a base and a constant";
463
464/// The same as [`MIDWAY_CAPABILITY`], for a lifetime check.
465const MIDWAY_CAPABILITY_LIVE: &str =
466    "lifetime check left alone, its capability names a pointer further back than its base";
467
468/// The same as [`MIDWAY_NO_EXTENT`], for a lifetime check.
469const MIDWAY_NO_EXTENT_LIVE: &str =
470    "lifetime check left alone, nothing here says how far the object its capability names runs";
471
472/// The same as [`MIDWAY_OVER`], for a lifetime check.
473const MIDWAY_OVER_LIVE: &str =
474    "lifetime check left alone, its walk can reach outside what is known about the object";
475
476/// Recorded once for each derivation check taken out.
477const REMOVED_DERIV: &str =
478    "derivation check removed, one checked range holds both the pointer and where it walked to";
479
480/// Recorded once for each derivation check taken out because it walked inside a local.
481const REMOVED_DERIV_LOCAL: &str =
482    "derivation check removed, it walks inside a local this function declares";
483
484/// Recorded once for each derivation check taken out because it walked inside a global.
485const REMOVED_DERIV_STATIC: &str =
486    "derivation check removed, it walks inside an object of static storage duration";
487
488/// Recorded once for each derivation check taken out because every caller hands in the object.
489const REMOVED_DERIV_HANDED: &str = "derivation check removed, it walks inside an object every call \
490                                    to this function hands it";
491
492/// Recorded once for each derivation check taken out because an allocator made the object.
493const REMOVED_DERIV_MADE: &str = "derivation check removed, it walks inside an object an allocator \
494                                  made and this function has tested";
495
496/// Recorded for a derivation check that would have gone if there had been fuel for it.
497const NO_FUEL_DERIV: &str = "derivation check kept, the pass ran out of fuel";
498
499/// Recorded for a derivation check a call cost.
500const PAST_A_CALL_DERIV: &str =
501    "derivation check kept, a call between it and the range that holds both ends might free";
502
503/// Recorded for a derivation check naming a capability that is not the one it is about.
504const NOT_ITS_CAPABILITY_DERIV: &str = "derivation check left alone, the capability it names is not the one the pointer that went in \
505     carries";
506
507/// Recorded for a derivation check whose two ends are not off one value.
508const TWO_BASES_DERIV: &str =
509    "derivation check left alone, its two pointers are not built on one base";
510
511/// Recorded for a derivation check whose walk can reach past the end of the local it starts in.
512const OVER_THE_LOCAL_DERIV: &str =
513    "derivation check left alone, the walk can reach past the end of the local it starts in";
514
515/// Recorded for a derivation check on a pointer this function loaded out of memory.
516const NO_EXTENT_LOADED: &str = "derivation check left alone, nothing here says how big the object \
517                                is and the pointer to it was loaded from memory";
518
519/// Recorded for a derivation check on a pointer this function was handed.
520const NO_EXTENT_HANDED: &str = "derivation check left alone, nothing here says how big the object \
521                                is and the pointer to it was handed to this function";
522
523/// Recorded for a derivation check on a pointer into a global.
524const NO_EXTENT_GLOBAL: &str = "derivation check left alone, nothing here says how big the object \
525                                is and the pointer to it is into a global";
526
527/// Recorded for a derivation check on a pointer a call handed back.
528const NO_EXTENT_RETURNED: &str = "derivation check left alone, nothing here says how big the \
529                                  object is and the pointer to it came back from a call";
530
531/// Recorded for a derivation check on a pointer none of the shapes above describes.
532const NO_EXTENT_OTHER: &str =
533    "derivation check left alone, nothing here says how big the object its pointers are in is";
534
535/// The pass. It holds nothing, because everything it works out is about one function.
536/// Which of the places a fact comes from a run of this pass may ask.
537///
538/// Everything is asked normally and there is one pass in the pipeline. The others are here for the
539/// measurement `spec/safe-memory/13-performance.md` section 13.5 asks for and
540/// `spec/safe-memory/17-open-questions.md` question 3 is: how much each source discharges on its
541/// own, and how much the same sources discharge together. A number for a source on its own cannot
542/// be read off the remarks of a full run, because the rules are asked in an order and whichever one
543/// answers first is the one the remark names, so the second source to be asked about a check two of
544/// them could answer looks like it answered nothing.
545///
546/// The four are document 07 section 7.2's four, with the caveat the measurement found: the ranges
547/// are not a fourth kind of fact but a way of asking the other three about a subscript instead of
548/// about an address written out in the program.
549#[derive(Clone, Copy, PartialEq, Eq, Debug)]
550pub struct Sources {
551    /// How big an object is, read off whatever made it. A global's extent comes from
552    /// `crate::extents`, a local's from its `alloca`, an allocation's from the call `crate::heap`
553    /// marked. Section 7.2's first source.
554    objects: bool,
555    /// What a check that has already run established, carried down the dominator tree. Section 7.3,
556    /// and the one the literature calls redundant check elimination.
557    dominance: bool,
558    /// What every caller of this function guarantees about what it was handed, from
559    /// `crate::params`. Section 7.5.
560    summaries: bool,
561    /// The value ranges and the recurrences, which widen the one address a check names into the
562    /// range of addresses a walk can reach so that the other three can be asked about a subscript.
563    /// Section 7.4, and the half of the PICO result this pass holds. The other half is
564    /// [`crate::hoist`] and [`crate::split`], which are passes of their own and have flags of their
565    /// own.
566    ranges: bool,
567}
568
569impl Sources {
570    /// Every one of them, which is what the pipeline runs.
571    pub const ALL: Self = Self { objects: true, dominance: true, summaries: true, ranges: true };
572    /// What an object says about itself and nothing else.
573    pub const OBJECTS: Self =
574        Self { objects: true, dominance: false, summaries: false, ranges: false };
575    /// What an earlier check established and nothing else.
576    pub const DOMINANCE: Self =
577        Self { objects: false, dominance: true, summaries: false, ranges: false };
578    /// What every caller guarantees and nothing else.
579    pub const SUMMARIES: Self =
580        Self { objects: false, dominance: false, summaries: true, ranges: false };
581    /// Every fact, asked only about addresses written out in the program.
582    pub const NARROW: Self =
583        Self { objects: true, dominance: true, summaries: true, ranges: false };
584}
585
586#[derive(Debug, Clone, Copy, PartialEq, Eq)]
587pub struct Discharge {
588    /// What `-f<name>` and `-fno-<name>` reach this run by.
589    name: &'static str,
590    /// Which places it may take a fact from. See [`Sources`].
591    sources: Sources,
592}
593
594/// The pass the pipeline runs, which asks everything.
595pub static DISCHARGE: Discharge = Discharge { name: "discharge", sources: Sources::ALL };
596
597/// The same pass asking an object how big it is and nothing else.
598pub static OBJECTS: Discharge = Discharge { name: "discharge-objects", sources: Sources::OBJECTS };
599
600/// The same pass asking what an earlier check established and nothing else.
601pub static DOMINANCE: Discharge =
602    Discharge { name: "discharge-dominance", sources: Sources::DOMINANCE };
603
604/// The same pass asking what every caller guarantees and nothing else.
605pub static SUMMARIES: Discharge =
606    Discharge { name: "discharge-summaries", sources: Sources::SUMMARIES };
607
608/// The same pass asking every fact, about addresses written out in the program only.
609pub static NARROW: Discharge = Discharge { name: "discharge-narrow", sources: Sources::NARROW };
610
611/// The same pass asking everything, under a name of its own.
612///
613/// [`DISCHARGE`] already asks everything, so this looks like a duplicate and is not. A pass the level
614/// did not choose goes on the end of the pipeline, so a run of `-fno-discharge -fdischarge-objects`
615/// asks its question in a different place from where the shipped pass asks it, and the two numbers
616/// are not comparable. This one is turned on the same way as the others and lands in the same place,
617/// so the sum of the parts and the whole are measured under one arrangement. What it costs against
618/// [`DISCHARGE`] is what the position is worth, which is a number the measurement wants anyway.
619pub static EVERY: Discharge = Discharge { name: "discharge-every", sources: Sources::ALL };
620
621impl Pass for Discharge {
622    fn name(&self) -> &'static str {
623        self.name
624    }
625
626    fn describe(&self) -> &'static str {
627        "a bounds, lifetime or derivation check whose answer is already known is removed"
628    }
629
630    fn preserves(&self) -> Preserved {
631        // Instructions go and blocks do not. A check is not a terminator and removing one leaves
632        // every edge where it was. What it does not leave where it was is the liveness, because
633        // the check was reading something and now nothing is.
634        Preserved::ALL.without(Analysis::Liveness)
635    }
636
637    fn run(&self, func: &mut Func, an: &mut Analyses, fuel: &mut Fuel) -> Stats {
638        let mut stats = Stats::new();
639        let Some(entry) = func.entry() else { return stats };
640        let dom = an.dominators(func);
641
642        // The graph is built for two reasons and neither is the common one, so a function with
643        // neither pays for no copy of it. The ranges want it when there is a walk the constant
644        // reader gives up on, and the allocation rule wants it to find where the program has tested
645        // what an allocator gave it.
646        let walks = self.sources.ranges && walks_by_a_value(func);
647        let cfg = (walks
648            || joins_a_pointer(func, entry)
649            || (self.sources.objects && heap::allocates(func)))
650        .then(|| an.cfg(func));
651        let mut ranges = cfg.filter(|_| walks).map(|cfg| Ranges::new(&*func, cfg, dom));
652
653        // One answer per allocation rather than one per check, because a function that reads twenty
654        // fields of the same object asks the same question about the same pointer twenty times.
655        let mut checked: HashMap<Value, HashSet<Block>> = HashMap::new();
656
657        // Whether anything in here says a lifetime is over. Read once over the whole function
658        // rather than carried down the walk, because what the frame slot rule needs is that no
659        // `meta_end` runs before the check on any path, and a fact carried down the dominator
660        // tree only ever says something about the paths that go through one block.
661        let ends = ends_a_lifetime(func);
662
663        // The walk is a stack rather than recursion because the dominator tree of a long chain of
664        // blocks is as deep as the function is long, and a pass is not a place to find that out.
665        // Each block carries its own copy of what holds at its start, which is what makes a fact a
666        // call killed in one arm of a branch still hold in the other.
667        let mut going: Vec<(Inst, &'static str)> = Vec::new();
668        let mut work = vec![(entry, Scope::default())];
669        while let Some((block, mut scope)) = work.pop() {
670            for inst in func.insts(block).collect::<Vec<Inst>>() {
671                match opaque(func, inst) {
672                    Some(Opaque::Called) => {
673                        scope.called();
674                        continue;
675                    }
676                    Some(Opaque::Everything) => {
677                        scope.forget();
678                        continue;
679                    }
680                    None => {}
681                }
682                match func[inst].opcode {
683                    Opcode::CheckBounds => {
684                        if func[func[inst].args].len() > 2 {
685                            stats.missed(COMPUTED_EXTENT);
686                            scope.proved(func, inst);
687                            continue;
688                        }
689                        let Some(asked) = about(func, inst) else {
690                            // The constant reader could not name the address, so the rules that
691                            // take one address never run. The range reader can, past the step the
692                            // other one stopped at, and only when the base it lands on is the
693                            // pointer the capability names.
694                            let mid = midway(func, inst);
695                            let size = match func[inst].extra {
696                                Extra::Mem(info) => i128::from(func[info].size),
697                                _ => 0,
698                            };
699                            let span =
700                                mid.then(|| beyond(func, ranges.as_mut(), inst, size)).flatten();
701                            let wide = span
702                                .filter(|wide| {
703                                    (self.sources.objects
704                                        && declared(func, wide.base)
705                                            .is_some_and(|local| reaches(&local, wide)))
706                                        || (self.sources.objects
707                                            && allocated_around(
708                                                func,
709                                                cfg,
710                                                &mut checked,
711                                                block,
712                                                &[wide],
713                                            ))
714                                        || (self.sources.dominance && scope.bounds.reaches(wide))
715                                })
716                                .filter(|_| match aligned(func, cfg, &scope.aligns, inst) {
717                                    Alignment::Answered => true,
718                                    Alignment::Unknown => {
719                                        stats.missed(UNKNOWN_ALIGNMENT);
720                                        false
721                                    }
722                                    Alignment::Lost => {
723                                        stats.missed(LOST_ALIGNMENT);
724                                        false
725                                    }
726                                });
727                            if wide.is_some() {
728                                if fuel.take() {
729                                    going.push((inst, REMOVED_MIDWAY));
730                                } else {
731                                    stats.missed(NO_FUEL);
732                                    scope.proved(func, inst);
733                                }
734                                continue;
735                            }
736                            stats.missed(if mid {
737                                why_midway(func, &scope.bounds, span, false)
738                            } else {
739                                UNKNOWN_SHAPE
740                            });
741                            scope.proved(func, inst);
742                            continue;
743                        };
744                        // The four objects whose extent is known without anybody having checked
745                        // it. A global was worked out over the module by `crate::extents` and an
746                        // object every caller hands in by `crate::params`, both of which arrive as
747                        // a flag; a local is read off its `alloca` here and an allocation off the
748                        // call `crate::heap` marked. All four are asked of the same rule as every
749                        // other fact. The reach of a walk the constant reader could not finish is
750                        // asked last, because it is the only one that costs an analysis to answer.
751                        let why = if self.sources.objects
752                            && func[inst].flags.contains(Flags::STATIC)
753                        {
754                            Some(REMOVED_STATIC)
755                        } else if self.sources.summaries && func[inst].flags.contains(Flags::HANDED)
756                        {
757                            Some(REMOVED_HANDED)
758                        } else if self.sources.objects
759                            && declared(func, asked.base)
760                                .is_some_and(|local| covers(&local, &asked))
761                        {
762                            Some(REMOVED_LOCAL)
763                        } else if self.sources.objects
764                            && allocated(func, cfg, &mut checked, block, &[&asked])
765                        {
766                            Some(REMOVED_MADE)
767                        } else if self.sources.dominance && scope.bounds.covers(&asked) {
768                            Some(REMOVED)
769                        } else {
770                            // The same four sources in the same order, asked of the range of
771                            // addresses the walk can reach rather than of the one address the
772                            // constant reader could name. A flag has already been read above and
773                            // reading it again would say the same thing, so what is left is the
774                            // local, the allocation and what the walk carries.
775                            reach(func, ranges.as_mut(), &asked, inst).and_then(|wide| {
776                                if self.sources.objects
777                                    && declared(func, wide.base)
778                                        .is_some_and(|local| reaches(&local, &wide))
779                                {
780                                    Some(REMOVED_RANGE)
781                                } else if self.sources.objects
782                                    && allocated_around(func, cfg, &mut checked, block, &[&wide])
783                                {
784                                    Some(REMOVED_MADE)
785                                } else if self.sources.dominance && scope.bounds.reaches(&wide) {
786                                    Some(REMOVED_RANGE)
787                                } else {
788                                    None
789                                }
790                            })
791                        };
792                        // Asked once a rule has answered the bounds rather than in front of them
793                        // all, because a check that was staying anyway costs the gate nothing and
794                        // the number somebody reads has to be what it actually costs. A check kept
795                        // here still runs, so it still establishes what it was about.
796                        let why = why.filter(|_| match aligned(func, cfg, &scope.aligns, inst) {
797                            Alignment::Answered => true,
798                            Alignment::Unknown => {
799                                stats.missed(UNKNOWN_ALIGNMENT);
800                                false
801                            }
802                            Alignment::Lost => {
803                                stats.missed(LOST_ALIGNMENT);
804                                false
805                            }
806                        });
807                        let Some(why) = why else {
808                            if scope.bounds.covered_before(&asked) {
809                                stats.missed(PAST_A_CALL);
810                            }
811                            // A check that stays is a check that runs, and a check that runs
812                            // establishes what it was about. One that was removed establishes
813                            // nothing new: whatever covered it covers everything it would have.
814                            // Both halves of what it was about, since the alignment conjunct rides
815                            // on this check and the gate above may well be the thing that kept it.
816                            scope.bounds.held.push(asked);
817                            scope.proved(func, inst);
818                            continue;
819                        };
820                        if !fuel.take() {
821                            stats.missed(NO_FUEL);
822                            scope.bounds.held.push(asked);
823                            scope.proved(func, inst);
824                            continue;
825                        }
826                        // A check that goes normally establishes nothing new, because whatever
827                        // answered it covers everything it would have. The range is the one
828                        // exception: what answered it was a fact about a made up range around the
829                        // address, and the next check on these bytes has to ask for that range
830                        // again and may not get the same answer. So the narrow fact goes in, which
831                        // is the thing that was actually proved.
832                        if why == REMOVED_RANGE {
833                            scope.bounds.held.push(asked);
834                        }
835                        going.push((inst, why));
836                    }
837                    Opcode::CheckLive => {
838                        let Some(asked) = alive(func, inst) else {
839                            // The bounds arm's paragraph, and the same two rules this arm already
840                            // asks of a range, which is a local that holds everything the walk can
841                            // reach and a lifetime fact that does.
842                            let mid = midway(func, inst);
843                            let span =
844                                mid.then(|| beyond(func, ranges.as_mut(), inst, 1)).flatten();
845                            let wide = span.filter(|wide| {
846                                (self.sources.objects
847                                    && !ends
848                                    && declared(func, wide.base)
849                                        .is_some_and(|local| reaches(&local, wide)))
850                                    || (self.sources.dominance && scope.alive.reaches(wide))
851                            });
852                            if wide.is_some() {
853                                if fuel.take() {
854                                    going.push((inst, REMOVED_MIDWAY_LIVE));
855                                } else {
856                                    stats.missed(NO_FUEL_LIVE);
857                                }
858                                continue;
859                            }
860                            stats.missed(if mid {
861                                why_midway(func, &scope.alive, span, true)
862                            } else {
863                                UNKNOWN_SHAPE_LIVE
864                            });
865                            continue;
866                        };
867                        // A global is alive as long as the program is, and a frame slot is alive
868                        // until the function returns, so both objects whose extent is known
869                        // without anybody having checked it answer this as well as a bounds
870                        // check. `ends` is what makes the second one true: where a local stops
871                        // being alive is written into the IR as `meta_end` and not read off the
872                        // shape of the source, so a function with one in it is a function this
873                        // does not claim anything about.
874                        let why = if self.sources.objects
875                            && func[inst].flags.contains(Flags::STATIC)
876                        {
877                            Some(REMOVED_LIVE_STATIC)
878                        } else if self.sources.summaries && func[inst].flags.contains(Flags::HANDED)
879                        {
880                            Some(REMOVED_LIVE_HANDED)
881                        } else if self.sources.objects
882                            && !ends
883                            && declared(func, asked.base)
884                                .is_some_and(|local| covers(&local, &asked))
885                        {
886                            Some(REMOVED_LIVE_LOCAL)
887                        } else if self.sources.dominance && scope.alive.covers(&asked) {
888                            Some(REMOVED_LIVE)
889                        } else {
890                            // A lifetime fact and not a bounds one, because what is being asked
891                            // is whether the storage is alive and a bounds check that passed says
892                            // nothing about that. The widening argument is the bounds arm's: a
893                            // range known alive that holds every address the walk can reach holds
894                            // the one it actually uses.
895                            reach(func, ranges.as_mut(), &asked, inst)
896                                .filter(|wide| {
897                                    (self.sources.objects
898                                        && !ends
899                                        && declared(func, wide.base)
900                                            .is_some_and(|local| reaches(&local, wide)))
901                                        || (self.sources.dominance && scope.alive.reaches(wide))
902                                })
903                                .map(|_| REMOVED_LIVE_RANGE)
904                        };
905                        let Some(why) = why else {
906                            if scope.alive.covered_before(&asked) {
907                                stats.missed(PAST_A_CALL_LIVE);
908                            }
909                            scope.alive.held.push(widened(func, &scope.bounds, asked));
910                            continue;
911                        };
912                        if !fuel.take() {
913                            stats.missed(NO_FUEL_LIVE);
914                            scope.alive.held.push(widened(func, &scope.bounds, asked));
915                            continue;
916                        }
917                        // The bounds arm's exception, for its reason. A range answered a made up
918                        // range around this address, so what was proved is about the address.
919                        if why == REMOVED_LIVE_RANGE {
920                            scope.alive.held.push(widened(func, &scope.bounds, asked));
921                        }
922                        going.push((inst, why));
923                    }
924                    Opcode::CheckDeriv => {
925                        let narrow = derives(func, inst);
926                        let why = narrow.and_then(|(from, to)| {
927                            if self.sources.objects && func[inst].flags.contains(Flags::STATIC) {
928                                Some(REMOVED_DERIV_STATIC)
929                            } else if self.sources.summaries
930                                && func[inst].flags.contains(Flags::HANDED)
931                            {
932                                Some(REMOVED_DERIV_HANDED)
933                            } else if self.sources.objects
934                                && declared(func, from.base).is_some_and(|local| {
935                                    covers(&local, &from) && covers(&local, &to)
936                                })
937                            {
938                                Some(REMOVED_DERIV_LOCAL)
939                            } else if self.sources.objects
940                                && allocated(func, cfg, &mut checked, block, &[&from, &to])
941                            {
942                                Some(REMOVED_DERIV_MADE)
943                            } else if self.sources.dominance && scope.bounds.holds_both(&from, &to)
944                            {
945                                Some(REMOVED_DERIV)
946                            } else {
947                                None
948                            }
949                        });
950                        // Asked last, and asked off the check's own operands rather than off what
951                        // `derives` worked out, because the case it is for is the one `derives`
952                        // cannot read at all: past a step the constant reader gives up on the two
953                        // ends are not one base and two constants. One thing has to hold both of
954                        // the ranges, for the same reason one thing has to hold both of the
955                        // addresses, which is that two things saying each end is inside something
956                        // say nothing about it being the same something.
957                        let why = why.or_else(|| {
958                            spread(func, ranges.as_mut(), inst, inst).and_then(|(near, far)| {
959                                if self.sources.objects
960                                    && declared(func, near.base).is_some_and(|local| {
961                                        reaches(&local, &near) && reaches(&local, &far)
962                                    })
963                                {
964                                    Some(REMOVED_DERIV_RANGE)
965                                } else if self.sources.objects
966                                    && allocated_around(
967                                        func,
968                                        cfg,
969                                        &mut checked,
970                                        block,
971                                        &[&near, &far],
972                                    )
973                                {
974                                    Some(REMOVED_DERIV_MADE)
975                                } else if self.sources.dominance
976                                    && scope.bounds.reaches_both(&near, &far)
977                                {
978                                    Some(REMOVED_DERIV_RANGE)
979                                } else {
980                                    None
981                                }
982                            })
983                        });
984                        let Some(why) = why else {
985                            match narrow {
986                                Some((from, to)) => {
987                                    if scope.bounds.held_both_before(&from, &to) {
988                                        stats.missed(PAST_A_CALL_DERIV);
989                                    }
990                                }
991                                None => {
992                                    stats.missed(unreadable(func, ranges.as_mut(), inst));
993                                }
994                            }
995                            continue;
996                        };
997                        if !fuel.take() {
998                            stats.missed(NO_FUEL_DERIV);
999                            continue;
1000                        }
1001                        going.push((inst, why));
1002                    }
1003                    _ => continue,
1004                }
1005            }
1006            for child in dom.children(block) {
1007                work.push((child, scope.clone()));
1008            }
1009        }
1010
1011        for (inst, why) in going {
1012            func.remove_inst(inst);
1013            stats.optimized(why);
1014        }
1015        stats
1016    }
1017}
1018
1019/// A range of bytes some check has already been passed on, or is being asked about.
1020///
1021/// The address is kept as the value it was computed from and the constant distance from it, rather
1022/// than as the pointer itself, because that is what makes two of these comparable: the whole of
1023/// what this pass knows about two addresses is that they are one value plus two constants.
1024#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1025pub(crate) struct Fact {
1026    /// The value the address was computed from.
1027    pub(crate) base: Value,
1028    /// How far past it the access starts.
1029    pub(crate) offset: i128,
1030    /// How many bytes it covers.
1031    size: i128,
1032}
1033
1034impl Fact {
1035    /// The whole of an object whose extent is known, starting at its own address.
1036    ///
1037    /// The two sources of one of these are an `alloca` of a fixed size and a global, and what they
1038    /// have in common is that the size is said by something other than a check that passed.
1039    pub(crate) fn whole(base: Value, size: i128) -> Self {
1040        Self { base, offset: 0, size }
1041    }
1042
1043    /// A range of bytes named by where it starts and how far it runs.
1044    ///
1045    /// The general form of [`Fact::whole`], for a caller that has both ends of a range in hand
1046    /// rather than an object. `crate::dead_plane` is the one, and what it has is a plane write
1047    /// rather than an access, which is a different thing to be about and the same thing to ask.
1048    pub(crate) fn range(base: Value, offset: i128, size: i128) -> Self {
1049        Self { base, offset, size }
1050    }
1051}
1052
1053/// A range of addresses an access can land in, and how many bytes it takes when it does.
1054///
1055/// What [`reach`] works out and the only thing it is used for. It is deliberately not a [`Fact`]:
1056/// a fact is something that was established and may be recorded, and this is a question and may
1057/// not. The address the program uses is `base` plus somewhere between `low` and `low` plus `width`
1058/// further along, and what a check proves when it runs is about that one address rather than about
1059/// the range this was made out of.
1060#[derive(Debug, Clone, Copy)]
1061struct Reach {
1062    /// The value the address was computed from.
1063    base: Value,
1064    /// The nearest the access can start to it.
1065    low: i128,
1066    /// How much further than that it can start.
1067    width: i128,
1068    /// How many bytes it covers.
1069    size: i128,
1070}
1071
1072/// One kind of fact, and what has become of it.
1073#[derive(Debug, Clone, Default)]
1074struct Known {
1075    /// The ranges a check has been passed on and nothing has cast doubt on since.
1076    held: Vec<Fact>,
1077    /// The ones a call threw away, kept only so that the cost of throwing them away is a number
1078    /// somebody can read rather than a paragraph somebody has to believe.
1079    lost: Vec<Fact>,
1080    /// Where in [`Known::held`] the facts established since the last call begin.
1081    ///
1082    /// Everything in front of that index is a fact that was true when it was established and has
1083    /// had a call run over it since. For the bounds half those facts still answer a bounds check,
1084    /// which is what [`Known::crossed`] is about, and there are two questions they may not answer.
1085    /// An index rather than a flag on each fact because nothing ever takes one out of the middle:
1086    /// the vector is pushed and emptied and never anything else, so the ones from before the call
1087    /// are exactly the ones in front of a mark.
1088    since: usize,
1089}
1090
1091impl Known {
1092    /// Whether something still standing answers this.
1093    fn covers(&self, asked: &Fact) -> bool {
1094        self.held.iter().any(|fact| covers(fact, asked))
1095    }
1096
1097    /// Whether something still standing answers a range of addresses an access can land in.
1098    fn reaches(&self, asked: &Reach) -> bool {
1099        self.held.iter().any(|fact| reaches(fact, asked))
1100    }
1101
1102    /// The facts no call has run over, which is the only kind two of the rules may read.
1103    fn fresh(&self) -> &[Fact] {
1104        let from = self.since.min(self.held.len());
1105        &self.held[from..]
1106    }
1107
1108    /// The facts a call has run over, which is what the cost of not reading them is counted from.
1109    fn stale(&self) -> impl Iterator<Item = &Fact> {
1110        let upto = self.since.min(self.held.len());
1111        self.held[..upto].iter().chain(self.lost.iter())
1112    }
1113
1114    /// Whether one thing still standing answers both of these ranges.
1115    ///
1116    /// One rather than one each, for the reason [`Known::holds_both`] gives, and the reason does
1117    /// not change when the ends are ranges instead of addresses.
1118    fn reaches_both(&self, from: &Reach, to: &Reach) -> bool {
1119        self.fresh().iter().any(|fact| reaches(fact, from) && reaches(fact, to))
1120    }
1121
1122    /// Whether something would have answered it before a call came along.
1123    fn covered_before(&self, asked: &Fact) -> bool {
1124        self.stale().any(|fact| covers(fact, asked))
1125    }
1126
1127    /// Whether one thing still standing answers both of these.
1128    ///
1129    /// One rather than one each, which is the whole point of asking it this way. Two facts saying
1130    /// two addresses are each inside some instance say nothing about whether it is the same
1131    /// instance, and that is the only thing a derivation check wants to know.
1132    fn holds_both(&self, from: &Fact, to: &Fact) -> bool {
1133        self.fresh().iter().any(|fact| covers(fact, from) && covers(fact, to))
1134    }
1135
1136    /// Whether one would have answered both before a call came along.
1137    fn held_both_before(&self, from: &Fact, to: &Fact) -> bool {
1138        self.stale().any(|fact| covers(fact, from) && covers(fact, to))
1139    }
1140
1141    /// Gives up everything, because something happened that this pass cannot see through.
1142    fn forget(&mut self) {
1143        self.lost.append(&mut self.held);
1144        self.since = 0;
1145    }
1146
1147    /// Keeps everything and marks it as having had a call run over it.
1148    ///
1149    /// The bounds half only, and the module comment's section on what keeping it takes is the whole
1150    /// argument for why that is allowed. In one line: the lifetime check beside the access is still
1151    /// there, it compares the version the capability carries against the plane's, and a range that
1152    /// was inside one instance is inside that instance still or is about to be refused.
1153    fn crossed(&mut self) {
1154        self.since = self.held.len();
1155    }
1156}
1157
1158/// What holds where the walk has got to.
1159///
1160/// The two kinds are apart because they are killed together and answered separately: a range being
1161/// inside one instance and that instance being alive are different claims, and reporting them as
1162/// one number would hide which of the two a check is still being paid for.
1163#[derive(Debug, Clone, Default)]
1164struct Scope {
1165    /// Ranges a `check_bounds` established are inside one storage instance.
1166    bounds: Known,
1167    /// Ranges a `check_live` established are in an instance that is alive.
1168    alive: Known,
1169    /// What a `check_bounds` that ran proved about where the address it was about starts.
1170    ///
1171    /// Nothing here is ever given up, and that is the difference between this and the other two.
1172    /// They are facts about storage, and storage can be freed and handed back out, which is the
1173    /// whole of what a call does to them. This is a fact about the number a value holds, and
1174    /// nothing in a function changes the number an SSA value holds. So it survives a call, it
1175    /// survives inline assembly and it survives a `meta_end`, and dominance is the only thing that
1176    /// bounds it, which the walk already handles by giving each child its own copy.
1177    aligns: HashMap<Value, u64>,
1178}
1179
1180impl Scope {
1181    /// Gives up every fact of either kind. The alignment facts are not one of the two, and
1182    /// [`Scope::aligns`] says why they are not given up here or anywhere else.
1183    fn forget(&mut self) {
1184        self.bounds.forget();
1185        self.alive.forget();
1186    }
1187
1188    /// Records what a `check_bounds` that is staying proves about where its address starts.
1189    ///
1190    /// The check runs the alignment conjunct, which is `addr & (align - 1) != 0` in the runtime's
1191    /// `bounds`, and refuses when it does not hold. So on any path past the check the address is a
1192    /// multiple of what the access assumed, and the first access through a pointer somebody handed
1193    /// in proves for nothing what every later access through the same value needs.
1194    ///
1195    /// Only for a check that stays. One that is removed does not run and proves nothing, and it
1196    /// needs nothing either, since [`aligned`] answered it before it was allowed to go.
1197    fn proved(&mut self, func: &Func, check: Inst) {
1198        let Extra::Mem(info) = func[check].extra else { return };
1199        let claim = u64::from(func[info].align);
1200        let Some(&pointer) = func[func[check].args].get(1) else { return };
1201        if claim > 1 {
1202            let held = self.aligns.entry(pointer).or_default();
1203            *held = (*held).max(claim);
1204        }
1205    }
1206
1207    /// Gives up the lifetime facts and keeps the bounds ones, marked as a call having run over them.
1208    fn called(&mut self) {
1209        self.bounds.crossed();
1210        self.alive.forget();
1211    }
1212}
1213
1214/// What an instruction the pass cannot see through does to the facts the walk is carrying.
1215#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1216enum Opaque {
1217    /// A call that might free. The lifetime facts go and the bounds facts stay, marked.
1218    Called,
1219    /// Everything else, which gives up both halves.
1220    Everything,
1221}
1222
1223/// Whether this instruction could do something to memory that this pass cannot account for, and
1224/// what that means for what the walk is carrying.
1225///
1226/// A call is most of it, in every spelling, and inline assembly with it. A `tail_call` ends the
1227/// block and there is nothing after it to protect, and it is here anyway so that the reason a fact
1228/// survives is never that the walk did not think of something.
1229///
1230/// A call carrying [`Flags::NOFREE`] reaches nothing that ends a lifetime, so there is nothing for
1231/// it to have done to the bytes an earlier check was passed on. `crate::nofree` is what put the
1232/// flag there and what argues for it.
1233///
1234/// A `meta_end` and a `meta_transfer` end a lifetime by saying so, which is the plainest way for a
1235/// fact to stop being true, and neither is emitted today. Inline assembly is with them rather than
1236/// with the calls, because the argument for keeping the bounds half rests on the lifetime check at
1237/// the access reading a plane the runtime wrote, and a block of assembly is the one thing in the
1238/// IR that can write over a plane without the runtime having been asked.
1239fn opaque(func: &Func, inst: Inst) -> Option<Opaque> {
1240    match func[inst].opcode {
1241        Opcode::Call | Opcode::CallIndirect | Opcode::TailCall => {
1242            (!func[inst].flags.contains(Flags::NOFREE)).then_some(Opaque::Called)
1243        }
1244        Opcode::InlineAsm | Opcode::MetaEnd | Opcode::MetaTransfer => Some(Opaque::Everything),
1245        _ => None,
1246    }
1247}
1248
1249/// What a `check_bounds` is about, when it is one this pass can read.
1250pub(crate) fn about(func: &Func, check: Inst) -> Option<Fact> {
1251    let (base, offset, whole) = addressed(func, check)?;
1252    let Extra::Mem(info) = func[check].extra else { return None };
1253    hull(base, offset, i128::from(func[info].size), whole)
1254}
1255
1256/// What a `check_live` is about, when it is one this pass can read.
1257///
1258/// One byte, because that is the whole of what the check says: the instance holding this address
1259/// is alive, and nothing about the address next door. The widening to a range that makes the fact
1260/// useful is `widened`, and it needs a bounds fact to do it.
1261pub(crate) fn alive(func: &Func, check: Inst) -> Option<Fact> {
1262    let (base, offset, whole) = addressed(func, check)?;
1263    hull(base, offset, 1, whole)
1264}
1265
1266/// The address a check is about, as a base and a constant, and whether the capability names the
1267/// base rather than the address.
1268///
1269/// The capability has to be one this pass can tie to the address, which [`its_own`] is, so a check
1270/// that does not have it is not a check this pass has anything to say about.
1271fn addressed(func: &Func, check: Inst) -> Option<(Value, i128, bool)> {
1272    let args = &func[func[check].args];
1273    let &capability = args.first()?;
1274    let &pointer = args.get(1)?;
1275    let (base, offset) = normal(func, pointer);
1276    let named = named_by(func, capability)?;
1277    its_own(named, pointer, base).map(|whole| (base, offset, whole))
1278}
1279
1280/// Whether a check's capability is about the address the check names or about the pointer that
1281/// address was worked out from, and which of the two it is.
1282///
1283/// Both are shapes `rucc-safety` emits. The first is what it used to emit everywhere, a `cap_of` in
1284/// front of each check naming the check's own pointer, and the second is what
1285/// `rucc_safety::origin` emits now, one capability taken where the object came from and shared by
1286/// every address walked off it. A check naming anything else is about some other instance and
1287/// nothing here is entitled to read it.
1288/// Whether a check this pass could not read names a capability the address really did come off.
1289///
1290/// Asked only where [`about`] has already answered nothing, so it is never on the path of a check
1291/// that goes, and it exists to tell one kind of miss from the rest. `rucc_safety::origin` shares one
1292/// capability down a whole derivation chain and [`normal`] stops walking at the first step it cannot
1293/// read, so a chain with a step like `i * 4` in it leaves the capability naming something further
1294/// back than the base and [`its_own`] refuses it. That is a miss something could be done about. A
1295/// capability naming a pointer the address was never walked off is a different instance and there is
1296/// nothing to do about one, so it stays in the row it was already in.
1297///
1298/// The walk here goes through a step of any kind, which is what makes it a different walk from
1299/// [`normal`], and it reads nothing but the chain, so it says the two are related and not by how
1300/// much.
1301fn midway(func: &Func, check: Inst) -> bool {
1302    let args = &func[func[check].args];
1303    let (Some(&capability), Some(&pointer)) = (args.first(), args.get(1)) else { return false };
1304    let Some(named) = named_by(func, capability) else { return false };
1305    let (base, _) = normal(func, pointer);
1306    let mut value = base;
1307    loop {
1308        if value == named {
1309            return true;
1310        }
1311        let Def::Result { inst, .. } = func[value].def else { return false };
1312        if func[inst].opcode != Opcode::PtrAdd {
1313            return false;
1314        }
1315        let Some(&from) = func[func[inst].args].first() else { return false };
1316        value = from;
1317    }
1318}
1319
1320/// Every address a check [`its_own`] refused can land in, when its capability names the far end.
1321///
1322/// The piece [`about`] cannot supply. That one reads an address as a base and a constant, and a
1323/// chain with a step nobody can read has no such reading, so it answers nothing and the rules never
1324/// run. [`spanned`] has no such trouble, because a step nobody can read is exactly what it asks the
1325/// ranges about, and it walks the whole chain rather than stopping at the first one. So the walk is
1326/// carried on from where the constant reader gave up, and what comes back is a range of addresses
1327/// off a base further back.
1328///
1329/// The base it lands on has to be the value the capability names, and that is the whole of what
1330/// makes this sound rather than a widening. A fact answers a range by [`reaches`], which already
1331/// insists on the same base, so what a rule says yes to is that every address this walk can reach is
1332/// inside something already known about that base. The capability naming that base is what says the
1333/// instance the rules are talking about is the instance this check is about.
1334/// Which of the three midway rows a check that got this far belongs in.
1335///
1336/// Told apart by whether anything in the pass has an extent for the base, rather than by whether a
1337/// rule said yes, because a rule saying no covers both "I have never heard of this object" and "I
1338/// have heard of it and the walk leaves it" and those are completely different pieces of work. The
1339/// first is the great majority and it is not fixable by anything in this pass.
1340fn why_midway(func: &Func, known: &Known, wide: Option<Reach>, live: bool) -> &'static str {
1341    let Some(wide) = wide else {
1342        return if live { MIDWAY_CAPABILITY_LIVE } else { MIDWAY_CAPABILITY };
1343    };
1344    let heard =
1345        declared(func, wide.base).is_some() || known.held.iter().any(|fact| fact.base == wide.base);
1346    match (heard, live) {
1347        (false, false) => MIDWAY_NO_EXTENT,
1348        (false, true) => MIDWAY_NO_EXTENT_LIVE,
1349        (true, false) => MIDWAY_OVER,
1350        (true, true) => MIDWAY_OVER_LIVE,
1351    }
1352}
1353
1354fn beyond(func: &Func, ranges: Option<&mut Ranges<'_>>, check: Inst, size: i128) -> Option<Reach> {
1355    let args = &func[func[check].args];
1356    let &capability = args.first()?;
1357    let &pointer = args.get(1)?;
1358    let named = named_by(func, capability)?;
1359    let (base, offset) = normal(func, pointer);
1360    let wide = spanned(func, ranges?, base, offset, size, check, Some(named))?;
1361    (wide.base == named).then_some(wide)
1362}
1363
1364fn its_own(named: Value, pointer: Value, base: Value) -> Option<bool> {
1365    if named == pointer {
1366        return Some(false);
1367    }
1368    (named == base).then_some(true)
1369}
1370
1371/// The bytes a check says belong to one instance.
1372///
1373/// Which is the access and nothing else when the capability was taken at the address, and the
1374/// access together with everything between it and the base when the capability was taken at the
1375/// base. The second is not a widening this pass made up. A capability names the instance its own
1376/// pointer is in, so the base is in that instance by the meaning of the operand, the access is in
1377/// it because that is what the check asks, and an instance is a run of bytes, so everything between
1378/// the two is in it as well.
1379///
1380/// That is what makes reading the second shape sound, and it has to be the fact rather than a note
1381/// on the side, because a fact is the thing both the asking and the recording go through. Asking
1382/// with it means whatever answers holds the base too, so the instance the answer is about is the
1383/// instance the capability names. Recording it after a check that stays is recording what the check
1384/// proves, and it is more than the narrow one, which is the whole reason a capability taken at the
1385/// base is worth having here.
1386fn hull(base: Value, offset: i128, size: i128, whole: bool) -> Option<Fact> {
1387    if !whole {
1388        return Some(Fact { base, offset, size });
1389    }
1390    let low = offset.min(0);
1391    let high = offset.checked_add(size)?.max(1);
1392    Some(Fact { base, offset: low, size: high.checked_sub(low)? })
1393}
1394
1395/// The two ends of a `check_deriv`, each as the single byte at it.
1396///
1397/// A derivation check asks whether the pointer that came out of a `ptr_add` is still in the storage
1398/// instance the pointer that went in belongs to, so both ends have to be readable and both have to
1399/// come out of the same value, which is what makes the two offsets comparable at all. One byte each
1400/// because that is what is being asked about: not a range, but whether an address is in an instance.
1401///
1402/// The capability has to be about the pointer that went in, for the reason [`addressed`] gives. The
1403/// instance the check is about is the one that pointer belongs to, and a check naming some other
1404/// capability is about some other instance.
1405///
1406/// The width operand is not read. It matters to the runtime only for a pointer that walked off the
1407/// near end, where the check passes on the byte a stride further along instead of on the address
1408/// itself, and this pass never gets that far: it discharges nothing it has not put inside a range
1409/// outright.
1410pub(crate) fn derives(func: &Func, check: Inst) -> Option<(Fact, Fact)> {
1411    let args = &func[func[check].args];
1412    let &capability = args.first()?;
1413    let &from = args.get(1)?;
1414    let &to = args.get(2)?;
1415    let (base, start) = normal(func, from);
1416    let named = named_by(func, capability)?;
1417    let whole = its_own(named, from, base)?;
1418    let (walked, end) = normal(func, to);
1419    if base != walked {
1420        return None;
1421    }
1422    // One fact has to hold both ends, so widening the near one to reach the base is what carries
1423    // the base into whatever answers, which is what [`hull`] is for. The far end is left as it is,
1424    // since the one fact that holds the pair holds it.
1425    Some((hull(base, start, 1, whole)?, Fact { base, offset: end, size: 1 }))
1426}
1427
1428/// The object a local is, when the address a check is about was computed from one.
1429///
1430/// This is the fact nobody had to check for, and section 7.2 puts it first of the four sources
1431/// because it is where most of the win is. An `alloca` of a fixed size is one storage instance of
1432/// that many bytes, said by the instruction that makes it rather than by a check that passed, so
1433/// the bytes from its address to that many further along are inside one instance for the same
1434/// reason a passing `check_bounds` says its own range is.
1435///
1436/// Only the fixed size form. The one that takes an operand is a variable length array, and how
1437/// many bytes it is is a value the program works out rather than a number in the payload, where
1438/// the field reads zero.
1439///
1440/// The fact holds everywhere in the function and no call takes it away, which is the other half of
1441/// what makes it worth having. A callee cannot free a frame slot: what it could free is whatever a
1442/// pointer stored in the slot points at, and that is a different instance and a different check.
1443/// So this is asked separately from the facts the walk carries rather than pushed into them, since
1444/// everything in there is thrown away at the first call this pass cannot see through.
1445fn declared(func: &Func, base: Value) -> Option<Fact> {
1446    let Def::Result { inst, .. } = func[base].def else { return None };
1447    if func[inst].opcode != Opcode::Alloca || !func[func[inst].args].is_empty() {
1448        return None;
1449    }
1450    let Extra::Mem(info) = func[inst].extra else { return None };
1451    Some(Fact::whole(base, i128::from(func[info].size)))
1452}
1453
1454/// The alignment an allocator promises, in bytes.
1455///
1456/// C says storage an allocator hands back is aligned for any object with a fundamental alignment,
1457/// which is sixteen bytes on the targets this compiles for. Eight is claimed rather than sixteen
1458/// because the claim has to hold wherever this pass runs and the pass is given a function rather
1459/// than a target. What it costs is an access that assumes more than eight bytes, which is a
1460/// `long double` or a vector, keeping a check it could have lost.
1461const ALLOCATED: u64 = 8;
1462
1463/// How far into an expression [`divides`] reads before it gives up.
1464///
1465/// A subscript is a multiply and a constant and the answer is two steps in. The bound is here
1466/// because the walk is over an expression the program wrote and nothing about an expression stops
1467/// it from being as deep as the source file is long.
1468const DEEP: u32 = 4;
1469
1470/// Whether the address a check is about starts where the access assumes it does.
1471///
1472/// The alignment conjunct of judgement J1 rides on `check_bounds`, which document 06 section 6.3
1473/// settled, so a check that goes takes the test of it with it and something here has to have
1474/// answered it first. An access that assumes nothing about where it starts has nothing to answer,
1475/// and that is what an alignment of one is and what a member of a packed record gets.
1476///
1477/// What answers it is the object the address was computed from and the steps taken from it, which
1478/// is the same ground the bounds question walks. An `alloca` says what it is aligned to and an
1479/// allocator promises [`ALLOCATED`], and each step from there leaves whatever the step itself
1480/// divides by. So `p[i]` on an `int *` out of `malloc` is answered by the four in the subscript's
1481/// own multiply, and `(int *)(p + 1)` is not answered at all, which is row S7 and the whole reason
1482/// this is here.
1483///
1484/// A global is not read here at all. It arrives as [`Flags::ALIGNED`] from `crate::extents`, which
1485/// is given the module this is not, and the flag is the whole of what this asks about one.
1486///
1487/// A pointer whose origin this cannot read is answered by a check that already ran on it, which is
1488/// [`Scope::proved`], or by `!aligned(a)` written on the value, which is the side table
1489/// `crate::params` fills from the call sites. Between them those are the only things that answer a
1490/// block parameter, a pointer loaded out of memory or one handed in. What is left after that is
1491/// zero, which answers nothing and keeps the check, and [`UNKNOWN_ALIGNMENT`] counts them.
1492///
1493/// The two answers given before [`settles`] is asked are not arithmetic and so are not a rule's.
1494/// An access of one byte assumes nothing about where it starts, so there is nothing to prove about
1495/// it, and the flag is a fact `crate::extents` established over the whole module and wrote down.
1496///
1497/// The two ways of saying no are told apart because they are worth different things to a reader.
1498/// [`Alignment::Unknown`] is a value nothing here says anything about and a better fact could take.
1499/// [`Alignment::Lost`] is an address this followed all the way back to an object it knows the
1500/// alignment of, where the steps taken from it landed somewhere the access may not start, and no
1501/// fact answers one of those because a check that stays is what the conjunct is for.
1502fn aligned(func: &Func, cfg: Option<&Cfg>, aligns: &HashMap<Value, u64>, check: Inst) -> Alignment {
1503    let Extra::Mem(info) = func[check].extra else { return Alignment::Unknown };
1504    let claim = u64::from(func[info].align);
1505    if claim <= 1 || func[check].flags.contains(Flags::ALIGNED) {
1506        return Alignment::Answered;
1507    }
1508    let Some(&pointer) = func[func[check].args].get(1) else { return Alignment::Unknown };
1509    let known = settled(func, cfg, aligns, pointer);
1510    if settles(known, claim) {
1511        Alignment::Answered
1512    } else if known == 0 {
1513        Alignment::Unknown
1514    } else {
1515        Alignment::Lost
1516    }
1517}
1518
1519/// What [`aligned`] found out about where an access starts.
1520#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1521enum Alignment {
1522    /// The address starts where the access assumes, so the check may go as far as this conjunct is
1523    /// concerned.
1524    Answered,
1525    /// Nothing here says where the address starts.
1526    Unknown,
1527    /// This knows where the address started and knows the steps took it off, which is row S7.
1528    Lost,
1529}
1530
1531/// Whether an address known to be a multiple of one number meets an access's claim.
1532///
1533/// The companion to [`covers`] for the alignment conjunct, and it decides nothing either. The walk
1534/// in [`settled`] worked out a number the address divides by, and whether that answers the access
1535/// is the rule file's to say. The address is opaque in the question, because nothing here knows
1536/// what it is and the answer is about every address the walk's number holds of.
1537///
1538/// It is worth saying what the rule catches that the comparison it replaced did not. `known` being
1539/// the larger number is not `claim` dividing it, and the two agree only because both are powers of
1540/// two. Every number that gets here is one, for the reason the head in `safety.model` writes out,
1541/// and now that reason is written somewhere a solver reads rather than only somewhere a person
1542/// does.
1543fn settles(known: u64, claim: u64) -> bool {
1544    let mut question = Question::default();
1545    let at = question.opaque();
1546    let at = question.app("value.i64", &[at]);
1547    let known = question.number(i128::from(known));
1548    let known = question.app("iconst.i64", &[known]);
1549    let claim = question.number(i128::from(claim));
1550    let claim = question.app("iconst.i64", &[claim]);
1551    let term = question.app("aligned.i64", &[at, known, claim]);
1552    match safety::TABLE.find(&question, term) {
1553        Some(found) => yes(&safety::TABLE, found.rule),
1554        None => false,
1555    }
1556}
1557
1558/// What a pointer is known to be aligned to, in bytes, or zero when nothing here says.
1559///
1560/// Every number involved is a power of two, so the greatest common divisor of two of them is the
1561/// smaller, which is why the steps are gathered with a `min` and why they start at the largest
1562/// number there is instead of at zero. Zero is the answer and not a step, since an alignment of
1563/// zero is not something an access can assume and a claim is never met by one.
1564///
1565/// `crate::params` calls this on an argument at a call site, with no graph and with `aligns`
1566/// carrying what the round before worked out about the caller's own parameters. Sharing the walk
1567/// is the point of doing it that way: what a fact says about a parameter is then exactly what the
1568/// callee would have worked out for itself if the value had not crossed a boundary.
1569pub(crate) fn settled(
1570    func: &Func,
1571    cfg: Option<&Cfg>,
1572    aligns: &HashMap<Value, u64>,
1573    pointer: Value,
1574) -> u64 {
1575    let mut budget = JOINS;
1576    joined(func, cfg, aligns, pointer, &mut Vec::new(), &mut budget)
1577}
1578
1579/// How many block parameters [`settled`] will walk through before answering nothing.
1580///
1581/// The cut in [`joined`] keeps the walk from going round for ever, and this keeps it from going
1582/// wide for ever. A chain of joins each of which has two predecessors is a walk that doubles at
1583/// every step, and a function with forty of those in a row is not a function worth an answer.
1584const JOINS: u32 = 256;
1585
1586/// [`settled`] with the two things a walk through a join needs, the values it is already inside of
1587/// and what is left of its budget.
1588///
1589/// A block parameter holds whatever its predecessors hand in, so it is aligned to at least the
1590/// least of what those are aligned to. That is arithmetic over values this function already has
1591/// and it needs nothing written on the function, which is why it is here rather than in the side
1592/// table tamnd/rucc#1385 is otherwise about. The side table is read at the top of the loop, beside
1593/// the answers the checks in this function gave.
1594///
1595/// The care it needs is a parameter that reaches itself round a loop, and the cut is that a value
1596/// the walk is already inside of contributes only the steps taken to get back to it. That lands on
1597/// the fixpoint rather than above it, which is worth the sentence because getting it wrong here
1598/// removes a check that should stay. Every step along a path is folded in as a divisor by the
1599/// `min` below, going round the loop a second time applies those same divisors again, and a
1600/// minimum does not move when you take it twice. So the answer after one lap is the answer after
1601/// any number of them, and there is nothing a second lap could lower that the first did not.
1602fn joined(
1603    func: &Func,
1604    cfg: Option<&Cfg>,
1605    aligns: &HashMap<Value, u64>,
1606    pointer: Value,
1607    inside: &mut Vec<Value>,
1608    budget: &mut u32,
1609) -> u64 {
1610    let mut steps = u64::MAX;
1611    let mut value = pointer;
1612    loop {
1613        // Asked in front of the shape, because the point of both is the values the shape gives up
1614        // on: a pointer handed in, a pointer read out of a field, a block parameter. A check that
1615        // ran on one of those is as good an answer as an `alloca`, and `!aligned(a)` is the same
1616        // answer about the same value worked out from outside the function, which is what
1617        // `crate::params` writes and what section 6.2.4 of
1618        // `spec/safe-memory/06-instrumentation.md` has the fact for.
1619        //
1620        // Whichever of the two says more is the one to take. A fact is a promise and never a
1621        // denial, so two promises about one value are both true and the larger is no less true
1622        // than the smaller.
1623        let proved = aligns.get(&value).copied();
1624        let written = func.facts(value).align.map(u64::from);
1625        if let Some(known) = proved.max(written) {
1626            return steps.min(known);
1627        }
1628        let inst = match func[value].def {
1629            Def::Result { inst, .. } => inst,
1630            // A function's parameter with no fact on it. Nothing inside the function says anything
1631            // about one, so this is where the row goes that the side table above is for, and it is
1632            // still the larger half of it.
1633            Def::Param { block, index } => {
1634                let Some(cfg) = cfg else { return 0 };
1635                if func.entry() == Some(block) {
1636                    return 0;
1637                }
1638                // The cut, and the only place a walk answers with the steps alone. Everywhere
1639                // else running out of things to look at is nothing known, which is zero.
1640                if inside.contains(&value) {
1641                    return steps;
1642                }
1643                if *budget == 0 {
1644                    return 0;
1645                }
1646                *budget -= 1;
1647                inside.push(value);
1648                let least = handed(func, cfg, aligns, block, index, inside, budget);
1649                inside.pop();
1650                return steps.min(least);
1651            }
1652        };
1653        match func[inst].opcode {
1654            Opcode::Alloca => {
1655                let Extra::Mem(info) = func[inst].extra else { return 0 };
1656                return steps.min(u64::from(func[info].align));
1657            }
1658            Opcode::Call if func[inst].flags.contains(Flags::HEAP) => {
1659                return steps.min(ALLOCATED);
1660            }
1661            Opcode::PtrAdd => {
1662                let args = &func[func[inst].args];
1663                let (Some(&from), Some(&by)) = (args.first(), args.get(1)) else { return 0 };
1664                steps = steps.min(divides(func, by, DEEP));
1665                value = from;
1666            }
1667            _ => return 0,
1668        }
1669    }
1670}
1671
1672/// The least alignment any predecessor hands to one parameter of a block.
1673///
1674/// Zero for a block nothing reaches and for an edge whose arguments do not run that far, since
1675/// either is a function this does not understand and claiming an alignment for one would be
1676/// claiming it out of nothing. A predecessor whose terminator is missing is the same case.
1677fn handed(
1678    func: &Func,
1679    cfg: &Cfg,
1680    aligns: &HashMap<Value, u64>,
1681    block: Block,
1682    index: u32,
1683    inside: &mut Vec<Value>,
1684    budget: &mut u32,
1685) -> u64 {
1686    let preds = cfg.predecessors(block);
1687    if preds.is_empty() {
1688        return 0;
1689    }
1690    let mut least = u64::MAX;
1691    for &pred in preds {
1692        let Some(term) = func.terminator(pred) else { return 0 };
1693        let Some(&came) = copy::edge_args(func, term, block).get(index as usize) else {
1694            return 0;
1695        };
1696        least = least.min(joined(func, Some(cfg), aligns, came, inside, budget));
1697        if least == 0 {
1698            break;
1699        }
1700    }
1701    least
1702}
1703
1704/// The largest power of two that divides a step, or one when nothing here says.
1705///
1706/// One is the answer for anything unreadable and it is the right one: every number divides by one,
1707/// so a step nobody can read leaves a pointer aligned to a byte and no more. Zero divides by
1708/// everything, which is a walk that took no step and has to leave what it started with alone.
1709fn divides(func: &Func, step: Value, depth: u32) -> u64 {
1710    if let Some(number) = constant(func, step) {
1711        let Ok(size) = u64::try_from(number.unsigned_abs()) else { return 1 };
1712        return if size == 0 { u64::MAX } else { 1 << size.trailing_zeros() };
1713    }
1714    let Def::Result { inst, .. } = func[step].def else { return 1 };
1715    let args = &func[func[inst].args];
1716    let (Some(&left), Some(&right)) = (args.first(), args.get(1)) else { return 1 };
1717    if depth == 0 {
1718        return 1;
1719    }
1720    match func[inst].opcode {
1721        // A subscript, which is an index nobody knows anything about times the element size.
1722        Opcode::Mul => {
1723            divides(func, left, depth - 1).saturating_mul(divides(func, right, depth - 1))
1724        }
1725        Opcode::Shl => match constant(func, right) {
1726            Some(by) if (0..64).contains(&by) => {
1727                divides(func, left, depth - 1).checked_shl(by as u32).unwrap_or(u64::MAX)
1728            }
1729            _ => 1,
1730        },
1731        // Two numbers added divide by whatever they both divide by, which is a field offset added
1732        // to a subscript and is how a member of an array of records comes out.
1733        Opcode::Add | Opcode::Sub => {
1734            divides(func, left, depth - 1).min(divides(func, right, depth - 1))
1735        }
1736        _ => 1,
1737    }
1738}
1739
1740/// The object an allocator made, when the address a check is about was computed from one and this
1741/// function has already found out it is not null.
1742///
1743/// The same shape as [`declared`] one storey up, with a marked call saying the size instead of an
1744/// `alloca` and one more thing to establish. `crate::heap` has the argument for both halves: what a
1745/// call to `malloc` says is an extent and never a lifetime, and it only says it where the program
1746/// has looked, because a null pointer is inside no object and a check on one is a check that is
1747/// meant to fail.
1748///
1749/// Nothing is claimed when the graph was not built, which is a function this found no allocation in
1750/// and so a function where the answer would have been no anyway.
1751fn allocation(
1752    func: &Func,
1753    cfg: Option<&Cfg>,
1754    checked: &mut HashMap<Value, HashSet<Block>>,
1755    block: Block,
1756    base: Value,
1757) -> Option<Fact> {
1758    let whole = heap::made(func, base)?;
1759    let cfg = cfg?;
1760    checked
1761        .entry(whole.base)
1762        .or_insert_with(|| heap::tested(func, cfg, whole.base))
1763        .contains(&block)
1764        .then_some(whole)
1765}
1766
1767/// Whether all of those bytes are inside one object an allocator made.
1768///
1769/// Every part has to be inside, and inside the same object, which is what asking [`covers`] with one
1770/// fact and several does.
1771fn allocated(
1772    func: &Func,
1773    cfg: Option<&Cfg>,
1774    checked: &mut HashMap<Value, HashSet<Block>>,
1775    block: Block,
1776    parts: &[&Fact],
1777) -> bool {
1778    let Some(first) = parts.first() else { return false };
1779    let Some(whole) = allocation(func, cfg, checked, block, first.base) else { return false };
1780    parts.iter().all(|part| covers(&whole, part))
1781}
1782
1783/// Whether every address a walk can reach is inside one object an allocator made.
1784///
1785/// [`allocated`] for the question [`reach`] and [`spread`] ask. The object comes from the same place
1786/// and is believed for the same reason, and what is asked of it is [`reaches`] rather than
1787/// [`covers`], so a walk by a step the ranges put numbers on can be answered by a call that says how
1788/// many bytes it made.
1789///
1790/// The wide path used to ask a local and the facts the walk carries and nothing else, so a program
1791/// that walked into its own `malloc` by an index kept its checks however plainly the size was
1792/// written. That is the first half of tamnd/rucc#880.
1793fn allocated_around(
1794    func: &Func,
1795    cfg: Option<&Cfg>,
1796    checked: &mut HashMap<Value, HashSet<Block>>,
1797    block: Block,
1798    spans: &[&Reach],
1799) -> bool {
1800    let Some(first) = spans.first() else { return false };
1801    let Some(whole) = allocation(func, cfg, checked, block, first.base) else { return false };
1802    spans.iter().all(|span| reaches(&whole, span))
1803}
1804
1805/// A lifetime fact grown from one address to the checked range it sits in.
1806///
1807/// The argument is in the module comment: a `check_bounds` that passed put its whole range inside
1808/// one instance, so the instance this lifetime check found alive is the instance that range is in.
1809/// With no range around the address the fact stays as it came, which is correct and answers only a
1810/// repeat of the very same check.
1811///
1812/// A local is asked about first, because the object it is is the widest range there can be for an
1813/// address computed from it and a wider fact answers more later checks. What that gives is a
1814/// lifetime check anywhere in a local discharging every later one in the same local, up to the
1815/// first call, which is the shape a function that reads several fields of a local struct has.
1816///
1817/// Only the bounds facts no call has run over, which is the third of the three things the module
1818/// comment's section on keeping the bounds half says have to hold. A range that was inside one
1819/// instance before a `free` and an allocation of something smaller at the same address is not
1820/// inside one instance after it, so widening a lifetime check that passed on the new instance by
1821/// that range would claim the whole of the old one is alive. The fact is still good enough to
1822/// answer a bounds check, because the lifetime check beside that access refuses the case, and it is
1823/// not good enough to be the reason a lifetime check goes away.
1824fn widened(func: &Func, bounds: &Known, asked: Fact) -> Fact {
1825    if let Some(local) = declared(func, asked.base).filter(|local| covers(local, &asked)) {
1826        return local;
1827    }
1828    bounds.fresh().iter().find(|fact| covers(fact, &asked)).copied().unwrap_or(asked)
1829}
1830
1831/// The value an address was computed from, and how far past it the address is.
1832///
1833/// A `ptr_add` over a constant is walked through, and anything else is where the answer stops. The
1834/// arithmetic here is exact because it is done in `i128` over offsets that came out of the IR as
1835/// sixty four bit constants, and whether it is small enough to mean anything at sixty four bits is
1836/// the rule's question rather than this function's.
1837pub(crate) fn normal(func: &Func, value: Value) -> (Value, i128) {
1838    let mut base = value;
1839    let mut offset: i128 = 0;
1840    while let Some((from, step)) = walked(func, base) {
1841        let Some(sum) = offset.checked_add(step) else { break };
1842        base = from;
1843        offset = sum;
1844    }
1845    (base, offset)
1846}
1847
1848/// Every address a walk can reach, when a step it takes is a value rather than a constant.
1849///
1850/// This is the third of the four sources section 7.2 lists, and it is the one that needs an
1851/// analysis. [`normal`] stops at the first `ptr_add` whose step it cannot read, and what it hands
1852/// back is a fact about a base nobody knows the size of. Document 10's ranges do know something
1853/// about the step: an index the program has already tested, or one a loop counts, is bounded even
1854/// though it is not constant. So the walk carries on past the step, adding the low end of its
1855/// range to the offset and the width of the range to the size.
1856///
1857/// What comes out is a range of addresses the access can land in, and it is a [`Reach`] rather than
1858/// a [`Fact`] on purpose. Whether an object holding all of that range holds the one address the
1859/// access actually uses is [`reaches`], which asks a rule with the distance left opaque, so one
1860/// answer covers every value the step could take.
1861///
1862/// It is only ever asked with. What this returns must never be recorded as established, and the
1863/// one place it could be is the push in the `check_bounds` arm, which happens only where this
1864/// returned nothing or answered nothing. The reason is that the widened range is not what a check
1865/// proves. A check that runs and passes proves the address the program used was inside the object,
1866/// and says nothing at all about the rest of the range this function made up around it.
1867fn reach(func: &Func, ranges: Option<&mut Ranges<'_>>, asked: &Fact, at: Inst) -> Option<Reach> {
1868    let wide = spanned(func, ranges?, asked.base, asked.offset, asked.size, at, None)?;
1869    // Nothing was walked past, so this is the fact that came in and asking it again is work
1870    // somebody already did.
1871    (wide.base != asked.base).then_some(wide)
1872}
1873
1874/// Which of the reasons a derivation check this pass could not read is kept for.
1875///
1876/// The census and nothing else. Whether the check goes has already been decided by the time this
1877/// runs, and what it answers is the question somebody reading `-fopt-info-missed` is actually
1878/// asking, which is what would have to be built for this pile to move.
1879///
1880/// It walks the same ground [`spread`] walks rather than being folded into it, because the two want
1881/// different things. [`spread`] wants an answer or nothing, and stopping at the first step it cannot
1882/// read is the fastest way to nothing. This wants to get as far as it can and name where it stopped,
1883/// so it runs only on checks that are staying and it is allowed to be the slower of the two.
1884///
1885/// The five that begin `nothing here says how big` are one refusal counted five ways. What is missing
1886/// in every one of them is how many bytes belong to the object, and where the pointer came from is
1887/// what says which piece of work would supply it: `__counted_by` and the type plane for a pointer out
1888/// of memory, section 7.5's summaries for one that was handed over, `crate::extents` reaching further
1889/// for a global, and the allocation summaries for one a call returned.
1890fn unreadable(func: &Func, ranges: Option<&mut Ranges<'_>>, check: Inst) -> &'static str {
1891    let args = &func[func[check].args];
1892    let (Some(&capability), Some(&from), Some(&to)) = (args.first(), args.get(1), args.get(2))
1893    else {
1894        return NO_EXTENT_OTHER;
1895    };
1896    if named_by(func, capability) != Some(from) {
1897        return NOT_ITS_CAPABILITY_DERIV;
1898    }
1899    // No ranges is a function with no walk in it that steps by a value, so every step here was a
1900    // constant, so the reader that gives up on two bases gave up on two bases.
1901    let Some(ranges) = ranges else { return TWO_BASES_DERIV };
1902    let (base, offset) = normal(func, from);
1903    let Some(near) = spanned(func, ranges, base, offset, 1, check, None) else {
1904        return NO_EXTENT_OTHER;
1905    };
1906    let (base, offset) = normal(func, to);
1907    let Some(far) = spanned(func, ranges, base, offset, 1, check, None) else {
1908        return NO_EXTENT_OTHER;
1909    };
1910    if near.base != far.base {
1911        return TWO_BASES_DERIV;
1912    }
1913    if declared(func, near.base).is_some() {
1914        return OVER_THE_LOCAL_DERIV;
1915    }
1916    match func[near.base].def {
1917        Def::Param { .. } => NO_EXTENT_HANDED,
1918        Def::Result { inst, .. } => match func[inst].opcode {
1919            Opcode::Load => NO_EXTENT_LOADED,
1920            Opcode::GlobalAddr => NO_EXTENT_GLOBAL,
1921            Opcode::Call | Opcode::CallIndirect => NO_EXTENT_RETURNED,
1922            _ => NO_EXTENT_OTHER,
1923        },
1924    }
1925}
1926
1927/// The two ends of a derivation check, each as the range of addresses it can be at.
1928///
1929/// A derivation check asks whether the pointer that came out of a walk is still in the storage
1930/// instance the pointer that went in belongs to. [`derives`] answers that only when both ends
1931/// normalize to one base over constants, and past a step the constant reader gives up on they do
1932/// not, which is why this reads the check's operands again rather than taking what that worked
1933/// out. Each end becomes a range, and the two still have to be off one base or there is nothing
1934/// comparable to ask about.
1935///
1936/// One byte each, for the reason [`derives`] gives. Nothing here claims anything about how many
1937/// bytes are readable at either address.
1938///
1939/// The capability has to be the `cap_of` of the pointer that went in, for the reason [`addressed`]
1940/// gives. Only that one, where [`derives`] reads a capability taken at the base the address was
1941/// worked out from as well: a range this reached by walking past a step comes back off a base of
1942/// its own, which is not the base the capability names, so there is nothing to widen towards.
1943fn spread(
1944    func: &Func,
1945    ranges: Option<&mut Ranges<'_>>,
1946    check: Inst,
1947    at: Inst,
1948) -> Option<(Reach, Reach)> {
1949    let ranges = ranges?;
1950    let args = &func[func[check].args];
1951    let &capability = args.first()?;
1952    let &from = args.get(1)?;
1953    let &to = args.get(2)?;
1954    if named_by(func, capability) != Some(from) {
1955        return None;
1956    }
1957    let (base, offset) = normal(func, from);
1958    let near = spanned(func, ranges, base, offset, 1, at, None)?;
1959    let (base, offset) = normal(func, to);
1960    let far = spanned(func, ranges, base, offset, 1, at, None)?;
1961    (near.base == far.base).then_some((near, far))
1962}
1963
1964/// Every address a walk off `base` can reach, and how many bytes it takes when it gets there.
1965///
1966/// The loop is [`normal`]'s with one more thing to try. A `ptr_add` over a constant is walked
1967/// through the same way, and a `ptr_add` over a value is walked through when document 10's ranges
1968/// put numbers on that value: the low end of the range goes on the distance and the width of it on
1969/// the slack. Anything else is where the walk stops.
1970///
1971/// A caller with a base in mind passes it as `stop` and the walk ends there rather than carrying on
1972/// past it, which matters because the pointer a capability was taken at is very often a walk off
1973/// something further back.
1974///
1975/// Nothing is returned when a step is a value the ranges say nothing useful about, rather than the
1976/// walk stopping there and handing back what it had. What it had would be a range off a `ptr_add`
1977/// nobody knows the size of, which answers nothing, so stopping would be a longer way of saying no.
1978fn spanned(
1979    func: &Func,
1980    ranges: &mut Ranges<'_>,
1981    base: Value,
1982    offset: i128,
1983    size: i128,
1984    at: Inst,
1985    stop: Option<Value>,
1986) -> Option<Reach> {
1987    let mut base = base;
1988    let mut low = offset;
1989    let mut width: i128 = 0;
1990    loop {
1991        // Where a caller has a base in mind, the walk is over when it gets there. Without this it
1992        // carries on past, because a pointer somebody took a capability at is often a walk off
1993        // something else, and a range off a base further back is a range about a base the caller
1994        // was not asking about.
1995        if stop == Some(base) {
1996            break;
1997        }
1998        // A constant step again, because past a step that needed a range there can be more of
1999        // them, and the frontend leaves a field offset as a constant under an array index.
2000        if let Some((from, step)) = walked(func, base) {
2001            low = low.checked_add(step)?;
2002            base = from;
2003            continue;
2004        }
2005        let Some(from) = operand_of(func, base, Opcode::PtrAdd, 0) else { break };
2006        let by = operand_of(func, base, Opcode::PtrAdd, 1)?;
2007        let (least, most) = ranges.at_inst(by, at).signed_bounds()?;
2008        low = low.checked_add(least)?;
2009        width = width.checked_add(most.checked_sub(least)?)?;
2010        base = from;
2011    }
2012    Some(Reach { base, low, width, size })
2013}
2014
2015/// Whether any walk in this function steps by a value rather than a constant.
2016///
2017/// The question the ranges are built for. A function without one of these would pay for a copy of
2018/// the control flow graph and never ask anything of it.
2019/// Whether anything in this function says a lifetime is over.
2020///
2021/// Nothing emits `meta_end` today, so this is false everywhere and the frame slot rule in
2022/// [`Discharge::run`] is on for every function. It is written anyway, and written over the whole
2023/// function rather than along the walk, because the day something does emit one the cheap reading
2024/// is the wrong one: a lifetime that ended in one arm of a branch has ended for a check after the
2025/// join, and a walk down the dominator tree would not have seen it. Turning the rule off for the
2026/// function is the reading that stays right when that day comes, and the finer one is a job for
2027/// whoever makes `meta_end` appear.
2028fn ends_a_lifetime(func: &Func) -> bool {
2029    func.blocks().any(|block| func.insts(block).any(|inst| func[inst].opcode == Opcode::MetaEnd))
2030}
2031
2032fn walks_by_a_value(func: &Func) -> bool {
2033    func.blocks().any(|block| {
2034        func.insts(block).any(|inst| {
2035            func[inst].opcode == Opcode::PtrAdd
2036                && func[func[inst].args].get(1).is_some_and(|&by| constant(func, by).is_none())
2037        })
2038    })
2039}
2040
2041/// Whether the control flow joins a pointer anywhere, which is what the alignment walk needs it for.
2042///
2043/// A block parameter of pointer type outside the entry is a pointer that came in one way on one
2044/// path and another way on another, and [`joined`] answers what it is aligned to by asking the
2045/// predecessors. Asked rather than always building the graph because the comment above says a
2046/// function that wants none of it should pay for none of it, and a function without a join has
2047/// nothing here to ask about.
2048fn joins_a_pointer(func: &Func, entry: Block) -> bool {
2049    func.blocks().any(|block| {
2050        block != entry && func[block].params.iter().any(|&param| func[param].ty == Type::PTR)
2051    })
2052}
2053
2054/// The pointer one `ptr_add` over a constant was computed from, and by how much.
2055fn walked(func: &Func, value: Value) -> Option<(Value, i128)> {
2056    let from = operand_of(func, value, Opcode::PtrAdd, 0)?;
2057    let by = operand_of(func, value, Opcode::PtrAdd, 1)?;
2058    Some((from, constant(func, by)?))
2059}
2060
2061/// Operand `index` of the instruction that produced `value`, when that instruction is `opcode`.
2062pub(crate) fn operand_of(func: &Func, value: Value, opcode: Opcode, index: usize) -> Option<Value> {
2063    let Def::Result { inst, .. } = func[value].def else { return None };
2064    if func[inst].opcode != opcode {
2065        return None;
2066    }
2067    func[func[inst].args].get(index).copied()
2068}
2069
2070/// The pointer a capability is about, whichever producer made it.
2071///
2072/// [`Opcode::capability_names`] is the fact and this is the lookup over a value. Asked instead of
2073/// `operand_of(func, capability, Opcode::CapOf, 0)`, which was the same question while `cap_of` was
2074/// the only producer `rucc-safety` emitted and became a narrower one when tamnd/rucc#1241 started
2075/// emitting the cheap ones. A rule here cares which pointer a capability describes and not how the
2076/// capability was arrived at, so asking for the opcode by name would have meant a check through a
2077/// pointer read out of memory quietly stopped being dischargeable on the day that read got cheaper.
2078pub(crate) fn named_by(func: &Func, capability: Value) -> Option<Value> {
2079    let Def::Result { inst, .. } = func[capability].def else { return None };
2080    let at = func[inst].opcode.capability_names()?;
2081    func[func[inst].args].get(at).copied()
2082}
2083
2084/// The value of an integer constant, read with its own sign.
2085pub(crate) fn constant(func: &Func, value: Value) -> Option<i128> {
2086    let Def::Result { inst, .. } = func[value].def else { return None };
2087    if func[inst].opcode != Opcode::IConst {
2088        return None;
2089    }
2090    let Extra::Imm(imm) = func[inst].extra else { return None };
2091    let ty = func[value].ty;
2092    ty.is_int().then(|| func[imm].signed(ty))
2093}
2094
2095/// Whether an established fact answers the check being asked about.
2096///
2097/// This function decides nothing. It puts the two together into the term the rule file is written
2098/// about and asks the table, which is the whole of section 7.7's split: the paragraph above worked
2099/// out that the two addresses are one value a constant apart, and whether that is enough is
2100/// somebody's proof rather than this file's opinion.
2101pub(crate) fn covers(fact: &Fact, asked: &Fact) -> bool {
2102    if fact.base != asked.base {
2103        return false;
2104    }
2105    let Some(delta) = asked.offset.checked_sub(fact.offset) else { return false };
2106    let mut question = Question::default();
2107    let at = question.opaque();
2108    let at = question.app("value.i64", &[at]);
2109    let span = question.number(fact.size);
2110    let span = question.app("iconst.i64", &[span]);
2111    let far = question.number(delta);
2112    let far = question.app("iconst.i64", &[far]);
2113    let reach = question.number(asked.size);
2114    let reach = question.app("iconst.i64", &[reach]);
2115    let term = question.app("covered.i64", &[at, span, far, reach]);
2116    match safety::TABLE.find(&question, term) {
2117        Some(found) => yes(&safety::TABLE, found.rule),
2118        None => false,
2119    }
2120}
2121
2122/// Whether an object holds every address a walk can land on.
2123///
2124/// The companion to [`covers`] for the question [`reach`] asks, and it decides nothing either. It
2125/// puts the object and the range of addresses into the term the rule file is written about and
2126/// asks the table. The distance the program actually walks is opaque in the question, which is
2127/// what makes one answer cover every value it could take.
2128fn reaches(fact: &Fact, asked: &Reach) -> bool {
2129    if fact.base != asked.base {
2130        return false;
2131    }
2132    let Some(delta) = asked.low.checked_sub(fact.offset) else { return false };
2133    let mut question = Question::default();
2134    let at = question.opaque();
2135    let at = question.app("value.i64", &[at]);
2136    let span = question.number(fact.size);
2137    let span = question.app("iconst.i64", &[span]);
2138    let delta = question.number(delta);
2139    let delta = question.app("iconst.i64", &[delta]);
2140    let width = question.number(asked.width);
2141    let width = question.app("iconst.i64", &[width]);
2142    let size = question.number(asked.size);
2143    let size = question.app("iconst.i64", &[size]);
2144    let step = question.opaque();
2145    let step = question.app("value.i64", &[step]);
2146    let term = question.app("reached.i64", &[at, span, delta, width, size, step]);
2147    match safety::TABLE.find(&question, term) {
2148        Some(found) => yes(&safety::TABLE, found.rule),
2149        None => false,
2150    }
2151}
2152
2153/// Whether the rule that fired answers yes.
2154///
2155/// A discharge rule replaces the question with a constant, and one is yes. Every rule in the file
2156/// answers that today, and reading it off the rule rather than assuming it is what keeps this
2157/// honest on the day one of them answers something else.
2158pub(crate) fn yes(table: &Table, rule: usize) -> bool {
2159    matches!(table.rules[rule].replacement, [Piece::App { .. }, Piece::Int(1)])
2160}
2161
2162/// A term built to be asked about, and nothing else.
2163///
2164/// The rules are matched against this rather than against the function, because what is being asked
2165/// about is not in the function: it is what the walk worked out about two of its instructions. So
2166/// the subject is a small arena of exactly the term being asked, built fresh for each question and
2167/// thrown away with the answer.
2168#[derive(Debug, Default)]
2169pub(crate) struct Question {
2170    held: Vec<Held>,
2171}
2172
2173/// One node of that term.
2174#[derive(Debug)]
2175enum Held {
2176    /// A number the pattern can read and a guard can be about.
2177    Int(i128),
2178    /// A head and its arguments.
2179    App(&'static str, Vec<usize>),
2180    /// Something with no structure, which is how an address the rule only names is written.
2181    Opaque,
2182}
2183
2184impl Question {
2185    /// Adds a constant and gives back where it went.
2186    ///
2187    /// Named for what it adds rather than for what it holds, because the arena also answers
2188    /// [`Subject::int`] and one name for the two would read as though building a term and asking
2189    /// about one were the same act.
2190    pub(crate) fn number(&mut self, value: i128) -> usize {
2191        self.held.push(Held::Int(value));
2192        self.held.len() - 1
2193    }
2194
2195    /// Adds an application of `head` to what is already in the arena.
2196    pub(crate) fn app(&mut self, head: &'static str, args: &[usize]) -> usize {
2197        self.held.push(Held::App(head, args.to_vec()));
2198        self.held.len() - 1
2199    }
2200
2201    /// Adds something the rule can bind and cannot look inside.
2202    pub(crate) fn opaque(&mut self) -> usize {
2203        self.held.push(Held::Opaque);
2204        self.held.len() - 1
2205    }
2206}
2207
2208impl Subject for Question {
2209    type Node = usize;
2210
2211    fn head(&self, node: usize) -> Option<(&str, usize)> {
2212        match &self.held[node] {
2213            Held::App(head, args) => Some((head, args.len())),
2214            Held::Int(_) | Held::Opaque => None,
2215        }
2216    }
2217
2218    fn arg(&self, node: usize, index: usize) -> usize {
2219        match &self.held[node] {
2220            Held::App(_, args) => args[index],
2221            // The walk only asks for an argument `head` said was there, so this is unreachable
2222            // rather than a case with an answer.
2223            Held::Int(_) | Held::Opaque => unreachable!("only an application has arguments"),
2224        }
2225    }
2226
2227    fn int(&self, node: usize) -> Option<i128> {
2228        match self.held[node] {
2229            Held::Int(value) => Some(value),
2230            Held::App(..) | Held::Opaque => None,
2231        }
2232    }
2233
2234    fn same(&self, a: usize, b: usize) -> bool {
2235        // Every node of a question is written once, so two places holding one thing are one place.
2236        a == b
2237    }
2238}
2239
2240#[cfg(test)]
2241mod tests {
2242    use rucc_base::Interner;
2243    use rucc_ir::{
2244        AsmInfo, Block, BlockCallList, Builder, Extra, Facts, Flags, Func, Inst, InstData, IntPred,
2245        MemInfo, MemOrder, Opcode, Restrict, Signature, Type, Value,
2246    };
2247
2248    use super::{DISCHARGE, Fact};
2249    use crate::stats::Kind;
2250    use crate::{Fuel, Pass, pass};
2251
2252    /// A function taking a pointer, with one block, ready to have accesses put in it.
2253    fn blank() -> (Interner, Func, Block, Value) {
2254        let mut names = Interner::new();
2255        let name = names.intern("f");
2256        let mut func = Func::new(name, Signature::new().with_params(&[Type::PTR]));
2257        let block = func.create_block();
2258        let pointer = func.append_param(block, Type::PTR);
2259        (names, func, block, pointer)
2260    }
2261
2262    /// Puts `cap_of` and a `check_bounds` over `size` bytes at `pointer` into a block.
2263    ///
2264    /// The same shape `rucc-safety` emits, written out here rather than reached for, because
2265    /// `rucc-opt` is rank 9 alongside `rucc-safety` and cannot depend on it.
2266    fn check(build: &mut Builder<'_>, pointer: Value, size: u64) {
2267        checking_at(build, pointer, pointer, size);
2268    }
2269
2270    /// The same, with the capability taken at `from` rather than at the address being checked.
2271    ///
2272    /// What `rucc_safety::origin` writes, once a capability belongs to a pointer rather than to an
2273    /// access: a field read off a struct is checked through the capability the struct's pointer
2274    /// got, and there is one of those for the whole function rather than one per field.
2275    fn checking_at(build: &mut Builder<'_>, from: Value, pointer: Value, size: u64) {
2276        let args = build.func().push_values(&[from]);
2277        let capability = build.value(InstData { args, ..InstData::new(Opcode::CapOf) }, Type::CAP);
2278        let info = MemInfo {
2279            size,
2280            align: 1,
2281            order: MemOrder::NotAtomic,
2282            tbaa: None,
2283            owns: 0,
2284            restrict: Restrict::NONE,
2285        };
2286        let args = build.func().push_values(&[capability, pointer]);
2287        let extra = Extra::Mem(build.func().add_mem(info));
2288        build.inst(InstData { args, extra, ..InstData::new(Opcode::CheckBounds) }, &[]);
2289    }
2290
2291    /// Puts `cap_of` and a `check_live` at `pointer` into a block.
2292    ///
2293    /// `rucc-safety` emits this straight after the bounds check for the same access and shares the
2294    /// one `cap_of` between the two. Sharing it is not what the pass reads, so the tests build a
2295    /// second one, which is the harder shape for it to accept.
2296    fn live(build: &mut Builder<'_>, pointer: Value) {
2297        let args = build.func().push_values(&[pointer]);
2298        let capability = build.value(InstData { args, ..InstData::new(Opcode::CapOf) }, Type::CAP);
2299        let args = build.func().push_values(&[capability, pointer]);
2300        build.inst(InstData { args, ..InstData::new(Opcode::CheckLive) }, &[]);
2301    }
2302
2303    /// Both checks in front of one access, in the order `rucc-safety` writes them.
2304    fn access(build: &mut Builder<'_>, pointer: Value, size: u64) {
2305        check(build, pointer, size);
2306        live(build, pointer);
2307    }
2308
2309    /// A pointer `bytes` past another one.
2310    fn past(build: &mut Builder<'_>, pointer: Value, bytes: i128) -> Value {
2311        let offset = build.iconst(Type::int(64), bytes);
2312        let args = build.func().push_values(&[pointer, offset]);
2313        build.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR)
2314    }
2315
2316    /// A pointer a number of bytes past another one, where the number is not one anybody can read.
2317    ///
2318    /// The shape an indexed access leaves behind: `p[i]` is a step by `i * 4` and `normal` stops
2319    /// walking at it, so the base it reaches is the stepped pointer itself rather than `p`.
2320    fn stepped(build: &mut Builder<'_>, pointer: Value, step: Value) -> Value {
2321        let args = build.func().push_values(&[pointer, step]);
2322        build.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR)
2323    }
2324
2325    /// A `check_live` with its capability taken at `from` rather than at the address it checks.
2326    fn living_at(build: &mut Builder<'_>, from: Value, pointer: Value) {
2327        let args = build.func().push_values(&[from]);
2328        let capability = build.value(InstData { args, ..InstData::new(Opcode::CapOf) }, Type::CAP);
2329        let args = build.func().push_values(&[capability, pointer]);
2330        build.inst(InstData { args, ..InstData::new(Opcode::CheckLive) }, &[]);
2331    }
2332
2333    /// Puts the flag `crate::extents` writes onto every check in a function.
2334    ///
2335    /// The pass reads what the IR says, so what a test has to build is an IR that says it. Working
2336    /// out which checks deserve it is `crate::extents`, is about a module rather than a function,
2337    /// and has its own tests.
2338    fn marked(func: &mut Func) {
2339        flagged(func, Flags::STATIC);
2340    }
2341
2342    /// Puts that flag on every check in the function, the way an annotator before the pipeline
2343    /// would have.
2344    fn flagged(func: &mut Func, flag: Flags) {
2345        let insts: Vec<Inst> =
2346            func.blocks().flat_map(|block| func.insts(block).collect::<Vec<_>>()).collect();
2347        for inst in insts {
2348            let check = matches!(
2349                func[inst].opcode,
2350                Opcode::CheckBounds | Opcode::CheckLive | Opcode::CheckDeriv
2351            );
2352            if check {
2353                func[inst].flags |= flag;
2354            }
2355        }
2356    }
2357
2358    /// How many checks are left in a function.
2359    fn checks(func: &Func) -> usize {
2360        func.blocks()
2361            .flat_map(|block| func.insts(block).collect::<Vec<_>>())
2362            .filter(|&inst| func[inst].opcode == Opcode::CheckBounds)
2363            .count()
2364    }
2365
2366    /// How many lifetime checks are left in a function.
2367    fn lives(func: &Func) -> usize {
2368        func.blocks()
2369            .flat_map(|block| func.insts(block).collect::<Vec<_>>())
2370            .filter(|&inst| func[inst].opcode == Opcode::CheckLive)
2371            .count()
2372    }
2373
2374    fn run(func: &mut Func) -> crate::Stats {
2375        DISCHARGE.run(func, &mut crate::machine::fixtures::analyses(), &mut Fuel::unlimited())
2376    }
2377
2378    /// The same, with one of the measurement's variants rather than the pass the pipeline runs.
2379    fn run_with(pass: &super::Discharge, func: &mut Func) -> crate::Stats {
2380        pass.run(func, &mut crate::machine::fixtures::analyses(), &mut Fuel::unlimited())
2381    }
2382
2383    #[test]
2384    fn a_run_that_may_only_ask_an_object_leaves_what_dominance_would_have_taken() {
2385        // Two checks of the same bytes on a pointer that came from outside. Nothing here says how
2386        // big the object is, so the only thing that could answer the second one is the first one
2387        // having run, and a run that may not ask that has to keep both.
2388        let (_, mut func, block, pointer) = blank();
2389        let mut build = Builder::new(&mut func, block);
2390        check(&mut build, pointer, 4);
2391        check(&mut build, pointer, 4);
2392        build.ret(&[]);
2393        let stats = run_with(&super::OBJECTS, &mut func);
2394        assert_eq!(checks(&func), 2);
2395        assert_eq!(stats.count(Kind::Optimized, super::REMOVED), 0);
2396    }
2397
2398    #[test]
2399    fn a_run_that_may_only_ask_dominance_takes_the_second_check_of_the_same_bytes() {
2400        let (_, mut func, block, pointer) = blank();
2401        let mut build = Builder::new(&mut func, block);
2402        check(&mut build, pointer, 4);
2403        check(&mut build, pointer, 4);
2404        build.ret(&[]);
2405        let stats = run_with(&super::DOMINANCE, &mut func);
2406        assert_eq!(checks(&func), 1);
2407        assert_eq!(stats.count(Kind::Optimized, super::REMOVED), 1);
2408    }
2409
2410    #[test]
2411    fn a_run_that_may_only_ask_dominance_leaves_a_check_inside_a_local() {
2412        // The other way round. One check, nothing in front of it, and the bytes are inside an
2413        // `alloca` whose size is written on it. Only the object can answer that one.
2414        let (_, mut func, block, _) = blank();
2415        let mut build = Builder::new(&mut func, block);
2416        let slot = local(&mut build, 16);
2417        check(&mut build, slot, 4);
2418        build.ret(&[]);
2419        let stats = run_with(&super::DOMINANCE, &mut func);
2420        assert_eq!(checks(&func), 1);
2421        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_LOCAL), 0);
2422        assert_eq!(
2423            run_with(&super::OBJECTS, &mut func).count(Kind::Optimized, super::REMOVED_LOCAL),
2424            1
2425        );
2426    }
2427
2428    #[test]
2429    fn the_measurement_variants_answer_to_names_of_their_own() {
2430        // A run that cannot be reached by a flag is a run nobody can measure with.
2431        let names: Vec<&str> = [
2432            &DISCHARGE,
2433            &super::OBJECTS,
2434            &super::DOMINANCE,
2435            &super::SUMMARIES,
2436            &super::NARROW,
2437            &super::EVERY,
2438        ]
2439        .iter()
2440        .map(|pass| pass.name())
2441        .collect();
2442        assert_eq!(
2443            names,
2444            [
2445                "discharge",
2446                "discharge-objects",
2447                "discharge-dominance",
2448                "discharge-summaries",
2449                "discharge-narrow",
2450                "discharge-every"
2451            ]
2452        );
2453        for name in names {
2454            assert!(pass::find(name).is_some(), "`{name}` is not in the pass list");
2455        }
2456    }
2457
2458    #[test]
2459    fn a_second_check_of_the_same_bytes_goes() {
2460        let (_, mut func, block, pointer) = blank();
2461        let mut build = Builder::new(&mut func, block);
2462        check(&mut build, pointer, 4);
2463        check(&mut build, pointer, 4);
2464        build.ret(&[]);
2465        let stats = run(&mut func);
2466        assert_eq!(checks(&func), 1);
2467        assert_eq!(stats.count(Kind::Optimized, super::REMOVED), 1);
2468    }
2469
2470    /// The same check over an access that assumes something about where it starts.
2471    ///
2472    /// [`check`] assumes nothing, which is the right default for the tests above it: what they are
2473    /// about is which bytes a check covers, and an access that assumes nothing has no alignment to
2474    /// answer and so reaches every rule. These are the ones about the alignment itself.
2475    fn assuming(build: &mut Builder<'_>, pointer: Value, size: u64, align: u32) {
2476        let args = build.func().push_values(&[pointer]);
2477        let capability = build.value(InstData { args, ..InstData::new(Opcode::CapOf) }, Type::CAP);
2478        let info = MemInfo {
2479            size,
2480            align,
2481            order: MemOrder::NotAtomic,
2482            tbaa: None,
2483            owns: 0,
2484            restrict: Restrict::NONE,
2485        };
2486        let args = build.func().push_values(&[capability, pointer]);
2487        let extra = Extra::Mem(build.func().add_mem(info));
2488        build.inst(InstData { args, extra, ..InstData::new(Opcode::CheckBounds) }, &[]);
2489    }
2490
2491    #[test]
2492    fn a_check_that_ran_answers_the_alignment_of_the_next_one_through_the_same_pointer() {
2493        // A pointer from outside, so nothing about where it came from says what it is aligned to,
2494        // and two checks of the same bytes. The first stays, because nothing covers its bytes, and
2495        // in staying it runs and refuses if the address is not a multiple of four. So on the way
2496        // to the second the address is a multiple of four whatever anybody knew before, the bytes
2497        // are covered by the first, and the second goes. This is the shape most of an ordinary
2498        // library is: a function reads a field of something it was handed and then reads it again.
2499        let (_, mut func, block, pointer) = blank();
2500        let mut build = Builder::new(&mut func, block);
2501        assuming(&mut build, pointer, 4, 4);
2502        assuming(&mut build, pointer, 4, 4);
2503        build.ret(&[]);
2504        let stats = run(&mut func);
2505        assert_eq!(checks(&func), 1);
2506        assert_eq!(stats.count(Kind::Optimized, super::REMOVED), 1);
2507        assert_eq!(stats.count(Kind::Missed, super::UNKNOWN_ALIGNMENT), 0);
2508    }
2509
2510    #[test]
2511    fn a_check_that_ran_answers_an_alignment_no_larger_than_the_one_it_tested() {
2512        // The first check assumes two bytes and the second assumes four, and two does not answer
2513        // four, so the second stays. Everything a check proves is what the access it stands beside
2514        // was allowed to assume and not a byte more.
2515        let (_, mut func, block, pointer) = blank();
2516        let mut build = Builder::new(&mut func, block);
2517        assuming(&mut build, pointer, 4, 2);
2518        assuming(&mut build, pointer, 4, 4);
2519        build.ret(&[]);
2520        let stats = run(&mut func);
2521        assert_eq!(checks(&func), 2);
2522        assert_eq!(stats.count(Kind::Missed, super::LOST_ALIGNMENT), 1);
2523    }
2524
2525    #[test]
2526    fn a_call_does_not_take_the_alignment_a_check_proved() {
2527        // What a call can do is free the storage and hand it back out smaller, which is why the
2528        // bounds facts are marked when one runs over them. It cannot change the number in a value,
2529        // and an alignment fact is about the number, so it crosses a call untouched. The bounds
2530        // half carries across too, marked, which is what leaves this with one check.
2531        let (mut names, mut func, block, pointer) = blank();
2532        let mut build = Builder::new(&mut func, block);
2533        assuming(&mut build, pointer, 4, 4);
2534        let callee = names.intern("might_free");
2535        let signature = build.func().add_signature(Signature::new());
2536        build.call(callee, signature, &[]);
2537        assuming(&mut build, pointer, 4, 4);
2538        build.ret(&[]);
2539        let stats = run(&mut func);
2540        assert_eq!(checks(&func), 1);
2541        assert_eq!(stats.count(Kind::Missed, super::UNKNOWN_ALIGNMENT), 0);
2542    }
2543
2544    #[test]
2545    fn an_alignment_a_check_proved_reaches_only_the_blocks_that_check_dominates() {
2546        // The alignment is proved in one arm of a branch and read in the join, so on the other
2547        // path nothing has tested the address at all. A fact that leaked here would take a check
2548        // the misaligned read needs, and dominance is the only thing holding an alignment fact.
2549        // The check in the entry assumes a byte, which is an access that assumes nothing about
2550        // where it starts, so it covers the bytes for the one in the join without saying anything
2551        // about its alignment and the gate is what is left deciding.
2552        let (_, mut func, block, pointer) = blank();
2553        let arm = func.create_block();
2554        let join = func.create_block();
2555        let mut build = Builder::new(&mut func, block);
2556        assuming(&mut build, pointer, 16, 1);
2557        let condition = build.iconst(Type::int(32), 1);
2558        build.br_if(condition, arm, &[], join, &[]);
2559        let mut build = Builder::new(&mut func, arm);
2560        assuming(&mut build, pointer, 32, 4);
2561        build.jump(join, &[]);
2562        let mut build = Builder::new(&mut func, join);
2563        assuming(&mut build, pointer, 4, 4);
2564        build.ret(&[]);
2565        let stats = run(&mut func);
2566        assert_eq!(checks(&func), 3, "the one in the arm reaches further, so all three stay");
2567        assert_eq!(stats.count(Kind::Missed, super::UNKNOWN_ALIGNMENT), 1);
2568    }
2569
2570    #[test]
2571    fn an_alignment_written_on_a_pointer_handed_in_answers_a_check_on_it() {
2572        // The side table half of tamnd/rucc#1385. The first check assumes a byte, so it covers the
2573        // bytes the second reads and says nothing about where either of them starts, and the
2574        // second one is left with the alignment conjunct and nothing inside the function to answer
2575        // it with. The fact is the answer, and it is the only one there is for a pointer handed in.
2576        let (_, mut func, block, pointer) = blank();
2577        func.set_facts(pointer, Facts { align: Some(4), ..Facts::NONE });
2578        let mut build = Builder::new(&mut func, block);
2579        assuming(&mut build, pointer, 4, 1);
2580        assuming(&mut build, pointer, 4, 4);
2581        build.ret(&[]);
2582        let stats = run(&mut func);
2583        assert_eq!(checks(&func), 1);
2584        assert_eq!(stats.count(Kind::Optimized, super::REMOVED), 1);
2585        assert_eq!(stats.count(Kind::Missed, super::UNKNOWN_ALIGNMENT), 0);
2586    }
2587
2588    #[test]
2589    fn an_alignment_written_on_a_pointer_answers_no_more_than_it_says() {
2590        // The same function with the fact saying two and the access assuming four. Two does not
2591        // answer four, so the check stays, and it stays as an address this knows about rather than
2592        // as one nothing has heard of, which is the difference between the two rows.
2593        let (_, mut func, block, pointer) = blank();
2594        func.set_facts(pointer, Facts { align: Some(2), ..Facts::NONE });
2595        let mut build = Builder::new(&mut func, block);
2596        assuming(&mut build, pointer, 4, 1);
2597        assuming(&mut build, pointer, 4, 4);
2598        build.ret(&[]);
2599        let stats = run(&mut func);
2600        assert_eq!(checks(&func), 2);
2601        assert_eq!(stats.count(Kind::Missed, super::UNKNOWN_ALIGNMENT), 0);
2602        assert_eq!(stats.count(Kind::Missed, super::LOST_ALIGNMENT), 1);
2603    }
2604
2605    #[test]
2606    fn an_alignment_every_arm_hands_in_reaches_the_join() {
2607        // Both predecessors hand the join a slot, both slots are aligned to eight, so the join's
2608        // parameter is aligned to eight whichever way control came. Nothing is written on the
2609        // function and nothing is assumed from a type: the answer is the least of what the
2610        // predecessors actually pass, which is arithmetic over values already here.
2611        let (_, mut func, block, _) = blank();
2612        let join = func.create_block();
2613        let arm = func.create_block();
2614        let carried = func.append_param(join, Type::PTR);
2615        let mut build = Builder::new(&mut func, block);
2616        let one = local(&mut build, 64);
2617        let condition = build.iconst(Type::int(32), 1);
2618        build.br_if(condition, arm, &[], join, &[one]);
2619        let mut build = Builder::new(&mut func, arm);
2620        let two = local(&mut build, 64);
2621        build.jump(join, &[two]);
2622        let mut build = Builder::new(&mut func, join);
2623        assuming(&mut build, carried, 16, 1);
2624        assuming(&mut build, carried, 4, 4);
2625        build.ret(&[]);
2626        let stats = run(&mut func);
2627        assert_eq!(stats.count(Kind::Missed, super::UNKNOWN_ALIGNMENT), 0);
2628        assert_eq!(checks(&func), 1, "the one that covers the bytes stays, the aligned one goes");
2629    }
2630
2631    #[test]
2632    fn an_alignment_one_arm_does_not_hand_in_does_not_reach_the_join() {
2633        // The same function with one arm handing in the pointer the function was given. Nothing
2634        // here says anything about that one, so the least over the predecessors is nothing, and a
2635        // walk that took the other arm's answer would be reading a fact off the arm the program
2636        // did not take.
2637        let (_, mut func, block, pointer) = blank();
2638        let join = func.create_block();
2639        let arm = func.create_block();
2640        let carried = func.append_param(join, Type::PTR);
2641        let mut build = Builder::new(&mut func, block);
2642        let one = local(&mut build, 64);
2643        let condition = build.iconst(Type::int(32), 1);
2644        build.br_if(condition, arm, &[], join, &[one]);
2645        let mut build = Builder::new(&mut func, arm);
2646        build.jump(join, &[pointer]);
2647        let mut build = Builder::new(&mut func, join);
2648        assuming(&mut build, carried, 16, 1);
2649        assuming(&mut build, carried, 4, 4);
2650        build.ret(&[]);
2651        let stats = run(&mut func);
2652        assert_eq!(stats.count(Kind::Missed, super::UNKNOWN_ALIGNMENT), 1);
2653        assert_eq!(checks(&func), 2);
2654    }
2655
2656    #[test]
2657    fn a_pointer_that_walks_a_loop_keeps_only_what_the_step_leaves() {
2658        // The cut, which is the part of the join walk worth a test of its own. The header's
2659        // parameter comes in from the entry as a slot aligned to eight and comes round the back
2660        // edge as itself stepped by eight. The walk meets the parameter inside itself and takes
2661        // only the steps it took to get back there, which is the fixpoint rather than a guess:
2662        // going round again steps by eight again and eight is already the least. So four is
2663        // answered, and sixteen is refused by an answer rather than by nothing, which is the
2664        // difference between the two rows.
2665        let (_, mut func, block, _) = blank();
2666        let header = func.create_block();
2667        let exit = func.create_block();
2668        let carried = func.append_param(header, Type::PTR);
2669        let mut build = Builder::new(&mut func, block);
2670        let slot = local(&mut build, 64);
2671        build.jump(header, &[slot]);
2672        let mut build = Builder::new(&mut func, header);
2673        assuming(&mut build, carried, 16, 1);
2674        assuming(&mut build, carried, 4, 4);
2675        assuming(&mut build, carried, 4, 16);
2676        let next = past(&mut build, carried, 8);
2677        let condition = build.iconst(Type::int(32), 1);
2678        build.br_if(condition, header, &[next], exit, &[]);
2679        let mut build = Builder::new(&mut func, exit);
2680        build.ret(&[]);
2681        let stats = run(&mut func);
2682        assert_eq!(stats.count(Kind::Missed, super::UNKNOWN_ALIGNMENT), 0);
2683        assert_eq!(stats.count(Kind::Missed, super::LOST_ALIGNMENT), 1);
2684    }
2685
2686    #[test]
2687    fn the_two_reasons_an_alignment_is_not_answered_are_counted_apart() {
2688        // One function with both in it. The pointer from outside is answered by nothing at all,
2689        // which is the row a fact from somewhere else could take, and the slot read a byte in is
2690        // answered by something that says no, which is the row no fact takes. The first check on
2691        // each is the one that covers the bytes for the second, since the gate is only asked once
2692        // a rule has answered those.
2693        let (_, mut func, block, pointer) = blank();
2694        let mut build = Builder::new(&mut func, block);
2695        assuming(&mut build, pointer, 16, 1);
2696        assuming(&mut build, pointer, 4, 4);
2697        let slot = local(&mut build, 16);
2698        let odd = past(&mut build, slot, 1);
2699        assuming(&mut build, odd, 4, 1);
2700        assuming(&mut build, odd, 4, 4);
2701        build.ret(&[]);
2702        let stats = run(&mut func);
2703        assert_eq!(stats.count(Kind::Missed, super::UNKNOWN_ALIGNMENT), 1);
2704        assert_eq!(stats.count(Kind::Missed, super::LOST_ALIGNMENT), 1);
2705    }
2706
2707    #[test]
2708    fn a_step_off_an_alignment_a_check_proved_is_walked_the_way_a_local_is() {
2709        // The pointer is proved four byte aligned by a check that ran, and then the two accesses
2710        // are at four bytes in, which keeps it, and at one byte in, which does not. Nothing about
2711        // the walk changes because the thing it ends at is a check rather than an `alloca`, which
2712        // is the point of putting the answer where `settled` already looks.
2713        let (_, mut func, block, pointer) = blank();
2714        let mut build = Builder::new(&mut func, block);
2715        assuming(&mut build, pointer, 64, 4);
2716        let even = past(&mut build, pointer, 4);
2717        assuming(&mut build, even, 4, 4);
2718        let odd = past(&mut build, pointer, 1);
2719        assuming(&mut build, odd, 4, 4);
2720        build.ret(&[]);
2721        let stats = run(&mut func);
2722        assert_eq!(checks(&func), 2, "the one four bytes in goes and the one a byte in stays");
2723        assert_eq!(stats.count(Kind::Missed, super::LOST_ALIGNMENT), 1);
2724    }
2725
2726    #[test]
2727    fn a_check_inside_a_local_at_an_offset_the_local_is_aligned_through_goes() {
2728        // An eight byte aligned slot read four bytes in, which is a member of a record and the
2729        // commonest access there is. The offset leaves four of the eight, the access assumes four,
2730        // and the check goes the way it did before any of this.
2731        let (_, mut func, block, _) = blank();
2732        let mut build = Builder::new(&mut func, block);
2733        let slot = local(&mut build, 16);
2734        let field = past(&mut build, slot, 4);
2735        assuming(&mut build, field, 4, 4);
2736        build.ret(&[]);
2737        let stats = run(&mut func);
2738        assert_eq!(checks(&func), 0);
2739        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_LOCAL), 1);
2740        assert_eq!(stats.count(Kind::Missed, super::UNKNOWN_ALIGNMENT), 0);
2741    }
2742
2743    #[test]
2744    fn a_check_a_cast_moved_off_the_alignment_stays_however_well_its_bytes_are_covered() {
2745        // Row S7 written in IR. The bytes are inside the slot and the slot is aligned, but the
2746        // access starts one byte in and assumes four, and one byte in is where the alignment is
2747        // lost. This is the check the misaligned read needs and the one the accounting run found
2748        // going missing.
2749        let (_, mut func, block, _) = blank();
2750        let mut build = Builder::new(&mut func, block);
2751        let slot = local(&mut build, 16);
2752        let odd = past(&mut build, slot, 1);
2753        assuming(&mut build, odd, 4, 4);
2754        build.ret(&[]);
2755        let stats = run(&mut func);
2756        assert_eq!(checks(&func), 1);
2757        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_LOCAL), 0);
2758        assert_eq!(stats.count(Kind::Missed, super::LOST_ALIGNMENT), 1);
2759    }
2760
2761    #[test]
2762    fn a_subscript_that_steps_by_the_width_it_reads_settles_its_own_alignment() {
2763        // `p[i]` on an `int *` an allocator made. Nobody knows what the index is, and nobody has
2764        // to: the step is the index times four, four divides it whatever the index turns out to
2765        // be, and the allocation it starts from is aligned to more than that.
2766        let (_, mut func, inside, _, pointer, index) = allocation(64);
2767        let mut build = Builder::new(&mut func, inside);
2768        let four = build.iconst(Type::int(64), 4);
2769        let step = build.binary(Opcode::Mul, index, four, Flags::NONE);
2770        let args = build.func().push_values(&[pointer, step]);
2771        let at = build.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
2772        assuming(&mut build, at, 4, 4);
2773        build.ret(&[]);
2774        let stats = run(&mut func);
2775        assert_eq!(stats.count(Kind::Missed, super::UNKNOWN_ALIGNMENT), 0);
2776    }
2777
2778    #[test]
2779    fn what_answers_an_alignment_claim_is_the_rule_and_not_a_comparison() {
2780        // The four cases the rule is asked about, and the fifth is the reason it is a rule. A
2781        // number larger than the claim and not a multiple of it answers nothing, and the guard is
2782        // written so that the question never gets asked with one, because `super::settled` only
2783        // ever gives back a power of two. The last one is the same point from the other end: the
2784        // largest number there is is larger than every claim and divides nothing, and what the
2785        // walk means by it is that it took no step rather than that it found an alignment.
2786        assert!(super::settles(8, 8));
2787        assert!(super::settles(16, 8));
2788        assert!(!super::settles(4, 8));
2789        assert!(!super::settles(0, 8));
2790        assert!(!super::settles(u64::MAX, 8));
2791    }
2792
2793    #[test]
2794    fn a_step_by_something_nobody_can_read_settles_nothing() {
2795        // A step the ranges do bound, so the bytes are answered and the check was on its way out,
2796        // and a step nothing says the low bits of, so where the access starts is not answered. A
2797        // mask of seven is nought to seven and three is one of those.
2798        let (_, mut func, block, _, index) = indexed();
2799        let mut build = Builder::new(&mut func, block);
2800        let slot = local(&mut build, 16);
2801        let step = low_bits(&mut build, index, 7);
2802        let args = build.func().push_values(&[slot, step]);
2803        let at = build.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
2804        assuming(&mut build, at, 4, 4);
2805        build.ret(&[]);
2806        let stats = run(&mut func);
2807        assert_eq!(checks(&func), 1);
2808        assert_eq!(stats.count(Kind::Missed, super::LOST_ALIGNMENT), 1);
2809    }
2810
2811    #[test]
2812    fn a_check_over_a_length_the_program_worked_out_is_not_this_pass_to_read() {
2813        // Section 7.4's hoisted check covers as many bytes as its loop runs times, which is a value
2814        // and not a number. Every range this pass compares is a pair of numbers, so it says so and
2815        // leaves the check alone rather than reading the payload, whose size is one element.
2816        let (_, mut func, block, pointer) = blank();
2817        let mut build = Builder::new(&mut func, block);
2818        check(&mut build, pointer, 4);
2819        let args = build.func().push_values(&[pointer]);
2820        let capability = build.value(InstData { args, ..InstData::new(Opcode::CapOf) }, Type::CAP);
2821        let bytes = build.iconst(Type::int(64), 4);
2822        let info = MemInfo {
2823            size: 4,
2824            align: 1,
2825            order: MemOrder::NotAtomic,
2826            tbaa: None,
2827            owns: 0,
2828            restrict: Restrict::NONE,
2829        };
2830        let extra = Extra::Mem(build.func().add_mem(info));
2831        let args = build.func().push_values(&[capability, pointer, bytes]);
2832        build.inst(InstData { args, extra, ..InstData::new(Opcode::CheckBounds) }, &[]);
2833        build.ret(&[]);
2834
2835        let stats = run(&mut func);
2836        assert_eq!(checks(&func), 2, "the second one stays");
2837        assert_eq!(stats.count(Kind::Missed, super::COMPUTED_EXTENT), 1);
2838    }
2839
2840    #[test]
2841    fn a_check_of_bytes_inside_a_checked_range_goes() {
2842        // Four bytes at offset four, inside sixteen bytes at offset zero. This is the shape the
2843        // whole pass is for: a struct whose fields are read one after another through one pointer.
2844        let (_, mut func, block, pointer) = blank();
2845        let mut build = Builder::new(&mut func, block);
2846        check(&mut build, pointer, 16);
2847        let field = past(&mut build, pointer, 4);
2848        check(&mut build, field, 4);
2849        build.ret(&[]);
2850        run(&mut func);
2851        assert_eq!(checks(&func), 1);
2852    }
2853
2854    #[test]
2855    fn a_check_of_bytes_past_the_end_of_a_checked_range_stays() {
2856        // Four bytes at offset fourteen is two bytes past the end of the sixteen that were
2857        // checked, and those two bytes are what the check is for.
2858        let (_, mut func, block, pointer) = blank();
2859        let mut build = Builder::new(&mut func, block);
2860        check(&mut build, pointer, 16);
2861        let over = past(&mut build, pointer, 14);
2862        check(&mut build, over, 4);
2863        build.ret(&[]);
2864        assert!(!run(&mut func).changed());
2865        assert_eq!(checks(&func), 2);
2866    }
2867
2868    #[test]
2869    fn a_check_of_bytes_before_a_checked_range_stays() {
2870        // The guard's `delta` is not negative, and this is why. A read four bytes below what was
2871        // checked is a read of somebody else's memory, and it is the bug the check exists for.
2872        let (_, mut func, block, pointer) = blank();
2873        let mut build = Builder::new(&mut func, block);
2874        check(&mut build, pointer, 16);
2875        let under = past(&mut build, pointer, -4);
2876        check(&mut build, under, 4);
2877        build.ret(&[]);
2878        assert!(!run(&mut func).changed());
2879        assert_eq!(checks(&func), 2);
2880    }
2881
2882    #[test]
2883    fn a_check_whose_capability_was_taken_where_the_pointer_came_from_covers_the_bytes_between() {
2884        // The shape a capability that belongs to a pointer produces. The check is on a field eight
2885        // bytes in and the capability was taken at the struct's pointer, so what it says is that
2886        // those four bytes and that pointer are in one instance. An instance is a run of bytes, so
2887        // everything from the pointer up to the end of the field is in it, and that is the fact.
2888        // The second check is inside it and goes.
2889        let (_, mut func, block, pointer) = blank();
2890        let mut build = Builder::new(&mut func, block);
2891        let field = past(&mut build, pointer, 8);
2892        checking_at(&mut build, pointer, field, 4);
2893        checking_at(&mut build, pointer, pointer, 4);
2894        build.ret(&[]);
2895        run(&mut func);
2896        assert_eq!(checks(&func), 1);
2897    }
2898
2899    #[test]
2900    fn a_check_whose_capability_was_taken_where_the_pointer_came_from_says_nothing_past_the_end() {
2901        // And the run stops where the access does. Four bytes at twelve are past the twelve the
2902        // check above established, and nothing here says the instance reaches that far.
2903        let (_, mut func, block, pointer) = blank();
2904        let mut build = Builder::new(&mut func, block);
2905        let field = past(&mut build, pointer, 8);
2906        checking_at(&mut build, pointer, field, 4);
2907        let over = past(&mut build, pointer, 12);
2908        checking_at(&mut build, pointer, over, 4);
2909        build.ret(&[]);
2910        assert!(!run(&mut func).changed());
2911        assert_eq!(checks(&func), 2);
2912    }
2913
2914    #[test]
2915    fn a_check_whose_capability_is_about_neither_end_of_the_walk_stays() {
2916        // Two rules and no third. A capability is about the address being checked or about the
2917        // pointer that address came off, and one about anything else is asking after an instance
2918        // this pass has nothing to say about. The capability here is readable and names a
2919        // pointer this address was never walked off, which is a different instance and nothing to
2920        // be done about, so it stays in the row it was in.
2921        let mut names = Interner::new();
2922        let name = names.intern("two");
2923        let mut func = Func::new(name, Signature::new().with_params(&[Type::PTR, Type::PTR]));
2924        let block = func.create_block();
2925        let pointer = func.append_param(block, Type::PTR);
2926        let other = func.append_param(block, Type::PTR);
2927        let mut build = Builder::new(&mut func, block);
2928        check(&mut build, pointer, 16);
2929        let field = past(&mut build, pointer, 4);
2930        checking_at(&mut build, other, field, 4);
2931        build.ret(&[]);
2932        let stats = run(&mut func);
2933        assert_eq!(checks(&func), 2);
2934        assert_eq!(stats.count(Kind::Missed, super::UNKNOWN_SHAPE), 1);
2935    }
2936
2937    #[test]
2938    fn the_two_reasons_a_shape_is_not_read_are_counted_apart() {
2939        // One row used to hold both of these and the row said the first thing about both. The
2940        // first check walks off the pointer by a step nobody here can read, and its capability
2941        // names that pointer, so what stopped it is that the capability is about something
2942        // further back than the base a walk this pass can follow reaches, and since the pointer is
2943        // a parameter nothing here has an extent for, it lands in the row that says so. The second
2944        // is checked through a capability taken at an unrelated pointer, which is a different
2945        // instance and is not the same problem at all.
2946        let mut names = Interner::new();
2947        let name = names.intern("two");
2948        let params = [Type::PTR, Type::PTR, Type::int(64)];
2949        let mut func = Func::new(name, Signature::new().with_params(&params));
2950        let block = func.create_block();
2951        let pointer = func.append_param(block, Type::PTR);
2952        let other = func.append_param(block, Type::PTR);
2953        let step = func.append_param(block, Type::int(64));
2954        let mut build = Builder::new(&mut func, block);
2955        let far = stepped(&mut build, pointer, step);
2956        checking_at(&mut build, pointer, far, 4);
2957        checking_at(&mut build, other, pointer, 4);
2958        build.ret(&[]);
2959        let stats = run(&mut func);
2960        assert_eq!(checks(&func), 2);
2961        assert_eq!(stats.count(Kind::Missed, super::MIDWAY_NO_EXTENT), 1);
2962        assert_eq!(stats.count(Kind::Missed, super::UNKNOWN_SHAPE), 1);
2963    }
2964
2965    #[test]
2966    fn a_lifetime_check_whose_capability_is_further_back_than_its_base_is_counted_apart_too() {
2967        // The same split on the other half, because the two rows are close to the same size on
2968        // the amalgamation and a relaxation would have to serve both.
2969        let mut names = Interner::new();
2970        let name = names.intern("two");
2971        let params = [Type::PTR, Type::PTR, Type::int(64)];
2972        let mut func = Func::new(name, Signature::new().with_params(&params));
2973        let block = func.create_block();
2974        let pointer = func.append_param(block, Type::PTR);
2975        let other = func.append_param(block, Type::PTR);
2976        let step = func.append_param(block, Type::int(64));
2977        let mut build = Builder::new(&mut func, block);
2978        let far = stepped(&mut build, pointer, step);
2979        living_at(&mut build, pointer, far);
2980        living_at(&mut build, other, pointer);
2981        build.ret(&[]);
2982        let stats = run(&mut func);
2983        assert_eq!(lives(&func), 2);
2984        assert_eq!(stats.count(Kind::Missed, super::MIDWAY_NO_EXTENT_LIVE), 1);
2985        assert_eq!(stats.count(Kind::Missed, super::UNKNOWN_SHAPE_LIVE), 1);
2986    }
2987
2988    #[test]
2989    fn a_check_through_a_pointer_nothing_relates_to_the_first_stays() {
2990        let mut names = Interner::new();
2991        let name = names.intern("two");
2992        let mut func = Func::new(name, Signature::new().with_params(&[Type::PTR, Type::PTR]));
2993        let block = func.create_block();
2994        let one = func.append_param(block, Type::PTR);
2995        let other = func.append_param(block, Type::PTR);
2996        let mut build = Builder::new(&mut func, block);
2997        check(&mut build, one, 16);
2998        check(&mut build, other, 4);
2999        build.ret(&[]);
3000        assert!(!run(&mut func).changed());
3001        assert_eq!(checks(&func), 2);
3002    }
3003
3004    #[test]
3005    fn a_bounds_check_a_call_stands_between_goes_and_its_lifetime_check_stays() {
3006        // The eighth box of tamnd/rucc#1241 and the module comment's section on why it is allowed.
3007        // The range the first check established is still one range on the far side of the call, or
3008        // the lifetime check at the second access is about to refuse, and that check is still here
3009        // to do it because the lifetime facts are still dropped.
3010        let (mut names, mut func, block, pointer) = blank();
3011        let mut build = Builder::new(&mut func, block);
3012        access(&mut build, pointer, 16);
3013        let callee = names.intern("might_free");
3014        let signature = build.func().add_signature(Signature::new());
3015        build.call(callee, signature, &[]);
3016        access(&mut build, pointer, 4);
3017        build.ret(&[]);
3018        let stats = run(&mut func);
3019        assert_eq!(checks(&func), 1, "the bounds check crossed the call");
3020        assert_eq!(lives(&func), 2, "and the lifetime check did not");
3021        assert_eq!(stats.count(Kind::Optimized, super::REMOVED), 1);
3022        assert_eq!(stats.count(Kind::Missed, super::PAST_A_CALL), 0);
3023        assert_eq!(stats.count(Kind::Missed, super::PAST_A_CALL_LIVE), 1);
3024    }
3025
3026    #[test]
3027    fn a_bounds_check_inline_assembly_stands_between_stays_and_is_counted() {
3028        // The other half of the split. A call hands the planes to the runtime and a block of
3029        // assembly does not, so this one drops both kinds and the row that says what that costs is
3030        // still reachable.
3031        let (_, mut func, block, pointer) = blank();
3032        let mut build = Builder::new(&mut func, block);
3033        check(&mut build, pointer, 16);
3034        build.inst(InstData::new(Opcode::InlineAsm), &[]);
3035        check(&mut build, pointer, 4);
3036        build.ret(&[]);
3037        let stats = run(&mut func);
3038        assert!(!stats.changed());
3039        assert_eq!(checks(&func), 2);
3040        assert_eq!(stats.count(Kind::Missed, super::PAST_A_CALL), 1);
3041    }
3042
3043    #[test]
3044    fn a_lifetime_check_is_not_widened_by_a_range_a_call_ran_over() {
3045        // The third of the three things the module comment says have to hold. The sixteen bytes
3046        // were one instance before the call and the call may have freed them and made something
3047        // smaller in their place, so the lifetime check at the pointer says the new instance is
3048        // alive and says nothing at all about the byte twelve further on. Widening by the older
3049        // range would discharge the second lifetime check, and the access it guards is the one
3050        // that would then land in storage the new instance does not own.
3051        let (mut names, mut func, block, pointer) = blank();
3052        let mut build = Builder::new(&mut func, block);
3053        check(&mut build, pointer, 16);
3054        let callee = names.intern("might_free");
3055        let signature = build.func().add_signature(Signature::new());
3056        build.call(callee, signature, &[]);
3057        live(&mut build, pointer);
3058        let field = past(&mut build, pointer, 12);
3059        live(&mut build, field);
3060        build.ret(&[]);
3061        let stats = run(&mut func);
3062        assert_eq!(lives(&func), 2, "the second one is not answered by the first");
3063        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_LIVE), 0);
3064    }
3065
3066    #[test]
3067    fn a_check_a_call_that_cannot_free_stands_between_goes() {
3068        // The other side of the paragraph above. The summary said this call reaches nothing that
3069        // ends a lifetime, so the range the first check established is still one range.
3070        let (mut names, mut func, block, pointer) = blank();
3071        let mut build = Builder::new(&mut func, block);
3072        check(&mut build, pointer, 16);
3073        let callee = names.intern("counts_them");
3074        let signature = build.func().add_signature(Signature::new());
3075        let call = build.call(callee, signature, &[]);
3076        check(&mut build, pointer, 4);
3077        build.ret(&[]);
3078        func[call].flags |= Flags::NOFREE;
3079        let stats = run(&mut func);
3080        assert_eq!(checks(&func), 1);
3081        assert_eq!(stats.count(Kind::Optimized, super::REMOVED), 1);
3082        assert_eq!(stats.count(Kind::Missed, super::PAST_A_CALL), 0);
3083    }
3084
3085    #[test]
3086    fn inline_assembly_throws_the_facts_away_whatever_it_is_flagged() {
3087        // There is no flag that would make this safe. The template is text the compiler does not
3088        // read, so nothing worked anything out about what it reaches.
3089        let (mut names, mut func, block, pointer) = blank();
3090        let mut build = Builder::new(&mut func, block);
3091        check(&mut build, pointer, 16);
3092        build.inline_asm(
3093            AsmInfo {
3094                template: names.intern("nop"),
3095                constraints: names.intern(""),
3096                clobbers: names.intern(""),
3097                targets: BlockCallList::EMPTY,
3098            },
3099            &[],
3100            &[],
3101            Flags::NONE,
3102        );
3103        check(&mut build, pointer, 4);
3104        build.ret(&[]);
3105        let stats = run(&mut func);
3106        assert!(!stats.changed());
3107        assert_eq!(stats.count(Kind::Missed, super::PAST_A_CALL), 1);
3108    }
3109
3110    #[test]
3111    fn a_check_that_only_one_path_covers_stays() {
3112        // The dominator tree is what makes this right. The check in the arm covers the one in the
3113        // join on one path and not on the other, and a check that goes has to be one that ran.
3114        let (_, mut func, block, pointer) = blank();
3115        let arm = func.create_block();
3116        let join = func.create_block();
3117        let mut build = Builder::new(&mut func, block);
3118        let condition = build.iconst(Type::int(32), 1);
3119        build.br_if(condition, arm, &[], join, &[]);
3120        let mut build = Builder::new(&mut func, arm);
3121        check(&mut build, pointer, 16);
3122        build.jump(join, &[]);
3123        let mut build = Builder::new(&mut func, join);
3124        check(&mut build, pointer, 4);
3125        build.ret(&[]);
3126        assert!(!run(&mut func).changed());
3127        assert_eq!(checks(&func), 2);
3128    }
3129
3130    #[test]
3131    fn a_check_a_dominating_block_covers_goes() {
3132        let (_, mut func, block, pointer) = blank();
3133        let after = func.create_block();
3134        let mut build = Builder::new(&mut func, block);
3135        check(&mut build, pointer, 16);
3136        build.jump(after, &[]);
3137        let mut build = Builder::new(&mut func, after);
3138        let field = past(&mut build, pointer, 8);
3139        check(&mut build, field, 8);
3140        build.ret(&[]);
3141        run(&mut func);
3142        assert_eq!(checks(&func), 1);
3143    }
3144
3145    #[test]
3146    fn fuel_stops_the_removing_and_not_the_looking() {
3147        let (_, mut func, block, pointer) = blank();
3148        let mut build = Builder::new(&mut func, block);
3149        check(&mut build, pointer, 4);
3150        check(&mut build, pointer, 4);
3151        check(&mut build, pointer, 4);
3152        build.ret(&[]);
3153        let mut fuel = Fuel::of(1);
3154        let stats = DISCHARGE.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut fuel);
3155        assert_eq!(checks(&func), 2);
3156        assert_eq!(stats.count(Kind::Optimized, super::REMOVED), 1);
3157        assert_eq!(stats.count(Kind::Missed, super::NO_FUEL), 1);
3158    }
3159
3160    #[test]
3161    fn a_second_lifetime_check_of_the_same_address_goes() {
3162        // The narrow fact on its own, with no range around it to widen into.
3163        let (_, mut func, block, pointer) = blank();
3164        let mut build = Builder::new(&mut func, block);
3165        live(&mut build, pointer);
3166        live(&mut build, pointer);
3167        build.ret(&[]);
3168        let stats = run(&mut func);
3169        assert_eq!(lives(&func), 1);
3170        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_LIVE), 1);
3171    }
3172
3173    #[test]
3174    fn a_lifetime_check_inside_a_checked_range_goes() {
3175        // The shape the pass is for, with both halves of it. Sixteen bytes are checked and found
3176        // alive, then a field four bytes in is read, and neither check in front of it survives.
3177        let (_, mut func, block, pointer) = blank();
3178        let mut build = Builder::new(&mut func, block);
3179        access(&mut build, pointer, 16);
3180        let field = past(&mut build, pointer, 4);
3181        access(&mut build, field, 4);
3182        build.ret(&[]);
3183        let stats = run(&mut func);
3184        assert_eq!(checks(&func), 1);
3185        assert_eq!(lives(&func), 1);
3186        assert_eq!(stats.count(Kind::Optimized, super::REMOVED), 1);
3187        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_LIVE), 1);
3188    }
3189
3190    #[test]
3191    fn a_lifetime_check_outside_every_checked_range_stays() {
3192        // Four bytes at offset twenty are past the sixteen that were checked, so nothing says the
3193        // address is in the instance that was found alive, and it might be in no instance at all.
3194        let (_, mut func, block, pointer) = blank();
3195        let mut build = Builder::new(&mut func, block);
3196        access(&mut build, pointer, 16);
3197        let over = past(&mut build, pointer, 20);
3198        live(&mut build, over);
3199        build.ret(&[]);
3200        assert!(!run(&mut func).changed());
3201        assert_eq!(lives(&func), 2);
3202    }
3203
3204    #[test]
3205    fn a_lifetime_check_with_no_range_around_it_does_not_widen() {
3206        // Without the bounds check the first lifetime check speaks only for its own address, so
3207        // the one four bytes along is a different question and stays.
3208        let (_, mut func, block, pointer) = blank();
3209        let mut build = Builder::new(&mut func, block);
3210        live(&mut build, pointer);
3211        let field = past(&mut build, pointer, 4);
3212        live(&mut build, field);
3213        build.ret(&[]);
3214        assert!(!run(&mut func).changed());
3215        assert_eq!(lives(&func), 2);
3216    }
3217
3218    #[test]
3219    fn a_lifetime_check_a_call_stands_between_stays_and_is_counted() {
3220        // Section 8.8's number. This is the one the summaries were written for.
3221        let (mut names, mut func, block, pointer) = blank();
3222        let mut build = Builder::new(&mut func, block);
3223        access(&mut build, pointer, 16);
3224        let callee = names.intern("might_free");
3225        let signature = build.func().add_signature(Signature::new());
3226        build.call(callee, signature, &[]);
3227        let field = past(&mut build, pointer, 4);
3228        live(&mut build, field);
3229        build.ret(&[]);
3230        let stats = run(&mut func);
3231        assert!(!stats.changed());
3232        assert_eq!(lives(&func), 2);
3233        assert_eq!(stats.count(Kind::Missed, super::PAST_A_CALL_LIVE), 1);
3234    }
3235
3236    #[test]
3237    fn a_lifetime_check_a_call_that_cannot_free_stands_between_goes() {
3238        let (mut names, mut func, block, pointer) = blank();
3239        let mut build = Builder::new(&mut func, block);
3240        access(&mut build, pointer, 16);
3241        let callee = names.intern("counts_them");
3242        let signature = build.func().add_signature(Signature::new());
3243        let call = build.call(callee, signature, &[]);
3244        let field = past(&mut build, pointer, 4);
3245        live(&mut build, field);
3246        build.ret(&[]);
3247        func[call].flags |= Flags::NOFREE;
3248        let stats = run(&mut func);
3249        assert_eq!(lives(&func), 1);
3250        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_LIVE), 1);
3251    }
3252
3253    #[test]
3254    fn ending_a_lifetime_throws_the_facts_away() {
3255        // Nothing emits `meta_end` yet, so this is the test that says what will happen when
3256        // something does, rather than a test of anything the compiler does today.
3257        let (_, mut func, block, pointer) = blank();
3258        let mut build = Builder::new(&mut func, block);
3259        access(&mut build, pointer, 16);
3260        let size = build.iconst(Type::int(64), 16);
3261        let args = build.func().push_values(&[pointer, size]);
3262        build.inst(InstData { args, ..InstData::new(Opcode::MetaEnd) }, &[]);
3263        access(&mut build, pointer, 16);
3264        build.ret(&[]);
3265        let stats = run(&mut func);
3266        assert!(!stats.changed());
3267        assert_eq!(checks(&func), 2);
3268        assert_eq!(lives(&func), 2);
3269        assert_eq!(stats.count(Kind::Missed, super::PAST_A_CALL), 1);
3270        assert_eq!(stats.count(Kind::Missed, super::PAST_A_CALL_LIVE), 1);
3271    }
3272
3273    #[test]
3274    fn fuel_runs_out_over_both_kinds_of_check() {
3275        let (_, mut func, block, pointer) = blank();
3276        let mut build = Builder::new(&mut func, block);
3277        access(&mut build, pointer, 16);
3278        access(&mut build, pointer, 4);
3279        build.ret(&[]);
3280        let mut fuel = Fuel::of(1);
3281        let stats = DISCHARGE.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut fuel);
3282        assert_eq!(checks(&func), 1);
3283        assert_eq!(lives(&func), 2);
3284        assert_eq!(stats.count(Kind::Optimized, super::REMOVED), 1);
3285        assert_eq!(stats.count(Kind::Missed, super::NO_FUEL_LIVE), 1);
3286    }
3287
3288    #[test]
3289    fn a_distance_too_large_to_be_a_real_access_is_not_discharged() {
3290        // The guard's bound. The two readings of the arithmetic agree while the numbers stay
3291        // small, so a rule proved at sixty four bits is not asked about anything else. Nothing
3292        // here is wrong, it simply is not proved, and a check that is not proved to be unnecessary
3293        // stays.
3294        let huge = i128::from(u64::MAX) * 4;
3295        let fact = Fact { base: Value::new(0), offset: 0, size: huge };
3296        let asked = Fact { base: Value::new(0), offset: huge / 2, size: 4 };
3297        assert!(!super::covers(&fact, &asked));
3298    }
3299
3300    #[test]
3301    fn a_range_of_addresses_wider_than_the_rule_allows_is_not_discharged() {
3302        // The guard on `reached.i64` bounds each of the three numbers at four gigabytes, for the
3303        // reason the rule file gives: past there the compiler's `i128` reading of the guard and the
3304        // solver's sixty four bit reading part company, and a rule proved under one and run under
3305        // the other is a rule proved about arithmetic that is not happening. A step whose range is
3306        // that wide is the usual case rather than a corner, since an index nothing has bounded says
3307        // nothing about where the access lands.
3308        let base = Value::new(0);
3309        let whole = Fact::whole(base, i128::from(u64::MAX) * 4);
3310        let asked = super::Reach { base, low: 0, width: i128::from(u64::MAX), size: 4 };
3311        assert!(!super::reaches(&whole, &asked));
3312    }
3313
3314    #[test]
3315    fn a_range_of_addresses_that_ends_where_the_object_does_is_discharged() {
3316        // Sixteen bytes, a step somewhere in nought to eleven, four bytes read. The last address
3317        // the walk can reach is the last one in the object, which is inside it.
3318        let base = Value::new(0);
3319        let whole = Fact::whole(base, 16);
3320        let asked = super::Reach { base, low: 0, width: 12, size: 4 };
3321        assert!(super::reaches(&whole, &asked));
3322        let over = super::Reach { base, low: 0, width: 13, size: 4 };
3323        assert!(!super::reaches(&whole, &over), "one byte further runs off the end");
3324    }
3325
3326    #[test]
3327    fn a_walk_by_a_bounded_step_off_a_local_takes_its_derivation_check_with_it() {
3328        // The shape `derives` cannot read at all: the pointer that went in is the slot and the one
3329        // that came out is a value past it, so the two are not one base and two constants. Both
3330        // ends widen to the slot, the slot holds both ranges, and one thing holding both is what a
3331        // derivation check asks about.
3332        let (_, mut func, block, _, index) = indexed();
3333        let mut build = Builder::new(&mut func, block);
3334        let slot = local(&mut build, 16);
3335        let step = low_bits(&mut build, index, 7);
3336        let at = walk(&mut build, slot, step);
3337        deriv(&mut build, slot, at, 4);
3338        build.ret(&[]);
3339        let stats = run(&mut func);
3340        assert_eq!(derivs(&func), 0);
3341        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_DERIV_RANGE), 1);
3342    }
3343
3344    #[test]
3345    fn a_walk_that_can_leave_the_local_keeps_its_derivation_check() {
3346        // Nought to fifteen off a slot of eight. Every step is bounded and the answer is still no,
3347        // because the question is whether the slot holds every address the walk can reach.
3348        let (_, mut func, block, _, index) = indexed();
3349        let mut build = Builder::new(&mut func, block);
3350        let slot = local(&mut build, 8);
3351        let step = low_bits(&mut build, index, 15);
3352        let at = walk(&mut build, slot, step);
3353        deriv(&mut build, slot, at, 4);
3354        build.ret(&[]);
3355        let stats = run(&mut func);
3356        assert_eq!(derivs(&func), 1);
3357        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_DERIV_RANGE), 0);
3358        assert_eq!(stats.count(Kind::Missed, super::OVER_THE_LOCAL_DERIV), 1);
3359    }
3360
3361    #[test]
3362    fn a_lifetime_check_a_bounded_walk_lands_inside_a_checked_range_goes() {
3363        // An access over thirty two bytes establishes the range, and the lifetime check beside it
3364        // makes that range one a check found alive. The lifetime check on the walk then goes,
3365        // because every address the walk can reach is in the range that was found alive.
3366        //
3367        // Written off a parameter rather than a slot because a slot answers the narrow question on
3368        // its own. What has to answer this one is a range a check was passed on.
3369        let (_, mut func, block, pointer, index) = indexed();
3370        let mut build = Builder::new(&mut func, block);
3371        access(&mut build, pointer, 32);
3372        let step = low_bits(&mut build, index, 7);
3373        let at = walk(&mut build, pointer, step);
3374        live(&mut build, at);
3375        build.ret(&[]);
3376        let stats = run(&mut func);
3377        assert_eq!(lives(&func), 1, "the one in front of the access stays");
3378        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_LIVE_RANGE), 1);
3379    }
3380
3381    #[test]
3382    fn a_lifetime_check_a_bounded_walk_can_leave_the_checked_range_keeps_it() {
3383        // The same over eight bytes, under a walk that can go fifteen past the start. A range of
3384        // eight bytes does not hold an address fifteen along from where it begins.
3385        let (_, mut func, block, pointer, index) = indexed();
3386        let mut build = Builder::new(&mut func, block);
3387        access(&mut build, pointer, 8);
3388        let step = low_bits(&mut build, index, 15);
3389        let at = walk(&mut build, pointer, step);
3390        live(&mut build, at);
3391        build.ret(&[]);
3392        let stats = run(&mut func);
3393        assert_eq!(lives(&func), 2);
3394        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_LIVE_RANGE), 0);
3395    }
3396
3397    /// A stack slot of `size` bytes, in the entry block where the verifier wants one.
3398    fn local(build: &mut Builder<'_>, size: u64) -> Value {
3399        let info = MemInfo {
3400            size,
3401            align: 8,
3402            order: MemOrder::NotAtomic,
3403            tbaa: None,
3404            owns: 0,
3405            restrict: Restrict::NONE,
3406        };
3407        let extra = Extra::Mem(build.func().add_mem(info));
3408        build.value(InstData { extra, ..InstData::new(Opcode::Alloca) }, Type::PTR)
3409    }
3410
3411    /// A function taking a pointer and an index, with one block.
3412    fn indexed() -> (Interner, Func, Block, Value, Value) {
3413        let mut names = Interner::new();
3414        let name = names.intern("f");
3415        let mut func = Func::new(name, Signature::new().with_params(&[Type::PTR, Type::int(64)]));
3416        let block = func.create_block();
3417        let pointer = func.append_param(block, Type::PTR);
3418        let index = func.append_param(block, Type::int(64));
3419        (names, func, block, pointer, index)
3420    }
3421
3422    /// A pointer a value past another one.
3423    fn walk(build: &mut Builder<'_>, pointer: Value, by: Value) -> Value {
3424        let args = build.func().push_values(&[pointer, by]);
3425        build.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR)
3426    }
3427
3428    /// The low bits of a value, which is a step the ranges can put a number on.
3429    fn low_bits(build: &mut Builder<'_>, value: Value, mask: i128) -> Value {
3430        let bits = build.iconst(Type::int(64), mask);
3431        build.binary(Opcode::And, value, bits, Flags::NONE)
3432    }
3433
3434    #[test]
3435    fn a_walk_by_a_step_the_ranges_bound_inside_a_local_goes() {
3436        // Section 7.2's third source. The step is not a constant, so the walk stops at the
3437        // `ptr_add` and the fact that comes out is about a base nobody knows the size of. What
3438        // the ranges say is that the step is somewhere in nought to seven, so the four bytes the
3439        // access wants are somewhere in nought to eleven, and all of that is inside the sixteen
3440        // the slot is.
3441        let (_, mut func, block, _, index) = indexed();
3442        let mut build = Builder::new(&mut func, block);
3443        let slot = local(&mut build, 16);
3444        let step = low_bits(&mut build, index, 7);
3445        let at = walk(&mut build, slot, step);
3446        check(&mut build, at, 4);
3447        build.ret(&[]);
3448        let stats = run(&mut func);
3449        assert_eq!(checks(&func), 0);
3450        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_RANGE), 1);
3451    }
3452
3453    #[test]
3454    fn a_check_whose_capability_is_further_back_than_its_base_is_answered_by_the_ranges() {
3455        // The row #1390 is about. The capability was taken at the pointer, the address is that
3456        // pointer stepped by something nobody can read, and the constant reader stops at the step
3457        // so it has no base and no constant to ask a rule with. Carrying the walk on past the
3458        // step with the ranges lands on the pointer the capability names, and the sixty four
3459        // bytes an earlier check proved hold every address the step can reach.
3460        let (_, mut func, block, pointer, index) = indexed();
3461        let mut build = Builder::new(&mut func, block);
3462        check(&mut build, pointer, 64);
3463        let step = low_bits(&mut build, index, 7);
3464        let at = walk(&mut build, pointer, step);
3465        checking_at(&mut build, pointer, at, 4);
3466        build.ret(&[]);
3467        let stats = run(&mut func);
3468        assert_eq!(checks(&func), 1);
3469        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_MIDWAY), 1);
3470    }
3471
3472    #[test]
3473    fn a_step_that_can_reach_past_what_was_checked_keeps_its_check() {
3474        // The same function with the mask widened. The step is somewhere in nought to a hundred
3475        // and twenty seven, the four bytes can start as far out as that, and the check that ran
3476        // proved sixty four. Nothing here says the rest of it belongs to the same instance.
3477        let (_, mut func, block, pointer, index) = indexed();
3478        let mut build = Builder::new(&mut func, block);
3479        check(&mut build, pointer, 64);
3480        let step = low_bits(&mut build, index, 127);
3481        let at = walk(&mut build, pointer, step);
3482        checking_at(&mut build, pointer, at, 4);
3483        build.ret(&[]);
3484        let stats = run(&mut func);
3485        assert_eq!(checks(&func), 2);
3486        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_MIDWAY), 0);
3487        // The row that says a fact about this pointer was there and the walk leaves it, rather
3488        // than the row for a pointer nothing has an extent for. This is the honest refusal.
3489        assert_eq!(stats.count(Kind::Missed, super::MIDWAY_OVER), 1);
3490    }
3491
3492    #[test]
3493    fn a_lifetime_check_further_back_than_its_base_is_answered_the_same_way() {
3494        // The other half. The first access puts a lifetime fact in, widened by its own bounds
3495        // check to the sixty four bytes that check proved are one instance, and every address the
3496        // step can reach is inside it.
3497        let (_, mut func, block, pointer, index) = indexed();
3498        let mut build = Builder::new(&mut func, block);
3499        access(&mut build, pointer, 64);
3500        let step = low_bits(&mut build, index, 7);
3501        let at = walk(&mut build, pointer, step);
3502        living_at(&mut build, pointer, at);
3503        build.ret(&[]);
3504        let stats = run(&mut func);
3505        assert_eq!(lives(&func), 1);
3506        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_MIDWAY_LIVE), 1);
3507    }
3508
3509    #[test]
3510    fn the_two_reasons_a_midway_lifetime_check_is_left_alone_are_counted_apart() {
3511        // The same pair as on the bounds half, so that all four midway rows are pinned. The first
3512        // function has a lifetime fact about the pointer and a step that walks off the end of it,
3513        // and the second has no fact about the pointer at all. The pass refuses both and the
3514        // rows have to say which refusal it was, because one of them is a range worth tightening
3515        // and the other is an object nothing here will ever have an extent for.
3516        let (_, mut func, block, pointer, index) = indexed();
3517        let mut build = Builder::new(&mut func, block);
3518        access(&mut build, pointer, 64);
3519        let step = low_bits(&mut build, index, 127);
3520        let at = walk(&mut build, pointer, step);
3521        living_at(&mut build, pointer, at);
3522        build.ret(&[]);
3523        let stats = run(&mut func);
3524        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_MIDWAY_LIVE), 0);
3525        assert_eq!(stats.count(Kind::Missed, super::MIDWAY_OVER_LIVE), 1);
3526
3527        let (_, mut func, block, pointer, index) = indexed();
3528        let mut build = Builder::new(&mut func, block);
3529        let step = low_bits(&mut build, index, 7);
3530        let at = walk(&mut build, pointer, step);
3531        living_at(&mut build, pointer, at);
3532        build.ret(&[]);
3533        let stats = run(&mut func);
3534        assert_eq!(stats.count(Kind::Missed, super::MIDWAY_NO_EXTENT_LIVE), 1);
3535        assert_eq!(stats.count(Kind::Missed, super::MIDWAY_OVER_LIVE), 0);
3536    }
3537
3538    #[test]
3539    fn a_walk_by_a_step_the_ranges_cannot_bound_is_left_alone() {
3540        // The same function with the mask taken off. A parameter can be anything, so the range of
3541        // addresses the walk reaches is the whole of memory and no slot covers it.
3542        let (_, mut func, block, _, index) = indexed();
3543        let mut build = Builder::new(&mut func, block);
3544        let slot = local(&mut build, 16);
3545        let at = walk(&mut build, slot, index);
3546        check(&mut build, at, 4);
3547        build.ret(&[]);
3548        let stats = run(&mut func);
3549        assert_eq!(checks(&func), 1);
3550        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_RANGE), 0);
3551    }
3552
3553    #[test]
3554    fn a_walk_a_bounded_step_can_take_off_the_end_of_a_local_is_left_alone() {
3555        // Nought to seven again, four bytes again, and a slot of eight this time. The step being
3556        // bounded is not the question. The question is whether every address it can reach is
3557        // inside the slot, and seven plus four is not.
3558        let (_, mut func, block, _, index) = indexed();
3559        let mut build = Builder::new(&mut func, block);
3560        let slot = local(&mut build, 8);
3561        let step = low_bits(&mut build, index, 7);
3562        let at = walk(&mut build, slot, step);
3563        check(&mut build, at, 4);
3564        build.ret(&[]);
3565        let stats = run(&mut func);
3566        assert_eq!(checks(&func), 1);
3567        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_RANGE), 0);
3568    }
3569
3570    #[test]
3571    fn a_constant_step_past_a_bounded_one_is_walked_too() {
3572        // A field of an element of an array of structs, which is the shape this is for. The array
3573        // index needs a range and the field offset does not, and the walk has to get through both.
3574        let (_, mut func, block, _, index) = indexed();
3575        let mut build = Builder::new(&mut func, block);
3576        let slot = local(&mut build, 32);
3577        let step = low_bits(&mut build, index, 15);
3578        let element = walk(&mut build, slot, step);
3579        let field = past(&mut build, element, 8);
3580        check(&mut build, field, 4);
3581        build.ret(&[]);
3582        let stats = run(&mut func);
3583        assert_eq!(checks(&func), 0);
3584        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_RANGE), 1);
3585    }
3586
3587    #[test]
3588    fn what_a_range_discharge_records_is_the_bytes_and_not_the_range() {
3589        // The second check is the same bytes as the first, and the first went because a made up
3590        // range around it was inside the slot. What the first one proved is that those bytes are
3591        // in the slot, so the second one goes on that rather than on the ranges being asked all
3592        // over again.
3593        let (_, mut func, block, _, index) = indexed();
3594        let mut build = Builder::new(&mut func, block);
3595        let slot = local(&mut build, 16);
3596        let step = low_bits(&mut build, index, 7);
3597        let at = walk(&mut build, slot, step);
3598        check(&mut build, at, 4);
3599        check(&mut build, at, 4);
3600        build.ret(&[]);
3601        let stats = run(&mut func);
3602        assert_eq!(checks(&func), 0);
3603        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_RANGE), 1);
3604        assert_eq!(stats.count(Kind::Optimized, super::REMOVED), 1);
3605    }
3606
3607    /// A stack slot whose size the program works out, which is what a variable length array is.
3608    fn growable(build: &mut Builder<'_>, size: Value) -> Value {
3609        let info = MemInfo {
3610            size: 0,
3611            align: 8,
3612            order: MemOrder::NotAtomic,
3613            tbaa: None,
3614            owns: 0,
3615            restrict: Restrict::NONE,
3616        };
3617        let extra = Extra::Mem(build.func().add_mem(info));
3618        let args = build.func().push_values(&[size]);
3619        build.value(InstData { args, extra, ..InstData::new(Opcode::Alloca) }, Type::PTR)
3620    }
3621
3622    #[test]
3623    fn a_check_of_bytes_inside_a_local_goes_with_nothing_in_front_of_it() {
3624        // Section 7.2's first source. No check established this and none had to: an `alloca` of
3625        // sixteen bytes is sixteen bytes of one storage instance because that is what it makes.
3626        let (_, mut func, block, _) = blank();
3627        let mut build = Builder::new(&mut func, block);
3628        let slot = local(&mut build, 16);
3629        let field = past(&mut build, slot, 8);
3630        check(&mut build, field, 4);
3631        build.ret(&[]);
3632        let stats = run(&mut func);
3633        assert_eq!(checks(&func), 0);
3634        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_LOCAL), 1);
3635    }
3636
3637    #[test]
3638    fn a_check_past_the_end_of_a_local_stays() {
3639        // The slot is sixteen bytes and the access runs to twenty. Nothing about it being a local
3640        // says anything about the four bytes after it, which belong to whatever the frame puts
3641        // there next.
3642        let (_, mut func, block, _) = blank();
3643        let mut build = Builder::new(&mut func, block);
3644        let slot = local(&mut build, 16);
3645        let field = past(&mut build, slot, 16);
3646        check(&mut build, field, 4);
3647        build.ret(&[]);
3648        let stats = run(&mut func);
3649        assert_eq!(checks(&func), 1);
3650        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_LOCAL), 0);
3651    }
3652
3653    #[test]
3654    fn a_check_of_bytes_inside_a_local_goes_across_a_call() {
3655        // The other half of what makes the fact worth having. A callee cannot free a frame slot,
3656        // so unlike everything the walk carries this one is not thrown away at a call.
3657        let (mut names, mut func, block, _) = blank();
3658        let mut build = Builder::new(&mut func, block);
3659        let slot = local(&mut build, 16);
3660        let callee = names.intern("might_free");
3661        let signature = build.func().add_signature(Signature::new());
3662        build.call(callee, signature, &[]);
3663        check(&mut build, slot, 4);
3664        build.ret(&[]);
3665        let stats = run(&mut func);
3666        assert_eq!(checks(&func), 0);
3667        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_LOCAL), 1);
3668        assert_eq!(stats.count(Kind::Missed, super::PAST_A_CALL), 0);
3669    }
3670
3671    #[test]
3672    fn a_check_inside_a_variable_length_array_stays() {
3673        // How many bytes it is is a value the program works out, and the payload's size field
3674        // reads zero. A pass that read it anyway would discharge every check in the array.
3675        let (_, mut func, block, _) = blank();
3676        let mut build = Builder::new(&mut func, block);
3677        let bytes = build.iconst(Type::int(64), 64);
3678        let slot = growable(&mut build, bytes);
3679        check(&mut build, slot, 4);
3680        build.ret(&[]);
3681        let stats = run(&mut func);
3682        assert_eq!(checks(&func), 1);
3683        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_LOCAL), 0);
3684    }
3685
3686    #[test]
3687    fn a_lifetime_check_in_a_local_goes_with_nothing_in_front_of_it() {
3688        // The frame slot rule, and the point is that neither of these has a check in front of it.
3689        // A slot is alive until the function returns, so a lifetime check anywhere inside one is
3690        // asking a question the `alloca` already answered.
3691        let (_, mut func, block, _) = blank();
3692        let mut build = Builder::new(&mut func, block);
3693        let slot = local(&mut build, 16);
3694        live(&mut build, slot);
3695        let field = past(&mut build, slot, 12);
3696        live(&mut build, field);
3697        build.ret(&[]);
3698        let stats = run(&mut func);
3699        assert_eq!(lives(&func), 0);
3700        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_LIVE_LOCAL), 2);
3701    }
3702
3703    #[test]
3704    fn a_lifetime_check_inside_a_local_goes_across_a_call() {
3705        // The last of the five ways a lifetime check is taken out, pinned here because the
3706        // argument in the module comment about keeping bounds facts across a call is an argument
3707        // about all five. Three of them survive a call and none of the three is about storage a
3708        // callee could free, which is what makes them harmless to a bounds fact that crossed. This
3709        // is the frame slot one, and the other two already have a test each.
3710        let (mut names, mut func, block, _) = blank();
3711        let mut build = Builder::new(&mut func, block);
3712        let slot = local(&mut build, 16);
3713        let callee = names.intern("might_free");
3714        let signature = build.func().add_signature(Signature::new());
3715        build.call(callee, signature, &[]);
3716        live(&mut build, slot);
3717        build.ret(&[]);
3718        let stats = run(&mut func);
3719        assert_eq!(lives(&func), 0);
3720        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_LIVE_LOCAL), 1);
3721        assert_eq!(stats.count(Kind::Missed, super::PAST_A_CALL_LIVE), 0);
3722    }
3723
3724    #[test]
3725    fn a_lifetime_check_past_the_end_of_a_local_stays() {
3726        // The slot answers for its own bytes and no further, so an address outside it is a
3727        // different instance and a question nothing has answered.
3728        let (_, mut func, block, _) = blank();
3729        let mut build = Builder::new(&mut func, block);
3730        let slot = local(&mut build, 16);
3731        live(&mut build, slot);
3732        let field = past(&mut build, slot, 24);
3733        live(&mut build, field);
3734        build.ret(&[]);
3735        let stats = run(&mut func);
3736        assert_eq!(lives(&func), 1);
3737        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_LIVE_LOCAL), 1);
3738    }
3739
3740    #[test]
3741    fn something_ending_a_lifetime_turns_the_frame_slot_rule_off() {
3742        // The gate, and with it the widening the frame slot rule usually hides. With a `meta_end`
3743        // anywhere in the function the slot answers nothing, so the first check stays and pays,
3744        // and what takes the second one out is the first one widened to the whole slot.
3745        let (_, mut func, block, pointer) = blank();
3746        let mut build = Builder::new(&mut func, block);
3747        let slot = local(&mut build, 16);
3748        live(&mut build, slot);
3749        let field = past(&mut build, slot, 12);
3750        live(&mut build, field);
3751        let size = build.iconst(Type::int(64), 16);
3752        let args = build.func().push_values(&[pointer, size]);
3753        build.inst(InstData { args, ..InstData::new(Opcode::MetaEnd) }, &[]);
3754        build.ret(&[]);
3755        let stats = run(&mut func);
3756        assert_eq!(lives(&func), 1);
3757        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_LIVE_LOCAL), 0);
3758        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_LIVE), 1);
3759    }
3760
3761    /// Puts `cap_of` and a `check_deriv` for a walk from `from` to `to` into a block.
3762    ///
3763    /// The stride is the width of one element, which is what `rucc-safety` passes and what the
3764    /// runtime uses for a pointer that walked off the near end. This pass does not read it.
3765    fn deriv(build: &mut Builder<'_>, from: Value, to: Value, stride: i128) {
3766        deriving_at(build, from, from, to, stride);
3767    }
3768
3769    /// The same, with the capability taken at `held` rather than at the address the walk starts on.
3770    fn deriving_at(build: &mut Builder<'_>, held: Value, from: Value, to: Value, stride: i128) {
3771        let args = build.func().push_values(&[held]);
3772        let capability = build.value(InstData { args, ..InstData::new(Opcode::CapOf) }, Type::CAP);
3773        let width = build.iconst(Type::int(64), stride);
3774        let args = build.func().push_values(&[capability, from, to, width]);
3775        build.inst(InstData { args, ..InstData::new(Opcode::CheckDeriv) }, &[]);
3776    }
3777
3778    /// How many derivation checks are left in a function.
3779    fn derivs(func: &Func) -> usize {
3780        func.blocks()
3781            .flat_map(|block| func.insts(block).collect::<Vec<_>>())
3782            .filter(|&inst| func[inst].opcode == Opcode::CheckDeriv)
3783            .count()
3784    }
3785
3786    #[test]
3787    fn a_walk_inside_a_checked_range_goes() {
3788        // Sixteen bytes were checked, and the walk goes from the start of them to eight in. Both
3789        // ends are in one range, so the second address is in the instance the first belongs to.
3790        let (_, mut func, block, pointer) = blank();
3791        let mut build = Builder::new(&mut func, block);
3792        check(&mut build, pointer, 16);
3793        let field = past(&mut build, pointer, 8);
3794        deriv(&mut build, pointer, field, 4);
3795        build.ret(&[]);
3796        let stats = run(&mut func);
3797        assert_eq!(derivs(&func), 0);
3798        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_DERIV), 1);
3799    }
3800
3801    #[test]
3802    fn a_walk_that_leaves_the_checked_range_stays() {
3803        // Four bytes were checked and the walk goes eight past them. Nothing here says the two
3804        // addresses are in one instance, which is the whole of what the check is about.
3805        let (_, mut func, block, pointer) = blank();
3806        let mut build = Builder::new(&mut func, block);
3807        check(&mut build, pointer, 4);
3808        let field = past(&mut build, pointer, 8);
3809        deriv(&mut build, pointer, field, 4);
3810        build.ret(&[]);
3811        let stats = run(&mut func);
3812        assert_eq!(derivs(&func), 1);
3813        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_DERIV), 0);
3814    }
3815
3816    #[test]
3817    fn a_walk_whose_capability_was_taken_where_the_pointer_came_from_is_read_too() {
3818        // The same two rules on the near end of a walk. Sixteen bytes were checked, the walk runs
3819        // from eight in to twelve in, and the capability is the one the pointer those two came off
3820        // got. The near end has to reach back to that pointer for the answer to be about the
3821        // instance the capability names, which is what the fact it asks does.
3822        let (_, mut func, block, pointer) = blank();
3823        let mut build = Builder::new(&mut func, block);
3824        check(&mut build, pointer, 16);
3825        let field = past(&mut build, pointer, 8);
3826        let next = past(&mut build, pointer, 12);
3827        deriving_at(&mut build, pointer, field, next, 4);
3828        build.ret(&[]);
3829        let stats = run(&mut func);
3830        assert_eq!(derivs(&func), 0);
3831        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_DERIV), 1);
3832    }
3833
3834    #[test]
3835    fn two_ranges_holding_one_end_each_do_not_answer_a_walk() {
3836        // The case the one fact rule is written for. Both addresses have been checked, so both are
3837        // inside some instance, and nothing says it is the same one. The walk stays.
3838        let (_, mut func, block, pointer) = blank();
3839        let mut build = Builder::new(&mut func, block);
3840        check(&mut build, pointer, 4);
3841        let field = past(&mut build, pointer, 64);
3842        check(&mut build, field, 4);
3843        deriv(&mut build, pointer, field, 4);
3844        build.ret(&[]);
3845        let stats = run(&mut func);
3846        assert_eq!(derivs(&func), 1);
3847        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_DERIV), 0);
3848    }
3849
3850    #[test]
3851    fn a_walk_inside_a_local_goes_with_nothing_in_front_of_it() {
3852        // The shape almost every derivation check in real code has: a field of a local struct.
3853        // `rucc-safety` emits the walk before the bounds check on what it produced, so a fact from
3854        // an earlier check is usually the wrong size for it and the local is what answers.
3855        let (_, mut func, block, _) = blank();
3856        let mut build = Builder::new(&mut func, block);
3857        let slot = local(&mut build, 16);
3858        let field = past(&mut build, slot, 8);
3859        deriv(&mut build, slot, field, 4);
3860        build.ret(&[]);
3861        let stats = run(&mut func);
3862        assert_eq!(derivs(&func), 0);
3863        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_DERIV_LOCAL), 1);
3864    }
3865
3866    #[test]
3867    fn a_walk_off_the_end_of_a_local_stays() {
3868        // Where the slot stops is where the fact stops. One past the end is the case the runtime
3869        // has slack for and this pass does not use any of it.
3870        let (_, mut func, block, _) = blank();
3871        let mut build = Builder::new(&mut func, block);
3872        let slot = local(&mut build, 16);
3873        let field = past(&mut build, slot, 16);
3874        deriv(&mut build, slot, field, 4);
3875        build.ret(&[]);
3876        let stats = run(&mut func);
3877        assert_eq!(derivs(&func), 1);
3878        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_DERIV_LOCAL), 0);
3879    }
3880
3881    #[test]
3882    fn a_walk_a_call_stands_between_stays_and_is_counted() {
3883        // The same price the other two kinds pay, reported the same way, so the cost of not
3884        // trusting a call is a number per function rather than a paragraph.
3885        let (mut names, mut func, block, pointer) = blank();
3886        let mut build = Builder::new(&mut func, block);
3887        check(&mut build, pointer, 16);
3888        let callee = names.intern("might_free");
3889        let signature = build.func().add_signature(Signature::new());
3890        build.call(callee, signature, &[]);
3891        let field = past(&mut build, pointer, 8);
3892        deriv(&mut build, pointer, field, 4);
3893        build.ret(&[]);
3894        let stats = run(&mut func);
3895        assert_eq!(derivs(&func), 1);
3896        assert_eq!(stats.count(Kind::Missed, super::PAST_A_CALL_DERIV), 1);
3897    }
3898
3899    #[test]
3900    fn a_check_the_module_says_is_inside_a_global_goes_with_nothing_in_front_of_it() {
3901        // The other half of section 7.2's first source. The size of a global lives on the module
3902        // and this pass is given one function, so the answer arrives as a flag `crate::extents`
3903        // wrote before the pipeline started, and all three kinds carry it.
3904        let (_, mut func, block, pointer) = blank();
3905        let mut build = Builder::new(&mut func, block);
3906        let field = past(&mut build, pointer, 8);
3907        deriv(&mut build, pointer, field, 1);
3908        access(&mut build, field, 4);
3909        build.ret(&[]);
3910        marked(&mut func);
3911        let stats = run(&mut func);
3912        assert_eq!(checks(&func), 0);
3913        assert_eq!(lives(&func), 0);
3914        assert_eq!(derivs(&func), 0);
3915        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_STATIC), 1);
3916        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_LIVE_STATIC), 1);
3917        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_DERIV_STATIC), 1);
3918    }
3919
3920    #[test]
3921    fn a_check_the_module_says_every_caller_hands_in_goes_with_nothing_in_front_of_it() {
3922        // Section 7.5's summaries, arriving the same way a global's extent does and for the same
3923        // reason: which object a caller passes is a fact about a different function. What the flag
3924        // says is an extent and a lifetime, because the objects `crate::params` believes are a
3925        // caller's frame slot and a global and both are alive for as long as the call runs.
3926        let (_, mut func, block, pointer) = blank();
3927        let mut build = Builder::new(&mut func, block);
3928        let field = past(&mut build, pointer, 8);
3929        deriv(&mut build, pointer, field, 1);
3930        access(&mut build, field, 4);
3931        build.ret(&[]);
3932        flagged(&mut func, Flags::HANDED);
3933        let stats = run(&mut func);
3934        assert_eq!(checks(&func), 0);
3935        assert_eq!(lives(&func), 0);
3936        assert_eq!(derivs(&func), 0);
3937        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_HANDED), 1);
3938        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_LIVE_HANDED), 1);
3939        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_DERIV_HANDED), 1);
3940    }
3941
3942    /// A function that takes an index, allocates `size` bytes and tests the answer against null.
3943    ///
3944    /// Gives back the block where the test has passed, the block where it has not, the pointer and
3945    /// the index. The flag is put on by hand, because which calls deserve it is a question about a
3946    /// module and `crate::heap` is what answers it.
3947    ///
3948    /// The index is there for the tests about a walk by a value. A parameter on its own is any
3949    /// number at all, so a test that wants a bounded one puts [`low_bits`] over it the same way the
3950    /// local tests do.
3951    fn allocation(size: i128) -> (Interner, Func, Block, Block, Value, Value) {
3952        let mut names = Interner::new();
3953        let name = names.intern("f");
3954        let mut func = Func::new(name, Signature::new().with_params(&[Type::int(64)]));
3955        let entry = func.create_block();
3956        let inside = func.create_block();
3957        let outside = func.create_block();
3958        let index = func.append_param(entry, Type::int(64));
3959        let mut build = Builder::new(&mut func, entry);
3960        let signature = build.func().add_signature(
3961            Signature::new().with_params(&[Type::int(64)]).with_returns(&[Type::PTR]),
3962        );
3963        let bytes = build.iconst(Type::int(64), size);
3964        let call = build.call(names.intern("malloc"), signature, &[bytes]);
3965        let at = build.func();
3966        at[call].flags |= Flags::HEAP;
3967        let pointer = at[call].results().next().expect("a call that gives back a pointer");
3968        let zero = build.iconst(Type::int(64), 0);
3969        let null = build.unary(Opcode::IntToPtr, zero, Type::PTR);
3970        let condition = build.icmp(IntPred::Ne, pointer, null);
3971        build.br_if(condition, inside, &[], outside, &[]);
3972        let mut build = Builder::new(&mut func, outside);
3973        build.ret(&[]);
3974        (names, func, inside, outside, pointer, index)
3975    }
3976
3977    #[test]
3978    fn a_check_inside_an_allocation_the_program_tested_goes() {
3979        // The third of the objects whose extent nobody had to check for. `malloc(16)` says how
3980        // many bytes it made in the call, and the branch on null is what makes it true here.
3981        let (_, mut func, inside, _, pointer, _) = allocation(16);
3982        let mut build = Builder::new(&mut func, inside);
3983        let field = past(&mut build, pointer, 8);
3984        deriv(&mut build, pointer, field, 1);
3985        access(&mut build, field, 4);
3986        build.ret(&[]);
3987        let stats = run(&mut func);
3988        assert_eq!(checks(&func), 0);
3989        assert_eq!(derivs(&func), 0);
3990        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_MADE), 1);
3991        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_DERIV_MADE), 1);
3992        // The lifetime check is the one an allocation says nothing about, because a `free` in this
3993        // same function can end it, and it is what reports a use after free.
3994        assert_eq!(lives(&func), 1);
3995    }
3996
3997    #[test]
3998    fn a_check_on_an_allocation_nobody_tested_stays() {
3999        // Down the other arm the pointer is null, a null pointer is inside no object at all, and
4000        // the check is one that is supposed to fail.
4001        let (_, mut func, _, outside, pointer, _) = allocation(16);
4002        let mut build = Builder::new(&mut func, outside);
4003        access(&mut build, pointer, 4);
4004        build.ret(&[]);
4005        let stats = run(&mut func);
4006        assert_eq!(checks(&func), 1);
4007        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_MADE), 0);
4008    }
4009
4010    #[test]
4011    fn a_check_past_the_end_of_an_allocation_stays() {
4012        // Four bytes at offset fourteen is two bytes past the sixteen that were asked for, and
4013        // those two bytes are what the check is for.
4014        let (_, mut func, inside, _, pointer, _) = allocation(16);
4015        let mut build = Builder::new(&mut func, inside);
4016        let field = past(&mut build, pointer, 14);
4017        access(&mut build, field, 4);
4018        build.ret(&[]);
4019        let stats = run(&mut func);
4020        assert_eq!(checks(&func), 1);
4021        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_MADE), 0);
4022    }
4023
4024    #[test]
4025    fn a_walk_that_leaves_an_allocation_stays() {
4026        // One end inside and the other past the end is a walk out of the object, which is what a
4027        // derivation check is there to catch, so both ends have to be inside before it goes.
4028        let (_, mut func, inside, _, pointer, _) = allocation(16);
4029        let mut build = Builder::new(&mut func, inside);
4030        let field = past(&mut build, pointer, 32);
4031        deriv(&mut build, pointer, field, 1);
4032        build.ret(&[]);
4033        let stats = run(&mut func);
4034        assert_eq!(derivs(&func), 1);
4035        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_DERIV_MADE), 0);
4036    }
4037
4038    #[test]
4039    fn a_check_inside_an_allocation_goes_across_a_call() {
4040        // The other reason a fact read off the instruction is worth having. How many bytes an
4041        // allocator made is not something a callee can change, so unlike a fact from a check that
4042        // ran this one is still there on the far side of a call.
4043        let (mut names, mut func, inside, _, pointer, _) = allocation(16);
4044        let mut build = Builder::new(&mut func, inside);
4045        access(&mut build, pointer, 4);
4046        let signature = build.func().add_signature(Signature::new());
4047        build.call(names.intern("g"), signature, &[]);
4048        access(&mut build, pointer, 4);
4049        build.ret(&[]);
4050        let stats = run(&mut func);
4051        assert_eq!(checks(&func), 0);
4052        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_MADE), 2);
4053        // Both lifetime checks stay, and the second one is the one a `free` inside `g` would make
4054        // report.
4055        assert_eq!(lives(&func), 2);
4056    }
4057
4058    /// A function that allocates `size` bytes and never looks at what it got back.
4059    ///
4060    /// The shape `bench/safety/a-strided-column-sum.c` has. The size on its own must not answer a
4061    /// check here, because reading through what `malloc` gave back without testing it is the bug
4062    /// this compiler is for.
4063    fn untested(size: i128) -> (Interner, Func, Block, Value, Value) {
4064        let mut names = Interner::new();
4065        let name = names.intern("f");
4066        let mut func = Func::new(name, Signature::new().with_params(&[Type::int(64)]));
4067        let block = func.create_block();
4068        let index = func.append_param(block, Type::int(64));
4069        let mut build = Builder::new(&mut func, block);
4070        let signature = build.func().add_signature(
4071            Signature::new().with_params(&[Type::int(64)]).with_returns(&[Type::PTR]),
4072        );
4073        let bytes = build.iconst(Type::int(64), size);
4074        let call = build.call(names.intern("malloc"), signature, &[bytes]);
4075        let at = build.func();
4076        at[call].flags |= Flags::HEAP;
4077        let pointer = at[call].results().next().expect("a call that gives back a pointer");
4078        (names, func, block, pointer, index)
4079    }
4080
4081    #[test]
4082    fn a_walk_by_a_step_the_ranges_bound_inside_an_allocation_goes() {
4083        // The first half of tamnd/rucc#880. The step is not a constant, so the walk stops at the
4084        // `ptr_add` and what answers the check has to be asked of the range of addresses it can
4085        // reach. That range is nought to seven plus the four bytes the access wants, all of it
4086        // inside the sixteen the call says it made, and the branch on null is what makes the
4087        // sixteen true here.
4088        let (_, mut func, inside, _, pointer, index) = allocation(16);
4089        let mut build = Builder::new(&mut func, inside);
4090        let step = low_bits(&mut build, index, 7);
4091        let at = walk(&mut build, pointer, step);
4092        check(&mut build, at, 4);
4093        build.ret(&[]);
4094        let stats = run(&mut func);
4095        assert_eq!(checks(&func), 0);
4096        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_MADE), 1);
4097    }
4098
4099    #[test]
4100    fn a_walk_by_a_step_that_can_leave_an_allocation_stays() {
4101        // The same function with the mask widened. Nought to thirty one plus four bytes runs off
4102        // the end of sixteen, and the bytes past the end are what the check is for.
4103        let (_, mut func, inside, _, pointer, index) = allocation(16);
4104        let mut build = Builder::new(&mut func, inside);
4105        let step = low_bits(&mut build, index, 31);
4106        let at = walk(&mut build, pointer, step);
4107        check(&mut build, at, 4);
4108        build.ret(&[]);
4109        let stats = run(&mut func);
4110        assert_eq!(checks(&func), 1);
4111        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_MADE), 0);
4112    }
4113
4114    #[test]
4115    fn a_derivation_by_a_step_the_ranges_bound_inside_an_allocation_goes() {
4116        // The same for the derivation check, which is the one the column sum is left with. Both
4117        // ends have to be inside and inside the same object: the near end is the pointer itself and
4118        // the far end is anywhere in nought to seven past it.
4119        let (_, mut func, inside, _, pointer, index) = allocation(16);
4120        let mut build = Builder::new(&mut func, inside);
4121        let step = low_bits(&mut build, index, 7);
4122        let at = walk(&mut build, pointer, step);
4123        deriv(&mut build, pointer, at, 1);
4124        build.ret(&[]);
4125        let stats = run(&mut func);
4126        assert_eq!(derivs(&func), 0);
4127        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_DERIV_MADE), 1);
4128    }
4129
4130    #[test]
4131    fn a_walk_into_an_allocation_nobody_tested_stays() {
4132        // The other half of the rule, which this does not weaken. A program that walks into what
4133        // `malloc` gave back without ever looking at it is a program that reads through null when
4134        // the allocation fails, and the checks are what report it.
4135        let (_, mut func, block, pointer, index) = untested(16);
4136        let mut build = Builder::new(&mut func, block);
4137        let step = low_bits(&mut build, index, 7);
4138        let at = walk(&mut build, pointer, step);
4139        check(&mut build, at, 4);
4140        deriv(&mut build, pointer, at, 1);
4141        build.ret(&[]);
4142        let stats = run(&mut func);
4143        assert_eq!(checks(&func), 1);
4144        assert_eq!(derivs(&func), 1);
4145        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_MADE), 0);
4146        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_DERIV_MADE), 0);
4147    }
4148
4149    #[test]
4150    fn a_check_every_caller_hands_in_goes_across_a_call() {
4151        // The reason the flag is worth having at all. A frame slot of the caller is not something
4152        // the callee's own callees can free, so the fact does not die at a call the way a fact
4153        // from a check that ran does.
4154        let (mut names, mut func, block, pointer) = blank();
4155        let mut build = Builder::new(&mut func, block);
4156        access(&mut build, pointer, 4);
4157        let signature = build.func().add_signature(Signature::new());
4158        build.call(names.intern("g"), signature, &[]);
4159        access(&mut build, pointer, 4);
4160        build.ret(&[]);
4161        flagged(&mut func, Flags::HANDED);
4162        let stats = run(&mut func);
4163        assert_eq!(checks(&func), 0);
4164        assert_eq!(lives(&func), 0);
4165        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_HANDED), 2);
4166        assert_eq!(stats.count(Kind::Optimized, super::REMOVED_LIVE_HANDED), 2);
4167    }
4168
4169    #[test]
4170    fn a_check_inside_a_global_goes_across_a_call() {
4171        // A callee can free what a global points at and cannot free the global, which lives as
4172        // long as the program does. So this is the one fact besides a local that a call leaves
4173        // standing, and it is read off the instruction rather than out of the scope for that
4174        // reason.
4175        let (mut names, mut func, block, pointer) = blank();
4176        let mut build = Builder::new(&mut func, block);
4177        let callee = names.intern("might_free");
4178        let signature = build.func().add_signature(Signature::new());
4179        build.call(callee, signature, &[]);
4180        access(&mut build, pointer, 4);
4181        build.ret(&[]);
4182        marked(&mut func);
4183        let stats = run(&mut func);
4184        assert_eq!(checks(&func), 0);
4185        assert_eq!(lives(&func), 0);
4186        assert_eq!(stats.count(Kind::Missed, super::PAST_A_CALL), 0);
4187        assert_eq!(stats.count(Kind::Missed, super::PAST_A_CALL_LIVE), 0);
4188    }
4189
4190    #[test]
4191    fn a_check_the_module_marked_costs_fuel_like_any_other() {
4192        // A discharge is a discharge whatever established the fact, so `-fpass-fuel` has to stop
4193        // this one too or a bisection would step over it.
4194        let (_, mut func, block, pointer) = blank();
4195        let mut build = Builder::new(&mut func, block);
4196        access(&mut build, pointer, 4);
4197        build.ret(&[]);
4198        marked(&mut func);
4199        let stats =
4200            DISCHARGE.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(1));
4201        assert_eq!(checks(&func) + lives(&func), 1);
4202        assert_eq!(stats.count(Kind::Missed, super::NO_FUEL_LIVE), 1);
4203    }
4204
4205    #[test]
4206    fn a_walk_whose_two_ends_are_off_two_pointers_with_no_ranges_says_the_same() {
4207        // Nothing in this function steps by a value, so the ranges are never built and the answer
4208        // has to come out of the constant reader alone. That reader stopped for one reason, and it
4209        // is the same reason.
4210        let (_, mut func, block, pointer) = blank();
4211        let other = func.append_param(block, Type::PTR);
4212        let mut build = Builder::new(&mut func, block);
4213        let at = past(&mut build, other, 8);
4214        deriv(&mut build, pointer, at, 4);
4215        build.ret(&[]);
4216        let stats = run(&mut func);
4217        assert_eq!(derivs(&func), 1);
4218        assert_eq!(stats.count(Kind::Missed, super::TWO_BASES_DERIV), 1);
4219    }
4220
4221    #[test]
4222    fn a_walk_whose_two_ends_are_off_two_pointers_says_so() {
4223        // Nothing comparable to ask about. Both ends are readable and each is somewhere inside
4224        // something, and two facts of that shape say nothing at all about it being one something,
4225        // which is the only thing a derivation check wants to know.
4226        let (_, mut func, block, pointer, index) = indexed();
4227        let other = func.append_param(block, Type::PTR);
4228        let mut build = Builder::new(&mut func, block);
4229        let step = low_bits(&mut build, index, 7);
4230        let at = walk(&mut build, other, step);
4231        deriv(&mut build, pointer, at, 4);
4232        build.ret(&[]);
4233        let stats = run(&mut func);
4234        assert_eq!(derivs(&func), 1);
4235        assert_eq!(stats.count(Kind::Missed, super::TWO_BASES_DERIV), 1);
4236    }
4237
4238    #[test]
4239    fn a_walk_off_a_pointer_this_function_was_handed_says_so() {
4240        // The largest pile after a loaded pointer, 1321 checks on SQLite. Everything about the
4241        // shape is readable: one base, a step the ranges bound, both ends off that base. What is
4242        // missing is how many bytes belong to the object, and a pointer that arrived as a
4243        // parameter is one nothing in the function can say that about. Section 7.5's summaries are
4244        // what would.
4245        let (_, mut func, block, pointer, index) = indexed();
4246        let mut build = Builder::new(&mut func, block);
4247        let step = low_bits(&mut build, index, 7);
4248        let at = walk(&mut build, pointer, step);
4249        deriv(&mut build, pointer, at, 4);
4250        build.ret(&[]);
4251        let stats = run(&mut func);
4252        assert_eq!(derivs(&func), 1);
4253        assert_eq!(stats.count(Kind::Missed, super::NO_EXTENT_HANDED), 1);
4254    }
4255
4256    #[test]
4257    fn a_walk_off_a_pointer_this_function_loaded_says_so() {
4258        // The largest pile of the lot, 2155 checks on SQLite, and the shape is `p->field[i]`. The
4259        // extent of what a pointer in memory points at is not written down anywhere the compiler
4260        // can see today, which is what `__counted_by` and the type plane are for.
4261        let (_, mut func, block, pointer, index) = indexed();
4262        let mut build = Builder::new(&mut func, block);
4263        let info = MemInfo {
4264            size: 8,
4265            align: 8,
4266            order: MemOrder::NotAtomic,
4267            tbaa: None,
4268            owns: 0,
4269            restrict: Restrict::NONE,
4270        };
4271        let args = build.func().push_values(&[pointer]);
4272        let extra = Extra::Mem(build.func().add_mem(info));
4273        let held = build.value(InstData { args, extra, ..InstData::new(Opcode::Load) }, Type::PTR);
4274        let step = low_bits(&mut build, index, 7);
4275        let at = walk(&mut build, held, step);
4276        deriv(&mut build, held, at, 4);
4277        build.ret(&[]);
4278        let stats = run(&mut func);
4279        assert_eq!(derivs(&func), 1);
4280        assert_eq!(stats.count(Kind::Missed, super::NO_EXTENT_LOADED), 1);
4281    }
4282
4283    #[test]
4284    fn a_walk_off_a_global_says_so() {
4285        // 485 checks on SQLite, and the one pile of the four where somebody does know the answer.
4286        // A global's extent is on the module, `crate::extents` reads it and writes the fact onto
4287        // every check it can settle before the pipeline starts, and it cannot settle this one
4288        // because it runs before anything has put a number on the index. See tamnd/rucc#878.
4289        let (mut names, mut func, block, _, index) = indexed();
4290        let mut build = Builder::new(&mut func, block);
4291        let extra = Extra::Symbol(names.intern("g"));
4292        let base = build.value(InstData { extra, ..InstData::new(Opcode::GlobalAddr) }, Type::PTR);
4293        let step = low_bits(&mut build, index, 7);
4294        let at = walk(&mut build, base, step);
4295        deriv(&mut build, base, at, 4);
4296        build.ret(&[]);
4297        let stats = run(&mut func);
4298        assert_eq!(derivs(&func), 1);
4299        assert_eq!(stats.count(Kind::Missed, super::NO_EXTENT_GLOBAL), 1);
4300    }
4301
4302    #[test]
4303    fn a_walk_off_a_pointer_the_check_does_not_name_stays() {
4304        // The capability has to be the `cap_of` of the pointer that went in. One naming something
4305        // else is asking about a different instance and is not this pass's to answer.
4306        let (_, mut func, block, pointer) = blank();
4307        let mut build = Builder::new(&mut func, block);
4308        check(&mut build, pointer, 16);
4309        let field = past(&mut build, pointer, 8);
4310        let args = build.func().push_values(&[field]);
4311        let capability = build.value(InstData { args, ..InstData::new(Opcode::CapOf) }, Type::CAP);
4312        let width = build.iconst(Type::int(64), 4);
4313        let args = build.func().push_values(&[capability, pointer, field, width]);
4314        build.inst(InstData { args, ..InstData::new(Opcode::CheckDeriv) }, &[]);
4315        build.ret(&[]);
4316        let stats = run(&mut func);
4317        assert_eq!(derivs(&func), 1);
4318        assert_eq!(stats.count(Kind::Missed, super::NOT_ITS_CAPABILITY_DERIV), 1);
4319    }
4320}