Skip to content

Commit b7c23ed

Browse files
committed
Fix SILCombine of partial_apply to correctly extend store_borrow when needed
1 parent 98fa7f0 commit b7c23ed

File tree

4 files changed

+67
-23
lines changed

4 files changed

+67
-23
lines changed

include/swift/SILOptimizer/Utils/InstOptUtils.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ void emitDestroyOperation(SILBuilder &builder, SILLocation loc,
247247
/// Returns true, if there are no other users beside those collected in \p
248248
/// destroys, i.e. if \p inst can be considered as "dead".
249249
bool collectDestroys(SingleValueInstruction *inst,
250-
SmallVectorImpl<SILInstruction *> &destroys);
250+
SmallVectorImpl<Operand *> &destroys);
251251

252252
/// If Closure is a partial_apply or thin_to_thick_function with only local
253253
/// ref count users and a set of post-dominating releases:

include/swift/SILOptimizer/Utils/ValueLifetime.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,20 @@ class ValueLifetimeAnalysis {
138138
propagateLiveness();
139139
}
140140

141+
ValueLifetimeAnalysis(SILArgument *def, ArrayRef<Operand *> useRange)
142+
: defValue(def), inLiveBlocks(def->getFunction()), userSet() {
143+
for (auto *use : useRange)
144+
userSet.insert(use->getUser());
145+
propagateLiveness();
146+
}
147+
148+
ValueLifetimeAnalysis(SILInstruction *def, ArrayRef<Operand *> useRange)
149+
: defValue(def), inLiveBlocks(def->getFunction()), userSet() {
150+
for (auto *use : useRange)
151+
userSet.insert(use->getUser());
152+
propagateLiveness();
153+
}
154+
141155
/// Compute the LifetimeBoundary--the last users and boundary edges. This
142156
/// always succeeds.
143157
///

lib/SILOptimizer/Utils/InstOptUtils.cpp

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "swift/SIL/SILDebugInfoExpression.h"
2626
#include "swift/SIL/SILModule.h"
2727
#include "swift/SIL/SILUndef.h"
28+
#include "swift/SIL/ScopedAddressUtils.h"
2829
#include "swift/SIL/TypeLowering.h"
2930
#include "swift/SILOptimizer/Analysis/ARCAnalysis.h"
3031
#include "swift/SILOptimizer/Analysis/Analysis.h"
@@ -971,16 +972,16 @@ void swift::getConsumedPartialApplyArgs(PartialApplyInst *pai,
971972
}
972973

