Skip to content

Commit 26a75fe

Browse files
committed
[region-isolation] Implement checking for transferring parameters.
NOTE: This does not handle yet assignment into transferring parameters. In the next commit, I am going to teach the checker that assigning into such a parameter is a transfer.
1 parent 9513d29 commit 26a75fe

File tree

5 files changed

+276
-51
lines changed

5 files changed

+276
-51
lines changed

include/swift/SIL/ApplySite.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -721,6 +721,18 @@ class FullApplySite : public ApplySite {
721721
getNumIndirectSILErrorResults());
722722
}
723723

724+
MutableArrayRef<Operand> getOperandsWithoutIndirectResults() const {
725+
return getArgumentOperands().slice(getNumIndirectSILResults() +
726+
getNumIndirectSILErrorResults());
727+
}
728+
729+
MutableArrayRef<Operand> getOperandsWithoutIndirectResultsOrSelf() const {
730+
auto ops = getOperandsWithoutIndirectResults();
731+
if (!hasSelfArgument())
732+
return ops;
733+
return ops.drop_back();
734+
}
735+
724736
InoutArgumentRange getInoutArguments() const {
725737
switch (getKind()) {
726738
case FullApplySiteKind::ApplyInst:
@@ -789,6 +801,15 @@ class FullApplySite : public ApplySite {
789801
}
790802
}
791803

804+
SILParameterInfo getArgumentParameterInfo(const Operand &oper) const {
805+
assert(!getArgumentConvention(oper).isIndirectOutParameter() &&
806+
"Can only be applied to non-out parameters");
807+
808+
// The ParameterInfo is going to be the parameter in the caller.
809+
unsigned calleeArgIndex = getCalleeArgIndex(oper);
810+
return getSubstCalleeConv().getParamInfoForSILArg(calleeArgIndex);
811+
}
812+
792813
static FullApplySite getFromOpaqueValue(void *p) { return FullApplySite(p); }
793814

794815
static bool classof(const SILInstruction *inst) {

include/swift/SIL/SILArgument.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,10 @@ class SILFunctionArgument : public SILArgument {
434434
sharedUInt32().SILFunctionArgument.hasResultDependsOn = flag;
435435
}
436436

437+
bool isTransferring() const {
438+
return getKnownParameterInfo().hasOption(SILParameterInfo::Transferring);
439+
}
440+
437441
Lifetime getLifetime() const {
438442
return getType()
439443
.getLifetime(*getFunction())

include/swift/SIL/SILInstruction.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3009,6 +3009,10 @@ class ApplyInstBase<Impl, Base, true>
30093009
return getArguments().slice(getNumIndirectResults());
30103010
}
30113011

3012+
MutableArrayRef<Operand> getOperandsWithoutIndirectResults() const {
3013+
return getArgumentOperands().slice(getNumIndirectResults());
3014+
}
3015+
30123016
/// Returns all `@inout` and `@inout_aliasable` arguments passed to the
30133017
/// instruction.
30143018
InoutArgumentRange getInoutArguments() const {

lib/SILOptimizer/Analysis/RegionAnalysis.cpp

Lines changed: 120 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,10 @@ using namespace swift::regionanalysisimpl;
5757
static bool isIsolationBoundaryCrossingApply(SILInstruction *inst) {
5858
if (ApplyExpr *apply = inst->getLoc().getAsASTNode<ApplyExpr>())
5959
return apply->getIsolationCrossing().has_value();
60-
if (auto fas = FullApplySite::isa(inst))
61-
return bool(fas.getIsolationCrossing());
60+
if (auto fas = FullApplySite::isa(inst)) {
61+
if (bool(fas.getIsolationCrossing()))
62+
return true;
63+
}
6264

6365
// We assume that any instruction that does not correspond to an ApplyExpr
6466
// cannot cross an isolation domain.
@@ -249,8 +251,6 @@ static SILValue getUnderlyingTrackedValue(SILValue value) {
249251
UseDefChainVisitor visitor;
250252
SILValue base = visitor.visitAll(value);
251253
assert(base);
252-
if (isa<GlobalAddrInst>(base))
253-
return value;
254254
if (base->getType().isObject())
255255
return getUnderlyingObject(base);
256256
return base;
@@ -400,6 +400,38 @@ static bool isGlobalActorInit(SILFunction *fn) {
400400
return globalDecl->getGlobalActorAttr() != std::nullopt;
401401
}
402402

403+
/// Returns true if this is a function argument that is able to be transferred
404+
/// in the body of our function.
405+
static bool isTransferrableFunctionArgument(SILFunctionArgument *arg) {
406+
// Indirect out parameters cannot be an input transferring parameter.
407+
if (arg->getArgumentConvention().isIndirectOutParameter())
408+
return false;
409+
410+
// If we have a function argument that is closure captured by a Sendable
411+
// closure, allow for the argument to be transferred.
412+
//
413+
// DISCUSSION: The reason that we do this is that in the case of us
414+
// having an actual Sendable closure there are two cases we can see:
415+
//
416+
// 1. If we have an actual Sendable closure, the AST will emit an
417+
// earlier error saying that we are capturing a non-Sendable value in a
418+
// Sendable closure. So we want to squelch the error that we would emit
419+
// otherwise. This only occurs when we are not in swift-6 mode since in
420+
// swift-6 mode we will error on the earlier error... but in the case of
421+
// us not being in swift 6 mode lets not emit extra errors.
422+
//
423+
// 2. If we have an async-let based Sendable closure, we want to allow
424+
// for the argument to be transferred in the async let's statement and
425+
// not emit an error.
426+
if (arg->isClosureCapture() &&
427+
arg->getFunction()->getLoweredFunctionType()->isSendable())
428+
return true;
429+
430+
// Otherwise, we only allow for the argument to be transferred if it is
431+
// explicitly marked as a strong transferring parameter.
432+
return arg->isTransferring();
433+
}
434+
403435
//===----------------------------------------------------------------------===//
404436
// MARK: Partial Apply Reachability
405437
//===----------------------------------------------------------------------===//
@@ -1160,31 +1192,22 @@ class PartitionOpTranslator {
11601192
llvm::SmallVector<Element, 8> nonSendableSeparateIndices;
11611193
for (SILArgument *arg : functionArguments) {
11621194
if (auto state = tryToTrackValue(arg)) {
1163-
LLVM_DEBUG(llvm::dbgs() << " %%" << state->getID() << ": ");
1195+
LLVM_DEBUG(llvm::dbgs() << " %%" << state->getID() << ": " << *arg);
11641196

1165-
// If we have a function argument that is closure captured by a Sendable
1166-
// closure, allow for the argument to be transferred.
1167-
//
1168-
// DISCUSSION: The reason that we do this is that in the case of us
1169-
// having an actual Sendable closure there are two cases we can see:
1197+
// If we can transfer our parameter, just add it to
1198+
// nonSendableSeparateIndices.
11701199
//
1171-
// 1. If we have an actual Sendable closure, the AST will emit an
1172-
// earlier error saying that we are capturing a non-Sendable value in a
1173-
// Sendable closure. So we want to squelch the error that we would emit
1174-
// otherwise. This only occurs when we are not in swift-6 mode since in
1175-
// swift-6 mode we will error on the earlier error... but in the case of
1176-
// us not being in swift 6 mode lets not emit extra errors.
1177-
//
1178-
// 2. If we have an async-let based Sendable closure, we want to allow
1179-
// for the value to be transferred and not emit an error.
1180-
if (!cast<SILFunctionArgument>(arg)->isClosureCapture() ||
1181-
!function->getLoweredFunctionType()->isSendable()) {
1182-
addNeverTransferredValueID(state->getID());
1183-
nonSendableJoinedIndices.push_back(state->getID());
1184-
} else {
1200+
// NOTE: We do not support today the ability to have multiple parameters
1201+
// transfer together as part of the same region.
1202+
if (isTransferrableFunctionArgument(cast<SILFunctionArgument>(arg))) {
11851203
nonSendableSeparateIndices.push_back(state->getID());
1204+
continue;
11861205
}
1187-
LLVM_DEBUG(llvm::dbgs() << *arg);
1206+
1207+
// Otherwise, it is one of our merged parameters. Add it to the never
1208+
// transfer list and to the region join list.
1209+
valueMap.addNeverTransferredValueID(state->getID());
1210+
nonSendableJoinedIndices.push_back(state->getID());
11881211
}
11891212
}
11901213

@@ -1464,43 +1487,89 @@ class PartitionOpTranslator {
14641487
translateSILMultiAssign(applyResults, pai->getOperandValues(), options);
14651488
}
14661489

1467-
void translateSILApply(SILInstruction *inst) {
1468-
if (auto *bi = dyn_cast<BuiltinInst>(inst)) {
1469-
if (auto kind = bi->getBuiltinKind()) {
1470-
if (kind == BuiltinValueKind::StartAsyncLetWithLocalBuffer) {
1471-
return translateAsyncLetStart(bi);
1472-
}
1490+
void translateSILBuiltin(BuiltinInst *bi) {
1491+
if (auto kind = bi->getBuiltinKind()) {
1492+
if (kind == BuiltinValueKind::StartAsyncLetWithLocalBuffer) {
1493+
return translateAsyncLetStart(bi);
14731494
}
14741495
}
14751496

1476-
if (auto fas = FullApplySite::isa(inst)) {
1477-
if (auto *f = fas.getCalleeFunction()) {
1478-
// Check against the actual SILFunction.
1479-
if (f->getName() == "swift_asyncLet_get") {
1480-
return translateAsyncLetGet(cast<ApplyInst>(*fas));
1481-
}
1497+
// If we do not have a special builtin, just do a multi-assign. Builtins do
1498+
// not cross async boundaries.
1499+
return translateSILMultiAssign(bi->getResults(), bi->getOperandValues(),
1500+
{});
1501+
}
1502+
1503+
void translateNonIsolationCrossingSILApply(FullApplySite fas) {
1504+
SILMultiAssignOptions options;
1505+
if (fas.hasSelfArgument()) {
1506+
if (auto self = fas.getSelfArgument()) {
1507+
if (self->getType().isActor())
1508+
options |= SILMultiAssignFlags::PropagatesActorSelf;
14821509
}
14831510
}
14841511

1485-
// If this apply does not cross isolation domains, it has normal
1486-
// non-transferring multi-assignment semantics
1487-
if (!isIsolationBoundaryCrossingApply(inst)) {
1488-
SILMultiAssignOptions options;
1489-
if (auto fas = FullApplySite::isa(inst)) {
1490-
if (fas.hasSelfArgument()) {
1491-
if (auto self = fas.getSelfArgument()) {
1492-
if (self->getType().isActor())
1493-
options |= SILMultiAssignFlags::PropagatesActorSelf;
1512+
// For non-self parameters, gather all of the transferring parameters and
1513+
// gather our non-transferring parameters.
1514+
SmallVector<SILValue, 8> nonTransferringParameters;
1515+
if (fas.getNumArguments()) {
1516+
// NOTE: We want to process indirect parameters as if they are
1517+
// parameters... so we process them in nonTransferringParameters.
1518+
for (auto &op : fas.getOperandsWithoutSelf()) {
1519+
if (!fas.getArgumentConvention(op).isIndirectOutParameter() &&
1520+
fas.getArgumentParameterInfo(op).hasOption(
1521+
SILParameterInfo::Transferring)) {
1522+
if (auto value = tryToTrackValue(op.get())) {
1523+
builder.addTransfer(value->getRepresentative().getValue(), &op);
14941524
}
1525+
} else {
1526+
nonTransferringParameters.push_back(op.get());
14951527
}
14961528
}
1529+
}
1530+
1531+
// If our self parameter was transferring, transfer it. Otherwise, just
1532+
// stick it in the non seld operand values array and run multiassign on
1533+
// it.
1534+
if (fas.hasSelfArgument()) {
1535+
auto &selfOperand = fas.getSelfArgumentOperand();
1536+
if (fas.getArgumentParameterInfo(selfOperand)
1537+
.hasOption(SILParameterInfo::Transferring)) {
1538+
if (auto value = tryToTrackValue(selfOperand.get())) {
1539+
builder.addTransfer(value->getRepresentative().getValue(),
1540+
&selfOperand);
1541+
}
1542+
} else {
1543+
nonTransferringParameters.push_back(selfOperand.get());
1544+
}
1545+
}
1546+
1547+
SmallVector<SILValue, 8> applyResults;
1548+
getApplyResults(*fas, applyResults);
1549+
return translateSILMultiAssign(applyResults, nonTransferringParameters,
1550+
options);
1551+
}
14971552

1498-
SmallVector<SILValue, 8> applyResults;
1499-
getApplyResults(inst, applyResults);
1500-
return translateSILMultiAssign(applyResults, inst->getOperandValues(),
1501-
options);
1553+
void translateSILApply(SILInstruction *inst) {
1554+
if (auto *bi = dyn_cast<BuiltinInst>(inst)) {
1555+
return translateSILBuiltin(bi);
15021556
}
15031557

1558+
auto fas = FullApplySite::isa(inst);
1559+
assert(bool(fas) && "Builtins should be handled above");
1560+
1561+
if (auto *f = fas.getCalleeFunction()) {
1562+
// Check against the actual SILFunction.
1563+
if (f->getName() == "swift_asyncLet_get") {
1564+
return translateAsyncLetGet(cast<ApplyInst>(*fas));
1565+
}
1566+
}
1567+
1568+
// If this apply does not cross isolation domains, it has normal
1569+
// non-transferring multi-assignment semantics
1570+
if (!isIsolationBoundaryCrossingApply(inst))
1571+
return translateNonIsolationCrossingSILApply(fas);
1572+
15041573
if (auto cast = dyn_cast<ApplyInst>(inst))
15051574
return translateIsolationCrossingSILApply(cast);
15061575
if (auto cast = dyn_cast<BeginApplyInst>(inst))
@@ -1521,7 +1590,7 @@ class PartitionOpTranslator {
15211590
"only ApplyExpr's should cross isolation domains");
15221591

15231592
// require all operands
1524-
for (auto op : applySite->getOperandValues())
1593+
for (auto op : applySite.getArguments())
15251594
if (auto value = tryToTrackValue(op))
15261595
builder.addRequire(value->getRepresentative().getValue());
15271596

0 commit comments

Comments
 (0)