Skip to content

Commit 0cdcc40

Browse files
author
Raj Barik
committed
Propagate concrete types for all arguments of an apply instruction
1 parent b3a3220 commit 0cdcc40

File tree

3 files changed

+856
-87
lines changed

3 files changed

+856
-87
lines changed

lib/SILOptimizer/SILCombiner/SILCombiner.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -295,9 +295,13 @@ 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, const ConcreteExistentialInfo *>
303+
&CEIs,
304+
SILBuilderContext &BuilderCtx);
301305

302306
// Common utility function to replace the WitnessMethodInst using a
303307
// 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, const ConcreteExistentialInfo *> CEIs;
698+
CEIs.insert(std::pair<unsigned, const 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,54 @@ 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 && (!CEI.InitExistential ||
799+
!canReplaceCopiedArg(Apply, CEI.InitExistential, DA, ArgIdx))) {
800+
return false;
801+
}
802+
// It is safe to replace Arg.
803+
return true;
804+
}
805+
754806
/// Rewrite the given method apply instruction in terms of the provided conrete
755807
/// type information.
756808
///
@@ -774,74 +826,61 @@ static bool canReplaceCopiedSelf(FullApplySite Apply,
774826
/// FIXME: Protocol methods (witness or default) that return Self will be given
775827
/// a new return type. This implementation fails to update the type signature of
776828
/// 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());
829+
SILInstruction *SILCombiner::createApplyWithConcreteType(
830+
FullApplySite Apply,
831+
const llvm::SmallDenseMap<unsigned, const ConcreteExistentialInfo *> &CEIs,
832+
SILBuilderContext &BuilderCtx) {
782833

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-
}
834+
// Ensure that the callee is polymorphic.
835+
assert(Apply.getOrigCalleeType()->isPolymorphic());
814836

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

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-
});
880+
if (!UpdatedArgs)
881+
return nullptr;
844882

883+
// Now create the new apply instruction.
845884
SILBuilderWithScope ApplyBuilder(Apply.getInstruction(), BuilderCtx);
846885
FullApplySite NewApply;
847886
if (auto *TAI = dyn_cast<TryApplyInst>(Apply))
@@ -920,8 +959,12 @@ SILCombiner::propagateConcreteTypeOfInitExistential(FullApplySite Apply,
920959
replaceWitnessMethodInst(WMI, BuilderCtx, CEI.ConcreteType,
921960
SelfConformance);
922961
}
962+
// Construct the map for Self to be used for createApplyWithConcreteType.
963+
llvm::SmallDenseMap<unsigned, const ConcreteExistentialInfo *> CEIs;
964+
CEIs.insert(std::pair<unsigned, const ConcreteExistentialInfo *>(
965+
Apply.getCalleeArgIndex(Apply.getSelfArgumentOperand()), &CEI));
923966
// Try to rewrite the apply.
924-
return createApplyWithConcreteType(Apply, CEI, BuilderCtx);
967+
return createApplyWithConcreteType(Apply, CEIs, BuilderCtx);
925968
}
926969

927970
/// Rewrite a protocol extension lookup type from an archetype to a concrete
@@ -936,27 +979,36 @@ SILCombiner::propagateConcreteTypeOfInitExistential(FullApplySite Apply,
936979
/// ==> apply %f<C : P>(%ref)
937980
SILInstruction *
938981
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())
982+
// This optimization requires a generic argument.
983+
if (!Apply.hasSubstitutions())
947984
return nullptr;
948985

949986
SILBuilderContext BuilderCtx(Builder.getModule(), Builder.getTrackingList());
950987
SILOpenedArchetypesTracker OpenedArchetypesTracker(&Builder.getFunction());
951988
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);
989+
llvm::SmallDenseMap<unsigned, const ConcreteExistentialInfo *> CEIs;
990+
for (unsigned ArgIdx = 0; ArgIdx < Apply.getNumArguments(); ArgIdx++) {
991+
auto ArgASTType = Apply.getArgument(ArgIdx)->getType().getASTType();
992+
if (!ArgASTType->hasArchetype())
993+
continue;
994+
const ConcreteExistentialInfo CEI(Apply.getArgumentOperands()[ArgIdx]);
995+
if (!CEI.isValid())
996+
continue;
997+
998+
CEIs.insert(
999+
std::pair<unsigned, const 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)