mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
878 lines
29 KiB
C++
878 lines
29 KiB
C++
//===--- 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/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<SingleValueInstruction>(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<SILArgument>(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<BranchInst>(PredTI)) {
|
|
V = BI->getArg(A->getIndex());
|
|
continue;
|
|
}
|
|
|
|
if (auto *CBI = dyn_cast<CondBranchInst>(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<SingleValueInstruction>(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<SingleValueInstruction>(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<UpcastInst>(V))
|
|
V = stripSinglePredecessorArgs(upcast->getOperand());
|
|
|
|
return V;
|
|
}
|
|
|
|
SILValue swift::stripClassCasts(SILValue V) {
|
|
while (true) {
|
|
if (auto *UI = dyn_cast<UpcastInst>(V)) {
|
|
V = UI->getOperand();
|
|
continue;
|
|
}
|
|
|
|
if (auto *UCCI = dyn_cast<UnconditionalCheckedCastInst>(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:
|
|
return stripAddressAccess(cast<SingleValueInstruction>(V)->getOperand(0));
|
|
}
|
|
}
|
|
}
|
|
|
|
SILValue swift::stripAddressProjections(SILValue V) {
|
|
while (true) {
|
|
V = stripSinglePredecessorArgs(V);
|
|
if (!Projection::isAddressProjection(V))
|
|
return V;
|
|
V = cast<SingleValueInstruction>(V)->getOperand(0);
|
|
}
|
|
}
|
|
|
|
SILValue swift::stripUnaryAddressProjections(SILValue V) {
|
|
while (true) {
|
|
V = stripSinglePredecessorArgs(V);
|
|
if (!Projection::isAddressProjection(V))
|
|
return V;
|
|
auto *Inst = cast<SingleValueInstruction>(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<SingleValueInstruction>(V)->getOperand(0);
|
|
}
|
|
}
|
|
|
|
SILValue swift::stripIndexingInsts(SILValue V) {
|
|
while (true) {
|
|
if (!isa<IndexingInst>(V))
|
|
return V;
|
|
V = cast<IndexingInst>(V)->getBase();
|
|
}
|
|
}
|
|
|
|
SILValue swift::stripExpectIntrinsic(SILValue V) {
|
|
auto *BI = dyn_cast<BuiltinInst>(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<BeginBorrowInst>(V))
|
|
return BBI->getOperand();
|
|
return V;
|
|
}
|
|
|
|
SingleValueInstruction *swift::getSingleValueCopyOrCast(SILInstruction *I) {
|
|
if (auto *convert = dyn_cast<ConversionInst>(I))
|
|
return convert;
|
|
|
|
switch (I->getKind()) {
|
|
default:
|
|
return nullptr;
|
|
case SILInstructionKind::CopyValueInst:
|
|
case SILInstructionKind::CopyBlockInst:
|
|
case SILInstructionKind::BeginBorrowInst:
|
|
case SILInstructionKind::BeginAccessInst:
|
|
return cast<SingleValueInstruction>(I);
|
|
}
|
|
}
|
|
|
|
bool swift::isIncidentalUse(SILInstruction *user) {
|
|
switch (user->getKind()) {
|
|
default:
|
|
return false;
|
|
case SILInstructionKind::DebugValueInst:
|
|
case SILInstructionKind::EndAccessInst:
|
|
case SILInstructionKind::EndBorrowInst:
|
|
case SILInstructionKind::EndLifetimeInst:
|
|
case SILInstructionKind::FixLifetimeInst:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
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<ConvertFunctionInst>(V)) {
|
|
V = CFI->getOperand();
|
|
continue;
|
|
}
|
|
else if (auto *Cvt = dyn_cast<ConvertEscapeToNoEscapeInst>(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<AllocStackInst>(address)->getDecl();
|
|
return decl && decl->isLet();
|
|
}
|
|
case ValueKind::AllocBoxInst: {
|
|
VarDecl *decl = cast<AllocBoxInst>(address)->getDecl();
|
|
return decl && decl->isLet();
|
|
}
|
|
case ValueKind::RefElementAddrInst: {
|
|
VarDecl *decl = cast<RefElementAddrInst>(address)->getField();
|
|
return decl && decl->isLet();
|
|
}
|
|
case ValueKind::GlobalAddrInst: {
|
|
SILGlobalVariable *global =
|
|
cast<GlobalAddrInst>(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<SwitchEnumInst>(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<SILPHIArgument>(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<SILBoxType>());
|
|
address = cast<LoadInst>(address)->getOperand();
|
|
assert(isa<UncheckedTakeEnumDataAddrInst>(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<SingleValueInstruction>(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<SingleValueInstruction>(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<BeginAccessInst>(baseAddress))
|
|
baseAddress = beginAccess->getOperand();
|
|
|
|
// Function arguments are accessed by the caller.
|
|
if (isa<SILFunctionArgument>(baseAddress))
|
|
return false;
|
|
|
|
if (isa<SILPHIArgument>(baseAddress)) {
|
|
checkSwitchEnumBlockArg(cast<SILPHIArgument>(baseAddress));
|
|
return false;
|
|
}
|
|
|
|
// Pointer-to-address exclusivity cannot be enforced. `baseAddress` may be
|
|
// pointing anywhere within an object.
|
|
if (isa<PointerToAddressInst>(baseAddress))
|
|
return false;
|
|
|
|
// Immutable values are only accessed for initialization.
|
|
if (isLetAccess(baseAddress))
|
|
return false;
|
|
|
|
// Special case unsafe value buffer access.
|
|
if (isa<BuiltinUnsafeValueBufferType>(
|
|
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<SILFunctionType>() ||
|
|
(!Arg->getType().isReferenceCounted(PAI->getFunction()->getModule()) &&
|
|
Arg->getType().getAs<SILFunctionType>()->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<SILFunctionType>();
|
|
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<CopyBlockInst>(V)) {
|
|
V = CBI->getOperand();
|
|
continue;
|
|
}
|
|
|
|
IBSHI = dyn_cast<InitBlockStorageHeaderInst>(V);
|
|
break;
|
|
}
|
|
|
|
if (!IBSHI)
|
|
return nullptr;
|
|
|
|
SILValue BlockStorage = IBSHI->getBlockStorage();
|
|
auto *PBSI = BlockStorage->getSingleUserOfType<ProjectBlockStorageInst>();
|
|
assert(PBSI && "Couldn't find block storage projection");
|
|
|
|
auto *SI = PBSI->getSingleUserOfType<StoreInst>();
|
|
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<BeginBorrowInst>(V))
|
|
V = bbi->getOperand();
|
|
|
|
if (V->getType().getOptionalObjectType())
|
|
V = cast<EnumInst>(V)->getOperand();
|
|
|
|
auto fnType = V->getType().getAs<SILFunctionType>();
|
|
if (fnType->getRepresentation() == SILFunctionTypeRepresentation::Block) {
|
|
V = findClosureStoredIntoBlock(V);
|
|
if (!V)
|
|
return FindClosureResult(nullptr, false);
|
|
}
|
|
auto *PAI = dyn_cast<PartialApplyInst>(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<void(Operand *)> 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<SILFunctionType>();
|
|
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<void(Operand *)> visitor) {
|
|
assert(I->mayReadOrWriteMemory());
|
|
|
|
// Reference counting instructions do not access user visible memory.
|
|
if (isa<RefCountingInst>(I))
|
|
return;
|
|
|
|
if (isa<DeallocationInst>(I))
|
|
return;
|
|
|
|
if (auto apply = FullApplySite::isa(I)) {
|
|
visitApplyAccesses(apply, visitor);
|
|
return;
|
|
}
|
|
|
|
if (auto builtin = dyn_cast<BuiltinInst>(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<OwnershipQualifiedKindVisitor, OwnershipQualifiedKind> {
|
|
|
|
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.");
|
|
}
|