Skip to main content

rucc_safety/
lib.rs

1//! The memory safety monitor: check insertion over the IR.
2//!
3//! Design: `spec/safe-memory/06-instrumentation.md` section 6.3.
4//!
5//! The one decision this crate exists to make is *when* checks are inserted. Every sanitizer that
6//! came before instruments after the optimizer, so that the optimizer cannot delete its checks,
7//! and pays the full naive cost of every one of them forever. We insert before the optimizer and
8//! let it discharge what it can prove, which is only possible because a check is an instruction
9//! with defined semantics rather than a call the optimizer has no opinion about.
10//!
11//! # What is here so far
12//!
13//! The three checks milestone S1 in `spec/safe-memory/16-milestones.md` asks for: bounds and
14//! lifetime on every access, and a derivation check on every pointer computed from another
15//! pointer. Nothing is discharged, so a function comes out with a check in front of everything,
16//! which is the baseline every elimination claim at S4 is measured against.
17//!
18//! And the boundary, in [`mod@wrap`]: a call the program wrote to one of the C library functions
19//! `rucc-safe-rt` has a row for is pointed at that row's wrapper instead, so the judgements happen
20//! before the call rather than not at all. That is milestone S2 and
21//! `spec/safe-memory/10-boundaries.md` section 10.3 is what it implements.
22//!
23//! And the other end of it, in [`mod@lower`]: after the optimizer has run, every check still standing
24//! becomes a call to the runtime carrying the index of a row in a table this crate puts in the
25//! object. That module is where the reason S1's checks are calls rather than compares is argued.
26//!
27//! And the rest of the boundary, in [`mod@boundary`]: the places where a pointer crosses between
28//! this build and code nobody instrumented, which is a function of this file that somebody else can
29//! call and a call this file makes to a library that has no wrapper. Neither can be modelled, so
30//! each of them is counted instead, which is what section 10.2 says the honest answer to a question
31//! you cannot answer is.
32//!
33//! And what all of that came to, in [`mod@summary`]: the counts `--emit=safety-summary` prints,
34//! which are what `spec/safe-memory/10-boundaries.md` section 10.2 means by a trust set that is
35//! counted per build rather than asserted.
36//!
37//! And the first of the planes, in [`mod@plane`]: every store records what the bytes it wrote were
38//! stored through, which is the judgement C 6.5 says a store makes, and every copy carries whatever
39//! the bytes it read said over to the bytes it wrote, which is the other half of the same rule.
40//! Those two are every write the type plane has, and every read that names a type now asks the
41//! plane whether the bytes agree with it, which is judgement J3. The order was deliberate: a check
42//! against a plane that only some of the writes maintain reports on programs that are correct, so
43//! the writes went in first and the question went in once they were all in.
44//!
45//! And the second of them, over the same two writes: every store records that the bytes it wrote
46//! hold something, and every copy carries whether the bytes it read held anything over to the bytes
47//! it wrote. The init plane is one bit per byte, so a store records the same thing whatever it
48//! stored, and a copy is the reason padding a member by member fill never touched is still padding
49//! nothing wrote after the structure moves. Every read now asks whether anything ever wrote the
50//! bytes it is about to read, which is document 03's Y6, and the writes went in first for the
51//! reason the type plane's did.
52//!
53//! And the one check that is not about a single access, in [`mod@promise`]: a block that declares
54//! `restrict` pointers keeps a record of what each of them reached, and every access through one of
55//! them asks whether another got there first. That is judgement J8 and it is off unless the build
56//! asks for it with `-fsafety-restrict`, which is the only check here that is, and the reason is on
57//! [`rucc_session::Promise`].
58//!
59//! The padding rule of `spec/safe-memory/09-type-init-and-races.md` section 9.3 arrives here as one
60//! number. A store carries how much padding the member it went through owns, and the range it
61//! records is the wider of that and what it wrote, which is all of `-fsafety-init=nopadding`. How
62//! far the padding goes takes a record's layout and this crate reads IR, so the front end is what
63//! decides it and `MemInfo.owns` is how the answer travels.
64//!
65//! The two questions a read asks come apart in one place. A read the front end named no type for
66//! asks the type plane nothing, because the question there is which type the bytes hold, and it
67//! asks the init plane the same thing every other read does, because the question there is about
68//! the bytes rather than about the access. What no `load` in any program asks about is padding: a
69//! read compiled into a `load` reads a member and a member is never padding, so the reads that
70//! cover padding are `memcmp` of two structures, hashing one and handing one to `write`, every one
71//! of which is a call into the movement group of [`mod@wrap`]. Which is why the flag selects what a
72//! store records rather than what a read asks about: the reads that would need it are not `load`s.
73//!
74//! The race check is not here, because the epoch plane is not written at all and a check against a
75//! plane nobody maintains would either report on every access or on none. That is S6. Neither are
76//! the other plane writes: `meta_begin` and `meta_end` for an automatic instance need the escape
77//! analysis of document 08 section 8.4, and until that exists the only instances the runtime knows
78//! about are the ones the allocator reports, which is also why a store to a local records into a
79//! plane that is not there and costs a call that decides nothing.
80//!
81//! # Why the rank matters
82//!
83//! `rucc-safety` is rank 10, alongside `rucc-lower` and `rucc-opt`, so it can depend on neither.
84//! That is the constraint and not an inconvenience: it consumes IR and produces IR, it never sees
85//! the AST, and `rucc-driver` at rank 13 is what sequences it between the two.
86//! `spec/safe-memory/15-integration.md` section 15.1 argues it out.
87//!
88//! # Stability
89//!
90//! Every crate in the workspace is published, and publishing implies a promise. This one is
91//! tier 3: its Rust API is explicitly unstable and will change without a major version bump.
92//! Depend on the `rucc` binary's behaviour, not on this.
93
94#![doc(html_root_url = "https://docs.rs/rucc-safety/0.10.75")]
95
96pub mod boundary;
97pub mod ending;
98pub mod frame;
99pub mod handover;
100pub mod lower;
101pub mod origin;
102pub mod plane;
103pub mod promise;
104pub mod slot;
105pub mod summary;
106pub mod wrap;
107
108pub use boundary::{Sites, WITNESS, witness};
109pub use lower::{Descriptor, SECTION, lower};
110pub use plane::Plane;
111pub use promise::{Kept, promise};
112pub use summary::{Frames, Summary, summarize};
113pub use wrap::{INTERPOSED, PREFIX, redirect};
114
115use rucc_ir::{Def, Extra, Func, Imm, Inst, InstData, Module, Opcode, Type, Value};
116pub use rucc_session::{Promise, Races, Subobject};
117
118/// How many checks a run of [`insert`] put in.
119///
120/// Reported rather than discarded because the number of checks a function starts with is the
121/// denominator of everything document 13 measures, and it is not recoverable later: by the time
122/// the optimizer has run, the checks that were discharged are gone and nothing says how many
123/// there were.
124///
125/// The three counts are kept apart rather than added up because they are discharged by different
126/// rules and at very different rates. Document 07 expects bounds to go away often, lifetime to go
127/// away when the instance does not escape, and derivation to survive, so one number would hide
128/// exactly the thing the measurement is for.
129#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
130pub struct Counts {
131    /// Accesses that were given a bounds check.
132    pub checked: usize,
133    /// Accesses that were given a lifetime check, which is the same set as `checked`.
134    pub live: usize,
135    /// Calls that end a storage instance and were given a check in front of them.
136    ///
137    /// Not filled in by [`insert`], which is the one count here that is not. It comes from
138    /// [`mod@ending`], a pass of its own for the reason that module gives, and the driver puts what
139    /// that returns in here so that a check class is reported beside the other check classes rather
140    /// than off to one side.
141    pub freed: usize,
142    /// Pointers computed from another pointer that were given a derivation check.
143    pub derived: usize,
144    /// Accesses that got nothing, because the pointer they go through is not a value this pass
145    /// can take the capability of.
146    pub skipped: usize,
147    /// Stores that recorded what the bytes they wrote were stored through.
148    ///
149    /// Not in `--emit=safety-summary` yet, which is the one count here that is not. The summary
150    /// reports a class as a pair, how many went in and how many are left, and nothing discharges a
151    /// plane write today, so the pair would be one number written twice. It goes in beside the
152    /// first rule that removes one.
153    pub judged: usize,
154    /// Copies that carried whatever the bytes they read said over to the bytes they wrote.
155    ///
156    /// Kept apart from `judged` for the reason the three check counts are kept apart. A store
157    /// records a type the compiler knows and a copy records one only the plane knows, so the two
158    /// are discharged by different rules: a store into storage nothing watches can be dropped by
159    /// looking at the store, and a copy cannot be looked at the same way.
160    pub carried: usize,
161    /// Accesses that asked the plane whether the bytes agree with the type they name.
162    ///
163    /// Fewer than `checked`, and the reason is in `ask`: an access the front end did not name a
164    /// type for has no question to put. Without `-fsafety-subobject` it is fewer again, because
165    /// only a read asks, and the reason a store asks only when somebody asked for it is on
166    /// [`rucc_session::Subobject`].
167    pub asked: usize,
168    /// Stores that recorded that the bytes they wrote hold what they wrote.
169    ///
170    /// The same set as `checked` minus the reads, and unlike `judged` it does not thin: a store
171    /// records into the init plane whatever it was storing through, because what the init plane
172    /// holds is whether anything was stored at all.
173    pub wrote: usize,
174    /// Reads that asked the plane whether anything ever wrote the bytes they are about to read.
175    ///
176    /// The same set as the reads in `checked`, and unlike `asked` it does not thin: the question is
177    /// whether the bytes hold anything at all, which is a question about every read whatever type
178    /// the front end did or did not name for it.
179    pub filled: usize,
180    /// Copies that carried whether the bytes they read held anything over to the bytes they wrote.
181    ///
182    /// The same set as `carried`, and counted beside it for the reason `wrote` is counted beside
183    /// `judged`: the two planes will be discharged by different rules, so the day one of them
184    /// thins the numbers have to be able to differ.
185    pub moved: usize,
186    /// Copies that carried the capability beside every pointer they moved over to the bytes they
187    /// wrote.
188    ///
189    /// The same set again, and counted apart from the two of them because it is not a plane write:
190    /// what it moves is the aux, which is the storage `saved` fills one word at a time. A build with
191    /// many of these and few of `saved` is a build whose pointers mostly travel inside structures
192    /// being copied whole, which is a different shape of program and worth being able to see.
193    pub relocated: usize,
194    /// Accesses that asked their block whether another `restrict` pointer of it got there first.
195    ///
196    /// Zero without `-fsafety-restrict`, and zero in the overwhelming majority of functions with
197    /// it, because the only accesses that ask are the ones the front end traced back to a
198    /// `restrict` declaration. [`mod@promise`] is where both of those are argued.
199    pub promised: usize,
200    /// Accesses of a pointer that asked whether another thread reached the same granule first.
201    ///
202    /// Zero without `-fsafety-races`. Every store that stamps also asks, so with `metadata` this
203    /// equals `stamped`, and `pointer` adds the loads, which is the one thing separating the two
204    /// modes. [`mod@rucc_session`]'s `Races` is where that is argued.
205    pub watched: usize,
206    /// Stores of a pointer that recorded which thread wrote it and how far that thread had counted.
207    ///
208    /// Zero without `-fsafety-races`, and far fewer than `wrote` with it, because the epoch plane
209    /// watches pointer shaped words and not every byte a program stores. [`mod@rucc_session`]'s
210    /// `Races` is where that is argued.
211    pub stamped: usize,
212    /// Halves of a synchronization edge put around an atomic, which is nought, one or two of them
213    /// per atomic depending on what the program asked that atomic to order.
214    ///
215    /// Zero without `-fsafety-races`, and zero with it in a program that uses no atomics, which is
216    /// most of them. Counted apart from `stamped` because it is not a plane write: the two of them
217    /// answer different questions and a build with a great many of one and none of the other is a
218    /// build somebody should be able to see.
219    pub edged: usize,
220    /// Stores of a pointer that wrote the pointer's capability into the slot beside it.
221    ///
222    /// A subset of `wrote`, and on ordinary code a small one, since most of what a program stores is
223    /// not a pointer. Counted apart from `stamped`, which is the other thing only a pointer store
224    /// does, because the two are paid for by different builds: that one is zero without
225    /// `-fsafety-races` and this one is not optional, being where the capability of everything read
226    /// back out of memory comes from.
227    pub saved: usize,
228    /// Reads of a pointer that took its capability out of the slot beside it.
229    ///
230    /// The one count here that is a saving rather than a cost. Every one of these is a `cap_of` that
231    /// did not go in, and a `cap_of` over a pointer nothing else produced is a walk of the lifetime
232    /// plane. What it becomes instead is a slot read, or the same walk when the word turns out to
233    /// have no slot, which is every local and every global today.
234    pub recalled: usize,
235    /// Blocks that opened a scope, which is one per `restrict` clique that has an access in it.
236    ///
237    /// Kept apart from `promised` because it is the part of the cost that is paid per call rather
238    /// than per access: two calls and a stack slot, against which a block that checks a thousand
239    /// accesses and a block that checks one look very different.
240    pub scoped: usize,
241}
242
243impl Counts {
244    /// Adds another function's counts to these.
245    fn add(&mut self, other: Counts) {
246        self.checked += other.checked;
247        self.live += other.live;
248        self.freed += other.freed;
249        self.derived += other.derived;
250        self.skipped += other.skipped;
251        self.judged += other.judged;
252        self.carried += other.carried;
253        self.asked += other.asked;
254        self.wrote += other.wrote;
255        self.filled += other.filled;
256        self.moved += other.moved;
257        self.relocated += other.relocated;
258        self.stamped += other.stamped;
259        self.saved += other.saved;
260        self.recalled += other.recalled;
261        self.watched += other.watched;
262        self.edged += other.edged;
263        self.promised += other.promised;
264        self.scoped += other.scoped;
265    }
266
267    /// Adds what the `restrict` walk of one function came to.
268    ///
269    /// A second function rather than a second [`Counts`] because that walk counts two things and
270    /// has no opinion about the other ten, and a conversion that filled in ten zeroes would let a
271    /// later count be lost by being added to a zero.
272    fn add_kept(&mut self, kept: Kept) {
273        self.promised += kept.promised;
274        self.scoped += kept.scoped;
275    }
276}
277
278/// Puts checks in every function a module defines.
279///
280/// The whole module rather than a function at a time, because that is the unit the driver hands
281/// around and because the pass has nothing to say about the order: no check depends on anything
282/// outside the function it is in. A declaration has no body and is skipped, for the same reason
283/// the back end skips it.
284///
285/// Whether this runs at all is `-fsafety=`, and the driver decides it. This crate does not read
286/// the flag, because a pass that decides for itself whether it runs is a pass whose effect cannot
287/// be read off the pipeline.
288pub fn run(module: &mut Module, subobject: Subobject, promise: Promise, races: Races) -> Counts {
289    // Before the walk, because the entries live in the module and a function is borrowed out of
290    // the module while its stores are being instrumented. It is also the reason this is the entry
291    // point rather than [`insert`]: there is one plane per module and every function records into
292    // the same one.
293    let plane = Plane::build(module);
294    // The one thing a pointer typed access cannot work out for itself, which is how wide it is.
295    let width = u64::from(module.datalayout.pointer_bits / 8);
296    let mut counts = Counts::default();
297    for id in module.funcs() {
298        if !module[id].is_declaration() {
299            counts.add(insert(&mut module[id], &plane, width, subobject, promise, races));
300        }
301    }
302    counts
303}
304
305/// Puts checks in front of every access and every derivation in a function.
306///
307/// Section 6.3: every `load` and `store` gets `check_bounds` and `check_live`, with the
308/// capability coming from the pointer operand, and every `ptr_add` gets `check_deriv` on the
309/// pointer it was computed from. The size and the alignment are the access's own, since a check
310/// that asked about a different number of bytes from the access it guards would be checking
311/// something the program does not do.
312///
313/// A capability belongs to a pointer rather than to an access, so two checks through the same
314/// pointer read one `cap_of` and a walk reads the one belonging to what it walked off.
315/// [`mod@origin`] is where it is made and where that is argued.
316///
317/// The two access checks are separate instructions rather than one fused check, which section
318/// 6.2.2 asks for and which matters more than it looks: the common case document 07 is built
319/// around is that the bounds check is discharged and the lifetime check is not, or the other way
320/// round for a local whose frame the compiler can see. One instruction would mean keeping both
321/// whenever either survived. Where both do survive, the backend fuses them behind one branch.
322///
323/// Nothing is discharged here. A `check_bounds` on a pointer whose bounds are statically obvious
324/// is still emitted, and the fact propagation in `rucc-opt` is what removes it. That split is the
325/// whole design: this pass is a walk anybody can read, and the deletions are rules that are
326/// verified.
327pub fn insert(
328    func: &mut Func,
329    plane: &Plane,
330    width: u64,
331    subobject: Subobject,
332    promise: Promise,
333    races: Races,
334) -> Counts {
335    let mut counts = Counts::default();
336    // One table for the whole function, because a capability belongs to a pointer and not to an
337    // access: two checks through the same pointer read the same one. [`mod@origin`] is where that
338    // is argued and where the placement that makes it sound is.
339    let mut origins = origin::Origins::new();
340    let insts: Vec<Inst> =
341        func.blocks().flat_map(|block| func.insts(block).collect::<Vec<_>>()).collect();
342    for inst in insts {
343        match func[inst].opcode {
344            Opcode::Load | Opcode::Store => match pointer_of(func, inst) {
345                Some(pointer) => {
346                    let capability = check(func, &mut origins, inst, pointer, width);
347                    counts.checked += 1;
348                    counts.live += 1;
349                    if func[inst].opcode == Opcode::Store {
350                        // In front of the store, and only when the build asked for it. Every other
351                        // question at a store is a recording made afterwards, and this is the one
352                        // that can refuse, so it has to be asked while the bytes still say what
353                        // they said before.
354                        if subobject.asks() && ask(func, plane, inst, pointer, capability, width) {
355                            counts.asked += 1;
356                        }
357                        // Also in front, and in second so that it lands nearest the store of the
358                        // two that can refuse. It has to be on this side of the store for the
359                        // reason [`raced`] gives, and being the last question asked is what makes
360                        // the stamp it reads the one that was there when the store began.
361                        if races.records() && raced(func, inst, pointer, capability, width) {
362                            counts.watched += 1;
363                        }
364                        // First of everything that goes after the store, so it ends up furthest
365                        // from it. It is the only one of these that is not a plane write: the rest
366                        // record something about the bytes, keyed on the address, and this writes
367                        // the slot that sits beside the word. Nothing here reads what anything else
368                        // here wrote, so the order is a matter of what reads well rather than of
369                        // what is correct.
370                        if saved(func, &mut origins, inst, pointer, capability) {
371                            counts.saved += 1;
372                        }
373                        // The init plane's write goes in first so that the type plane's ends up in
374                        // front of it, since both are inserted after the store and the one that
375                        // goes in second is the one that lands nearer to it.
376                        if wrote(func, inst, pointer, width) {
377                            counts.wrote += 1;
378                        }
379                        if judge(func, plane, inst, pointer, width) {
380                            counts.judged += 1;
381                        }
382                        // Last, so that it ends up nearest the store of the three recordings, which
383                        // is where the one that is read by another thread belongs. The question
384                        // above it went in front of the store, so it reads the plane before this
385                        // overwrites what it read.
386                        if races.records() && stamped(func, inst, pointer, width) {
387                            counts.stamped += 1;
388                        }
389                    } else {
390                        // Both go in front of the read, and the one that goes in second is the one
391                        // that lands nearer to it, so this order prints the type question and then
392                        // the init question. Either order is correct: neither reads what the other
393                        // wrote and the read happens after both.
394                        if ask(func, plane, inst, pointer, capability, width) {
395                            counts.asked += 1;
396                        }
397                        if filled(func, inst, pointer, capability, width) {
398                            counts.filled += 1;
399                        }
400                        // Only under `-fsafety-races=pointer`, which is the mode that reports C2.
401                        // The other one watches what a store does and leaves a load alone.
402                        if races.reads() && raced(func, inst, pointer, capability, width) {
403                            counts.watched += 1;
404                        }
405                        // After the read, since what it is about is the value the read produced.
406                        // The only one of these that puts something in behind the access rather
407                        // than in front of it, and the only one that answers rather than asks.
408                        if recalled(func, &mut origins, inst, pointer, capability) {
409                            counts.recalled += 1;
410                        }
411                    }
412                }
413                None => counts.skipped += 1,
414            },
415            Opcode::Memcpy | Opcode::Memmove => {
416                // The two ends of a copy are two accesses of the same width, a read through one
417                // pointer and a write through the other, so each gets the pair an access of its own
418                // would get. The destination's pair goes in first and so stands first in the
419                // stream, which is the order a report reads best in when both ends are bad: the
420                // write is what the program was for and the read is how it got there. Both pairs
421                // carry the one payload the copy has, since the width is the same width twice and
422                // the two ends of a copy the front end wrote have the same type as each other.
423                for through in func.bulk(inst).into_iter().flat_map(|bulk| [bulk.to, bulk.with]) {
424                    if spanned(func, &mut origins, inst, through) {
425                        counts.checked += 1;
426                        counts.live += 1;
427                    } else {
428                        counts.skipped += 1;
429                    }
430                }
431                // The aux first of the three behind it, so that it ends up behind the two plane
432                // copies in the stream. Any order is right here, since none of the three reads what
433                // another writes and all three happen after the copy, and this one matches the
434                // order the wrapper around the library's own `memcpy` does its three in.
435                if relocation(func, inst) {
436                    counts.relocated += 1;
437                }
438                // Second for the reason a store's two are in the order they are in.
439                if moved(func, inst) {
440                    counts.moved += 1;
441                }
442                if carry(func, inst) {
443                    counts.carried += 1;
444                }
445            }
446            Opcode::Memset => {
447                // The two checks in front, in the order a store's two go in, because a fill is a
448                // write of as many bytes as it says through a pointer somebody handed it and a
449                // fill that runs off the end of an object is the overflow this build exists to
450                // report.
451                let filled = func.bulk(inst).map(|bulk| bulk.to);
452                if filled.is_some_and(|to| spanned(func, &mut origins, inst, to)) {
453                    counts.checked += 1;
454                    counts.live += 1;
455                } else {
456                    counts.skipped += 1;
457                }
458                // Both plane writes go in behind it, and they go in together or not at all.
459                if recorded(func, plane, inst) {
460                    counts.wrote += 1;
461                    counts.judged += 1;
462                }
463            }
464            Opcode::PtrAdd => {
465                if derivation(func, &mut origins, inst) {
466                    counts.derived += 1;
467                } else {
468                    counts.skipped += 1;
469                }
470            }
471            // The edges, which are nothing like the rest of this walk: they put no question and
472            // record nothing about the bytes the atomic touched. What they do is tell the epoch
473            // plane that two threads were ordered here, which is the one thing a plane made of
474            // per thread counters cannot work out for itself. Both modes emit them, because what
475            // separates the modes is which classes are reported and an edge reports nothing.
476            Opcode::AtomicLoad | Opcode::AtomicStore | Opcode::AtomicRmw | Opcode::Cmpxchg
477                if races.records() =>
478            {
479                counts.edged += edges(func, inst);
480            }
481            // The same edge without a key. A fence is the other way a C program orders two
482            // threads without calling anything, and the reason it is a separate arm is that it
483            // has no address in it at all.
484            Opcode::Fence if races.records() => {
485                counts.edged += fenced(func, inst);
486            }
487            _ => {}
488        }
489    }
490    // Last, so that the check it puts in front of an access lands after the bounds check that is
491    // already there. It is its own walk rather than another arm above because what it puts in is
492    // not one check per access: the scopes are per function and the two calls that keep one go in
493    // the entry block and at every exit.
494    if promise.checks() {
495        counts.add_kept(promise::promise(func, width));
496    }
497    counts
498}
499
500/// The pointer an access goes through.
501///
502/// A `load` reads through its first operand and a `store` writes through its second, the value
503/// being written coming first because that is the order the text writes them in.
504fn pointer_of(func: &Func, access: Inst) -> Option<Value> {
505    let args = &func[func[access].args];
506    let at = match func[access].opcode {
507        Opcode::Load => 0,
508        Opcode::Store => 1,
509        _ => return None,
510    };
511    let &value = args.get(at)?;
512    func[value].ty.is_ptr().then_some(value)
513}
514
515/// Puts `check_bounds` and `check_live` immediately before one access, over the pointer's
516/// capability.
517///
518/// Gives back the capability the two checks read, so that a third check on the same access can read
519/// the same one rather than taking it again. An access with no payload gets nothing and answers
520/// nothing, which is the shape a caller has to handle anyway.
521///
522/// The capability comes out of `origins` rather than being taken here, so an access through a
523/// pointer some earlier access already asked about reads what that one read. Where it is made and
524/// why that is sound is [`mod@origin`].
525fn check(
526    func: &mut Func,
527    origins: &mut origin::Origins,
528    access: Inst,
529    pointer: Value,
530    width: u64,
531) -> Option<Value> {
532    let span = func.span(access);
533    let Extra::Mem(info) = func[access].extra else { return None };
534    let mut info = func[info];
535    info.size = covered(func, access, info.size, width);
536    // Not the padding after it. What a check is about is the bytes the access touches, and the
537    // padding is about what a store records rather than about what it reads or writes.
538    info.owns = 0;
539
540    let capability = origins.of(func, pointer, access);
541
542    // The check reads the same bytes the access does, so it carries the access's own payload
543    // rather than a copy of it that could later disagree.
544    let args = func.push_values(&[capability, pointer]);
545    let extra = Extra::Mem(func.add_mem(info));
546    let bounds =
547        func.create_inst(InstData { args, extra, ..InstData::new(Opcode::CheckBounds) }, &[], span);
548    func.insert_before(bounds, access);
549
550    // No payload on this one. Whether the capability still names whoever owns the address is a
551    // question about the pointer and not about how many bytes are being read through it.
552    let args = func.push_values(&[capability, pointer]);
553    let live = func.create_inst(InstData { args, ..InstData::new(Opcode::CheckLive) }, &[], span);
554    func.insert_before(live, access);
555
556    Some(capability)
557}
558
559/// Puts `check_bounds` and `check_live` immediately before one fill, over the bytes it covers.
560///
561/// The same two instructions [`check`] puts in front of a `load` or a `store`, and a separate
562/// function rather than a case in that one because of where the byte count comes from. That one
563/// takes it out of the access's payload, and half the fills in the IR hold their count in an
564/// operand instead, since an object whose length the program works out is zeroed by a fill of a
565/// length the program works out. `check_bounds` has an operand for exactly that, which
566/// `spec/safe-memory/07-check-elimination.md` section 7.4 put there for the one check that stands
567/// for a loop, and a fill is the other thing that needs it. The payload still travels either way,
568/// because it holds the alignment and what the front end named the bytes.
569///
570/// The lifetime check has no payload for the reason it has none in front of an access: whether the
571/// capability still names whoever owns the address is a question about the pointer rather than
572/// about how many bytes are going through it.
573fn spanned(func: &mut Func, origins: &mut origin::Origins, bulk: Inst, through: Value) -> bool {
574    let Some(length) = func.bulk(bulk).map(|bulk| bulk.length) else { return false };
575    let Extra::Mem(info) = func[bulk].extra else { return false };
576    let mut info = func[info];
577    // Not the padding after it, for the reason [`check`] gives about an access.
578    info.owns = 0;
579
580    let span = func.span(bulk);
581    let capability = origins.of(func, through, bulk);
582
583    let mut operands = vec![capability, through];
584    operands.extend(length);
585    let args = func.push_values(&operands);
586    let extra = Extra::Mem(func.add_mem(info));
587    let bounds =
588        func.create_inst(InstData { args, extra, ..InstData::new(Opcode::CheckBounds) }, &[], span);
589    func.insert_before(bounds, bulk);
590
591    let args = func.push_values(&[capability, through]);
592    let live = func.create_inst(InstData { args, ..InstData::new(Opcode::CheckLive) }, &[], span);
593    func.insert_before(live, bulk);
594
595    true
596}
597
598/// Puts the two plane writes a fill owes in behind it, and says whether it did.
599///
600/// One function rather than two calls beside each other in the walk, because both read the same
601/// length and the length of a fill whose payload holds it is a constant that has to be made:
602/// asking for it twice would make it twice.
603///
604/// The init plane's write goes in first so that the type plane's ends up in front of it, since both
605/// are inserted after the same instruction and the one that goes in second is the one that lands
606/// nearer to it. That is the order a store's two are in, and it is a matter of what reads well
607/// rather than of what is correct, since neither reads what the other wrote.
608fn recorded(func: &mut Func, plane: &Plane, fill: Inst) -> bool {
609    let Some(to) = func.bulk(fill).map(|bulk| bulk.to) else { return false };
610    let Some((made, length)) = copied(func, fill) else { return false };
611    scrubbed(func, fill, made, to, length);
612    untyped(func, plane, fill, made, to, length);
613    true
614}
615
616/// Puts a `meta_init` immediately after one fill, recording that the bytes it covered hold
617/// something.
618///
619/// What [`wrote`] does for a store, said about a range the fill already carries rather than about a
620/// width worked out from a type. A fill writes a byte into every one of those bytes, so every one
621/// of them holds what was written, and a plane that went on saying otherwise is what made
622/// `struct S s = {0};` leave a zeroed object the monitor thought was never written.
623///
624/// The length and the instruction to go in after are handed over rather than worked out, because
625/// the type plane's write beside this one reads the same length and a second [`copied`] would make
626/// a second constant for it.
627fn scrubbed(func: &mut Func, fill: Inst, made: Inst, to: Value, length: Value) {
628    let span = func.span(fill);
629    let args = func.push_values(&[to, length]);
630    let data = InstData { args, ..InstData::new(Opcode::MetaInit) };
631    let written = func.create_inst(data, &[], span);
632    func.insert_after(written, made);
633}
634
635/// Puts a `meta_type` immediately after one fill, recording that the bytes it covered hold no type.
636///
637/// The type-plane operation `spec/safe-memory/06-instrumentation.md` section 6.3 asks a fill for,
638/// and the entry is the untyped one every time rather than whatever the front end named. A fill
639/// writes a byte over a range and a byte is not a value of any type, so C 6.5 leaves the bytes with
640/// no effective type at all and the plane's untyped entry is the one that says so.
641///
642/// What it is really for is taking away what was there before. Without it the plane goes on
643/// describing an object that has been filled over, so a program that stores a `float` through a
644/// piece of allocated storage, fills the whole thing and reads it back as an `int` is refused for
645/// reading bytes at a type they do not hold, which is over-reporting and is the direction
646/// `spec/safe-memory/09-type-init-and-races.md` section 9.1 says this design does not go in. The
647/// untyped entry is compatible with every access, so what it costs is a question that is not asked.
648fn untyped(func: &mut Func, plane: &Plane, fill: Inst, made: Inst, to: Value, length: Value) {
649    let span = func.span(fill);
650    let args = func.push_values(&[to, length]);
651    let extra = Extra::Node(plane.entry(None));
652    let data = InstData { args, extra, ..InstData::new(Opcode::MetaType) };
653    let judged = func.create_inst(data, &[], span);
654    func.insert_after(judged, made);
655}
656
657/// Puts a `meta_type` immediately after one store, recording what its bytes were stored through.
658///
659/// The judgement of C 6.5: a store through an lvalue of type `T` sets the effective type of what it
660/// wrote to `T`, and the plane is where that is written down. What the store names is the aliasing
661/// node the walk put on it, and [`Plane::entry`] is the translation from that to the entry the
662/// plane holds, including the two cases that are not a type.
663///
664/// After the store rather than before it, which is the one thing about the placement that matters.
665/// The bytes are stored through that type once the store has happened, and a plane that said so
666/// first would be describing a store that the bounds check in front of it may yet refuse.
667///
668/// The length is a value rather than a field of the payload because that is the shape the opcode
669/// has, and it is a `meta_type` over a range because one store writes a run of bytes. Where the
670/// value comes from is [`extent`].
671fn judge(func: &mut Func, plane: &Plane, store: Inst, pointer: Value, width: u64) -> bool {
672    let Extra::Mem(info) = func[store].extra else { return false };
673    let size = covered(func, store, func[info].size, width);
674    // A store whose width nothing states covers no bytes anybody can name, and a plane write over
675    // nothing is an instruction with no effect.
676    if size == 0 {
677        return false;
678    }
679    let node = plane.entry(func[info].tbaa);
680
681    let span = func.span(store);
682    let (made, length) = extent(func, store, size);
683    let args = func.push_values(&[pointer, length]);
684    let data = InstData { args, extra: Extra::Node(node), ..InstData::new(Opcode::MetaType) };
685    let judged = func.create_inst(data, &[], span);
686    // After the constant it reads rather than after the store, since both go in the same place and
687    // the one that goes in second ends up in front.
688    func.insert_after(judged, made);
689    true
690}
691
692/// Puts a `check_type` immediately before one read, asking whether the bytes agree with the type
693/// they are about to be read as.
694///
695/// Judgement J3, and the half of the type plane that decides something. The two writes record what
696/// a store and a copy left behind, and this is the question they were recorded for: the effective
697/// type rule of C 6.5 says an object's stored value may only be read through a type compatible with
698/// the one it was stored through, and the plane is where the compiler wrote down which that was.
699///
700/// # Why only a read
701///
702/// A store does not ask, it answers. The plane covers storage the allocator reported and nothing
703/// else, which is exactly the storage C gives no declared type, and the effective type of such an
704/// object is whatever the last store through it set. So a store cannot disagree with the plane: it
705/// is what makes the plane say what it says, and a check in front of one would refuse the reuse of
706/// a buffer that the standard permits.
707///
708/// # Why a read that names no type asks nothing
709///
710/// An access whose payload carries no aliasing node is an access the front end did not say the type
711/// of, which is a copy of an aggregate, an array, or anything else reached by address. That is not
712/// the same as reading bytes nothing has been stored through, and the plane's untyped entry means
713/// the second one. Asking with it would refuse every read of a structure whose members had been
714/// stored through their own types, which is every correct program that has one.
715///
716/// The check goes in front of the read, behind the bounds and lifetime checks that are also in
717/// front of it. A question about what the bytes say is worth asking only once somebody owns them,
718/// and what the runtime answers for an address no region covers is nothing rather than a refusal.
719fn ask(
720    func: &mut Func,
721    plane: &Plane,
722    read: Inst,
723    pointer: Value,
724    capability: Option<Value>,
725    width: u64,
726) -> bool {
727    let Some(capability) = capability else { return false };
728    let Extra::Mem(at) = func[read].extra else { return false };
729    let mut info = func[at];
730    let Some(node) = info.tbaa else { return false };
731    info.size = covered(func, read, info.size, width);
732    // A read whose width nothing states reads no bytes anybody can name, the same way a store of
733    // none writes none.
734    if info.size == 0 {
735        return false;
736    }
737    // The payload the check carries is the access's, with the aliasing node replaced by the plane
738    // entry for it, because the plane and the aliasing tree are two vocabularies and the question is
739    // put in the plane's.
740    info.tbaa = Some(plane.entry(Some(node)));
741    // As in `access_checks`, and here it could never be anything else: a read carries no padding.
742    info.owns = 0;
743
744    let span = func.span(read);
745    let args = func.push_values(&[capability, pointer]);
746    let extra = Extra::Mem(func.add_mem(info));
747    let data = InstData { args, extra, ..InstData::new(Opcode::CheckType) };
748    let asked = func.create_inst(data, &[], span);
749    func.insert_before(asked, read);
750    true
751}
752
753/// Puts a `meta_type_copy` immediately after one copy, carrying what its source said to its
754/// destination.
755///
756/// The other half of the judgement C 6.5 describes. A copy does not store through a type, so there
757/// is no type for the compiler to record: what the copied bytes are is whatever the bytes they came
758/// from were, and the only place that is written down is the plane over the source. So this names
759/// two ranges and no node, and the runtime moves the entries across.
760///
761/// Without it the destination would keep whatever the bytes there said before the copy, which is
762/// the thing that makes a check against the plane unusable. A structure copied into a fresh
763/// allocation would come out untyped at best and, once the allocation had been reused, wrong at
764/// worst, and the very next read of a field would be refused on a program that is correct.
765///
766/// After the copy rather than before it, for the same reason a store's judgement goes after the
767/// store. The bytes say the new thing once the copy has happened. Reading the source's plane
768/// afterwards is the same answer as reading it before, overlap included, because a copy writes no
769/// plane entries of its own.
770fn carry(func: &mut Func, copy: Inst) -> bool {
771    let Some(bulk) = func.bulk(copy) else { return false };
772    let (to, from) = (bulk.to, bulk.with);
773    let Some((made, length)) = copied(func, copy) else { return false };
774
775    let span = func.span(copy);
776    let args = func.push_values(&[to, from, length]);
777    let data = InstData { args, ..InstData::new(Opcode::MetaTypeCopy) };
778    let carried = func.create_inst(data, &[], span);
779    // After the constant it reads rather than after the copy, since both go in the same place and
780    // the one that goes in second ends up in front.
781    func.insert_after(carried, made);
782    true
783}
784
785/// Puts a `meta_init` immediately after one store, recording that its bytes hold what it wrote.
786///
787/// The judgement of `spec/safe-memory/09-type-init-and-races.md` section 9.2, and the write the
788/// init plane is made of. An instance beginning is the only thing that makes a byte unwritten, and
789/// this is the only thing that makes one written again, so between the two of them the plane holds
790/// exactly the bytes the monitor watched a store land on.
791///
792/// After the store, and for the same reason the type plane's judgement goes after one: the bytes
793/// hold what was written once the store has happened, and saying so first would be describing a
794/// store the bounds check in front of it may yet refuse.
795///
796/// # Where the padding rule lives
797///
798/// Section 9.3 says a store that writes an object as a whole initializes it as a whole, padding
799/// included, and that a member by member fill leaves the padding alone. Nothing here implements
800/// that, and nothing has to. Both arrive as a range and the range is the access's own width: a
801/// store through a member of a structure is a `store` of the member's width and names the member,
802/// and a structure assigned whole, a `= {0}`, a `memset` and a `memcpy` are all a copy of `sizeof`
803/// bytes and name the object. The rule falls out of what the front end already lowered rather than
804/// out of anything this pass knows about structures, which is what keeps it one rule rather than a
805/// special case per shape.
806///
807/// # Why this does not thin
808///
809/// A store records into the init plane whatever type it was storing through, including the two
810/// cases the type plane has no entry for. What the init plane holds is whether anything was stored
811/// at all, and the answer to that does not depend on what the store thought it was writing, so
812/// every store that covers a byte records it.
813fn wrote(func: &mut Func, store: Inst, pointer: Value, width: u64) -> bool {
814    let Extra::Mem(info) = func[store].extra else { return false };
815    // The padding after a member, where the front end was asked to say how far it goes. That is
816    // the whole of `-fsafety-init=nopadding` and it is a number rather than a mode here, because
817    // what the padding is takes a record's layout and this pass reads IR.
818    let size = covered(func, store, func[info].size, width).max(u64::from(func[info].owns));
819    // A store whose width nothing states writes no bytes anybody can name, the same way the type
820    // plane's judgement over one records nothing.
821    if size == 0 {
822        return false;
823    }
824
825    let span = func.span(store);
826    let (made, length) = extent(func, store, size);
827    let args = func.push_values(&[pointer, length]);
828    let data = InstData { args, ..InstData::new(Opcode::MetaInit) };
829    let judged = func.create_inst(data, &[], span);
830    // After the constant it reads rather than after the store, since both go in the same place and
831    // the one that goes in second ends up in front.
832    func.insert_after(judged, made);
833    true
834}
835
836/// Puts a `check_init` in front of one read, asking whether anything ever wrote the bytes it is
837/// about to read.
838///
839/// Document 03's Y6, and the class MSan exists for. The two writes are in, a store recording that
840/// the bytes it wrote hold something and a copy carrying whether the bytes it read held anything,
841/// so the plane now says something true about every byte a program wrote and this is the question
842/// those writes were recorded for.
843///
844/// # Why every read and not only the ones that named a type
845///
846/// [`ask`] passes over a read the front end named no type for, because a question about which type
847/// bytes hold has nothing to ask when the access names none. This one has no such case: the plane
848/// holds one bit per byte and the bit says whether anything was ever stored there, which is a fact
849/// about the bytes and not about the access, so a read of an aggregate by address asks it just as a
850/// read of an `int` does.
851///
852/// # Padding
853///
854/// It does not come up here and that is worth writing down, because section 9.3 is where the
855/// padding rule lives and this is the check the rule is about. A read compiled into a `load` reads
856/// a member, and a member is never padding, so no `load` in any program covers a byte a
857/// member-by-member fill left alone. The reads that do cover padding are `memcmp` of two
858/// structures, hashing one, and handing one to `write`, and every one of those is a call into the
859/// movement group of `crate::wrap` rather than a `load`. That is where `-fsafety-init=padding` will
860/// have something to select, and it is why the flag is not here.
861///
862/// # Why a whole width and not a byte
863///
864/// The payload's size, the same one [`ask`] uses, so a read that straddles the end of what was
865/// written is refused on the first byte nothing wrote rather than on the byte the address names.
866/// A read of four bytes where two were written is a read of memory that was never written, and
867/// reporting it at the access is the only place a report means anything.
868fn filled(
869    func: &mut Func,
870    read: Inst,
871    pointer: Value,
872    capability: Option<Value>,
873    width: u64,
874) -> bool {
875    let Some(capability) = capability else { return false };
876    let Extra::Mem(at) = func[read].extra else { return false };
877    let mut info = func[at];
878    info.size = covered(func, read, info.size, width);
879    // A read whose width nothing states reads no bytes anybody can name, as in [`ask`].
880    if info.size == 0 {
881        return false;
882    }
883    // The plane holds no types, so whatever the access named is not a thing this question is in
884    // terms of, and carrying it would suggest the check compares against it.
885    info.tbaa = None;
886
887    let span = func.span(read);
888    let args = func.push_values(&[capability, pointer]);
889    let extra = Extra::Mem(func.add_mem(info));
890    let data = InstData { args, extra, ..InstData::new(Opcode::CheckInit) };
891    let asked = func.create_inst(data, &[], span);
892    func.insert_before(asked, read);
893    true
894}
895
896/// Puts a `cap_copy` immediately after one copy, carrying the capability beside every pointer it
897/// moved.
898///
899/// The third of the three, and the one about the aux rather than about a plane. A pointer written
900/// to memory leaves its capability in the slot beside it, and a copy of a structure moves the
901/// pointer without touching the slot, so a copy with nothing here leaves every pointer in the
902/// destination described by whatever the slot said before. On fresh storage that is nothing, and
903/// the first access through the copied pointer is refused on a program that is correct. On storage
904/// the allocator has handed out before it is worse and quieter, because the slot holds an older
905/// instance's answer and a pointer that really is stale inherits a version that says it is live.
906///
907/// `struct point *a = b;` does not need this and does not get it: an assignment of one pointer is a
908/// `store` and [`saved`] puts a `cap_store` behind it. What needs it is `*a = *b;` of a structure
909/// with a pointer in it, which the front end turns into a `memcpy` of the whole object, and there
910/// the pointers being moved are bytes rather than values and no `store` ever sees them.
911///
912/// The library's own `memcpy` has had this all along. The wrapper in
913/// `runtime/rucc-safe-rt/src/effects.rs` calls the same three, so what this closes is the
914/// difference between a copy the program wrote by hand and one the compiler wrote for it, which is
915/// tamnd/rucc#1471.
916fn relocation(func: &mut Func, copy: Inst) -> bool {
917    let Some(bulk) = func.bulk(copy) else { return false };
918    let (to, from) = (bulk.to, bulk.with);
919    let Some((made, length)) = copied(func, copy) else { return false };
920
921    let span = func.span(copy);
922    let args = func.push_values(&[to, from, length]);
923    let data = InstData { args, ..InstData::new(Opcode::CapCopy) };
924    let carried = func.create_inst(data, &[], span);
925    func.insert_after(carried, made);
926    true
927}
928
929/// Puts a `meta_init_copy` immediately after one copy, carrying whether its source held anything
930/// over to its destination.
931///
932/// The other half of the same write, and the thing that makes an infoleak visible rather than what
933/// hides it. A copy writes no values of its own: whether a destination byte holds anything is
934/// whether the byte it came from did, and the only place that is written down is the plane over the
935/// source. So this names two ranges and a length and nothing else, exactly as the type plane's
936/// carriage does.
937///
938/// A structure filled member by member and then handed whole to `write` or to a socket is the case
939/// worth stating. Marking the destination written would lose it, because the bytes that leave the
940/// program would be bytes the plane had just been told were fine, and those are exactly the bytes
941/// of CWE-200. Carrying the source's answer keeps the padding unwritten all the way to the
942/// boundary, which is where the read that matters happens.
943fn moved(func: &mut Func, copy: Inst) -> bool {
944    let Some(bulk) = func.bulk(copy) else { return false };
945    let (to, from) = (bulk.to, bulk.with);
946    let Some((made, length)) = copied(func, copy) else { return false };
947
948    let span = func.span(copy);
949    let args = func.push_values(&[to, from, length]);
950    let data = InstData { args, ..InstData::new(Opcode::MetaInitCopy) };
951    let carried = func.create_inst(data, &[], span);
952    func.insert_after(carried, made);
953    true
954}
955
956/// The constant a plane write over a range reads its length from, put in just after `at`.
957///
958/// Gives back the instruction as well as the value, because the caller inserts itself after the
959/// constant rather than after `at`: both go in the same place, and the one that goes in second ends
960/// up in front of the one that went in first.
961///
962/// Written in sixty four bits here and put into the target's width by [`lower::lower`], which is
963/// where the only thing that knows the target's width is.
964fn extent(func: &mut Func, at: Inst, size: u64) -> (Inst, Value) {
965    let span = func.span(at);
966    let word = Type::int(64);
967    let extra = Extra::Imm(func.add_imm(Imm::int(i128::from(size), word)));
968    let made = func.create_inst(InstData { extra, ..InstData::new(Opcode::IConst) }, &[word], span);
969    func.insert_after(made, at);
970    let length = func[made].results().next().expect("a constant created with one result has one");
971    (made, length)
972}
973
974/// How long the copy at `at` is, as a value, and what a plane write over it goes in after.
975///
976/// The same pair [`extent`] hands back and for the same reason, except that this one asks the copy
977/// rather than being told a number. A copy whose length the program works out hands its own operand
978/// over and there is no constant to make, so what the caller goes in after is the copy itself,
979/// which puts the write in the same place either way.
980///
981/// `None` for a copy that moves no bytes, which the verifier refuses, so it is a shape that does
982/// not arise rather than a case being handled.
983fn copied(func: &mut Func, at: Inst) -> Option<(Inst, Value)> {
984    let bulk = func.bulk(at)?;
985    if let Some(length) = bulk.length {
986        return Some((at, length));
987    }
988    let Extra::Mem(info) = func[at].extra else { return None };
989    let size = func[info].size;
990    if size == 0 {
991        return None;
992    }
993    Some(extent(func, at, size))
994}
995
996/// How many bytes an access covers.
997///
998/// An ordinary `load` or `store` leaves the `size` field of its payload at zero and takes its width
999/// from the type instead, which is fine for an access and no use at all to a check: a check is
1000/// asked how many bytes are being touched and has no type of its own to read. So the width is
1001/// worked out here and written into the copy of the payload the check carries, and an access that
1002/// did fill the field in keeps what it said.
1003///
1004/// `width` is the target's pointer width in bytes, and it is a parameter because a pointer is the
1005/// one type in the IR that has no width of its own. Reading a zero off `Type::PTR` and passing it
1006/// on is what made every check over a pointer decide over a single byte, which is #953.
1007fn covered(func: &Func, access: Inst, stated: u64, width: u64) -> u64 {
1008    if stated != 0 {
1009        return stated;
1010    }
1011    // A `load` produces the value and a `store` takes it as its first operand.
1012    let ty = match func[access].opcode {
1013        Opcode::Load => func[access].results().next().map(|value| func[value].ty),
1014        Opcode::Store => func[func[access].args].first().map(|&value| func[value].ty),
1015        _ => None,
1016    };
1017    ty.map_or(0, |ty| {
1018        if ty.is_ptr() {
1019            return width;
1020        }
1021        u64::from(ty.bits().div_ceil(8)) * u64::from(ty.lanes())
1022    })
1023}
1024
1025/// Puts a `meta_epoch` immediately after one store, recording which thread wrote a pointer.
1026///
1027/// The recording half of `spec/safe-memory/09-type-init-and-races.md` section 9.5, which is
1028/// judgement J9 of document 04 and document 03's C1 through C4. The plane holds one stamp per
1029/// eight bytes, the stamp names a thread and the step that thread had reached, and everything the
1030/// race classes decide is a comparison against a stamp some store left here.
1031///
1032/// After the store, for the reason the other two recordings go after one: the bytes hold what was
1033/// written once the store has happened, and the check that reads the plane back has to run before
1034/// this one so that it is asking about somebody else's write rather than about this one.
1035///
1036/// # Why only a store of a pointer
1037///
1038/// Because the plane's granule is a pointer and the classes are about pointers. Section 9.5 is the
1039/// section where a race produces a wrong *pointer* rather than a wrong number, which is what makes
1040/// it worth watching at a cost a program can carry in production: a torn integer is a wrong answer
1041/// and a torn pointer is a memory safety failure. Recording every store instead would put two
1042/// threads writing neighbouring bytes of one granule into the plane as each other's strangers, and
1043/// neighbouring bytes are exactly what a granule holding no pointer is made of.
1044///
1045/// So the thinning is the same shape as the type plane's and lands in a different place: that one
1046/// records a store whose type the plane has a name for, and this one records a store whose value is
1047/// a pointer. A store through a `void *` variable is one. A `memcpy` that happens to move pointers
1048/// is not, and that is a lost report rather than a wrong answer, since a copy does not say what the
1049/// bytes it moved were.
1050fn stamped(func: &mut Func, store: Inst, pointer: Value, width: u64) -> bool {
1051    if !pointer_valued(func, store) {
1052        return false;
1053    }
1054
1055    let span = func.span(store);
1056    let (made, length) = extent(func, store, width);
1057    let args = func.push_values(&[pointer, length]);
1058    let data = InstData { args, ..InstData::new(Opcode::MetaEpoch) };
1059    let judged = func.create_inst(data, &[], span);
1060    // After the constant it reads rather than after the store, as [`wrote`] does and for the same
1061    // reason: both go in the same place and the one that goes in second ends up in front.
1062    func.insert_after(judged, made);
1063    true
1064}
1065
1066/// Puts a `cap_store` immediately after one store of a pointer, so the capability goes with it.
1067///
1068/// The writing half of `spec/safe-memory/06-instrumentation.md` section 6.2.2, and the half that
1069/// makes [`recalled`] able to answer. A pointer in a register has its capability in another register
1070/// and a pointer in memory has nowhere to keep one, so the aux slot beside the word is where it
1071/// goes, and this is the instruction that puts it there.
1072///
1073/// It is the expensive half, which is worth saying plainly. Nothing discharges an aux write today,
1074/// so this is a call at every store of a pointer, and the capability it writes is one the stored
1075/// pointer may not have had, in which case asking for it here is what makes it exist. What buys that
1076/// back is on the other side: a pointer read out of memory stops being a walk of the lifetime plane
1077/// and becomes a slot read, and a program that keeps its pointers in structures reads them far more
1078/// often than it stores them.
1079///
1080/// Four operands, and the shape is the call's, which tamnd/rucc#1080 is where that was decided. The
1081/// container's capability comes first because finding the slot starts from the object the word is
1082/// in, then the address of the word, then the pointer that was written, then that pointer's own
1083/// capability, which is the thing being written down.
1084///
1085/// A store whose value is not a pointer has nothing to write and no slot to write it in. A store the
1086/// access could not be given a capability for is left alone as well, since the container is where
1087/// the slot is found and there is no second way to find it.
1088fn saved(
1089    func: &mut Func,
1090    origins: &mut origin::Origins,
1091    store: Inst,
1092    pointer: Value,
1093    container: Option<Value>,
1094) -> bool {
1095    let Some(container) = container else { return false };
1096    if !pointer_valued(func, store) {
1097        return false;
1098    }
1099    let Some(&value) = func[func[store].args].first() else { return false };
1100
1101    let held = origins.of(func, value, store);
1102    let span = func.span(store);
1103    let args = func.push_values(&[container, pointer, value, held]);
1104    let data = InstData { args, ..InstData::new(Opcode::CapStore) };
1105    let made = func.create_inst(data, &[], span);
1106    func.insert_after(made, store);
1107    true
1108}
1109
1110/// Puts a `cap_load` immediately after one read of a pointer, and makes that the pointer's own.
1111///
1112/// The reading half of section 6.2.2. Without it a pointer read out of memory has no producer but
1113/// `cap_of`, which lowers to a walk of the lifetime plane linear in the size of the object, and a
1114/// C program of any size keeps its pointers in structures and reads them back. That walk is the cost
1115/// tamnd/rucc#1241 is about and this is the second of the three producers that take it away.
1116///
1117/// It answers a question the walk cannot, which matters more than the speed. Recovering from an
1118/// address says which instance owns those bytes now. The slot says which instance the pointer was
1119/// written for. A pointer stored in a structure, freed, and the storage handed to somebody else has
1120/// those two disagree, and the version in the slot is what makes `check_live` refuse at the first
1121/// access through it rather than pass because the address landed inside a live object.
1122///
1123/// Three operands rather than four: the container, the word, and the value that came out of it, with
1124/// the capability being what comes back instead of what goes in.
1125///
1126/// [`origin::Origins::seed`] is what stops a second producer being made for the same value. The load
1127/// is the definition of the pointer, so this sits exactly where [`mod@origin`] would have put a
1128/// `cap_of`, and everything downstream that asks for the pointer's capability gets this one.
1129fn recalled(
1130    func: &mut Func,
1131    origins: &mut origin::Origins,
1132    load: Inst,
1133    pointer: Value,
1134    container: Option<Value>,
1135) -> bool {
1136    let Some(container) = container else { return false };
1137    let Some(value) = func[load].results().next() else { return false };
1138    if !func[value].ty.is_ptr() {
1139        return false;
1140    }
1141
1142    let span = func.span(load);
1143    let args = func.push_values(&[container, pointer, value]);
1144    let data = InstData { args, ..InstData::new(Opcode::CapLoad) };
1145    let made = func.create_inst(data, &[Type::CAP], span);
1146    func.insert_after(made, load);
1147    let Some(held) = func[made].results().next() else { return false };
1148    origins.seed(value, held);
1149    true
1150}
1151
1152/// Whether what an access carries is a pointer, which is the one thing the epoch plane watches.
1153///
1154/// A `store` carries it as its first operand, the value being written coming before the place it
1155/// goes. A `load` carries it as its result. The plane's granule is eight bytes because a pointer is
1156/// eight bytes, so a granule two threads both reach is a granule holding no pointer, and watching
1157/// anything wider than this would put two threads writing neighbouring members of one structure
1158/// into the plane as each other's strangers.
1159fn pointer_valued(func: &Func, access: Inst) -> bool {
1160    let carried = match func[access].opcode {
1161        Opcode::Store => func[func[access].args].first().map(|&value| func[value].ty),
1162        Opcode::Load => func[access].results().next().map(|value| func[value].ty),
1163        _ => None,
1164    };
1165    carried.is_some_and(Type::is_ptr)
1166}
1167
1168/// Puts a `check_race` immediately before one access, asking whether another thread reached these
1169/// bytes with nothing ordering that against this thread.
1170///
1171/// Judgement J9, and the reading half of section 9.5. The plane holds one stamp per granule saying
1172/// which thread last stored a pointer there and how far that thread had counted, and a stamp this
1173/// thread has not got past is a write no synchronization edge puts before this access. Which class
1174/// that is depends on which side asked: a store finding one is C3, two threads writing the same
1175/// slot with nothing between them, and a load finding one is C2, the pointer word race. One check
1176/// covers both because the comparison is the same from either side.
1177///
1178/// In front of the access, and at a store that puts it in front of the `meta_epoch` that goes
1179/// after. That order is the whole of what makes the question answerable: [`stamped`] overwrites the
1180/// stamp this reads, so a check on the other side of the store would be asking about the write it
1181/// was called for.
1182///
1183/// Only an access that carries a pointer, which [`pointer_valued`] argues. So this is not one check
1184/// per access the way the bounds check is, and on ordinary code it is a small fraction of them.
1185fn raced(
1186    func: &mut Func,
1187    access: Inst,
1188    pointer: Value,
1189    capability: Option<Value>,
1190    width: u64,
1191) -> bool {
1192    let Some(capability) = capability else { return false };
1193    if !pointer_valued(func, access) {
1194        return false;
1195    }
1196    let Extra::Mem(at) = func[access].extra else { return false };
1197    let mut info = func[at];
1198    info.size = covered(func, access, info.size, width);
1199    // An access whose width nothing states touches no bytes anybody can name, as in [`filled`].
1200    if info.size == 0 {
1201        return false;
1202    }
1203    // Neither field means anything to this question. The plane holds stamps rather than types, and
1204    // the alignment conjunct of J1 is the bounds check's to make.
1205    info.tbaa = None;
1206    info.align = 1;
1207    info.owns = 0;
1208
1209    let span = func.span(access);
1210    let args = func.push_values(&[capability, pointer]);
1211    let extra = Extra::Mem(func.add_mem(info));
1212    let data = InstData { args, extra, ..InstData::new(Opcode::CheckRace) };
1213    let asked = func.create_inst(data, &[], span);
1214    func.insert_before(asked, access);
1215    true
1216}
1217
1218/// Puts a `meta_release` in front of an atomic, a `meta_acquire` after it, or both, or neither.
1219///
1220/// The synchronization edges of `spec/safe-memory/09-type-init-and-races.md` section 9.5, for the
1221/// one kind of edge that cannot be interposed. Every other edge the monitor knows about is a
1222/// `pthread` call and `rucc_safe_rt::sync` wraps it. A C11 release store is a machine instruction,
1223/// so there is no call to wrap and the compiler is the only thing in the build that can say an
1224/// ordering happened here.
1225///
1226/// This matters more than a missing recording does, and in the opposite direction. Everywhere else
1227/// in this pass, instrumentation nobody wrote costs recall: a store that was not stamped is a race
1228/// that is not found. Here it costs precision. The epoch plane is a counter per thread and nothing
1229/// else, so two threads that really were ordered, by an edge this pass did not emit, are two
1230/// threads whose clocks say they are concurrent, and the check reports a race in a program that
1231/// has none. That is why these go in before the check is ever on by default.
1232///
1233/// Which side the marker lands on is which side the ordering is on. A release publishes everything
1234/// the thread has already done, so the clock has to be written down while that is still true, which
1235/// is in front of the atomic. An acquire takes an ordering from whatever the atomic just read, so
1236/// it is not there to be taken until the atomic has run, which puts it after. An `acq_rel` or a
1237/// `seq_cst` read-modify-write is both, and gets one of each.
1238///
1239/// No thinning by what the atomic carries, unlike [`stamped`] and [`raced`]. A release on an atomic
1240/// `int` is the ordinary publication pattern, the flag being set is not the pointer, and refusing
1241/// to record that edge because no pointer went through it would lose exactly the ordering the
1242/// pointers stored before it depend on.
1243///
1244/// A `fence` goes through [`fenced`] instead, because it has no address in it to be the key.
1245fn edges(func: &mut Func, atomic: Inst) -> usize {
1246    let Some(pointer) = keyed(func, atomic) else { return 0 };
1247    let order = match func[atomic].extra {
1248        Extra::Mem(at) => func[at].order,
1249        Extra::Rmw(_, at) => func[at].order,
1250        _ => return 0,
1251    };
1252
1253    let span = func.span(atomic);
1254    let mut put = 0;
1255    if order.is_release() {
1256        let args = func.push_values(&[pointer]);
1257        let data = InstData { args, ..InstData::new(Opcode::MetaRelease) };
1258        let made = func.create_inst(data, &[], span);
1259        func.insert_before(made, atomic);
1260        put += 1;
1261    }
1262    if order.is_acquire() {
1263        let args = func.push_values(&[pointer]);
1264        let data = InstData { args, ..InstData::new(Opcode::MetaAcquire) };
1265        let made = func.create_inst(data, &[], span);
1266        func.insert_after(made, atomic);
1267        put += 1;
1268    }
1269    put
1270}
1271
1272/// Puts a `meta_fence_release` in front of a fence, a `meta_fence_acquire` after it, or both.
1273///
1274/// The other way a C program orders two threads without calling anything, and the harder half of
1275/// [`edges`]. A fence orders against every other thread rather than against one object, so there is
1276/// no address in it to file the edge under and the markers it gets take no operands. The relaxed
1277/// atomic that usually sits next to a fence in the source is not the key either: what the fence
1278/// orders is everything the thread did, not that one word, and keying on the word would miss every
1279/// other pair the fence really ordered.
1280///
1281/// So the runtime keeps one cell for all of them, and that orders more pairs of threads than the
1282/// program did. `rucc_safe_rt::sync` carries the argument for why that is the safe direction, which
1283/// is the same argument the stale entries there already rest on: a thread put further ahead than it
1284/// needed to be reports fewer races, never a race that is not there. Given that a missing edge is
1285/// the one kind of missing instrumentation that costs precision, too much ordering beats none.
1286///
1287/// Which side the marker lands on is the same question as in [`edges`] and has the same answer.
1288fn fenced(func: &mut Func, fence: Inst) -> usize {
1289    let Extra::Order(order) = func[fence].extra else { return 0 };
1290
1291    let span = func.span(fence);
1292    let mut put = 0;
1293    if order.is_release() {
1294        let made = func.create_inst(InstData::new(Opcode::MetaFenceRelease), &[], span);
1295        func.insert_before(made, fence);
1296        put += 1;
1297    }
1298    if order.is_acquire() {
1299        let made = func.create_inst(InstData::new(Opcode::MetaFenceAcquire), &[], span);
1300        func.insert_after(made, fence);
1301        put += 1;
1302    }
1303    put
1304}
1305
1306/// The address an atomic operates on, which is the key its edge is filed under.
1307///
1308/// The same shape as [`pointer_of`] and a different set of opcodes: an `atomic_store` writes
1309/// through its second operand for the reason an ordinary store does, and the other three take the
1310/// object first because nothing comes before it.
1311fn keyed(func: &Func, atomic: Inst) -> Option<Value> {
1312    let args = &func[func[atomic].args];
1313    let at = match func[atomic].opcode {
1314        Opcode::AtomicStore => 1,
1315        Opcode::AtomicLoad | Opcode::AtomicRmw | Opcode::Cmpxchg => 0,
1316        _ => return None,
1317    };
1318    let &value = args.get(at)?;
1319    func[value].ty.is_ptr().then_some(value)
1320}
1321
1322/// Puts `check_deriv` immediately after one `ptr_add`, over the capability of what it walked off.
1323///
1324/// Judgement J2, which is the one that catches a pointer walking off its object *before* anything
1325/// is read through it. C says computing such a pointer is already undefined, and catching it here
1326/// rather than at the eventual access is what lets the report name the loop that ran too far
1327/// instead of whatever unrelated line finally dereferenced the result.
1328///
1329/// The check is handed the pointer the derivation produced, so it goes immediately after the
1330/// derivation rather than in front of it like the access checks. That is what section 6.2.2's
1331/// third operand means: the judgement is about where the derived pointer landed, and there is
1332/// nothing to decide before it has landed.
1333///
1334/// The fourth operand is the stride, which is how wide one element of whatever is being stepped
1335/// over is. Document 03 section 3.1 widened S5's window to `[lo - stride, hi]`, so the runtime
1336/// cannot decide the low end without it, and it is a value rather than a constant because a walk
1337/// over a variable length array steps by a width the program computes.
1338fn derivation(func: &mut Func, origins: &mut origin::Origins, add: Inst) -> bool {
1339    let Some(&base) = func[func[add].args].first() else { return false };
1340    if !func[base].ty.is_ptr() {
1341        return false;
1342    }
1343    let Some(derived) = func[add].results().next() else { return false };
1344
1345    let span = func.span(add);
1346    let width = stride(func, add);
1347    let capability = origins.of(func, base, add);
1348    let args = func.push_values(&[capability, base, derived, width]);
1349    let check = func.create_inst(InstData { args, ..InstData::new(Opcode::CheckDeriv) }, &[], span);
1350    func.insert_after(check, add);
1351    true
1352}
1353
1354/// How wide one element of the thing a `ptr_add` steps over is.
1355///
1356/// C computes a byte offset before the pointer arithmetic happens, so `ptr_add` takes bytes and the
1357/// element width is not in it. What is in it is the shape the frontend left behind, because this
1358/// pass runs before the optimizer and the offset operand is still exactly what lowering emitted:
1359/// `mul index, k` for a constant width, `mul index, w` for one the program computes, either of them
1360/// under a `sub 0, ...` for a walk that goes backwards, and the bare index when the width is one.
1361///
1362/// So the width is read back off that shape. Getting it wrong is not a soundness question: the
1363/// stride only decides how far below an object a derivation may land before it is refused, and an
1364/// access below the object is refused by judgement J1 either way. A shape nobody recognises answers
1365/// one byte, which is the strict reading of C and is where this check was before the window moved.
1366fn stride(func: &mut Func, add: Inst) -> Value {
1367    // The offset is the one operand of a `ptr_add` that is an integer, so its type is the width an
1368    // address is computed in and is the type the check's fourth operand has to have.
1369    let Some(&offset) = func[func[add].args].get(1) else { return one(func, add, Type::int(64)) };
1370    let word = func[offset].ty;
1371    // A walk that goes backwards negates the offset rather than the width, so the shape underneath
1372    // is the same one a forward walk has.
1373    let forwards = match operand_of(func, offset, Opcode::Sub, 0) {
1374        Some(zero) if is_zero(func, zero) => operand_of(func, offset, Opcode::Sub, 1),
1375        _ => None,
1376    };
1377    let scaled = forwards.unwrap_or(offset);
1378    match operand_of(func, scaled, Opcode::Mul, 1) {
1379        // The width is the right operand because `step` builds the multiply that way round, with
1380        // the index on the left and the size of one element on the right.
1381        Some(width) if func[width].ty == word => width,
1382        _ => one(func, add, word),
1383    }
1384}
1385
1386/// Operand `index` of the instruction that produced `value`, when that instruction is `opcode`.
1387fn operand_of(func: &Func, value: Value, opcode: Opcode, index: usize) -> Option<Value> {
1388    let Def::Result { inst, .. } = func[value].def else { return None };
1389    if func[inst].opcode != opcode {
1390        return None;
1391    }
1392    func[func[inst].args].get(index).copied()
1393}
1394
1395/// Whether a value is a constant zero, which is the left half of how a backwards walk is spelled.
1396fn is_zero(func: &Func, value: Value) -> bool {
1397    let Def::Result { inst, .. } = func[value].def else { return false };
1398    match func[inst].extra {
1399        Extra::Imm(imm) if func[inst].opcode == Opcode::IConst => func[imm].bits() == 0,
1400        _ => false,
1401    }
1402}
1403
1404/// A stride of one byte, which is what a shape this pass does not recognise answers.
1405fn one(func: &mut Func, at: Inst, ty: Type) -> Value {
1406    let span = func.span(at);
1407    let extra = Extra::Imm(func.add_imm(Imm::int(1, ty)));
1408    let made = func.create_inst(InstData { extra, ..InstData::new(Opcode::IConst) }, &[ty], span);
1409    func.insert_before(made, at);
1410    func[made].results().next().expect("a constant created with one result has one")
1411}
1412
1413#[cfg(test)]
1414mod tests {
1415    use rucc_base::Interner;
1416    use rucc_ir::{
1417        Builder, Flags, MemInfo, MemOrder, Meta, MetaNode, PlaneNode, Restrict, RmwOp, Signature,
1418        TbaaNode, print_func, verify_func,
1419    };
1420    use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
1421
1422    use super::*;
1423
1424    fn target() -> TargetInfo {
1425        TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
1426    }
1427
1428    /// A module to record into, and the plane entries it holds.
1429    ///
1430    /// The plane is the module's, so a test that instruments a bare function still has to have one
1431    /// to hand. It is empty of types here, since the functions these tests build name none.
1432    fn planed(names: &mut Interner, unit: &str) -> (Module, Plane) {
1433        let mut module = Module::new(names.intern(unit), &target());
1434        let plane = Plane::build(&mut module);
1435        (module, plane)
1436    }
1437
1438    /// A function that loads through its parameter and stores what it read back.
1439    fn one_of_each(names: &mut Interner) -> Func {
1440        let i32_ = Type::int(32);
1441        let mut func = Func::new(
1442            names.intern("both"),
1443            Signature::new().with_params(&[Type::PTR]).with_returns(&[i32_]),
1444        );
1445        let entry = func.create_block();
1446        let p = func.append_param(entry, Type::PTR);
1447
1448        let info = MemInfo {
1449            size: 4,
1450            align: 4,
1451            order: MemOrder::NotAtomic,
1452            tbaa: None,
1453            owns: 0,
1454            restrict: Restrict::NONE,
1455        };
1456        let mut b = Builder::new(&mut func, entry);
1457        let args = b.func().push_values(&[p]);
1458        let extra = Extra::Mem(b.func().add_mem(info));
1459        let loaded = b.value(InstData { args, extra, ..InstData::new(Opcode::Load) }, i32_);
1460        let args = b.func().push_values(&[loaded, p]);
1461        let extra = Extra::Mem(b.func().add_mem(info));
1462        b.inst(InstData { args, extra, ..InstData::new(Opcode::Store) }, &[]);
1463        b.ret(&[loaded]);
1464        func
1465    }
1466
1467    /// The same shape, with the two accesses said to go through two `restrict` pointers of a block.
1468    fn promising(names: &mut Interner) -> Func {
1469        let i32_ = Type::int(32);
1470        let mut func = Func::new(
1471            names.intern("kernel"),
1472            Signature::new().with_params(&[Type::PTR, Type::PTR]),
1473        );
1474        let entry = func.create_block();
1475        let to = func.append_param(entry, Type::PTR);
1476        let from = func.append_param(entry, Type::PTR);
1477
1478        let info = MemInfo {
1479            size: 4,
1480            align: 4,
1481            order: MemOrder::NotAtomic,
1482            tbaa: None,
1483            owns: 0,
1484            restrict: Restrict { clique: 1, base: 1 },
1485        };
1486        let mut b = Builder::new(&mut func, entry);
1487        let read = MemInfo { restrict: Restrict { clique: 1, base: 2 }, ..info };
1488        let loaded = b.load(i32_, from, read, Flags::default());
1489        b.store(loaded, to, info, Flags::default());
1490        b.ret(&[]);
1491        func
1492    }
1493
1494    #[test]
1495    fn the_restrict_checks_wait_until_the_build_asks_for_them() {
1496        // The one check in this crate that is off by default. What it costs is paid by the blocks
1497        // that declare `restrict` pointers and nobody else, and what it reports includes programs
1498        // the standard permits, so which it is is the build's decision. `rucc_session::Promise` is
1499        // where that is argued.
1500        let mut names = Interner::new();
1501        let (_, plane) = planed(&mut names, "kernel.c");
1502
1503        let mut quiet = promising(&mut names);
1504        let counts = insert(&mut quiet, &plane, 8, Subobject::Off, Promise::Off, Races::Off);
1505        assert_eq!((counts.promised, counts.scoped), (0, 0));
1506
1507        let mut asked = promising(&mut names);
1508        let counts = insert(&mut asked, &plane, 8, Subobject::Off, Promise::Blocks, Races::Off);
1509        assert_eq!((counts.promised, counts.scoped), (2, 1));
1510    }
1511
1512    #[test]
1513    fn every_access_gets_a_bounds_check_and_a_lifetime_check() {
1514        let mut names = Interner::new();
1515        let mut func = one_of_each(&mut names);
1516        let (module, plane) = planed(&mut names, "both.c");
1517        assert_eq!(
1518            insert(&mut func, &plane, 8, Subobject::Off, Promise::Off, Races::Off),
1519            Counts { checked: 2, live: 2, judged: 1, wrote: 1, filled: 1, ..Counts::default() }
1520        );
1521
1522        assert_eq!(
1523            print_func(&module, &func, &names),
1524            // The plane writes are after the store and not in front of it. The bytes were stored
1525            // through that type, and were stored at all, once the store has happened, and the
1526            // check in front of it may yet refuse the store both of them are about.
1527            //
1528            // One `cap_of` for two accesses, because both go through the same pointer and a
1529            // capability is about the pointer. `origin` is where that is argued.
1530            "func @both(ptr) -> i32, linkage(external) {\n\
1531             block0(%0: ptr):\n    \
1532             %1 = cap_of %0\n    \
1533             check_bounds %1, %0, size 4, align 4\n    \
1534             check_live %1, %0\n    \
1535             check_init %1, %0, size 4, align 4\n    \
1536             %2 = load.i32 %0, size 4, align 4\n    \
1537             check_bounds %1, %0, size 4, align 4\n    \
1538             check_live %1, %0\n    \
1539             store %2 -> %0, size 4, align 4\n    \
1540             %3 = iconst.i64 4\n    \
1541             meta_type %0, %3, tbaa !1\n    \
1542             %4 = iconst.i64 4\n    \
1543             meta_init %0, %4\n    \
1544             return %2\n\
1545             }\n"
1546        );
1547    }
1548
1549    /// A function that loads a pointer through its parameter.
1550    ///
1551    /// The payload states no size, which is what the front end emits: a load takes its width from
1552    /// the type it produces, and for a pointer that is the one type with no width of its own.
1553    fn one_pointer_read(names: &mut Interner) -> Func {
1554        let mut func = Func::new(
1555            names.intern("deref"),
1556            Signature::new().with_params(&[Type::PTR]).with_returns(&[Type::PTR]),
1557        );
1558        let entry = func.create_block();
1559        let p = func.append_param(entry, Type::PTR);
1560
1561        let info = MemInfo {
1562            size: 0,
1563            align: 8,
1564            order: MemOrder::NotAtomic,
1565            tbaa: None,
1566            owns: 0,
1567            restrict: Restrict::NONE,
1568        };
1569        let mut b = Builder::new(&mut func, entry);
1570        let args = b.func().push_values(&[p]);
1571        let extra = Extra::Mem(b.func().add_mem(info));
1572        let loaded = b.value(InstData { args, extra, ..InstData::new(Opcode::Load) }, Type::PTR);
1573        b.ret(&[loaded]);
1574        func
1575    }
1576
1577    /// A function that writes one of its pointer parameters through the other.
1578    fn one_pointer_write(names: &mut Interner) -> Func {
1579        let mut func =
1580            Func::new(names.intern("keep"), Signature::new().with_params(&[Type::PTR, Type::PTR]));
1581        let entry = func.create_block();
1582        let at = func.append_param(entry, Type::PTR);
1583        let value = func.append_param(entry, Type::PTR);
1584
1585        let info = MemInfo {
1586            size: 0,
1587            align: 8,
1588            order: MemOrder::NotAtomic,
1589            tbaa: None,
1590            owns: 0,
1591            restrict: Restrict::NONE,
1592        };
1593        let mut b = Builder::new(&mut func, entry);
1594        // The value first and the place second, which is the order the opcode is written in.
1595        let args = b.func().push_values(&[value, at]);
1596        let extra = Extra::Mem(b.func().add_mem(info));
1597        b.inst(InstData { args, extra, ..InstData::new(Opcode::Store) }, &[]);
1598        b.ret(&[]);
1599        func
1600    }
1601
1602    /// The same function writing a number, which is the one thing that differs from a pointer.
1603    fn one_number_write(names: &mut Interner) -> Func {
1604        let i32_ = Type::int(32);
1605        let mut func =
1606            Func::new(names.intern("set"), Signature::new().with_params(&[Type::PTR, i32_]));
1607        let entry = func.create_block();
1608        let at = func.append_param(entry, Type::PTR);
1609        let value = func.append_param(entry, i32_);
1610
1611        let info = MemInfo {
1612            size: 4,
1613            align: 4,
1614            order: MemOrder::NotAtomic,
1615            tbaa: None,
1616            owns: 0,
1617            restrict: Restrict::NONE,
1618        };
1619        let mut b = Builder::new(&mut func, entry);
1620        let args = b.func().push_values(&[value, at]);
1621        let extra = Extra::Mem(b.func().add_mem(info));
1622        b.inst(InstData { args, extra, ..InstData::new(Opcode::Store) }, &[]);
1623        b.ret(&[]);
1624        func
1625    }
1626
1627    /// The same function reading a number, so the two answers differ in one thing.
1628    fn one_number_read(names: &mut Interner) -> Func {
1629        let i32_ = Type::int(32);
1630        let mut func = Func::new(
1631            names.intern("count"),
1632            Signature::new().with_params(&[Type::PTR]).with_returns(&[i32_]),
1633        );
1634        let entry = func.create_block();
1635        let p = func.append_param(entry, Type::PTR);
1636
1637        let info = MemInfo {
1638            size: 0,
1639            align: 4,
1640            order: MemOrder::NotAtomic,
1641            tbaa: None,
1642            owns: 0,
1643            restrict: Restrict::NONE,
1644        };
1645        let mut b = Builder::new(&mut func, entry);
1646        let args = b.func().push_values(&[p]);
1647        let extra = Extra::Mem(b.func().add_mem(info));
1648        let loaded = b.value(InstData { args, extra, ..InstData::new(Opcode::Load) }, i32_);
1649        b.ret(&[loaded]);
1650        func
1651    }
1652
1653    #[test]
1654    fn an_access_that_reads_a_pointer_is_checked_over_the_targets_pointer_width() {
1655        // A pointer is the one type in the IR with no width of its own, so the width has to come
1656        // from the target. Answering zero is what left a bounds check over a pointer deciding
1657        // about a single byte and left the init question out of it altogether, which was #953.
1658        let mut names = Interner::new();
1659        let mut func = one_pointer_read(&mut names);
1660        let (module, plane) = planed(&mut names, "deref.c");
1661        assert_eq!(
1662            insert(&mut func, &plane, 8, Subobject::Off, Promise::Off, Races::Off),
1663            Counts { checked: 1, live: 1, filled: 1, recalled: 1, ..Counts::default() }
1664        );
1665
1666        let printed = print_func(&module, &func, &names);
1667        assert!(printed.contains("check_bounds %1, %0, size 8, align 8\n"), "{printed}");
1668        assert!(printed.contains("check_init %1, %0, size 8, align 8\n"), "{printed}");
1669    }
1670
1671    #[test]
1672    fn a_store_of_a_pointer_writes_its_capability_into_the_slot_beside_it() {
1673        // The other end of the aux pair. Four operands, and the order is the call's: the capability
1674        // of the object the word is in, the address of the word, the pointer written there, and
1675        // that pointer's own capability, which is the thing being written down. It comes last of
1676        // what goes in behind the store because it went in first, which is what puts it furthest.
1677        let mut names = Interner::new();
1678        let mut func = one_pointer_write(&mut names);
1679        let (module, plane) = planed(&mut names, "keep.c");
1680        let counts = insert(&mut func, &plane, 8, Subobject::Off, Promise::Off, Races::Off);
1681        assert_eq!(counts.saved, 1);
1682        assert_eq!(counts.recalled, 0);
1683
1684        let printed = print_func(&module, &func, &names);
1685        assert!(printed.contains("cap_store %3, %0, %1, %2\n    return\n"), "{printed}");
1686    }
1687
1688    #[test]
1689    fn a_store_of_something_that_is_not_a_pointer_writes_no_capability() {
1690        // There is nothing to write down and no slot for it. The aux is beside a pointer sized word
1691        // holding a pointer, and a number stored there is what makes the slot say so instead.
1692        let mut names = Interner::new();
1693        let mut func = one_number_write(&mut names);
1694        let (_module, plane) = planed(&mut names, "keep.c");
1695        let counts = insert(&mut func, &plane, 8, Subobject::Off, Promise::Off, Races::Off);
1696        assert_eq!(counts.saved, 0);
1697    }
1698
1699    #[test]
1700    fn a_pointer_width_of_four_is_what_a_thirty_two_bit_target_gets() {
1701        // The number is the target's and not this crate's, so a build for a target where a pointer
1702        // is four bytes asks about four.
1703        let mut names = Interner::new();
1704        let mut func = one_pointer_read(&mut names);
1705        let (module, plane) = planed(&mut names, "deref.c");
1706        insert(&mut func, &plane, 4, Subobject::Off, Promise::Off, Races::Off);
1707
1708        let printed = print_func(&module, &func, &names);
1709        assert!(printed.contains("check_bounds %1, %0, size 4, align 8\n"), "{printed}");
1710    }
1711
1712    /// A module with one aliasing node under the root, and the plane built over it.
1713    ///
1714    /// Two nodes rather than one, because the root is the character type and a type of its own has
1715    /// to hang under something. What comes back is the module, the plane, and the node for `int`.
1716    fn typed(names: &mut Interner, unit: &str) -> (Module, Plane, Meta) {
1717        let mut module = Module::new(names.intern(unit), &target());
1718        let root = names.intern("char");
1719        let root =
1720            module.add_meta(MetaNode::Tbaa(TbaaNode { name: root, parent: None, offset: 0 }));
1721        let int = names.intern("int");
1722        let int =
1723            module.add_meta(MetaNode::Tbaa(TbaaNode { name: int, parent: Some(root), offset: 0 }));
1724        let plane = Plane::build(&mut module);
1725        (module, plane, int)
1726    }
1727
1728    /// A function that reads through its parameter as an `int`, naming that type on the access.
1729    fn reading(names: &mut Interner, node: Option<Meta>) -> Func {
1730        let i32_ = Type::int(32);
1731        let mut func = Func::new(
1732            names.intern("read"),
1733            Signature::new().with_params(&[Type::PTR]).with_returns(&[i32_]),
1734        );
1735        let entry = func.create_block();
1736        let p = func.append_param(entry, Type::PTR);
1737        let info = MemInfo {
1738            size: 0,
1739            align: 4,
1740            order: MemOrder::NotAtomic,
1741            tbaa: node,
1742            owns: 0,
1743            restrict: Restrict::NONE,
1744        };
1745        let mut b = Builder::new(&mut func, entry);
1746        let args = b.func().push_values(&[p]);
1747        let extra = Extra::Mem(b.func().add_mem(info));
1748        let loaded = b.value(InstData { args, extra, ..InstData::new(Opcode::Load) }, i32_);
1749        b.ret(&[loaded]);
1750        func
1751    }
1752
1753    #[test]
1754    fn a_read_asks_the_plane_whether_the_bytes_agree_with_the_type_it_reads_them_as() {
1755        // Judgement J3, which is what the two plane writes were recorded for. The question is put
1756        // in the plane's vocabulary rather than the aliasing tree's, so what the check carries is
1757        // the entry for `int` and not the node for it.
1758        let mut names = Interner::new();
1759        let (module, plane, int) = typed(&mut names, "read.c");
1760        let mut func = reading(&mut names, Some(int));
1761
1762        assert_eq!(insert(&mut func, &plane, 8, Subobject::Off, Promise::Off, Races::Off).asked, 1);
1763
1764        let printed = print_func(&module, &func, &names);
1765        let entry = plane.entry(Some(int));
1766        assert_eq!(module[entry], MetaNode::Plane(PlaneNode::Type(int)));
1767        // Four bytes, which the payload does not say and the type of the value read does, and the
1768        // check is in front of the read rather than after it.
1769        let wanted = format!("check_type %1, %0, size 4, align 4, tbaa !{}\n", entry.index());
1770        assert!(printed.contains(&wanted), "{printed}");
1771        let asked = printed.find(&wanted).expect("the check is there");
1772        let read = printed.find("load.i32").expect("and so is the read");
1773        assert!(asked < read, "{printed}");
1774
1775        if let Err(errors) = verify_func(&module, &func, &names) {
1776            panic!("that was expected to be believed: {errors:#?}");
1777        }
1778    }
1779
1780    #[test]
1781    fn a_read_the_front_end_named_no_type_for_asks_nothing() {
1782        // An aggregate, an array, or anything else reached by address. The plane's untyped entry
1783        // means bytes nothing has stored through, which is a different statement from the front
1784        // end not having said what the access is through, and asking with it would refuse every
1785        // read of a structure whose members were stored through their own types.
1786        let mut names = Interner::new();
1787        let (module, plane, _) = typed(&mut names, "copy.c");
1788        let mut func = reading(&mut names, None);
1789
1790        assert_eq!(insert(&mut func, &plane, 8, Subobject::Off, Promise::Off, Races::Off).asked, 0);
1791        let printed = print_func(&module, &func, &names);
1792        assert!(!printed.contains("check_type"), "{printed}");
1793    }
1794
1795    /// A function that writes through its parameter as an `int`, naming that type on the access.
1796    fn writing(names: &mut Interner, node: Option<Meta>) -> Func {
1797        let i32_ = Type::int(32);
1798        let mut func =
1799            Func::new(names.intern("write"), Signature::new().with_params(&[Type::PTR, i32_]));
1800        let entry = func.create_block();
1801        let p = func.append_param(entry, Type::PTR);
1802        let v = func.append_param(entry, i32_);
1803        let info = MemInfo {
1804            size: 0,
1805            align: 4,
1806            order: MemOrder::NotAtomic,
1807            tbaa: node,
1808            owns: 0,
1809            restrict: Restrict::NONE,
1810        };
1811        let mut b = Builder::new(&mut func, entry);
1812        let args = b.func().push_values(&[v, p]);
1813        let extra = Extra::Mem(b.func().add_mem(info));
1814        b.inst(InstData { args, extra, ..InstData::new(Opcode::Store) }, &[]);
1815        b.ret(&[]);
1816        func
1817    }
1818
1819    #[test]
1820    fn a_store_asks_the_plane_too_once_the_build_has_said_the_member_matters() {
1821        // Row S4, which is a write that leaves one member and lands in the next. Read literally a
1822        // store like that is a program retyping storage it owns, which C 6.5 permits, so the
1823        // question is only put when somebody asked for it to be put.
1824        let mut names = Interner::new();
1825        let (module, plane, int) = typed(&mut names, "member.c");
1826        let mut func = writing(&mut names, Some(int));
1827
1828        let counts = insert(&mut func, &plane, 8, Subobject::Members, Promise::Off, Races::Off);
1829        assert_eq!((counts.asked, counts.judged), (1, 1));
1830
1831        let printed = print_func(&module, &func, &names);
1832        let entry = plane.entry(Some(int));
1833        let wanted = format!("check_type %2, %0, size 4, align 4, tbaa !{}\n", entry.index());
1834        assert!(printed.contains(&wanted), "{printed}");
1835        // In front of the store, because the bytes say what they said before it runs, and the
1836        // recording this pass makes afterwards is what would make the answer yes.
1837        let asked = printed.find(&wanted).expect("the check is there");
1838        let wrote = printed.find("store %1").expect("and so is the store");
1839        let recorded = printed.find("meta_type").expect("and so is the recording");
1840        assert!(asked < wrote && wrote < recorded, "{printed}");
1841
1842        if let Err(errors) = verify_func(&module, &func, &names) {
1843            panic!("that was expected to be believed: {errors:#?}");
1844        }
1845    }
1846
1847    #[test]
1848    fn a_store_the_front_end_named_no_type_for_asks_nothing_whatever_the_build_asked() {
1849        // The same reason a read of one does not. An access with no aliasing node is one the front
1850        // end did not say the type of, which is not the same as bytes nothing has been stored
1851        // through, and the plane has no way to tell the question apart from the answer.
1852        let mut names = Interner::new();
1853        let (module, plane, _) = typed(&mut names, "aggregate.c");
1854        let mut func = writing(&mut names, None);
1855
1856        assert_eq!(
1857            insert(&mut func, &plane, 8, Subobject::Members, Promise::Off, Races::Off).asked,
1858            0
1859        );
1860        let printed = print_func(&module, &func, &names);
1861        assert!(!printed.contains("check_type"), "{printed}");
1862    }
1863
1864    #[test]
1865    fn a_store_answers_the_question_rather_than_asking_it() {
1866        // The plane covers storage the allocator reported, which is the storage C gives no declared
1867        // type, and the effective type of one of those is whatever the last store set. So a store
1868        // cannot disagree with the plane unless the build asked it to, and a check in front of one
1869        // by default would refuse the reuse of a buffer that the standard permits.
1870        let mut names = Interner::new();
1871        let (module, plane, int) = typed(&mut names, "write.c");
1872        let i32_ = Type::int(32);
1873        let mut func =
1874            Func::new(names.intern("write"), Signature::new().with_params(&[Type::PTR, i32_]));
1875        let entry = func.create_block();
1876        let p = func.append_param(entry, Type::PTR);
1877        let v = func.append_param(entry, i32_);
1878        let info = MemInfo {
1879            size: 0,
1880            align: 4,
1881            order: MemOrder::NotAtomic,
1882            tbaa: Some(int),
1883            owns: 0,
1884            restrict: Restrict::NONE,
1885        };
1886        let mut b = Builder::new(&mut func, entry);
1887        let args = b.func().push_values(&[v, p]);
1888        let extra = Extra::Mem(b.func().add_mem(info));
1889        b.inst(InstData { args, extra, ..InstData::new(Opcode::Store) }, &[]);
1890        b.ret(&[]);
1891
1892        let counts = insert(&mut func, &plane, 8, Subobject::Off, Promise::Off, Races::Off);
1893        assert_eq!((counts.judged, counts.asked), (1, 0));
1894        let printed = print_func(&module, &func, &names);
1895        assert!(!printed.contains("check_type"), "{printed}");
1896    }
1897
1898    #[test]
1899    fn a_store_records_the_type_it_stored_through() {
1900        // The judgement of C 6.5, which is the half of the type plane the compiler makes rather
1901        // than asks. The access names a type, so the entry the store records is that type rather
1902        // than the distinguished value a store that names nothing records.
1903        let mut names = Interner::new();
1904        let (module, plane, int) = typed(&mut names, "typed.c");
1905
1906        let i32_ = Type::int(32);
1907        let mut func =
1908            Func::new(names.intern("record"), Signature::new().with_params(&[Type::PTR, i32_]));
1909        let entry = func.create_block();
1910        let p = func.append_param(entry, Type::PTR);
1911        let v = func.append_param(entry, i32_);
1912        let info = MemInfo {
1913            size: 0,
1914            align: 4,
1915            order: MemOrder::NotAtomic,
1916            tbaa: Some(int),
1917            owns: 0,
1918            restrict: Restrict::NONE,
1919        };
1920        let mut b = Builder::new(&mut func, entry);
1921        let args = b.func().push_values(&[v, p]);
1922        let extra = Extra::Mem(b.func().add_mem(info));
1923        b.inst(InstData { args, extra, ..InstData::new(Opcode::Store) }, &[]);
1924        b.ret(&[]);
1925
1926        assert_eq!(
1927            insert(&mut func, &plane, 8, Subobject::Off, Promise::Off, Races::Off).judged,
1928            1
1929        );
1930
1931        let printed = print_func(&module, &func, &names);
1932        // Four bytes, which the payload does not say and the type of the value stored does.
1933        assert!(printed.contains("%3 = iconst.i64 4\n"), "{printed}");
1934        // The entry for `int`, which is the node the plane made for the node the access named.
1935        let entry = plane.entry(Some(int));
1936        assert_eq!(module[entry], MetaNode::Plane(PlaneNode::Type(int)));
1937        let wanted = format!("meta_type %0, %3, tbaa !{}\n", entry.index());
1938        assert!(printed.contains(&wanted), "{printed}");
1939
1940        if let Err(errors) = verify_func(&module, &func, &names) {
1941            panic!("that was expected to be believed: {errors:#?}");
1942        }
1943    }
1944
1945    #[test]
1946    fn a_store_records_that_the_bytes_it_wrote_hold_something() {
1947        // The init plane's half of the same store. One bit per byte and nothing else, so the write
1948        // carries a range and no type, and the range is the width of the value stored rather than
1949        // anything the payload says. A store of eight bytes makes eight bytes readable however it
1950        // came to be written.
1951        let mut names = Interner::new();
1952        let (module, plane) = planed(&mut names, "wrote.c");
1953
1954        let i64_ = Type::int(64);
1955        let mut func =
1956            Func::new(names.intern("write"), Signature::new().with_params(&[Type::PTR, i64_]));
1957        let entry = func.create_block();
1958        let p = func.append_param(entry, Type::PTR);
1959        let v = func.append_param(entry, i64_);
1960        let info = MemInfo {
1961            size: 0,
1962            align: 8,
1963            order: MemOrder::NotAtomic,
1964            tbaa: None,
1965            owns: 0,
1966            restrict: Restrict::NONE,
1967        };
1968        let mut b = Builder::new(&mut func, entry);
1969        let args = b.func().push_values(&[v, p]);
1970        let extra = Extra::Mem(b.func().add_mem(info));
1971        b.inst(InstData { args, extra, ..InstData::new(Opcode::Store) }, &[]);
1972        b.ret(&[]);
1973
1974        assert_eq!(insert(&mut func, &plane, 8, Subobject::Off, Promise::Off, Races::Off).wrote, 1);
1975
1976        let printed = print_func(&module, &func, &names);
1977        assert!(printed.contains("%4 = iconst.i64 8\n    meta_init %0, %4\n"), "{printed}");
1978
1979        if let Err(errors) = verify_func(&module, &func, &names) {
1980            panic!("that was expected to be believed: {errors:#?}");
1981        }
1982    }
1983
1984    #[test]
1985    fn a_store_of_a_pointer_records_which_thread_wrote_it_and_a_store_of_a_number_does_not() {
1986        // Section 9.5's recording half, and the thinning that is the whole reason it is affordable.
1987        // The plane holds one stamp per eight bytes because eight bytes is what a pointer comes in,
1988        // so a granule two threads share is one holding no pointer and one no race class asks
1989        // about. Recording every store would put those two threads in the plane as each other's
1990        // strangers, which is a report about a program doing nothing wrong.
1991        let mut names = Interner::new();
1992        let (module, plane) = planed(&mut names, "stamp.c");
1993
1994        let i64_ = Type::int(64);
1995        let mut func = Func::new(
1996            names.intern("stamp"),
1997            Signature::new().with_params(&[Type::PTR, Type::PTR, i64_]),
1998        );
1999        let entry = func.create_block();
2000        let p = func.append_param(entry, Type::PTR);
2001        let q = func.append_param(entry, Type::PTR);
2002        let v = func.append_param(entry, i64_);
2003        let info = MemInfo {
2004            size: 0,
2005            align: 8,
2006            order: MemOrder::NotAtomic,
2007            tbaa: None,
2008            owns: 0,
2009            restrict: Restrict::NONE,
2010        };
2011        let mut b = Builder::new(&mut func, entry);
2012        let extra = Extra::Mem(b.func().add_mem(info));
2013        let args = b.func().push_values(&[q, p]);
2014        b.inst(InstData { args, extra, ..InstData::new(Opcode::Store) }, &[]);
2015        let args = b.func().push_values(&[v, p]);
2016        b.inst(InstData { args, extra, ..InstData::new(Opcode::Store) }, &[]);
2017        b.ret(&[]);
2018
2019        let counts = insert(&mut func, &plane, 8, Subobject::Off, Promise::Off, Races::Metadata);
2020        assert_eq!(counts.wrote, 2, "the init plane takes both, since both wrote bytes");
2021        assert_eq!(counts.stamped, 1, "and the epoch plane takes the one that wrote a pointer");
2022        assert_eq!(counts.watched, 1, "which is also the one that asks what was there before");
2023
2024        let printed = print_func(&module, &func, &names);
2025        assert_eq!(printed.matches("meta_epoch").count(), 1, "{printed}");
2026        assert!(printed.contains("meta_epoch %0, %"), "{printed}");
2027
2028        // In front of the store and the recording behind it, which is the order the reading half
2029        // depends on: the recording overwrites the stamp the check reads, so a check on the other
2030        // side of the store would be asking about the write it was called for.
2031        assert_eq!(printed.matches("check_race").count(), 1, "{printed}");
2032        let asked = printed.find("check_race").expect("the check is in there");
2033        let stamp = printed.find("meta_epoch").expect("so is the recording");
2034        assert!(asked < stamp, "{printed}");
2035
2036        if let Err(errors) = verify_func(&module, &func, &names) {
2037            panic!("that was expected to be believed: {errors:#?}");
2038        }
2039    }
2040
2041    #[test]
2042    fn nothing_records_into_the_epoch_plane_unless_the_build_asked_for_it() {
2043        // The default, and the reason it is the default is not cost. Every ordering the monitor has
2044        // was carried by an edge somebody interposed, and the atomics are not a call, so until the
2045        // compiler emits those edges a program that hands a pointer between threads through one
2046        // would be reported for doing nothing wrong. This is the one plane where instrumentation
2047        // nobody wrote costs a false report rather than a missed one.
2048        let mut names = Interner::new();
2049        let (module, plane) = planed(&mut names, "quiet.c");
2050
2051        let mut func =
2052            Func::new(names.intern("quiet"), Signature::new().with_params(&[Type::PTR, Type::PTR]));
2053        let entry = func.create_block();
2054        let p = func.append_param(entry, Type::PTR);
2055        let q = func.append_param(entry, Type::PTR);
2056        let info = MemInfo {
2057            size: 0,
2058            align: 8,
2059            order: MemOrder::NotAtomic,
2060            tbaa: None,
2061            owns: 0,
2062            restrict: Restrict::NONE,
2063        };
2064        let mut b = Builder::new(&mut func, entry);
2065        let args = b.func().push_values(&[q, p]);
2066        let extra = Extra::Mem(b.func().add_mem(info));
2067        b.inst(InstData { args, extra, ..InstData::new(Opcode::Store) }, &[]);
2068        b.ret(&[]);
2069
2070        let counts = insert(&mut func, &plane, 8, Subobject::Off, Promise::Off, Races::Off);
2071        assert_eq!((counts.stamped, counts.watched), (0, 0));
2072        let printed = print_func(&module, &func, &names);
2073        assert!(!printed.contains("meta_epoch"), "{printed}");
2074        assert!(!printed.contains("check_race"), "{printed}");
2075    }
2076
2077    #[test]
2078    fn a_read_of_a_pointer_asks_about_races_only_in_the_mode_that_reports_them() {
2079        // The one thing separating the two modes. A store asking is C3, two threads writing the
2080        // same slot, and a read asking is C2, the pointer word race, which section 9.5 lists apart
2081        // from the rest because it is reported in its own right. Tier E carries `metadata` and not
2082        // that, so a build wanting every race a read can see asks for it by name.
2083        let mut names = Interner::new();
2084        let (module, plane) = planed(&mut names, "read.c");
2085
2086        let mut quiet = one_pointer_read(&mut names);
2087        let counts = insert(&mut quiet, &plane, 8, Subobject::Off, Promise::Off, Races::Metadata);
2088        assert_eq!(counts.watched, 0);
2089        assert!(!print_func(&module, &quiet, &names).contains("check_race"));
2090
2091        let mut asking = one_pointer_read(&mut names);
2092        let counts = insert(&mut asking, &plane, 8, Subobject::Off, Promise::Off, Races::Pointer);
2093        assert_eq!(counts.watched, 1);
2094
2095        // Over the target's pointer width rather than over one byte, which is what #953 was about
2096        // and is the reason the width is a parameter of this pass at all.
2097        let printed = print_func(&module, &asking, &names);
2098        assert!(printed.contains("check_race %1, %0, size 8, align 1"), "{printed}");
2099
2100        if let Err(errors) = verify_func(&module, &asking, &names) {
2101            panic!("that was expected to be believed: {errors:#?}");
2102        }
2103    }
2104
2105    #[test]
2106    fn a_read_of_a_number_asks_nothing_even_in_the_mode_that_watches_reads() {
2107        // The same thinning the recording makes, from the other side. A granule two threads both
2108        // reach is a granule holding no pointer, so nothing ever stamped it and a check over it
2109        // would be a load of the plane that can only answer no.
2110        let mut names = Interner::new();
2111        let (module, plane) = planed(&mut names, "number.c");
2112
2113        let mut func = one_number_read(&mut names);
2114        let counts = insert(&mut func, &plane, 8, Subobject::Off, Promise::Off, Races::Pointer);
2115        assert_eq!(counts.watched, 0);
2116        assert!(!print_func(&module, &func, &names).contains("check_race"));
2117    }
2118
2119    /// An atomic store, an atomic load and an atomic read-modify-write in one function.
2120    ///
2121    /// Each takes the ordering it is given, so one builder covers every case the edges have an
2122    /// opinion about: which side a marker lands on, and whether one lands at all.
2123    fn three_atomics(names: &mut Interner, store: MemOrder, load: MemOrder, rmw: MemOrder) -> Func {
2124        let i64_ = Type::int(64);
2125        let mut func =
2126            Func::new(names.intern("atomics"), Signature::new().with_params(&[Type::PTR, i64_]));
2127        let entry = func.create_block();
2128        let p = func.append_param(entry, Type::PTR);
2129        let v = func.append_param(entry, i64_);
2130        let info = MemInfo {
2131            size: 8,
2132            align: 8,
2133            order: MemOrder::NotAtomic,
2134            tbaa: None,
2135            owns: 0,
2136            restrict: Restrict::NONE,
2137        };
2138
2139        let mut b = Builder::new(&mut func, entry);
2140        let extra = Extra::Mem(b.func().add_mem(MemInfo { order: store, ..info }));
2141        let args = b.func().push_values(&[v, p]);
2142        b.inst(InstData { args, extra, ..InstData::new(Opcode::AtomicStore) }, &[]);
2143
2144        let extra = Extra::Mem(b.func().add_mem(MemInfo { order: load, ..info }));
2145        let args = b.func().push_values(&[p]);
2146        b.inst(InstData { args, extra, ..InstData::new(Opcode::AtomicLoad) }, &[i64_]);
2147
2148        let at = b.func().add_mem(MemInfo { order: rmw, ..info });
2149        let args = b.func().push_values(&[p, v]);
2150        let extra = Extra::Rmw(RmwOp::Add, at);
2151        b.inst(InstData { args, extra, ..InstData::new(Opcode::AtomicRmw) }, &[i64_]);
2152        b.ret(&[]);
2153        func
2154    }
2155
2156    #[test]
2157    fn a_release_publishes_in_front_of_its_atomic_and_an_acquire_takes_after_it() {
2158        // The edges of section 9.5 that are not a call and so have nowhere to be interposed. Which
2159        // side a marker lands on is which side the ordering is on: a release publishes everything
2160        // the thread has already done, so the clock has to be written down while that is still
2161        // true, and an acquire takes an ordering from what the atomic just read, which is not there
2162        // to be taken until the atomic has run.
2163        let mut names = Interner::new();
2164        let (module, plane) = planed(&mut names, "edges.c");
2165
2166        let mut func =
2167            three_atomics(&mut names, MemOrder::Release, MemOrder::Acquire, MemOrder::Relaxed);
2168        let counts = insert(&mut func, &plane, 8, Subobject::Off, Promise::Off, Races::Metadata);
2169        assert_eq!(counts.edged, 2, "the release and the acquire, and not the relaxed one");
2170
2171        let printed = print_func(&module, &func, &names);
2172        assert_eq!(printed.matches("meta_release").count(), 1, "{printed}");
2173        assert_eq!(printed.matches("meta_acquire").count(), 1, "{printed}");
2174        assert!(printed.contains("meta_release %0\n    atomic_store"), "{printed}");
2175        assert!(printed.contains("= atomic_load"), "{printed}");
2176        let read = printed.find("atomic_load").expect("the load is in there");
2177        let took = printed.find("meta_acquire").expect("and so is the edge it takes");
2178        assert!(read < took, "{printed}");
2179
2180        if let Err(errors) = verify_func(&module, &func, &names) {
2181            panic!("that was expected to be believed: {errors:#?}");
2182        }
2183    }
2184
2185    #[test]
2186    fn a_read_modify_write_that_orders_both_ways_carries_both_halves_of_an_edge() {
2187        // `acq_rel` and `seq_cst` publish and take, which is what makes a lock built out of one
2188        // compare and exchange a lock this can follow. One marker each side, since the two are
2189        // about different moments and collapsing them would put the publication after the read.
2190        let mut names = Interner::new();
2191        let (module, plane) = planed(&mut names, "both.c");
2192
2193        let mut func =
2194            three_atomics(&mut names, MemOrder::Relaxed, MemOrder::Relaxed, MemOrder::AcqRel);
2195        let counts = insert(&mut func, &plane, 8, Subobject::Off, Promise::Off, Races::Metadata);
2196        assert_eq!(counts.edged, 2, "one of each, around the one atomic that orders anything");
2197
2198        let printed = print_func(&module, &func, &names);
2199        let published = printed.find("meta_release").expect("the publishing half is in there");
2200        let changed = printed.find("atomic_rmw").expect("so is the atomic");
2201        let took = printed.find("meta_acquire").expect("and so is the taking half");
2202        assert!(published < changed && changed < took, "{printed}");
2203
2204        if let Err(errors) = verify_func(&module, &func, &names) {
2205            panic!("that was expected to be believed: {errors:#?}");
2206        }
2207    }
2208
2209    #[test]
2210    fn a_relaxed_atomic_is_no_edge_and_neither_is_any_atomic_in_a_build_that_is_not_watching() {
2211        // Relaxed is atomic and is not an ordering. It says the word does not tear and it says
2212        // nothing about what happened either side of it, so an edge taken there would be an
2213        // ordering that does not exist, and inventing one hides the races it covers up.
2214        let mut names = Interner::new();
2215        let (module, plane) = planed(&mut names, "relaxed.c");
2216
2217        let mut loose =
2218            three_atomics(&mut names, MemOrder::Relaxed, MemOrder::Relaxed, MemOrder::Relaxed);
2219        let counts = insert(&mut loose, &plane, 8, Subobject::Off, Promise::Off, Races::Pointer);
2220        assert_eq!(counts.edged, 0);
2221        let printed = print_func(&module, &loose, &names);
2222        assert!(
2223            !printed.contains("meta_release") && !printed.contains("meta_acquire"),
2224            "{printed}"
2225        );
2226
2227        // And nothing at all without the flag, which is where every other part of this plane is.
2228        let mut off =
2229            three_atomics(&mut names, MemOrder::SeqCst, MemOrder::SeqCst, MemOrder::SeqCst);
2230        let counts = insert(&mut off, &plane, 8, Subobject::Off, Promise::Off, Races::Off);
2231        assert_eq!(counts.edged, 0);
2232        let printed = print_func(&module, &off, &names);
2233        assert!(
2234            !printed.contains("meta_release") && !printed.contains("meta_acquire"),
2235            "{printed}"
2236        );
2237    }
2238
2239    /// One fence with the ordering it is given, and nothing else in the function.
2240    fn one_fence(names: &mut Interner, order: MemOrder) -> Func {
2241        let mut func = Func::new(names.intern("fenced"), Signature::new());
2242        let entry = func.create_block();
2243        let mut b = Builder::new(&mut func, entry);
2244        b.inst(InstData { extra: Extra::Order(order), ..InstData::new(Opcode::Fence) }, &[]);
2245        b.ret(&[]);
2246        func
2247    }
2248
2249    #[test]
2250    fn a_fence_carries_the_same_edge_as_an_atomic_with_no_object_to_key_it_on() {
2251        // The other way a C program orders two threads without calling anything. A fence orders
2252        // against every other thread rather than against one object, so the markers take no
2253        // operands: there is no address in a fence that could be the key, and the relaxed atomic
2254        // beside it in the source is not the key either, since what the fence orders is everything
2255        // the thread did rather than that one word.
2256        let mut names = Interner::new();
2257        let (module, plane) = planed(&mut names, "fence.c");
2258
2259        let mut func = one_fence(&mut names, MemOrder::SeqCst);
2260        let counts = insert(&mut func, &plane, 8, Subobject::Off, Promise::Off, Races::Metadata);
2261        assert_eq!(counts.edged, 2, "seq_cst publishes and takes, so one of each");
2262
2263        let printed = print_func(&module, &func, &names);
2264        assert!(
2265            printed.contains(
2266                "meta_fence_release
2267    fence"
2268            ),
2269            "{printed}"
2270        );
2271        assert!(
2272            printed.contains(
2273                "fence seq_cst
2274    meta_fence_acquire"
2275            ),
2276            "{printed}"
2277        );
2278
2279        if let Err(errors) = verify_func(&module, &func, &names) {
2280            panic!("that was expected to be believed: {errors:#?}");
2281        }
2282    }
2283
2284    #[test]
2285    fn a_release_fence_publishes_and_an_acquire_fence_takes_and_neither_does_the_other() {
2286        // The halves apart, which is the shape a fence is usually written in: a release fence
2287        // after the record is filled and an acquire fence before it is read.
2288        let mut names = Interner::new();
2289        let (module, plane) = planed(&mut names, "halves.c");
2290
2291        let mut publishing = one_fence(&mut names, MemOrder::Release);
2292        let counts =
2293            insert(&mut publishing, &plane, 8, Subobject::Off, Promise::Off, Races::Pointer);
2294        assert_eq!(counts.edged, 1);
2295        let printed = print_func(&module, &publishing, &names);
2296        assert!(printed.contains("meta_fence_release"), "{printed}");
2297        assert!(!printed.contains("meta_fence_acquire"), "{printed}");
2298
2299        let mut taking = one_fence(&mut names, MemOrder::Acquire);
2300        let counts = insert(&mut taking, &plane, 8, Subobject::Off, Promise::Off, Races::Pointer);
2301        assert_eq!(counts.edged, 1);
2302        let printed = print_func(&module, &taking, &names);
2303        assert!(printed.contains("meta_fence_acquire"), "{printed}");
2304        assert!(!printed.contains("meta_fence_release"), "{printed}");
2305
2306        // And nothing at all without the flag, the same as every other part of this plane.
2307        let mut off = one_fence(&mut names, MemOrder::SeqCst);
2308        let counts = insert(&mut off, &plane, 8, Subobject::Off, Promise::Off, Races::Off);
2309        assert_eq!(counts.edged, 0);
2310        assert!(!print_func(&module, &off, &names).contains("meta_fence"));
2311    }
2312
2313    #[test]
2314    fn a_store_that_owns_the_padding_after_it_records_that_too() {
2315        // `-fsafety-init=nopadding`, which by the time it gets here is a number on the store and
2316        // nothing else. A `char` member with three bytes of padding behind it owns four, so the
2317        // record it is in comes out whole once the other member is written and the ordinary reads
2318        // of one, which are a `memcmp` or a hash or a `write`, are not refused. Working out what
2319        // the padding is takes a record's layout, which the front end has and this pass does not,
2320        // and that is why the number arrives rather than the mode.
2321        let mut names = Interner::new();
2322        let (module, plane) = planed(&mut names, "owns.c");
2323
2324        let byte = Type::int(8);
2325        let mut func =
2326            Func::new(names.intern("write"), Signature::new().with_params(&[Type::PTR, byte]));
2327        let entry = func.create_block();
2328        let p = func.append_param(entry, Type::PTR);
2329        let v = func.append_param(entry, byte);
2330        let info = MemInfo {
2331            size: 0,
2332            align: 1,
2333            order: MemOrder::NotAtomic,
2334            tbaa: None,
2335            owns: 4,
2336            restrict: Restrict::NONE,
2337        };
2338        let mut b = Builder::new(&mut func, entry);
2339        let args = b.func().push_values(&[v, p]);
2340        let extra = Extra::Mem(b.func().add_mem(info));
2341        b.inst(InstData { args, extra, ..InstData::new(Opcode::Store) }, &[]);
2342        b.ret(&[]);
2343
2344        assert_eq!(insert(&mut func, &plane, 8, Subobject::Off, Promise::Off, Races::Off).wrote, 1);
2345
2346        let printed = print_func(&module, &func, &names);
2347        // Four rather than the one byte the store wrote.
2348        assert!(printed.contains("%4 = iconst.i64 4\n    meta_init %0, %4\n"), "{printed}");
2349        // And the bounds check is still about the one byte the store touches, since the padding
2350        // is what a store records and not what it writes.
2351        assert!(printed.contains("check_bounds %2, %0, size 1"), "{printed}");
2352
2353        if let Err(errors) = verify_func(&module, &func, &names) {
2354            panic!("that was expected to be believed: {errors:#?}");
2355        }
2356    }
2357
2358    #[test]
2359    fn a_read_asks_whether_anything_ever_wrote_the_bytes_it_is_about_to_read() {
2360        // Document 03's Y6. The question carries the access's width and no type, because the plane
2361        // holds one bit per byte and the bit says whether anything was stored there at all.
2362        let mut names = Interner::new();
2363        let (module, plane) = planed(&mut names, "ask.c");
2364        let mut func = reading(&mut names, None);
2365
2366        assert_eq!(
2367            insert(&mut func, &plane, 8, Subobject::Off, Promise::Off, Races::Off).filled,
2368            1
2369        );
2370
2371        let printed = print_func(&module, &func, &names);
2372        assert!(printed.contains("check_init %1, %0, size 4, align 4\n"), "{printed}");
2373
2374        if let Err(errors) = verify_func(&module, &func, &names) {
2375            panic!("that was expected to be believed: {errors:#?}");
2376        }
2377    }
2378
2379    #[test]
2380    fn a_read_the_front_end_named_no_type_for_still_asks_the_init_plane() {
2381        // The one place the two questions a read asks come apart. A read with no type on it has
2382        // nothing to ask the type plane, because the question there is which type the bytes hold,
2383        // and it has the same thing to ask the init plane as any other read, because the question
2384        // there is about the bytes rather than about the access.
2385        let mut names = Interner::new();
2386        let (_module, plane) = planed(&mut names, "untyped.c");
2387        let mut func = reading(&mut names, None);
2388
2389        let counts = insert(&mut func, &plane, 8, Subobject::Off, Promise::Off, Races::Off);
2390        assert_eq!(counts.asked, 0);
2391        assert_eq!(counts.filled, 1);
2392    }
2393
2394    #[test]
2395    fn a_store_asks_the_init_plane_nothing() {
2396        // A store writes the bytes it is about to write, so whether anything wrote them before is
2397        // not a question about it. Asking would refuse the first write to every fresh instance,
2398        // which is every program.
2399        let mut names = Interner::new();
2400        let (module, plane) = planed(&mut names, "store.c");
2401
2402        let i64_ = Type::int(64);
2403        let mut func =
2404            Func::new(names.intern("write"), Signature::new().with_params(&[Type::PTR, i64_]));
2405        let entry = func.create_block();
2406        let p = func.append_param(entry, Type::PTR);
2407        let v = func.append_param(entry, i64_);
2408        let info = MemInfo {
2409            size: 8,
2410            align: 8,
2411            order: MemOrder::NotAtomic,
2412            tbaa: None,
2413            owns: 0,
2414            restrict: Restrict::NONE,
2415        };
2416        let mut b = Builder::new(&mut func, entry);
2417        let args = b.func().push_values(&[v, p]);
2418        let extra = Extra::Mem(b.func().add_mem(info));
2419        b.inst(InstData { args, extra, ..InstData::new(Opcode::Store) }, &[]);
2420        b.ret(&[]);
2421
2422        assert_eq!(
2423            insert(&mut func, &plane, 8, Subobject::Off, Promise::Off, Races::Off).filled,
2424            0
2425        );
2426
2427        let printed = print_func(&module, &func, &names);
2428        assert!(!printed.contains("check_init"), "{printed}");
2429    }
2430
2431    #[test]
2432    fn a_read_tells_the_init_plane_nothing() {
2433        // A read is a question and not a judgement. Whether the bytes it read hold anything is
2434        // what the plane already says, and a read that wrote the plane would make every read of
2435        // storage nothing ever wrote look like a read of storage something did.
2436        let mut names = Interner::new();
2437        let (module, plane) = planed(&mut names, "read.c");
2438        let mut func = reading(&mut names, None);
2439
2440        assert_eq!(insert(&mut func, &plane, 8, Subobject::Off, Promise::Off, Races::Off).wrote, 0);
2441
2442        let printed = print_func(&module, &func, &names);
2443        assert!(!printed.contains("meta_init"), "{printed}");
2444    }
2445
2446    /// A function that copies a fixed number of bytes from one of its parameters to the other.
2447    fn one_copy(names: &mut Interner, opcode: Opcode) -> Func {
2448        let mut func =
2449            Func::new(names.intern("move"), Signature::new().with_params(&[Type::PTR, Type::PTR]));
2450        let entry = func.create_block();
2451        let to = func.append_param(entry, Type::PTR);
2452        let from = func.append_param(entry, Type::PTR);
2453
2454        let info = MemInfo {
2455            size: 24,
2456            align: 8,
2457            order: MemOrder::NotAtomic,
2458            tbaa: None,
2459            owns: 0,
2460            restrict: Restrict::NONE,
2461        };
2462        let mut b = Builder::new(&mut func, entry);
2463        let args = b.func().push_values(&[to, from]);
2464        let extra = Extra::Mem(b.func().add_mem(info));
2465        b.inst(InstData { args, extra, ..InstData::new(opcode) }, &[]);
2466        b.ret(&[]);
2467        func
2468    }
2469
2470    #[test]
2471    fn a_copy_carries_whatever_the_bytes_it_read_said() {
2472        // The other half of the judgement C 6.5 describes. A copy does not store through a type, so
2473        // there is nothing here for the compiler to name: what the copied bytes are is whatever the
2474        // bytes they came from were, and the plane over the source is the only place that is
2475        // written down. Without this the destination would go on saying whatever was there before.
2476        let mut names = Interner::new();
2477        let mut func = one_copy(&mut names, Opcode::Memcpy);
2478        let (module, plane) = planed(&mut names, "move.c");
2479        assert_eq!(
2480            insert(&mut func, &plane, 8, Subobject::Off, Promise::Off, Races::Off),
2481            Counts { checked: 2, live: 2, carried: 1, moved: 1, relocated: 1, ..Counts::default() }
2482        );
2483
2484        assert_eq!(
2485            print_func(&module, &func, &names),
2486            // The four checks in front and the three copies behind, which is where a store's two
2487            // and a store's judgement go for the same reasons.
2488            "func @move(ptr, ptr), linkage(external) {\n\
2489             block0(%0: ptr, %1: ptr):\n    \
2490             %2 = cap_of %1\n    \
2491             %3 = cap_of %0\n    \
2492             check_bounds %3, %0, size 24, align 8\n    \
2493             check_live %3, %0\n    \
2494             check_bounds %2, %1, size 24, align 8\n    \
2495             check_live %2, %1\n    \
2496             memcpy %0, %1, size 24, align 8\n    \
2497             %4 = iconst.i64 24\n    \
2498             meta_type_copy %0, %1, %4\n    \
2499             %5 = iconst.i64 24\n    \
2500             meta_init_copy %0, %1, %5\n    \
2501             %6 = iconst.i64 24\n    \
2502             cap_copy %0, %1, %6\n    \
2503             return\n\
2504             }\n"
2505        );
2506
2507        if let Err(errors) = verify_func(&module, &func, &names) {
2508            panic!("that was expected to be believed: {errors:#?}");
2509        }
2510    }
2511
2512    #[test]
2513    fn a_copy_whose_ranges_may_overlap_is_carried_the_same_way() {
2514        // `memmove` is `memcpy` with the overlap allowed, and the overlap is the runtime's problem
2515        // rather than this pass's: a copy writes no plane entries of its own, so the entries over
2516        // the source are the same ones whichever end the bytes were moved from.
2517        let mut names = Interner::new();
2518        let mut func = one_copy(&mut names, Opcode::Memmove);
2519        let (module, plane) = planed(&mut names, "overlap.c");
2520        assert_eq!(
2521            insert(&mut func, &plane, 8, Subobject::Off, Promise::Off, Races::Off),
2522            Counts { checked: 2, live: 2, carried: 1, moved: 1, relocated: 1, ..Counts::default() }
2523        );
2524
2525        let printed = print_func(&module, &func, &names);
2526        assert!(printed.contains("check_bounds %3, %0, size 24, align 8\n"), "{printed}");
2527        assert!(printed.contains("check_bounds %2, %1, size 24, align 8\n"), "{printed}");
2528        assert!(printed.contains("meta_type_copy %0, %1, %4\n"), "{printed}");
2529        assert!(printed.contains("meta_init_copy %0, %1, %5\n"), "{printed}");
2530        assert!(printed.contains("cap_copy %0, %1, %6\n"), "{printed}");
2531    }
2532
2533    #[test]
2534    fn a_copy_of_a_length_the_program_works_out_carries_it_with_the_length_it_was_given() {
2535        // The three plane writes are the same three, and the count is the copy's own operand
2536        // rather than a constant made beside it, because there is no constant to make. They go in
2537        // after the copy, which is where the constant ones go in too.
2538        let mut names = Interner::new();
2539        let mut func = Func::new(
2540            names.intern("move"),
2541            Signature::new().with_params(&[Type::PTR, Type::PTR, Type::int(64)]),
2542        );
2543        let entry = func.create_block();
2544        let to = func.append_param(entry, Type::PTR);
2545        let from = func.append_param(entry, Type::PTR);
2546        let length = func.append_param(entry, Type::int(64));
2547
2548        let info = MemInfo {
2549            size: 0,
2550            align: 8,
2551            order: MemOrder::NotAtomic,
2552            tbaa: None,
2553            owns: 0,
2554            restrict: Restrict::NONE,
2555        };
2556        let mut b = Builder::new(&mut func, entry);
2557        let args = b.func().push_values(&[to, from, length]);
2558        let extra = Extra::Mem(b.func().add_mem(info));
2559        b.inst(InstData { args, extra, ..InstData::new(Opcode::Memcpy) }, &[]);
2560        b.ret(&[]);
2561
2562        let (module, plane) = planed(&mut names, "move.c");
2563        assert_eq!(
2564            insert(&mut func, &plane, 8, Subobject::Off, Promise::Off, Races::Off),
2565            Counts { checked: 2, live: 2, carried: 1, moved: 1, relocated: 1, ..Counts::default() }
2566        );
2567
2568        assert_eq!(
2569            print_func(&module, &func, &names),
2570            "func @move(ptr, ptr, i64), linkage(external) {\n\
2571             block0(%0: ptr, %1: ptr, %2: i64):\n    \
2572             %3 = cap_of %1\n    \
2573             %4 = cap_of %0\n    \
2574             check_bounds %4, %0, %2, align 8\n    \
2575             check_live %4, %0\n    \
2576             check_bounds %3, %1, %2, align 8\n    \
2577             check_live %3, %1\n    \
2578             memcpy %0, %1, %2, align 8\n    \
2579             meta_type_copy %0, %1, %2\n    \
2580             meta_init_copy %0, %1, %2\n    \
2581             cap_copy %0, %1, %2\n    \
2582             return\n\
2583             }\n"
2584        );
2585
2586        if let Err(errors) = verify_func(&module, &func, &names) {
2587            panic!("that was expected to be believed: {errors:#?}");
2588        }
2589    }
2590
2591    #[test]
2592    fn a_fill_is_checked_over_the_bytes_it_covers_and_records_that_it_wrote_them() {
2593        // Issue 1552. A fill went through this pass and came out with nothing at all in front of
2594        // it or behind it, so a `struct S s = {0};` was a write nothing bounded and a zeroed
2595        // object the init plane went on calling unwritten. The count is in the payload here, so
2596        // the bounds check reads it there and the two plane writes read a constant made beside
2597        // the fill.
2598        let mut names = Interner::new();
2599        let mut func = Func::new(
2600            names.intern("zero"),
2601            Signature::new().with_params(&[Type::PTR, Type::int(8)]),
2602        );
2603        let entry = func.create_block();
2604        let to = func.append_param(entry, Type::PTR);
2605        let byte = func.append_param(entry, Type::int(8));
2606
2607        let info = MemInfo {
2608            size: 24,
2609            align: 4,
2610            order: MemOrder::NotAtomic,
2611            tbaa: None,
2612            owns: 0,
2613            restrict: Restrict::NONE,
2614        };
2615        let mut b = Builder::new(&mut func, entry);
2616        let args = b.func().push_values(&[to, byte]);
2617        let extra = Extra::Mem(b.func().add_mem(info));
2618        b.inst(InstData { args, extra, ..InstData::new(Opcode::Memset) }, &[]);
2619        b.ret(&[]);
2620
2621        let (module, plane) = planed(&mut names, "zero.c");
2622        assert_eq!(
2623            insert(&mut func, &plane, 8, Subobject::Off, Promise::Off, Races::Off),
2624            Counts { checked: 1, live: 1, wrote: 1, judged: 1, ..Counts::default() }
2625        );
2626
2627        assert_eq!(
2628            print_func(&module, &func, &names),
2629            "func @zero(ptr, i8), linkage(external) {\n\
2630             block0(%0: ptr, %1: i8):\n    \
2631             %2 = cap_of %0\n    \
2632             check_bounds %2, %0, size 24, align 4\n    \
2633             check_live %2, %0\n    \
2634             memset %0, %1, size 24, align 4\n    \
2635             %3 = iconst.i64 24\n    \
2636             meta_type %0, %3, tbaa !1\n    \
2637             meta_init %0, %3\n    \
2638             return\n\
2639             }\n"
2640        );
2641
2642        if let Err(errors) = verify_func(&module, &func, &names) {
2643            panic!("that was expected to be believed: {errors:#?}");
2644        }
2645    }
2646
2647    #[test]
2648    fn a_fill_of_a_length_the_program_works_out_is_checked_over_that_length() {
2649        // The other form, and the reason the bounds check in front of a fill is not the one
2650        // [`check`] puts in front of an access: there is no number in the payload to read, so the
2651        // count travels as the third operand `check_bounds` has for a length nobody knew when the
2652        // access was parsed, and the two plane writes read the fill's own operand.
2653        let mut names = Interner::new();
2654        let mut func = Func::new(
2655            names.intern("zero"),
2656            Signature::new().with_params(&[Type::PTR, Type::int(8), Type::int(64)]),
2657        );
2658        let entry = func.create_block();
2659        let to = func.append_param(entry, Type::PTR);
2660        let byte = func.append_param(entry, Type::int(8));
2661        let length = func.append_param(entry, Type::int(64));
2662
2663        let info = MemInfo {
2664            size: 0,
2665            align: 1,
2666            order: MemOrder::NotAtomic,
2667            tbaa: None,
2668            owns: 0,
2669            restrict: Restrict::NONE,
2670        };
2671        let mut b = Builder::new(&mut func, entry);
2672        let args = b.func().push_values(&[to, byte, length]);
2673        let extra = Extra::Mem(b.func().add_mem(info));
2674        b.inst(InstData { args, extra, ..InstData::new(Opcode::Memset) }, &[]);
2675        b.ret(&[]);
2676
2677        let (module, plane) = planed(&mut names, "zero.c");
2678        assert_eq!(
2679            insert(&mut func, &plane, 8, Subobject::Off, Promise::Off, Races::Off),
2680            Counts { checked: 1, live: 1, wrote: 1, judged: 1, ..Counts::default() }
2681        );
2682
2683        assert_eq!(
2684            print_func(&module, &func, &names),
2685            "func @zero(ptr, i8, i64), linkage(external) {\n\
2686             block0(%0: ptr, %1: i8, %2: i64):\n    \
2687             %3 = cap_of %0\n    \
2688             check_bounds %3, %0, %2, align 1\n    \
2689             check_live %3, %0\n    \
2690             memset %0, %1, %2, align 1\n    \
2691             meta_type %0, %2, tbaa !1\n    \
2692             meta_init %0, %2\n    \
2693             return\n\
2694             }\n"
2695        );
2696
2697        if let Err(errors) = verify_func(&module, &func, &names) {
2698            panic!("that was expected to be believed: {errors:#?}");
2699        }
2700    }
2701
2702    #[test]
2703    fn a_walk_over_elements_hands_the_check_the_width_of_one() {
2704        // The low end of judgement J2's window is one element below the object, so the check has
2705        // to be told how wide an element is. C computed a byte offset before the arithmetic
2706        // happened, so the width is not in the `ptr_add`, and what is in it is the multiply the
2707        // frontend left behind. This pass runs before the optimizer, so that shape is still there.
2708        let mut names = Interner::new();
2709        let mut func = Func::new(
2710            names.intern("walk"),
2711            Signature::new().with_params(&[Type::PTR, Type::int(64)]).with_returns(&[Type::PTR]),
2712        );
2713        let entry = func.create_block();
2714        let p = func.append_param(entry, Type::PTR);
2715        let n = func.append_param(entry, Type::int(64));
2716
2717        let mut b = Builder::new(&mut func, entry);
2718        let width = b.iconst(Type::int(64), 24);
2719        let bytes = b.binary(Opcode::Mul, n, width, Flags::NSW);
2720        let args = b.func().push_values(&[p, bytes]);
2721        let moved = b.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
2722        b.ret(&[moved]);
2723
2724        let (module, plane) = planed(&mut names, "walk.c");
2725        insert(&mut func, &plane, 8, Subobject::Off, Promise::Off, Races::Off);
2726
2727        assert_eq!(
2728            print_func(&module, &func, &names),
2729            "func @walk(ptr, i64) -> ptr, linkage(external) {\n\
2730             block0(%0: ptr, %1: i64):\n    \
2731             %2 = cap_of %0\n    \
2732             %3 = iconst.i64 24\n    \
2733             %4 = mul.nsw %1, %3\n    \
2734             %5 = ptr_add %0, %4\n    \
2735             check_deriv %2, %0, %5, %3\n    \
2736             return %5\n\
2737             }\n"
2738        );
2739    }
2740
2741    #[test]
2742    fn a_walk_that_goes_backwards_is_still_a_walk_over_elements() {
2743        // Which is the case the whole widening is for. A walk backwards negates the byte offset
2744        // rather than the width, so the multiply is one instruction further down and the width is
2745        // the same one. Missing it here would mean `&a[-1]` getting a one byte window and being
2746        // refused, which is the report this change exists to stop.
2747        let mut names = Interner::new();
2748        let mut func = Func::new(
2749            names.intern("back"),
2750            Signature::new().with_params(&[Type::PTR, Type::int(64)]).with_returns(&[Type::PTR]),
2751        );
2752        let entry = func.create_block();
2753        let p = func.append_param(entry, Type::PTR);
2754        let n = func.append_param(entry, Type::int(64));
2755
2756        let mut b = Builder::new(&mut func, entry);
2757        let width = b.iconst(Type::int(64), 24);
2758        let bytes = b.binary(Opcode::Mul, n, width, Flags::NSW);
2759        let zero = b.iconst(Type::int(64), 0);
2760        let back = b.binary(Opcode::Sub, zero, bytes, Flags::NONE);
2761        let args = b.func().push_values(&[p, back]);
2762        let moved = b.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
2763        b.ret(&[moved]);
2764
2765        let (module, plane) = planed(&mut names, "back.c");
2766        insert(&mut func, &plane, 8, Subobject::Off, Promise::Off, Races::Off);
2767
2768        let printed = print_func(&module, &func, &names);
2769        assert!(printed.contains("check_deriv %2, %0, %7, %3\n"), "{printed}");
2770    }
2771
2772    #[test]
2773    fn a_pointer_computed_from_another_pointer_is_checked_where_it_is_computed() {
2774        // Judgement J2. The pointer that walked off its object is caught at the arithmetic, not
2775        // at whatever line eventually reads through it, which is what lets the report name the
2776        // loop that ran too far. Note where the check sits: after the ptr_add, because it is
2777        // handed the pointer the ptr_add produced.
2778        let mut names = Interner::new();
2779        let mut func = Func::new(
2780            names.intern("walk"),
2781            Signature::new().with_params(&[Type::PTR, Type::int(64)]).with_returns(&[Type::PTR]),
2782        );
2783        let entry = func.create_block();
2784        let p = func.append_param(entry, Type::PTR);
2785        let n = func.append_param(entry, Type::int(64));
2786
2787        let mut b = Builder::new(&mut func, entry);
2788        let args = b.func().push_values(&[p, n]);
2789        let moved = b.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
2790        b.ret(&[moved]);
2791
2792        let (module, plane) = planed(&mut names, "walk.c");
2793        assert_eq!(
2794            insert(&mut func, &plane, 8, Subobject::Off, Promise::Off, Races::Off),
2795            Counts { derived: 1, ..Counts::default() }
2796        );
2797
2798        assert_eq!(
2799            print_func(&module, &func, &names),
2800            // The stride is one, because the offset here is a block parameter and nothing about
2801            // it says what it is a count of. That is the answer a shape this pass does not
2802            // recognise gets, and it is the strict reading of C.
2803            "func @walk(ptr, i64) -> ptr, linkage(external) {\n\
2804             block0(%0: ptr, %1: i64):\n    \
2805             %2 = cap_of %0\n    \
2806             %3 = iconst.i64 1\n    \
2807             %4 = ptr_add %0, %1\n    \
2808             check_deriv %2, %0, %4, %3\n    \
2809             return %4\n\
2810             }\n"
2811        );
2812
2813        if let Err(errors) = verify_func(&module, &func, &names) {
2814            panic!("that was expected to be believed: {errors:#?}");
2815        }
2816    }
2817
2818    #[test]
2819    fn a_walk_and_the_access_through_it_read_the_capability_of_what_it_walked_off() {
2820        // One capability for the whole walk, and it is the base's rather than the derived
2821        // pointer's. That is the cheap answer and it is also the right one: asking about an
2822        // interior pointer means recovering whichever object the plane says that address is in,
2823        // and for a pointer that has already run off the end of its own object that is somebody
2824        // else's, which is a bounds check that passes where it should refuse.
2825        let mut names = Interner::new();
2826        let i32_ = Type::int(32);
2827        let mut func = Func::new(
2828            names.intern("through"),
2829            Signature::new().with_params(&[Type::PTR, Type::int(64)]).with_returns(&[i32_]),
2830        );
2831        let entry = func.create_block();
2832        let p = func.append_param(entry, Type::PTR);
2833        let n = func.append_param(entry, Type::int(64));
2834
2835        let info = MemInfo {
2836            size: 4,
2837            align: 4,
2838            order: MemOrder::NotAtomic,
2839            tbaa: None,
2840            owns: 0,
2841            restrict: Restrict::NONE,
2842        };
2843        let mut b = Builder::new(&mut func, entry);
2844        let args = b.func().push_values(&[p, n]);
2845        let moved = b.value(InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
2846        let args = b.func().push_values(&[moved]);
2847        let extra = Extra::Mem(b.func().add_mem(info));
2848        let read = b.value(InstData { args, extra, ..InstData::new(Opcode::Load) }, i32_);
2849        b.ret(&[read]);
2850
2851        let (module, plane) = planed(&mut names, "through.c");
2852        insert(&mut func, &plane, 8, Subobject::Off, Promise::Off, Races::Off);
2853
2854        let printed = print_func(&module, &func, &names);
2855        assert_eq!(printed.matches("cap_of").count(), 1, "{printed}");
2856        assert!(printed.contains("check_deriv %2, %0, %4, %3\n"), "{printed}");
2857        assert!(printed.contains("check_bounds %2, %4, size 4, align 4\n"), "{printed}");
2858    }
2859
2860    #[test]
2861    fn a_pointer_read_out_of_memory_takes_its_capability_at_the_read() {
2862        // Out of the slot beside the word it came from, which is a `cap_load` and not a `cap_of`.
2863        // Reading the pointer and reading its capability is one event, so the producer sits behind
2864        // the load the way the fallback used to, and what changed is which producer it is rather
2865        // than where it goes.
2866        //
2867        // The container's capability is the first operand, because the slot is found from the
2868        // object the word lives in, and the one the outer access already took is the one used. So
2869        // this function asks the plane once, for the parameter, and the pointer it reads through
2870        // that parameter costs a slot read instead of a second walk.
2871        let mut names = Interner::new();
2872        let i32_ = Type::int(32);
2873        let mut func = Func::new(
2874            names.intern("indirect"),
2875            Signature::new().with_params(&[Type::PTR]).with_returns(&[i32_]),
2876        );
2877        let entry = func.create_block();
2878        let p = func.append_param(entry, Type::PTR);
2879
2880        let mut b = Builder::new(&mut func, entry);
2881        let args = b.func().push_values(&[p]);
2882        let info = MemInfo {
2883            size: 0,
2884            align: 8,
2885            order: MemOrder::NotAtomic,
2886            tbaa: None,
2887            owns: 0,
2888            restrict: Restrict::NONE,
2889        };
2890        let extra = Extra::Mem(b.func().add_mem(info));
2891        let held = b.value(InstData { args, extra, ..InstData::new(Opcode::Load) }, Type::PTR);
2892        let args = b.func().push_values(&[held]);
2893        let info = MemInfo { size: 4, align: 4, ..info };
2894        let extra = Extra::Mem(b.func().add_mem(info));
2895        let read = b.value(InstData { args, extra, ..InstData::new(Opcode::Load) }, i32_);
2896        b.ret(&[read]);
2897
2898        let (module, plane) = planed(&mut names, "indirect.c");
2899        insert(&mut func, &plane, 8, Subobject::Off, Promise::Off, Races::Off);
2900
2901        let printed = print_func(&module, &func, &names);
2902        assert!(
2903            printed.contains("%2 = load %0, align 8\n    %3 = cap_load %1, %0, %2\n"),
2904            "{printed}"
2905        );
2906        assert!(printed.contains("check_bounds %3, %2, size 4, align 4\n"), "{printed}");
2907        assert_eq!(printed.matches("cap_of").count(), 1, "{printed}");
2908    }
2909
2910    #[test]
2911    fn what_it_produces_is_a_function_the_verifier_believes() {
2912        // The point of inserting checks as IR is that everything downstream may treat them as
2913        // IR, which is only true if the result is a module the verifier accepts.
2914        let mut names = Interner::new();
2915        let mut func = one_of_each(&mut names);
2916        let (module, plane) = planed(&mut names, "both.c");
2917        insert(&mut func, &plane, 8, Subobject::Off, Promise::Off, Races::Off);
2918
2919        if let Err(errors) = verify_func(&module, &func, &names) {
2920            panic!("that was expected to be believed: {errors:#?}");
2921        }
2922    }
2923
2924    #[test]
2925    fn every_definition_in_a_module_is_walked_and_the_declarations_are_not() {
2926        let mut names = Interner::new();
2927        let one = one_of_each(&mut names);
2928        let mut two = one_of_each(&mut names);
2929        two.name = names.intern("other");
2930        // A declaration of a function defined somewhere else. There is no body to put a check in
2931        // and reaching for one would be a crash rather than a wrong answer.
2932        let declared = Func::new(
2933            names.intern("elsewhere"),
2934            Signature::new().with_params(&[Type::PTR]).with_returns(&[Type::int(32)]),
2935        );
2936
2937        let mut module = Module::new(names.intern("two.c"), &target());
2938        module.add_func(one);
2939        module.add_func(two);
2940        module.add_func(declared);
2941
2942        assert_eq!(
2943            run(&mut module, Subobject::Off, Promise::Off, Races::Off),
2944            Counts { checked: 4, live: 4, judged: 2, wrote: 2, filled: 2, ..Counts::default() }
2945        );
2946        if let Err(errors) = rucc_ir::verify(&module, &names) {
2947            panic!("that was expected to be believed: {errors:#?}");
2948        }
2949    }
2950
2951    #[test]
2952    fn a_function_with_no_accesses_is_left_alone() {
2953        let mut names = Interner::new();
2954        let i32_ = Type::int(32);
2955        let mut func = Func::new(names.intern("nothing"), Signature::new().with_returns(&[i32_]));
2956        let entry = func.create_block();
2957        let mut b = Builder::new(&mut func, entry);
2958        let zero = b.iconst(i32_, 0);
2959        b.ret(&[zero]);
2960
2961        let (_module, plane) = planed(&mut names, "nothing.c");
2962        let before = func.counts();
2963        assert_eq!(
2964            insert(&mut func, &plane, 8, Subobject::Off, Promise::Off, Races::Off),
2965            Counts::default()
2966        );
2967        assert_eq!(func.counts(), before);
2968    }
2969}