Skip to content

Commit 01e11c5

Browse files
authored
Merge pull request #36118 from atrick/simplifycfg-trivial
OSSA: simplify-cfg support for trivial block arguments.
2 parents 8ae1b76 + ba9f520 commit 01e11c5

18 files changed

+4736
-118
lines changed

include/swift/SIL/SILInstruction.h

+10
Original file line numberDiff line numberDiff line change
@@ -1025,6 +1025,16 @@ class OwnershipForwardingMixin {
10251025
}
10261026

10271027
public:
1028+
/// Forwarding ownership is determined by the forwarding instruction's
1029+
/// constant ownership attribute. If forwarding ownership is owned, then the
1030+
/// instruction moves an owned operand to its result, ending its lifetime. If
1031+
/// forwarding ownership is guaranteed, then the instruction propagates the
1032+
/// lifetime of its borrows operand through its result.
1033+
///
1034+
/// The resulting forwarded value's ownership, returned by getOwnershipKind(),
1035+
/// is not identical to the forwarding ownership. It differs when the result
1036+
/// is trivial type. e.g. an owned or guaranteed value can be cast to a
1037+
/// trivial type using owned or guaranteed forwarding.
10281038
ValueOwnershipKind getForwardingOwnershipKind() const {
10291039
return ownershipKind;
10301040
}

include/swift/SIL/SILValue.h

