Skip to content

Commit 670df6d

Browse files
committed
Wire up improved epilogue release matcher with function signature
optimization. We get some improvements on the # of parameters converted to guanrateed from owned on the stdlib. before ====== 103 sil-function-signature-opts - Total owned args -> guaranteed args after ====== 118 sil-function-signature-opts - Total owned args -> guaranteed args I see the following improvements by running benchmarks with and without this change. Only difference >=1.05X ErrorHandling 8154 7497 -657 -8.1% 1.09x LinkedList 9973 9529 -444 -4.5% 1.05x ObjectAllocation 239 222 -17 -7.1% 1.08x RC4 23167 21993 -1174 -5.1% 1.05x (!) This is part of rdar://22380547
1 parent 99ca08e commit 670df6d

File tree

2 files changed

+141
-45
lines changed

2 files changed

+141
-45
lines changed

lib/SILOptimizer/IPO/FunctionSignatureOpts.cpp

Lines changed: 45 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -61,17 +61,30 @@ static bool isRelease(SILInstruction *I) {
6161
}
6262
}
6363

64+
/// Returns true if LHS and RHS contain identical set of releases.
65+
static bool hasIdenticalReleases(ReleaseList LHS, ReleaseList RHS) {
66+
llvm::DenseSet<SILInstruction *> Releases;
67+
if (LHS.size() != RHS.size())
68+
return false;
69+
for (auto &X : LHS)
70+
Releases.insert(X);
71+
for (auto &X : RHS)
72+
if (Releases.find(X) == Releases.end())
73+
return false;
74+
return true;
75+
}
76+
6477
/// Returns .Some(I) if I is a release that is the only non-debug instruction
6578
/// with side-effects in the use-def graph originating from Arg. Returns
6679
/// .Some(nullptr), if all uses from the arg were either debug insts or do not
6780
/// have side-effects. Returns .None if there were any non-release instructions
6881
/// with side-effects in the use-def graph from Arg or if there were multiple
6982
/// release instructions with side-effects in the use-def graph from Arg.
70-
static llvm::Optional<NullablePtr<SILInstruction>>
83+
static llvm::Optional<ReleaseList>
7184
getNonTrivialNonDebugReleaseUse(SILArgument *Arg) {
7285
llvm::SmallVector<SILInstruction *, 8> Worklist;
7386
llvm::SmallPtrSet<SILInstruction *, 8> SeenInsts;
74-
llvm::Optional<SILInstruction *> Result;
87+
ReleaseList Result;
7588

7689
for (Operand *I : getNonDebugUses(SILValue(Arg)))
7790
Worklist.push_back(I->getUser());
@@ -91,12 +104,8 @@ getNonTrivialNonDebugReleaseUse(SILArgument *Arg) {
91104
if (!isRelease(U))
92105
return None;
93106

94-
// If we have already seen a release of some sort, bail.
95-
if (Result.hasValue())
96-
return None;
97-
98107
// Otherwise, set result to that value.
99-
Result = U;
108+
Result.push_back(U);
100109
continue;
101110
}
102111

@@ -105,9 +114,7 @@ getNonTrivialNonDebugReleaseUse(SILArgument *Arg) {
105114
Worklist.push_back(I->getUser());
106115
}
107116

108-
if (!Result.hasValue())
109-
return NullablePtr<SILInstruction>();
110-
return NullablePtr<SILInstruction>(Result.getValue());
117+
return Result;
111118
}
112119

