Skip to content

Commit 5270079

Browse files
authored
Merge pull request #61558 from atrick/scoped-liveness
Handle scoped address in PrunedLiveness
2 parents 3e8d7da + f6cae0c commit 5270079

File tree

4 files changed

+100
-8
lines changed

4 files changed

+100
-8
lines changed

include/swift/SIL/PrunedLiveness.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,34 @@
8383
/// | Use | [LiveWithin]
8484
/// -----
8585
///
86+
/// ---------------------------------------------------------------------------
87+
///
88+
/// "Use points" are the instructions that "generate" liveness for a given
89+
/// operand. A generalized use point visitor would look like this:
90+
///
91+
/// Given an \p operand, visit the use points relevant for liveness. For most
92+
/// operands, this is simply the user instruction. For scoped operands, each
93+
/// scope-ending instruction is a separate use point.
94+
/// template<typename Operation>
95+
/// inline bool visitUsePoints(Operand *use, Operation visitUsePoint) {
96+
/// // Handle TrivialUse operands: begin_access & store_borrow address
97+
/// // Handle InteriorPointer operands: store_borrow source
98+
/// if (auto scopedAddress = ScopedAddressValue::forUse(use)) {
99+
/// return scopedAddress.visitScopeEndingUses(visitUsePoint);
100+
/// }
101+
/// // Handle Borrow operands...
102+
/// // Handles borrow scope introducers: begin_borrow & load_borrow.
103+
/// // Handles guaranteed return values: begin_apply.
104+
/// if (!BorrowingOperand(operand).visitScopeEndingUses([this](Operand *end) {
105+
/// if (!visitUsePoint(end))
106+
/// return false;
107+
/// return true;
108+
/// }
109+
/// return visitUsePoint(use);
110+
/// }
111+
///
112+
/// The visitors that switch on OperandOwnership use a specialized
113+
/// implementation because each case above is specific to an ownership case.
86114
//===----------------------------------------------------------------------===//
87115

88116
#ifndef SWIFT_SILOPTIMIZER_UTILS_PRUNEDLIVENESS_H

include/swift/SIL/ScopedAddressUtils.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,15 @@ struct ScopedAddressValue {
7474
return kind != ScopedAddressValueKind::Invalid && value;
7575
}
7676

77+
// Both the store_borrow source and address operands are effectively used for
78+
// the duration of the address scope.
79+
static ScopedAddressValue forUse(Operand *use) {
80+
if (auto svi = dyn_cast<SingleValueInstruction>(use->getUser()))
81+
return ScopedAddressValue(svi);
82+
83+
return ScopedAddressValue();
84+
}
85+
7786
void print(llvm::raw_ostream &os) const;
7887
SWIFT_DEBUG_DUMP { print(llvm::dbgs()); }
7988

