Skip to content

Commit d0097fe

Browse files
committed
[PrunedLiveness] Branch summary merges to ending.
A branch instruction that is both a lifetime-ending user and also a non-lifetime-ending of a value should be regarded as a lifetime-ending user. Without this, utilities that rely on PrunedLiveness such as `LinearLiveness` and `InteriorLiveness` both incorrectly report that a branch featuring a value and also a reborrow or guaranteed phi are non-ending. rdar://130427564
1 parent ec2e575 commit d0097fe

File tree

4 files changed

+83
-4
lines changed

4 files changed

+83
-4
lines changed

lib/SIL/Utils/PrunedLiveness.cpp

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,43 @@ static FunctionTest
192192
// PrunedLiveRange
193193
//===----------------------------------------------------------------------===//
194194

195+
static PrunedLiveness::LifetimeEnding
196+
branchMeet(PrunedLiveness::LifetimeEnding const lhs,
197+
PrunedLiveness::LifetimeEnding const rhs) {
198+
enum BranchLifetimeEnding {
199+
Ending,
200+
NonEnding,
201+
NonUse,
202+
};
203+
auto toBranch =
204+
[](PrunedLiveness::LifetimeEnding const ending) -> BranchLifetimeEnding {
205+
switch (ending) {
206+
case PrunedLiveness::LifetimeEnding::Value::NonEnding:
207+
return NonEnding;
208+
case PrunedLiveness::LifetimeEnding::Value::Ending:
209+
return Ending;
210+
case PrunedLiveness::LifetimeEnding::Value::NonUse:
211+
return NonUse;
212+
}
213+
};
214+
auto toRegular =
215+
[](BranchLifetimeEnding const ending) -> PrunedLiveness::LifetimeEnding {
216+
switch (ending) {
217+
case NonEnding:
218+
return PrunedLiveness::LifetimeEnding::Value::NonEnding;
219+
case Ending:
220+
return PrunedLiveness::LifetimeEnding::Value::Ending;
221+
case NonUse:
222+
return PrunedLiveness::LifetimeEnding::Value::NonUse;
223+
}
224+
};
225+
return toRegular(std::min(toBranch(lhs), toBranch(rhs)));
226+
}
227+
static void branchMeetInPlace(PrunedLiveness::LifetimeEnding &that,
228+
PrunedLiveness::LifetimeEnding const other) {
229+
that = branchMeet(that, other);
230+
}
231+
195232
template <typename LivenessWithDefs>
196233
void PrunedLiveRange<LivenessWithDefs>::updateForUse(
197234
SILInstruction *user,
@@ -210,8 +247,13 @@ void PrunedLiveRange<LivenessWithDefs>::updateForUse(
210247
// This call is not considered the end of %val's lifetime. The @owned
211248
// argument must be copied.
212249
auto iterAndSuccess = users.insert({user, lifetimeEnding});
213-
if (!iterAndSuccess.second)
214-
iterAndSuccess.first->second.meetInPlace(lifetimeEnding);
250+
if (!iterAndSuccess.second) {
251+
if (isa<BranchInst>(user)) {
252+
branchMeetInPlace(iterAndSuccess.first->second, lifetimeEnding);
253+
} else {
254+
iterAndSuccess.first->second.meetInPlace(lifetimeEnding);
255+
}
256+
}
215257
}
216258
template <typename LivenessWithDefs>
217259
void PrunedLiveRange<LivenessWithDefs>::updateForUse(SILInstruction *user,

test/SILOptimizer/liveness_unit.sil

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ bb1(%reborrow : @guaranteed $C, %phi : @guaranteed $PairC):
378378
// CHECK-LABEL: testSSAReborrowedPhi: ssa_liveness
379379
// CHECK: SSA lifetime analysis: %{{.*}} = begin_borrow
380380
// CHECK-NEXT: bb0: LiveWithin
381-
// CHECK-NEXT: regular user: br bb1
381+
// CHECK-NEXT: lifetime-ending user: br bb1
382382
// CHECK-NEXT: regular user: %{{.*}} = struct
383383
// CHECK-NEXT: last user: br bb1
384384
// CHECK-NEXT: end running

test/SILOptimizer/ossa_lifetime_completion.sil

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ public enum FakeOptional<T> {
1111
case some(T)
1212
}
1313

14+
sil @getC : $@convention(thin) () -> (@owned C)
15+
1416
// CHECK-LABEL: begin running test 1 of 1 on eagerConsumneOwnedArg: ossa-lifetime-completion with: @argument
1517
// CHECK-LABEL: OSSA lifetime completion: %0 = argument of bb0 : $C
1618
// CHECK: sil [ossa] @eagerConsumneOwnedArg : $@convention(thin) (@owned C) -> () {
@@ -395,3 +397,38 @@ exit:
395397
%retval = tuple ()
396398
return %retval : $()
397399
}
400+
401+
// CHECK-LABEL: begin running test {{.*}} on root_of_reborrow: ossa-lifetime-completion
402+
// Verify that no instructions were inserted after backedge2's terminator. (In
403+
// fact, if they were, the test would crash.)
404+
// CHECK-LABEL: sil [ossa] @root_of_reborrow : {{.*}} {
405+
// CHECK: bb1([[C0:%[^,]+]] : @owned $C, [[B0:%[^,]+]] : @reborrow @guaranteed $C):
406+
// CHECK-NEXT: end_borrow [[B0]]
407+
// CHECK-NEXT: destroy_value [[C0]]
408+
// CHECK-NEXT: br
409+
// CHECK-LABEL: } // end sil function 'root_of_reborrow'
410+
// CHECK-LABEL: end running test {{.*}} on root_of_reborrow: ossa-lifetime-completion
411+
sil [ossa] @root_of_reborrow : $@convention(thin) () -> () {
412+
entry:
413+
%getC = function_ref @getC : $@convention(thin) () -> (@owned C)
414+
%c = apply %getC() : $@convention(thin) () -> (@owned C)
415+
%b = begin_borrow %c : $C
416+
br header(%c : $C, %b : $C)
417+
header(%c0 : @owned $C, %b0 : @reborrow @guaranteed $C):
418+
end_borrow %b0 : $C
419+
destroy_value %c0 : $C
420+
br body
421+
body:
422+
br latch
423+
latch:
424+
cond_br undef, backedge, exit
425+
backedge:
426+
%c1 = apply %getC() : $@convention(thin) () -> (@owned C)
427+
%b1 = begin_borrow %c1 : $C
428+
br backedge2(%c1 : $C, %b1 : $C)
429+
backedge2(%c2 : @owned $C, %b2 : @reborrow @guaranteed $C):
430+
specify_test "ossa-lifetime-completion %c2"
431+
br header(%c2 : $C, %b2 : $C)
432+
exit:
433+
unreachable
434+
}

test/SILOptimizer/ownership_liveness_unit.sil

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ bb1(%reborrow : @guaranteed $C):
386386
// CHECK: Interior liveness: %{{.*}} = begin_borrow %0 : $C
387387
// CHECK-NEXT: Inner scope: %{{.*}} = begin_borrow %{{.*}} : $D
388388
// CHECK-NEXT: bb0: LiveWithin
389-
// CHECK-NEXT: regular user: br bb1(%{{.*}} : $C, %{{.*}} : $D)
389+
// CHECK-NEXT: lifetime-ending user: br bb1(%{{.*}} : $C, %{{.*}} : $D)
390390
// CHECK-NEXT: regular user: %{{.*}} = unchecked_ref_cast %{{.*}} : $C to $D
391391
// CHECK-NEXT: Complete liveness
392392
// CHECK-NEXT: Unenclosed phis {

0 commit comments

Comments
 (0)