@@ -338,93 +338,94 @@ class PartitionOpTranslator {
338
338
// Merge, etc functions that generate PartitionOps with more logic common to
339
339
// the translations from source-level SILInstructions.
340
340
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;
345
351
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 ());
349
355
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 ( ));
353
359
354
360
std::vector<PartitionOp> translated;
355
361
auto add_to_translation = [&](std::vector<PartitionOp> ops) {
356
362
for (auto op : ops) translated.push_back (op);
357
363
};
358
364
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
+ }
363
378
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
- }
370
379
return translated;
371
380
}
372
381
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));
375
385
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)));
382
390
}
383
391
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;
387
394
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 ()));
392
401
}
393
402
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 ()));
398
407
}
399
408
400
409
return translated;
401
410
}
402
411
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
+ }
415
420
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});
420
423
}
421
424
422
425
// If the passed SILValue is NonSendable, then create a fresh region for it,
423
426
// otherwise do nothing.
424
427
std::vector<PartitionOp> translateSILAssignFresh (SILValue val) {
425
- if (auto nonSendableVal = trackIfNonSendable (val))
426
- return AssignFresh (nonSendableVal.value ());
427
- return {};
428
+ return translateSILMultiAssign ({val}, {});
428
429
}
429
430
430
431
std::vector<PartitionOp> translateSILMerge (SILValue fst, SILValue snd) {
@@ -500,6 +501,7 @@ class PartitionOpTranslator {
500
501
RefElementAddrInst,
501
502
StrongCopyUnownedValueInst,
502
503
TailAddrInst,
504
+ TupleElementAddrInst,
503
505
UncheckedAddrCastInst,
504
506
UncheckedOwnershipConversionInst,
505
507
UncheckedRefCastInst>(instruction)) {
@@ -525,13 +527,19 @@ class PartitionOpTranslator {
525
527
return translateSILApply (instruction);
526
528
}
527
529
528
- // Treat tuple destruction as a series of individual assignments
530
+ // handle tuple destruction and creation
529
531
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 ()});
535
543
}
536
544
537
545
// Handle returns - require the operand to be non-consumed
@@ -549,7 +557,8 @@ class PartitionOpTranslator {
549
557
EndLifetimeInst,
550
558
HopToExecutorInst,
551
559
MetatypeInst,
552
- DeallocStackInst>(instruction)) {
560
+ DeallocStackInst,
561
+ BranchInst>(instruction)) {
553
562
// ignored instructions
554
563
return {};
555
564
}
@@ -1086,7 +1095,8 @@ class PartitionAnalysis {
1086
1095
1087
1096
bool solved;
1088
1097
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 ;
1090
1100
1091
1101
// The constructor initializes each block in the function by compiling it
1092
1102
// to PartitionOps, then seeds the solve method by setting `needsUpdate` to
@@ -1207,13 +1217,14 @@ class PartitionAnalysis {
1207
1217
blockState.diagnoseFailures (
1208
1218
/* handleFailure=*/
1209
1219
[&](const PartitionOp& partitionOp, TrackableValueID consumedVal) {
1220
+ auto expr = getExprForPartitionOp (partitionOp);
1221
+ if (hasBeenEmitted (expr)) return ;
1210
1222
raceTracer.traceUseOfConsumedValue (partitionOp, consumedVal);
1211
1223
/*
1212
1224
* This handles diagnosing accesses to consumed values at the site
1213
1225
* of access instead of the site of consumption, as this is less
1214
1226
* useful it will likely be eliminated, but leaving it for now
1215
- auto expr = getExprForPartitionOp(partitionOp);
1216
- if (hasBeenEmitted(expr)) return;
1227
+
1217
1228
function->getASTContext().Diags.diagnose(
1218
1229
expr->getLoc(), diag::consumed_value_used);
1219
1230
*/
0 commit comments