Skip to content

Commit e51e17f

Browse files
committed
[pruned-liveness] Change the internal bit-vector to use 2 bits to represent its state instead of 1 + missing value in DenseMap.
The reason why I am doing this is that in order to be able to use PrunedLivenessBlocks with multiple elements, we can no longer rely on dead being represented by a block not having any state, since a dead bit could have a neighboring live bit. That being said, the only place that this is used now is the current PrunedLiveness implementation which only stores a single bit... but that still at least exercises the code and lets us know that it works.
1 parent 04144d8 commit e51e17f

File tree

2 files changed

+171
-46
lines changed

2 files changed

+171
-46
lines changed

include/swift/SIL/PrunedLiveness.h

Lines changed: 130 additions & 17 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

@@ -116,9 +118,12 @@ class DeadEndBlocks;
116118
/// could be handled by adding an updateForUseBeforeFirstDef() API.
117119
///
118120
/// We allow for multiple bits of liveness information to be tracked by
119-
/// internally using a SmallBitVector. We default to only tracking a single
120-
/// bit. The multiple bit tracking is useful when tracking state for multiple
121-
/// fields of the same root value.
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.
122127
///
123128
/// TODO: This can be made space-efficient if all clients can maintain a block
124129
/// numbering so liveness info can be represented as bitsets across the blocks.
@@ -138,12 +143,81 @@ class PrunedLiveBlocks {
138143
///
139144
/// LiveOut blocks are live on at least one successor path. LiveOut blocks may
140145
/// or may not contain defs or uses.
141-
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+
};
142216

143217
private:
144218
/// Map all blocks in which current def is live to a SmallBitVector indicating
145219
/// whether the value represented by said bit is also liveout of the block.
146-
llvm::SmallDenseMap<SILBasicBlock *, SmallBitVector, 4> liveBlocks;
220+
llvm::SmallDenseMap<SILBasicBlock *, LivenessSmallBitVector, 4> liveBlocks;
147221

148222
/// Number of bits of liveness to track. By default 1. Used to track multiple
149223
/// liveness bits.
@@ -178,37 +252,76 @@ class PrunedLiveBlocks {
178252
}
179253

180254
void initializeDefBlock(SILBasicBlock *defBB, unsigned bitNo) {
181-
markBlockLive(defBB, LiveWithin, bitNo);
255+
markBlockLive(defBB, bitNo, LiveWithin);
256+
}
257+
258+
void initializeDefBlock(SILBasicBlock *defBB, unsigned startBitNo,
259+
unsigned endBitNo) {
260+
markBlockLive(defBB, startBitNo, endBitNo, LiveWithin);
182261
}
183262

184263
/// Update this liveness result for a single use.
185-
IsLive updateForUse(SILInstruction *user, unsigned bitNo);
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);
186274

187275
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 {
188284
auto liveBlockIter = liveBlocks.find(bb);
189-
if (liveBlockIter == liveBlocks.end())
190-
return Dead;
285+
if (liveBlockIter == liveBlocks.end()) {
286+
for (unsigned i : range(numBitsToTrack)) {
287+
(void)i;
288+
foundLivenessInfo.push_back(Dead);
289+
}
290+
return;
291+
}
292+
191293
assert(liveBlockIter->second.size() == 1);
192-
return liveBlockIter->second[bitNo] ? LiveOut : LiveWithin;
294+
liveBlockIter->second.getLiveness(startBitNo, endBitNo, foundLivenessInfo);
193295
}
194296

