@@ -381,64 +381,93 @@ bool mayFunctionMutateArgument(const AccessedStorage &storage, SILFunction &f) {
381
381
return true ;
382
382
}
383
383
384
- bool SemanticARCOptVisitor::isWrittenTo (LoadInst *load) {
385
- auto addr = load->getOperand ();
384
+ // Then find our accessed storage to determine whether it provides a guarantee
385
+ // for the loaded value.
386
+ namespace {
387
+ class StorageGuaranteesLoadVisitor
388
+ : public AccessUseDefChainVisitor<StorageGuaranteesLoadVisitor>
389
+ {
390
+ // The outer SemanticARCOptVisitor.
391
+ SemanticARCOptVisitor &ARCOpt;
386
392
387
- // Then find our accessed storage. If we can not find anything, be
388
- // conservative and assume that the value is written to.
389
- const auto &storage = findAccessedStorageNonNested (addr);
390
- if (!storage)
391
- return true ;
392
-
393
- // Then see if we ever write to this address in a flow insensitive
394
- // way (ignoring stores that are obviously the only initializer to
395
- // memory). We have to do this since load_borrow assumes that the
396
- // underlying memory is never written to.
397
- switch (storage.getKind ()) {
398
- case AccessedStorage::Class: {
393
+ // The original load instruction.
394
+ LoadInst *Load;
395
+
396
+ // The current address being visited.
397
+ SILValue currentAddress;
398
+
399
+ Optional<bool > isWritten;
400
+
401
+ public:
402
+ StorageGuaranteesLoadVisitor (SemanticARCOptVisitor &arcOpt, LoadInst *load)
403
+ : ARCOpt(arcOpt), Load(load), currentAddress(load->getOperand ())
404
+ {}
405
+
406
+ void answer (bool written) {
407
+ currentAddress = nullptr ;
408
+ isWritten = written;
409
+ }
410
+
411
+ void next (SILValue address) {
412
+ currentAddress = address;
413
+ }
414
+
415
+ void visitNestedAccess (BeginAccessInst *access) {
416
+ // Look through nested accesses.
417
+ return next (access->getOperand ());
418
+ }
419
+
420
+ void visitArgumentAccess (SILFunctionArgument *arg) {
421
+ return answer (mayFunctionMutateArgument (
422
+ AccessedStorage (arg, AccessedStorage::Argument),
423
+ ARCOpt.F ));
424
+ }
425
+
426
+ void visitGlobalAccess (SILValue global) {
427
+ return answer (!AccessedStorage (global, AccessedStorage::Global)
428
+ .isLetAccess (&ARCOpt.F ));
429
+ }
430
+
431
+ void visitClassAccess (RefElementAddrInst *field) {
432
+ currentAddress = nullptr ;
433
+
399
434
// We know a let property won't be written to if the base object is
400
435
// guaranteed for the duration of the access.
401
- if (!storage.isLetAccess (&F))
402
- return true ;
403
-
404
- auto baseObject = stripCasts (storage.getObject ());
436
+ // For non-let properties conservatively assume they may be written to.
437
+ if (!field->getField ()->isLet ()) {
438
+ return answer (true );
439
+ }
440
+
441
+ // The lifetime of the `let` is guaranteed if it's dominated by the
442
+ // guarantee on the base. Check for a borrow.
443
+ SILValue baseObject = field->getOperand ();
444
+ auto beginBorrow = dyn_cast<BeginBorrowInst>(baseObject);
445
+ if (beginBorrow)
446
+ baseObject = beginBorrow->getOperand ();
447
+ baseObject = stripCasts (baseObject);
448
+
405
449
// A guaranteed argument trivially keeps the base alive for the duration of
406
450
// the projection.
407
451
if (auto *arg = dyn_cast<SILFunctionArgument>(baseObject)) {
408
452
if (arg->getArgumentConvention ().isGuaranteedConvention ()) {
409
- return false ;
453
+ return answer ( false ) ;
410
454
}
411
455
}
412
456
413
457
// See if there's a borrow of the base object our load is based on.
414
458
SILValue borrowInst;
415
- if (isa<BeginBorrowInst>(baseObject)
416
- || isa<LoadBorrowInst>(baseObject)) {
459
+ if (isa<LoadBorrowInst>(baseObject)) {
417
460
borrowInst = baseObject;
418
461
} 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;
462
+ borrowInst = beginBorrow;
435
463
}
436
-
464
+ // TODO: We could also look at a guaranteed phi argument and see whether
465
+ // the loaded copy is dominated by it.
466
+ if (!borrowInst)
467
+ return answer (true );
468
+
437
469
// Use the linear lifetime checker to check whether the copied
438
470
// value is dominated by the lifetime of the borrow it's based on.
439
- if (!borrowInst)
440
- return true ;
441
-
442
471
SmallVector<BranchPropagatedUser, 4 > baseEndBorrows;
443
472
for (auto *use : borrowInst->getUses ()) {
444
473
if (isa<EndBorrowInst>(use->getUser ())) {
@@ -447,36 +476,52 @@ bool SemanticARCOptVisitor::isWrittenTo(LoadInst *load) {
447
476
}
448
477
449
478
SmallVector<BranchPropagatedUser, 4 > valueDestroys;
450
- for (auto *use : load ->getUses ()) {
479
+ for (auto *use : Load ->getUses ()) {
451
480
if (isa<DestroyValueInst>(use->getUser ())) {
452
481
valueDestroys.push_back (BranchPropagatedUser (use->getUser ()));
453
482
}
454
483
}
455
484
456
485
SmallPtrSet<SILBasicBlock *, 4 > visitedBlocks;
457
486
458
- return valueHasLinearLifetime (baseObject, baseEndBorrows, valueDestroys,
459
- visitedBlocks, getDeadEndBlocks (),
460
- ownership::ErrorBehaviorKind::ReturnFalse)
461
- .getFoundError ();
487
+ auto result = valueHasLinearLifetime (baseObject, baseEndBorrows,
488
+ valueDestroys, visitedBlocks,
489
+ ARCOpt.getDeadEndBlocks (),
490
+ ownership::ErrorBehaviorKind::ReturnFalse);
491
+ return answer (result.getFoundError ());
462
492
}
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
-
469
- case AccessedStorage::Box:
470
- case AccessedStorage::Stack:
471
- case AccessedStorage::Yield:
472
- case AccessedStorage::Nested:
473
- case AccessedStorage::Unidentified:
474
- return true ;
475
-
476
- case AccessedStorage::Argument:
477
- return mayFunctionMutateArgument (storage, F);
493
+
494
+ // TODO: Handle other access kinds?
495
+ void visitBase (SILValue base, AccessedStorage::Kind kind) {
496
+ return answer (true );
478
497
}
479
- llvm_unreachable (" covered switch" );
498
+
499
+ void visitNonAccess (SILValue addr) {
500
+ return answer (true );
501
+ }
502
+
503
+ void visitIncomplete (SILValue projectedAddr, SILValue parentAddr) {
504
+ return next (parentAddr);
505
+ }
506
+
507
+ void visitPhi (SILPhiArgument *phi) {
508
+ // We shouldn't have address phis in OSSA SIL, so we don't need to recur
509
+ // through the predecessors here.
510
+ return answer (true );
511
+ }
512
+
513
+ bool doIt () {
514
+ while (currentAddress) {
515
+ visit (currentAddress);
516
+ }
517
+ return *isWritten;
518
+ }
519
+ };
520
+ } // namespace
521
+
522
+ bool SemanticARCOptVisitor::isWrittenTo (LoadInst *load) {
523
+ StorageGuaranteesLoadVisitor visitor (*this , load);
524
+ return visitor.doIt ();
480
525
}
481
526
482
527
// Convert a load [copy] from unique storage [read] that has all uses that can
0 commit comments