//===--- MemAccessUtils.h - Utilities for SIL memory access. ----*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2018 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
//
//===----------------------------------------------------------------------===//
///
/// These utilities model formal memory access locations as marked by
/// begin_access and end_access instructions. The formal memory locations
/// identified here must be consistent with language rules for exclusity
/// enforcement. This is not meant to be a utility to reason about other general
/// properties of SIL memory operations such as reference count identity,
/// ownership, or aliasing. Code that queries the properties of arbitrary memory
/// operations independent of begin_access instructions should use a different
/// interface.
///
/// SIL memory addresses used for formal access need to meet special
/// requirements. In particular, it must be possible to identifiy the storage by
/// following the pointer's provenance. This is *not* true for SIL memory
/// operations in general. The utilities cannot simply bailout on unrecognized
/// patterns. Doing so would lead to undefined program behavior, which isn't
/// something that can be directly tested (i.e. if this breaks, we won't see
/// test failures).
///
/// These utilities are mainly meant to be used by AccessEnforcement passes,
/// which optimize exclusivity enforcement. They live in SIL so they can be used
/// by SIL verification.
///
//===----------------------------------------------------------------------===//

#ifndef SWIFT_SIL_MEMACCESSUTILS_H
#define SWIFT_SIL_MEMACCESSUTILS_H

#include "swift/SIL/ApplySite.h"
#include "swift/SIL/InstructionUtils.h"
#include "swift/SIL/SILArgument.h"
#include "swift/SIL/SILBasicBlock.h"
#include "swift/SIL/SILGlobalVariable.h"
#include "swift/SIL/SILInstruction.h"
#include "llvm/ADT/DenseMap.h"

namespace swift {

/// Get the base address of a formal access by stripping access markers and
/// borrows.
///
/// If \p v is an address, then the returned value is also an address
/// (pointer-to-address is not stripped).
SILValue stripAccessMarkers(SILValue v);

/// Return a non-null address-type SingleValueInstruction if \p v is the result
/// of an address projection that may be inside of a formal access, such as
/// (begin_borrow, struct_element_addr, tuple_element_addr).
///
/// The resulting projection must have an address-type operand at index zero
/// representing the projected address.
SingleValueInstruction *isAccessProjection(SILValue v);

/// Attempt to return the address corresponding to a variable's formal access
/// by stripping indexing and address projections.
///
/// \p v must be an address.
///
/// Returns an address. If the a formal access was successfully identified, and
/// access markers have not yet been removed, then the returned address is
/// produced by a begin_access marker.
///
/// To get the base address of the formal access behind the access marker,
/// either call stripAccessMarkers() on the returned value, or call
/// getAccessedAddress() on \p v.
///
/// To identify the underlying storage object of the access, use
/// findAccessedStorage() on either \p v or the returned address.
SILValue getAddressAccess(SILValue v);

/// Convenience for stripAccessMarkers(getAddressAccess(v)).
SILValue getAccessedAddress(SILValue v);

/// Return true if \p accessedAddress points to a let-variable.
///
/// Precondition: \p accessedAddress must be an address-type value representing
/// the base of a formal access (not a projection within the access).
///
/// let-variables are only written during let-variable initialization, which is
/// assumed to store directly to the same, unaliased accessedAddress.
///
/// The address of a let-variable must be the base of a formal access . A 'let'
/// member of a struct is *not* a let-variable, because it's memory may be
/// written when formally modifying the outer struct. A let-variable is either
/// an entire local variable, global variable, or class property (this is the
/// definition of the base address of a formal access).
///
/// The caller should derive the accessed address using
/// stripAccessMarkers(getAccessedAddress(ptr)).
bool isLetAddress(SILValue accessedAddress);

inline bool accessKindMayConflict(SILAccessKind a, SILAccessKind b) {
  return !(a == SILAccessKind::Read && b == SILAccessKind::Read);
}

/// Represents the identity of a storage object being accessed.
///
/// AccessedStorage is carefully designed to solve three problems:
///
/// 1. Full specification and verification of SIL's model for exclusive
///    formal memory access, as enforced by "access markers". It is not a
///    model to encompass all SIL memory operations.
///
/// 2. A bitwise comparable encoding and hash key to identify each location
///    being formally accessed. Any two accesses of uniquely identified storage
///    must have the same key if they access the same storage and distinct keys
///    if they access distinct storage. Accesses to non-uniquely identified
///    storage should ideally have the same key if they may point to the same
///    storage.
///
/// 3. Complete identification of all class or global accesses. Failing to
///    identify a class or global access will introduce undefined program
///    behavior which can't be tested.
///
/// AccessedStorage may be one of several kinds of "identified" storage
/// objects, or may be valid, but Unidentified storage. An identified object
/// is known to identify the base of the accessed storage, whether that is a
/// SILValue that produces the base address, or a variable
/// declaration. "Uniquely identified" storage refers to identified storage that
/// cannot be aliased. For example, local allocations are uniquely identified,
/// while global variables and class properties are not. Unidentified storage is
/// associated with a SILValue that produces the accessed address but has not
/// been determined to be the base of a storage object. It may, for example,
/// be a SILPhiArgument.
///
/// An invalid AccessedStorage object is marked Unidentified and contains an
/// invalid value. This signals that analysis has failed to recognize an
/// expected address producer pattern. Over time, more aggressive
/// SILVerification could allow the optimizer to aggressively assert that
/// AccessedStorage is always valid.
///
/// Note that the SILValue that represents a storage object is not
/// necessarilly an address type. It may instead be a SILBoxType.
///
/// AccessedStorage hashing and comparison (via DenseMapInfo) is used to
/// determine when two 'begin_access' instructions access the same or disjoint
/// underlying objects.
///
/// `DenseMapInfo::isEqual()` guarantees that two AccessStorage values refer to
/// the same memory if both values are valid.
///
/// `!DenseMapInfo::isEqual()` does not guarantee that two identified
/// AccessStorage values are distinct. Inequality does, however, guarantee that
/// two *uniquely* identified AccessStorage values are distinct.
class AccessedStorage {
public:
  /// Enumerate over all valid begin_access bases. Clients can use a covered
  /// switch to warn if AccessedStorage ever adds a case.
  enum Kind : uint8_t {
    Box,
    Stack,
    Global,
    Class,
    Argument,
    Yield,
    Nested,
    Unidentified,
    NumKindBits = countBitsUsed(static_cast<unsigned>(Unidentified))
  };

