Skip to content

Commit accc282

Browse files
authored
Merge pull request #60511 from gottesmm/pr-0796eed23b86f5812f817b15b928dbe11485c361
[move-only-addresses] Slicing some patches off of the larger patch.
2 parents 0d94fb1 + 5baf2af commit accc282

20 files changed

+298
-25
lines changed

docs/SIL.rst

+56-18
Original file line numberDiff line numberDiff line change
@@ -2762,6 +2762,32 @@ sil-opt, one will see that we actually have an ownership violation due to the
27622762
two uses of "value", one for initializing value2 and the other for the return
27632763
value.
27642764

2765+
Move Only Types
2766+
---------------
2767+
2768+
NOTE: This is experimental and is just an attempt to describe where the design
2769+
is currently for others reading SIL today. It should not be interpreted as
2770+
final.
2771+
2772+
Currently there are two kinds of "move only types" in SIL: pure move only types
2773+
that are always move only and move only wrapped types that are move only
2774+
versions of copyable types. The invariant that values of Move Only type obey is
2775+
that they can only be copied (e.x.: operand to a `copy_value`_, ``copy_addr [init]``) during the
2776+
guaranteed passes when we are in Raw SIL. Once we are in non-Raw SIL though
2777+
(i.e. Canonical and later SIL stages), a program is ill formed if one copies a
2778+
move only type.
2779+
2780+
The reason why we have this special rule for move only types is that this allows
2781+
for SIL code generators to insert copies and then have a later guaranteed
2782+
checker optimization pass recover the underlying move only semantics by
2783+
reconstructing needed copies and removing unneeded copies using Ownership
2784+
SSA. If any such copies are actually needed according to Ownership SSA, the
2785+
checker pass emits a diagnostic stating that move semantics have been
2786+
violated. If such a diagnostic is emitted then the checker pass transforms all
2787+
copies on move only types to their explicit copy forms to ensure that once we
2788+
leave the diagnostic passes and enter canonical SIL, our "copy" invariant is
2789+
maintained.
2790+
27652791
Runtime Failure
27662792
---------------
27672793

@@ -4219,6 +4245,25 @@ operations::
42194245
If ``T`` is a trivial type, then ``copy_addr`` is always equivalent to its
42204246
take-initialization form.
42214247

4248+
It is illegal in non-Raw SIL to apply ``copy_addr [init]`` to a value that is
4249+
move only.
4250+
4251+
explicit_copy_addr
4252+
``````````````````
4253+
::
4254+
4255+
sil-instruction ::= 'explicit_copy_addr' '[take]'? sil-value
4256+
'to' '[initialization]'? sil-operand
4257+
4258+
explicit_copy_addr [take] %0 to [initialization] %1 : $*T
4259+
// %0 and %1 must be of the same $*T address type
4260+
4261+
This instruction is exactly the same as `copy_addr`_ except that it has special
4262+
behavior for move only types. Specifically, an `explicit_copy_addr`_ is viewed
4263+
as a copy_addr that is allowed on values that are move only. This is only used
4264+
by a move checker after it has emitted an error diagnostic to preserve the
4265+
general ``copy_addr [init]`` ban in Canonical SIL on move only types.
4266+
42224267
destroy_addr
42234268
````````````
42244269
::
@@ -5557,6 +5602,8 @@ independent of the operand. In terms of specific types:
55575602
In ownership qualified functions, a ``copy_value`` produces a +1 value that must
55585603
be consumed at most once along any path through the program.
55595604

