Skip to content

Commit fd1fd25

Browse files
authored
Merge pull request #35024 from atrick/useownership
Introduce OperandOwnership to classify OSSA uses.
2 parents 043e709 + b1dba25 commit fd1fd25

File tree

8 files changed

+797
-818
lines changed

8 files changed

+797
-818
lines changed

include/swift/SIL/ApplySite.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ class ApplySite {
337337
}
338338

339339
/// Return the SILArgumentConvention for the given applied argument operand.
340-
SILArgumentConvention getArgumentConvention(Operand &oper) const {
340+
SILArgumentConvention getArgumentConvention(const Operand &oper) const {
341341
unsigned calleeArgIdx =
342342
getCalleeArgIndexOfFirstAppliedArg() + getAppliedArgIndex(oper);
343343
return getSubstCalleeConv().getSILArgumentConvention(calleeArgIdx);
@@ -615,7 +615,8 @@ class FullApplySite : public ApplySite {
615615
/// Returns true if \p op is an operand that passes an indirect
616616
/// result argument to the apply site.
617617
bool isIndirectResultOperand(const Operand &op) const {
618-
return getCalleeArgIndex(op) < getNumIndirectSILResults();
618+
return isArgumentOperand(op)
619+
&& (getCalleeArgIndex(op) < getNumIndirectSILResults());
619620
}
620621

621622
static FullApplySite getFromOpaqueValue(void *p) { return FullApplySite(p); }

include/swift/SIL/OwnershipUtils.h

Lines changed: 38 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -32,29 +32,47 @@ class DeadEndBlocks;
3232
/// Returns true if v is an address or trivial.
3333
bool isValueAddressOrTrivial(SILValue v);
3434

35-
/// Is this an operand that can forward both owned and guaranteed ownership into
36-
/// one of the operand's owner instruction's result.
37-
bool isOwnershipForwardingUse(Operand *op);
38-
39-
/// Is this an operand that can forward guaranteed ownership into one of the
40-
/// operand's owner instruction's result.
41-
bool isGuaranteedForwardingUse(Operand *op);
42-
43-
/// Is this an operand that can forward owned ownership into one of the
44-
/// operand's owner instruction's result.
45-
bool isOwnedForwardingUse(Operand *use);
35+
/// Is the opcode that produces \p value capable of forwarding guaranteed
36+
/// values?
37+
///
38+
/// This may be true even if the current instance of the instruction is not a
39+
/// ForwardingBorrow. If true, then the operation may be trivially rewritten
40+
/// with Guaranteed ownership.
41+
bool canOpcodeForwardGuaranteedValues(SILValue value);
4642

47-
/// Is this a value that is the result of an instruction that forwards
48-
/// guaranteed ownership from one of its operands.
49-
bool isGuaranteedForwardingValue(SILValue value);
43+
/// Is the opcode that consumes \p use capable of forwarding guaranteed values?
44+
///
45+
/// This may be true even if \p use is not a ForwardingBorrow. If true, then the
46+
/// operation may be trivially rewritten with Guaranteed ownership.
47+
bool canOpcodeForwardGuaranteedValues(Operand *use);
48+
49+
// This is the use-def equivalent of use->getOperandOwnership() ==
50+
// OperandOwnership::ForwardingBorrow.
51+
inline bool isForwardingBorrow(SILValue value) {
52+
assert(value.getOwnershipKind() == OwnershipKind::Guaranteed);
53+
return canOpcodeForwardGuaranteedValues(value);
54+
}
55+
56+
/// Is the opcode that produces \p value capable of forwarding owned values?
57+
///
58+
/// This may be true even if the current instance of the instruction is not a
59+
/// ForwardingConsume. If true, then the operation may be trivially rewritten
60+
/// with Owned ownership.
61+
bool canOpcodeForwardOwnedValues(SILValue value);
5062

51-
/// Is this value the result of an instruction that 'forward's owned ownership,
52-
/// but may not be able to forward guaranteed ownership.
63+
/// Is this opcode that consumes \p use capable of forwarding owned values?
5364
///
54-
/// This will be either a multiple value instruction resuilt, a single value
55-
/// instruction that forwards or an argument that forwards the ownership from a
56-
/// previous terminator.
57-
bool isOwnedForwardingValue(SILValue value);
65+
/// This may be true even if the current instance of the instruction is not a
66+
/// ForwardingConsume. If true, then the operation may be trivially rewritten
67+
/// with Owned ownership.
68+
bool canOpcodeForwardOwnedValues(Operand *use);
69+
70+
// This is the use-def equivalent of use->getOperandOwnership() ==
71+
// OperandOwnership::ForwardingConsume.
72+
inline bool isForwardingConsume(SILValue value) {
73+
assert(value.getOwnershipKind() == OwnershipKind::Owned);
74+
return canOpcodeForwardOwnedValues(value);
75+
}
5876

5977
class ForwardingOperand {
6078
Operand *use;
@@ -225,25 +243,6 @@ struct BorrowingOperand {
225243
llvm_unreachable("Covered switch isn't covered?!");
226244
}
227245

228-
/// Is this a borrow scope operand that can open new borrow scopes
229-
/// for owned values.
230-
bool canAcceptOwnedValues() const {
231-
switch (kind) {
232-
// begin_borrow can take any parameter
233-
case BorrowingOperandKind::BeginBorrow:
234-
// Yield can implicit borrow owned values.
235-
case BorrowingOperandKind::Yield:
236-
// FullApplySites can implicit borrow owned values.
237-
case BorrowingOperandKind::BeginApply:
238-
case BorrowingOperandKind::Apply:
239-
case BorrowingOperandKind::TryApply:
240-
return true;
241-
case BorrowingOperandKind::Branch:
242-
return false;
243-
}
244-
llvm_unreachable("Covered switch isn't covered?!");
245-
}
246-
247246
/// Is the result of this instruction also a borrow introducer?
248247
///
249248
/// TODO: This needs a better name.

include/swift/SIL/SILValue.h

Lines changed: 144 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,8 @@ struct OwnershipKind {
226226

227227
llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const OwnershipKind &kind);
228228

229+
enum class OperandOwnership;
230+
229231
/// A value representing the specific ownership semantics that a SILValue may
230232
/// have.
231233
struct ValueOwnershipKind {
@@ -286,6 +288,8 @@ struct ValueOwnershipKind {
286288
llvm_unreachable("covered switch");
287289
}
288290

291+
OperandOwnership getForwardingOperandOwnership() const;
292+
289293
/// Returns true if \p Other can be merged successfully with this, implying
290294
/// that the two ownership kinds are "compatibile".
291295
///
@@ -597,12 +601,6 @@ class OwnershipConstraint {
597601
return lifetimeConstraint;
598602
}
599603

600-
/// Return a constraint that is appropriate for an operand that can accept a
601-
/// value with any ownership kind without ending said value's lifetime.
602-
static OwnershipConstraint any() {
603-
return {OwnershipKind::Any, UseLifetimeConstraint::NonLifetimeEnding};
604-
}
605-
606604
bool satisfiedBy(const Operand *use) const;
607605

608606
bool satisfiesConstraint(ValueOwnershipKind testKind) const {
@@ -618,6 +616,124 @@ class OwnershipConstraint {
618616
llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
619617
OwnershipConstraint constraint);
620618

619+
/// Categorize all uses in terms of their ownership effect.
620+
///
621+
/// Used to verify completeness of the ownership use model and exhaustively
622+
/// switch over any category of ownership use. Implies ownership constraints and
623+
/// lifetime constraints.
624+
enum class OperandOwnership {
625+
/// Uses of ownership None. These uses are incompatible with values that have
626+
/// ownership but are otherwise not verified.
627+
None,
628+
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)
633+
InstantaneousUse,
634+
// FIXME: The PointerEscape category should be eliminated. All pointer escapes
635+
// should be InteriorPointer, guarded by a borrow scope.
636+
PointerEscape,
637+
/// Bitwise escape. Escapes the nontrivial contents of the value.
638+
/// OSSA does not enforce the lifetime of the escaping bits.
639+
/// The programmer must explicitly force lifetime extension.
640+
/// (ref_to_unowned, unchecked_trivial_bitcast)
641+
BitwiseEscape,
642+
643+
/// MARK: Uses of Unowned values:
644+
645+
/// Forwarding instruction with an Unowned result must have Unowned operands.
646+
ForwardingUnowned,
647+
648+
/// MARK: Uses of Owned values:
649+
650+
/// Borrow. Propagates the owned value within a scope, without consuming it.
651+
/// (begin_borrow, begin_apply with @guaranteed argument)
652+
Borrow,
653+
/// Destroying Consume. Destroys the owned value immediately.
654+
/// (store, destroy, @owned destructure).
655+
DestroyingConsume,
656+
/// Forwarding Consume. Consumes the owned value indirectly via a move.
657+
/// (br, destructure, tuple, struct, cast, switch).
658+
ForwardingConsume,
659+
660+
/// MARK: Uses of Guaranteed values:
661+
662+
/// Nested Borrow. Propagates the guaranteed value within a nested borrow
663+
/// scope, without ending the outer borrow scope, following stack discipline.
664+
/// (begin_borrow, begin_apply with @guaranteed).
665+
NestedBorrow,
666+
/// Interior Pointer. Propagates an address into the guaranteed value within
667+
/// the base's borrow scope. (ref_element_addr, open_existential_box)
668+
InteriorPointer,
669+
/// Forwarded Borrow. Propagates the guaranteed value within the base's
670+
/// borrow scope.
671+
/// (tuple_extract, struct_extract, cast, switch)
672+
ForwardingBorrow,
673+
/// End Borrow. End the borrow scope opened directly by the operand.
674+
/// The operand must be a begin_borrow, begin_apply, or function argument.
675+
/// (end_borrow, end_apply)
676+
EndBorrow,
677+
// Reborrow. Ends the borrow scope opened directly by the operand and begins
678+
// one or multiple disjoint borrow scopes. If a forwarded value is reborrowed,
679+
// then its base must also be reborrowed at the same point.
680+
// (br, FIXME: should also include destructure, tuple, struct)
681+
Reborrow
682+
};
683+
684+
llvm::raw_ostream &operator<<(llvm::raw_ostream &os, OperandOwnership operandOwnership);
685+
686+
/// Return the OwnershipConstraint for a OperandOwnership.
687+
///
688+
/// Defined inline so the switch is eliminated for constant OperandOwnership.
689+
inline OwnershipConstraint
690+
getOwnershipConstraint(OperandOwnership operandOwnership) {
691+
switch (operandOwnership) {
692+
case OperandOwnership::None:
693+
return {OwnershipKind::None, UseLifetimeConstraint::NonLifetimeEnding};
694+
case OperandOwnership::InstantaneousUse:
695+
case OperandOwnership::PointerEscape:
696+
case OperandOwnership::BitwiseEscape:
697+
return {OwnershipKind::Any, UseLifetimeConstraint::NonLifetimeEnding};
698+
case OperandOwnership::ForwardingUnowned:
699+
return {OwnershipKind::Unowned, UseLifetimeConstraint::NonLifetimeEnding};
700+
case OperandOwnership::Borrow:
701+
return {OwnershipKind::Owned, UseLifetimeConstraint::NonLifetimeEnding};
702+
case OperandOwnership::DestroyingConsume:
703+
case OperandOwnership::ForwardingConsume:
704+
return {OwnershipKind::Owned, UseLifetimeConstraint::LifetimeEnding};
705+
case OperandOwnership::NestedBorrow:
706+
case OperandOwnership::InteriorPointer:
707+
case OperandOwnership::ForwardingBorrow:
708+
return {OwnershipKind::Guaranteed,
709+
UseLifetimeConstraint::NonLifetimeEnding};
710+
case OperandOwnership::EndBorrow:
711+
case OperandOwnership::Reborrow:
712+
return {OwnershipKind::Guaranteed, UseLifetimeConstraint::LifetimeEnding};
713+
}
714+
}
715+
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.
721+
inline OperandOwnership
722+
ValueOwnershipKind::getForwardingOperandOwnership() const {
723+
switch (value) {
724+
case OwnershipKind::Any:
725+
llvm_unreachable("invalid value ownership");
726+
case OwnershipKind::None:
727+
return OperandOwnership::None;
728+
case OwnershipKind::Unowned:
729+
return OperandOwnership::ForwardingUnowned;
730+
case OwnershipKind::Guaranteed:
731+
return OperandOwnership::ForwardingBorrow;
732+
case OwnershipKind::Owned:
733+
return OperandOwnership::ForwardingConsume;
734+
}
735+
}
736+
621737
/// A formal SIL reference to a value, suitable for use as a stored
622738
/// operand.
623739
class Operand {
@@ -695,12 +811,22 @@ class Operand {
695811
/// Return which operand this is in the operand list of the using instruction.
696812
unsigned getOperandNumber() const;
697813

814+
/// Return the use ownership of this operand. Returns none if the operand is a
815+
/// type dependent operand.
816+
///
817+
/// NOTE: This is implemented in OperandOwnership.cpp.
818+
Optional<OperandOwnership> getOperandOwnership() const;
819+
698820
/// Return the ownership constraint that restricts what types of values this
699821
/// Operand can contain. Returns none if the operand is a type dependent
700822
/// operand.
701-
///
702-
/// NOTE: This is implemented in OperandOwnership.cpp.
703-
Optional<OwnershipConstraint> getOwnershipConstraint() const;
823+
Optional<OwnershipConstraint> getOwnershipConstraint() const {
824+
auto operandOwnership = getOperandOwnership();
825+
if (!operandOwnership) {
826+
return None;
827+
}
828+
return swift::getOwnershipConstraint(operandOwnership.getValue());
829+
}
704830

705831
/// Returns true if changing the operand to use a value with the given
706832
/// ownership kind would not cause the operand to violate the operand's
@@ -711,24 +837,28 @@ class Operand {
711837
/// operand constraint.
712838
bool satisfiesConstraints() const;
713839

714-
/// Returns true if this operand acts as a use that consumes its associated
715-
/// value.
840+
/// Returns true if this operand acts as a use that ends the lifetime its
841+
/// associated value, either by consuming the owned value or ending the
842+
/// guaranteed scope.
716843
bool isLifetimeEnding() const;
717844

718845
SILBasicBlock *getParentBlock() const;
719846
SILFunction *getParentFunction() const;
720847

721848
private:
722849
void removeFromCurrent() {
723-
if (!Back) return;
850+
if (!Back)
851+
return;
724852
*Back = NextUse;
725-
if (NextUse) NextUse->Back = Back;
853+
if (NextUse)
854+
NextUse->Back = Back;
726855
}
727856

728857
void insertIntoCurrent() {
729858
Back = &TheValue->FirstUse;
730859
NextUse = TheValue->FirstUse;
731-
if (NextUse) NextUse->Back = &NextUse;
860+
if (NextUse)
861+
NextUse->Back = &NextUse;
732862
TheValue->FirstUse = this;
733863
}
734864

0 commit comments

Comments
 (0)