Skip to content

Commit 0aecebd

Browse files
committed
[semantic-arc-opts] load [copy] -> load_borrow if copy is completely enclosed in certain kinds of exclusive access scopes.
Specifically if we have a begin_access [read] or a begin_access [modify] that is never actually written to. In that case if we can prove that the load [copy] is completely within the exclusive access region, we will load_borrow. Example: ``` %0 = begin_access [read] ... %1 = load [copy] %0 ... destroy_value %1 end_access %0 ``` => ``` %0 = begin_access [read] ... %1 = load_borrow %0 ... end_borrow %1 end_access %0 ``` rdar://60064692
1 parent 04007f2 commit 0aecebd

File tree

2 files changed

+158
-2
lines changed

2 files changed

+158
-2
lines changed

lib/SILOptimizer/Transforms/SemanticARCOpts.cpp

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1089,8 +1089,40 @@ class StorageGuaranteesLoadVisitor
10891089
}
10901090

10911091
void visitNestedAccess(BeginAccessInst *access) {
1092-
// Look through nested accesses.
1093-
return next(access->getOperand());
1092+
// First see if we have read/modify. If we do not, just look through the
1093+
// nested access.
1094+
switch (access->getAccessKind()) {
1095+
case SILAccessKind::Init:
1096+
case SILAccessKind::Deinit:
1097+
return next(access->getOperand());
1098+
case SILAccessKind::Read:
1099+
case SILAccessKind::Modify:
1100+
break;
1101+
}
1102+
1103+
// Next check if our live range is completely in the begin/end access
1104+
// scope. If so, we may be able to use a load_borrow here!
1105+
SmallVector<Operand *, 8> endScopeUses;
1106+
transform(access->getEndAccesses(), std::back_inserter(endScopeUses),
1107+
[](EndAccessInst *eai) {
1108+
return &eai->getAllOperands()[0];
1109+
});
1110+
SmallPtrSet<SILBasicBlock *, 4> visitedBlocks;
1111+
LinearLifetimeChecker checker(visitedBlocks, ARCOpt.getDeadEndBlocks());
1112+
if (!checker.validateLifetime(access, endScopeUses,
1113+
liveRange.getDestroyingUses())) {
1114+
// If we fail the linear lifetime check, then just recur:
1115+
return next(access->getOperand());
1116+
}
1117+
1118+
// Otherwise, if we have read, then we are done!
1119+
if (access->getAccessKind() == SILAccessKind::Read) {
1120+
return answer(false);
1121+
}
1122+
1123+
// If we have a modify, check if our value is /ever/ written to. If it is
1124+
// never actually written to, then we convert to a load_borrow.
1125+
return answer(ARCOpt.isAddressWrittenToDefUseAnalysis(access));
10941126
}
10951127

