Skip to content

Commit

Permalink
Merge pull request swiftlang#78207 from nate-chandler/cherrypick/rele…
Browse files Browse the repository at this point in the history
…ase/6.1/rdar141246601

6.1: [OSSACompleteLifetime] Recurse into scoped addresses.
  • Loading branch information
nate-chandler authored Dec 16, 2024
2 parents 06c5bda + 1726b32 commit 98ab6b2
Show file tree
Hide file tree
Showing 4 changed files with 229 additions and 4 deletions.
1 change: 1 addition & 0 deletions include/swift/SIL/OSSALifetimeCompletion.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ class OSSALifetimeCompletion {

protected:
bool analyzeAndUpdateLifetime(SILValue value, Boundary boundary);
bool analyzeAndUpdateLifetime(ScopedAddressValue value, Boundary boundary);
};

//===----------------------------------------------------------------------===//
Expand Down
55 changes: 51 additions & 4 deletions lib/SIL/Utils/OSSALifetimeCompletion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@

#include "swift/SIL/OSSALifetimeCompletion.h"
#include "swift/Basic/Assertions.h"
#include "swift/SIL/AddressWalker.h"
#include "swift/SIL/BasicBlockUtils.h"
#include "swift/SIL/SILBuilder.h"
#include "swift/SIL/SILFunction.h"
Expand Down Expand Up @@ -439,16 +440,62 @@ static bool endLifetimeAtBoundary(SILValue value,
return changed;
}

bool OSSALifetimeCompletion::analyzeAndUpdateLifetime(
ScopedAddressValue scopedAddress, Boundary boundary) {
SmallVector<SILBasicBlock *, 8> discoveredBlocks;
SSAPrunedLiveness liveness(scopedAddress->getFunction(), &discoveredBlocks);
liveness.initializeDef(scopedAddress.value);

struct Walker : TransitiveAddressWalker<Walker> {
OSSALifetimeCompletion &completion;
ScopedAddressValue scopedAddress;
Boundary boundary;
SSAPrunedLiveness &liveness;
Walker(OSSALifetimeCompletion &completion, ScopedAddressValue scopedAddress,
Boundary boundary, SSAPrunedLiveness &liveness)
: completion(completion), scopedAddress(scopedAddress),
boundary(boundary), liveness(liveness) {}
bool visitUse(Operand *use) {
auto *user = use->getUser();
if (scopedAddress.isScopeEndingUse(use)) {
liveness.updateForUse(user, /*lifetimeEnding=*/true);
return true;
}
liveness.updateForUse(user, /*lifetimeEnding=*/false);
for (auto result : user->getResults()) {
auto shouldComplete =
(bool)BorrowedValue(result) || (bool)ScopedAddressValue(result);
if (!shouldComplete)
continue;
auto completed = completion.completeOSSALifetime(result, boundary);
switch (completed) {
case LifetimeCompletion::NoLifetime:
break;
case LifetimeCompletion::AlreadyComplete:
case LifetimeCompletion::WasCompleted:
for (auto *consume : result->getConsumingUses()) {
liveness.updateForUse(consume->getUser(), /*lifetimeEnding=*/false);
}
break;
}
}
return true;
}
};
Walker walker(*this, scopedAddress, boundary, liveness);
std::move(walker).walk(scopedAddress.value);

return endLifetimeAtBoundary(scopedAddress.value, liveness, boundary,
deadEndBlocks);
}

/// End the lifetime of \p value at unreachable instructions.
///
/// Returns true if any new instructions were created to complete the lifetime.
bool OSSALifetimeCompletion::analyzeAndUpdateLifetime(SILValue value,
Boundary boundary) {
if (auto scopedAddress = ScopedAddressValue(value)) {
SmallVector<SILBasicBlock *, 8> discoveredBlocks;
SSAPrunedLiveness liveness(value->getFunction(), &discoveredBlocks);
scopedAddress.computeTransitiveLiveness(liveness);
return endLifetimeAtBoundary(value, liveness, boundary, deadEndBlocks);
return analyzeAndUpdateLifetime(scopedAddress, boundary);
}

// Called for inner borrows, inner adjacent reborrows, inner reborrows, and
Expand Down
62 changes: 62 additions & 0 deletions test/SILOptimizer/mem2reg_ossa_nontrivial.sil
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ sil [ossa] @get_nontrivialstruct : $@convention(thin) () -> @owned NonTrivialStr
sil [ossa] @get_nontrivialenum : $@convention(thin) () -> @owned NonTrivialEnum
sil [ossa] @get_optionalnontrivialstruct : $@convention(thin) () -> @owned FakeOptional<NonTrivialStruct>
sil [ossa] @take_nontrivialstruct : $@convention(thin) (@owned NonTrivialStruct) -> ()
sil @get : $@convention(thin) () -> @owned FakeOptional<Klass>
sil @use : $@convention(thin) (@guaranteed FakeOptional<Klass>) -> ()

///////////
// Tests //
Expand Down Expand Up @@ -1487,3 +1489,63 @@ bb1:
%t = tuple ()
return %t : $()
}

