@@ -601,7 +601,7 @@ SILCombiner::optimizeConcatenationOfStringLiterals(ApplyInst *AI) {
601
601
}
602
602
603
603
// / Returns the address of an object with which the stack location \p ASI is
604
- // / initialized. This is either a init_existential_addr or the source of a
604
+ // / initialized. This is either a init_existential_addr or the destination of a
605
605
// / copy_addr. Returns a null value if the address does not dominate the
606
606
// / alloc_stack user \p ASIUser.
607
607
// / If the value is copied from another stack location, \p isCopied is set to
@@ -808,21 +808,45 @@ SILCombiner::createApplyWithConcreteType(FullApplySite AI,
808
808
return NewAI.getInstruction ();
809
809
}
810
810
811
+ namespace {
812
+ // / Record conformance and concrete type info derived from init_existential.
813
+ struct ConformanceAndConcreteType {
814
+ Optional<ProtocolConformanceRef> Conformance;
815
+ // Concrete type of self from the found init_existential.
816
+ CanType ConcreteType;
817
+ // For opened existentials, record the type definition.
818
+ SingleValueInstruction *ConcreteTypeDef = nullptr ;
819
+ // The value of concrete type used to initialize the existential.
820
+ SILValue NewSelf;
821
+ // The value that owns the lifetime of NewSelf.
822
+ // init_existential_addr's source address.
823
+ // init_existential_ref's defined value.
824
+ SILValue NewSelfOwner;
825
+ ArrayRef<ProtocolConformanceRef> Conformances;
826
+
827
+ ConformanceAndConcreteType (ASTContext &Ctx, FullApplySite AI,
828
+ SILInstruction *InitExistential,
829
+ ProtocolDecl *Protocol);
830
+
831
+ bool isValid () const {
832
+ return Conformance.hasValue () && ConcreteType && NewSelf;
833
+ }
834
+
835
+ ProtocolConformanceRef getConformance () const {
836
+ return Conformance.getValue ();
837
+ }
838
+ };
839
+ } // namespace
840
+
811
841
// / Derive a concrete type of self and conformance from the init_existential
812
842
// / instruction.
813
- static Optional<std::tuple<ProtocolConformanceRef, CanType,
814
- SingleValueInstruction*, SILValue>>
815
- getConformanceAndConcreteType (ASTContext &Ctx,
816
- FullApplySite AI,
817
- SILInstruction *InitExistential,
818
- ProtocolDecl *Protocol,
819
- ArrayRef<ProtocolConformanceRef> &Conformances) {
820
- // Try to derive the concrete type of self from the found init_existential.
821
- CanType ConcreteType;
843
+ // / If successful, initializes a valid ConformanceAndConcreteType.
844
+ ConformanceAndConcreteType::ConformanceAndConcreteType (
845
+ ASTContext &Ctx, FullApplySite AI, SILInstruction *InitExistential,
846
+ ProtocolDecl *Protocol) {
847
+
822
848
// The existential type result of the found init_existential.
823
849
CanType ExistentialType;
824
- SingleValueInstruction *ConcreteTypeDef = nullptr ;
825
- SILValue NewSelf;
826
850
827
851
// FIXME: Factor this out. All we really need here is the ExistentialSig
828
852
// below, which should be stored directly in the SILInstruction.
@@ -846,7 +870,8 @@ getConformanceAndConcreteType(ASTContext &Ctx,
846
870
ConcreteType = cast<MetatypeType>(ConcreteType).getInstanceType ();
847
871
}
848
872
} else {
849
- return None;
873
+ assert (!isValid ());
874
+ return ;
850
875
}
851
876
852
877
// Construct a substitution map from the existential type's generic
@@ -860,9 +885,8 @@ getConformanceAndConcreteType(ASTContext &Ctx,
860
885
// If the requirement is in a base protocol that is refined by the
861
886
// conforming protocol, fish out the exact conformance for the base
862
887
// protocol using the substitution map.
863
- auto ExactConformance = SubMap.lookupConformance (
864
- CanType (ExistentialSig->getGenericParams ()[0 ]),
865
- Protocol);
888
+ Conformance = SubMap.lookupConformance (
889
+ CanType (ExistentialSig->getGenericParams ()[0 ]), Protocol);
866
890
867
891
// If the concrete type is another existential, we're "forwarding" an
868
892
// opened existential type, so we must keep track of the original
@@ -878,127 +902,136 @@ getConformanceAndConcreteType(ASTContext &Ctx,
878
902
InitExistential->getTypeDependentOperands ()[0 ].get ());
879
903
}
880
904
}
881
-
882
- return std::make_tuple (*ExactConformance, ConcreteType,
883
- ConcreteTypeDef, NewSelf);
905
+ assert (isValid ());
884
906
}
885
907
886
- static bool isUseAfterFree (SILValue val, SILInstruction *Apply,
887
- DominanceInfo *DT) {
888
- for (auto Use : val->getUses ()) {
889
- auto *User = Use->getUser ();
890
- if (!isa<DeallocStackInst>(User) && !isa<DestroyAddrInst>(User) &&
891
- !isa<DeinitExistentialAddrInst>(User)) {
892
- continue ;
893
- }
894
- if (!DT->properlyDominates (Apply, User)) {
895
- // we have use-after-free - Conservative heuristic
896
- // Non conservative solution would require data flow analysis
908
+ // Return true if the given value is guaranteed to be initialized across the
909
+ // given call site.
910
+ //
911
+ // It's possible for an address to be initialized/deinitialized/reinitialized.
912
+ // Rather than keeping track of liveness, we very conservatively check that all
913
+ // deinitialization occures after the call.
914
+ //
915
+ // FIXME: Rather than whitelisting, use a common AllocStackAnalyzer.
916
+ static bool isAddressInitializedAtCall (SILValue addr, SILInstruction *AI,
917
+ DominanceInfo *DT) {
918
+ auto isDestroy = [](Operand *use) {
919
+ switch (use->getUser ()->getKind ()) {
920
+ default :
921
+ return false ;
922
+ case SILInstructionKind::DestroyAddrInst:
923
+ case SILInstructionKind::DeinitExistentialAddrInst:
897
924
return true ;
925
+ case SILInstructionKind::CopyAddrInst: {
926
+ auto *copy = cast<CopyAddrInst>(use->getUser ());
927
+ return copy->getSrc () == use->get () && copy->isTakeOfSrc ();
928
+ }
929
+ }
930
+ };
931
+ for (auto use : addr->getUses ()) {
932
+ SILInstruction *user = use->getUser ();
933
+ if (isDestroy (use)) {
934
+ if (!DT->properlyDominates (AI, user))
935
+ return false ;
936
+ } else {
937
+ assert (isa<CopyAddrInst>(user) || isa<InitExistentialAddrInst>(user)
938
+ || isa<OpenExistentialAddrInst>(user)
939
+ || isa<DeallocStackInst>(user)
940
+ || isDebugInst (user) && " Unexpected instruction" );
898
941
}
899
942
}
900
- return false ;
943
+ return true ;
901
944
}
902
945
903
946
// / Propagate information about a concrete type from init_existential_addr
904
947
// / or init_existential_ref into witness_method conformances and into
905
948
// / apply instructions.
906
949
// / This helps the devirtualizer to replace witness_method by
907
950
// / class_method instructions and then devirtualize.
908
- SILInstruction *
909
- SILCombiner::propagateConcreteTypeOfInitExistential (FullApplySite AI,
910
- ProtocolDecl *Protocol,
911
- llvm::function_ref<void (CanType , ProtocolConformanceRef)> Propagate) {
951
+ SILInstruction *SILCombiner::propagateConcreteTypeOfInitExistential (
952
+ FullApplySite Apply, ProtocolDecl *Protocol,
953
+ llvm::function_ref<void (CanType, ProtocolConformanceRef)> Propagate) {
912
954
913
955
ASTContext &Ctx = Builder.getASTContext ();
914
956
915
957
// Get the self argument.
916
- SILValue Self;
917
- if (auto *Apply = dyn_cast<ApplyInst>(AI)) {
918
- if (Apply->hasSelfArgument ())
919
- Self = Apply->getSelfArgument ();
920
- } else if (auto *Apply = dyn_cast<TryApplyInst>(AI)) {
921
- if (Apply->hasSelfArgument ())
922
- Self = Apply->getSelfArgument ();
923
- }
924
-
925
- assert (Self && " Self argument should be present" );
958
+ assert (Apply.hasSelfArgument () && " Self argument should be present" );
959
+ SILValue Self = Apply.getSelfArgument ();
926
960
927
961
// Try to find the init_existential, which could be used to
928
962
// determine a concrete type of the self.
929
963
ArchetypeType *OpenedArchetype = nullptr ;
930
964
SILValue OpenedArchetypeDef;
931
965
bool isCopied = false ;
932
- SILInstruction *InitExistential =
933
- findInitExistential (AI, Self, OpenedArchetype, OpenedArchetypeDef,
934
- isCopied);
966
+ SILInstruction *InitExistential = findInitExistential (
967
+ Apply, Self, OpenedArchetype, OpenedArchetypeDef, isCopied);
935
968
if (!InitExistential)
936
969
return nullptr ;
937
970
938
971
// Try to derive the concrete type of self and a related conformance from
939
972
// the found init_existential.
940
- ArrayRef<ProtocolConformanceRef> Conformances;
941
- auto ConformanceAndConcreteType =
942
- getConformanceAndConcreteType (Ctx, AI, InitExistential,
943
- Protocol, Conformances);
944
- if (!ConformanceAndConcreteType)
973
+ ConformanceAndConcreteType CCT (Ctx, Apply, InitExistential, Protocol);
974
+ if (!CCT.isValid ())
945
975
return nullptr ;
946
976
947
- ProtocolConformanceRef Conformance = std::get<0 >(*ConformanceAndConcreteType);
948
- CanType ConcreteType = std::get<1 >(*ConformanceAndConcreteType);
949
- auto ConcreteTypeDef = std::get<2 >(*ConformanceAndConcreteType);
950
- SILValue NewSelf = std::get<3 >(*ConformanceAndConcreteType);
951
-
952
977
SILOpenedArchetypesTracker *OldOpenedArchetypesTracker =
953
978
Builder.getOpenedArchetypesTracker ();
954
979
955
- SILOpenedArchetypesTracker OpenedArchetypesTracker (AI .getFunction ());
980
+ SILOpenedArchetypesTracker OpenedArchetypesTracker (Apply .getFunction ());
956
981
957
- if (ConcreteType->isOpenedExistential ()) {
958
- // Prepare a mini-mapping for opened archetypes.
959
- // SILOpenedArchetypesTracker OpenedArchetypesTracker(*AI.getFunction());
982
+ if (CCT.ConcreteType ->isOpenedExistential ()) {
983
+ // Temporarily record this opened existential def. Don't permanently record
984
+ // in the Builder's tracker because this opened existential's original
985
+ // dominating def may not have been recorded yet.
986
+ // FIXME: Redesign the tracker. This is not robust.
960
987
OpenedArchetypesTracker.addOpenedArchetypeDef (
961
- cast<ArchetypeType>(ConcreteType), ConcreteTypeDef);
988
+ cast<ArchetypeType>(CCT. ConcreteType ), CCT. ConcreteTypeDef );
962
989
Builder.setOpenedArchetypesTracker (&OpenedArchetypesTracker);
963
990
}
964
991
965
992
// Propagate the concrete type into the callee-operand if required.
966
- Propagate (ConcreteType, Conformance );
993
+ Propagate (CCT. ConcreteType , CCT. getConformance () );
967
994
968
- if (isCopied ) {
995
+ auto canReplaceCopiedSelf = [&]( ) {
969
996
// If the witness method is mutating self, we cannot replace self with
970
997
// the source of a copy. Otherwise the call would modify another value than
971
998
// the original self.
972
- switch (AI.getArgumentConvention (AI.getNumArguments () - 1 )) {
973
- case SILArgumentConvention::ConventionType::Indirect_Inout:
974
- case SILArgumentConvention::ConventionType::Indirect_InoutAliasable:
975
- return nullptr ;
976
- default :
977
- break ;
978
- }
979
- // check if using the value in the apply would cause use-after-free
980
- auto *DT = DA->get (AI.getFunction ());
981
- auto *apply = AI.getInstruction ();
982
- auto op = InitExistential->getOperand (0 );
983
- if (isUseAfterFree (op, apply, DT)) {
984
- return nullptr ;
985
- }
986
- if (isUseAfterFree (NewSelf, apply, DT)) {
987
- return nullptr ;
988
- }
989
- }
999
+ if (Apply.getOrigCalleeType ()->getSelfParameter ().isIndirectMutating ())
1000
+ return false ;
1001
+
1002
+ auto *DT = DA->get (Apply.getFunction ());
1003
+ auto *AI = Apply.getInstruction ();
1004
+ // Only init_existential_addr may be copied.
1005
+ SILValue existentialAddr =
1006
+ cast<InitExistentialAddrInst>(InitExistential)->getOperand ();
1007
+ return isAddressInitializedAtCall (existentialAddr, AI, DT);
1008
+ };
1009
+
1010
+ // FIXME: Intentionally preserve this bug to avoid changing functionality.
1011
+ if (isCopied && !canReplaceCopiedSelf ())
1012
+ return nullptr ;
1013
+
990
1014
// Create a new apply instruction that uses the concrete type instead
991
1015
// of the existential type.
992
- auto *NewAI = createApplyWithConcreteType (AI, NewSelf, Self, ConcreteType,
993
- ConcreteTypeDef, Conformance ,
994
- OpenedArchetype);
1016
+ auto *NewAI = createApplyWithConcreteType (
1017
+ Apply, CCT. NewSelf , Self, CCT. ConcreteType , CCT. ConcreteTypeDef ,
1018
+ CCT. getConformance (), OpenedArchetype);
995
1019
996
- if (ConcreteType->isOpenedExistential ())
1020
+ if (CCT. ConcreteType ->isOpenedExistential ())
997
1021
Builder.setOpenedArchetypesTracker (OldOpenedArchetypesTracker);
998
1022
999
1023
return NewAI;
1000
1024
}
1001
1025
1026
+ // / Rewrite a witness method's lookup type from an archetype to a concrete type.
1027
+ // / Example:
1028
+ // / %existential = alloc_stack $Protocol
1029
+ // / %value = init_existential_addr %existential : $Concrete
1030
+ // / copy_addr ... to %value
1031
+ // / %witness = witness_method $@opened
1032
+ // / apply %witness<T : Protocol>(%existential)
1033
+ // /
1034
+ // / ==> apply %witness<Concrete : Protocol>(%existential)
1002
1035
SILInstruction *
1003
1036
SILCombiner::propagateConcreteTypeOfInitExistential (FullApplySite AI,
1004
1037
WitnessMethodInst *WMI) {
@@ -1097,7 +1130,7 @@ SILCombiner::propagateConcreteTypeOfInitExistential(FullApplySite AI) {
1097
1130
return nullptr ;
1098
1131
}
1099
1132
1100
- // Obtain the protocol whose which should be used by the conformance.
1133
+ // Obtain the protocol which should be used by the conformance.
1101
1134
auto *AFD = dyn_cast<AbstractFunctionDecl>(Callee->getDeclContext ());
1102
1135
if (!AFD)
1103
1136
return nullptr ;
0 commit comments