Skip to content

Commit 6d15e41

Browse files
authored
Merge pull request #74123 from gottesmm/pr-9e8378fdeee3204a34f48ea8d2ff8f0be40a4674
[region-isolation] Make store_borrow a store operation that does not require
2 parents e431216 + bd472b1 commit 6d15e41

File tree

4 files changed

+103
-39
lines changed

4 files changed

+103
-39
lines changed

Diff for: include/swift/SILOptimizer/Utils/PartitionUtils.h

-22
Original file line numberDiff line numberDiff line change
@@ -1025,14 +1025,6 @@ struct PartitionOpEvaluator {
10251025
"Assign PartitionOp should be passed 2 arguments");
10261026
assert(p.isTrackingElement(op.getOpArgs()[1]) &&
10271027
"Assign PartitionOp's source argument should be already tracked");
1028-
// If we are using a region that was transferred as our assignment source
1029-
// value... emit an error.
1030-
if (auto *transferredOperandSet = p.getTransferred(op.getOpArgs()[1])) {
1031-
for (auto transferredOperand : transferredOperandSet->data()) {
1032-
handleLocalUseAfterTransferHelper(op, op.getOpArgs()[1],
1033-
transferredOperand);
1034-
}
1035-
}
10361028
p.assignElement(op.getOpArgs()[0], op.getOpArgs()[1]);
10371029
return;
10381030
case PartitionOpKind::AssignFresh:
@@ -1122,20 +1114,6 @@ struct PartitionOpEvaluator {
11221114
p.isTrackingElement(op.getOpArgs()[1]) &&
11231115
"Merge PartitionOp's arguments should already be tracked");
11241116

1125-
// if attempting to merge a transferred region, handle the failure
1126-
if (auto *transferredOperandSet = p.getTransferred(op.getOpArgs()[0])) {
1127-
for (auto transferredOperand : transferredOperandSet->data()) {
1128-
handleLocalUseAfterTransferHelper(op, op.getOpArgs()[0],
1129-
transferredOperand);
1130-
}
1131-
}
1132-
if (auto *transferredOperandSet = p.getTransferred(op.getOpArgs()[1])) {
1133-
for (auto transferredOperand : transferredOperandSet->data()) {
1134-
handleLocalUseAfterTransferHelper(op, op.getOpArgs()[1],
1135-
transferredOperand);
1136-
}
1137-
}
1138-
11391117
p.merge(op.getOpArgs()[0], op.getOpArgs()[1]);
11401118
return;
11411119
case PartitionOpKind::Require:

Diff for: lib/SILOptimizer/Analysis/RegionAnalysis.cpp

