mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
1111 lines
48 KiB
C++
1111 lines
48 KiB
C++
//===--- OperandOwnership.cpp ---------------------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "swift/Basic/Assertions.h"
|
|
#include "swift/SIL/ApplySite.h"
|
|
#include "swift/SIL/OwnershipUtils.h"
|
|
#include "swift/SIL/SILBuiltinVisitor.h"
|
|
#include "swift/SIL/SILInstruction.h"
|
|
#include "swift/SIL/SILValue.h"
|
|
#include "swift/SIL/SILVisitor.h"
|
|
|
|
using namespace swift;
|
|
|
|
/// Return true if all OperandOwnership invariants hold.
|
|
bool swift::checkOperandOwnershipInvariants(const Operand *operand,
|
|
SILModuleConventions *silConv) {
|
|
OperandOwnership opOwnership = operand->getOperandOwnership(silConv);
|
|
if (opOwnership == OperandOwnership::Borrow) {
|
|
// Must be a valid BorrowingOperand.
|
|
return bool(BorrowingOperand(const_cast<Operand *>(operand)));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// OperandOwnershipClassifier
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
namespace {
|
|
|
|
class OperandOwnershipClassifier
|
|
: public SILInstructionVisitor<OperandOwnershipClassifier, OperandOwnership> {
|
|
LLVM_ATTRIBUTE_UNUSED SILModule &mod;
|
|
// Allow module conventions to be overridden while lowering between canonical
|
|
// and lowered SIL stages.
|
|
SILModuleConventions silConv;
|
|
|
|
const Operand &op;
|
|
|
|
public:
|
|
/// Create a new OperandOwnershipClassifier.
|
|
///
|
|
/// In most cases, one should only pass in \p Op and \p BaseValue will be set
|
|
/// to Op.get(). In cases where one is trying to verify subobjects, Op.get()
|
|
/// should be the subobject and Value should be the parent object. An example
|
|
/// of where one would want to do this is in the case of value projections
|
|
/// like struct_extract.
|
|
OperandOwnershipClassifier(SILModuleConventions silConv, const Operand &op)
|
|
: mod(silConv.getModule()), silConv(silConv), op(op) {}
|
|
|
|
SILValue getValue() const { return op.get(); }
|
|
|
|
ValueOwnershipKind getOwnershipKind() const {
|
|
return op.get()->getOwnershipKind();
|
|
}
|
|
|
|
unsigned getOperandIndex() const { return op.getOperandNumber(); }
|
|
|
|
SILType getType() const { return op.get()->getType(); }
|
|
|
|
bool compatibleWithOwnership(ValueOwnershipKind kind) const {
|
|
return getOwnershipKind().isCompatibleWith(kind);
|
|
}
|
|
|
|
bool hasExactOwnership(ValueOwnershipKind kind) const {
|
|
return getOwnershipKind() == kind;
|
|
}
|
|
|
|
OperandOwnership visitFullApply(FullApplySite apply);
|
|
|
|
// Create declarations for all instructions, so we get a warning at compile
|
|
// time if any instructions do not have an implementation.
|
|
#define INST(Id, Parent) OperandOwnership visit##Id(Id *);
|
|
#include "swift/SIL/SILNodes.def"
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
/// Implementation for instructions that we should never visit since they are
|
|
/// not valid in ossa or do not have operands. Since we should never visit
|
|
/// these, we just assert.
|
|
#define SHOULD_NEVER_VISIT_INST(INST) \
|
|
OperandOwnership OperandOwnershipClassifier::visit##INST##Inst( \
|
|
INST##Inst *i) { \
|
|
llvm::errs() << "Unhandled inst: " << *i; \
|
|
llvm::report_fatal_error( \
|
|
"Visited instruction that should never be visited?!"); \
|
|
}
|
|
SHOULD_NEVER_VISIT_INST(AllocBox)
|
|
SHOULD_NEVER_VISIT_INST(AllocExistentialBox)
|
|
SHOULD_NEVER_VISIT_INST(AllocGlobal)
|
|
SHOULD_NEVER_VISIT_INST(AllocStack)
|
|
SHOULD_NEVER_VISIT_INST(AllocPack)
|
|
SHOULD_NEVER_VISIT_INST(AllocPackMetadata)
|
|
SHOULD_NEVER_VISIT_INST(PackLength)
|
|
SHOULD_NEVER_VISIT_INST(DifferentiabilityWitnessFunction)
|
|
SHOULD_NEVER_VISIT_INST(FloatLiteral)
|
|
SHOULD_NEVER_VISIT_INST(FunctionRef)
|
|
SHOULD_NEVER_VISIT_INST(DebugStep)
|
|
SHOULD_NEVER_VISIT_INST(DynamicFunctionRef)
|
|
SHOULD_NEVER_VISIT_INST(PreviousDynamicFunctionRef)
|
|
SHOULD_NEVER_VISIT_INST(GlobalValue)
|
|
SHOULD_NEVER_VISIT_INST(BaseAddrForOffset)
|
|
SHOULD_NEVER_VISIT_INST(HasSymbol)
|
|
SHOULD_NEVER_VISIT_INST(IntegerLiteral)
|
|
SHOULD_NEVER_VISIT_INST(Metatype)
|
|
SHOULD_NEVER_VISIT_INST(ObjCProtocol)
|
|
SHOULD_NEVER_VISIT_INST(Object)
|
|
SHOULD_NEVER_VISIT_INST(RetainValue)
|
|
SHOULD_NEVER_VISIT_INST(RetainValueAddr)
|
|
SHOULD_NEVER_VISIT_INST(StringLiteral)
|
|
SHOULD_NEVER_VISIT_INST(StrongRetain)
|
|
SHOULD_NEVER_VISIT_INST(Unreachable)
|
|
SHOULD_NEVER_VISIT_INST(Unwind)
|
|
SHOULD_NEVER_VISIT_INST(ThrowAddr)
|
|
SHOULD_NEVER_VISIT_INST(ReleaseValue)
|
|
SHOULD_NEVER_VISIT_INST(ReleaseValueAddr)
|
|
SHOULD_NEVER_VISIT_INST(StrongRelease)
|
|
SHOULD_NEVER_VISIT_INST(GetAsyncContinuation)
|
|
SHOULD_NEVER_VISIT_INST(IncrementProfilerCounter)
|
|
SHOULD_NEVER_VISIT_INST(SpecifyTest)
|
|
SHOULD_NEVER_VISIT_INST(ScalarPackIndex)
|
|
SHOULD_NEVER_VISIT_INST(Vector)
|
|
SHOULD_NEVER_VISIT_INST(TypeValue)
|
|
|
|
#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
|
|
SHOULD_NEVER_VISIT_INST(StrongRetain##Name) \
|
|
SHOULD_NEVER_VISIT_INST(Name##Retain)
|
|
#include "swift/AST/ReferenceStorage.def"
|
|
#undef SHOULD_NEVER_VISIT_INST
|
|
|
|
// FIXME: The assert should be moved to the verifier.
|
|
#define OPERAND_OWNERSHIP(OWNERSHIP, INST) \
|
|
OperandOwnership \
|
|
OperandOwnershipClassifier::visit##INST##Inst(INST##Inst *i) { \
|
|
assert(i->getNumOperands() && "Expected to have non-zero operands"); \
|
|
return OperandOwnership::OWNERSHIP; \
|
|
}
|
|
|
|
// Instructions that require trivial operands.
|
|
OPERAND_OWNERSHIP(TrivialUse, AwaitAsyncContinuation)
|
|
OPERAND_OWNERSHIP(TrivialUse, AddressToPointer)
|
|
OPERAND_OWNERSHIP(TrivialUse, AllocRef) // with tail operand
|
|
OPERAND_OWNERSHIP(TrivialUse, AllocRefDynamic) // with tail operand
|
|
OPERAND_OWNERSHIP(TrivialUse, BeginAccess)
|
|
OPERAND_OWNERSHIP(TrivialUse, MoveOnlyWrapperToCopyableAddr)
|
|
OPERAND_OWNERSHIP(TrivialUse, CopyableToMoveOnlyWrapperAddr)
|
|
OPERAND_OWNERSHIP(TrivialUse, BeginUnpairedAccess)
|
|
OPERAND_OWNERSHIP(TrivialUse, BindMemory)
|
|
OPERAND_OWNERSHIP(TrivialUse, RebindMemory)
|
|
OPERAND_OWNERSHIP(TrivialUse, CheckedCastAddrBranch)
|
|
OPERAND_OWNERSHIP(TrivialUse, CondBranch)
|
|
OPERAND_OWNERSHIP(TrivialUse, CondFail)
|
|
OPERAND_OWNERSHIP(TrivialUse, CopyAddr)
|
|
OPERAND_OWNERSHIP(TrivialUse, ExplicitCopyAddr)
|
|
OPERAND_OWNERSHIP(TrivialUse, MarkUnresolvedMoveAddr)
|
|
OPERAND_OWNERSHIP(TrivialUse, DeallocStack)
|
|
OPERAND_OWNERSHIP(TrivialUse, DeallocPack)
|
|
OPERAND_OWNERSHIP(TrivialUse, DeallocPackMetadata)
|
|
OPERAND_OWNERSHIP(TrivialUse, DeinitExistentialAddr)
|
|
OPERAND_OWNERSHIP(TrivialUse, DestroyAddr)
|
|
OPERAND_OWNERSHIP(TrivialUse, EndAccess)
|
|
OPERAND_OWNERSHIP(TrivialUse, EndUnpairedAccess)
|
|
OPERAND_OWNERSHIP(TrivialUse, GetAsyncContinuationAddr)
|
|
OPERAND_OWNERSHIP(TrivialUse, VectorBaseAddr)
|
|
OPERAND_OWNERSHIP(TrivialUse, IndexAddr)
|
|
OPERAND_OWNERSHIP(TrivialUse, IndexRawPointer)
|
|
OPERAND_OWNERSHIP(TrivialUse, InitBlockStorageHeader)
|
|
OPERAND_OWNERSHIP(TrivialUse, InitEnumDataAddr)
|
|
OPERAND_OWNERSHIP(TrivialUse, InitExistentialAddr)
|
|
OPERAND_OWNERSHIP(TrivialUse, InitExistentialMetatype)
|
|
OPERAND_OWNERSHIP(TrivialUse, InjectEnumAddr)
|
|
OPERAND_OWNERSHIP(TrivialUse, IsUnique)
|
|
OPERAND_OWNERSHIP(TrivialUse, Load)
|
|
OPERAND_OWNERSHIP(TrivialUse, LoadBorrow)
|
|
OPERAND_OWNERSHIP(TrivialUse, MarkFunctionEscape)
|
|
OPERAND_OWNERSHIP(TrivialUse, ObjCExistentialMetatypeToObject)
|
|
OPERAND_OWNERSHIP(TrivialUse, ObjCMetatypeToObject)
|
|
OPERAND_OWNERSHIP(TrivialUse, ObjCToThickMetatype)
|
|
OPERAND_OWNERSHIP(TrivialUse, OpenExistentialAddr)
|
|
OPERAND_OWNERSHIP(TrivialUse, OpenExistentialMetatype)
|
|
OPERAND_OWNERSHIP(TrivialUse, OpenPackElement)
|
|
OPERAND_OWNERSHIP(TrivialUse, PointerToAddress)
|
|
OPERAND_OWNERSHIP(TrivialUse, ProjectBlockStorage)
|
|
OPERAND_OWNERSHIP(TrivialUse, RawPointerToRef)
|
|
OPERAND_OWNERSHIP(TrivialUse, SelectEnumAddr)
|
|
OPERAND_OWNERSHIP(TrivialUse, StructElementAddr)
|
|
OPERAND_OWNERSHIP(TrivialUse, SwitchEnumAddr)
|
|
OPERAND_OWNERSHIP(TrivialUse, SwitchValue)
|
|
OPERAND_OWNERSHIP(TrivialUse, TailAddr)
|
|
OPERAND_OWNERSHIP(TrivialUse, ThickToObjCMetatype)
|
|
OPERAND_OWNERSHIP(TrivialUse, ThinToThickFunction)
|
|
OPERAND_OWNERSHIP(TrivialUse, TupleElementAddr)
|
|
OPERAND_OWNERSHIP(TrivialUse, UncheckedAddrCast)
|
|
OPERAND_OWNERSHIP(TrivialUse, UncheckedRefCastAddr)
|
|
OPERAND_OWNERSHIP(TrivialUse, UncheckedTakeEnumDataAddr)
|
|
OPERAND_OWNERSHIP(TrivialUse, UnconditionalCheckedCastAddr)
|
|
OPERAND_OWNERSHIP(TrivialUse, DynamicPackIndex)
|
|
OPERAND_OWNERSHIP(TrivialUse, PackPackIndex)
|
|
OPERAND_OWNERSHIP(TrivialUse, PackElementGet)
|
|
OPERAND_OWNERSHIP(TrivialUse, PackElementSet)
|
|
OPERAND_OWNERSHIP(TrivialUse, TuplePackElementAddr)
|
|
OPERAND_OWNERSHIP(TrivialUse, GlobalAddr)
|
|
|
|
// The dealloc_stack_ref operand needs to have NonUse ownership because
|
|
// this use comes after the last consuming use (which is usually a dealloc_ref).
|
|
OPERAND_OWNERSHIP(NonUse, DeallocStackRef)
|
|
OPERAND_OWNERSHIP(InstantaneousUse, IgnoredUse)
|
|
|
|
// Use an owned or guaranteed value only for the duration of the operation.
|
|
OPERAND_OWNERSHIP(InstantaneousUse, ExistentialMetatype)
|
|
OPERAND_OWNERSHIP(InstantaneousUse, FixLifetime)
|
|
OPERAND_OWNERSHIP(InstantaneousUse, WitnessMethod)
|
|
OPERAND_OWNERSHIP(InstantaneousUse, DynamicMethodBranch)
|
|
OPERAND_OWNERSHIP(InstantaneousUse, ValueMetatype)
|
|
OPERAND_OWNERSHIP(InstantaneousUse, ClassMethod)
|
|
OPERAND_OWNERSHIP(InstantaneousUse, SuperMethod)
|
|
OPERAND_OWNERSHIP(InstantaneousUse, ClassifyBridgeObject)
|
|
OPERAND_OWNERSHIP(InstantaneousUse, UnownedCopyValue)
|
|
OPERAND_OWNERSHIP(InstantaneousUse, WeakCopyValue)
|
|
OPERAND_OWNERSHIP(InstantaneousUse, ExtendLifetime)
|
|
OPERAND_OWNERSHIP(InstantaneousUse, MergeIsolationRegion)
|
|
#define REF_STORAGE(Name, ...) \
|
|
OPERAND_OWNERSHIP(InstantaneousUse, StrongCopy##Name##Value)
|
|
#include "swift/AST/ReferenceStorage.def"
|
|
|
|
// Unowned uses ignore the value's ownership
|
|
OPERAND_OWNERSHIP(UnownedInstantaneousUse, DebugValue)
|
|
OPERAND_OWNERSHIP(UnownedInstantaneousUse, CopyBlock)
|
|
OPERAND_OWNERSHIP(UnownedInstantaneousUse, CopyValue)
|
|
OPERAND_OWNERSHIP(UnownedInstantaneousUse, ExplicitCopyValue)
|
|
OPERAND_OWNERSHIP(UnownedInstantaneousUse, ObjCMethod)
|
|
OPERAND_OWNERSHIP(UnownedInstantaneousUse, ObjCSuperMethod)
|
|
OPERAND_OWNERSHIP(UnownedInstantaneousUse, UnmanagedRetainValue)
|
|
OPERAND_OWNERSHIP(UnownedInstantaneousUse, UnmanagedReleaseValue)
|
|
OPERAND_OWNERSHIP(UnownedInstantaneousUse, UnmanagedAutoreleaseValue)
|
|
|
|
// These act as a form of conversion that does not imply ownership. Thus from an
|
|
// operand perspective we treat them as a pointer escape and from a value
|
|
// perspective, they return a value with OwnershipKind::Unowned.
|
|
#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
|
|
OPERAND_OWNERSHIP(PointerEscape, RefTo##Name) \
|
|
OPERAND_OWNERSHIP(PointerEscape, Name##ToRef)
|
|
#define UNCHECKED_REF_STORAGE(Name, ...) \
|
|
OPERAND_OWNERSHIP(PointerEscape, RefTo##Name)
|
|
#include "swift/AST/ReferenceStorage.def"
|
|
|
|
// Instructions that currently violate structural ownership requirements,
|
|
// and therefore completely defeat canonicalization and optimization of any
|
|
// OSSA value that they use.
|
|
OPERAND_OWNERSHIP(PointerEscape, ProjectBox) // The result is a T*.
|
|
OPERAND_OWNERSHIP(PointerEscape, ProjectExistentialBox)
|
|
OPERAND_OWNERSHIP(PointerEscape, UncheckedOwnershipConversion)
|
|
OPERAND_OWNERSHIP(PointerEscape, ConvertEscapeToNoEscape)
|
|
|
|
// UncheckedBitwiseCast ownership behaves like RefToUnowned. It produces an
|
|
// Unowned value from a non-trivial value, without consuming or borrowing the
|
|
// non-trivial value. Unlike RefToUnowned, a bitwise cast works on a compound
|
|
// value and may truncate the value. The resulting value is still Unowned and
|
|
// should be immediately copied to produce an owned value. These happen for two
|
|
// reasons:
|
|
//
|
|
// (1) A Builtin.reinterpretCast is used on a nontrivial type. If the result is
|
|
// non-trivial, then, as part of emitting the cast, SILGen emits a copy_value
|
|
// immediately after the unchecked_bitwise_cast for all uses of the cast.
|
|
//
|
|
// (2) SILGen emits special conversions using SILGenBuilder's
|
|
// createUncheckedBitCast utility. For non-trivial types, this emits an
|
|
// unchecked_bitwise_cast immediately followed by a copy.
|
|
//
|
|
// The only thing protecting the lifetime of the Unowned value is the cast
|
|
// operand's PointerEscape ownership, which prevents OSSA analysis and blocks
|
|
// most optimization of the incoming value.
|
|
//
|
|
// TODO: Verify that Unowned values are only used by copies and that the cast
|
|
// operand's lifetime exceeds the copies.
|
|
OPERAND_OWNERSHIP(PointerEscape, UncheckedBitwiseCast)
|
|
|
|
// Instructions that escape reference bits with unenforced lifetime.
|
|
// TODO: verify that BitwiseEscape results always have a trivial type.
|
|
OPERAND_OWNERSHIP(BitwiseEscape, ValueToBridgeObject)
|
|
OPERAND_OWNERSHIP(BitwiseEscape, RefToRawPointer)
|
|
OPERAND_OWNERSHIP(BitwiseEscape, UncheckedTrivialBitCast)
|
|
OPERAND_OWNERSHIP(BitwiseEscape, BridgeObjectToWord)
|
|
|
|
// Instructions that end the lifetime of an owned value.
|
|
OPERAND_OWNERSHIP(DestroyingConsume, AutoreleaseValue)
|
|
OPERAND_OWNERSHIP(DestroyingConsume, DeallocBox)
|
|
OPERAND_OWNERSHIP(DestroyingConsume, DeallocExistentialBox)
|
|
OPERAND_OWNERSHIP(DestroyingConsume, DeallocRef)
|
|
OPERAND_OWNERSHIP(DestroyingConsume, DestroyValue)
|
|
OPERAND_OWNERSHIP(DestroyingConsume, DestroyNotEscapedClosure)
|
|
OPERAND_OWNERSHIP(DestroyingConsume, EndLifetime)
|
|
OPERAND_OWNERSHIP(DestroyingConsume, BeginCOWMutation)
|
|
OPERAND_OWNERSHIP(DestroyingConsume, EndCOWMutation)
|
|
OPERAND_OWNERSHIP(TrivialUse, EndCOWMutationAddr)
|
|
OPERAND_OWNERSHIP(DestroyingConsume, EndInitLetRef)
|
|
// The move_value instruction creates a distinct lifetime.
|
|
OPERAND_OWNERSHIP(DestroyingConsume, MoveValue)
|
|
|
|
// Instructions that move an owned value.
|
|
OPERAND_OWNERSHIP(ForwardingConsume, InitExistentialValue)
|
|
OPERAND_OWNERSHIP(ForwardingConsume, DeinitExistentialValue)
|
|
OPERAND_OWNERSHIP(ForwardingConsume, MarkUninitialized)
|
|
OPERAND_OWNERSHIP(ForwardingConsume, Throw)
|
|
OPERAND_OWNERSHIP(ForwardingConsume, Thunk)
|
|
|
|
// Instructions that expose a pointer within a borrow scope.
|
|
OPERAND_OWNERSHIP(InteriorPointer, RefElementAddr)
|
|
OPERAND_OWNERSHIP(InteriorPointer, RefTailAddr)
|
|
OPERAND_OWNERSHIP(InteriorPointer, OpenExistentialBox)
|
|
OPERAND_OWNERSHIP(InstantaneousUse, HopToExecutor)
|
|
OPERAND_OWNERSHIP(PointerEscape, ExtractExecutor)
|
|
|
|
// Instructions that propagate a value within a borrow scope.
|
|
OPERAND_OWNERSHIP(GuaranteedForwarding, TupleExtract)
|
|
OPERAND_OWNERSHIP(GuaranteedForwarding, TuplePackExtract)
|
|
OPERAND_OWNERSHIP(GuaranteedForwarding, StructExtract)
|
|
OPERAND_OWNERSHIP(GuaranteedForwarding, DifferentiableFunctionExtract)
|
|
OPERAND_OWNERSHIP(GuaranteedForwarding, LinearFunctionExtract)
|
|
// FIXME: OpenExistential[Box]Value should be able to take owned values too by
|
|
// using getForwardingOperandOwnership.
|
|
OPERAND_OWNERSHIP(GuaranteedForwarding, OpenExistentialValue)
|
|
OPERAND_OWNERSHIP(GuaranteedForwarding, OpenExistentialBoxValue)
|
|
OPERAND_OWNERSHIP(GuaranteedForwarding, FunctionExtractIsolation)
|
|
OPERAND_OWNERSHIP(GuaranteedForwarding, ImplicitActorToOpaqueIsolationCast)
|
|
|
|
OPERAND_OWNERSHIP(EndBorrow, EndBorrow)
|
|
|
|
// The begin_apply token represents the borrow scope of all owned and guaranteed
|
|
// call arguments. Although SILToken is (currently) trivially typed, it must
|
|
// have guaranteed ownership so end_apply and abort_apply will be recognized
|
|
// as lifetime-ending uses.
|
|
OPERAND_OWNERSHIP(EndBorrow, EndApply)
|
|
OPERAND_OWNERSHIP(EndBorrow, AbortApply)
|
|
|
|
#define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
|
|
OPERAND_OWNERSHIP(TrivialUse, Load##Name)
|
|
#define ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
|
|
OPERAND_OWNERSHIP(DestroyingConsume, Name##Release)
|
|
#define SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
|
|
NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, "...") \
|
|
ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, "...")
|
|
#define UNCHECKED_REF_STORAGE(Name, ...) \
|
|
OPERAND_OWNERSHIP(TrivialUse, Name##ToRef)
|
|
#include "swift/AST/ReferenceStorage.def"
|
|
|
|
#define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
|
|
OPERAND_OWNERSHIP(InstantaneousUse, Store##Name)
|
|
#define SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
|
|
NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, "...")
|
|
#include "swift/AST/ReferenceStorage.def"
|
|
|
|
#undef OPERAND_OWNERSHIP
|
|
|
|
// Forwarding operations are conditionally either ForwardingConsumes or
|
|
// GuaranteedForwarding, depending on the instruction's constant ownership
|
|
// attribute.
|
|
#define FORWARDING_OWNERSHIP(INST) \
|
|
OperandOwnership OperandOwnershipClassifier::visit##INST##Inst( \
|
|
INST##Inst *i) { \
|
|
return i->getForwardingOwnershipKind().getForwardingOperandOwnership( \
|
|
/*allowUnowned*/ false); \
|
|
}
|
|
FORWARDING_OWNERSHIP(OpenExistentialRef)
|
|
FORWARDING_OWNERSHIP(ConvertFunction)
|
|
FORWARDING_OWNERSHIP(RefToBridgeObject)
|
|
FORWARDING_OWNERSHIP(BridgeObjectToRef)
|
|
FORWARDING_OWNERSHIP(UnconditionalCheckedCast)
|
|
FORWARDING_OWNERSHIP(InitExistentialRef)
|
|
FORWARDING_OWNERSHIP(DifferentiableFunction)
|
|
FORWARDING_OWNERSHIP(LinearFunction)
|
|
FORWARDING_OWNERSHIP(MarkUnresolvedNonCopyableValue)
|
|
FORWARDING_OWNERSHIP(MarkUnresolvedReferenceBinding)
|
|
FORWARDING_OWNERSHIP(MoveOnlyWrapperToCopyableValue)
|
|
FORWARDING_OWNERSHIP(CopyableToMoveOnlyWrapperValue)
|
|
FORWARDING_OWNERSHIP(MoveOnlyWrapperToCopyableBox)
|
|
#undef FORWARDING_OWNERSHIP
|
|
|
|
// Arbitrary value casts are forwarding instructions that are also allowed to
|
|
// propagate Unowned values.
|
|
#define FORWARDING_ANY_OWNERSHIP(INST) \
|
|
OperandOwnership OperandOwnershipClassifier::visit##INST##Inst( \
|
|
INST##Inst *i) { \
|
|
return i->getForwardingOwnershipKind().getForwardingOperandOwnership( \
|
|
/*allowUnowned*/ true); \
|
|
}
|
|
FORWARDING_ANY_OWNERSHIP(Upcast)
|
|
FORWARDING_ANY_OWNERSHIP(UncheckedRefCast)
|
|
FORWARDING_ANY_OWNERSHIP(UncheckedValueCast)
|
|
FORWARDING_ANY_OWNERSHIP(CheckedCastBranch)
|
|
#undef FORWARDING_ANY_OWNERSHIP
|
|
|
|
// Any valid ownership kind can be combined with values of None ownership, but
|
|
// they cannot be combined with each other. An aggregates result ownership is
|
|
// the meet of its operands' ownership. A destructured member has the same
|
|
// ownership as its aggregate unless its type gives it None ownership.
|
|
//
|
|
// TODO: Aggregate operations should be Reborrows, not GuaranteedForwarding,
|
|
// because the borrowed value is different on either side of the operation and
|
|
// the lifetimes of borrowed members could differ.
|
|
#define AGGREGATE_OWNERSHIP(INST) \
|
|
OperandOwnership OperandOwnershipClassifier::visit##INST##Inst( \
|
|
INST##Inst *i) { \
|
|
return i->getForwardingOwnershipKind().getForwardingOperandOwnership( \
|
|
/*allowUnowned*/ true); \
|
|
}
|
|
AGGREGATE_OWNERSHIP(Tuple)
|
|
AGGREGATE_OWNERSHIP(Struct)
|
|
AGGREGATE_OWNERSHIP(DestructureStruct)
|
|
AGGREGATE_OWNERSHIP(DestructureTuple)
|
|
AGGREGATE_OWNERSHIP(Enum)
|
|
AGGREGATE_OWNERSHIP(UncheckedEnumData)
|
|
AGGREGATE_OWNERSHIP(SwitchEnum)
|
|
AGGREGATE_OWNERSHIP(UncheckedOwnership)
|
|
#undef AGGREGATE_OWNERSHIP
|
|
|
|
// A begin_borrow is conditionally nested.
|
|
OperandOwnership
|
|
OperandOwnershipClassifier::visitBeginBorrowInst(BeginBorrowInst *borrow) {
|
|
return OperandOwnership::Borrow;
|
|
}
|
|
|
|
OperandOwnership
|
|
OperandOwnershipClassifier::visitBorrowedFromInst(BorrowedFromInst *bfi) {
|
|
return getOperandIndex() == 0 ? OperandOwnership::GuaranteedForwarding
|
|
: OperandOwnership::Borrow;
|
|
}
|
|
|
|
// MARK: Instructions whose use ownership depends on the operand in question.
|
|
|
|
OperandOwnership OperandOwnershipClassifier::
|
|
visitDeallocPartialRefInst(DeallocPartialRefInst *i) {
|
|
if (getValue() == i->getInstance()) {
|
|
return OperandOwnership::DestroyingConsume;
|
|
}
|
|
return OperandOwnership::TrivialUse;
|
|
}
|
|
|
|
OperandOwnership
|
|
OperandOwnershipClassifier::visitSelectEnumInst(SelectEnumInst *i) {
|
|
if (getValue() == i->getEnumOperand()) {
|
|
return OperandOwnership::InstantaneousUse;
|
|
}
|
|
assert(i->getType().isTrivial(i->getFunction()));
|
|
return OperandOwnership::TrivialUse;
|
|
}
|
|
|
|
OperandOwnership OperandOwnershipClassifier::visitBranchInst(BranchInst *bi) {
|
|
auto *destArg = bi->getDestBB()->getArgument(getOperandIndex());
|
|
ValueOwnershipKind destBlockArgOwnershipKind = destArg->getOwnershipKind();
|
|
|
|
if (destBlockArgOwnershipKind == OwnershipKind::Guaranteed) {
|
|
return destArg->isGuaranteedForwarding()
|
|
? OperandOwnership::GuaranteedForwarding
|
|
: OperandOwnership::Reborrow;
|
|
}
|
|
return destBlockArgOwnershipKind.getForwardingOperandOwnership(
|
|
/*allowUnowned*/true);
|
|
}
|
|
|
|
OperandOwnership
|
|
OperandOwnershipClassifier::visitStoreBorrowInst(StoreBorrowInst *i) {
|
|
if (getValue() == i->getSrc()) {
|
|
return OperandOwnership::Borrow;
|
|
}
|
|
return OperandOwnership::TrivialUse;
|
|
}
|
|
|
|
OperandOwnership
|
|
OperandOwnershipClassifier::visitDropDeinitInst(DropDeinitInst *i) {
|
|
return i->getType().isAddress() ? OperandOwnership::TrivialUse
|
|
: OperandOwnership::ForwardingConsume;
|
|
}
|
|
|
|
// Get the OperandOwnership for instantaneous apply, yield, and return uses.
|
|
// This does not apply to uses that begin an explicit borrow scope in the
|
|
// caller, such as begin_apply.
|
|
static OperandOwnership getFunctionArgOwnership(SILArgumentConvention argConv,
|
|
bool hasScopeInCaller,
|
|
bool forwardsGuaranteedValues) {
|
|
|
|
switch (argConv) {
|
|
case SILArgumentConvention::Indirect_In_CXX:
|
|
case SILArgumentConvention::Indirect_In:
|
|
case SILArgumentConvention::Direct_Owned:
|
|
case SILArgumentConvention::Pack_Owned:
|
|
return OperandOwnership::ForwardingConsume;
|
|
|
|
// A guaranteed argument is forwarded into the callee. If the call itself has
|
|
// no scope (i.e. it is a single apply, try_apply or yield), then from the
|
|
// caller's point of view it looks like an instantaneous use. Consequently,
|
|
// owned values may be passed to guaranteed arguments without an explicit
|
|
// borrow scope in the caller. In contrast, a begin_apply /does/ have an
|
|
// explicit borrow scope in the caller so we must treat arguments passed to it
|
|
// as being borrowed for the entire region of coroutine execution.
|
|
case SILArgumentConvention::Indirect_In_Guaranteed:
|
|
case SILArgumentConvention::Pack_Guaranteed:
|
|
case SILArgumentConvention::Pack_Inout:
|
|
case SILArgumentConvention::Pack_Out:
|
|
// For an apply that begins a borrow scope, its arguments are borrowed
|
|
// throughout the caller's borrow scope.
|
|
return hasScopeInCaller ? OperandOwnership::Borrow
|
|
: OperandOwnership::InstantaneousUse;
|
|
|
|
case SILArgumentConvention::Direct_Guaranteed: {
|
|
if (hasScopeInCaller) {
|
|
return OperandOwnership::Borrow;
|
|
}
|
|
if (forwardsGuaranteedValues) {
|
|
return OperandOwnership::GuaranteedForwarding;
|
|
}
|
|
return OperandOwnership::InstantaneousUse;
|
|
}
|
|
|
|
case SILArgumentConvention::Direct_Unowned:
|
|
return OperandOwnership::UnownedInstantaneousUse;
|
|
|
|
case SILArgumentConvention::Indirect_Out:
|
|
case SILArgumentConvention::Indirect_Inout:
|
|
case SILArgumentConvention::Indirect_InoutAliasable:
|
|
ASSERT(false && "Illegal convention for non-address types");
|
|
}
|
|
llvm_unreachable("covered switch");
|
|
}
|
|
|
|
OperandOwnership
|
|
OperandOwnershipClassifier::visitFullApply(FullApplySite apply) {
|
|
// Before considering conventions, filter all (trivial) indirect
|
|
// arguments. This also rules out result arguments.
|
|
if (getValue()->getType().isAddress()) {
|
|
return OperandOwnership::TrivialUse;
|
|
}
|
|
auto calleeTy = apply.getSubstCalleeType();
|
|
SILArgumentConvention argConv = [&]() {
|
|
if (apply.isCalleeOperand(op)) {
|
|
return SILArgumentConvention(calleeTy->getCalleeConvention());
|
|
} else {
|
|
unsigned calleeArgIdx = apply.getCalleeArgIndexOfFirstAppliedArg()
|
|
+ apply.getAppliedArgIndex(op);
|
|
return silConv.getFunctionConventions(calleeTy).getSILArgumentConvention(
|
|
calleeArgIdx);
|
|
}
|
|
}();
|
|
|
|
auto argOwnership = getFunctionArgOwnership(
|
|
argConv, /*hasScopeInCaller*/ apply.beginsCoroutineEvaluation(),
|
|
/*forwardsGuaranteedValues*/ apply.hasGuaranteedResult());
|
|
|
|
// OSSA cleanup needs to handle each of these callee ownership cases.
|
|
//
|
|
// OperandOwnership::ForwardingConsume is only for thick @callee_owned.
|
|
//
|
|
// OperandOwnership::Borrow would only happen for a coroutine closure, which
|
|
// isn't yet possible.
|
|
if (apply.isCalleeOperand(op)) {
|
|
assert((argOwnership == OperandOwnership::TrivialUse
|
|
|| argOwnership == OperandOwnership::UnownedInstantaneousUse
|
|
|| argOwnership == OperandOwnership::InstantaneousUse
|
|
|| argOwnership == OperandOwnership::ForwardingConsume
|
|
|| argOwnership == OperandOwnership::Borrow) &&
|
|
"unsupported callee ownership");
|
|
}
|
|
return argOwnership;
|
|
}
|
|
|
|
OperandOwnership
|
|
OperandOwnershipClassifier::visitBeginApplyInst(BeginApplyInst *i) {
|
|
return visitFullApply(i);
|
|
}
|
|
|
|
OperandOwnership OperandOwnershipClassifier::visitApplyInst(ApplyInst *i) {
|
|
return visitFullApply(i);
|
|
}
|
|
|
|
OperandOwnership
|
|
OperandOwnershipClassifier::visitTryApplyInst(TryApplyInst *i) {
|
|
return visitFullApply(i);
|
|
}
|
|
|
|
OperandOwnership
|
|
OperandOwnershipClassifier::visitPartialApplyInst(PartialApplyInst *i) {
|
|
// partial_apply [stack] borrows its operands.
|
|
if (i->isOnStack()) {
|
|
auto operandTy = getValue()->getType();
|
|
// Trivial values we can treat as trivial uses.
|
|
if (operandTy.isTrivial(*i->getFunction())) {
|
|
return OperandOwnership::TrivialUse;
|
|
}
|
|
|
|
// Borrowing of address operands is ultimately handled by the move-only
|
|
// address checker and/or exclusivity checker rather than by value ownership.
|
|
if (operandTy.isAddress()) {
|
|
return OperandOwnership::TrivialUse;
|
|
}
|
|
|
|
return OperandOwnership::Borrow;
|
|
}
|
|
// All non-trivial types should be captured.
|
|
return OperandOwnership::ForwardingConsume;
|
|
}
|
|
|
|
OperandOwnership OperandOwnershipClassifier::visitYieldInst(YieldInst *i) {
|
|
// Before considering conventions, filter all indirect arguments.
|
|
if (getValue()->getType().isAddress()) {
|
|
return OperandOwnership::TrivialUse;
|
|
}
|
|
auto fnType = i->getFunction()->getLoweredFunctionType();
|
|
SILArgumentConvention argConv(
|
|
fnType->getYields()[getOperandIndex()].getConvention());
|
|
return getFunctionArgOwnership(argConv, /*hasScopeInCaller*/ false,
|
|
/*forwardsGuaranteedValues*/ false);
|
|
}
|
|
|
|
OperandOwnership OperandOwnershipClassifier::visitReturnInst(ReturnInst *i) {
|
|
switch (i->getOwnershipKind()) {
|
|
case OwnershipKind::Any:
|
|
llvm_unreachable("invalid value ownership");
|
|
case OwnershipKind::Guaranteed:
|
|
return OperandOwnership::GuaranteedForwarding;
|
|
case OwnershipKind::None:
|
|
return OperandOwnership::TrivialUse;
|
|
case OwnershipKind::Unowned:
|
|
return OperandOwnership::UnownedInstantaneousUse;
|
|
case OwnershipKind::Owned:
|
|
return OperandOwnership::ForwardingConsume;
|
|
}
|
|
llvm_unreachable("covered switch");
|
|
}
|
|
|
|
OperandOwnership
|
|
OperandOwnershipClassifier::visitReturnBorrowInst(ReturnBorrowInst *rbi) {
|
|
return getOperandIndex() == 0 ? OperandOwnership::GuaranteedForwarding
|
|
: OperandOwnership::EndBorrow;
|
|
}
|
|
|
|
OperandOwnership OperandOwnershipClassifier::visitAssignInst(AssignInst *i) {
|
|
if (getValue() != i->getSrc()) {
|
|
return OperandOwnership::TrivialUse;
|
|
}
|
|
return OperandOwnership::DestroyingConsume;
|
|
}
|
|
|
|
OperandOwnership
|
|
OperandOwnershipClassifier::visitAssignOrInitInst(AssignOrInitInst *i) {
|
|
if (getValue() == i->getSrc()) {
|
|
return OperandOwnership::DestroyingConsume;
|
|
}
|
|
|
|
// initializer/setter closure
|
|
return OperandOwnership::InstantaneousUse;
|
|
}
|
|
|
|
OperandOwnership OperandOwnershipClassifier::visitStoreInst(StoreInst *i) {
|
|
if (getValue() != i->getSrc()) {
|
|
return OperandOwnership::TrivialUse;
|
|
}
|
|
return OperandOwnership::DestroyingConsume;
|
|
}
|
|
|
|
OperandOwnership OperandOwnershipClassifier::visitCopyBlockWithoutEscapingInst(
|
|
CopyBlockWithoutEscapingInst *i) {
|
|
// Consumes the closure parameter.
|
|
if (getValue() == i->getClosure()) {
|
|
return OperandOwnership::ForwardingConsume;
|
|
}
|
|
return OperandOwnership::UnownedInstantaneousUse;
|
|
}
|
|
|
|
template<SILInstructionKind Opc, typename Derived>
|
|
static OperandOwnership
|
|
visitMarkDependenceInstBase(MarkDependenceInstBase<Opc, Derived> *mdi) {
|
|
}
|
|
|
|
OperandOwnership
|
|
OperandOwnershipClassifier::visitMarkDependenceInst(MarkDependenceInst *mdi) {
|
|
// If we are analyzing "the value", we forward ownership.
|
|
if (getOperandIndex() == MarkDependenceInst::Dependent) {
|
|
return getOwnershipKind().getForwardingOperandOwnership(
|
|
/*allowUnowned*/true);
|
|
}
|
|
if (mdi->isNonEscaping()) {
|
|
if (auto *svi = dyn_cast<SingleValueInstruction>(mdi)) {
|
|
if (!svi->getType().isAddress()) {
|
|
// This creates a "dependent value", just like on-stack partial_apply,
|
|
// which we treat like a borrow.
|
|
return OperandOwnership::Borrow;
|
|
}
|
|
}
|
|
return OperandOwnership::AnyInteriorPointer;
|
|
}
|
|
if (mdi->hasUnresolvedEscape()) {
|
|
// This creates a dependent value that may extend beyond the parent's
|
|
// lifetime.
|
|
return OperandOwnership::UnownedInstantaneousUse;
|
|
}
|
|
return OperandOwnership::PointerEscape;
|
|
}
|
|
|
|
OperandOwnership OperandOwnershipClassifier::
|
|
visitMarkDependenceAddrInst(MarkDependenceAddrInst *mdai) {
|
|
// If we are analyzing "the value", this is a trivial use
|
|
if (getOperandIndex() == MarkDependenceInst::Dependent) {
|
|
return OperandOwnership::TrivialUse;
|
|
}
|
|
return OperandOwnership::PointerEscape;
|
|
}
|
|
|
|
OperandOwnership
|
|
OperandOwnershipClassifier::visitBeginDeallocRefInst(BeginDeallocRefInst *bdr) {
|
|
if (getOperandIndex() == 0) {
|
|
return OperandOwnership::DestroyingConsume;
|
|
}
|
|
return OperandOwnership::NonUse;
|
|
}
|
|
|
|
OperandOwnership OperandOwnershipClassifier::visitKeyPathInst(KeyPathInst *I) {
|
|
// KeyPath moves the value in memory out of address operands, but the
|
|
// ownership checker doesn't reason about that yet.
|
|
return OperandOwnership::ForwardingConsume;
|
|
}
|
|
|
|
OperandOwnership OperandOwnershipClassifier::visitTupleAddrConstructorInst(
|
|
TupleAddrConstructorInst *inst) {
|
|
// If we have an object, then we have an instantaneous use...
|
|
if (getValue()->getType().isObject())
|
|
return OperandOwnership::DestroyingConsume;
|
|
// Otherwise, we have a trivial use since we have an address.
|
|
return OperandOwnership::TrivialUse;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Builtin Use Checker
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
namespace {
|
|
|
|
struct OperandOwnershipBuiltinClassifier
|
|
: SILBuiltinVisitor<OperandOwnershipBuiltinClassifier, OperandOwnership> {
|
|
using Map = OperandOwnership;
|
|
|
|
const Operand &op;
|
|
OperandOwnershipBuiltinClassifier(const Operand &op) : op(op) {}
|
|
|
|
OperandOwnership visitLLVMIntrinsic(BuiltinInst *bi, llvm::Intrinsic::ID id) {
|
|
// LLVM intrinsics do not traffic in ownership, so if we have a result, it
|
|
// must be trivial.
|
|
return OperandOwnership::TrivialUse;
|
|
}
|
|
|
|
// BUILTIN_TYPE_CHECKER_OPERATION does not live past the type checker.
|
|
#define BUILTIN_TYPE_CHECKER_OPERATION(ID, NAME)
|
|
|
|
#define BUILTIN(ID, NAME, ATTRS) \
|
|
OperandOwnership visit##ID(BuiltinInst *bi, StringRef attr);
|
|
#include "swift/AST/Builtins.def"
|
|
|
|
OperandOwnership check(BuiltinInst *bi) { return visit(bi); }
|
|
|
|
OperandOwnership visitCreateAsyncTask(BuiltinInst *bi, StringRef attr,
|
|
int operationIndex);
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
#define BUILTIN_OPERAND_OWNERSHIP(OWNERSHIP, ID) \
|
|
OperandOwnership \
|
|
OperandOwnershipBuiltinClassifier::visit##ID(BuiltinInst *, StringRef) { \
|
|
return OperandOwnership::OWNERSHIP; \
|
|
}
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, ErrorInMain)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, UnexpectedError)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, WillThrow)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, AddressOfBorrowOpaque)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, UnprotectedAddressOfBorrowOpaque)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, AShr)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, GenericAShr)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, Add)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, GenericAdd)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, Alignof)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, AllocRaw)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, And)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, GenericAnd)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, AssertConf)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, AssignCopyArrayNoAlias)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, AssignCopyArrayFrontToBack)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, AssignCopyArrayBackToFront)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, AssignTakeArray)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, AssumeAlignment)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, AssumeNonNegative)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, AssumeTrue)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, AtomicLoad)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, AtomicRMW)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, AtomicStore)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, BitCast)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, CanBeObjCClass)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, CondFailMessage)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, CmpXChg)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, CondUnreachable)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, CopyArray)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, DeallocRaw)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, DestroyArray)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, ExactSDiv)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, GenericExactSDiv)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, ExactUDiv)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, GenericExactUDiv)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, ExtractElement)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, FAdd)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, GenericFAdd)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, FCMP_OEQ)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, FCMP_OGE)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, FCMP_OGT)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, FCMP_OLE)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, FCMP_OLT)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, FCMP_ONE)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, FCMP_ORD)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, FCMP_UEQ)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, FCMP_UGE)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, FCMP_UGT)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, FCMP_ULE)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, FCMP_ULT)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, FCMP_UNE)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, FCMP_UNO)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, FDiv)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, GenericFDiv)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, FMul)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, GenericFMul)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, FNeg)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, FPExt)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, FPToSI)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, FPToUI)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, FPTrunc)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, FRem)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, GenericFRem)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, FSub)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, GenericFSub)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, Fence)
|
|
BUILTIN_OPERAND_OWNERSHIP(TrivialUse, Freeze)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, Ifdef)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, GetObjCTypeEncoding)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, ICMP_EQ)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, ICMP_NE)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, ICMP_SGE)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, ICMP_SGT)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, ICMP_SLE)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, ICMP_SLT)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, ICMP_UGE)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, ICMP_UGT)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, ICMP_ULE)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, ICMP_ULT)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, InsertElement)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, IntToFPWithOverflow)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, BitWidth)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, IsNegative)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, WordAtIndex)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, IntToPtr)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, IsOptionalType)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, IsPOD)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, IsConcrete)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, IsBitwiseTakable)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, IsSameMetatype)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, LShr)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, GenericLShr)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, Mul)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, GenericMul)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, OnFastPath)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, Once)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, OnceWithContext)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, Or)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, GenericOr)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, PtrToInt)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, SAddOver)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, SDiv)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, GenericSDiv)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, SExt)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, SExtOrBitCast)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, SIToFP)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, SMulOver)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, SRem)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, GenericSRem)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, SSubOver)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, StackAlloc)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, UnprotectedStackAlloc)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, StackDealloc)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, AllocVector)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, SToSCheckedTrunc)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, SToUCheckedTrunc)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, Expect)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, Shl)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, GenericShl)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, Select)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, ShuffleVector)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, Interleave)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, Deinterleave)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, Sizeof)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, StaticReport)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, Strideof)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, StringObjectOr)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, Sub)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, GenericSub)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, TakeArrayNoAlias)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, TakeArrayBackToFront)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, TakeArrayFrontToBack)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, Trunc)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, TruncOrBitCast)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, TSanInoutAccess)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, UAddOver)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, UDiv)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, GenericUDiv)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, UIToFP)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, UMulOver)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, URem)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, GenericURem)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, USubOver)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, UToSCheckedTrunc)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, UToUCheckedTrunc)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, Unreachable)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, Xor)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, GenericXor)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, ZExt)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, ZExtOrBitCast)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, ZeroInitializer)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, PrepareInitialization)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, PoundAssert)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, GlobalStringTablePointer)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, TypePtrAuthDiscriminator)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, TargetOSVersionAtLeast)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, TargetVariantOSVersionAtLeast)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse,
|
|
TargetOSVersionOrVariantOSVersionAtLeast)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, GetEnumTag)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, InjectEnumTag)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, DistributedActorAsAnyActor)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, AddressOfRawLayout)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, EndAsyncLetLifetime)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, CreateTaskGroup)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, CreateTaskGroupWithFlags)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, DestroyTaskGroup)
|
|
|
|
BUILTIN_OPERAND_OWNERSHIP(ForwardingConsume, COWBufferForReading)
|
|
|
|
// This should actually never be seen in SIL
|
|
BUILTIN_OPERAND_OWNERSHIP(GuaranteedForwarding, ExtractFunctionIsolation)
|
|
|
|
static OperandOwnership
|
|
visitAnyFlowSensitiveSelfIsolation(BuiltinInst *bi) {
|
|
// In potentially-delegating initializers, the operand will be the
|
|
// address of the box instead of the actor instance.
|
|
auto operand = bi->getOperand(0);
|
|
if (operand->getType().isAddress())
|
|
return OperandOwnership::TrivialUse;
|
|
return OperandOwnership::InstantaneousUse;
|
|
}
|
|
|
|
OperandOwnership
|
|
OperandOwnershipBuiltinClassifier
|
|
::visitFlowSensitiveSelfIsolation(BuiltinInst *bi, StringRef attr) {
|
|
return visitAnyFlowSensitiveSelfIsolation(bi);
|
|
}
|
|
OperandOwnership
|
|
OperandOwnershipBuiltinClassifier
|
|
::visitFlowSensitiveDistributedSelfIsolation(BuiltinInst *bi, StringRef attr) {
|
|
return visitAnyFlowSensitiveSelfIsolation(bi);
|
|
}
|
|
|
|
OperandOwnership
|
|
OperandOwnershipBuiltinClassifier
|
|
::visitStartAsyncLetWithLocalBuffer(BuiltinInst *bi, StringRef attr) {
|
|
if (&op == &bi->getOperandRef(0)) {
|
|
// The result buffer pointer is a trivial use.
|
|
return OperandOwnership::TrivialUse;
|
|
}
|
|
|
|
// The closure is borrowed while the async let task is executing.
|
|
return OperandOwnership::Borrow;
|
|
}
|
|
|
|
OperandOwnership
|
|
OperandOwnershipBuiltinClassifier::visitCreateAsyncTask(BuiltinInst *bi,
|
|
StringRef attr) {
|
|
if (&op == &bi->getOperandRef(4)) {
|
|
// The (any TaskExecutor)? (optional) must be consumed by the builtin,
|
|
// as we will keep it alive and later destroy it as the task runs to completion.
|
|
return OperandOwnership::ForwardingConsume;
|
|
}
|
|
|
|
// The function operand is consumed by the new task.
|
|
if (&op == &bi->getArgumentOperands().back())
|
|
return OperandOwnership::DestroyingConsume;
|
|
|
|
return OperandOwnership::InstantaneousUse;
|
|
}
|
|
|
|
OperandOwnership OperandOwnershipBuiltinClassifier::
|
|
visitResumeNonThrowingContinuationReturning(BuiltinInst *bi, StringRef attr) {
|
|
// The value operand is consumed.
|
|
if (&op == &bi->getOperandRef(1))
|
|
return OperandOwnership::DestroyingConsume;
|
|
|
|
return OperandOwnership::TrivialUse;
|
|
}
|
|
|
|
OperandOwnership OperandOwnershipBuiltinClassifier::
|
|
visitResumeThrowingContinuationReturning(BuiltinInst *bi, StringRef attr) {
|
|
// The value operand is consumed.
|
|
if (&op == &bi->getOperandRef(1))
|
|
return OperandOwnership::DestroyingConsume;
|
|
|
|
return OperandOwnership::TrivialUse;
|
|
}
|
|
|
|
OperandOwnership OperandOwnershipBuiltinClassifier::
|
|
visitResumeThrowingContinuationThrowing(BuiltinInst *bi, StringRef attr) {
|
|
// The value operand is consumed.
|
|
if (&op == &bi->getOperandRef(1))
|
|
return OperandOwnership::DestroyingConsume;
|
|
|
|
return OperandOwnership::TrivialUse;
|
|
}
|
|
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, TaskRunInline)
|
|
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, CancelAsyncTask)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, InitializeDefaultActor)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, DestroyDefaultActor)
|
|
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, InitializeDistributedRemoteActor)
|
|
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse,
|
|
InitializeNonDefaultDistributedActor)
|
|
|
|
BUILTIN_OPERAND_OWNERSHIP(PointerEscape, AutoDiffAllocateSubcontextWithType)
|
|
BUILTIN_OPERAND_OWNERSHIP(PointerEscape, AutoDiffProjectTopLevelSubcontext)
|
|
|
|
// FIXME: ConvertTaskToJob is documented as taking NativePointer. It's operand's
|
|
// ownership should be 'TrivialUse'.
|
|
BUILTIN_OPERAND_OWNERSHIP(ForwardingConsume, ConvertTaskToJob)
|
|
|
|
BUILTIN_OPERAND_OWNERSHIP(BitwiseEscape, BuildOrdinaryTaskExecutorRef)
|
|
BUILTIN_OPERAND_OWNERSHIP(BitwiseEscape, BuildOrdinarySerialExecutorRef)
|
|
BUILTIN_OPERAND_OWNERSHIP(BitwiseEscape, BuildComplexEqualitySerialExecutorRef)
|
|
BUILTIN_OPERAND_OWNERSHIP(BitwiseEscape, BuildDefaultActorExecutorRef)
|
|
BUILTIN_OPERAND_OWNERSHIP(BitwiseEscape, BuildMainActorExecutorRef)
|
|
|
|
BUILTIN_OPERAND_OWNERSHIP(TrivialUse, AutoDiffCreateLinearMapContextWithType)
|
|
#undef BUILTIN_OPERAND_OWNERSHIP
|
|
|
|
#define SHOULD_NEVER_VISIT_BUILTIN(ID) \
|
|
OperandOwnership \
|
|
OperandOwnershipBuiltinClassifier::visit##ID(BuiltinInst *, StringRef) { \
|
|
llvm_unreachable( \
|
|
"Builtin should never be visited! E.x.: It may not have arguments"); \
|
|
}
|
|
SHOULD_NEVER_VISIT_BUILTIN(GetCurrentAsyncTask)
|
|
SHOULD_NEVER_VISIT_BUILTIN(GetCurrentExecutor)
|
|
#undef SHOULD_NEVER_VISIT_BUILTIN
|
|
|
|
// Builtins that should be lowered to SIL instructions so we should never see
|
|
// them.
|
|
#define BUILTIN_SIL_OPERATION(ID, NAME, CATEGORY) \
|
|
OperandOwnership \
|
|
OperandOwnershipBuiltinClassifier::visit##ID(BuiltinInst *, StringRef) { \
|
|
llvm_unreachable("Builtin should have been lowered to SIL instruction?!"); \
|
|
}
|
|
#define BUILTIN(X, Y, Z)
|
|
#include "swift/AST/Builtins.def"
|
|
|
|
OperandOwnership OperandOwnershipClassifier::visitBuiltinInst(BuiltinInst *bi) {
|
|
return OperandOwnershipBuiltinClassifier(op).check(bi);
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Top Level Entrypoint
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
OperandOwnership
|
|
Operand::getOperandOwnership(SILModuleConventions *silConv) const {
|
|
// A type-dependent operand is a NonUse (as opposed to say an
|
|
// InstantaneousUse) because it does not require liveness.
|
|
if (isTypeDependent())
|
|
return OperandOwnership::NonUse;
|
|
|
|
// If we do not have ownership enabled, just return any. This ensures that we
|
|
// do not have any consuming uses and everything from an ownership perspective
|
|
// is just a liveness use short-circuiting many of the optimizations.
|
|
//
|
|
// We do not ever call this function when an instruction isn't in a block.
|
|
assert(getUser()->getParent() &&
|
|
"Can not lookup ownership constraint unless inserted into block");
|
|
if (auto *block = getUser()->getParent()) {
|
|
auto *func = block->getParent();
|
|
// If we don't have a function, then we must have a SILGlobalVariable. In
|
|
// that case, we act as if we aren't in ownership.
|
|
if (!func || !func->hasOwnership()) {
|
|
return OperandOwnership(OperandOwnership::InstantaneousUse);
|
|
}
|
|
}
|
|
SILModuleConventions overrideConv =
|
|
silConv ? *silConv : SILModuleConventions(getUser()->getModule());
|
|
OperandOwnershipClassifier classifier(overrideConv, *this);
|
|
return classifier.visit(const_cast<SILInstruction *>(getUser()));
|
|
}
|