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            _ => return (Origin::Unknown(value), offset),
292        }
293    }
294    (Origin::Unknown(value), None)
295}
296
297/// One memory reference: which bytes an instruction touches and what it says about them.
298///
299/// Built by [`Alias::reads`] and [`Alias::writes`] rather than by hand, so that the size of a
300/// load comes from the type it produces and the size of a `memcpy` comes from its access, and no
301/// caller has to remember which.
302#[derive(Clone, Copy, Debug, PartialEq, Eq)]
303pub struct Access {
304    /// The object, or the address the walk stopped at.
305    pub origin: Origin,
306    /// How many bytes past the start of that the reference begins, when the walk could tell.
307    pub offset: Option<i64>,
308    /// How many bytes it covers, when that is known.
309    pub size: Option<u64>,
310    /// The type node the front end attached, if it attached one.
311    pub tbaa: Option<Meta>,
312    /// The `restrict` scope the access is in.
313    pub restrict: Restrict,
314    /// Whether the access is `volatile`.
315    pub volatile: bool,
316}
317
318impl Access {
319    /// A reference to somewhere behind this address, of unknown size and with nothing known
320    /// about its type.
321    ///
322    /// This is what a pointer handed to a call is: the call touches something through it and
323    /// there is nothing on the call saying how much.
324    #[must_use]
325    pub fn through(func: &Func, pointer: Value) -> Self {
326        let (origin, offset) = origin(func, pointer);
327        Self { origin, offset, size: None, tbaa: None, restrict: Restrict::NONE, volatile: false }
328    }
329
330    /// The half-open range of bytes this covers within its origin, when both ends are known.
331    #[must_use]
332    pub fn range(&self) -> Option<(i128, i128)> {
333        let (offset, size) = (self.offset?, self.size?);
334        let start = i128::from(offset);
335        Some((start, start + i128::from(size)))
336    }
337}
338
339/// Which of a function's locals had their address leave it.
340///
341/// Section 8.4 calls this the most valuable interprocedural-flavoured fact available without
342/// interprocedural analysis, because it covers every local a C programmer takes the address of
343/// only to pass one field of, and because a local whose address never escaped cannot be touched
344/// by any call at all.
345///
346/// Section 8.6 says how it goes wrong, which is by missing an escape, and what to do about it.
347/// [`keeps_address`] is a whitelist: an opcode it does not name lets the address out, and so
348/// does an opcode added to the IR after this was written. A blacklist would mean the next person
349/// to add an opcode introduces a miscompilation without touching this file.
350#[derive(Clone, Debug, Default)]
351pub struct Escapes {
352    escaped: HashSet<Inst>,
353}
354
355impl Escapes {
356    /// Works out which locals of this function escaped it.
357    #[must_use]
358    pub fn of(func: &Func) -> Self {
359        let mut escaped = HashSet::new();
360        for block in func.blocks() {
361            for inst in func.insts(block) {
362                let data = func[inst];
363                for (index, &arg) in func[data.args].iter().enumerate() {
364                    if keeps_address(data.opcode, index) {
365                        continue;
366                    }
367                    if let (Origin::Local(local), _) = origin(func, arg) {
368                        escaped.insert(local);
369                    }
370                }
371                // What a branch passes to a block parameter, which is where an address stops
372                // being one this function can follow back to anything.
373                for call in func.successors(inst) {
374                    for &arg in &func[call.args] {
375                        if let (Origin::Local(local), _) = origin(func, arg) {
376                            escaped.insert(local);
377                        }
378                    }
379                }
380            }
381        }
382        Self { escaped }
383    }
384
385    /// Whether the address of this `alloca` left the function.
386    #[must_use]
387    pub fn escaped(&self, local: Inst) -> bool {
388        self.escaped.contains(&local)
389    }
390
391    /// How many locals escaped.
392    #[must_use]
393    pub fn count(&self) -> usize {
394        self.escaped.len()
395    }
396}
397
398/// Whether a use of a pointer at this operand leaves the address inside the function.
399///
400/// A whitelist, per section 8.6, and the reason it is written this way is in [`Escapes`].
401#[must_use]
402pub const fn keeps_address(opcode: Opcode, index: usize) -> bool {
403    match (opcode, index) {
404        // Dereferenced, and the address itself goes nowhere.
405        (Opcode::Load | Opcode::AtomicLoad, 0)
406        | (Opcode::Store | Opcode::AtomicStore, 1)
407        | (Opcode::AtomicRmw | Opcode::Cmpxchg, 0)
408        | (Opcode::Memcpy | Opcode::Memmove, 0 | 1)
409        | (Opcode::Memset | Opcode::Prefetch, 0) => true,
410        // Copied, and the copy's own uses are walked in their turn, because the walk in
411        // [`origin`] goes back through both of these.
412        (Opcode::PtrAdd | Opcode::Bitcast, 0) => true,
413        // Comparing two addresses neither reads them nor keeps them. Note that the answer may
414        // not travel the other way: see [`rucc_ir::Restrict::disjoint`].
415        (Opcode::ICmp, 0 | 1) => true,
416        _ => false,
417    }
418}
419
420/// How many queries each layer answered.
421///
422/// Section 8.5 says these come for free once the answer carries its reason, and section 8.3
423/// wants them, because a layer that answers no on almost nothing is a layer to delete rather
424/// than a layer to improve.
425#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
426pub struct Counts {
427    queries: u64,
428    answered: [u64; Reason::COUNT],
429}
430
431impl Counts {
432    /// How many queries were asked.
433    #[must_use]
434    pub const fn queries(&self) -> u64 {
435        self.queries
436    }
437
438    /// How many of them this layer answered no.
439    #[must_use]
440    pub const fn answered(&self, reason: Reason) -> u64 {
441        self.answered[reason.index()]
442    }
443
444    /// How many were answered no by any layer.
445    #[must_use]
446    pub fn total(&self) -> u64 {
447        self.answered.iter().sum()
448    }
449}
450
451/// The analysis over one function.
452///
453/// It borrows the function because nearly everything it asks is a question about one, and it
454/// borrows an [`Outside`] because the rest is a question about the module: whether two symbols are
455/// two objects, what a callee is declared to do, where a type node sits in the tree, and how wide
456/// an address is. That is a copy of four module facts rather than the module itself, so that a
457/// pass handed `&mut module[id]` can still build this. See [`crate::outside`] for why.
458///
459/// The escape analysis is run once when this is built, since every query may ask it and it is one
460/// walk over the function.
461#[derive(Debug)]
462pub struct Alias<'a> {
463    func: &'a Func,
464    outside: &'a Outside,
465    options: Options,
466    escapes: Escapes,
467    counts: Counts,
468}
469
470impl<'a> Alias<'a> {
471    /// The analysis of this function, with the type-based layer on, which is GCC's `-O2`.
472    #[must_use]
473    pub fn new(func: &'a Func, outside: &'a Outside) -> Self {
474        Self::with(func, outside, Options::default())
475    }
476
477    /// The same, with the type-based layer where the command line left it.
478    #[must_use]
479    pub fn with(func: &'a Func, outside: &'a Outside, options: Options) -> Self {
480        Self { func, outside, options, escapes: Escapes::of(func), counts: Counts::default() }
481    }
482
483    /// Which locals escaped, for a caller that wants the fact on its own.
484    #[must_use]
485    pub const fn escapes(&self) -> &Escapes {
486        &self.escapes
487    }
488
489    /// What each layer has answered so far.
490    #[must_use]
491    pub const fn counts(&self) -> &Counts {
492        &self.counts
493    }
494
495    /// The bytes this instruction reads, if it reads any.
496    #[must_use]
497    pub fn reads(&self, inst: Inst) -> Option<Access> {
498        let data = self.func[inst];
499        let args = &self.func[data.args];
500        let info = self.mem(inst);
501        let (pointer, size) = match data.opcode {
502            Opcode::Load | Opcode::AtomicLoad => (args[0], self.width(self.result_type(inst)?)),
503            // A copy reads its source, which is its second operand, for the size on the access.
504            Opcode::Memcpy | Opcode::Memmove => (args[1], Some(info?.size)),
505            // A read-modify-write reads and writes the same bytes, and the width is the width
506            // of what it operates with.
507            Opcode::AtomicRmw => (args[0], self.width(self.func[args[1]].ty)),
508            Opcode::Cmpxchg => (args[0], self.width(self.func[args[1]].ty)),
509            Opcode::VaObject => (args[0], Some(info?.size)),
510            _ => return None,
511        };
512        Some(self.access(pointer, size, info, data.flags))
513    }
514
515    /// The bytes this instruction writes, if it writes any.
516    #[must_use]
517    pub fn writes(&self, inst: Inst) -> Option<Access> {
518        let data = self.func[inst];
519        let args = &self.func[data.args];
520        let info = self.mem(inst);
521        let (pointer, size) = match data.opcode {
522            Opcode::Store | Opcode::AtomicStore => (args[1], self.width(self.func[args[0]].ty)),
523            Opcode::Memcpy | Opcode::Memmove | Opcode::Memset => (args[0], Some(info?.size)),
524            Opcode::AtomicRmw | Opcode::Cmpxchg => (args[0], self.width(self.func[args[1]].ty)),
525            _ => return None,
526        };
527        Some(self.access(pointer, size, info, data.flags))
528    }
529
530    /// Whether these two references can touch the same byte.
531    pub fn query(&mut self, a: &Access, b: &Access) -> Answer {
532        self.counts.queries += 1;
533        let answer = self.decide(a, b);
534        if let Answer::No(reason) = answer {
535            self.counts.answered[reason.index()] += 1;
536        }
537        answer
538    }
539
540    /// Whether this call can write the bytes the reference covers.
541    ///
542    /// GCC's `call_may_clobber_ref_p_1`. Without interprocedural summaries the honest answer for
543    /// anything whose address escaped is yes, and section 8.4 says so plainly: the full mod and
544    /// ref summary is `ipa-modref`, it is five and a half thousand lines, and it is document
545    /// 34's. What is here is the cheap part of it, which is the attributes a C programmer
546    /// already wrote and the escape analysis.
547    pub fn clobbered_by(&mut self, reference: &Access, call: Inst) -> Answer {
548        self.touched_by(reference, call, true)
549    }
550
551    /// Whether this call can read them.
552    ///
553    /// GCC's `ref_maybe_used_by_call_p_1`, and the same argument as [`Alias::clobbered_by`].
554    pub fn read_by(&mut self, reference: &Access, call: Inst) -> Answer {
555        self.touched_by(reference, call, false)
556    }
557
558    // The layers.
559
560    fn decide(&self, a: &Access, b: &Access) -> Answer {
561        // Section 8.1, and it is first because every layer below would be glad to say no.
562        // Treating two volatile accesses as conflicting is what stops either being moved across
563        // the other, which is the whole of what `volatile` promises.
564        if a.volatile && b.volatile {
565            return Answer::May;
566        }
567
568        // Two objects this function can name. Different objects never alias, and for one object
569        // the offsets settle it on their own.
570        //
571        // The type-based layer is deliberately not reached from here, and that ordering is what
572        // makes union type punning work: writing one member and reading another is two accesses
573        // to one object at overlapping offsets whose types are unrelated, and asking about the
574        // types first would answer no.
575        if a.origin.is_object() && b.origin.is_object() {
576            if self.distinct(a.origin, b.origin) {
577                return Answer::No(Reason::Distinct);
578            }
579            if a.origin == b.origin {
580                return by_offset(a, b);
581            }
582            return Answer::May;
583        }
584
585        // A local whose address never left the function is not what an address this function
586        // cannot follow is pointing at, whatever it is pointing at.
587        if let Some(local) = self.private(a).or_else(|| self.private(b)) {
588            let _ = local;
589            return Answer::No(Reason::Escape);
590        }
591
592        if a.restrict.disjoint(b.restrict) {
593            return Answer::No(Reason::Restrict);
594        }
595
596        if self.options.strict_aliasing {
597            if let (Some(one), Some(other)) = (a.tbaa, b.tbaa) {
598                if !self.types_conflict(one, other) {
599                    return Answer::No(Reason::Tbaa);
600                }
601            }
602        }
603
604        // Two references through one address this function cannot follow, at offsets it can.
605        if a.origin == b.origin {
606            return by_offset(a, b);
607        }
608
609        Answer::May
610    }
611
612    /// The local one of these is a reference to, when it is one nothing outside can reach and
613    /// the other reference is not to it.
614    fn private(&self, reference: &Access) -> Option<Inst> {
615        match reference.origin {
616            Origin::Local(local) if !self.escapes.escaped(local) => Some(local),
617            _ => None,
618        }
619    }
620
621    /// Whether these two origins are two objects.
622    fn distinct(&self, a: Origin, b: Origin) -> bool {
623        match (a, b) {
624            (Origin::Local(one), Origin::Local(other)) => one != other,
625            // Fresh storage this function made is not any named object.
626            (Origin::Local(_), Origin::Global(_)) | (Origin::Global(_), Origin::Local(_)) => true,
627            (Origin::Global(one), Origin::Global(other)) => {
628                one != other && self.one_object(one) && self.one_object(other)
629            }
630            _ => false,
631        }
632    }
633
634    /// Whether this symbol is a name for an object no other name in the module also names.
635    ///
636    /// An `alias` or an `ifunc` is exactly a second name for something, so two different symbols
637    /// can be one object and the rule that two objects do not alias does not reach them. A name
638    /// the module does not have at all is treated the same way, because something is wrong and
639    /// the conservative answer is the one to be wrong in the direction of.
640    fn one_object(&self, name: Symbol) -> bool {
641        self.outside.one_object(name)
642    }
643
644    /// Whether two type nodes can describe the same byte.
645    ///
646    /// They can when one is at or above the other in the tree, which is what makes an access
647    /// through `char` conflict with everything: `char`'s node is the root and every other node
648    /// hangs below it. Two nodes in different parts of the tree describe no object in common.
649    fn types_conflict(&self, one: Meta, other: Meta) -> bool {
650        self.at_or_below(one, other) || self.at_or_below(other, one)
651    }
652
653    /// Whether `node` is `ancestor` or hangs below it.
654    fn at_or_below(&self, mut node: Meta, ancestor: Meta) -> bool {
655        for _ in 0..TREE_LIMIT {
656            if node == ancestor {
657                return true;
658            }
659            match self.outside.parent(node) {
660                Some(up) => node = up,
661                None => return false,
662            }
663        }
664        // A tree deeper than the limit, or a cycle the verifier would have turned down. Either
665        // way the answer that cannot be wrong is that they conflict.
666        true
667    }
668
669    fn touched_by(&mut self, reference: &Access, call: Inst, writing: bool) -> Answer {
670        self.counts.queries += 1;
671        let answer = self.decide_call(reference, call, writing);
672        if let Answer::No(reason) = answer {
673            self.counts.answered[reason.index()] += 1;
674        }
675        answer
676    }
677
678    fn decide_call(&self, reference: &Access, call: Inst, writing: bool) -> Answer {
679        // Not always a call. The memory chain sends everything that touches memory without an
680        // access saying what through here, and the safety instrumentation is most of that: a check
681        // reads a plane and a `meta_` writes one. Neither is memory the program can name, so
682        // neither is what this reference covers, and [`Opcode::touches_only_planes`] is the whole
683        // argument. It is first because it is a match on an opcode and the layers under it are not.
684        if self.func[call].opcode.touches_only_planes() {
685            return Answer::No(Reason::Plane);
686        }
687
688        // Everything a call reaches, it reaches through an address, and an object whose address
689        // never left this function is not one it has. Reaching here means the address was not
690        // handed to this call either, because that would have been an escape.
691        if self.private(reference).is_some() {
692            return Answer::No(Reason::Escape);
693        }
694
695        let Some(attrs) = self.callee(call) else {
696            return Answer::May;
697        };
698        // `const` reads no memory and writes none. `pure` may read and does not write.
699        if attrs.set.contains(AttrSet::READNONE)
700            || (writing && attrs.set.contains(AttrSet::READONLY))
701        {
702            return Answer::No(Reason::Attribute);
703        }
704
705        // Touching nothing except through the pointers it was passed. Every one of those is a
706        // reference of its own, and if none of them can reach these bytes then neither can the
707        // call. The reading is the non-transitive one the attribute's own documentation gives,
708        // which is what makes this sound without a points-to solver behind it: what the callee
709        // may reach by following a pointer it found in the memory it was passed is memory it
710        // was passed.
711        if attrs.set.contains(AttrSet::ARGMEM_ONLY) {
712            let args = &self.func[self.func[call].args];
713            let mut all = true;
714            for &arg in args {
715                if !self.func[arg].ty.is_ptr() {
716                    continue;
717                }
718                let through = Access::through(self.func, arg);
719                all &= self.decide(reference, &through).is_no();
720            }
721            if all {
722                return Answer::No(Reason::Attribute);
723            }
724        }
725
726        Answer::May
727    }
728
729    // Reading the instruction.
730
731    /// What the callee of a direct call is declared to be, for a call whose callee the module
732    /// has. An indirect call and a callee from nowhere both give nothing.
733    fn callee(&self, call: Inst) -> Option<Attrs> {
734        let Extra::Call(info) = self.func[call].extra else {
735            return None;
736        };
737        let name = self.func[info].callee?;
738        self.outside.attrs(name)
739    }
740
741    fn mem(&self, inst: Inst) -> Option<MemInfo> {
742        match self.func[inst].extra {
743            Extra::Mem(info) | Extra::Rmw(_, info) => Some(self.func[info]),
744            Extra::VaObject(object) => Some(self.func[self.func[object].mem]),
745            _ => None,
746        }
747    }
748
749    fn result_type(&self, inst: Inst) -> Option<Type> {
750        self.func[inst].results().next().map(|value| self.func[value].ty)
751    }
752
753    fn access(
754        &self,
755        pointer: Value,
756        size: Option<u64>,
757        info: Option<MemInfo>,
758        flags: Flags,
759    ) -> Access {
760        let (origin, offset) = origin(self.func, pointer);
761        Access {
762            origin,
763            offset,
764            size,
765            tbaa: info.and_then(|info| info.tbaa),
766            restrict: info.map_or(Restrict::NONE, |info| info.restrict),
767            volatile: flags.contains(Flags::VOLATILE),
768        }
769    }
770
771    /// How many bytes a value of this type takes, which for an address is the target's answer
772    /// and not the type's.
773    fn width(&self, ty: Type) -> Option<u64> {
774        if ty.is_ptr() {
775            return self.outside.pointer_bytes();
776        }
777        let bits = u64::from(ty.bits()) * u64::from(ty.lanes());
778        (bits > 0).then(|| bits.div_ceil(8))
779    }
780}
781
782/// Layer 4: one object, two byte ranges.
783fn by_offset(a: &Access, b: &Access) -> Answer {
784    let (Some((a_start, a_end)), Some((b_start, b_end))) = (a.range(), b.range()) else {
785        return Answer::May;
786    };
787    if a_end <= b_start || b_end <= a_start {
788        return Answer::No(Reason::Offset);
789    }
790    Answer::May
791}
792
793/// The value of an integer constant, as a byte count.
794fn constant(func: &Func, value: Value) -> Option<i64> {
795    let Def::Result { inst, .. } = func[value].def else {
796        return None;
797    };
798    let data = func[inst];
799    if data.opcode != Opcode::IConst {
800        return None;
801    }
802    let Extra::Imm(imm) = data.extra else {
803        return None;
804    };
805    i64::try_from(Imm::signed(func[imm], func[value].ty)).ok()
806}
807
808#[cfg(test)]
809mod tests {
810    use rucc_base::{Interner, Symbol};
811    use rucc_ir::{
812        AttrSet, Attrs, Builder, CallInfo, Extra, Flags, Func, Global, InstData, IntPred, MemInfo,
813        MemOrder, MetaNode, Module, Opcode, Restrict, Signature, TbaaNode, Type, Value,
814    };
815    use rucc_target::{TargetInfo, Triple};
816
817    use super::*;
818
819    /// A module for the host-shaped target, and the interner its names are in.
820    fn module(names: &mut Interner) -> Module {
821        let target = TargetInfo::new("x86_64-unknown-linux-gnu".parse::<Triple>().unwrap());
822        Module::new(names.intern("t.c"), &target)
823    }
824
825    /// A function taking those parameters, with an entry block and nothing in it.
826    fn func(names: &mut Interner, params: &[Type]) -> Func {
827        let mut func = Func::new(names.intern("f"), Signature::new().with_params(params));
828        let entry = func.create_block();
829        for &ty in params {
830            func.append_param(entry, ty);
831        }
832        func
833    }
834
835    /// A builder appending to the entry block, which is where every test here puts everything.
836    fn builder(func: &mut Func) -> Builder<'_> {
837        let entry = func.entry().expect("the function has an entry block");
838        Builder::new(func, entry)
839    }
840
841    fn param(func: &Func, index: usize) -> Value {
842        let entry = func.entry().expect("the function has an entry block");
843        func[entry].params[index]
844    }
845
846    fn plain(align: u32) -> MemInfo {
847        MemInfo {
848            size: 0,
849            align,
850            order: MemOrder::NotAtomic,
851            tbaa: None,
852            owns: 0,
853            restrict: Restrict::NONE,
854        }
855    }
856
857    fn sized(size: u64, align: u32) -> MemInfo {
858        MemInfo { size, ..plain(align) }
859    }
860
861    /// An `alloca` of that many bytes in the entry block.
862    fn local(build: &mut Builder<'_>, size: u64) -> Value {
863        let mem = build.func().add_mem(sized(size, 8));
864        build.value(InstData { extra: Extra::Mem(mem), ..InstData::new(Opcode::Alloca) }, Type::PTR)
865    }
866
867    /// That address, moved on by a constant number of bytes.
868    fn at(build: &mut Builder<'_>, base: Value, offset: i64) -> Value {
869        let by = build.iconst(Type::int(64), i128::from(offset));
870        build.binary(Opcode::PtrAdd, base, by, Flags::NONE)
871    }
872
873    /// The address of a global of that name, declared in the module as it goes.
874    fn global(build: &mut Builder<'_>, module: &mut Module, name: Symbol) -> Value {
875        module.add_global(Global::new(name, 16, 8));
876        build.value(
877            InstData { extra: Extra::Symbol(name), ..InstData::new(Opcode::GlobalAddr) },
878            Type::PTR,
879        )
880    }
881
882    #[test]
883    fn two_different_locals_are_two_objects() {
884        let mut names = Interner::new();
885        let module = module(&mut names);
886        let mut f = func(&mut names, &[]);
887        let mut build = builder(&mut f);
888        let one = local(&mut build, 16);
889        let other = local(&mut build, 16);
890        let read = build.load(Type::int(32), one, plain(4), Flags::NONE);
891        build.store(read, other, plain(4), Flags::NONE);
892        build.ret(&[]);
893
894        let outside = Outside::of(&module);
895        let mut alias = Alias::new(&f, &outside);
896        let (a, b) = two(&alias, &f);
897        assert_eq!(alias.query(&a, &b), Answer::No(Reason::Distinct));
898        assert_eq!(alias.counts().answered(Reason::Distinct), 1);
899        assert_eq!(alias.counts().queries(), 1);
900    }
901
902    /// The reference the first load in the function reads and the one the first store writes.
903    fn two(alias: &Alias<'_>, func: &Func) -> (Access, Access) {
904        let mut read = None;
905        let mut written = None;
906        for block in func.blocks() {
907            for inst in func.insts(block) {
908                if read.is_none() {
909                    read = alias.reads(inst);
910                }
911                if written.is_none() {
912                    written = alias.writes(inst);
913                }
914            }
915        }
916        (read.expect("a read"), written.expect("a write"))
917    }
918
919    #[test]
920    fn a_local_and_a_global_are_two_objects() {
921        let mut names = Interner::new();
922        let mut module = module(&mut names);
923        let x = names.intern("x");
924        let mut f = func(&mut names, &[]);
925        let mut build = builder(&mut f);
926        let one = local(&mut build, 16);
927        let other = global(&mut build, &mut module, x);
928        let read = build.load(Type::int(32), one, plain(4), Flags::NONE);
929        build.store(read, other, plain(4), Flags::NONE);
930        build.ret(&[]);
931
932        let outside = Outside::of(&module);
933        let mut alias = Alias::new(&f, &outside);
934        let (a, b) = two(&alias, &f);
935        assert_eq!(alias.query(&a, &b), Answer::No(Reason::Distinct));
936    }
937
938    #[test]
939    fn two_different_globals_are_two_objects() {
940        let mut names = Interner::new();
941        let mut module = module(&mut names);
942        let (x, y) = (names.intern("x"), names.intern("y"));
943        let mut f = func(&mut names, &[]);
944        let mut build = builder(&mut f);
945        let one = global(&mut build, &mut module, x);
946        let other = global(&mut build, &mut module, y);
947        let read = build.load(Type::int(32), one, plain(4), Flags::NONE);
948        build.store(read, other, plain(4), Flags::NONE);
949        build.ret(&[]);
950
951        let outside = Outside::of(&module);
952        let mut alias = Alias::new(&f, &outside);
953        let (a, b) = two(&alias, &f);
954        assert_eq!(alias.query(&a, &b), Answer::No(Reason::Distinct));
955    }
956
957    #[test]
958    fn a_global_the_module_does_not_have_is_not_argued_about() {
959        // Nothing should produce this, and if something does, the answer that cannot be wrong
960        // is that the two may alias.
961        let mut names = Interner::new();
962        let mut module = module(&mut names);
963        let (x, y) = (names.intern("x"), names.intern("y"));
964        let mut f = func(&mut names, &[]);
965        let mut build = builder(&mut f);
966        let one = global(&mut build, &mut module, x);
967        let other = build.value(
968            InstData { extra: Extra::Symbol(y), ..InstData::new(Opcode::GlobalAddr) },
969            Type::PTR,
970        );
971        let read = build.load(Type::int(32), one, plain(4), Flags::NONE);
972        build.store(read, other, plain(4), Flags::NONE);
973        build.ret(&[]);
974
975        let outside = Outside::of(&module);
976        let mut alias = Alias::new(&f, &outside);
977        let (a, b) = two(&alias, &f);
978        assert_eq!(alias.query(&a, &b), Answer::May);
979    }
980
981    #[test]
982    fn two_parts_of_one_object_that_do_not_overlap_are_disjoint() {
983        let mut names = Interner::new();
984        let module = module(&mut names);
985        let mut f = func(&mut names, &[]);
986        let mut build = builder(&mut f);
987        let object = local(&mut build, 16);
988        let first = at(&mut build, object, 0);
989        let second = at(&mut build, object, 4);
990        let read = build.load(Type::int(32), first, plain(4), Flags::NONE);
991        build.store(read, second, plain(4), Flags::NONE);
992        build.ret(&[]);
993
994        let outside = Outside::of(&module);
995        let mut alias = Alias::new(&f, &outside);
996        let (a, b) = two(&alias, &f);
997        assert_eq!(alias.query(&a, &b), Answer::No(Reason::Offset));
998    }
999
1000    #[test]
1001    fn two_parts_of_one_object_that_do_overlap_are_not() {
1002        let mut names = Interner::new();
1003        let module = module(&mut names);
1004        let mut f = func(&mut names, &[]);
1005        let mut build = builder(&mut f);
1006        let object = local(&mut build, 16);
1007        let first = at(&mut build, object, 0);
1008        let second = at(&mut build, object, 2);
1009        let read = build.load(Type::int(32), first, plain(4), Flags::NONE);
1010        build.store(read, second, plain(4), Flags::NONE);
1011        build.ret(&[]);
1012
1013        let outside = Outside::of(&module);
1014        let mut alias = Alias::new(&f, &outside);
1015        let (a, b) = two(&alias, &f);
1016        assert_eq!(alias.query(&a, &b), Answer::May);
1017    }
1018
1019    #[test]
1020    fn an_offset_nobody_knows_gives_up_the_offset_and_keeps_the_object() {
1021        let mut names = Interner::new();
1022        let module = module(&mut names);
1023        let mut f = func(&mut names, &[Type::int(64)]);
1024        let n = param(&f, 0);
1025        let mut build = builder(&mut f);
1026        let object = local(&mut build, 16);
1027        let somewhere = build.binary(Opcode::PtrAdd, object, n, Flags::NONE);
1028        let read = build.load(Type::int(32), somewhere, plain(4), Flags::NONE);
1029        build.store(read, object, plain(4), Flags::NONE);
1030        build.ret(&[]);
1031
1032        let outside = Outside::of(&module);
1033        let mut alias = Alias::new(&f, &outside);
1034        let (a, b) = two(&alias, &f);
1035        assert_eq!(a.origin, b.origin, "both are still that one object");
1036        assert_eq!(a.offset, None);
1037        assert_eq!(alias.query(&a, &b), Answer::May);
1038    }
1039
1040    #[test]
1041    fn a_local_whose_address_stays_here_is_not_what_a_parameter_points_at() {
1042        let mut names = Interner::new();
1043        let module = module(&mut names);
1044        let mut f = func(&mut names, &[Type::PTR]);
1045        let outside = param(&f, 0);
1046        let mut build = builder(&mut f);
1047        let object = local(&mut build, 16);
1048        let read = build.load(Type::int(32), object, plain(4), Flags::NONE);
1049        build.store(read, outside, plain(4), Flags::NONE);
1050        build.ret(&[]);
1051
1052        let outside = Outside::of(&module);
1053        let mut alias = Alias::new(&f, &outside);
1054        assert_eq!(alias.escapes().count(), 0);
1055        let (a, b) = two(&alias, &f);
1056        assert_eq!(alias.query(&a, &b), Answer::No(Reason::Escape));
1057    }
1058
1059    #[test]
1060    fn a_local_whose_address_was_stored_somewhere_is() {
1061        let mut names = Interner::new();
1062        let module = module(&mut names);
1063        let mut f = func(&mut names, &[Type::PTR]);
1064        let outside = param(&f, 0);
1065        let mut build = builder(&mut f);
1066        let object = local(&mut build, 16);
1067        // The address itself is written out through a pointer this function did not make, and
1068        // from here anything can reach the object.
1069        build.store(object, outside, plain(8), Flags::NONE);
1070        let read = build.load(Type::int(32), object, plain(4), Flags::NONE);
1071        build.store(read, outside, plain(4), Flags::NONE);
1072        build.ret(&[]);
1073
1074        let outside = Outside::of(&module);
1075        let mut alias = Alias::new(&f, &outside);
1076        assert_eq!(alias.escapes().count(), 1);
1077        let read = first(&f, Opcode::Load);
1078        let write = last(&f, Opcode::Store);
1079        let a = alias.reads(read).unwrap();
1080        let b = alias.writes(write).unwrap();
1081        assert_eq!(alias.query(&a, &b), Answer::May);
1082    }
1083
1084    fn first(func: &Func, opcode: Opcode) -> Inst {
1085        func.blocks()
1086            .flat_map(|block| func.insts(block))
1087            .find(|&inst| func[inst].opcode == opcode)
1088            .expect("an instruction with that opcode")
1089    }
1090
1091    fn last(func: &Func, opcode: Opcode) -> Inst {
1092        func.blocks()
1093            .flat_map(|block| func.insts(block))
1094            .filter(|&inst| func[inst].opcode == opcode)
1095            .last()
1096            .expect("an instruction with that opcode")
1097    }
1098
1099    #[test]
1100    fn an_address_carried_through_a_block_parameter_has_left_the_function() {
1101        let mut names = Interner::new();
1102        let module = module(&mut names);
1103        let mut f = func(&mut names, &[]);
1104        let start = f.entry().expect("an entry block");
1105        let next = f.create_block();
1106        f.append_param(next, Type::PTR);
1107
1108        let mut build = Builder::new(&mut f, start);
1109        let object = local(&mut build, 16);
1110        build.jump(next, &[object]);
1111        let mut build = Builder::new(&mut f, next);
1112        build.ret(&[]);
1113
1114        let outside = Outside::of(&module);
1115        let alias = Alias::new(&f, &outside);
1116        assert!(alias.escapes().escaped(first(&f, Opcode::Alloca)));
1117    }
1118
1119    #[test]
1120    fn comparing_two_addresses_does_not_let_either_of_them_out() {
1121        let mut names = Interner::new();
1122        let module = module(&mut names);
1123        let mut f = func(&mut names, &[Type::PTR]);
1124        let outside = param(&f, 0);
1125        let mut build = builder(&mut f);
1126        let object = local(&mut build, 16);
1127        build.icmp(IntPred::Eq, object, outside);
1128        build.ret(&[]);
1129
1130        let outside = Outside::of(&module);
1131        let alias = Alias::new(&f, &outside);
1132        assert_eq!(alias.escapes().count(), 0);
1133    }
1134
1135    #[test]
1136    fn an_address_turned_into_a_number_has_left_the_function() {
1137        // The number can be turned back into a pointer anywhere, including in a different
1138        // translation unit, so this is an escape and the whitelist is what makes it one.
1139        let mut names = Interner::new();
1140        let module = module(&mut names);
1141        let mut f = func(&mut names, &[]);
1142        let mut build = builder(&mut f);
1143        let object = local(&mut build, 16);
1144        build.unary(Opcode::PtrToInt, object, Type::int(64));
1145        build.ret(&[]);
1146
1147        let outside = Outside::of(&module);
1148        let alias = Alias::new(&f, &outside);
1149        assert!(alias.escapes().escaped(first(&f, Opcode::Alloca)));
1150    }
1151
1152    #[test]
1153    fn two_restrict_pointers_in_one_scope_do_not_reach_the_same_object() {
1154        let mut names = Interner::new();
1155        let module = module(&mut names);
1156        let mut f = func(&mut names, &[Type::PTR, Type::PTR]);
1157        let (one, other) = (param(&f, 0), param(&f, 1));
1158        let mut build = builder(&mut f);
1159        let mut info = plain(4);
1160        info.restrict = Restrict { clique: 1, base: 1 };
1161        let read = build.load(Type::int(32), one, info, Flags::NONE);
1162        info.restrict = Restrict { clique: 1, base: 2 };
1163        build.store(read, other, info, Flags::NONE);
1164        build.ret(&[]);
1165
1166        let outside = Outside::of(&module);
1167        let mut alias = Alias::new(&f, &outside);
1168        let (a, b) = two(&alias, &f);
1169        assert_eq!(alias.query(&a, &b), Answer::No(Reason::Restrict));
1170    }
1171
1172    #[test]
1173    fn two_restrict_pointers_in_different_scopes_say_nothing_about_each_other() {
1174        let mut names = Interner::new();
1175        let module = module(&mut names);
1176        let mut f = func(&mut names, &[Type::PTR, Type::PTR]);
1177        let (one, other) = (param(&f, 0), param(&f, 1));
1178        let mut build = builder(&mut f);
1179        let mut info = plain(4);
1180        info.restrict = Restrict { clique: 1, base: 1 };
1181        let read = build.load(Type::int(32), one, info, Flags::NONE);
1182        info.restrict = Restrict { clique: 2, base: 1 };
1183        build.store(read, other, info, Flags::NONE);
1184        build.ret(&[]);
1185
1186        let outside = Outside::of(&module);
1187        let mut alias = Alias::new(&f, &outside);
1188        let (a, b) = two(&alias, &f);
1189        assert_eq!(alias.query(&a, &b), Answer::May);
1190    }
1191
1192    /// A module with a `char` root and an `int` and a `float` hanging off it.
1193    fn types(module: &mut Module, names: &mut Interner) -> (Meta, Meta, Meta) {
1194        let root = module.add_meta(MetaNode::Tbaa(TbaaNode {
1195            name: names.intern("char"),
1196            parent: None,
1197            offset: 0,
1198        }));
1199        let int = module.add_meta(MetaNode::Tbaa(TbaaNode {
1200            name: names.intern("int"),
1201            parent: Some(root),
1202            offset: 0,
1203        }));
1204        let float = module.add_meta(MetaNode::Tbaa(TbaaNode {
1205            name: names.intern("float"),
1206            parent: Some(root),
1207            offset: 0,
1208        }));
1209        (root, int, float)
1210    }
1211
1212    #[test]
1213    fn two_unrelated_types_describe_no_object_in_common() {
1214        let mut names = Interner::new();
1215        let mut module = module(&mut names);
1216        let (_, int, float) = types(&mut module, &mut names);
1217        let mut f = func(&mut names, &[Type::PTR, Type::PTR]);
1218        let (one, other) = (param(&f, 0), param(&f, 1));
1219        let mut build = builder(&mut f);
1220        let mut info = plain(4);
1221        info.tbaa = Some(int);
1222        let read = build.load(Type::int(32), one, info, Flags::NONE);
1223        info.tbaa = Some(float);
1224        build.store(read, other, info, Flags::NONE);
1225        build.ret(&[]);
1226
1227        let outside = Outside::of(&module);
1228        let mut alias = Alias::new(&f, &outside);
1229        let (a, b) = two(&alias, &f);
1230        assert_eq!(alias.query(&a, &b), Answer::No(Reason::Tbaa));
1231    }
1232
1233    #[test]
1234    fn an_access_through_char_conflicts_with_everything() {
1235        let mut names = Interner::new();
1236        let mut module = module(&mut names);
1237        let (root, int, _) = types(&mut module, &mut names);
1238        let mut f = func(&mut names, &[Type::PTR, Type::PTR]);
1239        let (one, other) = (param(&f, 0), param(&f, 1));
1240        let mut build = builder(&mut f);
1241        let mut info = plain(4);
1242        info.tbaa = Some(int);
1243        let read = build.load(Type::int(32), one, info, Flags::NONE);
1244        info.tbaa = Some(root);
1245        build.store(read, other, info, Flags::NONE);
1246        build.ret(&[]);
1247
1248        let outside = Outside::of(&module);
1249        let mut alias = Alias::new(&f, &outside);
1250        let (a, b) = two(&alias, &f);
1251        assert_eq!(alias.query(&a, &b), Answer::May);
1252    }
1253
1254    #[test]
1255    fn turning_strict_aliasing_off_turns_off_that_layer_and_no_other() {
1256        let mut names = Interner::new();
1257        let mut module = module(&mut names);
1258        let (_, int, float) = types(&mut module, &mut names);
1259        let mut f = func(&mut names, &[Type::PTR, Type::PTR]);
1260        let (one, other) = (param(&f, 0), param(&f, 1));
1261        let mut build = builder(&mut f);
1262        let mut info = plain(4);
1263        info.tbaa = Some(int);
1264        info.restrict = Restrict { clique: 1, base: 1 };
1265        let read = build.load(Type::int(32), one, info, Flags::NONE);
1266        info.tbaa = Some(float);
1267        info.restrict = Restrict { clique: 1, base: 2 };
1268        build.store(read, other, info, Flags::NONE);
1269        build.ret(&[]);
1270
1271        let options = Options { strict_aliasing: false };
1272        let outside = Outside::of(&module);
1273        let mut alias = Alias::with(&f, &outside, options);
1274        let (a, b) = two(&alias, &f);
1275        // The `restrict` layer still answers, which is the point: the flag is one condition in
1276        // one place and it does not reach anything else.
1277        assert_eq!(alias.query(&a, &b), Answer::No(Reason::Restrict));
1278
1279        let mut without = Alias::with(&f, &outside, options);
1280        let plainer = Access { restrict: Restrict::NONE, ..a };
1281        let other = Access { restrict: Restrict::NONE, ..b };
1282        assert_eq!(without.query(&plainer, &other), Answer::May);
1283
1284        let mut with = Alias::new(&f, &outside);
1285        assert_eq!(with.query(&plainer, &other), Answer::No(Reason::Tbaa));
1286    }
1287
1288    #[test]
1289    fn writing_one_member_of_a_union_and_reading_another_is_one_object() {
1290        // The compatibility fact of section 8.6. Two accesses to one object at the same offset
1291        // with unrelated types, which is `union { int i; float f; }` written as one and read as
1292        // the other. The offset layer runs first, it says they overlap, and the type layer
1293        // never gets to say no. Twenty years of real C rests on this answer.
1294        let mut names = Interner::new();
1295        let mut module = module(&mut names);
1296        let (_, int, float) = types(&mut module, &mut names);
1297        let mut f = func(&mut names, &[]);
1298        let mut build = builder(&mut f);
1299        let object = local(&mut build, 4);
1300        let mut info = plain(4);
1301        info.tbaa = Some(float);
1302        let read = build.load(Type::int(32), object, info, Flags::NONE);
1303        info.tbaa = Some(int);
1304        build.store(read, object, info, Flags::NONE);
1305        build.ret(&[]);
1306
1307        let outside = Outside::of(&module);
1308        let mut alias = Alias::new(&f, &outside);
1309        let (a, b) = two(&alias, &f);
1310        assert_eq!(alias.query(&a, &b), Answer::May);
1311    }
1312
1313    #[test]
1314    fn two_volatile_accesses_conflict_whatever_else_is_true_of_them() {
1315        let mut names = Interner::new();
1316        let module = module(&mut names);
1317        let mut f = func(&mut names, &[]);
1318        let mut build = builder(&mut f);
1319        let one = local(&mut build, 16);
1320        let other = local(&mut build, 16);
1321        let read = build.load(Type::int(32), one, plain(4), Flags::VOLATILE);
1322        build.store(read, other, plain(4), Flags::VOLATILE);
1323        build.ret(&[]);
1324
1325        let outside = Outside::of(&module);
1326        let mut alias = Alias::new(&f, &outside);
1327        let (a, b) = two(&alias, &f);
1328        // Two different objects, and the answer is still that they conflict, because moving
1329        // one volatile access across another is the thing `volatile` exists to forbid.
1330        assert_eq!(alias.query(&a, &b), Answer::May);
1331    }
1332
1333    #[test]
1334    fn one_volatile_access_and_one_ordinary_one_are_argued_about_as_usual() {
1335        let mut names = Interner::new();
1336        let module = module(&mut names);
1337        let mut f = func(&mut names, &[]);
1338        let mut build = builder(&mut f);
1339        let one = local(&mut build, 16);
1340        let other = local(&mut build, 16);
1341        let read = build.load(Type::int(32), one, plain(4), Flags::VOLATILE);
1342        build.store(read, other, plain(4), Flags::NONE);
1343        build.ret(&[]);
1344
1345        let outside = Outside::of(&module);
1346        let mut alias = Alias::new(&f, &outside);
1347        let (a, b) = two(&alias, &f);
1348        assert_eq!(alias.query(&a, &b), Answer::No(Reason::Distinct));
1349    }
1350
1351    #[test]
1352    fn a_copy_reads_its_source_and_writes_its_destination() {
1353        let mut names = Interner::new();
1354        let module = module(&mut names);
1355        let mut f = func(&mut names, &[]);
1356        let mut build = builder(&mut f);
1357        let to = local(&mut build, 16);
1358        let from = local(&mut build, 16);
1359        let mem = build.func().add_mem(sized(16, 8));
1360        let args = build.func().push_values(&[to, from]);
1361        build.inst(InstData { args, extra: Extra::Mem(mem), ..InstData::new(Opcode::Memcpy) }, &[]);
1362        build.ret(&[]);
1363
1364        let outside = Outside::of(&module);
1365        let alias = Alias::new(&f, &outside);
1366        let copy = first(&f, Opcode::Memcpy);
1367        let read = alias.reads(copy).expect("a copy reads");
1368        let written = alias.writes(copy).expect("a copy writes");
1369        assert_eq!(read.size, Some(16));
1370        assert_eq!(written.size, Some(16));
1371        assert_ne!(read.origin, written.origin);
1372    }
1373
1374    /// A call to a function declared with those attributes.
1375    fn call_to(
1376        names: &mut Interner,
1377        module: &mut Module,
1378        f: &mut Func,
1379        attrs: Attrs,
1380        args: &[Value],
1381    ) -> Inst {
1382        let name = names.intern("g");
1383        let params: Vec<Type> = args.iter().map(|_| Type::PTR).collect();
1384        let mut callee = Func::new(name, Signature::new().with_params(&params));
1385        callee.attrs = attrs;
1386        module.add_func(callee);
1387        let signature = f.add_signature(Signature::new().with_params(&params));
1388        let mut build = builder(f);
1389        build.call(name, signature, args)
1390    }
1391
1392    fn attrs(set: AttrSet) -> Attrs {
1393        Attrs { set, ..Attrs::NONE }
1394    }
1395
1396    #[test]
1397    fn a_call_cannot_touch_a_local_whose_address_stayed_here() {
1398        let mut names = Interner::new();
1399        let mut module = module(&mut names);
1400        let mut f = func(&mut names, &[Type::PTR]);
1401        let outside = param(&f, 0);
1402        let mut build = builder(&mut f);
1403        let object = local(&mut build, 16);
1404        let read = build.load(Type::int(32), object, plain(4), Flags::NONE);
1405        let _ = read;
1406        let call = call_to(&mut names, &mut module, &mut f, Attrs::NONE, &[outside]);
1407        let mut build = builder(&mut f);
1408        build.ret(&[]);
1409
1410        let outside = Outside::of(&module);
1411        let mut alias = Alias::new(&f, &outside);
1412        let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1413        assert_eq!(alias.clobbered_by(&reference, call), Answer::No(Reason::Escape));
1414        assert_eq!(alias.read_by(&reference, call), Answer::No(Reason::Escape));
1415    }
1416
1417    #[test]
1418    fn a_call_can_touch_a_local_it_was_handed() {
1419        let mut names = Interner::new();
1420        let mut module = module(&mut names);
1421        let mut f = func(&mut names, &[]);
1422        let mut build = builder(&mut f);
1423        let object = local(&mut build, 16);
1424        build.load(Type::int(32), object, plain(4), Flags::NONE);
1425        let call = call_to(&mut names, &mut module, &mut f, Attrs::NONE, &[object]);
1426        let mut build = builder(&mut f);
1427        build.ret(&[]);
1428
1429        let outside = Outside::of(&module);
1430        let mut alias = Alias::new(&f, &outside);
1431        let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1432        assert_eq!(alias.clobbered_by(&reference, call), Answer::May);
1433    }
1434
1435    #[test]
1436    fn a_pure_callee_reads_memory_and_writes_none() {
1437        let mut names = Interner::new();
1438        let mut module = module(&mut names);
1439        let mut f = func(&mut names, &[Type::PTR]);
1440        let outside = param(&f, 0);
1441        let mut build = builder(&mut f);
1442        build.load(Type::int(32), outside, plain(4), Flags::NONE);
1443        let call = call_to(&mut names, &mut module, &mut f, attrs(AttrSet::READONLY), &[outside]);
1444        let mut build = builder(&mut f);
1445        build.ret(&[]);
1446
1447        let outside = Outside::of(&module);
1448        let mut alias = Alias::new(&f, &outside);
1449        let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1450        assert_eq!(alias.clobbered_by(&reference, call), Answer::No(Reason::Attribute));
1451        assert_eq!(alias.read_by(&reference, call), Answer::May);
1452    }
1453
1454    #[test]
1455    fn a_plane_write_is_not_a_write_to_the_address_it_names() {
1456        // The one that was costing the memory passes everything on a safety build. `meta_init %p`
1457        // writes the entry the lifetime plane keeps for `%p`, and the oracle reading its operand
1458        // the ordinary way sees a write to exactly the bytes a load of `%p` wants, which is the
1459        // worst possible wrong answer: the instrumentation blocking the optimization of the code
1460        // it was put in to check.
1461        let mut names = Interner::new();
1462        let module = module(&mut names);
1463        let mut f = func(&mut names, &[Type::PTR]);
1464        let outside = param(&f, 0);
1465        let mut build = builder(&mut f);
1466        build.load(Type::int(32), outside, plain(4), Flags::NONE);
1467        let width = build.iconst(Type::int(64), 4);
1468        let args = build.func().push_values(&[outside, width]);
1469        build.inst(InstData { args, ..InstData::new(Opcode::MetaInit) }, &[]);
1470        build.ret(&[]);
1471
1472        let outside = Outside::of(&module);
1473        let mut alias = Alias::new(&f, &outside);
1474        let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1475        let plane = first(&f, Opcode::MetaInit);
1476        assert_eq!(alias.clobbered_by(&reference, plane), Answer::No(Reason::Plane));
1477        // And it does not read it either, so a store the program made is not kept alive by one.
1478        assert_eq!(alias.read_by(&reference, plane), Answer::No(Reason::Plane));
1479    }
1480
1481    #[test]
1482    fn a_check_reads_a_plane_and_not_what_it_is_about() {
1483        // The reading half of the same fact, which is what a walk back over memory runs into
1484        // first: a check between a store and a load of the same address is on the chain, and
1485        // answering `May` for it is a load kept for no reason.
1486        let mut names = Interner::new();
1487        let module = module(&mut names);
1488        let mut f = func(&mut names, &[Type::PTR]);
1489        let outside = param(&f, 0);
1490        let mut build = builder(&mut f);
1491        build.load(Type::int(32), outside, plain(4), Flags::NONE);
1492        let width = build.iconst(Type::int(64), 4);
1493        let args = build.func().push_values(&[outside, width]);
1494        build.inst(InstData { args, ..InstData::new(Opcode::CheckBounds) }, &[]);
1495        build.ret(&[]);
1496
1497        let outside = Outside::of(&module);
1498        let mut alias = Alias::new(&f, &outside);
1499        let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1500        let check = first(&f, Opcode::CheckBounds);
1501        assert_eq!(alias.clobbered_by(&reference, check), Answer::No(Reason::Plane));
1502        assert_eq!(alias.read_by(&reference, check), Answer::No(Reason::Plane));
1503    }
1504
1505    #[test]
1506    fn a_const_callee_touches_no_memory_at_all() {
1507        let mut names = Interner::new();
1508        let mut module = module(&mut names);
1509        let mut f = func(&mut names, &[Type::PTR]);
1510        let outside = param(&f, 0);
1511        let mut build = builder(&mut f);
1512        build.load(Type::int(32), outside, plain(4), Flags::NONE);
1513        let call = call_to(&mut names, &mut module, &mut f, attrs(AttrSet::READNONE), &[outside]);
1514        let mut build = builder(&mut f);
1515        build.ret(&[]);
1516
1517        let outside = Outside::of(&module);
1518        let mut alias = Alias::new(&f, &outside);
1519        let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1520        assert_eq!(alias.clobbered_by(&reference, call), Answer::No(Reason::Attribute));
1521        assert_eq!(alias.read_by(&reference, call), Answer::No(Reason::Attribute));
1522    }
1523
1524    #[test]
1525    fn a_callee_that_touches_only_its_arguments_leaves_a_global_it_was_not_passed_alone() {
1526        let mut names = Interner::new();
1527        let mut module = module(&mut names);
1528        let x = names.intern("x");
1529        let mut f = func(&mut names, &[Type::PTR]);
1530        let outside = param(&f, 0);
1531        let mut build = builder(&mut f);
1532        let object = global(&mut build, &mut module, x);
1533        build.load(Type::int(32), object, plain(4), Flags::NONE);
1534        let call =
1535            call_to(&mut names, &mut module, &mut f, attrs(AttrSet::ARGMEM_ONLY), &[outside]);
1536        let mut build = builder(&mut f);
1537        build.ret(&[]);
1538
1539        let outside = Outside::of(&module);
1540        let mut alias = Alias::new(&f, &outside);
1541        let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1542        // The one pointer it was handed is a parameter of unknown origin, which may be that
1543        // global, so this is the answer that cannot be wrong.
1544        assert_eq!(alias.clobbered_by(&reference, call), Answer::May);
1545    }
1546
1547    #[test]
1548    fn a_callee_that_touches_only_its_arguments_and_was_handed_one_object_leaves_the_other() {
1549        let mut names = Interner::new();
1550        let mut module = module(&mut names);
1551        let (x, y) = (names.intern("x"), names.intern("y"));
1552        let mut f = func(&mut names, &[]);
1553        let mut build = builder(&mut f);
1554        let watched = global(&mut build, &mut module, x);
1555        let handed = global(&mut build, &mut module, y);
1556        build.load(Type::int(32), watched, plain(4), Flags::NONE);
1557        let call = call_to(&mut names, &mut module, &mut f, attrs(AttrSet::ARGMEM_ONLY), &[handed]);
1558        let mut build = builder(&mut f);
1559        build.ret(&[]);
1560
1561        let outside = Outside::of(&module);
1562        let mut alias = Alias::new(&f, &outside);
1563        let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1564        assert_eq!(alias.clobbered_by(&reference, call), Answer::No(Reason::Attribute));
1565    }
1566
1567    #[test]
1568    fn an_indirect_call_is_not_argued_about() {
1569        let mut names = Interner::new();
1570        let module = module(&mut names);
1571        let mut f = func(&mut names, &[Type::PTR, Type::PTR]);
1572        let (target, outside) = (param(&f, 0), param(&f, 1));
1573        let mut build = builder(&mut f);
1574        build.load(Type::int(32), outside, plain(4), Flags::NONE);
1575        let signature = build.func().add_signature(Signature::new().with_params(&[Type::PTR]));
1576        let varargs = build.func().push_abis(&[]);
1577        let info = build.func().add_call(CallInfo { callee: None, signature, varargs });
1578        let args = build.func().push_values(&[target, outside]);
1579        let call = build.inst(
1580            InstData { args, extra: Extra::Call(info), ..InstData::new(Opcode::CallIndirect) },
1581            &[],
1582        );
1583        build.ret(&[]);
1584
1585        let outside = Outside::of(&module);
1586        let mut alias = Alias::new(&f, &outside);
1587        let reference = alias.reads(first(&f, Opcode::Load)).unwrap();
1588        assert_eq!(alias.clobbered_by(&reference, call), Answer::May);
1589    }
1590
1591    #[test]
1592    fn every_reason_has_a_name_and_a_sentence() {
1593        for reason in Reason::ALL {
1594            assert!(!reason.name().is_empty());
1595            assert!(!reason.describe().is_empty());
1596            assert_eq!(Reason::ALL[reason.index()], reason);
1597        }
1598        assert_eq!(Reason::ALL.len(), Reason::COUNT);
1599        assert_eq!(Answer::No(Reason::Offset).reason(), Some(Reason::Offset));
1600        assert!(Answer::No(Reason::Offset).is_no());
1601        assert_eq!(Answer::May.reason(), None);
1602        assert!(!Answer::May.is_no());
1603    }
1604}