Skip to content

Commit cbe856e

Browse files
committed
Cleanup PrunedLiveBlocks
Use BasicBlockBitfield to record per-block liveness state. This has been the intention since BasicBlockBitfield was first introduced. Remove the per-field bitfield from PrunedLiveBlocks. This (re)specializes the data structure for scalar liveness and drastically simplifies the implementation. This utility is fundamental to all ownership utilities. It will be on the critical path in many areas of the compiler, including at -Onone. It needs to be minimal and as easy as possible for compiler engineers to understand, investigate, and debug. This is in preparation for fixing bugs related to multi-def liveness as used by the move checker.
1 parent ae64ff5 commit cbe856e

12 files changed

+225
-352
lines changed

include/swift/SIL/PrunedLiveness.h

Lines changed: 58 additions & 174 deletions
Original file line numberDiff line numberDiff line change
@@ -155,14 +155,6 @@ class DeadEndBlocks;
155155
/// not dominated by a def block, then liveness will include the entry block,
156156
/// as if defined by a function argument
157157
///
158-
/// We allow for multiple bits of liveness information to be tracked by
159-
/// internally using a SmallBitVector. The multiple bit tracking is useful when
160-
/// tracking state for multiple fields of the same root value. To do this, we
161-
/// actually track 2 bits per actual needed bit so we can represent 3 Dead,
162-
/// LiveOut, LiveWithin. This was previously unnecessary since we could just
163-
/// represent dead by not having liveness state for a block. With multiple bits
164-
/// possible this is no longer true.
165-
///
166158
/// TODO: For efficiency, use BasicBlockBitfield rather than SmallDenseMap.
167159
class PrunedLiveBlocks {
168160
public:
@@ -181,210 +173,101 @@ class PrunedLiveBlocks {
181173
/// LiveOut blocks are live on at least one successor path. LiveOut blocks may
182174
/// or may not contain defs or uses.
183175
///
184-
/// NOTE: The values below for Dead, LiveWithin, LiveOut were picked to ensure
185-
/// that given a 2 bit representation of the value, a value is Dead if the
186-
/// first bit is 0 and is LiveOut if the second bit is set.
176+
/// NOTE: The values below for Dead, LiveWithin, LiveOut were picked to
177+
/// establish a lattice such that:
178+
/// - Dead is the initial state (zero bitfield)
179+
/// - Merging liveness information is a bitwise-or
187180
enum IsLive {
188181
Dead = 0,
189182
LiveWithin = 1,
190183
LiveOut = 3,
191184
};
192185

193-
/// A bit vector that stores information about liveness. This is composed
194-
/// with SmallBitVector since it contains two bits per liveness so that it
195-
/// can represent 3 states, Dead, LiveWithin, LiveOut. We take advantage of
196-
/// their numeric values to make testing easier \see documentation on IsLive.
197-
class LivenessSmallBitVector {
198-
SmallBitVector bits;
199-
200-
public:
201-
LivenessSmallBitVector() : bits() {}
202-
203-
void init(unsigned numBits) {
204-
assert(bits.size() == 0);
205-
assert(numBits != 0);
206-
bits.resize(numBits * 2);
207-
}
208-
209-
unsigned size() const { return bits.size() / 2; }
210-
211-
IsLive getLiveness(unsigned bitNo) const {
212-
if (!bits[bitNo * 2])
213-
return IsLive::Dead;
214-
return bits[bitNo * 2 + 1] ? LiveOut : LiveWithin;
215-
}
216-
217-
/// Returns the liveness in \p resultingFoundLiveness. We only return the
218-
/// bits for endBitNo - startBitNo.
219-
void getLiveness(unsigned startBitNo, unsigned endBitNo,
220-
SmallVectorImpl<IsLive> &resultingFoundLiveness) const {
221-
unsigned actualStartBitNo = startBitNo * 2;
222-
unsigned actualEndBitNo = endBitNo * 2;
223-
224-
for (unsigned i = actualStartBitNo, e = actualEndBitNo; i != e; i += 2) {
225-
if (!bits[i]) {
226-
resultingFoundLiveness.push_back(Dead);
227-
continue;
228-
}
229-
230-
resultingFoundLiveness.push_back(bits[i + 1] ? LiveOut : LiveWithin);
231-
}
232-
}
233-
234-
void setLiveness(unsigned startBitNo, unsigned endBitNo, IsLive isLive) {
235-
for (unsigned i = startBitNo * 2, e = endBitNo * 2; i != e; i += 2) {
236-
bits[i] = isLive & 1;
237-
bits[i + 1] = isLive & 2;
238-
}
239-
}
240-
241-
void setLiveness(unsigned bitNo, IsLive isLive) {
242-
setLiveness(bitNo, bitNo + 1, isLive);
243-
}
244-
};
245-
246186
private:
247-
/// Map all blocks in which current def is live to a SmallBitVector indicating
248-
/// whether the value represented by said bit is also liveout of the block.
249-
llvm::SmallDenseMap<SILBasicBlock *, LivenessSmallBitVector, 4> liveBlocks;
250-
251-
/// Number of bits of liveness to track. By default 1. Used to track multiple
252-
/// liveness bits.
253-
unsigned numBitsToTrack;
187+
/// Map all blocks to an IsLive state.
188+
BasicBlockBitfield liveBlocks;
254189

255190
/// Optional vector of live blocks for clients that deterministically iterate.
256-
SmallVectorImpl<SILBasicBlock *> *discoveredBlocks;
191+
SmallVectorImpl<SILBasicBlock *> *discoveredBlocks = nullptr;
192+
193+
/// Only a clean bitfield can be initialized.
194+
SWIFT_ASSERT_ONLY_DECL(bool cleanFlag = true);
257195

258-
/// Once the first use has been seen, no definitions can be added.
259-
SWIFT_ASSERT_ONLY_DECL(bool seenUse = false);
196+
/// Once the first def has been initialized, uses can be added.
197+
SWIFT_ASSERT_ONLY_DECL(bool initializedFlag = false);
260198

261199
public:
262-
PrunedLiveBlocks(unsigned numBitsToTrack,
200+
PrunedLiveBlocks(SILFunction *function,
263201
SmallVectorImpl<SILBasicBlock *> *discoveredBlocks = nullptr)
264-
: numBitsToTrack(numBitsToTrack), discoveredBlocks(discoveredBlocks) {
202+
: liveBlocks(function, 2), discoveredBlocks(discoveredBlocks) {
265203
assert(!discoveredBlocks || discoveredBlocks->empty());
266204
}
267205

268-
unsigned getNumBitsToTrack() const { return numBitsToTrack; }
269-
270-
bool empty() const { return liveBlocks.empty(); }
206+
bool isInitialized() const { return initializedFlag; }
271207

272-
void clear() {
273-
liveBlocks.clear();
274-
SWIFT_ASSERT_ONLY(seenUse = false);
208+
void invalidate() {
209+
initializedFlag = false;
210+
cleanFlag = false;
275211
}
276212

277-
unsigned numLiveBlocks() const { return liveBlocks.size(); }
213+
void initializeDiscoveredBlocks(
214+
SmallVectorImpl<SILBasicBlock *> *discoveredBlocks) {
215+
assert(!isInitialized() && "cannot reinitialize after blocks are live");
216+
217+
this->discoveredBlocks = discoveredBlocks;
218+
}
278219

279220
/// If the constructor was provided with a vector to populate, then this
280221
/// returns the list of all live blocks with no duplicates.
281222
ArrayRef<SILBasicBlock *> getDiscoveredBlocks() const {
282223
return *discoveredBlocks;
283224
}
284225

285-
void initializeDefBlock(SILBasicBlock *defBB, unsigned bitNo) {
286-
markBlockLive(defBB, bitNo, LiveWithin);
287-
}
288-
289-
void initializeDefBlock(SILBasicBlock *defBB, unsigned startBitNo,
290-
unsigned endBitNo) {
291-
markBlockLive(defBB, startBitNo, endBitNo, LiveWithin);
226+
void initializeDefBlock(SILBasicBlock *defBB) {
227+
initializedFlag = true;
228+
markBlockLive(defBB, LiveWithin);
292229
}
293230

294231
/// Update this liveness result for a single use.
295-
IsLive updateForUse(SILInstruction *user, unsigned bitNo) {
232+
IsLive updateForUse(SILInstruction *user) {
233+
assert(isInitialized() && "at least one definition must be initialized");
234+
296235
auto *block = user->getParent();
297-
auto liveness = getBlockLiveness(block, bitNo);
236+
auto liveness = getBlockLiveness(block);
237+
// If a block is already marked live, assume that liveness was propagated to
238+
// its predecessors. This assumes that uses will never be added above a def
239+
// in the same block.
298240
if (liveness != Dead)
299241
return liveness;
300-
computeScalarUseBlockLiveness(block, bitNo);
301-
return getBlockLiveness(block, bitNo);
302-
}
303242

304-
/// Update this range of liveness results for a single use.
305-
void updateForUse(SILInstruction *user, unsigned startBitNo,
306-
unsigned endBitNo,
307-
SmallVectorImpl<IsLive> &resultingLiveness);
308-
309-
IsLive getBlockLiveness(SILBasicBlock *bb, unsigned bitNo) const {
310-
auto liveBlockIter = liveBlocks.find(bb);
311-
if (liveBlockIter == liveBlocks.end()) {
312-
return Dead;
313-
}
314-
315-
return liveBlockIter->second.getLiveness(bitNo);
243+
computeUseBlockLiveness(block);
244+
return getBlockLiveness(block);
316245
}
317246

318-
/// FIXME: This API should directly return the live bitset. The live bitset
319-
/// type should have an api for querying and iterating over the live fields.
320-
void getBlockLiveness(SILBasicBlock *bb, unsigned startBitNo,
321-
unsigned endBitNo,
322-
SmallVectorImpl<IsLive> &foundLivenessInfo) const {
323-
auto liveBlockIter = liveBlocks.find(bb);
324-
if (liveBlockIter == liveBlocks.end()) {
325-
for (unsigned i : range(endBitNo - startBitNo)) {
326-
(void)i;
327-
foundLivenessInfo.push_back(Dead);
328-
}
329-
return;
330-
}
331-
332-
liveBlockIter->second.getLiveness(startBitNo, endBitNo, foundLivenessInfo);
247+
IsLive getBlockLiveness(SILBasicBlock *bb) const {
248+
assert(isInitialized());
249+
return (IsLive)liveBlocks.get(bb);
333250
}
334251

335252
llvm::StringRef getStringRef(IsLive isLive) const;
253+
336254
void print(llvm::raw_ostream &OS) const;
255+
337256
void dump() const;
338257

339258
protected:
340-
void markBlockLive(SILBasicBlock *bb, unsigned bitNo, IsLive isLive) {
259+
void markBlockLive(SILBasicBlock *bb, IsLive isLive) {
341260
assert(isLive != Dead && "erasing live blocks isn't implemented.");
342-
auto iterAndInserted =
343-
liveBlocks.insert(std::make_pair(bb, LivenessSmallBitVector()));
344-
if (iterAndInserted.second) {
345-
// We initialize the size of the small bit vector here rather than in
346-
// liveBlocks.insert above to prevent us from allocating upon failure if
347-
// we have more than SmallBitVector's small size number of bits.
348-
auto &insertedBV = iterAndInserted.first->getSecond();
349-
insertedBV.init(numBitsToTrack);
350-
insertedBV.setLiveness(bitNo, bitNo + 1, isLive);
261+
auto state = (IsLive)liveBlocks.get(bb);
262+
liveBlocks.set(bb, state | isLive);
263+
if (state == IsLive::Dead) {
351264
if (discoveredBlocks)
352265
discoveredBlocks->push_back(bb);
353-
} else {
354-
// If we are dead, always update to the new liveness.
355-
switch (iterAndInserted.first->getSecond().getLiveness(bitNo)) {
356-
case Dead:
357-
iterAndInserted.first->getSecond().setLiveness(bitNo, bitNo + 1,
358-
isLive);
359-
break;
360-
case LiveWithin:
361-
if (isLive == LiveOut) {
362-
// Update the existing entry to be live-out.
363-
iterAndInserted.first->getSecond().setLiveness(bitNo, bitNo + 1,
364-
LiveOut);
365-
}
366-
break;
367-
case LiveOut:
368-
break;
369-
}
370-
}
371-
}
372-
373-
void markBlockLive(SILBasicBlock *bb, unsigned startBitNo, unsigned endBitNo,
374-
IsLive isLive) {
375-
for (unsigned index : range(startBitNo, endBitNo)) {
376-
markBlockLive(bb, index, isLive);
377266
}
378267
}
379268

380269
private:
381-
/// A helper routine that as a fast path handles the scalar case. We do not
382-
/// handle the mult-bit case today since the way the code is written today
383-
/// assumes we process a bit at a time.
384-
///
385-
/// TODO: Make a multi-bit query for efficiency reasons.
386-
void computeScalarUseBlockLiveness(SILBasicBlock *userBB,
387-
unsigned startBitNo);
270+
void computeUseBlockLiveness(SILBasicBlock *userBB);
388271
};
389272

390273
/// If inner borrows are 'Contained', then liveness is fully described by the
@@ -483,17 +366,19 @@ class PrunedLiveness {
483366
SmallVectorImpl<SILBasicBlock *> *discoveredBlocks = nullptr)
484367
: liveBlocks(function, discoveredBlocks) {}
485368

486-
bool empty() const {
487-
assert(!liveBlocks.empty() || users.empty());
488-
return liveBlocks.empty();
489-
}
369+
bool isInitialized() const { return liveBlocks.isInitialized(); }
370+
371+
bool empty() const { return users.empty(); }
490372

491373
void invalidate() {
492374
liveBlocks.invalidate();
493375
users.clear();
494376
}
495377

496-
unsigned numLiveBlocks() const { return liveBlocks.numLiveBlocks(); }
378+
void initializeDiscoveredBlocks(
379+
SmallVectorImpl<SILBasicBlock *> *discoveredBlocks) {
380+
liveBlocks.initializeDiscoveredBlocks(discoveredBlocks);
381+
}
497382

498383
/// If the constructor was provided with a vector to populate, then this
499384
/// returns the list of all live blocks with no duplicates.
@@ -502,7 +387,7 @@ class PrunedLiveness {
502387
}
503388

504389
void initializeDefBlock(SILBasicBlock *defBB) {
505-
liveBlocks.initializeDefBlock(defBB, 0);
390+
liveBlocks.initializeDefBlock(defBB);
506391
}
507392

508393
/// For flexibility, \p lifetimeEnding is provided by the
@@ -527,7 +412,7 @@ class PrunedLiveness {
527412
void extendAcrossLiveness(PrunedLiveness &otherLiveness);
528413

529414
PrunedLiveBlocks::IsLive getBlockLiveness(SILBasicBlock *bb) const {
530-
return liveBlocks.getBlockLiveness(bb, 0);
415+
return liveBlocks.getBlockLiveness(bb);
531416
}
532417

533418
enum IsInterestingUser {
@@ -671,8 +556,9 @@ class PrunedLiveRange : public PrunedLiveness {
671556
return static_cast<const LivenessWithDefs &>(*this);
672557
}
673558

674-
PrunedLiveRange(SmallVectorImpl<SILBasicBlock *> *discoveredBlocks = nullptr)
675-
: PrunedLiveness(discoveredBlocks) {}
559+
PrunedLiveRange(SILFunction *function,
560+
SmallVectorImpl<SILBasicBlock *> *discoveredBlocks = nullptr)
561+
: PrunedLiveness(function, discoveredBlocks) {}
676562

677563
LiveRangeSummary recursivelyUpdateForDef(SILValue initialDef,
678564
ValueSet &visited,
@@ -821,8 +707,6 @@ class MultiDefPrunedLiveness : public PrunedLiveRange<MultiDefPrunedLiveness> {
821707
defBlocks(function) {}
822708

823709
void invalidate() {
824-
defs.invalidate();
825-
defBlocks.invalidate();
826710
PrunedLiveRange::invalidate();
827711
}
828712

0 commit comments

Comments
 (0)