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