Skip to content

Commit 00d663d

Browse files
committed
[sil-generic-specializer] Add @_semantics("optimize.sil.specialize.generic.partial.never") to disable partial specialization on functions
This new @_semantics is used to annotate some very big functions in the standard library. It reduced the code size of the stdlib by 2%.
1 parent ef41353 commit 00d663d

File tree

5 files changed

+71
-6
lines changed

5 files changed

+71
-6
lines changed

lib/SILOptimizer/Utils/Generics.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,8 @@ static bool isTypeTooComplex(Type t) {
170170
// ReabstractionInfo
171171
// =============================================================================
172172

173-
static bool shouldNotSpecializeCallee(SILFunction *Callee) {
173+
static bool shouldNotSpecializeCallee(SILFunction *Callee,
174+
SubstitutionList Subs = {}) {
174175
if (!Callee->shouldOptimize()) {
175176
DEBUG(llvm::dbgs() << " Cannot specialize function " << Callee->getName()
176177
<< " marked to be excluded from optimizations.\n");
@@ -180,6 +181,10 @@ static bool shouldNotSpecializeCallee(SILFunction *Callee) {
180181
if (Callee->hasSemanticsAttr("optimize.sil.specialize.generic.never"))
181182
return true;
182183

184+
if (!Subs.empty() &&
185+
Callee->hasSemanticsAttr("optimize.sil.specialize.generic.partial.never"))
186+
return true;
187+
183188
return false;
184189
}
185190

@@ -291,6 +296,10 @@ bool ReabstractionInfo::prepareAndCheck(ApplySite Apply, SILFunction *Callee,
291296
// We need a generic environment for the partial specialization.
292297
if (!CalleeGenericEnv)
293298
return false;
299+
300+
// Bail if the callee should not be partially specialized.
301+
if (shouldNotSpecializeCallee(Callee, ParamSubs))
302+
return false;
294303
}
295304

296305
return true;

lib/SILOptimizer/Utils/PerformanceInlinerUtils.cpp

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,31 @@ static bool calleeHasPartialApplyWithOpenedExistentials(FullApplySite AI) {
591591
return false;
592592
}
593593

594+
// Returns true if a given apply site should be skipped during the
595+
// early inlining pass.
596+
//
597+
// NOTE: Add here the checks for any specific @_semantics/@_effects
598+
// attributes causing a given callee to be excluded from the inlining
599+
// during the early inlining pass.
600+
static bool shouldSkipApplyDuringEarlyInlining(FullApplySite AI) {
601+
// Add here the checks for any specific @_semantics attributes that need
602+
// to be skipped during the early inlining pass.
603+
ArraySemanticsCall ASC(AI.getInstruction());
604+
if (ASC && !ASC.canInlineEarly())
605+
return true;
606+
607+
SILFunction *Callee = AI.getReferencedFunction();
608+
if (!Callee)
609+
return false;
610+
611+
// Add here the checks for any specific @_effects attributes that need
612+
// to be skipped during the early inlining pass.
613+
if (Callee->hasEffectsKind())
614+
return true;
615+
616+
return false;
617+
}
618+
594619
// Returns the callee of an apply_inst if it is basically inlineable.
595620
SILFunction *swift::getEligibleFunction(FullApplySite AI,
596621
InlineSelection WhatToInline) {
@@ -606,8 +631,7 @@ SILFunction *swift::getEligibleFunction(FullApplySite AI,
606631
// attribute if the inliner is asked not to inline them.
607632
if (Callee->hasSemanticsAttrs() || Callee->hasEffectsKind()) {
608633
if (WhatToInline == InlineSelection::NoSemanticsAndGlobalInit) {
609-
ArraySemanticsCall ASC(AI.getInstruction());
610-
if (ASC && !ASC.canInlineEarly())
634+
if (shouldSkipApplyDuringEarlyInlining(AI))
611635
return nullptr;
612636
}
613637
// The "availability" semantics attribute is treated like global-init.

stdlib/public/core/Integers.swift.gyb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2271,6 +2271,7 @@ ${operatorComment(x.nonMaskingOperator, True)}
22712271
}
22722272

22732273
extension FixedWidthInteger {
2274+
@_semantics("optimize.sil.specialize.generic.partial.never")
22742275
public init<Other: BinaryInteger>(clamping source: Other) {
22752276
if _slowPath(source < Self.min) {
22762277
self = Self.min
@@ -2416,6 +2417,7 @@ extension UnsignedInteger {
24162417
}
24172418

24182419
extension UnsignedInteger where Self : FixedWidthInteger {
2420+
@_semantics("optimize.sil.specialize.generic.partial.never")
24192421
@inline(__always)
24202422
public init<T : BinaryInteger>(_ source: T) {
24212423
// This check is potentially removable by the optimizer
@@ -2430,6 +2432,7 @@ extension UnsignedInteger where Self : FixedWidthInteger {
24302432
self.init(extendingOrTruncating: source)
24312433
}
24322434

2435+
@_semantics("optimize.sil.specialize.generic.partial.never")
24332436
@inline(__always)
24342437
public init?<T : BinaryInteger>(exactly source: T) {
24352438
// This check is potentially removable by the optimizer
@@ -2494,6 +2497,7 @@ extension SignedInteger {
24942497
}
24952498

24962499
extension SignedInteger where Self : FixedWidthInteger {
2500+
@_semantics("optimize.sil.specialize.generic.partial.never")
24972501
@inline(__always)
24982502
public init<T : BinaryInteger>(_ source: T) {
24992503
// This check is potentially removable by the optimizer
@@ -2510,6 +2514,7 @@ extension SignedInteger where Self : FixedWidthInteger {
25102514
self.init(extendingOrTruncating: source)
25112515
}
25122516

2517+
@_semantics("optimize.sil.specialize.generic.partial.never")
25132518
@inline(__always)
25142519
public init?<T : BinaryInteger>(exactly source: T) {
25152520
// This check is potentially removable by the optimizer

stdlib/public/core/String.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ extension _StringCore {
211211
}
212212
}
213213

214+
@_semantics("optimize.sil.specialize.generic.partial.never")
214215
internal func _withCSubstringAndLength<
215216
Result, TargetEncoding: Unicode.Encoding
216217
>(

test/SILOptimizer/inline_semantics.sil

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,43 @@ bb0:
1212
return %1 : $Int32 // id: %2
1313
}
1414

15+
//Not every @_semantics should be skipped during the early inlining pass, but
16+
//only those ones which are explicitly listed in shouldSkipApplyDuringEarlyInlining.
17+
1518
//CHECK-LABEL: caller_func
16-
//CHECK: function_ref
17-
//CHECK: apply
18-
//CHECK-NEXT: ret
19+
//CHECK-NOT: function_ref
20+
//CHECK-NOT: apply
21+
//CHECK: end sil function 'caller_func'
1922
sil @caller_func : $@convention(thin) () -> Int32 {
2023
bb0:
2124
%0 = function_ref @callee_func : $@convention(thin) () -> Int32 // user: %1
2225
%1 = apply %0() : $@convention(thin) () -> Int32 // user: %2
2326
return %1 : $Int32 // id: %2
2427
}
2528

29+
sil [_semantics "array.make_mutable"] @callee_func_with_to_be_skipped_during_inlining_semantics : $@convention(method) (@inout Int32) -> Int32 {
30+
bb0(%self : $*Int32):
31+
%0 = integer_literal $Builtin.Int32, 3 // user: %1
32+
%1 = struct $Int32 (%0 : $Builtin.Int32) // user: %2
33+
return %1 : $Int32 // id: %2
34+
}
35+
36+
//Not every @_semantics should be skipped during the early inlining pass, but
37+
//only those ones which are explicitly listed in shouldSkipApplyDuringEarlyInlining.
38+
39+
//CHECK-LABEL: caller_func2
40+
//CHECK: function_ref
41+
//CHECK: apply
42+
//CHECK: end sil function 'caller_func2'
43+
sil @caller_func2 : $@convention(thin) () -> Int32 {
44+
bb0:
45+
%self = alloc_stack $Int32
46+
%0 = function_ref @callee_func_with_to_be_skipped_during_inlining_semantics : $@convention(method) (@inout Int32) -> Int32 // user: %1
47+
%1 = apply %0(%self) : $@convention(method) (@inout Int32) -> Int32 // user: %2
48+
dealloc_stack %self : $*Int32
49+
return %1 : $Int32 // id: %2
50+
}
51+
2652
// A function annotated with the @effects(readonly) attribute.
2753
sil [readonly] @callee_func2 : $@convention(thin) () -> Int32 {
2854
bb0:

0 commit comments

Comments
 (0)