Skip to content

Commit 781b133

Browse files
authored
Merge pull request #11230 from gottesmm/rdar33502257
2 parents 9a6fb39 + 81914b9 commit 781b133

File tree

4 files changed

+311
-25
lines changed

4 files changed

+311
-25
lines changed

include/swift/SIL/SILFunction.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -671,7 +671,17 @@ class SILFunction
671671
return isa<ThrowInst>(TI);
672672
});
673673
}
674-
674+
675+
/// Loop over all blocks in this function and add all function exiting blocks
676+
/// to output.
677+
void findExitingBlocks(llvm::SmallVectorImpl<SILBasicBlock *> &output) const {
678+
for (auto &Block : const_cast<SILFunction &>(*this)) {
679+
if (Block.getTerminator()->isFunctionExiting()) {
680+
output.emplace_back(&Block);
681+
}
682+
}
683+
}
684+
675685
//===--------------------------------------------------------------------===//
676686
// Argument Helper Methods
677687
//===--------------------------------------------------------------------===//

lib/SILOptimizer/Utils/Local.cpp

Lines changed: 105 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
1010
//
1111
//===----------------------------------------------------------------------===//
12+
1213
#include "swift/SILOptimizer/Utils/Local.h"
1314
#include "swift/SILOptimizer/Utils/CFG.h"
1415
#include "swift/SILOptimizer/Analysis/Analysis.h"
@@ -892,6 +893,55 @@ static bool useDoesNotKeepClosureAlive(const SILInstruction *I) {
892893
}
893894
}
894895

