Skip to content

[move-only] Add copyable_to_moveonlywrapper and moveonlywrapper_to_copyable instructions. #59355

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions docs/SIL.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7443,6 +7443,68 @@ The remaining components identify the SIL differentiability witness:
witness generic parameter clause is combined with the original function's
generic signature to form the full witness generic signature.

Optimizer Dataflow Marker Instructions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

mark_must_check
```````````````
::

sil-instruction ::= 'mark_must_check'
'[' sil-optimizer-analysis-marker ']'

sil-optimizer-analysis-marker ::= 'no_implicit_copy'

A canary value inserted by a SIL generating frontend to signal to the move
checker to check a specific value. Valid only in Raw SIL. The relevant checkers
should remove the `mark_must_check`_ instruction after successfully running the
relevant diagnostic. The idea here is that instead of needing to introduce
multiple "flaging" instructions for the optimizer, we can just reuse this one
instruction by varying the kind.

No Implicit Copy and No Escape Value Instructions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

copyable_to_moveonlywrapper
```````````````````````````
::

sil-instruction ::= 'copyable_to_moveonlywrapper'

`copyably_to_moveonlywrapper`_ takes in a 'T' and maps it to a move only wrapped
'@moveOnly T'. This is semantically used by a code generator initializing a new
moveOnly binding from a copyable value. It semantically destroys its input
@owned value and returns a brand new independent @owned @moveOnly value. It also
is used to convert a trivial copyable value with type 'Trivial' into an owned
non-trivial value of type '@moveOnly Trivial'. If one thinks of '@moveOnly' as a
monad, this is how one injects a copyable value into the move only space.

