Rename transfer -> send.

Accomplished using clangd's rename functionality.
This commit is contained in:
Michael Gottesman
2024-11-02 19:26:59 -07:00
parent 0a56827073
commit 32b4de60a9
17 changed files with 642 additions and 691 deletions

View File

@@ -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", ())
//===

View File

@@ -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);

View File

@@ -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")

View File

@@ -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.

View File

@@ -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) {}
};

View File

@@ -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,

View File

@@ -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

View File

@@ -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

View File

@@ -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) {

View File

@@ -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

View File

@@ -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!
//

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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());