mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Merge pull request #30227 from gottesmm/pr-d0ba712c9532bbdd21033e19354f8fb6b3bb253d
[semantic-arc-opts] load [copy] -> load_borrow if copy is completely enclosed in certain kinds of exclusive access scopes.
This commit is contained in:
@@ -1089,8 +1089,40 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void visitNestedAccess(BeginAccessInst *access) {
|
void visitNestedAccess(BeginAccessInst *access) {
|
||||||
// Look through nested accesses.
|
// First see if we have read/modify. If we do not, just look through the
|
||||||
|
// nested access.
|
||||||
|
switch (access->getAccessKind()) {
|
||||||
|
case SILAccessKind::Init:
|
||||||
|
case SILAccessKind::Deinit:
|
||||||
return next(access->getOperand());
|
return next(access->getOperand());
|
||||||
|
case SILAccessKind::Read:
|
||||||
|
case SILAccessKind::Modify:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next check if our live range is completely in the begin/end access
|
||||||
|
// scope. If so, we may be able to use a load_borrow here!
|
||||||
|
SmallVector<Operand *, 8> endScopeUses;
|
||||||
|
transform(access->getEndAccesses(), std::back_inserter(endScopeUses),
|
||||||
|
[](EndAccessInst *eai) {
|
||||||
|
return &eai->getAllOperands()[0];
|
||||||
|
});
|
||||||
|
SmallPtrSet<SILBasicBlock *, 4> visitedBlocks;
|
||||||
|
LinearLifetimeChecker checker(visitedBlocks, ARCOpt.getDeadEndBlocks());
|
||||||
|
if (!checker.validateLifetime(access, endScopeUses,
|
||||||
|
liveRange.getDestroyingUses())) {
|
||||||
|
// If we fail the linear lifetime check, then just recur:
|
||||||
|
return next(access->getOperand());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, if we have read, then we are done!
|
||||||
|
if (access->getAccessKind() == SILAccessKind::Read) {
|
||||||
|
return answer(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have a modify, check if our value is /ever/ written to. If it is
|
||||||
|
// never actually written to, then we convert to a load_borrow.
|
||||||
|
return answer(ARCOpt.isAddressWrittenToDefUseAnalysis(access));
|
||||||
}
|
}
|
||||||
|
|
||||||
void visitArgumentAccess(SILFunctionArgument *arg) {
|
void visitArgumentAccess(SILFunctionArgument *arg) {
|
||||||
|
|||||||
@@ -1657,3 +1657,127 @@ bb3(%0c : @guaranteed $FakeOptional<ClassLet>):
|
|||||||
%9999 = tuple()
|
%9999 = tuple()
|
||||||
return %9999 : $()
|
return %9999 : $()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: sil [ossa] @loadcopy_to_loadborrow_from_read_access : $@convention(thin) (@guaranteed ClassLet) -> () {
|
||||||
|
// CHECK-NOT: load [copy]
|
||||||
|
// CHECK: load_borrow
|
||||||
|
// CHECK-NOT: load [copy]
|
||||||
|
// CHECK: } // end sil function 'loadcopy_to_loadborrow_from_read_access'
|
||||||
|
sil [ossa] @loadcopy_to_loadborrow_from_read_access : $@convention(thin) (@guaranteed ClassLet) -> () {
|
||||||
|
bb0(%0 : @guaranteed $ClassLet):
|
||||||
|
%1 = ref_element_addr %0 : $ClassLet, #ClassLet.aVar
|
||||||
|
%2 = begin_access [read] [dynamic] %1 : $*Klass
|
||||||
|
%3 = load [copy] %2 : $*Klass
|
||||||
|
%f = function_ref @guaranteed_klass_user : $@convention(thin) (@guaranteed Klass) -> ()
|
||||||
|
apply %f(%3) : $@convention(thin) (@guaranteed Klass) -> ()
|
||||||
|
destroy_value %3 : $Klass
|
||||||
|
end_access %2 : $*Klass
|
||||||
|
%9999 = tuple()
|
||||||
|
return %9999 : $()
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: sil [ossa] @loadcopy_to_loadborrow_from_mut_access_without_writes : $@convention(thin) (@guaranteed ClassLet) -> () {
|
||||||
|
// CHECK-NOT: load [copy]
|
||||||
|
// CHECK: load_borrow
|
||||||
|
// CHECK-NOT: load [copy]
|
||||||
|
// CHECK: } // end sil function 'loadcopy_to_loadborrow_from_mut_access_without_writes'
|
||||||
|
sil [ossa] @loadcopy_to_loadborrow_from_mut_access_without_writes : $@convention(thin) (@guaranteed ClassLet) -> () {
|
||||||
|
bb0(%0 : @guaranteed $ClassLet):
|
||||||
|
%1 = ref_element_addr %0 : $ClassLet, #ClassLet.aVar
|
||||||
|
%2 = begin_access [modify] [dynamic] %1 : $*Klass
|
||||||
|
%3 = load [copy] %2 : $*Klass
|
||||||
|
%f = function_ref @guaranteed_klass_user : $@convention(thin) (@guaranteed Klass) -> ()
|
||||||
|
apply %f(%3) : $@convention(thin) (@guaranteed Klass) -> ()
|
||||||
|
destroy_value %3 : $Klass
|
||||||
|
end_access %2 : $*Klass
|
||||||
|
%9999 = tuple()
|
||||||
|
return %9999 : $()
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can with time handle this case by proving that the destroy_addr is after
|
||||||
|
// the destroy_value.
|
||||||
|
//
|
||||||
|
// CHECK-LABEL: sil [ossa] @loadcopy_to_loadborrow_from_mut_access_with_writes : $@convention(thin) (@guaranteed ClassLet) -> () {
|
||||||
|
// CHECK-NOT: load_borrow
|
||||||
|
// CHECK: load [copy]
|
||||||
|
// CHECK-NOT: load_borrow
|
||||||
|
// CHECK: } // end sil function 'loadcopy_to_loadborrow_from_mut_access_with_writes'
|
||||||
|
sil [ossa] @loadcopy_to_loadborrow_from_mut_access_with_writes : $@convention(thin) (@guaranteed ClassLet) -> () {
|
||||||
|
bb0(%0 : @guaranteed $ClassLet):
|
||||||
|
%1 = ref_element_addr %0 : $ClassLet, #ClassLet.aVar
|
||||||
|
%2 = begin_access [modify] [dynamic] %1 : $*Klass
|
||||||
|
%3 = load [copy] %2 : $*Klass
|
||||||
|
%f = function_ref @guaranteed_klass_user : $@convention(thin) (@guaranteed Klass) -> ()
|
||||||
|
apply %f(%3) : $@convention(thin) (@guaranteed Klass) -> ()
|
||||||
|
destroy_value %3 : $Klass
|
||||||
|
destroy_addr %2 : $*Klass
|
||||||
|
end_access %2 : $*Klass
|
||||||
|
%9999 = tuple()
|
||||||
|
return %9999 : $()
|
||||||
|
}
|
||||||
|
|
||||||
|
// We will never be able to handle this unless we can hoist the copy before the
|
||||||
|
// destroy_addr. Once we have begin_borrows around all interior_pointers, we can
|
||||||
|
// handle this version.
|
||||||
|
//
|
||||||
|
// CHECK-LABEL: sil [ossa] @loadcopy_to_loadborrow_from_mut_access_with_writes_2 : $@convention(thin) (@guaranteed ClassLet) -> () {
|
||||||
|
// CHECK-NOT: load_borrow
|
||||||
|
// CHECK: load [copy]
|
||||||
|
// CHECK-NOT: load_borrow
|
||||||
|
// CHECK: } // end sil function 'loadcopy_to_loadborrow_from_mut_access_with_writes_2'
|
||||||
|
sil [ossa] @loadcopy_to_loadborrow_from_mut_access_with_writes_2 : $@convention(thin) (@guaranteed ClassLet) -> () {
|
||||||
|
bb0(%0 : @guaranteed $ClassLet):
|
||||||
|
%1 = ref_element_addr %0 : $ClassLet, #ClassLet.aVar
|
||||||
|
%2 = begin_access [modify] [dynamic] %1 : $*Klass
|
||||||
|
%3 = load [copy] %2 : $*Klass
|
||||||
|
%f = function_ref @guaranteed_klass_user : $@convention(thin) (@guaranteed Klass) -> ()
|
||||||
|
apply %f(%3) : $@convention(thin) (@guaranteed Klass) -> ()
|
||||||
|
destroy_addr %2 : $*Klass
|
||||||
|
destroy_value %3 : $Klass
|
||||||
|
end_access %2 : $*Klass
|
||||||
|
%9999 = tuple()
|
||||||
|
return %9999 : $()
|
||||||
|
}
|
||||||
|
|
||||||
|
// We will never be able to handle this since we can't hoist the destroy_value
|
||||||
|
// before the guaranteed_klass_user.
|
||||||
|
//
|
||||||
|
// CHECK-LABEL: sil [ossa] @loadcopy_to_loadborrow_from_mut_access_with_writes_3 : $@convention(thin) (@guaranteed ClassLet) -> () {
|
||||||
|
// CHECK-NOT: load_borrow
|
||||||
|
// CHECK: load [copy]
|
||||||
|
// CHECK-NOT: load_borrow
|
||||||
|
// CHECK: } // end sil function 'loadcopy_to_loadborrow_from_mut_access_with_writes_3'
|
||||||
|
sil [ossa] @loadcopy_to_loadborrow_from_mut_access_with_writes_3 : $@convention(thin) (@guaranteed ClassLet) -> () {
|
||||||
|
bb0(%0 : @guaranteed $ClassLet):
|
||||||
|
%1 = ref_element_addr %0 : $ClassLet, #ClassLet.aVar
|
||||||
|
%2 = begin_access [modify] [dynamic] %1 : $*Klass
|
||||||
|
%3 = load [copy] %2 : $*Klass
|
||||||
|
destroy_addr %2 : $*Klass
|
||||||
|
%f = function_ref @guaranteed_klass_user : $@convention(thin) (@guaranteed Klass) -> ()
|
||||||
|
apply %f(%3) : $@convention(thin) (@guaranteed Klass) -> ()
|
||||||
|
destroy_value %3 : $Klass
|
||||||
|
end_access %2 : $*Klass
|
||||||
|
%9999 = tuple()
|
||||||
|
return %9999 : $()
|
||||||
|
}
|
||||||
|
|
||||||
|
// We will never be able to handle this since the end_access is before the use
|
||||||
|
// of %3, so we can not form a long enough load_borrow.
|
||||||
|
//
|
||||||
|
// CHECK-LABEL: sil [ossa] @loadcopy_to_loadborrow_from_mut_access_with_writes_4 : $@convention(thin) (@guaranteed ClassLet) -> () {
|
||||||
|
// CHECK-NOT: load_borrow
|
||||||
|
// CHECK: load [copy]
|
||||||
|
// CHECK-NOT: load_borrow
|
||||||
|
// CHECK: } // end sil function 'loadcopy_to_loadborrow_from_mut_access_with_writes_4'
|
||||||
|
sil [ossa] @loadcopy_to_loadborrow_from_mut_access_with_writes_4 : $@convention(thin) (@guaranteed ClassLet) -> () {
|
||||||
|
bb0(%0 : @guaranteed $ClassLet):
|
||||||
|
%1 = ref_element_addr %0 : $ClassLet, #ClassLet.aVar
|
||||||
|
%2 = begin_access [modify] [dynamic] %1 : $*Klass
|
||||||
|
%3 = load [copy] %2 : $*Klass
|
||||||
|
end_access %2 : $*Klass
|
||||||
|
%f = function_ref @guaranteed_klass_user : $@convention(thin) (@guaranteed Klass) -> ()
|
||||||
|
apply %f(%3) : $@convention(thin) (@guaranteed Klass) -> ()
|
||||||
|
destroy_value %3 : $Klass
|
||||||
|
%9999 = tuple()
|
||||||
|
return %9999 : $()
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user