Skip to content

[moveOnly] Add a skeleton _move function #39943

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
16 changes: 13 additions & 3 deletions include/swift/AST/Builtins.def
Original file line number Diff line number Diff line change
Expand Up @@ -515,9 +515,6 @@ BUILTIN_SIL_OPERATION(WithUnsafeThrowingContinuation, "withUnsafeThrowingContinu
/// Force the current task to be rescheduled on the specified actor.
BUILTIN_SIL_OPERATION(HopToActor, "hopToActor", None)

/// Generate a move_value instruction to convert a T to a @_moveOnly T.
BUILTIN_SIL_OPERATION(Move, "move", Special)

#undef BUILTIN_SIL_OPERATION

// BUILTIN_RUNTIME_CALL - A call into a runtime function.
Expand Down Expand Up @@ -780,6 +777,19 @@ BUILTIN_MISC_OPERATION(CreateTaskGroup,
BUILTIN_MISC_OPERATION(DestroyTaskGroup,
"destroyTaskGroup", "", Special)


/// A builtin that can only be called from a transparent generic function. Takes
/// two operands, the first operand the result address, the second operand the
/// input address. Transforms into load [take] + move_value + store [init] when
/// transparently inlined into a caller that has the generic of the callee
/// specialized into a loadable type. If the transparent inlining does not
/// specialize the type (due to being inlined into a non-generic context, the
/// SILVerifier will abort).
///
/// Illegal to call except for in Swift._move in the stdlib. This is enforced by
/// the SILVerifier.
BUILTIN_MISC_OPERATION(Move, "move", "", Special)

// BUILTIN_MISC_OPERATION_WITH_SILGEN - Miscellaneous operations that are
// specially emitted during SIL generation.
//
Expand Down
4 changes: 4 additions & 0 deletions include/swift/AST/DiagnosticsSIL.def
Original file line number Diff line number Diff line change
Expand Up @@ -686,5 +686,9 @@ NOTE(capturepromotion_concurrentcapture_capturinguse_here, none,
NOTE(capturepromotion_variable_defined_here,none,
"variable defined here", ())

// move operator used on generic or evalue
ERROR(move_operator_used_on_generic_or_existential_value, none,
"move() used on a generic or existential value", ())

#define UNDEFINE_DIAGNOSTIC_MACROS
#include "DefineDiagnosticMacros.h"
2 changes: 2 additions & 0 deletions include/swift/AST/SemanticAttrs.def
Original file line number Diff line number Diff line change
Expand Up @@ -113,5 +113,7 @@ SEMANTICS_ATTR(FORCE_EMIT_OPT_REMARK_PREFIX, "optremark")
/// for testing purposes.
SEMANTICS_ATTR(OBJC_FORBID_ASSOCIATED_OBJECTS, "objc.forbidAssociatedObjects")

SEMANTICS_ATTR(LIFETIMEMANAGEMENT_MOVE, "lifetimemanagement.move")

#undef SEMANTICS_ATTR

1 change: 1 addition & 0 deletions include/swift/Basic/Features.def
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,6 @@ LANGUAGE_FEATURE(ImplicitSelfCapture, 0, "@_implicitSelfCapture attribute", true
LANGUAGE_FEATURE(BuiltinBuildExecutor, 0, "Executor-building builtins", true)
LANGUAGE_FEATURE(BuiltinBuildMainExecutor, 0, "MainActor executor building builtin", true)
LANGUAGE_FEATURE(BuiltinCreateAsyncTaskInGroup, 0, "MainActor executor building builtin", true)
LANGUAGE_FEATURE(BuiltinMove, 0, "Builtin.move()", true)

#undef LANGUAGE_FEATURE
3 changes: 3 additions & 0 deletions include/swift/Basic/LangOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,9 @@ namespace swift {
/// Enable experimental 'distributed' actors and functions.
bool EnableExperimentalDistributed = false;

/// Enable experimental 'move only' features.
bool EnableExperimentalMoveOnly = false;

/// Disable the implicit import of the _Concurrency module.
bool DisableImplicitConcurrencyModuleImport =
!SWIFT_IMPLICIT_CONCURRENCY_IMPORT;
Expand Down
4 changes: 4 additions & 0 deletions include/swift/Option/FrontendOptions.td
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,10 @@ def enable_experimental_lexical_lifetimes :
Flag<["-"], "enable-experimental-lexical-lifetimes">,
HelpText<"Enable experimental lexical lifetimes">;

def enable_experimental_move_only :
Flag<["-"], "enable-experimental-move-only">,
HelpText<"Enable experimental move only">;

def enable_experimental_distributed :
Flag<["-"], "enable-experimental-distributed">,
HelpText<"Enable experimental 'distributed' actors and functions">;
Expand Down
3 changes: 3 additions & 0 deletions include/swift/SIL/SILType.h
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,9 @@ class SILType {
/// Get the SIL token type.
static SILType getSILTokenType(const ASTContext &C);

/// Return '()'
static SILType getEmptyTupleType(const ASTContext &C);

//
// Utilities for treating SILType as a pointer-like type.
//
Expand Down
4 changes: 4 additions & 0 deletions lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2812,6 +2812,10 @@ static bool usesFeatureBuiltinCreateAsyncTaskInGroup(Decl *decl) {
return false;
}

static bool usesFeatureBuiltinMove(Decl *decl) {
return false;
}

static bool usesFeatureInheritActorContext(Decl *decl) {
if (auto func = dyn_cast<AbstractFunctionDecl>(decl)) {
for (auto param : *func->getParameters()) {
Expand Down
3 changes: 1 addition & 2 deletions lib/AST/Builtins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -856,8 +856,7 @@ static ValueDecl *getDestroyArrayOperation(ASTContext &ctx, Identifier id) {

static ValueDecl *getMoveOperation(ASTContext &ctx, Identifier id) {
return getBuiltinFunction(ctx, id, _thin, _generics(_unrestricted),
_parameters(_owned(_typeparam(0))),
_moveOnly(_typeparam(0)));
_parameters(_owned(_typeparam(0))), _typeparam(0));
}

static ValueDecl *getTransferArrayOperation(ASTContext &ctx, Identifier id) {
Expand Down
8 changes: 8 additions & 0 deletions lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,9 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
Opts.EnableExperimentalDistributed |=
Args.hasArg(OPT_enable_experimental_distributed);

Opts.EnableExperimentalMoveOnly |=
Args.hasArg(OPT_enable_experimental_move_only);

Opts.EnableInferPublicSendable |=
Args.hasFlag(OPT_enable_infer_public_concurrent_value,
OPT_disable_infer_public_concurrent_value,
Expand Down Expand Up @@ -1399,6 +1402,11 @@ static bool ParseSILArgs(SILOptions &Opts, ArgList &Args,

Opts.EnableExperimentalLexicalLifetimes |=
Args.hasArg(OPT_enable_experimental_lexical_lifetimes);
// If experimental move only is enabled, always enable lexical lifetime as
// well. Move only depends on lexical lifetimes.
Opts.EnableExperimentalLexicalLifetimes |=
Args.hasArg(OPT_enable_experimental_move_only);

Opts.EnableCopyPropagation |= Args.hasArg(OPT_enable_copy_propagation);
Opts.DisableCopyPropagation |= Args.hasArg(OPT_disable_copy_propagation);
Opts.EnableARCOptimizations &= !Args.hasArg(OPT_disable_arc_opts);
Expand Down
11 changes: 11 additions & 0 deletions lib/IRGen/GenBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1308,5 +1308,16 @@ if (Builtin.ID == BuiltinValueKind::id) { \
return;
}

if (Builtin.ID == BuiltinValueKind::Move) {
auto input = args.claimNext();
auto result = args.claimNext();
SILType addrTy = argTypes[0];
const TypeInfo &addrTI = IGF.getTypeInfo(addrTy);
Address inputAttr = addrTI.getAddressForPointer(input);
Address resultAttr = addrTI.getAddressForPointer(result);
addrTI.initializeWithTake(IGF, resultAttr, inputAttr, addrTy, false);
return;
}

llvm_unreachable("IRGen unimplemented for this builtin!");
}
1 change: 1 addition & 0 deletions lib/SIL/IR/OperandOwnership.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -775,6 +775,7 @@ BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, PoundAssert)
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, GlobalStringTablePointer)
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, TypePtrAuthDiscriminator)
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, IntInstrprofIncrement)
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, Move)
BUILTIN_OPERAND_OWNERSHIP(DestroyingConsume, StartAsyncLet)
BUILTIN_OPERAND_OWNERSHIP(DestroyingConsume, EndAsyncLet)
BUILTIN_OPERAND_OWNERSHIP(DestroyingConsume, StartAsyncLetWithLocalBuffer)
Expand Down
4 changes: 4 additions & 0 deletions lib/SIL/IR/SILType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ SILType SILType::getOptionalType(SILType type) {
return getPrimitiveType(CanType(optType), type.getCategory());
}

SILType SILType::getEmptyTupleType(const ASTContext &C) {
return getPrimitiveObjectType(C.TheEmptyTupleType);
}

SILType SILType::getSILTokenType(const ASTContext &C) {
return getPrimitiveObjectType(C.TheSILTokenType);
}
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 @@ -556,6 +556,7 @@ CONSTANT_OWNERSHIP_BUILTIN(None, StartAsyncLetWithLocalBuffer)
CONSTANT_OWNERSHIP_BUILTIN(None, EndAsyncLetLifetime)
CONSTANT_OWNERSHIP_BUILTIN(None, CreateTaskGroup)
CONSTANT_OWNERSHIP_BUILTIN(None, DestroyTaskGroup)
CONSTANT_OWNERSHIP_BUILTIN(None, Move)

#undef CONSTANT_OWNERSHIP_BUILTIN

Expand Down
6 changes: 6 additions & 0 deletions lib/SIL/Utils/MemAccessUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2135,6 +2135,12 @@ static void visitBuiltinAddress(BuiltinInst *builtin,
visitor(&builtin->getAllOperands()[2]);
return;

// This consumes its second parameter (the arg) and takes/places that value
// into the first parameter (the result).
case BuiltinValueKind::Move:
visitor(&builtin->getAllOperands()[1]);
return;

// These consume values out of their second operand.
case BuiltinValueKind::ResumeNonThrowingContinuationReturning:
case BuiltinValueKind::ResumeThrowingContinuationReturning:
Expand Down
19 changes: 18 additions & 1 deletion lib/SIL/Verifier/SILVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@
#include "swift/AST/Module.h"
#include "swift/AST/ParameterList.h"
#include "swift/AST/ProtocolConformance.h"
#include "swift/AST/SemanticAttrs.h"
#include "swift/AST/Types.h"
#include "swift/Basic/Range.h"
#include "swift/ClangImporter/ClangModule.h"
#include "swift/SIL/ApplySite.h"
#include "swift/SIL/BasicBlockBits.h"
#include "swift/SIL/BasicBlockUtils.h"
#include "swift/SIL/DebugUtils.h"
#include "swift/SIL/Dominance.h"
Expand All @@ -32,7 +34,6 @@
#include "swift/SIL/OwnershipUtils.h"
#include "swift/SIL/PostOrder.h"
#include "swift/SIL/PrettyStackTrace.h"
#include "swift/SIL/BasicBlockBits.h"
#include "swift/SIL/SILDebugScope.h"
#include "swift/SIL/SILFunction.h"
#include "swift/SIL/SILModule.h"
Expand Down Expand Up @@ -1984,6 +1985,22 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
"result of build*ExecutorRef");
return;
}

if (builtinKind == BuiltinValueKind::Move) {
// We expect that this builtin will be specialized during transparent
// inlining into move_value if we inline into a non-generic context. If
// the builtin still remains and is not in the specific move semantic
// function (which is the only function marked with
// semantics::LIFETIMEMANAGEMENT_MOVE), then we know that we did
// transparent inlining into a function that did not result in the Builtin
// being specialized out which is user error.
//
// NOTE: Once we have opaque values, this restriction will go away. This
// is just so we can call Builtin.move outside of the stdlib.
auto semanticName = semantics::LIFETIMEMANAGEMENT_MOVE;
require(BI->getFunction()->hasSemanticsAttr(semanticName),
"_move used within a generic context");
}
}

void checkFunctionRefBaseInst(FunctionRefBaseInst *FRI) {
Expand Down
9 changes: 0 additions & 9 deletions lib/SILGen/SILGenBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1578,15 +1578,6 @@ static ManagedValue emitBuiltinHopToActor(SILGenFunction &SGF, SILLocation loc,
return ManagedValue::forUnmanaged(SGF.emitEmptyTuple(loc));
}

static ManagedValue emitBuiltinMove(SILGenFunction &SGF, SILLocation loc,
SubstitutionMap subs,
ArrayRef<ManagedValue> args, SGFContext C) {
assert(args.size() == 1 && "Move has a single argument");
auto firstArg = args[0].ensurePlusOne(SGF, loc);
CleanupCloner cloner(SGF, firstArg);
return cloner.clone(SGF.B.createMoveValue(loc, firstArg.forward(SGF)));
}

static ManagedValue emitBuiltinAutoDiffCreateLinearMapContext(
SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs,
ArrayRef<ManagedValue> args, SGFContext C) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ static bool isBarrier(SILInstruction *inst) {
case BuiltinValueKind::AssignCopyArrayBackToFront:
case BuiltinValueKind::AssignTakeArray:
case BuiltinValueKind::UnsafeGuaranteed:
case BuiltinValueKind::Move:
case BuiltinValueKind::UnsafeGuaranteedEnd:
case BuiltinValueKind::CancelAsyncTask:
case BuiltinValueKind::StartAsyncLet:
Expand Down
55 changes: 55 additions & 0 deletions lib/SILOptimizer/Utils/SILInliner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,16 @@
#define DEBUG_TYPE "sil-inliner"

#include "swift/SILOptimizer/Utils/SILInliner.h"
#include "swift/AST/Builtins.h"
#include "swift/AST/DiagnosticsSIL.h"
#include "swift/SIL/PrettyStackTrace.h"
#include "swift/SIL/SILDebugScope.h"
#include "swift/SIL/TypeSubstCloner.h"
#include "swift/SILOptimizer/Utils/CFGOptUtils.h"
#include "swift/SILOptimizer/Utils/SILOptFunctionBuilder.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Debug.h"

using namespace swift;

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

void visitTerminator(SILBasicBlock *BB);
void visitBuiltinInst(BuiltinInst *BI);

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

return SILCloner<SILInlineCloner>::visitDebugValueInst(Inst);
}

void SILInlineCloner::visitHopToExecutorInst(HopToExecutorInst *Inst) {
// Drop hop_to_executor in non async functions.
if (!Apply.getFunction()->isAsync()) {
Expand All @@ -637,6 +642,7 @@ void SILInlineCloner::visitHopToExecutorInst(HopToExecutorInst *Inst) {

return SILCloner<SILInlineCloner>::visitHopToExecutorInst(Inst);
}

const SILDebugScope *
SILInlineCloner::getOrCreateInlineScope(const SILDebugScope *CalleeScope) {
if (!CalleeScope)
Expand Down Expand Up @@ -665,6 +671,55 @@ SILInlineCloner::getOrCreateInlineScope(const SILDebugScope *CalleeScope) {
return InlinedScope;
}

template <typename... T, typename... U>
static void diagnose(ASTContext &Context, SourceLoc loc, Diag<T...> diag,
U &&...args) {
Context.Diags.diagnose(loc, diag, std::forward<U>(args)...);
}

void SILInlineCloner::visitBuiltinInst(BuiltinInst *Inst) {
if (IKind == InlineKind::MandatoryInline) {
if (auto kind = Inst->getBuiltinKind()) {
if (*kind == BuiltinValueKind::Move) {
auto otherResultAddr = getOpValue(Inst->getOperand(0));
auto otherSrcAddr = getOpValue(Inst->getOperand(1));
auto otherType = otherSrcAddr->getType();

if (!otherType.isLoadable(*Inst->getFunction())) {
// If otherType is not loadable, emit a diagnostic since it was used
// on a generic or existential value.
diagnose(Inst->getModule().getASTContext(),
getOpLocation(Inst->getLoc()).getSourceLoc(),
diag::move_operator_used_on_generic_or_existential_value);
return SILCloner<SILInlineCloner>::visitBuiltinInst(Inst);
}

// If our otherType is loadable, convert it to move_value.
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
// We stash otherValue in originalOtherValue in case we need to
// perform a writeback.
auto opLoc = getOpLocation(Inst->getLoc());

assert(otherType.isAddress());

SILValue otherValue = getBuilder().emitLoadValueOperation(
opLoc, otherSrcAddr, LoadOwnershipQualifier::Take);

auto *mvi = getBuilder().createMoveValue(opLoc, otherValue);

getBuilder().emitStoreValueOperation(opLoc, mvi, otherResultAddr,
StoreOwnershipQualifier::Init);

// We know that Inst returns a tuple value that isn't used by anything
// else, so this /should/ be safe.
return recordClonedInstruction(Inst, mvi);
}
}
}

return SILCloner<SILInlineCloner>::visitBuiltinInst(Inst);
}

//===----------------------------------------------------------------------===//
// Cost Model
//===----------------------------------------------------------------------===//
Expand Down
29 changes: 28 additions & 1 deletion stdlib/public/core/LifetimeManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -166,4 +166,31 @@ extension String {
}
}


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