Skip to content

Commit 84768bc

Browse files
committed
OSSA: Add requirements on Unowned uses.
Clarify which uses are allowed to take Unowned values. Add enforcement to ensure that Unowned values are not passed to other uses. Operations that can take unowned are: - copy_value - apply/return @unowned argument - aggregates (struct, tuple, destructure, phi) - forwarding operations that are arbitrary type casts Unowned values are currently borrowed within ObjC deinitializers materialized by the Swift compiler. This will be banned as soon as SILGen is fixed.
1 parent fd1fd25 commit 84768bc

File tree

5 files changed

+186
-84
lines changed

5 files changed

+186
-84
lines changed

include/swift/SIL/SILValue.h

Lines changed: 48 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,10 @@ struct ValueOwnershipKind {
288288
llvm_unreachable("covered switch");
289289
}
290290

291-
OperandOwnership getForwardingOperandOwnership() const;
291+
/// Return the OperandOwnership for a forwarded operand when the forwarded
292+
/// result has this ValueOwnershipKind. \p allowUnowned is true for a subset
293+
/// of forwarding operations that are allowed to propagate Unowned values.
294+
OperandOwnership getForwardingOperandOwnership(bool allowUnowned) const;
292295

293296
/// Returns true if \p Other can be merged successfully with this, implying
294297
/// that the two ownership kinds are "compatibile".
@@ -626,25 +629,35 @@ enum class OperandOwnership {
626629
/// ownership but are otherwise not verified.
627630
None,
628631

629-
/// MARK: Uses of any ownership values:
630-
631-
/// Point-in-time use. Uses the value instantaneously.
632-
/// (copy_value, single-instruction apply with @guaranteed argument)
632+
/// Use the value only for the duration of the operation, which may have side
633+
/// effects. Requires an owned or guaranteed value.
634+
/// (single-instruction apply with @guaranteed argument)
633635
InstantaneousUse,
634-
// FIXME: The PointerEscape category should be eliminated. All pointer escapes
635-
// should be InteriorPointer, guarded by a borrow scope.
636+
637+
/// MARK: Uses of Any ownership values:
638+
639+
/// Use a value without requiring or propagating ownership. The operation may
640+
/// not have side-effects that could affect ownership. This is limited to a
641+
/// small number of operations that are allowed to take Unowned values.
642+
/// (copy_value, single-instruction apply with @unowned argument))
643+
UnownedInstantaneousUse,
644+
645+
/// Forwarding instruction with an Unowned result. Its operands may have any
646+
/// ownership.
647+
ForwardingUnowned,
648+
649+
// Escape a pointer into a value in a way that cannot be tracked or verified.
650+
//
651+
// TODO: Eliminate the PointerEscape category. All pointer escapes should be
652+
// InteriorPointer, guarded by a borrow scope, and verified.
636653
PointerEscape,
654+
637655
/// Bitwise escape. Escapes the nontrivial contents of the value.
638656
/// OSSA does not enforce the lifetime of the escaping bits.
639657
/// The programmer must explicitly force lifetime extension.
640658
/// (ref_to_unowned, unchecked_trivial_bitcast)
641659
BitwiseEscape,
642660

643-
/// MARK: Uses of Unowned values:
644-
645-
/// Forwarding instruction with an Unowned result must have Unowned operands.
646-
ForwardingUnowned,
647-
648661
/// MARK: Uses of Owned values:
649662

650663
/// Borrow. Propagates the owned value within a scope, without consuming it.
@@ -663,8 +676,10 @@ enum class OperandOwnership {
663676
/// scope, without ending the outer borrow scope, following stack discipline.
664677
/// (begin_borrow, begin_apply with @guaranteed).
665678
NestedBorrow,
666-
/// Interior Pointer. Propagates an address into the guaranteed value within
667-
/// the base's borrow scope. (ref_element_addr, open_existential_box)
679+
/// Interior Pointer. Propagates a trivial value (e.g. address, pointer, or
680+
/// no-escape closure) that depends on the guaranteed value within the base's
681+
/// borrow scope. The verifier checks that all uses of the trivial value are
682+
/// in scope. (ref_element_addr, open_existential_box)
668683
InteriorPointer,
669684
/// Forwarded Borrow. Propagates the guaranteed value within the base's
670685
/// borrow scope.
@@ -692,11 +707,11 @@ getOwnershipConstraint(OperandOwnership operandOwnership) {
692707
case OperandOwnership::None:
693708
return {OwnershipKind::None, UseLifetimeConstraint::NonLifetimeEnding};
694709
case OperandOwnership::InstantaneousUse:
710+
case OperandOwnership::UnownedInstantaneousUse:
711+
case OperandOwnership::ForwardingUnowned:
695712
case OperandOwnership::PointerEscape:
696713
case OperandOwnership::BitwiseEscape:
697714
return {OwnershipKind::Any, UseLifetimeConstraint::NonLifetimeEnding};
698-
case OperandOwnership::ForwardingUnowned:
699-
return {OwnershipKind::Unowned, UseLifetimeConstraint::NonLifetimeEnding};
700715
case OperandOwnership::Borrow:
701716
return {OwnershipKind::Owned, UseLifetimeConstraint::NonLifetimeEnding};
702717
case OperandOwnership::DestroyingConsume:
@@ -713,20 +728,27 @@ getOwnershipConstraint(OperandOwnership operandOwnership) {
713728
}
714729
}
715730

716-
// Forwarding instructions have a dynamic ownership kind. Their forwarded
717-
// operand constraint depends on that dynamic result ownership. If the result is
718-
// owned, then the instruction moves owned operand to its result, ending its
719-
// lifetime. If the result is guaranteed value, then the instruction propagates
720-
// the lifetime of its borrows operand through its result.
731+
/// Return the OperandOwnership for a forwarded operand when the forwarded
732+
/// result has this ValueOwnershipKind. \p allowUnowned is true for a subset
733+
/// of forwarding operations that are allowed to propagate Unowned values.
734+
///
735+
/// The ownership of a forwarded value is derived from the forwarding
736+
/// instruction's constant ownership attribute. If the result is owned, then the
737+
/// instruction moves owned operand to its result, ending its lifetime. If the
738+
/// result is guaranteed value, then the instruction propagates the lifetime of
739+
/// its borrows operand through its result.
721740
inline OperandOwnership
722-
ValueOwnershipKind::getForwardingOperandOwnership() const {
741+
ValueOwnershipKind::getForwardingOperandOwnership(bool allowUnowned) const {
723742
switch (value) {
724743
case OwnershipKind::Any:
725744
llvm_unreachable("invalid value ownership");
745+
case OwnershipKind::Unowned:
746+
if (allowUnowned) {
747+
return OperandOwnership::ForwardingUnowned;
748+
}
749+
llvm_unreachable("invalid value ownership");
726750
case OwnershipKind::None:
727751
return OperandOwnership::None;
728-
case OwnershipKind::Unowned:
729-
return OperandOwnership::ForwardingUnowned;
730752
case OwnershipKind::Guaranteed:
731753
return OperandOwnership::ForwardingBorrow;
732754
case OwnershipKind::Owned:
@@ -829,8 +851,8 @@ class Operand {
829851
}
830852

831853
/// Returns true if changing the operand to use a value with the given
832-
/// ownership kind would not cause the operand to violate the operand's
833-
/// ownership constraints. Returns false otherwise.
854+
/// ownership kind, without rewriting the instruction, would not cause the
855+
/// operand to violate the operand's ownership constraints.
834856
bool canAcceptKind(ValueOwnershipKind kind) const;
835857

836858
/// Returns true if this operand and its value satisfy the operand's

lib/SIL/IR/OperandOwnership.cpp

Lines changed: 97 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -179,26 +179,18 @@ OPERAND_OWNERSHIP(None, UnconditionalCheckedCastAddr)
179179
OPERAND_OWNERSHIP(None, AllocValueBuffer)
180180
OPERAND_OWNERSHIP(None, DeallocValueBuffer)
181181

182-
// Point-in-time uses of any ownership.
183-
OPERAND_OWNERSHIP(InstantaneousUse, CopyValue)
184-
OPERAND_OWNERSHIP(InstantaneousUse, DebugValue)
182+
// Use an owned or guaranteed value only for the duration of the operation.
185183
OPERAND_OWNERSHIP(InstantaneousUse, ExistentialMetatype)
186184
OPERAND_OWNERSHIP(InstantaneousUse, FixLifetime)
187185
OPERAND_OWNERSHIP(InstantaneousUse, WitnessMethod)
188186
OPERAND_OWNERSHIP(InstantaneousUse, DynamicMethodBranch)
189187
OPERAND_OWNERSHIP(InstantaneousUse, ValueMetatype)
190188
OPERAND_OWNERSHIP(InstantaneousUse, IsEscapingClosure)
191189
OPERAND_OWNERSHIP(InstantaneousUse, ClassMethod)
192-
OPERAND_OWNERSHIP(InstantaneousUse, ObjCMethod)
193-
OPERAND_OWNERSHIP(InstantaneousUse, ObjCSuperMethod)
194190
OPERAND_OWNERSHIP(InstantaneousUse, SuperMethod)
195191
OPERAND_OWNERSHIP(InstantaneousUse, BridgeObjectToWord)
196192
OPERAND_OWNERSHIP(InstantaneousUse, ClassifyBridgeObject)
197-
OPERAND_OWNERSHIP(InstantaneousUse, CopyBlock)
198193
OPERAND_OWNERSHIP(InstantaneousUse, SetDeallocating)
199-
OPERAND_OWNERSHIP(InstantaneousUse, UnmanagedRetainValue)
200-
OPERAND_OWNERSHIP(InstantaneousUse, UnmanagedReleaseValue)
201-
OPERAND_OWNERSHIP(InstantaneousUse, UnmanagedAutoreleaseValue)
202194
#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
203195
OPERAND_OWNERSHIP(InstantaneousUse, RefTo##Name) \
204196
OPERAND_OWNERSHIP(InstantaneousUse, Name##ToRef) \
@@ -208,6 +200,16 @@ OPERAND_OWNERSHIP(InstantaneousUse, UnmanagedAutoreleaseValue)
208200
OPERAND_OWNERSHIP(InstantaneousUse, StrongCopy##Name##Value)
209201
#include "swift/AST/ReferenceStorage.def"
210202

203+
// Unowned uses ignore the value's ownership
204+
OPERAND_OWNERSHIP(UnownedInstantaneousUse, DebugValue)
205+
OPERAND_OWNERSHIP(UnownedInstantaneousUse, CopyBlock)
206+
OPERAND_OWNERSHIP(UnownedInstantaneousUse, CopyValue)
207+
OPERAND_OWNERSHIP(UnownedInstantaneousUse, ObjCMethod)
208+
OPERAND_OWNERSHIP(UnownedInstantaneousUse, ObjCSuperMethod)
209+
OPERAND_OWNERSHIP(UnownedInstantaneousUse, UnmanagedRetainValue)
210+
OPERAND_OWNERSHIP(UnownedInstantaneousUse, UnmanagedReleaseValue)
211+
OPERAND_OWNERSHIP(UnownedInstantaneousUse, UnmanagedAutoreleaseValue)
212+
211213
// Instructions that currently violate structural ownership requirements,
212214
// and therefore completely defeat canonicalization and optimization of any
213215
// OSSA value that they use.
@@ -278,39 +280,63 @@ OPERAND_OWNERSHIP(EndBorrow, EndBorrow)
278280

279281
#undef OPERAND_OWNERSHIP
280282

281-
// Conditionally either ForwardingConsume or ForwardingBorrow, depending on
282-
// instruction result's dynamic ownership kind.
283-
#define FORWARD_ANY_OWNERSHIP(INST) \
284-
OperandOwnership \
285-
OperandOwnershipClassifier::visit##INST##Inst(INST##Inst *i) { \
286-
return i->getOwnershipKind().getForwardingOperandOwnership(); \
283+
// Forwarding operations are conditionally either ForwardingConsumes or
284+
// ForwardingBorrows, depending on the instruction's constant ownership
285+
// attribute.
286+
#define FORWARDING_OWNERSHIP(INST) \
287+
OperandOwnership OperandOwnershipClassifier::visit##INST##Inst( \
288+
INST##Inst *i) { \
289+
return i->getOwnershipKind().getForwardingOperandOwnership( \
290+
/*allowUnowned*/false); \
291+
}
292+
FORWARDING_OWNERSHIP(Object)
293+
FORWARDING_OWNERSHIP(OpenExistentialRef)
294+
FORWARDING_OWNERSHIP(ConvertFunction)
295+
FORWARDING_OWNERSHIP(RefToBridgeObject)
296+
FORWARDING_OWNERSHIP(BridgeObjectToRef)
297+
FORWARDING_OWNERSHIP(UnconditionalCheckedCast)
298+
FORWARDING_OWNERSHIP(InitExistentialRef)
299+
FORWARDING_OWNERSHIP(DifferentiableFunction)
300+
FORWARDING_OWNERSHIP(LinearFunction)
301+
#undef FORWARDING_OWNERSHIP
302+
303+
// Arbitrary value casts are forwarding instructions that are also allowed to
304+
// propagate Unowned values. If the result is Unowned, then the operand must
305+
// also be Unowned.
306+
#define FORWARDING_ANY_OWNERSHIP(INST) \
307+
OperandOwnership OperandOwnershipClassifier::visit##INST##Inst( \
308+
INST##Inst *i) { \
309+
return i->getOwnershipKind().getForwardingOperandOwnership( \
310+
/*allowUnowned*/true); \
287311
}
288-
FORWARD_ANY_OWNERSHIP(Object)
289-
FORWARD_ANY_OWNERSHIP(Enum)
290-
FORWARD_ANY_OWNERSHIP(OpenExistentialRef)
291-
FORWARD_ANY_OWNERSHIP(Upcast)
292-
FORWARD_ANY_OWNERSHIP(UncheckedRefCast)
293-
FORWARD_ANY_OWNERSHIP(ConvertFunction)
294-
FORWARD_ANY_OWNERSHIP(RefToBridgeObject)
295-
FORWARD_ANY_OWNERSHIP(BridgeObjectToRef)
296-
FORWARD_ANY_OWNERSHIP(UnconditionalCheckedCast)
297-
FORWARD_ANY_OWNERSHIP(UncheckedEnumData)
298-
FORWARD_ANY_OWNERSHIP(InitExistentialRef)
299-
FORWARD_ANY_OWNERSHIP(DifferentiableFunction)
300-
FORWARD_ANY_OWNERSHIP(LinearFunction)
301-
FORWARD_ANY_OWNERSHIP(UncheckedValueCast)
302-
FORWARD_ANY_OWNERSHIP(SwitchEnum)
303-
FORWARD_ANY_OWNERSHIP(CheckedCastBranch)
304-
FORWARD_ANY_OWNERSHIP(Return)
305-
306-
// FIXME: Guaranteed Tuple, Struct, and Destructure should be Reborrow, not
307-
// ForwardingBorrow, because the borrowed value is different on either side of
308-
// the operation and the lifetimes of borrowed members could differ.
309-
FORWARD_ANY_OWNERSHIP(Tuple)
310-
FORWARD_ANY_OWNERSHIP(Struct)
311-
FORWARD_ANY_OWNERSHIP(DestructureStruct)
312-
FORWARD_ANY_OWNERSHIP(DestructureTuple)
313-
#undef FORWARD_ANY_OWNERSHIP
312+
FORWARDING_ANY_OWNERSHIP(Upcast)
313+
FORWARDING_ANY_OWNERSHIP(UncheckedRefCast)
314+
FORWARDING_ANY_OWNERSHIP(UncheckedValueCast)
315+
FORWARDING_ANY_OWNERSHIP(CheckedCastBranch)
316+
#undef FORWARDING_ANY_OWNERSHIP
317+
318+
// Any valid ownership kind can be combined with values of None ownership, but
319+
// they cannot be combined with each other. An aggregates result ownership is
320+
// the meet of its operands' ownership. A destructured member has the same
321+
// ownership as its aggregate unless its type gives it None ownership.
322+
//
323+
// TODO: Aggregate operations should be Reborrows, not ForwardingBorrows,
324+
// because the borrowed value is different on either side of the operation and
325+
// the lifetimes of borrowed members could differ.
326+
#define AGGREGATE_OWNERSHIP(INST) \
327+
OperandOwnership OperandOwnershipClassifier::visit##INST##Inst( \
328+
INST##Inst *i) { \
329+
return i->getOwnershipKind().getForwardingOperandOwnership( \
330+
/*allowUnowned*/true); \
331+
}
332+
AGGREGATE_OWNERSHIP(Tuple)
333+
AGGREGATE_OWNERSHIP(Struct)
334+
AGGREGATE_OWNERSHIP(DestructureStruct)
335+
AGGREGATE_OWNERSHIP(DestructureTuple)
336+
AGGREGATE_OWNERSHIP(Enum)
337+
AGGREGATE_OWNERSHIP(UncheckedEnumData)
338+
AGGREGATE_OWNERSHIP(SwitchEnum)
339+
#undef AGGREGATE_OWNERSHIP
314340

315341
// A begin_borrow is conditionally nested.
316342
OperandOwnership
@@ -321,7 +347,10 @@ OperandOwnershipClassifier::visitBeginBorrowInst(BeginBorrowInst *borrow) {
321347
case OwnershipKind::None:
322348
return OperandOwnership::None;
323349
case OwnershipKind::Unowned:
324-
return OperandOwnership::InstantaneousUse;
350+
// FIXME: disallow borrowing an Unowned value. Temporarily model it as an
351+
// instantaneous use until SILGenFunction::emitClassMemberDestruction is
352+
// fixed.
353+
return OperandOwnership::UnownedInstantaneousUse;
325354
case OwnershipKind::Guaranteed:
326355
return OperandOwnership::NestedBorrow;
327356
case OwnershipKind::Owned:
@@ -344,17 +373,21 @@ OperandOwnershipClassifier::visitSelectEnumInst(SelectEnumInst *i) {
344373
if (getValue() == i->getEnumOperand()) {
345374
return OperandOwnership::InstantaneousUse;
346375
}
347-
return getOwnershipKind().getForwardingOperandOwnership();
376+
return getOwnershipKind().getForwardingOperandOwnership(
377+
/*allowUnowned*/true);
348378
}
349379

350380
OperandOwnership OperandOwnershipClassifier::visitBranchInst(BranchInst *bi) {
351381
ValueOwnershipKind destBlockArgOwnershipKind =
352382
bi->getDestBB()->getArgument(getOperandIndex())->getOwnershipKind();
353383

384+
// FIXME: remove this special case once all aggregate operations behave just
385+
// like phis.
354386
if (destBlockArgOwnershipKind == OwnershipKind::Guaranteed) {
355387
return OperandOwnership::Reborrow;
356388
}
357-
return destBlockArgOwnershipKind.getForwardingOperandOwnership();
389+
return destBlockArgOwnershipKind.getForwardingOperandOwnership(
390+
/*allowUnowned*/true);
358391
}
359392

360393
OperandOwnership
@@ -378,9 +411,11 @@ static OperandOwnership getFunctionArgOwnership(SILArgumentConvention argConv) {
378411
case SILArgumentConvention::Indirect_In_Constant:
379412
case SILArgumentConvention::Indirect_In_Guaranteed:
380413
case SILArgumentConvention::Direct_Guaranteed:
381-
case SILArgumentConvention::Direct_Unowned:
382414
return OperandOwnership::InstantaneousUse;
383415

416+
case SILArgumentConvention::Direct_Unowned:
417+
return OperandOwnership::UnownedInstantaneousUse;
418+
384419
case SILArgumentConvention::Indirect_Out:
385420
case SILArgumentConvention::Indirect_Inout:
386421
case SILArgumentConvention::Indirect_InoutAliasable:
@@ -445,6 +480,20 @@ OperandOwnership OperandOwnershipClassifier::visitYieldInst(YieldInst *i) {
445480
return getFunctionArgOwnership(argConv);
446481
}
447482

483+
OperandOwnership OperandOwnershipClassifier::visitReturnInst(ReturnInst *i) {
484+
switch (i->getOwnershipKind()) {
485+
case OwnershipKind::Any:
486+
case OwnershipKind::Guaranteed:
487+
llvm_unreachable("invalid value ownership");
488+
case OwnershipKind::None:
489+
return OperandOwnership::None;
490+
case OwnershipKind::Unowned:
491+
return OperandOwnership::UnownedInstantaneousUse;
492+
case OwnershipKind::Owned:
493+
return OperandOwnership::ForwardingConsume;
494+
}
495+
}
496+
448497
OperandOwnership OperandOwnershipClassifier::visitAssignInst(AssignInst *i) {
449498
if (getValue() != i->getSrc()) {
450499
return OperandOwnership::None;
@@ -476,14 +525,15 @@ OperandOwnership OperandOwnershipClassifier::visitCopyBlockWithoutEscapingInst(
476525
if (getValue() == i->getClosure()) {
477526
return OperandOwnership::ForwardingConsume;
478527
}
479-
return OperandOwnership::InstantaneousUse;
528+
return OperandOwnership::UnownedInstantaneousUse;
480529
}
481530

482531
OperandOwnership
483532
OperandOwnershipClassifier::visitMarkDependenceInst(MarkDependenceInst *mdi) {
484533
// If we are analyzing "the value", we forward ownership.
485534
if (getValue() == mdi->getValue()) {
486-
return getOwnershipKind().getForwardingOperandOwnership();
535+
return getOwnershipKind().getForwardingOperandOwnership(
536+
/*allowUnowned*/true);
487537
}
488538
// FIXME: Add an end_dependence instruction so we can treat mark_dependence as
489539
// a borrow of the base (mark_dependence %base -> end_dependence is analogous

0 commit comments

Comments
 (0)