Skip to content

Commit 4e5b49a

Browse files
authored
Merge pull request #7917 from slavapestov/sil-combiner-cleanup
SILCombiner: Clean up the concrete -> existential peephole a bit
2 parents 4bd5e11 + 44f2397 commit 4e5b49a

File tree

2 files changed

+81
-69
lines changed

2 files changed

+81
-69
lines changed

lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp

Lines changed: 27 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -692,54 +692,41 @@ SILCombiner::createApplyWithConcreteType(FullApplySite AI,
692692
// replaced by a concrete type.
693693
SmallVector<Substitution, 8> Substitutions;
694694
for (auto Subst : AI.getSubstitutions()) {
695-
auto *A = Subst.getReplacement()->getAs<ArchetypeType>();
696-
if (A && A == OpenedArchetype) {
697-
auto Conformances = AI.getModule().getASTContext()
698-
.AllocateUninitialized<ProtocolConformanceRef>(1);
699-
Conformances[0] = Conformance;
700-
Substitution NewSubst(ConcreteType, Conformances);
701-
Substitutions.push_back(NewSubst);
702-
} else
703-
Substitutions.push_back(Subst);
695+
auto NewSubst = Subst.subst(
696+
AI.getModule().getSwiftModule(),
697+
[&](SubstitutableType *type) -> Type {
698+
if (type == OpenedArchetype)
699+
return ConcreteType;
700+
return type;
701+
},
702+
[&](Type origTy, Type substTy, ProtocolType *protoType)
703+
-> Optional<ProtocolConformanceRef> {
704+
assert(origTy->isEqual(OpenedArchetype));
705+
return Conformance;
706+
});
707+
Substitutions.push_back(NewSubst);
704708
}
705709

706-
SILType SubstCalleeType = AI.getSubstCalleeSILType();
707-
708-
SILType NewSubstCalleeType;
709-
710710
auto FnTy = AI.getCallee()->getType().castTo<SILFunctionType>();
711-
if (FnTy->isPolymorphic()) {
712-
// Handle polymorphic functions by properly substituting
713-
// their parameter types.
714-
CanSILFunctionType SFT = FnTy->substGenericArgs(
715-
AI.getModule(),
716-
Substitutions);
717-
NewSubstCalleeType = SILType::getPrimitiveObjectType(SFT);
718-
} else {
719-
NewSubstCalleeType =
720-
SubstCalleeType.subst(AI.getModule(),
721-
[&](SubstitutableType *type) -> Type {
722-
if (type == OpenedArchetype)
723-
return ConcreteType;
724-
return type;
725-
},
726-
MakeAbstractConformanceForGenericType());
727-
}
711+
assert(FnTy->isPolymorphic());
712+
713+
auto SFT = FnTy->substGenericArgs(AI.getModule(), Substitutions);
714+
auto NewSubstCalleeType = SILType::getPrimitiveObjectType(SFT);
728715

729716
FullApplySite NewAI;
730717
Builder.setCurrentDebugScope(AI.getDebugScope());
731718
Builder.addOpenedArchetypeOperands(AI.getInstruction());
732719

733720
if (auto *TAI = dyn_cast<TryApplyInst>(AI))
734721
NewAI = Builder.createTryApply(AI.getLoc(), AI.getCallee(),
735-
NewSubstCalleeType,
736-
Substitutions, Args,
737-
TAI->getNormalBB(), TAI->getErrorBB());
722+
NewSubstCalleeType,
723+
Substitutions, Args,
724+
TAI->getNormalBB(), TAI->getErrorBB());
738725
else
739726
NewAI = Builder.createApply(AI.getLoc(), AI.getCallee(),
740-
NewSubstCalleeType,
741-
AI.getType(), Substitutions, Args,
742-
cast<ApplyInst>(AI)->isNonThrowing());
727+
NewSubstCalleeType,
728+
AI.getType(), Substitutions, Args,
729+
cast<ApplyInst>(AI)->isNonThrowing());
743730

