13
13
#define DEBUG_TYPE " sil-semantic-arc-opts"
14
14
#include " swift/Basic/STLExtras.h"
15
15
#include " swift/SIL/BasicBlockUtils.h"
16
+ #include " swift/SIL/BranchPropagatedUser.h"
16
17
#include " swift/SIL/MemAccessUtils.h"
17
18
#include " swift/SIL/OwnershipUtils.h"
18
19
#include " swift/SIL/SILArgument.h"
@@ -122,10 +123,23 @@ namespace {
122
123
123
124
struct SemanticARCOptVisitor
124
125
: SILInstructionVisitor<SemanticARCOptVisitor, bool > {
126
+ SILFunction &F;
127
+ Optional<DeadEndBlocks> TheDeadEndBlocks;
128
+
129
+ explicit SemanticARCOptVisitor (SILFunction &F) : F(F) {}
130
+
131
+ DeadEndBlocks &getDeadEndBlocks () {
132
+ if (!TheDeadEndBlocks)
133
+ TheDeadEndBlocks.emplace (&F);
134
+ return *TheDeadEndBlocks;
135
+ }
136
+
125
137
bool visitSILInstruction (SILInstruction *i) { return false ; }
126
138
bool visitCopyValueInst (CopyValueInst *cvi);
127
139
bool visitBeginBorrowInst (BeginBorrowInst *bbi);
128
140
bool visitLoadInst (LoadInst *li);
141
+
142
+ bool isWrittenTo (LoadInst *li);
129
143
};
130
144
131
145
} // end anonymous namespace
@@ -367,10 +381,12 @@ bool mayFunctionMutateArgument(const AccessedStorage &storage, SILFunction &f) {
367
381
return true ;
368
382
}
369
383
370
- static bool isWrittenTo (SILFunction &f, SILValue value) {
384
+ bool SemanticARCOptVisitor::isWrittenTo (LoadInst *load) {
385
+ auto addr = load->getOperand ();
386
+
371
387
// Then find our accessed storage. If we can not find anything, be
372
388
// conservative and assume that the value is written to.
373
- const auto &storage = findAccessedStorageNonNested (value );
389
+ const auto &storage = findAccessedStorageNonNested (addr );
374
390
if (!storage)
375
391
return true ;
376
392
@@ -379,17 +395,86 @@ static bool isWrittenTo(SILFunction &f, SILValue value) {
379
395
// memory). We have to do this since load_borrow assumes that the
380
396
// underlying memory is never written to.
381
397
switch (storage.getKind ()) {
398
+ case AccessedStorage::Class: {
399
+ // We know a let property won't be written to if the base object is
400
+ // guaranteed for the duration of the access.
401
+ if (!storage.isLetAccess (&F))
402
+ return true ;
403
+
404
+ auto baseObject = stripCasts (storage.getObject ());
405
+ // A guaranteed argument trivially keeps the base alive for the duration of
406
+ // the projection.
407
+ if (auto *arg = dyn_cast<SILFunctionArgument>(baseObject)) {
408
+ if (arg->getArgumentConvention ().isGuaranteedConvention ()) {
409
+ return false ;
410
+ }
411
+ }
412
+
413
+ // See if there's a borrow of the base object our load is based on.
414
+ SILValue borrowInst;
415
+ if (isa<BeginBorrowInst>(baseObject)
416
+ || isa<LoadBorrowInst>(baseObject)) {
417
+ borrowInst = baseObject;
418
+ } else {
419
+ // TODO: We should walk the projection path again to get to the
420
+ // originating borrow, if any
421
+
422
+ BeginBorrowInst *singleBorrow = nullptr ;
423
+ for (auto *use : baseObject->getUses ()) {
424
+ if (auto *borrow = dyn_cast<BeginBorrowInst>(use->getUser ())) {
425
+ if (!singleBorrow) {
426
+ singleBorrow = borrow;
427
+ } else {
428
+ singleBorrow = nullptr ;
429
+ break ;
430
+ }
431
+ }
432
+ }
433
+
434
+ borrowInst = singleBorrow;
435
+ }
436
+
437
+ // Use the linear lifetime checker to check whether the copied
438
+ // value is dominated by the lifetime of the borrow it's based on.
439
+ if (!borrowInst)
440
+ return true ;
441
+
442
+ SmallVector<BranchPropagatedUser, 4 > baseEndBorrows;
443
+ for (auto *use : borrowInst->getUses ()) {
444
+ if (isa<EndBorrowInst>(use->getUser ())) {
445
+ baseEndBorrows.push_back (BranchPropagatedUser (use->getUser ()));
446
+ }
447
+ }
448
+
449
+ SmallVector<BranchPropagatedUser, 4 > valueDestroys;
450
+ for (auto *use : load->getUses ()) {
451
+ if (isa<DestroyValueInst>(use->getUser ())) {
452
+ valueDestroys.push_back (BranchPropagatedUser (use->getUser ()));
453
+ }
454
+ }
455
+
456
+ SmallPtrSet<SILBasicBlock *, 4 > visitedBlocks;
457
+
458
+ return valueHasLinearLifetime (baseObject, baseEndBorrows, valueDestroys,
459
+ visitedBlocks, getDeadEndBlocks (),
460
+ ownership::ErrorBehaviorKind::ReturnFalse)
461
+ .getFoundError ();
462
+ }
463
+ case AccessedStorage::Global:
464
+ // Any legal load of a global let should have happened after its
465
+ // initialization, at which point it can't be written to again for the
466
+ // lifetime of the program.
467
+ return !storage.isLetAccess (&F);
468
+
382
469
case AccessedStorage::Box:
383
470
case AccessedStorage::Stack:
384
- case AccessedStorage::Global:
385
- case AccessedStorage::Class:
386
471
case AccessedStorage::Yield:
387
472
case AccessedStorage::Nested:
388
473
case AccessedStorage::Unidentified:
389
474
return true ;
390
-
475
+
391
476
case AccessedStorage::Argument:
392
- return mayFunctionMutateArgument (storage, f );
477
+ return mayFunctionMutateArgument (storage, F );
393
478
}
394
479
llvm_unreachable (" covered switch" );
395
480
}
@@ -413,7 +498,7 @@ bool SemanticARCOptVisitor::visitLoadInst(LoadInst *li) {
413
498
414
499
// Then check if our address is ever written to. If it is, then we
415
500
// can not use the load_borrow.
416
- if (isWrittenTo (*li-> getFunction (), li-> getOperand () ))
501
+ if (isWrittenTo (li ))
417
502
return false ;
418
503
419
504
// Ok, we can perform our optimization. Convert the load [copy] into a
@@ -462,6 +547,9 @@ struct SemanticARCOpts : SILFunctionTransform {
462
547
//
463
548
// FIXME: Should we iterate or use a RPOT order here?
464
549
bool madeChange = false ;
550
+
551
+ SemanticARCOptVisitor visitor (f);
552
+
465
553
for (auto &bb : f) {
466
554
auto ii = bb.rend ();
467
555
auto start = bb.rbegin ();
@@ -482,7 +570,7 @@ struct SemanticARCOpts : SILFunctionTransform {
482
570
// ii. Move ii from the next value back onto the instruction
483
571
// after ii's old value in the block instruction list and then
484
572
// process that.
485
- if (SemanticARCOptVisitor () .visit (&*ii)) {
573
+ if (visitor .visit (&*ii)) {
486
574
madeChange = true ;
487
575
ii = std::prev (tmp);
488
576
continue ;
@@ -493,7 +581,7 @@ struct SemanticARCOpts : SILFunctionTransform {
493
581
}
494
582
495
583
// Finally visit the first instruction of the block.
496
- madeChange |= SemanticARCOptVisitor () .visit (&*ii);
584
+ madeChange |= visitor .visit (&*ii);
497
585
}
498
586
499
587
if (madeChange) {
0 commit comments