Skip to content

Commit c6de7c0

Browse files
committed
Improve funciton signature @owned return result to "not owned" conversion
More specifically, this handles a case of self-recursion. With this change we convert 11 @owned return value to "not owned", while we convert 179 @owned parameter to @guanrateed. rdar://24022375
1 parent 94f52ce commit c6de7c0

File tree

4 files changed

+134
-25
lines changed

4 files changed

+134
-25
lines changed

include/swift/SILOptimizer/Analysis/ARCAnalysis.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,13 @@ class ConsumedReturnValueToEpilogueRetainMatcher {
114114
public:
115115
enum class ExitKind { Return, Throw };
116116

117-
enum class FindRetainKind { None, Found, Blocked };
117+
/// The state on how retains are found in a basic block.
118+
enum class FindRetainKind {
119+
None, ///< Did not find a retain.
120+
Found, ///< Found a retain.
121+
Recursion, ///< Found a retain and its due to self-recursion.
122+
Blocked ///< Found a blocking instructions, i.e. MayDecrement.
123+
};
118124

119125
using RetainKindValue = std::pair<FindRetainKind, SILInstruction *>;
120126

lib/SILOptimizer/Analysis/ARCAnalysis.cpp

Lines changed: 58 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,22 @@
2626

2727
using namespace swift;
2828

29+
using BasicBlockRetainValue = std::pair<SILBasicBlock *, SILValue>;
30+
31+
32+
//===----------------------------------------------------------------------===//
33+
// Utilities
34+
//===----------------------------------------------------------------------===//
35+
36+
static SILValue
37+
findIncomingValue(SILBasicBlock *BB,
38+
llvm::SmallVector<std::pair<SILBasicBlock *, SILValue>, 2> Values) {
39+
for (auto &X : Values)
40+
if (X.first == BB)
41+
return X.second;
42+
return SILValue();
43+
}
44+
2945
//===----------------------------------------------------------------------===//
3046
// Decrement Analysis
3147
//===----------------------------------------------------------------------===//
@@ -481,7 +497,6 @@ void ConsumedReturnValueToEpilogueRetainMatcher::recompute() {
481497
findMatchingRetains(&*BB);
482498
}
483499

484-
485500
void
486501
ConsumedReturnValueToEpilogueRetainMatcher::
487502
findMatchingRetains(SILBasicBlock *BB) {
@@ -502,47 +517,69 @@ findMatchingRetains(SILBasicBlock *BB) {
502517
// OK. we've found the return value, now iterate on the CFG to find all the
503518
// post-dominating retains.
504519
constexpr unsigned WorkListMaxSize = 8;
505-
RV = RCFI->getRCIdentityRoot(RV);
520+
506521
llvm::DenseSet<SILBasicBlock *> RetainFrees;
507-
llvm::SmallVector<SILBasicBlock *, 4> WorkList;
522+
llvm::SmallVector<BasicBlockRetainValue, 4> WorkList;
508523
llvm::DenseSet<SILBasicBlock *> HandledBBs;
509-
WorkList.push_back(BB);
524+
WorkList.push_back(std::make_pair(BB, RV));
510525
HandledBBs.insert(BB);
511526
while (!WorkList.empty()) {
512-
auto *CBB = WorkList.pop_back_val();
513-
RetainKindValue Kind = findMatchingRetainsInner(CBB, RV);
514-
515527
// Too many blocks ?.
516528
if (WorkList.size() > WorkListMaxSize) {
517529
EpilogueRetainInsts.clear();
518530
return;
519531
}
520532

533+
// Try to find a retain %value in this basic block.
534+
auto BVP = WorkList.pop_back_val();
535+
RetainKindValue Kind = findMatchingRetainsInner(BVP.first, BVP.second);
536+
537+
// We've found a retain on this path.
538+
if (Kind.first == FindRetainKind::Found) {
539+
EpilogueRetainInsts.push_back(Kind.second);
540+
continue;
541+
}
542+
521543
// There is a MayDecrement instruction.
522544
if (Kind.first == FindRetainKind::Blocked)
523545
return;
546+
547+
// There is a self-recursion. Use the apply instruction as the retain.
548+
if (Kind.first == FindRetainKind::Recursion) {
549+
EpilogueRetainInsts.push_back(Kind.second);
550+
continue;
551+
}
524552

553+
// Did not find a retain in this block, try to go to its predecessors.
525554
if (Kind.first == FindRetainKind::None) {
526-
RetainFrees.insert(CBB);
527-
528555
// We can not find a retain in a block with no predecessors.
529-
if (CBB->getPreds().begin() == CBB->getPreds().end()) {
556+
if (BVP.first->getPreds().begin() == BVP.first->getPreds().end()) {
530557
EpilogueRetainInsts.clear();
531558
return;
532559
}
533560

561+
// This block does not have a retain.
562+
RetainFrees.insert(BVP.first);
563+
534564
// Check the predecessors.
535-
for (auto X : CBB->getPreds()){
565+
bool FoundArgValues = false;
566+
llvm::SmallVector<std::pair<SILBasicBlock *, SILValue>, 2> IncomingValues;
567+
if (auto *SA = dyn_cast<SILArgument>(BVP.second))
568+
FoundArgValues = SA->getIncomingValues(IncomingValues);
569+
570+
for (auto X : BVP.first->getPreds()){
536571
if (HandledBBs.find(X) != HandledBBs.end())
537572
continue;
538-
WorkList.push_back(X);
573+
574+
// Try to use the predecessor edge-value.
575+
if (FoundArgValues)
576+
WorkList.push_back(std::make_pair(X, findIncomingValue(X, IncomingValues)));
577+
else
578+
WorkList.push_back(std::make_pair(X, BVP.second));
579+
539580
HandledBBs.insert(X);
540581
}
541582
}
542-
543-
// We've found a retain on this path.
544-
if (Kind.first == FindRetainKind::Found)
545-
EpilogueRetainInsts.push_back(Kind.second);
546583
}
547584

548585
// For every block with retain, we need to check the transitive
@@ -573,6 +610,11 @@ ConsumedReturnValueToEpilogueRetainMatcher::RetainKindValue
573610
ConsumedReturnValueToEpilogueRetainMatcher::
574611
findMatchingRetainsInner(SILBasicBlock *BB, SILValue V) {
575612
for (auto II = BB->rbegin(), IE = BB->rend(); II != IE; ++II) {
613+
// Handle self-recursion.
614+
if (ApplyInst *AI = dyn_cast<ApplyInst>(&*II))
615+
if (AI->getCalleeFunction() == BB->getParent())
616+
return std::make_pair(FindRetainKind::Recursion, AI);
617+
576618
// If we do not have a retain_value or strong_retain...
577619
if (!isa<RetainValueInst>(*II) && !isa<StrongRetainInst>(*II)) {
578620
// we can ignore it if it can not decrement the reference count of the

lib/SILOptimizer/IPO/FunctionSignatureOpts.cpp

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ using namespace swift;
4141
STATISTIC(NumFunctionSignaturesOptimized, "Total func sig optimized");
4242
STATISTIC(NumDeadArgsEliminated, "Total dead args eliminated");
4343
STATISTIC(NumOwnedConvertedToGuaranteed, "Total owned args -> guaranteed args");
44-
STATISTIC(NumOwnedConvertedToGuaranteedReturnValue, "Total owned args -> guaranteed return value");
44+
STATISTIC(NumOwnedConvertedToNotOwnedResult, "Total owned result -> not owned result");
4545
STATISTIC(NumCallSitesOptimized, "Total call sites optimized");
4646
STATISTIC(NumSROAArguments, "Total SROA arguments optimized");
4747

@@ -75,6 +75,7 @@ static bool hasIdenticalReleases(ReleaseList LHS, ReleaseList RHS) {
7575
return true;
7676
}
7777

78+
7879
/// Returns .Some(I) if I is a release that is the only non-debug instruction
7980
/// with side-effects in the use-def graph originating from Arg. Returns
8081
/// .Some(nullptr), if all uses from the arg were either debug insts or do not
@@ -654,7 +655,7 @@ bool SignatureAnalyzer::analyze() {
654655
if (!Retains.empty()) {
655656
RI.CalleeRetain = Retains;
656657
ShouldOptimize = true;
657-
++NumOwnedConvertedToGuaranteedReturnValue;
658+
++NumOwnedConvertedToNotOwnedResult;
658659
}
659660
}
660661

@@ -781,11 +782,21 @@ SILFunction *SignatureOptimizer::createEmptyFunctionWithOptimizedSig(
781782
static void addRetainsForConvertedDirectResults(SILBuilder &Builder,
782783
SILLocation Loc,
783784
SILValue ReturnValue,
785+
SILInstruction *AI,
784786
ArrayRef<ResultDescriptor> DirectResults) {
785787
for (auto I : indices(DirectResults)) {
786788
auto &RV = DirectResults[I];
787789
if (RV.CalleeRetain.empty()) continue;
788790

791+
bool IsSelfRecursionEpilogueRetain = false;
792+
for (auto &X : RV.CalleeRetain) {
793+
IsSelfRecursionEpilogueRetain |= (AI == X);
794+
}
795+
796+
// We do not create an retain if this ApplyInst is a self-recursion.
797+
if (IsSelfRecursionEpilogueRetain)
798+
continue;
799+
789800
// Extract the return value if necessary.
790801
SILValue SpecificResultValue = ReturnValue;
791802
if (DirectResults.size() != 1)
@@ -804,6 +815,7 @@ static void addRetainsForConvertedDirectResults(SILBuilder &Builder,
804815
static void rewriteApplyInstToCallNewFunction(SignatureOptimizer &Optimizer,
805816
SILFunction *NewF,
806817
const ApplyList &CallSites) {
818+
llvm::DenseSet<SILInstruction *> ApplysToRemove;
807819
for (auto FAS : CallSites) {
808820
auto *AI = FAS.getInstruction();
809821

@@ -865,15 +877,20 @@ static void rewriteApplyInstToCallNewFunction(SignatureOptimizer &Optimizer,
865877

866878
// If we have converted the return value from @owned to @guaranteed,
867879
// insert a retain_value at the callsite.
868-
addRetainsForConvertedDirectResults(Builder, Loc, ReturnValue,
880+
addRetainsForConvertedDirectResults(Builder, Loc, ReturnValue, AI,
869881
Optimizer.getResultDescList());
870-
871-
// Erase the old apply and its callee.
872-
recursivelyDeleteTriviallyDeadInstructions(AI, true,
873-
[](SILInstruction *) {});
874882

883+
// Make sure we remove this apply in the end.
884+
ApplysToRemove.insert(AI);
875885
++NumCallSitesOptimized;
876886
}
887+
888+
// Lastly, we have rewritten all the callsites, erase the old applys and
889+
// its callee.
890+
for (auto *AI : ApplysToRemove) {
891+
recursivelyDeleteTriviallyDeadInstructions(AI, true,
892+
[](SILInstruction *) {});
893+
}
877894
}
878895

879896
static void createThunkBody(SILBasicBlock *BB, SILFunction *NewF,
@@ -937,7 +954,7 @@ static void createThunkBody(SILBasicBlock *BB, SILFunction *NewF,
937954
}
938955

939956
// Handle @owned to @unowned return value conversion.
940-
addRetainsForConvertedDirectResults(Builder, Loc, ReturnValue,
957+
addRetainsForConvertedDirectResults(Builder, Loc, ReturnValue, nullptr,
941958
Optimizer.getResultDescList());
942959

943960
// Function that are marked as @NoReturn must be followed by an 'unreachable'
@@ -1056,6 +1073,8 @@ static bool optimizeFunctionSignature(llvm::BumpPtrAllocator &BPA,
10561073
// to unowned conversion.
10571074
for (ResultDescriptor &RD : Optimizer.getResultDescList()) {
10581075
for (auto &X : RD.CalleeRetain) {
1076+
if (!isa<StrongRetainInst>(X) && !isa<RetainValueInst>(X))
1077+
continue;
10591078
X->eraseFromParent();
10601079
}
10611080
}

test/SILOptimizer/functionsigopts.sil

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,25 @@ bb0(%0 : $boo):
112112
return %0 : $boo
113113
}
114114

115+
116+
// CHECK-LABEL: sil [thunk] @single_owned_return_value_with_self_recursion
117+
// CHECK: function_ref @_TTSf4n_g_single_owned_return_value
118+
// CHECK: [[RET:%.*]] = apply
119+
// CHECK: retain_value [[RET]]
120+
sil @single_owned_return_value_with_self_recursion : $@convention(thin) (@owned boo) -> @owned boo {
121+
bb0(%0 : $boo):
122+
cond_br undef, bb1, bb2
123+
bb1:
124+
retain_value %0 : $boo
125+
br bb3(%0 : $boo)
126+
bb2:
127+
%2 = function_ref @single_owned_return_value_with_self_recursion : $@convention(thin) (@owned boo) -> @owned boo
128+
%3 = apply %2(%0) : $@convention(thin) (@owned boo) -> @owned boo
129+
br bb3 (%3 : $boo)
130+
bb3(%4 : $boo):
131+
return %4 : $boo
132+
}
133+
115134
// CHECK-LABEL: sil [thunk] @single_owned_return_value_with_interfering_release
116135
// CHECK: bb0([[INPUT_ARG0:%[0-9]+]] : $boo):
117136
// CHECK: [[IN1:%.*]] = function_ref @_TTSf4s__single_owned_return_value_with_interfering_release
@@ -245,6 +264,22 @@ bb0(%0 : $boo):
245264
return %5 : $()
246265
}
247266

267+
// Make sure we pull out the retain_value from the callee.
268+
//
269+
// The retain and release can be got rid of easily by arc this way.
270+
//
271+
// CHECK-LABEL: sil @single_owned_return_value_with_self_recursion_callsite
272+
// CHECK: [[RET:%.*]] = apply
273+
// CHECK: retain_value [[RET]]
274+
// CHECK: release_value [[RET]]
275+
sil @single_owned_return_value_with_self_recursion_callsite : $@convention(thin) (@owned boo) -> () {
276+
bb0(%0 : $boo):
277+
%2 = function_ref @single_owned_return_value_with_self_recursion : $@convention(thin) (@owned boo) -> @owned boo
278+
%4 = apply %2(%0) : $@convention(thin) (@owned boo) -> @owned boo
279+
release_value %4 : $boo
280+
%5 = tuple()
281+
return %5 : $()
282+
}
248283

249284
sil [fragile] @exploded_release_to_dead_param_callsite : $@convention(thin) (@owned boo) -> () {
250285
bb0(%0 : $boo):
@@ -648,6 +683,13 @@ bb0(%0 : $Builtin.Int32):
648683
// CHECK-NOT: retain_value
649684
// CHECK: return
650685

686+
// There should not be a single retain in this function.
687+
//
688+
// CHECK-LABEL: sil @_TTSf4n_g_single_owned_return_value_with_self_recursion : $@convention(thin) (@owned boo) -> boo
689+
// CHECK: bb0
690+
// CHECK-NOT: retain_value
691+
// CHECK: return
692+
651693
// CHECK-LABEL: sil @_TTSf4s__single_owned_return_value_with_interfering_release : $@convention(thin) (@owned foo, @owned foo, Int) -> @owned boo {
652694
// CHECK: retain_value
653695
// CHECK: release_value

0 commit comments

Comments
 (0)