CopyToBorrowOptimization: need to update borrowed-from instructions when changes are made

When ownership is changed from owned to guaranteed, the enclosing values of a guaranteed value can change.
Fixes a SIL verifier error.
This commit is contained in:
Erik Eckstein
2025-12-17 19:02:51 +01:00
parent 7e61773e4f
commit 7ace2c5736
2 changed files with 41 additions and 10 deletions

View File

@@ -53,27 +53,37 @@ let copyToBorrowOptimization = FunctionPass(name: "copy-to-borrow-optimization")
return
}
var changed = false
for inst in function.instructions {
switch inst {
case let load as LoadInst:
optimize(load: load, context)
if optimize(load: load, context) {
changed = true
}
case let copy as CopyValueInst:
optimize(copy: copy, context)
if optimize(copy: copy, context) {
changed = true
}
default:
break
}
}
if changed {
updateBorrowedFrom(in: function, context)
}
}
private func optimize(load: LoadInst, _ context: FunctionPassContext) {
private func optimize(load: LoadInst, _ context: FunctionPassContext) -> Bool {
if load.loadOwnership != .copy {
return
return false
}
var collectedUses = Uses(context)
defer { collectedUses.deinitialize() }
if !collectedUses.collectUses(of: load) {
return
return false
}
if mayWrite(toAddressOf: load,
@@ -81,21 +91,22 @@ private func optimize(load: LoadInst, _ context: FunctionPassContext) {
usersInDeadEndBlocks: collectedUses.usersInDeadEndBlocks,
context)
{
return
return false
}
load.replaceWithLoadBorrow(collectedUses: collectedUses)
return true
}
private func optimize(copy: CopyValueInst, _ context: FunctionPassContext) {
private func optimize(copy: CopyValueInst, _ context: FunctionPassContext) -> Bool {
if copy.fromValue.ownership != .guaranteed {
return
return false
}
var collectedUses = Uses(context)
defer { collectedUses.deinitialize() }
if !collectedUses.collectUses(of: copy) {
return
return false
}
var liverange = InstructionRange(begin: copy, context)
@@ -104,10 +115,11 @@ private func optimize(copy: CopyValueInst, _ context: FunctionPassContext) {
liverange.insert(contentsOf: collectedUses.usersInDeadEndBlocks)
if !liverange.isFullyContainedIn(borrowScopeOf: copy.fromValue.lookThroughForwardingInstructions) {
return
return false
}
remove(copy: copy, collectedUses: collectedUses, liverange: liverange)
return true
}
private struct Uses {

View File

@@ -2275,3 +2275,22 @@ bb1(%6 : @guaranteed $MyArrayStorageBase):
%13 = tuple ()
return %13
}
// CHECK-LABEL: sil [ossa] @test_updating_borrowed_from :
// CHECK: borrowed {{.*}} from (%0)
// CHECK-LABEL: } // end sil function 'test_updating_borrowed_from'
sil [ossa] @test_updating_borrowed_from : $@convention(thin) (@guaranteed NonTrivialStruct) -> () {
bb0(%0 : @guaranteed $NonTrivialStruct):
%1 = destructure_struct %0 : $NonTrivialStruct
%2 = copy_value %1 : $Klass
%3 = begin_borrow %2 : $Klass
br bb1(%3 : $Klass)
bb1(%5 : @reborrow $Klass):
%6 = borrowed %5 : $Klass from (%2 : $Klass)
end_borrow %6 : $Klass
destroy_value %2 : $Klass
%9 = tuple ()
return %9 : $()
}