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