Skip to content

Commit c9283e3

Browse files
authored
Merge pull request swiftlang#35130 from atrick/unowneduse
OSSA: Add strict requirements on Unowned uses.
2 parents e7d3a4f + 84768bc commit c9283e3

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)