Skip to content

Commit 5ffa17c

Browse files
committed
---
yaml --- r: 349201 b: refs/heads/master-next c: 9567bd4 h: refs/heads/master i: 349199: 5b39f53
1 parent 7d5c566 commit 5ffa17c

12 files changed

+3063
-327
lines changed

[refs]

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
refs/heads/master: 3574c513bbc5578dd9346b4ea9ab5995c5927bb5
3-
refs/heads/master-next: 23a042207fd86a3462ef36bd3f3497eb8cef2a55
3+
refs/heads/master-next: 9567bd434181a4084a4fedf90a5b7654878da8e9
44
refs/tags/osx-passed: b6b74147ef8a386f532cf9357a1bde006e552c54
55
refs/tags/swift-2.2-SNAPSHOT-2015-12-01-a: 6bb18e013c2284f2b45f5f84f2df2887dc0f7dea
66
refs/tags/swift-2.2-SNAPSHOT-2015-12-01-b: 66d897bfcf64a82cb9a87f5e663d889189d06d07

branches/master-next/lib/SILOptimizer/FunctionSignatureTransforms/ArgumentExplosionTransform.cpp

Lines changed: 271 additions & 23 deletions
Large diffs are not rendered by default.

branches/master-next/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.cpp

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ static bool isSpecializableRepresentation(SILFunctionTypeRepresentation Rep,
9595
llvm_unreachable("Unhandled SILFunctionTypeRepresentation in switch.");
9696
}
9797

