Skip to content

[sil] Add a move_value instruction. #39267

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
28 changes: 28 additions & 0 deletions docs/SIL.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5245,6 +5245,34 @@ independent of the operand. In terms of specific types:
In ownership qualified functions, a ``copy_value`` produces a +1 value that must
be consumed at most once along any path through the program.

move_value
``````````

::

sil-instruction ::= 'move_value' sil-operand

%1 = move_value %0 : $@_moveOnly A

Performs a move of the operand, ending its lifetime. When ownership is enabled,
it always takes in an `@owned T` and produces a new `@owned @_moveOnly T`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gottesmm Both the operand and result of move_value may be either moveOnly or copyable.
Whether either value is moveOnly depends on its type. The important thing about this operation is that it can do a type conversion in either direction between 'moveOnly T' and 'T'. It can also simply perform a move without doing any type conversion.

For the purpose of SIL ownership, it simply forwards an owned value. It has no special properties.


1. For trivial types, this is equivalent to just propagating through the trivial
value.
2. For reference types, this is equivalent to ending the lifetime of the
operand, beginning a new lifetime for the result and setting the result to
the value of the operand.
3. For aggregates, the operation is equivalent to performing a move_value on
each of its fields recursively.

After ownership is lowered, we leave in the move_value to provide a place for
IRGenSIL to know to store a potentially new variable (in case the move was
associated with a let binding).

NOTE: This instruction is used in an experimental feature called 'move only
values'. A move_value instruction is an instruction that introduces (or injects)
a type `T` into the move only value space.

