Skip to content

Commit 0b27c5c

Browse files
committed
[region-based-isolation] Rather than lazily translating while we visit all blocks during the dataflow... just do the translation when we initialize block state.
We are going to visit all of the blocks anyways... so it makes sense to just run them as part of the initializing of the block state. Otherwise, the logic is buried in the dataflow which makes it harder for someone who is not familiar with the pass to reason about it.
1 parent cb6ac6c commit 0b27c5c

File tree

1 file changed

+83
-56
lines changed

1 file changed

+83
-56
lines changed

lib/SILOptimizer/Mandatory/SendNonSendable.cpp

Lines changed: 83 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -466,12 +466,35 @@ class PartitionOpTranslator {
466466
: function(function),
467467
sendableProtocol(
468468
function->getASTContext().getProtocol(KnownProtocolKind::Sendable)),
469-
builder() {
469+
functionArgPartition(), builder() {
470470
assert(sendableProtocol && "PartitionOpTranslators should only be created "
471471
"in contexts in which the availability of the "
472472
"Sendable protocol has already been checked.");
473473
builder.translater = this;
474474
initCapturedUniquelyIdentifiedValues();
475+
476+
LLVM_DEBUG(llvm::dbgs() << "Initializing Function Args:\n");
477+
auto functionArguments = function->getArguments();
478+
if (functionArguments.empty()) {
479+
LLVM_DEBUG(llvm::dbgs() << " None.\n");
480+
functionArgPartition = Partition::singleRegion({});
481+
return;
482+
}
483+
484+
llvm::SmallVector<Element, 8> nonSendableIndices;
485+
for (SILArgument *arg : functionArguments) {
486+
if (auto state = tryToTrackValue(arg)) {
487+
// If we have an arg that is an actor, we allow for it to be
488+
// consumed... value ids derived from it though cannot be consumed.
489+
LLVM_DEBUG(llvm::dbgs() << " %%" << state->getID());
490+
neverTransferredValueIDs.push_back(state->getID());
491+
nonSendableIndices.push_back(state->getID());
492+
LLVM_DEBUG(llvm::dbgs() << *arg);
493+
}
494+
}
495+
496+
// All non actor values are in the same partition.
497+
functionArgPartition = Partition::singleRegion(nonSendableIndices);
475498
}
476499

477500
std::optional<TrackableValue> getValueForId(TrackableValueID id) const {
@@ -528,35 +551,7 @@ class PartitionOpTranslator {
528551
// including self if available, into the same region, ensuring those
529552
// arguments get IDs in doing so. This Partition will be used as the
530553
// entry point for the full partition analysis.
531-
Partition getEntryPartition() const {
532-
if (!functionArgPartition) {
533-
LLVM_DEBUG(llvm::dbgs() << "Initializing Function Args:\n");
534-
auto *self = const_cast<PartitionOpTranslator *>(this);
535-
auto functionArguments = function->getArguments();
536-
if (functionArguments.empty()) {
537-
LLVM_DEBUG(llvm::dbgs() << " None.\n");
538-
self->functionArgPartition = Partition::singleRegion({});
539-
} else {
540-
llvm::SmallVector<Element, 8> nonSendableIndices;
541-
for (SILArgument *arg : functionArguments) {
542-
if (auto state = tryToTrackValue(arg)) {
543-
// If we have an arg that is an actor, we allow for it to be
544-
// consumed... value ids derived from it though cannot be consumed.
545-
LLVM_DEBUG(llvm::dbgs() << " %%" << state->getID());
546-
self->neverTransferredValueIDs.push_back(state->getID());
547-
nonSendableIndices.push_back(state->getID());
548-
LLVM_DEBUG(llvm::dbgs() << *arg);
549-
}
550-
}
551-
552-
// All non actor values are in the same partition.
553-
self->functionArgPartition =
554-
Partition::singleRegion(nonSendableIndices);
555-
}
556-
}
557-
558-
return *functionArgPartition;
559-
}
554+
Partition getEntryPartition() const { return *functionArgPartition; }
560555

561556
// Get the vector of IDs that cannot be legally transferred at any point in
562557
// this function. Since we place all args and self in a single region right
@@ -589,6 +584,21 @@ class PartitionOpTranslator {
589584
llvm::report_fatal_error("all apply instructions should be covered");
590585
}
591586

587+
#ifndef NDEBUG
588+
void dumpValues() const {
589+
// Since this is just used for debug output, be inefficient to make nicer
590+
// output.
591+
std::vector<std::pair<unsigned, SILValue>> temp;
592+
for (auto p : stateIndexToEquivalenceClass) {
593+
temp.emplace_back(p.first, p.second);
594+
}
595+
std::sort(temp.begin(), temp.end());
596+
for (auto p : temp) {
597+
LLVM_DEBUG(llvm::dbgs() << "%%" << p.first << ": " << p.second);
598+
}
599+
}
600+
#endif
601+
592602
// ===========================================================================
593603
// The following section of functions wrap the more primitive Assign, Require,
594604
// Merge, etc functions that generate PartitionOps with more logic common to
@@ -1102,21 +1112,21 @@ class PartitionOpTranslator {
11021112
// translateSILBasicBlock reduces a SIL basic block to the vector of
11031113
// transformations to the non-Sendable partition that it induces.
11041114
// it accomplished this by sequentially calling translateSILInstruction
1105-
std::vector<PartitionOp> translateSILBasicBlock(SILBasicBlock *basicBlock) {
1115+
void translateSILBasicBlock(SILBasicBlock *basicBlock,
1116+
std::vector<PartitionOp> &foundPartitionOps) {
11061117
LLVM_DEBUG(llvm::dbgs() << SEP_STR << "Compiling basic block for function "
11071118
<< basicBlock->getFunction()->getName() << ": ";
11081119
basicBlock->dumpID(); llvm::dbgs() << SEP_STR;
11091120
basicBlock->print(llvm::dbgs());
11101121
llvm::dbgs() << SEP_STR << "Results:\n";);
1111-
//translate each SIL instruction to a PartitionOp, if necessary
1112-
std::vector<PartitionOp> partitionOps;
1122+
// Translate each SIL instruction to the PartitionOps that it represents if
1123+
// any.
11131124
for (auto &instruction : *basicBlock) {
11141125
LLVM_DEBUG(llvm::dbgs() << "Visiting: " << instruction);
11151126
translateSILInstruction(&instruction);
1116-
copy(builder.currentInstPartitionOps, std::back_inserter(partitionOps));
1127+
copy(builder.currentInstPartitionOps,
1128+
std::back_inserter(foundPartitionOps));
11171129
}
1118-
1119-
return partitionOps;
11201130
}
11211131
};
11221132

@@ -1199,25 +1209,18 @@ class BlockPartitionState {
11991209
SILBasicBlock *basicBlock;
12001210
PartitionOpTranslator &translator;
12011211

1202-
bool blockPartitionOpsPopulated = false;
12031212
std::vector<PartitionOp> blockPartitionOps = {};
12041213

12051214
BlockPartitionState(SILBasicBlock *basicBlock,
12061215
PartitionOpTranslator &translator)
1207-
: basicBlock(basicBlock), translator(translator) {}
1208-
1209-
void ensureBlockPartitionOpsPopulated() {
1210-
if (blockPartitionOpsPopulated) return;
1211-
blockPartitionOpsPopulated = true;
1212-
blockPartitionOps = translator.translateSILBasicBlock(basicBlock);
1216+
: basicBlock(basicBlock), translator(translator) {
1217+
translator.translateSILBasicBlock(basicBlock, blockPartitionOps);
12131218
}
12141219

12151220
// recomputes the exit partition from the entry partition,
12161221
// and returns whether this changed the exit partition.
12171222
// Note that this method ignored errors that arise.
12181223
bool recomputeExitFromEntry() {
1219-
ensureBlockPartitionOpsPopulated();
1220-
12211224
Partition workingPartition = entryPartition;
12221225
for (auto partitionOp : blockPartitionOps) {
12231226
// by calling apply without providing a `handleFailure` closure,
@@ -1782,7 +1785,7 @@ class PartitionAnalysis {
17821785
return BlockPartitionState(block, translator);
17831786
}),
17841787
raceTracer(fn, blockStates), function(fn), solved(false) {
1785-
// initialize the entry block as needing an update, and having a partition
1788+
// Initialize the entry block as needing an update, and having a partition
17861789
// that places all its non-sendable args in a single region
17871790
blockStates[fn->getEntryBlock()].needsUpdate = true;
17881791
blockStates[fn->getEntryBlock()].entryPartition =
@@ -1793,12 +1796,20 @@ class PartitionAnalysis {
17931796
assert(!solved && "solve should only be called once");
17941797
solved = true;
17951798

1799+
LLVM_DEBUG(llvm::dbgs() << SEP_STR << "Performing Dataflow!\n" << SEP_STR);
1800+
LLVM_DEBUG(llvm::dbgs() << "Values!\n"; translator.dumpValues());
1801+
17961802
bool anyNeedUpdate = true;
17971803
while (anyNeedUpdate) {
17981804
anyNeedUpdate = false;
17991805

18001806
for (auto [block, blockState] : blockStates) {
1801-
if (!blockState.needsUpdate) continue;
1807+
1808+
LLVM_DEBUG(llvm::dbgs() << "Block: bb" << block.getDebugID() << "\n");
1809+
if (!blockState.needsUpdate) {
1810+
LLVM_DEBUG(llvm::dbgs() << " Doesn't need update! Skipping!\n");
1811+
continue;
1812+
}
18021813

18031814
// mark this block as no longer needing an update
18041815
blockState.needsUpdate = false;
@@ -1810,31 +1821,45 @@ class PartitionAnalysis {
18101821
Partition newEntryPartition;
18111822
bool firstPred = true;
18121823

1813-
// this loop computes the join of the exit partitions of all
1824+
LLVM_DEBUG(llvm::dbgs() << " Visiting Preds!\n");
1825+
1826+
// This loop computes the join of the exit partitions of all
18141827
// predecessors of this block
18151828
for (SILBasicBlock *predBlock : block.getPredecessorBlocks()) {
18161829
BlockPartitionState &predState = blockStates[predBlock];
18171830
// ignore predecessors that haven't been reached by the analysis yet
1818-
if (!predState.reached) continue;
1831+
if (!predState.reached)
1832+
continue;
18191833

18201834
if (firstPred) {
18211835
firstPred = false;
18221836
newEntryPartition = predState.exitPartition;
1837+
LLVM_DEBUG(llvm::dbgs() << " First Pred. bb"
1838+
<< predBlock->getDebugID() << ": ";
1839+
newEntryPartition.print(llvm::dbgs()));
18231840
continue;
18241841
}
18251842

1826-
newEntryPartition = Partition::join(
1827-
newEntryPartition, predState.exitPartition);
1843+
LLVM_DEBUG(llvm::dbgs()
1844+
<< " Pred. bb" << predBlock->getDebugID() << ": ";
1845+
predState.exitPartition.print(llvm::dbgs()));
1846+
newEntryPartition =
1847+
Partition::join(newEntryPartition, predState.exitPartition);
1848+
LLVM_DEBUG(llvm::dbgs() << " Join: ";
1849+
newEntryPartition.print(llvm::dbgs()));
18281850
}
18291851

1830-
// if we found predecessor blocks, then attempt to use them to
1831-
// update the entry partition for this block, and abort this block's
1832-
// update if the entry partition was not updated
1852+
// If we found predecessor blocks, then attempt to use them to update
1853+
// the entry partition for this block, and abort this block's update if
1854+
// the entry partition was not updated.
18331855
if (!firstPred) {
18341856
// if the recomputed entry partition is the same as the current one,
18351857
// perform no update
1836-
if (Partition::equals(newEntryPartition, blockState.entryPartition))
1858+
if (Partition::equals(newEntryPartition, blockState.entryPartition)) {
1859+
LLVM_DEBUG(llvm::dbgs()
1860+
<< " Entry partition is the same... skipping!\n");
18371861
continue;
1862+
}
18381863

18391864
// otherwise update the entry partition
18401865
blockState.entryPartition = newEntryPartition;
@@ -1882,7 +1907,9 @@ class PartitionAnalysis {
18821907
<< function->getName() << "\n");
18831908
RaceTracer tracer(function, blockStates);
18841909

1885-
for (auto [_, blockState] : blockStates) {
1910+
for (auto [block, blockState] : blockStates) {
1911+
LLVM_DEBUG(llvm::dbgs() << "Block bb" << block.getDebugID() << "\n");
1912+
18861913
// populate the raceTracer with all requires of transferred valued found
18871914
// throughout the CFG
18881915
blockState.diagnoseFailures(

0 commit comments

Comments
 (0)