+35-9
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,16 @@ class ValueBase : public SILNode, public SILAllocated<ValueBase> {
515515
/// result index, or None if it is not defined by an instruction.
516516
Optional<DefiningInstructionResult> getDefiningInstructionResult();
517517

518+
/// Returns the ValueOwnershipKind that describes this SILValue's ownership
519+
/// semantics if the SILValue has ownership semantics. Returns is a value
520+
/// without any Ownership Semantics.
521+
///
522+
/// An example of a SILValue without ownership semantics is a
523+
/// struct_element_addr.
524+
///
525+
/// NOTE: This is implemented in ValueOwnership.cpp not SILValue.cpp.
526+
ValueOwnershipKind getOwnershipKind() const;
527+
518528
static bool classof(SILNodePointer node) {
519529
return node->getKind() >= SILNodeKind::First_ValueBase &&
520530
node->getKind() <= SILNodeKind::Last_ValueBase;
@@ -592,6 +602,8 @@ class SILValue {
592602

593603
/// If this SILValue is a result of an instruction, return its
594604
/// defining instruction. Returns nullptr otherwise.
605+
///
606+
/// FIXME: remove this redundant API from SILValue.
595607
SILInstruction *getDefiningInstruction() {
596608
return Value->getDefiningInstruction();
597609
}
@@ -610,7 +622,11 @@ class SILValue {
610622
/// struct_element_addr.
611623
///
612624
/// NOTE: This is implemented in ValueOwnership.cpp not SILValue.cpp.
613-
ValueOwnershipKind getOwnershipKind() const;
625+
///
626+
/// FIXME: remove this redundant API from SILValue.
627+
ValueOwnershipKind getOwnershipKind() const {
628+
return Value->getOwnershipKind();
629+
}
614630

615631
/// Verify that this SILValue and its uses respects ownership invariants.
616632
void verifyOwnership(DeadEndBlocks *DEBlocks) const;
@@ -660,6 +676,11 @@ class OwnershipConstraint {
660676
return lifetimeConstraint;
661677
}
662678

679+
bool isConsuming() const {
680+
return ownershipKind == OwnershipKind::Owned
681+
&& lifetimeConstraint == UseLifetimeConstraint::LifetimeEnding;
682+
}
683+
663684
bool satisfiedBy(const Operand *use) const;
664685

665686
bool satisfiesConstraint(ValueOwnershipKind testKind) const {
@@ -880,15 +901,20 @@ inline bool canAcceptUnownedValue(OperandOwnership operandOwnership) {
880901
}
881902
}
882903

883-
/// Return the OperandOwnership for a forwarded operand when the forwarded
884-
/// result has this ValueOwnershipKind. \p allowUnowned is true for a subset
885-
/// of forwarding operations that are allowed to propagate Unowned values.
904+
/// Return the OperandOwnership for a forwarded operand when the forwarding
905+
/// operation has this "forwarding ownership" (as returned by
906+
/// getForwardingOwnershipKind()). \p allowUnowned is true for a subset of
907+
/// forwarding operations that are allowed to propagate Unowned values.
908+
///
909+
/// Forwarding ownership is determined by the forwarding instruction's constant
910+
/// ownership attribute. If forwarding ownership is owned, then the instruction
911+
/// moves owned operand to its result, ending its lifetime. If forwarding
912+
/// ownership is guaranteed, then the instruction propagates the lifetime of its
913+
/// borrows operand through its result.
886914
///
887-
/// The ownership of a forwarded value is derived from the forwarding
888-
/// instruction's constant ownership attribute. If the result is owned, then the
889-
/// instruction moves owned operand to its result, ending its lifetime. If the
890-
/// result is guaranteed value, then the instruction propagates the lifetime of
891-
/// its borrows operand through its result.
915+
/// The resulting forwarded value typically has forwarding ownership, but may
916+
/// differ when the result is trivial type. e.g. an owned or guaranteed value
917+
/// can be cast to a trivial type using owned or guaranteed forwarding.
892918
inline OperandOwnership
893919
ValueOwnershipKind::getForwardingOperandOwnership(bool allowUnowned) const {
894920
switch (value) {

include/swift/SILOptimizer/Utils/InstOptUtils.h

+23-1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,14 @@ inline TransformRange<Range, UserTransform> makeUserRange(Range range) {
4747
return makeTransformRange(range, UserTransform(toUser));
4848
}
4949

50+
/// Transform a use_iterator range (Operand*) into an llvm::iterator_range
51+
/// of users (SILInstruction *)
52+
inline iterator_range<llvm::mapped_iterator<ValueBase::use_iterator, UserTransform>>
53+
makeUserIteratorRange(iterator_range<ValueBase::use_iterator> useRange) {
54+
auto toUser = [](Operand *operand) { return operand->getUser(); };
55+
return llvm::map_range(useRange, UserTransform(toUser));
56+
}
57+
5058
using DeadInstructionSet = llvm::SmallSetVector<SILInstruction *, 8>;
5159

5260
/// Create a retain of \p Ptr before the \p InsertPt.
@@ -265,9 +273,15 @@ SILValue getConcreteValueOfExistentialBoxAddr(SILValue addr,
265273
/// - a type of the return value is a subclass of the expected return type.
266274
/// - actual return type and expected return type differ in optionality.
267275
/// - both types are tuple-types and some of the elements need to be casted.
276+
///
277+
/// \p usePoints is required when \p value has guaranteed ownership. It must be
278+
/// the last users of the returned, casted value. A usePoint cannot be a
279+
/// BranchInst (a phi is never the last guaranteed user). \p builder's current
280+
/// insertion point must dominate all \p usePoints.
268281
std::pair<SILValue, bool /* changedCFG */>
269282
castValueToABICompatibleType(SILBuilder *builder, SILLocation Loc,
270-
SILValue value, SILType srcTy, SILType destTy);
283+
SILValue value, SILType srcTy, SILType destTy,
284+
ArrayRef<SILInstruction *> usePoints);
271285
/// Peek through trivial Enum initialization, typically for pointless
272286
/// Optionals.
273287
///
@@ -700,10 +714,18 @@ makeCopiedValueAvailable(SILValue value, SILBasicBlock *inBlock);
700714
/// Given a newly created @owned value \p value without any uses, this utility
701715
/// inserts control equivalent copy and destroy at leaking blocks to adjust
702716
/// ownership and make \p value available for use at \p inBlock.
717+
///
718+
/// inBlock must be the only point at which \p value will be consumed. If this
719+
/// consuming point is within a loop, this will create and return a copy of \p
720+
/// value inside \p inBlock.
703721
SILValue
704722
makeNewValueAvailable(SILValue value, SILBasicBlock *inBlock);
705723

706724
/// Given an ssa value \p value, create destroy_values at leaking blocks
725+
///
726+
/// Warning: This does not properly cleanup an OSSA lifetime with a consuming
727+
/// use blocks inside a loop relative to \p value. The client must create
728+
/// separate copies for any uses within the loop.
707729
void endLifetimeAtLeakingBlocks(SILValue value,
708730
ArrayRef<SILBasicBlock *> userBBs);
709731

include/swift/SILOptimizer/Utils/OwnershipOptUtils.h

+7
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,13 @@
2727

2828
namespace swift {
2929

30+
/// Returns true if this value requires OSSA cleanups.
31+
inline bool requiresOSSACleanup(SILValue v) {
32+
return v->getFunction()->hasOwnership()
33+
&& v.getOwnershipKind() != OwnershipKind::None
34+
&& v.getOwnershipKind() != OwnershipKind::Unowned;
35+
}
36+
3037
// Defined in BasicBlockUtils.h
3138
struct JointPostDominanceSetComputer;
3239

lib/SIL/IR/OperandOwnership.cpp

+18-3
Original file line numberDiff line numberDiff line change
@@ -443,9 +443,24 @@ OperandOwnershipClassifier::visitFullApply(FullApplySite apply) {
443443
? SILArgumentConvention(apply.getSubstCalleeType()->getCalleeConvention())
444444
: apply.getArgumentConvention(op);
445445

446-
return getFunctionArgOwnership(
447-
argConv,
448-
/*hasScopeInCaller*/ apply.beginsCoroutineEvaluation());
446+
auto argOwnership = getFunctionArgOwnership(
447+
argConv, /*hasScopeInCaller*/ apply.beginsCoroutineEvaluation());
448+
449+
// OSSA cleanup needs to handle each of these callee ownership cases.
450+
//
451+
// OperandOwnership::ForwardingConsume is only for thick @callee_owned.
452+
//
453+
// OperandOwnership::Borrow would only happen for a coroutine closure, which
454+
// isn't yet possible.
455+
if (apply.isCalleeOperand(op)) {
456+
assert((argOwnership == OperandOwnership::TrivialUse
457+
|| argOwnership == OperandOwnership::UnownedInstantaneousUse
458+
|| argOwnership == OperandOwnership::InstantaneousUse
459+
|| argOwnership == OperandOwnership::ForwardingConsume
460+
|| argOwnership == OperandOwnership::Borrow) &&
461+
"unsupported callee ownership");
462+
}
463+
return argOwnership;
449464
}
450465

451466
OperandOwnership

lib/SIL/IR/SILFunctionType.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -4319,6 +4319,9 @@ TypeConverter::getLoweredFormalTypes(SILDeclRef constant,
43194319
// match exactly.
43204320
// TODO: More sophisticated param and return ABI compatibility rules could
43214321
// diverge.
4322+
//
4323+
// Note: all cases recognized here must be handled in the SILOptimizer's
4324+
// castValueToABICompatibleType().
43224325
static bool areABICompatibleParamsOrReturns(SILType a, SILType b,
43234326
SILFunction *inFunction) {
43244327
// Address parameters are all ABI-compatible, though the referenced

lib/SIL/IR/ValueOwnership.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -586,15 +586,15 @@ ValueOwnershipKindClassifier::visitBuiltinInst(BuiltinInst *BI) {
586586
// Top Level Entrypoint
587587
//===----------------------------------------------------------------------===//
588588

589-
ValueOwnershipKind SILValue::getOwnershipKind() const {
589+
ValueOwnershipKind ValueBase::getOwnershipKind() const {
590590
// If we do not have an undef, we should always be able to get to our function
591591
// here. If we do not have ownership enabled, just return none for everything
592592
// to short circuit ownership optimizations. Since SILUndef in either case
593593
// will be ValueOwnershipKind::None, we will not get any wonky behavior here.
594594
//
595595
// We assume that any time we are in SILBuilder and call this without having a
596596
// value in a block yet, ossa is enabled.
597-
if (auto *block = Value->getParentBlock()) {
597+
if (auto *block = getParentBlock()) {
598598
auto *f = block->getParent();
599599
// If our block isn't in a function, then it must be in a global
600600
// variable. We don't verify ownership there so just return
@@ -609,7 +609,7 @@ ValueOwnershipKind SILValue::getOwnershipKind() const {
609609
}
610610

611611
ValueOwnershipKindClassifier Classifier;
612-
auto result = Classifier.visit(const_cast<ValueBase *>(Value));
612+
auto result = Classifier.visit(const_cast<ValueBase *>(this));
613613
assert(result && "Returned ownership kind invalid on values");
614614
return result;
615615
}

lib/SILOptimizer/Transforms/EagerSpecializer.cpp

+5-1
Original file line numberDiff line numberDiff line change
@@ -888,7 +888,11 @@ void EagerSpecializerTransform::run() {
888888
// removed.
889889
for (auto *SA : attrsToRemove)
890890
F.removeSpecializeAttr(SA);
891-
F.verify();
891+
892+
// If any specializations were created, reverify the original body now that it
893+
// has checks.
894+
if (!newFunctions.empty())
895+
F.verify();
892896

893897
for (SILFunction *newF : newFunctions) {
894898
addFunctionToPassManagerWorklist(newF, nullptr);

0 commit comments

Comments
 (0)