195297
protected:
196-
void markBlockLive(SILBasicBlock *bb, IsLive isLive, unsigned bitNo) {
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) {
197304
assert(isLive != Dead && "erasing live blocks isn't implemented.");
198-
bool isLiveOut = (isLive == LiveOut);
199305
auto iterAndInserted =
200-
liveBlocks.insert(std::make_pair(bb, SmallBitVector(numBitsToTrack)));
306+
liveBlocks.insert(std::make_pair(bb, LivenessSmallBitVector()));
201307
if (iterAndInserted.second) {
202-
iterAndInserted.first->getSecond()[bitNo] = isLiveOut;
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);
203314
if (discoveredBlocks)
204315
discoveredBlocks->push_back(bb);
205-
} else if (isLiveOut) {
316+
} else if (isLive == LiveOut) {
206317
// Update the existing entry to be live-out.
207-
iterAndInserted.first->getSecond()[bitNo] = true;
318+
iterAndInserted.first->getSecond().setLiveness(startBitNo, endBitNo,
319+
LiveOut);
208320
}
209321
}
210322

211-
void computeUseBlockLiveness(SILBasicBlock *userBB, unsigned bitNo);
323+
void computeUseBlockLiveness(SILBasicBlock *userBB, unsigned startBitNo,
324+
unsigned endBitNo);
212325
};
213326

214327
/// PrunedLiveness tracks PrunedLiveBlocks along with "interesting" use

lib/SIL/Utils/PrunedLiveness.cpp

Lines changed: 41 additions & 29 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"
@@ -20,29 +21,35 @@ using namespace swift;
2021
/// Mark blocks live during a reverse CFG traversal from one specific block
2122
/// containing a user.
2223
void PrunedLiveBlocks::computeUseBlockLiveness(SILBasicBlock *userBB,
23-
unsigned bitNo) {
24+
unsigned startBitNo,
25+
unsigned endBitNo) {
2426
// If, we are visiting this block, then it is not already LiveOut. Mark it
2527
// LiveWithin to indicate a liveness boundary within the block.
26-
markBlockLive(userBB, LiveWithin, bitNo);
28+
markBlockLive(userBB, startBitNo, endBitNo, LiveWithin);
2729

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

34+
while (auto *block = worklist.pop()) {
3235
// The popped `bb` is live; now mark all its predecessors LiveOut.
3336
//
3437
// Traversal terminates at any previously visited block, including the
3538
// blocks initialized as definition blocks.
36-
for (auto *predBB : bb->getPredecessorBlocks()) {
37-
switch (getBlockLiveness(predBB, bitNo)) {
38-
case Dead:
39-
predBBWorklist.push_back(predBB);
40-
LLVM_FALLTHROUGH;
41-
case LiveWithin:
42-
markBlockLive(predBB, LiveOut, bitNo);
43-
break;
44-
case LiveOut:
45-
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+
}
4653
}
4754
}
4855
}
@@ -53,24 +60,29 @@ void PrunedLiveBlocks::computeUseBlockLiveness(SILBasicBlock *userBB,
5360
/// Return the updated liveness of the \p use block (LiveOut or LiveWithin).
5461
///
5562
/// Terminators are not live out of the block.
56-
PrunedLiveBlocks::IsLive PrunedLiveBlocks::updateForUse(SILInstruction *user,
57-
unsigned bitNo) {
63+
void PrunedLiveBlocks::updateForUse(
64+
SILInstruction *user, unsigned startBitNo, unsigned endBitNo,
65+
SmallVectorImpl<IsLive> &resultingLivenessInfo) {
5866
SWIFT_ASSERT_ONLY(seenUse = true);
5967

6068
auto *bb = user->getParent();
61-
switch (getBlockLiveness(bb, bitNo)) {
62-
case LiveOut:
63-
return LiveOut;
64-
case LiveWithin:
65-
return LiveWithin;
66-
case Dead: {
67-
// This use block has not yet been marked live. Mark it and its predecessor
68-
// blocks live.
69-
computeUseBlockLiveness(bb, bitNo);
70-
return getBlockLiveness(bb, bitNo);
71-
}
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");
7285
}
73-
llvm_unreachable("covered switch");
7486
}
7587

7688
//===----------------------------------------------------------------------===//

0 commit comments

Comments
 (0)