Skip to content

Commit e034025

Browse files
authored
Merge pull request #19287 from rajbarik/raj-cp-allargs-2
Propagate concrete types for all arguments of an apply instruction
2 parents 8ce03fa + 3c5b13d commit e034025

File tree

4 files changed

+855
-89
lines changed

4 files changed

+855
-89
lines changed

include/swift/SILOptimizer/Utils/Existential.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,6 @@ struct ConcreteExistentialInfo {
9696
ConcreteExistentialInfo(Operand &ArgOperand, CanType ConcreteType,
9797
ProtocolDecl *Protocol);
9898

99-
ConcreteExistentialInfo(ConcreteExistentialInfo &) = delete;
100-
10199
/// For scenerios where ConcreteExistentialInfo is created using a known
102100
/// ConcreteType and ProtocolDecl, both of InitExistential
103101
/// and ConcreteValue can be null. So there is no need for explicit check for

lib/SILOptimizer/SILCombiner/SILCombiner.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -295,9 +295,12 @@ class SILCombiner :
295295
private:
296296
FullApplySite rewriteApplyCallee(FullApplySite apply, SILValue callee);
297297

298-
SILInstruction *createApplyWithConcreteType(FullApplySite Apply,
299-
const ConcreteExistentialInfo &CEI,
300-
SILBuilderContext &BuilderCtx);
298+
bool canReplaceArg(FullApplySite Apply, const ConcreteExistentialInfo &CEI,
299+
unsigned ArgIdx);
300+
SILInstruction *createApplyWithConcreteType(
301+
FullApplySite Apply,
302+
const llvm::SmallDenseMap<unsigned, ConcreteExistentialInfo> &CEIs,
303+
SILBuilderContext &BuilderCtx);
301304

302305
// Common utility function to replace the WitnessMethodInst using a
303306
// BuilderCtx.

lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp

Lines changed: 136 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -693,22 +693,26 @@ SILCombiner::propagateSoleConformingType(FullApplySite Apply,
693693
SILBuilderContext BuilderCtx(M, Builder.getTrackingList());
694694
replaceWitnessMethodInst(WMI, BuilderCtx, ConcreteType,
695695
*(CEI.ExistentialSubs.getConformances().begin()));
696+
// Construct the map for Self to be used for createApplyWithConcreteType.
697+
llvm::SmallDenseMap<unsigned, ConcreteExistentialInfo> CEIs;
698+
CEIs.insert(std::pair<unsigned, ConcreteExistentialInfo>(
699+
Apply.getCalleeArgIndex(Apply.getSelfArgumentOperand()), CEI));
696700
/// Create the new apply instruction using the concrete type.
697-
auto *NewAI = createApplyWithConcreteType(Apply, CEI, BuilderCtx);
701+
auto *NewAI = createApplyWithConcreteType(Apply, CEIs, BuilderCtx);
698702
return NewAI;
699703
}
700704

701705
/// Given an Apply and an argument value produced by InitExistentialAddrInst,
702706
/// return true if the argument can be replaced by a copy of its value.
703707
///
704708
/// FIXME: remove this helper when we can assume SIL opaque values.
705-
static bool canReplaceCopiedSelf(FullApplySite Apply,
709+
static bool canReplaceCopiedArg(FullApplySite Apply,
706710
SILInstruction *InitExistential,
707-
DominanceAnalysis *DA) {
708-
// If the witness method mutates self, we cannot replace self with
711+
DominanceAnalysis *DA, unsigned ArgIdx) {
712+
// If the witness method mutates Arg, we cannot replace Arg with
709713
// the source of a copy. Otherwise the call would modify another value than
710-
// the original self.
711-
if (Apply.getOrigCalleeType()->getSelfParameter().isIndirectMutating())
714+
// the original argument.
715+
if (Apply.getOrigCalleeType()->getParameters()[ArgIdx].isIndirectMutating())
712716
return false;
713717

714718
auto *DT = DA->get(Apply.getFunction());
@@ -751,6 +755,55 @@ static bool canReplaceCopiedSelf(FullApplySite Apply,
751755
return true;
752756
}
753757

758+
// Check the legal conditions under which a Arg parameter (specified as ArgIdx)
759+
// can be replaced with a concrete type. Concrete type info is passed as CEI
760+
// argument.
761+
bool SILCombiner::canReplaceArg(FullApplySite Apply,
762+
const ConcreteExistentialInfo &CEI,
763+
unsigned ArgIdx) {
764+
765+
// Don't specialize apply instructions that return the callee's Arg type,
766+
// because this optimization does not know how to substitute types in the
767+
// users of this apply. In the function type substitution below, all
768+
// references to OpenedArchetype will be substituted. So walk to type to
769+
// find all possible references, such as returning Optional<Arg>.
770+
if (Apply.getType().getASTType().findIf(
771+
[&CEI](Type t) -> bool { return t->isEqual(CEI.OpenedArchetype); })) {
772+
return false;
773+
}
774+
// Bail out if any other arguments or indirect result that refer to the
775+
// OpenedArchetype. The following optimization substitutes all occurrences
776+
// of OpenedArchetype in the function signature, but will only rewrite the
777+
// Arg operand.
778+
//
779+
// Note that the language does not allow Self to occur in contravariant
780+
// position. However, SIL does allow this and it can happen as a result of
781+
// upstream transformations. Since this is bail-out logic, it must handle
782+
// all verifiable SIL.
783+
784+
// This bailout check is also needed for non-Self arguments [including Self].
785+
unsigned NumApplyArgs = Apply.getNumArguments();
786+
for (unsigned Idx = 0; Idx < NumApplyArgs; Idx++) {
787+
if (Idx == ArgIdx)
788+
continue;
789+
if (Apply.getArgument(Idx)->getType().getASTType().findIf(
790+
[&CEI](Type t) -> bool {
791+
return t->isEqual(CEI.OpenedArchetype);
792+
})) {
793+
return false;
794+
}
795+
}
796+
// The apply can only be rewritten in terms of the concrete value if it is
797+
// legal to pass that value as the Arg argument.
798+
if (CEI.isCopied &&
799+
(!CEI.InitExistential ||
800+
!canReplaceCopiedArg(Apply, CEI.InitExistential, DA, ArgIdx))) {
801+
return false;
802+
}
803+
// It is safe to replace Arg.
804+
return true;
805+
}
806+
754807
/// Rewrite the given method apply instruction in terms of the provided conrete
755808
/// type information.
756809
///
@@ -774,74 +827,61 @@ static bool canReplaceCopiedSelf(FullApplySite Apply,
774827
/// FIXME: Protocol methods (witness or default) that return Self will be given
775828
/// a new return type. This implementation fails to update the type signature of
776829
/// SSA uses in those cases. Currently we bail out on methods that return Self.
777-
SILInstruction *
778-
SILCombiner::createApplyWithConcreteType(FullApplySite Apply,
779-
const ConcreteExistentialInfo &CEI,
780-
SILBuilderContext &BuilderCtx) {
781-
assert(Apply.getOrigCalleeType()->isPolymorphic());
830+
SILInstruction *SILCombiner::createApplyWithConcreteType(
831+
FullApplySite Apply,
832+
const llvm::SmallDenseMap<unsigned, ConcreteExistentialInfo> &CEIs,
833+
SILBuilderContext &BuilderCtx) {
782834

783-
// Don't specialize apply instructions that return the callee's Self type,
784-
// because this optimization does not know how to substitute types in the
785-
// users of this apply. In the function type substitution below, all
786-
// references to OpenedArchetype will be substituted. So walk to type to find
787-
// all possible references, such as returning Optional<Self>.
788-
if (Apply.getType().getASTType().findIf(
789-
[&CEI](Type t) -> bool { return t->isEqual(CEI.OpenedArchetype); })) {
790-
return nullptr;
791-
}
792-
// Bail out if any non-self arguments or indirect result that refer to the
793-
// OpenedArchetype. The following optimization substitutes all occurrences of
794-
// OpenedArchetype in the function signature, but will only rewrite the self
795-
// operand.
796-
//
797-
// Note that the language does not allow Self to occur in contravariant
798-
// position. However, SIL does allow this and it can happen as a result of
799-
// upstream transformations. Since this is bail-out logic, it must handle all
800-
// verifiable SIL.
801-
for (auto Arg : Apply.getArgumentsWithoutSelf()) {
802-
if (Arg->getType().getASTType().findIf([&CEI](Type t) -> bool {
803-
return t->isEqual(CEI.OpenedArchetype);
804-
})) {
805-
return nullptr;
806-
}
807-
}
808-
// The apply can only be rewritten in terms of the concrete value if it is
809-
// legal to pass that value as the self argument.
810-
if (CEI.isCopied && (!CEI.InitExistential ||
811-
!canReplaceCopiedSelf(Apply, CEI.InitExistential, DA))) {
812-
return nullptr;
813-
}
835+
// Ensure that the callee is polymorphic.
836+
assert(Apply.getOrigCalleeType()->isPolymorphic());
814837

815-
// Create a set of arguments.
838+
// Create the new set of arguments to apply including their substitutions.
839+
SubstitutionMap NewCallSubs = Apply.getSubstitutionMap();
816840
SmallVector<SILValue, 8> NewArgs;
817-
for (auto Arg : Apply.getArgumentsWithoutSelf()) {
818-
NewArgs.push_back(Arg);
841+
unsigned NumApplyArgs = Apply.getNumArguments();
842+
bool UpdatedArgs = false;
843+
for (unsigned ArgIdx = 0; ArgIdx < NumApplyArgs; ArgIdx++) {
844+
auto ArgIt = CEIs.find(ArgIdx);
845+
if (ArgIt == CEIs.end()) {
846+
// Use the old argument if it does not have a valid concrete existential.
847+
NewArgs.push_back(Apply.getArgument(ArgIdx));
848+
continue;
849+
}
850+
auto &CEI = ArgIt->second;
851+
// Check for Arg's concrete type propagation legality.
852+
if (!canReplaceArg(Apply, CEI, ArgIdx)) {
853+
NewArgs.push_back(Apply.getArgument(ArgIdx));
854+
continue;
855+
}
856+
UpdatedArgs = true;
857+
// Ensure that we have a concrete value to propagate.
858+
assert(CEI.ConcreteValue);
859+
NewArgs.push_back(CEI.ConcreteValue);
860+
// Form a new set of substitutions where the argument is
861+
// replaced with a concrete type.
862+
NewCallSubs = NewCallSubs.subst(
863+
[&](SubstitutableType *type) -> Type {
864+
if (type == CEI.OpenedArchetype)
865+
return CEI.ConcreteType;
866+
return type;
867+
},
868+
[&](CanType origTy, Type substTy,
869+
ProtocolDecl *proto) -> Optional<ProtocolConformanceRef> {
870+
if (origTy->isEqual(CEI.OpenedArchetype)) {
871+
assert(substTy->isEqual(CEI.ConcreteType));
872+
// Do a conformance lookup on this witness requirement using the
873+
// existential's conformances. The witness requirement may be a
874+
// base type of the existential's requirements.
875+
return CEI.lookupExistentialConformance(proto);
876+
}
877+
return ProtocolConformanceRef(proto);
878+
});
819879
}
820-
NewArgs.push_back(CEI.ConcreteValue);
821-
822-
assert(Apply.getOrigCalleeType()->isPolymorphic());
823880

824-
// Form a new set of substitutions where Self is
825-
// replaced by a concrete type.
826-
SubstitutionMap OrigCallSubs = Apply.getSubstitutionMap();
827-
SubstitutionMap NewCallSubs = OrigCallSubs.subst(
828-
[&](SubstitutableType *type) -> Type {
829-
if (type == CEI.OpenedArchetype)
830-
return CEI.ConcreteType;
831-
return type;
832-
},
833-
[&](CanType origTy, Type substTy,
834-
ProtocolDecl *proto) -> Optional<ProtocolConformanceRef> {
835-
if (origTy->isEqual(CEI.OpenedArchetype)) {
836-
assert(substTy->isEqual(CEI.ConcreteType));
837-
// Do a conformance lookup on this witness requirement using the
838-
// existential's conformances. The witness requirement may be a base
839-
// type of the existential's requirements.
840-
return CEI.lookupExistentialConformance(proto).getValue();
841-
}
842-
return ProtocolConformanceRef(proto);
843-
});
881+
if (!UpdatedArgs)
882+
return nullptr;
844883

884+
// Now create the new apply instruction.
845885
SILBuilderWithScope ApplyBuilder(Apply.getInstruction(), BuilderCtx);
846886
FullApplySite NewApply;
847887
if (auto *TAI = dyn_cast<TryApplyInst>(Apply))
@@ -920,8 +960,12 @@ SILCombiner::propagateConcreteTypeOfInitExistential(FullApplySite Apply,
920960
replaceWitnessMethodInst(WMI, BuilderCtx, CEI.ConcreteType,
921961
SelfConformance);
922962
}
963+
// Construct the map for Self to be used for createApplyWithConcreteType.
964+
llvm::SmallDenseMap<unsigned, ConcreteExistentialInfo> CEIs;
965+
CEIs.insert(std::pair<unsigned, ConcreteExistentialInfo>(
966+
Apply.getCalleeArgIndex(Apply.getSelfArgumentOperand()), CEI));
923967
// Try to rewrite the apply.
924-
return createApplyWithConcreteType(Apply, CEI, BuilderCtx);
968+
return createApplyWithConcreteType(Apply, CEIs, BuilderCtx);
925969
}
926970

927971
/// Rewrite a protocol extension lookup type from an archetype to a concrete
@@ -936,27 +980,35 @@ SILCombiner::propagateConcreteTypeOfInitExistential(FullApplySite Apply,
936980
/// ==> apply %f<C : P>(%ref)
937981
SILInstruction *
938982
SILCombiner::propagateConcreteTypeOfInitExistential(FullApplySite Apply) {
939-
// This optimization requires a generic self argument.
940-
if (!Apply.hasSelfArgument() || !Apply.hasSubstitutions())
941-
return nullptr;
942-
943-
// Try to derive the concrete type of self and a related conformance from
944-
// the found init_existential.
945-
const ConcreteExistentialInfo CEI(Apply.getSelfArgumentOperand());
946-
if (!CEI.isValid())
983+
// This optimization requires a generic argument.
984+
if (!Apply.hasSubstitutions())
947985
return nullptr;
948986

949987
SILBuilderContext BuilderCtx(Builder.getModule(), Builder.getTrackingList());
950988
SILOpenedArchetypesTracker OpenedArchetypesTracker(&Builder.getFunction());
951989
BuilderCtx.setOpenedArchetypesTracker(&OpenedArchetypesTracker);
952-
if (CEI.ConcreteType->isOpenedExistential()) {
953-
// Temporarily record this opened existential def in this local
954-
// BuilderContext before rewriting the witness method.
955-
OpenedArchetypesTracker.addOpenedArchetypeDef(
956-
cast<ArchetypeType>(CEI.ConcreteType), CEI.ConcreteTypeDef);
990+
llvm::SmallDenseMap<unsigned, ConcreteExistentialInfo> CEIs;
991+
for (unsigned ArgIdx = 0; ArgIdx < Apply.getNumArguments(); ArgIdx++) {
992+
auto ArgASTType = Apply.getArgument(ArgIdx)->getType().getASTType();
993+
if (!ArgASTType->hasArchetype())
994+
continue;
995+
const ConcreteExistentialInfo CEI(Apply.getArgumentOperands()[ArgIdx]);
996+
if (!CEI.isValid())
997+
continue;
998+
999+
CEIs.insert(std::pair<unsigned, ConcreteExistentialInfo>(ArgIdx, CEI));
1000+
1001+
if (CEI.ConcreteType->isOpenedExistential()) {
1002+
// Temporarily record this opened existential def in this local
1003+
// BuilderContext before rewriting the witness method.
1004+
OpenedArchetypesTracker.addOpenedArchetypeDef(
1005+
cast<ArchetypeType>(CEI.ConcreteType), CEI.ConcreteTypeDef);
1006+
}
9571007
}
958-
// Perform the transformation by rewriting the apply.
959-
return createApplyWithConcreteType(Apply, CEI, BuilderCtx);
1008+
// Bail, if no argument has a concrete existential to propagate.
1009+
if (CEIs.empty())
1010+
return nullptr;
1011+
return createApplyWithConcreteType(Apply, CEIs, BuilderCtx);
9601012
}
9611013

9621014
/// \brief Check that all users of the apply are retain/release ignoring one

0 commit comments

Comments
 (0)