  static const char *getKindName(Kind k);

  /// Directly create an AccessedStorage for class property access.
  static AccessedStorage forClass(SILValue object, unsigned propertyIndex) {
    AccessedStorage storage;
    storage.initKind(Class, propertyIndex);
    storage.value = object;
    return storage;
  }

protected:
  // Checking the storage kind is far more common than other fields. Make sure
  // it can be byte load with no shift.
  static const int ReservedKindBits = 8;
  static_assert(ReservedKindBits >= NumKindBits, "Too many storage kinds.");

  static const unsigned InvalidElementIndex =
      (1 << (32 - ReservedKindBits)) - 1;

  // Form a bitfield that is effectively a union over any pass-specific data
  // with the fields used within this class as a common prefix.
  //
  // This allows passes to embed analysis flags, and reserves enough space to
  // embed a unique index.
  //
  // AccessedStorageAnalysis defines an StorageAccessInfo object that maps each
  // storage object within a function to its unique storage index and summary
  // information of that storage object.
  //
  // AccessEnforcementOpts defines an AccessEnforcementOptsInfo object that maps
  // each begin_access to its storage object, unique access index, and summary
  // info for that access.
  union {
    uint64_t opaqueBits;
    // elementIndex can overflow while gracefully degrading analysis. For now,
    // reserve an absurd number of bits at a nice alignment boundary, but this
    // can be reduced.
    SWIFT_INLINE_BITFIELD_BASE(AccessedStorage, 32, kind
                               : ReservedKindBits,
                                 elementIndex : 32 - ReservedKindBits);

