mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Rename transfer -> send.
Accomplished using clangd's rename functionality.
This commit is contained in:
@@ -963,28 +963,28 @@ NOTE(regionbasedisolation_type_is_non_sendable, none,
|
||||
//===
|
||||
// Use After Send Emitter
|
||||
|
||||
ERROR(regionbasedisolation_named_transfer_yields_race, none,
|
||||
ERROR(regionbasedisolation_named_send_yields_race, none,
|
||||
"sending %0 risks causing data races",
|
||||
(Identifier))
|
||||
ERROR(regionbasedisolation_type_transfer_yields_race, none,
|
||||
ERROR(regionbasedisolation_type_send_yields_race, none,
|
||||
"sending value of non-Sendable type %0 risks causing data races",
|
||||
(Type))
|
||||
NOTE(regionbasedisolation_type_use_after_transfer, none,
|
||||
NOTE(regionbasedisolation_type_use_after_send, none,
|
||||
"sending value of non-Sendable type %0 to %1 callee risks causing data races between %1 and local %2 uses",
|
||||
(Type, ActorIsolation, ActorIsolation))
|
||||
NOTE(regionbasedisolation_type_use_after_transfer_callee, none,
|
||||
NOTE(regionbasedisolation_type_use_after_send_callee, none,
|
||||
"sending value of non-Sendable type %0 to %1 %2 %3 risks causing data "
|
||||
"races between %1 and local %4 uses",
|
||||
(Type, ActorIsolation, DescriptiveDeclKind, DeclName, ActorIsolation))
|
||||
|
||||
NOTE(regionbasedisolation_named_info_transfer_yields_race, none,
|
||||
NOTE(regionbasedisolation_named_info_send_yields_race, none,
|
||||
"sending %1%0 to %2 callee risks causing data races between %2 and local %3 uses",
|
||||
(Identifier, StringRef, ActorIsolation, ActorIsolation))
|
||||
NOTE(regionbasedisolation_named_info_transfer_yields_race_callee, none,
|
||||
NOTE(regionbasedisolation_named_info_send_yields_race_callee, none,
|
||||
"sending %1%0 to %2 %3 %4 risks causing data races between %2 and local %5 uses",
|
||||
(Identifier, StringRef, ActorIsolation, DescriptiveDeclKind, DeclName, ActorIsolation))
|
||||
|
||||
// Use after transfer closure.
|
||||
// Use after send closure.
|
||||
NOTE(regionbasedisolation_type_isolated_capture_yields_race, none,
|
||||
"sending value of non-Sendable type %0 to %1 closure due to closure capture risks causing races in between %1 and %2 uses",
|
||||
(Type, ActorIsolation, ActorIsolation))
|
||||
@@ -1011,18 +1011,18 @@ NOTE(regionbasedisolation_typed_use_after_sending_callee, none,
|
||||
//===
|
||||
// Sending Never Sendable Emitter
|
||||
|
||||
NOTE(regionbasedisolation_named_transfer_non_transferrable, none,
|
||||
NOTE(regionbasedisolation_named_send_never_sendable, none,
|
||||
"sending %1%0 to %2 callee risks causing data races between %2 and %3 uses",
|
||||
(Identifier, StringRef, ActorIsolation, StringRef))
|
||||
NOTE(regionbasedisolation_named_transfer_non_transferrable_callee, none,
|
||||
NOTE(regionbasedisolation_named_send_never_sendable_callee, none,
|
||||
"sending %1%0 to %2 %3 %4 risks causing data races between %2 and %5 uses",
|
||||
(Identifier, StringRef, ActorIsolation, DescriptiveDeclKind, DeclName, StringRef))
|
||||
|
||||
NOTE(regionbasedisolation_named_transfer_into_sending_param, none,
|
||||
NOTE(regionbasedisolation_named_send_into_sending_param, none,
|
||||
"%0%1 is passed as a 'sending' parameter; Uses in callee may race with "
|
||||
"later %0uses",
|
||||
(StringRef, Identifier))
|
||||
NOTE(regionbasedisolation_named_notransfer_transfer_into_result, none,
|
||||
NOTE(regionbasedisolation_named_nosend_send_into_result, none,
|
||||
"%0%1 cannot be a 'sending' result. %2 uses may race with caller uses",
|
||||
(StringRef, Identifier, StringRef))
|
||||
NOTE(regionbasedisolation_typed_tns_passed_to_sending, none,
|
||||
@@ -1054,19 +1054,19 @@ NOTE(regionbasedisolation_typed_tns_passed_to_sending_callee, none,
|
||||
"Passing %0 value of non-Sendable type %1 as a 'sending' parameter to %2 %3 risks causing races inbetween %0 uses and uses reachable from %3",
|
||||
(StringRef, Type, DescriptiveDeclKind, DeclName))
|
||||
|
||||
NOTE(regionbasedisolation_named_transfer_nt_asynclet_capture, none,
|
||||
NOTE(regionbasedisolation_named_send_nt_asynclet_capture, none,
|
||||
"sending %1 %0 into async let risks causing data races between nonisolated and %1 uses",
|
||||
(Identifier, StringRef))
|
||||
NOTE(regionbasedisolation_typed_transferneversendable_via_arg, none,
|
||||
NOTE(regionbasedisolation_typed_sendneversendable_via_arg, none,
|
||||
"sending %0 value of non-Sendable type %1 to %2 callee risks causing races in between %0 and %2 uses",
|
||||
(StringRef, Type, ActorIsolation))
|
||||
NOTE(regionbasedisolation_typed_transferneversendable_via_arg_callee, none,
|
||||
NOTE(regionbasedisolation_typed_sendneversendable_via_arg_callee, none,
|
||||
"sending %0 value of non-Sendable type %1 to %2 %3 %4 risks causing races in between %0 and %2 uses",
|
||||
(StringRef, Type, ActorIsolation, DescriptiveDeclKind, DeclName))
|
||||
|
||||
// Error that is only used when the send non sendable emitter cannot discover any
|
||||
// information to give a better diagnostic.
|
||||
ERROR(regionbasedisolation_task_or_actor_isolated_transferred, none,
|
||||
ERROR(regionbasedisolation_task_or_actor_isolated_sent, none,
|
||||
"task or actor isolated value cannot be sent", ())
|
||||
|
||||
//===
|
||||
|
||||
@@ -36,7 +36,7 @@ static inline bool shouldAbortOnUnknownPatternMatchError() {
|
||||
return AbortOnUnknownPatternMatchError;
|
||||
}
|
||||
|
||||
using TransferringOperandSetFactory = Partition::TransferringOperandSetFactory;
|
||||
using SendingOperandSetFactory = Partition::SendingOperandSetFactory;
|
||||
using Element = PartitionPrimitives::Element;
|
||||
using Region = PartitionPrimitives::Region;
|
||||
|
||||
@@ -74,15 +74,15 @@ class BlockPartitionState {
|
||||
/// block.
|
||||
std::vector<PartitionOp> blockPartitionOps = {};
|
||||
|
||||
TransferringOperandSetFactory &ptrSetFactory;
|
||||
SendingOperandSetFactory &ptrSetFactory;
|
||||
|
||||
TransferringOperandToStateMap &transferringOpToStateMap;
|
||||
SendingOperandToStateMap &sendingOpToStateMap;
|
||||
|
||||
BlockPartitionState(SILBasicBlock *basicBlock,
|
||||
PartitionOpTranslator &translator,
|
||||
TransferringOperandSetFactory &ptrSetFactory,
|
||||
SendingOperandSetFactory &ptrSetFactory,
|
||||
IsolationHistory::Factory &isolationHistoryFactory,
|
||||
TransferringOperandToStateMap &transferringOpToStateMap);
|
||||
SendingOperandToStateMap &sendingOpToStateMap);
|
||||
|
||||
public:
|
||||
bool getLiveness() const { return isLive; }
|
||||
@@ -359,8 +359,7 @@ private:
|
||||
class RegionAnalysisFunctionInfo {
|
||||
using BlockPartitionState = regionanalysisimpl::BlockPartitionState;
|
||||
using PartitionOpTranslator = regionanalysisimpl::PartitionOpTranslator;
|
||||
using TransferringOperandSetFactory =
|
||||
regionanalysisimpl::TransferringOperandSetFactory;
|
||||
using SendingOperandSetFactory = regionanalysisimpl::SendingOperandSetFactory;
|
||||
using BasicBlockData = BasicBlockData<BlockPartitionState>;
|
||||
|
||||
llvm::BumpPtrAllocator allocator;
|
||||
@@ -373,11 +372,11 @@ class RegionAnalysisFunctionInfo {
|
||||
// allocator when we allocate everything.
|
||||
PartitionOpTranslator *translator;
|
||||
|
||||
TransferringOperandSetFactory ptrSetFactory;
|
||||
SendingOperandSetFactory ptrSetFactory;
|
||||
|
||||
IsolationHistory::Factory isolationHistoryFactory;
|
||||
|
||||
TransferringOperandToStateMap transferringOpToStateMap;
|
||||
SendingOperandToStateMap sendingOperandToStateMap;
|
||||
|
||||
// We make this optional to prevent an issue that we have seen on windows when
|
||||
// capturing a field in a closure that is used to initialize a different
|
||||
@@ -464,7 +463,7 @@ public:
|
||||
reverse_range getReverseRange() { return {rbegin(), rend()}; }
|
||||
const_reverse_range getReverseRange() const { return {rbegin(), rend()}; }
|
||||
|
||||
TransferringOperandSetFactory &getOperandSetFactory() {
|
||||
SendingOperandSetFactory &getOperandSetFactory() {
|
||||
assert(supportedFunction && "Unsupported Function?!");
|
||||
return ptrSetFactory;
|
||||
}
|
||||
@@ -479,9 +478,9 @@ public:
|
||||
return isolationHistoryFactory;
|
||||
}
|
||||
|
||||
TransferringOperandToStateMap &getTransferringOpToStateMap() {
|
||||
SendingOperandToStateMap &getSendingOperandToStateMap() {
|
||||
assert(supportedFunction && "Unsupported Function?!");
|
||||
return transferringOpToStateMap;
|
||||
return sendingOperandToStateMap;
|
||||
}
|
||||
|
||||
bool isClosureCaptured(SILValue value, Operand *op);
|
||||
|
||||
@@ -387,7 +387,7 @@ PASS(TempRValueOpt, "temp-rvalue-opt",
|
||||
"Remove short-lived immutable temporary copies")
|
||||
PASS(IRGenPrepare, "irgen-prepare",
|
||||
"Cleanup SIL in preparation for IRGen")
|
||||
PASS(TransferNonSendable, "transfer-non-sendable",
|
||||
PASS(SendNonSendable, "send-non-sendable",
|
||||
"Checks calls that send non-sendable values between isolation domains")
|
||||
PASS(LowerTupleAddrConstructor, "lower-tuple-addr-constructor",
|
||||
"Lower tuple addr constructor to tuple_element_addr+copy_addr")
|
||||
|
||||
@@ -29,9 +29,8 @@
|
||||
///
|
||||
/// 2. The element in the PartitionOp that was asked to be alive.
|
||||
///
|
||||
/// 3. The operand of the instruction that originally transferred the
|
||||
/// region. Can be used to get the immediate value transferred or the
|
||||
/// transferring instruction.
|
||||
/// 3. The operand of the instruction that originally sent the region. Can be
|
||||
/// used to get the immediate value sent or the sending instruction.
|
||||
PARTITION_OP_ERROR(LocalUseAfterSend)
|
||||
|
||||
/// This is called if we detect a never sendable element that was actually sent.
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
#include <algorithm>
|
||||
#include <variant>
|
||||
|
||||
#define DEBUG_TYPE "transfer-non-sendable"
|
||||
#define DEBUG_TYPE "send-non-sendable"
|
||||
|
||||
namespace swift {
|
||||
|
||||
@@ -87,7 +87,7 @@ struct DenseMapInfo<swift::PartitionPrimitives::Region> {
|
||||
namespace swift {
|
||||
|
||||
class Partition;
|
||||
class TransferringOperandToStateMap;
|
||||
class SendingOperandToStateMap;
|
||||
|
||||
/// The representative value of the equivalence class that makes up a tracked
|
||||
/// value.
|
||||
@@ -145,12 +145,12 @@ private:
|
||||
/// A persistent data structure that is used to "rewind" partition history so
|
||||
/// that we can discover when values become part of the same region.
|
||||
///
|
||||
/// NOTE: This does not track whether or not values are transferred. This is
|
||||
/// NOTE: This does not track whether or not values are sent. This is
|
||||
/// because from the perspective of determining when two values become part of
|
||||
/// the same region, that information is not important. To unroll history, a
|
||||
/// Partition must have no transfers to use this. NOTE: There is a method that
|
||||
/// Partition must have no sends to use this. NOTE: There is a method that
|
||||
/// takes a Partition and produces a new Partition that does not have any
|
||||
/// transfers.
|
||||
/// sends.
|
||||
class IsolationHistory {
|
||||
public:
|
||||
class Factory;
|
||||
@@ -162,7 +162,7 @@ private:
|
||||
|
||||
// TODO: This shouldn't need to be a friend.
|
||||
friend class Partition;
|
||||
friend TransferringOperandToStateMap;
|
||||
friend SendingOperandToStateMap;
|
||||
|
||||
/// First node in the immutable linked list.
|
||||
Node *head = nullptr;
|
||||
@@ -380,8 +380,8 @@ public:
|
||||
IsolationHistory get() { return IsolationHistory(this); }
|
||||
};
|
||||
|
||||
struct TransferringOperandState {
|
||||
/// The dynamic isolation info of the region of value when we transferred.
|
||||
struct SendingOperandState {
|
||||
/// The dynamic isolation info of the region of value when we sent.
|
||||
///
|
||||
/// This will contain the isolated value if we found one.
|
||||
SILDynamicMergedIsolationInfo isolationInfo;
|
||||
@@ -389,28 +389,27 @@ struct TransferringOperandState {
|
||||
/// The dynamic isolation history at this point.
|
||||
IsolationHistory isolationHistory;
|
||||
|
||||
/// Set to true if the element associated with the operand's vlaue is closure
|
||||
/// Set to true if the element associated with the operand's value is closure
|
||||
/// captured by the user. In such a case, if our element is a sendable var of
|
||||
/// a non-Sendable type, we cannot access it since we could race against an
|
||||
/// assignment to the var in a closure.
|
||||
bool isClosureCaptured;
|
||||
|
||||
TransferringOperandState(IsolationHistory history)
|
||||
SendingOperandState(IsolationHistory history)
|
||||
: isolationInfo(), isolationHistory(history), isClosureCaptured(false) {}
|
||||
};
|
||||
|
||||
class TransferringOperandToStateMap {
|
||||
llvm::SmallDenseMap<Operand *, TransferringOperandState> internalMap;
|
||||
class SendingOperandToStateMap {
|
||||
llvm::SmallDenseMap<Operand *, SendingOperandState> internalMap;
|
||||
IsolationHistory::Factory &isolationHistoryFactory;
|
||||
|
||||
public:
|
||||
TransferringOperandToStateMap(
|
||||
IsolationHistory::Factory &isolationHistoryFactory)
|
||||
SendingOperandToStateMap(IsolationHistory::Factory &isolationHistoryFactory)
|
||||
: isolationHistoryFactory(isolationHistoryFactory) {}
|
||||
TransferringOperandState &get(Operand *op) const {
|
||||
auto *self = const_cast<TransferringOperandToStateMap *>(this);
|
||||
SendingOperandState &get(Operand *op) const {
|
||||
auto *self = const_cast<SendingOperandToStateMap *>(this);
|
||||
auto history = IsolationHistory(&isolationHistoryFactory);
|
||||
return self->internalMap.try_emplace(op, TransferringOperandState(history))
|
||||
return self->internalMap.try_emplace(op, SendingOperandState(history))
|
||||
.first->getSecond();
|
||||
}
|
||||
};
|
||||
@@ -423,24 +422,24 @@ namespace swift {
|
||||
/// SILInstructions can be translated to
|
||||
enum class PartitionOpKind : uint8_t {
|
||||
/// Assign one value to the region of another, takes two args, second arg
|
||||
/// must already be tracked with a non-transferred region
|
||||
/// must already be tracked with a non-sent region
|
||||
Assign,
|
||||
|
||||
/// Assign one value to a fresh region, takes one arg.
|
||||
AssignFresh,
|
||||
|
||||
/// Merge the regions of two values, takes two args, both must be from
|
||||
/// non-transferred regions.
|
||||
/// non-sent regions.
|
||||
Merge,
|
||||
|
||||
/// Transfer the region of a value if not already transferred, takes one arg.
|
||||
Transfer,
|
||||
/// Send the region of a value if not already sent, takes one arg.
|
||||
Send,
|
||||
|
||||
/// Due to an async let or something like that a value that was transferred is
|
||||
/// no longer transferred.
|
||||
UndoTransfer,
|
||||
/// Due to an async let or something like that a value that was sent is
|
||||
/// no longer sent.
|
||||
UndoSend,
|
||||
|
||||
/// Require the region of a value to be non-transferred, takes one arg.
|
||||
/// Require the region of a value to be non-sent, takes one arg.
|
||||
Require,
|
||||
|
||||
/// Emit an error saying that the given instruction was not understood for
|
||||
@@ -453,10 +452,10 @@ enum class PartitionOpKind : uint8_t {
|
||||
/// This is used if we need to reject the program and do not want to assert.
|
||||
UnknownPatternError,
|
||||
|
||||
/// Require that a 'inout sending' parameter's region is not transferred and
|
||||
/// Require that a 'inout sending' parameter's region is not sent and
|
||||
/// disconnected at a specific function exiting term inst.
|
||||
///
|
||||
/// This ensures that if users transfer away an inout sending parameter, the
|
||||
/// This ensures that if users send away an inout sending parameter, the
|
||||
/// parameter is reinitialized with a disconnected value.
|
||||
///
|
||||
/// Takes one parameter, the inout parameter that we need to check.
|
||||
@@ -464,7 +463,7 @@ enum class PartitionOpKind : uint8_t {
|
||||
};
|
||||
|
||||
/// PartitionOp represents a primitive operation that can be performed on
|
||||
/// Partitions. This is part of the TransferNonSendable SIL pass workflow:
|
||||
/// Partitions. This is part of the SendNonSendable SIL pass workflow:
|
||||
/// first SILBasicBlocks are compiled to vectors of PartitionOps, then a fixed
|
||||
/// point partition is found over the CFG.
|
||||
class PartitionOp {
|
||||
@@ -482,20 +481,20 @@ private:
|
||||
PartitionOp(PartitionOpKind opKind, Element arg1,
|
||||
SILInstruction *sourceInst = nullptr)
|
||||
: opKind(opKind), opArgs({arg1}), source(sourceInst) {
|
||||
assert(((opKind != PartitionOpKind::Transfer &&
|
||||
opKind != PartitionOpKind::UndoTransfer) ||
|
||||
assert(((opKind != PartitionOpKind::Send &&
|
||||
opKind != PartitionOpKind::UndoSend) ||
|
||||
sourceInst) &&
|
||||
"Transfer needs a sourceInst");
|
||||
"Send needs a sourceInst");
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
PartitionOp(PartitionOpKind opKind, T collectionOfIndices,
|
||||
SILInstruction *sourceInst = nullptr)
|
||||
: opKind(opKind), opArgs(), source(sourceInst) {
|
||||
assert(((opKind != PartitionOpKind::Transfer &&
|
||||
opKind != PartitionOpKind::UndoTransfer) ||
|
||||
assert(((opKind != PartitionOpKind::Send &&
|
||||
opKind != PartitionOpKind::UndoSend) ||
|
||||
sourceInst) &&
|
||||
"Transfer needs a sourceInst");
|
||||
"Send needs a sourceInst");
|
||||
for (Element elt : collectionOfIndices) {
|
||||
opArgs.push_back(elt);
|
||||
}
|
||||
@@ -503,19 +502,19 @@ private:
|
||||
|
||||
PartitionOp(PartitionOpKind opKind, Element arg1, Operand *sourceOperand)
|
||||
: opKind(opKind), opArgs({arg1}), source(sourceOperand) {
|
||||
assert(((opKind != PartitionOpKind::Transfer &&
|
||||
opKind != PartitionOpKind::UndoTransfer) ||
|
||||
assert(((opKind != PartitionOpKind::Send &&
|
||||
opKind != PartitionOpKind::UndoSend) ||
|
||||
bool(sourceOperand)) &&
|
||||
"Transfer needs a sourceInst");
|
||||
"Send needs a sourceInst");
|
||||
}
|
||||
|
||||
PartitionOp(PartitionOpKind opKind, Element arg1, Element arg2,
|
||||
SILInstruction *sourceInst = nullptr)
|
||||
: opKind(opKind), opArgs({arg1, arg2}), source(sourceInst) {
|
||||
assert(((opKind != PartitionOpKind::Transfer &&
|
||||
opKind != PartitionOpKind::UndoTransfer) ||
|
||||
assert(((opKind != PartitionOpKind::Send &&
|
||||
opKind != PartitionOpKind::UndoSend) ||
|
||||
sourceInst) &&
|
||||
"Transfer needs a sourceInst");
|
||||
"Send needs a sourceInst");
|
||||
}
|
||||
|
||||
PartitionOp(PartitionOpKind opKind, Element arg1, Element arg2,
|
||||
@@ -543,13 +542,12 @@ public:
|
||||
return PartitionOp(PartitionOpKind::AssignFresh, collection, sourceInst);
|
||||
}
|
||||
|
||||
static PartitionOp Transfer(Element tgt, Operand *transferringOp) {
|
||||
return PartitionOp(PartitionOpKind::Transfer, tgt, transferringOp);
|
||||
static PartitionOp Send(Element tgt, Operand *sendingOp) {
|
||||
return PartitionOp(PartitionOpKind::Send, tgt, sendingOp);
|
||||
}
|
||||
|
||||
static PartitionOp UndoTransfer(Element tgt,
|
||||
SILInstruction *untransferringInst) {
|
||||
return PartitionOp(PartitionOpKind::UndoTransfer, tgt, untransferringInst);
|
||||
static PartitionOp UndoSend(Element tgt, SILInstruction *unsendingInst) {
|
||||
return PartitionOp(PartitionOpKind::UndoSend, tgt, unsendingInst);
|
||||
}
|
||||
|
||||
static PartitionOp Merge(Element destElement, Element srcElement,
|
||||
@@ -617,12 +615,12 @@ public:
|
||||
|
||||
using Element = PartitionPrimitives::Element;
|
||||
using Region = PartitionPrimitives::Region;
|
||||
using TransferringOperandSet = ImmutablePointerSet<Operand *>;
|
||||
using TransferringOperandSetFactory = ImmutablePointerSetFactory<Operand *>;
|
||||
using SendingOperandSet = ImmutablePointerSet<Operand *>;
|
||||
using SendingOperandSetFactory = ImmutablePointerSetFactory<Operand *>;
|
||||
using IsolationHistoryNode = IsolationHistory::Node;
|
||||
|
||||
private:
|
||||
/// A map from a region number to a instruction that consumes it.
|
||||
/// A map from a region number to a instruction that sends it.
|
||||
///
|
||||
/// All we care is that we ever track a single SILInstruction for a region
|
||||
/// since we are fine with emitting a single error per value and letting the
|
||||
@@ -630,8 +628,7 @@ private:
|
||||
/// multi map here. The implication of this is that when we are performing
|
||||
/// dataflow we use a union operation to combine CFG elements and just take
|
||||
/// the first instruction that we see.
|
||||
llvm::SmallMapVector<Region, TransferringOperandSet *, 2>
|
||||
regionToTransferredOpMap;
|
||||
llvm::SmallMapVector<Region, SendingOperandSet *, 2> regionToSendingOpMap;
|
||||
|
||||
/// Label each index with a non-negative (unsigned) label if it is associated
|
||||
/// with a valid region.
|
||||
@@ -678,13 +675,12 @@ public:
|
||||
snd.canonicalize();
|
||||
|
||||
return fst.elementToRegionMap == snd.elementToRegionMap &&
|
||||
fst.regionToTransferredOpMap.size() ==
|
||||
snd.regionToTransferredOpMap.size() &&
|
||||
fst.regionToSendingOpMap.size() == snd.regionToSendingOpMap.size() &&
|
||||
llvm::all_of(
|
||||
fst.regionToTransferredOpMap,
|
||||
[&snd](const std::pair<Region, TransferringOperandSet *> &p) {
|
||||
auto sndIter = snd.regionToTransferredOpMap.find(p.first);
|
||||
return sndIter != snd.regionToTransferredOpMap.end() &&
|
||||
fst.regionToSendingOpMap,
|
||||
[&snd](const std::pair<Region, SendingOperandSet *> &p) {
|
||||
auto sndIter = snd.regionToSendingOpMap.find(p.first);
|
||||
return sndIter != snd.regionToSendingOpMap.end() &&
|
||||
sndIter->second == p.second;
|
||||
});
|
||||
}
|
||||
@@ -693,13 +689,12 @@ public:
|
||||
return elementToRegionMap.count(val);
|
||||
}
|
||||
|
||||
/// Mark val as transferred.
|
||||
void markTransferred(Element val,
|
||||
TransferringOperandSet *transferredOperandSet);
|
||||
/// Mark val as having been sent.
|
||||
void markSent(Element val, SendingOperandSet *sendingOperandSet);
|
||||
|
||||
/// If val was marked as transferred, unmark it as transfer. Returns true if
|
||||
/// we found that \p val was transferred. We return false otherwise.
|
||||
bool undoTransfer(Element val);
|
||||
/// If val was marked as sent, unmark it as sent. Returns true if we found
|
||||
/// that \p val was sent. We return false otherwise.
|
||||
bool undoSend(Element val);
|
||||
|
||||
/// If \p newElt is not being tracked, create a new region for \p newElt. If
|
||||
/// \p newElt is already being tracked, remove it from its old region as well.
|
||||
@@ -722,11 +717,11 @@ public:
|
||||
iterator end() { return elementToRegionMap.end(); }
|
||||
llvm::iterator_range<iterator> range() { return {begin(), end()}; }
|
||||
|
||||
void clearTransferState() { regionToTransferredOpMap.clear(); }
|
||||
void clearSendingOperandState() { regionToSendingOpMap.clear(); }
|
||||
|
||||
Partition removingTransferState() const {
|
||||
Partition removingSendingOperandState() const {
|
||||
Partition p = *this;
|
||||
p.clearTransferState();
|
||||
p.clearSendingOperandState();
|
||||
return p;
|
||||
}
|
||||
|
||||
@@ -737,12 +732,12 @@ public:
|
||||
/// afterwards if the current linear history does not find what one is looking
|
||||
/// for.
|
||||
///
|
||||
/// NOTE: This can only be used if one has cleared transfer state using
|
||||
/// Partition::clearTransferState or constructed a new Partiton using
|
||||
/// Partition::withoutTransferState(). This is because history rewinding
|
||||
/// doesn't use transfer information so just to be careful around potential
|
||||
/// invariants being broken, we just require the elimination of the transfer
|
||||
/// information.
|
||||
/// NOTE: This can only be used if one has cleared the sent state using
|
||||
/// Partition::clearSendingOperandState or constructed a new Partiton using
|
||||
/// Partition::removingSendingOperandState(). This is because history
|
||||
/// rewinding doesn't use send information so just to be careful around
|
||||
/// potential invariants being broken, we just require the elimination of the
|
||||
/// send information.
|
||||
///
|
||||
/// \returns true if there is more history that can be popped.
|
||||
bool popHistory(SmallVectorImpl<IsolationHistory> &foundJoinedHistories);
|
||||
@@ -779,19 +774,19 @@ public:
|
||||
/// Runs in quadratic time.
|
||||
static Partition join(const Partition &fst, Partition &snd);
|
||||
|
||||
/// Return a vector of the transferred values in this partition.
|
||||
std::vector<Element> getTransferredVals() const {
|
||||
/// Return a vector of the sent values in this partition.
|
||||
std::vector<Element> getSentValues() const {
|
||||
// For effeciency, this could return an iterator not a vector.
|
||||
std::vector<Element> transferredVals;
|
||||
std::vector<Element> sentVals;
|
||||
for (auto [i, _] : elementToRegionMap)
|
||||
if (isTransferred(i))
|
||||
transferredVals.push_back(i);
|
||||
return transferredVals;
|
||||
if (isSent(i))
|
||||
sentVals.push_back(i);
|
||||
return sentVals;
|
||||
}
|
||||
|
||||
/// Return a vector of the non-transferred regions in this partition, each
|
||||
/// Return a vector of the non-sent regions in this partition, each
|
||||
/// represented as a vector of values.
|
||||
std::vector<std::vector<Element>> getNonTransferredRegions() const {
|
||||
std::vector<std::vector<Element>> getNonSentRegions() const {
|
||||
// For effeciency, this could return an iterator not a vector.
|
||||
std::map<Region, std::vector<Element>> buckets;
|
||||
|
||||
@@ -832,38 +827,38 @@ public:
|
||||
return history.pushHistorySequenceBoundary(loc);
|
||||
}
|
||||
|
||||
bool isTransferred(Element val) const {
|
||||
bool isSent(Element val) const {
|
||||
auto iter = elementToRegionMap.find(val);
|
||||
if (iter == elementToRegionMap.end())
|
||||
return false;
|
||||
return regionToTransferredOpMap.count(iter->second);
|
||||
return regionToSendingOpMap.count(iter->second);
|
||||
}
|
||||
|
||||
/// Return the instruction that transferred \p val's region or nullptr
|
||||
/// Return the instruction that sent \p val's region or nullptr
|
||||
/// otherwise.
|
||||
TransferringOperandSet *getTransferred(Element val) const {
|
||||
SendingOperandSet *getSentOperandSet(Element val) const {
|
||||
auto iter = elementToRegionMap.find(val);
|
||||
if (iter == elementToRegionMap.end())
|
||||
return nullptr;
|
||||
auto iter2 = regionToTransferredOpMap.find(iter->second);
|
||||
if (iter2 == regionToTransferredOpMap.end())
|
||||
auto iter2 = regionToSendingOpMap.find(iter->second);
|
||||
if (iter2 == regionToSendingOpMap.end())
|
||||
return nullptr;
|
||||
auto *set = iter2->second;
|
||||
assert(!set->empty());
|
||||
return set;
|
||||
}
|
||||
|
||||
/// Validate that all regions in the regionToTransferredOpMap exist in the
|
||||
/// Validate that all regions in the regionToSentOpMap exist in the
|
||||
/// elementToRegionMap.
|
||||
///
|
||||
/// Asserts when NDEBUG is set. Does nothing otherwise.
|
||||
void validateRegionToTransferredOpMapRegions() const {
|
||||
void validateRegionToSendingOpMapRegions() const {
|
||||
#ifndef NDEBUG
|
||||
llvm::SmallSet<Region, 8> regions;
|
||||
for (auto [eltNo, regionNo] : elementToRegionMap) {
|
||||
regions.insert(regionNo);
|
||||
}
|
||||
for (auto [regionNo, opSet] : regionToTransferredOpMap) {
|
||||
for (auto [regionNo, opSet] : regionToSendingOpMap) {
|
||||
assert(regions.contains(regionNo) && "Region doesn't exist?!");
|
||||
}
|
||||
#endif
|
||||
@@ -930,7 +925,7 @@ private:
|
||||
history.pushMergeElementRegions(elementToMergeInto, otherRegions);
|
||||
}
|
||||
|
||||
/// Remove a single element without touching the region to transferring inst
|
||||
/// Remove a single element without touching the region to sending inst
|
||||
/// multimap. Assumes that the element is never the last element in a region.
|
||||
///
|
||||
/// Just a helper routine.
|
||||
@@ -1095,23 +1090,21 @@ private:
|
||||
public:
|
||||
using Element = PartitionPrimitives::Element;
|
||||
using Region = PartitionPrimitives::Region;
|
||||
using TransferringOperandSetFactory =
|
||||
Partition::TransferringOperandSetFactory;
|
||||
using SendingOperandSetFactory = Partition::SendingOperandSetFactory;
|
||||
|
||||
#define PARTITION_OP_ERROR(NAME) \
|
||||
using NAME##Error = PartitionOpError::NAME##Error;
|
||||
#include "PartitionOpError.def"
|
||||
|
||||
protected:
|
||||
TransferringOperandSetFactory &ptrSetFactory;
|
||||
TransferringOperandToStateMap &operandToStateMap;
|
||||
SendingOperandSetFactory &ptrSetFactory;
|
||||
SendingOperandToStateMap &operandToStateMap;
|
||||
|
||||
Partition &p;
|
||||
|
||||
public:
|
||||
PartitionOpEvaluator(Partition &p,
|
||||
TransferringOperandSetFactory &ptrSetFactory,
|
||||
TransferringOperandToStateMap &operandToStateMap)
|
||||
PartitionOpEvaluator(Partition &p, SendingOperandSetFactory &ptrSetFactory,
|
||||
SendingOperandToStateMap &operandToStateMap)
|
||||
: ptrSetFactory(ptrSetFactory), operandToStateMap(operandToStateMap),
|
||||
p(p) {}
|
||||
|
||||
@@ -1275,65 +1268,63 @@ public:
|
||||
}
|
||||
return;
|
||||
}
|
||||
case PartitionOpKind::Transfer: {
|
||||
// NOTE: We purposely do not check here if a transferred value is already
|
||||
// transferred. Callers are expected to put a require for that
|
||||
// purpose. This ensures that if we pass the same argument multiple times
|
||||
// to the same transferring function as weakly transferred arguments, we
|
||||
// do not get an error.
|
||||
case PartitionOpKind::Send: {
|
||||
// NOTE: We purposely do not check here if the value has already been
|
||||
// sent. Callers are expected to put a require for that purpose. This
|
||||
// ensures that if we pass the same argument multiple times to the same
|
||||
// sending function as weakly sent arguments, we do not get an
|
||||
// error.
|
||||
assert(op.getOpArgs().size() == 1 &&
|
||||
"Transfer PartitionOp should be passed 1 argument");
|
||||
"Send PartitionOp should be passed 1 argument");
|
||||
assert(p.isTrackingElement(op.getOpArgs()[0]) &&
|
||||
"Transfer PartitionOp's argument should already be tracked");
|
||||
"Send PartitionOp's argument should already be tracked");
|
||||
|
||||
// Before we do any further work, see if we have a nonisolated(unsafe)
|
||||
// element. In such a case, this is also not a real transfer point.
|
||||
Element transferredElement = op.getOpArgs()[0];
|
||||
if (getIsolationRegionInfo(transferredElement).isUnsafeNonIsolated()) {
|
||||
// element. In such a case, this is also not a real send point.
|
||||
Element sentElement = op.getOpArgs()[0];
|
||||
if (getIsolationRegionInfo(sentElement).isUnsafeNonIsolated()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, we need to merge our isolation region info with the
|
||||
// isolation region info of everything else in our region. This is the
|
||||
// dynamic isolation region info found by the dataflow.
|
||||
Region transferredRegion = p.getRegion(transferredElement);
|
||||
Region sentRegion = p.getRegion(sentElement);
|
||||
bool isClosureCapturedElt = false;
|
||||
SILDynamicMergedIsolationInfo transferredRegionIsolation;
|
||||
auto pairOpt =
|
||||
getIsolationRegionInfo(transferredRegion, op.getSourceOp());
|
||||
SILDynamicMergedIsolationInfo sentRegionIsolation;
|
||||
auto pairOpt = getIsolationRegionInfo(sentRegion, op.getSourceOp());
|
||||
if (!pairOpt) {
|
||||
return handleError(UnknownCodePatternError(op));
|
||||
}
|
||||
std::tie(transferredRegionIsolation, isClosureCapturedElt) = *pairOpt;
|
||||
std::tie(sentRegionIsolation, isClosureCapturedElt) = *pairOpt;
|
||||
|
||||
// If we merged anything, we need to handle a transfer non-transferrable
|
||||
// unless our value has the same isolation info as our callee.
|
||||
// If we merged anything, we need to handle an attempt to send a
|
||||
// never-sent value unless our value has the same isolation info as our
|
||||
// callee.
|
||||
auto calleeIsolationInfo = getIsolationInfo(op);
|
||||
if (!(calleeIsolationInfo &&
|
||||
transferredRegionIsolation.hasSameIsolation(calleeIsolationInfo)) &&
|
||||
!transferredRegionIsolation.isDisconnected()) {
|
||||
return handleTransferNonTransferrableHelper(op, op.getOpArgs()[0],
|
||||
transferredRegionIsolation);
|
||||
sentRegionIsolation.hasSameIsolation(calleeIsolationInfo)) &&
|
||||
!sentRegionIsolation.isDisconnected()) {
|
||||
return handleSendNeverSentHelper(op, op.getOpArgs()[0],
|
||||
sentRegionIsolation);
|
||||
}
|
||||
|
||||
// Next see if we are disconnected and have the same isolation. In such a
|
||||
// case, if we are not marked explicitly as sending, we do not transfer
|
||||
// case, if we are not marked explicitly as sending, we do not send
|
||||
// since the disconnected value is allowed to be resued after we
|
||||
// return. If we are passed as a sending parameter, we cannot do this.
|
||||
if (auto *sourceInst = Impl::getSourceInst(op)) {
|
||||
if (auto fas = FullApplySite::isa(sourceInst);
|
||||
(!fas || !fas.isSending(*op.getSourceOp())) &&
|
||||
transferredRegionIsolation.isDisconnected() &&
|
||||
calleeIsolationInfo &&
|
||||
transferredRegionIsolation.hasSameIsolation(calleeIsolationInfo))
|
||||
sentRegionIsolation.isDisconnected() && calleeIsolationInfo &&
|
||||
sentRegionIsolation.hasSameIsolation(calleeIsolationInfo))
|
||||
return;
|
||||
}
|
||||
|
||||
// Mark op.getOpArgs()[0] as transferred.
|
||||
TransferringOperandState &state = operandToStateMap.get(op.getSourceOp());
|
||||
// Mark op.getOpArgs()[0] as sent.
|
||||
SendingOperandState &state = operandToStateMap.get(op.getSourceOp());
|
||||
state.isClosureCaptured |= isClosureCapturedElt;
|
||||
if (auto newInfo =
|
||||
state.isolationInfo.merge(transferredRegionIsolation)) {
|
||||
if (auto newInfo = state.isolationInfo.merge(sentRegionIsolation)) {
|
||||
state.isolationInfo = *newInfo;
|
||||
} else {
|
||||
handleError(UnknownCodePatternError(op));
|
||||
@@ -1341,17 +1332,17 @@ public:
|
||||
assert(state.isolationInfo && "Cannot have unknown");
|
||||
state.isolationHistory.pushCFGHistoryJoin(p.getIsolationHistory());
|
||||
auto *ptrSet = ptrSetFactory.get(op.getSourceOp());
|
||||
p.markTransferred(op.getOpArgs()[0], ptrSet);
|
||||
p.markSent(op.getOpArgs()[0], ptrSet);
|
||||
return;
|
||||
}
|
||||
case PartitionOpKind::UndoTransfer: {
|
||||
case PartitionOpKind::UndoSend: {
|
||||
assert(op.getOpArgs().size() == 1 &&
|
||||
"UndoTransfer PartitionOp should be passed 1 argument");
|
||||
"UndoSend PartitionOp should be passed 1 argument");
|
||||
assert(p.isTrackingElement(op.getOpArgs()[0]) &&
|
||||
"UndoTransfer PartitionOp's argument should already be tracked");
|
||||
"UndoSend PartitionOp's argument should already be tracked");
|
||||
|
||||
// Mark op.getOpArgs()[0] as not transferred.
|
||||
p.undoTransfer(op.getOpArgs()[0]);
|
||||
// Mark op.getOpArgs()[0] as not sent.
|
||||
p.undoSend(op.getOpArgs()[0]);
|
||||
return;
|
||||
}
|
||||
case PartitionOpKind::Merge: {
|
||||
@@ -1392,10 +1383,9 @@ public:
|
||||
"Require PartitionOp should be passed 1 argument");
|
||||
assert(p.isTrackingElement(op.getOpArgs()[0]) &&
|
||||
"Require PartitionOp's argument should already be tracked");
|
||||
if (auto *transferredOperandSet = p.getTransferred(op.getOpArgs()[0])) {
|
||||
for (auto transferredOperand : transferredOperandSet->data()) {
|
||||
handleLocalUseAfterTransferHelper(op, op.getOpArgs()[0],
|
||||
transferredOperand);
|
||||
if (auto *sentOperandSet = p.getSentOperandSet(op.getOpArgs()[0])) {
|
||||
for (auto sentOperand : sentOperandSet->data()) {
|
||||
handleLocalUseAfterSendHelper(op, op.getOpArgs()[0], sentOperand);
|
||||
}
|
||||
}
|
||||
return;
|
||||
@@ -1406,17 +1396,17 @@ public:
|
||||
"Require PartitionOp's argument should already be tracked");
|
||||
|
||||
// First check if the region of our 'inout sending' element has been
|
||||
// transferred. In that case, we emit a special use after free error.
|
||||
if (auto *transferredOperandSet = p.getTransferred(op.getOpArgs()[0])) {
|
||||
for (auto transferredOperand : transferredOperandSet->data()) {
|
||||
// sent. In that case, we emit a special use after free error.
|
||||
if (auto *sentOperandSet = p.getSentOperandSet(op.getOpArgs()[0])) {
|
||||
for (auto sentOperand : sentOperandSet->data()) {
|
||||
handleError(InOutSendingNotInitializedAtExitError(
|
||||
op, op.getOpArgs()[0], transferredOperand));
|
||||
op, op.getOpArgs()[0], sentOperand));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// If we were not transferred, check if our region is actor isolated. If
|
||||
// so, error since we need a disconnected value in the inout parameter.
|
||||
// If we were not sent, check if our region is actor isolated. If so,
|
||||
// error since we need a disconnected value in the inout parameter.
|
||||
Region inoutSendingRegion = p.getRegion(op.getOpArgs()[0]);
|
||||
auto dynamicRegionIsolation = getIsolationRegionInfo(inoutSendingRegion);
|
||||
|
||||
@@ -1481,13 +1471,12 @@ private:
|
||||
equivalenceClassRep->getFunction());
|
||||
}
|
||||
|
||||
// Private helper that squelches the error if our transfer instruction and our
|
||||
// use have the same isolation.
|
||||
void handleLocalUseAfterTransferHelper(const PartitionOp &op, Element elt,
|
||||
Operand *transferringOp) {
|
||||
// Private helper that squelches the error if our send instruction and our use
|
||||
// have the same isolation.
|
||||
void handleLocalUseAfterSendHelper(const PartitionOp &op, Element elt,
|
||||
Operand *sentOp) {
|
||||
if (shouldTryToSquelchErrors()) {
|
||||
if (SILValue equivalenceClassRep =
|
||||
getRepresentative(transferringOp->get())) {
|
||||
if (SILValue equivalenceClassRep = getRepresentative(sentOp->get())) {
|
||||
|
||||
// If we have a temporary that is initialized with an unsafe nonisolated
|
||||
// value... squelch the error like if we were that value.
|
||||
@@ -1512,23 +1501,23 @@ private:
|
||||
|
||||
// If our instruction does not have any isolation info associated with it,
|
||||
// it must be nonisolated. See if our function has a matching isolation to
|
||||
// our transferring operand. If so, we can squelch this.
|
||||
// our sent operand. If so, we can squelch this.
|
||||
if (auto functionIsolation =
|
||||
transferringOp->getUser()->getFunction()->getActorIsolation()) {
|
||||
sentOp->getUser()->getFunction()->getActorIsolation()) {
|
||||
if (functionIsolation->isActorIsolated() &&
|
||||
SILIsolationInfo::get(transferringOp->getUser())
|
||||
SILIsolationInfo::get(sentOp->getUser())
|
||||
.hasSameIsolation(*functionIsolation))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Ok, we actually need to emit a call to the callback.
|
||||
return handleError(LocalUseAfterSendError(op, elt, transferringOp));
|
||||
return handleError(LocalUseAfterSendError(op, elt, sentOp));
|
||||
}
|
||||
|
||||
// Private helper that squelches the error if our transfer instruction and our
|
||||
// Private helper that squelches the error if our send instruction and our
|
||||
// use have the same isolation.
|
||||
void handleTransferNonTransferrableHelper(
|
||||
void handleSendNeverSentHelper(
|
||||
const PartitionOp &op, Element elt,
|
||||
SILDynamicMergedIsolationInfo dynamicMergedIsolationInfo) {
|
||||
if (shouldTryToSquelchErrors()) {
|
||||
@@ -1569,13 +1558,12 @@ template <typename Subclass>
|
||||
struct PartitionOpEvaluatorBaseImpl : PartitionOpEvaluator<Subclass> {
|
||||
using Element = PartitionPrimitives::Element;
|
||||
using Region = PartitionPrimitives::Region;
|
||||
using TransferringOperandSetFactory =
|
||||
Partition::TransferringOperandSetFactory;
|
||||
using SendingOperandSetFactory = Partition::SendingOperandSetFactory;
|
||||
using Super = PartitionOpEvaluator<Subclass>;
|
||||
|
||||
PartitionOpEvaluatorBaseImpl(Partition &workingPartition,
|
||||
TransferringOperandSetFactory &ptrSetFactory,
|
||||
TransferringOperandToStateMap &operandToStateMap)
|
||||
SendingOperandSetFactory &ptrSetFactory,
|
||||
SendingOperandToStateMap &operandToStateMap)
|
||||
: Super(workingPartition, ptrSetFactory, operandToStateMap) {}
|
||||
|
||||
/// Should we emit extra verbose logging statements when evaluating
|
||||
@@ -1583,8 +1571,8 @@ struct PartitionOpEvaluatorBaseImpl : PartitionOpEvaluator<Subclass> {
|
||||
bool shouldEmitVerboseLogging() const { return true; }
|
||||
|
||||
/// This is used to determine if an element is actor derived. If we determine
|
||||
/// that a region containing such an element is transferred, we emit an error
|
||||
/// since actor regions cannot be transferred.
|
||||
/// that a region containing such an element is sent, we emit an error since
|
||||
/// actor regions cannot be sent.
|
||||
bool isActorDerived(Element elt) const { return false; }
|
||||
|
||||
/// This is used to determine if an element is in the same region as a task
|
||||
@@ -1646,8 +1634,8 @@ struct PartitionOpEvaluatorBaseImpl : PartitionOpEvaluator<Subclass> {
|
||||
struct PartitionOpEvaluatorBasic final
|
||||
: PartitionOpEvaluatorBaseImpl<PartitionOpEvaluatorBasic> {
|
||||
PartitionOpEvaluatorBasic(Partition &workingPartition,
|
||||
TransferringOperandSetFactory &ptrSetFactory,
|
||||
TransferringOperandToStateMap &operandToStateMap)
|
||||
SendingOperandSetFactory &ptrSetFactory,
|
||||
SendingOperandToStateMap &operandToStateMap)
|
||||
: PartitionOpEvaluatorBaseImpl(workingPartition, ptrSetFactory,
|
||||
operandToStateMap) {}
|
||||
};
|
||||
|
||||
@@ -143,14 +143,14 @@ public:
|
||||
/// This forms a lattice of semantics. The lattice progresses from left ->
|
||||
/// right below:
|
||||
///
|
||||
/// Unknown -> Disconnected -> TransferringParameter -> Task -> Actor.
|
||||
/// Unknown -> Disconnected -> Task -> Actor.
|
||||
///
|
||||
enum Kind : uint8_t {
|
||||
/// Unknown means no information. We error when merging on it.
|
||||
Unknown,
|
||||
|
||||
/// An entity with disconnected isolation can be freely transferred into
|
||||
/// another isolation domain. These are associated with "use after transfer"
|
||||
/// An entity with disconnected isolation can be freely sent into another
|
||||
/// isolation domain. These are associated with "use after send"
|
||||
/// diagnostics.
|
||||
Disconnected,
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#define DEBUG_TYPE "transfer-non-sendable"
|
||||
#define DEBUG_TYPE "send-non-sendable"
|
||||
|
||||
#include "swift/SILOptimizer/Analysis/RegionAnalysis.h"
|
||||
|
||||
@@ -600,15 +600,15 @@ static bool isAsyncLetBeginPartialApply(PartialApplyInst *pai) {
|
||||
return *kind == BuiltinValueKind::StartAsyncLetWithLocalBuffer;
|
||||
}
|
||||
|
||||
/// Returns true if this is a function argument that is able to be transferred
|
||||
/// in the body of our function.
|
||||
static bool isTransferrableFunctionArgument(SILFunctionArgument *arg) {
|
||||
// Indirect out parameters cannot be an input transferring parameter.
|
||||
/// Returns true if this is a function argument that is able to be sent in the
|
||||
/// body of our function.
|
||||
static bool canFunctionArgumentBeSent(SILFunctionArgument *arg) {
|
||||
// Indirect out parameters can never be sent.
|
||||
if (arg->isIndirectResult() || arg->isIndirectErrorResult())
|
||||
return false;
|
||||
|
||||
// If we have a function argument that is closure captured by a Sendable
|
||||
// closure, allow for the argument to be transferred.
|
||||
// closure, allow for the argument to be sent.
|
||||
//
|
||||
// DISCUSSION: The reason that we do this is that in the case of us
|
||||
// having an actual Sendable closure there are two cases we can see:
|
||||
@@ -621,18 +621,18 @@ static bool isTransferrableFunctionArgument(SILFunctionArgument *arg) {
|
||||
// us not being in swift 6 mode lets not emit extra errors.
|
||||
//
|
||||
// 2. If we have an async-let based Sendable closure, we want to allow
|
||||
// for the argument to be transferred in the async let's statement and
|
||||
// for the argument to be sent in the async let's statement and
|
||||
// not emit an error.
|
||||
//
|
||||
// TODO: Once the async let refactoring change this will no longer be needed
|
||||
// since closure captures will have transferring parameters and be
|
||||
// since closure captures will have sending parameters and be
|
||||
// non-Sendable.
|
||||
if (arg->isClosureCapture() &&
|
||||
arg->getFunction()->getLoweredFunctionType()->isSendable())
|
||||
return true;
|
||||
|
||||
// Otherwise, we only allow for the argument to be transferred if it is
|
||||
// explicitly marked as a strong transferring parameter.
|
||||
// Otherwise, we only allow for the argument to be sent if it is explicitly
|
||||
// marked as a 'sending' parameter.
|
||||
return arg->isSending();
|
||||
}
|
||||
|
||||
@@ -653,7 +653,7 @@ bool TrackableValue::isSendingParameter() const {
|
||||
if (asi->getParent() != asi->getFunction()->getEntryBlock())
|
||||
return false;
|
||||
|
||||
// See if we are initialized from a transferring parameter and are the only
|
||||
// See if we are initialized from a 'sending' parameter and are the only
|
||||
// use of the parameter.
|
||||
OperandWorklist worklist(asi->getFunction());
|
||||
worklist.pushResultOperandsIfNotVisited(asi);
|
||||
@@ -668,9 +668,8 @@ bool TrackableValue::isSendingParameter() const {
|
||||
}
|
||||
|
||||
if (auto *si = dyn_cast<StoreInst>(user)) {
|
||||
// Check if our store inst is from a function argument that is
|
||||
// transferring and for which the store is the only use of the function
|
||||
// argument.
|
||||
// Check if our store inst is from a 'sending' function argument and for
|
||||
// which the store is the only use of the function argument.
|
||||
auto *fArg = dyn_cast<SILFunctionArgument>(si->getSrc());
|
||||
if (!fArg || !fArg->isSending())
|
||||
return false;
|
||||
@@ -678,8 +677,8 @@ bool TrackableValue::isSendingParameter() const {
|
||||
}
|
||||
|
||||
if (auto *copyAddr = dyn_cast<CopyAddrInst>(user)) {
|
||||
// Check if our copy_addr is from a function argument that is transferring
|
||||
// and for which the copy_addr is the only use of the function argument.
|
||||
// Check if our copy_addr is from a 'sending' function argument and for
|
||||
// which the copy_addr is the only use of the function argument.
|
||||
auto *fArg = dyn_cast<SILFunctionArgument>(copyAddr->getSrc());
|
||||
if (!fArg || !fArg->isSending())
|
||||
return false;
|
||||
@@ -700,8 +699,8 @@ namespace {
|
||||
/// We need to be able to know if instructions that extract sendable fields from
|
||||
/// non-sendable addresses are reachable from a partial_apply that captures the
|
||||
/// non-sendable value or its underlying object by reference. In such a case, we
|
||||
/// need to require the value to not be transferred when the extraction happens
|
||||
/// since we could race on extracting the value.
|
||||
/// need to require the value to not be sent when the extraction happens since
|
||||
/// we could race on extracting the value.
|
||||
///
|
||||
/// The reason why we use a dataflow to do this is that:
|
||||
///
|
||||
@@ -1189,21 +1188,20 @@ struct PartitionOpBuilder {
|
||||
lookupValueID(srcOperand->get()), srcOperand));
|
||||
}
|
||||
|
||||
void addTransfer(SILValue representative, Operand *op) {
|
||||
void addSend(SILValue representative, Operand *op) {
|
||||
assert(valueHasID(representative) &&
|
||||
"transferred value should already have been encountered");
|
||||
"sent value should already have been encountered");
|
||||
|
||||
currentInstPartitionOps.emplace_back(
|
||||
PartitionOp::Transfer(lookupValueID(representative), op));
|
||||
PartitionOp::Send(lookupValueID(representative), op));
|
||||
}
|
||||
|
||||
void addUndoTransfer(SILValue representative,
|
||||
SILInstruction *untransferringInst) {
|
||||
void addUndoSend(SILValue representative, SILInstruction *unsendingInst) {
|
||||
assert(valueHasID(representative) &&
|
||||
"transferred value should already have been encountered");
|
||||
"value should already have been encountered");
|
||||
|
||||
currentInstPartitionOps.emplace_back(PartitionOp::UndoTransfer(
|
||||
lookupValueID(representative), untransferringInst));
|
||||
currentInstPartitionOps.emplace_back(
|
||||
PartitionOp::UndoSend(lookupValueID(representative), unsendingInst));
|
||||
}
|
||||
|
||||
void addMerge(SILValue destValue, Operand *srcOperand) {
|
||||
@@ -1323,8 +1321,8 @@ enum class TranslationSemantics {
|
||||
/// their behavior depending on some state on the instruction itself.
|
||||
Special,
|
||||
|
||||
/// An instruction that is a full apply site. Can cause transfering or
|
||||
/// untransferring of regions.
|
||||
/// An instruction that is a full apply site. Can cause sending or
|
||||
/// unsending of regions.
|
||||
Apply,
|
||||
|
||||
/// A terminator instruction that acts like a phi in terms of its region.
|
||||
@@ -1344,9 +1342,9 @@ enum class TranslationSemantics {
|
||||
/// should be caught in testing when we assert.
|
||||
AssertingIfNonSendable,
|
||||
|
||||
/// Instructions that always unconditionally transfer all of their
|
||||
/// non-Sendable parameters and that do not have any results.
|
||||
TransferringNoResult,
|
||||
/// Instructions that always unconditionally send all of their non-Sendable
|
||||
/// parameters and that do not have any results.
|
||||
SendingNoResult,
|
||||
};
|
||||
|
||||
} // namespace
|
||||
@@ -1389,8 +1387,8 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
|
||||
case TranslationSemantics::AssertingIfNonSendable:
|
||||
os << "asserting_if_nonsendable";
|
||||
return os;
|
||||
case TranslationSemantics::TransferringNoResult:
|
||||
os << "transferring_no_result";
|
||||
case TranslationSemantics::SendingNoResult:
|
||||
os << "sending_no_result";
|
||||
return os;
|
||||
}
|
||||
|
||||
@@ -1521,20 +1519,20 @@ public:
|
||||
for (SILArgument *arg : functionArguments) {
|
||||
// This will decide what the isolation region is.
|
||||
if (auto state = tryToTrackValue(arg)) {
|
||||
// If we can transfer our parameter, just add it to
|
||||
// If we can send our parameter, just add it to
|
||||
// nonSendableSeparateIndices.
|
||||
//
|
||||
// NOTE: We do not support today the ability to have multiple parameters
|
||||
// transfer together as part of the same region.
|
||||
if (isTransferrableFunctionArgument(cast<SILFunctionArgument>(arg))) {
|
||||
// send together as part of the same region.
|
||||
if (canFunctionArgumentBeSent(cast<SILFunctionArgument>(arg))) {
|
||||
REGIONBASEDISOLATION_LOG(llvm::dbgs() << " %%" << state->getID()
|
||||
<< " (transferring): " << *arg);
|
||||
<< " (sending): " << *arg);
|
||||
nonSendableSeparateIndices.push_back(state->getID());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Otherwise, it is one of our merged parameters. Add it to the never
|
||||
// transfer list and to the region join list.
|
||||
// send list and to the region join list.
|
||||
REGIONBASEDISOLATION_LOG(
|
||||
llvm::dbgs() << " %%" << state->getID() << ": ";
|
||||
state->print(llvm::dbgs()); llvm::dbgs() << *arg);
|
||||
@@ -1774,7 +1772,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/// Transfer the parameters of our partial_apply.
|
||||
/// Send the parameters of our partial_apply.
|
||||
///
|
||||
/// Handling async let has three-four parts:
|
||||
///
|
||||
@@ -1783,11 +1781,11 @@ public:
|
||||
/// builtin "async let start"(%reabstraction | %partial_apply)
|
||||
/// call %asyncLetGet()
|
||||
///
|
||||
/// We transfer the captured parameters of %partial_apply at the async let
|
||||
/// start and then untransfer them at async let get.
|
||||
/// We send the captured parameters of %partial_apply at the async let
|
||||
/// start and then unsend them at async let get.
|
||||
void translateAsyncLetStart(BuiltinInst *bi) {
|
||||
// Just track the result of the builtin inst as an assign fresh. We do this
|
||||
// so we properly track the partial_apply get. We already transferred the
|
||||
// so we properly track the partial_apply get. We already sent the
|
||||
// parameters.
|
||||
builder.addAssignFresh(bi);
|
||||
}
|
||||
@@ -1827,7 +1825,7 @@ public:
|
||||
|
||||
// Then see if /any/ of our uses are passed to over a isolation boundary
|
||||
// that is actor isolated... if we find one continue so we do not undo
|
||||
// the transfer for that element.
|
||||
// the send for that element.
|
||||
if (llvm::any_of(
|
||||
typeInfo.applyUses,
|
||||
[](const std::pair<Type, std::optional<ApplyIsolationCrossing>>
|
||||
@@ -1840,8 +1838,8 @@ public:
|
||||
}))
|
||||
continue;
|
||||
|
||||
builder.addUndoTransfer(trackedArgValue->getRepresentative().getValue(),
|
||||
ai);
|
||||
builder.addUndoSend(trackedArgValue->getRepresentative().getValue(),
|
||||
ai);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1849,21 +1847,20 @@ public:
|
||||
void translateSILPartialApplyAsyncLetBegin(PartialApplyInst *pai) {
|
||||
REGIONBASEDISOLATION_LOG(llvm::dbgs()
|
||||
<< "Translating Async Let Begin Partial Apply!\n");
|
||||
// Grab our partial apply and transfer all of its non-sendable
|
||||
// Grab our partial apply and send all of its non-sendable
|
||||
// parameters. We do not merge the parameters since each individual capture
|
||||
// of the async let at the program level is viewed as still being in
|
||||
// separate regions. Otherwise, we would need to error on the following
|
||||
// code:
|
||||
//
|
||||
// let x = NonSendable(), x2 = NonSendable()
|
||||
// async let y = transferToActor(x) + transferToNonIsolated(x2)
|
||||
// async let y = sendToActor(x) + sendToNonIsolated(x2)
|
||||
// _ = await y
|
||||
// useValue(x2)
|
||||
for (auto &op : ApplySite(pai).getArgumentOperands()) {
|
||||
if (auto trackedArgValue = tryToTrackValue(op.get())) {
|
||||
builder.addRequire(trackedArgValue->getRepresentative().getValue());
|
||||
builder.addTransfer(trackedArgValue->getRepresentative().getValue(),
|
||||
&op);
|
||||
builder.addSend(trackedArgValue->getRepresentative().getValue(), &op);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1873,7 +1870,7 @@ public:
|
||||
|
||||
/// Handles the semantics for SIL applies that cross isolation.
|
||||
///
|
||||
/// Semantically this causes all arguments of the applysite to be transferred.
|
||||
/// Semantically this causes all arguments of the applysite to be sent.
|
||||
void translateIsolatedPartialApply(PartialApplyInst *pai,
|
||||
SILIsolationInfo actorIsolation) {
|
||||
ApplySite applySite(pai);
|
||||
@@ -1884,16 +1881,16 @@ public:
|
||||
for (auto &op : applySite.getArgumentOperands()) {
|
||||
// See if we tracked it.
|
||||
if (auto value = tryToTrackValue(op.get())) {
|
||||
// If we are tracking it, transfer it and if it is actor derived, mark
|
||||
// If we are tracking it, sent it and if it is actor derived, mark
|
||||
// our partial apply as actor derived.
|
||||
builder.addRequire(value->getRepresentative().getValue());
|
||||
builder.addTransfer(value->getRepresentative().getValue(), &op);
|
||||
builder.addSend(value->getRepresentative().getValue(), &op);
|
||||
}
|
||||
}
|
||||
|
||||
// Now that we have transferred everything into the partial_apply, perform
|
||||
// Now that we have sent everything into the partial_apply, perform
|
||||
// an assign fresh for the partial_apply if it is non-Sendable. If we use
|
||||
// any of the transferred values later, we will error, so it is safe to just
|
||||
// any of the sent values later, we will error, so it is safe to just
|
||||
// create a new value.
|
||||
if (pai->getFunctionType()->isSendable())
|
||||
return;
|
||||
@@ -1925,7 +1922,7 @@ public:
|
||||
// handle it especially.
|
||||
//
|
||||
// NOTE: If it is an async_let, then the closure itself will be Sendable. We
|
||||
// treat passing in a value into the async Sendable closure as transferring
|
||||
// treat passing in a value into the async Sendable closure as sending
|
||||
// it into the closure.
|
||||
if (isAsyncLetBeginPartialApply(pai)) {
|
||||
return translateSILPartialApplyAsyncLetBegin(pai);
|
||||
@@ -1951,8 +1948,8 @@ public:
|
||||
void translateCreateAsyncTask(BuiltinInst *bi) {
|
||||
if (auto value = tryToTrackValue(bi->getOperand(1))) {
|
||||
builder.addRequire(value->getRepresentative().getValue());
|
||||
builder.addTransfer(value->getRepresentative().getValue(),
|
||||
&bi->getAllOperands()[1]);
|
||||
builder.addSend(value->getRepresentative().getValue(),
|
||||
&bi->getAllOperands()[1]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1974,13 +1971,13 @@ public:
|
||||
}
|
||||
|
||||
void translateNonIsolationCrossingSILApply(FullApplySite fas) {
|
||||
// For non-self parameters, gather all of the transferring parameters and
|
||||
// gather our non-transferring parameters.
|
||||
SmallVector<Operand *, 8> nonTransferringParameters;
|
||||
// For non-self parameters, gather all of the sending parameters and
|
||||
// gather our non-sending parameters.
|
||||
SmallVector<Operand *, 8> nonSendingParameters;
|
||||
SmallVector<Operand *, 8> sendingIndirectResults;
|
||||
if (fas.getNumArguments()) {
|
||||
// NOTE: We want to process indirect parameters as if they are
|
||||
// parameters... so we process them in nonTransferringParameters.
|
||||
// parameters... so we process them in nonSendingParameters.
|
||||
for (auto &op : fas.getOperandsWithoutSelf()) {
|
||||
// If op is the callee operand, skip it.
|
||||
if (fas.isCalleeOperand(op))
|
||||
@@ -1994,16 +1991,16 @@ public:
|
||||
|
||||
if (auto value = tryToTrackValue(op.get())) {
|
||||
builder.addRequire(value->getRepresentative().getValue());
|
||||
builder.addTransfer(value->getRepresentative().getValue(), &op);
|
||||
builder.addSend(value->getRepresentative().getValue(), &op);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
nonTransferringParameters.push_back(&op);
|
||||
nonSendingParameters.push_back(&op);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If our self parameter was transferring, transfer it. Otherwise, just
|
||||
// If our self parameter was sending, send it. Otherwise, just
|
||||
// stick it in the non self operand values array and run multiassign on
|
||||
// it.
|
||||
if (fas.hasSelfArgument()) {
|
||||
@@ -2012,11 +2009,10 @@ public:
|
||||
.hasOption(SILParameterInfo::Sending)) {
|
||||
if (auto value = tryToTrackValue(selfOperand.get())) {
|
||||
builder.addRequire(value->getRepresentative().getValue());
|
||||
builder.addTransfer(value->getRepresentative().getValue(),
|
||||
&selfOperand);
|
||||
builder.addSend(value->getRepresentative().getValue(), &selfOperand);
|
||||
}
|
||||
} else {
|
||||
nonTransferringParameters.push_back(&selfOperand);
|
||||
nonSendingParameters.push_back(&selfOperand);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2026,7 +2022,7 @@ public:
|
||||
// region as our operands/results, we still need to require that it is live
|
||||
// at the point of application. Otherwise, we will not emit errors if the
|
||||
// closure before this function application is already in the same region as
|
||||
// a transferred value. In such a case, the function application must error.
|
||||
// a sent value. In such a case, the function application must error.
|
||||
if (auto value = tryToTrackValue(fas.getCallee())) {
|
||||
builder.addRequire(value->getRepresentative().getValue());
|
||||
}
|
||||
@@ -2037,16 +2033,16 @@ public:
|
||||
auto type = fas.getSubstCalleeSILType().castTo<SILFunctionType>();
|
||||
auto isolationInfo = SILIsolationInfo::get(*fas);
|
||||
|
||||
// If our result is not transferring, just do the normal multi-assign.
|
||||
// If our result is not a 'sending' result, just do the normal multi-assign.
|
||||
if (!type->hasSendingResult()) {
|
||||
return translateSILMultiAssign(applyResults, nonTransferringParameters,
|
||||
return translateSILMultiAssign(applyResults, nonSendingParameters,
|
||||
isolationInfo);
|
||||
}
|
||||
|
||||
// If our result is transferring, then pass in empty as our results, no
|
||||
// override isolation, then perform assign fresh.
|
||||
// If our result is a 'sending' result, then pass in empty as our results,
|
||||
// no override isolation, then perform assign fresh.
|
||||
ArrayRef<SILValue> empty;
|
||||
translateSILMultiAssign(empty, nonTransferringParameters, {});
|
||||
translateSILMultiAssign(empty, nonSendingParameters, {});
|
||||
|
||||
// Sending direct results.
|
||||
for (SILValue result : applyResults) {
|
||||
@@ -2080,7 +2076,7 @@ public:
|
||||
}
|
||||
|
||||
// If this apply does not cross isolation domains, it has normal
|
||||
// non-transferring multi-assignment semantics
|
||||
// non-sending multi-assignment semantics
|
||||
if (!getApplyIsolationCrossing(*fas))
|
||||
return translateNonIsolationCrossingSILApply(fas);
|
||||
|
||||
@@ -2098,9 +2094,9 @@ public:
|
||||
|
||||
/// Handles the semantics for SIL applies that cross isolation.
|
||||
///
|
||||
/// Semantically this causes all arguments of the applysite to be transferred.
|
||||
/// Semantically this causes all arguments of the applysite to be sent.
|
||||
void translateIsolationCrossingSILApply(FullApplySite applySite) {
|
||||
// Require all operands first before we emit transferring.
|
||||
// Require all operands first before we emit a send.
|
||||
for (auto op : applySite.getArguments())
|
||||
if (auto value = tryToTrackValue(op))
|
||||
builder.addRequire(value->getRepresentative().getValue());
|
||||
@@ -2108,14 +2104,14 @@ public:
|
||||
auto handleSILOperands = [&](MutableArrayRef<Operand> operands) {
|
||||
for (auto &op : operands) {
|
||||
if (auto value = tryToTrackValue(op.get())) {
|
||||
builder.addTransfer(value->getRepresentative().getValue(), &op);
|
||||
builder.addSend(value->getRepresentative().getValue(), &op);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
auto handleSILSelf = [&](Operand *self) {
|
||||
if (auto value = tryToTrackValue(self->get())) {
|
||||
builder.addTransfer(value->getRepresentative().getValue(), self);
|
||||
builder.addSend(value->getRepresentative().getValue(), self);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2251,22 +2247,22 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void translateSILAssignmentToTransferringParameter(TrackableValue destRoot,
|
||||
Operand *destOperand,
|
||||
TrackableValue srcRoot,
|
||||
Operand *srcOperand) {
|
||||
void translateSILAssignmentToSendingParameter(TrackableValue destRoot,
|
||||
Operand *destOperand,
|
||||
TrackableValue srcRoot,
|
||||
Operand *srcOperand) {
|
||||
assert(isa<AllocStackInst>(destRoot.getRepresentative().getValue()) &&
|
||||
"Destination should always be an alloc_stack");
|
||||
|
||||
// Transfer src. This ensures that we cannot use src again locally in this
|
||||
// function... which makes sense since its value is now in the transferring
|
||||
// Send src. This ensures that we cannot use src again locally in this
|
||||
// function... which makes sense since its value is now in the 'sending'
|
||||
// parameter.
|
||||
builder.addRequire(srcRoot.getRepresentative().getValue());
|
||||
builder.addTransfer(srcRoot.getRepresentative().getValue(), srcOperand);
|
||||
builder.addSend(srcRoot.getRepresentative().getValue(), srcOperand);
|
||||
|
||||
// Then check if we are assigning into an aggregate projection. In such a
|
||||
// case, we want to ensure that we keep tracking the elements already in the
|
||||
// region of transferring. This is more conservative than we need to be
|
||||
// region of sending. This is more conservative than we need to be
|
||||
// (since we could forget anything reachable from the aggregate
|
||||
// field)... but being more conservative is ok.
|
||||
if (isProjectedFromAggregate(destOperand->get()))
|
||||
@@ -2380,13 +2376,13 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/// Instructions that transfer all of their non-Sendable parameters
|
||||
/// Instructions that send all of their non-Sendable parameters
|
||||
/// unconditionally and that do not have a result.
|
||||
void translateSILTransferringNoResult(MutableArrayRef<Operand> values) {
|
||||
void translateSILSendingNoResult(MutableArrayRef<Operand> values) {
|
||||
for (auto &op : values) {
|
||||
if (auto ns = tryToTrackValue(op.get())) {
|
||||
builder.addRequire(ns->getRepresentative().getValue());
|
||||
builder.addTransfer(ns->getRepresentative().getValue(), &op);
|
||||
builder.addSend(ns->getRepresentative().getValue(), &op);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2497,8 +2493,7 @@ public:
|
||||
|
||||
case TranslationSemantics::Asserting:
|
||||
llvm::errs() << "BannedInst: " << *inst;
|
||||
llvm::report_fatal_error(
|
||||
"transfer-non-sendable: Found banned instruction?!");
|
||||
llvm::report_fatal_error("send-non-sendable: Found banned instruction?!");
|
||||
return;
|
||||
|
||||
case TranslationSemantics::AssertingIfNonSendable:
|
||||
@@ -2510,11 +2505,11 @@ public:
|
||||
return;
|
||||
llvm::errs() << "BadInst: " << *inst;
|
||||
llvm::report_fatal_error(
|
||||
"transfer-non-sendable: Found instruction that is not allowed to "
|
||||
"send-non-sendable: Found instruction that is not allowed to "
|
||||
"have non-Sendable parameters with such parameters?!");
|
||||
return;
|
||||
case TranslationSemantics::TransferringNoResult:
|
||||
return translateSILTransferringNoResult(inst->getAllOperands());
|
||||
case TranslationSemantics::SendingNoResult:
|
||||
return translateSILSendingNoResult(inst->getAllOperands());
|
||||
}
|
||||
llvm_unreachable("Covered switch isn't covered?!");
|
||||
}
|
||||
@@ -2843,7 +2838,7 @@ CONSTANT_TRANSLATION(DynamicMethodBranchInst, TerminatorPhi)
|
||||
// Function exiting terminators.
|
||||
//
|
||||
// We handle these especially since we want to make sure that inout parameters
|
||||
// that are transferred are forced to be reinitialized.
|
||||
// that are sent are forced to be reinitialized.
|
||||
//
|
||||
// There is an assert in TermInst::isFunctionExiting that makes sure we do this
|
||||
// correctly.
|
||||
@@ -3111,7 +3106,7 @@ PartitionOpTranslator::visitLoadBorrowInst(LoadBorrowInst *lbi) {
|
||||
TranslationSemantics PartitionOpTranslator::visitReturnInst(ReturnInst *ri) {
|
||||
addEndOfFunctionChecksForInOutSendingParameters(ri);
|
||||
if (ri->getFunction()->getLoweredFunctionType()->hasSendingResult()) {
|
||||
return TranslationSemantics::TransferringNoResult;
|
||||
return TranslationSemantics::SendingNoResult;
|
||||
}
|
||||
return TranslationSemantics::Require;
|
||||
}
|
||||
@@ -3329,13 +3324,12 @@ TranslationSemantics PartitionOpTranslator::visitCheckedCastAddrBranchInst(
|
||||
|
||||
BlockPartitionState::BlockPartitionState(
|
||||
SILBasicBlock *basicBlock, PartitionOpTranslator &translator,
|
||||
TransferringOperandSetFactory &ptrSetFactory,
|
||||
SendingOperandSetFactory &ptrSetFactory,
|
||||
IsolationHistory::Factory &isolationHistoryFactory,
|
||||
TransferringOperandToStateMap &transferringOpToStateMap)
|
||||
SendingOperandToStateMap &sendingOpToStateMap)
|
||||
: entryPartition(isolationHistoryFactory.get()),
|
||||
exitPartition(isolationHistoryFactory.get()), basicBlock(basicBlock),
|
||||
ptrSetFactory(ptrSetFactory),
|
||||
transferringOpToStateMap(transferringOpToStateMap) {
|
||||
ptrSetFactory(ptrSetFactory), sendingOpToStateMap(sendingOpToStateMap) {
|
||||
translator.translateSILBasicBlock(basicBlock, blockPartitionOps);
|
||||
}
|
||||
|
||||
@@ -3348,11 +3342,11 @@ bool BlockPartitionState::recomputeExitFromEntry(
|
||||
PartitionOpTranslator &translator;
|
||||
|
||||
ComputeEvaluator(Partition &workingPartition,
|
||||
TransferringOperandSetFactory &ptrSetFactory,
|
||||
SendingOperandSetFactory &ptrSetFactory,
|
||||
PartitionOpTranslator &translator,
|
||||
TransferringOperandToStateMap &transferringOpToStateMap)
|
||||
SendingOperandToStateMap &sendingOpToStateMap)
|
||||
: PartitionOpEvaluatorBaseImpl(workingPartition, ptrSetFactory,
|
||||
transferringOpToStateMap),
|
||||
sendingOpToStateMap),
|
||||
translator(translator) {}
|
||||
|
||||
SILIsolationInfo getIsolationRegionInfo(Element elt) const {
|
||||
@@ -3385,7 +3379,7 @@ bool BlockPartitionState::recomputeExitFromEntry(
|
||||
}
|
||||
};
|
||||
ComputeEvaluator eval(workingPartition, ptrSetFactory, translator,
|
||||
transferringOpToStateMap);
|
||||
sendingOpToStateMap);
|
||||
for (const auto &partitionOp : blockPartitionOps) {
|
||||
// By calling apply without providing any error handling callbacks, errors
|
||||
// will be surpressed. will be suppressed
|
||||
@@ -3454,7 +3448,7 @@ static bool canComputeRegionsForFunction(SILFunction *fn) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The sendable protocol should /always/ be available if TransferNonSendable
|
||||
// The sendable protocol should /always/ be available if SendNonSendable
|
||||
// is enabled. If not, there is a major bug in the compiler and we should
|
||||
// fail loudly.
|
||||
if (!fn->getASTContext().getProtocol(KnownProtocolKind::Sendable))
|
||||
@@ -3467,7 +3461,7 @@ RegionAnalysisFunctionInfo::RegionAnalysisFunctionInfo(
|
||||
SILFunction *fn, PostOrderFunctionInfo *pofi)
|
||||
: allocator(), fn(fn), valueMap(fn), translator(), ptrSetFactory(allocator),
|
||||
isolationHistoryFactory(allocator),
|
||||
transferringOpToStateMap(isolationHistoryFactory), blockStates(),
|
||||
sendingOperandToStateMap(isolationHistoryFactory), blockStates(),
|
||||
pofi(pofi), solved(false), supportedFunction(true) {
|
||||
// Before we do anything, make sure that we support processing this function.
|
||||
//
|
||||
@@ -3482,7 +3476,7 @@ RegionAnalysisFunctionInfo::RegionAnalysisFunctionInfo(
|
||||
blockStates.emplace(fn, [this](SILBasicBlock *block) -> BlockPartitionState {
|
||||
return BlockPartitionState(block, *translator, ptrSetFactory,
|
||||
isolationHistoryFactory,
|
||||
transferringOpToStateMap);
|
||||
sendingOperandToStateMap);
|
||||
});
|
||||
// Mark all blocks as needing to be updated.
|
||||
for (auto &block : *fn) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -134,7 +134,7 @@ static void addMandatoryDiagnosticOptPipeline(SILPassPipelinePlan &P) {
|
||||
// Passes that depend on region analysis information
|
||||
//
|
||||
|
||||
P.addTransferNonSendable();
|
||||
P.addSendNonSendable();
|
||||
|
||||
// Now that we have completed running passes that use region analysis, clear
|
||||
// region analysis and emit diagnostics for unnecessary preconcurrency
|
||||
|
||||
@@ -42,17 +42,17 @@ void PartitionOp::print(llvm::raw_ostream &os, bool extraSpace) const {
|
||||
case PartitionOpKind::AssignFresh:
|
||||
os << "assign_fresh %%" << opArgs[0];
|
||||
break;
|
||||
case PartitionOpKind::Transfer: {
|
||||
case PartitionOpKind::Send: {
|
||||
constexpr static char extraSpaceLiteral[10] = " ";
|
||||
os << "transfer ";
|
||||
os << "send ";
|
||||
if (extraSpace)
|
||||
os << extraSpaceLiteral;
|
||||
os << "%%" << opArgs[0];
|
||||
break;
|
||||
}
|
||||
case PartitionOpKind::UndoTransfer: {
|
||||
case PartitionOpKind::UndoSend: {
|
||||
constexpr static char extraSpaceLiteral[10] = " ";
|
||||
os << "undo_transfer ";
|
||||
os << "undo_send ";
|
||||
if (extraSpace)
|
||||
os << extraSpaceLiteral;
|
||||
os << "%%" << opArgs[0];
|
||||
@@ -145,14 +145,13 @@ Partition Partition::separateRegions(SILLocation loc, ArrayRef<Element> indices,
|
||||
return p;
|
||||
}
|
||||
|
||||
void Partition::markTransferred(Element val,
|
||||
TransferringOperandSet *transferredOperandSet) {
|
||||
void Partition::markSent(Element val, SendingOperandSet *sendingOperandSet) {
|
||||
// First see if our val is tracked. If it is not tracked, insert it and mark
|
||||
// its new region as transferred.
|
||||
// its new region as sent.
|
||||
if (!isTrackingElement(val)) {
|
||||
elementToRegionMap.insert_or_assign(val, freshLabel);
|
||||
pushNewElementRegion(val);
|
||||
regionToTransferredOpMap.insert({freshLabel, transferredOperandSet});
|
||||
regionToSendingOpMap.insert({freshLabel, sendingOperandSet});
|
||||
freshLabel = Region(freshLabel + 1);
|
||||
canonical = false;
|
||||
return;
|
||||
@@ -161,18 +160,17 @@ void Partition::markTransferred(Element val,
|
||||
// Otherwise, we already have this value in the map. Try to insert it.
|
||||
auto iter1 = elementToRegionMap.find(val);
|
||||
assert(iter1 != elementToRegionMap.end());
|
||||
auto iter2 =
|
||||
regionToTransferredOpMap.insert({iter1->second, transferredOperandSet});
|
||||
auto iter2 = regionToSendingOpMap.insert({iter1->second, sendingOperandSet});
|
||||
|
||||
// If we did insert, just return. We were not tracking any state.
|
||||
if (iter2.second)
|
||||
return;
|
||||
|
||||
// Otherwise, we need to merge the sets.
|
||||
iter2.first->second = iter2.first->second->merge(transferredOperandSet);
|
||||
iter2.first->second = iter2.first->second->merge(sendingOperandSet);
|
||||
}
|
||||
|
||||
bool Partition::undoTransfer(Element val) {
|
||||
bool Partition::undoSend(Element val) {
|
||||
// First see if our val is tracked. If it is not tracked, insert it.
|
||||
if (!isTrackingElement(val)) {
|
||||
elementToRegionMap.insert_or_assign(val, freshLabel);
|
||||
@@ -183,14 +181,14 @@ bool Partition::undoTransfer(Element val) {
|
||||
}
|
||||
|
||||
// Otherwise, we already have this value in the map. Remove it from the
|
||||
// transferred map.
|
||||
// "sending operand" map.
|
||||
auto iter1 = elementToRegionMap.find(val);
|
||||
assert(iter1 != elementToRegionMap.end());
|
||||
return regionToTransferredOpMap.erase(iter1->second);
|
||||
return regionToSendingOpMap.erase(iter1->second);
|
||||
}
|
||||
|
||||
void Partition::trackNewElement(Element newElt, bool updateHistory) {
|
||||
SWIFT_DEFER { validateRegionToTransferredOpMapRegions(); };
|
||||
SWIFT_DEFER { validateRegionToSendingOpMapRegions(); };
|
||||
|
||||
// First try to emplace newElt with fresh_label.
|
||||
auto iter = elementToRegionMap.try_emplace(newElt, freshLabel);
|
||||
@@ -214,10 +212,10 @@ void Partition::trackNewElement(Element newElt, bool updateHistory) {
|
||||
// 1. We of course need to update iter to point at fresh_label.
|
||||
//
|
||||
// 2. We need to see if this value was the last element in its current
|
||||
// region. If so, then we need to remove the region from the transferred op
|
||||
// region. If so, then we need to remove the region from the sending op
|
||||
// map.
|
||||
//
|
||||
// This is important to ensure that every region in the transferredOpMap is
|
||||
// This is important to ensure that every region in the sendingOpMap is
|
||||
// also in elementToRegionMap.
|
||||
auto oldRegion = iter.first->second;
|
||||
iter.first->second = freshLabel;
|
||||
@@ -234,7 +232,7 @@ void Partition::trackNewElement(Element newElt, bool updateHistory) {
|
||||
if (updateHistory)
|
||||
pushRemoveElementFromRegion(*matchingElt, newElt);
|
||||
} else {
|
||||
regionToTransferredOpMap.erase(oldRegion);
|
||||
regionToSendingOpMap.erase(oldRegion);
|
||||
if (updateHistory)
|
||||
pushRemoveLastElementFromRegion(newElt);
|
||||
}
|
||||
@@ -254,7 +252,7 @@ void Partition::assignElement(Element oldElt, Element newElt,
|
||||
if (oldElt == newElt)
|
||||
return;
|
||||
|
||||
SWIFT_DEFER { validateRegionToTransferredOpMapRegions(); };
|
||||
SWIFT_DEFER { validateRegionToSendingOpMapRegions(); };
|
||||
|
||||
// First try to emplace oldElt with the newRegion.
|
||||
auto newRegion = elementToRegionMap.at(newElt);
|
||||
@@ -283,7 +281,7 @@ void Partition::assignElement(Element oldElt, Element newElt,
|
||||
|
||||
// Otherwise, we need to actually assign. In such a case, we need to see if
|
||||
// oldElt was the last element in oldRegion. If so, we need to erase the
|
||||
// oldRegion from regionToTransferredOpMap.
|
||||
// oldRegion from regionToSendingOpMap.
|
||||
iter.first->second = newRegion;
|
||||
|
||||
auto getValueFromOtherRegion = [&]() -> std::optional<Element> {
|
||||
@@ -298,7 +296,7 @@ void Partition::assignElement(Element oldElt, Element newElt,
|
||||
if (updateHistory)
|
||||
pushRemoveElementFromRegion(*otherElt, oldElt);
|
||||
} else {
|
||||
regionToTransferredOpMap.erase(oldRegion);
|
||||
regionToSendingOpMap.erase(oldRegion);
|
||||
if (updateHistory)
|
||||
pushRemoveLastElementFromRegion(oldElt);
|
||||
}
|
||||
@@ -343,18 +341,18 @@ Partition Partition::join(const Partition &fst, Partition &mutableSnd) {
|
||||
// representative element of its region in sndReduced. We need to
|
||||
// ensure that in result, that representative and our current
|
||||
// value are in the same region. If they are the same value, we can
|
||||
// just reuse sndEltNumber's region in result for the transferring
|
||||
// just reuse sndEltNumber's region in result for the sending
|
||||
// check.
|
||||
if (sndEltNumber != Element(sndRegionNumber)) {
|
||||
// NOTE: History is updated by Partition::merge(...).
|
||||
resultRegion = result.merge(sndEltNumber, Element(sndRegionNumber));
|
||||
}
|
||||
|
||||
// Then if sndRegionNumber is transferred in sndReduced, make sure
|
||||
// mergedRegion is transferred in result.
|
||||
auto sndIter = snd.regionToTransferredOpMap.find(sndRegionNumber);
|
||||
if (sndIter != snd.regionToTransferredOpMap.end()) {
|
||||
auto resultIter = result.regionToTransferredOpMap.insert(
|
||||
// Then if sndRegionNumber is sent in sndReduced, make sure mergedRegion
|
||||
// is sent in result.
|
||||
auto sndIter = snd.regionToSendingOpMap.find(sndRegionNumber);
|
||||
if (sndIter != snd.regionToSendingOpMap.end()) {
|
||||
auto resultIter = result.regionToSendingOpMap.insert(
|
||||
{resultRegion, sndIter->second});
|
||||
if (!resultIter.second) {
|
||||
resultIter.first->second =
|
||||
@@ -377,7 +375,7 @@ Partition Partition::join(const Partition &fst, Partition &mutableSnd) {
|
||||
// be
|
||||
// <= our representative.
|
||||
//
|
||||
// In this case, we do not need to propagate transfer into resultRegion
|
||||
// In this case, we do not need to propagate 'send' into resultRegion
|
||||
// since we would have handled that already when we visited our earlier
|
||||
// representative element number.
|
||||
{
|
||||
@@ -401,9 +399,9 @@ Partition Partition::join(const Partition &fst, Partition &mutableSnd) {
|
||||
assert(sndEltNumber == Element(sndRegionNumber));
|
||||
result.elementToRegionMap.insert({sndEltNumber, sndRegionNumber});
|
||||
result.pushNewElementRegion(sndEltNumber);
|
||||
auto sndIter = snd.regionToTransferredOpMap.find(sndRegionNumber);
|
||||
if (sndIter != snd.regionToTransferredOpMap.end()) {
|
||||
auto fstIter = result.regionToTransferredOpMap.insert(
|
||||
auto sndIter = snd.regionToSendingOpMap.find(sndRegionNumber);
|
||||
if (sndIter != snd.regionToSendingOpMap.end()) {
|
||||
auto fstIter = result.regionToSendingOpMap.insert(
|
||||
{sndRegionNumber, sndIter->second});
|
||||
if (!fstIter.second)
|
||||
fstIter.first->second = fstIter.first->second->merge(sndIter->second);
|
||||
@@ -423,12 +421,12 @@ Partition Partition::join(const Partition &fst, Partition &mutableSnd) {
|
||||
bool Partition::popHistory(
|
||||
SmallVectorImpl<IsolationHistory> &foundJoinedHistories) {
|
||||
// We only allow for history rewinding if we are not tracking any
|
||||
// transferring operands. This is because the history rewinding does not
|
||||
// care about transferring. One can either construct a new Partition from
|
||||
// the current Partition using Partition::removingTransferringInfo or clear
|
||||
// the transferring information using Partition::clearTransferringInfo().
|
||||
assert(regionToTransferredOpMap.empty() &&
|
||||
"Can only rewind history if not tracking any transferring operands");
|
||||
// sending operands. This is because the history rewinding does not
|
||||
// care about sending. One can either construct a new Partition from
|
||||
// the current Partition using Partition::removeSendingOperandSet or clear
|
||||
// the sending information using Partition::clearSendingOperandState().
|
||||
assert(regionToSendingOpMap.empty() &&
|
||||
"Can only rewind history if not tracking any sending operands");
|
||||
|
||||
if (!history.getHead())
|
||||
return false;
|
||||
@@ -453,9 +451,9 @@ void Partition::print(llvm::raw_ostream &os) const {
|
||||
|
||||
os << "[";
|
||||
for (auto [regionNo, elementNumbers] : multimap.getRange()) {
|
||||
auto iter = regionToTransferredOpMap.find(regionNo);
|
||||
bool isTransferred = iter != regionToTransferredOpMap.end();
|
||||
if (isTransferred) {
|
||||
auto iter = regionToSendingOpMap.find(regionNo);
|
||||
bool wasSent = iter != regionToSendingOpMap.end();
|
||||
if (wasSent) {
|
||||
os << '{';
|
||||
} else {
|
||||
os << '(';
|
||||
@@ -465,7 +463,7 @@ void Partition::print(llvm::raw_ostream &os) const {
|
||||
for (Element i : elementNumbers) {
|
||||
os << (j++ ? " " : "") << i;
|
||||
}
|
||||
if (isTransferred) {
|
||||
if (wasSent) {
|
||||
os << '}';
|
||||
} else {
|
||||
os << ')';
|
||||
@@ -483,11 +481,11 @@ void Partition::printVerbose(llvm::raw_ostream &os) const {
|
||||
multimap.setFrozen();
|
||||
|
||||
for (auto [regionNo, elementNumbers] : multimap.getRange()) {
|
||||
auto iter = regionToTransferredOpMap.find(regionNo);
|
||||
bool isTransferred = iter != regionToTransferredOpMap.end();
|
||||
auto iter = regionToSendingOpMap.find(regionNo);
|
||||
bool wasSent = iter != regionToSendingOpMap.end();
|
||||
|
||||
os << "Region: " << regionNo << ". ";
|
||||
if (isTransferred) {
|
||||
if (wasSent) {
|
||||
os << '{';
|
||||
} else {
|
||||
os << '(';
|
||||
@@ -497,14 +495,14 @@ void Partition::printVerbose(llvm::raw_ostream &os) const {
|
||||
for (Element i : elementNumbers) {
|
||||
os << (j++ ? " " : "") << i;
|
||||
}
|
||||
if (isTransferred) {
|
||||
if (wasSent) {
|
||||
os << '}';
|
||||
} else {
|
||||
os << ')';
|
||||
}
|
||||
os << "\n";
|
||||
os << "TransferInsts:\n";
|
||||
if (isTransferred) {
|
||||
os << "SentInsts:\n";
|
||||
if (wasSent) {
|
||||
for (auto op : iter->second->data()) {
|
||||
os << " ";
|
||||
op->print(os);
|
||||
@@ -590,8 +588,8 @@ bool Partition::is_canonical_correct() const {
|
||||
return fail(eltNo, 3);
|
||||
}
|
||||
|
||||
// Before we do anything, validate region to transferred op map.
|
||||
validateRegionToTransferredOpMapRegions();
|
||||
// Before we do anything, validate region to region to sending op map.
|
||||
validateRegionToSendingOpMapRegions();
|
||||
|
||||
return true;
|
||||
#endif
|
||||
@@ -622,11 +620,11 @@ Region Partition::merge(Element fst, Element snd, bool updateHistory) {
|
||||
// Rename snd and snd's entire region to fst's region.
|
||||
SmallVector<Element, 32> mergedElements;
|
||||
horizontalUpdate(snd, fstRegion, mergedElements);
|
||||
auto iter = regionToTransferredOpMap.find(sndRegion);
|
||||
if (iter != regionToTransferredOpMap.end()) {
|
||||
auto iter = regionToSendingOpMap.find(sndRegion);
|
||||
if (iter != regionToSendingOpMap.end()) {
|
||||
auto operand = iter->second;
|
||||
regionToTransferredOpMap.erase(iter);
|
||||
regionToTransferredOpMap.insert({fstRegion, operand});
|
||||
regionToSendingOpMap.erase(iter);
|
||||
regionToSendingOpMap.insert({fstRegion, operand});
|
||||
}
|
||||
|
||||
assert(is_canonical_correct());
|
||||
@@ -643,7 +641,7 @@ void Partition::canonicalize() {
|
||||
return;
|
||||
canonical = true;
|
||||
|
||||
validateRegionToTransferredOpMapRegions();
|
||||
validateRegionToSendingOpMapRegions();
|
||||
std::map<Region, Region> oldRegionToRelabeledMap;
|
||||
|
||||
// We rely on in-order traversal of labels to ensure that we always take the
|
||||
@@ -665,17 +663,16 @@ void Partition::canonicalize() {
|
||||
freshLabel = Region(eltNo + 1);
|
||||
}
|
||||
|
||||
// Then relabel our regionToTransferredInst map if we need to by swapping
|
||||
// out the old map and updating.
|
||||
// Then relabel our regionToSendingOpMap map if we need to by swapping out the
|
||||
// old map and updating.
|
||||
//
|
||||
// TODO: If we just used an array for this, we could just rewrite and
|
||||
// re-sort and not have to deal with potential allocations.
|
||||
decltype(regionToTransferredOpMap) oldMap =
|
||||
std::move(regionToTransferredOpMap);
|
||||
decltype(regionToSendingOpMap) oldMap = std::move(regionToSendingOpMap);
|
||||
for (auto &[oldReg, op] : oldMap) {
|
||||
auto iter = oldRegionToRelabeledMap.find(oldReg);
|
||||
assert(iter != oldRegionToRelabeledMap.end());
|
||||
regionToTransferredOpMap[iter->second] = op;
|
||||
regionToSendingOpMap[iter->second] = op;
|
||||
}
|
||||
|
||||
assert(is_canonical_correct());
|
||||
@@ -719,7 +716,7 @@ bool Partition::popHistoryOnce(
|
||||
auto iter = elementToRegionMap.find(head->getFirstArgAsElement());
|
||||
assert(iter != elementToRegionMap.end());
|
||||
Region oldRegion = iter->second;
|
||||
regionToTransferredOpMap.erase(oldRegion);
|
||||
regionToSendingOpMap.erase(oldRegion);
|
||||
elementToRegionMap.erase(iter);
|
||||
assert(llvm::none_of(elementToRegionMap,
|
||||
[&](std::pair<Element, Region> pair) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// RUN: %target-sil-opt -transfer-non-sendable -strict-concurrency=complete %s -o /dev/null -verify
|
||||
// RUN: %target-sil-opt -send-non-sendable -strict-concurrency=complete %s -o /dev/null -verify
|
||||
|
||||
// REQUIRES: concurrency
|
||||
// REQUIRES: asserts
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// RUN: %target-sil-opt -transfer-non-sendable -enable-upcoming-feature RegionBasedIsolation -strict-concurrency=complete %s -verify
|
||||
// RUN: %target-sil-opt -send-non-sendable -enable-upcoming-feature RegionBasedIsolation -strict-concurrency=complete %s -verify
|
||||
|
||||
// PLEASE READ THIS!
|
||||
//
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// RUN: %target-sil-opt -transfer-non-sendable -enable-upcoming-feature RegionBasedIsolation -strict-concurrency=complete %s -verify -o /dev/null
|
||||
// RUN: %target-sil-opt -send-non-sendable -enable-upcoming-feature RegionBasedIsolation -strict-concurrency=complete %s -verify -o /dev/null
|
||||
|
||||
// REQUIRES: concurrency
|
||||
// REQUIRES: asserts
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// RUN: %target-sil-opt -transfer-non-sendable -strict-concurrency=complete %s -verify -o /dev/null
|
||||
// RUN: %target-sil-opt -send-non-sendable -strict-concurrency=complete %s -verify -o /dev/null
|
||||
|
||||
// REQUIRES: concurrency
|
||||
// REQUIRES: asserts
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// RUN: %target-sil-opt -transfer-non-sendable -enable-sil-opaque-values -enable-upcoming-feature RegionBasedIsolation -strict-concurrency=complete %s -verify -o /dev/null
|
||||
// RUN: %target-sil-opt -send-non-sendable -enable-sil-opaque-values -enable-upcoming-feature RegionBasedIsolation -strict-concurrency=complete %s -verify -o /dev/null
|
||||
|
||||
// REQUIRES: concurrency
|
||||
// REQUIRES: asserts
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// RUN: %target-sil-opt -transfer-non-sendable -strict-concurrency=complete -swift-version 5 %s -verify -verify-additional-prefix complete-
|
||||
// RUN: %target-sil-opt -transfer-non-sendable -swift-version 6 %s -verify -verify-additional-prefix tns-
|
||||
// RUN: %target-sil-opt -send-non-sendable -strict-concurrency=complete -swift-version 5 %s -verify -verify-additional-prefix complete-
|
||||
// RUN: %target-sil-opt -send-non-sendable -swift-version 6 %s -verify -verify-additional-prefix tns-
|
||||
|
||||
// READ THIS: This test takes advantage of the semantics of concurrency to
|
||||
// validate that sil-opt can properly set the swift-version flag in a way that
|
||||
|
||||
@@ -40,8 +40,8 @@ using PartitionTester = Partition::PartitionTester;
|
||||
struct MockedPartitionOpEvaluator final
|
||||
: PartitionOpEvaluatorBaseImpl<MockedPartitionOpEvaluator> {
|
||||
MockedPartitionOpEvaluator(Partition &workingPartition,
|
||||
TransferringOperandSetFactory &ptrSetFactory,
|
||||
TransferringOperandToStateMap &operandToStateMap)
|
||||
SendingOperandSetFactory &ptrSetFactory,
|
||||
SendingOperandToStateMap &operandToStateMap)
|
||||
: PartitionOpEvaluatorBaseImpl(workingPartition, ptrSetFactory,
|
||||
operandToStateMap) {}
|
||||
|
||||
@@ -83,8 +83,8 @@ struct MockedPartitionOpEvaluatorWithFailureCallback final
|
||||
FailureCallbackTy failureCallback;
|
||||
|
||||
MockedPartitionOpEvaluatorWithFailureCallback(
|
||||
Partition &workingPartition, TransferringOperandSetFactory &ptrSetFactory,
|
||||
TransferringOperandToStateMap &operandToStateMap,
|
||||
Partition &workingPartition, SendingOperandSetFactory &ptrSetFactory,
|
||||
SendingOperandToStateMap &operandToStateMap,
|
||||
FailureCallbackTy failureCallback)
|
||||
: PartitionOpEvaluatorBaseImpl(workingPartition, ptrSetFactory,
|
||||
operandToStateMap),
|
||||
@@ -133,10 +133,10 @@ struct MockedPartitionOpEvaluatorWithFailureCallback final
|
||||
// Tests
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// When we transfer we need a specific transfer instruction. We do not ever
|
||||
// When we send we need a specific send instruction. We do not ever
|
||||
// actually dereference the instruction, so just use some invalid ptr values so
|
||||
// we can compare.
|
||||
Operand *transferSingletons[5] = {
|
||||
Operand *operandSingletons[5] = {
|
||||
(Operand *)0xDEAD0000, (Operand *)0xFEAD0000, (Operand *)0xAEDF0000,
|
||||
(Operand *)0xFEDA0000, (Operand *)0xFBDA0000,
|
||||
};
|
||||
@@ -154,16 +154,16 @@ SILLocation fakeLoc = SILLocation::invalid();
|
||||
// yields p3.
|
||||
TEST(PartitionUtilsTest, TestMergeAndJoin) {
|
||||
llvm::BumpPtrAllocator allocator;
|
||||
Partition::TransferringOperandSetFactory factory(allocator);
|
||||
Partition::SendingOperandSetFactory factory(allocator);
|
||||
IsolationHistory::Factory historyFactory(allocator);
|
||||
TransferringOperandToStateMap transferringOpToStateMap(historyFactory);
|
||||
SendingOperandToStateMap sendingOpToStateMap(historyFactory);
|
||||
|
||||
Partition p1(historyFactory.get());
|
||||
Partition p2(historyFactory.get());
|
||||
Partition p3(historyFactory.get());
|
||||
|
||||
{
|
||||
MockedPartitionOpEvaluator eval(p1, factory, transferringOpToStateMap);
|
||||
MockedPartitionOpEvaluator eval(p1, factory, sendingOpToStateMap);
|
||||
eval.apply({PartitionOp::AssignFresh(Element(0)),
|
||||
PartitionOp::AssignFresh(Element(1)),
|
||||
PartitionOp::AssignFresh(Element(2)),
|
||||
@@ -171,7 +171,7 @@ TEST(PartitionUtilsTest, TestMergeAndJoin) {
|
||||
}
|
||||
|
||||
{
|
||||
MockedPartitionOpEvaluator eval(p2, factory, transferringOpToStateMap);
|
||||
MockedPartitionOpEvaluator eval(p2, factory, sendingOpToStateMap);
|
||||
eval.apply({PartitionOp::AssignFresh(Element(5)),
|
||||
PartitionOp::AssignFresh(Element(6)),
|
||||
PartitionOp::AssignFresh(Element(7)),
|
||||
@@ -179,7 +179,7 @@ TEST(PartitionUtilsTest, TestMergeAndJoin) {
|
||||
}
|
||||
|
||||
{
|
||||
MockedPartitionOpEvaluator eval(p3, factory, transferringOpToStateMap);
|
||||
MockedPartitionOpEvaluator eval(p3, factory, sendingOpToStateMap);
|
||||
eval.apply({PartitionOp::AssignFresh(Element(2)),
|
||||
PartitionOp::AssignFresh(Element(3)),
|
||||
PartitionOp::AssignFresh(Element(4)),
|
||||
@@ -191,7 +191,7 @@ TEST(PartitionUtilsTest, TestMergeAndJoin) {
|
||||
EXPECT_FALSE(Partition::equals(p1, p3));
|
||||
|
||||
{
|
||||
MockedPartitionOpEvaluator eval(p1, factory, transferringOpToStateMap);
|
||||
MockedPartitionOpEvaluator eval(p1, factory, sendingOpToStateMap);
|
||||
eval.apply({PartitionOp::AssignFresh(Element(4)),
|
||||
PartitionOp::AssignFresh(Element(5)),
|
||||
PartitionOp::AssignFresh(Element(6)),
|
||||
@@ -200,7 +200,7 @@ TEST(PartitionUtilsTest, TestMergeAndJoin) {
|
||||
}
|
||||
|
||||
{
|
||||
MockedPartitionOpEvaluator eval(p2, factory, transferringOpToStateMap);
|
||||
MockedPartitionOpEvaluator eval(p2, factory, sendingOpToStateMap);
|
||||
eval.apply({PartitionOp::AssignFresh(Element(1)),
|
||||
PartitionOp::AssignFresh(Element(2)),
|
||||
PartitionOp::AssignFresh(Element(3)),
|
||||
@@ -209,7 +209,7 @@ TEST(PartitionUtilsTest, TestMergeAndJoin) {
|
||||
}
|
||||
|
||||
{
|
||||
MockedPartitionOpEvaluator eval(p3, factory, transferringOpToStateMap);
|
||||
MockedPartitionOpEvaluator eval(p3, factory, sendingOpToStateMap);
|
||||
eval.apply({PartitionOp::AssignFresh(Element(6)),
|
||||
PartitionOp::AssignFresh(Element(7)),
|
||||
PartitionOp::AssignFresh(Element(0)),
|
||||
@@ -228,12 +228,12 @@ TEST(PartitionUtilsTest, TestMergeAndJoin) {
|
||||
|
||||
auto apply_to_p1_and_p3 = [&](PartitionOp op) {
|
||||
{
|
||||
MockedPartitionOpEvaluator eval(p1, factory, transferringOpToStateMap);
|
||||
MockedPartitionOpEvaluator eval(p1, factory, sendingOpToStateMap);
|
||||
eval.apply(op);
|
||||
}
|
||||
|
||||
{
|
||||
MockedPartitionOpEvaluator eval(p3, factory, transferringOpToStateMap);
|
||||
MockedPartitionOpEvaluator eval(p3, factory, sendingOpToStateMap);
|
||||
eval.apply(op);
|
||||
}
|
||||
expect_join_eq();
|
||||
@@ -241,12 +241,12 @@ TEST(PartitionUtilsTest, TestMergeAndJoin) {
|
||||
|
||||
auto apply_to_p2_and_p3 = [&](PartitionOp op) {
|
||||
{
|
||||
MockedPartitionOpEvaluator eval(p2, factory, transferringOpToStateMap);
|
||||
MockedPartitionOpEvaluator eval(p2, factory, sendingOpToStateMap);
|
||||
eval.apply(op);
|
||||
}
|
||||
|
||||
{
|
||||
MockedPartitionOpEvaluator eval(p3, factory, transferringOpToStateMap);
|
||||
MockedPartitionOpEvaluator eval(p3, factory, sendingOpToStateMap);
|
||||
eval.apply(op);
|
||||
}
|
||||
expect_join_eq();
|
||||
@@ -272,9 +272,9 @@ TEST(PartitionUtilsTest, TestMergeAndJoin) {
|
||||
|
||||
TEST(PartitionUtilsTest, Join1) {
|
||||
llvm::BumpPtrAllocator allocator;
|
||||
Partition::TransferringOperandSetFactory factory(allocator);
|
||||
Partition::SendingOperandSetFactory factory(allocator);
|
||||
IsolationHistory::Factory historyFactory(allocator);
|
||||
TransferringOperandToStateMap transferringOpToStateMap(historyFactory);
|
||||
SendingOperandToStateMap transferringOpToStateMap(historyFactory);
|
||||
|
||||
Element data1[] = {Element(0), Element(1), Element(2),
|
||||
Element(3), Element(4), Element(5)};
|
||||
@@ -315,9 +315,9 @@ TEST(PartitionUtilsTest, Join1) {
|
||||
|
||||
TEST(PartitionUtilsTest, Join2) {
|
||||
llvm::BumpPtrAllocator allocator;
|
||||
Partition::TransferringOperandSetFactory factory(allocator);
|
||||
Partition::SendingOperandSetFactory factory(allocator);
|
||||
IsolationHistory::Factory historyFactory(allocator);
|
||||
TransferringOperandToStateMap transferringOpToStateMap(historyFactory);
|
||||
SendingOperandToStateMap transferringOpToStateMap(historyFactory);
|
||||
|
||||
Element data1[] = {Element(0), Element(1), Element(2),
|
||||
Element(3), Element(4), Element(5)};
|
||||
@@ -364,9 +364,9 @@ TEST(PartitionUtilsTest, Join2) {
|
||||
|
||||
TEST(PartitionUtilsTest, Join2Reversed) {
|
||||
llvm::BumpPtrAllocator allocator;
|
||||
Partition::TransferringOperandSetFactory factory(allocator);
|
||||
Partition::SendingOperandSetFactory factory(allocator);
|
||||
IsolationHistory::Factory historyFactory(allocator);
|
||||
TransferringOperandToStateMap transferringOpToStateMap(historyFactory);
|
||||
SendingOperandToStateMap transferringOpToStateMap(historyFactory);
|
||||
|
||||
Element data1[] = {Element(0), Element(1), Element(2),
|
||||
Element(3), Element(4), Element(5)};
|
||||
@@ -413,9 +413,9 @@ TEST(PartitionUtilsTest, Join2Reversed) {
|
||||
|
||||
TEST(PartitionUtilsTest, JoinLarge) {
|
||||
llvm::BumpPtrAllocator allocator;
|
||||
Partition::TransferringOperandSetFactory factory(allocator);
|
||||
Partition::SendingOperandSetFactory factory(allocator);
|
||||
IsolationHistory::Factory historyFactory(allocator);
|
||||
TransferringOperandToStateMap transferringOpToStateMap(historyFactory);
|
||||
SendingOperandToStateMap transferringOpToStateMap(historyFactory);
|
||||
|
||||
Element data1[] = {
|
||||
Element(0), Element(1), Element(2), Element(3), Element(4),
|
||||
@@ -554,9 +554,9 @@ TEST(PartitionUtilsTest, JoinLarge) {
|
||||
// This test tests the semantics of assignment.
|
||||
TEST(PartitionUtilsTest, TestAssign) {
|
||||
llvm::BumpPtrAllocator allocator;
|
||||
Partition::TransferringOperandSetFactory factory(allocator);
|
||||
Partition::SendingOperandSetFactory factory(allocator);
|
||||
IsolationHistory::Factory historyFactory(allocator);
|
||||
TransferringOperandToStateMap transferringOpToStateMap(historyFactory);
|
||||
SendingOperandToStateMap transferringOpToStateMap(historyFactory);
|
||||
|
||||
Partition p1(historyFactory.get());
|
||||
Partition p2(historyFactory.get());
|
||||
@@ -645,9 +645,9 @@ TEST(PartitionUtilsTest, TestAssign) {
|
||||
// This test tests that consumption consumes entire regions as expected
|
||||
TEST(PartitionUtilsTest, TestConsumeAndRequire) {
|
||||
llvm::BumpPtrAllocator allocator;
|
||||
Partition::TransferringOperandSetFactory factory(allocator);
|
||||
Partition::SendingOperandSetFactory factory(allocator);
|
||||
IsolationHistory::Factory historyFactory(allocator);
|
||||
TransferringOperandToStateMap transferringOpToStateMap(historyFactory);
|
||||
SendingOperandToStateMap transferringOpToStateMap(historyFactory);
|
||||
|
||||
Partition p(historyFactory.get());
|
||||
|
||||
@@ -678,9 +678,9 @@ TEST(PartitionUtilsTest, TestConsumeAndRequire) {
|
||||
// expected: p: ((0 1 2) (3 4 5) (6 7) (8 9) (Element(10))
|
||||
// (Element(11)))
|
||||
|
||||
PartitionOp::Transfer(Element(2), transferSingletons[0]),
|
||||
PartitionOp::Transfer(Element(7), transferSingletons[1]),
|
||||
PartitionOp::Transfer(Element(10), transferSingletons[2])});
|
||||
PartitionOp::Send(Element(2), operandSingletons[0]),
|
||||
PartitionOp::Send(Element(7), operandSingletons[1]),
|
||||
PartitionOp::Send(Element(10), operandSingletons[2])});
|
||||
}
|
||||
|
||||
// expected: p: ({0 1 2 6 7 10} (3 4 5) (8 9) (Element(11)))
|
||||
@@ -744,9 +744,9 @@ TEST(PartitionUtilsTest, TestConsumeAndRequire) {
|
||||
// copies of partitions
|
||||
TEST(PartitionUtilsTest, TestCopyConstructor) {
|
||||
llvm::BumpPtrAllocator allocator;
|
||||
Partition::TransferringOperandSetFactory factory(allocator);
|
||||
Partition::SendingOperandSetFactory factory(allocator);
|
||||
IsolationHistory::Factory historyFactory(allocator);
|
||||
TransferringOperandToStateMap transferringOpToStateMap(historyFactory);
|
||||
SendingOperandToStateMap transferringOpToStateMap(historyFactory);
|
||||
|
||||
Partition p1(historyFactory.get());
|
||||
{
|
||||
@@ -760,7 +760,7 @@ TEST(PartitionUtilsTest, TestCopyConstructor) {
|
||||
// Change p1 again.
|
||||
{
|
||||
MockedPartitionOpEvaluator eval(p1, factory, transferringOpToStateMap);
|
||||
eval.apply(PartitionOp::Transfer(Element(0), transferSingletons[0]));
|
||||
eval.apply(PartitionOp::Send(Element(0), operandSingletons[0]));
|
||||
}
|
||||
|
||||
{
|
||||
@@ -782,9 +782,9 @@ TEST(PartitionUtilsTest, TestCopyConstructor) {
|
||||
|
||||
TEST(PartitionUtilsTest, TestUndoTransfer) {
|
||||
llvm::BumpPtrAllocator allocator;
|
||||
Partition::TransferringOperandSetFactory factory(allocator);
|
||||
Partition::SendingOperandSetFactory factory(allocator);
|
||||
IsolationHistory::Factory historyFactory(allocator);
|
||||
TransferringOperandToStateMap transferringOpToStateMap(historyFactory);
|
||||
SendingOperandToStateMap transferringOpToStateMap(historyFactory);
|
||||
|
||||
Partition p(historyFactory.get());
|
||||
MockedPartitionOpEvaluatorWithFailureCallback eval(
|
||||
@@ -793,16 +793,16 @@ TEST(PartitionUtilsTest, TestUndoTransfer) {
|
||||
|
||||
// Shouldn't error on this.
|
||||
eval.apply({PartitionOp::AssignFresh(Element(0)),
|
||||
PartitionOp::Transfer(Element(0), transferSingletons[0]),
|
||||
PartitionOp::UndoTransfer(Element(0), instSingletons[0]),
|
||||
PartitionOp::Send(Element(0), operandSingletons[0]),
|
||||
PartitionOp::UndoSend(Element(0), instSingletons[0]),
|
||||
PartitionOp::Require(Element(0), instSingletons[0])});
|
||||
}
|
||||
|
||||
TEST(PartitionUtilsTest, TestLastEltInTransferredRegion) {
|
||||
llvm::BumpPtrAllocator allocator;
|
||||
Partition::TransferringOperandSetFactory factory(allocator);
|
||||
Partition::SendingOperandSetFactory factory(allocator);
|
||||
IsolationHistory::Factory historyFactory(allocator);
|
||||
TransferringOperandToStateMap transferringOpToStateMap(historyFactory);
|
||||
SendingOperandToStateMap transferringOpToStateMap(historyFactory);
|
||||
|
||||
// First make sure that we do this correctly with an assign fresh.
|
||||
Partition p(historyFactory.get());
|
||||
@@ -811,10 +811,10 @@ TEST(PartitionUtilsTest, TestLastEltInTransferredRegion) {
|
||||
eval.apply({PartitionOp::AssignFresh(Element(0)),
|
||||
PartitionOp::AssignFresh(Element(1)),
|
||||
PartitionOp::AssignFresh(Element(2)),
|
||||
PartitionOp::Transfer(Element(0), transferSingletons[0]),
|
||||
PartitionOp::Send(Element(0), operandSingletons[0]),
|
||||
PartitionOp::AssignFresh(Element(0))});
|
||||
}
|
||||
p.validateRegionToTransferredOpMapRegions();
|
||||
p.validateRegionToSendingOpMapRegions();
|
||||
|
||||
// Now make sure that we do this correctly with assign.
|
||||
Partition p2(historyFactory.get());
|
||||
@@ -823,18 +823,18 @@ TEST(PartitionUtilsTest, TestLastEltInTransferredRegion) {
|
||||
eval.apply({PartitionOp::AssignFresh(Element(0)),
|
||||
PartitionOp::AssignFresh(Element(1)),
|
||||
PartitionOp::AssignFresh(Element(2)),
|
||||
PartitionOp::Transfer(Element(0), transferSingletons[0]),
|
||||
PartitionOp::Send(Element(0), operandSingletons[0]),
|
||||
PartitionOp::Assign(Element(0), Element(2))});
|
||||
}
|
||||
p2.validateRegionToTransferredOpMapRegions();
|
||||
p2.validateRegionToSendingOpMapRegions();
|
||||
}
|
||||
|
||||
TEST(PartitionUtilsTest, TestHistory_CreateVariable) {
|
||||
llvm::BumpPtrAllocator allocator;
|
||||
Partition::TransferringOperandSetFactory factory(allocator);
|
||||
Partition::SendingOperandSetFactory factory(allocator);
|
||||
IsolationHistory::Factory historyFactory(allocator);
|
||||
SmallVector<IsolationHistory, 8> joinedHistories;
|
||||
TransferringOperandToStateMap transferringOpToStateMap(historyFactory);
|
||||
SendingOperandToStateMap transferringOpToStateMap(historyFactory);
|
||||
|
||||
// First make sure that we do this correctly with an assign fresh.
|
||||
Partition p(historyFactory.get());
|
||||
@@ -859,9 +859,9 @@ TEST(PartitionUtilsTest, TestHistory_CreateVariable) {
|
||||
|
||||
TEST(PartitionUtilsTest, TestHistory_AssignRegion) {
|
||||
llvm::BumpPtrAllocator allocator;
|
||||
Partition::TransferringOperandSetFactory factory(allocator);
|
||||
Partition::SendingOperandSetFactory factory(allocator);
|
||||
IsolationHistory::Factory historyFactory(allocator);
|
||||
TransferringOperandToStateMap transferringOpToStateMap(historyFactory);
|
||||
SendingOperandToStateMap transferringOpToStateMap(historyFactory);
|
||||
SmallVector<IsolationHistory, 8> joinedHistories;
|
||||
|
||||
// First make sure that we do this correctly with an assign fresh.
|
||||
@@ -899,9 +899,9 @@ TEST(PartitionUtilsTest, TestHistory_AssignRegion) {
|
||||
|
||||
TEST(PartitionUtilsTest, TestHistory_BuildNewRegionRepIsMergee) {
|
||||
llvm::BumpPtrAllocator allocator;
|
||||
Partition::TransferringOperandSetFactory factory(allocator);
|
||||
Partition::SendingOperandSetFactory factory(allocator);
|
||||
IsolationHistory::Factory historyFactory(allocator);
|
||||
TransferringOperandToStateMap transferringOpToStateMap(historyFactory);
|
||||
SendingOperandToStateMap transferringOpToStateMap(historyFactory);
|
||||
SmallVector<IsolationHistory, 8> joinedHistories;
|
||||
|
||||
Partition p(historyFactory.get());
|
||||
@@ -947,10 +947,10 @@ TEST(PartitionUtilsTest, TestHistory_BuildNewRegionRepIsMergee) {
|
||||
|
||||
TEST(PartitionUtilsTest, TestHistory_ReturnFalseWhenNoneLeft) {
|
||||
llvm::BumpPtrAllocator allocator;
|
||||
Partition::TransferringOperandSetFactory factory(allocator);
|
||||
Partition::SendingOperandSetFactory factory(allocator);
|
||||
IsolationHistory::Factory historyFactory(allocator);
|
||||
SmallVector<IsolationHistory, 8> joinedHistories;
|
||||
TransferringOperandToStateMap transferringOpToStateMap(historyFactory);
|
||||
SendingOperandToStateMap transferringOpToStateMap(historyFactory);
|
||||
|
||||
Partition p(historyFactory.get());
|
||||
|
||||
@@ -973,7 +973,7 @@ TEST(PartitionUtilsTest, TestHistory_ReturnFalseWhenNoneLeft) {
|
||||
TEST(PartitionUtilsTest, TestHistory_JoiningTwoEmpty) {
|
||||
// Make sure that we do sane things when we join empty history.
|
||||
llvm::BumpPtrAllocator allocator;
|
||||
Partition::TransferringOperandSetFactory factory(allocator);
|
||||
Partition::SendingOperandSetFactory factory(allocator);
|
||||
IsolationHistory::Factory historyFactory(allocator);
|
||||
SmallVector<IsolationHistory, 8> joinedHistories;
|
||||
|
||||
@@ -988,10 +988,10 @@ TEST(PartitionUtilsTest, TestHistory_JoiningTwoEmpty) {
|
||||
TEST(PartitionUtilsTest, TestHistory_JoiningNotEmptyAndEmpty) {
|
||||
// Make sure that we do sane things when we join empty history.
|
||||
llvm::BumpPtrAllocator allocator;
|
||||
Partition::TransferringOperandSetFactory factory(allocator);
|
||||
Partition::SendingOperandSetFactory factory(allocator);
|
||||
IsolationHistory::Factory historyFactory(allocator);
|
||||
SmallVector<IsolationHistory, 8> joinedHistories;
|
||||
TransferringOperandToStateMap transferringOpToStateMap(historyFactory);
|
||||
SendingOperandToStateMap transferringOpToStateMap(historyFactory);
|
||||
|
||||
Partition p1(historyFactory.get());
|
||||
Partition p2(historyFactory.get());
|
||||
@@ -1013,9 +1013,9 @@ TEST(PartitionUtilsTest, TestHistory_JoiningNotEmptyAndEmpty) {
|
||||
TEST(PartitionUtilsTest, TestHistory_JoiningEmptyAndNotEmpty) {
|
||||
// Make sure that we do sane things when we join empty history.
|
||||
llvm::BumpPtrAllocator allocator;
|
||||
Partition::TransferringOperandSetFactory factory(allocator);
|
||||
Partition::SendingOperandSetFactory factory(allocator);
|
||||
IsolationHistory::Factory historyFactory(allocator);
|
||||
TransferringOperandToStateMap transferringOpToStateMap(historyFactory);
|
||||
SendingOperandToStateMap transferringOpToStateMap(historyFactory);
|
||||
SmallVector<IsolationHistory, 8> joinedHistories;
|
||||
|
||||
Partition p1(historyFactory.get());
|
||||
|
||||
Reference in New Issue
Block a user