Skip to content

Commit 65a6d9a

Browse files
committed
MemoryLifetimeVerifier: support verifying enum memory locations.
This is kind of complicated, because an enum can be trivial for one case and not trivial for another case. We need to check at which parts of the function we can prove that the enum does (or could) have a trivial case. In such a branch, it's not required in SIL to destroy the enum location. Also, document the rules and requirements for enum memory locations in SIL.rst. rdar://73770085
1 parent 13f8f73 commit 65a6d9a

File tree

13 files changed

+450
-63
lines changed

13 files changed

+450
-63
lines changed

docs/SIL.rst

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2320,6 +2320,54 @@ The current list of interior pointer SIL instructions are:
23202320
(*) We still need to finish adding support for project_box, but all other
23212321
interior pointers are guarded already.
23222322

2323+
Memory Lifetime
2324+
~~~~~~~~~~~~~~~
2325+
2326+
Similar to Ownership SSA, there are also lifetime rules for values in memory.
2327+
With "memory" we refer to memory which is addressed by SIL instruction with
2328+
address-type operands, like ``load``, ``store``, ``switch_enum_addr``, etc.
2329+
2330+
Each memory location which holds a non-trivial value is either uninitialized
2331+
or initialized. A memory location gets initialized by storing values into it
2332+
(except assignment, which expects a location to be already initialized).
2333+
A memory location gets de-initialized by "taking" from it or destroying it, e.g.
2334+
with ``destroy_addr``. It is illegal to re-initialize a memory location or to
2335+
use a location after it was de-initialized.
2336+
2337+
If a memory location holds a trivial value (e.g. an ``Int``), it is not
2338+
required to de-initialize the location.
2339+
2340+
The SIL verifier checks this rule for memory locations which can be uniquely
2341+
identified, for example and ``alloc_stack`` or an indirect parameter. The
2342+
verifier cannot check memory locations which are potentially aliased, e.g.
2343+
a ``ref_element_addr`` (a stored class property).
2344+
2345+
Lifetime of Enums in Memory
2346+
```````````````````````````
2347+
2348+
The situation is a bit more complicated with enums, because an enum can have
2349+
both, cases with non-trivial payloads and cases with no payload or trivial
2350+
payloads.
2351+
2352+
Even if an enum itself is not trivial (because it has at least on case with a
2353+
non-trivial payload), it is not required to de-initialize such an enum memory
2354+
location on paths where it's statically provable that the enum contains a
2355+
trivial or non-payload case.
2356+
2357+
That's the case if the destroy point is jointly dominated by:
2358+
2359+
* a ``store [trivial]`` to the enum memory location.
2360+
2361+
or
2362+
2363+
* an ``inject_enum_addr`` to the enum memory location with a non-trivial or
2364+
non-payload case.
2365+
2366+
or
2367+
2368+
* a successor of a ``switch_enum`` or ``switch_enum_addr`` for a non-trivial
2369+
or non-payload case.
2370+
23232371
Dead End Blocks
23242372
~~~~~~~~~~~~~~~
23252373

include/swift/SIL/MemoryLifetime.h

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,11 @@ class MemoryLocations {
114114
/// Bit 2 is never set because Inner is completly represented by its
115115
/// sub-locations 3 and 4. But bit 0 is set in location 0 (the "self" bit),
116116
/// because it represents the untracked field ``Outer.z``.
117+
///
118+
/// Single-payload enums are represented by a location with a single sub-
119+
/// location (the projected payload address, i.e. an ``init_enum_data_addr``
120+
/// or an ``unchecked_take_enum_data_addr``.
121+
/// Multi-payload enums are not supported right now.
117122
Bits subLocations;
118123

119124
/// The accumulated parent bits, including the "self" bit.
@@ -142,6 +147,12 @@ class MemoryLocations {
142147
/// \endcode
143148
int parentIdx;
144149

150+
/// Returns true if the location with index \p idx is this location or a
151+
/// sub location of this location.
152+
bool isSubLocation(unsigned idx) const {
153+
return idx < subLocations.size() && subLocations.test(idx);
154+
}
155+
145156
private:
146157
friend class MemoryLocations;
147158

@@ -178,11 +189,18 @@ class MemoryLocations {
178189
/// small. They can be handled separately with handleSingleBlockLocations().
179190
llvm::SmallVector<SingleValueInstruction *, 16> singleBlockLocations;
180191

192+
/// A Cache for single-payload enums.
193+
llvm::DenseMap<SILType, EnumElementDecl *> singlePayloadEnums;
194+
181195
/// The bit-set of locations for which numNonTrivialFieldsNotCovered is > 0.
182196
Bits nonTrivialLocations;
183197

198+
/// If true, support init_enum_data_addr and unchecked_take_enum_data_addr
199+
bool handleEnumDataProjections;
200+
184201
public:
185-
MemoryLocations() {}
202+
MemoryLocations(bool handleEnumDataProjections) :
203+
handleEnumDataProjections(handleEnumDataProjections) {}
186204

187205
MemoryLocations(const MemoryLocations &) = delete;
188206
MemoryLocations &operator=(const MemoryLocations &) = delete;
@@ -207,6 +225,9 @@ class MemoryLocations {
207225
const Location *getLocation(unsigned index) const {
208226
return &locations[index];
209227
}
228+
229+
/// Returns the root location of \p index.
230+
const Location *getRootLocation(unsigned index) const;
210231

211232
/// Registers an address projection instruction for a location.
212233
void registerProjection(SingleValueInstruction *projection, unsigned locIdx) {
@@ -262,6 +283,15 @@ class MemoryLocations {
262283
// (locationIdx, fieldNr) -> subLocationIdx
263284
using SubLocationMap = llvm::DenseMap<std::pair<unsigned, unsigned>, unsigned>;
264285

286+
/// Returns the payload case of a single-payload enum.
287+
///
288+
/// Returns null if \p enumTy is not a single-payload enum.
289+
/// We are currently only handling enum data projections for single-payload
290+
/// enums, because it's much simpler to represent them with Locations. We
291+
/// could also support multi-payload enums, but that gets complicated. Most
292+
/// importantly, we can handle Swift.Optional.
293+
EnumElementDecl *getSinglePayloadEnumCase(SILType enumTy);
294+
265295
/// Helper function called by analyzeLocation to check all uses of the
266296
/// location recursively.
267297
///

0 commit comments

Comments
 (0)