Skip to content

[moveOnly] Add a semi-generic _copy function similar to the semi-generic _move #39984

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
30 changes: 30 additions & 0 deletions docs/SIL.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5205,6 +5205,36 @@ 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.

explicit_copy_value
```````````````````

::

sil-instruction ::= 'explicit_copy_value' sil-operand

%1 = explicit_copy_value %0 : $A

Performs a copy of a loadable value as if by the value's type lowering and
returns the copy. The returned copy semantically is a value that is completely
independent of the operand. In terms of specific types:

1. For trivial types, this is equivalent to just propagating through the trivial
value.
2. For reference types, this is equivalent to performing a ``strong_retain``
operation and returning the reference.
3. For ``@unowned`` types, this is equivalent to performing an
``unowned_retain`` and returning the operand.
4. For aggregate types, this is equivalent to recursively performing a
``copy_value`` on its components, forming a new aggregate from the copied
components, and then returning the new aggregate.

In ownership qualified functions, a ``explicit_copy_value`` produces a +1 value
that must be consumed at most once along any path through the program.

When move only variable checking is performed, ``explicit_copy_value`` is
treated as an explicit copy asked for by the user that should not be rewritten
and should be treated as a non-consuming use.

move_value
``````````

Expand Down
18 changes: 18 additions & 0 deletions include/swift/AST/Builtins.def
Original file line number Diff line number Diff line change
Expand Up @@ -790,6 +790,24 @@ BUILTIN_MISC_OPERATION(DestroyTaskGroup,
/// the SILVerifier.
BUILTIN_MISC_OPERATION(Move, "move", "", 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
///
/// %input = load [take] %inputAddr
/// %result = explicit_copy_value %input
/// store %result to [init] %resultAddr
/// store %input to [init] %inputAddr
///
/// 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._copy in the stdlib. This is enforced by
/// the SILVerifier.
BUILTIN_MISC_OPERATION(Copy, "copy", "", 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 @@ -718,6 +718,10 @@ NOTE(capturepromotion_variable_defined_here,none,
ERROR(move_operator_used_on_generic_or_existential_value, none,
"move() used on a generic or existential value", ())

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

// noimplicitcopy on generic or existential binding
ERROR(noimplicitcopy_used_on_generic_or_existential, none,
"@_noImplicitCopy can not be used on a generic or existential typed "
Expand Down
1 change: 1 addition & 0 deletions include/swift/AST/SemanticAttrs.def
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ SEMANTICS_ATTR(FORCE_EMIT_OPT_REMARK_PREFIX, "optremark")
SEMANTICS_ATTR(OBJC_FORBID_ASSOCIATED_OBJECTS, "objc.forbidAssociatedObjects")

SEMANTICS_ATTR(LIFETIMEMANAGEMENT_MOVE, "lifetimemanagement.move")
SEMANTICS_ATTR(LIFETIMEMANAGEMENT_COPY, "lifetimemanagement.copy")

SEMANTICS_ATTR(NO_PERFORMANCE_ANALYSIS, "no_performance_analysis")

Expand Down
1 change: 1 addition & 0 deletions include/swift/Basic/Features.def
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,6 @@ 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)
LANGUAGE_FEATURE(BuiltinCopy, 0, "Builtin.copy()", true)

#undef LANGUAGE_FEATURE
9 changes: 9 additions & 0 deletions include/swift/SIL/SILBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -1237,6 +1237,15 @@ class SILBuilder {
CopyValueInst(getSILDebugLocation(Loc), operand));
}

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

DestroyValueInst *createDestroyValue(SILLocation Loc, SILValue operand,
bool poisonRefs = false) {
assert(isLoadableOrOpaque(operand->getType()));
Expand Down
15 changes: 15 additions & 0 deletions include/swift/SIL/SILCloner.h
Original file line number Diff line number Diff line change
Expand Up @@ -1713,6 +1713,21 @@ void SILCloner<ImplClass>::visitCopyValueInst(CopyValueInst *Inst) {
getOpValue(Inst->getOperand())));
}

template <typename ImplClass>
void SILCloner<ImplClass>::visitExplicitCopyValueInst(
ExplicitCopyValueInst *Inst) {
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
if (!getBuilder().hasOwnership()) {
SILValue newValue = getBuilder().emitCopyValueOperation(
getOpLocation(Inst->getLoc()), getOpValue(Inst->getOperand()));
return recordFoldedValue(Inst, newValue);
}

recordClonedInstruction(
Inst, getBuilder().createExplicitCopyValue(
getOpLocation(Inst->getLoc()), getOpValue(Inst->getOperand())));
}

template <typename ImplClass>
void SILCloner<ImplClass>::visitMoveValueInst(MoveValueInst *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 @@ -7267,6 +7267,15 @@ class CopyValueInst
: UnaryInstructionBase(DebugLoc, operand, operand->getType()) {}
};

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

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

#define UNCHECKED_REF_STORAGE(Name, ...) \
class StrongCopy##Name##ValueInst \
: public UnaryInstructionBase< \
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 @@ -575,6 +575,10 @@ ABSTRACT_VALUE_AND_INST(SingleValueInstruction, ValueBase, SILInstruction)
// A copy_value's retain semantics are fully encapsulated in OSSA
// invariants. It has no side effects relative to other OSSA values.
BRIDGED_SINGLE_VALUE_INST(CopyValueInst, copy_value,
SingleValueInstruction, None, DoesNotRelease)
// A copy_value instruction that was explicitly asked for by the user. Left
// alone by OSSA optimizations.
SINGLE_VALUE_INST(ExplicitCopyValueInst, explicit_copy_value,
SingleValueInstruction, None, DoesNotRelease)
#define UNCHECKED_REF_STORAGE(Name, name, ...) \
SINGLE_VALUE_INST(StrongCopy##Name##ValueInst, strong_copy_##name##_value, \
Expand Down
2 changes: 2 additions & 0 deletions lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2816,6 +2816,8 @@ static bool usesFeatureBuiltinMove(Decl *decl) {
return false;
}

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

static bool usesFeatureInheritActorContext(Decl *decl) {
if (auto func = dyn_cast<AbstractFunctionDecl>(decl)) {
for (auto param : *func->getParameters()) {
Expand Down
10 changes: 10 additions & 0 deletions lib/AST/Builtins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -859,6 +859,11 @@ static ValueDecl *getMoveOperation(ASTContext &ctx, Identifier id) {
_parameters(_owned(_typeparam(0))), _typeparam(0));
}

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

static ValueDecl *getTransferArrayOperation(ASTContext &ctx, Identifier id) {
return getBuiltinFunction(ctx, id, _thin,
_generics(_unrestricted),
Expand Down Expand Up @@ -2528,6 +2533,11 @@ ValueDecl *swift::getBuiltinValueDecl(ASTContext &Context, Identifier Id) {
return nullptr;
return getMoveOperation(Context, Id);

case BuiltinValueKind::Copy:
if (!Types.empty())
return nullptr;
return getCopyOperation(Context, Id);

#define BUILTIN(id, name, Attrs)
#define BUILTIN_BINARY_OPERATION(id, name, attrs)
#define BUILTIN_BINARY_OPERATION_OVERLOADED_STATIC(id, name, attrs, overload) \
Expand Down
11 changes: 11 additions & 0 deletions lib/IRGen/GenBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1319,5 +1319,16 @@ if (Builtin.ID == BuiltinValueKind::id) { \
return;
}

if (Builtin.ID == BuiltinValueKind::Copy) {
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.initializeWithCopy(IGF, resultAttr, inputAttr, addrTy, false);
return;
}

llvm_unreachable("IRGen unimplemented for this builtin!");
}
3 changes: 3 additions & 0 deletions lib/IRGen/IRGenSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1147,6 +1147,9 @@ class IRGenSILFunction :
void visitRetainValueInst(RetainValueInst *i);
void visitRetainValueAddrInst(RetainValueAddrInst *i);
void visitCopyValueInst(CopyValueInst *i);
void visitExplicitCopyValueInst(ExplicitCopyValueInst *i) {
llvm_unreachable("Valid only when ownership is enabled");
}
void visitMoveValueInst(MoveValueInst *i) {
auto e = getLoweredExplosion(i->getOperand());
setLoweredExplosion(i, e);
Expand Down
2 changes: 2 additions & 0 deletions lib/SIL/IR/OperandOwnership.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ OPERAND_OWNERSHIP(InstantaneousUse, SetDeallocating)
OPERAND_OWNERSHIP(UnownedInstantaneousUse, DebugValue)
OPERAND_OWNERSHIP(UnownedInstantaneousUse, CopyBlock)
OPERAND_OWNERSHIP(UnownedInstantaneousUse, CopyValue)
OPERAND_OWNERSHIP(UnownedInstantaneousUse, ExplicitCopyValue)
OPERAND_OWNERSHIP(UnownedInstantaneousUse, ObjCMethod)
OPERAND_OWNERSHIP(UnownedInstantaneousUse, ObjCSuperMethod)
OPERAND_OWNERSHIP(UnownedInstantaneousUse, UnmanagedRetainValue)
Expand Down Expand Up @@ -776,6 +777,7 @@ BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, GlobalStringTablePointer)
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, TypePtrAuthDiscriminator)
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, IntInstrprofIncrement)
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, Move)
BUILTIN_OPERAND_OWNERSHIP(UnownedInstantaneousUse, Copy)
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/SILPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1842,6 +1842,10 @@ class SILPrinter : public SILInstructionVisitor<SILPrinter> {
*this << getIDAndType(I->getOperand());
}

void visitExplicitCopyValueInst(ExplicitCopyValueInst *I) {
*this << getIDAndType(I->getOperand());
}

void visitMoveValueInst(MoveValueInst *I) {
*this << getIDAndType(I->getOperand());
}
Expand Down
2 changes: 2 additions & 0 deletions lib/SIL/IR/ValueOwnership.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ CONSTANT_OWNERSHIP_INST(Owned, AllocRefDynamic)
CONSTANT_OWNERSHIP_INST(Owned, CopyBlock)
CONSTANT_OWNERSHIP_INST(Owned, CopyBlockWithoutEscaping)
CONSTANT_OWNERSHIP_INST(Owned, CopyValue)
CONSTANT_OWNERSHIP_INST(Owned, ExplicitCopyValue)
CONSTANT_OWNERSHIP_INST(Owned, MoveValue)
CONSTANT_OWNERSHIP_INST(Owned, EndCOWMutation)
CONSTANT_OWNERSHIP_INST(Owned, KeyPath)
Expand Down Expand Up @@ -557,6 +558,7 @@ CONSTANT_OWNERSHIP_BUILTIN(None, EndAsyncLetLifetime)
CONSTANT_OWNERSHIP_BUILTIN(None, CreateTaskGroup)
CONSTANT_OWNERSHIP_BUILTIN(None, DestroyTaskGroup)
CONSTANT_OWNERSHIP_BUILTIN(None, Move)
CONSTANT_OWNERSHIP_BUILTIN(None, Copy)

#undef CONSTANT_OWNERSHIP_BUILTIN

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 @@ -3138,6 +3138,7 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B,
UNARY_INSTRUCTION(IsUnique)
UNARY_INSTRUCTION(DestroyAddr)
UNARY_INSTRUCTION(CopyValue)
UNARY_INSTRUCTION(ExplicitCopyValue)
UNARY_INSTRUCTION(MoveValue)
UNARY_INSTRUCTION(EndBorrow)
UNARY_INSTRUCTION(DestructureStruct)
Expand Down
1 change: 1 addition & 0 deletions lib/SIL/Utils/InstructionUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,7 @@ RuntimeEffect swift::getRuntimeEffect(SILInstruction *inst, SILType &impactType)
case SILInstructionKind::RetainValueInst:
case SILInstructionKind::BeginCOWMutationInst:
case SILInstructionKind::CopyValueInst:
case SILInstructionKind::ExplicitCopyValueInst:
case SILInstructionKind::SetDeallocatingInst:
case SILInstructionKind::IsUniqueInst:
case SILInstructionKind::IsEscapingClosureInst:
Expand Down
4 changes: 2 additions & 2 deletions lib/SIL/Utils/MemAccessUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2191,9 +2191,9 @@ 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).
// These effect both operands.
case BuiltinValueKind::Move:
case BuiltinValueKind::Copy:
visitor(&builtin->getAllOperands()[1]);
return;

Expand Down
16 changes: 16 additions & 0 deletions lib/SIL/Verifier/SILVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2001,6 +2001,22 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
require(BI->getFunction()->hasSemanticsAttr(semanticName),
"_move used within a generic context");
}

if (builtinKind == BuiltinValueKind::Copy) {
// We expect that this builtin will be specialized during transparent
// inlining into explicit_copy_value if we inline into a non-generic
// context. If the builtin still remains and is not in the specific copy
// semantic function (which is the only function marked with
// semantics::LIFETIMEMANAGEMENT_COPY), 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.copy outside of the stdlib.
auto semanticName = semantics::LIFETIMEMANAGEMENT_COPY;
require(BI->getFunction()->hasSemanticsAttr(semanticName),
"_copy used within a generic context");
}
}

void checkFunctionRefBaseInst(FunctionRefBaseInst *FRI) {
Expand Down
16 changes: 16 additions & 0 deletions lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ struct OwnershipModelEliminatorVisitor
bool visitStoreInst(StoreInst *si);
bool visitStoreBorrowInst(StoreBorrowInst *si);
bool visitCopyValueInst(CopyValueInst *cvi);
bool visitExplicitCopyValueInst(ExplicitCopyValueInst *cvi);
bool visitDestroyValueInst(DestroyValueInst *dvi);
bool visitLoadBorrowInst(LoadBorrowInst *lbi);
bool visitBeginBorrowInst(BeginBorrowInst *bbi) {
Expand Down Expand Up @@ -288,6 +289,21 @@ bool OwnershipModelEliminatorVisitor::visitCopyValueInst(CopyValueInst *cvi) {
return true;
}

bool OwnershipModelEliminatorVisitor::visitExplicitCopyValueInst(
ExplicitCopyValueInst *cvi) {
// A copy_value of an address-only type cannot be replaced.
if (cvi->getType().isAddressOnly(*cvi->getFunction()))
return false;

// Now that we have set the unqualified ownership flag, destroy value
// operation will delegate to the appropriate strong_release, etc.
withBuilder<void>(cvi, [&](SILBuilder &b, SILLocation loc) {
b.emitCopyValueOperation(loc, cvi->getOperand());
});
eraseInstructionAndRAUW(cvi, cvi->getOperand());
return true;
}

bool OwnershipModelEliminatorVisitor::visitUnmanagedRetainValueInst(
UnmanagedRetainValueInst *urvi) {
// Now that we have set the unqualified ownership flag, destroy value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ static bool isBarrier(SILInstruction *inst) {
case BuiltinValueKind::AssignTakeArray:
case BuiltinValueKind::UnsafeGuaranteed:
case BuiltinValueKind::Move:
case BuiltinValueKind::Copy:
case BuiltinValueKind::UnsafeGuaranteedEnd:
case BuiltinValueKind::CancelAsyncTask:
case BuiltinValueKind::StartAsyncLet:
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 @@ -203,6 +203,7 @@ static bool hasOpaqueArchetype(TypeExpansionContext context,
case SILInstructionKind::CopyBlockInst:
case SILInstructionKind::CopyBlockWithoutEscapingInst:
case SILInstructionKind::CopyValueInst:
case SILInstructionKind::ExplicitCopyValueInst:
case SILInstructionKind::MoveValueInst:
#define UNCHECKED_REF_STORAGE(Name, ...) \
case SILInstructionKind::StrongCopy##Name##ValueInst:
Expand Down
Loading