Skip to content

Commit 1147897

Browse files
committed
[moveOnly] Implement a semi-generic _move function that can be used on non-generic, non-existential values.
This patch introduces a new stdlib function called _move: ```Swift @_alwaysEmitIntoClient @_transparent @_semantics("lifetimemanagement.move") public func _move<T>(_ value: __owned T) -> T { #if $ExperimentalMoveOnly Builtin.move(value) #else value #endif } ``` It is a first attempt at creating a "move" function for Swift, albeit a skleton one since we do not yet perform the "no use after move" analysis. But this at leasts gets the skeleton into place so we can built the analysis on top of it and churn tree in a manageable way. Thus in its current incarnation, all it does is take in an __owned +1 parameter and returns it after moving it through Builtin.move. Given that we want to use an OSSA based analysis for our "no use after move" analysis and we do not have opaque values yet, we can not supporting moving generic values since they are address only. This has stymied us in the past from creating this function. With the implementation in this PR via a bit of cleverness, we are now able to support this as a generic function over all concrete types by being a little clever. The trick is that when we transparent inline _move (to get the builtin), we perform one level of specialization causing the inlined Builtin.move to be of a loadable type. If after transparent inlining, we inline builtin "move" into a context where it is still address only, we emit a diagnostic telling the user that they applied move to a generic or existential and that this is not yet supported. The reason why we are taking this approach is that we wish to use this to implement a new (as yet unwritten) diagnostic pass that verifies that _move (even for non-trivial copyable values) ends the lifetime of the value. This will ensure that one can write the following code to reliably end the lifetime of a let binding in Swift: ```Swift let x = Klass() let _ = _move(x) // hypotheticalUse(x) ``` Without the diagnostic pass, if one were to write another hypothetical use of x after the _move, the compiler would copy x to at least hypotheticalUse(x) meaning the lifetime of x would not end at the _move, =><=. So to implement this diagnostic pass, we want to use the OSSA infrastructure and that only works on objects! So how do we square this circle: by taking advantage of the mandatory SIL optimzier pipeline! Specifically we take advantage of the following: 1. Mandatory Inlining and Predictable Dead Allocation Elimination run before any of the move only diagnostic passes that we run. 2. Mandatory Inlining is able to specialize a callee a single level when it inlines code. One can take advantage of this to even at -Onone to monomorphosize code. and then note that _move is such a simple function that predictable dead allocation elimination is able to without issue eliminate the extra alloc_stack that appear in the caller after inlining without issue. So we (as the tests show) get SIL that for concrete types looks exactly like we just had run a move_value for that specific type as an object since we promote away the stores/loads in favor of object operations when we eliminate the allocation. In order to prevent any issue with this being used in a context where multiple specializations may occur, I made the inliner emit a diagnostic if it inlines _move into a function that applies it to an address only value. The diagnostic is emitted at the source location where the function call occurs so it is easy to find, e.x.: ``` func addressOnlyMove<T>(t: T) -> T { _move(t) // expected-error {{move() used on a generic or existential value}} } moveonly_builtin_generic_failure.swift:12:5: error: move() used on a generic or existential value _move(t) ^ ``` To eliminate any potential ABI impact, if someone calls _move in a way that causes it to be used in a context where the transparent inliner will not inline it, I taught IRGen that Builtin.move is equivalent to a take from src -> dst and marked _move as always emit into client (AEIC). I also took advantage of the feature flag I added in the previous commit in order to prevent any cond_fails from exposing Builtin.move in the stdlib. If one does not pass in the flag -enable-experimental-move-only then the function just returns the value without calling Builtin.move, so we are safe. rdar://83957028
1 parent 44bd180 commit 1147897

17 files changed

+195
-31
lines changed

