Expand description
Alias analysis: whether two memory references can touch the same byte.
Design: spec/optimizer/08-alias-analysis.md, with the switch section 41.9 of
spec/optimizer/41-correctness.md asks for.
§The one question
There is one primitive and everything else is built on it. Given two memory references, can they touch the same byte. Every memory optimization documents 16, 17 and 27 will bring is gated on it, an answer that is too conservative costs performance quietly and forever, and an answer that is too aggressive miscompiles in the way that produces a bug report three years later from somebody whose program worked on every other compiler.
So the answer is an Answer, which is either Answer::May or a no carrying the layer
that concluded it. Section 8.5 asks for that and it is the best decision in spec 9.4: a
miscompilation from an alias bug is localised to one layer rather than bisected across the
whole analysis, the layer statistics come for free, and a user asking why something was not
optimized gets a real answer. It costs one byte in a return value that was going in a
register anyway.
§The layers, in the order they run
Section 8.2 lists six. This is the first five, and the order they run in is load bearing.
Two volatile accesses conflict, and that is checked before anything else. Not may
conflict: they are treated as conflicting so that neither can be moved across the other,
which is what volatile is for.
Distinct storage and provenance, layers 1 and 2, are one walk here because the IR names
the object a pointer came from. origin chases a pointer back through ptr_add and
bitcast to the alloca or the global_addr it started at, and two different objects never
alias. GCC gets the same answer less directly, out of tracking base declarations through a
tree walk. This layer answers a startling fraction of the queries real code asks and it is
the only one -O1 needs.
Offsets, layer 4, run next and only for two references to the same object, and running
them before the type-based layer rather than after is the whole of what makes union type
punning work. Writing through one member of a union and reading another is two accesses to
one object at overlapping offsets with unrelated types. It is undefined in ISO C, it is
defined by GCC, an enormous amount of real C rests on it, and a layer that asked about the
types first would answer no and miscompile all of it. GCC’s comment at
gcc/tree-ssa-alias.cc:2461 says exactly this and rucc reproduces the ordering rather than
the accident.
Escape, which section 8.4 counts as the cheapest interprocedural-flavoured fact there is: a local whose address never leaves the function is not the object some pointer this function cannot follow is pointing at, and it is not one a call can touch either.
restrict, layer 5, is two small numbers on the access and one comparison, which is all
GCC’s is. See rucc_ir::Restrict, including the trap.
Type-based aliasing, layer 3, runs last of the five. Two accesses conflict when one of
their type nodes is at or above the other in the metadata tree, so an access through char,
whose node is the root, conflicts with everything. -fno-strict-aliasing is one condition in
one place, Options::strict_aliasing, which is what section 41.9 means by the flag having
to actually work.
Layer 6 is points-to, and it is not here. It is a module-wide fixed point rather than a fact
the IR already carries, section 8.3 has an open question about which solver it should be, and
section 8.6 is emphatic that provenance and points-to are different things that must not be
confused. So Origin is provenance, there is no points-to type for it to be converted
into, and the solver lands separately with the constraint generator split out from it the way
GCC 16 split its own.
§What the front end still owes this
Layers 3 and 5 read fields the front end fills in during lowering, and lowering does not fill
them in yet: every access carries no type node and no restrict clique today. The layers are
here, they are tested, and they answer correctly for the accesses that do carry them. Giving
them something to read is the next piece of work, and section 8.2 says what it has to be
careful about, which is that an alias set is derived from a canonical encoding of the type
and never from allocation order, or document 35’s LTO silently gains disambiguations.
Structs§
- Access
- One memory reference: which bytes an instruction touches and what it says about them.
- Alias
- The analysis over one function.
- Counts
- How many queries each layer answered.
- Escapes
- Which of a function’s locals had their address leave it.
- Options
- What the command line turns off.
Enums§
- Answer
- What the analysis answers.
- Origin
- Where a pointer came from, as far as this function can tell.
- Reason
- Which rule concluded that two references cannot touch the same byte.
Functions§
- keeps_
address - Whether a use of a pointer at this operand leaves the address inside the function.
- origin
- Where a pointer came from, and how many bytes past the start of it the pointer is.