Skip to content

6.0: [PrunedLiveness] Branch summary merges to ending. #74836

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion include/swift/SIL/PrunedLiveness.h
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ class PrunedLiveness {
: value(lifetimeEnding ? Value::Ending : Value::NonEnding) {}
operator Value() const { return value; }
LifetimeEnding meet(LifetimeEnding const other) const {
return value < other.value ? *this : other;
return std::min(value, other.value);
}
void meetInPlace(LifetimeEnding const other) { *this = meet(other); }
bool isEnding() const { return value == Value::Ending; }
Expand Down
46 changes: 44 additions & 2 deletions lib/SIL/Utils/PrunedLiveness.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,43 @@ static FunctionTest
// PrunedLiveRange
//===----------------------------------------------------------------------===//

static PrunedLiveness::LifetimeEnding
branchMeet(PrunedLiveness::LifetimeEnding const lhs,
PrunedLiveness::LifetimeEnding const rhs) {
enum BranchLifetimeEnding {
Ending,
NonEnding,
NonUse,
};
auto toBranch =
[](PrunedLiveness::LifetimeEnding const ending) -> BranchLifetimeEnding {
switch (ending) {
case PrunedLiveness::LifetimeEnding::Value::NonEnding:
return NonEnding;
case PrunedLiveness::LifetimeEnding::Value::Ending:
return Ending;
case PrunedLiveness::LifetimeEnding::Value::NonUse:
return NonUse;
}
};
auto toRegular =
[](BranchLifetimeEnding const ending) -> PrunedLiveness::LifetimeEnding {
switch (ending) {
case NonEnding:
return PrunedLiveness::LifetimeEnding::Value::NonEnding;
case Ending:
return PrunedLiveness::LifetimeEnding::Value::Ending;
case NonUse:
return PrunedLiveness::LifetimeEnding::Value::NonUse;
}
};
return toRegular(std::min(toBranch(lhs), toBranch(rhs)));
}
static void branchMeetInPlace(PrunedLiveness::LifetimeEnding &that,
PrunedLiveness::LifetimeEnding const other) {
that = branchMeet(that, other);
}

template <typename LivenessWithDefs>
void PrunedLiveRange<LivenessWithDefs>::updateForUse(
SILInstruction *user,
Expand All @@ -210,8 +247,13 @@ void PrunedLiveRange<LivenessWithDefs>::updateForUse(
// This call is not considered the end of %val's lifetime. The @owned
// argument must be copied.
auto iterAndSuccess = users.insert({user, lifetimeEnding});
if (!iterAndSuccess.second)
iterAndSuccess.first->second.meetInPlace(lifetimeEnding);
if (!iterAndSuccess.second) {
if (isa<BranchInst>(user)) {
branchMeetInPlace(iterAndSuccess.first->second, lifetimeEnding);
} else {
iterAndSuccess.first->second.meetInPlace(lifetimeEnding);
}
}
}
template <typename LivenessWithDefs>
void PrunedLiveRange<LivenessWithDefs>::updateForUse(SILInstruction *user,
Expand Down
9 changes: 9 additions & 0 deletions lib/SILOptimizer/Mandatory/MoveOnlyChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,15 @@ void MoveOnlyChecker::completeObjectLifetimes(
}
}
}
for (SILArgument *arg : block->getArguments()) {
assert(!arg->isReborrow() && "reborrows not legal at this SIL stage");
if (!transitiveValues.isVisited(arg))
continue;
if (completion.completeOSSALifetime(arg) ==
LifetimeCompletion::WasCompleted) {
madeChange = true;
}
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion test/SILOptimizer/liveness_unit.sil
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ bb1(%reborrow : @guaranteed $C, %phi : @guaranteed $PairC):
// CHECK-LABEL: testSSAReborrowedPhi: ssa_liveness
// CHECK: SSA lifetime analysis: %{{.*}} = begin_borrow
// CHECK-NEXT: bb0: LiveWithin
// CHECK-NEXT: regular user: br bb1
// CHECK-NEXT: lifetime-ending user: br bb1
// CHECK-NEXT: regular user: %{{.*}} = struct
// CHECK-NEXT: last user: br bb1
// CHECK-NEXT: end running
Expand Down
37 changes: 37 additions & 0 deletions test/SILOptimizer/moveonly_addresschecker_unmaximized.sil
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,16 @@ struct M4: ~Copyable {
let s4: M
}

class C {}

sil @get_M4 : $@convention(thin) () -> @owned M4
sil @end_2 : $@convention(thin) (@owned M, @owned M) -> ()
sil @see_addr : $@convention(thin) (@in_guaranteed M) -> ()
sil @see_addr_2 : $@convention(thin) (@in_guaranteed M, @in_guaranteed M) -> ()
sil @replace_2 : $@convention(thin) (@inout M, @inout M) -> ()
sil @get_out_2 : $@convention(thin) () -> (@out M, @out M)
sil @take_addr_2 : $@convention(thin) (@in M, @in M) -> ()
sil @getC : $@convention(thin) () -> (@owned C)

/// Two non-contiguous fields (#M4.s2, #M4.s4) are borrowed by @see_addr_2.
/// Two non-contiguous fields (#M4.s1, #M$.s3) are consumed by @end_2.
Expand Down Expand Up @@ -253,3 +256,37 @@ bb0(%0 : @guaranteed $M):
return %retval : $()
}

// CHECK-LABEL: sil [ossa] @rdar130427564 : {{.*}} {
// Verify that no instructions were inserted after backedge2's terminator. (In
// fact, if they were, the test would crash.)
// CHECK: bb2([[C0:%[^,]+]] : @owned $C, [[B0:%[^,]+]] : @reborrow @guaranteed $C):
// CHECK-NEXT: end_borrow [[B0]]
// CHECK-NEXT: destroy_value [[C0]]
// CHECK-NEXT: br
// CHECK-LABEL: } // end sil function 'rdar130427564'
sil [ossa] @rdar130427564 : $@convention(thin) (@in_guaranteed M) -> () {
entry(%ignore_me : $*M):
%ignore_me_2 = mark_unresolved_non_copyable_value [no_consume_or_assign] %ignore_me : $*M
br fn
fn:
%getC = function_ref @getC : $@convention(thin) () -> (@owned C)
%c = apply %getC() : $@convention(thin) () -> (@owned C)
%b = begin_borrow %c : $C
br header(%c : $C, %b : $C)
header(%c0 : @owned $C, %b0 : @reborrow @guaranteed $C):
end_borrow %b0 : $C
destroy_value %c0 : $C
br body
body:
br latch
latch:
cond_br undef, backedge, ecit
backedge:
%c1 = apply %getC() : $@convention(thin) () -> (@owned C)
%b1 = begin_borrow %c1 : $C
br backedge2(%c1 : $C, %b1 : $C)
backedge2(%c2 : @owned $C, %b2 : @reborrow @guaranteed $C):
br header(%c2 : $C, %b2 : $C)
ecit:
unreachable
}
37 changes: 37 additions & 0 deletions test/SILOptimizer/ossa_lifetime_completion.sil
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ public enum FakeOptional<T> {
case some(T)
}

sil @getC : $@convention(thin) () -> (@owned C)

// CHECK-LABEL: begin running test 1 of 1 on eagerConsumneOwnedArg: ossa-lifetime-completion with: @argument
// CHECK-LABEL: OSSA lifetime completion: %0 = argument of bb0 : $C
// CHECK: sil [ossa] @eagerConsumneOwnedArg : $@convention(thin) (@owned C) -> () {
Expand Down Expand Up @@ -395,3 +397,38 @@ exit:
%retval = tuple ()
return %retval : $()
}