include/swift/AST/Builtins.def

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -515,9 +515,6 @@ BUILTIN_SIL_OPERATION(WithUnsafeThrowingContinuation, "withUnsafeThrowingContinu
515515
/// Force the current task to be rescheduled on the specified actor.
516516
BUILTIN_SIL_OPERATION(HopToActor, "hopToActor", None)
517517

518-
/// Generate a move_value instruction to convert a T to a @_moveOnly T.
519-
BUILTIN_SIL_OPERATION(Move, "move", Special)
520-
521518
#undef BUILTIN_SIL_OPERATION
522519

523520
// BUILTIN_RUNTIME_CALL - A call into a runtime function.
@@ -780,6 +777,19 @@ BUILTIN_MISC_OPERATION(CreateTaskGroup,
780777
BUILTIN_MISC_OPERATION(DestroyTaskGroup,
781778
"destroyTaskGroup", "", Special)
782779

780+
781+
/// A builtin that can only be called from a transparent generic function. Takes
782+
/// two operands, the first operand the result address, the second operand the
783+
/// input address. Transforms into load [take] + move_value + store [init] when
784+
/// transparently inlined into a caller that has the generic of the callee
785+
/// specialized into a loadable type. If the transparent inlining does not
786+
/// specialize the type (due to being inlined into a non-generic context, the
787+
/// SILVerifier will abort).
788+
///
789+
/// Illegal to call except for in Swift._move in the stdlib. This is enforced by
790+
/// the SILVerifier.
791+
BUILTIN_MISC_OPERATION(Move, "move", "", Special)
792+
783793
// BUILTIN_MISC_OPERATION_WITH_SILGEN - Miscellaneous operations that are
784794
// specially emitted during SIL generation.
785795
//

include/swift/AST/DiagnosticsSIL.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -686,5 +686,9 @@ NOTE(capturepromotion_concurrentcapture_capturinguse_here, none,
686686
NOTE(capturepromotion_variable_defined_here,none,
687687
"variable defined here", ())
688688

689+
// move operator used on generic or evalue
690+
ERROR(move_operator_used_on_generic_or_existential_value, none,
691+
"move() used on a generic or existential value", ())
692+
689693
#define UNDEFINE_DIAGNOSTIC_MACROS
690694
#include "DefineDiagnosticMacros.h"

include/swift/AST/SemanticAttrs.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,5 +113,7 @@ SEMANTICS_ATTR(FORCE_EMIT_OPT_REMARK_PREFIX, "optremark")
113113
/// for testing purposes.
114114
SEMANTICS_ATTR(OBJC_FORBID_ASSOCIATED_OBJECTS, "objc.forbidAssociatedObjects")
115115

116+
SEMANTICS_ATTR(LIFETIMEMANAGEMENT_MOVE, "lifetimemanagement.move")
117+
116118
#undef SEMANTICS_ATTR
117119

include/swift/SIL/SILType.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,9 @@ class SILType {
620620
/// Get the SIL token type.
621621
static SILType getSILTokenType(const ASTContext &C);
622622

623+
/// Return '()'
624+
static SILType getEmptyTupleType(const ASTContext &C);
625+
623626
//
624627
// Utilities for treating SILType as a pointer-like type.
625628
//

lib/AST/Builtins.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -856,8 +856,7 @@ static ValueDecl *getDestroyArrayOperation(ASTContext &ctx, Identifier id) {
856856

857857
static ValueDecl *getMoveOperation(ASTContext &ctx, Identifier id) {
858858
return getBuiltinFunction(ctx, id, _thin, _generics(_unrestricted),
859-
_parameters(_owned(_typeparam(0))),
860-
_moveOnly(_typeparam(0)));
859+
_parameters(_owned(_typeparam(0))), _typeparam(0));
861860
}
862861

863862
static ValueDecl *getTransferArrayOperation(ASTContext &ctx, Identifier id) {

lib/IRGen/GenBuiltin.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1308,5 +1308,16 @@ if (Builtin.ID == BuiltinValueKind::id) { \
13081308
return;
13091309
}
13101310

1311+
if (Builtin.ID == BuiltinValueKind::Move) {
1312+
auto input = args.claimNext();
1313+
auto result = args.claimNext();
1314+
SILType addrTy = argTypes[0];
1315+
const TypeInfo &addrTI = IGF.getTypeInfo(addrTy);
1316+
Address inputAttr = addrTI.getAddressForPointer(input);
1317+
Address resultAttr = addrTI.getAddressForPointer(result);
1318+
addrTI.initializeWithTake(IGF, resultAttr, inputAttr, addrTy, false);
1319+
return;
1320+
}
1321+
13111322
llvm_unreachable("IRGen unimplemented for this builtin!");
13121323
}

lib/SIL/IR/OperandOwnership.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -775,6 +775,7 @@ BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, PoundAssert)
775775
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, GlobalStringTablePointer)
776776
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, TypePtrAuthDiscriminator)
777777
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, IntInstrprofIncrement)
778+
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, Move)
778779
BUILTIN_OPERAND_OWNERSHIP(DestroyingConsume, StartAsyncLet)
779780
BUILTIN_OPERAND_OWNERSHIP(DestroyingConsume, EndAsyncLet)
780781
BUILTIN_OPERAND_OWNERSHIP(DestroyingConsume, StartAsyncLetWithLocalBuffer)

