Skip to content

Commit 73c4fb9

Browse files
committed
Rewrite MemAccessUtils comments.
Update, clarify, and prepare for generalization.
1 parent ee0955a commit 73c4fb9

File tree

1 file changed

+109
-66
lines changed

1 file changed

+109
-66
lines changed

include/swift/SIL/MemAccessUtils.h

Lines changed: 109 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -10,26 +10,51 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212
///
13-
/// These utilities model formal memory access locations as marked by
14-
/// begin_access and end_access instructions. The formal memory locations
15-
/// identified here must be consistent with language rules for exclusity
16-
/// enforcement. This is not meant to be a utility to reason about other general
17-
/// properties of SIL memory operations such as reference count identity,
18-
/// ownership, or aliasing. Code that queries the properties of arbitrary memory
19-
/// operations independent of begin_access instructions should use a different
20-
/// interface.
21-
///
22-
/// SIL memory addresses used for formal access need to meet special
23-
/// requirements. In particular, it must be possible to identifiy the storage by
24-
/// following the pointer's provenance. This is *not* true for SIL memory
25-
/// operations in general. The utilities cannot simply bailout on unrecognized
26-
/// patterns. Doing so would lead to undefined program behavior, which isn't
27-
/// something that can be directly tested (i.e. if this breaks, we won't see
28-
/// test failures).
29-
///
30-
/// These utilities are mainly meant to be used by AccessEnforcement passes,
31-
/// which optimize exclusivity enforcement. They live in SIL so they can be used
32-
/// by SIL verification.
13+
/// These utilities model the storage locations of memory access.
14+
///
15+
/// All memory operations that are part of a formal access, as defined by
16+
/// exclusivity rules, are marked by begin_access and end_access instructions.
17+
///
18+
/// Currently, access markers are stripped early in the pipeline. An active
19+
/// goal is to require access markers in OSSA form, and to enable access
20+
/// marker verification.
21+
///
22+
/// To verify access markers, SIL checks that all memory operations either have
23+
/// an address that originates in begin_access, or originates from a pattern
24+
/// that is recognized as a non-formal-access. This implies that every SIL
25+
/// memory operation has a recognizable address source.
26+
///
27+
/// If the memory operation is part of a formal access, then getAddressAccess()
28+
/// returns the begin_access marker.
29+
///
30+
/// AccessedStorage identifies the storage location of a memory access.
31+
///
32+
/// identifyFormalAccess() returns the formally accessed storage of a
33+
/// begin_access instruction. This must return a valid AccessedStorage value
34+
/// unless the access has "Unsafe" enforcement. The formal access location may
35+
/// be nested within an outer begin_access. For the purpose of exclusivity,
36+
/// nested accesses are considered distinct formal accesses so they return
37+
/// distinct AccessedStorage values even though they may access the same
38+
/// memory.
39+
///
40+
/// findAccessedStorage() returns the outermost AccessedStorage for any memory
41+
/// address. It can be called on the address of a memory operation, the address
42+
/// of a begin_access, or any other address value. If the address is from an
43+
/// enforced begin_access or from any memory operation that is part of a formal
44+
/// access, then it returns a valid AccessedStorage value. If the memory
45+
/// operation is not part of a formal access, then it still identifies the
46+
/// accessed location as a best effort, but the result may be invalid storage.
47+
///
48+
/// An active goal is to require findAccessedStorage() to always return a
49+
/// valid AccessedStorage value even for operations that aren't part of a
50+
/// formal access.
51+
///
52+
/// The AccessEnforcementWMO pass is an example of an optimistic optimization
53+
/// that relies on the above requirements for correctness. If
54+
/// findAccessedStorage() simply bailed out on an unrecognized memory address by
55+
/// returning an invalid AccessedStorage, then the optimization could make
56+
/// incorrect assumptions about the absence of access to globals or class
57+
/// properties.
3358
///
3459
//===----------------------------------------------------------------------===//
3560

