Skip to content

Commit ca8179a

Browse files
committed
[region-isolation] Track operand info in a separate map rather than inline in a TransferringOperand data structure.
This is backing out an approach that I thought would be superior, but ended up causing problems. Originally, we mapped a region number to an immutable pointer set containing Operand * where the region was tranferred. This worked great for a time... until I began to need to propagate other information from the transferring code in the analysis to the actual diagnostic emitter. To be able to do that, my thought was to make a wrapper type around Operand called TransferringOperand that contained the operand and the other information I needed. This seemed to provide me what I wanted but I later found that since the immutable pointer set was tracking TransferringOperands which were always newly wrapped with an Operand *, we actually always created new pointer sets. This is of course wasteful from a memory perspective, but also prevents me from tracking transferring operand sets during the dataflow since we would never converge. In this commit, I fix that issue by again tracking just an Operand * in the TransferringOperandSet and instead map each operand to a state structure which we merge dataflow state into whenever we visit it. This provides us with everything we need to in the next commit to including a region -> transferring operand set equality check in our dataflow equations and always converge.
1 parent 13d0139 commit ca8179a

File tree

7 files changed

+257
-247
lines changed

7 files changed

+257
-247
lines changed

include/swift/SILOptimizer/Analysis/RegionAnalysis.h

+16-1
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,13 @@ class BlockPartitionState {
8888

8989
TransferringOperandSetFactory &ptrSetFactory;
9090

91+
TransferringOperandToStateMap &transferringOpToStateMap;
92+
9193
BlockPartitionState(SILBasicBlock *basicBlock,
9294
PartitionOpTranslator &translator,
9395
TransferringOperandSetFactory &ptrSetFactory,
94-
IsolationHistory::Factory &isolationHistoryFactory);
96+
IsolationHistory::Factory &isolationHistoryFactory,
97+
TransferringOperandToStateMap &transferringOpToStateMap);
9598

