Skip to content

Commit f95d9b3

Browse files
committed
Change FSO heuristic.
FSO functions that have high potential but does not have caller inside current module. The thunk can then be inlined into the module calling the function and the function would get the benefit of FSO. The heuristic for selecting such function is 1. Have no indirect caller. This would introduce a thunk. 2. Have potential to give better performance. i.e. function argument can be O2G. Regression TEST | OLD_MIN | NEW_MIN | DELTA (%) | SPEEDUP --- | --- | --- | --- | --- BenchLangCallingCFunction | 184 | 211 | +14.7% | **0.87x** Calculator | 55 | 59 | +7.3% | **0.93x** DeadArray | 687 | 741 | +7.9% | **0.93x** MonteCarloPi | 39275 | 41669 | +6.1% | **0.94x** Improvement TEST | OLD_MIN | NEW_MIN | DELTA (%) | SPEEDUP --- | --- | --- | --- | --- LuhnAlgoLazy | 2478 | 2327 | -6.1% | **1.06x** OpenClose | 54 | 51 | -5.6% | **1.06x** SortLettersInPlace | 1016 | 946 | -6.9% | **1.07x** ObjectiveCBridgeFromNSDictionaryAnyObjectToStringForced | 149993 | 139755 | -6.8% | **1.07x** Phonebook | 9666 | 8992 | -7.0% | **1.07x** ObjectiveCBridgeFromNSDictionaryAnyObjectToString | 222713 | 206538 | -7.3% | **1.08x** LuhnAlgoEager | 2393 | 2226 | -7.0% | **1.08x** Dictionary | 1307 | 1196 | -8.5% | **1.09x** JSONHelperDeserialize | 3808 | 3492 | -8.3% | **1.09x** StdlibSort | 7310 | 4084 | -44.1% | **1.79x** I see 0.15% increase in code size for Benchmark_O. Thanks @gottesmm for suggesting this opportunity. rdar://25345056
1 parent 99ea2fd commit f95d9b3

11 files changed

+149
-128
lines changed

include/swift/SILOptimizer/Utils/FunctionSignatureOptUtils.h

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,12 @@ struct ResultDescriptor {
172172
};
173173

