Skip to content

Commit b7249e7

Browse files
committed
[region-isolation] Rather than considering the callee as part of an applies merged region as a value, just propagate its isolation.
The reason that I am doing this is it ensures that if we have a region isolation merge failure due to a mismatch in between the actual args in the region and the propagated callee isolation, we see it immediately when we translate the apply into the pseudo-IR instead of later when we perform the actual diagnostic emission. This makes it far easier to diagnose these issues since we get an unknown pattern very early which can be asserted on via the option -sil-region-isolation-assert-on-unknown-pattern.
1 parent 0cd0868 commit b7249e7

File tree

1 file changed

+71
-29
lines changed

1 file changed

+71
-29
lines changed

lib/SILOptimizer/Analysis/RegionAnalysis.cpp

Lines changed: 71 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@ using namespace swift::PartitionPrimitives;
4747
using namespace swift::PatternMatch;
4848
using namespace swift::regionanalysisimpl;
4949

50+
static llvm::cl::opt<bool> AbortOnUnknownPatternMatchError(
51+
"sil-region-isolation-assert-on-unknown-pattern",
52+
llvm::cl::desc("Abort if SIL region isolation detects an unknown pattern. "
53+
"Intended only to be used when debugging the compiler!"),
54+
llvm::cl::init(false), llvm::cl::Hidden);
55+
5056
//===----------------------------------------------------------------------===//
5157
// MARK: Utilities
5258
//===----------------------------------------------------------------------===//
@@ -1191,6 +1197,10 @@ struct PartitionOpBuilder {
11911197
}
11921198