744731
if (isa<ApplyInst>(NewAI))
745732
replaceInstUsesWith(*AI.getInstruction(), NewAI.getInstruction());
@@ -886,21 +873,6 @@ SILCombiner::propagateConcreteTypeOfInitExistential(FullApplySite AI,
886873
if (WMI->getConformance().isConcrete())
887874
return nullptr;
888875

889-
// Don't specialize Apply instructions that return the Self type.
890-
// Notice that it is sufficient to compare the return type to the
891-
// substituted type because types that depend on the Self type are
892-
// not allowed (for example [Self] is not allowed).
893-
if (AI.getType().getSwiftRValueType() == WMI->getLookupType())
894-
return nullptr;
895-
896-
// We need to handle the Self return type.
897-
// In we find arguments that are not the 'self' argument and if
898-
// they are of the Self type then we abort the optimization.
899-
for (auto Arg : AI.getArgumentsWithoutSelf()) {
900-
if (Arg->getType().getSwiftRValueType() == WMI->getLookupType())
901-
return nullptr;
902-
}
903-
904876
// The lookup type is not an opened existential type,
905877
// thus it cannot be made more concrete.
906878
if (!WMI->getLookupType()->isOpenedExistential())
@@ -916,10 +888,10 @@ SILCombiner::propagateConcreteTypeOfInitExistential(FullApplySite AI,
916888
// Keep around the dependence on the open instruction unless we've
917889
// actually eliminated the use.
918890
auto *NewWMI = Builder.createWitnessMethod(WMI->getLoc(),
919-
ConcreteType,
920-
Conformance, WMI->getMember(),
921-
WMI->getType(),
922-
WMI->isVolatile());
891+
ConcreteType,
892+
Conformance, WMI->getMember(),
893+
WMI->getType(),
894+
WMI->isVolatile());
923895
// Replace only uses of the witness_method in the apply that is going to
924896
// be changed.
925897
MutableArrayRef<Operand> Operands = AI.getInstruction()->getAllOperands();
@@ -957,15 +929,6 @@ SILCombiner::propagateConcreteTypeOfInitExistential(FullApplySite AI) {
957929
if (!Self)
958930
return nullptr;
959931

960-
// We need to handle the Self return type.
961-
// In we find arguments that are not the 'self' argument and if
962-
// they are of the Self type then we abort the optimization.
963-
for (auto Arg : AI.getArgumentsWithoutSelf()) {
964-
if (Arg->getType().getSwiftRValueType() ==
965-
AI.getArguments().back()->getType().getSwiftRValueType())
966-
return nullptr;
967-
}
968-
969932
// Obtain the protocol whose which should be used by the conformance.
970933
auto *AFD = dyn_cast<AbstractFunctionDecl>(Callee->getDeclContext());
971934
if (!AFD)

test/SILOptimizer/devirtualize_existential.swift

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,61 @@ class Foo : Pingable {
88
}
99

1010
// Everything gets devirtualized, inlined, and promoted to the stack.
11-
//CHECK: @_T024devirtualize_existential17interesting_stuffyyF
12-
//CHECK-NOT: init_existential_addr
13-
//CHECK-NOT: apply
14-
//CHECK: return
11+
// CHECK-LABEL: @_T024devirtualize_existential17interesting_stuffyyF
12+
// CHECK-NOT: init_existential_addr
13+
// CHECK-NOT: apply
14+
// CHECK: return
1515
public func interesting_stuff() {
16-
var x : Pingable = Foo()
16+
var x: Pingable = Foo()
1717
x.ping(1)
1818
}
1919

20+
protocol Cloneable {
21+
func clone() -> Self
22+
func maybeClone() -> Self?
23+
}
24+
25+
struct Bar : Cloneable {
26+
@inline(never)
27+
func clone() -> Bar { return self }
28+
29+
@inline(never)
30+
func maybeClone() -> Bar? { return self }
31+
}
32+
33+
// In this example, we don't eliminate the init_existential_addr, because
34+
// of the stack allocated existential value used for the return.
35+
//
36+
// If the combiner was generalized to replace the opened existential type
37+
// with the concrete type in all instructions that use it, instead of just
38+
// special-casing witness_method and apply, we could eliminate the opened
39+
// existential type completely.
40+
//
41+
// However even though IRGen still performs more work at runtime than is
42+
// necessary here, the call is devirtualized.
43+
44+
// CHECK-LABEL: sil @_T024devirtualize_existential22more_interesting_stuffyyF
45+
// CHECK: [[EXISTENTIAL:%.*]] = alloc_stack $Cloneable
46+
// CHECK: [[EXISTENTIAL_ADDR:%.*]] = init_existential_addr [[EXISTENTIAL]]
47+
// CHECK: [[VALUE:%.*]] = struct $Bar ()
48+
// CHECK: [[RESULT_ADDR:%.*]] = unchecked_addr_cast [[EXISTENTIAL_ADDR:%.*]]
49+
// CHECK: [[FN:%.*]] = function_ref @_T024devirtualize_existential3BarV5cloneACyF
50+
// CHECK: [[RETURN:%.*]] = apply [[FN]]([[VALUE]])
51+
// CHECK: store [[RETURN]] to [[RESULT_ADDR]]
52+
53+
// CHECK: [[ENUM:%.*]] = alloc_stack $Optional<Cloneable>
54+
// CHECK: [[ENUM_ADDR:%.*]] = init_enum_data_addr [[ENUM]]
55+
// CHECK: [[EXISTENTIAL_ADDR:%.*]] = init_existential_addr [[ENUM_ADDR]]
56+
// CHECK: [[RESULT_ADDR:%.*]] = unchecked_addr_cast [[EXISTENTIAL_ADDR:%.*]]
57+
// CHECK: [[FN:%.*]] = function_ref @_T024devirtualize_existential3BarV10maybeCloneACSgyF
58+
// CHECK: [[RETURN:%.*]] = apply [[FN]]([[VALUE]])
59+
// CHECK: store [[RETURN]] to [[RESULT_ADDR]]
60+
61+
// CHECK: return
62+
63+
public func more_interesting_stuff() {
64+
var x: Cloneable = Bar()
65+
66+
x.clone()
67+
x.maybeClone()
68+
}

0 commit comments

Comments
 (0)