@@ -92,16 +117,18 @@ SILValue getAccessedAddress(SILValue v);
92117
/// let-variables are only written during let-variable initialization, which is
93118
/// assumed to store directly to the same, unaliased accessedAddress.
94119
///
95-
/// The address of a let-variable must be the base of a formal access . A 'let'
96-
/// member of a struct is *not* a let-variable, because it's memory may be
97-
/// written when formally modifying the outer struct. A let-variable is either
98-
/// an entire local variable, global variable, or class property (this is the
99-
/// definition of the base address of a formal access).
120+
/// The address of a let-variable must be the base of a formal access, not an
121+
/// access projection. A 'let' member of a struct is *not* a let-variable,
122+
/// because it's memory may be written when formally modifying the outer
123+
/// struct. A let-variable is either an entire local variable, global variable,
124+
/// or class property (these are all formal access base addresses).
100125
///
101126
/// The caller should derive the accessed address using
102127
/// stripAccessMarkers(getAccessedAddress(ptr)).
103128
bool isLetAddress(SILValue accessedAddress);
104129

130+
/// Return true if two accesses to the same storage may conflict given the kind
131+
/// of each access.
105132
inline bool accessKindMayConflict(SILAccessKind a, SILAccessKind b) {
106133
return !(a == SILAccessKind::Read && b == SILAccessKind::Read);
107134
}
@@ -116,53 +143,54 @@ namespace swift {
116143

117144
/// Represents the identity of a storage object being accessed.
118145
///
119-
/// AccessedStorage is carefully designed to solve three problems:
146+
/// Requirements:
120147
///
121-
/// 1. Full specification and verification of SIL's model for exclusive
122-
/// formal memory access, as enforced by "access markers". It is not a
123-
/// model to encompass all SIL memory operations.
148+
/// A bitwise comparable encoding and hash key to identify each location
149+
/// being formally accessed. Any two accesses of "uniquely identified"
150+
/// storage must have the same key if they access the same storage and
151+
/// distinct keys if they access distinct storage. For more efficient
152+
/// analysis, accesses to non-uniquely identified storage should have the
153+
/// same key if they may point to the same storage.
124154
///
125-
/// 2. A bitwise comparable encoding and hash key to identify each location
126-
/// being formally accessed. Any two accesses of uniquely identified storage
127-
/// must have the same key if they access the same storage and distinct keys
128-
/// if they access distinct storage. Accesses to non-uniquely identified
129-
/// storage should ideally have the same key if they may point to the same
130-
/// storage.
155+
/// Complete identification of all class or global accesses. Failing to
156+
/// identify a class or global access will introduce undefined program
157+
/// behavior which can't be tested.
131158
///
132-
/// 3. Complete identification of all class or global accesses. Failing to
133-
/// identify a class or global access will introduce undefined program
134-
/// behavior which can't be tested.
159+
/// Memory operations on "uniquely identified" storage cannot overlap with any
160+
/// other memory operation on distinct "uniquely identified" storage.
135161
///
136162
/// AccessedStorage may be one of several kinds of "identified" storage
137-
/// objects, or may be valid, but Unidentified storage. An identified object
138-
/// is known to identify the base of the accessed storage, whether that is a
139-
/// SILValue that produces the base address, or a variable
140-
/// declaration. "Uniquely identified" storage refers to identified storage that
141-
/// cannot be aliased. For example, local allocations are uniquely identified,
142-
/// while global variables and class properties are not. Unidentified storage is
143-
/// associated with a SILValue that produces the accessed address but has not
144-
/// been determined to be the base of a storage object. It may, for example,
145-
/// be a SILPhiArgument.
146-
///
147-
/// An invalid AccessedStorage object is marked Unidentified and contains an
148-
/// invalid value. This signals that analysis has failed to recognize an
149-
/// expected address producer pattern. Over time, more aggressive
150-
/// SILVerification could allow the optimizer to aggressively assert that
151-
/// AccessedStorage is always valid.
163+
/// objects. Storage is "identified" when the base of the formal access is
164+
/// recognized and the kind of storage precisely identified. The base is usually
165+
/// represented by the SILValue that the memory address is derived from. For
166+
/// global variable access, the base is the global's declaration instead.
167+
///
168+
/// Unidentified *valid* storage is also associated with a SILValue that
169+
/// produces the accessed address but that value has not been determined to be
170+
/// the base of a formal access. It may be from a ref_tail_addr, undef, or some
171+
/// recognized memory initialization pattern. Unidentified valid storage cannot
172+
/// represent any arbitrary base address--it must at least been proven not to
173+
/// correspond to any class or global variable access.
174+
///
175+
/// An *invalid* AccessedStorage object is Unidentified and associated with an
176+
/// invalid SILValue. This signals that analysis has failed to recognize an
177+
/// expected address producer pattern.
178+
///
179+
/// An active goal is to enforce that every memory operation's
180+
/// AccessedStorage is either valid or explicitly guarded by an "unsafe"
181+
/// begin_access.
152182
///
153183
/// Note that the SILValue that represents a storage object is not
154-
/// necessarilly an address type. It may instead be a SILBoxType.
155-
///
156-
/// AccessedStorage hashing and comparison (via DenseMapInfo) is used to
157-
/// determine when two 'begin_access' instructions access the same or disjoint
158-
/// underlying objects.
159-
///
160-
/// `DenseMapInfo::isEqual()` guarantees that two AccessStorage values refer to
161-
/// the same memory if both values are valid.
162-
///
163-
/// `!DenseMapInfo::isEqual()` does not guarantee that two identified
164-
/// AccessStorage values are distinct. Inequality does, however, guarantee that
165-
/// two *uniquely* identified AccessStorage values are distinct.
184+
/// necessarilly an address type. It may instead be a SILBoxType. So, even
185+
/// though address phis are not allowed, finding the base of an access may
186+
/// require traversing phis.
187+
///
188+
/// Support for integer IDs and bitsets. An AccessedStorage value has enough
189+
/// extra bits to store a unique index for each identified access in a
190+
/// function. An AccessedStorage (without an ID) can be cheaply formed
191+
/// on-the-fly for any memory operation then used as a hash key to lookup its
192+
/// unique integer index which is stored directly in the hashed value but not
193+
/// used as part of the hash key.
166194
class AccessedStorage {
167195
public:
168196
/// Enumerate over all valid begin_access bases. Clients can use a covered
@@ -359,7 +387,12 @@ class AccessedStorage {
359387
}
360388

361389
bool isLetAccess(SILFunction *F) const;
362-
390+
391+
/// If this is a uniquely identified formal access, then it cannot
392+
/// alias with any other uniquely identified access to different storage.
393+
///
394+
/// This determines whether access markers may conflict, so it cannot assume
395+
/// that exclusivity is enforced.
363396
bool isUniquelyIdentified() const {
364397
switch (getKind()) {
365398
case Box:
@@ -423,6 +456,16 @@ class AccessedStorage {
423456
namespace llvm {
424457
/// Enable using AccessedStorage as a key in DenseMap.
425458
/// Do *not* include any extra pass data in key equality.
459+
///
460+
/// AccessedStorage hashing and comparison is used to determine when two
461+
/// 'begin_access' instructions access the same or disjoint underlying objects.
462+
///
463+
/// `DenseMapInfo::isEqual()` guarantees that two AccessStorage values refer to
464+
/// the same memory if both values are valid.
465+
///
466+
/// `!DenseMapInfo::isEqual()` does not guarantee that two identified
467+
/// AccessStorage values are distinct. Inequality does, however, guarantee that
468+
/// two *uniquely* identified AccessStorage values are distinct.
426469
template <> struct DenseMapInfo<swift::AccessedStorage> {
427470
static swift::AccessedStorage getEmptyKey() {
428471
return swift::AccessedStorage(swift::SILValue::getFromOpaqueValue(

0 commit comments

Comments
 (0)