Skip to content

Commit 38dcfec

Browse files
authored
Merge pull request #65167 from meg-gupta/temprvalueenum
Enable store elimination of enums with uses in multiple blocks
2 parents 78c1711 + 05d64b8 commit 38dcfec

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"
@@ -742,13 +743,12 @@ TempRValueOptPass::tryOptimizeStoreIntoTemp(StoreInst *si) {
742743
if (user == si)
743744
continue;
744745

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

@@ -864,6 +864,7 @@ TempRValueOptPass::tryOptimizeStoreIntoTemp(StoreInst *si) {
864864
si->eraseFromParent();
865865
tempObj->eraseFromParent();
866866
invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions);
867+
867868
return nextIter;
868869
}
869870

@@ -873,12 +874,19 @@ TempRValueOptPass::tryOptimizeStoreIntoTemp(StoreInst *si) {
873874

874875
/// The main entry point of the pass.
875876
void TempRValueOptPass::run() {
876-
LLVM_DEBUG(llvm::dbgs() << "Copy Peephole in Func "
877-
<< getFunction()->getName() << "\n");
877+
auto *function = getFunction();
878+
879+
auto *da = PM->getAnalysis<DominanceAnalysis>();
880+
881+
LLVM_DEBUG(llvm::dbgs() << "Copy Peephole in Func " << function->getName()
882+
<< "\n");
883+
884+
SmallVector<SILValue> valuesToComplete;
878885

879886
// Find all copy_addr instructions.
880887
llvm::SmallSetVector<CopyAddrInst *, 8> deadCopies;
881-
for (auto &block : *getFunction()) {
888+
889+
for (auto &block : *function) {
882890
// Increment the instruction iterator only after calling
883891
// tryOptimizeCopyIntoTemp because the instruction after CopyInst might be
884892
// deleted, but copyInst itself won't be deleted until later.
@@ -899,7 +907,21 @@ void TempRValueOptPass::run() {
899907
}
900908

901909
if (auto *si = dyn_cast<StoreInst>(&*ii)) {
910+
auto stored = si->getSrc();
911+
bool isOrHasEnum = stored->getType().isOrHasEnum();
912+
auto nextIter = std::next(si->getIterator());
913+
902914
ii = tryOptimizeStoreIntoTemp(si);
915+
916+
// If the optimization was successful, and the stack loc was an enum
917+
// type, collect the stored value for lifetime completion.
918+
// This is needed because we can have incomplete address lifetimes on
919+
// none/trivial paths for an enum type. Once we convert to value form,
920+
// this will cause incomplete value lifetimes which can raise ownership
921+
// verification errors, because we rely on linear lifetimes in OSSA.
922+
if (ii == nextIter && isOrHasEnum) {
923+
valuesToComplete.push_back(stored);
924+
}
903925
continue;
904926
}
905927

@@ -916,7 +938,7 @@ void TempRValueOptPass::run() {
916938
}
917939
);
918940

919-
DeadEndBlocks deBlocks(getFunction());
941+
DeadEndBlocks deBlocks(function);
920942
for (auto *deadCopy : deadCopies) {
921943
auto *srcInst = deadCopy->getSrc()->getDefiningInstruction();
922944
deadCopy->eraseFromParent();
@@ -929,6 +951,12 @@ void TempRValueOptPass::run() {
929951
if (!deadCopies.empty()) {
930952
invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions);
931953
}
954+
955+
// Call the utlity to complete ossa lifetime.
956+
OSSALifetimeCompletion completion(function, da->get(function));
957+
for (auto it : valuesToComplete) {
958+
completion.completeOSSALifetime(it);
959+
}
932960
}
933961

934962
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
@@ -1338,10 +1338,10 @@ bb0(%0 : @owned $GS<Builtin.NativeObject>):
13381338
return %v : $()
13391339
}
13401340

1341-
// CHECK-LABEL: sil [ossa] @dont_optimize_store_of_enum
1342-
// CHECK: alloc_stack
1343-
// CHECK: } // end sil function 'dont_optimize_store_of_enum'
1344-
sil [ossa] @dont_optimize_store_of_enum : $@convention(method) <Element> (@guaranteed Optional<Klass>) -> () {
1341+
// CHECK-LABEL: sil [ossa] @test_optimize_store_of_enum1
1342+
// CHECK-NOT: alloc_stack
1343+
// CHECK: } // end sil function 'test_optimize_store_of_enum1'
1344+
sil [ossa] @test_optimize_store_of_enum1 : $@convention(method) <Element> (@guaranteed Optional<Klass>) -> () {
13451345
bb0(%0 : @guaranteed $Optional<Klass>):
13461346
%1 = copy_value %0 : $Optional<Klass>
13471347
%32 = alloc_stack $Optional<Klass>
@@ -1363,6 +1363,32 @@ bb7:
13631363
return %r : $()
13641364
}
13651365

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

0 commit comments

Comments
 (0)