    // Define bits for use in AccessedStorageAnalysis. Each identified storage
    // object is mapped to one instance of this subclass.
    SWIFT_INLINE_BITFIELD_FULL(StorageAccessInfo, AccessedStorage,
                               64 - NumAccessedStorageBits,
                               accessKind : NumSILAccessKindBits,
                               noNestedConflict : 1,
                               storageIndex : 64 - (NumAccessedStorageBits
                                                    + NumSILAccessKindBits
                                                    + 1));

    // Define bits for use in the AccessEnforcementOpts pass. Each begin_access
    // in the function is mapped to one instance of this subclass.  Reserve a
    // bit for a seenNestedConflict flag, which is the per-begin-access result
    // of pass-specific analysis. The remaning bits are sufficient to index all
    // begin_[unpaired_]access instructions.
    //
    // `AccessedStorage` refers to the AccessedStorageBitfield defined above,
    // setting aside enough bits for common data.
    SWIFT_INLINE_BITFIELD_FULL(AccessEnforcementOptsInfo, AccessedStorage,
                               64 - NumAccessedStorageBits,
                               seenNestedConflict : 1,
                               seenIdenticalStorage : 1,
                               beginAccessIndex : 62 - NumAccessedStorageBits);

    // Define data flow bits for use in the AccessEnforcementDom pass. Each
    // begin_access in the function is mapped to one instance of this subclass.
    SWIFT_INLINE_BITFIELD(DomAccessedStorage, AccessedStorage, 1 + 1,
                          isInner : 1, containsRead : 1);
  } Bits;

private:
  union {
    // For non-class storage, 'value' is the access base. For class storage
    // 'value' is the object base, where the access base is the class' stored
    // property.
    SILValue value;
    SILGlobalVariable *global;
  };

  void initKind(Kind k, unsigned elementIndex = InvalidElementIndex) {
    Bits.opaqueBits = 0;
    Bits.AccessedStorage.kind = k;
    Bits.AccessedStorage.elementIndex = elementIndex;
  }

  unsigned getElementIndex() const { return Bits.AccessedStorage.elementIndex; }
  void setElementIndex(unsigned elementIndex) {
    Bits.AccessedStorage.elementIndex = elementIndex;
  }

public:
  AccessedStorage() : value() { initKind(Unidentified); }

  AccessedStorage(SILValue base, Kind kind);

  // Return true if this is a valid storage object.
  operator bool() const { return getKind() != Unidentified || value; }

  Kind getKind() const { return static_cast<Kind>(Bits.AccessedStorage.kind); }

  // Clear any bits reserved for subclass data. Useful for up-casting back to
  // the base class.
  void resetSubclassData() {
    initKind(getKind(), Bits.AccessedStorage.elementIndex);
  }

  SILValue getValue() const {
    assert(getKind() != Global && getKind() != Class);
    return value;
  }

  unsigned getParamIndex() const {
    assert(getKind() == Argument);
    return getElementIndex();
  }

  SILArgument *getArgument() const {
    assert(getKind() == Argument);
    return cast<SILArgument>(value);
  }

  SILGlobalVariable *getGlobal() const {
    assert(getKind() == Global);
    return global;
  }

  SILValue getObject() const {
    assert(getKind() == Class);
    return value;
  }
  unsigned getPropertyIndex() const {
    assert(getKind() == Class);
    return getElementIndex();
  }

  /// Return true if the given storage objects have identical storage locations.
  ///
  /// This compares only the AccessedStorage base class bits, ignoring the
  /// subclass bits. It is used for hash lookup equality, so it should not
  /// perform any additional lookups or dereference memory outside itself.
  bool hasIdenticalBase(const AccessedStorage &other) const {
    if (getKind() != other.getKind())
      return false;

    switch (getKind()) {
    case Box:
    case Stack:
    case Argument:
    case Yield:
    case Nested:
    case Unidentified:
      return value == other.value;
    case Global:
      return global == other.global;
    case Class:
      return value == other.value
             && getElementIndex() == other.getElementIndex();
    }
    llvm_unreachable("covered switch");
  }

  /// Return true if the storage is guaranteed local.
  bool isLocal() const {
    switch (getKind()) {
    case Box:
    case Stack:
      return true;
    case Global:
    case Class:
    case Argument:
    case Yield:
    case Nested:
    case Unidentified:
      return false;
    }
    llvm_unreachable("unhandled kind");
  }