973974
bool swift::collectDestroys(SingleValueInstruction *inst,
974-
SmallVectorImpl<SILInstruction *> &destroys) {
975+
SmallVectorImpl<Operand *> &destroys) {
975976
bool isDead = true;
976977
for (Operand *use : inst->getUses()) {
977978
SILInstruction *user = use->getUser();
978979
if (useHasTransitiveOwnership(user)) {
979980
if (!collectDestroys(cast<SingleValueInstruction>(user), destroys))
980981
isDead = false;
981-
destroys.push_back(user);
982+
destroys.push_back(use);
982983
} else if (useDoesNotKeepValueAlive(user)) {
983-
destroys.push_back(user);
984+
destroys.push_back(use);
984985
} else {
985986
isDead = false;
986987
}
@@ -996,7 +997,7 @@ bool swift::collectDestroys(SingleValueInstruction *inst,
996997
/// weirdness of the old retain/release ARC model. Most likely this will
997998
/// not be needed anymore with OSSA.
998999
static bool keepArgsOfPartialApplyAlive(PartialApplyInst *pai,
999-
ArrayRef<SILInstruction *> paiUsers,
1000+
ArrayRef<Operand *> paiUses,
10001001
SILBuilderContext &builderCtxt,
10011002
InstModCallbacks callbacks) {
10021003
SmallVector<Operand *, 8> argsToHandle;
@@ -1008,7 +1009,7 @@ static bool keepArgsOfPartialApplyAlive(PartialApplyInst *pai,
10081009
// Compute the set of endpoints, which will be used to insert destroys of
10091010
// temporaries. This may fail if the frontier is located on a critical edge
10101011
// which we may not split.
1011-
ValueLifetimeAnalysis vla(pai, paiUsers);
1012+
ValueLifetimeAnalysis vla(pai, paiUses);
10121013

10131014
ValueLifetimeAnalysis::Frontier partialApplyFrontier;
10141015
if (!vla.computeFrontier(partialApplyFrontier,
@@ -1068,7 +1069,7 @@ bool swift::tryDeleteDeadClosure(SingleValueInstruction *closure,
10681069

10691070
// Collect all destroys of the closure (transitively including destorys of
10701071
// copies) and check if those are the only uses of the closure.
1071-
SmallVector<SILInstruction *, 16> closureDestroys;
1072+
SmallVector<Operand *, 16> closureDestroys;
10721073
if (!collectDestroys(closure, closureDestroys))
10731074
return false;
10741075

@@ -1102,7 +1103,8 @@ bool swift::tryDeleteDeadClosure(SingleValueInstruction *closure,
11021103

11031104
// Delete all copy and destroy instructions in order so that leaf uses are
11041105
// deleted first.
1105-
for (SILInstruction *user : closureDestroys) {
1106+
for (auto *use : closureDestroys) {
1107+
auto *user = use->getUser();
11061108
assert(
11071109
(useDoesNotKeepValueAlive(user) || useHasTransitiveOwnership(user)) &&
11081110
"We expect only ARC operations without "

lib/SILOptimizer/Utils/PartialApplyCombiner.cpp

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "swift/SIL/SILValue.h"
14+
#include "swift/SIL/ScopedAddressUtils.h"
1415
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
1516
#include "swift/SILOptimizer/Utils/ValueLifetime.h"
1617

@@ -59,34 +60,44 @@ class PartialApplyCombiner {
5960
/// apply instructions.
6061
bool PartialApplyCombiner::copyArgsToTemporaries(
6162
ArrayRef<FullApplySite> applies) {
63+
SmallVector<Operand *, 8> argsToHandle;
64+
65+
// Find args that need extension for a non-stack partial_apply
6266
// A partial_apply [stack]'s argument are not owned by the partial_apply and
6367
// therefore their lifetime must outlive any uses.
64-
if (pai->isOnStack())
65-
return true;
66-
67-
SmallVector<Operand *, 8> argsToHandle;
68-
getConsumedPartialApplyArgs(pai, argsToHandle,
69-
/*includeTrivialAddrArgs*/ true);
70-
if (argsToHandle.empty())
71-
return true;
68+
if (!pai->isOnStack()) {
69+
getConsumedPartialApplyArgs(pai, argsToHandle,
70+
/*includeTrivialAddrArgs*/ true);
71+
}
7272

7373
// Compute the set of endpoints, which will be used to insert destroys of
7474
// temporaries.
75-
SmallVector<SILInstruction *, 16> paiUsers;
75+
SmallVector<Operand *, 16> paiUses;
7676

7777
// Of course we must inlude all apply instructions which we want to optimize.
7878
for (FullApplySite ai : applies) {
79-
paiUsers.push_back(ai.getInstruction());
79+
paiUses.push_back(ai.getCalleeOperand());
80+
}
81+
82+
SmallVector<StoreBorrowInst *, 8> storeBorrowsToHandle;
83+
for (auto arg : pai->getArguments()) {
84+
if (auto *sbi = dyn_cast<StoreBorrowInst>(arg)) {
85+
storeBorrowsToHandle.push_back(sbi);
86+
}
8087
}
8188

89+
if (argsToHandle.empty() && storeBorrowsToHandle.empty()) {
90+
return true;
91+
}
8292
// Also include all destroys in the liferange for the arguments.
8393
// This is needed for later processing in tryDeleteDeadClosure: in case the
84-
// pai gets dead after this optimization, tryDeleteDeadClosure relies on that
85-
// we already copied the pai arguments to extend their lifetimes until the pai
86-
// is finally destroyed.
87-
collectDestroys(pai, paiUsers);
94+
// pai gets dead after this optimization, tryDeleteDeadClosure relies on
95+
// that we already copied the pai arguments to extend their lifetimes until
96+
// the pai is finally destroyed.
97+
collectDestroys(pai, paiUses);
8898

89-
ValueLifetimeAnalysis vla(pai, paiUsers);
99+
ValueLifetimeAnalysis vla(pai,
100+
llvm::makeArrayRef(paiUses.begin(), paiUses.end()));
90101
ValueLifetimeAnalysis::Frontier partialApplyFrontier;
91102

92103
// Computing the frontier may fail if the frontier is located on a critical
@@ -114,6 +125,23 @@ bool PartialApplyCombiner::copyArgsToTemporaries(
114125
// allocated temporary) at the end of the partial_apply's lifetime.
115126
endLifetimeAtFrontier(tmp, partialApplyFrontier, builderCtxt, callbacks);
116127
}
128+
129+
DeadEndBlocks deBlocks(pai->getFunction());
130+
for (auto *storeBorrow : storeBorrowsToHandle) {
131+
if (extendStoreBorrow(storeBorrow, paiUses, &deBlocks, callbacks)) {
132+
continue;
133+
}
134+
SILBuilderWithScope builder(pai, builderCtxt);
135+
// Copy address-arguments into a stack-allocated temporary.
136+
auto *asi = builder.createAllocStack(pai->getLoc(), storeBorrow->getType());
137+
builder.createCopyAddr(pai->getLoc(), storeBorrow, asi, IsTake_t::IsNotTake,
138+
IsInitialization_t::IsInitialization);
139+
argToTmpCopy.insert(std::make_pair(storeBorrow, asi));
140+
141+
// Destroy the argument value (either as SSA value or in the stack-
142+
// allocated temporary) at the end of the partial_apply's lifetime.
143+
endLifetimeAtFrontier(asi, partialApplyFrontier, builderCtxt, callbacks);
144+
}
117145
return true;
118146
}
119147

0 commit comments

Comments
 (0)