Skip to content

Commit 12f2c2e

Browse files
authored
Merge pull request #65345 from meg-gupta/enableenumoptlexical
Enable mem2reg and temprvalueopt on lexical enums
2 parents ac7a478 + 1f4efeb commit 12f2c2e

File tree

7 files changed

+64
-75
lines changed

7 files changed

+64
-75
lines changed

include/swift/SIL/OSSALifetimeCompletion.h

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,19 @@ class OSSALifetimeCompletion {
5353
/// Insert a lifetime-ending instruction on every path to complete the OSSA
5454
/// lifetime of \p value. Lifetime completion is only relevant for owned
5555
/// values or borrow introducers.
56-
///
56+
/// For lexical values lifetime is completed at unreachable instructions.
57+
/// For non-lexical values lifetime is completed at the lifetime boundary.
58+
/// When \p forceBoundaryCompletion is true, the client is able to guarantee
59+
/// that lifetime completion of lexical values at the lifetime boundary is
60+
/// sufficient.
61+
/// Currently \p forceBoundaryCompletion is used by mem2reg and temprvalueopt
62+
/// to complete lexical enum values on trivial paths.
5763
/// Returns true if any new instructions were created to complete the
5864
/// lifetime.
5965
///
6066
/// TODO: We also need to complete scoped addresses (e.g. store_borrow)!
61-
LifetimeCompletion completeOSSALifetime(SILValue value) {
67+
LifetimeCompletion
68+
completeOSSALifetime(SILValue value, bool forceBoundaryCompletion = false) {
6269
if (value->getOwnershipKind() == OwnershipKind::None)
6370
return LifetimeCompletion::NoLifetime;
6471

@@ -73,13 +80,13 @@ class OSSALifetimeCompletion {
7380
if (!completedValues.insert(value))
7481
return LifetimeCompletion::AlreadyComplete;
7582

76-
return analyzeAndUpdateLifetime(value)
77-
? LifetimeCompletion::WasCompleted
78-
: LifetimeCompletion::AlreadyComplete;
83+
return analyzeAndUpdateLifetime(value, forceBoundaryCompletion)
84+
? LifetimeCompletion::WasCompleted
85+
: LifetimeCompletion::AlreadyComplete;
7986
}
8087

8188
protected:
82-
bool analyzeAndUpdateLifetime(SILValue value);
89+
bool analyzeAndUpdateLifetime(SILValue value, bool forceBoundaryCompletion);
8390
};
8491

8592
//===----------------------------------------------------------------------===//

lib/SIL/Utils/OSSALifetimeCompletion.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,8 @@ static bool endLifetimeAtUnreachableBlocks(SILValue value,
142142
/// This is only meant to cleanup lifetimes that lead to dead-end blocks. After
143143
/// recursively completing all nested scopes, it then simply ends the lifetime
144144
/// at the Unreachable instruction.
145-
bool OSSALifetimeCompletion::analyzeAndUpdateLifetime(SILValue value) {
145+
bool OSSALifetimeCompletion::analyzeAndUpdateLifetime(
146+
SILValue value, bool forceBoundaryCompletion) {
146147
// Called for inner borrows, inner adjacent reborrows, inner reborrows, and
147148
// scoped addresses.
148149
auto handleInnerScope = [this](SILValue innerBorrowedValue) {
@@ -152,7 +153,7 @@ bool OSSALifetimeCompletion::analyzeAndUpdateLifetime(SILValue value) {
152153
liveness.compute(domInfo, handleInnerScope);
153154

154155
bool changed = false;
155-
if (value->isLexical()) {
156+
if (value->isLexical() && !forceBoundaryCompletion) {
156157
changed |= endLifetimeAtUnreachableBlocks(value, liveness.getLiveness());
157158
} else {
158159
changed |= endLifetimeAtBoundary(value, liveness.getLiveness());

lib/SILOptimizer/Transforms/SILMem2Reg.cpp

Lines changed: 43 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,13 @@ class LiveValues {
195195
return LiveValues::forOwned({stored, move});
196196
}
197197

198+
static LiveValues forValues(SILValue stored, SILValue lexical) {
199+
if (stored->getOwnershipKind() == OwnershipKind::Guaranteed) {
200+
return LiveValues::forGuaranteed({stored, lexical});
201+
}
202+
return LiveValues::forOwned({stored, lexical});
203+
}
204+
198205
static LiveValues toReplace(AllocStackInst *asi, SILValue replacement) {
199206
if (replacement->getOwnershipKind() == OwnershipKind::Guaranteed) {
200207
return LiveValues::forGuaranteed(Guaranteed::toReplace(asi, replacement));
@@ -715,6 +722,27 @@ static bool canEndLexicalLifetime(LiveValues values) {
715722
return values.canEndLexicalLifetime();
716723
}
717724

725+
static SILValue getLexicalValueForStore(SILInstruction *inst,
726+
AllocStackInst *asi) {
727+
assert(isa<StoreInst>(inst) || isa<StoreBorrowInst>(inst));
728+
729+
SILValue stored = inst->getOperand(CopyLikeInstruction::Src);
730+
LLVM_DEBUG(llvm::dbgs() << "*** Found Store def " << stored);
731+
732+
if (!lexicalLifetimeEnsured(asi)) {
733+
return SILValue();
734+
}
735+
if (isa<StoreBorrowInst>(inst)) {
736+
if (isGuaranteedLexicalValue(stored)) {
737+
return SILValue();
738+
}
739+
auto borrow = cast<BeginBorrowInst>(inst->getNextInstruction());
740+
return borrow;
741+
}
742+
auto move = cast<MoveValueInst>(inst->getNextInstruction());
743+
return move;
744+
}
745+
718746
/// Begin a lexical borrow scope for the value stored into the provided
719747
/// StoreInst after that instruction.
720748
///
@@ -1233,27 +1261,9 @@ StackAllocationPromoter::getLiveOutValues(BlockSetVector &phiBlocks,
12331261
BlockToInstMap::iterator it = initializationPoints.find(domBlock);
12341262
if (it != initializationPoints.end()) {
12351263
auto *inst = it->second;
1236-
assert(isa<StoreInst>(inst) || isa<StoreBorrowInst>(inst));
1237-
1238-
SILValue stored = inst->getOperand(CopyLikeInstruction::Src);
1239-
LLVM_DEBUG(llvm::dbgs() << "*** Found Store def " << stored);
1240-
1241-
if (!lexicalLifetimeEnsured(asi)) {
1242-
auto values = LiveValues::forOwned(stored, {});
1243-
return values;
1244-
}
1245-
if (isa<StoreBorrowInst>(inst)) {
1246-
if (isGuaranteedLexicalValue(stored)) {
1247-
auto values = LiveValues::forGuaranteed(stored, {});
1248-
return values;
1249-
}
1250-
auto borrow = cast<BeginBorrowInst>(inst->getNextInstruction());
1251-
auto values = LiveValues::forGuaranteed(stored, borrow);
1252-
return values;
1253-
}
1254-
auto move = cast<MoveValueInst>(inst->getNextInstruction());
1255-
auto values = LiveValues::forOwned(stored, move);
1256-
return values;
1264+
auto stored = inst->getOperand(CopyLikeInstruction::Src);
1265+
auto lexical = getLexicalValueForStore(inst, asi);
1266+
return LiveValues::forValues(stored, lexical);
12571267
}
12581268

12591269
// If there is a Phi definition in this block:
@@ -1806,8 +1816,13 @@ void StackAllocationPromoter::run() {
18061816
if (asi->getType().isOrHasEnum()) {
18071817
for (auto it : initializationPoints) {
18081818
auto *si = it.second;
1809-
auto src = si->getOperand(0);
1810-
valuesToComplete.push_back(src);
1819+
auto stored = si->getOperand(CopyLikeInstruction::Src);
1820+
valuesToComplete.push_back(stored);
1821+
if (lexicalLifetimeEnsured(asi)) {
1822+
if (auto lexical = getLexicalValueForStore(si, asi)) {
1823+
valuesToComplete.push_back(lexical);
1824+
}
1825+
}
18111826
}
18121827
}
18131828

@@ -1817,8 +1832,12 @@ void StackAllocationPromoter::run() {
18171832
// Now, complete lifetimes!
18181833
OSSALifetimeCompletion completion(function, domInfo);
18191834

1835+
// We may have incomplete lifetimes for enum locations on trivial paths.
1836+
// After promoting them, complete lifetime here.
18201837
for (auto it : valuesToComplete) {
1821-
completion.completeOSSALifetime(it);
1838+
// Set forceBoundaryCompletion as true so that we complete at boundary for
1839+
// lexical values as well.
1840+
completion.completeOSSALifetime(it, /* forceBoundaryCompletion */ true);
18221841
}
18231842
}
18241843

@@ -2119,33 +2138,6 @@ bool MemoryToRegisters::promoteSingleAllocation(AllocStackInst *alloc) {
21192138
<< *alloc);
21202139
deleter.forceDeleteWithUsers(alloc);
21212140
return true;
2122-
} else {
2123-
auto enableOptimizationForEnum = [](AllocStackInst *asi) {
2124-
if (asi->isLexical()) {
2125-
return false;
2126-
}
2127-
for (auto *use : asi->getUses()) {
2128-
auto *user = use->getUser();
2129-
if (!isa<StoreInst>(user) && !isa<StoreBorrowInst>(user)) {
2130-
continue;
2131-
}
2132-
auto stored = user->getOperand(CopyLikeInstruction::Src);
2133-
if (stored->isLexical()) {
2134-
return false;
2135-
}
2136-
}
2137-
return true;
2138-
};
2139-
// For stack locs of enum type that are lexical or with lexical stored
2140-
// values, we require that all uses are in the same block. This is because
2141-
// we can have incomplete lifetime of enum typed addresses, and on
2142-
// converting to value form this causes verification error. For all other
2143-
// stack locs of enum type, we use the lifetime completion utility to fix
2144-
// the lifetime. But when we have a lexical value, the utility can complete
2145-
// lifetimes on dead end blocks only.
2146-
if (f.hasOwnership() && alloc->getType().isOrHasEnum() &&
2147-
!enableOptimizationForEnum(alloc))
2148-
return false;
21492141
}
21502142

21512143
LLVM_DEBUG(llvm::dbgs() << "*** Need to insert BB arguments for " << *alloc);

lib/SILOptimizer/Transforms/TempRValueElimination.cpp

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -731,8 +731,6 @@ TempRValueOptPass::tryOptimizeStoreIntoTemp(StoreInst *si) {
731731
return std::next(si->getIterator());
732732
}
733733

734-
bool isOrHasEnum = tempObj->getType().isOrHasEnum();
735-
736734
// Scan all uses of the temporary storage (tempObj) to verify they all refer
737735
// to the value initialized by this copy. It is sufficient to check that the
738736
// only users that modify memory are the copy_addr [initialization] and
@@ -743,15 +741,6 @@ TempRValueOptPass::tryOptimizeStoreIntoTemp(StoreInst *si) {
743741
if (user == si)
744742
continue;
745743

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)) {
752-
return std::next(si->getIterator());
753-
}
754-
755744
// Bail if there is any kind of user which is not handled in the code below.
756745
switch (user->getKind()) {
757746
case SILInstructionKind::DestroyAddrInst:
@@ -955,7 +944,7 @@ void TempRValueOptPass::run() {
955944
// Call the utlity to complete ossa lifetime.
956945
OSSALifetimeCompletion completion(function, da->get(function));
957946
for (auto it : valuesToComplete) {
958-
completion.completeOSSALifetime(it);
947+
completion.completeOSSALifetime(it, /* forceBoundaryCompletion */ true);
959948
}
960949
}
961950

test/SILOptimizer/mem2reg_lifetime_nontrivial.sil

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1061,7 +1061,7 @@ enum KlassOptional {
10611061
sil @return_optional_or_error : $@convention(thin) () -> (@owned KlassOptional, @error Error)
10621062

10631063
// CHECK-LABEL: sil [ossa] @test_deadphi4 :
1064-
// CHECK: alloc_stack
1064+
// CHECK-NOT: alloc_stack
10651065
// CHECK-LABEL: } // end sil function 'test_deadphi4'
10661066
sil [ossa] @test_deadphi4 : $@convention(thin) (@owned KlassOptional) -> () {
10671067
bb0(%0 : @owned $KlassOptional):

test/SILOptimizer/mem2reg_ossa.sil

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -516,7 +516,7 @@ bb7:
516516
}
517517

518518
// CHECK-LABEL: sil [ossa] @test_optional_in_multiple_blocks_lexical :
519-
// CHECK: alloc_stack
519+
// CHECK-NOT: alloc_stack
520520
// CHECK: } // end sil function 'test_optional_in_multiple_blocks_lexical'
521521
sil [ossa] @test_optional_in_multiple_blocks_lexical : $@convention(method) (@guaranteed Optional<Klass>) -> () {
522522
bb0(%0 : @guaranteed $Optional<Klass>):
@@ -542,7 +542,7 @@ bb7:
542542
}
543543

544544
// CHECK-LABEL: sil [ossa] @test_optional_in_multiple_blocks_lexical_storedvalue :
545-
// CHECK: alloc_stack
545+
// CHECK-NOT: alloc_stack
546546
// CHECK: } // end sil function 'test_optional_in_multiple_blocks_lexical_storedvalue'
547547
sil [ossa] @test_optional_in_multiple_blocks_lexical_storedvalue : $@convention(method) (@owned Optional<Klass>) -> () {
548548
bb0(%0 : @owned $Optional<Klass>):

test/SILOptimizer/temp_rvalue_opt_ossa.sil

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1364,7 +1364,7 @@ bb7:
13641364
}
13651365

13661366
// CHECK-LABEL: sil [ossa] @test_optimize_store_of_enum2
1367-
// CHECK: alloc_stack
1367+
// CHECK-NOT: alloc_stack
13681368
// CHECK: } // end sil function 'test_optimize_store_of_enum2'
13691369
sil [ossa] @test_optimize_store_of_enum2 : $@convention(method) <Element> (@owned Optional<Klass>) -> () {
13701370
bb0(%0 : @owned $Optional<Klass>):

0 commit comments

Comments
 (0)