98-
/// Returns true if F is a function which the pass know show to specialize
98+
/// Returns true if F is a function which the pass knows how to specialize
9999
/// function signatures for.
100100
static bool canSpecializeFunction(SILFunction *F,
101101
const CallerAnalysis::FunctionInfo *FuncInfo,
@@ -622,7 +622,11 @@ void FunctionSignatureTransform::createFunctionSignatureOptimizedFunction() {
622622

623623
// Run the optimization.
624624
bool FunctionSignatureTransform::run(bool hasCaller) {
625-
bool Changed = false;
625+
// We use a reference here on purpose so our transformations can know if we
626+
// are going to make a thunk and thus should just optimize.
627+
bool &Changed = TransformDescriptor.Changed;
628+
bool hasOnlyDirectInModuleCallers =
629+
TransformDescriptor.hasOnlyDirectInModuleCallers;
626630
SILFunction *F = TransformDescriptor.OriginalFunction;
627631

628632
// Never repeat the same function signature optimization on the same function.
@@ -657,7 +661,8 @@ bool FunctionSignatureTransform::run(bool hasCaller) {
657661
// Run DeadArgument elimination transformation. We only specialize
658662
// if this function has a caller inside the current module or we have
659663
// already created a thunk.
660-
if ((hasCaller || Changed) && DeadArgumentAnalyzeParameters()) {
664+
if ((hasCaller || Changed || hasOnlyDirectInModuleCallers) &&
665+
DeadArgumentAnalyzeParameters()) {
661666
Changed = true;
662667
LLVM_DEBUG(llvm::dbgs() << " remove dead arguments\n");
663668
DeadArgumentTransformFunction();
@@ -675,7 +680,8 @@ bool FunctionSignatureTransform::run(bool hasCaller) {
675680
// In order to not miss any opportunity, we send the optimized function
676681
// to the passmanager to optimize any opportunities exposed by argument
677682
// explosion.
678-
if ((hasCaller || Changed) && ArgumentExplosionAnalyzeParameters()) {
683+
if ((hasCaller || Changed || hasOnlyDirectInModuleCallers) &&
684+
ArgumentExplosionAnalyzeParameters()) {
679685
Changed = true;
680686
}
681687

@@ -830,7 +836,8 @@ class FunctionSignatureOpts : public SILFunctionTransform {
830836
SILOptFunctionBuilder FuncBuilder(*this);
831837
// Owned to guaranteed optimization.
832838
FunctionSignatureTransform FST(FuncBuilder, F, RCIA, EA, Mangler, AIM,
833-
ArgumentDescList, ResultDescList);
839+
ArgumentDescList, ResultDescList,
840+
FuncInfo.foundAllCallers());
834841

835842
bool Changed = false;
836843
if (OptForPartialApply) {

branches/master-next/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.h

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,13 @@ struct FunctionSignatureTransformDescriptor {
198198
/// will use during our optimization.
199199
MutableArrayRef<ResultDescriptor> ResultDescList;
200200

201+
/// Are we going to make a change to this function?
202+
bool Changed;
203+
204+
/// Does this function only have direct callers. In such a case we know that
205+
/// all thunks we create will be eliminated so we can be more aggressive.
206+
bool hasOnlyDirectInModuleCallers;
207+
201208
/// Return a function name based on the current state of ArgumentDescList and
202209
/// ResultDescList.
203210
///
@@ -218,6 +225,19 @@ struct FunctionSignatureTransformDescriptor {
218225
/// simply passes it through.
219226
void addThunkArgument(ArgumentDescriptor &AD, SILBuilder &Builder,
220227
SILBasicBlock *BB, SmallVectorImpl<SILValue> &NewArgs);
228+
229+
/// Whether specializing the function will result in a thunk with the same
230+
/// signature as the original function that calls through to the specialized
231+
/// function.
232+
///
233+
/// Such a thunk is necessary if there is (or could be) code that calls the
234+
/// function which we are unable to specialize to match the function's
235+
/// specialization.
236+
bool willSpecializationIntroduceThunk() {
237+
return !hasOnlyDirectInModuleCallers ||
238+
OriginalFunction->isPossiblyUsedExternally() ||
239+
OriginalFunction->isAvailableExternally();
240+
}
221241
};
222242

223243
class FunctionSignatureTransform {
@@ -291,15 +311,17 @@ class FunctionSignatureTransform {
291311
public:
292312
/// Constructor.
293313
FunctionSignatureTransform(
294-
SILOptFunctionBuilder &FunctionBuilder,
295-
SILFunction *F, RCIdentityAnalysis *RCIA, EpilogueARCAnalysis *EA,
314+
SILOptFunctionBuilder &FunctionBuilder, SILFunction *F,
315+
RCIdentityAnalysis *RCIA, EpilogueARCAnalysis *EA,
296316
Mangle::FunctionSignatureSpecializationMangler &Mangler,
297317
llvm::SmallDenseMap<int, int> &AIM,
298318
llvm::SmallVector<ArgumentDescriptor, 4> &ADL,
299-
llvm::SmallVector<ResultDescriptor, 4> &RDL)
319+
llvm::SmallVector<ResultDescriptor, 4> &RDL,
320+
bool hasOnlyDirectInModuleCallers)
300321
: FunctionBuilder(FunctionBuilder),
301-
TransformDescriptor{F, nullptr, AIM, false, ADL, RDL}, RCIA(RCIA),
302-
EA(EA) {}
322+
TransformDescriptor{F, nullptr, AIM, false,
323+
ADL, RDL, false, hasOnlyDirectInModuleCallers},
324+
RCIA(RCIA), EA(EA) {}
303325

304326
/// Return the optimized function.
305327
SILFunction *getOptimizedFunction() {
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
// RUN: %target-sil-opt -enable-objc-interop -enable-sil-verify-all -function-signature-opts -sil-fso-disable-dead-argument -sil-fso-disable-owned-to-guaranteed -enable-expand-all -sil-fso-optimize-if-not-called %s | %FileCheck %s
2+
3+
// *NOTE* We turn off all other fso optimizations including dead arg so we can
4+
// make sure that we are not exploding those.
5+
6+
sil_stage canonical
7+
8+
import Builtin
9+
10+
//////////////////
11+
// Declarations //
12+
//////////////////
13+
14+
struct BigTrivial {
15+
var x1: Builtin.Int32
16+
var x2: Builtin.Int32
17+
var x3: Builtin.Int32
18+
var x4: Builtin.Int32
19+
var x5: Builtin.Int32
20+
var x6: Builtin.Int32
21+
}
22+
23+
class Klass {}
24+
25+
struct LargeNonTrivialStructOneNonTrivialField {
26+
var k1: Klass
27+
var k2: Klass
28+
var x1: Builtin.Int32
29+
var x2: Builtin.Int32
30+
var x3: Builtin.Int32
31+
var x4: Builtin.Int32
32+
}
33+
34+
sil @int_user : $@convention(thin) (Builtin.Int32) -> ()
35+
sil @consuming_user : $@convention(thin) (@owned Klass) -> ()
36+
sil @guaranteed_user : $@convention(thin) (@guaranteed Klass) -> ()
37+
38+
///////////
39+
// Tests //
40+
///////////
41+
42+
// We should never optimize this. If we did this would become a thunk, so we
43+
// know that just be checking NFC we have proven no optimization has occured.
44+
//
45+
// CHECK-LABEL: sil @never_explode_trivial : $@convention(thin) (BigTrivial) -> () {
46+
// CHECK: } // end sil function 'never_explode_trivial'
47+
sil @never_explode_trivial : $@convention(thin) (BigTrivial) -> () {
48+
bb0(%0 : $BigTrivial):
49+
%1 = struct_extract %0 : $BigTrivial, #BigTrivial.x1
50+
%intfunc = function_ref @int_user : $@convention(thin) (Builtin.Int32) -> ()
51+
apply %intfunc(%1) : $@convention(thin) (Builtin.Int32) -> ()
52+
%9999 = tuple()
53+
return %9999 : $()
54+
}
55+
56+
// If a value is never used, do not touch it. We leave it for dead argument
57+
// elimination. We have delibrately turned this off to test that behavior.
58+
//
59+
// CHECK-LABEL: sil @big_arg_with_no_uses : $@convention(thin) (@guaranteed LargeNonTrivialStructOneNonTrivialField) -> () {
60+
// CHECK-NOT: apply
61+
// CHECK: } // end sil function 'big_arg_with_no_uses'
62+
sil @big_arg_with_no_uses : $@convention(thin) (@guaranteed LargeNonTrivialStructOneNonTrivialField) -> () {
63+
bb0(%0 : $LargeNonTrivialStructOneNonTrivialField):
64+
%9999 = tuple()
65+
return %9999 : $()
66+
}
67+
68+
// We are using a single non-trivial field of the struct. We should explode this
69+
// so we eliminate the second non-trivial leaf.
70+
//
71+
// CHECK-LABEL: sil [signature_optimized_thunk] [always_inline] @big_arg_with_one_nontrivial_use : $@convention(thin) (@guaranteed LargeNonTrivialStructOneNonTrivialField) -> () {
72+
// CHECK: bb0([[ARG:%.*]] : $LargeNonTrivialStructOneNonTrivialField):
73+
// CHECK: [[FUNC:%.*]] = function_ref @$s31big_arg_with_one_nontrivial_useTf4x_n
74+
// CHECK: [[FIELD:%.*]] = struct_extract [[ARG]] : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.k1
75+
// CHECK: apply [[FUNC]]([[FIELD]])
76+
// CHECK: } // end sil function 'big_arg_with_one_nontrivial_use'
77+
sil @big_arg_with_one_nontrivial_use : $@convention(thin) (@guaranteed LargeNonTrivialStructOneNonTrivialField) -> () {
78+
bb0(%0 : $LargeNonTrivialStructOneNonTrivialField):
79+
%1 = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.k1
80+
%2 = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Klass) -> ()
81+
apply %2(%1) : $@convention(thin) (@guaranteed Klass) -> ()
82+
%9999 = tuple()
83+
return %9999 : $()
84+
}
85+
86+
// We are using a single non-trivial field and a single trivial field. We are
87+
// willing to blow this up.
88+
//
89+
// CHECK-LABEL: sil [signature_optimized_thunk] [always_inline] @big_arg_with_one_nontrivial_use_one_trivial_use : $@convention(thin) (@guaranteed LargeNonTrivialStructOneNonTrivialField) -> () {
90+
// CHECK: bb0([[ARG:%.*]] : $LargeNonTrivialStructOneNonTrivialField):
91+
// CHECK: [[FUNC:%.*]] = function_ref @$s032big_arg_with_one_nontrivial_use_d9_trivial_F0Tf4x_n : $@convention(thin) (@guaranteed Klass, Builtin.Int32) -> ()
92+
// CHECK: [[TRIVIAL_FIELD:%.*]] = struct_extract [[ARG]] : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.x1
93+
// CHECK: [[NON_TRIVIAL_FIELD:%.*]] = struct_extract [[ARG]] : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.k1
94+
// CHECK: apply [[FUNC]]([[NON_TRIVIAL_FIELD]], [[TRIVIAL_FIELD]])
95+
// CHECK: } // end sil function 'big_arg_with_one_nontrivial_use_one_trivial_use'
96+
sil @big_arg_with_one_nontrivial_use_one_trivial_use : $@convention(thin) (@guaranteed LargeNonTrivialStructOneNonTrivialField) -> () {
97+
bb0(%0 : $LargeNonTrivialStructOneNonTrivialField):
98+
%1 = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.k1
99+
%2 = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.x1
100+
%3 = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Klass) -> ()
101+
apply %3(%1) : $@convention(thin) (@guaranteed Klass) -> ()
102+
%intfunc = function_ref @int_user : $@convention(thin) (Builtin.Int32) -> ()
103+
apply %intfunc(%2) : $@convention(thin) (Builtin.Int32) -> ()
104+
%9999 = tuple()
105+
return %9999 : $()
106+
}
107+
108+
// We can still explode this, since our limit is 3 values.
109+
//
110+
// CHECK-LABEL: sil [signature_optimized_thunk] [always_inline] @big_arg_with_one_nontrivial_use_two_trivial_uses : $@convention(thin) (@guaranteed LargeNonTrivialStructOneNonTrivialField) -> () {
111+
// CHECK: bb0([[ARG:%.*]] : $LargeNonTrivialStructOneNonTrivialField):
112+
// CHECK: [[FUNC:%.*]] = function_ref @$s48big_arg_with_one_nontrivial_use_two_trivial_usesTf4x_n : $@convention(thin)
113+
// CHECK: [[TRIVIAL_FIELD1:%.*]] = struct_extract [[ARG]] : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.x2
114+
// CHECK: [[TRIVIAL_FIELD2:%.*]] = struct_extract [[ARG]] : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.x1
115+
// CHECK: [[NON_TRIVIAL_FIELD:%.*]] = struct_extract [[ARG]] : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.k1
116+
// CHECK: apply [[FUNC]]([[NON_TRIVIAL_FIELD]], [[TRIVIAL_FIELD2]], [[TRIVIAL_FIELD1]])
117+
sil @big_arg_with_one_nontrivial_use_two_trivial_uses : $@convention(thin) (@guaranteed LargeNonTrivialStructOneNonTrivialField) -> () {
118+
bb0(%0 : $LargeNonTrivialStructOneNonTrivialField):
119+
%1 = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.k1
120+
%2 = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.x1
121+
%3 = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.x2
122+
%4 = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Klass) -> ()
123+
apply %4(%1) : $@convention(thin) (@guaranteed Klass) -> ()
124+
%intfunc = function_ref @int_user : $@convention(thin) (Builtin.Int32) -> ()
125+
apply %intfunc(%2) : $@convention(thin) (Builtin.Int32) -> ()
126+
apply %intfunc(%3) : $@convention(thin) (Builtin.Int32) -> ()
127+
%9999 = tuple()
128+
return %9999 : $()
129+
}
130+
131+
// We do not blow up the struct here since we have 4 uses, not 3.
132+
//
133+
// CHECK-LABEL: sil @big_arg_with_one_nontrivial_use_three_trivial_uses : $@convention(thin) (@guaranteed LargeNonTrivialStructOneNonTrivialField) -> () {
134+
sil @big_arg_with_one_nontrivial_use_three_trivial_uses : $@convention(thin) (@guaranteed LargeNonTrivialStructOneNonTrivialField) -> () {
135+
bb0(%0 : $LargeNonTrivialStructOneNonTrivialField):
136+
%1 = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.k1
137+
%2 = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.x1
138+
%3 = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.x2
139+
%3a = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.x3
140+
%4 = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Klass) -> ()
141+
apply %4(%1) : $@convention(thin) (@guaranteed Klass) -> ()
142+
%intfunc = function_ref @int_user : $@convention(thin) (Builtin.Int32) -> ()
143+
apply %intfunc(%2) : $@convention(thin) (Builtin.Int32) -> ()
144+
apply %intfunc(%3) : $@convention(thin) (Builtin.Int32) -> ()
145+
apply %intfunc(%3a) : $@convention(thin) (Builtin.Int32) -> ()
146+
%9999 = tuple()
147+
return %9999 : $()
148+
}
149+
150+
// In this case, we shouldn't blow up the struct since we have not reduced the
151+
// number of non-trivial leaf nodes used.
152+
//
153+
// CHECK-LABEL: sil @big_arg_with_two_nontrivial_use : $@convention(thin) (@guaranteed LargeNonTrivialStructOneNonTrivialField) -> () {
154+
sil @big_arg_with_two_nontrivial_use : $@convention(thin) (@guaranteed LargeNonTrivialStructOneNonTrivialField) -> () {
155+
bb0(%0 : $LargeNonTrivialStructOneNonTrivialField):
156+
%1 = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.k1
157+
%2 = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.k2
158+
%3 = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Klass) -> ()
159+
apply %3(%1) : $@convention(thin) (@guaranteed Klass) -> ()
160+
apply %3(%2) : $@convention(thin) (@guaranteed Klass) -> ()
161+
%9999 = tuple()
162+
return %9999 : $()
163+
}
164+
165+
// If we have one non-trivial value that is live and only live because of a
166+
// destroy, we can delete the argument after performing o2g.
167+
//
168+
// We are using a single non-trivial field of the struct. We should explode this
169+
// so we eliminate the second non-trivial leaf.
170+
//
171+
// CHECK-LABEL: sil [signature_optimized_thunk] [always_inline] @big_arg_with_one_nontrivial_use_o2g_other_dead : $@convention(thin) (@owned LargeNonTrivialStructOneNonTrivialField) -> () {
172+
// CHECK-NOT: release_value
173+
// CHECK: apply
174+
// CHECK-NOT: release_value
175+
// CHECK: } // end sil function 'big_arg_with_one_nontrivial_use_o2g_other_dead'
176+
sil @big_arg_with_one_nontrivial_use_o2g_other_dead : $@convention(thin) (@owned LargeNonTrivialStructOneNonTrivialField) -> () {
177+
bb0(%0 : $LargeNonTrivialStructOneNonTrivialField):
178+
%1 = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.k1
179+
release_value %1 : $Klass
180+
%9999 = tuple()
181+
return %9999 : $()
182+
}
183+
184+
// If we have two non-trivial values that are live and one is always dead and
185+
// the other is kept alive due to a release, we can get rid of both since FSO
186+
// reruns with o2g. Test here that we explode it appropriatel even though we
187+
// aren't reducing the number of non-trivial uses. The
188+
// funcsig_explode_heuristic_inline.sil test makes sure we in combination
189+
// produce the appropriate SIL.
190+
//
191+
// We check that we can inline this correctly in the inline test.
192+
//
193+
// CHECK-LABEL: sil [signature_optimized_thunk] [always_inline] @big_arg_with_one_nontrivial_use_o2g : $@convention(thin) (@owned LargeNonTrivialStructOneNonTrivialField) -> () {
194+
// CHECK: bb0([[ARG:%.*]] : $LargeNonTrivialStructOneNonTrivialField):
195+
// CHECK: [[FUNC:%.*]] = function_ref @$s35big_arg_with_one_nontrivial_use_o2gTf4x_n : $@convention(thin) (@owned Klass, @owned Klass) -> ()
196+
// CHECK: apply [[FUNC]](
197+
// CHECK: } // end sil function 'big_arg_with_one_nontrivial_use_o2g'
198+
sil @big_arg_with_one_nontrivial_use_o2g : $@convention(thin) (@owned LargeNonTrivialStructOneNonTrivialField) -> () {
199+
bb0(%0 : $LargeNonTrivialStructOneNonTrivialField):
200+
%1 = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.k1
201+
%2 = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.k2
202+
%3 = function_ref @consuming_user : $@convention(thin) (@owned Klass) -> ()
203+
apply %3(%2) : $@convention(thin) (@owned Klass) -> ()
204+
release_value %1 : $Klass
205+
%9999 = tuple()
206+
return %9999 : $()
207+
}

0 commit comments

Comments
 (0)