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