Skip to main content

rucc_opt/
alias.rs

1//! Alias analysis: whether two memory references can touch the same byte.
2//!
3//! Design: `spec/optimizer/08-alias-analysis.md`, with the switch section 41.9 of
4//! `spec/optimizer/41-correctness.md` asks for.
5//!
6//! # The one question
7//!
8//! There is one primitive and everything else is built on it. Given two memory references, can
9//! they touch the same byte. Every memory optimization documents 16, 17 and 27 will bring is
10//! gated on it, an answer that is too conservative costs performance quietly and forever, and an
11//! answer that is too aggressive miscompiles in the way that produces a bug report three years
12//! later from somebody whose program worked on every other compiler.
13//!
14//! So the answer is an [`Answer`], which is either [`Answer::May`] or a no carrying the layer
15//! that concluded it. Section 8.5 asks for that and it is the best decision in spec 9.4: a
16//! miscompilation from an alias bug is localised to one layer rather than bisected across the
17//! whole analysis, the layer statistics come for free, and a user asking why something was not
18//! optimized gets a real answer. It costs one byte in a return value that was going in a
19//! register anyway.
20//!
21//! # The layers, in the order they run
22//!
23//! Section 8.2 lists six. This is the first five, and the order they run in is load bearing.
24//!
25//! **Two volatile accesses conflict**, and that is checked before anything else. Not may
26//! conflict: they are treated as conflicting so that neither can be moved across the other,
27//! which is what `volatile` is for.
28//!
29//! **Distinct storage and provenance**, layers 1 and 2, are one walk here because the IR names
30//! the object a pointer came from. [`origin`] chases a pointer back through `ptr_add` and
31//! `bitcast` to the `alloca` or the `global_addr` it started at, and two different objects never
32//! alias. GCC gets the same answer less directly, out of tracking base declarations through a
33//! tree walk. This layer answers a startling fraction of the queries real code asks and it is
34//! the only one `-O1` needs.
35//!
36//! **Offsets**, layer 4, run next and only for two references to the same object, and running
37//! them before the type-based layer rather than after is the whole of what makes union type
38//! punning work. Writing through one member of a union and reading another is two accesses to
39//! one object at overlapping offsets with unrelated types. It is undefined in ISO C, it is
40//! defined by GCC, an enormous amount of real C rests on it, and a layer that asked about the
41//! types first would answer no and miscompile all of it. GCC's comment at
42//! `gcc/tree-ssa-alias.cc:2461` says exactly this and rucc reproduces the ordering rather than
43//! the accident.
44//!
45//! **Escape**, which section 8.4 counts as the cheapest interprocedural-flavoured fact there is:
46//! a local whose address never leaves the function is not the object some pointer this function
47//! cannot follow is pointing at, and it is not one a call can touch either.
48//!
49//! **`restrict`**, layer 5, is two small numbers on the access and one comparison, which is all
50//! GCC's is. See [`rucc_ir::Restrict`], including the trap.
51//!
52//! **Type-based aliasing**, layer 3, runs last of the five. Two accesses conflict when one of
53//! their type nodes is at or above the other in the metadata tree, so an access through `char`,
54//! whose node is the root, conflicts with everything. `-fno-strict-aliasing` is one condition in
55//! one place, [`Options::strict_aliasing`], which is what section 41.9 means by the flag having
56//! to actually work.
57//!
58//! Layer 6 is points-to, and it is not here. It is a module-wide fixed point rather than a fact
59//! the IR already carries, section 8.3 has an open question about which solver it should be, and
60//! section 8.6 is emphatic that provenance and points-to are different things that must not be
61//! confused. So [`Origin`] is provenance, there is no points-to type for it to be converted
62//! into, and the solver lands separately with the constraint generator split out from it the way
63//! GCC 16 split its own.
64//!
65//! # What the front end still owes this
66//!
67//! Layer 3 is fed. `rucc_lower::aliasing` builds the tree and every load and every store an access
68//! through a C type becomes carries the node for that type, keyed on a canonical spelling rather
69//! than on the order the walk met it, which is what section 8.2 asks for so that document 35's LTO
70//! does not silently gain disambiguations when two modules are merged. The tree is one level deep:
71//! `char` is the root and every other scalar hangs under it, so a struct member is not yet
72//! separated from the struct it is in. A member of a union carries the root rather than the node
73//! for its own type, which this layer would not have needed, since layer 4 runs first and settles
74//! it. It is there for the type plane, which has no layer 4, and it costs this layer nothing but a
75//! disambiguation between a union member and an unrelated object of a different scalar type.
76//!
77//! Layer 5 is fed for the accesses that go through a `restrict` parameter, which is where the
78//! qualifier is nearly always written and which the front end works out. A `restrict`
79//! pointer declared inside a block does not carry one yet, which is tamnd/rucc#970, and neither
80//! does an access through a pointer that came out of memory, which is not a question about names
81//! and never will be.
82
83use std::cell::OnceCell;
84use std::collections::HashSet;
85
86use rucc_base::Symbol;
87use rucc_ir::{
88    AttrSet, Attrs, Def, Extra, Flags, Func, Imm, Inst, MemInfo, Meta, Opcode, Restrict, Type,
89    Value,
90};
91
92use crate::modref::Summaries;
93use crate::outside::Outside;
94
95/// How far back through address arithmetic a pointer is chased before the answer is given up on.
96///
97/// The chain from an `alloca` to the address a load uses is two or three instructions in
98/// anything a person writes. The limit is here so that a generated function with a thousand
99/// `ptr_add`s in a row costs a bounded amount, and giving up produces an unknown origin, which
100/// is the conservative answer rather than a wrong one.
101const CHASE_LIMIT: u32 = 64;
102
103/// How far up the metadata tree a type node is followed.
104///
105/// The tree is shallow, and the verifier is what would catch one that is not a tree at all. The
106/// limit means a query cannot fail to terminate even on a module that came from somewhere the
107/// verifier has not run.
108const TREE_LIMIT: u32 = 32;
109
110/// Which rule concluded that two references cannot touch the same byte.
111///
112/// Section 8.5. This is the whole point of the return type being an enum rather than a boolean.
113#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
114pub enum Reason {
115    /// They are references to two different objects, which is layers 1 and 2 together.
116    Distinct,
117    /// One is a local whose address never leaves the function and the other is not that local.
118    Escape,
119    /// They are references to one object at offsets whose byte ranges do not overlap.
120    Offset,
121    /// Their type nodes are in different parts of the tree, so no object has both types.
122    Tbaa,
123    /// They are in one `restrict` scope through different `restrict` pointers.
124    Restrict,
125    /// The callee's attributes say it does not touch memory this way.
126    Attribute,
127    /// What the callee does to memory was worked out from its body, and it does not do this.
128    Summary,
129    /// One of them touches only the safety planes, which nothing the program can name reaches.
130    Plane,
131}
132
133impl Reason {
134    /// Every reason, which is what a report walks.
135    pub const ALL: [Self; 8] = [
136        Self::Distinct,
137        Self::Escape,
138        Self::Offset,
139        Self::Tbaa,
140        Self::Restrict,
141        Self::Attribute,
142        Self::Summary,
143        Self::Plane,
144    ];
145
146    /// How many there are, which is the width of a [`Counts`].
147    pub const COUNT: usize = Self::ALL.len();
148
149    /// Where this sits in [`Reason::ALL`].
150    #[must_use]
151    pub const fn index(self) -> usize {
152        match self {
153            Self::Distinct => 0,
154            Self::Escape => 1,
155            Self::Offset => 2,
156            Self::Tbaa => 3,
157            Self::Restrict => 4,
158            Self::Attribute => 5,
159            Self::Summary => 6,
160            Self::Plane => 7,
161        }
162    }
163
164    /// The one word `-fdump-alias` prints for it.
165    #[must_use]
166    pub const fn name(self) -> &'static str {
167        match self {
168            Self::Distinct => "distinct",
169            Self::Escape => "escape",
170            Self::Offset => "offset",
171            Self::Tbaa => "tbaa",
172            Self::Restrict => "restrict",
173            Self::Attribute => "attribute",
174            Self::Summary => "summary",
175            Self::Plane => "plane",
176        }
177    }
178
179    /// The sentence a user gets when they ask why something was not optimized.
180    #[must_use]
181    pub const fn describe(self) -> &'static str {
182        match self {
183            Self::Distinct => "they are two different objects",
184            Self::Escape => "the address of that local never leaves this function",
185            Self::Offset => "they are parts of one object that do not overlap",
186            Self::Tbaa => "no object has both of those types",
187            Self::Restrict => "restrict says those two pointers do not reach the same object",
188            Self::Attribute => "the callee is declared not to touch memory that way",
189            Self::Summary => "what that callee does to memory was worked out, and it does not",
190            Self::Plane => "that one touches only the planes, which the program cannot name",
191        }
192    }
193}
194
195/// What the analysis answers.
196#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
197pub enum Answer {
198    /// They may touch the same byte, which is the answer whenever nothing proved otherwise.
199    May,
200    /// They cannot, and this is the rule that says so.
201    No(Reason),
202}
203
204impl Answer {
205    /// Whether this is a no.
206    #[must_use]
207    pub const fn is_no(self) -> bool {
208        matches!(self, Self::No(_))
209    }
210
211    /// The rule behind a no.
212    #[must_use]
213    pub const fn reason(self) -> Option<Reason> {
214        match self {
215            Self::No(reason) => Some(reason),
216            Self::May => None,
217        }
218    }
219}
220
221/// What the command line turns off.
222///
223/// One field, because there is one flag. Section 41.9 asks that `-fno-strict-aliasing` disable
224/// the type-based component and nothing else, exactly as `gcc/alias.cc:420` and :556 do, and the
225/// way to make that true rather than hoped for is to have one condition in one place.
226#[derive(Clone, Copy, Debug, PartialEq, Eq)]
227pub struct Options {
228    /// Whether the type-based layer is consulted. GCC's default at `-O2` is on and rucc matches
229    /// it, so `-fno-strict-aliasing` is what clears this.
230    pub strict_aliasing: bool,
231}
232
233impl Default for Options {
234    fn default() -> Self {
235        Self { strict_aliasing: true }
236    }
237}
238
239/// Where a pointer came from, as far as this function can tell.
240///
241/// This is provenance and it is not points-to. Provenance says which object a pointer was
242/// derived from, which the IR knows locally and cheaply. Points-to says which objects a pointer
243/// might hold at run time, which needs a module-wide fixed point. Section 8.6 lists confusing
244/// the two as one of the ways this analysis goes wrong, so there is no conversion between them
245/// and there is no points-to type here at all.
246#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
247pub enum Origin {
248    /// An `alloca` in this function, which is storage nothing outside it knew about until the
249    /// address was handed out.
250    Local(Inst),
251    /// A named object, by the symbol its address was taken by.
252    Global(Symbol),
253    /// An address this function cannot follow any further back: a parameter, something loaded
254    /// out of memory, what a call returned, or an integer turned into a pointer.
255    Unknown(Value),
256}
257
258impl Origin {
259    /// Whether this names an object rather than an address of unknown origin.
260    #[must_use]
261    pub const fn is_object(self) -> bool {
262        matches!(self, Self::Local(_) | Self::Global(_))
263    }
264}
265
266/// Where a pointer came from, and how many bytes past the start of it the pointer is.
267///
268/// The offset is `None` when the walk passed arithmetic whose amount is not a constant, which
269/// costs the offset layer and nothing else: the origin is still the origin, because adding an
270/// unknown number of bytes to a pointer does not move it to a different object.
271#[must_use]
272pub fn origin(func: &Func, mut value: Value) -> (Origin, Option<i64>) {
273    let mut offset = Some(0i64);
274    for _ in 0..CHASE_LIMIT {
275        let Def::Result { inst, .. } = func[value].def else {
276            // A block parameter, which is where the address arrived from somewhere else.
277            return (Origin::Unknown(value), offset);
278        };
279        let data = func[inst];
280        match data.opcode {
281            Opcode::Alloca => return (Origin::Local(inst), offset),
282            Opcode::GlobalAddr => {
283                let Extra::Symbol(name) = data.extra else {
284                    return (Origin::Unknown(value), offset);
285                };
286                return (Origin::Global(name), offset);
287            }
288            Opcode::PtrAdd => {
289                let args = &func[data.args];
290                let (base, by) = (args[0], args[1]);
291                offset = offset
292                    .and_then(|so_far| Some((so_far, constant(func, by)?)))
293                    .and_then(|(so_far, by)| so_far.checked_add(by));
294                value = base;
295            }
296            // A cast between two pointers moves nothing, so it is the same address as its
297            // operand and the walk goes through it.
298            Opcode::Bitcast => value = func[data.args][0],
299            // A capability is about the object its pointer is in, so the object at the end of
300            // this walk is the same object either way. It is not an address and nothing loads
301            // through one, so this is never the origin of an access. What it is for is the
302            // escape analysis: once the walk gets here, a use of a capability that could let the
303            // object out is a use this function can see, and [`keeps_address`] is what decides
304            // which uses those are.
305            Opcode::CapOf => value = func[data.args][0],
306            _ => return (Origin::Unknown(value), offset),
307        }
308    }
309    (Origin::Unknown(value), None)
310}
311
312/// One memory reference: which bytes an instruction touches and what it says about them.
313///
314/// Built by [`Alias::reads`] and [`Alias::writes`] rather than by hand, so that the size of a
315/// load comes from the type it produces and the size of a `memcpy` comes from its access, and no
316/// caller has to remember which.
317#[derive(Clone, Copy, Debug, PartialEq, Eq)]
318pub struct Access {
319    /// The object, or the address the walk stopped at.
320    pub origin: Origin,
321    /// How many bytes past the start of that the reference begins, when the walk could tell.
322    pub offset: Option<i64>,
323    /// How many bytes it covers, when that is known.
324    pub size: Option<u64>,
325    /// The type node the front end attached, if it attached one.
326    pub tbaa: Option<Meta>,
327    /// The `restrict` scope the access is in.
328    pub restrict: Restrict,
329    /// Whether the access is `volatile`.
330    pub volatile: bool,
331}
332
333impl Access {
334    /// A reference to somewhere behind this address, of unknown size and with nothing known
335    /// about its type.
336    ///
337    /// This is what a pointer handed to a call is: the call touches something through it and
338    /// there is nothing on the call saying how much.
339    #[must_use]
340    pub fn through(func: &Func, pointer: Value) -> Self {
341        let (origin, offset) = origin(func, pointer);
342        Self { origin, offset, size: None, tbaa: None, restrict: Restrict::NONE, volatile: false }
343    }
344
345    /// The half-open range of bytes this covers within its origin, when both ends are known.
346    #[must_use]
347    pub fn range(&self) -> Option<(i128, i128)> {
348        let (offset, size) = (self.offset?, self.size?);
349        let start = i128::from(offset);
350        Some((start, start + i128::from(size)))
351    }
352}
353
354/// Which of a function's locals had their address leave it.
355///
356/// Section 8.4 calls this the most valuable interprocedural-flavoured fact available without
357/// interprocedural analysis, because it covers every local a C programmer takes the address of
358/// only to pass one field of, and because a local whose address never escaped cannot be touched
359/// by any call at all.
360///
361/// Section 8.6 says how it goes wrong, which is by missing an escape, and what to do about it.
362/// [`keeps_address`] is a whitelist: an opcode it does not name lets the address out, and so
363/// does an opcode added to the IR after this was written. A blacklist would mean the next person
364/// to add an opcode introduces a miscompilation without touching this file.
365#[derive(Clone, Debug, Default)]
366pub struct Escapes {
367    escaped: HashSet<Inst>,
368}
369
370impl Escapes {
371    /// Works out which locals of this function escaped it.
372    #[must_use]
373    pub fn of(func: &Func) -> Self {
374        Self::with(func, |_, _| false)
375    }
376
377    /// The same, given what the functions this one calls do to what they are handed.
378    ///
379    /// Section 34.6 calls this the upgrade the mod and ref summary makes possible: the question
380    /// goes from does the address leave this function to does the address leave this function
381    /// given what the callees do. An address handed to a call is an address gone as far as
382    /// [`Escapes::of`] is concerned, and that is most of what a C program does with the address
383    /// of a local, so a callee whose summary says it keeps nothing takes a whole class of locals
384    /// out of the escaped set and every question about them afterwards is answered.
385    ///
386    /// Note what this does to the reader of the answer. The invariant that a call reaching the
387    /// escape layer of the oracle cannot have been handed the address does not hold any more, so
388    /// that layer asks whether this call was handed it rather than assuming not.
389    #[must_use]
390    pub fn knowing(func: &Func, summaries: &Summaries) -> Self {
391        Self::with(func, |inst, index| {
392            summaries.at(func, inst).is_some_and(|summary| !summary.param(index).escapes)
393        })
394    }
395
396    /// The same, where `kept` says which operands of which calls hand the address to something
397    /// that does not let it out. For [`crate::modref`], whose own answers are still moving while
398    /// it asks and which therefore cannot hand over a finished [`Summaries`].
399    #[must_use]
400    pub fn with(func: &Func, kept: impl Fn(Inst, usize) -> bool) -> Self {
401        let mut escaped = HashSet::new();
402        for block in func.blocks() {
403            for inst in func.insts(block) {
404                let data = func[inst];
405                for (index, &arg) in func[data.args].iter().enumerate() {
406                    if keeps_address(data.opcode, index) || kept(inst, index) {
407                        continue;
408                    }
409                    if let (Origin::Local(local), _) = origin(func, arg) {
410                        escaped.insert(local);
411                    }
412                }
413                // What a branch passes to a block parameter, which is where an address stops
414                // being one this function can follow back to anything.
415                for call in func.successors(inst) {
416                    for &arg in &func[call.args] {
417                        if let (Origin::Local(local), _) = origin(func, arg) {
418                            escaped.insert(local);
419                        }
420                    }
421                }
422            }
423        }
424        Self { escaped }
425    }
426
427    /// Whether the address of this `alloca` left the function.
428    #[must_use]
429    pub fn escaped(&self, local: Inst) -> bool {
430        self.escaped.contains(&local)
431    }
432
433    /// How many locals escaped.
434    #[must_use]
435    pub fn count(&self) -> usize {
436        self.escaped.len()
437    }
438}
439
440/// Whether a use of a pointer at this operand leaves the address inside the function.
441///
442/// A whitelist, per section 8.6, and the reason it is written this way is in [`Escapes`].
443#[must_use]
444pub const fn keeps_address(opcode: Opcode, index: usize) -> bool {
445    match (opcode, index) {
446        // Dereferenced, and the address itself goes nowhere.
447        (Opcode::Load | Opcode::AtomicLoad, 0)
448        | (Opcode::Store | Opcode::AtomicStore, 1)
449        | (Opcode::AtomicRmw | Opcode::Cmpxchg, 0)
450        | (Opcode::Memcpy | Opcode::Memmove, 0 | 1)
451        | (Opcode::Memset | Opcode::Prefetch, 0) => true,
452        // Copied, and the copy's own uses are walked in their turn, because the walk in
453        // [`origin`] goes back through both of these.
454        (Opcode::PtrAdd | Opcode::Bitcast, 0) => true,
455        // Comparing two addresses neither reads them nor keeps them. Note that the answer may
456        // not travel the other way: see [`rucc_ir::Restrict::disjoint`].
457        (Opcode::ICmp, 0 | 1) => true,
458        // A plane access takes the address as the row to look up and not as somewhere to put it.
459        // [`Opcode::touches_only_planes`] is the argument, and what matters here is the last part
460        // of it: the storage these reach is the runtime's, and no name in the program reaches one,
461        // so nothing the program can run afterwards can get at the object through what one of them
462        // did. Every operand, because every pointer one of them takes is a locator.
463        (op, _) if op.touches_only_planes() => true,
464        // The aux pair reaches the runtime's storage the same way, and the operands named here are
465        // the ones that say which slot rather than the ones that say what goes in it. `cap_load`
466        // takes the capability of the object the word is in and the address of the word.
467        // `cap_store` takes those two as well, and its other two are the pointer being written and
468        // that pointer's own capability, which are the thing being put somewhere a later `cap_load`
469        // can read, so they are not here. `cap_copy` is not here either, and for the opposite
470        // reason: every one of its operands is a locator, so it is above under the arm that takes
471        // all of them.
472        (Opcode::CapLoad | Opcode::CapStore, 0 | 1) => true,
473        // Asking what object a pointer is in is not letting the pointer out. The capability that
474        // comes back is about the object and the walk in [`origin`] goes through it, which is
475        // what makes this safe: a use of the capability that could let the object out is a use
476        // that arrives back here under its own opcode, and the ones that can are not in this
477        // list. `cap_store` is above for the operand that takes a capability as a locator, so what
478        // is left out is its other one, which hands a capability over to be written down, and then
479        // `cap_narrow`, which makes a second capability from it, and `cap_recover`, which is the
480        // road back to a usable pointer.
481        (Opcode::CapOf, 0) => true,
482        _ => false,
483    }
484}
485
486/// How many queries each layer answered.
487///
488/// Section 8.5 says these come for free once the answer carries its reason, and section 8.3
489/// wants them, because a layer that answers no on almost nothing is a layer to delete rather
490/// than a layer to improve.
491#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
492pub struct Counts {
493    queries: u64,
494    answered: [u64; Reason::COUNT],
495}
496
497impl Counts {
498    /// How many queries were asked.
499    #[must_use]
500    pub const fn queries(&self) -> u64 {
501        self.queries
502    }
503
504    /// How many of them this layer answered no.
505    #[must_use]
506    pub const fn answered(&self, reason: Reason) -> u64 {
507        self.answered[reason.index()]
508    }
509
510    /// How many were answered no by any layer.
511    #[must_use]
512    pub fn total(&self) -> u64 {
513        self.answered.iter().sum()
514    }
515}
516
517/// The analysis over one function.
518///
519/// It borrows the function because nearly everything it asks is a question about one, and it
520/// borrows an [`Outside`] because the rest is a question about the module: whether two symbols are
521/// two objects, what a callee is declared to do, where a type node sits in the tree, and how wide
522/// an address is. That is a copy of four module facts rather than the module itself, so that a
523/// pass handed `&mut module[id]` can still build this. See [`crate::outside`] for why.
524///
525/// The escape analysis is run the first time a query needs it and kept, since it is one walk over
526/// the function and every query after that may ask it. Not when this is built, because a pass that
527/// builds one of these and then adds the summaries would walk the function twice and throw the
528/// first answer away, and licm builds one per loop, so on a function with hundreds of loops that
529/// was most of the time spent compiling it.
530#[derive(Debug)]
531pub struct Alias<'a> {
532    func: &'a Func,
533    outside: &'a Outside,
534    summaries: Option<&'a Summaries>,
535    options: Options,
536    escapes: OnceCell<Escapes>,
537    counts: Counts,
538}
539
540impl<'a> Alias<'a> {
541    /// The analysis of this function, with the type-based layer on, which is GCC's `-O2`.
542    #[must_use]
543    pub fn new(func: &'a Func, outside: &'a Outside) -> Self {
544        Self::with(func, outside, Options::default())
545    }
546
547    /// The same, with the type-based layer where the command line left it.
548    #[must_use]
549    pub fn with(func: &'a Func, outside: &'a Outside, options: Options) -> Self {
550        Self {
551            func,
552            outside,
553            summaries: None,
554            options,
555            escapes: OnceCell::new(),
556            counts: Counts::default(),
557        }
558    }
559
560    /// The same, with what the whole module's functions were worked out to do to memory.
561    ///
562    /// Without this the only thing known about a call is what somebody declared about it, which
563    /// for most of a real translation unit is nothing. With it, a call to a function in the same
564    /// unit is answered from what that function's body actually does. See [`crate::modref`].
565    #[must_use]
566    pub fn knowing(mut self, summaries: &'a Summaries) -> Self {
567        self.escapes = OnceCell::new();
568        self.summaries = Some(summaries);
569        self
570    }
571
572    /// Which locals escaped, for a caller that wants the fact on its own.
573    #[must_use]
574    pub fn escapes(&self) -> &Escapes {
575        self.escapes.get_or_init(|| match self.summaries {
576            Some(summaries) => Escapes::knowing(self.func, summaries),
577            None => Escapes::of(self.func),
578        })
579    }
580
581    /// What each layer has answered so far.
582    #[must_use]
583    pub const fn counts(&self) -> &Counts {
584        &self.counts
585    }
586
587    /// The bytes this instruction reads, if it reads any.
588    #[must_use]
589    pub fn reads(&self, inst: Inst) -> Option<Access> {
590        let data = self.func[inst];
591        let args = &self.func[data.args];
592        let info = self.mem(inst);
593        let (pointer, size) = match data.opcode {
594            Opcode::Load | Opcode::AtomicLoad => (args[0], self.width(self.result_type(inst)?)),
595            // A copy reads its source, which is its second operand, for the size on the access.
596            // The size is not known where the program works the length out, and an access of no
597            // known size is one every other access here may touch, which is the honest answer.
598            Opcode::Memcpy | Opcode::Memmove => (args[1], self.bytes(inst, info?)),
599            // A read-modify-write reads and writes the same bytes, and the width is the width
600            // of what it operates with.
601            Opcode::AtomicRmw => (args[0], self.width(self.func[args[1]].ty)),
602            Opcode::Cmpxchg => (args[0], self.width(self.func[args[1]].ty)),
603            Opcode::VaObject => (args[0], Some(info?.size)),
604            _ => return None,
605        };
606        Some(self.access(pointer, size, info, data.flags))
607    }
608
609    /// How many bytes a bulk operation covers, where that is a number at all.
610    ///
611    /// One whose length the program works out has no number here, and `None` is what an access of
612    /// unknown size is written as everywhere else in this file. It matters that this is not the
613    /// payload's zero: a zero byte access is one nothing overlaps, so reading the payload on this
614    /// shape would say a copy touches nothing rather than that it may touch anything.
615    fn bytes(&self, inst: Inst, info: MemInfo) -> Option<u64> {
616        match self.func.bulk(inst) {
617            Some(bulk) if bulk.length.is_some() => None,
618            _ => Some(info.size),
619        }
620    }
621
622    /// The bytes this instruction writes, if it writes any.
623    #[must_use]
624    pub fn writes(&self, inst: Inst) -> Option<Access> {
625        let data = self.func[inst];
626        let args = &self.func[data.args];
627        let info = self.mem(inst);
628        let (pointer, size) = match data.opcode {
629            Opcode::Store | Opcode::AtomicStore => (args[1], self.width(self.func[args[0]].ty)),
630            Opcode::Memcpy | Opcode::Memmove | Opcode::Memset => (args[0], self.bytes(inst, info?)),
631            Opcode::AtomicRmw | Opcode::Cmpxchg => (args[0], self.width(self.func[args[1]].ty)),
632            _ => return None,
633        };
634        Some(self.access(pointer, size, info, data.flags))
635    }
636
637    /// Whether these two references can touch the same byte.
638    pub fn query(&mut self, a: &Access, b: &Access) -> Answer {
639        self.counts.queries += 1;
640        let answer = self.decide(a, b);
641        if let Answer::No(reason) = answer {
642            self.counts.answered[reason.index()] += 1;
643        }
644        answer
645    }
646
647    /// Whether this call can write the bytes the reference covers.
648    ///
649    /// GCC's `call_may_clobber_ref_p_1`. Three things answer it and they are asked in that
650    /// order: the escape analysis, which is the cheap one and needs nothing outside this
651    /// function; the attributes a C programmer already wrote, which are section 8.4's; and the
652    /// mod and ref summaries of document 34, which are what the same three questions look like
653    /// when the callee's body is read rather than taken on trust. Without the last of those the
654    /// honest answer for anything whose address escaped was yes, and `crate::modref` is where it
655    /// stopped being. All three are in `Alias::decide_call`, which is also what
656    /// [`Alias::read_by`] asks.
657    pub fn clobbered_by(&mut self, reference: &Access, call: Inst) -> Answer {
658        self.touched_by(reference, call, true)
659    }
660
661    /// Whether this call can read them.
662    ///
663    /// GCC's `ref_maybe_used_by_call_p_1`, and the same argument as [`Alias::clobbered_by`].
664    pub fn read_by(&mut self, reference: &Access, call: Inst) -> Answer {
665        self.touched_by(reference, call, false)
666    }
667
668    // The layers.
669
670    fn decide(&self, a: &Access, b: &Access) -> Answer {
671        // Section 8.1, and it is first because every layer below would be glad to say no.
672        // Treating two volatile accesses as conflicting is what stops either being moved across
673        // the other, which is the whole of what `volatile` promises.
674        if a.volatile && b.volatile {
675            return Answer::May;
676        }
677
678        // Two objects this function can name. Different objects never alias, and for one object
679        // the offsets settle it on their own.
680        //
681        // The type-based layer is deliberately not reached from here, and that ordering is what
682        // makes union type punning work: writing one member and reading another is two accesses
683        // to one object at overlapping offsets whose types are unrelated, and asking about the
684        // types first would answer no.
685        if a.origin.is_object() && b.origin.is_object() {
686            if self.distinct(a.origin, b.origin) {
687                return Answer::No(Reason::Distinct);
688            }
689            if a.origin == b.origin {
690                return by_offset(a, b);
691            }
692            return Answer::May;
693        }
694
695        // A local whose address never left the function is not what an address this function
696        // cannot follow is pointing at, whatever it is pointing at.
697        if let Some(local) = self.private(a).or_else(|| self.private(b)) {
698            let _ = local;
699            return Answer::No(Reason::Escape);
700        }
701
702        if a.restrict.disjoint(b.restrict) {
703            return Answer::No(Reason::Restrict);
704        }
705
706        if self.options.strict_aliasing {
707            if let (Some(one), Some(other)) = (a.tbaa, b.tbaa) {
708                if !self.types_conflict(one, other) {
709                    return Answer::No(Reason::Tbaa);
710                }
711            }
712        }
713
714        // Two references through one address this function cannot follow, at offsets it can.
715        if a.origin == b.origin {
716            return by_offset(a, b);
717        }
718
719        Answer::May
720    }
721
722    /// The local one of these is a reference to, when it is one nothing outside can reach and
723    /// the other reference is not to it.
724    fn private(&self, reference: &Access) -> Option<Inst> {
725        match reference.origin {
726            Origin::Local(local) if !self.escapes().escaped(local) => Some(local),
727            _ => None,
728        }
729    }
730
731    /// Whether one of this call's operands is the address of that local.
732    ///
733    /// Only for a local that did not escape, where it is the difference between the one call that
734    /// was handed the address and every other call in the function.
735    fn handed(&self, local: Inst, call: Inst) -> bool {
736        self.func[self.func[call].args]
737            .iter()
738            .any(|&arg| matches!(origin(self.func, arg).0, Origin::Local(it) if it == local))
739    }
740
741    /// Whether these two origins are two objects.
742    fn distinct(&self, a: Origin, b: Origin) -> bool {
743        match (a, b) {
744            (Origin::Local(one), Origin::Local(other)) => one != other,
745            // Fresh storage this function made is not any named object.
746            (Origin::Local(_), Origin::Global(_)) | (Origin::Global(_), Origin::Local(_)) => true,
747            (Origin::Global(one), Origin::Global(other)) => {
748                one != other && self.one_object(one) && self.one_object(other)
749            }
750            _ => false,
751        }
752    }
753
754    /// Whether this symbol is a name for an object no other name in the module also names.
755    ///
756    /// An `alias` or an `ifunc` is exactly a second name for something, so two different symbols
757    /// can be one object and the rule that two objects do not alias does not reach them. A name
758    /// the module does not have at all is treated the same way, because something is wrong and
759    /// the conservative answer is the one to be wrong in the direction of.
760    fn one_object(&self, name: Symbol) -> bool {
761        self.outside.one_object(name)
762    }
763
764    /// Whether two type nodes can describe the same byte.
765    ///
766    /// They can when one is at or above the other in the tree, which is what makes an access
767    /// through `char` conflict with everything: `char`'s node is the root and every other node
768    /// hangs below it. Two nodes in different parts of the tree describe no object in common.
769    fn types_conflict(&self, one: Meta, other: Meta) -> bool {
770        self.at_or_below(one, other) || self.at_or_below(other, one)
771    }
772
773    /// Whether `node` is `ancestor` or hangs below it.
774    fn at_or_below(&self, mut node: Meta, ancestor: Meta) -> bool {
775        for _ in 0..TREE_LIMIT {
776            if node == ancestor {
777                return true;
778            }
779            match self.outside.parent(node) {
780                Some(up) => node = up,
781                None => return false,
782            }
783        }
784        // A tree deeper than the limit, or a cycle the verifier would have turned down. Either
785        // way the answer that cannot be wrong is that they conflict.
786        true
787    }
788
789    fn touched_by(&mut self, reference: &Access, call: Inst, writing: bool) -> Answer {
790        self.counts.queries += 1;
791        let answer = self.decide_call(reference, call, writing);
792        if let Answer::No(reason) = answer {
793            self.counts.answered[reason.index()] += 1;
794        }
795        answer
796    }
797
798    fn decide_call(&self, reference: &Access, call: Inst, writing: bool) -> Answer {
799        // Not always a call. The memory chain sends everything that touches memory without an
800        // access saying what through here, and the safety instrumentation is most of that: a check
801        // reads a plane and a `meta_` writes one. Neither is memory the program can name, so
802        // neither is what this reference covers, and [`Opcode::touches_only_planes`] is the whole
803        // argument. It is first because it is a match on an opcode and the layers under it are not.
804        if self.func[call].opcode.touches_only_planes() {
805            return Answer::No(Reason::Plane);
806        }
807
808        // A `setjmp` marker is not a call and the escape argument under this one does not reach it,
809        // so it has to be turned away before that argument is made. See
810        // [`Opcode::is_jump_marker`] for why, and what it costs to get this wrong is a store
811        // forwarded over the marker to a load the jump was the whole reason for.
812        if self.func[call].opcode.is_jump_marker() {
813            return Answer::May;
814        }
815
816        // Everything a call reaches, it reaches through an address, and an object whose address
817        // never left this function is not one it has. The second half used to be free: reaching
818        // here meant the address was not handed to this call either, because that would have been
819        // an escape. [`Escapes::knowing`] is what took it away, since a local handed to a callee
820        // that keeps nothing no longer counts as escaped, so the question gets asked outright.
821        if let Some(local) = self.private(reference) {
822            if !self.handed(local, call) {
823                return Answer::No(Reason::Escape);
824            }
825        }
826
827        let Some(attrs) = self.callee(call) else {
828            return Answer::May;
829        };
830        // `const` reads no memory and writes none. `pure` may read and does not write.
831        if attrs.set.contains(AttrSet::READNONE)
832            || (writing && attrs.set.contains(AttrSet::READONLY))
833        {
834            return Answer::No(Reason::Attribute);
835        }
836
837        // Touching nothing except through the pointers it was passed. Every one of those is a
838        // reference of its own, and if none of them can reach these bytes then neither can the
839        // call. The reading is the non-transitive one the attribute's own documentation gives,
840        // which is what makes this sound without a points-to solver behind it: what the callee
841        // may reach by following a pointer it found in the memory it was passed is memory it
842        // was passed.
843        if attrs.set.contains(AttrSet::ARGMEM_ONLY) {
844            let args = &self.func[self.func[call].args];
845            let mut all = true;
846            for &arg in args {
847                if !self.func[arg].ty.is_ptr() {
848                    continue;
849                }
850                let through = Access::through(self.func, arg);
851                all &= self.decide(reference, &through).is_no();
852            }
853            if all {
854                return Answer::No(Reason::Attribute);
855            }
856        }
857
858        // The same three questions again, this time answered from the callee's body rather than
859        // from what somebody wrote above it. Below the declarations because a declaration is a
860        // promise the caller was told to rely on, and a body that does less than it promised is
861        // still reached here.
862        if let Some(summary) = self.summaries.and_then(|known| known.at(self.func, call)) {
863            if summary.touches_nothing() || (writing && summary.writes_nothing()) {
864                return Answer::No(Reason::Summary);
865            }
866            // Everything it touched, it reached through an argument. Unlike the attribute above
867            // this is worked out rather than asserted, and the walk that worked it out gave up on
868            // any address it could not follow back to a parameter, so a callee that follows a
869            // pointer out of the memory it was handed is not one that reaches here.
870            if summary.only_through_arguments() {
871                let args = &self.func[self.func[call].args];
872                let mut all = true;
873                for (at, &arg) in args.iter().enumerate() {
874                    if !self.func[arg].ty.is_ptr() {
875                        continue;
876                    }
877                    // And not every argument, only the ones it does this to. A callee that reads
878                    // one array and writes another is one whose write cannot be the read of the
879                    // array it only reads, which is the thing an attribute cannot say.
880                    let touch = summary.param(at);
881                    let reached =
882                        if writing { touch.effect.writes() } else { touch.effect.reads() };
883                    if !reached {
884                        continue;
885                    }
886                    let through = Access::through(self.func, arg);
887                    all &= self.decide(reference, &through).is_no();
888                }
889                if all {
890                    return Answer::No(Reason::Summary);
891                }
892            }
893        }
894
895        Answer::May
896    }
897
898    // Reading the instruction.
899
900    /// What the callee of a direct call is declared to be, for a call whose callee the module
901    /// has. An indirect call and a callee from nowhere both give nothing.
902    fn callee(&self, call: Inst) -> Option<Attrs> {
903        let Extra::Call(info) = self.func[call].extra else {
904            return None;
905        };
906        let name = self.func[info].callee?;
907        self.outside.attrs(name)
908    }
909
910    fn mem(&self, inst: Inst) -> Option<MemInfo> {
911        match self.func[inst].extra {
912            Extra::Mem(info) | Extra::Rmw(_, info) => Some(self.func[info]),
913            Extra::VaObject(object) => Some(self.func[self.func[object].mem]),
914            _ => None,
915        }
916    }
917
918    fn result_type(&self, inst: Inst) -> Option<Type> {
919        self.func[inst].results().next().map(|value| self.func[value].ty)
920    }
921
922    fn access(
923        &self,
924        pointer: Value,
925        size: Option<u64>,
926        info: Option<MemInfo>,
927        flags: Flags,
928    ) -> Access {
929        let (origin, offset) = origin(self.func, pointer);
930        Access {
931            origin,
932            offset,
933            size,
934            tbaa: info.and_then(|info| info.tbaa),
935            restrict: info.map_or(Restrict::NONE, |info| info.restrict),
936            volatile: flags.contains(Flags::VOLATILE),
937        }
938    }
939
940    /// How many bytes a value of this type takes, which for an address is the target's answer
941    /// and not the type's.
942    fn width(&self, ty: Type) -> Option<u64> {
943        if ty.is_ptr() {
944            return self.outside.pointer_bytes();
945        }
946        let bits = u64::from(ty.bits()) * u64::from(ty.lanes());
947        (bits > 0).then(|| bits.div_ceil(8))
948    }
949}
950
951/// Layer 4: one object, two byte ranges.
952fn by_offset(a: &Access, b: &Access) -> Answer {
953    let (Some((a_start, a_end)), Some((b_start, b_end))) = (a.range(), b.range()) else {
954        return Answer::May;
955    };
956    if a_end <= b_start || b_end <= a_start {
957        return Answer::No(Reason::Offset);
958    }
959    Answer::May
960}
961
962/// The value of an integer constant, as a byte count.
963fn constant(func: &Func, value: Value) -> Option<i64> {
964    let Def::Result { inst, .. } = func[value].def else {
965        return None;
966    };
967    let data = func[inst];
968    if data.opcode != Opcode::IConst {
969        return None;
970    }
971    let Extra::Imm(imm) = data.extra else {
972        return None;
973    };
974    i64::try_from(Imm::signed(func[imm], func[value].ty)).ok()
975}
976
977#[cfg(test)]
978mod tests {
979    use rucc_base::{Interner, Symbol};
980    use rucc_ir::{
981        AttrSet, Attrs, Builder, CallInfo, Extra, Flags, Func, Global, InstData, IntPred, MemInfo,
982        MemOrder, MetaNode, Module, Opcode, Pic, Restrict, Signature, TbaaNode, Type, Value,
983    };
984
985    use crate::callgraph::CallGraph;
986    use crate::modref::{Summaries, summarize};
987    use rucc_target::{TargetInfo, Triple};
988
989    use super::*;
990
991    /// A module for the host-shaped target, and the interner its names are in.
992    fn module(names: &mut Interner) -> Module {
993        let target = TargetInfo::new("x86_64-unknown-linux-gnu".parse::<Triple>().unwrap());
994        Module::new(names.intern("t.c"), &target)
995    }
996
997    /// A function taking those parameters, with an entry block and nothing in it.
998    fn func(names: &mut Interner, params: &[Type]) -> Func {
999        let mut func = Func::new(names.intern("f"), Signature::new().with_params(params));
1000        let entry = func.create_block();
1001        for &ty in params {
1002            func.append_param(entry, ty);
1003        }
1004        func
1005    }
1006
1007    /// A builder appending to the entry block, which is where every test here puts everything.
1008    fn builder(func: &mut Func) -> Builder<'_> {
1009        let entry = func.entry().expect("the function has an entry block");
1010        Builder::new(func, entry)
1011    }
1012
1013    fn param(func: &Func, index: usize) -> Value {
1014        let entry = func.entry().expect("the function has an entry block");
1015        func[entry].params[index]
1016    }
1017
1018    fn plain(align: u32) -> MemInfo {
1019        MemInfo {
1020            size: 0,
1021            align,
1022            order: MemOrder::NotAtomic,
1023            tbaa: None,
1024            owns: 0,
1025            restrict: Restrict::NONE,
1026        }
1027    }
1028
1029    fn sized(size: u64, align: u32) -> MemInfo {
1030        MemInfo { size, ..plain(align) }
1031    }
1032
1033    /// An `alloca` of that many bytes in the entry block.
1034    fn local(build: &mut Builder<'_>, size: u64) -> Value {
1035        let mem = build.func().add_mem(sized(size, 8));
1036        build.value(InstData { extra: Extra::Mem(mem), ..InstData::new(Opcode::Alloca) }, Type::PTR)
1037    }
1038
1039    /// That address, moved on by a constant number of bytes.
1040    fn at(build: &mut Builder<'_>, base: Value, offset: i64) -> Value {
1041        let by = build.iconst(Type::int(64), i128::from(offset));
1042        build.binary(Opcode::PtrAdd, base, by, Flags::NONE)
1043    }
1044
1045    /// The address of a global of that name, declared in the module as it goes.
1046    fn global(build: &mut Builder<'_>, module: &mut Module, name: Symbol) -> Value {
1047        module.add_global(Global::new(name, 16, 8));
1048        build.value(
1049            InstData { extra: Extra::Symbol(name), ..InstData::new(Opcode::GlobalAddr) },
1050            Type::PTR,
1051        )
1052    }
1053
1054    #[test]
1055    fn two_different_locals_are_two_objects() {
1056        let mut names = Interner::new();
1057        let module = module(&mut names);
1058        let mut f = func(&mut names, &[]);
1059        let mut build = builder(&mut f);
1060        let one = local(&mut build, 16);
1061        let other = local(&mut build, 16);
1062        let read = build.load(Type::int(32), one, plain(4), Flags::NONE);
1063        build.store(read, other, plain(4), Flags::NONE);
1064        build.ret(&[]);
1065
1066        let outside = Outside::of(&module);
1067        let mut alias = Alias::new(&f, &outside);
1068        let (a, b) = two(&alias, &f);
1069        assert_eq!(alias.query(&a, &b), Answer::No(Reason::Distinct));
1070        assert_eq!(alias.counts().answered(Reason::Distinct), 1);
1071        assert_eq!(alias.counts().queries(), 1);
1072    }
1073
1074    /// The reference the first load in the function reads and the one the first store writes.
1075    fn two(alias: &Alias<'_>, func: &Func) -> (Access, Access) {
1076        let mut read = None;
1077        let mut written = None;
1078        for block in func.blocks() {
1079            for inst in func.insts(block) {
1080                if read.is_none() {
1081                    read = alias.reads(inst);
1082                }
1083                if written.is_none() {
1084                    written = alias.writes(inst);
1085                }
1086            }
1087        }
1088        (read.expect("a read"), written.expect("a write"))
1089    }
1090
1091    #[test]
1092    fn a_local_and_a_global_are_two_objects() {
1093        let mut names = Interner::new();
1094        let mut module = module(&mut names);
1095        let x = names.intern("x");
1096        let mut f = func(&mut names, &[]);
1097        let mut build = builder(&mut f);
1098        let one = local(&mut build, 16);
1099        let other = global(&mut build, &mut module, x);
1100        let read = build.load(Type::int(32), one, plain(4), Flags::NONE);
1101        build.store(read, other, plain(4), Flags::NONE);
1102        build.ret(&[]);
1103
1104        let outside = Outside::of(&module);
1105        let mut alias = Alias::new(&f, &outside);
1106        let (a, b) = two(&alias, &f);
1107        assert_eq!(alias.query(&a, &b), Answer::No(Reason::Distinct));
1108    }
1109
1110    #[test]
1111    fn two_different_globals_are_two_objects() {
1112        let mut names = Interner::new();
1113        let mut module = module(&mut names);
1114        let (x, y) = (names.intern("x"), names.intern("y"));
1115        let mut f = func(&mut names, &[]);
1116        let mut build = builder(&mut f);
1117        let one = global(&mut build, &mut module, x);
1118        let other = global(&mut build, &mut module, y);
1119        let read = build.load(Type::int(32), one, plain(4), Flags::NONE);
1120        build.store(read, other, plain(4), Flags::NONE);
1121        build.ret(&[]);
1122
1123        let outside = Outside::of(&module);
1124        let mut alias = Alias::new(&f, &outside);
1125        let (a, b) = two(&alias, &f);
1126        assert_eq!(alias.query(&a, &b), Answer::No(Reason::Distinct));
1127    }
1128
1129    #[test]
1130    fn a_global_the_module_does_not_have_is_not_argued_about() {
1131        // Nothing should produce this, and if something does, the answer that cannot be wrong
1132        // is that the two may alias.
1133        let mut names = Interner::new();
1134        let mut module = module(&mut names);
1135        let (x, y) = (names.intern("x"), names.intern("y"));
1136        let mut f = func(&mut names, &[]);
1137        let mut build = builder(&mut f);
1138        let one = global(&mut build, &mut module, x);
1139        let other = build.value(
1140            InstData { extra: Extra::Symbol(y), ..InstData::new(Opcode::GlobalAddr) },
1141            Type::PTR,
1142        );
1143        let read = build.load(Type::int(32), one, plain(4), Flags::NONE);
1144        build.store(read, other, plain(4), Flags::NONE);
1145        build.ret(&[]);
1146
1147        let outside = Outside::of(&module);
1148        let mut alias = Alias::new(&f, &outside);
1149        let (a, b) = two(&alias, &f);
1150        assert_eq!(alias.query(&a, &b), Answer::May);
1151    }
1152
1153    #[test]
1154    fn two_parts_of_one_object_that_do_not_overlap_are_disjoint() {
1155        let mut names = Interner::new();
1156        let module = module(&mut names);
1157        let mut f = func(&mut names, &[]);
1158        let mut build = builder(&mut f);
1159        let object = local(&mut build, 16);
1160        let first = at(&mut build, object, 0);
1161        let second = at(&mut build, object, 4);
1162        let read = build.load(Type::int(32), first, plain(4), Flags::NONE);
1163        build.store(read, second, plain(4), Flags::NONE);
1164        build.ret(&[]);
1165
1166        let outside = Outside::of(&module);
1167        let mut alias = Alias::new(&f, &outside);
1168        let (a, b) = two(&alias, &f);
1169        assert_eq!(alias.query(&a, &b), Answer::No(Reason::Offset));
1170    }
1171
1172    #[test]
1173    fn two_parts_of_one_object_that_do_overlap_are_not() {
1174        let mut names = Interner::new();
1175        let module = module(&mut names);
1176        let mut f = func(&mut names, &[]);
1177        let mut build = builder(&mut f);
1178        let object = local(&mut build, 16);
1179        let first = at(&mut build, object, 0);
1180        let second = at(&mut build, object, 2);
1181        let read = build.load(Type::int(32), first, plain(4), Flags::NONE);
1182        build.store(read, second, plain(4), Flags::NONE);
1183        build.ret(&[]);
1184
1185        let outside = Outside::of(&module);
1186        let mut alias = Alias::new(&f, &outside);
1187        let (a, b) = two(&alias, &f);
1188        assert_eq!(alias.query(&a, &b), Answer::May);
1189    }
1190
1191    #[test]
1192    fn an_offset_nobody_knows_gives_up_the_offset_and_keeps_the_object() {
1193        let mut names = Interner::new();
1194        let module = module(&mut names);
1195        let mut f = func(&mut names, &[Type::int(64)]);
1196        let n = param(&f, 0);
1197        let mut build = builder(&mut f);
1198        let object = local(&mut build, 16);
1199        let somewhere = build.binary(Opcode::PtrAdd, object, n, Flags::NONE);
1200        let read = build.load(Type::int(32), somewhere, plain(4), Flags::NONE);
1201        build.store(read, object, plain(4), Flags::NONE);
1202        build.ret(&[]);
1203
1204        let outside = Outside::of(&module);
1205        let mut alias = Alias::new(&f, &outside);
1206        let (a, b) = two(&alias, &f);
1207        assert_eq!(a.origin, b.origin, "both are still that one object");
1208        assert_eq!(a.offset, None);
1209        assert_eq!(alias.query(&a, &b), Answer::May);
1210    }
1211
1212    #[test]
1213    fn a_local_whose_address_stays_here_is_not_what_a_parameter_points_at() {
1214        let mut names = Interner::new();
1215        let module = module(&mut names);
1216        let mut f = func(&mut names, &[Type::PTR]);
1217        let outside = param(&f, 0);
1218        let mut build = builder(&mut f);
1219        let object = local(&mut build, 16);
1220        let read = build.load(Type::int(32), object, plain(4), Flags::NONE);
1221        build.store(read, outside, plain(4), Flags::NONE);
1222        build.ret(&[]);
1223
1224        let outside = Outside::of(&module);
1225        let mut alias = Alias::new(&f, &outside);
1226        assert_eq!(alias.escapes().count(), 0);
1227        let (a, b) = two(&alias, &f);
1228        assert_eq!(alias.query(&a, &b), Answer::No(Reason::Escape));
1229    }
1230
1231    #[test]
1232    fn a_local_whose_address_was_stored_somewhere_is() {
1233        let mut names = Interner::new();
1234        let module = module(&mut names);
1235        let mut f = func(&mut names, &[Type::PTR]);
1236        let outside = param(&f, 0);
1237        let mut build = builder(&mut f);
1238        let object = local(&mut build, 16);
1239        // The address itself is written out through a pointer this function did not make, and
1240        // from here anything can reach the object.
1241        build.store(object, outside, plain(8), Flags::NONE);
1242        let read = build.load(Type::int(32), object, plain(4), Flags::NONE);
1243        build.store(read, outside, plain(4), Flags::NONE);
1244        build.ret(&[]);
1245
1246        let outside = Outside::of(&module);
1247        let mut alias = Alias::new(&f, &outside);
1248        assert_eq!(alias.escapes().count(), 1);
1249        let read = first(&f, Opcode::Load);
1250        let write = last(&f, Opcode::Store);
1251        let a = alias.reads(read).unwrap();
1252        let b = alias.writes(write).unwrap();
1253        assert_eq!(alias.query(&a, &b), Answer::May);
1254    }
1255
1256    fn first(func: &Func, opcode: Opcode) -> Inst {
1257        func.blocks()
1258            .flat_map(|block| func.insts(block))
1259            .find(|&inst| func[inst].opcode == opcode)
1260            .expect("an instruction with that opcode")
1261    }
1262
1263    fn last(func: &Func, opcode: Opcode) -> Inst {
1264        func.blocks()
1265            .flat_map(|block| func.insts(block))
1266            .filter(|&inst| func[inst].opcode == opcode)
1267            .last()
1268            .expect("an instruction with that opcode")
1269    }
1270
1271    #[test]
1272    fn an_address_carried_through_a_block_parameter_has_left_the_function() {
1273        let mut names = Interner::new();
1274        let module = module(&mut names);
1275        let mut f = func(&mut names, &[]);
1276        let start = f.entry().expect("an entry block");
1277        let next = f.create_block();
1278        f.append_param(next, Type::PTR);
1279
1280        let mut build = Builder::new(&mut f, start);
1281        let object = local(&mut build, 16);
1282        build.jump(next, &[object]);
1283        let mut build = Builder::new(&mut f, next);
1284        build.ret(&[]);
1285
1286        let outside = Outside::of(&module);
1287        let alias = Alias::new(&f, &outside);
1288        assert!(alias.escapes().escaped(first(&f, Opcode::Alloca)));
1289    }
1290
1291    #[test]
1292    fn comparing_two_addresses_does_not_let_either_of_them_out() {
1293        let mut names = Interner::new();
1294        let module = module(&mut names);
1295        let mut f = func(&mut names, &[Type::PTR]);
1296        let outside = param(&f, 0);
1297        let mut build = builder(&mut f);
1298        let object = local(&mut build, 16);
1299        build.icmp(IntPred::Eq, object, outside);
1300        build.ret(&[]);
1301
1302        let outside = Outside::of(&module);
1303        let alias = Alias::new(&f, &outside);
1304        assert_eq!(alias.escapes().count(), 0);
1305    }
1306
1307    #[test]
1308    fn a_plane_write_on_a_local_does_not_let_its_address_out() {
1309        // What a `-fsafety=detect` build puts beside the first store into a local. The runtime
1310        // writes down that those bytes are now initialised, in storage of its own, and nothing
1311        // the program can run afterwards reaches the local through it.
1312        let mut names = Interner::new();
1313        let module = module(&mut names);
1314        let mut f = func(&mut names, &[]);
1315        let mut build = builder(&mut f);
1316        let object = local(&mut build, 16);
1317        let width = build.iconst(Type::int(64), 16);
1318        let args = build.func().push_values(&[object, width]);
1319        build.inst(InstData { args, ..InstData::new(Opcode::MetaInit) }, &[]);
1320        build.ret(&[]);
1321
1322        let outside = Outside::of(&module);
1323        let alias = Alias::new(&f, &outside);
1324        assert_eq!(alias.escapes().count(), 0);
1325    }
1326
1327    #[test]
1328    fn a_local_that_is_only_asked_about_and_checked_does_not_leave_the_function() {
1329        // What one bounds check on a local lowers to before `rucc_safety::lower` runs, which is
1330        // an `alloca`, the capability of the object it is, and a check that reads a plane. None
1331        // of the three hands the address to anything, and before this was written the `cap_of`
1332        // in the middle of it escaped every local in a program built with the checks on.
1333        let mut names = Interner::new();
1334        let module = module(&mut names);
1335        let mut f = func(&mut names, &[]);
1336        let mut build = builder(&mut f);
1337        let object = local(&mut build, 16);
1338        let args = build.func().push_values(&[object]);
1339        let capability = build.value(InstData { args, ..InstData::new(Opcode::CapOf) }, Type::CAP);
1340        let args = build.func().push_values(&[capability, object]);
1341        build.inst(InstData { args, ..InstData::new(Opcode::CheckBounds) }, &[]);
1342        build.ret(&[]);
1343
1344        let outside = Outside::of(&module);
1345        let alias = Alias::new(&f, &outside);
1346        assert_eq!(alias.escapes().count(), 0);
1347    }
1348
1349    #[test]
1350    fn a_capability_of_a_local_used_for_anything_else_does_let_it_out() {
1351        // The other side of the same line, and the reason the walk goes through `cap_of` rather
1352        // than the whitelist naming it on its own. `cap_narrow` makes a second capability from
1353        // the first, and where that one ends up is not something this walk follows, so the local
1354        // it is about has to count as gone.
1355        let mut names = Interner::new();
1356        let module = module(&mut names);
1357        let mut f = func(&mut names, &[]);
1358        let mut build = builder(&mut f);
1359        let object = local(&mut build, 16);
1360        let args = build.func().push_values(&[object]);
1361        let capability = build.value(InstData { args, ..InstData::new(Opcode::CapOf) }, Type::CAP);
1362        let base = build.iconst(Type::int(64), 0);
1363        let size = build.iconst(Type::int(64), 4);
1364        let args = build.func().push_values(&[capability, base, size]);
1365        build.value(InstData { args, ..InstData::new(Opcode::CapNarrow) }, Type::CAP);
1366        build.ret(&[]);
1367
1368        let outside = Outside::of(&module);
1369        let alias = Alias::new(&f, &outside);
1370        assert!(alias.escapes().escaped(first(&f, Opcode::Alloca)));
1371    }
1372
1373    #[test]
1374    fn the_whitelist_says_yes_to_a_plane_access_at_every_operand() {
1375        // Asked of the list rather than of a program, because what makes this safe is that every
1376        // pointer one of these takes is a row to look up, and a test built out of one instruction
1377        // only ever says it about the operand that instruction has.
1378        for opcode in Opcode::all().filter(|opcode| opcode.touches_only_planes()) {
1379            for index in 0..4 {
1380                assert!(keeps_address(opcode, index), "{opcode} at {index}");
1381            }
1382        }
1383        for opcode in [Opcode::CapNarrow, Opcode::CapRecover] {
1384            assert!(!keeps_address(opcode, 0), "{opcode}");
1385        }
1386        // The two operands of the aux pair that say which slot, against the two of `cap_store`
1387        // that say what goes in it.
1388        for opcode in [Opcode::CapLoad, Opcode::CapStore, Opcode::CapCopy] {
1389            for index in 0..2 {
1390                assert!(keeps_address(opcode, index), "{opcode} at {index}");
1391            }
1392        }
1393        assert!(!keeps_address(Opcode::CapStore, 2));
1394        assert!(!keeps_address(Opcode::CapStore, 3));
1395        assert!(keeps_address(Opcode::CapOf, 0));
1396    }
1397
1398    #[test]
1399    fn a_local_a_pointer_is_written_into_does_not_leave_the_function_for_the_writing_down() {
1400        // What `int *slot; slot = p;` lowers to with the checks on, which is the store and a
1401        // `cap_store` behind it putting the pointer's capability in the slot beside the word. The
1402        // local holding the pointer is the container and it is named twice, once as its capability
1403        // and once as the address of the word, and neither of those is a way to reach it later.
1404        let mut names = Interner::new();
1405        let module = module(&mut names);
1406        let mut f = func(&mut names, &[Type::PTR]);
1407        let written = param(&f, 0);
1408        let mut build = builder(&mut f);
1409        let object = local(&mut build, 8);
1410        let args = build.func().push_values(&[object]);
1411        let container = build.value(InstData { args, ..InstData::new(Opcode::CapOf) }, Type::CAP);
1412        let args = build.func().push_values(&[written]);
1413        let held = build.value(InstData { args, ..InstData::new(Opcode::CapOf) }, Type::CAP);
1414        build.store(written, object, plain(8), Flags::NONE);
1415        let args = build.func().push_values(&[container, object, written, held]);
1416        build.inst(InstData { args, ..InstData::new(Opcode::CapStore) }, &[]);
1417        build.ret(&[]);
1418
1419        let outside = Outside::of(&module);
1420        let alias = Alias::new(&f, &outside);
1421        assert_eq!(alias.escapes().count(), 0);
1422    }
1423
1424    #[test]
1425    fn a_local_whose_capability_is_written_into_a_slot_does_leave_the_function() {
1426        // The other two operands, and the line between them and the two above. Here the local is
1427        // the pointer being stored rather than the object being stored into, so its address goes
1428        // into somebody else's memory and its capability goes into the slot beside it, and both of
1429        // those are places a later `cap_load` in another function can read.
1430        let mut names = Interner::new();
1431        let module = module(&mut names);
1432        let mut f = func(&mut names, &[Type::PTR]);
1433        let into = param(&f, 0);
1434        let mut build = builder(&mut f);
1435        let object = local(&mut build, 8);
1436        let args = build.func().push_values(&[into]);
1437        let container = build.value(InstData { args, ..InstData::new(Opcode::CapOf) }, Type::CAP);
1438        let args = build.func().push_values(&[object]);
1439        let held = build.value(InstData { args, ..InstData::new(Opcode::CapOf) }, Type::CAP);
1440        let args = build.func().push_values(&[container, into, object, held]);
1441        build.inst(InstData { args, ..InstData::new(Opcode::CapStore) }, &[]);
1442        build.ret(&[]);
1443
1444        let outside = Outside::of(&module);
1445        let alias = Alias::new(&f, &outside);
1446        assert!(alias.escapes().escaped(first(&f, Opcode::Alloca)));
1447    }
1448
1449    #[test]
1450    fn an_address_turned_into_a_number_has_left_the_function() {
1451        // The number can be turned back into a pointer anywhere, including in a different
1452        // translation unit, so this is an escape and the whitelist is what makes it one.
1453        let mut names = Interner::new();
1454        let module = module(&mut names);
1455        let mut f = func(&mut names, &[]);
1456        let mut build = builder(&mut f);
1457        let object = local(&mut build, 16);
1458        build.unary(Opcode::PtrToInt, object, Type::int(64));
1459        build.ret(&[]);
1460
1461        let outside = Outside::of(&module);
1462        let alias = Alias::new(&f, &outside);
1463        assert!(alias.escapes().escaped(first(&f, Opcode::Alloca)));
1464    }
1465
1466    #[test]
1467    fn two_restrict_pointers_in_one_scope_do_not_reach_the_same_object() {
1468        let mut names = Interner::new();
1469        let module = module(&mut names);
1470        let mut f = func(&mut names, &[Type::PTR, Type::PTR]);
1471        let (one, other) = (param(&f, 0), param(&f, 1));
1472        let mut build = builder(&mut f);
1473        let mut info = plain(4);
1474        info.restrict = Restrict { clique: 1, base: 1 };
1475        let read = build.load(Type::int(32), one, info, Flags::NONE);
1476        info.restrict = Restrict { clique: 1, base: 2 };
1477        build.store(read, other, info, Flags::NONE);
1478        build.ret(&[]);
1479
1480        let outside = Outside::of(&module);
1481        let mut alias = Alias::new(&f, &outside);
1482        let (a, b) = two(&alias, &f);
1483        assert_eq!(alias.query(&a, &b), Answer::No(Reason::Restrict));
1484    }
1485
1486    #[test]
1487    fn two_restrict_pointers_in_different_scopes_say_nothing_about_each_other() {
1488        let mut names = Interner::new();
1489        let module = module(&mut names);
1490        let mut f = func(&mut names, &[Type::PTR, Type::PTR]);
1491        let (one, other) = (param(&f, 0), param(&f, 1));
1492        let mut build = builder(&mut f);
1493        let mut info = plain(4);
1494        info.restrict = Restrict { clique: 1, base: 1 };
1495        let read = build.load(Type::int(32), one, info, Flags::NONE);
1496        info.restrict = Restrict { clique: 2, base: 1 };
1497        build.store(read, other, info, Flags::NONE);
1498        build.ret(&[]);
1499
1500        let outside = Outside::of(&module);
1501        let mut alias = Alias::new(&f, &outside);
1502        let (a, b) = two(&alias, &f);
1503        assert_eq!(alias.query(&a, &b), Answer::May);
1504    }
1505
1506    /// A module with a `char` root and an `int` and a `float` hanging off it.
1507    fn types(module: &mut Module, names: &mut Interner) -> (Meta, Meta, Meta) {
1508        let root = module.add_meta(MetaNode::Tbaa(TbaaNode {
1509            name: names.intern("char"),
1510            parent: None,
1511            offset: 0,
1512        }));
1513        let int = module.add_meta(MetaNode::Tbaa(TbaaNode {
1514            name: names.intern("int"),
1515            parent: Some(root),
1516            offset: 0,
1517        }));
1518        let float = module.add_meta(MetaNode::Tbaa(TbaaNode {
1519            name: names.intern("float"),
1520            parent: Some(root),
1521            offset: 0,
1522        }));
1523        (root, int, float)
1524    }
1525
1526    #[test]
1527    fn two_unrelated_types_describe_no_object_in_common() {
1528        let mut names = Interner::new();
1529        let mut module = module(&mut names);
1530        let (_, int, float) = types(&mut module, &mut names);
1531        let mut f = func(&mut names, &[Type::PTR, Type::PTR]);
1532        let (one, other) = (param(&f, 0), param(&f, 1));
1533        let mut build = builder(&mut f);
1534        let mut info = plain(4);
1535        info.tbaa = Some(int);
1536        let read = build.load(Type::int(32), one, info, Flags::NONE);
1537        info.tbaa = Some(float);
1538        build.store(read, other, info, Flags::NONE);
1539        build.ret(&[]);
1540
1541        let outside = Outside::of(&module);
1542        let mut alias = Alias::new(&f, &outside);
1543        let (a, b) = two(&alias, &f);
1544        assert_eq!(alias.query(&a, &b), Answer::No(Reason::Tbaa));
1545    }
1546
1547    #[test]
1548    fn an_access_through_char_conflicts_with_everything() {
1549        let mut names = Interner::new();
1550        let mut module = module(&mut names);
1551        let (root, int, _) = types(&mut module, &mut names);
1552        let mut f = func(&mut names, &[Type::PTR, Type::PTR]);
1553        let (one, other) = (param(&f, 0), param(&f, 1));
1554        let mut build = builder(&mut f);
1555        let mut info = plain(4);
1556        info.tbaa = Some(int);
1557        let read = build.load(Type::int(32), one, info, Flags::NONE);
1558        info.tbaa = Some(root);
1559        build.store(read, other, info, Flags::NONE);
1560        build.ret(&[]);
1561
1562        let outside = Outside::of(&module);
1563        let mut alias = Alias::new(&f, &outside);
1564        let (a, b) = two(&alias, &f);
1565        assert_eq!(alias.query(&a, &b), Answer::May);
1566    }
1567
1568    #[test]
1569    fn turning_strict_aliasing_off_turns_off_that_layer_and_no_other() {
1570        let mut names = Interner::new();
1571        let mut module = module(&mut names);
1572        let (_, int, float) = types(&mut module, &mut names);
1573        let mut f = func(&mut names, &[Type::PTR, Type::PTR]);
1574        let (one, other) = (param(&f, 0), param(&f, 1));
1575        let mut build = builder(&mut f);
1576        let mut info = plain(4);
1577        info.tbaa = Some(int);
1578        info.restrict = Restrict { clique: 1, base: 1 };
1579        let read = build.load(Type::int(32), one, info, Flags::NONE);
1580        info.tbaa = Some(float);
1581        info.restrict = Restrict { clique: 1, base: 2 };
1582        build.store(read, other, info, Flags::NONE);
1583        build.ret(&[]);
1584
1585        let options = Options { strict_aliasing: false };
1586        let outside = Outside::of(&module);
1587        let mut alias = Alias::with(&f, &outside, options);
1588        let (a, b) = two(&alias, &f);
1589        // The `restrict` layer still answers, which is the point: the flag is one condition in
1590        // one place and it does not reach anything else.
1591        assert_eq!(alias.query(&a, &b), Answer::No(Reason::Restrict));
1592
1593        let mut without = Alias::with(&f, &outside, options);
1594        let plainer = Access { restrict: Restrict::NONE, ..a };
1595        let other = Access { restrict: Restrict::NONE, ..b };
1596        assert_eq!(without.query(&plainer, &other), Answer::May);
1597
1598        let mut with = Alias::new(&f, &outside);
1599        assert_eq!(with.query(&plainer, &other), Answer::No(Reason::Tbaa));
1600    }
1601
1602    #[test]
1603    fn writing_one_member_of_a_union_and_reading_another_is_one_object() {
1604        // The compatibility fact of section 8.6. Two accesses to one object at the same offset
1605        // with unrelated types, which is `union { int i; float f; }` written as one and read as
1606        // the other. The offset layer runs first, it says they overlap, and the type layer
1607        // never gets to say no. Twenty years of real C rests on this answer.
1608        let mut names = Interner::new();
1609        let mut module = module(&mut names);
1610        let (_, int, float) = types(&mut module, &mut names);
1611        let mut f = func(&mut names, &[]);
1612        let mut build = builder(&mut f);
1613        let object = local(&mut build, 4);
1614        let mut info = plain(4);
1615        info.tbaa = Some(float);
1616        let read = build.load(Type::int(32), object, info, Flags::NONE);
1617        info.tbaa = Some(int);
1618        build.store(read, object, info, Flags::NONE);
1619        build.ret(&[]);
1620
1621        let outside = Outside::of(&module);
1622        let mut alias = Alias::new(&f, &outside);
1623        let (a, b) = two(&alias, &f);
1624        assert_eq!(alias.query(&a, &b), Answer::May);
1625    }
1626
1627    #[test]
1628    fn two_volatile_accesses_conflict_whatever_else_is_true_of_them() {
1629        let mut names = Interner::new();
1630        let module = module(&mut names);
1631        let mut f = func(&mut names, &[]);
1632        let mut build = builder(&mut f);
1633        let one = local(&mut build, 16);
1634        let other = local(&mut build, 16);
1635        let read = build.load(Type::int(32), one, plain(4), Flags::VOLATILE);
1636        build.store(read, other, plain(4), Flags::VOLATILE);
1637        build.ret(&[]);
1638
1639        let outside = Outside::of(&module);
1640        let mut alias = Alias::new(&f, &outside);
1641        let (a, b) = two(&alias, &f);
1642        // Two different objects, and the answer is still that they conflict, because moving
1643        // one volatile access across another is the thing `volatile` exists to forbid.
1644        assert_eq!(alias.query(&a, &b), Answer::May);
1645    }
1646
1647    #[test]
1648    fn one_volatile_access_and_one_ordinary_one_are_argued_about_as_usual() {
1649        let mut names = Interner::new();
1650        let module = module(&mut names);
1651        let mut f = func(&mut names, &[]);
1652        let mut build = builder(&mut f);
1653        let one = local(&mut build, 16);
1654        let other = local(&mut build, 16);
1655        let read = build.load(Type::int(32), one, plain(4), Flags::VOLATILE);
1656        build.store(read, other, plain(4), Flags::NONE);
1657        build.ret(&[]);
1658
1659        let outside = Outside::of(&module);
1660        let mut alias = Alias::new(&f, &outside);
1661        let (a, b) = two(&alias, &f);
1662        assert_eq!(alias.query(&a, &b), Answer::No(Reason::Distinct));
1663    }
1664
1665    #[test]
1666    fn a_copy_reads_its_source_and_writes_its_destination() {
1667        let mut names = Interner::new();
1668        let module = module(&mut names);
1669        let mut f = func(&mut names, &[]);
1670        let mut build = builder(&mut f);
1671        let to = local(&mut build, 16);
1672        let from = local(&mut build, 16);
1673        let mem = build.func().add_mem(sized(16, 8));
1674        let args = build.func().push_values(&[to, from]);
1675        build.inst(InstData { args, extra: Extra::Mem(mem), ..InstData::new(Opcode::Memcpy) }, &[]);
1676        build.ret(&[]);
1677
1678        let outside = Outside::of(&module);
1679        let alias = Alias::new(&f, &outside);
1680        let copy = first(&f, Opcode::Memcpy);
1681        let read = alias.reads(copy).expect("a copy reads");
1682        let written = alias.writes(copy).expect("a copy writes");
1683        assert_eq!(read.size, Some(16));
1684        assert_eq!(written.size, Some(16));
1685        assert_ne!(read.origin, written.origin);
1686    }
1687
1688    #[test]
1689    fn a_copy_of_a_length_the_program_works_out_is_an_access_of_no_known_size() {
1690        let mut names = Interner::new();
1691        let module = module(&mut names);
1692        let mut f = func(&mut names, &[Type::int(64)]);
1693        let length = param(&f, 0);
1694        let mut build = builder(&mut f);
1695        let to = local(&mut build, 16);
1696        let from = local(&mut build, 16);
1697        let mem = build.func().add_mem(sized(0, 8));
1698        let args = build.func().push_values(&[to, from, length]);
1699        build.inst(InstData { args, extra: Extra::Mem(mem), ..InstData::new(Opcode::Memcpy) }, &[]);
1700        build.ret(&[]);
1701
1702        let outside = Outside::of(&module);
1703        let alias = Alias::new(&f, &outside);
1704        let copy = first(&f, Opcode::Memcpy);
1705        // Not `Some(0)`, which is what the payload says and which would make this a copy that
1706        // touches nothing rather than one that may touch anything.
1707        assert_eq!(alias.reads(copy).expect("a copy reads").size, None);
1708        assert_eq!(alias.writes(copy).expect("a copy writes").size, None);
1709    }
1710
1711    /// A call to a function declared with those attributes.
1712    fn call_to(
1713        names: &mut Interner,
1714        module: &mut Module,
1715        f: &mut Func,
1716        attrs: Attrs,
1717        args: &[Value],
1718    ) -> Inst {
1719        let name = names.intern("g");
1720        let params: Vec<Type> = args.iter().map(|_| Type::PTR).collect();
1721        let mut callee = Func::new(name, Signature::new().with_params(&params));
1722        callee.attrs = attrs;
1723        module.add_func(callee);
1724        let signature = f.add_signature(Signature::new().with_params(&params));
1725        let mut build = builder(f);
1726        build.call(name, signature, args)
1727    }
1728
1729    fn attrs(set: AttrSet) -> Attrs {
1730        Attrs { set, ..Attrs::NONE }
1731    }
1732
1733    #[test]
1734    fn a_call_cannot_touch_a_local_whose_address_stayed_here() {
1735        let mut names = Interner::new();
1736        let mut module = module(&mut names);
1737        let mut f = func(&mut names, &[Type::PTR]);
1738        let outside = param(&f, 0);
1739        let mut build = builder(&mut f);
1740        let object = local(&mut build, 16);
1741        let read = build.load(Type::int(32), object, plain(4), Flags::NONE);
1742        let _ = read;
1743        let call = call_to(&mut names, &mut module, &mut f, Attrs::NONE, &[outside]);
1744        let mut build = builder(&mut f);
1745        build.ret(&[]);
1746
1747        let outside = Outside::of(&module);
1748        let mut alias = Alias::new(&f, &outside);
1749        let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1750        assert_eq!(alias.clobbered_by(&reference, call), Answer::No(Reason::Escape));
1751        assert_eq!(alias.read_by(&reference, call), Answer::No(Reason::Escape));
1752    }
1753
1754    #[test]
1755    fn a_call_can_touch_a_local_it_was_handed() {
1756        let mut names = Interner::new();
1757        let mut module = module(&mut names);
1758        let mut f = func(&mut names, &[]);
1759        let mut build = builder(&mut f);
1760        let object = local(&mut build, 16);
1761        build.load(Type::int(32), object, plain(4), Flags::NONE);
1762        let call = call_to(&mut names, &mut module, &mut f, Attrs::NONE, &[object]);
1763        let mut build = builder(&mut f);
1764        build.ret(&[]);
1765
1766        let outside = Outside::of(&module);
1767        let mut alias = Alias::new(&f, &outside);
1768        let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1769        assert_eq!(alias.clobbered_by(&reference, call), Answer::May);
1770    }
1771
1772    #[test]
1773    fn a_setjmp_marker_can_touch_a_local_whose_address_stayed_here() {
1774        // The marker is on the memory chain and it is not a call, so the argument the two tests
1775        // above rest on says nothing about it. Control arrives at what follows it from wherever
1776        // the matching `longjmp` sits, and the jump comes back into this frame, so a local this
1777        // function never let out is exactly what the landing goes on to read.
1778        let mut names = Interner::new();
1779        let mut module = module(&mut names);
1780        let name = names.intern("jmp_buf");
1781        let mut f = func(&mut names, &[]);
1782        let mut build = builder(&mut f);
1783        let object = local(&mut build, 16);
1784        build.load(Type::int(32), object, plain(4), Flags::NONE);
1785        let buffer = global(&mut build, &mut module, name);
1786        let args = build.func().push_values(&[buffer]);
1787        let marker =
1788            build.inst(InstData { args, ..InstData::new(Opcode::SetjmpMarker) }, &[Type::int(32)]);
1789        build.ret(&[]);
1790
1791        let outside = Outside::of(&module);
1792        let mut alias = Alias::new(&f, &outside);
1793        let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1794        assert_eq!(alias.clobbered_by(&reference, marker), Answer::May);
1795        assert_eq!(alias.read_by(&reference, marker), Answer::May);
1796    }
1797
1798    #[test]
1799    fn a_pure_callee_reads_memory_and_writes_none() {
1800        let mut names = Interner::new();
1801        let mut module = module(&mut names);
1802        let mut f = func(&mut names, &[Type::PTR]);
1803        let outside = param(&f, 0);
1804        let mut build = builder(&mut f);
1805        build.load(Type::int(32), outside, plain(4), Flags::NONE);
1806        let call = call_to(&mut names, &mut module, &mut f, attrs(AttrSet::READONLY), &[outside]);
1807        let mut build = builder(&mut f);
1808        build.ret(&[]);
1809
1810        let outside = Outside::of(&module);
1811        let mut alias = Alias::new(&f, &outside);
1812        let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1813        assert_eq!(alias.clobbered_by(&reference, call), Answer::No(Reason::Attribute));
1814        assert_eq!(alias.read_by(&reference, call), Answer::May);
1815    }
1816
1817    #[test]
1818    fn a_plane_write_is_not_a_write_to_the_address_it_names() {
1819        // The one that was costing the memory passes everything on a safety build. `meta_init %p`
1820        // writes the entry the lifetime plane keeps for `%p`, and the oracle reading its operand
1821        // the ordinary way sees a write to exactly the bytes a load of `%p` wants, which is the
1822        // worst possible wrong answer: the instrumentation blocking the optimization of the code
1823        // it was put in to check.
1824        let mut names = Interner::new();
1825        let module = module(&mut names);
1826        let mut f = func(&mut names, &[Type::PTR]);
1827        let outside = param(&f, 0);
1828        let mut build = builder(&mut f);
1829        build.load(Type::int(32), outside, plain(4), Flags::NONE);
1830        let width = build.iconst(Type::int(64), 4);
1831        let args = build.func().push_values(&[outside, width]);
1832        build.inst(InstData { args, ..InstData::new(Opcode::MetaInit) }, &[]);
1833        build.ret(&[]);
1834
1835        let outside = Outside::of(&module);
1836        let mut alias = Alias::new(&f, &outside);
1837        let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1838        let plane = first(&f, Opcode::MetaInit);
1839        assert_eq!(alias.clobbered_by(&reference, plane), Answer::No(Reason::Plane));
1840        // And it does not read it either, so a store the program made is not kept alive by one.
1841        assert_eq!(alias.read_by(&reference, plane), Answer::No(Reason::Plane));
1842    }
1843
1844    #[test]
1845    fn a_check_reads_a_plane_and_not_what_it_is_about() {
1846        // The reading half of the same fact, which is what a walk back over memory runs into
1847        // first: a check between a store and a load of the same address is on the chain, and
1848        // answering `May` for it is a load kept for no reason.
1849        let mut names = Interner::new();
1850        let module = module(&mut names);
1851        let mut f = func(&mut names, &[Type::PTR]);
1852        let outside = param(&f, 0);
1853        let mut build = builder(&mut f);
1854        build.load(Type::int(32), outside, plain(4), Flags::NONE);
1855        let width = build.iconst(Type::int(64), 4);
1856        let args = build.func().push_values(&[outside, width]);
1857        build.inst(InstData { args, ..InstData::new(Opcode::CheckBounds) }, &[]);
1858        build.ret(&[]);
1859
1860        let outside = Outside::of(&module);
1861        let mut alias = Alias::new(&f, &outside);
1862        let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1863        let check = first(&f, Opcode::CheckBounds);
1864        assert_eq!(alias.clobbered_by(&reference, check), Answer::No(Reason::Plane));
1865        assert_eq!(alias.read_by(&reference, check), Answer::No(Reason::Plane));
1866    }
1867
1868    #[test]
1869    fn a_const_callee_touches_no_memory_at_all() {
1870        let mut names = Interner::new();
1871        let mut module = module(&mut names);
1872        let mut f = func(&mut names, &[Type::PTR]);
1873        let outside = param(&f, 0);
1874        let mut build = builder(&mut f);
1875        build.load(Type::int(32), outside, plain(4), Flags::NONE);
1876        let call = call_to(&mut names, &mut module, &mut f, attrs(AttrSet::READNONE), &[outside]);
1877        let mut build = builder(&mut f);
1878        build.ret(&[]);
1879
1880        let outside = Outside::of(&module);
1881        let mut alias = Alias::new(&f, &outside);
1882        let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1883        assert_eq!(alias.clobbered_by(&reference, call), Answer::No(Reason::Attribute));
1884        assert_eq!(alias.read_by(&reference, call), Answer::No(Reason::Attribute));
1885    }
1886
1887    #[test]
1888    fn a_callee_that_touches_only_its_arguments_leaves_a_global_it_was_not_passed_alone() {
1889        let mut names = Interner::new();
1890        let mut module = module(&mut names);
1891        let x = names.intern("x");
1892        let mut f = func(&mut names, &[Type::PTR]);
1893        let outside = param(&f, 0);
1894        let mut build = builder(&mut f);
1895        let object = global(&mut build, &mut module, x);
1896        build.load(Type::int(32), object, plain(4), Flags::NONE);
1897        let call =
1898            call_to(&mut names, &mut module, &mut f, attrs(AttrSet::ARGMEM_ONLY), &[outside]);
1899        let mut build = builder(&mut f);
1900        build.ret(&[]);
1901
1902        let outside = Outside::of(&module);
1903        let mut alias = Alias::new(&f, &outside);
1904        let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1905        // The one pointer it was handed is a parameter of unknown origin, which may be that
1906        // global, so this is the answer that cannot be wrong.
1907        assert_eq!(alias.clobbered_by(&reference, call), Answer::May);
1908    }
1909
1910    #[test]
1911    fn a_callee_that_touches_only_its_arguments_and_was_handed_one_object_leaves_the_other() {
1912        let mut names = Interner::new();
1913        let mut module = module(&mut names);
1914        let (x, y) = (names.intern("x"), names.intern("y"));
1915        let mut f = func(&mut names, &[]);
1916        let mut build = builder(&mut f);
1917        let watched = global(&mut build, &mut module, x);
1918        let handed = global(&mut build, &mut module, y);
1919        build.load(Type::int(32), watched, plain(4), Flags::NONE);
1920        let call = call_to(&mut names, &mut module, &mut f, attrs(AttrSet::ARGMEM_ONLY), &[handed]);
1921        let mut build = builder(&mut f);
1922        build.ret(&[]);
1923
1924        let outside = Outside::of(&module);
1925        let mut alias = Alias::new(&f, &outside);
1926        let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1927        assert_eq!(alias.clobbered_by(&reference, call), Answer::No(Reason::Attribute));
1928    }
1929
1930    /// A call to a function of that many pointer parameters whose body is that.
1931    ///
1932    /// Unlike [`call_to`] the callee is defined, which is what gives [`crate::modref`] something
1933    /// to read. Nothing is declared about it, so every answer below comes from the body.
1934    fn call_to_body(
1935        names: &mut Interner,
1936        module: &mut Module,
1937        f: &mut Func,
1938        arity: usize,
1939        body: fn(&mut Builder<'_>, &[Value]),
1940        args: &[Value],
1941    ) -> Inst {
1942        defines(names, module, "g", arity, body);
1943        calls_it(names, f, "g", arity, args)
1944    }
1945
1946    /// Adds a function of that name to the module, with that many pointer parameters and that
1947    /// body.
1948    fn defines(
1949        names: &mut Interner,
1950        module: &mut Module,
1951        called: &str,
1952        arity: usize,
1953        body: fn(&mut Builder<'_>, &[Value]),
1954    ) {
1955        let name = names.intern(called);
1956        let params = vec![Type::PTR; arity];
1957        let mut callee = Func::new(name, Signature::new().with_params(&params));
1958        let entry = callee.create_block();
1959        let got: Vec<Value> = params.iter().map(|&ty| callee.append_param(entry, ty)).collect();
1960        let mut build = Builder::new(&mut callee, entry);
1961        body(&mut build, &got);
1962        module.add_func(callee);
1963    }
1964
1965    /// A call to a function of that name, for a test that wants more than one of them.
1966    fn calls_it(
1967        names: &mut Interner,
1968        f: &mut Func,
1969        called: &str,
1970        arity: usize,
1971        args: &[Value],
1972    ) -> Inst {
1973        let name = names.intern(called);
1974        let signature = f.add_signature(Signature::new().with_params(&vec![Type::PTR; arity]));
1975        let mut build = builder(f);
1976        build.call(name, signature, args)
1977    }
1978
1979    /// What the module's functions were worked out to do to memory.
1980    fn worked_out(module: &Module) -> Summaries {
1981        let mut summaries = Summaries::of_module(module);
1982        summarize(module, &CallGraph::of(module, Pic::Executable), &mut summaries);
1983        summaries
1984    }
1985
1986    /// Comes back and does nothing on the way.
1987    fn body_does_nothing(build: &mut Builder<'_>, _: &[Value]) {
1988        build.ret(&[]);
1989    }
1990
1991    /// Reads four bytes through the first pointer it was handed.
1992    fn body_reads_the_first(build: &mut Builder<'_>, args: &[Value]) {
1993        let value = build.load(Type::int(32), args[0], plain(4), Flags::NONE);
1994        build.ret(&[value]);
1995    }
1996
1997    /// Writes four bytes through the first pointer it was handed and leaves the rest alone.
1998    fn body_writes_the_first(build: &mut Builder<'_>, args: &[Value]) {
1999        let zero = build.iconst(Type::int(32), 0);
2000        build.store(zero, args[0], plain(4), Flags::NONE);
2001        build.ret(&[]);
2002    }
2003
2004    /// Writes the first pointer it was handed down at the second, so the address gets out.
2005    fn body_keeps_the_first(build: &mut Builder<'_>, args: &[Value]) {
2006        build.store(args[0], args[1], plain(8), Flags::NONE);
2007        build.ret(&[]);
2008    }
2009
2010    #[test]
2011    fn a_callee_nobody_declared_anything_about_is_read_out_of_its_body() {
2012        let mut names = Interner::new();
2013        let mut module = module(&mut names);
2014        let x = names.intern("x");
2015        let mut f = func(&mut names, &[]);
2016        let mut build = builder(&mut f);
2017        let object = global(&mut build, &mut module, x);
2018        build.load(Type::int(32), object, plain(4), Flags::NONE);
2019        let call = call_to_body(&mut names, &mut module, &mut f, 0, body_does_nothing, &[]);
2020        let mut build = builder(&mut f);
2021        build.ret(&[]);
2022
2023        let outside = Outside::of(&module);
2024        let reference = Alias::new(&f, &outside).reads(first(&f, Opcode::Load)).unwrap();
2025        // Without the summaries there is nothing to go on, because nobody wrote an attribute.
2026        let mut blind = Alias::new(&f, &outside);
2027        assert_eq!(blind.clobbered_by(&reference, call), Answer::May);
2028
2029        let summaries = worked_out(&module);
2030        let mut alias = Alias::new(&f, &outside).knowing(&summaries);
2031        assert_eq!(alias.clobbered_by(&reference, call), Answer::No(Reason::Summary));
2032        assert_eq!(alias.read_by(&reference, call), Answer::No(Reason::Summary));
2033    }
2034
2035    #[test]
2036    fn a_callee_worked_out_to_write_nothing_clobbers_nothing() {
2037        let mut names = Interner::new();
2038        let mut module = module(&mut names);
2039        let mut f = func(&mut names, &[Type::PTR]);
2040        let handed = param(&f, 0);
2041        let mut build = builder(&mut f);
2042        build.load(Type::int(32), handed, plain(4), Flags::NONE);
2043        let call =
2044            call_to_body(&mut names, &mut module, &mut f, 1, body_reads_the_first, &[handed]);
2045        let mut build = builder(&mut f);
2046        build.ret(&[]);
2047
2048        let outside = Outside::of(&module);
2049        let summaries = worked_out(&module);
2050        let reference = Alias::new(&f, &outside).reads(first(&f, Opcode::Load)).unwrap();
2051        let mut alias = Alias::new(&f, &outside).knowing(&summaries);
2052        assert_eq!(alias.clobbered_by(&reference, call), Answer::No(Reason::Summary));
2053        // It was handed that very object and it does read, so the other question is still open.
2054        assert_eq!(alias.read_by(&reference, call), Answer::May);
2055    }
2056
2057    #[test]
2058    fn a_callee_that_writes_one_of_the_two_it_was_handed_leaves_the_other() {
2059        // The answer no attribute can give. `argmemonly` says the call touched nothing it was not
2060        // handed, and it was handed both of these, so the declaration alone has to say `May`.
2061        let mut names = Interner::new();
2062        let mut module = module(&mut names);
2063        let (x, y) = (names.intern("x"), names.intern("y"));
2064        let mut f = func(&mut names, &[]);
2065        let mut build = builder(&mut f);
2066        let watched = global(&mut build, &mut module, x);
2067        let written = global(&mut build, &mut module, y);
2068        build.load(Type::int(32), watched, plain(4), Flags::NONE);
2069        let call = call_to_body(
2070            &mut names,
2071            &mut module,
2072            &mut f,
2073            2,
2074            body_writes_the_first,
2075            &[written, watched],
2076        );
2077        let mut build = builder(&mut f);
2078        build.ret(&[]);
2079
2080        let outside = Outside::of(&module);
2081        let summaries = worked_out(&module);
2082        let reference = Alias::new(&f, &outside).reads(first(&f, Opcode::Load)).unwrap();
2083        let mut alias = Alias::new(&f, &outside).knowing(&summaries);
2084        assert_eq!(alias.clobbered_by(&reference, call), Answer::No(Reason::Summary));
2085    }
2086
2087    #[test]
2088    fn a_local_lent_to_a_callee_that_keeps_it_not_is_still_private_everywhere_else() {
2089        // Section 34.6's upgrade. Handing the address of a local to a call is what a C program
2090        // does with most of the locals it takes the address of at all, and without a summary of
2091        // the callee that is the end of every question about that local.
2092        let mut names = Interner::new();
2093        let mut module = module(&mut names);
2094        let x = names.intern("x");
2095        let mut f = func(&mut names, &[]);
2096        let mut build = builder(&mut f);
2097        let object = local(&mut build, 16);
2098        let elsewhere = global(&mut build, &mut module, x);
2099        build.load(Type::int(32), object, plain(4), Flags::NONE);
2100        defines(&mut names, &mut module, "g", 1, body_writes_the_first);
2101        let lent = calls_it(&mut names, &mut f, "g", 1, &[object]);
2102        let other = calls_it(&mut names, &mut f, "g", 1, &[elsewhere]);
2103        let mut build = builder(&mut f);
2104        build.ret(&[]);
2105
2106        let outside = Outside::of(&module);
2107        let reference = Alias::new(&f, &outside).reads(first(&f, Opcode::Load)).unwrap();
2108        // Without the summaries the address went out at the first call and stayed out.
2109        let mut blind = Alias::new(&f, &outside);
2110        assert_eq!(blind.clobbered_by(&reference, lent), Answer::May);
2111        assert_eq!(blind.clobbered_by(&reference, other), Answer::May);
2112
2113        let summaries = worked_out(&module);
2114        let mut alias = Alias::new(&f, &outside).knowing(&summaries);
2115        // The call that was handed it can still have written it, and is asked about rather than
2116        // assumed away, which is the invariant the upgrade took off the escape layer.
2117        assert_eq!(alias.clobbered_by(&reference, lent), Answer::May);
2118        // The one that was not never had the address and never could get it.
2119        assert_eq!(alias.clobbered_by(&reference, other), Answer::No(Reason::Escape));
2120        assert_eq!(alias.escapes().count(), 0);
2121    }
2122
2123    #[test]
2124    fn a_local_written_down_by_a_callee_is_gone_exactly_as_before() {
2125        let mut names = Interner::new();
2126        let mut module = module(&mut names);
2127        let x = names.intern("x");
2128        let mut f = func(&mut names, &[]);
2129        let mut build = builder(&mut f);
2130        let object = local(&mut build, 16);
2131        let elsewhere = global(&mut build, &mut module, x);
2132        build.load(Type::int(32), object, plain(4), Flags::NONE);
2133        defines(&mut names, &mut module, "g", 2, body_keeps_the_first);
2134        defines(&mut names, &mut module, "h", 1, body_does_nothing);
2135        calls_it(&mut names, &mut f, "g", 2, &[object, elsewhere]);
2136        let other = calls_it(&mut names, &mut f, "h", 1, &[elsewhere]);
2137        let mut build = builder(&mut f);
2138        build.ret(&[]);
2139
2140        let outside = Outside::of(&module);
2141        let summaries = worked_out(&module);
2142        let reference = Alias::new(&f, &outside).reads(first(&f, Opcode::Load)).unwrap();
2143        let mut alias = Alias::new(&f, &outside).knowing(&summaries);
2144        // That callee put the address somewhere, so the upgrade does not save it and every
2145        // question about the local is back to the answer that cannot be wrong.
2146        assert_eq!(alias.escapes().count(), 1);
2147        assert_eq!(alias.private(&reference), None);
2148        // `h` touches nothing at all, so it is still answered, just not by the escape layer.
2149        assert_eq!(alias.clobbered_by(&reference, other), Answer::No(Reason::Summary));
2150    }
2151
2152    #[test]
2153    fn a_local_handed_to_a_const_declaration_is_still_gone() {
2154        // `const` says the result comes out of the arguments. It does not say the function did
2155        // not hand one of them back, so the address may be in the caller's hands after the call
2156        // under a name the escape walk cannot follow to this local, and the upgrade has to leave
2157        // this one alone.
2158        let mut names = Interner::new();
2159        let mut module = module(&mut names);
2160        let mut f = func(&mut names, &[]);
2161        let mut build = builder(&mut f);
2162        let object = local(&mut build, 16);
2163        build.load(Type::int(32), object, plain(4), Flags::NONE);
2164        call_to(&mut names, &mut module, &mut f, attrs(AttrSet::READNONE), &[object]);
2165        let mut build = builder(&mut f);
2166        build.ret(&[]);
2167
2168        let outside = Outside::of(&module);
2169        let summaries = worked_out(&module);
2170        let reference = Alias::new(&f, &outside).reads(first(&f, Opcode::Load)).unwrap();
2171        let alias = Alias::new(&f, &outside).knowing(&summaries);
2172        assert_eq!(alias.escapes().count(), 1);
2173        assert_eq!(alias.private(&reference), None);
2174    }
2175
2176    #[test]
2177    fn an_indirect_call_is_not_argued_about() {
2178        let mut names = Interner::new();
2179        let module = module(&mut names);
2180        let mut f = func(&mut names, &[Type::PTR, Type::PTR]);
2181        let (target, outside) = (param(&f, 0), param(&f, 1));
2182        let mut build = builder(&mut f);
2183        build.load(Type::int(32), outside, plain(4), Flags::NONE);
2184        let signature = build.func().add_signature(Signature::new().with_params(&[Type::PTR]));
2185        let varargs = build.func().push_abis(&[]);
2186        let info = build.func().add_call(CallInfo { callee: None, signature, varargs });
2187        let args = build.func().push_values(&[target, outside]);
2188        let call = build.inst(
2189            InstData { args, extra: Extra::Call(info), ..InstData::new(Opcode::CallIndirect) },
2190            &[],
2191        );
2192        build.ret(&[]);
2193
2194        let outside = Outside::of(&module);
2195        let mut alias = Alias::new(&f, &outside);
2196        let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
2197        assert_eq!(alias.clobbered_by(&reference, call), Answer::May);
2198    }
2199
2200    #[test]
2201    fn every_reason_has_a_name_and_a_sentence() {
2202        for reason in Reason::ALL {
2203            assert!(!reason.name().is_empty());
2204            assert!(!reason.describe().is_empty());
2205            assert_eq!(Reason::ALL[reason.index()], reason);
2206        }
2207        assert_eq!(Reason::ALL.len(), Reason::COUNT);
2208        assert_eq!(Answer::No(Reason::Offset).reason(), Some(Reason::Offset));
2209        assert!(Answer::No(Reason::Offset).is_no());
2210        assert_eq!(Answer::May.reason(), None);
2211        assert!(!Answer::May.is_no());
2212    }
2213}