//===--- InstructionUtils.cpp - Utilities for SIL instructions ------------===// // // 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-inst-utils" #include "swift/SIL/InstructionUtils.h" #include "swift/AST/SubstitutionMap.h" #include "swift/Basic/NullablePtr.h" #include "swift/SIL/DebugUtils.h" #include "swift/SIL/Projection.h" #include "swift/SIL/SILArgument.h" #include "swift/SIL/SILBasicBlock.h" #include "swift/SIL/SILVisitor.h" using namespace swift; /// Strip off casts/indexing insts/address projections from V until there is /// nothing left to strip. /// FIXME: Why don't we strip projections after stripping indexes? SILValue swift::getUnderlyingObject(SILValue V) { while (true) { SILValue V2 = stripIndexingInsts(stripAddressProjections(stripCasts(V))); if (V2 == V) return V2; V = V2; } } /// Strip off casts and address projections into the interior of a value. Unlike /// getUnderlyingObject, this does not find the root of a heap object--a class /// property is itself an address root. SILValue swift::getUnderlyingAddressRoot(SILValue V) { while (true) { SILValue V2 = stripIndexingInsts(stripCasts(V)); switch (V2->getKind()) { case ValueKind::StructElementAddrInst: case ValueKind::TupleElementAddrInst: case ValueKind::UncheckedTakeEnumDataAddrInst: V2 = cast(V2)->getOperand(0); break; default: break; } if (V2 == V) return V2; V = V2; } } SILValue swift::getUnderlyingObjectStopAtMarkDependence(SILValue V) { while (true) { SILValue V2 = stripIndexingInsts(stripAddressProjections(stripCastsWithoutMarkDependence(V))); if (V2 == V) return V2; V = V2; } } static bool isRCIdentityPreservingCast(ValueKind Kind) { switch (Kind) { case ValueKind::UpcastInst: case ValueKind::UncheckedRefCastInst: case ValueKind::UnconditionalCheckedCastInst: case ValueKind::UnconditionalCheckedCastValueInst: case ValueKind::RefToBridgeObjectInst: case ValueKind::BridgeObjectToRefInst: return true; default: return false; } } /// Return the underlying SILValue after stripping off identity SILArguments if /// we belong to a BB with one predecessor. SILValue swift::stripSinglePredecessorArgs(SILValue V) { while (true) { auto *A = dyn_cast(V); if (!A) return V; SILBasicBlock *BB = A->getParent(); // First try and grab the single predecessor of our parent BB. If we don't // have one, bail. SILBasicBlock *Pred = BB->getSinglePredecessorBlock(); if (!Pred) return V; // Then grab the terminator of Pred... TermInst *PredTI = Pred->getTerminator(); // And attempt to find our matching argument. // // *NOTE* We can only strip things here if we know that there is no semantic // change in terms of upcasts/downcasts/enum extraction since this is used // by other routines here. This means that we can only look through // cond_br/br. // // For instance, routines that use stripUpcasts() do not want to strip off a // downcast that results from checked_cast_br. if (auto *BI = dyn_cast(PredTI)) { V = BI->getArg(A->getIndex()); continue; } if (auto *CBI = dyn_cast(PredTI)) { if (SILValue Arg = CBI->getArgForDestBB(BB, A)) { V = Arg; continue; } } return V; } } SILValue swift::stripCastsWithoutMarkDependence(SILValue V) { while (true) { V = stripSinglePredecessorArgs(V); auto K = V->getKind(); if (isRCIdentityPreservingCast(K) || K == ValueKind::UncheckedTrivialBitCastInst) { V = cast(V)->getOperand(0); continue; } return V; } } SILValue swift::stripCasts(SILValue V) { while (true) { V = stripSinglePredecessorArgs(V); auto K = V->getKind(); if (isRCIdentityPreservingCast(K) || K == ValueKind::UncheckedTrivialBitCastInst || K == ValueKind::MarkDependenceInst) { V = cast(V)->getOperand(0); continue; } return V; } } SILValue swift::stripUpCasts(SILValue V) { assert(V->getType().isClassOrClassMetatype() && "Expected class or class metatype!"); V = stripSinglePredecessorArgs(V); while (auto upcast = dyn_cast(V)) V = stripSinglePredecessorArgs(upcast->getOperand()); return V; } SILValue swift::stripClassCasts(SILValue V) { while (true) { if (auto *UI = dyn_cast(V)) { V = UI->getOperand(); continue; } if (auto *UCCI = dyn_cast(V)) { V = UCCI->getOperand(); continue; } return V; } } SILValue swift::stripAddressAccess(SILValue V) { while (true) { switch (V->getKind()) { default: return V; case ValueKind::BeginBorrowInst: case ValueKind::BeginAccessInst: V = cast(V)->getOperand(0); } } } SILValue swift::stripAddressProjections(SILValue V) { while (true) { V = stripSinglePredecessorArgs(V); if (!Projection::isAddressProjection(V)) return V; V = cast(V)->getOperand(0); } } SILValue swift::stripUnaryAddressProjections(SILValue V) { while (true) { V = stripSinglePredecessorArgs(V); if (!Projection::isAddressProjection(V)) return V; auto *Inst = cast(V); if (Inst->getNumOperands() > 1) return V; V = Inst->getOperand(0); } } SILValue swift::stripValueProjections(SILValue V) { while (true) { V = stripSinglePredecessorArgs(V); if (!Projection::isObjectProjection(V)) return V; V = cast(V)->getOperand(0); } } SILValue swift::stripIndexingInsts(SILValue V) { while (true) { if (!isa(V)) return V; V = cast(V)->getBase(); } } SILValue swift::stripExpectIntrinsic(SILValue V) { auto *BI = dyn_cast(V); if (!BI) return V; if (BI->getIntrinsicInfo().ID != llvm::Intrinsic::expect) return V; return BI->getArguments()[0]; } SILValue swift::stripBorrow(SILValue V) { if (auto *BBI = dyn_cast(V)) return BBI->getOperand(); return V; } SingleValueInstruction *swift::getSingleValueCopyOrCast(SILInstruction *I) { if (auto *convert = dyn_cast(I)) return convert; switch (I->getKind()) { default: return nullptr; case SILInstructionKind::CopyValueInst: case SILInstructionKind::CopyBlockInst: case SILInstructionKind::BeginBorrowInst: case SILInstructionKind::BeginAccessInst: return cast(I); } } // Does this instruction terminate a SIL-level scope? bool swift::isEndOfScopeMarker(SILInstruction *user) { switch (user->getKind()) { default: return false; case SILInstructionKind::EndAccessInst: case SILInstructionKind::EndBorrowInst: case SILInstructionKind::EndLifetimeInst: return true; } } bool swift::isIncidentalUse(SILInstruction *user) { return isEndOfScopeMarker(user) || isDebugInst(user) || isa(user); } bool swift::onlyAffectsRefCount(SILInstruction *user) { switch (user->getKind()) { default: return false; case SILInstructionKind::AutoreleaseValueInst: case SILInstructionKind::DestroyValueInst: case SILInstructionKind::ReleaseValueInst: case SILInstructionKind::RetainValueInst: case SILInstructionKind::StrongReleaseInst: case SILInstructionKind::StrongRetainInst: case SILInstructionKind::UnmanagedAutoreleaseValueInst: case SILInstructionKind::UnmanagedReleaseValueInst: case SILInstructionKind::UnmanagedRetainValueInst: case SILInstructionKind::UnownedReleaseInst: case SILInstructionKind::UnownedRetainInst: return true; } } SILValue swift::stripConvertFunctions(SILValue V) { while (true) { if (auto CFI = dyn_cast(V)) { V = CFI->getOperand(); continue; } else if (auto *Cvt = dyn_cast(V)) { V = Cvt->getOperand(); continue; } break; } return V; } // Return true if the given address is a 'let' lvalue. static bool isLetAccess(SILValue address) { switch (address->getKind()) { default: return false; case ValueKind::AllocStackInst: { VarDecl *decl = cast(address)->getDecl(); return decl && decl->isLet(); } case ValueKind::AllocBoxInst: { VarDecl *decl = cast(address)->getDecl(); return decl && decl->isLet(); } case ValueKind::RefElementAddrInst: { VarDecl *decl = cast(address)->getField(); return decl && decl->isLet(); } case ValueKind::GlobalAddrInst: { SILGlobalVariable *global = cast(address)->getReferencedGlobal(); return global && global->isLet(); } }; } // An address base is a block argument. Verify that it is actually a box // projected from a switch_enum. static void checkSwitchEnumBlockArg(SILPHIArgument *arg) { assert(!arg->getType().isAddress()); SILBasicBlock *Pred = arg->getParent()->getSinglePredecessorBlock(); if (!Pred || !isa(Pred->getTerminator())) { arg->dump(); llvm_unreachable("unexpected box source."); } } SILValue swift::findAccessedAddressBase(SILValue sourceAddr) { SILValue address = sourceAddr; while (true) { switch (address->getKind()) { default: address->dump(); llvm_unreachable("unexpected address source."); // Base cases: these are always the base of a formal access. case ValueKind::GlobalAddrInst: case ValueKind::RefElementAddrInst: // An AllocBox is a fully identified memory location. case ValueKind::AllocBoxInst: // An AllocStack is a fully identified memory location, which may occur // after inlining code already subjected to stack promotion. case ValueKind::AllocStackInst: // View the outer begin_access as a separate location because nested // accesses do not conflict with each other. case ValueKind::BeginAccessInst: // A function argument is effectively a nested access, enforced // independently in the caller and callee. case ValueKind::SILFunctionArgument: // An addressor provides access to a global or class property via a // RawPointer. Calling the addressor casts that raw pointer to an address. case ValueKind::PointerToAddressInst: return address; // A block argument may be a box value projected out of // switch_enum. Address-type block arguments are not allowed. case ValueKind::SILPHIArgument: checkSwitchEnumBlockArg(cast(address)); return address; // Load a box from an indirect payload of an opaque enum. // We must have peeked past the project_box earlier in this loop. // (the indirectness makes it a box, the load is for address-only). // // %payload_adr = unchecked_take_enum_data_addr %enum : $*Enum, #Enum.case // %box = load [take] %payload_adr : $*{ var Enum } // // FIXME: this case should go away with opaque values. case ValueKind::LoadInst: { assert(address->getType().is()); address = cast(address)->getOperand(); assert(isa(address)); continue; } // Inductive cases: // Look through address casts to find the source address. case ValueKind::MarkUninitializedInst: case ValueKind::OpenExistentialAddrInst: case ValueKind::UncheckedAddrCastInst: // Inductive cases that apply to any type. case ValueKind::CopyValueInst: case ValueKind::MarkDependenceInst: // Look through a project_box to identify the underlying alloc_box as the // accesed object. It must be possible to reach either the alloc_box or the // containing enum in this loop, only looking through simple value // propagation such as copy_value. case ValueKind::ProjectBoxInst: // Handle project_block_storage just like project_box. case ValueKind::ProjectBlockStorageInst: // Look through begin_borrow in case a local box is borrowed. case ValueKind::BeginBorrowInst: address = cast(address)->getOperand(0); continue; // Subobject projections. case ValueKind::StructElementAddrInst: case ValueKind::TupleElementAddrInst: case ValueKind::UncheckedTakeEnumDataAddrInst: case ValueKind::RefTailAddrInst: case ValueKind::TailAddrInst: case ValueKind::IndexAddrInst: address = cast(address)->getOperand(0); continue; // Value to address conversions: the operand is the non-address source // value. These allow local mutation of the value but should never be used // for formal access of an lvalue. case ValueKind::OpenExistentialBoxInst: case ValueKind::ProjectExistentialBoxInst: case ValueKind::ProjectValueBufferInst: return SILValue(); // Local initialization: these cases are skipped. case ValueKind::InitEnumDataAddrInst: case ValueKind::InitExistentialAddrInst: case ValueKind::AllocExistentialBoxInst: case ValueKind::AllocValueBufferInst: case ValueKind::SILUndef: return SILValue(); } } } bool swift::isPossibleFormalAccessBase(SILValue baseAddress) { // A begin_access is considered a separate base for the purpose of conflict // checking. However, for the purpose of inserting unenforced markers and // performaing verification, it needs to be ignored. while (auto *beginAccess = dyn_cast(baseAddress)) baseAddress = beginAccess->getOperand(); // Function arguments are accessed by the caller. if (isa(baseAddress)) return false; if (isa(baseAddress)) { checkSwitchEnumBlockArg(cast(baseAddress)); return false; } // Pointer-to-address exclusivity cannot be enforced. `baseAddress` may be // pointing anywhere within an object. if (isa(baseAddress)) return false; // Immutable values are only accessed for initialization. if (isLetAccess(baseAddress)) return false; // Special case unsafe value buffer access. if (isa( baseAddress->getType().getSwiftRValueType())) { return false; } return true; } SILValue swift::isPartialApplyOfReabstractionThunk(PartialApplyInst *PAI) { if (PAI->getNumArguments() != 1) return SILValue(); auto *Fun = PAI->getReferencedFunction(); if (!Fun) return SILValue(); // Make sure we have a reabstraction thunk. if (Fun->isThunk() != IsReabstractionThunk) return SILValue(); // The argument should be a closure. auto Arg = PAI->getArgument(0); if (!Arg->getType().is() || (!Arg->getType().isReferenceCounted(PAI->getFunction()->getModule()) && Arg->getType().getAs()->getRepresentation() != SILFunctionType::Representation::Thick)) return SILValue(); return Arg; } /// Given a block used as a noescape function argument, attempt to find /// the Swift closure that invoking the block will call. static SILValue findClosureStoredIntoBlock(SILValue V) { auto FnType = V->getType().castTo(); assert(FnType->getRepresentation() == SILFunctionTypeRepresentation::Block); // Given a no escape block argument to a function, // pattern match to find the noescape closure that invoking the block // will call: // %noescape_closure = ... // %storage = alloc_stack // %storage_address = project_block_storage %storage // store %noescape_closure to [init] %storage_address // %block = init_block_storage_header %storage invoke %thunk // %arg = copy_block %block InitBlockStorageHeaderInst *IBSHI = nullptr; // Look through block copies to find the initialization of block storage. while (true) { if (auto *CBI = dyn_cast(V)) { V = CBI->getOperand(); continue; } IBSHI = dyn_cast(V); break; } if (!IBSHI) return nullptr; SILValue BlockStorage = IBSHI->getBlockStorage(); auto *PBSI = BlockStorage->getSingleUserOfType(); assert(PBSI && "Couldn't find block storage projection"); auto *SI = PBSI->getSingleUserOfType(); assert(SI && "Couldn't find single store of function into block storage"); return SI->getSrc(); } /// Look through a value passed as a function argument to determine whether /// it is a closure. /// /// Return the partial_apply and a flag set to true if the closure is /// indirectly captured by a reabstraction thunk. FindClosureResult swift::findClosureForAppliedArg(SILValue V) { // Look through borrows. if (auto *bbi = dyn_cast(V)) V = bbi->getOperand(); if (V->getType().getOptionalObjectType()) V = cast(V)->getOperand(); auto fnType = V->getType().getAs(); if (fnType->getRepresentation() == SILFunctionTypeRepresentation::Block) { V = findClosureStoredIntoBlock(V); if (!V) return FindClosureResult(nullptr, false); } auto *PAI = dyn_cast(stripConvertFunctions(V)); if (!PAI) return FindClosureResult(nullptr, false); SILValue thunkArg = isPartialApplyOfReabstractionThunk(PAI); if (thunkArg) { // Handle reabstraction thunks recursively. This may reabstract over // @convention(block). auto result = findClosureForAppliedArg(thunkArg); return FindClosureResult(result.PAI, true); } return FindClosureResult(PAI, false); } /// Helper for visitApplyAccesses that visits address-type call arguments, /// including arguments to @noescape functions that are passed as closures to /// the current call. static void visitApplyAccesses(ApplySite apply, std::function visitor) { for (Operand &oper : apply.getArgumentOperands()) { // Consider any address-type operand an access. Whether it is read or modify // depends on the argument convention. if (oper.get()->getType().isAddress()) { visitor(&oper); continue; } auto fnType = oper.get()->getType().getAs(); if (!fnType || !fnType->isNoEscape()) continue; // When @noescape function closures are passed as arguments, their // arguments are considered accessed at the call site. FindClosureResult result = findClosureForAppliedArg(oper.get()); if (!result.PAI) continue; // Recursively visit @noescape function closure arguments. visitApplyAccesses(result.PAI, visitor); } } void swift::visitAccessedAddress(SILInstruction *I, std::function visitor) { assert(I->mayReadOrWriteMemory()); // Reference counting instructions do not access user visible memory. if (isa(I)) return; if (isa(I)) return; if (auto apply = FullApplySite::isa(I)) { visitApplyAccesses(apply, visitor); return; } if (auto builtin = dyn_cast(I)) { if (auto Kind = builtin->getBuiltinKind()) { switch (Kind.getValue()) { default: I->dump(); llvm_unreachable("unexpected bulitin memory access."); // Buitins that affect memory but can't be formal accesses. case BuiltinValueKind::UnexpectedError: case BuiltinValueKind::ErrorInMain: case BuiltinValueKind::IsOptionalType: case BuiltinValueKind::AllocRaw: case BuiltinValueKind::DeallocRaw: case BuiltinValueKind::Fence: case BuiltinValueKind::StaticReport: case BuiltinValueKind::Once: case BuiltinValueKind::OnceWithContext: case BuiltinValueKind::Unreachable: case BuiltinValueKind::CondUnreachable: case BuiltinValueKind::DestroyArray: case BuiltinValueKind::UnsafeGuaranteed: case BuiltinValueKind::UnsafeGuaranteedEnd: case BuiltinValueKind::Swift3ImplicitObjCEntrypoint: case BuiltinValueKind::TSanInoutAccess: return; // General memory access to a pointer in first operand position. case BuiltinValueKind::CmpXChg: case BuiltinValueKind::AtomicLoad: case BuiltinValueKind::AtomicStore: case BuiltinValueKind::AtomicRMW: // Currently ignored because the access is on a RawPointer, not a // SIL address. // visitor(&builtin->getAllOperands()[0]); return; // Arrays: (T.Type, Builtin.RawPointer, Builtin.RawPointer, // Builtin.Word) case BuiltinValueKind::CopyArray: case BuiltinValueKind::TakeArrayNoAlias: case BuiltinValueKind::TakeArrayFrontToBack: case BuiltinValueKind::TakeArrayBackToFront: case BuiltinValueKind::AssignCopyArrayNoAlias: case BuiltinValueKind::AssignCopyArrayFrontToBack: case BuiltinValueKind::AssignCopyArrayBackToFront: case BuiltinValueKind::AssignTakeArray: // Currently ignored because the access is on a RawPointer. // visitor(&builtin->getAllOperands()[1]); // visitor(&builtin->getAllOperands()[2]); return; } } if (auto ID = builtin->getIntrinsicID()) { switch (ID.getValue()) { // Exhaustively verifying all LLVM instrinsics that access memory is // impractical. Instead, we call out the few common cases and return in // the default case. default: return; case llvm::Intrinsic::memcpy: case llvm::Intrinsic::memmove: // Currently ignored because the access is on a RawPointer. // visitor(&builtin->getAllOperands()[0]); // visitor(&builtin->getAllOperands()[1]); return; case llvm::Intrinsic::memset: // Currently ignored because the access is on a RawPointer. // visitor(&builtin->getAllOperands()[0]); return; } } llvm_unreachable("Must be either a builtin or intrinsic."); } switch (I->getKind()) { default: I->dump(); llvm_unreachable("unexpected memory access."); case SILInstructionKind::AssignInst: visitor(&I->getAllOperands()[AssignInst::Dest]); return; case SILInstructionKind::CheckedCastAddrBranchInst: visitor(&I->getAllOperands()[CheckedCastAddrBranchInst::Src]); visitor(&I->getAllOperands()[CheckedCastAddrBranchInst::Dest]); return; case SILInstructionKind::CopyAddrInst: visitor(&I->getAllOperands()[CopyAddrInst::Src]); visitor(&I->getAllOperands()[CopyAddrInst::Dest]); return; case SILInstructionKind::StoreInst: case SILInstructionKind::StoreBorrowInst: case SILInstructionKind::StoreUnownedInst: case SILInstructionKind::StoreWeakInst: visitor(&I->getAllOperands()[StoreInst::Dest]); return; case SILInstructionKind::SelectEnumAddrInst: visitor(&I->getAllOperands()[0]); return; case SILInstructionKind::InitExistentialAddrInst: case SILInstructionKind::InjectEnumAddrInst: case SILInstructionKind::LoadInst: case SILInstructionKind::LoadBorrowInst: case SILInstructionKind::LoadWeakInst: case SILInstructionKind::LoadUnownedInst: case SILInstructionKind::OpenExistentialAddrInst: case SILInstructionKind::SwitchEnumAddrInst: case SILInstructionKind::UncheckedTakeEnumDataAddrInst: case SILInstructionKind::UnconditionalCheckedCastInst: { assert(I->getNumOperands() - I->getNumTypeDependentOperands() == 1); Operand *singleOperand = &I->getAllOperands()[0]; if (singleOperand->get()->getType().isAddress()) visitor(singleOperand); return; } // Non-access cases: these are marked with memory side effects, but, by // themselves, do not access formal memory. case SILInstructionKind::AbortApplyInst: case SILInstructionKind::AllocBoxInst: case SILInstructionKind::AllocExistentialBoxInst: case SILInstructionKind::AllocGlobalInst: case SILInstructionKind::BeginAccessInst: case SILInstructionKind::BeginApplyInst: case SILInstructionKind::BeginBorrowInst: case SILInstructionKind::BeginUnpairedAccessInst: case SILInstructionKind::BindMemoryInst: case SILInstructionKind::CheckedCastValueBranchInst: case SILInstructionKind::CondFailInst: case SILInstructionKind::CopyBlockInst: case SILInstructionKind::CopyValueInst: case SILInstructionKind::CopyUnownedValueInst: case SILInstructionKind::DeinitExistentialAddrInst: case SILInstructionKind::DeinitExistentialValueInst: case SILInstructionKind::DestroyAddrInst: case SILInstructionKind::DestroyValueInst: case SILInstructionKind::EndAccessInst: case SILInstructionKind::EndApplyInst: case SILInstructionKind::EndBorrowArgumentInst: case SILInstructionKind::EndBorrowInst: case SILInstructionKind::EndUnpairedAccessInst: case SILInstructionKind::EndLifetimeInst: case SILInstructionKind::ExistentialMetatypeInst: case SILInstructionKind::FixLifetimeInst: case SILInstructionKind::InitExistentialValueInst: case SILInstructionKind::IsUniqueInst: case SILInstructionKind::IsEscapingClosureInst: case SILInstructionKind::IsUniqueOrPinnedInst: case SILInstructionKind::KeyPathInst: case SILInstructionKind::OpenExistentialBoxInst: case SILInstructionKind::OpenExistentialBoxValueInst: case SILInstructionKind::OpenExistentialValueInst: case SILInstructionKind::PartialApplyInst: case SILInstructionKind::ProjectValueBufferInst: case SILInstructionKind::StrongPinInst: case SILInstructionKind::YieldInst: case SILInstructionKind::UnwindInst: case SILInstructionKind::UncheckedOwnershipConversionInst: case SILInstructionKind::UncheckedRefCastAddrInst: case SILInstructionKind::UnconditionalCheckedCastAddrInst: case SILInstructionKind::UnconditionalCheckedCastValueInst: case SILInstructionKind::UnownedReleaseInst: case SILInstructionKind::UnownedRetainInst: case SILInstructionKind::ValueMetatypeInst: return; } } namespace { enum class OwnershipQualifiedKind { NotApplicable, Qualified, Unqualified, }; struct OwnershipQualifiedKindVisitor : SILInstructionVisitor { OwnershipQualifiedKind visitSILInstruction(SILInstruction *I) { return OwnershipQualifiedKind::NotApplicable; } #define QUALIFIED_INST(CLASS) \ OwnershipQualifiedKind visit ## CLASS(CLASS *I) { \ return OwnershipQualifiedKind::Qualified; \ } QUALIFIED_INST(EndBorrowInst) QUALIFIED_INST(LoadBorrowInst) QUALIFIED_INST(CopyValueInst) QUALIFIED_INST(CopyUnownedValueInst) QUALIFIED_INST(DestroyValueInst) #undef QUALIFIED_INST OwnershipQualifiedKind visitLoadInst(LoadInst *LI) { if (LI->getOwnershipQualifier() == LoadOwnershipQualifier::Unqualified) return OwnershipQualifiedKind::Unqualified; return OwnershipQualifiedKind::Qualified; } OwnershipQualifiedKind visitStoreInst(StoreInst *SI) { if (SI->getOwnershipQualifier() == StoreOwnershipQualifier::Unqualified) return OwnershipQualifiedKind::Unqualified; return OwnershipQualifiedKind::Qualified; } }; } // end anonymous namespace bool FunctionOwnershipEvaluator::evaluate(SILInstruction *I) { assert(I->getFunction() == F.get() && "Can not evaluate function ownership " "implications of an instruction that " "does not belong to the instruction " "that we are evaluating"); switch (OwnershipQualifiedKindVisitor().visit(I)) { case OwnershipQualifiedKind::Unqualified: { // If we already know that the function has unqualified ownership, just // return early. if (!F.get()->hasQualifiedOwnership()) return true; // Ok, so we know at this point that we have qualified ownership. If we have // seen any instructions with qualified ownership, we have an error since // the function mixes qualified and unqualified instructions. if (HasOwnershipQualifiedInstruction) return false; // Otherwise, set the function to have unqualified ownership. This will // ensure that no more Qualified instructions can be added to the given // function. F.get()->setUnqualifiedOwnership(); return true; } case OwnershipQualifiedKind::Qualified: { // First check if our function has unqualified ownership. If we already do // have unqualified ownership, then we know that we have already seen an // unqualified ownership instruction. This means the function has both // qualified and unqualified instructions. =><=. if (!F.get()->hasQualifiedOwnership()) return false; // Ok, at this point we know that we are still qualified. Since functions // start as qualified, we need to set the HasOwnershipQualifiedInstructions // so we do not need to look back through the function if we see an // unqualified instruction later on. HasOwnershipQualifiedInstruction = true; return true; } case OwnershipQualifiedKind::NotApplicable: { // Not Applicable instr return true; } } llvm_unreachable("Unhandled OwnershipQualifiedKind in switch."); }