  bool isLetAccess(SILFunction *F) const;
  
  bool isUniquelyIdentified() const {
    switch (getKind()) {
    case Box:
    case Stack:
    case Global:
      return true;
    case Class:
    case Argument:
    case Yield:
    case Nested:
    case Unidentified:
      return false;
    }
    llvm_unreachable("unhandled kind");
  }

  bool isUniquelyIdentifiedOrClass() const {
    if (isUniquelyIdentified())
      return true;
    return (getKind() == Class);
  }

  bool isDistinctFrom(const AccessedStorage &other) const {
    if (isUniquelyIdentified() && other.isUniquelyIdentified()) {
      return !hasIdenticalBase(other);
    }
    if (getKind() != Class || other.getKind() != Class)
      // At least one side is an Argument or Yield, or is unidentified.
      return false;

    // Classes are not uniquely identified by their base. However, if the
    // underling objects have identical types and distinct property indices then
    // they are distinct storage locations.
    if (getObject()->getType() == other.getObject()->getType()
        && getPropertyIndex() != other.getPropertyIndex()) {
      return true;
    }
    return false;
  }

  /// Returns the ValueDecl for the underlying storage, if it can be
  /// determined. Otherwise returns null.
  ///
  /// WARNING: This is not a constant-time operation. It is for diagnostics and
  /// checking via the ValueDecl if we are processing a `let` variable.
  const ValueDecl *getDecl() const;

  void print(raw_ostream &os) const;
  void dump() const;

private:
  // Disable direct comparison because we allow subclassing with bitfields.
  // Currently, we use DenseMapInfo to unique storage, which defines key
  // equalilty only in terms of the base AccessedStorage class bits.
  bool operator==(const AccessedStorage &) const = delete;
  bool operator!=(const AccessedStorage &) const = delete;
};
} // end namespace swift

namespace llvm {

/// Enable using AccessedStorage as a key in DenseMap.
/// Do *not* include any extra pass data in key equality.
template <> struct DenseMapInfo<swift::AccessedStorage> {
  static swift::AccessedStorage getEmptyKey() {
    return swift::AccessedStorage(swift::SILValue::getFromOpaqueValue(
                                    llvm::DenseMapInfo<void *>::getEmptyKey()),
                                  swift::AccessedStorage::Unidentified);
  }

  static swift::AccessedStorage getTombstoneKey() {
    return swift::AccessedStorage(
      swift::SILValue::getFromOpaqueValue(
        llvm::DenseMapInfo<void *>::getTombstoneKey()),
      swift::AccessedStorage::Unidentified);
  }

  static unsigned getHashValue(swift::AccessedStorage storage) {
    switch (storage.getKind()) {
    case swift::AccessedStorage::Box:
    case swift::AccessedStorage::Stack:
    case swift::AccessedStorage::Nested:
    case swift::AccessedStorage::Yield:
    case swift::AccessedStorage::Unidentified:
      return DenseMapInfo<swift::SILValue>::getHashValue(storage.getValue());
    case swift::AccessedStorage::Argument:
      return storage.getParamIndex();
    case swift::AccessedStorage::Global:
      return DenseMapInfo<void *>::getHashValue(storage.getGlobal());
    case swift::AccessedStorage::Class: {
      return llvm::hash_combine(storage.getObject(),
                                storage.getPropertyIndex());
    }
    }
    llvm_unreachable("Unhandled AccessedStorageKind");
  }

  static bool isEqual(swift::AccessedStorage LHS, swift::AccessedStorage RHS) {
    return LHS.hasIdenticalBase(RHS);
  }
};

} // end namespace llvm

namespace swift {

/// Abstract CRTP class for a visitor passed to \c visitAccessUseDefChain.
template<typename Impl, typename Result = void>
class AccessUseDefChainVisitor {
protected:
  Impl &asImpl() {
    return static_cast<Impl&>(*this);
  }
public:
  // Subclasses can provide a method for any identified access base:
  // Result visitBase(SILValue base, AccessedStorage::Kind kind);
  
  // Visitors for specific identified access kinds. These default to calling out
  // to visitIdentified.
  
