@@ -1225,3 +1225,159 @@ SILBasicBlock::iterator OwnershipReplaceSingleUseHelper::perform() {
12251225 SingleUseReplacementUtility utility{use, newValue, *ctx};
12261226 return utility.perform ();
12271227}
1228+
1229+ // ===----------------------------------------------------------------------===//
1230+ // createBorrowScopeForPhiOperands
1231+ // ===----------------------------------------------------------------------===//
1232+
1233+ // / Given a phi that has been newly created or converted from terminator
1234+ // / results, check for inner guaranteed operands (which do not introduce a
1235+ // / borrow scope). This is invalid OSSA because the phi is a reborrow, and all
1236+ // / borrow-scope-ending instructions must directly use the BorrowedValue that
1237+ // / introduces the scope.
1238+ // /
1239+ // / Create nested borrow scopes for its operands.
1240+ // /
1241+ // / Transitively follow its phi uses.
1242+ // /
1243+ // / Create end_borrows at all points that cover the inner uses.
1244+ // /
1245+ // / The client must check canCloneTerminator() first to make sure that the
1246+ // / search for transitive uses does not encouter a PointerEscape.
1247+ class GuaranteedPhiBorrowFixup {
1248+ // A phi in mustConvertPhis has already been determined to be part of this
1249+ // new nested borrow scope.
1250+ SmallSetVector<SILPhiArgument *, 8 > mustConvertPhis;
1251+
1252+ // Phi operands that are already within the new nested borrow scope.
1253+ llvm::SmallDenseSet<PhiOperand, 8 > nestedPhiOperands;
1254+
1255+ public:
1256+ // / Return true if an extended nested borrow scope was created.
1257+ bool createExtendedNestedBorrowScope (SILPhiArgument *newPhi);
1258+
1259+ protected:
1260+ bool phiOperandNeedsBorrow (Operand *operand) {
1261+ SILValue inVal = operand->get ();
1262+ if (inVal.getOwnershipKind () != OwnershipKind::Guaranteed) {
1263+ assert (inVal.getOwnershipKind () == OwnershipKind::None);
1264+ return false ;
1265+ }
1266+ // This operand needs a nested borrow if inVal is not a BorrowedValue.
1267+ return !bool (BorrowedValue (inVal));
1268+ }
1269+
1270+ void borrowPhiOperand (Operand *oper) {
1271+ // Begin the borrow just before the branch.
1272+ SILInstruction *borrowPoint = oper->getUser ();
1273+ auto loc = RegularLocation::getAutoGeneratedLocation (borrowPoint->getLoc ());
1274+ auto *borrow =
1275+ SILBuilderWithScope (borrowPoint).createBeginBorrow (loc, oper->get ());
1276+ oper->set (borrow);
1277+ }
1278+
1279+ EndBorrowInst *createEndBorrow (SILValue guaranteedValue,
1280+ SILBasicBlock::iterator borrowPoint) {
1281+ auto loc = borrowPoint->getLoc ();
1282+ return SILBuilderWithScope (borrowPoint)
1283+ .createEndBorrow (loc, guaranteedValue);
1284+ }
1285+
1286+ void insertEndBorrowsAndFindPhis (SILPhiArgument *phi);
1287+ };
1288+
1289+ void GuaranteedPhiBorrowFixup::insertEndBorrowsAndFindPhis (
1290+ SILPhiArgument *phi) {
1291+ // Scope ending instructions are only needed for nontrivial results.
1292+ if (phi->getOwnershipKind () != OwnershipKind::Guaranteed) {
1293+ assert (phi->getOwnershipKind () == OwnershipKind::None);
1294+ return ;
1295+ }
1296+ SmallVector<Operand *, 16 > usePoints;
1297+ bool result = findInnerTransitiveGuaranteedUses (phi, usePoints);
1298+ assert (result && " should be checked by canCloneTerminator" );
1299+ (void )result;
1300+
1301+ // Add usePoints to a set for phi membership checking.
1302+ //
1303+ // FIXME: consider integrating with ValueLifetimeBoundary instead.
1304+ SmallPtrSet<Operand *, 16 > useSet (usePoints.begin (), usePoints.end ());
1305+
1306+ auto phiUsers = llvm::map_range (usePoints, ValueBase::UseToUser ());
1307+ ValueLifetimeAnalysis lifetimeAnalysis (phi, phiUsers);
1308+ ValueLifetimeBoundary boundary;
1309+ lifetimeAnalysis.computeLifetimeBoundary (boundary);
1310+
1311+ for (auto *boundaryEdge : boundary.boundaryEdges ) {
1312+ createEndBorrow (phi, boundaryEdge->begin ());
1313+ }
1314+
1315+ for (SILInstruction *lastUser : boundary.lastUsers ) {
1316+ // If the last use is a branch, transitively process the phi.
1317+ if (isa<BranchInst>(lastUser)) {
1318+ for (Operand &oper : lastUser->getAllOperands ()) {
1319+ if (!useSet.count (&oper))
1320+ continue ;
1321+
1322+ PhiOperand phiOper (&oper);
1323+ nestedPhiOperands.insert (phiOper);
1324+ mustConvertPhis.insert (phiOper.getValue ());
1325+ continue ;
1326+ }
1327+ }
1328+ // If the last user is a terminator, add the successors as boundary edges.
1329+ if (isa<TermInst>(lastUser)) {
1330+ for (auto *succBB : lastUser->getParent ()->getSuccessorBlocks ()) {
1331+ // succBB cannot already be in boundaryEdges. It has a
1332+ // single predecessor with liveness ending at the terminator, which
1333+ // means it was not live into any successor blocks.
1334+ createEndBorrow (phi, succBB->begin ());
1335+ }
1336+ continue ;
1337+ }
1338+ // Otherwise, just plop down an end_borrow after the last use.
1339+ createEndBorrow (phi, std::next (lastUser->getIterator ()));
1340+ }
1341+ };
1342+
1343+ // For each phi that transitively uses an inner guaranteed value, create nested
1344+ // borrow scopes so that it is a well-formed reborrow.
1345+ bool GuaranteedPhiBorrowFixup::
1346+ createExtendedNestedBorrowScope (SILPhiArgument *newPhi) {
1347+ // Determine if this new phi needs a nested borrow scope. If so, seed the
1348+ // Visit phi operands, returning false as soon as one needs a borrow.
1349+ if (!newPhi->visitIncomingPhiOperands (
1350+ [&](Operand *op) { return !phiOperandNeedsBorrow (op); })) {
1351+ mustConvertPhis.insert (newPhi);
1352+ }
1353+ if (mustConvertPhis.empty ())
1354+ return false ;
1355+
1356+ // mustConvertPhis grows in this loop.
1357+ for (unsigned mustConvertIdx = 0 ; mustConvertIdx < mustConvertPhis.size ();
1358+ ++mustConvertIdx) {
1359+ SILPhiArgument *phi = mustConvertPhis[mustConvertIdx];
1360+ insertEndBorrowsAndFindPhis (phi);
1361+ }
1362+ // To handle recursive phis, first discover all phis before attempting to
1363+ // borrow any phi operands.
1364+ for (SILPhiArgument *phi : mustConvertPhis) {
1365+ phi->visitIncomingPhiOperands ([&](Operand *op) {
1366+ if (!nestedPhiOperands.count (op))
1367+ borrowPhiOperand (op);
1368+ return true ;
1369+ });
1370+ }
1371+ return true ;
1372+ }
1373+
1374+ // Note: \p newPhi itself might not have Guaranteed ownership. A phi that
1375+ // converts Guaranteed to None ownership still needs nested borrows.
1376+ //
1377+ // Note: This may be called on partially invalid OSSA form, where multiple
1378+ // newly created phis do not yet have a borrow scope. The implementation
1379+ // assumes that this API will eventually be called for all such new phis until
1380+ // OSSA is fully valid.
1381+ bool swift::createBorrowScopeForPhiOperands (SILPhiArgument *newPhi) {
1382+ return GuaranteedPhiBorrowFixup ().createExtendedNestedBorrowScope (newPhi);
1383+ }
0 commit comments