174174
class FunctionSignatureInfo {
175+
/// Should this function be optimized.
176+
bool ShouldOptimize;
177+
178+
/// Optimizing this function may lead to good performance potential.
179+
bool HighlyProfitable;
180+
175181
/// Function currently analyzing.
176182
SILFunction *F;
177183

@@ -204,10 +210,15 @@ class FunctionSignatureInfo {
204210
public:
205211
FunctionSignatureInfo(SILFunction *F, llvm::BumpPtrAllocator &BPA,
206212
AliasAnalysis *AA, RCIdentityFunctionInfo *RCFI) :
207-
F(F), Allocator(BPA), AA(AA), RCFI(RCFI),
208-
MayBindDynamicSelf(computeMayBindDynamicSelf(F)) {}
213+
ShouldOptimize(false), HighlyProfitable(false), F(F), Allocator(BPA),
214+
AA(AA), RCFI(RCFI), MayBindDynamicSelf(computeMayBindDynamicSelf(F)) {
215+
analyze();
216+
}
217+
218+
bool shouldOptimize() const { return ShouldOptimize; }
219+
bool profitableOptimize() const { return HighlyProfitable; }
209220

210-
bool analyze();
221+
void analyze();
211222
bool analyzeParameters();
212223
bool analyzeResult();
213224

lib/SILOptimizer/Transforms/FunctionSignatureOpts.cpp

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -486,10 +486,6 @@ static SILFunction*
486486
createOptimizedFunction(RCIdentityFunctionInfo *RCIA,
487487
FunctionSignatureInfo *FSI,
488488
AliasAnalysis *AA, SILFunction *F) {
489-
// Analyze function arguments. If there is no work to be done, exit early.
490-
if (!FSI->analyze())
491-
return nullptr;
492-
493489
// This is the new function name.
494490
auto NewFName = FSI->getOptimizedName();
495491

@@ -551,10 +547,21 @@ class FunctionSignatureOpts : public SILFunctionTransform {
551547
if (!F->shouldOptimize())
552548
return;
553549

554-
// If this function does not have a caller in the current module. We do not
555-
// function signature specialize it.
556-
if (!CA->hasCaller(F))
557-
return;
550+
// If there is no opportunity on the signature, simply return.
551+
if (!FSI.shouldOptimize())
552+
return;
553+
554+
// If this function does not have a caller in the current module.
555+
if (!CA->hasCaller(F)) {
556+
// If this function maybe called indirectly, e.g. from virtual table
557+
// do not function signature specialize it, as this will introduce a thunk.
558+
if (canBeCalledIndirectly(F->getRepresentation()))
559+
return;
560+
// if its not highly profitable to optimize this function. We do not
561+
// function signature specialize it.
562+
if (!FSI.profitableOptimize())
563+
return;
564+
}
558565

559566
// Check the signature of F to make sure that it is a function that we
560567
// can specialize. These are conditions independent of the call graph.

lib/SILOptimizer/Utils/FunctionSignatureOptUtils.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -345,11 +345,19 @@ bool FunctionSignatureInfo::analyzeResult() {
345345
/// This function goes through the arguments of F and sees if we have anything
346346
/// to optimize in which case it returns true. If we have nothing to optimize,
347347
/// it returns false.
348-
bool FunctionSignatureInfo::analyze() {
348+
void FunctionSignatureInfo::analyze() {
349349
// Compute the signature optimization.
350350
bool OptimizedParams = analyzeParameters();
351351
bool OptimizedResult = analyzeResult();
352-
return OptimizedParams || OptimizedResult;
352+
ShouldOptimize = OptimizedParams || OptimizedResult;
353+
// We set this function to highly profitable if we have a O2G on one of its
354+
// parameters or results.
355+
for (auto &X : ArgDescList) {
356+
HighlyProfitable |= !X.CalleeRelease.empty();
357+
}
358+
for (auto &X : ResultDescList) {
359+
HighlyProfitable |= !X.CalleeRetain.empty();
360+
}
353361
}
354362

355363
//===----------------------------------------------------------------------===//

test/SILOptimizer/cast_folding_objc.swift

Lines changed: 29 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -55,33 +55,6 @@ func test0() -> Bool {
5555
return cast0(NSNumber(integer:1))
5656
}
5757

58-
// Check that compiler understands that this cast always succeeds.
59-
// Since it is can be statically proven that NSString is bridgeable to String,
60-
// _forceBridgeFromObjectiveC from String should be invoked instead of
61-
// a more general, but less effective swift_bridgeNonVerbatimFromObjectiveC, which
62-
// also performs conformance checks at runtime.
63-
// CHECK-LABEL: sil [noinline] @_TF17cast_folding_objc30testBridgedCastFromObjCtoSwiftFCSo8NSStringSS
64-
// CHECK-NOT: {{ cast}}
65-
// CHECK: metatype $@thick String.Type
66-
// CHECK: function_ref @_TTWSSs21_ObjectiveCBridgeable10FoundationZFS_26_forceBridgeFromObjectiveCfTwx15_ObjectiveCType6resultRGSqx__T_
67-
// CHECK: apply
68-
// CHECK: return
69-
@inline(never)
70-
public func testBridgedCastFromObjCtoSwift(ns: NSString) -> String {
71-
return ns as String
72-
}
73-
74-
// Check that compiler understands that this cast always succeeds
75-
// CHECK-LABEL: sil [noinline] @_TF17cast_folding_objc30testBridgedCastFromSwiftToObjCFSSCSo8NSString
76-
// CHECK-NOT: {{ cast}}
77-
// CHECK: function_ref @_TFE10FoundationSS19_bridgeToObjectiveC
78-
// CHECK: apply
79-
// CHECK: return
80-
@inline(never)
81-
public func testBridgedCastFromSwiftToObjC(s: String) -> NSString {
82-
return s as NSString
83-
}
84-
8558
// Check that this cast does not get eliminated, because
8659
// the compiler does not statically know if this object
8760
// is NSNumber can be converted into Int.
@@ -270,3 +243,32 @@ public func testCastEveryToNonClassType<T>(o: T) -> Int.Type {
270243
}
271244

272245
print("test0=\(test0())")
246+
247+
// Check that compiler understands that this cast always succeeds.
248+
// Since it is can be statically proven that NSString is bridgeable to String,
249+
// _forceBridgeFromObjectiveC from String should be invoked instead of
250+
// a more general, but less effective swift_bridgeNonVerbatimFromObjectiveC, which
251+
// also performs conformance checks at runtime.
252+
// CHECK-LABEL: sil [noinline] @_TTSf4g___TF17cast_folding_objc30testBridgedCastFromObjCtoSwiftFCSo8NSStringSS
253+
// CHECK-NOT: {{ cast}}
254+
// CHECK: metatype $@thick String.Type
255+
// CHECK: function_ref @_TTWSSs21_ObjectiveCBridgeable10FoundationZFS_26_forceBridgeFromObjectiveCfTwx15_ObjectiveCType6resultRGSqx__T_
256+
// CHECK: apply
257+
// CHECK: return
258+
@inline(never)
259+
public func testBridgedCastFromObjCtoSwift(ns: NSString) -> String {
260+
return ns as String
261+
}
262+
263+
// Check that compiler understands that this cast always succeeds
264+
// CHECK-LABEL: sil [noinline] @_TTSf4gs___TF17cast_folding_objc30testBridgedCastFromSwiftToObjCFSSCSo8NSString
265+
// CHECK-NOT: {{ cast}}
266+
// CHECK: function_ref @_TFE10FoundationSS19_bridgeToObjectiveC
267+
// CHECK: apply
268+
// CHECK: return
269+
@inline(never)
270+
public func testBridgedCastFromSwiftToObjC(s: String) -> NSString {
271+
return s as NSString
272+
}
273+
274+

test/SILOptimizer/devirt_access_other_module.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public func getExternalClass() -> ExternalClass {
1818
// Note: This will eventually be illegal (to have a public @_transparent function
1919
// referring to a private method), but for now it lets us test what can and
2020
// can't be optimized.
21-
// CHECK-LABEL: sil [transparent] [fragile] @_TF26devirt_access_other_module9invokeFooFCS_13ExternalClassT_
21+
// CHECK-LABEL: sil [transparent] [fragile] @_TTSf4g___TF26devirt_access_other_module9invokeFooFCS_13ExternalClassT_
2222
// CHECK-NOT: function_ref
2323
// CHECK: class_method
2424
// CHECK-NOT: function_ref

test/SILOptimizer/devirt_covariant_return.swift

Lines changed: 46 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,37 @@ func driver() -> () {
105105

106106
driver()
107107

108+
public class Bear {
109+
public init?(fail: Bool) {
110+
if fail { return nil }
111+
}
112+
113+
// Check that devirtualizer can handle convenience initializers, which have covariant optional
114+
// return types.
115+
// CHECK-LABEL: sil @_TFC23devirt_covariant_return4Bearc
116+
// CHECK: checked_cast_br [exact] %{{.*}} : $Bear to $PolarBear
117+
// CHECK: upcast %{{.*}} : $Optional<PolarBear> to $Optional<Bear>
118+
// CHECK: }
119+
public convenience init?(delegateFailure: Bool, failAfter: Bool) {
120+
self.init(fail: delegateFailure)
121+
if failAfter { return nil }
122+
}
123+
}
124+
125+
final class PolarBear: Bear {
126+
127+
override init?(fail: Bool) {
128+
super.init(fail: fail)
129+
}
130+
131+
init?(chainFailure: Bool, failAfter: Bool) {
132+
super.init(fail: chainFailure)
133+
if failAfter { return nil }
134+
}
135+
}
136+
137+
138+
108139

109140
class Payload {
110141
let value: Int32
@@ -148,7 +179,7 @@ final class C2:C {
148179
// Check that the Optional return value from doSomething
149180
// gets properly unwrapped into a Payload object and then further
150181
// devirtualized.
151-
// CHECK-LABEL: sil hidden @_TF23devirt_covariant_return7driver1FCS_2C1Vs5Int32 :
182+
// CHECK-LABEL: sil hidden @_TTSf4dg___TF23devirt_covariant_return7driver1FCS_2C1Vs5Int32
152183
// CHECK: integer_literal $Builtin.Int32, 2
153184
// CHECK: struct $Int32 (%{{.*}} : $Builtin.Int32)
154185
// CHECK-NOT: class_method
@@ -161,7 +192,7 @@ func driver1(c: C1) -> Int32 {
161192
// Check that the Optional return value from doSomething
162193
// gets properly unwrapped into a Payload object and then further
163194
// devirtualized.
164-
// CHECK-LABEL: sil hidden @_TF23devirt_covariant_return7driver3FCS_1CVs5Int32 :
195+
// CHECK-LABEL: sil hidden @_TTSf4g___TF23devirt_covariant_return7driver3FCS_1CVs5Int32
165196
// CHECK: bb{{[0-9]+}}(%{{[0-9]+}} : $C2):
166197
// CHECK-NOT: bb{{.*}}:
167198
// check that for C2, we convert the non-optional result into an optional and then cast.
@@ -172,35 +203,6 @@ func driver3(c: C) -> Int32 {
172203
return c.doSomething()!.getValue()
173204
}
174205

175-
public class Bear {
176-
public init?(fail: Bool) {
177-
if fail { return nil }
178-
}
179-
180-
// Check that devirtualizer can handle convenience initializers, which have covariant optional
181-
// return types.
182-
// CHECK-LABEL: sil @_TFC23devirt_covariant_return4Bearc
183-
// CHECK: checked_cast_br [exact] %{{.*}} : $Bear to $PolarBear
184-
// CHECK: upcast %{{.*}} : $Optional<PolarBear> to $Optional<Bear>
185-
// CHECK: }
186-
public convenience init?(delegateFailure: Bool, failAfter: Bool) {
187-
self.init(fail: delegateFailure)
188-
if failAfter { return nil }
189-
}
190-
}
191-
192-
final class PolarBear: Bear {
193-
194-
override init?(fail: Bool) {
195-
super.init(fail: fail)
196-
}
197-
198-
init?(chainFailure: Bool, failAfter: Bool) {
199-
super.init(fail: chainFailure)
200-
if failAfter { return nil }
201-
}
202-
}
203-
204206
public class D {
205207
let v: Int32
206208
init(_ n: Int32) {
@@ -230,7 +232,7 @@ public class D2: D1 {
230232

231233
// Check that the boo call gets properly devirtualized and that
232234
// that D2.foo() is inlined thanks to this.
233-
// CHECK-LABEL: sil hidden @_TF23devirt_covariant_return7driver2FCS_2D2Vs5Int32
235+
// CHECK-LABEL: sil hidden @_TTSf4g___TF23devirt_covariant_return7driver2FCS_2D2Vs5Int32
234236
// CHECK-NOT: class_method
235237
// CHECK: checked_cast_br [exact] %{{.*}} : $D1 to $D2
236238
// CHECK: bb2
@@ -274,7 +276,7 @@ class EEE : CCC {
274276

275277
// Check that c.foo() is devirtualized, because the optimizer can handle the casting the return type
276278
// correctly, i.e. it can cast (BBB, BBB) into (AAA, AAA)
277-
// CHECK-LABEL: sil hidden @_TF23devirt_covariant_return37testDevirtOfMethodReturningTupleTypesFTCS_3CCC1bCS_2BB_TCS_2AAS2__
279+
// CHECK-LABEL: sil hidden @_TTSf4g_n___TF23devirt_covariant_return37testDevirtOfMethodReturningTupleTypesFTCS_3CCC1bCS_2BB_TCS_2AAS2__
278280
// CHECK: checked_cast_br [exact] %{{.*}} : $CCC to $CCC
279281
// CHECK: checked_cast_br [exact] %{{.*}} : $CCC to $DDD
280282
// CHECK: checked_cast_br [exact] %{{.*}} : $CCC to $EEE
@@ -311,17 +313,19 @@ class DDDD : CCCC {
311313
}
312314
}
313315

314-
// Check that c.foo OSX 10.9 be devirtualized, because the optimizer can handle the casting the return type
315-
// correctly, i.e. it cannot cast (BBBB, BBBB) into (AAAA, AAAA)
316-
// CHECK-LABEL: sil hidden @_TF23devirt_covariant_return38testDevirtOfMethodReturningTupleTypes2FCS_4CCCCTCS_4AAAAS1__
317-
// CHECK: checked_cast_br [exact] %{{.*}} : $CCCC to $CCCC
318-
// CHECK: checked_cast_br [exact] %{{.*}} : $CCCC to $DDDD
319-
// CHECK: class_method
320-
// CHECK: }
321-
func testDevirtOfMethodReturningTupleTypes2(c: CCCC) -> (AAAA, AAAA) {
322-
return c.foo
316+
// Check devirtualization of methods with optional results, where
317+
// optional results need to be casted.
318+
// CHECK-LABEL: sil @{{.*}}testOverridingMethodWithOptionalResult
319+
// CHECK: checked_cast_br [exact] %{{.*}} : $F to $F
320+
// CHECK: checked_cast_br [exact] %{{.*}} : $F to $G
321+
// CHECK: switch_enum
322+
// CHECK: checked_cast_br [exact] %{{.*}} : $F to $H
323+
// CHECK: switch_enum
324+
public func testOverridingMethodWithOptionalResult(f: F) -> (F?, Int)? {
325+
return f.foo()
323326
}
324327

328+
325329
public class F {
326330
@inline(never)
327331
public func foo() -> (F?, Int)? {
@@ -342,16 +346,3 @@ public class H: F {
342346
return nil
343347
}
344348
}
345-
346-
// Check devirtualization of methods with optional results, where
347-
// optional results need to be casted.
348-
// CHECK-LABEL: sil @{{.*}}testOverridingMethodWithOptionalResult
349-
// CHECK: checked_cast_br [exact] %{{.*}} : $F to $F
350-
// CHECK: checked_cast_br [exact] %{{.*}} : $F to $G
351-
// CHECK: switch_enum
352-
// CHECK: checked_cast_br [exact] %{{.*}} : $F to $H
353-
// CHECK: switch_enum
354-
public func testOverridingMethodWithOptionalResult(f: F) -> (F?, Int)? {
355-
return f.foo()
356-
}
357-

0 commit comments

Comments
 (0)