Skip to content

Commit 82eda71

Browse files
committed
Update PrunedLiveness
- To detect instructions occuring before definition - To handle reborrows
1 parent f0ef917 commit 82eda71

File tree

3 files changed

+113
-29
lines changed

3 files changed

+113
-29
lines changed

include/swift/SIL/PrunedLiveness.h

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,6 @@ class PrunedLiveBlocks {
164164
}
165165

166166
void initializeDefBlock(SILBasicBlock *defBB) {
167-
assert(!seenUse && "cannot initialize more defs with partial liveness");
168167
markBlockLive(defBB, LiveWithin);
169168
}
170169

@@ -229,6 +228,15 @@ class PrunedLiveness {
229228
/// ending uses that extend liveness into a loop body.
230229
SmallSetVector<SILInstruction *, 8> *nonLifetimeEndingUsesInLiveOut;
231230

231+
private:
232+
bool isWithinBoundaryHelper(SILInstruction *inst, SILValue def) const;
233+
234+
bool areUsesWithinBoundaryHelper(ArrayRef<Operand *> uses, SILValue def,
235+
DeadEndBlocks *deadEndBlocks) const;
236+
237+
bool areUsesOutsideBoundaryHelper(ArrayRef<Operand *> uses, SILValue def,
238+
DeadEndBlocks *deadEndBlocks) const;
239+
232240
public:
233241
PrunedLiveness(SmallVectorImpl<SILBasicBlock *> *discoveredBlocks = nullptr,
234242
SmallSetVector<SILInstruction *, 8>
@@ -334,14 +342,26 @@ class PrunedLiveness {
334342
/// client already knows that inst occurs after the start of liveness.
335343
bool isWithinBoundary(SILInstruction *inst) const;
336344

345+
/// Return true if \p inst occurs in between the definition \p def and the
346+
/// liveness boundary.
347+
bool isWithinBoundaryOfDef(SILInstruction *inst, SILValue def) const;
348+
337349
/// \p deadEndBlocks is optional.
338350
bool areUsesWithinBoundary(ArrayRef<Operand *> uses,
339351
DeadEndBlocks *deadEndBlocks) const;
340352

353+
/// \p deadEndBlocks is optional.
354+
bool areUsesWithinBoundaryOfDef(ArrayRef<Operand *> uses, SILValue def,
355+
DeadEndBlocks *deadEndBlocks) const;
356+
341357
/// \p deadEndBlocks is optional.
342358
bool areUsesOutsideBoundary(ArrayRef<Operand *> uses,
343359
DeadEndBlocks *deadEndBlocks) const;
344360

361+
/// \p deadEndBlocks is optional.
362+
bool areUsesOutsideBoundaryOfDef(ArrayRef<Operand *> uses, SILValue def,
363+
DeadEndBlocks *deadEndBlocks) const;
364+
345365
/// Compute liveness for a single SSA definition.
346366
void computeSSALiveness(SILValue def);
347367
};

lib/SIL/Utils/OwnershipUtils.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -689,8 +689,15 @@ llvm::raw_ostream &swift::operator<<(llvm::raw_ostream &os,
689689
/// Add this scopes live blocks into the PrunedLiveness result.
690690
void BorrowedValue::computeLiveness(PrunedLiveness &liveness) const {
691691
liveness.initializeDefBlock(value->getParentBlock());
692-
visitLocalScopeEndingUses([&](Operand *endOp) {
693-
liveness.updateForUse(endOp->getUser(), true);
692+
visitTransitiveLifetimeEndingUses([&](Operand *endOp) {
693+
if (endOp->getOperandOwnership() == OperandOwnership::EndBorrow) {
694+
liveness.updateForUse(endOp->getUser(), /*lifetimeEnding*/ true);
695+
return true;
696+
}
697+
assert(endOp->getOperandOwnership() == OperandOwnership::Reborrow);
698+
auto *succBlock = cast<BranchInst>(endOp->getUser())->getDestBB();
699+
liveness.initializeDefBlock(succBlock);
700+
liveness.updateForUse(endOp->getUser(), /*lifetimeEnding*/ false);
694701
return true;
695702
});
696703
}

lib/SIL/Utils/PrunedLiveness.cpp

Lines changed: 83 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -127,67 +127,124 @@ bool PrunedLiveness::updateForBorrowingOperand(Operand *op) {
127127
}
128128

129129
void PrunedLiveness::extendAcrossLiveness(PrunedLiveness &otherLivesness) {
130-
// update this liveness for all the interesting users in otherLivesness.
130+
// update this liveness for all the interesting users in otherLiveness.
131131
for (std::pair<SILInstruction *, bool> userAndEnd : otherLivesness.users) {
132132
updateForUse(userAndEnd.first, userAndEnd.second);
133133
}
134134
}
135135

136-
bool PrunedLiveness::isWithinBoundary(SILInstruction *inst) const {
136+
bool PrunedLiveness::isWithinBoundaryHelper(SILInstruction *inst,
137+
SILValue def) const {
137138
SILBasicBlock *block = inst->getParent();
139+
140+
/// Returns true if \p inst is before \p def in this block.
141+
auto foundInBlockBeforeDef = [](SILInstruction *inst, SILBasicBlock *block,
142+
SILValue def) {
143+
if (!def || def->getParentBlock() != block) {
144+
return false;
145+
}
146+
auto *defInst = def->getDefiningInstruction();
147+
if (!defInst) {
148+
return false;
149+
}
150+
// Check if instruction is before the definition
151+
for (SILInstruction &it :
152+
make_range(block->begin(), defInst->getIterator())) {
153+
if (&it == inst) {
154+
return true;
155+
}
156+
}
157+
return false;
158+
};
159+
138160
switch (getBlockLiveness(block)) {
139161
case PrunedLiveBlocks::Dead:
140162
return false;
141-
case PrunedLiveBlocks::LiveWithin:
142-
break;
143163
case PrunedLiveBlocks::LiveOut:
144-
return true;
145-
}
146-
// The boundary is within this block. This instruction is before the boundary
147-
// iff any interesting uses occur after it.
148-
for (SILInstruction &inst :
164+
return !foundInBlockBeforeDef(inst, block, def);
165+
case PrunedLiveBlocks::LiveWithin:
166+
if (foundInBlockBeforeDef(inst, block, def)) {
167+
return false;
168+
}
169+
// The boundary is within this block. This instruction is before the
170+
// boundary iff any interesting uses occur after it.
171+
for (SILInstruction &it :
149172
make_range(std::next(inst->getIterator()), block->end())) {
150-
switch (isInterestingUser(&inst)) {
151-
case PrunedLiveness::NonUser:
152-
break;
153-
case PrunedLiveness::NonLifetimeEndingUse:
154-
case PrunedLiveness::LifetimeEndingUse:
155-
return true;
173+
switch (isInterestingUser(&it)) {
174+
case PrunedLiveness::NonUser:
175+
break;
176+
case PrunedLiveness::NonLifetimeEndingUse:
177+
case PrunedLiveness::LifetimeEndingUse:
178+
return true;
179+
}
156180
}
181+
return false;
157182
}
158-
return false;
159183
}
160184

161-
bool PrunedLiveness::areUsesWithinBoundary(ArrayRef<Operand *> uses,
162-
DeadEndBlocks *deadEndBlocks) const {
185+
bool PrunedLiveness::isWithinBoundary(SILInstruction *inst) const {
186+
return isWithinBoundaryHelper(inst, /*def*/ SILValue());
187+
}
188+
189+
bool PrunedLiveness::isWithinBoundaryOfDef(SILInstruction *inst,
190+
SILValue def) const {
191+
return isWithinBoundaryHelper(inst, def);
192+
}
193+
194+
bool PrunedLiveness::areUsesWithinBoundaryHelper(
195+
ArrayRef<Operand *> uses, SILValue def,
196+
DeadEndBlocks *deadEndBlocks) const {
163197
auto checkDeadEnd = [deadEndBlocks](SILInstruction *inst) {
164198
return deadEndBlocks && deadEndBlocks->isDeadEnd(inst->getParent());
165199
};
166200

167201
for (auto *use : uses) {
168202
auto *user = use->getUser();
169-
if (!isWithinBoundary(user) && !checkDeadEnd(user))
203+
if (!isWithinBoundaryHelper(user, def) && !checkDeadEnd(user))
170204
return false;
171205
}
172206
return true;
173207
}
174208

175-
bool PrunedLiveness::areUsesOutsideBoundary(
176-
ArrayRef<Operand *> uses, DeadEndBlocks *deadEndBlocks) const {
209+
bool PrunedLiveness::areUsesWithinBoundary(ArrayRef<Operand *> uses,
210+
DeadEndBlocks *deadEndBlocks) const {
211+
return areUsesWithinBoundaryHelper(uses, SILValue(), deadEndBlocks);
212+
}
213+
214+
bool PrunedLiveness::areUsesWithinBoundaryOfDef(
215+
ArrayRef<Operand *> uses, SILValue def,
216+
DeadEndBlocks *deadEndBlocks) const {
217+
return areUsesWithinBoundaryHelper(uses, def, deadEndBlocks);
218+
}
219+
220+
bool PrunedLiveness::areUsesOutsideBoundaryHelper(
221+
ArrayRef<Operand *> uses, SILValue def,
222+
DeadEndBlocks *deadEndBlocks) const {
177223
auto checkDeadEnd = [deadEndBlocks](SILInstruction *inst) {
178224
return deadEndBlocks && deadEndBlocks->isDeadEnd(inst->getParent());
179225
};
180226

181227
for (auto *use : uses) {
182228
auto *user = use->getUser();
183-
if (isWithinBoundary(user) || checkDeadEnd(user))
229+
if (isWithinBoundaryHelper(user, def) || checkDeadEnd(user))
184230
return false;
185231
}
186232
return true;
187233
}
188234

189-
// An SSA def meets all the criteria for pruned liveness--def dominates all uses
190-
// with no holes in the liverange. The lifetime-ending uses are also
235+
bool PrunedLiveness::areUsesOutsideBoundary(
236+
ArrayRef<Operand *> uses, DeadEndBlocks *deadEndBlocks) const {
237+
return areUsesOutsideBoundaryHelper(uses, SILValue(), deadEndBlocks);
238+
}
239+
240+
bool PrunedLiveness::areUsesOutsideBoundaryOfDef(
241+
ArrayRef<Operand *> uses, SILValue def,
242+
DeadEndBlocks *deadEndBlocks) const {
243+
return areUsesOutsideBoundaryHelper(uses, def, deadEndBlocks);
244+
}
245+
246+
// An SSA def meets all the criteria for pruned liveness--def dominates all
247+
// uses with no holes in the liverange. The lifetime-ending uses are also
191248
// recorded--destroy_value or end_borrow. However destroy_values may not
192249
// jointly-post dominate if dead-end blocks are present.
193250
void PrunedLiveness::computeSSALiveness(SILValue def) {
@@ -278,8 +335,8 @@ void PrunedLivenessBoundary::compute(const PrunedLiveness &liveness,
278335
// Process each block that has not been visited and is not LiveOut.
279336
switch (liveness.getBlockLiveness(bb)) {
280337
case PrunedLiveBlocks::LiveOut:
281-
// A lifetimeEndBlock may be determined to be LiveOut after analyzing the
282-
// extended liveness. It is irrelevant for finding the boundary.
338+
// A lifetimeEndBlock may be determined to be LiveOut after analyzing
339+
// the extended liveness. It is irrelevant for finding the boundary.
283340
break;
284341
case PrunedLiveBlocks::LiveWithin: {
285342
// The liveness boundary is inside this block. Insert a final destroy

0 commit comments

Comments
 (0)