lib/SIL/IR/SILType.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@ SILType SILType::getOptionalType(SILType type) {
9494
return getPrimitiveType(CanType(optType), type.getCategory());
9595
}
9696

97+
SILType SILType::getEmptyTupleType(const ASTContext &C) {
98+
return getPrimitiveObjectType(C.TheEmptyTupleType);
99+
}
100+
97101
SILType SILType::getSILTokenType(const ASTContext &C) {
98102
return getPrimitiveObjectType(C.TheSILTokenType);
99103
}

lib/SIL/IR/ValueOwnership.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,7 @@ CONSTANT_OWNERSHIP_BUILTIN(None, StartAsyncLetWithLocalBuffer)
556556
CONSTANT_OWNERSHIP_BUILTIN(None, EndAsyncLetLifetime)
557557
CONSTANT_OWNERSHIP_BUILTIN(None, CreateTaskGroup)
558558
CONSTANT_OWNERSHIP_BUILTIN(None, DestroyTaskGroup)
559+
CONSTANT_OWNERSHIP_BUILTIN(None, Move)
559560

560561
#undef CONSTANT_OWNERSHIP_BUILTIN
561562

lib/SIL/Utils/MemAccessUtils.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2135,6 +2135,12 @@ static void visitBuiltinAddress(BuiltinInst *builtin,
21352135
visitor(&builtin->getAllOperands()[2]);
21362136
return;
21372137

2138+
// This consumes its second parameter (the arg) and takes/places that value
2139+
// into the first parameter (the result).
2140+
case BuiltinValueKind::Move:
2141+
visitor(&builtin->getAllOperands()[1]);
2142+
return;
2143+
21382144
// These consume values out of their second operand.
21392145
case BuiltinValueKind::ResumeNonThrowingContinuationReturning:
21402146
case BuiltinValueKind::ResumeThrowingContinuationReturning:

lib/SIL/Verifier/SILVerifier.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,12 @@
2020
#include "swift/AST/Module.h"
2121
#include "swift/AST/ParameterList.h"
2222
#include "swift/AST/ProtocolConformance.h"
23+
#include "swift/AST/SemanticAttrs.h"
2324
#include "swift/AST/Types.h"
2425
#include "swift/Basic/Range.h"
2526
#include "swift/ClangImporter/ClangModule.h"
2627
#include "swift/SIL/ApplySite.h"
28+
#include "swift/SIL/BasicBlockBits.h"
2729
#include "swift/SIL/BasicBlockUtils.h"
2830
#include "swift/SIL/DebugUtils.h"
2931
#include "swift/SIL/Dominance.h"
@@ -32,7 +34,6 @@
3234
#include "swift/SIL/OwnershipUtils.h"
3335
#include "swift/SIL/PostOrder.h"
3436
#include "swift/SIL/PrettyStackTrace.h"
35-
#include "swift/SIL/BasicBlockBits.h"
3637
#include "swift/SIL/SILDebugScope.h"
3738
#include "swift/SIL/SILFunction.h"
3839
#include "swift/SIL/SILModule.h"
@@ -1984,6 +1985,22 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
19841985
"result of build*ExecutorRef");
19851986
return;
19861987
}
1988+
1989+
if (builtinKind == BuiltinValueKind::Move) {
1990+
// We expect that this builtin will be specialized during transparent
1991+
// inlining into move_value if we inline into a non-generic context. If
1992+
// the builtin still remains and is not in the specific move semantic
1993+
// function (which is the only function marked with
1994+
// semantics::LIFETIMEMANAGEMENT_MOVE), then we know that we did
1995+
// transparent inlining into a function that did not result in the Builtin
1996+
// being specialized out which is user error.
1997+
//
1998+
// NOTE: Once we have opaque values, this restriction will go away. This
1999+
// is just so we can call Builtin.move outside of the stdlib.
2000+
auto semanticName = semantics::LIFETIMEMANAGEMENT_MOVE;
2001+
require(BI->getFunction()->hasSemanticsAttr(semanticName),
2002+
"_move used within a generic context");
2003+
}
19872004
}
19882005

