Skip to content

Commit 8686d3f

Browse files
authored
Merge pull request #84830 from gottesmm/pr-280fbe6eca8e73a6caec46dd571aa9afe8bf8b93
[rbi] Cleanup handling of how we send parameters to fix issues with inout sending.
2 parents c6e814f + 13e8eed commit 8686d3f

File tree

4 files changed

+2750
-2562
lines changed

4 files changed

+2750
-2562
lines changed

include/swift/SIL/ApplySite.h

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -645,14 +645,27 @@ class ApplySite {
645645
return getSubstCalleeConv().getParamInfoForSILArg(calleeArgIndex);
646646
}
647647

648+
/// Returns true if \p op is the callee operand of this apply site
649+
/// and not an argument operand.
650+
///
651+
/// If this instruction is not a full apply site, this always returns false.
652+
bool isCalleeOperand(const Operand &op) const;
653+
654+
/// Returns true if this is an 'out' parameter.
648655
bool isSending(const Operand &oper) const {
649-
if (isIndirectErrorResultOperand(oper))
656+
if (isIndirectErrorResultOperand(oper) || oper.isTypeDependent() ||
657+
isCalleeOperand(oper))
650658
return false;
651659
if (isIndirectResultOperand(oper))
652660
return getSubstCalleeType()->hasSendingResult();
653661
return getArgumentParameterInfo(oper).hasOption(SILParameterInfo::Sending);
654662
}
655663

664+
/// Returns true if this operand is an 'inout sending' parameter.
665+
bool isInOutSending(const Operand &oper) const {
666+
return isSending(oper) && getArgumentConvention(oper).isInoutConvention();
667+
}
668+
656669
/// Return true if 'operand' is addressable after type substitution in the
657670
/// caller's context.
658671
bool isAddressable(const Operand &operand) const;
@@ -1056,6 +1069,13 @@ inline bool ApplySite::isIndirectErrorResultOperand(const Operand &op) const {
10561069
return fas.isIndirectErrorResultOperand(op);
10571070
}
10581071

1072+
inline bool ApplySite::isCalleeOperand(const Operand &op) const {
1073+
auto fas = asFullApplySite();
1074+
if (!fas)
1075+
return false;
1076+
return fas.isCalleeOperand(op);
1077+
}
1078+
10591079
} // namespace swift
10601080

10611081
#endif

lib/SILOptimizer/Analysis/RegionAnalysis.cpp

Lines changed: 106 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1723,9 +1723,13 @@ struct PartitionOpBuilder {
17231723
PartitionOp::Send(lookupValueID(representative), op));
17241724
}
17251725

1726-
void addUndoSend(SILValue representative, SILInstruction *unsendingInst) {
1726+
void addUndoSend(TrackableValue value, SILInstruction *unsendingInst) {
1727+
if (value.isSendable())
1728+
return;
1729+
1730+
auto representative = value.getRepresentative().getValue();
17271731
assert(valueHasID(representative) &&
1728-
"value should already have been encountered");
1732+
"sent value should already have been encountered");
17291733

17301734
currentInstPartitionOps->emplace_back(
17311735
PartitionOp::UndoSend(lookupValueID(representative), unsendingInst));
@@ -2430,7 +2434,7 @@ class PartitionOpTranslator {
24302434
}))
24312435
continue;
24322436

