Skip to content

Commit 60f3d61

Browse files
authored
Merge pull request #39943 from gottesmm/pr-65ca66f5402c00e0f0fa0608409ef1c732d1fd2b
[moveOnly] Add a skeleton _move function
2 parents 9e41e37 + 1147897 commit 60f3d61

23 files changed

+226
-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/Basic/Features.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,6 @@ LANGUAGE_FEATURE(ImplicitSelfCapture, 0, "@_implicitSelfCapture attribute", true
5454
LANGUAGE_FEATURE(BuiltinBuildExecutor, 0, "Executor-building builtins", true)
5555
LANGUAGE_FEATURE(BuiltinBuildMainExecutor, 0, "MainActor executor building builtin", true)
5656
LANGUAGE_FEATURE(BuiltinCreateAsyncTaskInGroup, 0, "MainActor executor building builtin", true)
57+
LANGUAGE_FEATURE(BuiltinMove, 0, "Builtin.move()", true)
5758

5859
#undef LANGUAGE_FEATURE

include/swift/Basic/LangOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,9 @@ namespace swift {
320320
/// Enable experimental 'distributed' actors and functions.
321321
bool EnableExperimentalDistributed = false;
322322

323+
/// Enable experimental 'move only' features.
324+
bool EnableExperimentalMoveOnly = false;
325+
323326
/// Disable the implicit import of the _Concurrency module.
324327
bool DisableImplicitConcurrencyModuleImport =
325328
!SWIFT_IMPLICIT_CONCURRENCY_IMPORT;

include/swift/Option/FrontendOptions.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,10 @@ def enable_experimental_lexical_lifetimes :
259259
Flag<["-"], "enable-experimental-lexical-lifetimes">,
260260
HelpText<"Enable experimental lexical lifetimes">;
261261

262+
def enable_experimental_move_only :
263+
Flag<["-"], "enable-experimental-move-only">,
264+
HelpText<"Enable experimental move only">;
265+
262266
def enable_experimental_distributed :
263267
Flag<["-"], "enable-experimental-distributed">,
264268
HelpText<"Enable experimental 'distributed' actors and functions">;

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/ASTPrinter.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2812,6 +2812,10 @@ static bool usesFeatureBuiltinCreateAsyncTaskInGroup(Decl *decl) {
28122812
return false;
28132813
}
28142814

2815+
static bool usesFeatureBuiltinMove(Decl *decl) {
2816+
return false;
2817+
}
2818+
28152819
static bool usesFeatureInheritActorContext(Decl *decl) {
28162820
if (auto func = dyn_cast<AbstractFunctionDecl>(decl)) {
28172821
for (auto param : *func->getParameters()) {

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/Frontend/CompilerInvocation.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,9 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
443443
Opts.EnableExperimentalDistributed |=
444444
Args.hasArg(OPT_enable_experimental_distributed);
445445

446+
Opts.EnableExperimentalMoveOnly |=
447+
Args.hasArg(OPT_enable_experimental_move_only);
448+
446449
Opts.EnableInferPublicSendable |=
447450
Args.hasFlag(OPT_enable_infer_public_concurrent_value,
448451
OPT_disable_infer_public_concurrent_value,
@@ -1399,6 +1402,11 @@ static bool ParseSILArgs(SILOptions &Opts, ArgList &Args,
13991402

14001403
Opts.EnableExperimentalLexicalLifetimes |=
14011404
Args.hasArg(OPT_enable_experimental_lexical_lifetimes);
1405+
// If experimental move only is enabled, always enable lexical lifetime as
1406+
// well. Move only depends on lexical lifetimes.
1407+
Opts.EnableExperimentalLexicalLifetimes |=
1408+
Args.hasArg(OPT_enable_experimental_move_only);
1409+
14021410
Opts.EnableCopyPropagation |= Args.hasArg(OPT_enable_copy_propagation);
14031411
Opts.DisableCopyPropagation |= Args.hasArg(OPT_disable_copy_propagation);
14041412
Opts.EnableARCOptimizations &= !Args.hasArg(OPT_disable_arc_opts);

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+
}

0 commit comments

Comments
 (0)