This encourages AccessPathWalker clients to handle enclosing mark_deps. In
some cases, it is necessary. The accessBaseWithScopes API now provides both
nested begin_access and mark_dependence.
And simplify enclosingAccessScope.
These two APIs now directly compute what is being asked for. Previously, each
invocation of enclosingAccessScope would recompute the base and all nested
scopes, but throw away that information. Sometimes we do not want to compute
that information, and other times we need all of it. Instead, provide separate
APIs that do exactly what is needed.
These are common APIs used by other utilities. This avoids quadratic traversals in those
cases. It is also easier to understand and debug.
These utilities will be used in LifetimeDependenceScopeFixup.
LifetimeDependence uses the address of the AccessBase to compute liveness. It is
more convenient to switch over all AccessBase kinds here rather than force all
clients to do it.
AccessBase already identifies a base address as either a .global (addressor) or
a .pointer. When initializing an AccessBase from a pointer_to_address, precisely
identify one of these kinds of accesses, rather than simply marking it
.unidentified.
This used to be a special case in the AccessPathWalker. But that's not the right
place to handle it.
Add `Value.constantAccessPath`. It is like `accessPath`, but ensures that the projectionPath only contains "constant" elements.
This means: if the access contains an `index_addr` projection with a non-constant index, the `projectionPath` does _not_ contain the `index_addr`.
Instead, the `base` is an `AccessBase.index` which refers to the `index_addr`.
The result of a store_borrow can "escape" to other access bases, like a "pointer" access base.
Then a store-borrow access base is _not_ distinct from such another base.
Don't treat StoreBorrow addresses as unknown bases. While they are never the base of a formal access, they are returned
as the AccessBase when querying the enclosing scope of an address.
This fixes all analyses that use AccessUtils in the presence of new
forwarding instructions. This is also needed for consistency with the
C++ source base. And for general sanity.
Utilities that walk the use-def chain should never hard-code a list of
opcodes that happened to work with some specific pass. Passes should
never assume that a basic SIL utility handles specific SIL operations
a certain way. Instead, the utility needs to be defined in terms of
SIL semantics. In this case, the utility is supposed to handle all
instructions that semantically forward ownership of a reference.