896+
static SILValue createLifetimeExtendedAllocStack(
897+
SILBuilder &Builder, SILLocation Loc, SILValue Arg,
898+
ArrayRef<SILBasicBlock *> ExitingBlocks, InstModCallbacks Callbacks) {
899+
AllocStackInst *ASI = nullptr;
900+
{
901+
// Save our insert point and create a new alloc_stack in the initial BB and
902+
// dealloc_stack in all exit blocks.
903+
auto *OldInsertPt = &*Builder.getInsertionPoint();
904+
Builder.setInsertionPoint(Builder.getFunction().begin()->begin());
905+
ASI = Builder.createAllocStack(Loc, Arg->getType());
906+
Callbacks.CreatedNewInst(ASI);
907+
908+
for (auto *BB : ExitingBlocks) {
909+
Builder.setInsertionPoint(BB->getTerminator());
910+
Callbacks.CreatedNewInst(Builder.createDeallocStack(Loc, ASI));
911+
}
912+
Builder.setInsertionPoint(OldInsertPt);
913+
}
914+
assert(ASI != nullptr);
915+
916+
// Then perform a copy_addr [take] [init] right after the partial_apply from
917+
// the original address argument to the new alloc_stack that we have
918+
// created.
919+
Callbacks.CreatedNewInst(
920+
Builder.createCopyAddr(Loc, Arg, ASI, IsTake, IsInitialization));
921+
922+
// Return the new alloc_stack inst that has the appropriate live range to
923+
// destroy said values.
924+
return ASI;
925+
}
926+
927+
static bool shouldDestroyPartialApplyCapturedArg(SILValue Arg,
928+
SILParameterInfo PInfo,
929+
SILModule &M) {
930+
// If we have a non-trivial type and the argument is passed in @inout, we do
931+
// not need to destroy it here. This is something that is implicit in the
932+
// partial_apply design that will be revisited when partial_apply is
933+
// redesigned.
934+
if (PInfo.isIndirectMutating())
935+
return false;
936+
937+
// If we have a trivial type, we do not need to put in any extra releases.
938+
if (Arg->getType().isTrivial(M))
939+
return false;
940+
941+
// We handle all other cases.
942+
return true;
943+
}
944+
895945
// *HEY YOU, YES YOU, PLEASE READ*. Even though a textual partial apply is
896946
// printed with the convention of the closed over function upon it, all
897947
// non-inout arguments to a partial_apply are passed at +1. This includes
@@ -902,20 +952,14 @@ static bool useDoesNotKeepClosureAlive(const SILInstruction *I) {
902952
void swift::releasePartialApplyCapturedArg(SILBuilder &Builder, SILLocation Loc,
903953
SILValue Arg, SILParameterInfo PInfo,
904954
InstModCallbacks Callbacks) {
905-
// If we have a non-trivial type and the argument is passed in @inout, we do
906-
// not need to destroy it here. This is something that is implicit in the
907-
// partial_apply design that will be revisited when partial_apply is
908-
// redesigned.
909-
if (PInfo.isIndirectMutating())
955+
if (!shouldDestroyPartialApplyCapturedArg(Arg, PInfo, Builder.getModule()))
910956
return;
911957

912-
// If we have a trivial type, we do not need to put in any extra releases.
913-
if (Arg->getType().isTrivial(Builder.getModule()))
914-
return;
915-
916-
// Otherwise, we need to destroy the argument. If we have an address, just
917-
// emit a destroy_addr.
958+
// Otherwise, we need to destroy the argument. If we have an address, we
959+
// insert a destroy_addr and return. Any live range issues must have been
960+
// dealt with by our caller.
918961
if (Arg->getType().isAddress()) {
962+
// Then emit the destroy_addr for this arg
919963
SILInstruction *NewInst = Builder.emitDestroyAddrAndFold(Loc, Arg);
920964
Callbacks.CreatedNewInst(NewInst);
921965
return;
@@ -959,30 +1003,68 @@ void swift::releasePartialApplyCapturedArg(SILBuilder &Builder, SILLocation Loc,
9591003
/// For each captured argument of PAI, decrement the ref count of the captured
9601004
/// argument as appropriate at each of the post dominated release locations
9611005
/// found by Tracker.
962-
static void releaseCapturedArgsOfDeadPartialApply(PartialApplyInst *PAI,
1006+
static bool releaseCapturedArgsOfDeadPartialApply(PartialApplyInst *PAI,
9631007
ReleaseTracker &Tracker,
9641008
InstModCallbacks Callbacks) {
9651009
SILBuilderWithScope Builder(PAI);
9661010
SILLocation Loc = PAI->getLoc();
9671011
CanSILFunctionType PAITy =
9681012
PAI->getCallee()->getType().getAs<SILFunctionType>();
9691013

970-
// Emit a destroy value for each captured closure argument.
9711014
ArrayRef<SILParameterInfo> Params = PAITy->getParameters();
972-
auto Args = PAI->getArguments();
1015+
llvm::SmallVector<SILValue, 8> Args;
1016+
for (SILValue v : PAI->getArguments()) {
1017+
// If any of our arguments contain open existentials, bail. We do not
1018+
// support this for now so that we can avoid having to re-order stack
1019+
// locations (a larger change).
1020+
if (v->getType().hasOpenedExistential())
1021+
return false;
1022+
Args.emplace_back(v);
1023+
}
9731024
unsigned Delta = Params.size() - Args.size();
9741025
assert(Delta <= Params.size() && "Error, more Args to partial apply than "
9751026
"params in its interface.");
1027+
Params = Params.drop_front(Delta);
1028+
1029+
llvm::SmallVector<SILBasicBlock *, 2> ExitingBlocks;
1030+
PAI->getFunction()->findExitingBlocks(ExitingBlocks);
1031+
1032+
// Go through our argument list and create new alloc_stacks for each
1033+
// non-trivial address value. This ensures that the memory location that we
1034+
// are cleaning up has the same live range as the partial_apply. Otherwise, we
1035+
// may be inserting destroy_addr of alloc_stack that have already been passed
1036+
// to a dealloc_stack.
1037+
for (unsigned i : reversed(indices(Args))) {
1038+
SILValue Arg = Args[i];
1039+
SILParameterInfo PInfo = Params[i];
1040+
1041+
// If we are not going to destroy this partial_apply, continue.
1042+
if (!shouldDestroyPartialApplyCapturedArg(Arg, PInfo, Builder.getModule()))
1043+
continue;
1044+
1045+
// If we have an object, we will not have live range issues, just continue.
1046+
if (Arg->getType().isObject())
1047+
continue;
1048+
1049+
// Now that we know that we have a non-argument address, perform a take-init
1050+
// of Arg into a lifetime extended alloc_stack
1051+
Args[i] = createLifetimeExtendedAllocStack(Builder, Loc, Arg, ExitingBlocks,
1052+
Callbacks);
1053+
}
9761054

1055+
// Emit a destroy for each captured closure argument at each final release
1056+
// point.
9771057
for (auto *FinalRelease : Tracker.getFinalReleases()) {
9781058
Builder.setInsertionPoint(FinalRelease);
979-
for (unsigned AI = 0, AE = Args.size(); AI != AE; ++AI) {
980-
SILValue Arg = Args[AI];
981-
SILParameterInfo Param = Params[AI + Delta];
1059+
for (unsigned i : indices(Args)) {
1060+
SILValue Arg = Args[i];
1061+
SILParameterInfo Param = Params[i];
9821062

9831063
releasePartialApplyCapturedArg(Builder, Loc, Arg, Param, Callbacks);
9841064
}
9851065
}
1066+
1067+
return true;
9861068
}
9871069

9881070
/// TODO: Generalize this to general objects.
@@ -1007,8 +1089,12 @@ bool swift::tryDeleteDeadClosure(SILInstruction *Closure,
10071089

10081090
// If we have a partial_apply, release each captured argument at each one of
10091091
// the final release locations of the partial apply.
1010-
if (auto *PAI = dyn_cast<PartialApplyInst>(Closure))
1011-
releaseCapturedArgsOfDeadPartialApply(PAI, Tracker, Callbacks);
1092+
if (auto *PAI = dyn_cast<PartialApplyInst>(Closure)) {
1093+
// If we can not decrement the ref counts of the dead partial apply for any
1094+
// reason, bail.
1095+
if (!releaseCapturedArgsOfDeadPartialApply(PAI, Tracker, Callbacks))
1096+
return false;
1097+
}
10121098

10131099
// Then delete all user instructions.
10141100
for (auto *User : Tracker.getTrackedUsers()) {

test/SILOptimizer/sil_combine.sil

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2878,8 +2878,8 @@ bb2:
28782878
// CHECK-NEXT: [[TMP:%.*]] = alloc_stack $T
28792879
// CHECK: [[FN:%.*]] = function_ref @generic_callee
28802880
// CHECK-NEXT: copy_addr [[ARG1]] to [initialization] [[TMP]] : $*T
2881-
// CHECK-NEXT: apply [[FN]]<T, T>([[ARG0]], [[TMP]])
28822881
// CHECK-NEXT: destroy_addr [[ARG1]]
2882+
// CHECK-NEXT: apply [[FN]]<T, T>([[ARG0]], [[TMP]])
28832883
// CHECK-NEXT: destroy_addr [[TMP]]
28842884
// CHECK-NEXT: tuple
28852885
// CHECK-NEXT: dealloc_stack [[TMP]]

0 commit comments

Comments
 (0)