9699
public:
97100
bool getLiveness() const { return isLive; }
@@ -397,6 +400,8 @@ class RegionAnalysisFunctionInfo {
397400

398401
IsolationHistory::Factory isolationHistoryFactory;
399402

403+
TransferringOperandToStateMap transferringOpToStateMap;
404+
400405
// We make this optional to prevent an issue that we have seen on windows when
401406
// capturing a field in a closure that is used to initialize a different
402407
// field.
@@ -492,6 +497,16 @@ class RegionAnalysisFunctionInfo {
492497
return valueMap;
493498
}
494499

500+
IsolationHistory::Factory &getIsolationHistoryFactory() {
501+
assert(supportedFunction && "Unsupported Function?!");
502+
return isolationHistoryFactory;
503+
}
504+
505+
TransferringOperandToStateMap &getTransferringOpToStateMap() {
506+
assert(supportedFunction && "Unsupported Function?!");
507+
return transferringOpToStateMap;
508+
}
509+
495510
bool isClosureCaptured(SILValue value, Operand *op);
496511

497512
static SILValue getUnderlyingTrackedValue(SILValue value);

include/swift/SILOptimizer/Utils/PartitionUtils.h

+53-76
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ class SILIsolationInfo {
224224
};
225225

226226
class Partition;
227+
class TransferringOperandToStateMap;
227228

228229
/// A persistent data structure that is used to "rewind" partition history so
229230
/// that we can discover when values become part of the same region.
@@ -245,6 +246,7 @@ class IsolationHistory {
245246

246247
// TODO: This shouldn't need to be a friend.
247248
friend class Partition;
249+
friend TransferringOperandToStateMap;
248250

249251
/// First node in the immutable linked list.
250252
Node *head = nullptr;
@@ -298,6 +300,11 @@ class IsolationHistory {
298300
/// Push that \p other should be merged into this region.
299301
void pushCFGHistoryJoin(Node *otherNode);
300302

303+
/// Push the top node of \p history as a CFG history join.
304+
void pushCFGHistoryJoin(IsolationHistory history) {
305+
return pushCFGHistoryJoin(history.getHead());
306+
}
307+
301308
Node *pop();
302309
};
303310

@@ -457,10 +464,7 @@ class IsolationHistory::Factory {
457464
IsolationHistory get() { return IsolationHistory(this); }
458465
};
459466

460-
class TransferringOperand {
461-
using ValueType = llvm::PointerIntPair<Operand *, 1>;
462-
ValueType value;
463-
467+
struct TransferringOperandState {
464468
/// The dynamic isolation info of the region of value when we transferred.
465469
///
466470
/// This will contain the isolated value if we found one.
@@ -469,65 +473,30 @@ class TransferringOperand {
469473
/// The dynamic isolation history at this point.
470474
IsolationHistory isolationHistory;
471475

472-
TransferringOperand(ValueType newValue, SILIsolationInfo isolationRegionInfo,
473-
IsolationHistory isolationHistory)
474-
: value(newValue), isolationInfo(isolationRegionInfo),
475-
isolationHistory(isolationHistory) {
476-
assert(isolationInfo && "Should never see unknown isolation info");
477-
}
478-
479-
public:
480-
TransferringOperand(Operand *op, bool isClosureCaptured,
481-
SILIsolationInfo isolationRegionInfo,
482-
IsolationHistory isolationHistory)
483-
: TransferringOperand({op, isClosureCaptured}, isolationRegionInfo,
484-
isolationHistory) {}
485-
explicit TransferringOperand(Operand *op,
486-
SILIsolationInfo isolationRegionInfo,
487-
IsolationHistory isolationHistory)
488-
: TransferringOperand({op, false}, isolationRegionInfo,
489-
isolationHistory) {}
490-
491-
operator bool() const { return bool(value.getPointer()); }
492-
493-
Operand *getOperand() const { return value.getPointer(); }
494-
495-
SILValue get() const { return getOperand()->get(); }
496-
497-
bool isClosureCaptured() const { return value.getInt(); }
498-
499-
SILInstruction *getUser() const { return getOperand()->getUser(); }
476+
/// Set to true if the element associated with the operand's vlaue is closure
477+
/// captured by the user. In such a case, if our element is a sendable var of
478+
/// a non-Sendable type, we cannot access it since we could race against an
479+
/// assignment to the var in a closure.
480+
bool isClosureCaptured;
500481

501-
SILIsolationInfo getIsolationInfo() const { return isolationInfo; }
502-
503-
IsolationHistory getIsolationHistory() const { return isolationHistory; }
504-
505-
unsigned getOperandNumber() const { return getOperand()->getOperandNumber(); }
506-
507-
void print(llvm::raw_ostream &os) const {
508-
os << "Op Num: " << getOperand()->getOperandNumber() << ". "
509-
<< "Capture: " << (isClosureCaptured() ? "yes. " : "no. ")
510-
<< "IsolationInfo: ";
511-
isolationInfo.print(os);
512-
os << "\nUser: " << *getUser();
513-
}
482+
TransferringOperandState(IsolationHistory history)
483+
: isolationInfo(), isolationHistory(history), isClosureCaptured(false) {}
484+
};
514485

515-
static void Profile(llvm::FoldingSetNodeID &id, Operand *op,
516-
bool isClosureCaptured,
517-
SILIsolationInfo isolationRegionInfo,
518-
IsolationHistory isolationHistory) {
519-
id.AddPointer(op);
520-
id.AddBoolean(isClosureCaptured);
521-
isolationRegionInfo.Profile(id);
522-
id.AddPointer(isolationHistory.getHead());
523-
}
486+
class TransferringOperandToStateMap {
487+
llvm::SmallDenseMap<Operand *, TransferringOperandState> internalMap;
488+
IsolationHistory::Factory &isolationHistoryFactory;
524489

525-
void Profile(llvm::FoldingSetNodeID &id) const {
526-
Profile(id, getOperand(), isClosureCaptured(), isolationInfo,
527-
isolationHistory);
490+
public:
491+
TransferringOperandToStateMap(
492+
IsolationHistory::Factory &isolationHistoryFactory)
493+
: isolationHistoryFactory(isolationHistoryFactory) {}
494+
TransferringOperandState &get(Operand *op) const {
495+
auto *self = const_cast<TransferringOperandToStateMap *>(this);
496+
auto history = IsolationHistory(&isolationHistoryFactory);
497+
return self->internalMap.try_emplace(op, TransferringOperandState(history))
498+
.first->getSecond();
528499
}
529-
530-
SWIFT_DEBUG_DUMP { print(llvm::dbgs()); }
531500
};
532501

533502
} // namespace swift
@@ -676,9 +645,8 @@ class Partition {
676645

677646
using Element = PartitionPrimitives::Element;
678647
using Region = PartitionPrimitives::Region;
679-
using TransferringOperandSet = ImmutablePointerSet<TransferringOperand *>;
680-
using TransferringOperandSetFactory =
681-
ImmutablePointerSetFactory<TransferringOperand *>;
648+
using TransferringOperandSet = ImmutablePointerSet<Operand *>;
649+
using TransferringOperandSetFactory = ImmutablePointerSetFactory<Operand *>;
682650
using IsolationHistoryNode = IsolationHistory::Node;
683651

684652
private:
@@ -1014,13 +982,16 @@ struct PartitionOpEvaluator {
1014982

1015983
protected:
1016984
TransferringOperandSetFactory &ptrSetFactory;
985+
TransferringOperandToStateMap &operandToStateMap;
1017986

1018987
Partition &p;
1019988

1020989
public:
1021990
PartitionOpEvaluator(Partition &p,
1022-
TransferringOperandSetFactory &ptrSetFactory)
1023-
: ptrSetFactory(ptrSetFactory), p(p) {}
991+
TransferringOperandSetFactory &ptrSetFactory,
992+
TransferringOperandToStateMap &operandToStateMap)
993+
: ptrSetFactory(ptrSetFactory), operandToStateMap(operandToStateMap),
994+
p(p) {}
1024995

1025996
/// Call shouldEmitVerboseLogging on our CRTP subclass.
1026997
bool shouldEmitVerboseLogging() const {
@@ -1029,7 +1000,7 @@ struct PartitionOpEvaluator {
10291000

10301001
/// Call handleLocalUseAfterTransfer on our CRTP subclass.
10311002
void handleLocalUseAfterTransfer(const PartitionOp &op, Element elt,
1032-
TransferringOperand *transferringOp) const {
1003+
Operand *transferringOp) const {
10331004
return asImpl().handleLocalUseAfterTransfer(op, elt, transferringOp);
10341005
}
10351006

@@ -1194,9 +1165,13 @@ struct PartitionOpEvaluator {
11941165
}
11951166

11961167
// Mark op.getOpArgs()[0] as transferred.
1197-
auto *ptrSet = ptrSetFactory.emplace(
1198-
op.getSourceOp(), isClosureCapturedElt, transferredRegionIsolation,
1199-
p.getIsolationHistory());
1168+
TransferringOperandState &state = operandToStateMap.get(op.getSourceOp());
1169+
state.isClosureCaptured |= isClosureCapturedElt;
1170+
state.isolationInfo =
1171+
state.isolationInfo.merge(transferredRegionIsolation);
1172+
assert(state.isolationInfo && "Cannot have unknown");
1173+
state.isolationHistory.pushCFGHistoryJoin(p.getIsolationHistory());
1174+
auto *ptrSet = ptrSetFactory.get(op.getSourceOp());
12001175
p.markTransferred(op.getOpArgs()[0], ptrSet);
12011176
return;
12021177
}
@@ -1266,9 +1241,8 @@ struct PartitionOpEvaluator {
12661241
private:
12671242
// Private helper that squelches the error if our transfer instruction and our
12681243
// use have the same isolation.
1269-
void
1270-
handleLocalUseAfterTransferHelper(const PartitionOp &op, Element elt,
1271-
TransferringOperand *transferringOp) const {
1244+
void handleLocalUseAfterTransferHelper(const PartitionOp &op, Element elt,
1245+
Operand *transferringOp) const {
12721246
if (shouldTryToSquelchErrors()) {
12731247
if (auto isolationInfo = SILIsolationInfo::get(op.getSourceInst())) {
12741248
if (isolationInfo.isActorIsolated() &&
@@ -1306,8 +1280,9 @@ struct PartitionOpEvaluatorBaseImpl : PartitionOpEvaluator<Subclass> {
13061280
using Super = PartitionOpEvaluator<Subclass>;
13071281

13081282
PartitionOpEvaluatorBaseImpl(Partition &workingPartition,
1309-
TransferringOperandSetFactory &ptrSetFactory)
1310-
: Super(workingPartition, ptrSetFactory) {}
1283+
TransferringOperandSetFactory &ptrSetFactory,
1284+
TransferringOperandToStateMap &operandToStateMap)
1285+
: Super(workingPartition, ptrSetFactory, operandToStateMap) {}
13111286

13121287
/// Should we emit extra verbose logging statements when evaluating
13131288
/// PartitionOps.
@@ -1326,7 +1301,7 @@ struct PartitionOpEvaluatorBaseImpl : PartitionOpEvaluator<Subclass> {
13261301
/// region. Can be used to get the immediate value transferred or the
13271302
/// transferring instruction.
13281303
void handleLocalUseAfterTransfer(const PartitionOp &op, Element elt,
1329-
TransferringOperand *transferringOp) const {}
1304+
Operand *transferringOp) const {}
13301305

13311306
/// This is called if we detect a never transferred element that was passed to
13321307
/// a transfer instruction.
@@ -1374,8 +1349,10 @@ struct PartitionOpEvaluatorBaseImpl : PartitionOpEvaluator<Subclass> {
13741349
struct PartitionOpEvaluatorBasic final
13751350
: PartitionOpEvaluatorBaseImpl<PartitionOpEvaluatorBasic> {
13761351
PartitionOpEvaluatorBasic(Partition &workingPartition,
1377-
TransferringOperandSetFactory &ptrSetFactory)
1378-
: PartitionOpEvaluatorBaseImpl(workingPartition, ptrSetFactory) {}
1352+
TransferringOperandSetFactory &ptrSetFactory,
1353+
TransferringOperandToStateMap &operandToStateMap)
1354+
: PartitionOpEvaluatorBaseImpl(workingPartition, ptrSetFactory,
1355+
operandToStateMap) {}
13791356
};
13801357

13811358
} // namespace swift

lib/SILOptimizer/Analysis/RegionAnalysis.cpp

+15-8
Original file line numberDiff line numberDiff line change
@@ -2954,10 +2954,12 @@ TranslationSemantics PartitionOpTranslator::visitCheckedCastAddrBranchInst(
29542954
BlockPartitionState::BlockPartitionState(
29552955
SILBasicBlock *basicBlock, PartitionOpTranslator &translator,
29562956
TransferringOperandSetFactory &ptrSetFactory,
2957-
IsolationHistory::Factory &isolationHistoryFactory)
2957+
IsolationHistory::Factory &isolationHistoryFactory,
2958+
TransferringOperandToStateMap &transferringOpToStateMap)
29582959
: entryPartition(isolationHistoryFactory.get()),
29592960
exitPartition(isolationHistoryFactory.get()), basicBlock(basicBlock),
2960-
ptrSetFactory(ptrSetFactory) {
2961+
ptrSetFactory(ptrSetFactory),
2962+
transferringOpToStateMap(transferringOpToStateMap) {
29612963
translator.translateSILBasicBlock(basicBlock, blockPartitionOps);
29622964
}
29632965

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

29722974
ComputeEvaluator(Partition &workingPartition,
29732975
TransferringOperandSetFactory &ptrSetFactory,
2974-
PartitionOpTranslator &translator)
2975-
: PartitionOpEvaluatorBaseImpl(workingPartition, ptrSetFactory),
2976+
PartitionOpTranslator &translator,
2977+
TransferringOperandToStateMap &transferringOpToStateMap)
2978+
: PartitionOpEvaluatorBaseImpl(workingPartition, ptrSetFactory,
2979+
transferringOpToStateMap),
29762980
translator(translator) {}
29772981

29782982
SILIsolationInfo getIsolationRegionInfo(Element elt) const {
@@ -2989,7 +2993,8 @@ bool BlockPartitionState::recomputeExitFromEntry(
29892993
return translator.isClosureCaptured(value, op->getUser());
29902994
}
29912995
};
2992-
ComputeEvaluator eval(workingPartition, ptrSetFactory, translator);
2996+
ComputeEvaluator eval(workingPartition, ptrSetFactory, translator,
2997+
transferringOpToStateMap);
29932998
for (const auto &partitionOp : blockPartitionOps) {
29942999
// By calling apply without providing any error handling callbacks, errors
29953000
// will be surpressed. will be suppressed
@@ -3074,8 +3079,9 @@ static bool canComputeRegionsForFunction(SILFunction *fn) {
30743079
RegionAnalysisFunctionInfo::RegionAnalysisFunctionInfo(
30753080
SILFunction *fn, PostOrderFunctionInfo *pofi)
30763081
: allocator(), fn(fn), valueMap(fn), translator(), ptrSetFactory(allocator),
3077-
isolationHistoryFactory(allocator), blockStates(), pofi(pofi),
3078-
solved(false), supportedFunction(true) {
3082+
isolationHistoryFactory(allocator),
3083+
transferringOpToStateMap(isolationHistoryFactory), blockStates(),
3084+
pofi(pofi), solved(false), supportedFunction(true) {
30793085
// Before we do anything, make sure that we support processing this function.
30803086
//
30813087
// NOTE: See documentation on supportedFunction for criteria.
@@ -3088,7 +3094,8 @@ RegionAnalysisFunctionInfo::RegionAnalysisFunctionInfo(
30883094
PartitionOpTranslator(fn, pofi, valueMap, isolationHistoryFactory);
30893095
blockStates.emplace(fn, [this](SILBasicBlock *block) -> BlockPartitionState {
30903096
return BlockPartitionState(block, *translator, ptrSetFactory,
3091-
isolationHistoryFactory);
3097+
isolationHistoryFactory,
3098+
transferringOpToStateMap);
30923099
});
30933100
// Mark all blocks as needing to be updated.
30943101
for (auto &block : *fn) {

0 commit comments

Comments
 (0)