Skip to content

Commit 119e712

Browse files
authored
Merge pull request #64534 from atrick/liveblocks-bitfield
Cleanup PrunedLiveBlocks
2 parents c5e4af9 + cbe856e commit 119e712

27 files changed

+335
-401
lines changed

include/swift/SIL/OwnershipLiveness.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,8 @@ class OSSALiveness {
178178
OSSALiveness &operator=(const OSSALiveness &) = delete;
179179

180180
public:
181-
OSSALiveness(SILValue def): ownershipDef(def), liveness(&discoveredBlocks) {}
181+
OSSALiveness(SILValue def): ownershipDef(def),
182+
liveness(def->getFunction(), &discoveredBlocks) {}
182183

183184
const SSAPrunedLiveness &getLiveness() const { return liveness; }
184185

include/swift/SIL/PrunedLiveness.h

Lines changed: 75 additions & 186 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; }
206+
bool isInitialized() const { return initializedFlag; }
269207

270-
bool empty() const { return liveBlocks.empty(); }
271-
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
@@ -479,20 +362,23 @@ class PrunedLiveness {
479362
llvm::SmallMapVector<SILInstruction *, bool, 8> users;
480363

481364
public:
482-
PrunedLiveness(SmallVectorImpl<SILBasicBlock *> *discoveredBlocks = nullptr)
483-
: liveBlocks(1 /*num bits*/, discoveredBlocks) {}
365+
PrunedLiveness(SILFunction *function,
366+
SmallVectorImpl<SILBasicBlock *> *discoveredBlocks = nullptr)
367+
: liveBlocks(function, discoveredBlocks) {}
484368

485-
bool empty() const {
486-
assert(!liveBlocks.empty() || users.empty());
487-
return liveBlocks.empty();
488-
}
369+
bool isInitialized() const { return liveBlocks.isInitialized(); }
489370

490-
void clear() {
491-
liveBlocks.clear();
371+
bool empty() const { return users.empty(); }
372+
373+
void invalidate() {
374+
liveBlocks.invalidate();
492375
users.clear();
493376
}
494377

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

497383
/// If the constructor was provided with a vector to populate, then this
498384
/// returns the list of all live blocks with no duplicates.
@@ -501,7 +387,7 @@ class PrunedLiveness {
501387
}
502388

503389
void initializeDefBlock(SILBasicBlock *defBB) {
504-
liveBlocks.initializeDefBlock(defBB, 0);
390+
liveBlocks.initializeDefBlock(defBB);
505391
}
506392

507393
/// For flexibility, \p lifetimeEnding is provided by the
@@ -526,7 +412,7 @@ class PrunedLiveness {
526412
void extendAcrossLiveness(PrunedLiveness &otherLiveness);
527413

528414
PrunedLiveBlocks::IsLive getBlockLiveness(SILBasicBlock *bb) const {
529-
return liveBlocks.getBlockLiveness(bb, 0);
415+
return liveBlocks.getBlockLiveness(bb);
530416
}
531417

532418
enum IsInterestingUser {
@@ -670,8 +556,9 @@ class PrunedLiveRange : public PrunedLiveness {
670556
return static_cast<const LivenessWithDefs &>(*this);
671557
}
672558

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

676563
LiveRangeSummary recursivelyUpdateForDef(SILValue initialDef,
677564
ValueSet &visited,
@@ -745,15 +632,16 @@ class SSAPrunedLiveness : public PrunedLiveRange<SSAPrunedLiveness> {
745632

746633
public:
747634
SSAPrunedLiveness(
635+
SILFunction *function,
748636
SmallVectorImpl<SILBasicBlock *> *discoveredBlocks = nullptr)
749-
: PrunedLiveRange(discoveredBlocks) {}
637+
: PrunedLiveRange(function, discoveredBlocks) {}
750638

751639
SILValue getDef() const { return def; }
752640

753-
void clear() {
641+
void invalidate() {
754642
def = SILValue();
755643
defInst = nullptr;
756-
PrunedLiveRange::clear();
644+
PrunedLiveRange::invalidate();
757645
}
758646

759647
void initializeDef(SILValue def) {
@@ -815,11 +703,11 @@ class MultiDefPrunedLiveness : public PrunedLiveRange<MultiDefPrunedLiveness> {
815703
MultiDefPrunedLiveness(
816704
SILFunction *function,
817705
SmallVectorImpl<SILBasicBlock *> *discoveredBlocks = nullptr)
818-
: PrunedLiveRange(discoveredBlocks), defs(function), defBlocks(function) {
819-
}
706+
: PrunedLiveRange(function, discoveredBlocks), defs(function),
707+
defBlocks(function) {}
820708

821-
void clear() {
822-
llvm_unreachable("multi-def liveness cannot be reused");
709+
void invalidate() {
710+
PrunedLiveRange::invalidate();
823711
}
824712

825713
void initializeDef(SILInstruction *defInst) {
@@ -886,14 +774,15 @@ class DiagnosticPrunedLiveness : public SSAPrunedLiveness {
886774

887775
public:
888776
DiagnosticPrunedLiveness(
777+
SILFunction *function,
889778
SmallVectorImpl<SILBasicBlock *> *discoveredBlocks = nullptr,
890779
SmallSetVector<SILInstruction *, 8> *nonLifetimeEndingUsesInLiveOut =
891780
nullptr)
892-
: SSAPrunedLiveness(discoveredBlocks),
781+
: SSAPrunedLiveness(function, discoveredBlocks),
893782
nonLifetimeEndingUsesInLiveOut(nonLifetimeEndingUsesInLiveOut) {}
894783

895-
void clear() {
896-
SSAPrunedLiveness::clear();
784+
void invalidate() {
785+
SSAPrunedLiveness::invalidate();
897786
if (nonLifetimeEndingUsesInLiveOut)
898787
nonLifetimeEndingUsesInLiveOut->clear();
899788
}

0 commit comments

Comments
 (0)