  Result visitClassAccess(RefElementAddrInst *field) {
    return asImpl().visitBase(field, AccessedStorage::Class);
  }
  Result visitArgumentAccess(SILFunctionArgument *arg) {
    return asImpl().visitBase(arg, AccessedStorage::Argument);
  }
  Result visitBoxAccess(AllocBoxInst *box) {
    return asImpl().visitBase(box, AccessedStorage::Box);
  }
  /// The argument may be either a GlobalAddrInst or the ApplyInst for a global accessor function.
  Result visitGlobalAccess(SILValue global) {
    return asImpl().visitBase(global, AccessedStorage::Global);
  }
  Result visitYieldAccess(BeginApplyResult *yield) {
    return asImpl().visitBase(yield, AccessedStorage::Yield);
  }
  Result visitStackAccess(AllocStackInst *stack) {
    return asImpl().visitBase(stack, AccessedStorage::Stack);
  }
  Result visitNestedAccess(BeginAccessInst *access) {
    return asImpl().visitBase(access, AccessedStorage::Nested);
  }
  
  // Visitors for unidentified base values.
  
  Result visitUnidentified(SILValue base) {
    return asImpl().visitBase(base, AccessedStorage::Unidentified);
  }
  
  // Subclasses must provide implementations to visit non-access bases
  // and phi arguments, and for incomplete projections from the access:
  // void visitNonAccess(SILValue base);
  // void visitPhi(SILPhiArgument *phi);
  // void visitIncomplete(SILValue projectedAddr, SILValue parentAddr);
  
