@@ -425,7 +425,9 @@ class AvailableValueAggregator {
425
425
// / If as a result of us copying values, we may have unconsumed destroys, find
426
426
// / the appropriate location and place the values there. Only used when
427
427
// / ownership is enabled.
428
- LoadInst *addMissingDestroysForCopiedValues (LoadInst *li, SILValue newVal);
428
+ SingleValueInstruction *
429
+ addMissingDestroysForCopiedValues (SingleValueInstruction *li,
430
+ SILValue newVal);
429
431
430
432
void print (llvm::raw_ostream &os) const ;
431
433
void dump () const LLVM_ATTRIBUTE_USED;
@@ -746,14 +748,14 @@ SILValue AvailableValueAggregator::handlePrimitiveValue(SILType loadTy,
746
748
return eltVal;
747
749
}
748
750
749
- LoadInst *
750
- AvailableValueAggregator::addMissingDestroysForCopiedValues (LoadInst *li,
751
- SILValue newVal) {
751
+ SingleValueInstruction *
752
+ AvailableValueAggregator::addMissingDestroysForCopiedValues (
753
+ SingleValueInstruction *svi, SILValue newVal) {
752
754
// If ownership is not enabled... bail. We do not need to do this since we do
753
755
// not need to insert an extra copy unless we have ownership since without
754
756
// ownership stores do not consume.
755
757
if (!B.hasOwnership ())
756
- return li ;
758
+ return svi ;
757
759
758
760
SmallPtrSet<SILBasicBlock *, 8 > visitedBlocks;
759
761
SmallVector<SILBasicBlock *, 8 > leakingBlocks;
@@ -776,8 +778,9 @@ AvailableValueAggregator::addMissingDestroysForCopiedValues(LoadInst *li,
776
778
// Then perform the linear lifetime check. If we succeed, continue. We have
777
779
// no further work to do.
778
780
auto errorKind = ownership::ErrorBehaviorKind::ReturnFalse;
779
- auto error = valueHasLinearLifetime (
780
- cvi, {li}, {}, visitedBlocks, deadEndBlocks, errorKind, &leakingBlocks);
781
+ auto error =
782
+ valueHasLinearLifetime (cvi, {svi}, {}, visitedBlocks, deadEndBlocks,
783
+ errorKind, &leakingBlocks);
781
784
if (!error.getFoundError ())
782
785
continue ;
783
786
@@ -795,17 +798,40 @@ AvailableValueAggregator::addMissingDestroysForCopiedValues(LoadInst *li,
795
798
}
796
799
}
797
800
798
- // If we didn't find a loop, we are done, just return li to get RAUWed.
799
- if (!foundLoop)
800
- return li;
801
+ // If we didn't find a loop, we are done, just return svi to get RAUWed.
802
+ if (!foundLoop) {
803
+ // If we had a load_borrow, we have created an extra copy that we are going
804
+ // to borrow at the load point. This means we need to handle the destroying
805
+ // of the value along paths reachable from the load_borrow. Luckily that
806
+ // will exactly be after the end_borrows of the load_borrow.
807
+ if (isa<LoadBorrowInst>(svi)) {
808
+ for (auto *use : svi->getUses ()) {
809
+ if (auto *ebi = dyn_cast<EndBorrowInst>(use->getUser ())) {
810
+ auto next = std::next (ebi->getIterator ());
811
+ SILBuilderWithScope (next).emitDestroyValueOperation (ebi->getLoc (),
812
+ newVal);
813
+ }
814
+ }
815
+ }
816
+ return svi;
817
+ }
801
818
802
819
// If we found a loop, then we know that our leaking blocks are the exiting
803
- // blocks of the loop. Thus we need to change the load inst to a copy_value
804
- // instead of deleting it.
805
- newVal = SILBuilderWithScope (li).emitCopyValueOperation (loc, newVal);
806
- li->replaceAllUsesWith (newVal);
807
- SILValue addr = li->getOperand ();
808
- li->eraseFromParent ();
820
+ // blocks of the loop and the value has been lifetime extended over the loop.
821
+ if (isa<LoadInst>(svi)) {
822
+ // If we have a load, we need to put in a copy so that the destroys within
823
+ // the loop are properly balanced.
824
+ newVal = SILBuilderWithScope (svi).emitCopyValueOperation (loc, newVal);
825
+ } else {
826
+ // If we have a load_borrow, we create a begin_borrow for the end_borrows in
827
+ // the loop.
828
+ assert (isa<LoadBorrowInst>(svi));
829
+ newVal = SILBuilderWithScope (svi).createBeginBorrow (svi->getLoc (), newVal);
830
+ }
831
+
832
+ svi->replaceAllUsesWith (newVal);
833
+ SILValue addr = svi->getOperand (0 );
834
+ svi->eraseFromParent ();
809
835
if (auto *addrI = addr->getDefiningInstruction ())
810
836
recursivelyDeleteTriviallyDeadInstructions (addrI);
811
837
return nullptr ;
@@ -1308,19 +1334,23 @@ class AllocOptimize {
1308
1334
// / If we are able to optimize \p Inst, return the source address that
1309
1335
// / instruction is loading from. If we can not optimize \p Inst, then just
1310
1336
// / return an empty SILValue.
1311
- static SILValue tryFindSrcAddrForLoad (SILInstruction *Inst) {
1337
+ static SILValue tryFindSrcAddrForLoad (SILInstruction *i) {
1338
+ // We can always promote a load_borrow.
1339
+ if (auto *lbi = dyn_cast<LoadBorrowInst>(i))
1340
+ return lbi->getOperand ();
1341
+
1312
1342
// We only handle load [copy], load [trivial], load and copy_addr right
1313
1343
// now. Notably we do not support load [take] when promoting loads.
1314
- if (auto *LI = dyn_cast<LoadInst>(Inst ))
1315
- if (LI ->getOwnershipQualifier () != LoadOwnershipQualifier::Take)
1316
- return LI ->getOperand ();
1344
+ if (auto *li = dyn_cast<LoadInst>(i ))
1345
+ if (li ->getOwnershipQualifier () != LoadOwnershipQualifier::Take)
1346
+ return li ->getOperand ();
1317
1347
1318
1348
// If this is a CopyAddr, verify that the element type is loadable. If not,
1319
1349
// we can't explode to a load.
1320
- auto *CAI = dyn_cast<CopyAddrInst>(Inst );
1321
- if (!CAI || !CAI ->getSrc ()->getType ().isLoadable (CAI ->getModule ()))
1350
+ auto *cai = dyn_cast<CopyAddrInst>(i );
1351
+ if (!cai || !cai ->getSrc ()->getType ().isLoadable (cai ->getModule ()))
1322
1352
return SILValue ();
1323
- return CAI ->getSrc ();
1353
+ return cai ->getSrc ();
1324
1354
}
1325
1355
1326
1356
// / At this point, we know that this element satisfies the definitive init
@@ -1385,25 +1415,30 @@ bool AllocOptimize::promoteLoad(SILInstruction *Inst) {
1385
1415
// removing the instruction from Uses for us, so we return false.
1386
1416
return false ;
1387
1417
}
1388
-
1418
+
1419
+ assert ((isa<LoadBorrowInst>(Inst) || isa<LoadInst>(Inst)) &&
1420
+ " Unhandled instruction for this code path!" );
1421
+
1389
1422
// Aggregate together all of the subelements into something that has the same
1390
1423
// type as the load did, and emit smaller loads for any subelements that were
1391
- // not available.
1392
- auto *Load = cast<LoadInst>(Inst);
1393
- AvailableValueAggregator Agg (Load, AvailableValues, Uses, deadEndBlocks,
1424
+ // not available. We are "propagating" a +1 available value from the store
1425
+ // points.
1426
+ auto *load = dyn_cast<SingleValueInstruction>(Inst);
1427
+ AvailableValueAggregator agg (load, AvailableValues, Uses, deadEndBlocks,
1394
1428
false /* isTake*/ );
1395
- SILValue newVal = Agg .aggregateValues (LoadTy, Load ->getOperand (), FirstElt);
1429
+ SILValue newVal = agg .aggregateValues (LoadTy, load ->getOperand (0 ), FirstElt);
1396
1430
1397
- LLVM_DEBUG (llvm::dbgs () << " *** Promoting load: " << *Load << " \n " );
1431
+ LLVM_DEBUG (llvm::dbgs () << " *** Promoting load: " << *load << " \n " );
1398
1432
LLVM_DEBUG (llvm::dbgs () << " To value: " << *newVal << " \n " );
1399
1433
1400
1434
// If we inserted any copies, we created the copies at our stores. We know
1401
1435
// that in our load block, we will reform the aggregate as appropriate at the
1402
- // load implying that the value /must/ be fully consumed. Thus any leaking
1436
+ // load implying that the value /must/ be fully consumed. If we promoted a +0
1437
+ // value, we created dominating destroys along those paths. Thus any leaking
1403
1438
// blocks that we may have can be found by performing a linear lifetime check
1404
1439
// over all copies that we found using the load as the "consuming uses" (just
1405
1440
// for the purposes of identifying the consuming block).
1406
- auto *oldLoad = Agg .addMissingDestroysForCopiedValues (Load , newVal);
1441
+ auto *oldLoad = agg .addMissingDestroysForCopiedValues (load , newVal);
1407
1442
1408
1443
++NumLoadPromoted;
1409
1444
@@ -1412,8 +1447,14 @@ bool AllocOptimize::promoteLoad(SILInstruction *Inst) {
1412
1447
if (!oldLoad)
1413
1448
return true ;
1414
1449
1450
+ // If our load was a +0 value, borrow the value and the RAUW. We reuse the
1451
+ // end_borrows of our load_borrow.
1452
+ if (isa<LoadBorrowInst>(oldLoad)) {
1453
+ newVal = SILBuilderWithScope (oldLoad).createBeginBorrow (oldLoad->getLoc (),
1454
+ newVal);
1455
+ }
1415
1456
oldLoad->replaceAllUsesWith (newVal);
1416
- SILValue addr = oldLoad->getOperand ();
1457
+ SILValue addr = oldLoad->getOperand (0 );
1417
1458
oldLoad->eraseFromParent ();
1418
1459
if (auto *addrI = addr->getDefiningInstruction ())
1419
1460
recursivelyDeleteTriviallyDeadInstructions (addrI);
0 commit comments