// Ensure no verification failure
sil [ossa] @test_store_enum_value_in_multiple_locs1 : $@convention(thin) () -> () {
bb0:
%0 = function_ref @get : $@convention(thin) () -> @owned FakeOptional<Klass>
%1 = apply %0() : $@convention(thin) () -> @owned FakeOptional<Klass>
%2 = begin_borrow [lexical] %1 : $FakeOptional<Klass>
cond_br undef, bb2, bb1

bb1:
br bb3

bb2:
%5 = alloc_stack [lexical] $FakeOptional<Klass>
%6 = store_borrow %2 to %5 : $*FakeOptional<Klass>
cond_br undef, bb8, bb9

bb3:
cond_br undef, bb5, bb4

bb4:
unreachable

bb5:
%11 = alloc_stack $FakeOptional<Klass>
%12 = store_borrow %2 to %11 : $*FakeOptional<Klass>
%14 = load_borrow %12 : $*FakeOptional<Klass>
%15 = begin_borrow [lexical] %14 : $FakeOptional<Klass>
%16 = alloc_stack $FakeOptional<Klass>
%17 = store_borrow %15 to %16 : $*FakeOptional<Klass>
cond_br undef, bb6, bb7

bb6:
fix_lifetime %17 : $*FakeOptional<Klass>
end_borrow %17 : $*FakeOptional<Klass>
dealloc_stack %16 : $*FakeOptional<Klass>
end_borrow %15 : $FakeOptional<Klass>
end_borrow %14 : $FakeOptional<Klass>
end_borrow %12 : $*FakeOptional<Klass>
dealloc_stack %11 : $*FakeOptional<Klass>
end_borrow %2 : $FakeOptional<Klass>
destroy_value %1 : $FakeOptional<Klass>
%29 = tuple ()
return %29 : $()

bb7:
end_borrow %17 : $*FakeOptional<Klass>
dealloc_stack %16 : $*FakeOptional<Klass>
unreachable

bb8:
end_borrow %6 : $*FakeOptional<Klass>
dealloc_stack %5 : $*FakeOptional<Klass>
unreachable

bb9:
end_borrow %6 : $*FakeOptional<Klass>
dealloc_stack %5 : $*FakeOptional<Klass>
br bb3
}
115 changes: 115 additions & 0 deletions test/SILOptimizer/ossa_lifetime_completion.sil
Original file line number Diff line number Diff line change
Expand Up @@ -817,6 +817,90 @@ die:
unreachable
}

// CHECK-LABEL: begin running test {{.*}} on load_borrow_of_store_borrow_1: ossa_lifetime_completion
// CHECK-LABEL: sil [ossa] @load_borrow_of_store_borrow_1 : {{.*}} {
// CHECK: [[TOKEN:%[^,]+]] = store_borrow
// CHECK: [[LOAD:%[^,]+]] = load_borrow [[TOKEN]]
// CHECK: [[BORROW:%[^,]+]] = begin_borrow [[LOAD]]
// CHECK: cond_br undef, {{bb[0-9]+}}, [[DIE:bb[0-9]+]]
// CHECK: [[DIE]]:
// CHECK-NEXT: end_borrow [[BORROW]]
// CHECK-NEXT: end_borrow [[LOAD]]
// CHECK-NEXT: end_borrow [[TOKEN]]
// CHECK-NEXT: unreachable
// CHECK-LABEL: } // end sil function 'load_borrow_of_store_borrow_1'
// CHECK-LABEL: end running test {{.*}} on load_borrow_of_store_borrow_1: ossa_lifetime_completion
sil [ossa] @load_borrow_of_store_borrow_1 : $@convention(thin) (@guaranteed C) -> () {
entry(%instance : @guaranteed $C):
specify_test "ossa_lifetime_completion %token availability"
%addr = alloc_stack $C
%token = store_borrow %instance to %addr : $*C
%load = load_borrow %token : $*C
%borrow = begin_borrow %load : $C
cond_br undef, exit, die

exit:
end_borrow %borrow : $C
end_borrow %load : $C
end_borrow %token : $*C
dealloc_stack %addr : $*C
%retval = tuple ()
return %retval : $()
die:
unreachable
}

