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