13
13
#define DEBUG_TYPE " predictable-memopt"
14
14
15
15
#include " PMOMemoryUseCollector.h"
16
+ #include " swift/SIL/BasicBlockUtils.h"
17
+ #include " swift/SIL/BranchPropagatedUser.h"
18
+ #include " swift/SIL/OwnershipUtils.h"
16
19
#include " swift/SIL/SILBuilder.h"
17
20
#include " swift/SILOptimizer/PassManager/Passes.h"
18
21
#include " swift/SILOptimizer/PassManager/Transforms.h"
@@ -390,13 +393,22 @@ class AvailableValueAggregator {
390
393
SILLocation Loc;
391
394
MutableArrayRef<AvailableValue> AvailableValueList;
392
395
SmallVectorImpl<PMOMemoryUse> &Uses;
396
+ DeadEndBlocks &deadEndBlocks;
397
+
398
+ // / Keep track of all instructions that we have added. Once we are done
399
+ // / promoting a value, we need to make sure that if we need to balance any
400
+ // / copies (to avoid leaks), we do so. This is not used if we are performing a
401
+ // / take.
402
+ SmallVector<SILInstruction *, 16 > insertedInsts;
393
403
394
404
public:
395
405
AvailableValueAggregator (SILInstruction *Inst,
396
406
MutableArrayRef<AvailableValue> AvailableValueList,
397
- SmallVectorImpl<PMOMemoryUse> &Uses)
407
+ SmallVectorImpl<PMOMemoryUse> &Uses,
408
+ DeadEndBlocks &deadEndBlocks)
398
409
: M(Inst->getModule ()), B(Inst), Loc(Inst->getLoc ()),
399
- AvailableValueList(AvailableValueList), Uses(Uses) {}
410
+ AvailableValueList(AvailableValueList), Uses(Uses),
411
+ deadEndBlocks(deadEndBlocks) {}
400
412
401
413
// This is intended to be passed by reference only once constructed.
402
414
AvailableValueAggregator (const AvailableValueAggregator &) = delete;
@@ -406,10 +418,10 @@ class AvailableValueAggregator {
406
418
AvailableValueAggregator &operator =(AvailableValueAggregator &&) = delete ;
407
419
408
420
SILValue aggregateValues (SILType LoadTy, SILValue Address, unsigned FirstElt);
421
+ void addMissingDestroysForCopiedValues (LoadInst *li);
409
422
410
423
void print (llvm::raw_ostream &os) const ;
411
424
void dump () const LLVM_ATTRIBUTE_USED;
412
-
413
425
private:
414
426
SILValue aggregateFullyAvailableValue (SILType LoadTy, unsigned FirstElt);
415
427
SILValue aggregateTupleSubElts (TupleType *TT, SILType LoadTy,
@@ -493,7 +505,7 @@ AvailableValueAggregator::aggregateFullyAvailableValue(SILType loadTy,
493
505
ArrayRef<StoreInst *> insertPts = firstVal.getInsertionPoints ();
494
506
if (insertPts.size () == 1 ) {
495
507
// Use the scope and location of the store at the insertion point.
496
- SILBuilderWithScope builder (insertPts[0 ]);
508
+ SILBuilderWithScope builder (insertPts[0 ], &insertedInsts );
497
509
SILLocation loc = insertPts[0 ]->getLoc ();
498
510
return builder.emitCopyValueOperation (loc, firstVal.getValue ());
499
511
}
@@ -506,7 +518,7 @@ AvailableValueAggregator::aggregateFullyAvailableValue(SILType loadTy,
506
518
updater.Initialize (loadTy);
507
519
for (auto *insertPt : firstVal.getInsertionPoints ()) {
508
520
// Use the scope and location of the store at the insertion point.
509
- SILBuilderWithScope builder (insertPt);
521
+ SILBuilderWithScope builder (insertPt, &insertedInsts );
510
522
SILLocation loc = insertPt->getLoc ();
511
523
SILValue eltVal = builder.emitCopyValueOperation (loc, firstVal.getValue ());
512
524
updater.AddAvailableValue (insertPt->getParent (), eltVal);
@@ -568,62 +580,109 @@ SILValue AvailableValueAggregator::aggregateStructSubElts(StructDecl *SD,
568
580
// We have looked through all of the aggregate values and finally found a
569
581
// "primitive value". If the value is available, use it (extracting if we need
570
582
// to), otherwise emit a load of the value with the appropriate qualifier.
571
- SILValue AvailableValueAggregator::handlePrimitiveValue (SILType LoadTy ,
572
- SILValue Address ,
573
- unsigned FirstElt ) {
574
- auto &Val = AvailableValueList[FirstElt ];
583
+ SILValue AvailableValueAggregator::handlePrimitiveValue (SILType loadTy ,
584
+ SILValue address ,
585
+ unsigned firstElt ) {
586
+ auto &val = AvailableValueList[firstElt ];
575
587
576
588
// If the value is not available, load the value and update our use list.
577
- if (!Val ) {
578
- LoadInst *Load = ([&]() {
589
+ if (!val ) {
590
+ LoadInst *load = ([&]() {
579
591
if (B.hasOwnership ()) {
580
- return B.createTrivialLoadOr (Loc, Address ,
592
+ return B.createTrivialLoadOr (Loc, address ,
581
593
LoadOwnershipQualifier::Copy);
582
594
}
583
- return B.createLoad (Loc, Address , LoadOwnershipQualifier::Unqualified);
595
+ return B.createLoad (Loc, address , LoadOwnershipQualifier::Unqualified);
584
596
}());
585
- Uses.emplace_back (Load , PMOUseKind::Load);
586
- return Load ;
597
+ Uses.emplace_back (load , PMOUseKind::Load);
598
+ return load ;
587
599
}
588
600
589
601
// If we have 1 insertion point, just extract the value and return.
590
602
//
591
603
// This saves us from having to spend compile time in the SSA updater in this
592
604
// case.
593
- ArrayRef<StoreInst *> InsertPts = Val .getInsertionPoints ();
594
- if (InsertPts .size () == 1 ) {
605
+ ArrayRef<StoreInst *> insertPts = val .getInsertionPoints ();
606
+ if (insertPts .size () == 1 ) {
595
607
// Use the scope and location of the store at the insertion point.
596
- SILBuilderWithScope Builder (InsertPts [0 ]);
597
- SILLocation Loc = InsertPts [0 ]->getLoc ();
598
- SILValue EltVal = nonDestructivelyExtractSubElement (Val, Builder, Loc );
608
+ SILBuilderWithScope builder (insertPts [0 ], &insertedInsts );
609
+ SILLocation loc = insertPts [0 ]->getLoc ();
610
+ SILValue eltVal = nonDestructivelyExtractSubElement (val, builder, loc );
599
611
assert (
600
- !Builder .hasOwnership () ||
601
- EltVal .getOwnershipKind ().isCompatibleWith (ValueOwnershipKind::Owned));
602
- assert (EltVal ->getType () == LoadTy && " Subelement types mismatch" );
603
- return EltVal ;
612
+ !builder .hasOwnership () ||
613
+ eltVal .getOwnershipKind ().isCompatibleWith (ValueOwnershipKind::Owned));
614
+ assert (eltVal ->getType () == loadTy && " Subelement types mismatch" );
615
+ return eltVal ;
604
616
}
605
617
606
618
// If we have an available value, then we want to extract the subelement from
607
619
// the borrowed aggregate before each insertion point.
608
- SILSSAUpdater Updater (B.getModule ());
609
- Updater.Initialize (LoadTy);
610
- for (auto *I : Val.getInsertionPoints ()) {
620
+ SILSSAUpdater updater (B.getModule ());
621
+ updater.Initialize (loadTy);
622
+
623
+ for (auto *i : insertPts) {
611
624
// Use the scope and location of the store at the insertion point.
612
- SILBuilderWithScope Builder (I );
613
- SILLocation Loc = I ->getLoc ();
614
- SILValue EltVal = nonDestructivelyExtractSubElement (Val, Builder, Loc );
625
+ SILBuilderWithScope builder (i, &insertedInsts );
626
+ SILLocation loc = i ->getLoc ();
627
+ SILValue eltVal = nonDestructivelyExtractSubElement (val, builder, loc );
615
628
assert (
616
- !Builder.hasOwnership () ||
617
- EltVal.getOwnershipKind ().isCompatibleWith (ValueOwnershipKind::Owned));
618
- Updater.AddAvailableValue (I->getParent (), EltVal);
629
+ !builder.hasOwnership () ||
630
+ eltVal.getOwnershipKind ().isCompatibleWith (ValueOwnershipKind::Owned));
631
+
632
+ updater.AddAvailableValue (i->getParent (), eltVal);
619
633
}
620
634
621
635
// Finally, grab the value from the SSA updater.
622
- SILValue EltVal = Updater .GetValueInMiddleOfBlock (B.getInsertionBB ());
636
+ SILValue eltVal = updater .GetValueInMiddleOfBlock (B.getInsertionBB ());
623
637
assert (!B.hasOwnership () ||
624
- EltVal.getOwnershipKind ().isCompatibleWith (ValueOwnershipKind::Owned));
625
- assert (EltVal->getType () == LoadTy && " Subelement types mismatch" );
626
- return EltVal;
638
+ eltVal.getOwnershipKind ().isCompatibleWith (ValueOwnershipKind::Owned));
639
+ assert (eltVal->getType () == loadTy && " Subelement types mismatch" );
640
+ return eltVal;
641
+ }
642
+
643
+ void AvailableValueAggregator::addMissingDestroysForCopiedValues (LoadInst *li) {
644
+ // If ownership is not enabled... bail. We do not need to do this since we do
645
+ // not need to insert an extra copy unless we have ownership since without
646
+ // ownership stores do not consume.
647
+ if (!B.hasOwnership ())
648
+ return ;
649
+
650
+ SmallVector<BranchPropagatedUser, 1 > consumingUses;
651
+ SmallPtrSet<SILBasicBlock *, 8 > visitedBlocks;
652
+ SmallVector<SILBasicBlock *, 8 > leakingBlocks;
653
+ while (!insertedInsts.empty ()) {
654
+ auto *cvi = dyn_cast<CopyValueInst>(insertedInsts.pop_back_val ());
655
+ if (!cvi)
656
+ continue ;
657
+
658
+ // Clear our worklist.
659
+ consumingUses.clear ();
660
+ visitedBlocks.clear ();
661
+ leakingBlocks.clear ();
662
+
663
+ // The linear lifetime checker doesn't care if the passed in load is
664
+ // actually a user of our copy_value. What we care about is that the load is
665
+ // guaranteed to be in the block where we have reformed the tuple in a
666
+ // consuming manner. This means if we add it as the consuming use of the
667
+ // copy, we can find the leaking places if any exist.
668
+ consumingUses.push_back (li);
669
+
670
+ // Then perform the linear lifetime check. If we succeed, continue. We have
671
+ // no further work to do.
672
+ auto errorKind =
673
+ ownership::ErrorBehaviorKind::ReturnFalseOnLeakAssertOtherwise;
674
+ if (valueHasLinearLifetime (cvi, consumingUses, {}, visitedBlocks,
675
+ deadEndBlocks, errorKind, &leakingBlocks))
676
+ continue ;
677
+
678
+ // Ok, we found some leaking blocks. Insert destroys at the
679
+ // beginning of these blocks for our copy_value.
680
+ auto loc = RegularLocation::getAutoGeneratedLocation ();
681
+ for (auto *bb : leakingBlocks) {
682
+ SILBuilderWithScope b (bb->begin ());
683
+ b.emitDestroyValueOperation (loc, cvi);
684
+ }
685
+ }
627
686
}
628
687
629
688
// ===----------------------------------------------------------------------===//
@@ -1091,16 +1150,19 @@ class AllocOptimize {
1091
1150
SmallVectorImpl<PMOMemoryUse> &Uses;
1092
1151
SmallVectorImpl<SILInstruction *> &Releases;
1093
1152
1153
+ DeadEndBlocks &deadEndBlocks;
1154
+
1094
1155
// / A structure that we use to compute our available values.
1095
1156
AvailableValueDataflowContext DataflowContext;
1096
1157
1097
1158
public:
1098
1159
AllocOptimize (AllocationInst *memory, SmallVectorImpl<PMOMemoryUse> &uses,
1099
- SmallVectorImpl<SILInstruction *> &releases)
1160
+ SmallVectorImpl<SILInstruction *> &releases,
1161
+ DeadEndBlocks &deadEndBlocks)
1100
1162
: Module(memory->getModule ()), TheMemory(memory),
1101
1163
MemoryType(getMemoryType(memory)),
1102
1164
NumMemorySubElements(getNumSubElements(MemoryType, Module)), Uses(uses),
1103
- Releases(releases),
1165
+ Releases(releases), deadEndBlocks(deadEndBlocks),
1104
1166
DataflowContext(TheMemory, NumMemorySubElements, uses) {}
1105
1167
1106
1168
bool optimizeMemoryAccesses ();
@@ -1202,9 +1264,17 @@ bool AllocOptimize::promoteLoad(SILInstruction *Inst) {
1202
1264
// type as the load did, and emit smaller loads for any subelements that were
1203
1265
// not available.
1204
1266
auto *Load = cast<LoadInst>(Inst);
1205
- AvailableValueAggregator Agg (Load, AvailableValues, Uses);
1267
+ AvailableValueAggregator Agg (Load, AvailableValues, Uses, deadEndBlocks );
1206
1268
SILValue NewVal = Agg.aggregateValues (LoadTy, Load->getOperand (), FirstElt);
1207
1269
1270
+ // If we inserted any copies, we created the copies at our stores. We know
1271
+ // that in our load block, we will reform the aggregate as appropriate at the
1272
+ // load implying that the value /must/ be fully consumed. Thus any leaking
1273
+ // blocks that we may have can be found by performing a linear lifetime check
1274
+ // over all copies that we found using the load as the "consuming uses" (just
1275
+ // for the purposes of identifying the consuming block).
1276
+ Agg.addMissingDestroysForCopiedValues (Load);
1277
+
1208
1278
++NumLoadPromoted;
1209
1279
1210
1280
// Simply replace the load.
@@ -1283,7 +1353,7 @@ void AllocOptimize::promoteDestroyAddr(
1283
1353
// Aggregate together all of the subelements into something that has the same
1284
1354
// type as the load did, and emit smaller) loads for any subelements that were
1285
1355
// not available.
1286
- AvailableValueAggregator Agg (DAI, AvailableValues, Uses);
1356
+ AvailableValueAggregator Agg (DAI, AvailableValues, Uses, deadEndBlocks );
1287
1357
SILValue NewVal = Agg.aggregateValues (LoadTy, Address, FirstElt);
1288
1358
1289
1359
++NumDestroyAddrPromoted;
@@ -1493,6 +1563,8 @@ static AllocationInst *getOptimizableAllocation(SILInstruction *i) {
1493
1563
1494
1564
static bool optimizeMemoryAccesses (SILFunction &fn) {
1495
1565
bool changed = false ;
1566
+ DeadEndBlocks deadEndBlocks (&fn);
1567
+
1496
1568
for (auto &bb : fn) {
1497
1569
auto i = bb.begin (), e = bb.end ();
1498
1570
while (i != e) {
@@ -1519,7 +1591,7 @@ static bool optimizeMemoryAccesses(SILFunction &fn) {
1519
1591
continue ;
1520
1592
}
1521
1593
1522
- AllocOptimize allocOptimize (alloc, uses, destroys);
1594
+ AllocOptimize allocOptimize (alloc, uses, destroys, deadEndBlocks );
1523
1595
changed |= allocOptimize.optimizeMemoryAccesses ();
1524
1596
1525
1597
// Move onto the next instruction. We know this is safe since we do not
@@ -1533,6 +1605,8 @@ static bool optimizeMemoryAccesses(SILFunction &fn) {
1533
1605
1534
1606
static bool eliminateDeadAllocations (SILFunction &fn) {
1535
1607
bool changed = false ;
1608
+ DeadEndBlocks deadEndBlocks (&fn);
1609
+
1536
1610
for (auto &bb : fn) {
1537
1611
auto i = bb.begin (), e = bb.end ();
1538
1612
while (i != e) {
@@ -1560,7 +1634,7 @@ static bool eliminateDeadAllocations(SILFunction &fn) {
1560
1634
continue ;
1561
1635
}
1562
1636
1563
- AllocOptimize allocOptimize (alloc, uses, destroys);
1637
+ AllocOptimize allocOptimize (alloc, uses, destroys, deadEndBlocks );
1564
1638
changed |= allocOptimize.tryToRemoveDeadAllocation ();
1565
1639
1566
1640
// Move onto the next instruction. We know this is safe since we do not
0 commit comments