Skip to content

Commit 05d64b8

Browse files
committed
Enable store elimination of enums with uses in multiple blocks
1 parent 9a6ad6f commit 05d64b8

File tree

2 files changed

+69
-15
lines changed

2 files changed

+69
-15
lines changed

lib/SILOptimizer/Transforms/TempRValueElimination.cpp

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "swift/SIL/DebugUtils.h"
2323
#include "swift/SIL/MemAccessUtils.h"
2424
#include "swift/SIL/NodeBits.h"
25+
#include "swift/SIL/OSSALifetimeCompletion.h"
2526
#include "swift/SIL/OwnershipUtils.h"
2627
#include "swift/SIL/SILArgument.h"
2728
#include "swift/SIL/SILBuilder.h"
@@ -704,13 +705,12 @@ TempRValueOptPass::tryOptimizeStoreIntoTemp(StoreInst *si) {
704705
if (user == si)
705706
continue;
706707

707-
// For enums we require that all uses are in the same block.
708-
// Otherwise it could be a switch_enum of an optional where the none-case
709-
// does not have a destroy of the enum value.
710-
// After transforming such an alloc_stack the value would leak in the none-
711-
// case block.
712-
if (isOrHasEnum && user->getParent() != si->getParent() &&
713-
!isa<DeallocStackInst>(user)) {
708+
// For lexical stored values that are enums, we require that all uses are in
709+
// the same block. This is because we can have incomplete address lifetimes
710+
// on none/trivial paths. and OSSALifetimeCompletion currently can complete
711+
// lexical values only in the presence of dead end blocks.
712+
if (isOrHasEnum && si->getSrc()->isLexical() &&
713+
user->getParent() != si->getParent() && !isa<DeallocStackInst>(user)) {
714714
return std::next(si->getIterator());
715715
}
716716

@@ -826,6 +826,7 @@ TempRValueOptPass::tryOptimizeStoreIntoTemp(StoreInst *si) {
826826
si->eraseFromParent();
827827
tempObj->eraseFromParent();
828828
invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions);
829+
829830
return nextIter;
830831
}
831832

@@ -835,12 +836,19 @@ TempRValueOptPass::tryOptimizeStoreIntoTemp(StoreInst *si) {
835836

836837
/// The main entry point of the pass.
837838
void TempRValueOptPass::run() {
838-
LLVM_DEBUG(llvm::dbgs() << "Copy Peephole in Func "
839-
<< getFunction()->getName() << "\n");
839+
auto *function = getFunction();
840+
841+
auto *da = PM->getAnalysis<DominanceAnalysis>();
842+
843+
LLVM_DEBUG(llvm::dbgs() << "Copy Peephole in Func " << function->getName()
844+
<< "\n");
845+
846+
SmallVector<SILValue> valuesToComplete;
840847

841848
// Find all copy_addr instructions.
842849
llvm::SmallSetVector<CopyAddrInst *, 8> deadCopies;
843-
for (auto &block : *getFunction()) {
850+
851+
for (auto &block : *function) {
844852
// Increment the instruction iterator only after calling
845853
// tryOptimizeCopyIntoTemp because the instruction after CopyInst might be
846854
// deleted, but copyInst itself won't be deleted until later.
@@ -861,7 +869,21 @@ void TempRValueOptPass::run() {
861869
}
862870

863871
if (auto *si = dyn_cast<StoreInst>(&*ii)) {
872+
auto stored = si->getSrc();
873+
bool isOrHasEnum = stored->getType().isOrHasEnum();
874+
auto nextIter = std::next(si->getIterator());
875+
864876
ii = tryOptimizeStoreIntoTemp(si);
877+
878+
// If the optimization was successful, and the stack loc was an enum
879+
// type, collect the stored value for lifetime completion.
880+
// This is needed because we can have incomplete address lifetimes on
881+
// none/trivial paths for an enum type. Once we convert to value form,
882+
// this will cause incomplete value lifetimes which can raise ownership
883+
// verification errors, because we rely on linear lifetimes in OSSA.
884+
if (ii == nextIter && isOrHasEnum) {
885+
valuesToComplete.push_back(stored);
886+
}
865887
continue;
866888
}
867889

@@ -878,7 +900,7 @@ void TempRValueOptPass::run() {
878900
}
879901
);
880902

881-
DeadEndBlocks deBlocks(getFunction());
903+
DeadEndBlocks deBlocks(function);
882904
for (auto *deadCopy : deadCopies) {
883905
auto *srcInst = deadCopy->getSrc()->getDefiningInstruction();
884906
deadCopy->eraseFromParent();
@@ -891,6 +913,12 @@ void TempRValueOptPass::run() {
891913
if (!deadCopies.empty()) {
892914
invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions);
893915
}
916+
917+
// Call the utlity to complete ossa lifetime.
918+
OSSALifetimeCompletion completion(function, da->get(function));
919+
for (auto it : valuesToComplete) {
920+
completion.completeOSSALifetime(it);
921+
}
894922
}
895923

896924
SILTransform *swift::createTempRValueOpt() { return new TempRValueOptPass(); }

test/SILOptimizer/temp_rvalue_opt_ossa.sil

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1331,10 +1331,10 @@ bb0(%0 : @owned $GS<Builtin.NativeObject>):
13311331
return %v : $()
13321332
}
13331333

1334-
// CHECK-LABEL: sil [ossa] @dont_optimize_store_of_enum
1335-
// CHECK: alloc_stack
1336-
// CHECK: } // end sil function 'dont_optimize_store_of_enum'
1337-
sil [ossa] @dont_optimize_store_of_enum : $@convention(method) <Element> (@guaranteed Optional<Klass>) -> () {
1334+
// CHECK-LABEL: sil [ossa] @test_optimize_store_of_enum1
1335+
// CHECK-NOT: alloc_stack
1336+
// CHECK: } // end sil function 'test_optimize_store_of_enum1'
1337+
sil [ossa] @test_optimize_store_of_enum1 : $@convention(method) <Element> (@guaranteed Optional<Klass>) -> () {
13381338
bb0(%0 : @guaranteed $Optional<Klass>):
13391339
%1 = copy_value %0 : $Optional<Klass>
13401340
%32 = alloc_stack $Optional<Klass>
@@ -1356,6 +1356,32 @@ bb7:
13561356
return %r : $()
13571357
}
13581358

1359+
// CHECK-LABEL: sil [ossa] @test_optimize_store_of_enum2
1360+
// CHECK: alloc_stack
1361+
// CHECK: } // end sil function 'test_optimize_store_of_enum2'
1362+
sil [ossa] @test_optimize_store_of_enum2 : $@convention(method) <Element> (@owned Optional<Klass>) -> () {
1363+
bb0(%0 : @owned $Optional<Klass>):
1364+
%1 = copy_value %0 : $Optional<Klass>
1365+
%32 = alloc_stack $Optional<Klass>
1366+
store %0 to [init] %32 : $*Optional<Klass>
1367+
switch_enum %1 : $Optional<Klass>, case #Optional.some!enumelt: bb6, case #Optional.none!enumelt: bb5
1368+
1369+
bb5:
1370+
dealloc_stack %32 : $*Optional<Klass>
1371+
br bb7
1372+
1373+
bb6(%50 : @owned $Klass):
1374+
%53 = load [take] %32 : $*Optional<Klass>
1375+
destroy_value %53 : $Optional<Klass>
1376+
destroy_value %50 : $Klass
1377+
dealloc_stack %32 : $*Optional<Klass>
1378+
br bb7
1379+
1380+
bb7:
1381+
%r = tuple ()
1382+
return %r : $()
1383+
}
1384+
13591385
// CHECK-LABEL: sil [ossa] @store_of_enum_must_be_in_same_block
13601386
// CHECK-NOT: alloc_stack
13611387
// CHECK: } // end sil function 'store_of_enum_must_be_in_same_block'

0 commit comments

Comments
 (0)