lib/SIL/Utils/PrunedLiveness.cpp

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -136,14 +136,12 @@ InnerBorrowKind PrunedLiveness::updateForBorrowingOperand(Operand *operand) {
136136
AddressUseKind PrunedLiveness::checkAndUpdateInteriorPointer(Operand *operand) {
137137
assert(operand->getOperandOwnership() == OperandOwnership::InteriorPointer);
138138

139-
if (auto *svi = dyn_cast<SingleValueInstruction>(operand->getUser())) {
140-
if (auto scopedAddress = ScopedAddressValue(svi)) {
141-
scopedAddress.visitScopeEndingUses([this](Operand *end) {
142-
updateForUse(end->getUser(), /*lifetimeEnding*/ false);
143-
return true;
144-
});
145-
return AddressUseKind::NonEscaping;
146-
}
139+
if (auto scopedAddress = ScopedAddressValue::forUse(operand)) {
140+
scopedAddress.visitScopeEndingUses([this](Operand *end) {
141+
updateForUse(end->getUser(), /*lifetimeEnding*/ false);
142+
return true;
143+
});
144+
return AddressUseKind::NonEscaping;
147145
}
148146
// FIXME: findTransitiveUses should be a visitor so we're not recursively
149147
// allocating use vectors and potentially merging the use points.
@@ -152,6 +150,10 @@ AddressUseKind PrunedLiveness::checkAndUpdateInteriorPointer(Operand *operand) {
152150
for (auto *use : uses) {
153151
updateForUse(use->getUser(), /*lifetimeEnding*/ false);
154152
}
153+
if (uses.empty()) {
154+
// Handle a dead address
155+
updateForUse(operand->getUser(), /*lifetimeEnding*/ false);
156+
}
155157
return useKind;
156158
}
157159

@@ -303,6 +305,16 @@ SimpleLiveRangeSummary PrunedLiveRange<LivenessWithDefs>::updateForDef(SILValue
303305
});
304306
break;
305307
}
308+
case OperandOwnership::TrivialUse: {
309+
if (auto scopedAddress = ScopedAddressValue::forUse(use)) {
310+
scopedAddress.visitScopeEndingUses([this](Operand *end) {
311+
updateForUse(end->getUser(), /*lifetimeEnding*/false);
312+
return true;
313+
});
314+
}
315+
updateForUse(use->getUser(), /*lifetimeEnding*/false);
316+
break;
317+
}
306318
default:
307319
updateForUse(use->getUser(), use->isLifetimeEnding());
308320
break;

test/SILOptimizer/ossa_lifetime_analysis.sil

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ case some(T)
2222
class C {}
2323
class D {
2424
var object: C
25+
@_borrowed @_hasStorage var borrowed: C { get set }
2526
}
2627

2728
// CHECK-LABEL: @testSelfLoop
@@ -132,3 +133,45 @@ bb0(%0 : @owned $D):
132133
%99 = tuple()
133134
return %99 : $()
134135
}
136+
137+
// CHECK-LABEL: @testGuaranteedResult
138+
// CHECK: SSA lifetime analysis: %0 = argument of bb0 : $D
139+
// CHECK: bb0: LiveWithin
140+
// CHECK: last user: end_apply
141+
sil [ossa] @testGuaranteedResult : $@convention(thin) (@guaranteed D) -> () {
142+
bb0(%0 : @guaranteed $D):
143+
debug_value [trace] %0 : $D
144+
%2 = class_method %0 : $D, #D.borrowed!read : (D) -> () -> (), $@yield_once @convention(method) (@guaranteed D) -> @yields @guaranteed C
145+
(%3, %4) = begin_apply %2(%0) : $@yield_once @convention(method) (@guaranteed D) -> @yields @guaranteed C
146+
end_apply %4
147+
%99 = tuple()
148+
return %99 : $()
149+
}
150+
151+
// CHECK-LABEL: @testScopedAddress
152+
// CHECK: SSA lifetime analysis: %{{.*}} = ref_element_addr %0
153+
// CHECK: bb0: LiveWithin
154+
// CHECK: last user: end_access
155+
sil [ossa] @testScopedAddress : $@convention(thin) (@guaranteed D) -> () {
156+
bb0(%0 : @guaranteed $D):
157+
%f = ref_element_addr %0 : $D, #D.object
158+
debug_value [trace] %f : $*C
159+
%access = begin_access [read] [static] %f : $*C
160+
%o = load [copy] %access : $*C
161+
end_access %access : $*C
162+
destroy_value %o : $C
163+
%99 = tuple()
164+
return %99 : $()
165+
}
166+
167+
// CHECK-LABEL: @testDeadAddress
168+
// CHECK: SSA lifetime analysis: %0 = argument of bb0 : $D
169+
// CHECK: bb0: LiveWithin
170+
// CHECK: last user: %{{.*}} = ref_element_addr
171+
sil [ossa] @testDeadAddress : $@convention(thin) (@guaranteed D) -> () {
172+
bb0(%0 : @guaranteed $D):
173+
debug_value [trace] %0 : $D
174+
%f = ref_element_addr %0 : $D, #D.object
175+
%99 = tuple()
176+
return %99 : $()
177+
}

0 commit comments

Comments
 (0)