release_value
`````````````

Expand Down
19 changes: 19 additions & 0 deletions include/swift/SIL/SILBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -1232,6 +1232,14 @@ class SILBuilder {
operand, poisonRefs));
}

MoveValueInst *createMoveValue(SILLocation loc, SILValue operand) {
assert(!operand->getType().isTrivial(getFunction()) &&
"Should not be passing trivial values to this api. Use instead "
"emitMoveValueOperation");
return insert(new (getModule())
MoveValueInst(getSILDebugLocation(loc), operand));
}

UnconditionalCheckedCastInst *
createUnconditionalCheckedCast(SILLocation Loc, SILValue op,
SILType destLoweredTy,
Expand Down Expand Up @@ -2466,6 +2474,17 @@ class SILBuilder {
createDestroyAddr(loc, v);
}

/// Convenience function that is a no-op for trivial values and inserts a
/// move_value on non-trivial instructions.
SILValue emitMoveValueOperation(SILLocation Loc, SILValue v) {
assert(!v->getType().isAddress());
if (v->getType().isTrivial(*getInsertionBB()->getParent()))
return v;
assert(v.getOwnershipKind() == OwnershipKind::Owned &&
"move_value consumes its argument");
return createMoveValue(Loc, v);
}

SILValue emitTupleExtract(SILLocation Loc, SILValue Operand, unsigned FieldNo,
SILType ResultTy) {
// Fold tuple_extract(tuple(x,y,z),2)
Expand Down
8 changes: 8 additions & 0 deletions include/swift/SIL/SILCloner.h
Original file line number Diff line number Diff line change
Expand Up @@ -1717,6 +1717,14 @@ void SILCloner<ImplClass>::visitCopyValueInst(CopyValueInst *Inst) {
getOpValue(Inst->getOperand())));
}

template <typename ImplClass>
void SILCloner<ImplClass>::visitMoveValueInst(MoveValueInst *Inst) {
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
recordClonedInstruction(
Inst, getBuilder().createMoveValue(getOpLocation(Inst->getLoc()),
getOpValue(Inst->getOperand())));
}

template <typename ImplClass>
void SILCloner<ImplClass>::visitReleaseValueInst(ReleaseValueInst *Inst) {
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
Expand Down
9 changes: 9 additions & 0 deletions include/swift/SIL/SILInstruction.h
Original file line number Diff line number Diff line change
Expand Up @@ -7321,6 +7321,15 @@ class DestroyValueInst
}
};

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

MoveValueInst(SILDebugLocation DebugLoc, SILValue operand)
: UnaryInstructionBase(DebugLoc, operand, operand->getType()) {}
};

/// 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
4 changes: 4 additions & 0 deletions include/swift/SIL/SILNodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,10 @@ ABSTRACT_VALUE_AND_INST(SingleValueInstruction, ValueBase, SILInstruction)
#include "swift/AST/ReferenceStorage.def"
SINGLE_VALUE_INST(UncheckedOwnershipConversionInst, unchecked_ownership_conversion,
SingleValueInstruction, None, MayRelease)
// A move_value is an OSSA only instruction. Its result does not have any side
// effects relative to other OSSA values like copy_value.
SINGLE_VALUE_INST(MoveValueInst, move_value,
SingleValueInstruction, None, DoesNotRelease)

// IsUnique does not actually write to memory but should be modeled
// as such. Its operand is a pointer to an object reference. The
Expand Down
4 changes: 4 additions & 0 deletions lib/IRGen/IRGenSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1149,6 +1149,10 @@ class IRGenSILFunction :
void visitRetainValueInst(RetainValueInst *i);
void visitRetainValueAddrInst(RetainValueAddrInst *i);
void visitCopyValueInst(CopyValueInst *i);
void visitMoveValueInst(MoveValueInst *i) {
auto e = getLoweredExplosion(i->getOperand());
setLoweredExplosion(i, e);
}
void visitReleaseValueInst(ReleaseValueInst *i);
void visitReleaseValueAddrInst(ReleaseValueAddrInst *i);
void visitDestroyValueInst(DestroyValueInst *i);
Expand Down
3 changes: 3 additions & 0 deletions lib/SIL/IR/OperandOwnership.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,9 @@ OPERAND_OWNERSHIP(DestroyingConsume, EndLifetime)
OPERAND_OWNERSHIP(DestroyingConsume, BeginCOWMutation)
OPERAND_OWNERSHIP(DestroyingConsume, EndCOWMutation)

// TODO: Should this be a forwarding consume.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it should be ForwardingConsume, so why isn't it!

OPERAND_OWNERSHIP(DestroyingConsume, MoveValue)

// Instructions that move an owned value.
OPERAND_OWNERSHIP(ForwardingConsume, CheckedCastValueBranch)
OPERAND_OWNERSHIP(ForwardingConsume, UnconditionalCheckedCastValue)
Expand Down
4 changes: 4 additions & 0 deletions lib/SIL/IR/SILPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1826,6 +1826,10 @@ class SILPrinter : public SILInstructionVisitor<SILPrinter> {
*this << getIDAndType(I->getOperand());
}

void visitMoveValueInst(MoveValueInst *I) {
*this << getIDAndType(I->getOperand());
}

#define UNCHECKED_REF_STORAGE(Name, ...) \
void visitStrongCopy##Name##ValueInst(StrongCopy##Name##ValueInst *I) { \
*this << getIDAndType(I->getOperand()); \
Expand Down
1 change: 1 addition & 0 deletions lib/SIL/IR/ValueOwnership.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ CONSTANT_OWNERSHIP_INST(None, AllocValueBuffer)
CONSTANT_OWNERSHIP_INST(Owned, CopyBlock)
CONSTANT_OWNERSHIP_INST(Owned, CopyBlockWithoutEscaping)
CONSTANT_OWNERSHIP_INST(Owned, CopyValue)
CONSTANT_OWNERSHIP_INST(Owned, MoveValue)
CONSTANT_OWNERSHIP_INST(Owned, EndCOWMutation)
CONSTANT_OWNERSHIP_INST(Owned, KeyPath)
CONSTANT_OWNERSHIP_INST(Owned, InitExistentialValue)
Expand Down
1 change: 1 addition & 0 deletions lib/SIL/Parser/ParseSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3113,6 +3113,7 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B,
UNARY_INSTRUCTION(IsUnique)
UNARY_INSTRUCTION(DestroyAddr)
UNARY_INSTRUCTION(CopyValue)
UNARY_INSTRUCTION(MoveValue)
UNARY_INSTRUCTION(EndBorrow)
UNARY_INSTRUCTION(DestructureStruct)
UNARY_INSTRUCTION(DestructureTuple)
Expand Down
7 changes: 7 additions & 0 deletions lib/SIL/Verifier/SILVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5271,6 +5271,13 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
"convention");
}

void checkMoveValueInst(MoveValueInst *mvi) {
require(mvi->getOperand()->getType().isObject(),
"Operand value should be an object");
require(mvi->getType() == mvi->getOperand()->getType(),
"Result and operand must have the same type, today.");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clearly this type restriction needs to be relaxed once you have moveOnly SIL types

}

void verifyEpilogBlocks(SILFunction *F) {
bool FoundReturnBlock = false;
bool FoundThrowBlock = false;
Expand Down
1 change: 1 addition & 0 deletions lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ static bool hasOpaqueArchetype(TypeExpansionContext context,
case SILInstructionKind::CopyBlockInst:
case SILInstructionKind::CopyBlockWithoutEscapingInst:
case SILInstructionKind::CopyValueInst:
case SILInstructionKind::MoveValueInst:
#define UNCHECKED_REF_STORAGE(Name, ...) \
case SILInstructionKind::StrongCopy##Name##ValueInst:
#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
Expand Down
1 change: 1 addition & 0 deletions lib/SILOptimizer/Utils/SILInliner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -687,6 +687,7 @@ InlineCost swift::instructionInlineCost(SILInstruction &I) {
case SILInstructionKind::EndLifetimeInst:
case SILInstructionKind::UncheckedOwnershipConversionInst:
case SILInstructionKind::BindMemoryInst:
case SILInstructionKind::MoveValueInst:
return InlineCost::Free;

// Typed GEPs are free.
Expand Down
1 change: 1 addition & 0 deletions lib/Serialization/DeserializeSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1846,6 +1846,7 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn,
REFCOUNTING_INSTRUCTION(RetainValueAddr)
REFCOUNTING_INSTRUCTION(UnmanagedRetainValue)
UNARY_INSTRUCTION(CopyValue)
UNARY_INSTRUCTION(MoveValue)
REFCOUNTING_INSTRUCTION(ReleaseValue)
REFCOUNTING_INSTRUCTION(ReleaseValueAddr)
REFCOUNTING_INSTRUCTION(UnmanagedReleaseValue)
Expand Down
2 changes: 1 addition & 1 deletion lib/Serialization/ModuleFormat.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
/// describe what change you made. The content of this comment isn't important;
/// it just ensures a conflict if two people change the module format.
/// Don't worry about adhering to the 80-column limit for this line.
const uint16_t SWIFTMODULE_VERSION_MINOR = 626; // switch & checkcast ownership
const uint16_t SWIFTMODULE_VERSION_MINOR = 627; // move_value inst

/// A standard hash seed used for all string hashes in a serialized module.
///
Expand Down
1 change: 1 addition & 0 deletions lib/Serialization/SerializeSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1388,6 +1388,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) {
case SILInstructionKind::UnmanagedRetainValueInst:
case SILInstructionKind::EndBorrowInst:
case SILInstructionKind::CopyValueInst:
case SILInstructionKind::MoveValueInst:
case SILInstructionKind::DestroyValueInst:
case SILInstructionKind::ReleaseValueInst:
case SILInstructionKind::ReleaseValueAddrInst:
Expand Down
17 changes: 17 additions & 0 deletions test/IRGen/move_value.sil
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// RUN: %swift -disable-legacy-type-info -target x86_64-apple-macosx10.9 -module-name main %s -emit-ir -o - | %FileCheck %s

// REQUIRES: CODEGENERATOR=X86

sil_stage canonical

import Builtin

// CHECK-LABEL: define swiftcc %swift.refcounted* @move_value_test(
// CHECK-NEXT: entry:
// CHECK-NEXT: ret
// CHECK-NEXT: }
sil @move_value_test : $@convention(thin) (@owned Builtin.NativeObject) -> @owned Builtin.NativeObject {
bb0(%0 : $Builtin.NativeObject):
%1 = move_value %0 : $Builtin.NativeObject
return %1 : $Builtin.NativeObject
}
22 changes: 22 additions & 0 deletions test/SIL/Parser/basic2.sil
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,25 @@ bb0(%0 : @owned $Builtin.NativeObject):
%2 = tuple ()
return %2 : $()
}

// CHECK-LABEL: sil [ossa] @test_movevalue_parsing : $@convention(thin) (@owned Builtin.NativeObject) -> @owned Builtin.NativeObject {
// CHECK: bb0(%0 :
// CHECK-NEXT: %1 = move_value %0 : $Builtin.NativeObject
// CHECK-NEXT: return
// CHECK-NEXT: } // end sil function 'test_movevalue_parsing'
sil [ossa] @test_movevalue_parsing : $@convention(thin) (@owned Builtin.NativeObject) -> @owned Builtin.NativeObject {
bb0(%0 : @owned $Builtin.NativeObject):
%1 = move_value %0 : $Builtin.NativeObject
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: %1 should be copyable since there's no moveOnly on its SIL type.

return %1 : $Builtin.NativeObject
}

// CHECK-LABEL: sil @test_movevalue_parsing_non_ossa : $@convention(thin) (@owned Builtin.NativeObject) -> @owned Builtin.NativeObject {
// CHECK: bb0(%0 :
// CHECK-NEXT: %1 = move_value %0 : $Builtin.NativeObject
// CHECK-NEXT: return
// CHECK-NEXT: } // end sil function 'test_movevalue_parsing_non_ossa'
sil @test_movevalue_parsing_non_ossa : $@convention(thin) (@owned Builtin.NativeObject) -> @owned Builtin.NativeObject {
bb0(%0 : $Builtin.NativeObject):
%1 = move_value %0 : $Builtin.NativeObject
return %1 : $Builtin.NativeObject
}
22 changes: 22 additions & 0 deletions test/SIL/Serialization/basic.sil
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,17 @@ bb0(%0 : @owned $Builtin.NativeObject):
return undef : $()
}

// CHECK-LABEL: sil @test_movevalue_parsing_non_ossa : $@convention(thin) (@owned Builtin.NativeObject) -> @owned Builtin.NativeObject {
// CHECK: bb0(%0 :
// CHECK-NEXT: %1 = move_value %0 : $Builtin.NativeObject
// CHECK-NEXT: return
// CHECK-NEXT: } // end sil function 'test_movevalue_parsing_non_ossa'
sil @test_movevalue_parsing_non_ossa : $@convention(thin) (@owned Builtin.NativeObject) -> @owned Builtin.NativeObject {
bb0(%0 : $Builtin.NativeObject):
%1 = move_value %0 : $Builtin.NativeObject
return %1 : $Builtin.NativeObject
}

class TestArrayStorage {
@_hasStorage var count: Int32
init()
Expand Down Expand Up @@ -59,6 +70,17 @@ bb0(%0 : @owned $(Builtin.NativeObject, Builtin.Int32), %1 : @owned $TestArray2)
return %7 : $(Builtin.NativeObject, Builtin.Int32, TestArrayStorage, Int32, TestArrayStorage)
}

// CHECK-LABEL: sil [ossa] @test_movevalue_parsing : $@convention(thin) (@owned Builtin.NativeObject) -> @owned Builtin.NativeObject {
// CHECK: bb0(%0 :
// CHECK-NEXT: %1 = move_value %0 : $Builtin.NativeObject
// CHECK-NEXT: return
// CHECK-NEXT: } // end sil function 'test_movevalue_parsing'
sil [ossa] @test_movevalue_parsing : $@convention(thin) (@owned Builtin.NativeObject) -> @owned Builtin.NativeObject {
bb0(%0 : @owned $Builtin.NativeObject):
%1 = move_value %0 : $Builtin.NativeObject
return %1 : $Builtin.NativeObject
}

// CHECK-LABEL: sil [serialized] [ossa] @test_subst_function_type_generic_context : $@convention(thin) <X, Y> (@guaranteed @callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in τ_0_0, @in τ_0_1) -> () for <X, Y>) -> ()
sil [serialized] [ossa] @test_subst_function_type_generic_context : $@convention(thin) <X, Y> (@guaranteed @callee_guaranteed @substituted <A, B> (@in A, @in B) -> () for <X, Y>) -> () {
entry(%0 : @guaranteed $@callee_guaranteed @substituted <C, D> (@in C, @in D) -> () for <X, Y>):
Expand Down
2 changes: 1 addition & 1 deletion utils/sil-mode.el
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@
"unmanaged_retain_value" "unmanaged_release_value"
"unmanaged_autorelease_value"
"strong_copy_unowned_value" "strong_copy_unmanaged_value"
"destructure_struct" "destructure_tuple")
"destructure_struct" "destructure_tuple" "move_value")
'words) . font-lock-keyword-face)
;; Enums. *NOTE* We do not include enum itself here since enum is a
;; swift declaration as well handled at the top.
Expand Down