Skip to content

Commit ff5a800

Browse files
author
Raj Barik
committed
Refactor SILCombiner to build ConcreteExistentialInfo using an Uniform interface that first uses Data-Flow analysis and then uses ProtocolConformanceAnalysis
1 parent 25c6c16 commit ff5a800

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,
@@ -933,26 +971,35 @@ SILCombiner::propagateConcreteTypeOfInitExistential(FullApplySite Apply,
933971
if (!WMI->getLookupType()->isOpenedExistential())
934972
return nullptr;
935973

936-
// Try to derive the concrete type of self and the related conformance by
937-
// searching for a preceding init_existential.
938-
const ConcreteExistentialInfo CEI(Apply.getSelfArgumentOperand());
939-
if (!CEI.isValid())
974+
// Try to derive the concrete type and the related conformance of self and
975+
// other existential arguments by searching either for a preceding
976+
// init_existential or looking up sole conforming type.
977+
SILBuilderContext BuilderCtx(Builder.getModule(), Builder.getTrackingList());
978+
SILOpenedArchetypesTracker OpenedArchetypesTracker(&Builder.getFunction());
979+
BuilderCtx.setOpenedArchetypesTracker(&OpenedArchetypesTracker);
980+
llvm::SmallDenseMap<unsigned, ConcreteExistentialInfo> CEIs;
981+
buildConcreteExistentialInfos(Apply, CEIs, BuilderCtx,
982+
OpenedArchetypesTracker);
983+
984+
// Bail, if no argument has a concrete existential to propagate.
985+
if (CEIs.empty())
940986
return nullptr;
987+
auto SelfCEIIt =
988+
CEIs.find(Apply.getCalleeArgIndex(Apply.getSelfArgumentOperand()));
989+
990+
// If no SelfCEI is found, then just update the Apply with new CEIs for
991+
// other arguments.
992+
if (SelfCEIIt == CEIs.end())
993+
return createApplyWithConcreteType(Apply, CEIs, BuilderCtx);
994+
995+
auto &SelfCEI = SelfCEIIt->second;
996+
assert(SelfCEI.isValid());
941997

942998
// Get the conformance of the init_existential type, which is passed as the
943999
// self argument, on the witness' protocol.
9441000
ProtocolConformanceRef SelfConformance =
945-
*CEI.lookupExistentialConformance(WMI->getLookupProtocol());
1001+
*SelfCEI.lookupExistentialConformance(WMI->getLookupProtocol());
9461002

947-
SILBuilderContext BuilderCtx(Builder.getModule(), Builder.getTrackingList());
948-
SILOpenedArchetypesTracker OpenedArchetypesTracker(&Builder.getFunction());
949-
BuilderCtx.setOpenedArchetypesTracker(&OpenedArchetypesTracker);
950-
if (CEI.ConcreteType->isOpenedExistential()) {
951-
// Temporarily record this opened existential def in this local
952-
// BuilderContext before rewriting the witness method.
953-
OpenedArchetypesTracker.addOpenedArchetypeDef(
954-
cast<ArchetypeType>(CEI.ConcreteType), CEI.ConcreteTypeDef);
955-
}
9561003
// Propagate the concrete type into a callee-operand, which is a
9571004
// witness_method instruction. It's ok to rewrite the witness method in terms
9581005
// of a concrete type without rewriting the apply itself. In fact, doing so
@@ -966,15 +1013,12 @@ SILCombiner::propagateConcreteTypeOfInitExistential(FullApplySite Apply,
9661013
// are stuck:
9671014
// We will re-create the same instruction and re-populate the worklist
9681015
// with it.
969-
if (CEI.ConcreteType != WMI->getLookupType() ||
1016+
if (SelfCEI.ConcreteType != WMI->getLookupType() ||
9701017
SelfConformance != WMI->getConformance()) {
971-
replaceWitnessMethodInst(WMI, BuilderCtx, CEI.ConcreteType,
1018+
replaceWitnessMethodInst(WMI, BuilderCtx, SelfCEI.ConcreteType,
9721019
SelfConformance);
9731020
}
974-
// Construct the map for Self to be used for createApplyWithConcreteType.
975-
llvm::SmallDenseMap<unsigned, ConcreteExistentialInfo> CEIs;
976-
CEIs.insert(std::pair<unsigned, ConcreteExistentialInfo>(
977-
Apply.getCalleeArgIndex(Apply.getSelfArgumentOperand()), CEI));
1021+
9781022
// Try to rewrite the apply.
9791023
return createApplyWithConcreteType(Apply, CEIs, BuilderCtx);
9801024
}
@@ -995,27 +1039,16 @@ SILCombiner::propagateConcreteTypeOfInitExistential(FullApplySite Apply) {
9951039
if (!Apply.hasSubstitutions())
9961040
return nullptr;
9971041

1042+
// Try to derive the concrete type and the related conformance of self and
1043+
// other existential arguments by searching either for a preceding
1044+
// init_existential or looking up sole conforming type.
1045+
llvm::SmallDenseMap<unsigned, ConcreteExistentialInfo> CEIs;
9981046
SILBuilderContext BuilderCtx(Builder.getModule(), Builder.getTrackingList());
9991047
SILOpenedArchetypesTracker OpenedArchetypesTracker(&Builder.getFunction());
10001048
BuilderCtx.setOpenedArchetypesTracker(&OpenedArchetypesTracker);
1001-
llvm::SmallDenseMap<unsigned, ConcreteExistentialInfo> CEIs;
1002-
for (unsigned ArgIdx = 0; ArgIdx < Apply.getNumArguments(); ArgIdx++) {
1003-
auto ArgASTType = Apply.getArgument(ArgIdx)->getType().getASTType();
1004-
if (!ArgASTType->hasArchetype())
1005-
continue;
1006-
const ConcreteExistentialInfo CEI(Apply.getArgumentOperands()[ArgIdx]);
1007-
if (!CEI.isValid())
1008-
continue;
1049+
buildConcreteExistentialInfos(Apply, CEIs, BuilderCtx,
1050+
OpenedArchetypesTracker);
10091051

1010-
CEIs.insert(std::pair<unsigned, ConcreteExistentialInfo>(ArgIdx, CEI));
1011-
1012-
if (CEI.ConcreteType->isOpenedExistential()) {
1013-
// Temporarily record this opened existential def in this local
1014-
// BuilderContext before rewriting the witness method.
1015-
OpenedArchetypesTracker.addOpenedArchetypeDef(
1016-
cast<ArchetypeType>(CEI.ConcreteType), CEI.ConcreteTypeDef);
1017-
}
1018-
}
10191052
// Bail, if no argument has a concrete existential to propagate.
10201053
if (CEIs.empty())
10211054
return nullptr;
@@ -1241,13 +1274,9 @@ SILInstruction *SILCombiner::visitApplyInst(ApplyInst *AI) {
12411274
// (apply (witness_method)) -> propagate information about
12421275
// a concrete type from init_existential_addr or init_existential_ref.
12431276
if (auto *WMI = dyn_cast<WitnessMethodInst>(AI->getCallee())) {
1244-
if (!propagateConcreteTypeOfInitExistential(AI, WMI)) {
1245-
if (auto *WitnessMethod = dyn_cast<WitnessMethodInst>(AI->getCallee())) {
1246-
// Propagate concrete type from ProtocolConformanceAnalysis.
1247-
propagateSoleConformingType(AI, WitnessMethod);
1248-
}
1277+
if (propagateConcreteTypeOfInitExistential(AI, WMI)) {
1278+
return nullptr;
12491279
}
1250-
return nullptr;
12511280
}
12521281

12531282
// (apply (function_ref method_from_protocol_extension)) ->
@@ -1368,13 +1397,9 @@ SILInstruction *SILCombiner::visitTryApplyInst(TryApplyInst *AI) {
13681397
// (apply (witness_method)) -> propagate information about
13691398
// a concrete type from init_existential_addr or init_existential_ref.
13701399
if (auto *WMI = dyn_cast<WitnessMethodInst>(AI->getCallee())) {
1371-
if (!propagateConcreteTypeOfInitExistential(AI, WMI)) {
1372-
if (auto *WitnessMethod = dyn_cast<WitnessMethodInst>(AI->getCallee())) {
1373-
// Propagate concrete type from ProtocolConformanceAnalysis.
1374-
propagateSoleConformingType(AI, WitnessMethod);
1375-
}
1400+
if (propagateConcreteTypeOfInitExistential(AI, WMI)) {
1401+
return nullptr;
13761402
}
1377-
return nullptr;
13781403
}
13791404

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

0 commit comments

Comments
 (0)