| //===--- SILOwnershipVerifier.cpp -----------------------------------------===// |
| // |
| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2014 - 2017 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 |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #define DEBUG_TYPE "sil-ownership-verifier" |
| |
| #include "swift/AST/ASTContext.h" |
| #include "swift/AST/AnyFunctionRef.h" |
| #include "swift/AST/Decl.h" |
| #include "swift/AST/GenericEnvironment.h" |
| #include "swift/AST/Module.h" |
| #include "swift/AST/Types.h" |
| #include "swift/Basic/Range.h" |
| #include "swift/Basic/STLExtras.h" |
| #include "swift/Basic/TransformArrayRef.h" |
| #include "swift/ClangImporter/ClangModule.h" |
| #include "swift/SIL/BasicBlockUtils.h" |
| #include "swift/SIL/BranchPropagatedUser.h" |
| #include "swift/SIL/Dominance.h" |
| #include "swift/SIL/DynamicCasts.h" |
| #include "swift/SIL/OwnershipUtils.h" |
| #include "swift/SIL/PrettyStackTrace.h" |
| #include "swift/SIL/Projection.h" |
| #include "swift/SIL/SILBuiltinVisitor.h" |
| #include "swift/SIL/SILDebugScope.h" |
| #include "swift/SIL/SILFunction.h" |
| #include "swift/SIL/SILModule.h" |
| #include "swift/SIL/SILOpenedArchetypesTracker.h" |
| #include "swift/SIL/SILVTable.h" |
| #include "swift/SIL/SILVisitor.h" |
| #include "swift/SIL/TypeLowering.h" |
| #include "llvm/ADT/DenseSet.h" |
| #include "llvm/ADT/PostOrderIterator.h" |
| #include "llvm/ADT/StringSet.h" |
| #include "llvm/Support/CommandLine.h" |
| #include "llvm/Support/Debug.h" |
| #include <algorithm> |
| |
| using namespace swift; |
| using namespace swift::ownership; |
| |
| // This is an option to put the SILOwnershipVerifier in testing mode. This |
| // causes the following: |
| // |
| // 1. Instead of printing an error message and aborting, the verifier will print |
| // the message and continue. This allows for FileCheck testing of the verifier. |
| // |
| // 2. SILInstruction::verifyOperandOwnership() is disabled. This is used for |
| // verification in SILBuilder. This causes errors to be printed twice, once when |
| // we build the IR and a second time when we perform a full verification of the |
| // IR. For testing purposes, we just want the later. |
| llvm::cl::opt<bool> IsSILOwnershipVerifierTestingEnabled( |
| "sil-ownership-verifier-enable-testing", |
| llvm::cl::desc("Put the sil ownership verifier in testing mode. See " |
| "comment in SILOwnershipVerifier.cpp above option for more " |
| "information.")); |
| |
| /// This is an option to turn off ownership verification on a specific file. We |
| /// still emit code as if we are in ownership mode, but we do not verify. This |
| /// is useful for temporarily turning off verification on tests. |
| static llvm::cl::opt<bool> |
| DisableOwnershipVerification("disable-sil-ownership-verification"); |
| |
| //===----------------------------------------------------------------------===// |
| // Utility |
| //===----------------------------------------------------------------------===// |
| |
| static bool isValueAddressOrTrivial(SILValue V, SILModule &M) { |
| return V->getType().isAddress() || |
| V.getOwnershipKind() == ValueOwnershipKind::Trivial || |
| V.getOwnershipKind() == ValueOwnershipKind::Any; |
| } |
| |
| // These operations forward both owned and guaranteed ownership. |
| static bool isOwnershipForwardingValueKind(SILNodeKind K) { |
| switch (K) { |
| case SILNodeKind::TupleInst: |
| case SILNodeKind::StructInst: |
| case SILNodeKind::EnumInst: |
| case SILNodeKind::OpenExistentialRefInst: |
| case SILNodeKind::UpcastInst: |
| case SILNodeKind::UncheckedRefCastInst: |
| case SILNodeKind::ConvertFunctionInst: |
| case SILNodeKind::RefToBridgeObjectInst: |
| case SILNodeKind::BridgeObjectToRefInst: |
| case SILNodeKind::UnconditionalCheckedCastInst: |
| case SILNodeKind::UncheckedEnumDataInst: |
| case SILNodeKind::MarkUninitializedInst: |
| case SILNodeKind::SelectEnumInst: |
| case SILNodeKind::SwitchEnumInst: |
| case SILNodeKind::CheckedCastBranchInst: |
| case SILNodeKind::BranchInst: |
| case SILNodeKind::CondBranchInst: |
| case SILNodeKind::DestructureStructInst: |
| case SILNodeKind::DestructureTupleInst: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| // These operations forward guaranteed ownership, but don't necessarily forward |
| // owned values. |
| static bool isGuaranteedForwardingValueKind(SILNodeKind K) { |
| switch (K) { |
| case SILNodeKind::TupleExtractInst: |
| case SILNodeKind::StructExtractInst: |
| case SILNodeKind::OpenExistentialValueInst: |
| case SILNodeKind::OpenExistentialBoxValueInst: |
| return true; |
| default: |
| return isOwnershipForwardingValueKind(K); |
| } |
| } |
| |
| static bool isGuaranteedForwardingValue(SILValue V) { |
| return isGuaranteedForwardingValueKind( |
| V->getKindOfRepresentativeSILNodeInObject()); |
| } |
| |
| static bool isGuaranteedForwardingInst(SILInstruction *I) { |
| return isGuaranteedForwardingValueKind(SILNodeKind(I->getKind())); |
| } |
| |
| LLVM_ATTRIBUTE_UNUSED |
| static bool isOwnershipForwardingInst(SILInstruction *I) { |
| return isOwnershipForwardingValueKind(SILNodeKind(I->getKind())); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // OperandOwnershipKindClassifier |
| //===----------------------------------------------------------------------===// |
| |
| namespace { |
| |
| class OperandOwnershipKindClassifier |
| : public SILInstructionVisitor<OperandOwnershipKindClassifier, |
| OperandOwnershipKindMap> { |
| public: |
| using Map = OperandOwnershipKindMap; |
| |
| private: |
| LLVM_ATTRIBUTE_UNUSED SILModule &mod; |
| |
| const Operand &op; |
| ErrorBehaviorKind errorBehavior; |
| bool checkingSubObject; |
| |
| public: |
| /// Create a new OperandOwnershipKindClassifier. |
| /// |
| /// 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. |
| OperandOwnershipKindClassifier(SILModule &mod, const Operand &op, |
| ErrorBehaviorKind errorBehavior, |
| bool checkingSubObject) |
| : mod(mod), op(op), errorBehavior(errorBehavior), |
| checkingSubObject(checkingSubObject) {} |
| |
| bool isCheckingSubObject() const { return checkingSubObject; } |
| |
| SILValue getValue() const { return op.get(); } |
| |
| ValueOwnershipKind getOwnershipKind() const { |
| assert(getValue().getOwnershipKind() == op.get().getOwnershipKind() && |
| "Expected ownership kind of parent value and operand"); |
| return getValue().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; |
| } |
| |
| bool isAddressOrTrivialType() const { |
| if (getType().isAddress()) |
| return true; |
| return getOwnershipKind() == ValueOwnershipKind::Trivial || |
| getOwnershipKind() == ValueOwnershipKind::Any; |
| } |
| |
| OperandOwnershipKindMap visitForwardingInst(SILInstruction *I, |
| ArrayRef<Operand> Ops); |
| OperandOwnershipKindMap visitForwardingInst(SILInstruction *I) { |
| return visitForwardingInst(I, I->getAllOperands()); |
| } |
| |
| OperandOwnershipKindMap |
| visitEnumArgument(ValueOwnershipKind RequiredConvention); |
| OperandOwnershipKindMap |
| visitApplyParameter(ValueOwnershipKind RequiredConvention, |
| UseLifetimeConstraint Requirement); |
| OperandOwnershipKindMap visitFullApply(FullApplySite apply); |
| |
| OperandOwnershipKindMap visitCallee(CanSILFunctionType SubstCalleeType); |
| OperandOwnershipKindMap |
| checkTerminatorArgumentMatchesDestBB(SILBasicBlock *DestBB, unsigned OpIndex); |
| |
| // 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) OperandOwnershipKindMap visit##Id(Id *); |
| #include "swift/SIL/SILNodes.def" |
| }; |
| |
| } // end anonymous namespace |
| |
| /// Implementation for instructions without operands. These should never be |
| /// visited. |
| #define NO_OPERAND_INST(INST) \ |
| OperandOwnershipKindMap OperandOwnershipKindClassifier::visit##INST##Inst( \ |
| INST##Inst *i) { \ |
| assert(i->getNumOperands() == 0 && \ |
| "Expected instruction without operands?!"); \ |
| llvm_unreachable("Instruction without operand can not be compatible with " \ |
| "any def's OwnershipValueKind"); \ |
| } |
| NO_OPERAND_INST(AllocBox) |
| NO_OPERAND_INST(AllocExistentialBox) |
| NO_OPERAND_INST(AllocGlobal) |
| NO_OPERAND_INST(AllocStack) |
| NO_OPERAND_INST(FloatLiteral) |
| NO_OPERAND_INST(FunctionRef) |
| NO_OPERAND_INST(GlobalAddr) |
| NO_OPERAND_INST(GlobalValue) |
| NO_OPERAND_INST(IntegerLiteral) |
| NO_OPERAND_INST(Metatype) |
| NO_OPERAND_INST(ObjCProtocol) |
| NO_OPERAND_INST(RetainValue) |
| NO_OPERAND_INST(RetainValueAddr) |
| NO_OPERAND_INST(StringLiteral) |
| NO_OPERAND_INST(StrongRetain) |
| NO_OPERAND_INST(Unreachable) |
| NO_OPERAND_INST(Unwind) |
| #define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ |
| NO_OPERAND_INST(StrongRetain##Name) \ |
| NO_OPERAND_INST(Name##Retain) |
| #include "swift/AST/ReferenceStorage.def" |
| #undef NO_OPERAND_INST |
| |
| /// Instructions whose arguments are always compatible with one convention. |
| #define CONSTANT_OWNERSHIP_INST(OWNERSHIP, USE_LIFETIME_CONSTRAINT, INST) \ |
| OperandOwnershipKindMap OperandOwnershipKindClassifier::visit##INST##Inst( \ |
| INST##Inst *i) { \ |
| assert(i->getNumOperands() && "Expected to have non-zero operands"); \ |
| return Map::compatibilityMap( \ |
| ValueOwnershipKind::OWNERSHIP, \ |
| UseLifetimeConstraint::USE_LIFETIME_CONSTRAINT); \ |
| } |
| CONSTANT_OWNERSHIP_INST(Guaranteed, MustBeLive, IsEscapingClosure) |
| CONSTANT_OWNERSHIP_INST(Guaranteed, MustBeLive, RefElementAddr) |
| CONSTANT_OWNERSHIP_INST(Guaranteed, MustBeLive, OpenExistentialValue) |
| CONSTANT_OWNERSHIP_INST(Guaranteed, MustBeLive, OpenExistentialBoxValue) |
| CONSTANT_OWNERSHIP_INST(Owned, MustBeInvalidated, AutoreleaseValue) |
| CONSTANT_OWNERSHIP_INST(Owned, MustBeInvalidated, DeallocBox) |
| CONSTANT_OWNERSHIP_INST(Owned, MustBeInvalidated, DeallocExistentialBox) |
| CONSTANT_OWNERSHIP_INST(Owned, MustBeInvalidated, DeallocRef) |
| CONSTANT_OWNERSHIP_INST(Owned, MustBeInvalidated, DestroyValue) |
| CONSTANT_OWNERSHIP_INST(Owned, MustBeInvalidated, ReleaseValue) |
| CONSTANT_OWNERSHIP_INST(Owned, MustBeInvalidated, ReleaseValueAddr) |
| CONSTANT_OWNERSHIP_INST(Owned, MustBeInvalidated, StrongRelease) |
| CONSTANT_OWNERSHIP_INST(Owned, MustBeInvalidated, InitExistentialRef) |
| CONSTANT_OWNERSHIP_INST(Owned, MustBeInvalidated, EndLifetime) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, AbortApply) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, AddressToPointer) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, BeginAccess) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, BeginUnpairedAccess) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, BindMemory) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, CheckedCastAddrBranch) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, CondFail) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, CopyAddr) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, DeallocStack) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, DebugValueAddr) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, DeinitExistentialAddr) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, DestroyAddr) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, EndAccess) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, EndApply) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, EndUnpairedAccess) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, IndexAddr) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, IndexRawPointer) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, InitBlockStorageHeader) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, InitEnumDataAddr) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, InitExistentialAddr) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, InitExistentialMetatype) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, InjectEnumAddr) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, IsUnique) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, Load) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, LoadBorrow) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, MarkFunctionEscape) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, MarkUninitializedBehavior) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, ObjCExistentialMetatypeToObject) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, ObjCMetatypeToObject) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, ObjCToThickMetatype) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, OpenExistentialAddr) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, OpenExistentialMetatype) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, PointerToAddress) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, PointerToThinFunction) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, ProjectBlockStorage) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, ProjectValueBuffer) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, RawPointerToRef) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, SelectEnumAddr) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, SelectValue) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, StructElementAddr) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, SwitchEnumAddr) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, SwitchValue) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, TailAddr) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, ThickToObjCMetatype) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, ThinFunctionToPointer) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, ThinToThickFunction) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, TupleElementAddr) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, UncheckedAddrCast) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, UncheckedRefCastAddr) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, UncheckedTakeEnumDataAddr) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, UnconditionalCheckedCastAddr) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, AllocValueBuffer) |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, DeallocValueBuffer) |
| #define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, Load##Name) |
| #define ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ |
| CONSTANT_OWNERSHIP_INST(Owned, MustBeInvalidated, 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, ...) \ |
| CONSTANT_OWNERSHIP_INST(Trivial, MustBeLive, Name##ToRef) |
| #include "swift/AST/ReferenceStorage.def" |
| #undef CONSTANT_OWNERSHIP_INST |
| |
| /// Instructions whose arguments are always compatible with one convention. |
| #define CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(OWNERSHIP, USE_LIFETIME_CONSTRAINT, \ |
| INST) \ |
| OperandOwnershipKindMap OperandOwnershipKindClassifier::visit##INST##Inst( \ |
| INST##Inst *i) { \ |
| assert(i->getNumOperands() && "Expected to have non-zero operands"); \ |
| return Map::compatibilityMap( \ |
| {{ValueOwnershipKind::Trivial, UseLifetimeConstraint::MustBeLive}, \ |
| {ValueOwnershipKind::OWNERSHIP, \ |
| UseLifetimeConstraint::USE_LIFETIME_CONSTRAINT}}); \ |
| } |
| CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(Owned, MustBeInvalidated, |
| CheckedCastValueBranch) |
| CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(Owned, MustBeInvalidated, |
| UnconditionalCheckedCastValue) |
| CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(Owned, MustBeInvalidated, |
| InitExistentialValue) |
| CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(Owned, MustBeInvalidated, |
| DeinitExistentialValue) |
| #undef CONSTANT_OR_TRIVIAL_OWNERSHIP_INST |
| |
| #define ACCEPTS_ANY_OWNERSHIP_INST(INST) \ |
| OperandOwnershipKindMap OperandOwnershipKindClassifier::visit##INST##Inst( \ |
| INST##Inst *I) { \ |
| return Map::allLive(); \ |
| } |
| ACCEPTS_ANY_OWNERSHIP_INST(BeginBorrow) |
| ACCEPTS_ANY_OWNERSHIP_INST(CopyValue) |
| ACCEPTS_ANY_OWNERSHIP_INST(DebugValue) |
| ACCEPTS_ANY_OWNERSHIP_INST(FixLifetime) |
| ACCEPTS_ANY_OWNERSHIP_INST(UncheckedBitwiseCast) // Is this right? |
| ACCEPTS_ANY_OWNERSHIP_INST(WitnessMethod) // Is this right? |
| ACCEPTS_ANY_OWNERSHIP_INST(ProjectBox) // The result is a T*. |
| ACCEPTS_ANY_OWNERSHIP_INST(DynamicMethodBranch) |
| ACCEPTS_ANY_OWNERSHIP_INST(UncheckedTrivialBitCast) |
| ACCEPTS_ANY_OWNERSHIP_INST(ExistentialMetatype) |
| ACCEPTS_ANY_OWNERSHIP_INST(ValueMetatype) |
| ACCEPTS_ANY_OWNERSHIP_INST(UncheckedOwnershipConversion) |
| ACCEPTS_ANY_OWNERSHIP_INST(ValueToBridgeObject) |
| #undef ACCEPTS_ANY_OWNERSHIP_INST |
| |
| // Trivial if trivial typed, otherwise must accept owned? |
| #define ACCEPTS_ANY_NONTRIVIAL_OWNERSHIP_OR_METATYPE(USE_LIFETIME_CONSTRAINT, \ |
| INST) \ |
| OperandOwnershipKindMap OperandOwnershipKindClassifier::visit##INST##Inst( \ |
| INST##Inst *I) { \ |
| assert(I->getNumOperands() && "Expected to have non-zero operands"); \ |
| if (getType().is<AnyMetatypeType>()) { \ |
| return Map::compatibilityMap(ValueOwnershipKind::Trivial, \ |
| UseLifetimeConstraint::MustBeLive); \ |
| } \ |
| return Map::compatibleWithAllExcept(ValueOwnershipKind::Trivial); \ |
| } |
| ACCEPTS_ANY_NONTRIVIAL_OWNERSHIP_OR_METATYPE(MustBeLive, ClassMethod) |
| ACCEPTS_ANY_NONTRIVIAL_OWNERSHIP_OR_METATYPE(MustBeLive, ObjCMethod) |
| ACCEPTS_ANY_NONTRIVIAL_OWNERSHIP_OR_METATYPE(MustBeLive, ObjCSuperMethod) |
| ACCEPTS_ANY_NONTRIVIAL_OWNERSHIP_OR_METATYPE(MustBeLive, SuperMethod) |
| #undef ACCEPTS_ANY_NONTRIVIAL_OWNERSHIP_OR_METATYPE |
| |
| // Trivial if trivial typed, otherwise must accept owned? |
| #define ACCEPTS_ANY_NONTRIVIAL_OWNERSHIP(USE_LIFETIME_CONSTRAINT, INST) \ |
| OperandOwnershipKindMap OperandOwnershipKindClassifier::visit##INST##Inst( \ |
| INST##Inst *I) { \ |
| assert(I->getNumOperands() && "Expected to have non-zero operands"); \ |
| return Map::compatibleWithAllExcept(ValueOwnershipKind::Trivial); \ |
| } |
| ACCEPTS_ANY_NONTRIVIAL_OWNERSHIP(MustBeLive, BridgeObjectToWord) |
| ACCEPTS_ANY_NONTRIVIAL_OWNERSHIP(MustBeLive, ClassifyBridgeObject) |
| ACCEPTS_ANY_NONTRIVIAL_OWNERSHIP(MustBeLive, CopyBlock) |
| ACCEPTS_ANY_NONTRIVIAL_OWNERSHIP(MustBeLive, OpenExistentialBox) |
| ACCEPTS_ANY_NONTRIVIAL_OWNERSHIP(MustBeLive, RefTailAddr) |
| ACCEPTS_ANY_NONTRIVIAL_OWNERSHIP(MustBeLive, RefToRawPointer) |
| ACCEPTS_ANY_NONTRIVIAL_OWNERSHIP(MustBeLive, SetDeallocating) |
| ACCEPTS_ANY_NONTRIVIAL_OWNERSHIP(MustBeLive, ProjectExistentialBox) |
| ACCEPTS_ANY_NONTRIVIAL_OWNERSHIP(MustBeLive, UnmanagedRetainValue) |
| ACCEPTS_ANY_NONTRIVIAL_OWNERSHIP(MustBeLive, UnmanagedReleaseValue) |
| ACCEPTS_ANY_NONTRIVIAL_OWNERSHIP(MustBeLive, UnmanagedAutoreleaseValue) |
| ACCEPTS_ANY_NONTRIVIAL_OWNERSHIP(MustBeLive, ConvertEscapeToNoEscape) |
| #define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ |
| ACCEPTS_ANY_NONTRIVIAL_OWNERSHIP(MustBeLive, RefTo##Name) \ |
| ACCEPTS_ANY_NONTRIVIAL_OWNERSHIP(MustBeLive, Name##ToRef) \ |
| ACCEPTS_ANY_NONTRIVIAL_OWNERSHIP(MustBeLive, Copy##Name##Value) |
| #define UNCHECKED_REF_STORAGE(Name, ...) \ |
| ACCEPTS_ANY_NONTRIVIAL_OWNERSHIP(MustBeLive, RefTo##Name) |
| #include "swift/AST/ReferenceStorage.def" |
| #undef ACCEPTS_ANY_NONTRIVIAL_OWNERSHIP |
| |
| OperandOwnershipKindMap |
| OperandOwnershipKindClassifier::visitForwardingInst(SILInstruction *i, |
| ArrayRef<Operand> ops) { |
| assert(i->getNumOperands() && "Expected to have non-zero operands"); |
| assert(isOwnershipForwardingInst(i) && |
| "Expected to have an ownership forwarding inst"); |
| |
| // Find the first index where we have a non-trivial value. |
| auto iter = find_if(ops, [&i](const Operand &op) -> bool { |
| if (i->isTypeDependentOperand(op)) |
| return false; |
| return op.get().getOwnershipKind() != ValueOwnershipKind::Trivial; |
| }); |
| |
| // If we do not find a non-trivial value, then we know for sure that we have a |
| // trivial value. |
| if (iter == ops.end()) { |
| return Map::compatibilityMap(ValueOwnershipKind::Trivial, |
| UseLifetimeConstraint::MustBeLive); |
| } |
| |
| // Ok, we have at least a single non-trivial value. Grab the type of the |
| // operand and see if it is trivial or non-trivial. If the type of the operand |
| // is trivial, then return that we accept trivial here. Otherwise, return the |
| // base ownership kind. |
| if (getType().isTrivial(mod)) |
| return Map::compatibilityMap(ValueOwnershipKind::Trivial, |
| UseLifetimeConstraint::MustBeLive); |
| |
| // Otherwise, return the value ownership kind and forwarding lifetime |
| // constraint of the first non-trivial operand. This will ensure that all |
| // non-trivial operands have the same ownership kind. |
| unsigned index = std::distance(ops.begin(), iter); |
| ValueOwnershipKind kind = ops[index].get().getOwnershipKind(); |
| auto lifetimeConstraint = kind.getForwardingLifetimeConstraint(); |
| return Map::compatibilityMap(kind, lifetimeConstraint); |
| } |
| |
| #define FORWARD_ANY_OWNERSHIP_INST(INST) \ |
| OperandOwnershipKindMap OperandOwnershipKindClassifier::visit##INST##Inst( \ |
| INST##Inst *I) { \ |
| return visitForwardingInst(I); \ |
| } |
| FORWARD_ANY_OWNERSHIP_INST(Tuple) |
| FORWARD_ANY_OWNERSHIP_INST(Struct) |
| FORWARD_ANY_OWNERSHIP_INST(Object) |
| FORWARD_ANY_OWNERSHIP_INST(Enum) |
| FORWARD_ANY_OWNERSHIP_INST(OpenExistentialRef) |
| FORWARD_ANY_OWNERSHIP_INST(Upcast) |
| FORWARD_ANY_OWNERSHIP_INST(UncheckedRefCast) |
| FORWARD_ANY_OWNERSHIP_INST(ConvertFunction) |
| FORWARD_ANY_OWNERSHIP_INST(RefToBridgeObject) |
| FORWARD_ANY_OWNERSHIP_INST(BridgeObjectToRef) |
| FORWARD_ANY_OWNERSHIP_INST(UnconditionalCheckedCast) |
| FORWARD_ANY_OWNERSHIP_INST(MarkUninitialized) |
| FORWARD_ANY_OWNERSHIP_INST(UncheckedEnumData) |
| FORWARD_ANY_OWNERSHIP_INST(DestructureStruct) |
| FORWARD_ANY_OWNERSHIP_INST(DestructureTuple) |
| #undef FORWARD_ANY_OWNERSHIP_INST |
| |
| // An instruction that forwards a constant ownership or trivial ownership. |
| #define FORWARD_CONSTANT_OR_TRIVIAL_OWNERSHIP_INST( \ |
| OWNERSHIP, USE_LIFETIME_CONSTRAINT, INST) \ |
| OperandOwnershipKindMap OperandOwnershipKindClassifier::visit##INST##Inst( \ |
| INST##Inst *I) { \ |
| assert(I->getNumOperands() && "Expected to have non-zero operands"); \ |
| assert(isGuaranteedForwardingInst(I) && \ |
| "Expected an ownership forwarding inst"); \ |
| OperandOwnershipKindMap map; \ |
| map.add(ValueOwnershipKind::Trivial, UseLifetimeConstraint::MustBeLive); \ |
| map.addCompatibilityConstraint( \ |
| ValueOwnershipKind::OWNERSHIP, \ |
| UseLifetimeConstraint::USE_LIFETIME_CONSTRAINT); \ |
| return map; \ |
| } |
| FORWARD_CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(Guaranteed, MustBeLive, TupleExtract) |
| FORWARD_CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(Guaranteed, MustBeLive, |
| StructExtract) |
| #undef CONSTANT_OR_TRIVIAL_OWNERSHIP_INST |
| |
| OperandOwnershipKindMap |
| OperandOwnershipKindClassifier::visitDeallocPartialRefInst( |
| DeallocPartialRefInst *i) { |
| if (getValue() == i->getInstance()) { |
| return Map::compatibilityMap(ValueOwnershipKind::Owned, |
| UseLifetimeConstraint::MustBeInvalidated); |
| } |
| |
| return Map::compatibilityMap(ValueOwnershipKind::Trivial, |
| UseLifetimeConstraint::MustBeLive); |
| } |
| |
| OperandOwnershipKindMap |
| OperandOwnershipKindClassifier::visitSelectEnumInst(SelectEnumInst *I) { |
| if (getValue() == I->getEnumOperand()) { |
| return Map::allLive(); |
| } |
| |
| return visitForwardingInst(I, I->getAllOperands().drop_front()); |
| } |
| |
| OperandOwnershipKindMap |
| OperandOwnershipKindClassifier::visitAllocRefInst(AllocRefInst *I) { |
| assert(I->getNumOperands() != 0 |
| && "If we reach this point, we must have a tail operand"); |
| return Map::compatibilityMap(ValueOwnershipKind::Trivial, |
| UseLifetimeConstraint::MustBeLive); |
| } |
| |
| OperandOwnershipKindMap |
| OperandOwnershipKindClassifier::visitAllocRefDynamicInst( |
| AllocRefDynamicInst *I) { |
| assert(I->getNumOperands() != 0 && |
| "If we reach this point, we must have a tail operand"); |
| return Map::compatibilityMap(ValueOwnershipKind::Trivial, |
| UseLifetimeConstraint::MustBeLive); |
| } |
| |
| OperandOwnershipKindMap |
| OperandOwnershipKindClassifier::checkTerminatorArgumentMatchesDestBB( |
| SILBasicBlock *destBB, unsigned opIndex) { |
| // Grab the ownership kind of the destination block. |
| ValueOwnershipKind destBlockArgOwnershipKind = |
| destBB->getArgument(opIndex)->getOwnershipKind(); |
| |
| // Then if we do not have an enum, make sure that the conventions match. |
| if (!getType().getEnumOrBoundGenericEnum()) { |
| auto lifetimeConstraint = |
| destBlockArgOwnershipKind.getForwardingLifetimeConstraint(); |
| return Map::compatibilityMap(destBlockArgOwnershipKind, lifetimeConstraint); |
| } |
| |
| // Otherwise, we need to properly handle the sum type nature of enum |
| // arguments. |
| return visitEnumArgument(destBlockArgOwnershipKind); |
| } |
| |
| OperandOwnershipKindMap |
| OperandOwnershipKindClassifier::visitBranchInst(BranchInst *BI) { |
| return checkTerminatorArgumentMatchesDestBB(BI->getDestBB(), |
| getOperandIndex()); |
| } |
| |
| OperandOwnershipKindMap |
| OperandOwnershipKindClassifier::visitCondBranchInst(CondBranchInst *cbi) { |
| // If our conditional branch is the condition, it is trivial. Check that the |
| // ownership kind is trivial. |
| if (cbi->isConditionOperandIndex(getOperandIndex())) |
| return Map::compatibilityMap(ValueOwnershipKind::Trivial, |
| UseLifetimeConstraint::MustBeLive); |
| |
| // Otherwise, make sure that our operand matches the ownership of the relevant |
| // argument. |
| // |
| // TODO: Use more updated APIs here to get the operands/etc. |
| if (cbi->isTrueOperandIndex(getOperandIndex())) { |
| unsigned trueOffset = 1; |
| return checkTerminatorArgumentMatchesDestBB(cbi->getTrueBB(), |
| getOperandIndex() - trueOffset); |
| } |
| |
| assert(cbi->isFalseOperandIndex(getOperandIndex()) && |
| "If an operand is not the condition index or a true operand index, it " |
| "must be a false operand index"); |
| unsigned falseOffset = 1 + cbi->getTrueOperands().size(); |
| return checkTerminatorArgumentMatchesDestBB(cbi->getFalseBB(), |
| getOperandIndex() - falseOffset); |
| } |
| |
| OperandOwnershipKindMap |
| OperandOwnershipKindClassifier::visitSwitchEnumInst(SwitchEnumInst *sei) { |
| auto opTy = sei->getOperand()->getType(); |
| auto &mod = sei->getModule(); |
| // If our passed in type is trivial, we shouldn't have any non-trivial |
| // successors. Just bail early returning trivial. |
| if (opTy.isTrivial(mod)) |
| return Map::compatibilityMap(ValueOwnershipKind::Trivial, |
| UseLifetimeConstraint::MustBeLive); |
| |
| // Otherwise, go through the cases of the enum. If we have any cases with |
| // trivial payload or no payload cases, add trivial as a base ownership kind |
| // we can accept. |
| OperandOwnershipKindMap map; |
| |
| bool foundNonTrivialCase = false; |
| |
| auto *enumDecl = opTy.getEnumOrBoundGenericEnum(); |
| assert(enumDecl); |
| for (auto *eltDecl : enumDecl->getAllElements()) { |
| // If we have a no-payload case add that we support trivial and continue. |
| if (!eltDecl->hasAssociatedValues()) { |
| map.addCompatibilityConstraint(ValueOwnershipKind::Trivial, |
| UseLifetimeConstraint::MustBeLive); |
| continue; |
| } |
| |
| // If we have a completely trivial payload case, then add that we support |
| // trivial and continue. |
| if (opTy.getEnumElementType(eltDecl, mod).isTrivial(mod)) { |
| map.addCompatibilityConstraint(ValueOwnershipKind::Trivial, |
| UseLifetimeConstraint::MustBeLive); |
| continue; |
| } |
| |
| // Otherwise, we have a non-trivial case. Set foundNonTrivialCase to |
| // true. We will need to check the arguments of the switch_enum's successors |
| // for the ownership kind that we can accept. |
| foundNonTrivialCase = true; |
| } |
| |
| // If we didn't find a non-trivial case, return the map we have constructed so |
| // far. |
| if (!foundNonTrivialCase) |
| return map; |
| |
| // Otherwise, we want to find the ownership constraint of our successor |
| // arguments. |
| Optional<ValueOwnershipKind> nonTrivialKind; |
| for (auto argArray : sei->getSuccessorBlockArguments()) { |
| if (argArray.empty()) |
| continue; |
| SILValue arg = argArray[getOperandIndex()]; |
| if (arg->getType().isTrivial(mod)) |
| continue; |
| |
| // If we haven't found a non-trivial kind yet, stash the kind we find. |
| if (!nonTrivialKind) { |
| nonTrivialKind = arg.getOwnershipKind(); |
| continue; |
| } |
| |
| // Otherwise if we /do/ have a non trivial kind and the argument's ownership |
| // kind is compatible, merge in case the first value we saw had Any |
| // ownership. |
| auto newKind = nonTrivialKind->merge(arg.getOwnershipKind()); |
| if (newKind) { |
| nonTrivialKind = newKind; |
| continue; |
| } |
| |
| // Otherwise, we have inconsistent ownership in between our successors. To |
| // be sure that we error, return an empty map. |
| return Map(); |
| } |
| |
| // We should never have an enum with a non-trivial case where we do not have |
| // at least one successor with a proper ownership qualifier since we either |
| // switch over the entire enum implying we visit that case, or we go through |
| // the default which will have our enum type as its type and thus some form of |
| // non-trivial ownership. So it is correct to use the optional here without |
| // checking. |
| auto lifetimeConstraint = nonTrivialKind->getForwardingLifetimeConstraint(); |
| map.addCompatibilityConstraint(*nonTrivialKind, lifetimeConstraint); |
| |
| return map; |
| } |
| |
| OperandOwnershipKindMap |
| OperandOwnershipKindClassifier::visitCheckedCastBranchInst( |
| CheckedCastBranchInst *ccbi) { |
| Optional<OperandOwnershipKindMap> map; |
| for (auto argArray : ccbi->getSuccessorBlockArguments()) { |
| assert(!argArray.empty()); |
| |
| auto argOwnershipKind = argArray[getOperandIndex()]->getOwnershipKind(); |
| // If we do not have a map yet, initialize it and continue. |
| if (!map) { |
| auto lifetimeConstraint = |
| argOwnershipKind.getForwardingLifetimeConstraint(); |
| map = Map::compatibilityMap(argOwnershipKind, lifetimeConstraint); |
| continue; |
| } |
| |
| // Otherwise, make sure that we can accept the rest of our |
| // arguments. If not, we return an empty ownership kind to make |
| // sure that we flag everything as an error. |
| if (map->canAcceptKind(argOwnershipKind)) { |
| continue; |
| } |
| |
| return OperandOwnershipKindMap(); |
| } |
| |
| return map.getValue(); |
| } |
| |
| OperandOwnershipKindMap |
| OperandOwnershipKindClassifier::visitReturnInst(ReturnInst *RI) { |
| bool IsTrivial = RI->getOperand()->getType().isTrivial(mod); |
| SILFunctionConventions fnConv = RI->getFunction()->getConventions(); |
| auto Results = fnConv.getDirectSILResults(); |
| // FIXME: Shouldn't we return an empty OperandOwnershipKindMap here if we do |
| // not have any results? |
| if (Results.empty() || IsTrivial) { |
| return Map::compatibilityMap(ValueOwnershipKind::Trivial, |
| UseLifetimeConstraint::MustBeLive); |
| } |
| |
| CanGenericSignature Sig = fnConv.funcTy->getGenericSignature(); |
| |
| // Find the first index where we have a trivial value. |
| auto Iter = find_if(Results, [this, &Sig](const SILResultInfo &Info) -> bool { |
| return Info.getOwnershipKind(mod, Sig) != ValueOwnershipKind::Trivial; |
| }); |
| |
| // If we have all trivial, then we must be trivial. Why wasn't our original |
| // type trivial? This is a hard error since this is a logic error in our code |
| // here. |
| if (Iter == Results.end()) |
| llvm_unreachable("Should have already checked a trivial type?!"); |
| |
| ValueOwnershipKind Base = Iter->getOwnershipKind(mod, Sig); |
| |
| for (const SILResultInfo &ResultInfo : |
| SILFunctionConventions::DirectSILResultRange(std::next(Iter), |
| Results.end())) { |
| auto RKind = ResultInfo.getOwnershipKind(mod, Sig); |
| // Ignore trivial types. |
| if (RKind.merge(ValueOwnershipKind::Trivial)) |
| continue; |
| |
| auto MergedValue = Base.merge(RKind); |
| // If we fail to merge all types in, bail. We can not come up with a proper |
| // result type. We assert here since this is a hard error in the normal |
| // SILVerifier since the return type of the function would not match its |
| // terminator. |
| assert(MergedValue.hasValue() && |
| "Failed to merge all types in on a return?!"); |
| // In case Base is Any. |
| Base = MergedValue.getValue(); |
| } |
| |
| if (getType().getEnumOrBoundGenericEnum()) { |
| return visitEnumArgument(Base); |
| } |
| |
| return Map::compatibilityMap(Base, UseLifetimeConstraint::MustBeInvalidated); |
| } |
| |
| OperandOwnershipKindMap |
| OperandOwnershipKindClassifier::visitEndBorrowInst(EndBorrowInst *I) { |
| // If we are checking a subobject, make sure that we are from a guaranteed |
| // basic block argument. |
| if (isCheckingSubObject()) { |
| auto *phiArg = cast<SILPhiArgument>(op.get()); |
| (void)phiArg; |
| return Map::compatibilityMap(ValueOwnershipKind::Guaranteed, |
| UseLifetimeConstraint::MustBeLive); |
| } |
| |
| /// An end_borrow is modeled as invalidating the guaranteed value preventing |
| /// any further uses of the value. |
| return Map::compatibilityMap(ValueOwnershipKind::Guaranteed, |
| UseLifetimeConstraint::MustBeInvalidated); |
| } |
| |
| OperandOwnershipKindMap |
| OperandOwnershipKindClassifier::visitThrowInst(ThrowInst *I) { |
| return Map::compatibilityMap(ValueOwnershipKind::Owned, |
| UseLifetimeConstraint::MustBeInvalidated); |
| } |
| |
| #define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ |
| OperandOwnershipKindMap \ |
| OperandOwnershipKindClassifier::visitStore##Name##Inst( \ |
| Store##Name##Inst *I) { \ |
| /* A store instruction implies that the value to be stored to be live, */ \ |
| /* but it does not touch the strong reference count of the value. We */ \ |
| /* also just care about liveness for the dest. So just match everything */ \ |
| /* as must be live. */ \ |
| return Map::allLive(); \ |
| } |
| #define SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ |
| NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, "...") |
| #include "swift/AST/ReferenceStorage.def" |
| |
| OperandOwnershipKindMap |
| OperandOwnershipKindClassifier::visitStoreBorrowInst(StoreBorrowInst *I) { |
| if (getValue() == I->getSrc()) { |
| return Map::compatibilityMap(ValueOwnershipKind::Guaranteed, |
| UseLifetimeConstraint::MustBeLive); |
| } |
| return Map::compatibilityMap(ValueOwnershipKind::Trivial, |
| UseLifetimeConstraint::MustBeLive); |
| } |
| |
| // FIXME: Why not use SILArgumentConvention here? |
| OperandOwnershipKindMap OperandOwnershipKindClassifier::visitCallee( |
| CanSILFunctionType substCalleeType) { |
| ParameterConvention conv = substCalleeType->getCalleeConvention(); |
| switch (conv) { |
| case ParameterConvention::Indirect_In: |
| case ParameterConvention::Indirect_In_Constant: |
| assert(!SILModuleConventions(mod).isSILIndirect( |
| SILParameterInfo(substCalleeType, conv))); |
| return Map::compatibilityMap(ValueOwnershipKind::Owned, |
| UseLifetimeConstraint::MustBeInvalidated); |
| case ParameterConvention::Indirect_In_Guaranteed: |
| assert(!SILModuleConventions(mod).isSILIndirect( |
| SILParameterInfo(substCalleeType, conv))); |
| return Map::compatibilityMap(ValueOwnershipKind::Guaranteed, |
| UseLifetimeConstraint::MustBeLive); |
| case ParameterConvention::Indirect_Inout: |
| case ParameterConvention::Indirect_InoutAliasable: |
| llvm_unreachable("Illegal convention for callee"); |
| case ParameterConvention::Direct_Unowned: |
| return Map::allLive(); |
| case ParameterConvention::Direct_Owned: |
| return Map::compatibilityMap(ValueOwnershipKind::Owned, |
| UseLifetimeConstraint::MustBeInvalidated); |
| case ParameterConvention::Direct_Guaranteed: |
| if (substCalleeType->isNoEscape()) |
| return Map::compatibilityMap(ValueOwnershipKind::Trivial, |
| UseLifetimeConstraint::MustBeLive); |
| return Map::compatibilityMap(ValueOwnershipKind::Guaranteed, |
| UseLifetimeConstraint::MustBeLive); |
| } |
| |
| llvm_unreachable("Unhandled ParameterConvention in switch."); |
| } |
| |
| // Visit an enum value that is passed at argument position, including block |
| // arguments, apply arguments, and return values. |
| // |
| // The operand definition's ownership kind may be known to be "trivial", |
| // but it is still valid to pass that enum to a argument nontrivial type. |
| // For example: |
| // |
| // %val = enum $Optional<SomeClass>, #Optional.none // trivial ownership |
| // apply %f(%val) : (@owned Optional<SomeClass>) // owned argument |
| OperandOwnershipKindMap OperandOwnershipKindClassifier::visitEnumArgument( |
| ValueOwnershipKind requiredKind) { |
| // If this value is already categorized as a trivial ownership kind, |
| // it is safe to pass to any argument convention. This is ok since |
| // we know that the enum type must match up as checked by the |
| // ownership verifier. |
| OperandOwnershipKindMap map; |
| map.addCompatibilityConstraint(ValueOwnershipKind::Trivial, |
| UseLifetimeConstraint::MustBeLive); |
| |
| // The operand has a non-trivial ownership kind. It must match the argument |
| // convention. |
| if (requiredKind != ValueOwnershipKind::Owned) { |
| map.addCompatibilityConstraint(ValueOwnershipKind::Owned, |
| UseLifetimeConstraint::MustBeLive); |
| } else { |
| map.addCompatibilityConstraint(ValueOwnershipKind::Owned, |
| UseLifetimeConstraint::MustBeInvalidated); |
| } |
| map.addCompatibilityConstraint(ValueOwnershipKind::Guaranteed, |
| UseLifetimeConstraint::MustBeLive); |
| map.addCompatibilityConstraint(ValueOwnershipKind::Unowned, |
| UseLifetimeConstraint::MustBeLive); |
| return map; |
| } |
| |
| // We allow for trivial cases of enums with non-trivial cases to be passed in |
| // non-trivial argument positions. This fits with modeling of a |
| // SILFunctionArgument as a phi in a global program graph. |
| OperandOwnershipKindMap OperandOwnershipKindClassifier::visitApplyParameter( |
| ValueOwnershipKind Kind, UseLifetimeConstraint Requirement) { |
| |
| // Check if we have an enum. If not, then we just check against the passed in |
| // convention. |
| if (!getType().getEnumOrBoundGenericEnum()) { |
| // We allow for owned to be passed to apply parameters. |
| if (Kind != ValueOwnershipKind::Owned) { |
| return Map::compatibilityMap( |
| {{Kind, Requirement}, |
| {ValueOwnershipKind::Owned, UseLifetimeConstraint::MustBeLive}}); |
| } |
| return Map::compatibilityMap(Kind, Requirement); |
| } |
| |
| // Otherwise consider that we may have a payload with a trivial case |
| // that has other non-trivial cases. |
| return visitEnumArgument(Kind); |
| } |
| |
| // Handle Apply and TryApply. |
| OperandOwnershipKindMap |
| OperandOwnershipKindClassifier::visitFullApply(FullApplySite apply) { |
| // If we are visiting the callee operand, handle it specially. |
| if (apply.isCalleeOperand(op)) { |
| return visitCallee(apply.getSubstCalleeType()); |
| } |
| |
| // Indirect return arguments are address types. |
| if (apply.isIndirectResultOperand(op)) { |
| return Map::compatibilityMap(ValueOwnershipKind::Trivial, |
| UseLifetimeConstraint::MustBeLive); |
| } |
| |
| unsigned argIndex = apply.getCalleeArgIndex(op); |
| auto conv = apply.getSubstCalleeConv(); |
| SILParameterInfo paramInfo = conv.getParamInfoForSILArg(argIndex); |
| |
| switch (paramInfo.getConvention()) { |
| case ParameterConvention::Direct_Owned: |
| return visitApplyParameter(ValueOwnershipKind::Owned, |
| UseLifetimeConstraint::MustBeInvalidated); |
| case ParameterConvention::Direct_Unowned: |
| return Map::allLive(); |
| |
| case ParameterConvention::Indirect_In: { |
| // This expects an @trivial if we have lowered addresses and @ |
| if (conv.useLoweredAddresses()) { |
| return visitApplyParameter(ValueOwnershipKind::Trivial, |
| UseLifetimeConstraint::MustBeLive); |
| } |
| // TODO: Once trivial is subsumed in any, this goes away. |
| auto map = visitApplyParameter(ValueOwnershipKind::Owned, |
| UseLifetimeConstraint::MustBeInvalidated); |
| map.addCompatibilityConstraint(ValueOwnershipKind::Trivial, |
| UseLifetimeConstraint::MustBeLive); |
| return map; |
| } |
| |
| case ParameterConvention::Indirect_In_Guaranteed: { |
| // This expects an @trivial if we have lowered addresses and @ |
| if (conv.useLoweredAddresses()) { |
| return visitApplyParameter(ValueOwnershipKind::Trivial, |
| UseLifetimeConstraint::MustBeLive); |
| } |
| // TODO: Once trivial is subsumed in any, this goes away. |
| auto map = visitApplyParameter(ValueOwnershipKind::Guaranteed, |
| UseLifetimeConstraint::MustBeLive); |
| map.addCompatibilityConstraint(ValueOwnershipKind::Trivial, |
| UseLifetimeConstraint::MustBeLive); |
| return map; |
| } |
| |
| // The following conventions should take address types and thus be |
| // trivial. |
| case ParameterConvention::Indirect_In_Constant: |
| case ParameterConvention::Indirect_Inout: |
| case ParameterConvention::Indirect_InoutAliasable: |
| return visitApplyParameter(ValueOwnershipKind::Trivial, |
| UseLifetimeConstraint::MustBeLive); |
| |
| case ParameterConvention::Direct_Guaranteed: |
| // A +1 value may be passed to a guaranteed argument. From the caller's |
| // point of view, this is just like a normal non-consuming use. |
| // Direct_Guaranteed only accepts non-trivial types, but trivial types are |
| // already handled above. |
| return visitApplyParameter(ValueOwnershipKind::Guaranteed, |
| UseLifetimeConstraint::MustBeLive); |
| } |
| llvm_unreachable("unhandled convension"); |
| } |
| |
| OperandOwnershipKindMap |
| OperandOwnershipKindClassifier::visitBeginApplyInst(BeginApplyInst *I) { |
| return visitFullApply(I); |
| } |
| |
| OperandOwnershipKindMap |
| OperandOwnershipKindClassifier::visitApplyInst(ApplyInst *I) { |
| return visitFullApply(I); |
| } |
| |
| OperandOwnershipKindMap |
| OperandOwnershipKindClassifier::visitTryApplyInst(TryApplyInst *I) { |
| return visitFullApply(I); |
| } |
| |
| OperandOwnershipKindMap |
| OperandOwnershipKindClassifier::visitPartialApplyInst(PartialApplyInst *I) { |
| return Map::compatibilityMap( |
| {{ValueOwnershipKind::Trivial, UseLifetimeConstraint::MustBeLive}, |
| // All non-trivial types should be captured. |
| {ValueOwnershipKind::Owned, UseLifetimeConstraint::MustBeInvalidated}}); |
| } |
| |
| // TODO: FIX THIS |
| OperandOwnershipKindMap |
| OperandOwnershipKindClassifier::visitYieldInst(YieldInst *I) { |
| // Indirect return arguments are address types. |
| // |
| // TODO: Change this to check if this operand is an indirect result |
| if (isAddressOrTrivialType()) |
| return Map::compatibilityMap(ValueOwnershipKind::Trivial, |
| UseLifetimeConstraint::MustBeLive); |
| |
| auto fnType = I->getFunction()->getLoweredFunctionType(); |
| auto yieldInfo = fnType->getYields()[getOperandIndex()]; |
| switch (yieldInfo.getConvention()) { |
| case ParameterConvention::Indirect_In: |
| case ParameterConvention::Direct_Owned: |
| return visitApplyParameter(ValueOwnershipKind::Owned, |
| UseLifetimeConstraint::MustBeInvalidated); |
| case ParameterConvention::Indirect_In_Constant: |
| case ParameterConvention::Direct_Unowned: |
| // We accept unowned, owned, and guaranteed in unowned positions. |
| return Map::allLive(); |
| case ParameterConvention::Indirect_In_Guaranteed: |
| case ParameterConvention::Direct_Guaranteed: |
| return visitApplyParameter(ValueOwnershipKind::Guaranteed, |
| UseLifetimeConstraint::MustBeLive); |
| // The following conventions should take address types. |
| case ParameterConvention::Indirect_Inout: |
| case ParameterConvention::Indirect_InoutAliasable: |
| llvm_unreachable("Unexpected non-trivial parameter convention."); |
| } |
| llvm_unreachable("unhandled convension"); |
| } |
| |
| OperandOwnershipKindMap |
| OperandOwnershipKindClassifier::visitAssignInst(AssignInst *I) { |
| if (getValue() != I->getSrc()) { |
| return Map::compatibilityMap(ValueOwnershipKind::Trivial, |
| UseLifetimeConstraint::MustBeLive); |
| } |
| |
| return Map::compatibilityMap({ |
| {ValueOwnershipKind::Trivial, UseLifetimeConstraint::MustBeLive}, |
| {ValueOwnershipKind::Owned, UseLifetimeConstraint::MustBeInvalidated}, |
| }); |
| } |
| |
| OperandOwnershipKindMap |
| OperandOwnershipKindClassifier::visitStoreInst(StoreInst *I) { |
| if (getValue() != I->getSrc()) { |
| return Map::compatibilityMap(ValueOwnershipKind::Trivial, |
| UseLifetimeConstraint::MustBeLive); |
| } |
| |
| return Map::compatibilityMap({ |
| {ValueOwnershipKind::Trivial, UseLifetimeConstraint::MustBeLive}, |
| {ValueOwnershipKind::Owned, UseLifetimeConstraint::MustBeInvalidated}, |
| }); |
| } |
| |
| OperandOwnershipKindMap |
| OperandOwnershipKindClassifier::visitCopyBlockWithoutEscapingInst( |
| CopyBlockWithoutEscapingInst *I) { |
| // Consumes the closure parameter. |
| if (getValue() == I->getClosure()) { |
| return Map::compatibilityMap(ValueOwnershipKind::Owned, |
| UseLifetimeConstraint::MustBeInvalidated); |
| } |
| |
| return Map::compatibleWithAllExcept(ValueOwnershipKind::Trivial); |
| } |
| |
| OperandOwnershipKindMap OperandOwnershipKindClassifier::visitMarkDependenceInst( |
| MarkDependenceInst *MDI) { |
| |
| // Forward ownership if the mark_dependence instruction marks a dependence |
| // on a @noescape function type for an escaping function type. |
| if (getValue() == MDI->getValue()) |
| if (auto ResFnTy = MDI->getType().getAs<SILFunctionType>()) |
| if (auto BaseFnTy = MDI->getBase()->getType().getAs<SILFunctionType>()) |
| if (!ResFnTy->isNoEscape() && BaseFnTy->isNoEscape()) |
| return Map::compatibilityMap( |
| ValueOwnershipKind::Owned, |
| UseLifetimeConstraint::MustBeInvalidated); |
| |
| // We always treat mark dependence as a use that keeps a value alive. We will |
| // be introducing a begin_dependence/end_dependence version of this later. |
| return Map::allLive(); |
| } |
| |
| OperandOwnershipKindMap |
| OperandOwnershipKindClassifier::visitKeyPathInst(KeyPathInst *I) { |
| // KeyPath moves the value in memory out of address operands, but the |
| // ownership checker doesn't reason about that yet. |
| return Map::compatibilityMap( |
| {{ValueOwnershipKind::Trivial, UseLifetimeConstraint::MustBeLive}, |
| {ValueOwnershipKind::Owned, UseLifetimeConstraint::MustBeInvalidated}}); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Builtin Use Checker |
| //===----------------------------------------------------------------------===// |
| |
| namespace { |
| |
| struct OperandOwnershipKindBuiltinClassifier |
| : SILBuiltinVisitor<OperandOwnershipKindBuiltinClassifier, |
| OperandOwnershipKindMap> { |
| using Map = OperandOwnershipKindMap; |
| |
| OperandOwnershipKindMap 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 {ValueOwnershipKind::Trivial, UseLifetimeConstraint::MustBeLive}; |
| } |
| |
| // BUILTIN_TYPE_CHECKER_OPERATION does not live past the type checker. |
| #define BUILTIN_TYPE_CHECKER_OPERATION(ID, NAME) |
| |
| #define BUILTIN(ID, NAME, ATTRS) \ |
| OperandOwnershipKindMap visit##ID(BuiltinInst *BI, StringRef Attr); |
| #include "swift/AST/Builtins.def" |
| |
| OperandOwnershipKindMap check(BuiltinInst *BI) { return visit(BI); } |
| }; |
| |
| } // end anonymous namespace |
| |
| // This is correct today since we do not have any builtins which return |
| // @guaranteed parameters. This means that we can only have a lifetime ending |
| // use with our builtins if it is owned. |
| #define CONSTANT_OWNERSHIP_BUILTIN(OWNERSHIP, USE_LIFETIME_CONSTRAINT, ID) \ |
| OperandOwnershipKindMap OperandOwnershipKindBuiltinClassifier::visit##ID( \ |
| BuiltinInst *BI, StringRef Attr) { \ |
| return Map::compatibilityMap( \ |
| ValueOwnershipKind::OWNERSHIP, \ |
| UseLifetimeConstraint::USE_LIFETIME_CONSTRAINT); \ |
| } |
| CONSTANT_OWNERSHIP_BUILTIN(Owned, MustBeLive, ErrorInMain) |
| CONSTANT_OWNERSHIP_BUILTIN(Owned, MustBeLive, UnexpectedError) |
| CONSTANT_OWNERSHIP_BUILTIN(Owned, MustBeLive, WillThrow) |
| CONSTANT_OWNERSHIP_BUILTIN(Owned, MustBeInvalidated, UnsafeGuaranteed) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, AShr) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, Add) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, Alignof) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, AllocRaw) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, And) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, AssertConf) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, AssignCopyArrayNoAlias) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, AssignCopyArrayFrontToBack) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, AssignCopyArrayBackToFront) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, AssignTakeArray) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, AssumeNonNegative) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, AtomicLoad) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, AtomicRMW) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, AtomicStore) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, BitCast) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, CanBeObjCClass) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, CmpXChg) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, CondUnreachable) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, CopyArray) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, DeallocRaw) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, DestroyArray) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, ExactSDiv) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, ExactUDiv) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, ExtractElement) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, FAdd) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, FCMP_OEQ) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, FCMP_OGE) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, FCMP_OGT) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, FCMP_OLE) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, FCMP_OLT) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, FCMP_ONE) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, FCMP_ORD) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, FCMP_UEQ) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, FCMP_UGE) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, FCMP_UGT) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, FCMP_ULE) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, FCMP_ULT) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, FCMP_UNE) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, FCMP_UNO) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, FDiv) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, FMul) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, FNeg) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, FPExt) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, FPToSI) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, FPToUI) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, FPTrunc) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, FRem) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, FSub) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, Fence) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, GetObjCTypeEncoding) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, ICMP_EQ) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, ICMP_NE) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, ICMP_SGE) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, ICMP_SGT) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, ICMP_SLE) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, ICMP_SLT) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, ICMP_UGE) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, ICMP_UGT) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, ICMP_ULE) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, ICMP_ULT) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, InsertElement) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, IntToFPWithOverflow) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, IntToPtr) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, IsOptionalType) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, IsPOD) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, IsBitwiseTakable) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, IsSameMetatype) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, LShr) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, Mul) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, OnFastPath) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, Once) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, OnceWithContext) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, Or) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, PtrToInt) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, SAddOver) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, SDiv) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, SExt) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, SExtOrBitCast) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, SIToFP) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, SMulOver) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, SRem) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, SSubOver) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, SToSCheckedTrunc) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, SToUCheckedTrunc) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, SUCheckedConversion) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, Shl) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, Sizeof) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, StaticReport) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, Strideof) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, StringObjectOr) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, Sub) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, TakeArrayNoAlias) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, TakeArrayBackToFront) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, TakeArrayFrontToBack) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, Trunc) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, TruncOrBitCast) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, TSanInoutAccess) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, UAddOver) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, UDiv) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, UIToFP) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, UMulOver) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, URem) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, USCheckedConversion) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, USubOver) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, UToSCheckedTrunc) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, UToUCheckedTrunc) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, Unreachable) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, UnsafeGuaranteedEnd) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, Xor) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, ZExt) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, ZExtOrBitCast) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, ZeroInitializer) |
| CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, Swift3ImplicitObjCEntrypoint) |
| #undef CONSTANT_OWNERSHIP_BUILTIN |
| |
| // Builtins that should be lowered to SIL instructions so we should never see |
| // them. |
| #define BUILTIN_SIL_OPERATION(ID, NAME, CATEGORY) \ |
| OperandOwnershipKindMap OperandOwnershipKindBuiltinClassifier::visit##ID( \ |
| BuiltinInst *BI, StringRef Attr) { \ |
| llvm_unreachable("Builtin should have been lowered to SIL instruction?!"); \ |
| } |
| #define BUILTIN(X, Y, Z) |
| #include "swift/AST/Builtins.def" |
| |
| OperandOwnershipKindMap |
| OperandOwnershipKindClassifier::visitBuiltinInst(BuiltinInst *BI) { |
| return OperandOwnershipKindBuiltinClassifier().check(BI); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // SILValueOwnershipChecker |
| //===----------------------------------------------------------------------===// |
| |
| namespace { |
| |
| // TODO: This class uses a bunch of global state like variables. It should be |
| // refactored into a large state object that is used by functions. |
| class SILValueOwnershipChecker { |
| /// The result of performing the check. |
| llvm::Optional<bool> result; |
| |
| /// The module that we are in. |
| SILModule &mod; |
| |
| /// A cache of dead-end basic blocks that we use to determine if we can |
| /// ignore "leaks". |
| DeadEndBlocks &deadEndBlocks; |
| |
| /// The value whose ownership we will check. |
| SILValue value; |
| |
| /// The action that the checker should perform on detecting an error. |
| ErrorBehaviorKind errorBehavior; |
| |
| /// The list of lifetime ending users that we found. Only valid if check is |
| /// successful. |
| SmallVector<BranchPropagatedUser, 16> lifetimeEndingUsers; |
| |
| /// The list of non lifetime ending users that we found. Only valid if check |
| /// is successful. |
| SmallVector<BranchPropagatedUser, 16> regularUsers; |
| |
| /// The list of implicit non lifetime ending users that we found. This |
| /// consists of instructions like end_borrow that end a scoped lifetime. We |
| /// must treat those as regular uses and ensure that our value is not |
| /// destroyed while that sub-scope is valid. |
| /// |
| /// TODO: Rename to SubBorrowScopeUsers? |
| SmallVector<BranchPropagatedUser, 4> implicitRegularUsers; |
| |
| /// The set of blocks that we have visited. |
| SmallPtrSetImpl<SILBasicBlock *> &visitedBlocks; |
| |
| public: |
| SILValueOwnershipChecker( |
| SILModule &mod, DeadEndBlocks &deadEndBlocks, SILValue value, |
| ErrorBehaviorKind errorBehavior, |
| llvm::SmallPtrSetImpl<SILBasicBlock *> &visitedBlocks) |
| : result(), mod(mod), deadEndBlocks(deadEndBlocks), value(value), |
| errorBehavior(errorBehavior), visitedBlocks(visitedBlocks) { |
| assert(value && "Can not initialize a checker with an empty SILValue"); |
| } |
| |
| ~SILValueOwnershipChecker() = default; |
| SILValueOwnershipChecker(SILValueOwnershipChecker &) = delete; |
| SILValueOwnershipChecker(SILValueOwnershipChecker &&) = delete; |
| |
| bool check() { |
| if (result.hasValue()) |
| return result.getValue(); |
| |
| LLVM_DEBUG(llvm::dbgs() << "Verifying ownership of: " << *value); |
| result = checkUses(); |
| if (!result.getValue()) |
| return false; |
| |
| SmallVector<BranchPropagatedUser, 32> allRegularUsers; |
| copy(regularUsers, std::back_inserter(allRegularUsers)); |
| copy(implicitRegularUsers, std::back_inserter(allRegularUsers)); |
| result = |
| valueHasLinearLifetime(value, lifetimeEndingUsers, allRegularUsers, |
| visitedBlocks, deadEndBlocks, errorBehavior); |
| |
| return result.getValue(); |
| } |
| |
| using user_array_transform = |
| std::function<SILInstruction *(BranchPropagatedUser)>; |
| using user_array = TransformArrayRef<user_array_transform>; |
| |
| /// A function that returns a range of lifetime ending users found for the |
| /// given value. |
| user_array getLifetimeEndingUsers() const { |
| assert(result.hasValue() && "Can not call until check() is called"); |
| assert(result.getValue() && "Can not call if check() returned false"); |
| |
| user_array_transform transform( |
| [](BranchPropagatedUser user) -> SILInstruction * { |
| return user.getInst(); |
| }); |
| return user_array(ArrayRef<BranchPropagatedUser>(lifetimeEndingUsers), |
| transform); |
| } |
| |
| /// A function that returns a range of regular (i.e. "non lifetime ending") |
| /// users found for the given value. |
| user_array getRegularUsers() const { |
| assert(result.hasValue() && "Can not call until check() is called"); |
| assert(result.getValue() && "Can not call if check() returned false"); |
| |
| user_array_transform transform( |
| [](BranchPropagatedUser user) -> SILInstruction * { |
| return user.getInst(); |
| }); |
| return user_array(ArrayRef<BranchPropagatedUser>(regularUsers), transform); |
| } |
| |
| private: |
| bool checkUses(); |
| bool gatherUsers(SmallVectorImpl<BranchPropagatedUser> &lifetimeEndingUsers, |
| SmallVectorImpl<BranchPropagatedUser> ®ularUsers, |
| SmallVectorImpl<BranchPropagatedUser> &implicitRegularUsers); |
| |
| bool checkValueWithoutLifetimeEndingUses(); |
| |
| bool checkFunctionArgWithoutLifetimeEndingUses(SILFunctionArgument *arg); |
| bool checkYieldWithoutLifetimeEndingUses(BeginApplyResult *yield); |
| |
| bool isGuaranteedFunctionArgWithLifetimeEndingUses( |
| SILFunctionArgument *arg, |
| const SmallVectorImpl<BranchPropagatedUser> &lifetimeEndingUsers) const; |
| bool isSubobjectProjectionWithLifetimeEndingUses( |
| SILValue value, |
| const SmallVectorImpl<BranchPropagatedUser> &lifetimeEndingUsers) const; |
| |
| /// Depending on our initialization, either return false or call Func and |
| /// throw an error. |
| bool handleError(function_ref<void()> &&messagePrinterFunc) const { |
| if (errorBehavior.shouldPrintMessage()) { |
| messagePrinterFunc(); |
| } |
| |
| if (errorBehavior.shouldReturnFalse()) { |
| return false; |
| } |
| |
| assert(errorBehavior.shouldAssert() && "At this point, we should assert"); |
| llvm_unreachable("triggering standard assertion failure routine"); |
| } |
| }; |
| |
| } // end anonymous namespace |
| |
| bool SILValueOwnershipChecker::gatherUsers( |
| SmallVectorImpl<BranchPropagatedUser> &lifetimeEndingUsers, |
| SmallVectorImpl<BranchPropagatedUser> &nonLifetimeEndingUsers, |
| SmallVectorImpl<BranchPropagatedUser> &implicitRegularUsers) { |
| |
| // See if Value is guaranteed. If we are guaranteed and not forwarding, then |
| // we need to look through subobject uses for more uses. Otherwise, if we are |
| // forwarding, we do not create any lifetime ending users/non lifetime ending |
| // users since we verify against our base. |
| auto ownershipKind = value.getOwnershipKind(); |
| bool isGuaranteed = ownershipKind == ValueOwnershipKind::Guaranteed; |
| bool isOwned = ownershipKind == ValueOwnershipKind::Owned; |
| |
| if (isGuaranteed && isGuaranteedForwardingValue(value)) |
| return true; |
| |
| // Then gather up our initial list of users. |
| SmallVector<Operand *, 8> users; |
| std::copy(value->use_begin(), value->use_end(), std::back_inserter(users)); |
| |
| auto addCondBranchToList = [](SmallVectorImpl<BranchPropagatedUser> &list, |
| CondBranchInst *cbi, unsigned operandIndex) { |
| if (cbi->isConditionOperandIndex(operandIndex)) { |
| list.emplace_back(cbi); |
| return; |
| } |
| |
| bool isTrueOperand = cbi->isTrueOperandIndex(operandIndex); |
| list.emplace_back(cbi, isTrueOperand ? CondBranchInst::TrueIdx |
| : CondBranchInst::FalseIdx); |
| }; |
| |
| bool foundError = false; |
| while (!users.empty()) { |
| Operand *op = users.pop_back_val(); |
| SILInstruction *user = op->getUser(); |
| |
| // If this op is a type dependent operand, skip it. It is not interesting |
| // from an ownership perspective. |
| if (user->isTypeDependentOperand(*op)) |
| continue; |
| |
| bool isGuaranteedSubValue = false; |
| if (isGuaranteed && isGuaranteedForwardingInst(op->getUser())) { |
| isGuaranteedSubValue = true; |
| } |
| |
| auto opOwnershipKindMap = op->getOwnershipKindMap(isGuaranteedSubValue); |
| // If our ownership kind doesn't match, track that we found an error, emit |
| // an error message optionally and then continue. |
| if (!opOwnershipKindMap.canAcceptKind(ownershipKind)) { |
| foundError = true; |
| |
| // If we did not support /any/ ownership kind, it means that we found a |
| // conflicting answer so the kind map that was returned is the empty |
| // map. Put out a more specific error here. |
| if (!opOwnershipKindMap.data.any()) { |
| handleError([&]() { |
| llvm::errs() << "Function: '" << user->getFunction()->getName() |
| << "'\n" |
| << "Ill-formed SIL! Unable to compute ownership kind " |
| "map for user?!\n" |
| << "For terminator users, check that successors have " |
| "compatible ownership kinds.\n" |
| << "Value: " << op->get() << "User: " << *user |
| << "Operand Number: " << op->getOperandNumber() << '\n' |
| << "Conv: " << ownershipKind << "\n\n"; |
| }); |
| continue; |
| } |
| |
| handleError([&]() { |
| llvm::errs() << "Function: '" << user->getFunction()->getName() << "'\n" |
| << "Have operand with incompatible ownership?!\n" |
| << "Value: " << op->get() << "User: " << *user |
| << "Operand Number: " << op->getOperandNumber() << '\n' |
| << "Conv: " << ownershipKind << '\n' |
| << "OwnershipMap:\n" |
| << opOwnershipKindMap << '\n'; |
| }); |
| continue; |
| } |
| |
| auto lifetimeConstraint = |
| opOwnershipKindMap.getLifetimeConstraint(ownershipKind); |
| if (lifetimeConstraint == UseLifetimeConstraint::MustBeInvalidated) { |
| LLVM_DEBUG(llvm::dbgs() << " Lifetime Ending User: " << *user); |
| if (auto *cbi = dyn_cast<CondBranchInst>(user)) { |
| addCondBranchToList(lifetimeEndingUsers, cbi, op->getOperandNumber()); |
| } else { |
| lifetimeEndingUsers.emplace_back(user); |
| } |
| } else { |
| LLVM_DEBUG(llvm::dbgs() << " Regular User: " << *user); |
| if (auto *cbi = dyn_cast<CondBranchInst>(user)) { |
| addCondBranchToList(nonLifetimeEndingUsers, cbi, |
| op->getOperandNumber()); |
| } else { |
| nonLifetimeEndingUsers.emplace_back(user); |
| } |
| } |
| |
| // If our base value is not guaranteed, we do not to try to visit |
| // subobjects. |
| if (!isGuaranteed) { |
| // But if we are owned, check if we have any end_borrows. We |
| // need to treat these as sub-scope users. We can rely on the |
| // end_borrow to prevent recursion. |
| if (isOwned) { |
| // Do a check if any of our users are begin_borrows. If we find such a |
| // use, then we want to include the end_borrow associated with the |
| // begin_borrow in our NonLifetimeEndingUser lists. |
| // |
| // For correctness reasons we use indices to make sure that we can |
| // append to NonLifetimeEndingUsers without needing to deal with |
| // iterator invalidation. |
| SmallVector<SILInstruction *, 4> endBorrowInsts; |
| for (unsigned i : indices(nonLifetimeEndingUsers)) { |
| if (auto *bbi = dyn_cast<BeginBorrowInst>( |
| nonLifetimeEndingUsers[i].getInst())) { |
| copy(bbi->getEndBorrows(), |
| std::back_inserter(implicitRegularUsers)); |
| } |
| } |
| } |
| continue; |
| } |
| |
| // If we are guaranteed, but are not a guaranteed forwarding inst, |
| // just continue. This user is just treated as a normal use. |
| if (!isGuaranteedForwardingInst(user)) |
| continue; |
| |
| // At this point, we know that we must have a forwarded subobject. Since the |
| // base type is guaranteed, we know that the subobject is either guaranteed |
| // or trivial. We now split into two cases, if the user is a terminator or |
| // not. If we do not have a terminator, then just add the uses of all of |
| // User's results to the worklist. |
| if (user->getResults().size()) { |
| for (SILValue result : user->getResults()) { |
| if (result.getOwnershipKind() == ValueOwnershipKind::Trivial) { |
| continue; |
| } |
| |
| // Now, we /must/ have a guaranteed subobject, so let's assert that the |
| // user is actually guaranteed and add the subobject's users to our |
| // worklist. |
| assert(result.getOwnershipKind() == ValueOwnershipKind::Guaranteed && |
| "Our value is guaranteed and this is a forwarding instruction. " |
| "Should have guaranteed ownership as well."); |
| copy(result->getUses(), std::back_inserter(users)); |
| } |
| |
| continue; |
| } |
| |
| assert(user->getResults().empty()); |
| |
| auto *ti = dyn_cast<TermInst>(user); |
| if (!ti) { |
| continue; |
| } |
| |
| // Otherwise if we have a terminator, add any as uses any end_borrow to |
| // ensure that the subscope is completely enclsed within the super scope. We |
| // require all of our arguments to be either trivial or guaranteed. |
| for (auto &succ : ti->getSuccessors()) { |
| auto *succBlock = succ.getBB(); |
| |
| // If we do not have any arguments, then continue. |
| if (succBlock->args_empty()) |
| continue; |
| |
| // Otherwise, make sure that all arguments are trivial or guaranteed. If |
| // we fail, emit an error. |
| // |
| // TODO: We could ignore this error and emit a more specific error on the |
| // actual terminator. |
| for (auto *succArg : succBlock->getPhiArguments()) { |
| // *NOTE* We do not emit an error here since we want to allow for more |
| // specific errors to be found during use_verification. |
| // |
| // TODO: Add a flag that associates the terminator instruction with |
| // needing to be verified. If it isn't verified appropriately, assert |
| // when the verifier is destroyed. |
| auto succArgOwnershipKind = succArg->getOwnershipKind(); |
| if (!succArgOwnershipKind.isTrivialOrCompatibleWith(ownershipKind)) { |
| // This is where the error would go. |
| continue; |
| } |
| |
| // If we have a trivial value, just continue. |
| if (succArgOwnershipKind == ValueOwnershipKind::Trivial) |
| continue; |
| |
| // Otherwise add all end_borrow users for this BBArg to the |
| // implicit regular user list. We know that BBArg must be |
| // completely joint post-dominated by these users, so we use |
| // them to ensure that all of BBArg's uses are completely |
| // enclosed within the end_borrow of this argument. |
| for (auto *op : succArg->getUses()) { |
| if (auto *ebi = dyn_cast<EndBorrowInst>(op->getUser())) { |
| implicitRegularUsers.push_back(ebi); |
| } |
| } |
| } |
| } |
| } |
| |
| // Return true if we did not have an error and false if we did find an error. |
| // |
| // The reason why we use this extra variable is to make sure that when we are |
| // testing, we print out all mismatching pairs rather than just the first. |
| return !foundError; |
| } |
| |
| bool SILValueOwnershipChecker::checkFunctionArgWithoutLifetimeEndingUses( |
| SILFunctionArgument *arg) { |
| switch (arg->getOwnershipKind()) { |
| case ValueOwnershipKind::Guaranteed: |
| case ValueOwnershipKind::Unowned: |
| case ValueOwnershipKind::Trivial: |
| return true; |
| case ValueOwnershipKind::Any: |
| llvm_unreachable( |
| "Function arguments should never have ValueOwnershipKind::Any"); |
| case ValueOwnershipKind::Owned: |
| break; |
| } |
| |
| if (deadEndBlocks.isDeadEnd(arg->getParent())) |
| return true; |
| |
| return !handleError([&] { |
| llvm::errs() << "Function: '" << arg->getFunction()->getName() << "'\n" |
| << " Owned function parameter without life ending uses!\n" |
| << "Value: " << *arg << '\n'; |
| }); |
| } |
| |
| bool SILValueOwnershipChecker::checkYieldWithoutLifetimeEndingUses( |
| BeginApplyResult *yield) { |
| switch (yield->getOwnershipKind()) { |
| case ValueOwnershipKind::Guaranteed: |
| case ValueOwnershipKind::Unowned: |
| case ValueOwnershipKind::Trivial: |
| return true; |
| case ValueOwnershipKind::Any: |
| llvm_unreachable("Yields should never have ValueOwnershipKind::Any"); |
| case ValueOwnershipKind::Owned: |
| break; |
| } |
| |
| if (deadEndBlocks.isDeadEnd(yield->getParent()->getParent())) |
| return true; |
| |
| return !handleError([&] { |
| llvm::errs() << "Function: '" << yield->getFunction()->getName() << "'\n" |
| << " Owned yield without life ending uses!\n" |
| << "Value: " << *yield << '\n'; |
| }); |
| } |
| bool SILValueOwnershipChecker::checkValueWithoutLifetimeEndingUses() { |
| LLVM_DEBUG(llvm::dbgs() << " No lifetime ending users?! Bailing early.\n"); |
| if (auto *arg = dyn_cast<SILFunctionArgument>(value)) { |
| if (checkFunctionArgWithoutLifetimeEndingUses(arg)) { |
| return true; |
| } |
| } |
| |
| if (auto *yield = dyn_cast<BeginApplyResult>(value)) { |
| if (checkYieldWithoutLifetimeEndingUses(yield)) { |
| return true; |
| } |
| } |
| |
| // Check if we are a guaranteed subobject. In such a case, we should never |
| // have lifetime ending uses, since our lifetime is guaranteed by our |
| // operand, so there is nothing further to do. So just return true. |
| if (isGuaranteedForwardingValue(value) && |
| value.getOwnershipKind() == ValueOwnershipKind::Guaranteed) |
| return true; |
| |
| // If we have an unowned value, then again there is nothing left to do. |
| if (value.getOwnershipKind() == ValueOwnershipKind::Unowned) |
| return true; |
| |
| if (auto *parentBlock = value->getParentBlock()) { |
| if (deadEndBlocks.isDeadEnd(parentBlock)) { |
| LLVM_DEBUG(llvm::dbgs() << " Ignoring transitively unreachable value " |
| << "without users!\n" |
| << " Function: '" |
| << value->getFunction()->getName() << "'\n" |
| << " Value: " << *value << '\n'); |
| return true; |
| } |
| } |
| |
| if (!isValueAddressOrTrivial(value, mod)) { |
| return !handleError([&] { |
| llvm::errs() << "Function: '" << value->getFunction()->getName() << "'\n"; |
| if (value.getOwnershipKind() == ValueOwnershipKind::Owned) { |
| llvm::errs() << "Error! Found a leaked owned value that was never " |
| "consumed.\n"; |
| } else { |
| llvm::errs() << "Non trivial values, non address values, and non " |
| "guaranteed function args must have at least one " |
| "lifetime ending use?!\n"; |
| } |
| llvm::errs() << "Value: " << *value << '\n'; |
| }); |
| } |
| |
| return true; |
| } |
| |
| bool SILValueOwnershipChecker::isGuaranteedFunctionArgWithLifetimeEndingUses( |
| SILFunctionArgument *arg, |
| const llvm::SmallVectorImpl<BranchPropagatedUser> &lifetimeEndingUsers) |
| const { |
| if (arg->getOwnershipKind() != ValueOwnershipKind::Guaranteed) |
| return true; |
| |
| return handleError([&] { |
| llvm::errs() << " Function: '" << arg->getFunction()->getName() << "'\n" |
| << " Guaranteed function parameter with life ending uses!\n" |
| << " Value: " << *arg; |
| for (const auto &user : lifetimeEndingUsers) { |
| llvm::errs() << " Lifetime Ending User: " << *user; |
| } |
| llvm::errs() << '\n'; |
| }); |
| } |
| |
| bool SILValueOwnershipChecker::isSubobjectProjectionWithLifetimeEndingUses( |
| SILValue value, |
| const llvm::SmallVectorImpl<BranchPropagatedUser> &lifetimeEndingUsers) |
| const { |
| return handleError([&] { |
| llvm::errs() << " Function: '" << value->getFunction()->getName() |
| << "'\n" |
| << " Subobject projection with life ending uses!\n" |
| << " Value: " << *value; |
| for (const auto &user : lifetimeEndingUsers) { |
| llvm::errs() << " Lifetime Ending User: " << *user; |
| } |
| llvm::errs() << '\n'; |
| }); |
| } |
| |
| bool SILValueOwnershipChecker::checkUses() { |
| LLVM_DEBUG(llvm::dbgs() << " Gathering and classifying uses!\n"); |
| |
| // First go through V and gather up its uses. While we do this we: |
| // |
| // 1. Verify that none of the uses are in the same block. This would be an |
| // overconsume so in this case we assert. |
| // 2. Verify that the uses are compatible with our ownership convention. |
| if (!gatherUsers(lifetimeEndingUsers, regularUsers, implicitRegularUsers)) { |
| // Silently return false if this fails. |
| // |
| // If the user pass in a ErrorBehaviorKind that will assert, we |
| // will have asserted in gatherUsers(). If we get here the user |
| // asked us to optionally print out a message and indicate that |
| // the verification failed. |
| return false; |
| } |
| |
| // We can only have no lifetime ending uses if we have: |
| // |
| // 1. A trivial typed value. |
| // 2. An address type value. |
| // 3. A guaranteed function argument. |
| // |
| // In the first two cases, it is easy to see that there is nothing further to |
| // do but return false. |
| // |
| // In the case of a function argument, one must think about the issues a bit |
| // more. Specifically, we should have /no/ lifetime ending uses of a |
| // guaranteed function argument, since a guaranteed function argument should |
| // outlive the current function always. |
| if (lifetimeEndingUsers.empty() && checkValueWithoutLifetimeEndingUses()) { |
| return false; |
| } |
| |
| LLVM_DEBUG(llvm::dbgs() << " Found lifetime ending users! Performing " |
| "initial checks\n"); |
| |
| // See if we have a guaranteed function address. Guaranteed function addresses |
| // should never have any lifetime ending uses. |
| if (auto *arg = dyn_cast<SILFunctionArgument>(value)) { |
| if (!isGuaranteedFunctionArgWithLifetimeEndingUses(arg, |
| lifetimeEndingUsers)) { |
| return false; |
| } |
| } |
| |
| // Check if we are an instruction that forwards forwards guaranteed |
| // ownership. In such a case, we are a subobject projection. We should not |
| // have any lifetime ending uses. |
| if (isGuaranteedForwardingValue(value) && |
| value.getOwnershipKind() == ValueOwnershipKind::Guaranteed) { |
| if (!isSubobjectProjectionWithLifetimeEndingUses(value, |
| lifetimeEndingUsers)) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Top Level Entrypoints |
| //===----------------------------------------------------------------------===// |
| |
| OperandOwnershipKindMap |
| Operand::getOwnershipKindMap(bool isForwardingSubValue) const { |
| OperandOwnershipKindClassifier classifier(getUser()->getModule(), *this, |
| ErrorBehaviorKind::ReturnFalse, |
| isForwardingSubValue); |
| return classifier.visit(const_cast<SILInstruction *>(getUser())); |
| } |
| |
| void SILInstruction::verifyOperandOwnership() const { |
| #ifndef NDEBUG |
| if (DisableOwnershipVerification) |
| return; |
| |
| if (isStaticInitializerInst()) |
| return; |
| |
| // If SILOwnership is not enabled, do not perform verification. |
| if (!getModule().getOptions().EnableSILOwnership) |
| return; |
| |
| // If the given function has unqualified ownership or we have been asked by |
| // the user not to verify this function, there is nothing to verify. |
| if (!getFunction()->hasQualifiedOwnership() || |
| !getFunction()->shouldVerifyOwnership()) |
| return; |
| |
| // If we are testing the verifier, bail so we only print errors once when |
| // performing a full verification, instead of additionally in the SILBuilder. |
| if (IsSILOwnershipVerifierTestingEnabled) |
| return; |
| |
| // If this is a terminator instruction, do not verify in SILBuilder. This is |
| // because when building a new function, one must create the destination block |
| // first which is an unnatural pattern and pretty brittle. |
| if (isa<TermInst>(this)) |
| return; |
| |
| ErrorBehaviorKind errorBehavior; |
| if (IsSILOwnershipVerifierTestingEnabled) { |
| errorBehavior = ErrorBehaviorKind::PrintMessageAndReturnFalse; |
| } else { |
| errorBehavior = ErrorBehaviorKind::PrintMessageAndAssert; |
| } |
| for (const Operand &op : getAllOperands()) { |
| // Skip type dependence operands. |
| if (isTypeDependentOperand(op)) |
| continue; |
| SILValue opValue = op.get(); |
| |
| // Skip any SILUndef that we see. |
| if (isa<SILUndef>(opValue)) |
| continue; |
| auto operandOwnershipKindMap = op.getOwnershipKindMap(); |
| auto valueOwnershipKind = opValue.getOwnershipKind(); |
| if (operandOwnershipKindMap.canAcceptKind(valueOwnershipKind)) |
| continue; |
| |
| if (errorBehavior.shouldPrintMessage()) { |
| llvm::errs() << "Found an operand with a value that is not compatible " |
| "with the operand's operand ownership kind map.\n"; |
| llvm::errs() << "Value: " << opValue; |
| llvm::errs() << "Value Ownership Kind: " << valueOwnershipKind << "\n"; |
| llvm::errs() << "Instruction: " << *this; |
| llvm::errs() << "Operand Ownership Kind Map: " << operandOwnershipKindMap; |
| } |
| |
| if (errorBehavior.shouldReturnFalse()) |
| continue; |
| |
| assert(errorBehavior.shouldAssert() && |
| "At this point, we are expected to assert"); |
| llvm_unreachable("triggering standard assertion failure routine"); |
| } |
| #endif |
| } |
| |
| void SILValue::verifyOwnership(SILModule &mod, |
| DeadEndBlocks *deadEndBlocks) const { |
| #ifndef NDEBUG |
| if (DisableOwnershipVerification) |
| return; |
| |
| // If we are SILUndef, just bail. SILUndef can pair with anything. Any uses of |
| // the SILUndef will make sure that the matching checks out. |
| if (isa<SILUndef>(*this)) |
| return; |
| |
| // Since we do not have SILUndef, we now know that getFunction() should return |
| // a real function. Assert in case this assumption is no longer true. |
| SILFunction *f = (*this)->getFunction(); |
| assert(f && "Instructions and arguments should have a function"); |
| |
| // If the given function has unqualified ownership or we have been asked by |
| // the user not to verify this function, there is nothing to verify. |
| if (!f->hasQualifiedOwnership() || !f->shouldVerifyOwnership()) |
| return; |
| |
| ErrorBehaviorKind errorBehavior; |
| if (IsSILOwnershipVerifierTestingEnabled) { |
| errorBehavior = ErrorBehaviorKind::PrintMessageAndReturnFalse; |
| } else { |
| errorBehavior = ErrorBehaviorKind::PrintMessageAndAssert; |
| } |
| |
| llvm::SmallPtrSet<SILBasicBlock *, 32> liveBlocks; |
| if (deadEndBlocks) { |
| SILValueOwnershipChecker(mod, *deadEndBlocks, *this, errorBehavior, |
| liveBlocks) |
| .check(); |
| } else { |
| DeadEndBlocks deadEndBlocks(f); |
| SILValueOwnershipChecker(mod, deadEndBlocks, *this, errorBehavior, |
| liveBlocks) |
| .check(); |
| } |
| #endif |
| } |
| |
| bool OwnershipChecker::checkValue(SILValue value) { |
| regularUsers.clear(); |
| lifetimeEndingUsers.clear(); |
| liveBlocks.clear(); |
| |
| // If we are SILUndef, just bail. SILUndef can pair with anything. Any uses of |
| // the SILUndef will make sure that the matching checks out. |
| if (isa<SILUndef>(value)) |
| return false; |
| |
| // Since we do not have SILUndef, we now know that getFunction() should return |
| // a real function. Assert in case this assumption is no longer true. |
| SILFunction *f = value->getFunction(); |
| assert(f && "Instructions and arguments should have a function"); |
| |
| // If the given function has unqualified ownership, there is nothing further |
| // to verify. |
| if (!f->hasQualifiedOwnership()) |
| return false; |
| |
| ErrorBehaviorKind errorBehavior(ErrorBehaviorKind::ReturnFalse); |
| SILValueOwnershipChecker checker(mod, deadEndBlocks, value, errorBehavior, |
| liveBlocks); |
| if (!checker.check()) { |
| return false; |
| } |
| |
| // TODO: Make this more efficient. |
| copy(checker.getRegularUsers(), std::back_inserter(regularUsers)); |
| copy(checker.getLifetimeEndingUsers(), |
| std::back_inserter(lifetimeEndingUsers)); |
| return true; |
| } |