Merge pull request #7917 from slavapestov/sil-combiner-cleanup
SILCombiner: Clean up the concrete -> existential peephole a bit
diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp
index 3d8f05c..54b99d4 100644
--- a/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp
+++ b/lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp
@@ -692,39 +692,26 @@
// replaced by a concrete type.
SmallVector<Substitution, 8> Substitutions;
for (auto Subst : AI.getSubstitutions()) {
- auto *A = Subst.getReplacement()->getAs<ArchetypeType>();
- if (A && A == OpenedArchetype) {
- auto Conformances = AI.getModule().getASTContext()
- .AllocateUninitialized<ProtocolConformanceRef>(1);
- Conformances[0] = Conformance;
- Substitution NewSubst(ConcreteType, Conformances);
- Substitutions.push_back(NewSubst);
- } else
- Substitutions.push_back(Subst);
+ auto NewSubst = Subst.subst(
+ AI.getModule().getSwiftModule(),
+ [&](SubstitutableType *type) -> Type {
+ if (type == OpenedArchetype)
+ return ConcreteType;
+ return type;
+ },
+ [&](Type origTy, Type substTy, ProtocolType *protoType)
+ -> Optional<ProtocolConformanceRef> {
+ assert(origTy->isEqual(OpenedArchetype));
+ return Conformance;
+ });
+ Substitutions.push_back(NewSubst);
}
- SILType SubstCalleeType = AI.getSubstCalleeSILType();
-
- SILType NewSubstCalleeType;
-
auto FnTy = AI.getCallee()->getType().castTo<SILFunctionType>();
- if (FnTy->isPolymorphic()) {
- // Handle polymorphic functions by properly substituting
- // their parameter types.
- CanSILFunctionType SFT = FnTy->substGenericArgs(
- AI.getModule(),
- Substitutions);
- NewSubstCalleeType = SILType::getPrimitiveObjectType(SFT);
- } else {
- NewSubstCalleeType =
- SubstCalleeType.subst(AI.getModule(),
- [&](SubstitutableType *type) -> Type {
- if (type == OpenedArchetype)
- return ConcreteType;
- return type;
- },
- MakeAbstractConformanceForGenericType());
- }
+ assert(FnTy->isPolymorphic());
+
+ auto SFT = FnTy->substGenericArgs(AI.getModule(), Substitutions);
+ auto NewSubstCalleeType = SILType::getPrimitiveObjectType(SFT);
FullApplySite NewAI;
Builder.setCurrentDebugScope(AI.getDebugScope());
@@ -732,14 +719,14 @@
if (auto *TAI = dyn_cast<TryApplyInst>(AI))
NewAI = Builder.createTryApply(AI.getLoc(), AI.getCallee(),
- NewSubstCalleeType,
- Substitutions, Args,
- TAI->getNormalBB(), TAI->getErrorBB());
+ NewSubstCalleeType,
+ Substitutions, Args,
+ TAI->getNormalBB(), TAI->getErrorBB());
else
NewAI = Builder.createApply(AI.getLoc(), AI.getCallee(),
- NewSubstCalleeType,
- AI.getType(), Substitutions, Args,
- cast<ApplyInst>(AI)->isNonThrowing());
+ NewSubstCalleeType,
+ AI.getType(), Substitutions, Args,
+ cast<ApplyInst>(AI)->isNonThrowing());
if (isa<ApplyInst>(NewAI))
replaceInstUsesWith(*AI.getInstruction(), NewAI.getInstruction());
@@ -886,21 +873,6 @@
if (WMI->getConformance().isConcrete())
return nullptr;
- // Don't specialize Apply instructions that return the Self type.
- // Notice that it is sufficient to compare the return type to the
- // substituted type because types that depend on the Self type are
- // not allowed (for example [Self] is not allowed).
- if (AI.getType().getSwiftRValueType() == WMI->getLookupType())
- return nullptr;
-
- // We need to handle the Self return type.
- // In we find arguments that are not the 'self' argument and if
- // they are of the Self type then we abort the optimization.
- for (auto Arg : AI.getArgumentsWithoutSelf()) {
- if (Arg->getType().getSwiftRValueType() == WMI->getLookupType())
- return nullptr;
- }
-
// The lookup type is not an opened existential type,
// thus it cannot be made more concrete.
if (!WMI->getLookupType()->isOpenedExistential())
@@ -916,10 +888,10 @@
// Keep around the dependence on the open instruction unless we've
// actually eliminated the use.
auto *NewWMI = Builder.createWitnessMethod(WMI->getLoc(),
- ConcreteType,
- Conformance, WMI->getMember(),
- WMI->getType(),
- WMI->isVolatile());
+ ConcreteType,
+ Conformance, WMI->getMember(),
+ WMI->getType(),
+ WMI->isVolatile());
// Replace only uses of the witness_method in the apply that is going to
// be changed.
MutableArrayRef<Operand> Operands = AI.getInstruction()->getAllOperands();
@@ -957,15 +929,6 @@
if (!Self)
return nullptr;
- // We need to handle the Self return type.
- // In we find arguments that are not the 'self' argument and if
- // they are of the Self type then we abort the optimization.
- for (auto Arg : AI.getArgumentsWithoutSelf()) {
- if (Arg->getType().getSwiftRValueType() ==
- AI.getArguments().back()->getType().getSwiftRValueType())
- return nullptr;
- }
-
// Obtain the protocol whose which should be used by the conformance.
auto *AFD = dyn_cast<AbstractFunctionDecl>(Callee->getDeclContext());
if (!AFD)
diff --git a/test/SILOptimizer/devirtualize_existential.swift b/test/SILOptimizer/devirtualize_existential.swift
index 570460b..8072de8 100644
--- a/test/SILOptimizer/devirtualize_existential.swift
+++ b/test/SILOptimizer/devirtualize_existential.swift
@@ -8,12 +8,61 @@
}
// Everything gets devirtualized, inlined, and promoted to the stack.
-//CHECK: @_T024devirtualize_existential17interesting_stuffyyF
-//CHECK-NOT: init_existential_addr
-//CHECK-NOT: apply
-//CHECK: return
+// CHECK-LABEL: @_T024devirtualize_existential17interesting_stuffyyF
+// CHECK-NOT: init_existential_addr
+// CHECK-NOT: apply
+// CHECK: return
public func interesting_stuff() {
- var x : Pingable = Foo()
+ var x: Pingable = Foo()
x.ping(1)
}
+protocol Cloneable {
+ func clone() -> Self
+ func maybeClone() -> Self?
+}
+
+struct Bar : Cloneable {
+ @inline(never)
+ func clone() -> Bar { return self }
+
+ @inline(never)
+ func maybeClone() -> Bar? { return self }
+}
+
+// In this example, we don't eliminate the init_existential_addr, because
+// of the stack allocated existential value used for the return.
+//
+// If the combiner was generalized to replace the opened existential type
+// with the concrete type in all instructions that use it, instead of just
+// special-casing witness_method and apply, we could eliminate the opened
+// existential type completely.
+//
+// However even though IRGen still performs more work at runtime than is
+// necessary here, the call is devirtualized.
+
+// CHECK-LABEL: sil @_T024devirtualize_existential22more_interesting_stuffyyF
+// CHECK: [[EXISTENTIAL:%.*]] = alloc_stack $Cloneable
+// CHECK: [[EXISTENTIAL_ADDR:%.*]] = init_existential_addr [[EXISTENTIAL]]
+// CHECK: [[VALUE:%.*]] = struct $Bar ()
+// CHECK: [[RESULT_ADDR:%.*]] = unchecked_addr_cast [[EXISTENTIAL_ADDR:%.*]]
+// CHECK: [[FN:%.*]] = function_ref @_T024devirtualize_existential3BarV5cloneACyF
+// CHECK: [[RETURN:%.*]] = apply [[FN]]([[VALUE]])
+// CHECK: store [[RETURN]] to [[RESULT_ADDR]]
+
+// CHECK: [[ENUM:%.*]] = alloc_stack $Optional<Cloneable>
+// CHECK: [[ENUM_ADDR:%.*]] = init_enum_data_addr [[ENUM]]
+// CHECK: [[EXISTENTIAL_ADDR:%.*]] = init_existential_addr [[ENUM_ADDR]]
+// CHECK: [[RESULT_ADDR:%.*]] = unchecked_addr_cast [[EXISTENTIAL_ADDR:%.*]]
+// CHECK: [[FN:%.*]] = function_ref @_T024devirtualize_existential3BarV10maybeCloneACSgyF
+// CHECK: [[RETURN:%.*]] = apply [[FN]]([[VALUE]])
+// CHECK: store [[RETURN]] to [[RESULT_ADDR]]
+
+// CHECK: return
+
+public func more_interesting_stuff() {
+ var x: Cloneable = Bar()
+
+ x.clone()
+ x.maybeClone()
+}