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