Skip to content

Commit c6a0f51

Browse files
committed
SIL: Add devirtualizer support for default witness methods
We ignore substitutions from the conformance, using the Self type substitution from the call site instead. The new SILFunctionType::getDefaultWitnessMethodProtocol() method is used to figure out what "shape" the Self substitutions need to take. This is cleaner than it was before the method was added, but is still a bit of a hack; more and more it appears that we need to stop thinking of witness_method as a separate calling convention, and design what @rjmccall described as "abstraction patterns for generic signatures" instead.
1 parent 7dcd168 commit c6a0f51

File tree

2 files changed

+117
-23
lines changed

2 files changed

+117
-23
lines changed

lib/SILOptimizer/Utils/Devirtualize.cpp

Lines changed: 55 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -679,29 +679,42 @@ DevirtualizationResult swift::tryDevirtualizeClassMethod(FullApplySite AI,
679679
// Witness Method Optimization
680680
//===----------------------------------------------------------------------===//
681681

682-
/// Generate a new apply of a function_ref to replace an apply of a
683-
/// witness_method when we've determined the actual function we'll end
684-
/// up calling.
685-
static ApplySite devirtualizeWitnessMethod(ApplySite AI, SILFunction *F,
686-
ArrayRef<Substitution> Subs) {
687-
// We know the witness thunk and the corresponding set of substitutions
688-
// required to invoke the protocol method at this point.
682+
static void getWitnessMethodSubstitutions(ApplySite AI, SILFunction *F,
683+
ArrayRef<Substitution> Subs,
684+
SmallVectorImpl<Substitution> &NewSubs) {
689685
auto &Module = AI.getModule();
690686

691-
// Collect all the required substitutions.
692-
//
693-
// The complete set of substitutions may be different, e.g. because the found
694-
// witness thunk F may have been created by a specialization pass and have
695-
// additional generic parameters.
696-
SmallVector<Substitution, 16> NewSubstList(Subs.begin(), Subs.end());
687+
auto CalleeCanType = F->getLoweredFunctionType();
688+
689+
ProtocolDecl *proto = nullptr;
690+
if (CalleeCanType->getRepresentation() ==
691+
SILFunctionTypeRepresentation::WitnessMethod) {
692+
proto = CalleeCanType->getDefaultWitnessMethodProtocol(
693+
*Module.getSwiftModule());
694+
}
695+
696+
ArrayRef<Substitution> origSubs = AI.getSubstitutions();
697+
698+
if (proto != nullptr) {
699+
// If the callee is a default witness method thunk, preserve substitutions
700+
// from the call site.
701+
NewSubs.append(origSubs.begin(), origSubs.end());
702+
return;
703+
}
704+
705+
// If the callee is a concrete witness method thunk, apply substitutions
706+
// from the conformance, and drop any substitutions derived from the Self
707+
// type.
708+
NewSubs.append(Subs.begin(), Subs.end());
709+
697710
if (auto generics = AI.getOrigCalleeType()->getGenericSignature()) {
698-
ArrayRef<Substitution> origSubs = AI.getSubstitutions();
699711
for (auto genericParam : generics->getAllDependentTypes()) {
700712
auto origSub = origSubs.front();
701713
origSubs = origSubs.slice(1);
702714

703-
// Ignore generic parameters derived from 'self', the generic
704-
// parameter at depth 0, index 0.
715+
// If the callee is a concrete witness method thunk, we ignore
716+
// generic parameters derived from 'self', the generic parameter at
717+
// depth 0, index 0.
705718
auto type = genericParam->getCanonicalType();
706719
while (auto memberType = dyn_cast<DependentMemberType>(type)) {
707720
type = memberType.getBase();
@@ -714,17 +727,36 @@ static ApplySite devirtualizeWitnessMethod(ApplySite AI, SILFunction *F,
714727
}
715728

716729
// Okay, remember this substitution.
717-
NewSubstList.push_back(origSub);
730+
NewSubs.push_back(origSub);
718731
}
719-
720-
assert(origSubs.empty() && "subs not parallel to dependent types");
721732
}
722733

734+
assert(origSubs.empty() && "subs not parallel to dependent types");
735+
}
736+
737+
/// Generate a new apply of a function_ref to replace an apply of a
738+
/// witness_method when we've determined the actual function we'll end
739+
/// up calling.
740+
static ApplySite devirtualizeWitnessMethod(ApplySite AI, SILFunction *F,
741+
ArrayRef<Substitution> Subs) {
742+
// We know the witness thunk and the corresponding set of substitutions
743+
// required to invoke the protocol method at this point.
744+
auto &Module = AI.getModule();
745+
746+
// Collect all the required substitutions.
747+
//
748+
// The complete set of substitutions may be different, e.g. because the found
749+
// witness thunk F may have been created by a specialization pass and have
750+
// additional generic parameters.
751+
SmallVector<Substitution, 4> NewSubs;
752+
753+
getWitnessMethodSubstitutions(AI, F, Subs, NewSubs);
754+
723755
// Figure out the exact bound type of the function to be called by
724756
// applying all substitutions.
725757
auto CalleeCanType = F->getLoweredFunctionType();
726758
auto SubstCalleeCanType = CalleeCanType->substGenericArgs(
727-
Module, Module.getSwiftModule(), NewSubstList);
759+
Module, Module.getSwiftModule(), NewSubs);
728760

729761
// Collect arguments from the apply instruction.
730762
auto Arguments = SmallVector<SILValue, 4>();
@@ -754,15 +786,15 @@ static ApplySite devirtualizeWitnessMethod(ApplySite AI, SILFunction *F,
754786

755787
if (auto *A = dyn_cast<ApplyInst>(AI))
756788
SAI = Builder.createApply(Loc, FRI, SubstCalleeSILType,
757-
ResultSILType, NewSubstList, Arguments,
789+
ResultSILType, NewSubs, Arguments,
758790
A->isNonThrowing());
759791
if (auto *TAI = dyn_cast<TryApplyInst>(AI))
760792
SAI = Builder.createTryApply(Loc, FRI, SubstCalleeSILType,
761-
NewSubstList, Arguments,
793+
NewSubs, Arguments,
762794
TAI->getNormalBB(), TAI->getErrorBB());
763795
if (auto *PAI = dyn_cast<PartialApplyInst>(AI))
764796
SAI = Builder.createPartialApply(Loc, FRI, SubstCalleeSILType,
765-
NewSubstList, Arguments, PAI->getType());
797+
NewSubs, Arguments, PAI->getType());
766798

767799
NumWitnessDevirt++;
768800
return SAI;
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// RUN: %target-sil-opt -enable-sil-verify-all %s -sil-combine -enable-resilience | FileCheck %s
2+
sil_stage canonical
3+
4+
import Builtin
5+
import Swift
6+
import SwiftShims
7+
8+
public protocol ResilientProtocol {
9+
func defaultA()
10+
}
11+
12+
sil @defaultA : $@convention(witness_method) <Self where Self : ResilientProtocol> (@in_guaranteed Self) -> () {
13+
bb0(%0 : $*Self):
14+
%result = tuple ()
15+
return %result : $()
16+
}
17+
18+
sil_default_witness_table ResilientProtocol 1 {
19+
method #ResilientProtocol.defaultA!1: @defaultA
20+
}
21+
22+
struct ConformingStruct : ResilientProtocol {
23+
func defaultA()
24+
}
25+
26+
sil_witness_table ConformingStruct : ResilientProtocol module protocol_resilience {
27+
method #ResilientProtocol.defaultA!1: @defaultA
28+
}
29+
30+
struct ConformingGenericStruct<T> : ResilientProtocol {
31+
func defaultA()
32+
}
33+
34+
sil_witness_table <T> ConformingGenericStruct<T> : ResilientProtocol module protocol_resilience {
35+
method #ResilientProtocol.defaultA!1: @defaultA
36+
}
37+
38+
// CHECK-LABEL: sil hidden @test_devirt_of_default_witness_method : $@convention(thin) (@in_guaranteed ConformingStruct) -> ()
39+
// CHECK: bb0(%0 : $*ConformingStruct):
40+
// CHECK: [[FN:%.*]] = function_ref @defaultA : $@convention(witness_method) <τ_0_0 where τ_0_0 : ResilientProtocol> (@in_guaranteed τ_0_0) -> ()
41+
// CHECK-NEXT: [[RESULT:%.*]] = apply [[FN]]<ConformingStruct>(%0) : $@convention(witness_method) <τ_0_0 where τ_0_0 : ResilientProtocol> (@in_guaranteed τ_0_0)
42+
// CHECK-NEXT: return [[RESULT]] : $()
43+
44+
sil hidden @test_devirt_of_default_witness_method : $@convention(thin) (@in_guaranteed ConformingStruct) -> () {
45+
bb0(%0 : $*ConformingStruct):
46+
%fn = witness_method $ConformingStruct, #ResilientProtocol.defaultA!1 : $@convention(witness_method) <T where T : ResilientProtocol> (@in_guaranteed T) -> ()
47+
%result = apply %fn<ConformingStruct>(%0) : $@convention(witness_method) <T where T : ResilientProtocol> (@in_guaranteed T) -> ()
48+
return %result : $()
49+
}
50+
51+
// CHECK-LABEL: sil hidden @test_devirt_of_generic_default_witness_method : $@convention(thin) (@in_guaranteed ConformingGenericStruct<Int>) -> ()
52+
// CHECK: bb0(%0 : $*ConformingGenericStruct<Int>):
53+
// CHECK: [[FN:%.*]] = function_ref @defaultA : $@convention(witness_method) <τ_0_0 where τ_0_0 : ResilientProtocol> (@in_guaranteed τ_0_0) -> ()
54+
// CHECK-NEXT: [[RESULT:%.*]] = apply [[FN]]<ConformingGenericStruct<Int>>(%0) : $@convention(witness_method) <τ_0_0 where τ_0_0 : ResilientProtocol> (@in_guaranteed τ_0_0)
55+
// CHECK-NEXT: return [[RESULT]] : $()
56+
57+
sil hidden @test_devirt_of_generic_default_witness_method : $@convention(thin) (@in_guaranteed ConformingGenericStruct<Int>) -> () {
58+
bb0(%0 : $*ConformingGenericStruct<Int>):
59+
%fn = witness_method $ConformingGenericStruct<Int>, #ResilientProtocol.defaultA!1 : $@convention(witness_method) <T where T : ResilientProtocol> (@in_guaranteed T) -> ()
60+
%result = apply %fn<ConformingGenericStruct<Int>>(%0) : $@convention(witness_method) <T where T : ResilientProtocol> (@in_guaranteed T) -> ()
61+
return %result : $()
62+
}

0 commit comments

Comments
 (0)