Skip to content

Commit 55761fa

Browse files
committed
SILOptimizer: Use BasicBlockData in MemoryDataflow
And change improve the API of MemoryDataflow: make it iterable by adding begin()/end() and add a subscript operator.
1 parent 0287a1e commit 55761fa

File tree

3 files changed

+141
-159
lines changed

3 files changed

+141
-159
lines changed

include/swift/SIL/MemoryLifetime.h

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#include "swift/SIL/SILBasicBlock.h"
2121
#include "swift/SIL/SILFunction.h"
22+
#include "swift/SIL/BasicBlockData.h"
2223

2324
namespace swift {
2425

@@ -307,9 +308,6 @@ class MemoryDataflow {
307308

308309
/// Basic-block specific information used for dataflow analysis.
309310
struct BlockState {
310-
/// The backlink to the SILBasicBlock.
311-
SILBasicBlock *block;
312-
313311
/// The bits valid at the entry (i.e. the first instruction) of the block.
314312
Bits entrySet;
315313

@@ -333,7 +331,9 @@ class MemoryDataflow {
333331
/// This is only computed if exitReachableAnalysis is called.
334332
ExitReachability exitReachability = ExitReachability::InInfiniteLoop;
335333

336-
BlockState(SILBasicBlock *block = nullptr) : block(block) { }
334+
BlockState(unsigned numLocations) :
335+
entrySet(numLocations), exitSet(numLocations),
336+
genSet(numLocations), killSet(numLocations) {}
337337

338338
// Utility functions for setting and clearing gen- and kill-bits.
339339

@@ -361,28 +361,25 @@ class MemoryDataflow {
361361
};
362362

363363
private:
364-
/// All block states.
365-
std::vector<BlockState> blockStates;
366-
367-
/// Getting from SILBasicBlock to BlockState.
368-
llvm::DenseMap<SILBasicBlock *, BlockState *> block2State;
364+
BasicBlockData<BlockState> blockStates;
369365

370366
public:
367+
368+
using iterator = BasicBlockData<BlockState>::iterator;
369+
371370
/// Sets up the BlockState datastructures and associates all basic blocks with
372371
/// a state.
373372
MemoryDataflow(SILFunction *function, unsigned numLocations);
374373

375374
MemoryDataflow(const MemoryDataflow &) = delete;
376375
MemoryDataflow &operator=(const MemoryDataflow &) = delete;
377376

378-
using iterator = std::vector<BlockState>::iterator;
379-
380377
iterator begin() { return blockStates.begin(); }
381378
iterator end() { return blockStates.end(); }
382379

383380
/// Returns the state of a block.
384-
BlockState *getState(SILBasicBlock *block) {
385-
return block2State[block];
381+
BlockState &operator[] (SILBasicBlock *block) {
382+
return blockStates[block];
386383
}
387384

388385
/// Calculates the BlockState::reachableFromEntry flags.

lib/SIL/Verifier/MemoryLifetime.cpp

Lines changed: 81 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -393,64 +393,50 @@ void MemoryLocations::initFieldsCounter(Location &loc) {
393393
// MemoryDataflow members
394394
//===----------------------------------------------------------------------===//
395395

396-
MemoryDataflow::MemoryDataflow(SILFunction *function, unsigned numLocations) {
397-
// Resizing is mandatory! Just adding states with push_back would potentially
398-
// invalidate previous pointers to states, which are stored in block2State.
399-
blockStates.resize(function->size());
400-
401-
unsigned idx = 0;
402-
unsigned numBits = numLocations;
403-
for (SILBasicBlock &BB : *function) {
404-
BlockState *st = &blockStates[idx++];
405-
st->block = &BB;
406-
st->entrySet.resize(numBits);
407-
st->genSet.resize(numBits);
408-
st->killSet.resize(numBits);
409-
st->exitSet.resize(numBits);
410-
block2State[&BB] = st;
411-
}
412-
}
396+
MemoryDataflow::MemoryDataflow(SILFunction *function, unsigned numLocations) :
397+
blockStates(function, [numLocations](SILBasicBlock *block) {
398+
return BlockState(numLocations);
399+
}) {}
413400

414401
void MemoryDataflow::entryReachabilityAnalysis() {
415-
llvm::SmallVector<BlockState *, 16> workList;
416-
BlockState *entryState = &blockStates[0];
417-
assert(entryState ==
418-
block2State[entryState->block->getParent()->getEntryBlock()]);
419-
entryState->reachableFromEntry = true;
420-
workList.push_back(entryState);
402+
llvm::SmallVector<SILBasicBlock *, 16> workList;
403+
auto entry = blockStates.entry();
404+
entry.data.reachableFromEntry = true;
405+
workList.push_back(&entry.block);
421406

422407
while (!workList.empty()) {
423-
BlockState *state = workList.pop_back_val();
424-
for (SILBasicBlock *succ : state->block->getSuccessorBlocks()) {
425-
BlockState *succState = block2State[succ];
426-
if (!succState->reachableFromEntry) {
427-
succState->reachableFromEntry = true;
428-
workList.push_back(succState);
408+
SILBasicBlock *block = workList.pop_back_val();
409+
for (SILBasicBlock *succ : block->getSuccessorBlocks()) {
410+
BlockState &succState = blockStates[succ];
411+
if (!succState.reachableFromEntry) {
412+
succState.reachableFromEntry = true;
413+
workList.push_back(succ);
429414
}
430415
}
431416
}
432417
}
433418

434419
void MemoryDataflow::exitReachableAnalysis() {
435-
llvm::SmallVector<BlockState *, 16> workList;
436-
for (BlockState &state : blockStates) {
437-
if (state.block->getTerminator()->isFunctionExiting()) {
438-
state.exitReachability = ExitReachability::ReachesExit;
439-
workList.push_back(&state);
440-
} else if (isa<UnreachableInst>(state.block->getTerminator())) {
441-
state.exitReachability = ExitReachability::ReachesUnreachable;
442-
workList.push_back(&state);
420+
llvm::SmallVector<SILBasicBlock *, 16> workList;
421+
for (auto bd : blockStates) {
422+
if (bd.block.getTerminator()->isFunctionExiting()) {
423+
bd.data.exitReachability = ExitReachability::ReachesExit;
424+
workList.push_back(&bd.block);
425+
} else if (isa<UnreachableInst>(bd.block.getTerminator())) {
426+
bd.data.exitReachability = ExitReachability::ReachesUnreachable;
427+
workList.push_back(&bd.block);
443428
}
444429
}
445430
while (!workList.empty()) {
446-
BlockState *state = workList.pop_back_val();
447-
for (SILBasicBlock *pred : state->block->getPredecessorBlocks()) {
448-
BlockState *predState = block2State[pred];
449-
if (predState->exitReachability < state->exitReachability) {
431+
SILBasicBlock *block = workList.pop_back_val();
432+
BlockState &state = blockStates[block];
433+
for (SILBasicBlock *pred : block->getPredecessorBlocks()) {
434+
BlockState &predState = blockStates[pred];
435+
if (predState.exitReachability < state.exitReachability) {
450436
// As there are 3 states, each block can be put into the workList 2
451437
// times maximum.
452-
predState->exitReachability = state->exitReachability;
453-
workList.push_back(predState);
438+
predState.exitReachability = state.exitReachability;
439+
workList.push_back(pred);
454440
}
455441
}
456442
}
@@ -462,18 +448,18 @@ void MemoryDataflow::solveForward(JoinOperation join) {
462448
bool firstRound = true;
463449
do {
464450
changed = false;
465-
for (BlockState &st : blockStates) {
466-
Bits bits = st.entrySet;
451+
for (auto bd : blockStates) {
452+
Bits bits = bd.data.entrySet;
467453
assert(!bits.empty());
468-
for (SILBasicBlock *pred : st.block->getPredecessorBlocks()) {
469-
join(bits, block2State[pred]->exitSet);
454+
for (SILBasicBlock *pred : bd.block.getPredecessorBlocks()) {
455+
join(bits, blockStates[pred].exitSet);
470456
}
471-
if (firstRound || bits != st.entrySet) {
457+
if (firstRound || bits != bd.data.entrySet) {
472458
changed = true;
473-
st.entrySet = bits;
474-
bits |= st.genSet;
475-
bits.reset(st.killSet);
476-
st.exitSet = bits;
459+
bd.data.entrySet = bits;
460+
bits |= bd.data.genSet;
461+
bits.reset(bd.data.killSet);
462+
bd.data.exitSet = bits;
477463
}
478464
}
479465
firstRound = false;
@@ -498,18 +484,18 @@ void MemoryDataflow::solveBackward(JoinOperation join) {
498484
bool firstRound = true;
499485
do {
500486
changed = false;
501-
for (BlockState &st : llvm::reverse(blockStates)) {
502-
Bits bits = st.exitSet;
487+
for (auto bd : llvm::reverse(blockStates)) {
488+
Bits bits = bd.data.exitSet;
503489
assert(!bits.empty());
504-
for (SILBasicBlock *succ : st.block->getSuccessorBlocks()) {
505-
join(bits, block2State[succ]->entrySet);
490+
for (SILBasicBlock *succ : bd.block.getSuccessorBlocks()) {
491+
join(bits, blockStates[succ].entrySet);
506492
}
507-
if (firstRound || bits != st.exitSet) {
493+
if (firstRound || bits != bd.data.exitSet) {
508494
changed = true;
509-
st.exitSet = bits;
510-
bits |= st.genSet;
511-
bits.reset(st.killSet);
512-
st.entrySet = bits;
495+
bd.data.exitSet = bits;
496+
bits |= bd.data.genSet;
497+
bits.reset(bd.data.killSet);
498+
bd.data.entrySet = bits;
513499
}
514500
}
515501
firstRound = false;
@@ -529,12 +515,12 @@ void MemoryDataflow::solveBackwardWithUnion() {
529515
}
530516

531517
void MemoryDataflow::dump() const {
532-
for (const BlockState &st : blockStates) {
533-
llvm::dbgs() << "bb" << st.block->getDebugID() << ":\n"
534-
<< " entry: " << st.entrySet << '\n'
535-
<< " gen: " << st.genSet << '\n'
536-
<< " kill: " << st.killSet << '\n'
537-
<< " exit: " << st.exitSet << '\n';
518+
for (auto bd : blockStates) {
519+
llvm::dbgs() << "bb" << bd.block.getDebugID() << ":\n"
520+
<< " entry: " << bd.data.entrySet << '\n'
521+
<< " gen: " << bd.data.genSet << '\n'
522+
<< " kill: " << bd.data.killSet << '\n'
523+
<< " exit: " << bd.data.exitSet << '\n';
538524
}
539525
}
540526

@@ -585,7 +571,7 @@ class MemoryLifetimeVerifier {
585571
void initDataflow(MemoryDataflow &dataFlow);
586572

587573
/// Initializes the data flow bits sets in the block state for a single block.
588-
void initDataflowInBlock(BlockState &state);
574+
void initDataflowInBlock(SILBasicBlock *block, BlockState &state);
589575

590576
/// Helper function to set bits for function arguments and returns.
591577
void setFuncOperandBits(BlockState &state, Operand &op,
@@ -658,36 +644,37 @@ void MemoryLifetimeVerifier::requireBitsSet(const Bits &bits, SILValue addr,
658644
void MemoryLifetimeVerifier::initDataflow(MemoryDataflow &dataFlow) {
659645
// Initialize the entry and exit sets to all-bits-set. Except for the function
660646
// entry.
661-
for (BlockState &st : dataFlow) {
662-
if (st.block == function->getEntryBlock()) {
663-
st.entrySet.reset();
647+
for (auto bs : dataFlow) {
648+
if (&bs.block == function->getEntryBlock()) {
649+
bs.data.entrySet.reset();
664650
for (SILArgument *arg : function->getArguments()) {
665651
SILFunctionArgument *funcArg = cast<SILFunctionArgument>(arg);
666652
if (funcArg->getArgumentConvention() !=
667653
SILArgumentConvention::Indirect_Out) {
668-
locations.setBits(st.entrySet, arg);
654+
locations.setBits(bs.data.entrySet, arg);
669655
}
670656
}
671657
} else {
672-
st.entrySet.set();
658+
bs.data.entrySet.set();
673659
}
674-
st.exitSet.set();
660+
bs.data.exitSet.set();
675661

676662
// Anything weired can happen in unreachable blocks. So just ignore them.
677663
// Note: while solving the dataflow, unreachable blocks are implicitly
678664
// ignored, because their entry/exit sets are all-ones and their gen/kill
679665
// sets are all-zeroes.
680-
if (st.reachableFromEntry)
681-
initDataflowInBlock(st);
666+
if (bs.data.reachableFromEntry)
667+
initDataflowInBlock(&bs.block, bs.data);
682668
}
683669
}
684670

685-
void MemoryLifetimeVerifier::initDataflowInBlock(BlockState &state) {
671+
void MemoryLifetimeVerifier::initDataflowInBlock(SILBasicBlock *block,
672+
BlockState &state) {
686673
// Initialize the genSet with special cases, like the @out results of an
687674
// try_apply in the predecessor block.
688-
setBitsOfPredecessor(state.genSet, state.block);
675+
setBitsOfPredecessor(state.genSet, block);
689676

690-
for (SILInstruction &I : *state.block) {
677+
for (SILInstruction &I : *block) {
691678
switch (I.getKind()) {
692679
case SILInstructionKind::LoadInst: {
693680
auto *LI = cast<LoadInst>(&I);
@@ -809,38 +796,38 @@ void MemoryLifetimeVerifier::checkFunction(MemoryDataflow &dataFlow) {
809796

810797
const Bits &nonTrivialLocations = locations.getNonTrivialLocations();
811798
Bits bits(locations.getNumLocations());
812-
for (BlockState &st : dataFlow) {
813-
if (!st.reachableFromEntry || !st.exitReachable())
799+
for (auto bs : dataFlow) {
800+
if (!bs.data.reachableFromEntry || !bs.data.exitReachable())
814801
continue;
815802

816803
// Check all instructions in the block.
817-
bits = st.entrySet;
818-
checkBlock(st.block, bits);
804+
bits = bs.data.entrySet;
805+
checkBlock(&bs.block, bits);
819806

820807
// Check if there is a mismatch in location lifetime at the merge point.
821-
for (SILBasicBlock *pred : st.block->getPredecessorBlocks()) {
822-
BlockState *predState = dataFlow.getState(pred);
823-
if (predState->reachableFromEntry) {
824-
require((st.entrySet ^ predState->exitSet) & nonTrivialLocations,
825-
"lifetime mismatch in predecessors", &*st.block->begin());
808+
for (SILBasicBlock *pred : bs.block.getPredecessorBlocks()) {
809+
BlockState &predState = dataFlow[pred];
810+
if (predState.reachableFromEntry) {
811+
require((bs.data.entrySet ^ predState.exitSet) & nonTrivialLocations,
812+
"lifetime mismatch in predecessors", &*bs.block.begin());
826813
}
827814
}
828815

829816
// Check the bits at function exit.
830-
TermInst *term = st.block->getTerminator();
831-
assert(bits == st.exitSet || isa<TryApplyInst>(term));
817+
TermInst *term = bs.block.getTerminator();
818+
assert(bits == bs.data.exitSet || isa<TryApplyInst>(term));
832819
switch (term->getKind()) {
833820
case SILInstructionKind::ReturnInst:
834821
case SILInstructionKind::UnwindInst:
835-
require(expectedReturnBits & ~st.exitSet,
822+
require(expectedReturnBits & ~bs.data.exitSet,
836823
"indirect argument is not alive at function return", term);
837-
require(st.exitSet & ~expectedReturnBits & nonTrivialLocations,
824+
require(bs.data.exitSet & ~expectedReturnBits & nonTrivialLocations,
838825
"memory is initialized at function return but shouldn't", term);
839826
break;
840827
case SILInstructionKind::ThrowInst:
841-
require(expectedThrowBits & ~st.exitSet,
828+
require(expectedThrowBits & ~bs.data.exitSet,
842829
"indirect argument is not alive at throw", term);
843-
require(st.exitSet & ~expectedThrowBits & nonTrivialLocations,
830+
require(bs.data.exitSet & ~expectedThrowBits & nonTrivialLocations,
844831
"memory is initialized at throw but shouldn't", term);
845832
break;
846833
default:

0 commit comments

Comments
 (0)