Skip to content

Commit f9122a7

Browse files
committed
[moveOnly] Implement a new _copy function that performs an explicit copy value.
The key thing is that the move checker will not consider the explicit copy value to be a copy_value that can be rewritten, ensuring that any uses of the result of the explicit copy_value (consuming or other wise) are not checked. Similar to the _move operator I recently introduced, this is a transparent function so we can perform one level of specialization and thus at least be generic over all concrete types.
1 parent 94d824a commit f9122a7

31 files changed

+360
-3
lines changed

docs/SIL.rst

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5205,6 +5205,36 @@ independent of the operand. In terms of specific types:
52055205
In ownership qualified functions, a ``copy_value`` produces a +1 value that must
52065206
be consumed at most once along any path through the program.
52075207

5208+
explicit_copy_value
5209+
```````````````````
5210+
5211+
::
5212+
5213+
sil-instruction ::= 'explicit_copy_value' sil-operand
5214+
5215+
%1 = explicit_copy_value %0 : $A
5216+
5217+
Performs a copy of a loadable value as if by the value's type lowering and
5218+
returns the copy. The returned copy semantically is a value that is completely
5219+
independent of the operand. In terms of specific types:
5220+
5221+
1. For trivial types, this is equivalent to just propagating through the trivial
5222+
value.
5223+
2. For reference types, this is equivalent to performing a ``strong_retain``
5224+
operation and returning the reference.
5225+
3. For ``@unowned`` types, this is equivalent to performing an
5226+
``unowned_retain`` and returning the operand.
5227+
4. For aggregate types, this is equivalent to recursively performing a
5228+
``copy_value`` on its components, forming a new aggregate from the copied
5229+
components, and then returning the new aggregate.
5230+
5231+
In ownership qualified functions, a ``explicit_copy_value`` produces a +1 value
5232+
that must be consumed at most once along any path through the program.
5233+
5234+
When move only variable checking is performed, ``explicit_copy_value`` is
5235+
treated as an explicit copy asked for by the user that should not be rewritten
5236+
and should be treated as a non-consuming use.
5237+
52085238
move_value
52095239
``````````
52105240

include/swift/AST/Builtins.def

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -790,6 +790,24 @@ BUILTIN_MISC_OPERATION(DestroyTaskGroup,
790790
/// the SILVerifier.
791791
BUILTIN_MISC_OPERATION(Move, "move", "", Special)
792792

793+
/// A builtin that can only be called from a transparent generic function. Takes
794+
/// two operands, the first operand the result address, the second operand the
795+
/// input address. Transforms into
796+
///
797+
/// %input = load [take] %inputAddr
798+
/// %result = explicit_copy_value %input
799+
/// store %result to [init] %resultAddr
800+
/// store %input to [init] %inputAddr
801+
///
802+
/// transparently inlined into a caller that has the generic of the callee
803+
/// specialized into a loadable type. If the transparent inlining does not
804+
/// specialize the type (due to being inlined into a non-generic context, the
805+
/// SILVerifier will abort).
806+
///
807+
/// Illegal to call except for in Swift._copy in the stdlib. This is enforced by
808+
/// the SILVerifier.
809+
BUILTIN_MISC_OPERATION(Copy, "copy", "", Special)
810+
793811
// BUILTIN_MISC_OPERATION_WITH_SILGEN - Miscellaneous operations that are
794812
// specially emitted during SIL generation.
795813
//

include/swift/AST/DiagnosticsSIL.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -718,6 +718,10 @@ NOTE(capturepromotion_variable_defined_here,none,
718718
ERROR(move_operator_used_on_generic_or_existential_value, none,
719719
"move() used on a generic or existential value", ())
720720

721+
// copy operator used on generic or evalue
722+
ERROR(copy_operator_used_on_generic_or_existential_value, none,
723+
"copy() used on a generic or existential value", ())
724+
721725
// noimplicitcopy on generic or existential binding
722726
ERROR(noimplicitcopy_used_on_generic_or_existential, none,
723727
"@_noImplicitCopy can not be used on a generic or existential typed "

include/swift/AST/SemanticAttrs.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ SEMANTICS_ATTR(FORCE_EMIT_OPT_REMARK_PREFIX, "optremark")
114114
SEMANTICS_ATTR(OBJC_FORBID_ASSOCIATED_OBJECTS, "objc.forbidAssociatedObjects")
115115

116116
SEMANTICS_ATTR(LIFETIMEMANAGEMENT_MOVE, "lifetimemanagement.move")
117+
SEMANTICS_ATTR(LIFETIMEMANAGEMENT_COPY, "lifetimemanagement.copy")
117118

118119
SEMANTICS_ATTR(NO_PERFORMANCE_ANALYSIS, "no_performance_analysis")
119120

include/swift/Basic/Features.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,5 +55,6 @@ 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)
5757
LANGUAGE_FEATURE(BuiltinMove, 0, "Builtin.move()", true)
58+
LANGUAGE_FEATURE(BuiltinCopy, 0, "Builtin.copy()", true)
5859

5960
#undef LANGUAGE_FEATURE

include/swift/SIL/SILBuilder.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1237,6 +1237,15 @@ class SILBuilder {
12371237
CopyValueInst(getSILDebugLocation(Loc), operand));
12381238
}
12391239

1240+
ExplicitCopyValueInst *createExplicitCopyValue(SILLocation Loc,
1241+
SILValue operand) {
1242+
assert(!operand->getType().isTrivial(getFunction()) &&
1243+
"Should not be passing trivial values to this api. Use instead "
1244+
"emitCopyValueOperation");
1245+
return insert(new (getModule())
1246+
ExplicitCopyValueInst(getSILDebugLocation(Loc), operand));
1247+
}
1248+
12401249
DestroyValueInst *createDestroyValue(SILLocation Loc, SILValue operand,
12411250
bool poisonRefs = false) {
12421251
assert(isLoadableOrOpaque(operand->getType()));

include/swift/SIL/SILCloner.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1713,6 +1713,21 @@ void SILCloner<ImplClass>::visitCopyValueInst(CopyValueInst *Inst) {
17131713
getOpValue(Inst->getOperand())));
17141714
}
17151715

1716+
template <typename ImplClass>
1717+
void SILCloner<ImplClass>::visitExplicitCopyValueInst(
1718+
ExplicitCopyValueInst *Inst) {
1719+
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
1720+
if (!getBuilder().hasOwnership()) {
1721+
SILValue newValue = getBuilder().emitCopyValueOperation(
1722+
getOpLocation(Inst->getLoc()), getOpValue(Inst->getOperand()));
1723+
return recordFoldedValue(Inst, newValue);
1724+
}
1725+
1726+
recordClonedInstruction(
1727+
Inst, getBuilder().createExplicitCopyValue(
1728+
getOpLocation(Inst->getLoc()), getOpValue(Inst->getOperand())));
1729+
}
1730+
17161731
template <typename ImplClass>
17171732
void SILCloner<ImplClass>::visitMoveValueInst(MoveValueInst *Inst) {
17181733
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));

include/swift/SIL/SILInstruction.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7267,6 +7267,15 @@ class CopyValueInst
72677267
: UnaryInstructionBase(DebugLoc, operand, operand->getType()) {}
72687268
};
72697269

7270+
class ExplicitCopyValueInst
7271+
: public UnaryInstructionBase<SILInstructionKind::ExplicitCopyValueInst,
7272+
SingleValueInstruction> {
7273+
friend class SILBuilder;
7274+
7275+
ExplicitCopyValueInst(SILDebugLocation DebugLoc, SILValue operand)
7276+
: UnaryInstructionBase(DebugLoc, operand, operand->getType()) {}
7277+
};
7278+
72707279
#define UNCHECKED_REF_STORAGE(Name, ...) \
72717280
class StrongCopy##Name##ValueInst \
72727281
: public UnaryInstructionBase< \

include/swift/SIL/SILNodes.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,10 @@ ABSTRACT_VALUE_AND_INST(SingleValueInstruction, ValueBase, SILInstruction)
575575
// A copy_value's retain semantics are fully encapsulated in OSSA
576576
// invariants. It has no side effects relative to other OSSA values.
577577
BRIDGED_SINGLE_VALUE_INST(CopyValueInst, copy_value,
578+
SingleValueInstruction, None, DoesNotRelease)
579+
// A copy_value instruction that was explicitly asked for by the user. Left
580+
// alone by OSSA optimizations.
581+
SINGLE_VALUE_INST(ExplicitCopyValueInst, explicit_copy_value,
578582
SingleValueInstruction, None, DoesNotRelease)
579583
#define UNCHECKED_REF_STORAGE(Name, name, ...) \
580584
SINGLE_VALUE_INST(StrongCopy##Name##ValueInst, strong_copy_##name##_value, \

lib/AST/ASTPrinter.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2816,6 +2816,8 @@ static bool usesFeatureBuiltinMove(Decl *decl) {
28162816
return false;
28172817
}
28182818

2819+
static bool usesFeatureBuiltinCopy(Decl *decl) { return false; }
2820+
28192821
static bool usesFeatureInheritActorContext(Decl *decl) {
28202822
if (auto func = dyn_cast<AbstractFunctionDecl>(decl)) {
28212823
for (auto param : *func->getParameters()) {

lib/AST/Builtins.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -859,6 +859,11 @@ static ValueDecl *getMoveOperation(ASTContext &ctx, Identifier id) {
859859
_parameters(_owned(_typeparam(0))), _typeparam(0));
860860
}
861861

862+
static ValueDecl *getCopyOperation(ASTContext &ctx, Identifier id) {
863+
return getBuiltinFunction(ctx, id, _thin, _generics(_unrestricted),
864+
_parameters(_typeparam(0)), _typeparam(0));
865+
}
866+
862867
static ValueDecl *getTransferArrayOperation(ASTContext &ctx, Identifier id) {
863868
return getBuiltinFunction(ctx, id, _thin,
864869
_generics(_unrestricted),
@@ -2528,6 +2533,11 @@ ValueDecl *swift::getBuiltinValueDecl(ASTContext &Context, Identifier Id) {
25282533
return nullptr;
25292534
return getMoveOperation(Context, Id);
25302535

2536+
case BuiltinValueKind::Copy:
2537+
if (!Types.empty())
2538+
return nullptr;
2539+
return getCopyOperation(Context, Id);
2540+
25312541
#define BUILTIN(id, name, Attrs)
25322542
#define BUILTIN_BINARY_OPERATION(id, name, attrs)
25332543
#define BUILTIN_BINARY_OPERATION_OVERLOADED_STATIC(id, name, attrs, overload) \

lib/IRGen/GenBuiltin.cpp

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

1322+
if (Builtin.ID == BuiltinValueKind::Copy) {
1323+
auto input = args.claimNext();
1324+
auto result = args.claimNext();
1325+
SILType addrTy = argTypes[0];
1326+
const TypeInfo &addrTI = IGF.getTypeInfo(addrTy);
1327+
Address inputAttr = addrTI.getAddressForPointer(input);
1328+
Address resultAttr = addrTI.getAddressForPointer(result);
1329+
addrTI.initializeWithCopy(IGF, resultAttr, inputAttr, addrTy, false);
1330+
return;
1331+
}
1332+
13221333
llvm_unreachable("IRGen unimplemented for this builtin!");
13231334
}

lib/IRGen/IRGenSIL.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,6 +1147,9 @@ class IRGenSILFunction :
11471147
void visitRetainValueInst(RetainValueInst *i);
11481148
void visitRetainValueAddrInst(RetainValueAddrInst *i);
11491149
void visitCopyValueInst(CopyValueInst *i);
1150+
void visitExplicitCopyValueInst(ExplicitCopyValueInst *i) {
1151+
llvm_unreachable("Valid only when ownership is enabled");
1152+
}
11501153
void visitMoveValueInst(MoveValueInst *i) {
11511154
auto e = getLoweredExplosion(i->getOperand());
11521155
setLoweredExplosion(i, e);

lib/SIL/IR/OperandOwnership.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ OPERAND_OWNERSHIP(InstantaneousUse, SetDeallocating)
203203
OPERAND_OWNERSHIP(UnownedInstantaneousUse, DebugValue)
204204
OPERAND_OWNERSHIP(UnownedInstantaneousUse, CopyBlock)
205205
OPERAND_OWNERSHIP(UnownedInstantaneousUse, CopyValue)
206+
OPERAND_OWNERSHIP(UnownedInstantaneousUse, ExplicitCopyValue)
206207
OPERAND_OWNERSHIP(UnownedInstantaneousUse, ObjCMethod)
207208
OPERAND_OWNERSHIP(UnownedInstantaneousUse, ObjCSuperMethod)
208209
OPERAND_OWNERSHIP(UnownedInstantaneousUse, UnmanagedRetainValue)
@@ -776,6 +777,7 @@ BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, GlobalStringTablePointer)
776777
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, TypePtrAuthDiscriminator)
777778
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, IntInstrprofIncrement)
778779
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, Move)
780+
BUILTIN_OPERAND_OWNERSHIP(UnownedInstantaneousUse, Copy)
779781
BUILTIN_OPERAND_OWNERSHIP(DestroyingConsume, StartAsyncLet)
780782
BUILTIN_OPERAND_OWNERSHIP(DestroyingConsume, EndAsyncLet)
781783
BUILTIN_OPERAND_OWNERSHIP(DestroyingConsume, StartAsyncLetWithLocalBuffer)

lib/SIL/IR/SILPrinter.cpp

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

1845+
void visitExplicitCopyValueInst(ExplicitCopyValueInst *I) {
1846+
*this << getIDAndType(I->getOperand());
1847+
}
1848+
18451849
void visitMoveValueInst(MoveValueInst *I) {
18461850
*this << getIDAndType(I->getOperand());
18471851
}

lib/SIL/IR/ValueOwnership.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ CONSTANT_OWNERSHIP_INST(Owned, AllocRefDynamic)
7373
CONSTANT_OWNERSHIP_INST(Owned, CopyBlock)
7474
CONSTANT_OWNERSHIP_INST(Owned, CopyBlockWithoutEscaping)
7575
CONSTANT_OWNERSHIP_INST(Owned, CopyValue)
76+
CONSTANT_OWNERSHIP_INST(Owned, ExplicitCopyValue)
7677
CONSTANT_OWNERSHIP_INST(Owned, MoveValue)
7778
CONSTANT_OWNERSHIP_INST(Owned, EndCOWMutation)
7879
CONSTANT_OWNERSHIP_INST(Owned, KeyPath)
@@ -557,6 +558,7 @@ CONSTANT_OWNERSHIP_BUILTIN(None, EndAsyncLetLifetime)
557558
CONSTANT_OWNERSHIP_BUILTIN(None, CreateTaskGroup)
558559
CONSTANT_OWNERSHIP_BUILTIN(None, DestroyTaskGroup)
559560
CONSTANT_OWNERSHIP_BUILTIN(None, Move)
561+
CONSTANT_OWNERSHIP_BUILTIN(None, Copy)
560562

561563
#undef CONSTANT_OWNERSHIP_BUILTIN
562564

lib/SIL/Parser/ParseSIL.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3138,6 +3138,7 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B,
31383138
UNARY_INSTRUCTION(IsUnique)
31393139
UNARY_INSTRUCTION(DestroyAddr)
31403140
UNARY_INSTRUCTION(CopyValue)
3141+
UNARY_INSTRUCTION(ExplicitCopyValue)
31413142
UNARY_INSTRUCTION(MoveValue)
31423143
UNARY_INSTRUCTION(EndBorrow)
31433144
UNARY_INSTRUCTION(DestructureStruct)

lib/SIL/Utils/InstructionUtils.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -704,6 +704,7 @@ RuntimeEffect swift::getRuntimeEffect(SILInstruction *inst, SILType &impactType)
704704
case SILInstructionKind::RetainValueInst:
705705
case SILInstructionKind::BeginCOWMutationInst:
706706
case SILInstructionKind::CopyValueInst:
707+
case SILInstructionKind::ExplicitCopyValueInst:
707708
case SILInstructionKind::SetDeallocatingInst:
708709
case SILInstructionKind::IsUniqueInst:
709710
case SILInstructionKind::IsEscapingClosureInst:

lib/SIL/Utils/MemAccessUtils.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2191,9 +2191,9 @@ static void visitBuiltinAddress(BuiltinInst *builtin,
21912191
visitor(&builtin->getAllOperands()[2]);
21922192
return;
21932193

2194-
// This consumes its second parameter (the arg) and takes/places that value
2195-
// into the first parameter (the result).
2194+
// These effect both operands.
21962195
case BuiltinValueKind::Move:
2196+
case BuiltinValueKind::Copy:
21972197
visitor(&builtin->getAllOperands()[1]);
21982198
return;
21992199

lib/SIL/Verifier/SILVerifier.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2001,6 +2001,22 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
20012001
require(BI->getFunction()->hasSemanticsAttr(semanticName),
20022002
"_move used within a generic context");
20032003
}
2004+
2005+
if (builtinKind == BuiltinValueKind::Copy) {
2006+
// We expect that this builtin will be specialized during transparent
2007+
// inlining into explicit_copy_value if we inline into a non-generic
2008+
// context. If the builtin still remains and is not in the specific copy
2009+
// semantic function (which is the only function marked with
2010+
// semantics::LIFETIMEMANAGEMENT_COPY), then we know that we did
2011+
// transparent inlining into a function that did not result in the Builtin
2012+
// being specialized out which is user error.
2013+
//
2014+
// NOTE: Once we have opaque values, this restriction will go away. This
2015+
// is just so we can call Builtin.copy outside of the stdlib.
2016+
auto semanticName = semantics::LIFETIMEMANAGEMENT_COPY;
2017+
require(BI->getFunction()->hasSemanticsAttr(semanticName),
2018+
"_copy used within a generic context");
2019+
}
20042020
}
20052021

20062022
void checkFunctionRefBaseInst(FunctionRefBaseInst *FRI) {

lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ struct OwnershipModelEliminatorVisitor
132132
bool visitStoreInst(StoreInst *si);
133133
bool visitStoreBorrowInst(StoreBorrowInst *si);
134134
bool visitCopyValueInst(CopyValueInst *cvi);
135+
bool visitExplicitCopyValueInst(ExplicitCopyValueInst *cvi);
135136
bool visitDestroyValueInst(DestroyValueInst *dvi);
136137
bool visitLoadBorrowInst(LoadBorrowInst *lbi);
137138
bool visitBeginBorrowInst(BeginBorrowInst *bbi) {
@@ -288,6 +289,21 @@ bool OwnershipModelEliminatorVisitor::visitCopyValueInst(CopyValueInst *cvi) {
288289
return true;
289290
}
290291

292+
bool OwnershipModelEliminatorVisitor::visitExplicitCopyValueInst(
293+
ExplicitCopyValueInst *cvi) {
294+
// A copy_value of an address-only type cannot be replaced.
295+
if (cvi->getType().isAddressOnly(*cvi->getFunction()))
296+
return false;
297+
298+
// Now that we have set the unqualified ownership flag, destroy value
299+
// operation will delegate to the appropriate strong_release, etc.
300+
withBuilder<void>(cvi, [&](SILBuilder &b, SILLocation loc) {
301+
b.emitCopyValueOperation(loc, cvi->getOperand());
302+
});
303+
eraseInstructionAndRAUW(cvi, cvi->getOperand());
304+
return true;
305+
}
306+
291307
bool OwnershipModelEliminatorVisitor::visitUnmanagedRetainValueInst(
292308
UnmanagedRetainValueInst *urvi) {
293309
// Now that we have set the unqualified ownership flag, destroy value

lib/SILOptimizer/Transforms/AccessEnforcementReleaseSinking.cpp

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

lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp

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

0 commit comments

Comments
 (0)