Skip to main content

rucc_opt/
discharge.rs

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