// CHECK-LABEL: begin running test {{.*}} on root_of_reborrow: ossa-lifetime-completion
// Verify that no instructions were inserted after backedge2's terminator. (In
// fact, if they were, the test would crash.)
// CHECK-LABEL: sil [ossa] @root_of_reborrow : {{.*}} {
// CHECK: bb1([[C0:%[^,]+]] : @owned $C, [[B0:%[^,]+]] : @reborrow @guaranteed $C):
// CHECK-NEXT: end_borrow [[B0]]
// CHECK-NEXT: destroy_value [[C0]]
// CHECK-NEXT: br
// CHECK-LABEL: } // end sil function 'root_of_reborrow'
// CHECK-LABEL: end running test {{.*}} on root_of_reborrow: ossa-lifetime-completion
sil [ossa] @root_of_reborrow : $@convention(thin) () -> () {
entry:
%getC = function_ref @getC : $@convention(thin) () -> (@owned C)
%c = apply %getC() : $@convention(thin) () -> (@owned C)
%b = begin_borrow %c : $C
br header(%c : $C, %b : $C)
header(%c0 : @owned $C, %b0 : @reborrow @guaranteed $C):
end_borrow %b0 : $C
destroy_value %c0 : $C
br body
body:
br latch
latch:
cond_br undef, backedge, exit
backedge:
%c1 = apply %getC() : $@convention(thin) () -> (@owned C)
%b1 = begin_borrow %c1 : $C
br backedge2(%c1 : $C, %b1 : $C)
backedge2(%c2 : @owned $C, %b2 : @reborrow @guaranteed $C):
specify_test "ossa-lifetime-completion %c2"
br header(%c2 : $C, %b2 : $C)
exit:
unreachable
}
2 changes: 1 addition & 1 deletion test/SILOptimizer/ownership_liveness_unit.sil
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ bb1(%reborrow : @guaranteed $C):
// CHECK: Interior liveness: %{{.*}} = begin_borrow %0 : $C
// CHECK-NEXT: Inner scope: %{{.*}} = begin_borrow %{{.*}} : $D
// CHECK-NEXT: bb0: LiveWithin
// CHECK-NEXT: regular user: br bb1(%{{.*}} : $C, %{{.*}} : $D)
// CHECK-NEXT: lifetime-ending user: br bb1(%{{.*}} : $C, %{{.*}} : $D)
// CHECK-NEXT: regular user: %{{.*}} = unchecked_ref_cast %{{.*}} : $C to $D
// CHECK-NEXT: Complete liveness
// CHECK-NEXT: Unenclosed phis {
Expand Down