Skip to content

Commit e90b6ca

Browse files
authored
Merge pull request #19460 from rajbarik/raj-cp-pca-allargs
Refactor SILCombiner to build ConcreteExistentialInfo uniformly
2 parents 388f4b8 + ff5a800 commit e90b6ca

File tree

2 files changed

+138
-97
lines changed

2 files changed

+138
-97
lines changed

lib/SILOptimizer/SILCombiner/SILCombiner.h

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

298+
// Build concrete existential information using findInitExistential.
299+
Optional<ConcreteExistentialInfo>
300+
buildConcreteExistentialInfo(Operand &ArgOperand);
301+
302+
// Build concrete existential information using SoleConformingType.
303+
Optional<ConcreteExistentialInfo>
304+
buildConcreteExistentialInfoFromSoleConformingType(Operand &ArgOperand);
305+
306+
// Common utility function to build concrete existential information for all
307+
// arguments of an apply instruction.
308+
void buildConcreteExistentialInfos(
309+
FullApplySite Apply,
310+
llvm::SmallDenseMap<unsigned, ConcreteExistentialInfo> &CEIs,
311+
SILBuilderContext &BuilderCtx,
312+
SILOpenedArchetypesTracker &OpenedArchetypesTracker);
313+
298314
bool canReplaceArg(FullApplySite Apply, const ConcreteExistentialInfo &CEI,
299315
unsigned ArgIdx);
300316
SILInstruction *createApplyWithConcreteType(

lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp

Lines changed: 122 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -615,91 +615,129 @@ void SILCombiner::replaceWitnessMethodInst(
615615
eraseInstFromFunction(*WMI);
616616
}
617617

618-
// This function propagates concrete type of existential self argument using
618+
// This function determines concrete type of existential self argument using
619619
// ProtocolConformanceAnalysis. The concrete type of self can be a class,
620620
// struct, or an enum. It replaces the witness_method instruction
621621
// with one that has a concrete type, allowing other optimizations to
622622
// devirtualize it later.
623-
SILInstruction *
624-
SILCombiner::propagateSoleConformingType(FullApplySite Apply,
625-
WitnessMethodInst *WMI) {
626-
// If WMI has concrete conformance, it can be optimized.
627-
if (WMI->getConformance().isConcrete())
628-
return nullptr;
629-
630-
// If the lookup type is an opened existential type,
631-
// it cannot be made concrete.
632-
if (!WMI->getLookupType()->isOpenedExistential())
633-
return nullptr;
634-
635-
// If the witness method mutates self, we cannot replace self.
636-
if (Apply.getOrigCalleeType()->getSelfParameter().isIndirectMutating())
637-
return nullptr;
638-
639-
// Only applicable in whole-module compilation.
640-
if (!Apply.getModule().isWholeModule())
641-
return nullptr;
623+
Optional<ConcreteExistentialInfo>
624+
SILCombiner::buildConcreteExistentialInfoFromSoleConformingType(
625+
Operand &ArgOperand) {
626+
SILInstruction *AI = ArgOperand.getUser();
627+
SILModule &M = AI->getModule();
628+
629+
// SoleConformingType is only applicable in whole-module compilation.
630+
if (!M.isWholeModule())
631+
return None;
632+
633+
// Determine the protocol.
634+
ProtocolDecl *PD = nullptr;
635+
WitnessMethodInst *WMI = nullptr;
636+
FullApplySite FAS = FullApplySite::isa(AI);
637+
if (FAS && (WMI = dyn_cast<WitnessMethodInst>(FAS.getCallee())) &&
638+
(FAS.getSelfArgumentOperand().get() == ArgOperand.get())) {
639+
// If the witness method mutates self, we cannot replace self.
640+
if (FAS.getOrigCalleeType()->getSelfParameter().isIndirectMutating())
641+
return None;
642+
PD = WMI->getLookupProtocol();
643+
} else {
644+
auto ArgType = ArgOperand.get()->getType();
645+
auto SwiftArgType = ArgType.getASTType();
646+
if (!ArgType.isExistentialType() || ArgType.isAnyObject() ||
647+
SwiftArgType->isAny())
648+
return None;
649+
PD = dyn_cast<ProtocolDecl>(SwiftArgType->getAnyNominal());
650+
}
642651

643-
auto *PD = WMI->getLookupProtocol();
652+
if (!PD)
653+
return None;
644654

645655
// Determine the sole conforming type.
646656
auto *NTD = PCA->findSoleConformingType(PD);
647657
if (!NTD)
648-
return nullptr;
658+
return None;
649659

650660
// Sole conforming class should not be open access or have any derived class.
651661
ClassDecl *CD;
652662
if ((CD = dyn_cast<ClassDecl>(NTD)) &&
653663
(CD->getEffectiveAccess() == AccessLevel::Open ||
654664
CHA->hasKnownDirectSubclasses(CD))) {
655-
return nullptr;
665+
return None;
656666
}
657667

658668
// Create SIL type for the concrete type.
659669
auto ElementType = NTD->getDeclaredType();
660670
auto ConcreteType = ElementType->getCanonicalType();
661-
auto &M = Builder.getModule();
662671

663672
/// Determine OpenedArchetypeDef and SubstituionMap.
664-
ConcreteExistentialInfo CEI(Apply.getSelfArgumentOperand(), ConcreteType, PD);
665-
if (!CEI.isValid())
666-
return nullptr;
673+
ConcreteExistentialInfo SoleCEI(ArgOperand, ConcreteType, PD);
674+
if (!SoleCEI.isValid())
675+
return None;
667676

668-
if (!CEI.InitExistential) {
677+
/// Determine the CEI.ConcreteValue.
678+
if (!SoleCEI.InitExistential) {
669679
// Create SIL type for the concrete type.
670680
SILType ConcreteSILType = M.Types.getLoweredType(ConcreteType);
671681

672682
// Prepare the code by adding UncheckedCast instructions that cast opened
673683
// existentials to concrete types. Set the ConcreteValue of CEI.
674-
if (auto *OER = dyn_cast<OpenExistentialRefInst>(CEI.OpenedArchetypeDef)) {
684+
if (auto *OER =
685+
dyn_cast<OpenExistentialRefInst>(SoleCEI.OpenedArchetypeDef)) {
675686
auto *URCI =
676687
Builder.createUncheckedRefCast(OER->getLoc(), OER, ConcreteSILType);
677-
CEI.ConcreteValue = URCI;
678-
} else if (auto *OEA =
679-
dyn_cast<OpenExistentialAddrInst>(CEI.OpenedArchetypeDef)) {
688+
SoleCEI.ConcreteValue = URCI;
689+
} else if (auto *OEA = dyn_cast<OpenExistentialAddrInst>(
690+
SoleCEI.OpenedArchetypeDef)) {
680691
auto *UACI = Builder.createUncheckedAddrCast(
681692
OEA->getLoc(), OEA, ConcreteSILType.getAddressType());
682-
CEI.ConcreteValue = UACI;
693+
SoleCEI.ConcreteValue = UACI;
683694
} else {
684-
llvm_unreachable(
685-
"Unhandled Argument Type in propagateSoleConformingType");
695+
return None;
686696
}
687697
}
698+
return SoleCEI;
699+
}
700+
// This function builds a ConcreteExistentialInfo by first following the data
701+
// flow chain from the ArgOperand. Otherwise, we check if the operand is of
702+
// protocol type that conforms to a single concrete type.
703+
Optional<ConcreteExistentialInfo>
704+
SILCombiner::buildConcreteExistentialInfo(Operand &ArgOperand) {
705+
// Build a ConcreteExistentialInfo usfollowing the data flow chain of the
706+
// ArgOperand until the init_existential.
707+
ConcreteExistentialInfo CEI(ArgOperand);
708+
if (CEI.isValid())
709+
return CEI;
710+
// Use SoleConformingType information.
711+
return buildConcreteExistentialInfoFromSoleConformingType(ArgOperand);
712+
}
688713

689-
assert(CEI.ConcreteValue);
714+
// Build ConcreteExistentialInfo for every existential argument of an Apply
715+
// instruction including Self.
716+
void SILCombiner::buildConcreteExistentialInfos(
717+
FullApplySite Apply,
718+
llvm::SmallDenseMap<unsigned, ConcreteExistentialInfo> &CEIs,
719+
SILBuilderContext &BuilderCtx,
720+
SILOpenedArchetypesTracker &OpenedArchetypesTracker) {
721+
for (unsigned ArgIdx = 0; ArgIdx < Apply.getNumArguments(); ArgIdx++) {
722+
auto ArgASTType = Apply.getArgument(ArgIdx)->getType().getASTType();
723+
if (!ArgASTType->hasArchetype())
724+
continue;
690725

691-
/// Replace the old WitnessMethod with a new one that has concrete type and
692-
/// conformance.
693-
SILBuilderContext BuilderCtx(M, Builder.getTrackingList());
694-
replaceWitnessMethodInst(WMI, BuilderCtx, ConcreteType,
695-
*(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));
700-
/// Create the new apply instruction using the concrete type.
701-
auto *NewAI = createApplyWithConcreteType(Apply, CEIs, BuilderCtx);
702-
return NewAI;
726+
auto OptionalCEI =
727+
buildConcreteExistentialInfo(Apply.getArgumentOperands()[ArgIdx]);
728+
if (!OptionalCEI.hasValue())
729+
continue;
730+
auto CEI = OptionalCEI.getValue();
731+
assert(CEI.isValid());
732+
CEIs.try_emplace(ArgIdx, CEI);
733+
734+
if (CEI.ConcreteType->isOpenedExistential()) {
735+
// Temporarily record this opened existential def in this local
736+
// BuilderContext before rewriting the witness method.
737+
OpenedArchetypesTracker.addOpenedArchetypeDef(
738+
cast<ArchetypeType>(CEI.ConcreteType), CEI.ConcreteTypeDef);
739+
}
740+
}
703741
}
704742

705743
/// Given an Apply and an argument value produced by InitExistentialAddrInst,
@@ -940,26 +978,35 @@ SILCombiner::propagateConcreteTypeOfInitExistential(FullApplySite Apply,
940978
if (!WMI->getLookupType()->isOpenedExistential())
941979
return nullptr;
942980

943-
// Try to derive the concrete type of self and the related conformance by
944-
// searching for a preceding init_existential.
945-
const ConcreteExistentialInfo CEI(Apply.getSelfArgumentOperand());
946-
if (!CEI.isValid())
981+
// Try to derive the concrete type and the related conformance of self and
982+
// other existential arguments by searching either for a preceding
983+
// init_existential or looking up sole conforming type.
984+
SILBuilderContext BuilderCtx(Builder.getModule(), Builder.getTrackingList());
985+
SILOpenedArchetypesTracker OpenedArchetypesTracker(&Builder.getFunction());
986+
BuilderCtx.setOpenedArchetypesTracker(&OpenedArchetypesTracker);
987+
llvm::SmallDenseMap<unsigned, ConcreteExistentialInfo> CEIs;
988+
buildConcreteExistentialInfos(Apply, CEIs, BuilderCtx,
989+
OpenedArchetypesTracker);
990+
991+
// Bail, if no argument has a concrete existential to propagate.
992+
if (CEIs.empty())
947993
return nullptr;
994+
auto SelfCEIIt =
995+
CEIs.find(Apply.getCalleeArgIndex(Apply.getSelfArgumentOperand()));
996+
997+
// If no SelfCEI is found, then just update the Apply with new CEIs for
998+
// other arguments.
999+
if (SelfCEIIt == CEIs.end())
1000+
return createApplyWithConcreteType(Apply, CEIs, BuilderCtx);
1001+
1002+
auto &SelfCEI = SelfCEIIt->second;
1003+
assert(SelfCEI.isValid());
9481004

9491005
// Get the conformance of the init_existential type, which is passed as the
9501006
// self argument, on the witness' protocol.
9511007
ProtocolConformanceRef SelfConformance =
952-
*CEI.lookupExistentialConformance(WMI->getLookupProtocol());
1008+
*SelfCEI.lookupExistentialConformance(WMI->getLookupProtocol());
9531009

954-
SILBuilderContext BuilderCtx(Builder.getModule(), Builder.getTrackingList());
955-
SILOpenedArchetypesTracker OpenedArchetypesTracker(&Builder.getFunction());
956-
BuilderCtx.setOpenedArchetypesTracker(&OpenedArchetypesTracker);
957-
if (CEI.ConcreteType->isOpenedExistential()) {
958-
// Temporarily record this opened existential def in this local
959-
// BuilderContext before rewriting the witness method.
960-
OpenedArchetypesTracker.addOpenedArchetypeDef(
961-
cast<ArchetypeType>(CEI.ConcreteType), CEI.ConcreteTypeDef);
962-
}
9631010
// Propagate the concrete type into a callee-operand, which is a
9641011
// witness_method instruction. It's ok to rewrite the witness method in terms
9651012
// of a concrete type without rewriting the apply itself. In fact, doing so
@@ -973,15 +1020,12 @@ SILCombiner::propagateConcreteTypeOfInitExistential(FullApplySite Apply,
9731020
// are stuck:
9741021
// We will re-create the same instruction and re-populate the worklist
9751022
// with it.
976-
if (CEI.ConcreteType != WMI->getLookupType() ||
1023+
if (SelfCEI.ConcreteType != WMI->getLookupType() ||
9771024
SelfConformance != WMI->getConformance()) {
978-
replaceWitnessMethodInst(WMI, BuilderCtx, CEI.ConcreteType,
1025+
replaceWitnessMethodInst(WMI, BuilderCtx, SelfCEI.ConcreteType,
9791026
SelfConformance);
9801027
}
981-
// Construct the map for Self to be used for createApplyWithConcreteType.
982-
llvm::SmallDenseMap<unsigned, ConcreteExistentialInfo> CEIs;
983-
CEIs.insert(std::pair<unsigned, ConcreteExistentialInfo>(
984-
Apply.getCalleeArgIndex(Apply.getSelfArgumentOperand()), CEI));
1028+
9851029
// Try to rewrite the apply.
9861030
return createApplyWithConcreteType(Apply, CEIs, BuilderCtx);
9871031
}
@@ -1002,27 +1046,16 @@ SILCombiner::propagateConcreteTypeOfInitExistential(FullApplySite Apply) {
10021046
if (!Apply.hasSubstitutions())
10031047
return nullptr;
10041048

1049+
// Try to derive the concrete type and the related conformance of self and
1050+
// other existential arguments by searching either for a preceding
1051+
// init_existential or looking up sole conforming type.
1052+
llvm::SmallDenseMap<unsigned, ConcreteExistentialInfo> CEIs;
10051053
SILBuilderContext BuilderCtx(Builder.getModule(), Builder.getTrackingList());
10061054
SILOpenedArchetypesTracker OpenedArchetypesTracker(&Builder.getFunction());
10071055
BuilderCtx.setOpenedArchetypesTracker(&OpenedArchetypesTracker);
1008-
llvm::SmallDenseMap<unsigned, ConcreteExistentialInfo> CEIs;
1009-
for (unsigned ArgIdx = 0; ArgIdx < Apply.getNumArguments(); ArgIdx++) {
1010-
auto ArgASTType = Apply.getArgument(ArgIdx)->getType().getASTType();
1011-
if (!ArgASTType->hasArchetype())
1012-
continue;
1013-
const ConcreteExistentialInfo CEI(Apply.getArgumentOperands()[ArgIdx]);
1014-
if (!CEI.isValid())
1015-
continue;
1056+
buildConcreteExistentialInfos(Apply, CEIs, BuilderCtx,
1057+
OpenedArchetypesTracker);
10161058

1017-
CEIs.insert(std::pair<unsigned, ConcreteExistentialInfo>(ArgIdx, CEI));
1018-
1019-
if (CEI.ConcreteType->isOpenedExistential()) {
1020-
// Temporarily record this opened existential def in this local
1021-
// BuilderContext before rewriting the witness method.
1022-
OpenedArchetypesTracker.addOpenedArchetypeDef(
1023-
cast<ArchetypeType>(CEI.ConcreteType), CEI.ConcreteTypeDef);
1024-
}
1025-
}
10261059
// Bail, if no argument has a concrete existential to propagate.
10271060
if (CEIs.empty())
10281061
return nullptr;
@@ -1248,13 +1281,9 @@ SILInstruction *SILCombiner::visitApplyInst(ApplyInst *AI) {
12481281
// (apply (witness_method)) -> propagate information about
12491282
// a concrete type from init_existential_addr or init_existential_ref.
12501283
if (auto *WMI = dyn_cast<WitnessMethodInst>(AI->getCallee())) {
1251-
if (!propagateConcreteTypeOfInitExistential(AI, WMI)) {
1252-
if (auto *WitnessMethod = dyn_cast<WitnessMethodInst>(AI->getCallee())) {
1253-
// Propagate concrete type from ProtocolConformanceAnalysis.
1254-
propagateSoleConformingType(AI, WitnessMethod);
1255-
}
1284+
if (propagateConcreteTypeOfInitExistential(AI, WMI)) {
1285+
return nullptr;
12561286
}
1257-
return nullptr;
12581287
}
12591288

12601289
// (apply (function_ref method_from_protocol_extension)) ->
@@ -1375,13 +1404,9 @@ SILInstruction *SILCombiner::visitTryApplyInst(TryApplyInst *AI) {
13751404
// (apply (witness_method)) -> propagate information about
13761405
// a concrete type from init_existential_addr or init_existential_ref.
13771406
if (auto *WMI = dyn_cast<WitnessMethodInst>(AI->getCallee())) {
1378-
if (!propagateConcreteTypeOfInitExistential(AI, WMI)) {
1379-
if (auto *WitnessMethod = dyn_cast<WitnessMethodInst>(AI->getCallee())) {
1380-
// Propagate concrete type from ProtocolConformanceAnalysis.
1381-
propagateSoleConformingType(AI, WitnessMethod);
1382-
}
1407+
if (propagateConcreteTypeOfInitExistential(AI, WMI)) {
1408+
return nullptr;
13831409
}
1384-
return nullptr;
13851410
}
13861411

13871412
// (apply (function_ref method_from_protocol_extension)) ->

0 commit comments

Comments
 (0)