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