10961128
void visitArgumentAccess(SILFunctionArgument *arg) {

test/SILOptimizer/semantic-arc-opts.sil

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1657,3 +1657,127 @@ bb3(%0c : @guaranteed $FakeOptional<ClassLet>):
16571657
%9999 = tuple()
16581658
return %9999 : $()
16591659
}
1660+
1661+
// CHECK-LABEL: sil [ossa] @loadcopy_to_loadborrow_from_read_access : $@convention(thin) (@guaranteed ClassLet) -> () {
1662+
// CHECK-NOT: load [copy]
1663+
// CHECK: load_borrow
1664+
// CHECK-NOT: load [copy]
1665+
// CHECK: } // end sil function 'loadcopy_to_loadborrow_from_read_access'
1666+
sil [ossa] @loadcopy_to_loadborrow_from_read_access : $@convention(thin) (@guaranteed ClassLet) -> () {
1667+
bb0(%0 : @guaranteed $ClassLet):
1668+
%1 = ref_element_addr %0 : $ClassLet, #ClassLet.aVar
1669+
%2 = begin_access [read] [dynamic] %1 : $*Klass
1670+
%3 = load [copy] %2 : $*Klass
1671+
%f = function_ref @guaranteed_klass_user : $@convention(thin) (@guaranteed Klass) -> ()
1672+
apply %f(%3) : $@convention(thin) (@guaranteed Klass) -> ()
1673+
destroy_value %3 : $Klass
1674+
end_access %2 : $*Klass
1675+
%9999 = tuple()
1676+
return %9999 : $()
1677+
}
1678+
1679+
// CHECK-LABEL: sil [ossa] @loadcopy_to_loadborrow_from_mut_access_without_writes : $@convention(thin) (@guaranteed ClassLet) -> () {
1680+
// CHECK-NOT: load [copy]
1681+
// CHECK: load_borrow
1682+
// CHECK-NOT: load [copy]
1683+
// CHECK: } // end sil function 'loadcopy_to_loadborrow_from_mut_access_without_writes'
1684+
sil [ossa] @loadcopy_to_loadborrow_from_mut_access_without_writes : $@convention(thin) (@guaranteed ClassLet) -> () {
1685+
bb0(%0 : @guaranteed $ClassLet):
1686+
%1 = ref_element_addr %0 : $ClassLet, #ClassLet.aVar
1687+
%2 = begin_access [modify] [dynamic] %1 : $*Klass
1688+
%3 = load [copy] %2 : $*Klass
1689+
%f = function_ref @guaranteed_klass_user : $@convention(thin) (@guaranteed Klass) -> ()
1690+
apply %f(%3) : $@convention(thin) (@guaranteed Klass) -> ()
1691+
destroy_value %3 : $Klass
1692+
end_access %2 : $*Klass
1693+
%9999 = tuple()
1694+
return %9999 : $()
1695+
}
1696+
1697+
// We can with time handle this case by proving that the destroy_addr is after
1698+
// the destroy_value.
1699+
//
1700+
// CHECK-LABEL: sil [ossa] @loadcopy_to_loadborrow_from_mut_access_with_writes : $@convention(thin) (@guaranteed ClassLet) -> () {
1701+
// CHECK-NOT: load_borrow
1702+
// CHECK: load [copy]
1703+
// CHECK-NOT: load_borrow
1704+
// CHECK: } // end sil function 'loadcopy_to_loadborrow_from_mut_access_with_writes'
1705+
sil [ossa] @loadcopy_to_loadborrow_from_mut_access_with_writes : $@convention(thin) (@guaranteed ClassLet) -> () {
1706+
bb0(%0 : @guaranteed $ClassLet):
1707+
%1 = ref_element_addr %0 : $ClassLet, #ClassLet.aVar
1708+
%2 = begin_access [modify] [dynamic] %1 : $*Klass
1709+
%3 = load [copy] %2 : $*Klass
1710+
%f = function_ref @guaranteed_klass_user : $@convention(thin) (@guaranteed Klass) -> ()
1711+
apply %f(%3) : $@convention(thin) (@guaranteed Klass) -> ()
1712+
destroy_value %3 : $Klass
1713+
destroy_addr %2 : $*Klass
1714+
end_access %2 : $*Klass
1715+
%9999 = tuple()
1716+
return %9999 : $()
1717+
}
1718+
1719+
// We will never be able to handle this unless we can hoist the copy before the
1720+
// destroy_addr. Once we have begin_borrows around all interior_pointers, we can
1721+
// handle this version.
1722+
//
1723+
// CHECK-LABEL: sil [ossa] @loadcopy_to_loadborrow_from_mut_access_with_writes_2 : $@convention(thin) (@guaranteed ClassLet) -> () {
1724+
// CHECK-NOT: load_borrow
1725+
// CHECK: load [copy]
1726+
// CHECK-NOT: load_borrow
1727+
// CHECK: } // end sil function 'loadcopy_to_loadborrow_from_mut_access_with_writes_2'
1728+
sil [ossa] @loadcopy_to_loadborrow_from_mut_access_with_writes_2 : $@convention(thin) (@guaranteed ClassLet) -> () {
1729+
bb0(%0 : @guaranteed $ClassLet):
1730+
%1 = ref_element_addr %0 : $ClassLet, #ClassLet.aVar
1731+
%2 = begin_access [modify] [dynamic] %1 : $*Klass
1732+
%3 = load [copy] %2 : $*Klass
1733+
%f = function_ref @guaranteed_klass_user : $@convention(thin) (@guaranteed Klass) -> ()
1734+
apply %f(%3) : $@convention(thin) (@guaranteed Klass) -> ()
1735+
destroy_addr %2 : $*Klass
1736+
destroy_value %3 : $Klass
1737+
end_access %2 : $*Klass
1738+
%9999 = tuple()
1739+
return %9999 : $()
1740+
}
1741+
1742+
// We will never be able to handle this since we can't hoist the destroy_value
1743+
// before the guaranteed_klass_user.
1744+
//
1745+
// CHECK-LABEL: sil [ossa] @loadcopy_to_loadborrow_from_mut_access_with_writes_3 : $@convention(thin) (@guaranteed ClassLet) -> () {
1746+
// CHECK-NOT: load_borrow
1747+
// CHECK: load [copy]
1748+
// CHECK-NOT: load_borrow
1749+
// CHECK: } // end sil function 'loadcopy_to_loadborrow_from_mut_access_with_writes_3'
1750+
sil [ossa] @loadcopy_to_loadborrow_from_mut_access_with_writes_3 : $@convention(thin) (@guaranteed ClassLet) -> () {
1751+
bb0(%0 : @guaranteed $ClassLet):
1752+
%1 = ref_element_addr %0 : $ClassLet, #ClassLet.aVar
1753+
%2 = begin_access [modify] [dynamic] %1 : $*Klass
1754+
%3 = load [copy] %2 : $*Klass
1755+
destroy_addr %2 : $*Klass
1756+
%f = function_ref @guaranteed_klass_user : $@convention(thin) (@guaranteed Klass) -> ()
1757+
apply %f(%3) : $@convention(thin) (@guaranteed Klass) -> ()
1758+
destroy_value %3 : $Klass
1759+
end_access %2 : $*Klass
1760+
%9999 = tuple()
1761+
return %9999 : $()
1762+
}
1763+
1764+
// We will never be able to handle this since the end_access is before the use
1765+
// of %3, so we can not form a long enough load_borrow.
1766+
//
1767+
// CHECK-LABEL: sil [ossa] @loadcopy_to_loadborrow_from_mut_access_with_writes_4 : $@convention(thin) (@guaranteed ClassLet) -> () {
1768+
// CHECK-NOT: load_borrow
1769+
// CHECK: load [copy]
1770+
// CHECK-NOT: load_borrow
1771+
// CHECK: } // end sil function 'loadcopy_to_loadborrow_from_mut_access_with_writes_4'
1772+
sil [ossa] @loadcopy_to_loadborrow_from_mut_access_with_writes_4 : $@convention(thin) (@guaranteed ClassLet) -> () {
1773+
bb0(%0 : @guaranteed $ClassLet):
1774+
%1 = ref_element_addr %0 : $ClassLet, #ClassLet.aVar
1775+
%2 = begin_access [modify] [dynamic] %1 : $*Klass
1776+
%3 = load [copy] %2 : $*Klass
1777+
end_access %2 : $*Klass
1778+
%f = function_ref @guaranteed_klass_user : $@convention(thin) (@guaranteed Klass) -> ()
1779+
apply %f(%3) : $@convention(thin) (@guaranteed Klass) -> ()
1780+
destroy_value %3 : $Klass
1781+
%9999 = tuple()
1782+
return %9999 : $()
1783+
}

0 commit comments

Comments
 (0)