5605+
It is illegal in non-Raw SIL to `copy_value`_ a value that is "move only".
5606+
55605607
explicit_copy_value
55615608
```````````````````
55625609

@@ -5566,27 +5613,18 @@ explicit_copy_value
55665613

55675614
%1 = explicit_copy_value %0 : $A
55685615

5569-
Performs a copy of a loadable value as if by the value's type lowering and
5570-
returns the copy. The returned copy semantically is a value that is completely
5571-
independent of the operand. In terms of specific types:
5572-
5573-
1. For trivial types, this is equivalent to just propagating through the trivial
5574-
value.
5575-
2. For reference types, this is equivalent to performing a ``strong_retain``
5576-
operation and returning the reference.
5577-
3. For ``@unowned`` types, this is equivalent to performing an
5578-
``unowned_retain`` and returning the operand.
5579-
4. For aggregate types, this is equivalent to recursively performing a
5580-
``copy_value`` on its components, forming a new aggregate from the copied
5581-
components, and then returning the new aggregate.
5582-
5583-
In ownership qualified functions, a ``explicit_copy_value`` produces a +1 value
5584-
that must be consumed at most once along any path through the program.
5585-
5586-
When move only variable checking is performed, ``explicit_copy_value`` is
5616+
This is exactly the same instruction semantically as `copy_value`_ with the
5617+
exception that when move only checking is performed, `explicit_copy_value`_ is
55875618
treated as an explicit copy asked for by the user that should not be rewritten
55885619
and should be treated as a non-consuming use.
55895620

5621+
This is used for two things:
5622+
5623+
1. Implementing a copy builtin for no implicit copy types.
5624+
2. To enable the move checker, once it has emitted an error diagnostic, to still
5625+
produce valid Ownership SSA SIL at the end of the guaranteed optimization
5626+
pipeline when we enter the Canonical SIL stage.
5627+
55905628
move_value
55915629
``````````
55925630

include/swift/SIL/SILBuilder.h

+8
Original file line numberDiff line numberDiff line change
@@ -1037,6 +1037,14 @@ class SILBuilder {
10371037
getSILDebugLocation(Loc), srcAddr, destAddr, isTake, isInitialize));
10381038
}
10391039

1040+
ExplicitCopyAddrInst *
1041+
createExplicitCopyAddr(SILLocation Loc, SILValue srcAddr, SILValue destAddr,
1042+
IsTake_t isTake, IsInitialization_t isInitialize) {
1043+
assert(srcAddr->getType() == destAddr->getType());
1044+
return insert(new (getModule()) ExplicitCopyAddrInst(
1045+
getSILDebugLocation(Loc), srcAddr, destAddr, isTake, isInitialize));
1046+
}
1047+
10401048
BindMemoryInst *createBindMemory(SILLocation Loc, SILValue base,
10411049
SILValue index, SILType boundType) {
10421050
return insert(BindMemoryInst::create(getSILDebugLocation(Loc), base, index,

include/swift/SIL/SILCloner.h

+11
Original file line numberDiff line numberDiff line change
@@ -1383,6 +1383,17 @@ SILCloner<ImplClass>::visitCopyAddrInst(CopyAddrInst *Inst) {
13831383
Inst->isInitializationOfDest()));
13841384
}
13851385

1386+
template <typename ImplClass>
1387+
void SILCloner<ImplClass>::visitExplicitCopyAddrInst(
1388+
ExplicitCopyAddrInst *Inst) {
1389+
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
1390+
recordClonedInstruction(
1391+
Inst, getBuilder().createExplicitCopyAddr(
1392+
getOpLocation(Inst->getLoc()), getOpValue(Inst->getSrc()),
1393+
getOpValue(Inst->getDest()), Inst->isTakeOfSrc(),
1394+
Inst->isInitializationOfDest()));
1395+
}
1396+
13861397
template <typename ImplClass>
13871398
void SILCloner<ImplClass>::visitMarkUnresolvedMoveAddrInst(
13881399
MarkUnresolvedMoveAddrInst *Inst) {

include/swift/SIL/SILInstruction.h

+42
Original file line numberDiff line numberDiff line change
@@ -5000,6 +5000,48 @@ class CopyAddrInst
50005000
MutableArrayRef<Operand> getAllOperands() { return Operands.asArray(); }
50015001
};
50025002

5003+
/// ExplicitCopyAddrInst - A copy_addr that should not be optimized and should
5004+
/// be viewed
5005+
class ExplicitCopyAddrInst
5006+
: public InstructionBase<SILInstructionKind::ExplicitCopyAddrInst,
5007+
NonValueInstruction>,
5008+
public CopyLikeInstruction {
5009+
friend SILBuilder;
5010+
5011+
private:
5012+
FixedOperandList<2> Operands;
5013+
USE_SHARED_UINT8;
5014+
5015+
ExplicitCopyAddrInst(SILDebugLocation DebugLoc, SILValue Src, SILValue Dest,
5016+
IsTake_t isTakeOfSrc,
5017+
IsInitialization_t isInitializationOfDest);
5018+
5019+
public:
5020+
SILValue getSrc() const { return Operands[Src].get(); }
5021+
SILValue getDest() const { return Operands[Dest].get(); }
5022+
5023+
void setSrc(SILValue V) { Operands[Src].set(V); }
5024+
void setDest(SILValue V) { Operands[Dest].set(V); }
5025+
5026+
IsTake_t isTakeOfSrc() const {
5027+
return IsTake_t(sharedUInt8().ExplicitCopyAddrInst.isTakeOfSrc);
5028+
}
5029+
IsInitialization_t isInitializationOfDest() const {
5030+
return IsInitialization_t(
5031+
sharedUInt8().ExplicitCopyAddrInst.isInitializationOfDest);
5032+
}
5033+
5034+
void setIsTakeOfSrc(IsTake_t T) {
5035+
sharedUInt8().ExplicitCopyAddrInst.isTakeOfSrc = (bool)T;
5036+
}
5037+
void setIsInitializationOfDest(IsInitialization_t I) {
5038+
sharedUInt8().ExplicitCopyAddrInst.isInitializationOfDest = (bool)I;
5039+
}
5040+
5041+
ArrayRef<Operand> getAllOperands() const { return Operands.asArray(); }
5042+
MutableArrayRef<Operand> getAllOperands() { return Operands.asArray(); }
5043+
};
5044+
50035045
/// "%token = bind_memory %0 : $Builtin.RawPointer, %1 : $Builtin.Word to $T"
50045046
///
50055047
/// Binds memory at the raw pointer %0 to type $T with enough capacity

include/swift/SIL/SILNode.h

+4
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,10 @@ class alignas(8) SILNode :
219219
isTakeOfSrc : 1,
220220
isInitializationOfDest : 1);
221221

222+
SHARED_FIELD(ExplicitCopyAddrInst, uint8_t
223+
isTakeOfSrc : 1,
224+
isInitializationOfDest : 1);
225+
222226
SHARED_FIELD(PointerToAddressInst, uint8_t
223227
isStrict : 1,
224228
isInvariant : 1);

include/swift/SIL/SILNodes.def

+2
Original file line numberDiff line numberDiff line change
@@ -781,6 +781,8 @@ BRIDGED_NON_VALUE_INST(DebugValueInst, debug_value,
781781
#include "swift/AST/ReferenceStorage.def"
782782
BRIDGED_NON_VALUE_INST(CopyAddrInst, copy_addr,
783783
SILInstruction, MayHaveSideEffects, MayRelease)
784+
BRIDGED_NON_VALUE_INST(ExplicitCopyAddrInst, explicit_copy_addr,
785+
SILInstruction, MayHaveSideEffects, MayRelease)
784786
BRIDGED_NON_VALUE_INST(DestroyAddrInst, destroy_addr,
785787
SILInstruction, MayHaveSideEffects, MayRelease)
786788
NON_VALUE_INST(EndLifetimeInst, end_lifetime,

lib/IRGen/IRGenSIL.cpp

+25
Original file line numberDiff line numberDiff line change
@@ -1310,6 +1310,7 @@ class IRGenSILFunction :
13101310
void visitDeallocPartialRefInst(DeallocPartialRefInst *i);
13111311

13121312
void visitCopyAddrInst(CopyAddrInst *i);
1313+
void visitExplicitCopyAddrInst(ExplicitCopyAddrInst *i);
13131314
void visitMarkUnresolvedMoveAddrInst(MarkUnresolvedMoveAddrInst *mai) {
13141315
llvm_unreachable("Valid only when ownership is enabled");
13151316
}
@@ -6884,6 +6885,30 @@ void IRGenSILFunction::visitCopyAddrInst(swift::CopyAddrInst *i) {
68846885
}
68856886
}
68866887

6888+
void IRGenSILFunction::visitExplicitCopyAddrInst(
6889+
swift::ExplicitCopyAddrInst *i) {
6890+
SILType addrTy = i->getSrc()->getType();
6891+
const TypeInfo &addrTI = getTypeInfo(addrTy);
6892+
Address src = getLoweredAddress(i->getSrc());
6893+
// See whether we have a deferred fixed-size buffer initialization.
6894+
auto &loweredDest = getLoweredValue(i->getDest());
6895+
assert(!loweredDest.isUnallocatedAddressInBuffer());
6896+
Address dest = loweredDest.getAnyAddress();
6897+
if (i->isInitializationOfDest()) {
6898+
if (i->isTakeOfSrc()) {
6899+
addrTI.initializeWithTake(*this, dest, src, addrTy, false);
6900+
} else {
6901+
addrTI.initializeWithCopy(*this, dest, src, addrTy, false);
6902+
}
6903+
} else {
6904+
if (i->isTakeOfSrc()) {
6905+
addrTI.assignWithTake(*this, dest, src, addrTy, false);
6906+
} else {
6907+
addrTI.assignWithCopy(*this, dest, src, addrTy, false);
6908+
}
6909+
}
6910+
}
6911+
68876912
// bind_memory and rebind_memory are no-ops because Swift TBAA info is not
68886913
// lowered to LLVM IR TBAA, and the output token is ignored except for
68896914
// verification.

lib/SIL/IR/OperandOwnership.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ OPERAND_OWNERSHIP(TrivialUse, CheckedCastAddrBranch)
147147
OPERAND_OWNERSHIP(TrivialUse, CondBranch)
148148
OPERAND_OWNERSHIP(TrivialUse, CondFail)
149149
OPERAND_OWNERSHIP(TrivialUse, CopyAddr)
150+
OPERAND_OWNERSHIP(TrivialUse, ExplicitCopyAddr)
150151
OPERAND_OWNERSHIP(TrivialUse, MarkUnresolvedMoveAddr)
151152
OPERAND_OWNERSHIP(TrivialUse, DeallocStack)
152153
OPERAND_OWNERSHIP(TrivialUse, DeinitExistentialAddr)

lib/SIL/IR/SILInstructions.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -1172,6 +1172,15 @@ CopyAddrInst::CopyAddrInst(SILDebugLocation Loc, SILValue SrcLValue,
11721172
bool(isInitializationOfDest);
11731173
}
11741174

1175+
ExplicitCopyAddrInst::ExplicitCopyAddrInst(
1176+
SILDebugLocation Loc, SILValue SrcLValue, SILValue DestLValue,
1177+
IsTake_t isTakeOfSrc, IsInitialization_t isInitializationOfDest)
1178+
: InstructionBase(Loc), Operands(this, SrcLValue, DestLValue) {
1179+
sharedUInt8().ExplicitCopyAddrInst.isTakeOfSrc = bool(isTakeOfSrc);
1180+
sharedUInt8().ExplicitCopyAddrInst.isInitializationOfDest =
1181+
bool(isInitializationOfDest);
1182+
}
1183+
11751184
BindMemoryInst *
11761185
BindMemoryInst::create(SILDebugLocation Loc, SILValue Base, SILValue Index,
11771186
SILType BoundType, SILFunction &F) {

lib/SIL/IR/SILPrinter.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -1749,6 +1749,15 @@ class SILPrinter : public SILInstructionVisitor<SILPrinter> {
17491749
*this << getIDAndType(CI->getDest());
17501750
}
17511751

1752+
void visitExplicitCopyAddrInst(ExplicitCopyAddrInst *CI) {
1753+
if (CI->isTakeOfSrc())
1754+
*this << "[take] ";
1755+
*this << Ctx.getID(CI->getSrc()) << " to ";
1756+
if (CI->isInitializationOfDest())
1757+
*this << "[initialization] ";
1758+
*this << getIDAndType(CI->getDest());
1759+
}
1760+
17521761
void visitMarkUnresolvedMoveAddrInst(MarkUnresolvedMoveAddrInst *CI) {
17531762
*this << Ctx.getID(CI->getSrc()) << " to ";
17541763
*this << getIDAndType(CI->getDest());

lib/SIL/Parser/ParseSIL.cpp

+31
Original file line numberDiff line numberDiff line change
@@ -4920,6 +4920,37 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B,
49204920
IsInitialization_t(IsInit));
49214921
break;
49224922
}
4923+
case SILInstructionKind::ExplicitCopyAddrInst: {
4924+
bool IsTake = false, IsInit = false;
4925+
UnresolvedValueName SrcLName;
4926+
SILValue DestLVal;
4927+
SourceLoc ToLoc, DestLoc;
4928+
Identifier ToToken;
4929+
if (parseSILOptional(IsTake, *this, "take") || parseValueName(SrcLName) ||
4930+
parseSILIdentifier(ToToken, ToLoc, diag::expected_tok_in_sil_instr,
4931+
"to") ||
4932+
parseSILOptional(IsInit, *this, "initialization") ||
4933+
parseTypedValueRef(DestLVal, DestLoc, B) ||
4934+
parseSILDebugLocation(InstLoc, B))
4935+
return true;
4936+
4937+
if (ToToken.str() != "to") {
4938+
P.diagnose(ToLoc, diag::expected_tok_in_sil_instr, "to");
4939+
return true;
4940+
}
4941+
4942+
if (!DestLVal->getType().isAddress()) {
4943+
P.diagnose(DestLoc, diag::sil_invalid_instr_operands);
4944+
return true;
4945+
}
4946+
4947+
SILValue SrcLVal =
4948+
getLocalValue(SrcLName, DestLVal->getType(), InstLoc, B);
4949+
ResultVal =
4950+
B.createExplicitCopyAddr(InstLoc, SrcLVal, DestLVal, IsTake_t(IsTake),
4951+
IsInitialization_t(IsInit));
4952+
break;
4953+
}
49234954
case SILInstructionKind::MarkUnresolvedMoveAddrInst: {
49244955
UnresolvedValueName SrcLName;
49254956
SILValue DestLVal;

lib/SIL/Utils/InstructionUtils.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,15 @@ RuntimeEffect swift::getRuntimeEffect(SILInstruction *inst, SILType &impactType)
630630
return RuntimeEffect::MetaData | RuntimeEffect::RefCounting;
631631
return RuntimeEffect::MetaData;
632632
}
633+
case SILInstructionKind::ExplicitCopyAddrInst: {
634+
auto *ca = cast<ExplicitCopyAddrInst>(inst);
635+
impactType = ca->getSrc()->getType();
636+
if (!ca->isInitializationOfDest())
637+
return RuntimeEffect::MetaData | RuntimeEffect::Releasing;
638+
if (!ca->isTakeOfSrc())
639+
return RuntimeEffect::MetaData | RuntimeEffect::RefCounting;
640+
return RuntimeEffect::MetaData;
641+
}
633642
// Equivalent to a copy_addr [init]
634643
case SILInstructionKind::MarkUnresolvedMoveAddrInst: {
635644
return RuntimeEffect::MetaData | RuntimeEffect::RefCounting;

lib/SIL/Verifier/SILVerifier.cpp

+12-6
Original file line numberDiff line numberDiff line change
@@ -2189,6 +2189,10 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
21892189
"Load with unqualified ownership in a qualified function");
21902190
break;
21912191
case LoadOwnershipQualifier::Copy:
2192+
require(LI->getModule().getStage() == SILStage::Raw ||
2193+
!LI->getOperand()->getType().isMoveOnly(),
2194+
"'MoveOnly' types can only be copied in Raw SIL?!");
2195+
[[fallthrough]];
21922196
case LoadOwnershipQualifier::Take:
21932197
require(F.hasOwnership(),
21942198
"Load with qualified ownership in an unqualified function");
@@ -2655,15 +2659,17 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
26552659
require(Elt->getType().isAddress(), "MFE must refer to variable addrs");
26562660
}
26572661

2658-
void checkCopyAddrInst(CopyAddrInst *SI) {
2659-
require(SI->getSrc()->getType().isAddress(),
2660-
"Src value should be lvalue");
2661-
require(SI->getDest()->getType().isAddress(),
2662+
void checkCopyAddrInst(CopyAddrInst *cai) {
2663+
require(cai->getSrc()->getType().isAddress(), "Src value should be lvalue");
2664+
require(cai->getDest()->getType().isAddress(),
26622665
"Dest address should be lvalue");
2663-
requireSameType(SI->getDest()->getType(), SI->getSrc()->getType(),
2666+
requireSameType(cai->getDest()->getType(), cai->getSrc()->getType(),
26642667
"Store operand type and dest type mismatch");
2665-
require(F.isTypeABIAccessible(SI->getDest()->getType()),
2668+
require(F.isTypeABIAccessible(cai->getDest()->getType()),
26662669
"cannot directly copy type with inaccessible ABI");
2670+
require(cai->getModule().getStage() == SILStage::Raw ||
2671+
(cai->isTakeOfSrc() || !cai->getSrc()->getType().isMoveOnly()),
2672+
"'MoveOnly' types can only be copied in Raw SIL?!");
26672673
}
26682674

26692675
void checkMarkUnresolvedMoveAddrInst(MarkUnresolvedMoveAddrInst *SI) {

lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,7 @@ static bool hasOpaqueArchetype(TypeExpansionContext context,
314314
case SILInstructionKind::Store##Name##Inst:
315315
#include "swift/AST/ReferenceStorage.def"
316316
case SILInstructionKind::CopyAddrInst:
317+
case SILInstructionKind::ExplicitCopyAddrInst:
317318
case SILInstructionKind::MarkUnresolvedMoveAddrInst:
318319
case SILInstructionKind::DestroyAddrInst:
319320
case SILInstructionKind::EndLifetimeInst:

lib/SILOptimizer/Utils/SILInliner.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -884,6 +884,7 @@ InlineCost swift::instructionInlineCost(SILInstruction &I) {
884884
case SILInstructionKind::CopyBlockInst:
885885
case SILInstructionKind::CopyBlockWithoutEscapingInst:
886886
case SILInstructionKind::CopyAddrInst:
887+
case SILInstructionKind::ExplicitCopyAddrInst:
887888
case SILInstructionKind::MarkUnresolvedMoveAddrInst:
888889
case SILInstructionKind::RetainValueInst:
889890
case SILInstructionKind::RetainValueAddrInst:

0 commit comments

Comments
 (0)