Skip to content

Commit a95e018

Browse files
authored
Merge pull request #60710 from gottesmm/pr-1212688e62da727448883168d77ba9b98fa6d4dc
[pruned-liveness] Teach PrunedLiveBlocks how to handle liveness for multiple bits.
2 parents 7d94c24 + e51e17f commit a95e018

File tree

2 files changed

+193
-54
lines changed

2 files changed

+193
-54
lines changed

include/swift/SIL/PrunedLiveness.h

Lines changed: 149 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@
9696

9797
#include "swift/SIL/SILBasicBlock.h"
9898
#include "llvm/ADT/MapVector.h"
99+
#include "llvm/ADT/PointerIntPair.h"
100+
#include "llvm/ADT/SmallVector.h"
99101

100102
namespace swift {
101103

@@ -115,6 +117,14 @@ class DeadEndBlocks;
115117
/// which the def block's predecessors incorrectly remain dead. This situation
116118
/// could be handled by adding an updateForUseBeforeFirstDef() API.
117119
///
120+
/// We allow for multiple bits of liveness information to be tracked by
121+
/// internally using a SmallBitVector. The multiple bit tracking is useful when
122+
/// tracking state for multiple fields of the same root value. To do this, we
123+
/// actually track 2 bits per actual needed bit so we can represent 3 Dead,
124+
/// LiveOut, LiveWithin. This was previously unnecessary since we could just
125+
/// represent dead by not having liveness state for a block. With multiple bits
126+
/// possible this is no longer true.
127+
///
118128
/// TODO: This can be made space-efficient if all clients can maintain a block
119129
/// numbering so liveness info can be represented as bitsets across the blocks.
120130
class PrunedLiveBlocks {
@@ -133,22 +143,96 @@ class PrunedLiveBlocks {
133143
///
134144
/// LiveOut blocks are live on at least one successor path. LiveOut blocks may
135145
/// or may not contain defs or uses.
136-
enum IsLive { Dead, LiveWithin, LiveOut };
146+
///
147+
/// NOTE: The values below for Dead, LiveWithin, LiveOut were picked to ensure
148+
/// that given a 2 bit representation of the value, a value is Dead if the
149+
/// first bit is 0 and is LiveOut if the second bit is set.
150+
enum IsLive {
151+
Dead = 0,
152+
LiveWithin = 1,
153+
LiveOut = 3,
154+
};
155+
156+
/// A bit vector that stores information about liveness. This is composed
157+
/// with SmallBitVector since it contains two bits per liveness so that it
158+
/// can represent 3 states, Dead, LiveWithin, LiveOut. We take advantage of
159+
/// their numeric values to make testing easier \see documentation on IsLive.
160+
class LivenessSmallBitVector {
161+
SmallBitVector bits;
162+
163+
public:
164+
LivenessSmallBitVector() : bits() {}
165+
166+
void init(unsigned numBits) {
167+
assert(bits.size() == 0);
168+
assert(numBits != 0);
169+
bits.resize(numBits * 2);
170+
}
171+
172+
unsigned size() const { return bits.size() / 2; }
173+
174+
IsLive getLiveness(unsigned bitNo) const {
175+
SmallVector<IsLive, 1> foundLiveness;
176+
getLiveness(bitNo, bitNo + 1, foundLiveness);
177+
return foundLiveness[0];
178+
}
179+
180+
void getLiveness(unsigned startBitNo, unsigned endBitNo,
181+
SmallVectorImpl<IsLive> &resultingFoundLiveness) const {
182+
unsigned actualStartBitNo = startBitNo * 2;
183+
unsigned actualEndBitNo = endBitNo * 2;
184+
185+
// NOTE: We pad both before/after with Dead to ensure that we are
186+
// returning an array that acts as a bit mask and thus can be directly
187+
// compared against other such bitmasks. This invariant is used when
188+
// computing boundaries.
189+
for (unsigned i = 0; i != startBitNo; ++i) {
190+
resultingFoundLiveness.push_back(Dead);
191+
}
192+
for (unsigned i = actualStartBitNo, e = actualEndBitNo; i != e; i += 2) {
193+
if (!bits[i]) {
194+
resultingFoundLiveness.push_back(Dead);
195+
continue;
196+
}
197+
198+
resultingFoundLiveness.push_back(bits[i + 1] ? LiveOut : LiveWithin);
199+
}
200+
for (unsigned i = endBitNo, e = size(); i != e; ++i) {
201+
resultingFoundLiveness.push_back(Dead);
202+
}
203+
}
204+
205+
void setLiveness(unsigned startBitNo, unsigned endBitNo, IsLive isLive) {
206+
for (unsigned i = startBitNo * 2, e = endBitNo * 2; i != e; i += 2) {
207+
bits[i] = isLive & 1;
208+
bits[i + 1] = isLive & 2;
209+
}
210+
}
211+
212+
void setLiveness(unsigned bitNo, IsLive isLive) {
213+
setLiveness(bitNo, bitNo + 1, isLive);
214+
}
215+
};
137216

138217
private:
139-
// Map all blocks in which current def is live to a flag indicating whether
140-
// the value is also liveout of the block.
141-
llvm::SmallDenseMap<SILBasicBlock *, bool, 4> liveBlocks;
218+
/// Map all blocks in which current def is live to a SmallBitVector indicating
219+
/// whether the value represented by said bit is also liveout of the block.
220+
llvm::SmallDenseMap<SILBasicBlock *, LivenessSmallBitVector, 4> liveBlocks;
142221

143-
// Optional vector of live blocks for clients that deterministically iterate.
222+
/// Number of bits of liveness to track. By default 1. Used to track multiple
223+
/// liveness bits.
224+
unsigned numBitsToTrack;
225+
226+
/// Optional vector of live blocks for clients that deterministically iterate.
144227
SmallVectorImpl<SILBasicBlock *> *discoveredBlocks;
145228

146-
// Once the first use has been seen, no definitions can be added.
229+
/// Once the first use has been seen, no definitions can be added.
147230
SWIFT_ASSERT_ONLY_DECL(bool seenUse = false);
148231

149232
public:
150-
PrunedLiveBlocks(SmallVectorImpl<SILBasicBlock *> *discoveredBlocks = nullptr)
151-
: discoveredBlocks(discoveredBlocks) {
233+
PrunedLiveBlocks(unsigned numBitsToTrack,
234+
SmallVectorImpl<SILBasicBlock *> *discoveredBlocks = nullptr)
235+
: numBitsToTrack(numBitsToTrack), discoveredBlocks(discoveredBlocks) {
152236
assert(!discoveredBlocks || discoveredBlocks->empty());
153237
}
154238

@@ -167,36 +251,77 @@ class PrunedLiveBlocks {
167251
return *discoveredBlocks;
168252
}
169253

170-
void initializeDefBlock(SILBasicBlock *defBB) {
171-
markBlockLive(defBB, LiveWithin);
254+
void initializeDefBlock(SILBasicBlock *defBB, unsigned bitNo) {
255+
markBlockLive(defBB, bitNo, LiveWithin);
256+
}
257+
258+
void initializeDefBlock(SILBasicBlock *defBB, unsigned startBitNo,
259+
unsigned endBitNo) {
260+
markBlockLive(defBB, startBitNo, endBitNo, LiveWithin);
172261
}
173262

174263
/// Update this liveness result for a single use.
175-
IsLive updateForUse(SILInstruction *user);
264+
IsLive updateForUse(SILInstruction *user, unsigned bitNo) {
265+
SmallVector<IsLive, 1> resultingLiveness;
266+
updateForUse(user, bitNo, bitNo + 1, resultingLiveness);
267+
return resultingLiveness[0];
268+
}
269+
270+
/// Update this range of liveness results for a single use.
271+
void updateForUse(SILInstruction *user, unsigned startBitNo,
272+
unsigned endBitNo,
273+
SmallVectorImpl<IsLive> &resultingLiveness);
176274

177-
IsLive getBlockLiveness(SILBasicBlock *bb) const {
275+
IsLive getBlockLiveness(SILBasicBlock *bb, unsigned bitNo) const {
276+
SmallVector<IsLive, 1> isLive;
277+
getBlockLiveness(bb, bitNo, bitNo + 1, isLive);
278+
return isLive[0];
279+
}
280+
281+
void getBlockLiveness(SILBasicBlock *bb, unsigned startBitNo,
282+
unsigned endBitNo,
283+
SmallVectorImpl<IsLive> &foundLivenessInfo) const {
178284
auto liveBlockIter = liveBlocks.find(bb);
179-
if (liveBlockIter == liveBlocks.end())
180-
return Dead;
181-
return liveBlockIter->second ? LiveOut : LiveWithin;
285+
if (liveBlockIter == liveBlocks.end()) {
286+
for (unsigned i : range(numBitsToTrack)) {
287+
(void)i;
288+
foundLivenessInfo.push_back(Dead);
289+
}
290+
return;
291+
}
292+
293+
assert(liveBlockIter->second.size() == 1);
294+
liveBlockIter->second.getLiveness(startBitNo, endBitNo, foundLivenessInfo);
182295
}
183296

184297
protected:
185-
void markBlockLive(SILBasicBlock *bb, IsLive isLive) {
298+
void markBlockLive(SILBasicBlock *bb, unsigned bitNo, IsLive isLive) {
299+
markBlockLive(bb, bitNo, bitNo + 1, isLive);
300+
}
301+
302+
void markBlockLive(SILBasicBlock *bb, unsigned startBitNo, unsigned endBitNo,
303+
IsLive isLive) {
186304
assert(isLive != Dead && "erasing live blocks isn't implemented.");
187-
bool isLiveOut = (isLive == LiveOut);
188305
auto iterAndInserted =
189-
liveBlocks.insert(std::make_pair(bb, isLiveOut));
306+
liveBlocks.insert(std::make_pair(bb, LivenessSmallBitVector()));
190307
if (iterAndInserted.second) {
308+
// We initialize the size of the small bit vector here rather than in
309+
// liveBlocks.insert above to prevent us from allocating upon failure if
310+
// we have more than SmallBitVector's small size number of bits.
311+
auto &insertedBV = iterAndInserted.first->getSecond();
312+
insertedBV.init(numBitsToTrack);
313+
insertedBV.setLiveness(startBitNo, endBitNo, isLive);
191314
if (discoveredBlocks)
192315
discoveredBlocks->push_back(bb);
193-
} else if (isLiveOut) {
316+
} else if (isLive == LiveOut) {
194317
// Update the existing entry to be live-out.
195-
iterAndInserted.first->getSecond() = true;
318+
iterAndInserted.first->getSecond().setLiveness(startBitNo, endBitNo,
319+
LiveOut);
196320
}
197321
}
198322

199-
void computeUseBlockLiveness(SILBasicBlock *userBB);
323+
void computeUseBlockLiveness(SILBasicBlock *userBB, unsigned startBitNo,
324+
unsigned endBitNo);
200325
};
201326

202327
/// PrunedLiveness tracks PrunedLiveBlocks along with "interesting" use
@@ -252,7 +377,7 @@ class PrunedLiveness {
252377
PrunedLiveness(SmallVectorImpl<SILBasicBlock *> *discoveredBlocks = nullptr,
253378
SmallSetVector<SILInstruction *, 8>
254379
*nonLifetimeEndingUsesInLiveOut = nullptr)
255-
: liveBlocks(discoveredBlocks),
380+
: liveBlocks(1 /*num bits*/, discoveredBlocks),
256381
nonLifetimeEndingUsesInLiveOut(nonLifetimeEndingUsesInLiveOut) {}
257382

258383
bool empty() const {
@@ -317,7 +442,7 @@ class PrunedLiveness {
317442
}
318443

319444
void initializeDefBlock(SILBasicBlock *defBB) {
320-
liveBlocks.initializeDefBlock(defBB);
445+
liveBlocks.initializeDefBlock(defBB, 0);
321446
}
322447

323448
/// For flexibility, \p lifetimeEnding is provided by the
@@ -335,7 +460,7 @@ class PrunedLiveness {
335460
void extendAcrossLiveness(PrunedLiveness &otherLiveness);
336461

337462
PrunedLiveBlocks::IsLive getBlockLiveness(SILBasicBlock *bb) const {
338-
return liveBlocks.getBlockLiveness(bb);
463+
return liveBlocks.getBlockLiveness(bb, 0);
339464
}
340465

341466
enum IsInterestingUser { NonUser, NonLifetimeEndingUse, LifetimeEndingUse };

lib/SIL/Utils/PrunedLiveness.cpp

Lines changed: 44 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "swift/SIL/PrunedLiveness.h"
14+
#include "swift/Basic/Defer.h"
1415
#include "swift/SIL/BasicBlockDatastructures.h"
1516
#include "swift/SIL/BasicBlockUtils.h"
1617
#include "swift/SIL/OwnershipUtils.h"
@@ -19,29 +20,36 @@ using namespace swift;
1920

2021
/// Mark blocks live during a reverse CFG traversal from one specific block
2122
/// containing a user.
22-
void PrunedLiveBlocks::computeUseBlockLiveness(SILBasicBlock *userBB) {
23-
// If we are visiting this block, then it is not already LiveOut. Mark it
23+
void PrunedLiveBlocks::computeUseBlockLiveness(SILBasicBlock *userBB,
24+
unsigned startBitNo,
25+
unsigned endBitNo) {
26+
// If, we are visiting this block, then it is not already LiveOut. Mark it
2427
// LiveWithin to indicate a liveness boundary within the block.
25-
markBlockLive(userBB, LiveWithin);
28+
markBlockLive(userBB, startBitNo, endBitNo, LiveWithin);
2629

27-
SmallVector<SILBasicBlock *, 8> predBBWorklist({userBB});
28-
while (!predBBWorklist.empty()) {
29-
SILBasicBlock *bb = predBBWorklist.pop_back_val();
30+
SmallVector<IsLive, 8> predLivenessInfo;
31+
BasicBlockWorklist worklist(userBB->getFunction());
32+
worklist.push(userBB);
3033

34+
while (auto *block = worklist.pop()) {
3135
// The popped `bb` is live; now mark all its predecessors LiveOut.
3236
//
3337
// Traversal terminates at any previously visited block, including the
3438
// blocks initialized as definition blocks.
35-
for (auto *predBB : bb->getPredecessorBlocks()) {
36-
switch (getBlockLiveness(predBB)) {
37-
case Dead:
38-
predBBWorklist.push_back(predBB);
39-
LLVM_FALLTHROUGH;
40-
case LiveWithin:
41-
markBlockLive(predBB, LiveOut);
42-
break;
43-
case LiveOut:
44-
break;
39+
for (auto *predBlock : block->getPredecessorBlocks()) {
40+
SWIFT_DEFER { predLivenessInfo.clear(); };
41+
getBlockLiveness(predBlock, startBitNo, endBitNo, predLivenessInfo);
42+
for (unsigned i : indices(predLivenessInfo)) {
43+
switch (predLivenessInfo[i]) {
44+
case Dead:
45+
worklist.pushIfNotVisited(predBlock);
46+
LLVM_FALLTHROUGH;
47+
case LiveWithin:
48+
markBlockLive(predBlock, startBitNo, endBitNo, LiveOut);
49+
break;
50+
case LiveOut:
51+
break;
52+
}
4553
}
4654
}
4755
}
@@ -52,31 +60,37 @@ void PrunedLiveBlocks::computeUseBlockLiveness(SILBasicBlock *userBB) {
5260
/// Return the updated liveness of the \p use block (LiveOut or LiveWithin).
5361
///
5462
/// Terminators are not live out of the block.
55-
PrunedLiveBlocks::IsLive PrunedLiveBlocks::updateForUse(SILInstruction *user) {
63+
void PrunedLiveBlocks::updateForUse(
64+
SILInstruction *user, unsigned startBitNo, unsigned endBitNo,
65+
SmallVectorImpl<IsLive> &resultingLivenessInfo) {
5666
SWIFT_ASSERT_ONLY(seenUse = true);
5767

5868
auto *bb = user->getParent();
59-
switch (getBlockLiveness(bb)) {
60-
case LiveOut:
61-
return LiveOut;
62-
case LiveWithin:
63-
return LiveWithin;
64-
case Dead: {
65-
// This use block has not yet been marked live. Mark it and its predecessor
66-
// blocks live.
67-
computeUseBlockLiveness(bb);
68-
return getBlockLiveness(bb);
69-
}
69+
getBlockLiveness(bb, startBitNo, endBitNo, resultingLivenessInfo);
70+
71+
for (auto isLive : resultingLivenessInfo) {
72+
switch (isLive) {
73+
case LiveOut:
74+
case LiveWithin:
75+
continue;
76+
case Dead: {
77+
// This use block has not yet been marked live. Mark it and its
78+
// predecessor blocks live.
79+
computeUseBlockLiveness(bb, startBitNo, endBitNo);
80+
resultingLivenessInfo.clear();
81+
return getBlockLiveness(bb, startBitNo, endBitNo, resultingLivenessInfo);
82+
}
83+
}
84+
llvm_unreachable("covered switch");
7085
}
71-
llvm_unreachable("covered switch");
7286
}
7387

7488
//===----------------------------------------------------------------------===//
7589
// MARK: PrunedLiveness
7690
//===----------------------------------------------------------------------===//
7791

7892
void PrunedLiveness::updateForUse(SILInstruction *user, bool lifetimeEnding) {
79-
auto useBlockLive = liveBlocks.updateForUse(user);
93+
auto useBlockLive = liveBlocks.updateForUse(user, 0);
8094
// Record all uses of blocks on the liveness boundary. For blocks marked
8195
// LiveWithin, the boundary is considered to be the last use in the block.
8296
//

0 commit comments

Comments
 (0)