Skip to content

Commit 3a1c8fd

Browse files
authored
Merge pull request #35248 from atrick/fix-operandownership
OperandOwnership fixes required for CanonicalOSSA
2 parents e5f7d5b + 24fa288 commit 3a1c8fd

File tree

17 files changed

+287
-162
lines changed

17 files changed

+287
-162
lines changed

include/swift/SIL/SILValue.h

Lines changed: 60 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -116,10 +116,18 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
116116
/// statically. Thus we treat Any as representing an invalid
117117
/// value. ValueOwnershipKinds can only perform a meet operation to determine
118118
/// if two ownership kinds are compatible with a merge of Any showing the
119-
/// merge is impossible since values can not have any ownership.
119+
/// merge is impossible since values can not have any ownership. Values with
120+
/// ownership None are statically proven to be trivial values, often because
121+
/// they are trivially typed, but sometimes because of path-sensitive
122+
/// information like knowledge of an enum case. Trivial values have no
123+
/// ownership semantics.
120124
///
121-
/// * OperandConstraint: This represents a constraint on the values that can be
122-
/// used by a specific operand. Here Any is valid.
125+
/// * OwnershipConstraint: This represents a constraint on the values that can
126+
/// be used by a specific operand. Here Any is valid and is used for operands
127+
/// that don't care about the ownership kind (lack ownership constraints). In
128+
/// contrast, a constraint of None is the most restrictive. It requires a
129+
/// trivial value. An Unowned, Owned, or Guaranteed constraint requires either
130+
/// a value with the named ownership, or a trivial value.
123131
struct OwnershipKind {
124132
enum innerty : uint8_t {
125133
/// An ownership kind that models an ownership that is unknown statically at
@@ -159,10 +167,12 @@ struct OwnershipKind {
159167
Guaranteed,
160168

161169
/// A SILValue with None ownership kind is an independent value outside of
162-
/// the ownership system. It is used to model trivially typed values as well
170+
/// the ownership system. It is used to model values that are statically
171+
/// determined to be trivial. This includes trivially typed values as well
163172
/// as trivial cases of non-trivial enums. Naturally None can be merged with
164173
/// any ValueOwnershipKind allowing us to naturally model merge and branch
165-
/// points in the SSA graph.
174+
/// points in the SSA graph, where more information about the value is
175+
/// statically available on some control flow paths.
166176
None,
167177

168178
LastValueOwnershipKind = None,
@@ -578,6 +588,18 @@ inline bool ValueOwnershipKind::isCompatibleWith(SILValue other) const {
578588
return isCompatibleWith(other.getOwnershipKind());
579589
}
580590

591+
/// Constraints on the ownership of an operand value.
592+
///
593+
/// The ownershipKind component constrains the operand's value ownership to be
594+
/// the same or "above" the constraint in the lattice, such that
595+
/// join(constraint, valueOwnership) == valueOwnership. In other words, applying
596+
/// the constraint does not change the value's ownership. For example, a value
597+
/// with None ownership is accepted by any OwnershipConstraint, and an
598+
/// OwnershipConstraint with 'Any' ownership kind can accept any value. Note
599+
/// that operands commonly allow either Owned or Guaranteed operands. These
600+
/// operands have an Any ownership constraint to allow either. However,
601+
/// enforcement of Unowned value is more strict. This requires separate logic in
602+
/// canAcceptUnownedValue() to avoid complicating the OwnershipKind lattice.
581603
class OwnershipConstraint {
582604
OwnershipKind ownershipKind;
583605
UseLifetimeConstraint lifetimeConstraint;
@@ -624,11 +646,26 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
624646
/// Used to verify completeness of the ownership use model and exhaustively
625647
/// switch over any category of ownership use. Implies ownership constraints and
626648
/// lifetime constraints.
649+
///
650+
/// OperandOwnership may be statically determined by the user's opcode alone, or
651+
/// by the opcode and operand type. Or it may be dynamically determined by an
652+
/// ownership kind variable in the user's state. However, it may never be
653+
/// inferred from the ownership of the incoming value. This way, the logic for
654+
/// determining which ValueOwnershipKind an operand may accept is reliable.
655+
///
656+
/// Any use that takes an Owned or Guaranteed value may also take a trivial
657+
/// value (ownership None), because the ownership semantics are irrelevant.
627658
struct OperandOwnership {
628659
enum innerty : uint8_t {
629-
/// Uses of ownership None. These uses are incompatible with values that
630-
/// have ownership but are otherwise not verified.
631-
None,
660+
/// Operands that do not use the value. They only represent a dependence
661+
/// on a dominating definition and do not require liveness.
662+
/// (type-dependent operands)
663+
NonUse,
664+
665+
/// Uses that can only handle trivial values. The operand value must have
666+
/// None ownership. These uses require liveness but are otherwise
667+
/// unverified.
668+
TrivialUse,
632669

633670
/// Use the value only for the duration of the operation, which may have
634671
/// side effects. Requires an owned or guaranteed value.
@@ -659,25 +696,22 @@ struct OperandOwnership {
659696
/// (ref_to_unowned, unchecked_trivial_bitcast)
660697
BitwiseEscape,
661698

662-
/// MARK: Uses of Owned values:
663-
664-
/// Borrow. Propagates the owned value within a scope, without consuming it.
699+
/// Borrow. Propagates the owned or guaranteed value within a scope, without
700+
/// ending its lifetime.
665701
/// (begin_borrow, begin_apply with @guaranteed argument)
666702
Borrow,
703+
704+
/// MARK: Uses of Owned (or None) values:
705+
667706
/// Destroying Consume. Destroys the owned value immediately.
668707
/// (store, destroy, @owned destructure).
669708
DestroyingConsume,
670709
/// Forwarding Consume. Consumes the owned value indirectly via a move.
671710
/// (br, destructure, tuple, struct, cast, switch).
672711
ForwardingConsume,
673712

674-
/// MARK: Uses of Guaranteed values:
713+
/// MARK: Uses of Guaranteed (or None) values:
675714

676-
/// Nested Borrow. Propagates the guaranteed value within a nested borrow
677-
/// scope, without ending the outer borrow scope, following stack
678-
/// discipline.
679-
/// (begin_borrow, begin_apply with @guaranteed).
680-
NestedBorrow,
681715
/// Interior Pointer. Propagates a trivial value (e.g. address, pointer, or
682716
/// no-escape closure) that depends on the guaranteed value within the
683717
/// base's borrow scope. The verifier checks that all uses of the trivial
@@ -724,22 +758,26 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
724758
const OperandOwnership &operandOwnership);
725759

726760
/// Defined inline so the switch is eliminated for constant OperandOwnership.
761+
///
762+
/// Here, an Any ownership constraint is used to allow either Owned or
763+
/// Guaranteed values. However, enforcement of Unowned values is more
764+
/// strict. This is handled by separate logic in canAcceptUnownedValue() to
765+
/// avoid complicating the OwnershipKind lattice.
727766
inline OwnershipConstraint OperandOwnership::getOwnershipConstraint() {
728767
switch (value) {
729-
case OperandOwnership::None:
768+
case OperandOwnership::TrivialUse:
730769
return {OwnershipKind::None, UseLifetimeConstraint::NonLifetimeEnding};
770+
case OperandOwnership::NonUse:
731771
case OperandOwnership::InstantaneousUse:
732772
case OperandOwnership::UnownedInstantaneousUse:
733773
case OperandOwnership::ForwardingUnowned:
734774
case OperandOwnership::PointerEscape:
735775
case OperandOwnership::BitwiseEscape:
736-
return {OwnershipKind::Any, UseLifetimeConstraint::NonLifetimeEnding};
737776
case OperandOwnership::Borrow:
738-
return {OwnershipKind::Owned, UseLifetimeConstraint::NonLifetimeEnding};
777+
return {OwnershipKind::Any, UseLifetimeConstraint::NonLifetimeEnding};
739778
case OperandOwnership::DestroyingConsume:
740779
case OperandOwnership::ForwardingConsume:
741780
return {OwnershipKind::Owned, UseLifetimeConstraint::LifetimeEnding};
742-
case OperandOwnership::NestedBorrow:
743781
case OperandOwnership::InteriorPointer:
744782
case OperandOwnership::ForwardingBorrow:
745783
return {OwnershipKind::Guaranteed,
@@ -770,7 +808,7 @@ ValueOwnershipKind::getForwardingOperandOwnership(bool allowUnowned) const {
770808
}
771809
llvm_unreachable("invalid value ownership");
772810
case OwnershipKind::None:
773-
return OperandOwnership::None;
811+
return OperandOwnership::TrivialUse;
774812
case OwnershipKind::Guaranteed:
775813
return OperandOwnership::ForwardingBorrow;
776814
case OwnershipKind::Owned:

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,8 @@ PASS(ForEachLoopUnroll, "for-each-loop-unroll",
357357
"Unroll forEach loops over array literals")
358358
PASS(MandatoryCombine, "mandatory-combine",
359359
"Perform mandatory peephole combines")
360+
PASS(OptimizedMandatoryCombine, "optimized-mandatory-combine",
361+
"Perform -O level mandatory peephole combines")
360362
PASS(BugReducerTester, "bug-reducer-tester",
361363
"sil-bug-reducer Tool Testing by Asserting on a Sentinel Function")
362364
PASS(OptRemarkGenerator, "sil-opt-remark-generator",

0 commit comments

Comments
 (0)