11931199
void addUnknownPatternError(SILValue value) {
1200+
if (AbortOnUnknownPatternMatchError) {
1201+
llvm::report_fatal_error(
1202+
"RegionIsolation: Aborting on unknown pattern match error");
1203+
}
11941204
currentInstPartitionOps.emplace_back(
11951205
PartitionOp::UnknownPatternError(lookupValueID(value), currentInst));
11961206
}
@@ -1510,12 +1520,12 @@ class PartitionOpTranslator {
15101520
std::optional<std::pair<TrackableValue, bool>>
15111521
initializeTrackedValue(SILValue value, SILIsolationInfo info) const {
15121522
auto trackedValuePair = valueMap.initializeTrackableValue(value, info);
1513-
if (trackedValuePair.first.isNonSendable()) {
1514-
assert(trackedValuePair.second);
1515-
return trackedValuePair;
1516-
}
1517-
assert(trackedValuePair.second);
1518-
return {};
1523+
1524+
// If we have a Sendable value return none.
1525+
if (!trackedValuePair.first.isNonSendable())
1526+
return {};
1527+
1528+
return trackedValuePair;
15191529
}
15201530

15211531
TrackableValue
@@ -1564,27 +1574,52 @@ class PartitionOpTranslator {
15641574
/// Require all non-sendable sources, merge their regions, and assign the
15651575
/// resulting region to all non-sendable targets, or assign non-sendable
15661576
/// targets to a fresh region if there are no non-sendable sources.
1577+
///
1578+
/// \arg isolationInfo An isolation info that can be specified as the true
1579+
/// base isolation of results. Otherwise, results are assumed to have a
1580+
/// element isolation of disconnected. NOTE: The results will still be in the
1581+
/// region of the non-Sendable arguments so at the region level they will have
1582+
/// the same value.
15671583
template <typename TargetRange, typename SourceRange>
1568-
void translateSILMultiAssign(const TargetRange &resultValues,
1569-
const SourceRange &sourceValues,
1570-
SILIsolationInfo isolationInfo = {}) {
1584+
void
1585+
translateSILMultiAssign(const TargetRange &resultValues,
1586+
const SourceRange &sourceValues,
1587+
SILIsolationInfo resultIsolationInfoOverride = {}) {
15711588
SmallVector<SILValue, 8> assignOperands;
15721589
SmallVector<SILValue, 8> assignResults;
15731590

1591+
std::optional<SILDynamicMergedIsolationInfo> mergedInfo;
1592+
if (resultIsolationInfoOverride) {
1593+
mergedInfo = resultIsolationInfoOverride;
1594+
} else {
1595+
mergedInfo = SILIsolationInfo::getDisconnected(false);
1596+
}
1597+
15741598
for (SILValue src : sourceValues) {
15751599
if (auto value = tryToTrackValue(src)) {
15761600
assignOperands.push_back(value->getRepresentative().getValue());
1601+
mergedInfo = mergedInfo->merge(value->getIsolationRegionInfo());
1602+
1603+
// If we fail to merge, then we have an incompatibility in between some
1604+
// of our arguments (consider isolated to different actors) or with the
1605+
// isolationInfo we specified. Emit an unknown patten error.
1606+
if (!mergedInfo) {
1607+
builder.addUnknownPatternError(src);
1608+
continue;
1609+
}
15771610
}
15781611
}
15791612

15801613
for (SILValue result : resultValues) {
1581-
if (isolationInfo) {
1614+
// If we had isolation info explicitly passed in... use our
1615+
// mergedInfo. Otherwise, we want to infer.
1616+
if (resultIsolationInfoOverride) {
15821617
// We only get back result if it is non-Sendable.
15831618
if (auto nonSendableValue =
1584-
initializeTrackedValue(result, isolationInfo)) {
1619+
initializeTrackedValue(result, resultIsolationInfoOverride)) {
1620+
// If we did not insert, emit an unknown patten error.
15851621
if (!nonSendableValue->second) {
15861622
builder.addUnknownPatternError(result);
1587-
continue;
15881623
}
15891624
assignResults.push_back(
15901625
nonSendableValue->first.getRepresentative().getValue());
@@ -1611,8 +1646,13 @@ class PartitionOpTranslator {
16111646
// non-Sendable operands and we are supposed to mark value as actor
16121647
// derived, introduce a fake element so we just propagate the actor
16131648
// region.
1614-
if (assignOperands.size() && isolationInfo) {
1615-
builder.addActorIntroducingInst(assignOperands.back(), isolationInfo);
1649+
//
1650+
// NOTE: Here we check if we have mergedInfo rather than isolationInfo
1651+
// since we want to do this regardless of whether or not we passed in a
1652+
// specific isolation info unlike earlier when processing actual results.
1653+
if (assignOperands.size() && resultIsolationInfoOverride) {
1654+
builder.addActorIntroducingInst(assignOperands.back(),
1655+
resultIsolationInfoOverride);
16161656
}
16171657

16181658
return;
@@ -1818,8 +1858,7 @@ class PartitionOpTranslator {
18181858

18191859
// If we do not have a special builtin, just do a multi-assign. Builtins do
18201860
// not cross async boundaries.
1821-
return translateSILMultiAssign(bi->getResults(), bi->getOperandValues(),
1822-
{});
1861+
return translateSILMultiAssign(bi->getResults(), bi->getOperandValues());
18231862
}
18241863

18251864
void translateNonIsolationCrossingSILApply(FullApplySite fas) {
@@ -1864,11 +1903,16 @@ class PartitionOpTranslator {
18641903
}
18651904
}
18661905

1867-
// Add our callee to non-transferring parameters. This ensures that if it is
1868-
// actor isolated, that propagates into our results. This is especially
1869-
// important since our callee could be dynamically isolated and we cannot
1870-
// know that until we perform dataflow.
1871-
nonTransferringParameters.push_back(fas.getCallee());
1906+
// Require our callee operand if it is non-Sendable.
1907+
//
1908+
// DISCUSSION: Even though we do not include our callee operand in the same
1909+
// region as our operands/results, we still need to require that it is live
1910+
// at the point of application. Otherwise, we will not emit errors if the
1911+
// closure before this function application is already in the same region as
1912+
// a transferred value. In such a case, the function application must error.
1913+
if (auto value = tryToTrackValue(fas.getCallee())) {
1914+
builder.addRequire(value->getRepresentative().getValue());
1915+
}
18721916

18731917
SmallVector<SILValue, 8> applyResults;
18741918
getApplyResults(*fas, applyResults);
@@ -2769,18 +2813,14 @@ PartitionOpTranslator::visitAllocStackInst(AllocStackInst *asi) {
27692813

27702814
// Ok at this point we know that our value is a non-Sendable temporary.
27712815
auto isolationInfo = SILIsolationInfo::get(asi);
2772-
if (!bool(isolationInfo)) {
2773-
return TranslationSemantics::AssignFresh;
2774-
}
2775-
2776-
if (isolationInfo.isDisconnected()) {
2816+
if (!bool(isolationInfo) || isolationInfo.isDisconnected()) {
27772817
return TranslationSemantics::AssignFresh;
27782818
}
27792819

27802820
// Ok, we can handle this and have a valid isolation. Initialize the value.
27812821
auto v = initializeTrackedValue(asi, isolationInfo);
2782-
if (!v)
2783-
return TranslationSemantics::AssignFresh;
2822+
assert(v && "Only return none if we have a sendable value, but we checked "
2823+
"that earlier!");
27842824

27852825
// If we already had a value for this alloc_stack (which we shouldn't
27862826
// ever)... emit an unknown pattern error.
@@ -2789,7 +2829,9 @@ PartitionOpTranslator::visitAllocStackInst(AllocStackInst *asi) {
27892829
return TranslationSemantics::Special;
27902830
}
27912831

2792-
translateSILAssignFresh(v->first.getRepresentative().getValue());
2832+
// NOTE: To prevent an additional reinitialization by the canned AssignFresh
2833+
// code, we do our own assign fresh and return special.
2834+
builder.addAssignFresh(v->first.getRepresentative().getValue());
27932835
return TranslationSemantics::Special;
27942836
}
27952837

0 commit comments

Comments
 (0)