  Result visit(SILValue sourceAddr);
};

/// Given an address accessed by an instruction that reads or modifies
/// memory, return an AccessedStorage object that identifies the formal access.
///
/// The returned AccessedStorage represents the best attempt to find the base of
/// the storage object being accessed at `sourceAddr`. This may be a fully
/// identified storage base of known kind, or a valid but Unidentified storage
/// object, such as a SILPhiArgument.
///
/// This may return an invalid storage object if the address producer is not
/// recognized by a whitelist of recognizable access patterns. The result must
/// always be valid when `sourceAddr` is used for formal memory access, i.e. as
/// the operand of begin_access.
///
/// If `sourceAddr` is produced by a begin_access, this returns a Nested
/// AccessedStorage kind. This is useful for exclusivity checking to distinguish
/// between a nested access vs. a conflict.
AccessedStorage findAccessedStorage(SILValue sourceAddr);

/// Given an address accessed by an instruction that reads or modifies
/// memory, return an AccessedStorage that identifies the formal access, looking
/// through any Nested access to find the original storage.
///
/// This is identical to findAccessedStorage(), but never returns Nested
/// storage and may return invalid storage for nested access when the outer
/// access has Unsafe enforcement.
AccessedStorage findAccessedStorageNonNested(SILValue sourceAddr);

/// Return true if the given address operand is used by a memory operation that
/// initializes the memory at that address, implying that the previous value is
/// uninitialized.
bool memInstMustInitialize(Operand *memOper);

/// Is this an alloc_stack instruction that is:
///
/// 1. Only initialized once in its own def block.
/// 2. Never written to again except by destroy_addr.
///
/// On return, destroyingUsers contains the list of users that destroy the
/// alloc_stack. If the alloc_stack is destroyed in pieces, we do not guarantee
/// that the list of destroying users is a minimal jointly post-dominating set.
bool isSingleInitAllocStack(AllocStackInst *asi,
                            SmallVectorImpl<Operand *> &destroyingUses);

/// Return true if the given address producer may be the source of a formal
/// access (a read or write of a potentially aliased, user visible variable).
///
/// `storage` must be a valid AccessedStorage object.
///
/// If this returns false, then the address can be safely accessed without
/// a begin_access marker. To determine whether to emit begin_access:
///   storage = findAccessedStorage(address)
///   needsAccessMarker = storage && isPossibleFormalAccessBase(storage)
///
/// Warning: This is only valid for SIL with well-formed accessed. For example,
/// it will not handle address-type phis. Optimization passes after
/// DiagnoseStaticExclusivity may violate these assumptions.
bool isPossibleFormalAccessBase(const AccessedStorage &storage, SILFunction *F);

/// Visit each address accessed by the given memory operation.
///
/// This only visits instructions that modify memory in some user-visible way,
/// which could be considered part of a formal access.
void visitAccessedAddress(SILInstruction *I,
                          llvm::function_ref<void(Operand *)> visitor);

/// Perform a RAUW operation on begin_access with it's own source operand.
/// Then erase the begin_access and all associated end_access instructions.
/// Return an iterator to the following instruction.
///
/// The caller should use this iterator rather than assuming that the
/// instruction following this begin_access was not also erased.
SILBasicBlock::iterator removeBeginAccess(BeginAccessInst *beginAccess);

/// Return true if the given address value is produced by a special address
/// producer that is only used for local initialization, not formal access.
bool isAddressForLocalInitOnly(SILValue sourceAddr);
/// Return true if the given apply invokes a global addressor defined in another
/// module.
bool isExternalGlobalAddressor(ApplyInst *AI);
/// Return true if the given StructExtractInst extracts the RawPointer from
/// Unsafe[Mutable]Pointer.
bool isUnsafePointerExtraction(StructExtractInst *SEI);
/// Given a block argument address base, check if it is actually a box projected
/// from a switch_enum. This is a valid pattern at any SIL stage resulting in a
/// block-type phi. In later SIL stages, the optimizer may form address-type
/// phis, causing this assert if called on those cases.
void checkSwitchEnumBlockArg(SILPhiArgument *arg);

template<typename Impl, typename Result>
Result AccessUseDefChainVisitor<Impl, Result>::visit(SILValue sourceAddr) {
  // Handle immediately-identifiable instructions.
  while (true) {
    switch (sourceAddr->getKind()) {
    // An AllocBox is a fully identified memory location.
    case ValueKind::AllocBoxInst:
      return asImpl().visitBoxAccess(cast<AllocBoxInst>(sourceAddr));
    // An AllocStack is a fully identified memory location, which may occur
    // after inlining code already subjected to stack promotion.
    case ValueKind::AllocStackInst:
      return asImpl().visitStackAccess(cast<AllocStackInst>(sourceAddr));
    case ValueKind::GlobalAddrInst:
      return asImpl().visitGlobalAccess(sourceAddr);
    case ValueKind::ApplyInst: {
      FullApplySite apply(cast<ApplyInst>(sourceAddr));
      if (auto *funcRef = apply.getReferencedFunctionOrNull()) {
        if (getVariableOfGlobalInit(funcRef)) {
          return asImpl().visitGlobalAccess(sourceAddr);
        }
      }
      // Try to classify further below.
      break;
    }
    case ValueKind::RefElementAddrInst:
      return asImpl().visitClassAccess(cast<RefElementAddrInst>(sourceAddr));
    // A yield is effectively a nested access, enforced independently in
    // the caller and callee.
    case ValueKind::BeginApplyResult:
      return asImpl().visitYieldAccess(cast<BeginApplyResult>(sourceAddr));
    // A function argument is effectively a nested access, enforced
    // independently in the caller and callee.
    case ValueKind::SILFunctionArgument:
      return asImpl().visitArgumentAccess(cast<SILFunctionArgument>(sourceAddr));
    // View the outer begin_access as a separate location because nested
    // accesses do not conflict with each other.
    case ValueKind::BeginAccessInst:
      return asImpl().visitNestedAccess(cast<BeginAccessInst>(sourceAddr));
    default:
      // Try to classify further below.
      break;
    }
        
    // If the sourceAddr producer cannot immediately be classified, follow the
    // use-def chain of sourceAddr, box, or RawPointer producers.
    assert(sourceAddr->getType().isAddress()
           || isa<SILBoxType>(sourceAddr->getType().getASTType())
           || isa<BuiltinRawPointerType>(sourceAddr->getType().getASTType()));

    // Handle other unidentified address sources.
    switch (sourceAddr->getKind()) {
    default:
      if (isAddressForLocalInitOnly(sourceAddr))
        return asImpl().visitUnidentified(sourceAddr);
      return asImpl().visitNonAccess(sourceAddr);

    case ValueKind::SILUndef:
      return asImpl().visitUnidentified(sourceAddr);

    case ValueKind::ApplyInst:
      if (isExternalGlobalAddressor(cast<ApplyInst>(sourceAddr)))
        return asImpl().visitUnidentified(sourceAddr);

      // Don't currently allow any other calls to return an accessed address.
      return asImpl().visitNonAccess(sourceAddr);

    case ValueKind::StructExtractInst:
      // Handle nested access to a KeyPath projection. The projection itself
      // uses a Builtin. However, the returned UnsafeMutablePointer may be
      // converted to an address and accessed via an inout argument.
      if (isUnsafePointerExtraction(cast<StructExtractInst>(sourceAddr)))
        return asImpl().visitUnidentified(sourceAddr);
      return asImpl().visitNonAccess(sourceAddr);

    case ValueKind::SILPhiArgument: {
      auto *phiArg = cast<SILPhiArgument>(sourceAddr);
      if (phiArg->isPhiArgument()) {
        return asImpl().visitPhi(phiArg);
      }

      // A non-phi block argument may be a box value projected out of
      // switch_enum. Address-type block arguments are not allowed.
      if (sourceAddr->getType().isAddress())
        return asImpl().visitNonAccess(sourceAddr);

      checkSwitchEnumBlockArg(cast<SILPhiArgument>(sourceAddr));
      return asImpl().visitUnidentified(sourceAddr);
    }
    // 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.
    //
    // Otherwise return invalid AccessedStorage.
    case ValueKind::LoadInst:
      if (sourceAddr->getType().is<SILBoxType>()) {
        SILValue operAddr = cast<LoadInst>(sourceAddr)->getOperand();
        assert(isa<UncheckedTakeEnumDataAddrInst>(operAddr));
        return asImpl().visitIncomplete(sourceAddr, operAddr);
      }
      return asImpl().visitNonAccess(sourceAddr);

    // ref_tail_addr project an address from a reference.
    // This is a valid address producer for nested @inout argument
    // access, but it is never used for formal access of identified objects.
    case ValueKind::RefTailAddrInst:
      return asImpl().visitUnidentified(sourceAddr);

    // Inductive single-operand 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:
      return asImpl().visitIncomplete(sourceAddr,
        cast<SingleValueInstruction>(sourceAddr)->getOperand(0));

    // Access to a Builtin.RawPointer. Treat this like the inductive cases
    // above because some RawPointers originate from identified locations. See
    // the special case for global addressors, which return RawPointer,
    // above. AddressToPointer is also handled because it results from inlining a
    // global addressor without folding the AddressToPointer->PointerToAddress.
    //
    // If the inductive search does not find a valid addressor, it will
    // eventually reach the default case that returns in invalid location. This
    // is correct for RawPointer because, although accessing a RawPointer is
    // legal SIL, there is no way to guarantee that it doesn't access class or
    // global storage, so returning a valid unidentified storage object would be
    // incorrect. It is the caller's responsibility to know that formal access
    // to such a location can be safely ignored.
    //
    // For example:
    //
    // - KeyPath Builtins access RawPointer. However, the caller can check
    // that the access `isFromBuilin` and ignore the storage.
    //
    // - lldb generates RawPointer access for debugger variables, but SILGen
    // marks debug VarDecl access as 'Unsafe' and SIL passes don't need the
    // AccessedStorage for 'Unsafe' access.
    case ValueKind::PointerToAddressInst:
    case ValueKind::AddressToPointerInst:
      return asImpl().visitIncomplete(sourceAddr,
        cast<SingleValueInstruction>(sourceAddr)->getOperand(0));

    // Address-to-address subobject projections.
    case ValueKind::StructElementAddrInst:
    case ValueKind::TupleElementAddrInst:
    case ValueKind::UncheckedTakeEnumDataAddrInst:
    case ValueKind::TailAddrInst:
    case ValueKind::IndexAddrInst:
      return asImpl().visitIncomplete(sourceAddr,
        cast<SingleValueInstruction>(sourceAddr)->getOperand(0));
    }
  };
}

} // end namespace swift

#endif
