Skip to content

Commit af354e3

Browse files
authored
Merge pull request swiftlang#41724 from eeckstein/fix-temprvalueopt
TempRValueElimination: fix a problem with leaking enum values
2 parents af461ec + 90695d9 commit af354e3

File tree

4 files changed

+70
-7
lines changed

4 files changed

+70
-7
lines changed

include/swift/SIL/SILType.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,14 @@ class SILType {
231231
EnumDecl *getEnumOrBoundGenericEnum() const {
232232
return getASTType().getEnumOrBoundGenericEnum();
233233
}
234+
235+
/// Returns true if this type is an enum or contains an enum.
236+
bool isOrHasEnum() const {
237+
return getASTType().findIf([](Type ty) {
238+
return ty->getEnumOrBoundGenericEnum() != nullptr;
239+
});
240+
}
241+
234242
/// Retrieve the NominalTypeDecl for a type that maps to a Swift
235243
/// nominal or bound generic nominal type.
236244
NominalTypeDecl *getNominalOrBoundGenericNominal() const {

lib/SIL/Verifier/MemoryLifetimeVerifier.cpp

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -185,12 +185,6 @@ static bool injectsNoPayloadCase(InjectEnumAddrInst *IEAI) {
185185
return elemType.isEmpty(*function);
186186
}
187187

188-
static bool isOrHasEnum(SILType type) {
189-
return type.getASTType().findIf([](Type ty) {
190-
return ty->getEnumOrBoundGenericEnum() != nullptr;
191-
});
192-
}
193-
194188
bool MemoryLifetimeVerifier::storesTrivialEnum(int locIdx,
195189
SILBasicBlock::reverse_iterator start,
196190
SILBasicBlock::reverse_iterator end) {
@@ -204,7 +198,7 @@ bool MemoryLifetimeVerifier::storesTrivialEnum(int locIdx,
204198
if (auto *SI = dyn_cast<StoreInst>(&inst)) {
205199
const Location *loc = locations.getLocation(SI->getDest());
206200
if (loc && loc->isSubLocation(locIdx) &&
207-
isOrHasEnum(SI->getSrc()->getType())) {
201+
SI->getSrc()->getType().isOrHasEnum()) {
208202
return SI->getOwnershipQualifier() == StoreOwnershipQualifier::Trivial;
209203
}
210204
}

lib/SILOptimizer/Transforms/TempRValueElimination.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -653,6 +653,8 @@ TempRValueOptPass::tryOptimizeStoreIntoTemp(StoreInst *si) {
653653
return std::next(si->getIterator());
654654
}
655655

656+
bool isOrHasEnum = tempObj->getType().isOrHasEnum();
657+
656658
// Scan all uses of the temporary storage (tempObj) to verify they all refer
657659
// to the value initialized by this copy. It is sufficient to check that the
658660
// only users that modify memory are the copy_addr [initialization] and
@@ -664,6 +666,16 @@ TempRValueOptPass::tryOptimizeStoreIntoTemp(StoreInst *si) {
664666
if (user == si)
665667
continue;
666668

669+
// For enums we require that all uses are in the same block.
670+
// Otherwise it could be a switch_enum of an optional where the none-case
671+
// does not have a destroy of the enum value.
672+
// After transforming such an alloc_stack the value would leak in the none-
673+
// case block.
674+
if (isOrHasEnum && user->getParent() != si->getParent() &&
675+
!isa<DeallocStackInst>(user)) {
676+
return std::next(si->getIterator());
677+
}
678+
667679
// Bail if there is any kind of user which is not handled in the code below.
668680
switch (user->getKind()) {
669681
case SILInstructionKind::DestroyAddrInst:

test/SILOptimizer/temp_rvalue_opt_ossa.sil

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1293,6 +1293,55 @@ bb0(%0 : @owned $GS<Builtin.NativeObject>):
12931293
return %v : $()
12941294
}
12951295

1296+
// CHECK-LABEL: sil [ossa] @dont_optimize_store_of_enum
1297+
// CHECK: alloc_stack
1298+
// CHECK: } // end sil function 'dont_optimize_store_of_enum'
1299+
sil [ossa] @dont_optimize_store_of_enum : $@convention(method) <Element> (@guaranteed Optional<Klass>) -> () {
1300+
bb0(%0 : @guaranteed $Optional<Klass>):
1301+
%1 = copy_value %0 : $Optional<Klass>
1302+
%32 = alloc_stack $Optional<Klass>
1303+
store %1 to [init] %32 : $*Optional<Klass>
1304+
switch_enum %0 : $Optional<Klass>, case #Optional.some!enumelt: bb6, case #Optional.none!enumelt: bb5
1305+
1306+
bb5:
1307+
dealloc_stack %32 : $*Optional<Klass>
1308+
br bb7
1309+
1310+
bb6(%50 : @guaranteed $Klass):
1311+
%53 = load [take] %32 : $*Optional<Klass>
1312+
destroy_value %53 : $Optional<Klass>
1313+
dealloc_stack %32 : $*Optional<Klass>
1314+
br bb7
1315+
1316+
bb7:
1317+
%r = tuple ()
1318+
return %r : $()
1319+
}
1320+
1321+
// CHECK-LABEL: sil [ossa] @store_of_enum_must_be_in_same_block
1322+
// CHECK-NOT: alloc_stack
1323+
// CHECK: } // end sil function 'store_of_enum_must_be_in_same_block'
1324+
sil [ossa] @store_of_enum_must_be_in_same_block : $@convention(method) <Element> (@guaranteed Optional<Klass>) -> () {
1325+
bb0(%0 : @guaranteed $Optional<Klass>):
1326+
%32 = alloc_stack $Optional<Klass>
1327+
switch_enum %0 : $Optional<Klass>, case #Optional.some!enumelt: bb6, case #Optional.none!enumelt: bb5
1328+
1329+
bb5:
1330+
dealloc_stack %32 : $*Optional<Klass>
1331+
br bb7
1332+
1333+
bb6(%50 : @guaranteed $Klass):
1334+
%1 = copy_value %0 : $Optional<Klass>
1335+
store %1 to [init] %32 : $*Optional<Klass>
1336+
%53 = load [take] %32 : $*Optional<Klass>
1337+
destroy_value %53 : $Optional<Klass>
1338+
dealloc_stack %32 : $*Optional<Klass>
1339+
br bb7
1340+
1341+
bb7:
1342+
%r = tuple ()
1343+
return %r : $()
1344+
}
12961345
////////////////////////////////////////
12971346
// Unchecked Take Enum Data Addr Inst //
12981347
////////////////////////////////////////

0 commit comments

Comments
 (0)