Skip to content

[region-isolation] Include the region -> transferring operand map in the dataflow convergence. #72955

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion include/swift/SILOptimizer/Analysis/RegionAnalysis.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,13 @@ class BlockPartitionState {

TransferringOperandSetFactory &ptrSetFactory;

TransferringOperandToStateMap &transferringOpToStateMap;

BlockPartitionState(SILBasicBlock *basicBlock,
PartitionOpTranslator &translator,
TransferringOperandSetFactory &ptrSetFactory,
IsolationHistory::Factory &isolationHistoryFactory);
IsolationHistory::Factory &isolationHistoryFactory,
TransferringOperandToStateMap &transferringOpToStateMap);

public:
bool getLiveness() const { return isLive; }
Expand Down Expand Up @@ -397,6 +400,8 @@ class RegionAnalysisFunctionInfo {

IsolationHistory::Factory isolationHistoryFactory;

TransferringOperandToStateMap transferringOpToStateMap;

// We make this optional to prevent an issue that we have seen on windows when
// capturing a field in a closure that is used to initialize a different
// field.
Expand Down Expand Up @@ -492,6 +497,16 @@ class RegionAnalysisFunctionInfo {
return valueMap;
}

IsolationHistory::Factory &getIsolationHistoryFactory() {
assert(supportedFunction && "Unsupported Function?!");
return isolationHistoryFactory;
}

TransferringOperandToStateMap &getTransferringOpToStateMap() {
assert(supportedFunction && "Unsupported Function?!");
return transferringOpToStateMap;
}

bool isClosureCaptured(SILValue value, Operand *op);

static SILValue getUnderlyingTrackedValue(SILValue value);
Expand Down
146 changes: 68 additions & 78 deletions include/swift/SILOptimizer/Utils/PartitionUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,12 @@
#include "swift/Basic/LLVM.h"
#include "swift/SIL/SILFunction.h"
#include "swift/SIL/SILInstruction.h"

#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Debug.h"

#include <algorithm>
#include <variant>

Expand Down Expand Up @@ -223,6 +227,7 @@ class SILIsolationInfo {
};

class Partition;
class TransferringOperandToStateMap;

/// A persistent data structure that is used to "rewind" partition history so
/// that we can discover when values become part of the same region.
Expand All @@ -244,6 +249,7 @@ class IsolationHistory {

// TODO: This shouldn't need to be a friend.
friend class Partition;
friend TransferringOperandToStateMap;

/// First node in the immutable linked list.
Node *head = nullptr;
Expand Down Expand Up @@ -297,6 +303,11 @@ class IsolationHistory {
/// Push that \p other should be merged into this region.
void pushCFGHistoryJoin(Node *otherNode);

/// Push the top node of \p history as a CFG history join.
void pushCFGHistoryJoin(IsolationHistory history) {
return pushCFGHistoryJoin(history.getHead());
}

Node *pop();
};

Expand Down Expand Up @@ -456,10 +467,7 @@ class IsolationHistory::Factory {
IsolationHistory get() { return IsolationHistory(this); }
};

class TransferringOperand {
using ValueType = llvm::PointerIntPair<Operand *, 1>;
ValueType value;

struct TransferringOperandState {
/// The dynamic isolation info of the region of value when we transferred.
///
/// This will contain the isolated value if we found one.
Expand All @@ -468,65 +476,30 @@ class TransferringOperand {
/// The dynamic isolation history at this point.
IsolationHistory isolationHistory;

TransferringOperand(ValueType newValue, SILIsolationInfo isolationRegionInfo,
IsolationHistory isolationHistory)
: value(newValue), isolationInfo(isolationRegionInfo),
isolationHistory(isolationHistory) {
assert(isolationInfo && "Should never see unknown isolation info");
}

public:
TransferringOperand(Operand *op, bool isClosureCaptured,
SILIsolationInfo isolationRegionInfo,
IsolationHistory isolationHistory)
: TransferringOperand({op, isClosureCaptured}, isolationRegionInfo,
isolationHistory) {}
explicit TransferringOperand(Operand *op,
SILIsolationInfo isolationRegionInfo,
IsolationHistory isolationHistory)
: TransferringOperand({op, false}, isolationRegionInfo,
isolationHistory) {}

operator bool() const { return bool(value.getPointer()); }

Operand *getOperand() const { return value.getPointer(); }

SILValue get() const { return getOperand()->get(); }

bool isClosureCaptured() const { return value.getInt(); }
/// Set to true if the element associated with the operand's vlaue is closure
/// captured by the user. In such a case, if our element is a sendable var of
/// a non-Sendable type, we cannot access it since we could race against an
/// assignment to the var in a closure.
bool isClosureCaptured;

SILInstruction *getUser() const { return getOperand()->getUser(); }

SILIsolationInfo getIsolationInfo() const { return isolationInfo; }

IsolationHistory getIsolationHistory() const { return isolationHistory; }

unsigned getOperandNumber() const { return getOperand()->getOperandNumber(); }

void print(llvm::raw_ostream &os) const {
os << "Op Num: " << getOperand()->getOperandNumber() << ". "
<< "Capture: " << (isClosureCaptured() ? "yes. " : "no. ")
<< "IsolationInfo: ";
isolationInfo.print(os);
os << "\nUser: " << *getUser();
}
TransferringOperandState(IsolationHistory history)
: isolationInfo(), isolationHistory(history), isClosureCaptured(false) {}
};

static void Profile(llvm::FoldingSetNodeID &id, Operand *op,
bool isClosureCaptured,
SILIsolationInfo isolationRegionInfo,
IsolationHistory isolationHistory) {
id.AddPointer(op);
id.AddBoolean(isClosureCaptured);
isolationRegionInfo.Profile(id);
id.AddPointer(isolationHistory.getHead());
}
class TransferringOperandToStateMap {
llvm::SmallDenseMap<Operand *, TransferringOperandState> internalMap;
IsolationHistory::Factory &isolationHistoryFactory;

void Profile(llvm::FoldingSetNodeID &id) const {
Profile(id, getOperand(), isClosureCaptured(), isolationInfo,
isolationHistory);
public:
TransferringOperandToStateMap(
IsolationHistory::Factory &isolationHistoryFactory)
: isolationHistoryFactory(isolationHistoryFactory) {}
TransferringOperandState &get(Operand *op) const {
auto *self = const_cast<TransferringOperandToStateMap *>(this);
auto history = IsolationHistory(&isolationHistoryFactory);
return self->internalMap.try_emplace(op, TransferringOperandState(history))
.first->getSecond();
}

SWIFT_DEBUG_DUMP { print(llvm::dbgs()); }
};

} // namespace swift
Expand Down Expand Up @@ -675,9 +648,8 @@ class Partition {

using Element = PartitionPrimitives::Element;
using Region = PartitionPrimitives::Region;
using TransferringOperandSet = ImmutablePointerSet<TransferringOperand *>;
using TransferringOperandSetFactory =
ImmutablePointerSetFactory<TransferringOperand *>;
using TransferringOperandSet = ImmutablePointerSet<Operand *>;
using TransferringOperandSetFactory = ImmutablePointerSetFactory<Operand *>;
using IsolationHistoryNode = IsolationHistory::Node;

private:
Expand All @@ -689,7 +661,7 @@ class Partition {
/// multi map here. The implication of this is that when we are performing
/// dataflow we use a union operation to combine CFG elements and just take
/// the first instruction that we see.
llvm::SmallDenseMap<Region, TransferringOperandSet *, 2>
llvm::SmallMapVector<Region, TransferringOperandSet *, 2>
regionToTransferredOpMap;

/// Label each index with a non-negative (unsigned) label if it is associated
Expand Down Expand Up @@ -736,7 +708,16 @@ class Partition {
fst.canonicalize();
snd.canonicalize();

return fst.elementToRegionMap == snd.elementToRegionMap;
return fst.elementToRegionMap == snd.elementToRegionMap &&
fst.regionToTransferredOpMap.size() ==
snd.regionToTransferredOpMap.size() &&
llvm::all_of(
fst.regionToTransferredOpMap,
[&snd](const std::pair<Region, TransferringOperandSet *> &p) {
auto sndIter = snd.regionToTransferredOpMap.find(p.first);
return sndIter != snd.regionToTransferredOpMap.end() &&
sndIter->second == p.second;
});
}

bool isTrackingElement(Element val) const {
Expand Down Expand Up @@ -1013,13 +994,16 @@ struct PartitionOpEvaluator {

protected:
TransferringOperandSetFactory &ptrSetFactory;
TransferringOperandToStateMap &operandToStateMap;

Partition &p;

public:
PartitionOpEvaluator(Partition &p,
TransferringOperandSetFactory &ptrSetFactory)
: ptrSetFactory(ptrSetFactory), p(p) {}
TransferringOperandSetFactory &ptrSetFactory,
TransferringOperandToStateMap &operandToStateMap)
: ptrSetFactory(ptrSetFactory), operandToStateMap(operandToStateMap),
p(p) {}

/// Call shouldEmitVerboseLogging on our CRTP subclass.
bool shouldEmitVerboseLogging() const {
Expand All @@ -1028,7 +1012,7 @@ struct PartitionOpEvaluator {

/// Call handleLocalUseAfterTransfer on our CRTP subclass.
void handleLocalUseAfterTransfer(const PartitionOp &op, Element elt,
TransferringOperand *transferringOp) const {
Operand *transferringOp) const {
return asImpl().handleLocalUseAfterTransfer(op, elt, transferringOp);
}

Expand Down Expand Up @@ -1193,9 +1177,13 @@ struct PartitionOpEvaluator {
}

// Mark op.getOpArgs()[0] as transferred.
auto *ptrSet = ptrSetFactory.emplace(
op.getSourceOp(), isClosureCapturedElt, transferredRegionIsolation,
p.getIsolationHistory());
TransferringOperandState &state = operandToStateMap.get(op.getSourceOp());
state.isClosureCaptured |= isClosureCapturedElt;
state.isolationInfo =
state.isolationInfo.merge(transferredRegionIsolation);
assert(state.isolationInfo && "Cannot have unknown");
state.isolationHistory.pushCFGHistoryJoin(p.getIsolationHistory());
auto *ptrSet = ptrSetFactory.get(op.getSourceOp());
p.markTransferred(op.getOpArgs()[0], ptrSet);
return;
}
Expand Down Expand Up @@ -1265,9 +1253,8 @@ struct PartitionOpEvaluator {
private:
// Private helper that squelches the error if our transfer instruction and our
// use have the same isolation.
void
handleLocalUseAfterTransferHelper(const PartitionOp &op, Element elt,
TransferringOperand *transferringOp) const {
void handleLocalUseAfterTransferHelper(const PartitionOp &op, Element elt,
Operand *transferringOp) const {
if (shouldTryToSquelchErrors()) {
if (auto isolationInfo = SILIsolationInfo::get(op.getSourceInst())) {
if (isolationInfo.isActorIsolated() &&
Expand Down Expand Up @@ -1305,8 +1292,9 @@ struct PartitionOpEvaluatorBaseImpl : PartitionOpEvaluator<Subclass> {
using Super = PartitionOpEvaluator<Subclass>;

PartitionOpEvaluatorBaseImpl(Partition &workingPartition,
TransferringOperandSetFactory &ptrSetFactory)
: Super(workingPartition, ptrSetFactory) {}
TransferringOperandSetFactory &ptrSetFactory,
TransferringOperandToStateMap &operandToStateMap)
: Super(workingPartition, ptrSetFactory, operandToStateMap) {}

/// Should we emit extra verbose logging statements when evaluating
/// PartitionOps.
Expand All @@ -1325,7 +1313,7 @@ struct PartitionOpEvaluatorBaseImpl : PartitionOpEvaluator<Subclass> {
/// region. Can be used to get the immediate value transferred or the
/// transferring instruction.
void handleLocalUseAfterTransfer(const PartitionOp &op, Element elt,
TransferringOperand *transferringOp) const {}
Operand *transferringOp) const {}

/// This is called if we detect a never transferred element that was passed to
/// a transfer instruction.
Expand Down Expand Up @@ -1373,8 +1361,10 @@ struct PartitionOpEvaluatorBaseImpl : PartitionOpEvaluator<Subclass> {
struct PartitionOpEvaluatorBasic final
: PartitionOpEvaluatorBaseImpl<PartitionOpEvaluatorBasic> {
PartitionOpEvaluatorBasic(Partition &workingPartition,
TransferringOperandSetFactory &ptrSetFactory)
: PartitionOpEvaluatorBaseImpl(workingPartition, ptrSetFactory) {}
TransferringOperandSetFactory &ptrSetFactory,
TransferringOperandToStateMap &operandToStateMap)
: PartitionOpEvaluatorBaseImpl(workingPartition, ptrSetFactory,
operandToStateMap) {}
};

} // namespace swift
Expand Down
25 changes: 17 additions & 8 deletions lib/SILOptimizer/Analysis/RegionAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2954,10 +2954,12 @@ TranslationSemantics PartitionOpTranslator::visitCheckedCastAddrBranchInst(
BlockPartitionState::BlockPartitionState(
SILBasicBlock *basicBlock, PartitionOpTranslator &translator,
TransferringOperandSetFactory &ptrSetFactory,
IsolationHistory::Factory &isolationHistoryFactory)
IsolationHistory::Factory &isolationHistoryFactory,
TransferringOperandToStateMap &transferringOpToStateMap)
: entryPartition(isolationHistoryFactory.get()),
exitPartition(isolationHistoryFactory.get()), basicBlock(basicBlock),
ptrSetFactory(ptrSetFactory) {
ptrSetFactory(ptrSetFactory),
transferringOpToStateMap(transferringOpToStateMap) {
translator.translateSILBasicBlock(basicBlock, blockPartitionOps);
}

Expand All @@ -2971,8 +2973,10 @@ bool BlockPartitionState::recomputeExitFromEntry(

ComputeEvaluator(Partition &workingPartition,
TransferringOperandSetFactory &ptrSetFactory,
PartitionOpTranslator &translator)
: PartitionOpEvaluatorBaseImpl(workingPartition, ptrSetFactory),
PartitionOpTranslator &translator,
TransferringOperandToStateMap &transferringOpToStateMap)
: PartitionOpEvaluatorBaseImpl(workingPartition, ptrSetFactory,
transferringOpToStateMap),
translator(translator) {}

SILIsolationInfo getIsolationRegionInfo(Element elt) const {
Expand All @@ -2989,7 +2993,8 @@ bool BlockPartitionState::recomputeExitFromEntry(
return translator.isClosureCaptured(value, op->getUser());
}
};
ComputeEvaluator eval(workingPartition, ptrSetFactory, translator);
ComputeEvaluator eval(workingPartition, ptrSetFactory, translator,
transferringOpToStateMap);
for (const auto &partitionOp : blockPartitionOps) {
// By calling apply without providing any error handling callbacks, errors
// will be surpressed. will be suppressed
Expand All @@ -3001,6 +3006,8 @@ bool BlockPartitionState::recomputeExitFromEntry(
exitPartition = workingPartition;
LLVM_DEBUG(llvm::dbgs() << " Exit Partition: ";
exitPartition.print(llvm::dbgs()));
LLVM_DEBUG(llvm::dbgs() << " Updated Partition: "
<< (exitUpdated ? "yes\n" : "no\n"));
return exitUpdated;
}

Expand Down Expand Up @@ -3072,8 +3079,9 @@ static bool canComputeRegionsForFunction(SILFunction *fn) {
RegionAnalysisFunctionInfo::RegionAnalysisFunctionInfo(
SILFunction *fn, PostOrderFunctionInfo *pofi)
: allocator(), fn(fn), valueMap(fn), translator(), ptrSetFactory(allocator),
isolationHistoryFactory(allocator), blockStates(), pofi(pofi),
solved(false), supportedFunction(true) {
isolationHistoryFactory(allocator),
transferringOpToStateMap(isolationHistoryFactory), blockStates(),
pofi(pofi), solved(false), supportedFunction(true) {
// Before we do anything, make sure that we support processing this function.
//
// NOTE: See documentation on supportedFunction for criteria.
Expand All @@ -3086,7 +3094,8 @@ RegionAnalysisFunctionInfo::RegionAnalysisFunctionInfo(
PartitionOpTranslator(fn, pofi, valueMap, isolationHistoryFactory);
blockStates.emplace(fn, [this](SILBasicBlock *block) -> BlockPartitionState {
return BlockPartitionState(block, *translator, ptrSetFactory,
isolationHistoryFactory);
isolationHistoryFactory,
transferringOpToStateMap);
});
// Mark all blocks as needing to be updated.
for (auto &block : *fn) {
Expand Down
Loading