2433-
builder.addUndoSend(trackedArgValue.getRepresentative().getValue(), ai);
2437+
builder.addUndoSend(trackedArgValue, ai);
24342438
}
24352439
}
24362440
}
@@ -2567,60 +2571,53 @@ class PartitionOpTranslator {
25672571
// gather our non-sending parameters.
25682572
SmallVector<Operand *, 8> nonSendingParameters;
25692573
SmallVector<Operand *, 8> sendingIndirectResults;
2570-
if (fas.getNumArguments()) {
2571-
// NOTE: We want to process indirect parameters as if they are
2572-
// parameters... so we process them in nonSendingParameters.
2573-
for (auto &op : fas.getOperandsWithoutSelf()) {
2574-
// If op is the callee operand, skip it.
2575-
if (fas.isCalleeOperand(op))
2576-
continue;
25772574

2578-
if (fas.isSending(op)) {
2579-
if (fas.isIndirectResultOperand(op)) {
2580-
sendingIndirectResults.push_back(&op);
2581-
continue;
2582-
}
2575+
// NOTE: We want to process indirect parameters as if they are
2576+
// parameters... so we process them in nonSendingParameters.
2577+
for (auto &op : fas->getAllOperands()) {
2578+
// If op is the callee operand or type dependent operand, skip it.
2579+
if (op.isTypeDependent())
2580+
continue;
25832581

2584-
// Attempt to lookup the value we are passing as sending. We want to
2585-
// require/send value if it is non-Sendable and require its base if it
2586-
// is non-Sendable as well.
2587-
if (auto lookupResult = tryToTrackValue(op.get())) {
2588-
builder.addRequire(*lookupResult);
2589-
builder.addSend(lookupResult->value, &op);
2590-
}
2591-
} else {
2592-
nonSendingParameters.push_back(&op);
2582+
if (fas.isCalleeOperand(op)) {
2583+
if (auto calleeResult = tryToTrackValue(op.get())) {
2584+
builder.addRequire(*calleeResult);
25932585
}
2586+
continue;
25942587
}
2595-
}
25962588

2597-
// If our self parameter was sending, send it. Otherwise, just
2598-
// stick it in the non self operand values array and run multiassign on
2599-
// it.
2600-
if (fas.hasSelfArgument()) {
2601-
auto &selfOperand = fas.getSelfArgumentOperand();
2602-
if (fas.getArgumentParameterInfo(selfOperand)
2603-
.hasOption(SILParameterInfo::Sending)) {
2604-
if (auto lookupResult = tryToTrackValue(selfOperand.get())) {
2605-
builder.addRequire(*lookupResult);
2606-
builder.addSend(lookupResult->value, &selfOperand);
2607-
}
2608-
} else {
2609-
nonSendingParameters.push_back(&selfOperand);
2589+
// If our parameter is not sending, just add it to the non-sending
2590+
// parameters array and continue.
2591+
if (!fas.isSending(op)) {
2592+
nonSendingParameters.push_back(&op);
2593+
continue;
26102594
}
2611-
}
26122595

2613-
// Require our callee operand if it is non-Sendable.
2614-
//
2615-
// DISCUSSION: Even though we do not include our callee operand in the same
2616-
// region as our operands/results, we still need to require that it is live
2617-
// at the point of application. Otherwise, we will not emit errors if the
2618-
// closure before this function application is already in the same region as
2619-
// a sent value. In such a case, the function application must error.
2620-
if (auto calleeResult = tryToTrackValue(fas.getCallee())) {
2621-
builder.addRequire(*calleeResult);
2596+
// Otherwise, first handle indirect result operands.
2597+
if (fas.isIndirectResultOperand(op)) {
2598+
sendingIndirectResults.push_back(&op);
2599+
continue;
2600+
}
2601+
2602+
// Attempt to lookup the value we are passing as sending. We want to
2603+
// require/send value if it is non-Sendable and require its base if it
2604+
// is non-Sendable as well.
2605+
if (auto lookupResult = tryToTrackValue(op.get())) {
2606+
builder.addRequire(*lookupResult);
2607+
builder.addSend(lookupResult->value, &op);
2608+
}
26222609
}
26232610

2611+
SWIFT_DEFER {
2612+
for (auto &op : fas->getAllOperands()) {
2613+
if (!fas.isInOutSending(op))
2614+
continue;
2615+
if (auto lookupResult = tryToTrackValue(op.get())) {
2616+
builder.addUndoSend(lookupResult->value, op.getUser());
2617+
}
2618+
}
2619+
};
2620+
26242621
SmallVector<SILValue, 8> applyResults;
26252622
getApplyResults(*fas, applyResults);
26262623

@@ -2688,36 +2685,77 @@ class PartitionOpTranslator {
26882685

26892686
/// Handles the semantics for SIL applies that cross isolation.
26902687
///
2691-
/// Semantically this causes all arguments of the applysite to be sent.
2688+
/// Semantically we are attempting to implement the following:
2689+
///
2690+
/// * Step 1: Require all non-sending parameters and then send those
2691+
/// parameters. We perform all of the requires first and the sends second
2692+
/// since all of the parameters are getting sent to the same isolation
2693+
/// domains and become part of the same region in our callee. So in a
2694+
/// certain sense, we are performing a require over the entire merge of the
2695+
/// parameter regions and then send each constituant part of the region
2696+
/// without requiring again so we do not emit use-after-send diagnostics.
2697+
///
2698+
/// * Step 2: Require/Send each of the sending parameters one by one. This
2699+
/// includes both 'sending' and 'inout sending' parameters. We purposely
2700+
/// interleave the require/send operations to ensure that if one passes a
2701+
/// value twice to different 'sending' or 'inout sending' parameters, we
2702+
/// will emit an error.
2703+
///
2704+
/// * Step 3: Unsend each of the unsending parameters. Since our caller
2705+
/// ensures that 'inout sending' parameters are disconnected on return and
2706+
/// are in different regions from all other parameters, we can just simply
2707+
/// unsend the parameter so we can use it again later.
26922708
void translateIsolationCrossingSILApply(FullApplySite applySite) {
2693-
// Require all operands first before we emit a send.
2694-
for (auto op : applySite.getArguments()) {
2695-
if (auto lookupResult = tryToTrackValue(op)) {
2709+
SmallVector<TrackableValue, 8> inoutSendingParams;
2710+
2711+
// First go through and require all of our operands that are not 'sending'
2712+
for (auto &op : applySite.getArgumentOperands()) {
2713+
if (applySite.isSending(op))
2714+
continue;
2715+
if (auto lookupResult = tryToTrackValue(op.get()))
26962716
builder.addRequire(*lookupResult);
2697-
}
26982717
}
26992718

2700-
auto handleSILOperands = [&](MutableArrayRef<Operand> operands) {
2701-
for (auto &op : operands) {
2702-
if (auto lookupResult = tryToTrackValue(op.get())) {
2703-
builder.addSend(lookupResult->value, &op);
2704-
}
2719+
// Then go through our operands again and send all of our non-sending
2720+
// parameters. We do not interleave these sends with our requires since we
2721+
// are considering these values to be merged into the same region. We could
2722+
// also merge them in the caller but there is no point in doing so
2723+
// semantically since the values cannot be used again locally.
2724+
for (auto &op : applySite.getOperandsWithoutIndirectResults()) {
2725+
if (applySite.isSending(op))
2726+
continue;
2727+
if (auto lookupResult = tryToTrackValue(op.get())) {
2728+
builder.addSend(lookupResult->value, &op);
27052729
}
27062730
};
27072731

2708-
auto handleSILSelf = [&](Operand *self) {
2709-
if (auto lookupResult = tryToTrackValue(self->get())) {
2710-
builder.addSend(lookupResult->value, self);
2732+
// Then go through our 'sending' params and require/send each in sequence.
2733+
//
2734+
// We do this interleaved so that if a value is passed to multiple 'sending'
2735+
// parameters, we emit errors.
2736+
for (auto &op : applySite.getOperandsWithoutIndirectResults()) {
2737+
if (!applySite.isSending(op))
2738+
continue;
2739+
auto lookupResult = tryToTrackValue(op.get());
2740+
if (!lookupResult)
2741+
continue;
2742+
builder.addRequire(*lookupResult);
2743+
builder.addSend(lookupResult->value, &op);
2744+
}
2745+
2746+
// Now use a SWIFT_DEFER so that when the function is done executing, we
2747+
// unsend 'inout sending' params.
2748+
SWIFT_DEFER {
2749+
for (auto &op : applySite.getOperandsWithoutIndirectResults()) {
2750+
if (!applySite.isInOutSending(op))
2751+
continue;
2752+
auto lookupResult = tryToTrackValue(op.get());
2753+
if (!lookupResult)
2754+
continue;
2755+
builder.addUndoSend(lookupResult->value, *applySite);
27112756
}
27122757
};
27132758

2714-
if (applySite.hasSelfArgument()) {
2715-
handleSILOperands(applySite.getOperandsWithoutIndirectResultsOrSelf());
2716-
handleSILSelf(&applySite.getSelfArgumentOperand());
2717-
} else {
2718-
handleSILOperands(applySite.getOperandsWithoutIndirectResults());
2719-
}
2720-
27212759
// Create a new assign fresh for each one of our values and unless our
27222760
// return value is sending, emit an extra error bit on the results that are
27232761
// non-Sendable.

0 commit comments

Comments
 (0)