// CHECK-LABEL: begin running test {{.*}} on load_borrow_of_store_borrow_2: ossa_lifetime_completion
// CHECK-LABEL: sil [ossa] @load_borrow_of_store_borrow_2 : {{.*}} {
// CHECK: [[TOKEN:%[^,]+]] = store_borrow
// CHECK: [[LOAD:%[^,]+]] = load_borrow [[TOKEN]]
// CHECK: [[BORROW:%[^,]+]] = begin_borrow [[LOAD]]
// CHECK: cond_br undef, {{bb[0-9]+}}, [[DIE:bb[0-9]+]]
// CHECK: [[DIE]]:
// CHECK-NEXT: end_borrow [[BORROW]]
// CHECK-NEXT: end_borrow [[LOAD]]
// CHECK-NEXT: end_borrow [[TOKEN]]
// CHECK-NEXT: unreachable
// CHECK-LABEL: } // end sil function 'load_borrow_of_store_borrow_2'
// CHECK-LABEL: end running test {{.*}} on load_borrow_of_store_borrow_2: ossa_lifetime_completion
sil [ossa] @load_borrow_of_store_borrow_2 : $@convention(thin) (@guaranteed C) -> () {
entry(%instance : @guaranteed $C):
specify_test "ossa_lifetime_completion %token availability"
%addr = alloc_stack $C
%token = store_borrow %instance to %addr : $*C
%load = load_borrow %token : $*C
%borrow = begin_borrow %load : $C
cond_br undef, exit, die

exit:
end_borrow %borrow : $C
end_borrow %load : $C
end_borrow %token : $*C
dealloc_stack %addr : $*C
%retval = tuple ()
return %retval : $()
die:
end_borrow %borrow : $C
end_borrow %load : $C
unreachable
}

sil [ossa] @load_borrow_1 : $@convention(thin) (@in_guaranteed C) -> () {
entry(%addr : $*C):
specify_test "ossa_lifetime_completion %load availability"
%load = load_borrow %addr : $*C
%borrow = begin_borrow %load : $C
cond_br undef, exit, die

exit:
end_borrow %borrow : $C
end_borrow %load : $C
%retval = tuple ()
return %retval : $()
die:
unreachable
}

// CHECK-LABEL: begin running test {{.*}} on begin_access: ossa_lifetime_completion
// CHECK-LABEL: sil [ossa] @begin_access : {{.*}} {
// CHECK: [[TOKEN:%[^,]+]] = begin_access
Expand All @@ -841,3 +925,34 @@ exit:
die:
unreachable
}

// CHECK-LABEL: begin running test {{.*}} on load_borrow_of_begin_access: ossa_lifetime_completion
// CHECK-LABEL: sil [ossa] @load_borrow_of_begin_access : {{.*}} {
// CHECK: [[ACCESS:%[^,]+]] = begin_access
// CHECK: [[LOAD:%[^,]+]] = load_borrow [[ACCESS]]
// CHECK: [[BORROW:%[^,]+]] = begin_borrow [[LOAD]]
// CHECK: cond_br undef, {{bb[0-9]+}}, [[DIE:bb[0-9]+]]
// CHECK: [[DIE]]:
// CHECK-NEXT: end_borrow [[BORROW]]
// CHECK-NEXT: end_borrow [[LOAD]]
// CHECK-NEXT: end_access [[ACCESS]]
// CHECK-NEXT: unreachable
// CHECK-LABEL: } // end sil function 'load_borrow_of_begin_access'
// CHECK-LABEL: end running test {{.*}} on load_borrow_of_begin_access: ossa_lifetime_completion
sil [ossa] @load_borrow_of_begin_access : $@convention(thin) (@in_guaranteed C) -> () {
entry(%addr : $*C):
specify_test "ossa_lifetime_completion %access availability"
%access = begin_access [static] [read] %addr : $*C
%load = load_borrow %access : $*C
%borrow = begin_borrow %load : $C
cond_br undef, exit, die

exit:
end_borrow %borrow : $C
end_borrow %load : $C
end_access %access : $*C
%retval = tuple ()
return %retval : $()
die:
unreachable
}

0 comments on commit 98ab6b2

Please sign in to comment.