Skip to content

Commit d836ca6

Browse files
committed
---
yaml --- r: 343279 b: refs/heads/master-rebranch c: 97d15f6 h: refs/heads/master i: 343277: 3434a0d 343275: d9e054d 343271: 7462f72 343263: f2ab040
1 parent 74e158b commit d836ca6

File tree

3 files changed

+134
-89
lines changed

3 files changed

+134
-89
lines changed

[refs]

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1455,7 +1455,7 @@ refs/tags/swift-DEVELOPMENT-SNAPSHOT-2019-08-02-a: ddd2b2976aa9bfde5f20fe37f6bd2
14551455
refs/tags/swift-DEVELOPMENT-SNAPSHOT-2019-08-03-a: 171cc166f2abeb5ca2a4003700a8a78a108bd300
14561456
refs/heads/benlangmuir-patch-1: baaebaf39d52f3bf36710d4fe40cf212e996b212
14571457
refs/heads/i-do-redeclare: 8c4e6d5de5c1e3f0a2cedccf319df713ea22c48e
1458-
refs/heads/master-rebranch: 0e3987a4fcba9753bf9c2f47c17126abe762ce23
1458+
refs/heads/master-rebranch: 97d15f6e0549284997a6a9975f2f2d809dc76d56
14591459
refs/heads/rdar-53901732: 9bd06af3284e18a109cdbf9aa59d833b24eeca7b
14601460
refs/heads/revert-26776-subst-always-returns-a-type: 1b8e18fdd391903a348970a4c848995d4cdd789c
14611461
refs/heads/tensorflow-merge: 8b854f62f80d4476cb383d43c4aac2001dde3cec

branches/master-rebranch/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp

Lines changed: 107 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -381,64 +381,93 @@ bool mayFunctionMutateArgument(const AccessedStorage &storage, SILFunction &f) {
381381
return true;
382382
}
383383

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;
386392

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+
399434
// We know a let property won't be written to if the base object is
400435
// 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+
405449
// A guaranteed argument trivially keeps the base alive for the duration of
406450
// the projection.
407451
if (auto *arg = dyn_cast<SILFunctionArgument>(baseObject)) {
408452
if (arg->getArgumentConvention().isGuaranteedConvention()) {
409-
return false;
453+
return answer(false);
410454
}
411455
}
412456

413457
// See if there's a borrow of the base object our load is based on.
414458
SILValue borrowInst;
415-
if (isa<BeginBorrowInst>(baseObject)
416-
|| isa<LoadBorrowInst>(baseObject)) {
459+
if (isa<LoadBorrowInst>(baseObject)) {
417460
borrowInst = baseObject;
418461
} 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;
435463
}
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+
437469
// Use the linear lifetime checker to check whether the copied
438470
// value is dominated by the lifetime of the borrow it's based on.
439-
if (!borrowInst)
440-
return true;
441-
442471
SmallVector<BranchPropagatedUser, 4> baseEndBorrows;
443472
for (auto *use : borrowInst->getUses()) {
444473
if (isa<EndBorrowInst>(use->getUser())) {
@@ -447,36 +476,52 @@ bool SemanticARCOptVisitor::isWrittenTo(LoadInst *load) {
447476
}
448477

449478
SmallVector<BranchPropagatedUser, 4> valueDestroys;
450-
for (auto *use : load->getUses()) {
479+
for (auto *use : Load->getUses()) {
451480
if (isa<DestroyValueInst>(use->getUser())) {
452481
valueDestroys.push_back(BranchPropagatedUser(use->getUser()));
453482
}
454483
}
455484

456485
SmallPtrSet<SILBasicBlock *, 4> visitedBlocks;
457486

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());
462492
}
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);
478497
}
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();
480525
}
481526

482527
// Convert a load [copy] from unique storage [read] that has all uses that can

branches/master-rebranch/test/SILOptimizer/semantic-arc-opts.sil

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -561,19 +561,19 @@ bb0(%x : @owned $ClassLet):
561561
}
562562