+61-8
Original file line numberDiff line numberDiff line change
@@ -1584,7 +1584,8 @@ class PartitionOpTranslator {
15841584
void
15851585
translateSILMultiAssign(const TargetRange &resultValues,
15861586
const SourceRange &sourceValues,
1587-
SILIsolationInfo resultIsolationInfoOverride = {}) {
1587+
SILIsolationInfo resultIsolationInfoOverride = {},
1588+
bool requireSrcValues = true) {
15881589
SmallVector<SILValue, 8> assignOperands;
15891590
SmallVector<SILValue, 8> assignResults;
15901591

@@ -1631,9 +1632,17 @@ class PartitionOpTranslator {
16311632
}
16321633
}
16331634

1634-
// Require all srcs.
1635-
for (auto src : assignOperands)
1636-
builder.addRequire(src);
1635+
// Require all srcs if we are supposed to. (By default we do).
1636+
//
1637+
// DISCUSSION: The reason that this may be useful is for special
1638+
// instructions like store_borrow. On the one hand, we want store_borrow to
1639+
// act like a store in the sense that we want to combine the regions of its
1640+
// src and dest... but at the same time, we do not want to treat the store
1641+
// itself as a use of its parent value. We want that to be any subsequent
1642+
// uses of the store_borrow.
1643+
if (requireSrcValues)
1644+
for (auto src : assignOperands)
1645+
builder.addRequire(src);
16371646

16381647
// Merge all srcs.
16391648
for (unsigned i = 1; i < assignOperands.size(); i++) {
@@ -2082,21 +2091,28 @@ class PartitionOpTranslator {
20822091
}
20832092

20842093
template <typename Collection>
2085-
void translateSILMerge(SILValue dest, Collection collection) {
2094+
void translateSILMerge(SILValue dest, Collection collection,
2095+
bool requireOperands = true) {
20862096
auto trackableDest = tryToTrackValue(dest);
20872097
if (!trackableDest)
20882098
return;
20892099
for (SILValue elt : collection) {
20902100
if (auto trackableSrc = tryToTrackValue(elt)) {
2101+
if (requireOperands) {
2102+
builder.addRequire(trackableSrc->getRepresentative().getValue());
2103+
builder.addRequire(trackableDest->getRepresentative().getValue());
2104+
}
20912105
builder.addMerge(trackableDest->getRepresentative().getValue(),
20922106
trackableSrc->getRepresentative().getValue());
20932107
}
20942108
}
20952109
}
20962110

20972111
template <>
2098-
void translateSILMerge<SILValue>(SILValue dest, SILValue src) {
2099-
return translateSILMerge(dest, TinyPtrVector<SILValue>(src));
2112+
void translateSILMerge<SILValue>(SILValue dest, SILValue src,
2113+
bool requireOperands) {
2114+
return translateSILMerge(dest, TinyPtrVector<SILValue>(src),
2115+
requireOperands);
21002116
}
21012117

21022118
void translateSILAssignmentToTransferringParameter(TrackableValue destRoot,
@@ -2572,7 +2588,6 @@ CONSTANT_TRANSLATION(InitExistentialValueInst, LookThrough)
25722588
CONSTANT_TRANSLATION(CopyAddrInst, Store)
25732589
CONSTANT_TRANSLATION(ExplicitCopyAddrInst, Store)
25742590
CONSTANT_TRANSLATION(StoreInst, Store)
2575-
CONSTANT_TRANSLATION(StoreBorrowInst, Store)
25762591
CONSTANT_TRANSLATION(StoreWeakInst, Store)
25772592
CONSTANT_TRANSLATION(MarkUnresolvedMoveAddrInst, Store)
25782593
CONSTANT_TRANSLATION(UncheckedRefCastAddrInst, Store)
@@ -2804,6 +2819,44 @@ LOOKTHROUGH_IF_NONSENDABLE_RESULT_AND_OPERAND(UncheckedTakeEnumDataAddrInst)
28042819
// Custom Handling
28052820
//
28062821

2822+
TranslationSemantics
2823+
PartitionOpTranslator::visitStoreBorrowInst(StoreBorrowInst *sbi) {
2824+
// A store_borrow is an interesting instruction since we are essentially
2825+
// temporarily binding an object value to an address... so really any uses of
2826+
// the address, we want to consider to be uses of the parent object. So we
2827+
// basically put source/dest into the same region, but do not consider the
2828+
// store_borrow itself to be a require use. This prevents the store_borrow
2829+
// from causing incorrect diagnostics.
2830+
SILValue destValue = sbi->getDest();
2831+
SILValue srcValue = sbi->getSrc();
2832+
2833+
auto nonSendableDest = tryToTrackValue(destValue);
2834+
if (!nonSendableDest)
2835+
return TranslationSemantics::Ignored;
2836+
2837+
// In the following situations, we can perform an assign:
2838+
//
2839+
// 1. A store to unaliased storage.
2840+
// 2. A store that is to an entire value.
2841+
//
2842+
// DISCUSSION: If we have case 2, we need to merge the regions since we
2843+
// are not overwriting the entire region of the value. This does mean that
2844+
// we artificially include the previous region that was stored
2845+
// specifically in this projection... but that is better than
2846+
// miscompiling. For memory like this, we probably need to track it on a
2847+
// per field basis to allow for us to assign.
2848+
if (nonSendableDest.value().isNoAlias() &&
2849+
!isProjectedFromAggregate(destValue)) {
2850+
translateSILMultiAssign(sbi->getResults(), sbi->getOperandValues(),
2851+
SILIsolationInfo(), false /*require src*/);
2852+
} else {
2853+
// Stores to possibly aliased storage must be treated as merges.
2854+
translateSILMerge(destValue, srcValue, false /*require src*/);
2855+
}
2856+
2857+
return TranslationSemantics::Special;
2858+
}
2859+
28072860
TranslationSemantics
28082861
PartitionOpTranslator::visitAllocStackInst(AllocStackInst *asi) {
28092862
// Before we do anything, see if asi is Sendable or if it is non-Sendable,

Diff for: test/Concurrency/transfernonsendable.swift

+11-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
////////////////////////
1616

1717
/// Classes are always non-sendable, so this is non-sendable
18-
class NonSendableKlass { // expected-complete-note 51{{}}
18+
class NonSendableKlass { // expected-complete-note 53{{}}
1919
// expected-typechecker-only-note @-1 3{{}}
2020
// expected-tns-note @-2 {{}}
2121
var field: NonSendableKlass? = nil
@@ -1736,3 +1736,13 @@ func sendableGlobalActorIsolated() {
17361736
}
17371737
print(x) // expected-tns-note {{access can happen concurrently}}
17381738
}
1739+
1740+
// We do not get an error here since we are transferring x both times to a main
1741+
// actor isolated thing function. We used to emit an error when using region
1742+
// isolation since we would trip on the store_borrow we used to materialize the
1743+
// value.
1744+
func testIndirectParameterSameIsolationNoError() async {
1745+
let x = NonSendableKlass()
1746+
await transferToMain(x) // expected-complete-warning {{passing argument of non-sendable type 'NonSendableKlass' into main actor-isolated context may introduce data races}}
1747+
await transferToMain(x) // expected-complete-warning {{passing argument of non-sendable type 'NonSendableKlass' into main actor-isolated context may introduce data races}}
1748+
}

Diff for: test/Concurrency/transfernonsendable_asynclet.swift

+31-8
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
////////////////////////
1010

1111
/// Classes are always non-sendable, so this is non-sendable
12-
class NonSendableKlass { // expected-complete-note 96{{}}
12+
class NonSendableKlass { // expected-complete-note 99{{}}
1313
// expected-tns-note @-1 {{}}
1414
var field: NonSendableKlass? = nil
1515
var field2: NonSendableKlass? = nil
@@ -34,6 +34,13 @@ final actor FinalActor {
3434
func useKlass(_ x: NonSendableKlass) {}
3535
}
3636

37+
actor CustomActorInstance {}
38+
39+
@globalActor
40+
struct CustomActor {
41+
static let shared = CustomActorInstance()
42+
}
43+
3744
func useInOut<T>(_ x: inout T) {}
3845
@discardableResult
3946
func useValue<T>(_ x: T) -> T { x }
@@ -42,6 +49,7 @@ func useValueWrapInOptional<T>(_ x: T) -> T? { x }
4249
@MainActor func returnValueFromMain<T>() async -> T { fatalError() }
4350
@MainActor func transferToMain<T>(_ t: T) async {}
4451
@MainActor func transferToMainInt<T>(_ t: T) async -> Int { 5 }
52+
@CustomActor func transferToCustomInt<T>(_ t: T) async -> Int { 5 }
4553
@MainActor func transferToMainIntOpt<T>(_ t: T) async -> Int? { 5 }
4654

4755
func transferToNonIsolated<T>(_ t: T) async {}
@@ -306,18 +314,33 @@ func asyncLet_Let_ActorIsolated_CallBuriedInOtherExpr3() async {
306314
let _ = await y
307315
}
308316

309-
// Make sure that we emit an error for transferToMainInt in the async val
310-
// function itself.
317+
// Make sure that we do not emit an error for transferToMainInt in the async val
318+
// function itself since we are sending the value to the same main actor
319+
// isolated use and transferring it into one async let variable.
311320
func asyncLet_Let_ActorIsolated_CallBuriedInOtherExpr4() async {
312321
let x = NonSendableKlass()
313322

314-
async let y = useValue(transferToMainInt(x) + transferToMainInt(x)) // expected-tns-warning {{sending 'x' risks causing data races}}
315-
// expected-tns-note @-1 {{sending 'x' to main actor-isolated global function 'transferToMainInt' risks causing data races between main actor-isolated and local nonisolated uses}}
316-
// expected-tns-note @-2:67 {{access can happen concurrently}}
323+
async let y = useValue(transferToMainInt(x) + transferToMainInt(x))
324+
// expected-complete-warning @-1 {{capture of 'x' with non-sendable type 'NonSendableKlass' in 'async let' binding}}
325+
// expected-complete-warning @-2 {{passing argument of non-sendable type 'NonSendableKlass' into main actor-isolated context may introduce data races}}
326+
// expected-complete-warning @-3 {{passing argument of non-sendable type 'NonSendableKlass' into main actor-isolated context may introduce data races}}
317327

318-
// expected-complete-warning @-4 {{capture of 'x' with non-sendable type 'NonSendableKlass' in 'async let' binding}}
319-
// expected-complete-warning @-5 {{passing argument of non-sendable type 'NonSendableKlass' into main actor-isolated context may introduce data races}}
328+
let _ = await y
329+
}
330+
331+
// Make sure that we do emit an error since we are sending the value to two
332+
// different isolation domains in the async let.
333+
func asyncLet_Let_ActorIsolated_CallBuriedInOtherExpr5() async {
334+
let x = NonSendableKlass()
335+
336+
async let y = useValue(transferToMainInt(x) + transferToCustomInt(x))
337+
// expected-tns-warning @-1 {{sending 'x' risks causing data races}}
338+
// expected-tns-note @-2 {{sending 'x' to main actor-isolated global function 'transferToMainInt' risks causing data races between main actor-isolated and local nonisolated uses}}
339+
// expected-tns-note @-3:49 {{access can happen concurrently}}
340+
341+
// expected-complete-warning @-5 {{capture of 'x' with non-sendable type 'NonSendableKlass' in 'async let' binding}}
320342
// expected-complete-warning @-6 {{passing argument of non-sendable type 'NonSendableKlass' into main actor-isolated context may introduce data races}}
343+
// expected-complete-warning @-7 {{passing argument of non-sendable type 'NonSendableKlass' into global actor 'CustomActor'-isolated context may introduce data races}}
321344

322345
let _ = await y
323346
}

0 commit comments

Comments
 (0)