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