19892006
void checkFunctionRefBaseInst(FunctionRefBaseInst *FRI) {

lib/SILGen/SILGenBuiltin.cpp

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1578,15 +1578,6 @@ static ManagedValue emitBuiltinHopToActor(SILGenFunction &SGF, SILLocation loc,
15781578
return ManagedValue::forUnmanaged(SGF.emitEmptyTuple(loc));
15791579
}
15801580

1581-
static ManagedValue emitBuiltinMove(SILGenFunction &SGF, SILLocation loc,
1582-
SubstitutionMap subs,
1583-
ArrayRef<ManagedValue> args, SGFContext C) {
1584-
assert(args.size() == 1 && "Move has a single argument");
1585-
auto firstArg = args[0].ensurePlusOne(SGF, loc);
1586-
CleanupCloner cloner(SGF, firstArg);
1587-
return cloner.clone(SGF.B.createMoveValue(loc, firstArg.forward(SGF)));
1588-
}
1589-
15901581
static ManagedValue emitBuiltinAutoDiffCreateLinearMapContext(
15911582
SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs,
15921583
ArrayRef<ManagedValue> args, SGFContext C) {

lib/SILOptimizer/Transforms/AccessEnforcementReleaseSinking.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ static bool isBarrier(SILInstruction *inst) {
174174
case BuiltinValueKind::AssignCopyArrayBackToFront:
175175
case BuiltinValueKind::AssignTakeArray:
176176
case BuiltinValueKind::UnsafeGuaranteed:
177+
case BuiltinValueKind::Move:
177178
case BuiltinValueKind::UnsafeGuaranteedEnd:
178179
case BuiltinValueKind::CancelAsyncTask:
179180
case BuiltinValueKind::StartAsyncLet:

lib/SILOptimizer/Utils/SILInliner.cpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,16 @@
1313
#define DEBUG_TYPE "sil-inliner"
1414

1515
#include "swift/SILOptimizer/Utils/SILInliner.h"
16+
#include "swift/AST/Builtins.h"
17+
#include "swift/AST/DiagnosticsSIL.h"
1618
#include "swift/SIL/PrettyStackTrace.h"
1719
#include "swift/SIL/SILDebugScope.h"
1820
#include "swift/SIL/TypeSubstCloner.h"
1921
#include "swift/SILOptimizer/Utils/CFGOptUtils.h"
2022
#include "swift/SILOptimizer/Utils/SILOptFunctionBuilder.h"
2123
#include "llvm/ADT/STLExtras.h"
2224
#include "llvm/Support/Debug.h"
25+
2326
using namespace swift;
2427

2528
static bool canInlineBeginApply(BeginApplyInst *BA) {
@@ -289,6 +292,7 @@ class SILInlineCloner
289292
void visitHopToExecutorInst(HopToExecutorInst *Inst);
290293

291294
void visitTerminator(SILBasicBlock *BB);
295+
void visitBuiltinInst(BuiltinInst *BI);
292296

293297
/// This hook is called after either of the top-level visitors:
294298
/// cloneReachableBlocks or cloneSILFunction.
@@ -628,6 +632,7 @@ void SILInlineCloner::visitDebugValueInst(DebugValueInst *Inst) {
628632

629633
return SILCloner<SILInlineCloner>::visitDebugValueInst(Inst);
630634
}
635+
631636
void SILInlineCloner::visitHopToExecutorInst(HopToExecutorInst *Inst) {
632637
// Drop hop_to_executor in non async functions.
633638
if (!Apply.getFunction()->isAsync()) {
@@ -637,6 +642,7 @@ void SILInlineCloner::visitHopToExecutorInst(HopToExecutorInst *Inst) {
637642

638643
return SILCloner<SILInlineCloner>::visitHopToExecutorInst(Inst);
639644
}
645+
640646
const SILDebugScope *
641647
SILInlineCloner::getOrCreateInlineScope(const SILDebugScope *CalleeScope) {
642648
if (!CalleeScope)
@@ -665,6 +671,55 @@ SILInlineCloner::getOrCreateInlineScope(const SILDebugScope *CalleeScope) {
665671
return InlinedScope;
666672
}
667673

674+
template <typename... T, typename... U>
675+
static void diagnose(ASTContext &Context, SourceLoc loc, Diag<T...> diag,
676+
U &&...args) {
677+
Context.Diags.diagnose(loc, diag, std::forward<U>(args)...);
678+
}
679+
680+
void SILInlineCloner::visitBuiltinInst(BuiltinInst *Inst) {
681+
if (IKind == InlineKind::MandatoryInline) {
682+
if (auto kind = Inst->getBuiltinKind()) {
683+
if (*kind == BuiltinValueKind::Move) {
684+
auto otherResultAddr = getOpValue(Inst->getOperand(0));
685+
auto otherSrcAddr = getOpValue(Inst->getOperand(1));
686+
auto otherType = otherSrcAddr->getType();
687+
688+
if (!otherType.isLoadable(*Inst->getFunction())) {
689+
// If otherType is not loadable, emit a diagnostic since it was used
690+
// on a generic or existential value.
691+
diagnose(Inst->getModule().getASTContext(),
692+
getOpLocation(Inst->getLoc()).getSourceLoc(),
693+
diag::move_operator_used_on_generic_or_existential_value);
694+
return SILCloner<SILInlineCloner>::visitBuiltinInst(Inst);
695+
}
696+
697+
// If our otherType is loadable, convert it to move_value.
698+
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
699+
// We stash otherValue in originalOtherValue in case we need to
700+
// perform a writeback.
701+
auto opLoc = getOpLocation(Inst->getLoc());
702+
703+
assert(otherType.isAddress());
704+
705+
SILValue otherValue = getBuilder().emitLoadValueOperation(
706+
opLoc, otherSrcAddr, LoadOwnershipQualifier::Take);
707+
708+
auto *mvi = getBuilder().createMoveValue(opLoc, otherValue);
709+
710+
getBuilder().emitStoreValueOperation(opLoc, mvi, otherResultAddr,
711+
StoreOwnershipQualifier::Init);
712+
713+
// We know that Inst returns a tuple value that isn't used by anything
714+
// else, so this /should/ be safe.
715+
return recordClonedInstruction(Inst, mvi);
716+
}
717+
}
718+
}
719+
720+
return SILCloner<SILInlineCloner>::visitBuiltinInst(Inst);
721+
}
722+
668723
//===----------------------------------------------------------------------===//
669724
// Cost Model
670725
//===----------------------------------------------------------------------===//

stdlib/public/core/LifetimeManager.swift

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,4 +166,31 @@ extension String {
166166
}
167167
}
168168

169-
169+
/// Takes in a value at +1 and performs a Builtin.move upon it.
170+
///
171+
/// IMPLEMENTATION NOTES: During transparent inlining, Builtin.move becomes the
172+
/// move_value instruction if we are inlining into a context where the
173+
/// specialized type is loadable. If the transparent function is called in a
174+
/// context where the inlined function specializes such that the specialized
175+
/// type is still not loadable, the compiler aborts (a). Once we have opaque
176+
/// values, this restriction will be lifted since after that address only types
177+
/// at SILGen time will be loadable objects.
178+
///
179+
/// (a). This is implemented by requiring that Builtin.move only be called
180+
/// within a function marked with the semantic tag "lifetimemanagement.move"
181+
/// which conveniently is only the function we are defining here: _move.
182+
///
183+
/// NOTE: We mark this _alwaysEmitIntoClient to ensure that we are not creating
184+
/// new ABI that the stdlib must maintain if a user calls this ignoring the '_'
185+
/// implying it is stdlib SPI.
186+
@_alwaysEmitIntoClient
187+
@inlinable
188+
@_transparent
189+
@_semantics("lifetimemanagement.move")
190+
func _move<T>(_ value: __owned T) -> T {
191+
#if $BuiltinMove
192+
Builtin.move(value)
193+
#else
194+
value
195+
#endif
196+
}

test/SILGen/moveonly_builtin.swift

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
// RUN: %target-swift-emit-silgen -module-name moveonly -parse-stdlib %s -disable-access-control -disable-objc-attr-requires-foundation-module | %FileCheck %s
1+
// RUN: %target-swift-emit-silgen -module-name moveonly -parse-stdlib %s -disable-access-control -disable-objc-attr-requires-foundation-module -enable-experimental-move-only | %FileCheck %s
2+
// RUN: %target-swift-emit-sil -module-name moveonly -parse-stdlib %s -disable-access-control -disable-objc-attr-requires-foundation-module -enable-experimental-move-only | %FileCheck -check-prefix=CHECK-SIL %s
23

34
import Swift
45

@@ -7,21 +8,55 @@ class Klass {}
78
// CHECK-LABEL: sil hidden [ossa] @$s8moveonly7useMoveyAA5KlassCADF : $@convention(thin) (@guaranteed Klass) -> @owned Klass {
89
// CHECK: bb0([[ARG:%.*]] :
910
// CHECK-NEXT: debug_value
10-
// CHECK-NEXT: copy_value
11-
// CHECK-NEXT: move_value
12-
// CHECK-NEXT: return
11+
// CHECK-NEXT: [[RESULT_ADDR:%.*]] = alloc_stack $Klass
12+
// CHECK-NEXT: [[ARC_COPY:%.*]] = copy_value [[ARG]]
13+
// CHECK-NEXT: [[INPUT_ADDR:%.*]] = alloc_stack $Klass
14+
// CHECK-NEXT: store [[ARC_COPY]] to [init] [[INPUT_ADDR]]
15+
// CHECK-NEXT: // function_ref _move<A>(_:)
16+
// CHECK-NEXT: [[MOVE:%.*]] = function_ref @$ss5_moveyxxnlF : $@convention(thin) <τ_0_0> (@in τ_0_0) -> @out τ_0_0
17+
// CHECK-NEXT: [[APPLY_RESULT:%.*]] = apply [[MOVE]]<Klass>([[RESULT_ADDR]], [[INPUT_ADDR]])
18+
// CHECK-NEXT: dealloc_stack [[INPUT_ADDR]]
19+
// CHECK-NEXT: [[RELOADED_VALUE:%.*]] = load [take] [[RESULT_ADDR]]
20+
// CHECK-NEXT: dealloc_stack [[RESULT_ADDR]]
21+
// CHECK-NEXT: return [[RELOADED_VALUE]]
1322
// CHECK: } // end sil function '$s8moveonly7useMoveyAA5KlassCADF'
23+
24+
// CHECK-SIL-LABEL: sil hidden @$s8moveonly7useMoveyAA5KlassCADF : $@convention(thin) (@guaranteed Klass) -> @owned Klass {
25+
// CHECK-SIL: bb0([[ARG:%.*]] :
26+
// CHECK-SIL-NEXT: debug_value
27+
// CHECK-SIL-NEXT: strong_retain
28+
// CHECK-SIL-NEXT: move_value
29+
// CHECK-SIL-NEXT: tuple
30+
// CHECK-SIL-NEXT: return
31+
// CHECK-SIL: } // end sil function '$s8moveonly7useMoveyAA5KlassCADF'
1432
func useMove(_ k: Klass) -> Klass {
15-
Builtin.move(k)
33+
_move(k)
1634
}
1735

1836
// CHECK-LABEL: sil hidden [ossa] @$s8moveonly7useMoveyxxRlzClF : $@convention(thin) <T where T : AnyObject> (@guaranteed T) -> @owned T {
1937
// CHECK: bb0([[ARG:%.*]] :
2038
// CHECK-NEXT: debug_value
21-
// CHECK-NEXT: copy_value
22-
// CHECK-NEXT: move_value
23-
// CHECK-NEXT: return
39+
// CHECK-NEXT: [[RESULT_ADDR:%.*]] = alloc_stack $T
40+
// CHECK-NEXT: [[ARC_COPY:%.*]] = copy_value [[ARG]]
41+
// CHECK-NEXT: [[INPUT_ADDR:%.*]] = alloc_stack $T
42+
// CHECK-NEXT: store [[ARC_COPY]] to [init] [[INPUT_ADDR]]
43+
// CHECK-NEXT: // function_ref _move<A>(_:)
44+
// CHECK-NEXT: [[MOVE:%.*]] = function_ref @$ss5_moveyxxnlF : $@convention(thin) <τ_0_0> (@in τ_0_0) -> @out τ_0_0
45+
// CHECK-NEXT: [[APPLY_RESULT:%.*]] = apply [[MOVE]]<T>([[RESULT_ADDR]], [[INPUT_ADDR]])
46+
// CHECK-NEXT: dealloc_stack [[INPUT_ADDR]]
47+
// CHECK-NEXT: [[RELOADED_VALUE:%.*]] = load [take] [[RESULT_ADDR]]
48+
// CHECK-NEXT: dealloc_stack [[RESULT_ADDR]]
49+
// CHECK-NEXT: return [[RELOADED_VALUE]]
2450
// CHECK: } // end sil function '$s8moveonly7useMoveyxxRlzClF'
51+
52+
// CHECK-SIL-LABEL: sil hidden @$s8moveonly7useMoveyxxRlzClF : $@convention(thin) <T where T : AnyObject> (@guaranteed T) -> @owned T {
53+
// CHECK-SIL: bb0([[ARG:%.*]] :
54+
// CHECK-SIL-NEXT: debug_value
55+
// CHECK-SIL-NEXT: strong_retain
56+
// CHECK-SIL-NEXT: move_value
57+
// CHECK-SIL-NEXT: tuple
58+
// CHECK-SIL-NEXT: return
59+
// CHECK-SIL: } // end sil function '$s8moveonly7useMoveyxxRlzClF'
2560
func useMove<T : AnyObject>(_ k: T) -> T {
26-
Builtin.move(k)
61+
_move(k)
2762
}

0 commit comments

Comments
 (0)