10
10
//
11
11
// ===----------------------------------------------------------------------===//
12
12
// /
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.
33
58
// /
34
59
// ===----------------------------------------------------------------------===//
35
60
@@ -92,16 +117,18 @@ SILValue getAccessedAddress(SILValue v);
92
117
// / let-variables are only written during let-variable initialization, which is
93
118
// / assumed to store directly to the same, unaliased accessedAddress.
94
119
// /
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 ).
100
125
// /
101
126
// / The caller should derive the accessed address using
102
127
// / stripAccessMarkers(getAccessedAddress(ptr)).
103
128
bool isLetAddress (SILValue accessedAddress);
104
129
130
+ // / Return true if two accesses to the same storage may conflict given the kind
131
+ // / of each access.
105
132
inline bool accessKindMayConflict (SILAccessKind a, SILAccessKind b) {
106
133
return !(a == SILAccessKind::Read && b == SILAccessKind::Read);
107
134
}
@@ -116,53 +143,54 @@ namespace swift {
116
143
117
144
// / Represents the identity of a storage object being accessed.
118
145
// /
119
- // / AccessedStorage is carefully designed to solve three problems :
146
+ // / Requirements :
120
147
// /
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.
124
154
// /
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.
131
158
// /
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.
135
161
// /
136
162
// / 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.
152
182
// /
153
183
// / 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.
166
194
class AccessedStorage {
167
195
public:
168
196
// / Enumerate over all valid begin_access bases. Clients can use a covered
@@ -359,7 +387,12 @@ class AccessedStorage {
359
387
}
360
388
361
389
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.
363
396
bool isUniquelyIdentified () const {
364
397
switch (getKind ()) {
365
398
case Box:
@@ -423,6 +456,16 @@ class AccessedStorage {
423
456
namespace llvm {
424
457
// / Enable using AccessedStorage as a key in DenseMap.
425
458
// / 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.
426
469
template <> struct DenseMapInfo <swift::AccessedStorage> {
427
470
static swift::AccessedStorage getEmptyKey () {
428
471
return swift::AccessedStorage (swift::SILValue::getFromOpaqueValue (
0 commit comments