113120
//===----------------------------------------------------------------------===//
@@ -138,11 +145,11 @@ struct ArgumentDescriptor {
138145
/// If non-null, this is the release in the return block of the callee, which
139146
/// is associated with this parameter if it is @owned. If the parameter is not
140147
/// @owned or we could not find such a release in the callee, this is null.
141-
SILInstruction *CalleeRelease;
148+
ReleaseList CalleeRelease;
142149

143150
/// The same as CalleeRelease, but the release in the throw block, if it is a
144151
/// function which has a throw block.
145-
SILInstruction *CalleeReleaseInThrowBlock;
152+
ReleaseList CalleeReleaseInThrowBlock;
146153

147154
/// The projection tree of this arguments.
148155
ProjectionTree ProjTree;
@@ -239,9 +246,9 @@ void ArgumentDescriptor::computeOptimizedInterfaceParams(
239246
// If we cannot explode this value, handle callee release and return.
240247
if (!shouldExplode()) {
241248
DEBUG(llvm::dbgs() << " ProjTree cannot explode arg.\n");
242-
// If we found a release in the callee in the last BB on an @owned
249+
// If we found releases in the callee in the last BB on an @owned
243250
// parameter, change the parameter to @guaranteed and continue...
244-
if (CalleeRelease) {
251+
if (!CalleeRelease.empty()) {
245252
DEBUG(llvm::dbgs() << " Has callee release.\n");
246253
assert(ParameterInfo.getConvention() ==
247254
ParameterConvention::Direct_Owned &&
@@ -277,8 +284,8 @@ void ArgumentDescriptor::computeOptimizedInterfaceParams(
277284
// If Ty is guaranteed, just pass it through.
278285
ParameterConvention Conv = ParameterInfo.getConvention();
279286
if (Conv == ParameterConvention::Direct_Guaranteed) {
280-
assert(!CalleeRelease && "Guaranteed parameter should not have a callee "
281-
"release.");
287+
assert(CalleeRelease.empty() && "Guaranteed parameter should not have a "
288+
"callee release.");
282289
SILParameterInfo NewInfo(Ty.getSwiftRValueType(),
283290
ParameterConvention::Direct_Guaranteed);
284291
Out.push_back(NewInfo);
@@ -289,7 +296,7 @@ void ArgumentDescriptor::computeOptimizedInterfaceParams(
289296
// guaranteed.
290297
assert(ParameterInfo.getConvention() == ParameterConvention::Direct_Owned &&
291298
"Can only transform @owned => @guaranteed in this code path");
292-
if (CalleeRelease) {
299+
if (!CalleeRelease.empty()) {
293300
SILParameterInfo NewInfo(Ty.getSwiftRValueType(),
294301
ParameterConvention::Direct_Guaranteed);
295302
Out.push_back(NewInfo);
@@ -344,16 +351,10 @@ unsigned ArgumentDescriptor::updateOptimizedBBArgs(SILBuilder &Builder,
344351
// do need the instruction to be non-null.
345352
//
346353
// TODO: This should not be necessary.
347-
if (CalleeRelease) {
348-
SILType CalleeReleaseTy = CalleeRelease->getOperand(0)->getType();
349-
CalleeRelease->setOperand(
354+
for (auto &X : CalleeRelease) {
355+
SILType CalleeReleaseTy = X->getOperand(0)->getType();
356+
X->setOperand(
350357
0, SILUndef::get(CalleeReleaseTy, Builder.getModule()));
351-
352-
// TODO: Currently we cannot mark arguments as dead if they are released
353-
// in a throw block. But as soon as we can do this, we have to handle
354-
// CalleeReleaseInThrowBlock as well.
355-
assert(!CalleeReleaseInThrowBlock &&
356-
"released arg in throw block cannot be dead");
357358
}
358359

359360
// We should be able to recursively delete all of the remaining
@@ -533,7 +534,7 @@ bool ParameterAnalyzer::analyze() {
533534

534535
// If this argument is not ABI required and has no uses except for debug
535536
// instructions, remove it.
536-
if (!isABIRequired && OnlyRelease && OnlyRelease.getValue().isNull()) {
537+
if (!isABIRequired && OnlyRelease && OnlyRelease.getValue().empty()) {
537538
A.IsDead = true;
538539
HaveOptimizedArg = true;
539540
++NumDeadArgsEliminated;
@@ -542,21 +543,22 @@ bool ParameterAnalyzer::analyze() {
542543
// See if we can find a ref count equivalent strong_release or release_value
543544
// at the end of this function if our argument is an @owned parameter.
544545
if (A.hasConvention(ParameterConvention::Direct_Owned)) {
545-
if (auto *Release = ArgToReturnReleaseMap.getSingleReleaseForArgument(A.Arg)) {
546-
SILInstruction *ReleaseInThrow = nullptr;
546+
auto Releases = ArgToReturnReleaseMap.getReleasesForArgument(A.Arg);
547+
if (!Releases.empty()) {
547548

548549
// If the function has a throw block we must also find a matching
549550
// release in the throw block.
550-
if (!ArgToThrowReleaseMap.hasBlock() ||
551-
(ReleaseInThrow = ArgToThrowReleaseMap.getSingleReleaseForArgument(A.Arg))) {
551+
auto ReleasesInThrow = ArgToThrowReleaseMap.getReleasesForArgument(A.Arg);
552+
if (!ArgToThrowReleaseMap.hasBlock() || !ReleasesInThrow.empty()) {
552553

553554
// TODO: accept a second release in the throw block to let the
554555
// argument be dead.
555-
if (OnlyRelease && OnlyRelease.getValue().getPtrOrNull() == Release) {
556+
if (OnlyRelease && hasIdenticalReleases(OnlyRelease.getValue(), Releases)) {
556557
A.IsDead = true;
557558
}
558-
A.CalleeRelease = Release;
559-
A.CalleeReleaseInThrowBlock = ReleaseInThrow;
559+
560+
A.CalleeRelease = Releases;
561+
A.CalleeReleaseInThrowBlock = ReleasesInThrow;
560562
HaveOptimizedArg = true;
561563
++NumOwnedConvertedToGuaranteed;
562564
}
@@ -600,7 +602,7 @@ std::string ParameterAnalyzer::getOptimizedName() const {
600602

601603
// If we have an @owned argument and found a callee release for it,
602604
// convert the argument to guaranteed.
603-
if (Arg.CalleeRelease) {
605+
if (!Arg.CalleeRelease.empty()) {
604606
FSSM.setArgumentOwnedToGuaranteed(i);
605607
}
606608

@@ -721,7 +723,7 @@ static void rewriteApplyInstToCallNewFunction(SignatureOptimizer &Optimizer,
721723
// If we have any arguments that were consumed but are now guaranteed,
722724
// insert a release_value in the error block.
723725
for (auto &ArgDesc : ArgDescs) {
724-
if (!ArgDesc.CalleeRelease)
726+
if (ArgDesc.CalleeRelease.empty())
725727
continue;
726728
Builder.createReleaseValue(Loc, FAS.getArgument(ArgDesc.Index));
727729
}
@@ -733,7 +735,7 @@ static void rewriteApplyInstToCallNewFunction(SignatureOptimizer &Optimizer,
733735
// If we have any arguments that were consumed but are now guaranteed,
734736
// insert a release_value.
735737
for (auto &ArgDesc : ArgDescs) {
736-
if (!ArgDesc.CalleeRelease)
738+
if (ArgDesc.CalleeRelease.empty())
737739
continue;
738740
Builder.createReleaseValue(Loc, FAS.getArgument(ArgDesc.Index));
739741
}
@@ -784,7 +786,7 @@ static void createThunkBody(SILBasicBlock *BB, SILFunction *NewF,
784786
// insert a release_value in the error block.
785787
Builder.setInsertionPoint(ErrorBlock);
786788
for (auto &ArgDesc : ArgDescs) {
787-
if (!ArgDesc.CalleeRelease)
789+
if (ArgDesc.CalleeRelease.empty())
788790
continue;
789791
Builder.createReleaseValue(Loc, BB->getBBArg(ArgDesc.Index));
790792
}
@@ -801,7 +803,7 @@ static void createThunkBody(SILBasicBlock *BB, SILFunction *NewF,
801803
// If we have any arguments that were consumed but are now guaranteed,
802804
// insert a release_value.
803805
for (auto &ArgDesc : ArgDescs) {
804-
if (!ArgDesc.CalleeRelease)
806+
if (ArgDesc.CalleeRelease.empty())
805807
continue;
806808
Builder.createReleaseValue(Loc, BB->getBBArg(ArgDesc.Index));
807809
}
@@ -911,12 +913,10 @@ static bool optimizeFunctionSignature(llvm::BumpPtrAllocator &BPA,
911913
//
912914
// TODO: If more stuff needs to be placed here, refactor into its own method.
913915
for (auto &A : Optimizer.getArgDescList()) {
914-
if (A.CalleeRelease) {
915-
A.CalleeRelease->eraseFromParent();
916-
if (A.CalleeReleaseInThrowBlock) {
917-
A.CalleeReleaseInThrowBlock->eraseFromParent();
918-
}
919-
}
916+
for (auto &X : A.CalleeRelease)
917+
X->eraseFromParent();
918+
for (auto &X : A.CalleeReleaseInThrowBlock)
919+
X->eraseFromParent();
920920
}
921921

922922
// Rewrite all apply insts calling F to call NewF. Update each call site as

test/SILOptimizer/functionsigopts.sil

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,38 @@
44
import Builtin
55
import Swift
66

7+
/////////////////////
8+
// Data Structures //
9+
/////////////////////
10+
11+
class foo {
12+
var a: Int
13+
deinit
14+
init()
15+
}
16+
17+
class bar {
18+
var start: foo
19+
var end: foo
20+
deinit
21+
init()
22+
}
23+
24+
struct baz {
25+
var start: foo
26+
var end: foo
27+
init()
28+
}
29+
30+
struct boo {
31+
var tbaz = baz()
32+
var a = 0
33+
init()
34+
}
35+
36+
37+
sil @use_Int : $@convention(thin) (Int) -> ()
38+
739
// We do this check separately to ensure that this does not occur anywhere in
840
// the output for the file. (Maybe this is an interesting enough feature to add to FileCheck?).
941

@@ -32,6 +64,44 @@ bb3:
3264
return %4 : $()
3365
}
3466

67+
// Make sure %0 is a dead argument.
68+
//
69+
// CHECK-LABEL: sil [thunk] @exploded_release_to_dead_argument
70+
// CHECK: bb0([[INPUT_ARG0:%[0-9]+]] : $boo):
71+
// CHECK: [[IN1:%.*]] = function_ref @_TTSf4dg__exploded_release_to_dead_argument
72+
// CHECK: apply [[IN1]]()
73+
// CHECK: release_value [[INPUT_ARG0]]
74+
sil @exploded_release_to_dead_argument : $@convention(thin) (@owned boo) -> () {
75+
bb0(%0 : $boo):
76+
%1 = struct_extract %0 : $boo, #boo.tbaz
77+
%2 = struct_extract %1 : $baz, #baz.start
78+
%3 = struct_extract %1 : $baz, #baz.end
79+
release_value %2 : $foo
80+
release_value %3 : $foo
81+
%4 = tuple ()
82+
return %4 : $()
83+
}
84+
85+
// Make sure %0 is not a dead argument, but gets converted to a guaranteed arg.
86+
//
87+
// CHECK-LABEL: sil [thunk] @exploded_release_to_guaranteed_param
88+
// CHECK: bb0([[INPUT_ARG0:%[0-9]+]] : $boo):
89+
// CHECK: [[IN1:%.*]] = function_ref @_TTSf4gs__exploded_release_to_guaranteed_param
90+
// CHECK: release_value [[INPUT_ARG0]]
91+
sil @exploded_release_to_guaranteed_param : $@convention(thin) (@owned boo) -> () {
92+
bb0(%0 : $boo):
93+
%1 = struct_extract %0 : $boo, #boo.tbaz
94+
%2 = struct_extract %1 : $baz, #baz.start
95+
%3 = struct_extract %1 : $baz, #baz.end
96+
%4 = struct_extract %0 : $boo, #boo.a
97+
%5 = function_ref @use_Int : $@convention(thin) (Int) -> ()
98+
apply %5(%4) : $@convention(thin) (Int) -> ()
99+
release_value %2 : $foo
100+
release_value %3 : $foo
101+
%6 = tuple ()
102+
return %6 : $()
103+
}
104+
35105
// CHECK-LABEL: sil [fragile] [thunk] @dead_arg_with_callsites : $@convention(thin) (Builtin.NativeObject, Builtin.NativeObject) -> () {
36106
// CHECK: bb0
37107
// CHECK-NEXT: function_ref
@@ -122,6 +192,25 @@ bb0(%0 : $Builtin.NativeObject, %1 : $Builtin.NativeObject):
122192
return %5 : $()
123193
}
124194

195+
sil [fragile] @exploded_release_to_guaranteed_param_callsite : $@convention(thin) (@owned boo) -> () {
196+
bb0(%0 : $boo):
197+
%2 = function_ref @exploded_release_to_guaranteed_param : $@convention(thin) (@owned boo) -> ()
198+
retain_value %0 : $boo
199+
%4 = apply %2(%0) : $@convention(thin) (@owned boo) -> ()
200+
%5 = tuple()
201+
return %5 : $()
202+
}
203+
204+
205+
sil [fragile] @exploded_release_to_dead_param_callsite : $@convention(thin) (@owned boo) -> () {
206+
bb0(%0 : $boo):
207+
%2 = function_ref @exploded_release_to_dead_argument : $@convention(thin) (@owned boo) -> ()
208+
retain_value %0 : $boo
209+
%4 = apply %2(%0) : $@convention(thin) (@owned boo) -> ()
210+
%5 = tuple()
211+
return %5 : $()
212+
}
213+
125214
//========================================
126215
// @owned => @guaranteed with error result
127216

@@ -446,6 +535,13 @@ bb0(%0 : $Builtin.Int32):
446535
return %9999 : $()
447536
}
448537

538+
// CHECK-LABEL: sil @_TTSf4dg__exploded_release_to_dead_argument
539+
540+
// CHECK-LABEL: sil @_TTSf4gs__exploded_release_to_guaranteed_param
541+
// CHECK: bb0([[INPUT_ARG0:%[0-9]+]] : $foo, [[INPUT_ARG1:%[0-9]+]] : $foo, [[INPUT_ARG02:%[0-9]+]] : $Int):
542+
// CHECK-NOT: strong_release
543+
// CHECK: return
544+
449545
// Check that we specialized this function by removing the dead argument and
450546
// copied everything appropriately.
451547

0 commit comments

Comments
 (0)