@@ -978,10 +978,8 @@ SILInstruction *SILCombiner::visitConvertEscapeToNoEscapeInst(
978
978
SILType::getPrimitiveObjectType (NewTy));
979
979
}
980
980
981
- SILInstruction *SILCombiner::visitConvertFunctionInst (ConvertFunctionInst *CFI) {
982
- if (CFI->getFunction ()->hasOwnership ())
983
- return nullptr ;
984
-
981
+ SILInstruction *
982
+ SILCombiner::visitConvertFunctionInst (ConvertFunctionInst *cfi) {
985
983
// If this conversion only changes substitutions, then rewrite applications
986
984
// of the converted function as applications of the original.
987
985
//
@@ -990,84 +988,124 @@ SILInstruction *SILCombiner::visitConvertFunctionInst(ConvertFunctionInst *CFI)
990
988
//
991
989
// TODO: We could generalize this to handle other ABI-compatible cases, by
992
990
// inserting the necessary casts around the arguments.
993
- if (CFI->onlyConvertsSubstitutions ()) {
994
- auto usei = CFI->use_begin ();
995
- while (usei != CFI->use_end ()) {
996
- auto use = *usei++;
997
- auto user = use->getUser ();
998
- if (isa<ApplySite>(user) && use->getOperandNumber () == 0 ) {
999
- auto applySite = ApplySite (user);
1000
- // If this is a partial_apply, insert a convert_function back to the
1001
- // original result type.
1002
-
1003
- if (auto pa = dyn_cast<PartialApplyInst>(user)) {
1004
- auto partialApplyTy = pa->getType ();
1005
- Builder.setInsertionPoint (std::next (pa->getIterator ()));
1006
-
1007
- SmallVector<SILValue, 4 > args (pa->getArguments ().begin (),
1008
- pa->getArguments ().end ());
1009
-
1010
- auto newPA = Builder.createPartialApply (pa->getLoc (),
1011
- CFI->getConverted (),
1012
- pa->getSubstitutionMap (),
1013
- args,
1014
- pa->getFunctionType ()->getCalleeConvention ());
1015
- auto newConvert = Builder.createConvertFunction (pa->getLoc (),
1016
- newPA, partialApplyTy,
1017
- false );
1018
- pa->replaceAllUsesWith (newConvert);
1019
- eraseInstFromFunction (*pa);
1020
-
1021
- continue ;
1022
- }
1023
-
991
+ if (cfi->onlyConvertsSubstitutions ()) {
992
+ SmallVector<Operand *, 32 > worklist (cfi->getUses ());
993
+ while (!worklist.empty ()) {
994
+ auto *use = worklist.pop_back_val ();
995
+ auto *user = use->getUser ();
996
+
997
+ // Look through begin_borrow and copy_value.
998
+ if (isa<BeginBorrowInst>(user) || isa<CopyValueInst>(user)) {
999
+ for (auto result : user->getResults ())
1000
+ for (auto *resultUse : result->getUses ())
1001
+ worklist.push_back (resultUse);
1002
+ continue ;
1003
+ }
1004
+
1005
+ if (!isa<ApplySite>(user) || use->getOperandNumber () != 0 )
1006
+ continue ;
1007
+
1008
+ if (auto fas = FullApplySite::isa (user)) {
1024
1009
// For full apply sites, we only need to replace the `convert_function`
1025
1010
// with the original value.
1026
- use->set (CFI->getConverted ());
1027
- applySite.setSubstCalleeType (
1028
- CFI->getConverted ()->getType ().castTo <SILFunctionType>());
1011
+ //
1012
+ // OWNERSHIP DISCUSSION: We know that cfi is forwarding, so we know that
1013
+ // if cfi is not owned, then we know that cfi->getConverted() must be
1014
+ // valid at applySite and also that the applySite does not consume a
1015
+ // value. In such a case, just perform the change and continue.
1016
+ SILValue newValue = cfi->getConverted ();
1017
+ if (newValue.getOwnershipKind () != OwnershipKind::Owned &&
1018
+ newValue.getOwnershipKind () != OwnershipKind::Guaranteed) {
1019
+ instModCallbacks.setUseValue (use, newValue);
1020
+ fas.setSubstCalleeType (newValue->getType ().castTo <SILFunctionType>());
1021
+ continue ;
1022
+ }
1023
+
1024
+ // Otherwise, we need to use the OwnershipReplaceSingleUseHelper since
1025
+ // we have been looking through ownership forwarding insts and newValue
1026
+ // may be a value with a different lifetime from our original value
1027
+ // beyond the initial base value.
1028
+ OwnershipReplaceSingleUseHelper helper (ownershipFixupContext, use,
1029
+ newValue);
1030
+ if (!helper)
1031
+ continue ;
1032
+ helper.perform ();
1033
+ fas.setSubstCalleeType (newValue->getType ().castTo <SILFunctionType>());
1034
+ continue ;
1035
+ }
1036
+
1037
+ // If this is a partial_apply, insert a convert_function back to the
1038
+ // original result type.
1039
+ auto *pa = dyn_cast<PartialApplyInst>(user);
1040
+ if (!pa)
1041
+ continue ;
1042
+
1043
+ auto partialApplyTy = pa->getType ();
1044
+ if (!hasOwnership ()) {
1045
+ SILBuilderWithScope localBuilder (std::next (pa->getIterator ()), Builder);
1046
+ SmallVector<SILValue, 4 > args (pa->getArguments ().begin (),
1047
+ pa->getArguments ().end ());
1048
+
1049
+ auto newPA = Builder.createPartialApply (
1050
+ pa->getLoc (), cfi->getConverted (), pa->getSubstitutionMap (), args,
1051
+ pa->getFunctionType ()->getCalleeConvention ());
1052
+ auto newConvert = Builder.createConvertFunction (pa->getLoc (), newPA,
1053
+ partialApplyTy, false );
1054
+ replaceInstUsesWith (*pa, newConvert);
1055
+ eraseInstFromFunction (*pa);
1056
+ continue ;
1029
1057
}
1058
+
1059
+ OwnershipRAUWHelper helper (ownershipFixupContext, pa,
1060
+ cfi->getConverted ());
1061
+ if (!helper)
1062
+ continue ;
1063
+ SmallVector<SILValue, 4 > args (pa->getArguments ().begin (),
1064
+ pa->getArguments ().end ());
1065
+ auto newValue = withJointPostDomComputer<SILValue>([&](auto &j) {
1066
+ return makeCopiedValueAvailable (cfi->getConverted (), pa->getParent (),
1067
+ &j);
1068
+ });
1069
+
1070
+ SILBuilderWithScope localBuilder (std::next (pa->getIterator ()), Builder);
1071
+ auto *newPA = localBuilder.createPartialApply (
1072
+ pa->getLoc (), newValue, pa->getSubstitutionMap (), args,
1073
+ pa->getFunctionType ()->getCalleeConvention ());
1074
+ if (!use->isLifetimeEnding ()) {
1075
+ localBuilder.emitDestroyValueOperation (pa->getLoc (), newValue);
1076
+ }
1077
+ auto *newConvert = localBuilder.createConvertFunction (
1078
+ pa->getLoc (), newPA, partialApplyTy, false );
1079
+ // We need to end the lifetime of the convert_function/partial_apply since
1080
+ // the helper assumes that ossa is correct upon input.
1081
+ localBuilder.emitDestroyValueOperation (pa->getLoc (), newConvert);
1082
+ helper.perform (newConvert);
1030
1083
}
1031
1084
}
1032
-
1085
+
1033
1086
// (convert_function (convert_function x)) => (convert_function x)
1034
- if (auto subCFI = dyn_cast<ConvertFunctionInst>(CFI->getConverted ())) {
1035
- // If we convert the function type back to itself, we can replace the
1036
- // conversion completely.
1037
- if (subCFI->getConverted ()->getType () == CFI->getType ()) {
1038
- CFI->replaceAllUsesWith (subCFI->getConverted ());
1039
- eraseInstFromFunction (*CFI);
1040
- return nullptr ;
1087
+ if (auto *subCFI = dyn_cast<ConvertFunctionInst>(cfi->getConverted ())) {
1088
+ // We handle the case of an identity conversion in inst simplify, so if we
1089
+ // see this pattern then we know that we don't have a round trip and thus
1090
+ // should just bypass the intermediate conversion.
1091
+ if (cfi->getOwnershipKind () != OwnershipKind::Owned) {
1092
+ cfi->getOperandRef ().set (subCFI->getConverted ());
1093
+ // Return cfi to show we changed it.
1094
+ return cfi;
1041
1095
}
1042
-
1043
- // Otherwise, we can still bypass the intermediate conversion.
1044
- CFI->getOperandRef ().set (subCFI->getConverted ());
1045
- }
1046
-
1047
- // Replace a convert_function that only has refcounting uses with its
1048
- // operand.
1049
- auto anyNonRefCountUse =
1050
- std::any_of (CFI->use_begin (),
1051
- CFI->use_end (),
1052
- [](Operand *Use) {
1053
- return !isa<RefCountingInst>(Use->getUser ());
1054
- });
1055
-
1056
- if (anyNonRefCountUse)
1057
- return nullptr ;
1058
1096
1059
- // Replace all retain/releases on convert_function by retain/releases on
1060
- // its argument. This is required to preserve the lifetime of its argument,
1061
- // which could be e.g. a partial_apply instruction capturing some further
1062
- // arguments.
1063
- auto Converted = CFI->getConverted ();
1064
- while (!CFI->use_empty ()) {
1065
- auto *Use = *(CFI->use_begin ());
1066
- assert (Use->getUser ()->getResults ().empty () &&
1067
- " Did not expect user with a result!" );
1068
- Use->set (Converted);
1097
+ // If we have an owned value, we can only perform this optimization if the
1098
+ // convert_function is in the same block to ensure that we know we will
1099
+ // eliminate the convert_function. Otherwise we may be breaking up a
1100
+ // forwarding chain in favor of additional ARC traffic which isn't
1101
+ // canonical.
1102
+ SingleBlockOwnedForwardingInstFolder folder (*this , cfi);
1103
+ if (folder.add (subCFI))
1104
+ return std::move (folder).optimizeWithSetValue (subCFI->getConverted ());
1069
1105
}
1070
1106
1071
- eraseInstFromFunction (*CFI);
1107
+ // Replace a convert_function that only has refcounting uses with its
1108
+ // operand.
1109
+ tryEliminateOnlyOwnershipUsedForwardingInst (cfi, instModCallbacks);
1072
1110
return nullptr ;
1073
1111
}
0 commit comments