Skip to content

Commit 5590c7b

Browse files
committed
[sil] Add a move_value instruction.
This is a new instruction that can be used by SILGen to perform a semantic move in between two entities that are considered separate variables at the AST level. I am going to use it to implement an experimental borrow checker. This PR contains the following: 1. I define move_value, setup parsing, printing, serializing, deserializing, cloning, and filled in all of the visitors as appropriate. 2. I added createMoveValue and emitMoveValueOperation SILBuilder APIs. createMoveValue always creates a move and asserts is passed a trivial type. emitMoveValueOperation in contrast, will short circuit if passed a trivial value and just return the trivial value. 3. I added IRGen tests to show that we can push this through the entire system. This is all just scaffolding for the instruction to live in SIL land and as of this PR doesn't actually do anything.
1 parent 03e32c4 commit 5590c7b

File tree

20 files changed

+155
-2
lines changed

20 files changed

+155
-2
lines changed

docs/SIL.rst

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5245,6 +5245,34 @@ independent of the operand. In terms of specific types:
52455245
In ownership qualified functions, a ``copy_value`` produces a +1 value that must
52465246
be consumed at most once along any path through the program.
52475247

5248+
move_value
5249+
``````````
5250+
5251+
::
5252+
5253+
sil-instruction ::= 'move_value' sil-operand
5254+
5255+
%1 = move_value %0 : $@_moveOnly A
5256+
5257+
Performs a move of the operand, ending its lifetime. When ownership is enabled,
5258+
it always takes in an `@owned T` and produces a new `@owned @_moveOnly T`.
5259+
5260+
1. For trivial types, this is equivalent to just propagating through the trivial
5261+
value.
5262+
2. For reference types, this is equivalent to ending the lifetime of the
5263+
operand, beginning a new lifetime for the result and setting the result to
5264+
the value of the operand.
5265+
3. For aggregates, the operation is equivalent to performing a move_value on
5266+
each of its fields recursively.
5267+
5268+
After ownership is lowered, we leave in the move_value to provide a place for
5269+
IRGenSIL to know to store a potentially new variable (in case the move was
5270+
associated with a let binding).
5271+
5272+
NOTE: This instruction is used in an experimental feature called 'move only
5273+
values'. A move_value instruction is an instruction that introduces (or injects)
5274+
a type `T` into the move only value space.
5275+
52485276
release_value
52495277
`````````````
52505278

include/swift/SIL/SILBuilder.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1232,6 +1232,14 @@ class SILBuilder {
12321232
operand, poisonRefs));
12331233
}
12341234

1235+
MoveValueInst *createMoveValue(SILLocation loc, SILValue operand) {
1236+
assert(!operand->getType().isTrivial(getFunction()) &&
1237+
"Should not be passing trivial values to this api. Use instead "
1238+
"emitMoveValueOperation");
1239+
return insert(new (getModule())
1240+
MoveValueInst(getSILDebugLocation(loc), operand));
1241+
}
1242+
12351243
UnconditionalCheckedCastInst *
12361244
createUnconditionalCheckedCast(SILLocation Loc, SILValue op,
12371245
SILType destLoweredTy,
@@ -2466,6 +2474,17 @@ class SILBuilder {
24662474
createDestroyAddr(loc, v);
24672475
}
24682476

2477+
/// Convenience function that is a no-op for trivial values and inserts a
2478+
/// move_value on non-trivial instructions.
2479+
SILValue emitMoveValueOperation(SILLocation Loc, SILValue v) {
2480+
assert(!v->getType().isAddress());
2481+
if (v->getType().isTrivial(*getInsertionBB()->getParent()))
2482+
return v;
2483+
assert(v.getOwnershipKind() == OwnershipKind::Owned &&
2484+
"move_value consumes its argument");
2485+
return createMoveValue(Loc, v);
2486+
}
2487+
24692488
SILValue emitTupleExtract(SILLocation Loc, SILValue Operand, unsigned FieldNo,
24702489
SILType ResultTy) {
24712490
// Fold tuple_extract(tuple(x,y,z),2)

include/swift/SIL/SILCloner.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1717,6 +1717,14 @@ void SILCloner<ImplClass>::visitCopyValueInst(CopyValueInst *Inst) {
17171717
getOpValue(Inst->getOperand())));
17181718
}
17191719

1720+
template <typename ImplClass>
1721+
void SILCloner<ImplClass>::visitMoveValueInst(MoveValueInst *Inst) {
1722+
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
1723+
recordClonedInstruction(
1724+
Inst, getBuilder().createMoveValue(getOpLocation(Inst->getLoc()),
1725+
getOpValue(Inst->getOperand())));
1726+
}
1727+
17201728
template <typename ImplClass>
17211729
void SILCloner<ImplClass>::visitReleaseValueInst(ReleaseValueInst *Inst) {
17221730
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));

include/swift/SIL/SILInstruction.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7321,6 +7321,15 @@ class DestroyValueInst
73217321
}
73227322
};
73237323

7324+
class MoveValueInst
7325+
: public UnaryInstructionBase<SILInstructionKind::MoveValueInst,
7326+
SingleValueInstruction> {
7327+
friend class SILBuilder;
7328+
7329+
MoveValueInst(SILDebugLocation DebugLoc, SILValue operand)
7330+
: UnaryInstructionBase(DebugLoc, operand, operand->getType()) {}
7331+
};
7332+
73247333
/// Given an object reference, return true iff it is non-nil and refers
73257334
/// to a native swift object with strong reference count of 1.
73267335
class IsUniqueInst

include/swift/SIL/SILNodes.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,10 @@ ABSTRACT_VALUE_AND_INST(SingleValueInstruction, ValueBase, SILInstruction)
587587
#include "swift/AST/ReferenceStorage.def"
588588
SINGLE_VALUE_INST(UncheckedOwnershipConversionInst, unchecked_ownership_conversion,
589589
SingleValueInstruction, None, MayRelease)
590+
// A move_value is an OSSA only instruction. Its result does not have any side
591+
// effects relative to other OSSA values like copy_value.
592+
SINGLE_VALUE_INST(MoveValueInst, move_value,
593+
SingleValueInstruction, None, DoesNotRelease)
590594

591595
// IsUnique does not actually write to memory but should be modeled
592596
// as such. Its operand is a pointer to an object reference. The

lib/IRGen/IRGenSIL.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1149,6 +1149,10 @@ class IRGenSILFunction :
11491149
void visitRetainValueInst(RetainValueInst *i);
11501150
void visitRetainValueAddrInst(RetainValueAddrInst *i);
11511151
void visitCopyValueInst(CopyValueInst *i);
1152+
void visitMoveValueInst(MoveValueInst *i) {
1153+
auto e = getLoweredExplosion(i->getOperand());
1154+
setLoweredExplosion(i, e);
1155+
}
11521156
void visitReleaseValueInst(ReleaseValueInst *i);
11531157
void visitReleaseValueAddrInst(ReleaseValueAddrInst *i);
11541158
void visitDestroyValueInst(DestroyValueInst *i);

lib/SIL/IR/OperandOwnership.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,9 @@ OPERAND_OWNERSHIP(DestroyingConsume, EndLifetime)
249249
OPERAND_OWNERSHIP(DestroyingConsume, BeginCOWMutation)
250250
OPERAND_OWNERSHIP(DestroyingConsume, EndCOWMutation)
251251

252+
// TODO: Should this be a forwarding consume.
253+
OPERAND_OWNERSHIP(DestroyingConsume, MoveValue)
254+
252255
// Instructions that move an owned value.
253256
OPERAND_OWNERSHIP(ForwardingConsume, CheckedCastValueBranch)
254257
OPERAND_OWNERSHIP(ForwardingConsume, UnconditionalCheckedCastValue)

lib/SIL/IR/SILPrinter.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1826,6 +1826,10 @@ class SILPrinter : public SILInstructionVisitor<SILPrinter> {
18261826
*this << getIDAndType(I->getOperand());
18271827
}
18281828

1829+
void visitMoveValueInst(MoveValueInst *I) {
1830+
*this << getIDAndType(I->getOperand());
1831+
}
1832+
18291833
#define UNCHECKED_REF_STORAGE(Name, ...) \
18301834
void visitStrongCopy##Name##ValueInst(StrongCopy##Name##ValueInst *I) { \
18311835
*this << getIDAndType(I->getOperand()); \

lib/SIL/IR/ValueOwnership.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ CONSTANT_OWNERSHIP_INST(None, AllocValueBuffer)
7474
CONSTANT_OWNERSHIP_INST(Owned, CopyBlock)
7575
CONSTANT_OWNERSHIP_INST(Owned, CopyBlockWithoutEscaping)
7676
CONSTANT_OWNERSHIP_INST(Owned, CopyValue)
77+
CONSTANT_OWNERSHIP_INST(Owned, MoveValue)
7778
CONSTANT_OWNERSHIP_INST(Owned, EndCOWMutation)
7879
CONSTANT_OWNERSHIP_INST(Owned, KeyPath)
7980
CONSTANT_OWNERSHIP_INST(Owned, InitExistentialValue)

lib/SIL/Parser/ParseSIL.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3113,6 +3113,7 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B,
31133113
UNARY_INSTRUCTION(IsUnique)
31143114
UNARY_INSTRUCTION(DestroyAddr)
31153115
UNARY_INSTRUCTION(CopyValue)
3116+
UNARY_INSTRUCTION(MoveValue)
31163117
UNARY_INSTRUCTION(EndBorrow)
31173118
UNARY_INSTRUCTION(DestructureStruct)
31183119
UNARY_INSTRUCTION(DestructureTuple)

lib/SIL/Verifier/SILVerifier.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5271,6 +5271,13 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
52715271
"convention");
52725272
}
52735273

5274+
void checkMoveValueInst(MoveValueInst *mvi) {
5275+
require(mvi->getOperand()->getType().isObject(),
5276+
"Operand value should be an object");
5277+
require(mvi->getType() == mvi->getOperand()->getType(),
5278+
"Result and operand must have the same type, today.");
5279+
}
5280+
52745281
void verifyEpilogBlocks(SILFunction *F) {
52755282
bool FoundReturnBlock = false;
52765283
bool FoundThrowBlock = false;

lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ static bool hasOpaqueArchetype(TypeExpansionContext context,
204204
case SILInstructionKind::CopyBlockInst:
205205
case SILInstructionKind::CopyBlockWithoutEscapingInst:
206206
case SILInstructionKind::CopyValueInst:
207+
case SILInstructionKind::MoveValueInst:
207208
#define UNCHECKED_REF_STORAGE(Name, ...) \
208209
case SILInstructionKind::StrongCopy##Name##ValueInst:
209210
#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \

lib/SILOptimizer/Utils/SILInliner.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -687,6 +687,7 @@ InlineCost swift::instructionInlineCost(SILInstruction &I) {
687687
case SILInstructionKind::EndLifetimeInst:
688688
case SILInstructionKind::UncheckedOwnershipConversionInst:
689689
case SILInstructionKind::BindMemoryInst:
690+
case SILInstructionKind::MoveValueInst:
690691
return InlineCost::Free;
691692

692693
// Typed GEPs are free.

lib/Serialization/DeserializeSIL.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1846,6 +1846,7 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn,
18461846
REFCOUNTING_INSTRUCTION(RetainValueAddr)
18471847
REFCOUNTING_INSTRUCTION(UnmanagedRetainValue)
18481848
UNARY_INSTRUCTION(CopyValue)
1849+
UNARY_INSTRUCTION(MoveValue)
18491850
REFCOUNTING_INSTRUCTION(ReleaseValue)
18501851
REFCOUNTING_INSTRUCTION(ReleaseValueAddr)
18511852
REFCOUNTING_INSTRUCTION(UnmanagedReleaseValue)

lib/Serialization/ModuleFormat.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
5656
/// describe what change you made. The content of this comment isn't important;
5757
/// it just ensures a conflict if two people change the module format.
5858
/// Don't worry about adhering to the 80-column limit for this line.
59-
const uint16_t SWIFTMODULE_VERSION_MINOR = 626; // switch & checkcast ownership
59+
const uint16_t SWIFTMODULE_VERSION_MINOR = 627; // move_value inst
6060

6161
/// A standard hash seed used for all string hashes in a serialized module.
6262
///

lib/Serialization/SerializeSIL.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1388,6 +1388,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) {
13881388
case SILInstructionKind::UnmanagedRetainValueInst:
13891389
case SILInstructionKind::EndBorrowInst:
13901390
case SILInstructionKind::CopyValueInst:
1391+
case SILInstructionKind::MoveValueInst:
13911392
case SILInstructionKind::DestroyValueInst:
13921393
case SILInstructionKind::ReleaseValueInst:
13931394
case SILInstructionKind::ReleaseValueAddrInst:

test/IRGen/move_value.sil

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// RUN: %swift -disable-legacy-type-info -target x86_64-apple-macosx10.9 -module-name main %s -emit-ir -o - | %FileCheck %s
2+
3+
// REQUIRES: CODEGENERATOR=X86
4+
5+
sil_stage canonical
6+
7+
import Builtin
8+
9+
// CHECK-LABEL: define swiftcc %swift.refcounted* @move_value_test(
10+
// CHECK-NEXT: entry:
11+
// CHECK-NEXT: ret
12+
// CHECK-NEXT: }
13+
sil @move_value_test : $@convention(thin) (@owned Builtin.NativeObject) -> @owned Builtin.NativeObject {
14+
bb0(%0 : $Builtin.NativeObject):
15+
%1 = move_value %0 : $Builtin.NativeObject
16+
return %1 : $Builtin.NativeObject
17+
}

test/SIL/Parser/basic2.sil

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,25 @@ bb0(%0 : @owned $Builtin.NativeObject):
4343
%2 = tuple ()
4444
return %2 : $()
4545
}
46+
47+
// CHECK-LABEL: sil [ossa] @test_movevalue_parsing : $@convention(thin) (@owned Builtin.NativeObject) -> @owned Builtin.NativeObject {
48+
// CHECK: bb0(%0 :
49+
// CHECK-NEXT: %1 = move_value %0 : $Builtin.NativeObject
50+
// CHECK-NEXT: return
51+
// CHECK-NEXT: } // end sil function 'test_movevalue_parsing'
52+
sil [ossa] @test_movevalue_parsing : $@convention(thin) (@owned Builtin.NativeObject) -> @owned Builtin.NativeObject {
53+
bb0(%0 : @owned $Builtin.NativeObject):
54+
%1 = move_value %0 : $Builtin.NativeObject
55+
return %1 : $Builtin.NativeObject
56+
}
57+
58+
// CHECK-LABEL: sil @test_movevalue_parsing_non_ossa : $@convention(thin) (@owned Builtin.NativeObject) -> @owned Builtin.NativeObject {
59+
// CHECK: bb0(%0 :
60+
// CHECK-NEXT: %1 = move_value %0 : $Builtin.NativeObject
61+
// CHECK-NEXT: return
62+
// CHECK-NEXT: } // end sil function 'test_movevalue_parsing_non_ossa'
63+
sil @test_movevalue_parsing_non_ossa : $@convention(thin) (@owned Builtin.NativeObject) -> @owned Builtin.NativeObject {
64+
bb0(%0 : $Builtin.NativeObject):
65+
%1 = move_value %0 : $Builtin.NativeObject
66+
return %1 : $Builtin.NativeObject
67+
}

test/SIL/Serialization/basic.sil

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,17 @@ bb0(%0 : @owned $Builtin.NativeObject):
3030
return undef : $()
3131
}
3232

33+
// CHECK-LABEL: sil @test_movevalue_parsing_non_ossa : $@convention(thin) (@owned Builtin.NativeObject) -> @owned Builtin.NativeObject {
34+
// CHECK: bb0(%0 :
35+
// CHECK-NEXT: %1 = move_value %0 : $Builtin.NativeObject
36+
// CHECK-NEXT: return
37+
// CHECK-NEXT: } // end sil function 'test_movevalue_parsing_non_ossa'
38+
sil @test_movevalue_parsing_non_ossa : $@convention(thin) (@owned Builtin.NativeObject) -> @owned Builtin.NativeObject {
39+
bb0(%0 : $Builtin.NativeObject):
40+
%1 = move_value %0 : $Builtin.NativeObject
41+
return %1 : $Builtin.NativeObject
42+
}
43+
3344
class TestArrayStorage {
3445
@_hasStorage var count: Int32
3546
init()
@@ -59,6 +70,17 @@ bb0(%0 : @owned $(Builtin.NativeObject, Builtin.Int32), %1 : @owned $TestArray2)
5970
return %7 : $(Builtin.NativeObject, Builtin.Int32, TestArrayStorage, Int32, TestArrayStorage)
6071
}
6172

73+
// CHECK-LABEL: sil [ossa] @test_movevalue_parsing : $@convention(thin) (@owned Builtin.NativeObject) -> @owned Builtin.NativeObject {
74+
// CHECK: bb0(%0 :
75+
// CHECK-NEXT: %1 = move_value %0 : $Builtin.NativeObject
76+
// CHECK-NEXT: return
77+
// CHECK-NEXT: } // end sil function 'test_movevalue_parsing'
78+
sil [ossa] @test_movevalue_parsing : $@convention(thin) (@owned Builtin.NativeObject) -> @owned Builtin.NativeObject {
79+
bb0(%0 : @owned $Builtin.NativeObject):
80+
%1 = move_value %0 : $Builtin.NativeObject
81+
return %1 : $Builtin.NativeObject
82+
}
83+
6284
// 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>) -> ()
6385
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>) -> () {
6486
entry(%0 : @guaranteed $@callee_guaranteed @substituted <C, D> (@in C, @in D) -> () for <X, Y>):

utils/sil-mode.el

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@
124124
"unmanaged_retain_value" "unmanaged_release_value"
125125
"unmanaged_autorelease_value"
126126
"strong_copy_unowned_value" "strong_copy_unmanaged_value"
127-
"destructure_struct" "destructure_tuple")
127+
"destructure_struct" "destructure_tuple" "move_value")
128128
'words) . font-lock-keyword-face)
129129
;; Enums. *NOTE* We do not include enum itself here since enum is a
130130
;; swift declaration as well handled at the top.

0 commit comments

Comments
 (0)