Skip to content

Commit b7da742

Browse files
authored
Merge pull request #11240 from gottesmm/swift-4.0-branchrdar33502257
[4.0] When creating destroys for addresses passed into a partial apply, fir…
2 parents 57de9c4 + 828bc37 commit b7da742

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
@@ -667,7 +667,17 @@ class SILFunction
667667
return isa<ThrowInst>(TI);
668668
});
669669
}
670-
670+
671+
/// Loop over all blocks in this function and add all function exiting blocks
672+
/// to output.
673+
void findExitingBlocks(llvm::SmallVectorImpl<SILBasicBlock *> &output) const {
674+
for (auto &Block : const_cast<SILFunction &>(*this)) {
675+
if (Block.getTerminator()->isFunctionExiting()) {
676+
output.emplace_back(&Block);
677+
}
678+
}
679+
}
680+
671681
//===--------------------------------------------------------------------===//
672682
// Argument Helper Methods
673683
//===--------------------------------------------------------------------===//

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"
@@ -891,6 +892,55 @@ static bool useDoesNotKeepClosureAlive(const SILInstruction *I) {
891892
}
892893
}
893894

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

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

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

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

9821062
releasePartialApplyCapturedArg(Builder, Loc, Arg, Param, Callbacks);
9831063
}
9841064
}
1065+
1066+
return true;
9851067
}
9861068

9871069
/// TODO: Generalize this to general objects.
@@ -1006,8 +1088,12 @@ bool swift::tryDeleteDeadClosure(SILInstruction *Closure,
10061088

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

10121098
// Then delete all user instructions.
10131099
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)