moveonlywrapper_to_copyable
```````````````````````````
::

sil-instruction ::= 'moveonlywrapper_to_copyable [owned]'
sil-instruction ::= 'moveonlywrapper_to_copyable [guaranteed]'

`moveonlywrapper_to_copyable`_ takes in a '@moveOnly T' and produces a new 'T'
value. This is a 'forwarding' instruction where at parse time, we only allow for
one to choose it to be [owned] or [guaranteed]. With time, we may eliminate the
need for the guaranteed form in the future.

* `moveonlywrapper_to_copyable [owned]` is used to signal the end of lifetime of
the '@moveOnly' wrapper. SILGen inserts these when ever a move only value has
its ownership passed to a situation where a copyable value is needed. Since it
is consuming, we know that the no implicit copy or no-escape checker will ensure
that if we need a copy for it, the program will emit a diagnostic.

* `moveonlywrapper_to_copyable [guaranteed]` is used to pass a @moveOnly T value
as a copyable guaranteed parameter with type 'T' to a function. In the case of
using no-implicit-copy checking this is always fine since no-implicit-copy is a
local pattern. This would be an error when performing no escape
checking. Importantly, this instruction also is where in the case of an
@moveOnly trivial type, we convert from the non-trivial representation to the
trivial representation.

Assertion configuration
~~~~~~~~~~~~~~~~~~~~~~~

Expand Down
4 changes: 4 additions & 0 deletions include/swift/AST/DiagnosticsParse.def
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,10 @@ ERROR(sil_markmustcheck_invalid_attribute,none,
"Attribute '[%0]' can not be applied to mark_value_as_moveonly", (StringRef))
ERROR(sil_markmustcheck_requires_attribute,none,
"mark_must_check requires an attribute like 'noImplicitCopy'", ())
ERROR(sil_moveonlytocopyable_invalid_attribute,none,
"Attribute '[%0]' can not be applied to moveonlywrapper_to_copyable", (StringRef))
ERROR(sil_moveonlytocopyable_requires_attribute,none,
"moveonlywrapper_to_copyable requires either a [guaranteed] or [owned] attribute", ())

// SIL Basic Blocks
ERROR(expected_sil_block_name,none,
Expand Down
20 changes: 20 additions & 0 deletions include/swift/SIL/SILBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "swift/SIL/SILArgument.h"
#include "swift/SIL/SILDebugScope.h"
#include "swift/SIL/SILFunction.h"
#include "swift/SIL/SILInstruction.h"
#include "swift/SIL/SILModule.h"
#include "swift/SIL/SILUndef.h"
#include "llvm/ADT/PointerUnion.h"
Expand Down Expand Up @@ -1289,6 +1290,25 @@ class SILBuilder {
MarkMustCheckInst(getSILDebugLocation(loc), src, kind));
}

CopyableToMoveOnlyWrapperValueInst *
createCopyableToMoveOnlyWrapperValue(SILLocation loc, SILValue src) {
return insert(new (getModule()) CopyableToMoveOnlyWrapperValueInst(
getSILDebugLocation(loc), src));
}

MoveOnlyWrapperToCopyableValueInst *
createOwnedMoveOnlyWrapperToCopyableValue(SILLocation loc, SILValue src) {
return insert(new (getModule()) MoveOnlyWrapperToCopyableValueInst(
*F, getSILDebugLocation(loc), src, OwnershipKind::Owned));
}

MoveOnlyWrapperToCopyableValueInst *
createGuaranteedMoveOnlyWrapperToCopyableValue(SILLocation loc,
SILValue src) {
return insert(new (getModule()) MoveOnlyWrapperToCopyableValueInst(
*F, getSILDebugLocation(loc), src, OwnershipKind::Guaranteed));
}

UnconditionalCheckedCastInst *
createUnconditionalCheckedCast(SILLocation Loc, SILValue op,
SILType destLoweredTy,
Expand Down
26 changes: 26 additions & 0 deletions include/swift/SIL/SILCloner.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "swift/SIL/Dominance.h"
#include "swift/SIL/SILBuilder.h"
#include "swift/SIL/SILDebugScope.h"
#include "swift/SIL/SILInstruction.h"
#include "swift/SIL/SILVisitor.h"

namespace swift {
Expand Down Expand Up @@ -1769,6 +1770,31 @@ void SILCloner<ImplClass>::visitMarkMustCheckInst(MarkMustCheckInst *Inst) {
recordClonedInstruction(Inst, MVI);
}

template <typename ImplClass>
void SILCloner<ImplClass>::visitMoveOnlyWrapperToCopyableValueInst(
MoveOnlyWrapperToCopyableValueInst *inst) {
getBuilder().setCurrentDebugScope(getOpScope(inst->getDebugScope()));
MoveOnlyWrapperToCopyableValueInst *cvt;
if (inst->getOwnershipKind() == OwnershipKind::Owned) {
cvt = getBuilder().createOwnedMoveOnlyWrapperToCopyableValue(
getOpLocation(inst->getLoc()), getOpValue(inst->getOperand()));
} else {
assert(inst->getOwnershipKind() == OwnershipKind::Guaranteed);
cvt = getBuilder().createGuaranteedMoveOnlyWrapperToCopyableValue(
getOpLocation(inst->getLoc()), getOpValue(inst->getOperand()));
}
recordClonedInstruction(inst, cvt);
}

template <typename ImplClass>
void SILCloner<ImplClass>::visitCopyableToMoveOnlyWrapperValueInst(
CopyableToMoveOnlyWrapperValueInst *Inst) {
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
auto *MVI = getBuilder().createCopyableToMoveOnlyWrapperValue(
getOpLocation(Inst->getLoc()), getOpValue(Inst->getOperand()));
recordClonedInstruction(Inst, MVI);
}

template <typename ImplClass>
void SILCloner<ImplClass>::visitReleaseValueInst(ReleaseValueInst *Inst) {
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
Expand Down
60 changes: 59 additions & 1 deletion include/swift/SIL/SILInstruction.h
Original file line number Diff line number Diff line change
Expand Up @@ -1316,6 +1316,7 @@ FirstArgOwnershipForwardingSingleValueInst::classof(SILInstructionKind kind) {
case SILInstructionKind::OpenExistentialRefInst:
case SILInstructionKind::InitExistentialRefInst:
case SILInstructionKind::MarkDependenceInst:
case SILInstructionKind::MoveOnlyWrapperToCopyableValueInst:
return true;
default:
return false;
Expand Down Expand Up @@ -7587,6 +7588,60 @@ class MarkMustCheckInst
bool isNoImplicitCopy() const { return kind == CheckKind::NoImplicitCopy; }
};

class CopyableToMoveOnlyWrapperValueInst
: public UnaryInstructionBase<
SILInstructionKind::CopyableToMoveOnlyWrapperValueInst,
SingleValueInstruction> {
friend class SILBuilder;

CopyableToMoveOnlyWrapperValueInst(SILDebugLocation DebugLoc,
SILValue operand)
: UnaryInstructionBase(DebugLoc, operand,
operand->getType().addingMoveOnlyWrapper()) {}
};

/// Convert from an @moveOnly wrapper type to the underlying copyable type. Can
/// be either owned or guaranteed.
///
/// IMPORTANT: Unlike other forwarding instructions, the ownership of moveonly
/// to copyable is not forwarded from the operand. Instead in SILBuilder one
/// must select the specific type of ownership one wishes by using the following
/// APIs:
///
/// * SILBuilder::createOwnedMoveOnlyWrapperToCopyableValueInst
/// * SILBuilder::createGuaranteedMoveOnlyWrapperToCopyableValueInst
///
/// The reason why this instruction was designed in this manner is that a
/// frontend chooses the ownership form of this instruction based off of the
/// semantic place that the value is used. As an example:
///
/// 1. When calling a function semantically with guaranteed ownership, the
/// frontend would use the "guaranteed variant".
///
/// 2. When returning a value or assigning into another binding, a frontend
/// would want to use the owned variant so that the move only checker will
/// enforce the end of the moved value's lifetime.
///
/// NOTE: With time, we are going to eliminate the guaranteed form of this
/// instruction in favor of a function conversion instruction.
class MoveOnlyWrapperToCopyableValueInst
: public UnaryInstructionBase<
SILInstructionKind::MoveOnlyWrapperToCopyableValueInst,
SingleValueInstruction>,
public OwnershipForwardingMixin {
friend class SILBuilder;

MoveOnlyWrapperToCopyableValueInst(const SILFunction &fn,
SILDebugLocation DebugLoc,
SILValue operand,
OwnershipKind forwardingOwnershipKind)
: UnaryInstructionBase(DebugLoc, operand,
operand->getType().removingMoveOnlyWrapper()),
OwnershipForwardingMixin(
SILInstructionKind::MoveOnlyWrapperToCopyableValueInst,
forwardingOwnershipKind) {}
};

/// Given an object reference, return true iff it is non-nil and refers
/// to a native swift object with strong reference count of 1.
class IsUniqueInst
Expand Down Expand Up @@ -9728,7 +9783,8 @@ inline bool OwnershipForwardingMixin::isa(SILInstructionKind kind) {
OwnershipForwardingConversionInst::classof(kind) ||
OwnershipForwardingSelectEnumInstBase::classof(kind) ||
OwnershipForwardingMultipleValueInstruction::classof(kind) ||
kind == SILInstructionKind::MarkMustCheckInst;
kind == SILInstructionKind::MarkMustCheckInst ||
kind == SILInstructionKind::MoveOnlyWrapperToCopyableValueInst;
}

inline OwnershipForwardingMixin *
Expand All @@ -9752,6 +9808,8 @@ OwnershipForwardingMixin::get(SILInstruction *inst) {
return result;
if (auto *result = dyn_cast<MarkMustCheckInst>(inst))
return result;
if (auto *result = dyn_cast<MoveOnlyWrapperToCopyableValueInst>(inst))
return result;
return nullptr;
}

Expand Down
9 changes: 9 additions & 0 deletions include/swift/SIL/SILNodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,15 @@ ABSTRACT_VALUE_AND_INST(SingleValueInstruction, ValueBase, SILInstruction)
// running the relevant diagnostic.
SINGLE_VALUE_INST(MarkMustCheckInst, mark_must_check,
SingleValueInstruction, None, DoesNotRelease)
// Convert a $T to $@moveOnly T. This is the method that one uses to convert a
// trivial value to a non-trivial move only value.
SINGLE_VALUE_INST(CopyableToMoveOnlyWrapperValueInst, copyable_to_moveonlywrapper,
SingleValueInstruction, None, DoesNotRelease)
// Convert a $@moveOnly T to $T. Ownership is fixed at construction by
// frontend to express specific semantics: guaranteed for function arguments
// and owned for assignment/return values.
SINGLE_VALUE_INST(MoveOnlyWrapperToCopyableValueInst, moveonlywrapper_to_copyable,
SingleValueInstruction, None, DoesNotRelease)
// A move_addr is a Raw SIL only instruction that is equivalent to a copy_addr
// [init]. It is lowered during the diagnostic passes to a copy_addr [init] if
// the move checker found uses that prevented us from converting this to a
Expand Down
6 changes: 4 additions & 2 deletions include/swift/SIL/SILType.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ enum class SILValueCategory : uint8_t {
};

class SILPrinter;
class SILParser;

/// SILType - A Swift type that has been lowered to a SIL representation type.
/// In addition to the Swift type system, SIL adds "address" types that can
Expand Down Expand Up @@ -116,6 +117,7 @@ class SILType {
friend class Lowering::TypeConverter;
friend struct llvm::DenseMapInfo<SILType>;
friend class SILPrinter;
friend class SILParser;

public:
SILType() = default;
Expand Down Expand Up @@ -160,13 +162,13 @@ class SILType {
/// Returns the address variant of this type. Instructions which
/// manipulate memory will generally work with object addresses.
SILType getAddressType() const {
return SILType(getASTType(), SILValueCategory::Address);
return SILType(getRawASTType(), SILValueCategory::Address);
}

/// Returns the object variant of this type. Note that address-only
/// types are not legal to manipulate directly as objects in SIL.
SILType getObjectType() const {
return SILType(getASTType(), SILValueCategory::Object);
return SILType(getRawASTType(), SILValueCategory::Object);
}

/// Returns the canonical AST type referenced by this SIL type.
Expand Down
13 changes: 12 additions & 1 deletion lib/IRGen/IRGenSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,14 @@
#include "swift/Basic/STLExtras.h"
#include "swift/IRGen/Linking.h"
#include "swift/SIL/ApplySite.h"
#include "swift/SIL/BasicBlockDatastructures.h"
#include "swift/SIL/Dominance.h"
#include "swift/SIL/InstructionUtils.h"
#include "swift/SIL/MemAccessUtils.h"
#include "swift/SIL/PrettyStackTrace.h"
#include "swift/SIL/BasicBlockDatastructures.h"
#include "swift/SIL/SILDebugScope.h"
#include "swift/SIL/SILDeclRef.h"
#include "swift/SIL/SILInstruction.h"
#include "swift/SIL/SILLinkage.h"
#include "swift/SIL/SILModule.h"
#include "swift/SIL/SILType.h"
Expand Down Expand Up @@ -1190,6 +1191,16 @@ class IRGenSILFunction :
void visitMarkMustCheckInst(MarkMustCheckInst *i) {
llvm_unreachable("Invalid in Lowered SIL");
}
void visitCopyableToMoveOnlyWrapperValueInst(
CopyableToMoveOnlyWrapperValueInst *i) {
auto e = getLoweredExplosion(i->getOperand());
setLoweredExplosion(i, e);
}
void visitMoveOnlyWrapperToCopyableValueInst(
MoveOnlyWrapperToCopyableValueInst *i) {
auto e = getLoweredExplosion(i->getOperand());
setLoweredExplosion(i, e);
}
void visitReleaseValueInst(ReleaseValueInst *i);
void visitReleaseValueAddrInst(ReleaseValueAddrInst *i);
void visitDestroyValueInst(DestroyValueInst *i);
Expand Down
2 changes: 2 additions & 0 deletions lib/SIL/IR/OperandOwnership.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ OPERAND_OWNERSHIP(DestroyingConsume, EndCOWMutation)

// TODO: Should this be a forwarding consume.
OPERAND_OWNERSHIP(DestroyingConsume, MoveValue)
OPERAND_OWNERSHIP(DestroyingConsume, CopyableToMoveOnlyWrapperValue)

// Instructions that move an owned value.
OPERAND_OWNERSHIP(ForwardingConsume, InitExistentialValue)
Expand Down Expand Up @@ -328,6 +329,7 @@ FORWARDING_OWNERSHIP(InitExistentialRef)
FORWARDING_OWNERSHIP(DifferentiableFunction)
FORWARDING_OWNERSHIP(LinearFunction)
FORWARDING_OWNERSHIP(MarkMustCheck)
FORWARDING_OWNERSHIP(MoveOnlyWrapperToCopyableValue)
#undef FORWARDING_OWNERSHIP

// Arbitrary value casts are forwarding instructions that are also allowed to
Expand Down
25 changes: 24 additions & 1 deletion lib/SIL/IR/SILPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,16 @@
#include "swift/AST/ProtocolConformance.h"
#include "swift/AST/Types.h"
#include "swift/Basic/QuotedString.h"
#include "swift/Basic/SourceManager.h"
#include "swift/Basic/STLExtras.h"
#include "swift/Basic/SourceManager.h"
#include "swift/Demangling/Demangle.h"
#include "swift/SIL/ApplySite.h"
#include "swift/SIL/CFG.h"
#include "swift/SIL/SILCoverageMap.h"
#include "swift/SIL/SILDebugScope.h"
#include "swift/SIL/SILDeclRef.h"
#include "swift/SIL/SILFunction.h"
#include "swift/SIL/SILInstruction.h"
#include "swift/SIL/SILModule.h"
#include "swift/SIL/SILPrintContext.h"
#include "swift/SIL/SILVTable.h"
Expand Down Expand Up @@ -1913,6 +1914,28 @@ class SILPrinter : public SILInstructionVisitor<SILPrinter> {
*this << getIDAndType(I->getOperand());
}

void visitCopyableToMoveOnlyWrapperValueInst(
CopyableToMoveOnlyWrapperValueInst *I) {
*this << getIDAndType(I->getOperand());
}

void visitMoveOnlyWrapperToCopyableValueInst(
MoveOnlyWrapperToCopyableValueInst *I) {
switch (I->getForwardingOwnershipKind()) {
case OwnershipKind::None:
case OwnershipKind::Any:
case OwnershipKind::Unowned:
llvm_unreachable("Move only values are always non-trivial");
case OwnershipKind::Owned:
*this << "[owned] ";
break;
case OwnershipKind::Guaranteed:
*this << "[guaranteed] ";
break;
}
*this << getIDAndType(I->getOperand());
}

#define UNCHECKED_REF_STORAGE(Name, ...) \
void visitStrongCopy##Name##ValueInst(StrongCopy##Name##ValueInst *I) { \
*this << getIDAndType(I->getOperand()); \
Expand Down
Loading