Skip to content

Commit 2722f1a

Browse files
committed
MemoryLifetimeVerifier: Treat only destructive enum_data_addr insns as writes.
It is ok to project the data from an enum in cases where we know spare bit packing can never happen. Fixes rdar://124240723.
1 parent e06f811 commit 2722f1a

File tree

4 files changed

+30
-18
lines changed

4 files changed

+30
-18
lines changed

docs/SIL.rst

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7031,18 +7031,25 @@ unchecked_take_enum_data_addr
70317031
// #U.DataCase must be a case of enum $U with data
70327032
// %1 will be of address type $*T for the data type of case U.DataCase
70337033

7034-
Invalidates an enum value, and takes the address of the payload for the given
7035-
enum ``case`` in-place in memory. The referenced enum value is no longer valid,
7036-
but the payload value referenced by the result address is valid and must be
7037-
destroyed. It is undefined behavior if the referenced enum does not contain a
7038-
value of the given ``case``. The result shares memory with the original enum
7039-
value; the enum memory cannot be reinitialized as an enum until the payload has
7040-
also been invalidated.
7041-
7042-
(1.0 only)
7043-
7044-
For the first payloaded case of an enum, ``unchecked_take_enum_data_addr``
7045-
is guaranteed to have no side effects; the enum value will not be invalidated.
7034+
Takes the address of the payload for the given enum ``case`` in-place in
7035+
memory. It is undefined behavior if the referenced enum does not contain a
7036+
value of the given ``case``.
7037+
7038+
The result shares memory with the original enum value. If an enum declaration
7039+
is unconditionally loadable (meaning it's loadable regardless of any generic
7040+
parameters), and it has more than one case with an associated value, then it
7041+
may embed the enum tag within the payload area. If this is the case, then
7042+
`unchecked_take_enum_data_addr` will clear the tag from the payload,
7043+
invalidating the referenced enum value, but leaving the
7044+
payload value referenced by the result address valid. In these cases,
7045+
the enum memory cannot be reinitialized as an enum until the payload has also
7046+
been invalidated.
7047+
7048+
If an enum has no more than one payload case, or if the declaration is ever
7049+
address-only, then `unchecked_take_enum_data_addr` is guaranteed to be
7050+
nondestructive, and the payload address can be accessed without invalidating
7051+
the enum in these cases. The payload can be invalidated to invalidate the
7052+
enum (assuming the enum does not have a `deinit` at the type level).
70467053

70477054
select_enum
70487055
```````````

lib/SIL/Verifier/MemoryLifetimeVerifier.cpp

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -757,11 +757,16 @@ void MemoryLifetimeVerifier::checkBlock(SILBasicBlock *block, Bits &bits) {
757757
// Note that despite the name, unchecked_take_enum_data_addr does _not_
758758
// "take" the payload of the Swift.Optional enum. This is a terrible
759759
// hack in SIL.
760-
SILValue enumAddr = cast<UncheckedTakeEnumDataAddrInst>(&I)->getOperand();
761-
int enumIdx = locations.getLocationIdx(enumAddr);
762-
if (enumIdx >= 0)
763-
requireBitsSet(bits, enumAddr, &I);
764-
requireNoStoreBorrowLocation(enumAddr, &I);
760+
auto enumInst = cast<UncheckedTakeEnumDataAddrInst>(&I);
761+
// For some enums, projecting the enum data requires masking out
762+
// embedded tag bits, which invalidates the value as an enum.
763+
if (enumInst->isDestructive()) {
764+
SILValue enumAddr = enumInst->getOperand();
765+
int enumIdx = locations.getLocationIdx(enumAddr);
766+
if (enumIdx >= 0)
767+
requireBitsSet(bits, enumAddr, &I);
768+
requireNoStoreBorrowLocation(enumAddr, &I);
769+
}
765770
break;
766771
}
767772
case SILInstructionKind::DestroyAddrInst: {

test/Interpreter/moveonly_linkedlist_2_simple.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
// RUN: %FileCheck %s --check-prefix=CHECK-IR
99
// RUN: %target-run-simple-swift(-parse-as-library -enable-builtin-module -enable-experimental-feature NoncopyableGenerics -enable-experimental-feature BorrowingSwitch -Xfrontend -sil-verify-all) | %FileCheck %s
1010
// RUN: %target-run-simple-swift(-O -parse-as-library -enable-builtin-module -enable-experimental-feature NoncopyableGenerics -enable-experimental-feature BorrowingSwitch -Xfrontend -sil-verify-all) | %FileCheck %s
11+
// RUN: %target-run-simple-swift(-O -parse-as-library -enable-builtin-module -enable-experimental-feature NoncopyableGenerics -enable-experimental-feature BorrowingSwitch -Xfrontend -sil-verify-all -Xfrontend -enable-ossa-modules) | %FileCheck %s
1112

1213
// REQUIRES: executable_test
1314

test/SIL/memory_lifetime_failures.sil

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,6 @@ bb0(%0 : @guaranteed $Optional<T>):
362362
return %res : $()
363363
}
364364

365-
// CHECK: SIL memory lifetime failure in @test_store_borrow_take_enum: store-borrow location cannot be written
366365
sil [ossa] @test_store_borrow_take_enum : $@convention(thin) (@guaranteed Optional<T>) -> () {
367366
bb0(%0 : @guaranteed $Optional<T>):
368367
%s = alloc_stack $Optional<T>

0 commit comments

Comments
 (0)