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 not here either, and for the opposite
469        // reason: every one of its operands is a locator, so it is above under the arm that takes
470        // all of them.
471        (Opcode::CapLoad | Opcode::CapStore, 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`. Three things answer it and they are asked in that
643    /// order: the escape analysis, which is the cheap one and needs nothing outside this
644    /// function; the attributes a C programmer already wrote, which are section 8.4's; and the
645    /// mod and ref summaries of document 34, which are what the same three questions look like
646    /// when the callee's body is read rather than taken on trust. Without the last of those the
647    /// honest answer for anything whose address escaped was yes, and `crate::modref` is where it
648    /// stopped being. All three are in `Alias::decide_call`, which is also what
649    /// [`Alias::read_by`] asks.
650    pub fn clobbered_by(&mut self, reference: &Access, call: Inst) -> Answer {
651        self.touched_by(reference, call, true)
652    }
653
654    /// Whether this call can read them.
655    ///
656    /// GCC's `ref_maybe_used_by_call_p_1`, and the same argument as [`Alias::clobbered_by`].
657    pub fn read_by(&mut self, reference: &Access, call: Inst) -> Answer {
658        self.touched_by(reference, call, false)
659    }
660
661    // The layers.
662
663    fn decide(&self, a: &Access, b: &Access) -> Answer {
664        // Section 8.1, and it is first because every layer below would be glad to say no.
665        // Treating two volatile accesses as conflicting is what stops either being moved across
666        // the other, which is the whole of what `volatile` promises.
667        if a.volatile && b.volatile {
668            return Answer::May;
669        }
670
671        // Two objects this function can name. Different objects never alias, and for one object
672        // the offsets settle it on their own.
673        //
674        // The type-based layer is deliberately not reached from here, and that ordering is what
675        // makes union type punning work: writing one member and reading another is two accesses
676        // to one object at overlapping offsets whose types are unrelated, and asking about the
677        // types first would answer no.
678        if a.origin.is_object() && b.origin.is_object() {
679            if self.distinct(a.origin, b.origin) {
680                return Answer::No(Reason::Distinct);
681            }
682            if a.origin == b.origin {
683                return by_offset(a, b);
684            }
685            return Answer::May;
686        }
687
688        // A local whose address never left the function is not what an address this function
689        // cannot follow is pointing at, whatever it is pointing at.
690        if let Some(local) = self.private(a).or_else(|| self.private(b)) {
691            let _ = local;
692            return Answer::No(Reason::Escape);
693        }
694
695        if a.restrict.disjoint(b.restrict) {
696            return Answer::No(Reason::Restrict);
697        }
698
699        if self.options.strict_aliasing {
700            if let (Some(one), Some(other)) = (a.tbaa, b.tbaa) {
701                if !self.types_conflict(one, other) {
702                    return Answer::No(Reason::Tbaa);
703                }
704            }
705        }
706
707        // Two references through one address this function cannot follow, at offsets it can.
708        if a.origin == b.origin {
709            return by_offset(a, b);
710        }
711
712        Answer::May
713    }
714
715    /// The local one of these is a reference to, when it is one nothing outside can reach and
716    /// the other reference is not to it.
717    fn private(&self, reference: &Access) -> Option<Inst> {
718        match reference.origin {
719            Origin::Local(local) if !self.escapes.escaped(local) => Some(local),
720            _ => None,
721        }
722    }
723
724    /// Whether one of this call's operands is the address of that local.
725    ///
726    /// Only for a local that did not escape, where it is the difference between the one call that
727    /// was handed the address and every other call in the function.
728    fn handed(&self, local: Inst, call: Inst) -> bool {
729        self.func[self.func[call].args]
730            .iter()
731            .any(|&arg| matches!(origin(self.func, arg).0, Origin::Local(it) if it == local))
732    }
733
734    /// Whether these two origins are two objects.
735    fn distinct(&self, a: Origin, b: Origin) -> bool {
736        match (a, b) {
737            (Origin::Local(one), Origin::Local(other)) => one != other,
738            // Fresh storage this function made is not any named object.
739            (Origin::Local(_), Origin::Global(_)) | (Origin::Global(_), Origin::Local(_)) => true,
740            (Origin::Global(one), Origin::Global(other)) => {
741                one != other && self.one_object(one) && self.one_object(other)
742            }
743            _ => false,
744        }
745    }
746
747    /// Whether this symbol is a name for an object no other name in the module also names.
748    ///
749    /// An `alias` or an `ifunc` is exactly a second name for something, so two different symbols
750    /// can be one object and the rule that two objects do not alias does not reach them. A name
751    /// the module does not have at all is treated the same way, because something is wrong and
752    /// the conservative answer is the one to be wrong in the direction of.
753    fn one_object(&self, name: Symbol) -> bool {
754        self.outside.one_object(name)
755    }
756
757    /// Whether two type nodes can describe the same byte.
758    ///
759    /// They can when one is at or above the other in the tree, which is what makes an access
760    /// through `char` conflict with everything: `char`'s node is the root and every other node
761    /// hangs below it. Two nodes in different parts of the tree describe no object in common.
762    fn types_conflict(&self, one: Meta, other: Meta) -> bool {
763        self.at_or_below(one, other) || self.at_or_below(other, one)
764    }
765
766    /// Whether `node` is `ancestor` or hangs below it.
767    fn at_or_below(&self, mut node: Meta, ancestor: Meta) -> bool {
768        for _ in 0..TREE_LIMIT {
769            if node == ancestor {
770                return true;
771            }
772            match self.outside.parent(node) {
773                Some(up) => node = up,
774                None => return false,
775            }
776        }
777        // A tree deeper than the limit, or a cycle the verifier would have turned down. Either
778        // way the answer that cannot be wrong is that they conflict.
779        true
780    }
781
782    fn touched_by(&mut self, reference: &Access, call: Inst, writing: bool) -> Answer {
783        self.counts.queries += 1;
784        let answer = self.decide_call(reference, call, writing);
785        if let Answer::No(reason) = answer {
786            self.counts.answered[reason.index()] += 1;
787        }
788        answer
789    }
790
791    fn decide_call(&self, reference: &Access, call: Inst, writing: bool) -> Answer {
792        // Not always a call. The memory chain sends everything that touches memory without an
793        // access saying what through here, and the safety instrumentation is most of that: a check
794        // reads a plane and a `meta_` writes one. Neither is memory the program can name, so
795        // neither is what this reference covers, and [`Opcode::touches_only_planes`] is the whole
796        // argument. It is first because it is a match on an opcode and the layers under it are not.
797        if self.func[call].opcode.touches_only_planes() {
798            return Answer::No(Reason::Plane);
799        }
800
801        // A `setjmp` marker is not a call and the escape argument under this one does not reach it,
802        // so it has to be turned away before that argument is made. See
803        // [`Opcode::is_jump_marker`] for why, and what it costs to get this wrong is a store
804        // forwarded over the marker to a load the jump was the whole reason for.
805        if self.func[call].opcode.is_jump_marker() {
806            return Answer::May;
807        }
808
809        // Everything a call reaches, it reaches through an address, and an object whose address
810        // never left this function is not one it has. The second half used to be free: reaching
811        // here meant the address was not handed to this call either, because that would have been
812        // an escape. [`Escapes::knowing`] is what took it away, since a local handed to a callee
813        // that keeps nothing no longer counts as escaped, so the question gets asked outright.
814        if let Some(local) = self.private(reference) {
815            if !self.handed(local, call) {
816                return Answer::No(Reason::Escape);
817            }
818        }
819
820        let Some(attrs) = self.callee(call) else {
821            return Answer::May;
822        };
823        // `const` reads no memory and writes none. `pure` may read and does not write.
824        if attrs.set.contains(AttrSet::READNONE)
825            || (writing && attrs.set.contains(AttrSet::READONLY))
826        {
827            return Answer::No(Reason::Attribute);
828        }
829
830        // Touching nothing except through the pointers it was passed. Every one of those is a
831        // reference of its own, and if none of them can reach these bytes then neither can the
832        // call. The reading is the non-transitive one the attribute's own documentation gives,
833        // which is what makes this sound without a points-to solver behind it: what the callee
834        // may reach by following a pointer it found in the memory it was passed is memory it
835        // was passed.
836        if attrs.set.contains(AttrSet::ARGMEM_ONLY) {
837            let args = &self.func[self.func[call].args];
838            let mut all = true;
839            for &arg in args {
840                if !self.func[arg].ty.is_ptr() {
841                    continue;
842                }
843                let through = Access::through(self.func, arg);
844                all &= self.decide(reference, &through).is_no();
845            }
846            if all {
847                return Answer::No(Reason::Attribute);
848            }
849        }
850
851        // The same three questions again, this time answered from the callee's body rather than
852        // from what somebody wrote above it. Below the declarations because a declaration is a
853        // promise the caller was told to rely on, and a body that does less than it promised is
854        // still reached here.
855        if let Some(summary) = self.summaries.and_then(|known| known.at(self.func, call)) {
856            if summary.touches_nothing() || (writing && summary.writes_nothing()) {
857                return Answer::No(Reason::Summary);
858            }
859            // Everything it touched, it reached through an argument. Unlike the attribute above
860            // this is worked out rather than asserted, and the walk that worked it out gave up on
861            // any address it could not follow back to a parameter, so a callee that follows a
862            // pointer out of the memory it was handed is not one that reaches here.
863            if summary.only_through_arguments() {
864                let args = &self.func[self.func[call].args];
865                let mut all = true;
866                for (at, &arg) in args.iter().enumerate() {
867                    if !self.func[arg].ty.is_ptr() {
868                        continue;
869                    }
870                    // And not every argument, only the ones it does this to. A callee that reads
871                    // one array and writes another is one whose write cannot be the read of the
872                    // array it only reads, which is the thing an attribute cannot say.
873                    let touch = summary.param(at);
874                    let reached =
875                        if writing { touch.effect.writes() } else { touch.effect.reads() };
876                    if !reached {
877                        continue;
878                    }
879                    let through = Access::through(self.func, arg);
880                    all &= self.decide(reference, &through).is_no();
881                }
882                if all {
883                    return Answer::No(Reason::Summary);
884                }
885            }
886        }
887
888        Answer::May
889    }
890
891    // Reading the instruction.
892
893    /// What the callee of a direct call is declared to be, for a call whose callee the module
894    /// has. An indirect call and a callee from nowhere both give nothing.
895    fn callee(&self, call: Inst) -> Option<Attrs> {
896        let Extra::Call(info) = self.func[call].extra else {
897            return None;
898        };
899        let name = self.func[info].callee?;
900        self.outside.attrs(name)
901    }
902
903    fn mem(&self, inst: Inst) -> Option<MemInfo> {
904        match self.func[inst].extra {
905            Extra::Mem(info) | Extra::Rmw(_, info) => Some(self.func[info]),
906            Extra::VaObject(object) => Some(self.func[self.func[object].mem]),
907            _ => None,
908        }
909    }
910
911    fn result_type(&self, inst: Inst) -> Option<Type> {
912        self.func[inst].results().next().map(|value| self.func[value].ty)
913    }
914
915    fn access(
916        &self,
917        pointer: Value,
918        size: Option<u64>,
919        info: Option<MemInfo>,
920        flags: Flags,
921    ) -> Access {
922        let (origin, offset) = origin(self.func, pointer);
923        Access {
924            origin,
925            offset,
926            size,
927            tbaa: info.and_then(|info| info.tbaa),
928            restrict: info.map_or(Restrict::NONE, |info| info.restrict),
929            volatile: flags.contains(Flags::VOLATILE),
930        }
931    }
932
933    /// How many bytes a value of this type takes, which for an address is the target's answer
934    /// and not the type's.
935    fn width(&self, ty: Type) -> Option<u64> {
936        if ty.is_ptr() {
937            return self.outside.pointer_bytes();
938        }
939        let bits = u64::from(ty.bits()) * u64::from(ty.lanes());
940        (bits > 0).then(|| bits.div_ceil(8))
941    }
942}
943
944/// Layer 4: one object, two byte ranges.
945fn by_offset(a: &Access, b: &Access) -> Answer {
946    let (Some((a_start, a_end)), Some((b_start, b_end))) = (a.range(), b.range()) else {
947        return Answer::May;
948    };
949    if a_end <= b_start || b_end <= a_start {
950        return Answer::No(Reason::Offset);
951    }
952    Answer::May
953}
954
955/// The value of an integer constant, as a byte count.
956fn constant(func: &Func, value: Value) -> Option<i64> {
957    let Def::Result { inst, .. } = func[value].def else {
958        return None;
959    };
960    let data = func[inst];
961    if data.opcode != Opcode::IConst {
962        return None;
963    }
964    let Extra::Imm(imm) = data.extra else {
965        return None;
966    };
967    i64::try_from(Imm::signed(func[imm], func[value].ty)).ok()
968}
969
970#[cfg(test)]
971mod tests {
972    use rucc_base::{Interner, Symbol};
973    use rucc_ir::{
974        AttrSet, Attrs, Builder, CallInfo, Extra, Flags, Func, Global, InstData, IntPred, MemInfo,
975        MemOrder, MetaNode, Module, Opcode, Pic, Restrict, Signature, TbaaNode, Type, Value,
976    };
977
978    use crate::callgraph::CallGraph;
979    use crate::modref::{Summaries, summarize};
980    use rucc_target::{TargetInfo, Triple};
981
982    use super::*;
983
984    /// A module for the host-shaped target, and the interner its names are in.
985    fn module(names: &mut Interner) -> Module {
986        let target = TargetInfo::new("x86_64-unknown-linux-gnu".parse::<Triple>().unwrap());
987        Module::new(names.intern("t.c"), &target)
988    }
989
990    /// A function taking those parameters, with an entry block and nothing in it.
991    fn func(names: &mut Interner, params: &[Type]) -> Func {
992        let mut func = Func::new(names.intern("f"), Signature::new().with_params(params));
993        let entry = func.create_block();
994        for &ty in params {
995            func.append_param(entry, ty);
996        }
997        func
998    }
999
1000    /// A builder appending to the entry block, which is where every test here puts everything.
1001    fn builder(func: &mut Func) -> Builder<'_> {
1002        let entry = func.entry().expect("the function has an entry block");
1003        Builder::new(func, entry)
1004    }
1005
1006    fn param(func: &Func, index: usize) -> Value {
1007        let entry = func.entry().expect("the function has an entry block");
1008        func[entry].params[index]
1009    }
1010
1011    fn plain(align: u32) -> MemInfo {
1012        MemInfo {
1013            size: 0,
1014            align,
1015            order: MemOrder::NotAtomic,
1016            tbaa: None,
1017            owns: 0,
1018            restrict: Restrict::NONE,
1019        }
1020    }
1021
1022    fn sized(size: u64, align: u32) -> MemInfo {
1023        MemInfo { size, ..plain(align) }
1024    }
1025
1026    /// An `alloca` of that many bytes in the entry block.
1027    fn local(build: &mut Builder<'_>, size: u64) -> Value {
1028        let mem = build.func().add_mem(sized(size, 8));
1029        build.value(InstData { extra: Extra::Mem(mem), ..InstData::new(Opcode::Alloca) }, Type::PTR)
1030    }
1031
1032    /// That address, moved on by a constant number of bytes.
1033    fn at(build: &mut Builder<'_>, base: Value, offset: i64) -> Value {
1034        let by = build.iconst(Type::int(64), i128::from(offset));
1035        build.binary(Opcode::PtrAdd, base, by, Flags::NONE)
1036    }
1037
1038    /// The address of a global of that name, declared in the module as it goes.
1039    fn global(build: &mut Builder<'_>, module: &mut Module, name: Symbol) -> Value {
1040        module.add_global(Global::new(name, 16, 8));
1041        build.value(
1042            InstData { extra: Extra::Symbol(name), ..InstData::new(Opcode::GlobalAddr) },
1043            Type::PTR,
1044        )
1045    }
1046
1047    #[test]
1048    fn two_different_locals_are_two_objects() {
1049        let mut names = Interner::new();
1050        let module = module(&mut names);
1051        let mut f = func(&mut names, &[]);
1052        let mut build = builder(&mut f);
1053        let one = local(&mut build, 16);
1054        let other = local(&mut build, 16);
1055        let read = build.load(Type::int(32), one, plain(4), Flags::NONE);
1056        build.store(read, other, plain(4), Flags::NONE);
1057        build.ret(&[]);
1058
1059        let outside = Outside::of(&module);
1060        let mut alias = Alias::new(&f, &outside);
1061        let (a, b) = two(&alias, &f);
1062        assert_eq!(alias.query(&a, &b), Answer::No(Reason::Distinct));
1063        assert_eq!(alias.counts().answered(Reason::Distinct), 1);
1064        assert_eq!(alias.counts().queries(), 1);
1065    }
1066
1067    /// The reference the first load in the function reads and the one the first store writes.
1068    fn two(alias: &Alias<'_>, func: &Func) -> (Access, Access) {
1069        let mut read = None;
1070        let mut written = None;
1071        for block in func.blocks() {
1072            for inst in func.insts(block) {
1073                if read.is_none() {
1074                    read = alias.reads(inst);
1075                }
1076                if written.is_none() {
1077                    written = alias.writes(inst);
1078                }
1079            }
1080        }
1081        (read.expect("a read"), written.expect("a write"))
1082    }
1083
1084    #[test]
1085    fn a_local_and_a_global_are_two_objects() {
1086        let mut names = Interner::new();
1087        let mut module = module(&mut names);
1088        let x = names.intern("x");
1089        let mut f = func(&mut names, &[]);
1090        let mut build = builder(&mut f);
1091        let one = local(&mut build, 16);
1092        let other = global(&mut build, &mut module, x);
1093        let read = build.load(Type::int(32), one, plain(4), Flags::NONE);
1094        build.store(read, other, plain(4), Flags::NONE);
1095        build.ret(&[]);
1096
1097        let outside = Outside::of(&module);
1098        let mut alias = Alias::new(&f, &outside);
1099        let (a, b) = two(&alias, &f);
1100        assert_eq!(alias.query(&a, &b), Answer::No(Reason::Distinct));
1101    }
1102
1103    #[test]
1104    fn two_different_globals_are_two_objects() {
1105        let mut names = Interner::new();
1106        let mut module = module(&mut names);
1107        let (x, y) = (names.intern("x"), names.intern("y"));
1108        let mut f = func(&mut names, &[]);
1109        let mut build = builder(&mut f);
1110        let one = global(&mut build, &mut module, x);
1111        let other = global(&mut build, &mut module, y);
1112        let read = build.load(Type::int(32), one, plain(4), Flags::NONE);
1113        build.store(read, other, plain(4), Flags::NONE);
1114        build.ret(&[]);
1115
1116        let outside = Outside::of(&module);
1117        let mut alias = Alias::new(&f, &outside);
1118        let (a, b) = two(&alias, &f);
1119        assert_eq!(alias.query(&a, &b), Answer::No(Reason::Distinct));
1120    }
1121
1122    #[test]
1123    fn a_global_the_module_does_not_have_is_not_argued_about() {
1124        // Nothing should produce this, and if something does, the answer that cannot be wrong
1125        // is that the two may alias.
1126        let mut names = Interner::new();
1127        let mut module = module(&mut names);
1128        let (x, y) = (names.intern("x"), names.intern("y"));
1129        let mut f = func(&mut names, &[]);
1130        let mut build = builder(&mut f);
1131        let one = global(&mut build, &mut module, x);
1132        let other = build.value(
1133            InstData { extra: Extra::Symbol(y), ..InstData::new(Opcode::GlobalAddr) },
1134            Type::PTR,
1135        );
1136        let read = build.load(Type::int(32), one, plain(4), Flags::NONE);
1137        build.store(read, other, plain(4), Flags::NONE);
1138        build.ret(&[]);
1139
1140        let outside = Outside::of(&module);
1141        let mut alias = Alias::new(&f, &outside);
1142        let (a, b) = two(&alias, &f);
1143        assert_eq!(alias.query(&a, &b), Answer::May);
1144    }
1145
1146    #[test]
1147    fn two_parts_of_one_object_that_do_not_overlap_are_disjoint() {
1148        let mut names = Interner::new();
1149        let module = module(&mut names);
1150        let mut f = func(&mut names, &[]);
1151        let mut build = builder(&mut f);
1152        let object = local(&mut build, 16);
1153        let first = at(&mut build, object, 0);
1154        let second = at(&mut build, object, 4);
1155        let read = build.load(Type::int(32), first, plain(4), Flags::NONE);
1156        build.store(read, second, plain(4), Flags::NONE);
1157        build.ret(&[]);
1158
1159        let outside = Outside::of(&module);
1160        let mut alias = Alias::new(&f, &outside);
1161        let (a, b) = two(&alias, &f);
1162        assert_eq!(alias.query(&a, &b), Answer::No(Reason::Offset));
1163    }
1164
1165    #[test]
1166    fn two_parts_of_one_object_that_do_overlap_are_not() {
1167        let mut names = Interner::new();
1168        let module = module(&mut names);
1169        let mut f = func(&mut names, &[]);
1170        let mut build = builder(&mut f);
1171        let object = local(&mut build, 16);
1172        let first = at(&mut build, object, 0);
1173        let second = at(&mut build, object, 2);
1174        let read = build.load(Type::int(32), first, plain(4), Flags::NONE);
1175        build.store(read, second, plain(4), Flags::NONE);
1176        build.ret(&[]);
1177
1178        let outside = Outside::of(&module);
1179        let mut alias = Alias::new(&f, &outside);
1180        let (a, b) = two(&alias, &f);
1181        assert_eq!(alias.query(&a, &b), Answer::May);
1182    }
1183
1184    #[test]
1185    fn an_offset_nobody_knows_gives_up_the_offset_and_keeps_the_object() {
1186        let mut names = Interner::new();
1187        let module = module(&mut names);
1188        let mut f = func(&mut names, &[Type::int(64)]);
1189        let n = param(&f, 0);
1190        let mut build = builder(&mut f);
1191        let object = local(&mut build, 16);
1192        let somewhere = build.binary(Opcode::PtrAdd, object, n, Flags::NONE);
1193        let read = build.load(Type::int(32), somewhere, plain(4), Flags::NONE);
1194        build.store(read, object, plain(4), Flags::NONE);
1195        build.ret(&[]);
1196
1197        let outside = Outside::of(&module);
1198        let mut alias = Alias::new(&f, &outside);
1199        let (a, b) = two(&alias, &f);
1200        assert_eq!(a.origin, b.origin, "both are still that one object");
1201        assert_eq!(a.offset, None);
1202        assert_eq!(alias.query(&a, &b), Answer::May);
1203    }
1204
1205    #[test]
1206    fn a_local_whose_address_stays_here_is_not_what_a_parameter_points_at() {
1207        let mut names = Interner::new();
1208        let module = module(&mut names);
1209        let mut f = func(&mut names, &[Type::PTR]);
1210        let outside = param(&f, 0);
1211        let mut build = builder(&mut f);
1212        let object = local(&mut build, 16);
1213        let read = build.load(Type::int(32), object, plain(4), Flags::NONE);
1214        build.store(read, outside, plain(4), Flags::NONE);
1215        build.ret(&[]);
1216
1217        let outside = Outside::of(&module);
1218        let mut alias = Alias::new(&f, &outside);
1219        assert_eq!(alias.escapes().count(), 0);
1220        let (a, b) = two(&alias, &f);
1221        assert_eq!(alias.query(&a, &b), Answer::No(Reason::Escape));
1222    }
1223
1224    #[test]
1225    fn a_local_whose_address_was_stored_somewhere_is() {
1226        let mut names = Interner::new();
1227        let module = module(&mut names);
1228        let mut f = func(&mut names, &[Type::PTR]);
1229        let outside = param(&f, 0);
1230        let mut build = builder(&mut f);
1231        let object = local(&mut build, 16);
1232        // The address itself is written out through a pointer this function did not make, and
1233        // from here anything can reach the object.
1234        build.store(object, outside, plain(8), Flags::NONE);
1235        let read = build.load(Type::int(32), object, plain(4), Flags::NONE);
1236        build.store(read, outside, plain(4), Flags::NONE);
1237        build.ret(&[]);
1238
1239        let outside = Outside::of(&module);
1240        let mut alias = Alias::new(&f, &outside);
1241        assert_eq!(alias.escapes().count(), 1);
1242        let read = first(&f, Opcode::Load);
1243        let write = last(&f, Opcode::Store);
1244        let a = alias.reads(read).unwrap();
1245        let b = alias.writes(write).unwrap();
1246        assert_eq!(alias.query(&a, &b), Answer::May);
1247    }
1248
1249    fn first(func: &Func, opcode: Opcode) -> Inst {
1250        func.blocks()
1251            .flat_map(|block| func.insts(block))
1252            .find(|&inst| func[inst].opcode == opcode)
1253            .expect("an instruction with that opcode")
1254    }
1255
1256    fn last(func: &Func, opcode: Opcode) -> Inst {
1257        func.blocks()
1258            .flat_map(|block| func.insts(block))
1259            .filter(|&inst| func[inst].opcode == opcode)
1260            .last()
1261            .expect("an instruction with that opcode")
1262    }
1263
1264    #[test]
1265    fn an_address_carried_through_a_block_parameter_has_left_the_function() {
1266        let mut names = Interner::new();
1267        let module = module(&mut names);
1268        let mut f = func(&mut names, &[]);
1269        let start = f.entry().expect("an entry block");
1270        let next = f.create_block();
1271        f.append_param(next, Type::PTR);
1272
1273        let mut build = Builder::new(&mut f, start);
1274        let object = local(&mut build, 16);
1275        build.jump(next, &[object]);
1276        let mut build = Builder::new(&mut f, next);
1277        build.ret(&[]);
1278
1279        let outside = Outside::of(&module);
1280        let alias = Alias::new(&f, &outside);
1281        assert!(alias.escapes().escaped(first(&f, Opcode::Alloca)));
1282    }
1283
1284    #[test]
1285    fn comparing_two_addresses_does_not_let_either_of_them_out() {
1286        let mut names = Interner::new();
1287        let module = module(&mut names);
1288        let mut f = func(&mut names, &[Type::PTR]);
1289        let outside = param(&f, 0);
1290        let mut build = builder(&mut f);
1291        let object = local(&mut build, 16);
1292        build.icmp(IntPred::Eq, object, outside);
1293        build.ret(&[]);
1294
1295        let outside = Outside::of(&module);
1296        let alias = Alias::new(&f, &outside);
1297        assert_eq!(alias.escapes().count(), 0);
1298    }
1299
1300    #[test]
1301    fn a_plane_write_on_a_local_does_not_let_its_address_out() {
1302        // What a `-fsafety=detect` build puts beside the first store into a local. The runtime
1303        // writes down that those bytes are now initialised, in storage of its own, and nothing
1304        // the program can run afterwards reaches the local through it.
1305        let mut names = Interner::new();
1306        let module = module(&mut names);
1307        let mut f = func(&mut names, &[]);
1308        let mut build = builder(&mut f);
1309        let object = local(&mut build, 16);
1310        let width = build.iconst(Type::int(64), 16);
1311        let args = build.func().push_values(&[object, width]);
1312        build.inst(InstData { args, ..InstData::new(Opcode::MetaInit) }, &[]);
1313        build.ret(&[]);
1314
1315        let outside = Outside::of(&module);
1316        let alias = Alias::new(&f, &outside);
1317        assert_eq!(alias.escapes().count(), 0);
1318    }
1319
1320    #[test]
1321    fn a_local_that_is_only_asked_about_and_checked_does_not_leave_the_function() {
1322        // What one bounds check on a local lowers to before `rucc_safety::lower` runs, which is
1323        // an `alloca`, the capability of the object it is, and a check that reads a plane. None
1324        // of the three hands the address to anything, and before this was written the `cap_of`
1325        // in the middle of it escaped every local in a program built with the checks on.
1326        let mut names = Interner::new();
1327        let module = module(&mut names);
1328        let mut f = func(&mut names, &[]);
1329        let mut build = builder(&mut f);
1330        let object = local(&mut build, 16);
1331        let args = build.func().push_values(&[object]);
1332        let capability = build.value(InstData { args, ..InstData::new(Opcode::CapOf) }, Type::CAP);
1333        let args = build.func().push_values(&[capability, object]);
1334        build.inst(InstData { args, ..InstData::new(Opcode::CheckBounds) }, &[]);
1335        build.ret(&[]);
1336
1337        let outside = Outside::of(&module);
1338        let alias = Alias::new(&f, &outside);
1339        assert_eq!(alias.escapes().count(), 0);
1340    }
1341
1342    #[test]
1343    fn a_capability_of_a_local_used_for_anything_else_does_let_it_out() {
1344        // The other side of the same line, and the reason the walk goes through `cap_of` rather
1345        // than the whitelist naming it on its own. `cap_narrow` makes a second capability from
1346        // the first, and where that one ends up is not something this walk follows, so the local
1347        // it is about has to count as gone.
1348        let mut names = Interner::new();
1349        let module = module(&mut names);
1350        let mut f = func(&mut names, &[]);
1351        let mut build = builder(&mut f);
1352        let object = local(&mut build, 16);
1353        let args = build.func().push_values(&[object]);
1354        let capability = build.value(InstData { args, ..InstData::new(Opcode::CapOf) }, Type::CAP);
1355        let base = build.iconst(Type::int(64), 0);
1356        let size = build.iconst(Type::int(64), 4);
1357        let args = build.func().push_values(&[capability, base, size]);
1358        build.value(InstData { args, ..InstData::new(Opcode::CapNarrow) }, Type::CAP);
1359        build.ret(&[]);
1360
1361        let outside = Outside::of(&module);
1362        let alias = Alias::new(&f, &outside);
1363        assert!(alias.escapes().escaped(first(&f, Opcode::Alloca)));
1364    }
1365
1366    #[test]
1367    fn the_whitelist_says_yes_to_a_plane_access_at_every_operand() {
1368        // Asked of the list rather than of a program, because what makes this safe is that every
1369        // pointer one of these takes is a row to look up, and a test built out of one instruction
1370        // only ever says it about the operand that instruction has.
1371        for opcode in Opcode::all().filter(|opcode| opcode.touches_only_planes()) {
1372            for index in 0..4 {
1373                assert!(keeps_address(opcode, index), "{opcode} at {index}");
1374            }
1375        }
1376        for opcode in [Opcode::CapNarrow, Opcode::CapRecover] {
1377            assert!(!keeps_address(opcode, 0), "{opcode}");
1378        }
1379        // The two operands of the aux pair that say which slot, against the two of `cap_store`
1380        // that say what goes in it.
1381        for opcode in [Opcode::CapLoad, Opcode::CapStore, Opcode::CapCopy] {
1382            for index in 0..2 {
1383                assert!(keeps_address(opcode, index), "{opcode} at {index}");
1384            }
1385        }
1386        assert!(!keeps_address(Opcode::CapStore, 2));
1387        assert!(!keeps_address(Opcode::CapStore, 3));
1388        assert!(keeps_address(Opcode::CapOf, 0));
1389    }
1390
1391    #[test]
1392    fn a_local_a_pointer_is_written_into_does_not_leave_the_function_for_the_writing_down() {
1393        // What `int *slot; slot = p;` lowers to with the checks on, which is the store and a
1394        // `cap_store` behind it putting the pointer's capability in the slot beside the word. The
1395        // local holding the pointer is the container and it is named twice, once as its capability
1396        // and once as the address of the word, and neither of those is a way to reach it later.
1397        let mut names = Interner::new();
1398        let module = module(&mut names);
1399        let mut f = func(&mut names, &[Type::PTR]);
1400        let written = param(&f, 0);
1401        let mut build = builder(&mut f);
1402        let object = local(&mut build, 8);
1403        let args = build.func().push_values(&[object]);
1404        let container = build.value(InstData { args, ..InstData::new(Opcode::CapOf) }, Type::CAP);
1405        let args = build.func().push_values(&[written]);
1406        let held = build.value(InstData { args, ..InstData::new(Opcode::CapOf) }, Type::CAP);
1407        build.store(written, object, plain(8), Flags::NONE);
1408        let args = build.func().push_values(&[container, object, written, held]);
1409        build.inst(InstData { args, ..InstData::new(Opcode::CapStore) }, &[]);
1410        build.ret(&[]);
1411
1412        let outside = Outside::of(&module);
1413        let alias = Alias::new(&f, &outside);
1414        assert_eq!(alias.escapes().count(), 0);
1415    }
1416
1417    #[test]
1418    fn a_local_whose_capability_is_written_into_a_slot_does_leave_the_function() {
1419        // The other two operands, and the line between them and the two above. Here the local is
1420        // the pointer being stored rather than the object being stored into, so its address goes
1421        // into somebody else's memory and its capability goes into the slot beside it, and both of
1422        // those are places a later `cap_load` in another function can read.
1423        let mut names = Interner::new();
1424        let module = module(&mut names);
1425        let mut f = func(&mut names, &[Type::PTR]);
1426        let into = param(&f, 0);
1427        let mut build = builder(&mut f);
1428        let object = local(&mut build, 8);
1429        let args = build.func().push_values(&[into]);
1430        let container = build.value(InstData { args, ..InstData::new(Opcode::CapOf) }, Type::CAP);
1431        let args = build.func().push_values(&[object]);
1432        let held = build.value(InstData { args, ..InstData::new(Opcode::CapOf) }, Type::CAP);
1433        let args = build.func().push_values(&[container, into, object, held]);
1434        build.inst(InstData { args, ..InstData::new(Opcode::CapStore) }, &[]);
1435        build.ret(&[]);
1436
1437        let outside = Outside::of(&module);
1438        let alias = Alias::new(&f, &outside);
1439        assert!(alias.escapes().escaped(first(&f, Opcode::Alloca)));
1440    }
1441
1442    #[test]
1443    fn an_address_turned_into_a_number_has_left_the_function() {
1444        // The number can be turned back into a pointer anywhere, including in a different
1445        // translation unit, so this is an escape and the whitelist is what makes it one.
1446        let mut names = Interner::new();
1447        let module = module(&mut names);
1448        let mut f = func(&mut names, &[]);
1449        let mut build = builder(&mut f);
1450        let object = local(&mut build, 16);
1451        build.unary(Opcode::PtrToInt, object, Type::int(64));
1452        build.ret(&[]);
1453
1454        let outside = Outside::of(&module);
1455        let alias = Alias::new(&f, &outside);
1456        assert!(alias.escapes().escaped(first(&f, Opcode::Alloca)));
1457    }
1458
1459    #[test]
1460    fn two_restrict_pointers_in_one_scope_do_not_reach_the_same_object() {
1461        let mut names = Interner::new();
1462        let module = module(&mut names);
1463        let mut f = func(&mut names, &[Type::PTR, Type::PTR]);
1464        let (one, other) = (param(&f, 0), param(&f, 1));
1465        let mut build = builder(&mut f);
1466        let mut info = plain(4);
1467        info.restrict = Restrict { clique: 1, base: 1 };
1468        let read = build.load(Type::int(32), one, info, Flags::NONE);
1469        info.restrict = Restrict { clique: 1, base: 2 };
1470        build.store(read, other, info, Flags::NONE);
1471        build.ret(&[]);
1472
1473        let outside = Outside::of(&module);
1474        let mut alias = Alias::new(&f, &outside);
1475        let (a, b) = two(&alias, &f);
1476        assert_eq!(alias.query(&a, &b), Answer::No(Reason::Restrict));
1477    }
1478
1479    #[test]
1480    fn two_restrict_pointers_in_different_scopes_say_nothing_about_each_other() {
1481        let mut names = Interner::new();
1482        let module = module(&mut names);
1483        let mut f = func(&mut names, &[Type::PTR, Type::PTR]);
1484        let (one, other) = (param(&f, 0), param(&f, 1));
1485        let mut build = builder(&mut f);
1486        let mut info = plain(4);
1487        info.restrict = Restrict { clique: 1, base: 1 };
1488        let read = build.load(Type::int(32), one, info, Flags::NONE);
1489        info.restrict = Restrict { clique: 2, base: 1 };
1490        build.store(read, other, info, Flags::NONE);
1491        build.ret(&[]);
1492
1493        let outside = Outside::of(&module);
1494        let mut alias = Alias::new(&f, &outside);
1495        let (a, b) = two(&alias, &f);
1496        assert_eq!(alias.query(&a, &b), Answer::May);
1497    }
1498
1499    /// A module with a `char` root and an `int` and a `float` hanging off it.
1500    fn types(module: &mut Module, names: &mut Interner) -> (Meta, Meta, Meta) {
1501        let root = module.add_meta(MetaNode::Tbaa(TbaaNode {
1502            name: names.intern("char"),
1503            parent: None,
1504            offset: 0,
1505        }));
1506        let int = module.add_meta(MetaNode::Tbaa(TbaaNode {
1507            name: names.intern("int"),
1508            parent: Some(root),
1509            offset: 0,
1510        }));
1511        let float = module.add_meta(MetaNode::Tbaa(TbaaNode {
1512            name: names.intern("float"),
1513            parent: Some(root),
1514            offset: 0,
1515        }));
1516        (root, int, float)
1517    }
1518
1519    #[test]
1520    fn two_unrelated_types_describe_no_object_in_common() {
1521        let mut names = Interner::new();
1522        let mut module = module(&mut names);
1523        let (_, int, float) = types(&mut module, &mut names);
1524        let mut f = func(&mut names, &[Type::PTR, Type::PTR]);
1525        let (one, other) = (param(&f, 0), param(&f, 1));
1526        let mut build = builder(&mut f);
1527        let mut info = plain(4);
1528        info.tbaa = Some(int);
1529        let read = build.load(Type::int(32), one, info, Flags::NONE);
1530        info.tbaa = Some(float);
1531        build.store(read, other, info, Flags::NONE);
1532        build.ret(&[]);
1533
1534        let outside = Outside::of(&module);
1535        let mut alias = Alias::new(&f, &outside);
1536        let (a, b) = two(&alias, &f);
1537        assert_eq!(alias.query(&a, &b), Answer::No(Reason::Tbaa));
1538    }
1539
1540    #[test]
1541    fn an_access_through_char_conflicts_with_everything() {
1542        let mut names = Interner::new();
1543        let mut module = module(&mut names);
1544        let (root, int, _) = types(&mut module, &mut names);
1545        let mut f = func(&mut names, &[Type::PTR, Type::PTR]);
1546        let (one, other) = (param(&f, 0), param(&f, 1));
1547        let mut build = builder(&mut f);
1548        let mut info = plain(4);
1549        info.tbaa = Some(int);
1550        let read = build.load(Type::int(32), one, info, Flags::NONE);
1551        info.tbaa = Some(root);
1552        build.store(read, other, info, Flags::NONE);
1553        build.ret(&[]);
1554
1555        let outside = Outside::of(&module);
1556        let mut alias = Alias::new(&f, &outside);
1557        let (a, b) = two(&alias, &f);
1558        assert_eq!(alias.query(&a, &b), Answer::May);
1559    }
1560
1561    #[test]
1562    fn turning_strict_aliasing_off_turns_off_that_layer_and_no_other() {
1563        let mut names = Interner::new();
1564        let mut module = module(&mut names);
1565        let (_, int, float) = types(&mut module, &mut names);
1566        let mut f = func(&mut names, &[Type::PTR, Type::PTR]);
1567        let (one, other) = (param(&f, 0), param(&f, 1));
1568        let mut build = builder(&mut f);
1569        let mut info = plain(4);
1570        info.tbaa = Some(int);
1571        info.restrict = Restrict { clique: 1, base: 1 };
1572        let read = build.load(Type::int(32), one, info, Flags::NONE);
1573        info.tbaa = Some(float);
1574        info.restrict = Restrict { clique: 1, base: 2 };
1575        build.store(read, other, info, Flags::NONE);
1576        build.ret(&[]);
1577
1578        let options = Options { strict_aliasing: false };
1579        let outside = Outside::of(&module);
1580        let mut alias = Alias::with(&f, &outside, options);
1581        let (a, b) = two(&alias, &f);
1582        // The `restrict` layer still answers, which is the point: the flag is one condition in
1583        // one place and it does not reach anything else.
1584        assert_eq!(alias.query(&a, &b), Answer::No(Reason::Restrict));
1585
1586        let mut without = Alias::with(&f, &outside, options);
1587        let plainer = Access { restrict: Restrict::NONE, ..a };
1588        let other = Access { restrict: Restrict::NONE, ..b };
1589        assert_eq!(without.query(&plainer, &other), Answer::May);
1590
1591        let mut with = Alias::new(&f, &outside);
1592        assert_eq!(with.query(&plainer, &other), Answer::No(Reason::Tbaa));
1593    }
1594
1595    #[test]
1596    fn writing_one_member_of_a_union_and_reading_another_is_one_object() {
1597        // The compatibility fact of section 8.6. Two accesses to one object at the same offset
1598        // with unrelated types, which is `union { int i; float f; }` written as one and read as
1599        // the other. The offset layer runs first, it says they overlap, and the type layer
1600        // never gets to say no. Twenty years of real C rests on this answer.
1601        let mut names = Interner::new();
1602        let mut module = module(&mut names);
1603        let (_, int, float) = types(&mut module, &mut names);
1604        let mut f = func(&mut names, &[]);
1605        let mut build = builder(&mut f);
1606        let object = local(&mut build, 4);
1607        let mut info = plain(4);
1608        info.tbaa = Some(float);
1609        let read = build.load(Type::int(32), object, info, Flags::NONE);
1610        info.tbaa = Some(int);
1611        build.store(read, object, info, Flags::NONE);
1612        build.ret(&[]);
1613
1614        let outside = Outside::of(&module);
1615        let mut alias = Alias::new(&f, &outside);
1616        let (a, b) = two(&alias, &f);
1617        assert_eq!(alias.query(&a, &b), Answer::May);
1618    }
1619
1620    #[test]
1621    fn two_volatile_accesses_conflict_whatever_else_is_true_of_them() {
1622        let mut names = Interner::new();
1623        let module = module(&mut names);
1624        let mut f = func(&mut names, &[]);
1625        let mut build = builder(&mut f);
1626        let one = local(&mut build, 16);
1627        let other = local(&mut build, 16);
1628        let read = build.load(Type::int(32), one, plain(4), Flags::VOLATILE);
1629        build.store(read, other, plain(4), Flags::VOLATILE);
1630        build.ret(&[]);
1631
1632        let outside = Outside::of(&module);
1633        let mut alias = Alias::new(&f, &outside);
1634        let (a, b) = two(&alias, &f);
1635        // Two different objects, and the answer is still that they conflict, because moving
1636        // one volatile access across another is the thing `volatile` exists to forbid.
1637        assert_eq!(alias.query(&a, &b), Answer::May);
1638    }
1639
1640    #[test]
1641    fn one_volatile_access_and_one_ordinary_one_are_argued_about_as_usual() {
1642        let mut names = Interner::new();
1643        let module = module(&mut names);
1644        let mut f = func(&mut names, &[]);
1645        let mut build = builder(&mut f);
1646        let one = local(&mut build, 16);
1647        let other = local(&mut build, 16);
1648        let read = build.load(Type::int(32), one, plain(4), Flags::VOLATILE);
1649        build.store(read, other, plain(4), Flags::NONE);
1650        build.ret(&[]);
1651
1652        let outside = Outside::of(&module);
1653        let mut alias = Alias::new(&f, &outside);
1654        let (a, b) = two(&alias, &f);
1655        assert_eq!(alias.query(&a, &b), Answer::No(Reason::Distinct));
1656    }
1657
1658    #[test]
1659    fn a_copy_reads_its_source_and_writes_its_destination() {
1660        let mut names = Interner::new();
1661        let module = module(&mut names);
1662        let mut f = func(&mut names, &[]);
1663        let mut build = builder(&mut f);
1664        let to = local(&mut build, 16);
1665        let from = local(&mut build, 16);
1666        let mem = build.func().add_mem(sized(16, 8));
1667        let args = build.func().push_values(&[to, from]);
1668        build.inst(InstData { args, extra: Extra::Mem(mem), ..InstData::new(Opcode::Memcpy) }, &[]);
1669        build.ret(&[]);
1670
1671        let outside = Outside::of(&module);
1672        let alias = Alias::new(&f, &outside);
1673        let copy = first(&f, Opcode::Memcpy);
1674        let read = alias.reads(copy).expect("a copy reads");
1675        let written = alias.writes(copy).expect("a copy writes");
1676        assert_eq!(read.size, Some(16));
1677        assert_eq!(written.size, Some(16));
1678        assert_ne!(read.origin, written.origin);
1679    }
1680
1681    #[test]
1682    fn a_copy_of_a_length_the_program_works_out_is_an_access_of_no_known_size() {
1683        let mut names = Interner::new();
1684        let module = module(&mut names);
1685        let mut f = func(&mut names, &[Type::int(64)]);
1686        let length = param(&f, 0);
1687        let mut build = builder(&mut f);
1688        let to = local(&mut build, 16);
1689        let from = local(&mut build, 16);
1690        let mem = build.func().add_mem(sized(0, 8));
1691        let args = build.func().push_values(&[to, from, length]);
1692        build.inst(InstData { args, extra: Extra::Mem(mem), ..InstData::new(Opcode::Memcpy) }, &[]);
1693        build.ret(&[]);
1694
1695        let outside = Outside::of(&module);
1696        let alias = Alias::new(&f, &outside);
1697        let copy = first(&f, Opcode::Memcpy);
1698        // Not `Some(0)`, which is what the payload says and which would make this a copy that
1699        // touches nothing rather than one that may touch anything.
1700        assert_eq!(alias.reads(copy).expect("a copy reads").size, None);
1701        assert_eq!(alias.writes(copy).expect("a copy writes").size, None);
1702    }
1703
1704    /// A call to a function declared with those attributes.
1705    fn call_to(
1706        names: &mut Interner,
1707        module: &mut Module,
1708        f: &mut Func,
1709        attrs: Attrs,
1710        args: &[Value],
1711    ) -> Inst {
1712        let name = names.intern("g");
1713        let params: Vec<Type> = args.iter().map(|_| Type::PTR).collect();
1714        let mut callee = Func::new(name, Signature::new().with_params(&params));
1715        callee.attrs = attrs;
1716        module.add_func(callee);
1717        let signature = f.add_signature(Signature::new().with_params(&params));
1718        let mut build = builder(f);
1719        build.call(name, signature, args)
1720    }
1721
1722    fn attrs(set: AttrSet) -> Attrs {
1723        Attrs { set, ..Attrs::NONE }
1724    }
1725
1726    #[test]
1727    fn a_call_cannot_touch_a_local_whose_address_stayed_here() {
1728        let mut names = Interner::new();
1729        let mut module = module(&mut names);
1730        let mut f = func(&mut names, &[Type::PTR]);
1731        let outside = param(&f, 0);
1732        let mut build = builder(&mut f);
1733        let object = local(&mut build, 16);
1734        let read = build.load(Type::int(32), object, plain(4), Flags::NONE);
1735        let _ = read;
1736        let call = call_to(&mut names, &mut module, &mut f, Attrs::NONE, &[outside]);
1737        let mut build = builder(&mut f);
1738        build.ret(&[]);
1739
1740        let outside = Outside::of(&module);
1741        let mut alias = Alias::new(&f, &outside);
1742        let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1743        assert_eq!(alias.clobbered_by(&reference, call), Answer::No(Reason::Escape));
1744        assert_eq!(alias.read_by(&reference, call), Answer::No(Reason::Escape));
1745    }
1746
1747    #[test]
1748    fn a_call_can_touch_a_local_it_was_handed() {
1749        let mut names = Interner::new();
1750        let mut module = module(&mut names);
1751        let mut f = func(&mut names, &[]);
1752        let mut build = builder(&mut f);
1753        let object = local(&mut build, 16);
1754        build.load(Type::int(32), object, plain(4), Flags::NONE);
1755        let call = call_to(&mut names, &mut module, &mut f, Attrs::NONE, &[object]);
1756        let mut build = builder(&mut f);
1757        build.ret(&[]);
1758
1759        let outside = Outside::of(&module);
1760        let mut alias = Alias::new(&f, &outside);
1761        let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1762        assert_eq!(alias.clobbered_by(&reference, call), Answer::May);
1763    }
1764
1765    #[test]
1766    fn a_setjmp_marker_can_touch_a_local_whose_address_stayed_here() {
1767        // The marker is on the memory chain and it is not a call, so the argument the two tests
1768        // above rest on says nothing about it. Control arrives at what follows it from wherever
1769        // the matching `longjmp` sits, and the jump comes back into this frame, so a local this
1770        // function never let out is exactly what the landing goes on to read.
1771        let mut names = Interner::new();
1772        let mut module = module(&mut names);
1773        let name = names.intern("jmp_buf");
1774        let mut f = func(&mut names, &[]);
1775        let mut build = builder(&mut f);
1776        let object = local(&mut build, 16);
1777        build.load(Type::int(32), object, plain(4), Flags::NONE);
1778        let buffer = global(&mut build, &mut module, name);
1779        let args = build.func().push_values(&[buffer]);
1780        let marker =
1781            build.inst(InstData { args, ..InstData::new(Opcode::SetjmpMarker) }, &[Type::int(32)]);
1782        build.ret(&[]);
1783
1784        let outside = Outside::of(&module);
1785        let mut alias = Alias::new(&f, &outside);
1786        let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1787        assert_eq!(alias.clobbered_by(&reference, marker), Answer::May);
1788        assert_eq!(alias.read_by(&reference, marker), Answer::May);
1789    }
1790
1791    #[test]
1792    fn a_pure_callee_reads_memory_and_writes_none() {
1793        let mut names = Interner::new();
1794        let mut module = module(&mut names);
1795        let mut f = func(&mut names, &[Type::PTR]);
1796        let outside = param(&f, 0);
1797        let mut build = builder(&mut f);
1798        build.load(Type::int(32), outside, plain(4), Flags::NONE);
1799        let call = call_to(&mut names, &mut module, &mut f, attrs(AttrSet::READONLY), &[outside]);
1800        let mut build = builder(&mut f);
1801        build.ret(&[]);
1802
1803        let outside = Outside::of(&module);
1804        let mut alias = Alias::new(&f, &outside);
1805        let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1806        assert_eq!(alias.clobbered_by(&reference, call), Answer::No(Reason::Attribute));
1807        assert_eq!(alias.read_by(&reference, call), Answer::May);
1808    }
1809
1810    #[test]
1811    fn a_plane_write_is_not_a_write_to_the_address_it_names() {
1812        // The one that was costing the memory passes everything on a safety build. `meta_init %p`
1813        // writes the entry the lifetime plane keeps for `%p`, and the oracle reading its operand
1814        // the ordinary way sees a write to exactly the bytes a load of `%p` wants, which is the
1815        // worst possible wrong answer: the instrumentation blocking the optimization of the code
1816        // it was put in to check.
1817        let mut names = Interner::new();
1818        let module = module(&mut names);
1819        let mut f = func(&mut names, &[Type::PTR]);
1820        let outside = param(&f, 0);
1821        let mut build = builder(&mut f);
1822        build.load(Type::int(32), outside, plain(4), Flags::NONE);
1823        let width = build.iconst(Type::int(64), 4);
1824        let args = build.func().push_values(&[outside, width]);
1825        build.inst(InstData { args, ..InstData::new(Opcode::MetaInit) }, &[]);
1826        build.ret(&[]);
1827
1828        let outside = Outside::of(&module);
1829        let mut alias = Alias::new(&f, &outside);
1830        let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1831        let plane = first(&f, Opcode::MetaInit);
1832        assert_eq!(alias.clobbered_by(&reference, plane), Answer::No(Reason::Plane));
1833        // And it does not read it either, so a store the program made is not kept alive by one.
1834        assert_eq!(alias.read_by(&reference, plane), Answer::No(Reason::Plane));
1835    }
1836
1837    #[test]
1838    fn a_check_reads_a_plane_and_not_what_it_is_about() {
1839        // The reading half of the same fact, which is what a walk back over memory runs into
1840        // first: a check between a store and a load of the same address is on the chain, and
1841        // answering `May` for it is a load kept for no reason.
1842        let mut names = Interner::new();
1843        let module = module(&mut names);
1844        let mut f = func(&mut names, &[Type::PTR]);
1845        let outside = param(&f, 0);
1846        let mut build = builder(&mut f);
1847        build.load(Type::int(32), outside, plain(4), Flags::NONE);
1848        let width = build.iconst(Type::int(64), 4);
1849        let args = build.func().push_values(&[outside, width]);
1850        build.inst(InstData { args, ..InstData::new(Opcode::CheckBounds) }, &[]);
1851        build.ret(&[]);
1852
1853        let outside = Outside::of(&module);
1854        let mut alias = Alias::new(&f, &outside);
1855        let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1856        let check = first(&f, Opcode::CheckBounds);
1857        assert_eq!(alias.clobbered_by(&reference, check), Answer::No(Reason::Plane));
1858        assert_eq!(alias.read_by(&reference, check), Answer::No(Reason::Plane));
1859    }
1860
1861    #[test]
1862    fn a_const_callee_touches_no_memory_at_all() {
1863        let mut names = Interner::new();
1864        let mut module = module(&mut names);
1865        let mut f = func(&mut names, &[Type::PTR]);
1866        let outside = param(&f, 0);
1867        let mut build = builder(&mut f);
1868        build.load(Type::int(32), outside, plain(4), Flags::NONE);
1869        let call = call_to(&mut names, &mut module, &mut f, attrs(AttrSet::READNONE), &[outside]);
1870        let mut build = builder(&mut f);
1871        build.ret(&[]);
1872
1873        let outside = Outside::of(&module);
1874        let mut alias = Alias::new(&f, &outside);
1875        let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1876        assert_eq!(alias.clobbered_by(&reference, call), Answer::No(Reason::Attribute));
1877        assert_eq!(alias.read_by(&reference, call), Answer::No(Reason::Attribute));
1878    }
1879
1880    #[test]
1881    fn a_callee_that_touches_only_its_arguments_leaves_a_global_it_was_not_passed_alone() {
1882        let mut names = Interner::new();
1883        let mut module = module(&mut names);
1884        let x = names.intern("x");
1885        let mut f = func(&mut names, &[Type::PTR]);
1886        let outside = param(&f, 0);
1887        let mut build = builder(&mut f);
1888        let object = global(&mut build, &mut module, x);
1889        build.load(Type::int(32), object, plain(4), Flags::NONE);
1890        let call =
1891            call_to(&mut names, &mut module, &mut f, attrs(AttrSet::ARGMEM_ONLY), &[outside]);
1892        let mut build = builder(&mut f);
1893        build.ret(&[]);
1894
1895        let outside = Outside::of(&module);
1896        let mut alias = Alias::new(&f, &outside);
1897        let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1898        // The one pointer it was handed is a parameter of unknown origin, which may be that
1899        // global, so this is the answer that cannot be wrong.
1900        assert_eq!(alias.clobbered_by(&reference, call), Answer::May);
1901    }
1902
1903    #[test]
1904    fn a_callee_that_touches_only_its_arguments_and_was_handed_one_object_leaves_the_other() {
1905        let mut names = Interner::new();
1906        let mut module = module(&mut names);
1907        let (x, y) = (names.intern("x"), names.intern("y"));
1908        let mut f = func(&mut names, &[]);
1909        let mut build = builder(&mut f);
1910        let watched = global(&mut build, &mut module, x);
1911        let handed = global(&mut build, &mut module, y);
1912        build.load(Type::int(32), watched, plain(4), Flags::NONE);
1913        let call = call_to(&mut names, &mut module, &mut f, attrs(AttrSet::ARGMEM_ONLY), &[handed]);
1914        let mut build = builder(&mut f);
1915        build.ret(&[]);
1916
1917        let outside = Outside::of(&module);
1918        let mut alias = Alias::new(&f, &outside);
1919        let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1920        assert_eq!(alias.clobbered_by(&reference, call), Answer::No(Reason::Attribute));
1921    }
1922
1923    /// A call to a function of that many pointer parameters whose body is that.
1924    ///
1925    /// Unlike [`call_to`] the callee is defined, which is what gives [`crate::modref`] something
1926    /// to read. Nothing is declared about it, so every answer below comes from the body.
1927    fn call_to_body(
1928        names: &mut Interner,
1929        module: &mut Module,
1930        f: &mut Func,
1931        arity: usize,
1932        body: fn(&mut Builder<'_>, &[Value]),
1933        args: &[Value],
1934    ) -> Inst {
1935        defines(names, module, "g", arity, body);
1936        calls_it(names, f, "g", arity, args)
1937    }
1938
1939    /// Adds a function of that name to the module, with that many pointer parameters and that
1940    /// body.
1941    fn defines(
1942        names: &mut Interner,
1943        module: &mut Module,
1944        called: &str,
1945        arity: usize,
1946        body: fn(&mut Builder<'_>, &[Value]),
1947    ) {
1948        let name = names.intern(called);
1949        let params = vec![Type::PTR; arity];
1950        let mut callee = Func::new(name, Signature::new().with_params(&params));
1951        let entry = callee.create_block();
1952        let got: Vec<Value> = params.iter().map(|&ty| callee.append_param(entry, ty)).collect();
1953        let mut build = Builder::new(&mut callee, entry);
1954        body(&mut build, &got);
1955        module.add_func(callee);
1956    }
1957
1958    /// A call to a function of that name, for a test that wants more than one of them.
1959    fn calls_it(
1960        names: &mut Interner,
1961        f: &mut Func,
1962        called: &str,
1963        arity: usize,
1964        args: &[Value],
1965    ) -> Inst {
1966        let name = names.intern(called);
1967        let signature = f.add_signature(Signature::new().with_params(&vec![Type::PTR; arity]));
1968        let mut build = builder(f);
1969        build.call(name, signature, args)
1970    }
1971
1972    /// What the module's functions were worked out to do to memory.
1973    fn worked_out(module: &Module) -> Summaries {
1974        let mut summaries = Summaries::of_module(module);
1975        summarize(module, &CallGraph::of(module, Pic::Executable), &mut summaries);
1976        summaries
1977    }
1978
1979    /// Comes back and does nothing on the way.
1980    fn body_does_nothing(build: &mut Builder<'_>, _: &[Value]) {
1981        build.ret(&[]);
1982    }
1983
1984    /// Reads four bytes through the first pointer it was handed.
1985    fn body_reads_the_first(build: &mut Builder<'_>, args: &[Value]) {
1986        let value = build.load(Type::int(32), args[0], plain(4), Flags::NONE);
1987        build.ret(&[value]);
1988    }
1989
1990    /// Writes four bytes through the first pointer it was handed and leaves the rest alone.
1991    fn body_writes_the_first(build: &mut Builder<'_>, args: &[Value]) {
1992        let zero = build.iconst(Type::int(32), 0);
1993        build.store(zero, args[0], plain(4), Flags::NONE);
1994        build.ret(&[]);
1995    }
1996
1997    /// Writes the first pointer it was handed down at the second, so the address gets out.
1998    fn body_keeps_the_first(build: &mut Builder<'_>, args: &[Value]) {
1999        build.store(args[0], args[1], plain(8), Flags::NONE);
2000        build.ret(&[]);
2001    }
2002
2003    #[test]
2004    fn a_callee_nobody_declared_anything_about_is_read_out_of_its_body() {
2005        let mut names = Interner::new();
2006        let mut module = module(&mut names);
2007        let x = names.intern("x");
2008        let mut f = func(&mut names, &[]);
2009        let mut build = builder(&mut f);
2010        let object = global(&mut build, &mut module, x);
2011        build.load(Type::int(32), object, plain(4), Flags::NONE);
2012        let call = call_to_body(&mut names, &mut module, &mut f, 0, body_does_nothing, &[]);
2013        let mut build = builder(&mut f);
2014        build.ret(&[]);
2015
2016        let outside = Outside::of(&module);
2017        let reference = Alias::new(&f, &outside).reads(first(&f, Opcode::Load)).unwrap();
2018        // Without the summaries there is nothing to go on, because nobody wrote an attribute.
2019        let mut blind = Alias::new(&f, &outside);
2020        assert_eq!(blind.clobbered_by(&reference, call), Answer::May);
2021
2022        let summaries = worked_out(&module);
2023        let mut alias = Alias::new(&f, &outside).knowing(&summaries);
2024        assert_eq!(alias.clobbered_by(&reference, call), Answer::No(Reason::Summary));
2025        assert_eq!(alias.read_by(&reference, call), Answer::No(Reason::Summary));
2026    }
2027
2028    #[test]
2029    fn a_callee_worked_out_to_write_nothing_clobbers_nothing() {
2030        let mut names = Interner::new();
2031        let mut module = module(&mut names);
2032        let mut f = func(&mut names, &[Type::PTR]);
2033        let handed = param(&f, 0);
2034        let mut build = builder(&mut f);
2035        build.load(Type::int(32), handed, plain(4), Flags::NONE);
2036        let call =
2037            call_to_body(&mut names, &mut module, &mut f, 1, body_reads_the_first, &[handed]);
2038        let mut build = builder(&mut f);
2039        build.ret(&[]);
2040
2041        let outside = Outside::of(&module);
2042        let summaries = worked_out(&module);
2043        let reference = Alias::new(&f, &outside).reads(first(&f, Opcode::Load)).unwrap();
2044        let mut alias = Alias::new(&f, &outside).knowing(&summaries);
2045        assert_eq!(alias.clobbered_by(&reference, call), Answer::No(Reason::Summary));
2046        // It was handed that very object and it does read, so the other question is still open.
2047        assert_eq!(alias.read_by(&reference, call), Answer::May);
2048    }
2049
2050    #[test]
2051    fn a_callee_that_writes_one_of_the_two_it_was_handed_leaves_the_other() {
2052        // The answer no attribute can give. `argmemonly` says the call touched nothing it was not
2053        // handed, and it was handed both of these, so the declaration alone has to say `May`.
2054        let mut names = Interner::new();
2055        let mut module = module(&mut names);
2056        let (x, y) = (names.intern("x"), names.intern("y"));
2057        let mut f = func(&mut names, &[]);
2058        let mut build = builder(&mut f);
2059        let watched = global(&mut build, &mut module, x);
2060        let written = global(&mut build, &mut module, y);
2061        build.load(Type::int(32), watched, plain(4), Flags::NONE);
2062        let call = call_to_body(
2063            &mut names,
2064            &mut module,
2065            &mut f,
2066            2,
2067            body_writes_the_first,
2068            &[written, watched],
2069        );
2070        let mut build = builder(&mut f);
2071        build.ret(&[]);
2072
2073        let outside = Outside::of(&module);
2074        let summaries = worked_out(&module);
2075        let reference = Alias::new(&f, &outside).reads(first(&f, Opcode::Load)).unwrap();
2076        let mut alias = Alias::new(&f, &outside).knowing(&summaries);
2077        assert_eq!(alias.clobbered_by(&reference, call), Answer::No(Reason::Summary));
2078    }
2079
2080    #[test]
2081    fn a_local_lent_to_a_callee_that_keeps_it_not_is_still_private_everywhere_else() {
2082        // Section 34.6's upgrade. Handing the address of a local to a call is what a C program
2083        // does with most of the locals it takes the address of at all, and without a summary of
2084        // the callee that is the end of every question about that local.
2085        let mut names = Interner::new();
2086        let mut module = module(&mut names);
2087        let x = names.intern("x");
2088        let mut f = func(&mut names, &[]);
2089        let mut build = builder(&mut f);
2090        let object = local(&mut build, 16);
2091        let elsewhere = global(&mut build, &mut module, x);
2092        build.load(Type::int(32), object, plain(4), Flags::NONE);
2093        defines(&mut names, &mut module, "g", 1, body_writes_the_first);
2094        let lent = calls_it(&mut names, &mut f, "g", 1, &[object]);
2095        let other = calls_it(&mut names, &mut f, "g", 1, &[elsewhere]);
2096        let mut build = builder(&mut f);
2097        build.ret(&[]);
2098
2099        let outside = Outside::of(&module);
2100        let reference = Alias::new(&f, &outside).reads(first(&f, Opcode::Load)).unwrap();
2101        // Without the summaries the address went out at the first call and stayed out.
2102        let mut blind = Alias::new(&f, &outside);
2103        assert_eq!(blind.clobbered_by(&reference, lent), Answer::May);
2104        assert_eq!(blind.clobbered_by(&reference, other), Answer::May);
2105
2106        let summaries = worked_out(&module);
2107        let mut alias = Alias::new(&f, &outside).knowing(&summaries);
2108        // The call that was handed it can still have written it, and is asked about rather than
2109        // assumed away, which is the invariant the upgrade took off the escape layer.
2110        assert_eq!(alias.clobbered_by(&reference, lent), Answer::May);
2111        // The one that was not never had the address and never could get it.
2112        assert_eq!(alias.clobbered_by(&reference, other), Answer::No(Reason::Escape));
2113        assert_eq!(alias.escapes().count(), 0);
2114    }
2115
2116    #[test]
2117    fn a_local_written_down_by_a_callee_is_gone_exactly_as_before() {
2118        let mut names = Interner::new();
2119        let mut module = module(&mut names);
2120        let x = names.intern("x");
2121        let mut f = func(&mut names, &[]);
2122        let mut build = builder(&mut f);
2123        let object = local(&mut build, 16);
2124        let elsewhere = global(&mut build, &mut module, x);
2125        build.load(Type::int(32), object, plain(4), Flags::NONE);
2126        defines(&mut names, &mut module, "g", 2, body_keeps_the_first);
2127        defines(&mut names, &mut module, "h", 1, body_does_nothing);
2128        calls_it(&mut names, &mut f, "g", 2, &[object, elsewhere]);
2129        let other = calls_it(&mut names, &mut f, "h", 1, &[elsewhere]);
2130        let mut build = builder(&mut f);
2131        build.ret(&[]);
2132
2133        let outside = Outside::of(&module);
2134        let summaries = worked_out(&module);
2135        let reference = Alias::new(&f, &outside).reads(first(&f, Opcode::Load)).unwrap();
2136        let mut alias = Alias::new(&f, &outside).knowing(&summaries);
2137        // That callee put the address somewhere, so the upgrade does not save it and every
2138        // question about the local is back to the answer that cannot be wrong.
2139        assert_eq!(alias.escapes().count(), 1);
2140        assert_eq!(alias.private(&reference), None);
2141        // `h` touches nothing at all, so it is still answered, just not by the escape layer.
2142        assert_eq!(alias.clobbered_by(&reference, other), Answer::No(Reason::Summary));
2143    }
2144
2145    #[test]
2146    fn a_local_handed_to_a_const_declaration_is_still_gone() {
2147        // `const` says the result comes out of the arguments. It does not say the function did
2148        // not hand one of them back, so the address may be in the caller's hands after the call
2149        // under a name the escape walk cannot follow to this local, and the upgrade has to leave
2150        // this one alone.
2151        let mut names = Interner::new();
2152        let mut module = module(&mut names);
2153        let mut f = func(&mut names, &[]);
2154        let mut build = builder(&mut f);
2155        let object = local(&mut build, 16);
2156        build.load(Type::int(32), object, plain(4), Flags::NONE);
2157        call_to(&mut names, &mut module, &mut f, attrs(AttrSet::READNONE), &[object]);
2158        let mut build = builder(&mut f);
2159        build.ret(&[]);
2160
2161        let outside = Outside::of(&module);
2162        let summaries = worked_out(&module);
2163        let reference = Alias::new(&f, &outside).reads(first(&f, Opcode::Load)).unwrap();
2164        let alias = Alias::new(&f, &outside).knowing(&summaries);
2165        assert_eq!(alias.escapes().count(), 1);
2166        assert_eq!(alias.private(&reference), None);
2167    }
2168
2169    #[test]
2170    fn an_indirect_call_is_not_argued_about() {
2171        let mut names = Interner::new();
2172        let module = module(&mut names);
2173        let mut f = func(&mut names, &[Type::PTR, Type::PTR]);
2174        let (target, outside) = (param(&f, 0), param(&f, 1));
2175        let mut build = builder(&mut f);
2176        build.load(Type::int(32), outside, plain(4), Flags::NONE);
2177        let signature = build.func().add_signature(Signature::new().with_params(&[Type::PTR]));
2178        let varargs = build.func().push_abis(&[]);
2179        let info = build.func().add_call(CallInfo { callee: None, signature, varargs });
2180        let args = build.func().push_values(&[target, outside]);
2181        let call = build.inst(
2182            InstData { args, extra: Extra::Call(info), ..InstData::new(Opcode::CallIndirect) },
2183            &[],
2184        );
2185        build.ret(&[]);
2186
2187        let outside = Outside::of(&module);
2188        let mut alias = Alias::new(&f, &outside);
2189        let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
2190        assert_eq!(alias.clobbered_by(&reference, call), Answer::May);
2191    }
2192
2193    #[test]
2194    fn every_reason_has_a_name_and_a_sentence() {
2195        for reason in Reason::ALL {
2196            assert!(!reason.name().is_empty());
2197            assert!(!reason.describe().is_empty());
2198            assert_eq!(Reason::ALL[reason.index()], reason);
2199        }
2200        assert_eq!(Reason::ALL.len(), Reason::COUNT);
2201        assert_eq!(Answer::No(Reason::Offset).reason(), Some(Reason::Offset));
2202        assert!(Answer::No(Reason::Offset).is_no());
2203        assert_eq!(Answer::May.reason(), None);
2204        assert!(!Answer::May.is_no());
2205    }
2206}