563563
// CHECK-LABEL: sil [ossa] @dont_copy_let_properties_with_multi_borrowed_base_that_dominates
564-
// TODO: [[OUTER:%.*]] = begin_borrow
565-
// TODO-NEXT: ref_element_addr
566-
// TODO-NEXT: [[INNER:%.*]] = load_borrow
567-
// TODO-NEXT: apply
568-
// TODO-NEXT: end_borrow [[INNER]]
569-
// TODO-NEXT: end_borrow [[OUTER]]
570-
// TODO-NEXT: [[OUTER:%.*]] = begin_borrow
571-
// TODO-NEXT: ref_element_addr
572-
// TODO-NEXT: [[INNER:%.*]] = load_borrow
573-
// TODO-NEXT: apply
574-
// TODO-NEXT: end_borrow [[INNER]]
575-
// TODO-NEXT: end_borrow [[OUTER]]
576-
// TODO-NEXT: destroy_value
564+
// CHECK: [[OUTER:%.*]] = begin_borrow
565+
// CHECK-NEXT: ref_element_addr
566+
// CHECK-NEXT: [[INNER:%.*]] = load_borrow
567+
// CHECK-NEXT: apply
568+
// CHECK-NEXT: end_borrow [[INNER]]
569+
// CHECK-NEXT: end_borrow [[OUTER]]
570+
// CHECK-NEXT: [[OUTER:%.*]] = begin_borrow
571+
// CHECK-NEXT: ref_element_addr
572+
// CHECK-NEXT: [[INNER:%.*]] = load_borrow
573+
// CHECK-NEXT: apply
574+
// CHECK-NEXT: end_borrow [[INNER]]
575+
// CHECK-NEXT: end_borrow [[OUTER]]
576+
// CHECK-NEXT: destroy_value
577577
sil [ossa] @dont_copy_let_properties_with_multi_borrowed_base_that_dominates : $@convention(thin) (@owned ClassLet) -> () {
578578
bb0(%x : @owned $ClassLet):
579579
%f = function_ref @black_hole : $@convention(thin) (@guaranteed Klass) -> ()
@@ -635,19 +635,19 @@ bb0(%x : @owned $ClassLet):
635635

636636
// CHECK-LABEL: sil [ossa] @do_or_dont_copy_let_properties_with_multi_borrowed_base_when_it_dominates
637637
// CHECK: [[OUTER:%.*]] = begin_borrow
638-
// TODO-NEXT: ref_element_addr
639-
// TODO-NEXT: [[INNER:%.*]] = load_borrow
640-
// TODO-NEXT: apply
641-
// TODO-NEXT: end_borrow [[INNER]]
642-
// TODO-NEXT: end_borrow [[OUTER]]
643-
// TODO-NEXT: begin_borrow
644-
// TODO-NEXT: ref_element_addr
645-
// TODO-NEXT: load [copy]
646-
// TODO-NEXT: apply
647-
// TODO-NEXT: end_borrow
648-
// TODO-NEXT: destroy_value
649-
// TODO-NEXT: apply
650-
// TODO-NEXT: destroy_value
638+
// CHECK-NEXT: ref_element_addr
639+
// CHECK-NEXT: [[INNER:%.*]] = load_borrow
640+
// CHECK-NEXT: apply
641+
// CHECK-NEXT: end_borrow [[INNER]]
642+
// CHECK-NEXT: end_borrow [[OUTER]]
643+
// CHECK-NEXT: begin_borrow
644+
// CHECK-NEXT: ref_element_addr
645+
// CHECK-NEXT: load [copy]
646+
// CHECK-NEXT: apply
647+
// CHECK-NEXT: end_borrow
648+
// CHECK-NEXT: destroy_value
649+
// CHECK-NEXT: apply
650+
// CHECK-NEXT: destroy_value
651651
sil [ossa] @do_or_dont_copy_let_properties_with_multi_borrowed_base_when_it_dominates : $@convention(thin) (@owned ClassLet) -> () {
652652
bb0(%x : @owned $ClassLet):
653653
%f = function_ref @black_hole : $@convention(thin) (@guaranteed Klass) -> ()

0 commit comments

Comments
 (0)