Skip to content

Commit 5f42a14

Browse files
author
jturcotti
committed
handle tuple creation
1 parent 3868f1d commit 5f42a14

File tree

2 files changed

+151
-68
lines changed

2 files changed

+151
-68
lines changed

lib/SILOptimizer/Mandatory/SendNonSendable.cpp

Lines changed: 79 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -338,93 +338,94 @@ class PartitionOpTranslator {
338338
// Merge, etc functions that generate PartitionOps with more logic common to
339339
// the translations from source-level SILInstructions.
340340

341-
std::vector<PartitionOp> translateSILApply(const SILInstruction *applyInst) {
342-
// accumulates the non-Sendable operands to this apply, include self and
343-
// the callee
344-
std::vector<TrackableSILValue> nonSendableOperands;
341+
// require all non-sendable sources, merge their regions, and assign the
342+
// resulting region to all non-sendable targets. If consumeSrcs is true then
343+
// consume the source region instead and put all the targets in a single,
344+
// fresh region.
345+
std::vector<PartitionOp> translateSILMultiAssign(
346+
std::vector<SILValue> tgts, std::vector<SILValue> srcs,
347+
bool consumeSrcs = false) {
348+
349+
std::vector<TrackableSILValue> nonSendableSrcs;
350+
std::vector<TrackableSILValue> nonSendableTgts;
345351

346-
for (SILValue operand : applyInst->getOperandValues())
347-
if (auto trackOperand = trackIfNonSendable(operand))
348-
nonSendableOperands.push_back(trackOperand.value());
352+
for (SILValue src : srcs)
353+
if (auto trackSrc = trackIfNonSendable(src))
354+
nonSendableSrcs.push_back(trackSrc.value());
349355

350-
// check whether the result is non-Sendable
351-
llvm::Optional<TrackableSILValue> nonSendableResult =
352-
trackIfNonSendable(applyInst->getResult(0));
356+
for (SILValue tgt : tgts)
357+
if (auto trackTgt = trackIfNonSendable(tgt))
358+
nonSendableTgts.push_back(trackTgt.value());
353359

354360
std::vector<PartitionOp> translated;
355361
auto add_to_translation = [&](std::vector<PartitionOp> ops) {
356362
for (auto op : ops) translated.push_back(op);
357363
};
358364

359-
if (SILApplyCrossesIsolation(applyInst)) {
360-
// for calls that cross isolation domains, consume all operands
361-
for (auto operand : nonSendableOperands)
362-
add_to_translation(Consume(operand));
365+
if (consumeSrcs) {
366+
for (auto src : nonSendableSrcs)
367+
add_to_translation(Consume(src));
368+
369+
// put all the tgts in a fresh region
370+
llvm::Optional<TrackableSILValue> fstTgt;
371+
for (auto tgt : nonSendableTgts)
372+
if (!fstTgt) {
373+
fstTgt = tgt;
374+
add_to_translation(AssignFresh(tgt));
375+
} else {
376+
add_to_translation(Assign(tgt, fstTgt.value()));
377+
}
363378

364-
if (nonSendableResult) {
365-
// returning non-Sendable values from a cross isolation call will always
366-
// be an error, but doesn't need to be diagnosed here, so let's pretend
367-
// it gets a fresh region
368-
add_to_translation(AssignFresh(nonSendableResult.value()));
369-
}
370379
return translated;
371380
}
372381

373-
// for calls that do not cross isolation domains, merge all non-Sendable
374-
// operands and assign the result to the region of the operands
382+
// require all srcs
383+
for (auto src : nonSendableSrcs)
384+
add_to_translation(Require(src));
375385

376-
if (nonSendableOperands.empty()) {
377-
// if no operands, a non-Sendable result gets a fresh region
378-
if (nonSendableResult) {
379-
add_to_translation(AssignFresh(nonSendableResult.value()));
380-
}
381-
return translated;
386+
// merge all srcs
387+
for (unsigned i = 1; i < nonSendableSrcs.size(); i++) {
388+
add_to_translation(Merge(nonSendableSrcs.at(i-1),
389+
nonSendableSrcs.at(i)));
382390
}
383391

384-
// insert Requires for all operands
385-
for (auto operand : nonSendableOperands)
386-
add_to_translation(Require(operand));
392+
// if no non-sendable tgts, return at this point
393+
if (nonSendableTgts.empty()) return translated;
387394

388-
// insert Merges all operands (left to right)
389-
for (unsigned i = 1; i < nonSendableOperands.size(); i++) {
390-
add_to_translation(Merge(nonSendableOperands.at(i-1),
391-
nonSendableOperands.at(i)));
395+
if (nonSendableSrcs.empty()) {
396+
// if no non-sendable srcs, non-sendable tgts get a fresh region
397+
add_to_translation(AssignFresh(nonSendableTgts.front()));
398+
} else {
399+
add_to_translation(Assign(nonSendableTgts.front(),
400+
nonSendableSrcs.front()));
392401
}
393402

394-
// if the result is non-Sendable, assign it to the region of the operands
395-
if (nonSendableResult) {
396-
add_to_translation(
397-
Assign(nonSendableResult.value(), nonSendableOperands.front()));
403+
// assign all targets to the target region
404+
for (unsigned i = 1; i < nonSendableTgts.size(); i++) {
405+
add_to_translation(Assign(nonSendableTgts.at(i),
406+
nonSendableTgts.front()));
398407
}
399408

400409
return translated;
401410
}
402411

403-
std::vector<PartitionOp> translateSILAssign(SILValue tgt, SILValue src) {
404-
auto nonSendableTarget = trackIfNonSendable(tgt);
405-
// no work to be done if assignment is to a Sendable target
406-
if (!nonSendableTarget)
407-
return {};
408-
409-
auto nonSendableSource = trackIfNonSendable(src);
410-
411-
if (nonSendableSource) {
412-
// non-Sendable source and target of assignment, so just perform the assign
413-
return Assign(nonSendableTarget.value(), nonSendableSource.value());
414-
}
412+
std::vector<PartitionOp> translateSILApply(const SILInstruction *applyInst) {
413+
return translateSILMultiAssign(
414+
{applyInst->getResult(0)},
415+
{applyInst->getOperandValues().begin(),
416+
applyInst->getOperandValues().end()},
417+
// consume operands iff the apply crosses isolation
418+
/*consumeSrcs=*/ SILApplyCrossesIsolation(applyInst));
419+
}
415420

416-
// a non-Sendable value is extracted from a Sendable value,
417-
// e.g. unchecked casts like `unchecked_ref_cast` or storing
418-
// of a sendable value into non-sendable storage
419-
return AssignFresh(nonSendableTarget.value());
421+
std::vector<PartitionOp> translateSILAssign(SILValue tgt, SILValue src) {
422+
return translateSILMultiAssign({tgt}, {src});
420423
}
421424

422425
// If the passed SILValue is NonSendable, then create a fresh region for it,
423426
// otherwise do nothing.
424427
std::vector<PartitionOp> translateSILAssignFresh(SILValue val) {
425-
if (auto nonSendableVal = trackIfNonSendable(val))
426-
return AssignFresh(nonSendableVal.value());
427-
return {};
428+
return translateSILMultiAssign({val}, {});
428429
}
429430

430431
std::vector<PartitionOp> translateSILMerge(SILValue fst, SILValue snd) {
@@ -500,6 +501,7 @@ class PartitionOpTranslator {
500501
RefElementAddrInst,
501502
StrongCopyUnownedValueInst,
502503
TailAddrInst,
504+
TupleElementAddrInst,
503505
UncheckedAddrCastInst,
504506
UncheckedOwnershipConversionInst,
505507
UncheckedRefCastInst>(instruction)) {
@@ -525,13 +527,19 @@ class PartitionOpTranslator {
525527
return translateSILApply(instruction);
526528
}
527529

528-
// Treat tuple destruction as a series of individual assignments
530+
// handle tuple destruction and creation
529531
if (auto destructTupleInst = dyn_cast<DestructureTupleInst>(instruction)) {
530-
std::vector<PartitionOp> translated;
531-
for (SILValue result : destructTupleInst->getResults())
532-
for (auto op : translateSILAssign(result, instruction->getOperand(0)))
533-
translated.push_back(op);
534-
return translated;
532+
return translateSILMultiAssign(
533+
{destructTupleInst->getResults().begin(),
534+
destructTupleInst->getResults().end()},
535+
{destructTupleInst->getOperand()});
536+
}
537+
538+
if (auto tupleInst = dyn_cast<TupleInst>(instruction)) {
539+
return translateSILMultiAssign(
540+
{tupleInst->getResult(0)},
541+
{tupleInst->getOperandValues().begin(),
542+
tupleInst->getOperandValues().end()});
535543
}
536544

537545
// Handle returns - require the operand to be non-consumed
@@ -549,7 +557,8 @@ class PartitionOpTranslator {
549557
EndLifetimeInst,
550558
HopToExecutorInst,
551559
MetatypeInst,
552-
DeallocStackInst>(instruction)) {
560+
DeallocStackInst,
561+
BranchInst>(instruction)) {
553562
//ignored instructions
554563
return {};
555564
}
@@ -1086,7 +1095,8 @@ class PartitionAnalysis {
10861095

10871096
bool solved;
10881097

1089-
const static int NUM_REQUIREMENTS_TO_DIAGNOSE = 5;
1098+
// TODO: make this configurable in a better way
1099+
const static int NUM_REQUIREMENTS_TO_DIAGNOSE = 50;
10901100

10911101
// The constructor initializes each block in the function by compiling it
10921102
// to PartitionOps, then seeds the solve method by setting `needsUpdate` to
@@ -1207,13 +1217,14 @@ class PartitionAnalysis {
12071217
blockState.diagnoseFailures(
12081218
/*handleFailure=*/
12091219
[&](const PartitionOp& partitionOp, TrackableValueID consumedVal) {
1220+
auto expr = getExprForPartitionOp(partitionOp);
1221+
if (hasBeenEmitted(expr)) return;
12101222
raceTracer.traceUseOfConsumedValue(partitionOp, consumedVal);
12111223
/*
12121224
* This handles diagnosing accesses to consumed values at the site
12131225
* of access instead of the site of consumption, as this is less
12141226
* useful it will likely be eliminated, but leaving it for now
1215-
auto expr = getExprForPartitionOp(partitionOp);
1216-
if (hasBeenEmitted(expr)) return;
1227+
12171228
function->getASTContext().Diags.diagnose(
12181229
expr->getLoc(), diag::consumed_value_used);
12191230
*/

test/Concurrency/DeferredSendableChecking/region_based_sendability.swift

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,3 +433,75 @@ func test_stack_assign_and_capture_merges(a : A, b : Bool) async {
433433
await a.foo(ns0) //expected-warning{{non-Sendable value sent across isolation domains here, but could be accessed later in this function}}
434434
await a.foo(ns1) //expected-note{{access here could race with non-Sendable value send above}}
435435
}
436+
437+
func test_tuple_formation(a : A, i : Int) async {
438+
let ns0 = NonSendable();
439+
let ns1 = NonSendable();
440+
let ns2 = NonSendable();
441+
let ns3 = NonSendable();
442+
let ns4 = NonSendable();
443+
let ns012 = (ns0, ns1, ns2);
444+
let ns13 = (ns1, ns3);
445+
446+
switch (i) {
447+
case 0:
448+
await a.foo(ns0) //expected-warning{{non-Sendable value sent across isolation domains here, but could be accessed later in this function}}
449+
450+
foo_noniso(ns0); //expected-note{{access here could race with non-Sendable value send above}}
451+
foo_noniso(ns1); //expected-note{{access here could race with non-Sendable value send above}}
452+
foo_noniso(ns2); //expected-note{{access here could race with non-Sendable value send above}}
453+
foo_noniso(ns3); //expected-note{{access here could race with non-Sendable value send above}}
454+
foo_noniso(ns4);
455+
foo_noniso(ns012); //expected-note{{access here could race with non-Sendable value send above}}
456+
foo_noniso(ns13); //expected-note{{access here could race with non-Sendable value send above}}
457+
case 1:
458+
await a.foo(ns1) //expected-warning{{non-Sendable value sent across isolation domains here, but could be accessed later in this function}}
459+
460+
foo_noniso(ns0); //expected-note{{access here could race with non-Sendable value send above}}
461+
foo_noniso(ns1); //expected-note{{access here could race with non-Sendable value send above}}
462+
foo_noniso(ns2); //expected-note{{access here could race with non-Sendable value send above}}
463+
foo_noniso(ns3); //expected-note{{access here could race with non-Sendable value send above}}
464+
foo_noniso(ns4);
465+
foo_noniso(ns012); //expected-note{{access here could race with non-Sendable value send above}}
466+
foo_noniso(ns13); //expected-note{{access here could race with non-Sendable value send above}}
467+
case 2:
468+
await a.foo(ns2) //expected-warning{{non-Sendable value sent across isolation domains here, but could be accessed later in this function}}
469+
470+
foo_noniso(ns0); //expected-note{{access here could race with non-Sendable value send above}}
471+
foo_noniso(ns1); //expected-note{{access here could race with non-Sendable value send above}}
472+
foo_noniso(ns2); //expected-note{{access here could race with non-Sendable value send above}}
473+
foo_noniso(ns3); //expected-note{{access here could race with non-Sendable value send above}}
474+
foo_noniso(ns4);
475+
foo_noniso(ns012); //expected-note{{access here could race with non-Sendable value send above}}
476+
foo_noniso(ns13); //expected-note{{access here could race with non-Sendable value send above}}
477+
case 3:
478+
await a.foo(ns4) //expected-warning{{non-Sendable value sent across isolation domains here, but could be accessed later in this function}}
479+
480+
foo_noniso(ns0);
481+
foo_noniso(ns1);
482+
foo_noniso(ns2);
483+
foo_noniso(ns4); //expected-note{{access here could race with non-Sendable value send above}}
484+
foo_noniso(ns012);
485+
foo_noniso(ns13);
486+
case 4:
487+
await a.foo(ns012) //expected-warning{{non-Sendable value sent across isolation domains here, but could be accessed later in this function}}
488+
489+
foo_noniso(ns0); //expected-note{{access here could race with non-Sendable value send above}}
490+
foo_noniso(ns1); //expected-note{{access here could race with non-Sendable value send above}}
491+
foo_noniso(ns2); //expected-note{{access here could race with non-Sendable value send above}}
492+
foo_noniso(ns3); //expected-note{{access here could race with non-Sendable value send above}}
493+
foo_noniso(ns4);
494+
foo_noniso(ns012); //expected-note{{access here could race with non-Sendable value send above}}
495+
foo_noniso(ns13); //expected-note{{access here could race with non-Sendable value send above}}
496+
default:
497+
await a.foo(ns13) //expected-warning{{non-Sendable value sent across isolation domains here, but could be accessed later in this function}}
498+
499+
foo_noniso(ns0); //expected-note{{access here could race with non-Sendable value send above}}
500+
foo_noniso(ns1); //expected-note{{access here could race with non-Sendable value send above}}
501+
foo_noniso(ns2); //expected-note{{access here could race with non-Sendable value send above}}
502+
foo_noniso(ns3); //expected-note{{access here could race with non-Sendable value send above}}
503+
foo_noniso(ns4);
504+
foo_noniso(ns012); //expected-note{{access here could race with non-Sendable value send above}}
505+
foo_noniso(ns13); //expected-note{{access here could race with non-Sendable value send above}}
506+
}
507+
}

0 commit comments

Comments
 (0)