//===- PMOMemoryUseCollector.h - Memory use information for PMO -*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
///
/// \file
///
/// High Level Overview
/// -------------------
///
/// This file declares logic that is used by the predictable memory optimization
/// pass. Like definite initialization, it is tuple element sensitive instead of
/// relying on an SROA optimization or the like to chop up structs.
///
/// NOTE: PMO is an abbreviation for "Predictable Memory Optimizations".
///
/// Historical Note
/// ---------------
///
/// This file looks very similar to DIMemoryUseCollector.* and has many
/// connections to Definite Initialization. This is because early in the
/// development of Swift, Predictable Memory Optimizations and Definite
/// Initialization were actually the same pass. The pass grew really big and the
/// two passes were split, but still used similra utility code. This became the
/// original DIMemoryUseCollector.*. This code was full of conditional logic for
/// all of the various cases that made it difficult to understand which code was
/// needed for Predictable Mem Opts and what was needed for DI. The introduction
/// of the SIL ownership model to SIL was used as an opportunity to split the
/// two, flatten the sphagetti conditional code so the logic was clear, and
/// allow the two passes to diverge and hew their form closer to their function.
///
//===----------------------------------------------------------------------===//

#ifndef SWIFT_SILOPTIMIZER_MANDATORY_PMOMEMORYUSECOLLECTOR_H
#define SWIFT_SILOPTIMIZER_MANDATORY_PMOMEMORYUSECOLLECTOR_H

#include "swift/Basic/LLVM.h"
#include "swift/SIL/SILInstruction.h"
#include "swift/SIL/SILType.h"
#include "llvm/ADT/APInt.h"
#include "llvm/Support/Compiler.h"

namespace swift {

class SILBuilder;

/// PMOMemoryObjectInfo - This struct holds information about the memory object
/// being analyzed that is required to correctly break it down into elements.
///
/// This includes a collection of utilities for reasoning about (potentially
/// recursively) exploded aggregate elements, and computing access paths and
/// indexes into the flattened namespace.
///
/// The flattened namespace is assigned lexicographically.  For example, in:
///   (Int, ((Float, (), Double)))
/// the Int member is numbered 0, the Float is numbered 1, and the Double is
/// numbered 2.  Empty tuples don't get numbered since they contain no state.
///
/// Structs and classes have their elements exploded when we are analyzing the
/// 'self' member in an initializer for the aggregate.
///
/// Derived classes have an additional field at the end that models whether or
/// not super.init() has been called or not.
class PMOMemoryObjectInfo {
public:
  /// This is the instruction that represents the memory. It is either an
  /// alloc_box or alloc_stack.
  AllocationInst *MemoryInst;

  /// This is the base type of the memory allocation.
  SILType MemorySILType;

public:
  PMOMemoryObjectInfo(AllocationInst *MemoryInst);

  SILLocation getLoc() const { return MemoryInst->getLoc(); }
  SILFunction &getFunction() const { return *MemoryInst->getFunction(); }

  /// Return the first instruction of the function containing the memory object.
  SILInstruction *getFunctionEntryPoint() const;

  CanType getType() const { return MemorySILType.getASTType(); }

  SingleValueInstruction *getAddress() const {
    if (isa<AllocStackInst>(MemoryInst))
      return MemoryInst;
    assert(false);
    return nullptr;
  }

  AllocBoxInst *getContainer() const {
    return dyn_cast<AllocBoxInst>(MemoryInst);
  }
};

enum PMOUseKind {
  /// The instruction is a Load.
  Load,

  /// The instruction is either an initialization or an assignment, we don't
  /// know which.  This classification only happens with values of trivial type
  /// where the different isn't significant.
  InitOrAssign,

  /// The instruction is an initialization of the tuple element.
  Initialization,

  /// The instruction is an assignment, overwriting an already initialized
  /// value.
  Assign,

  /// An indirect 'inout' parameter of an Apply instruction.
  InOutUse,

  /// An indirect 'in' parameter of an Apply instruction.
  IndirectIn,

  /// This instruction is a general escape of the value, e.g. a call to a
  /// closure that captures it.
  Escape,
};

/// This struct represents a single classified access to the memory object
/// being analyzed, along with classification information about the access.
struct PMOMemoryUse {
  /// This is the instruction accessing the memory.
  SILInstruction *Inst;

  /// This is what kind of access it is, load, store, escape, etc.
  PMOUseKind Kind;

  PMOMemoryUse(SILInstruction *Inst, PMOUseKind Kind)
      : Inst(Inst), Kind(Kind) {}

  PMOMemoryUse() : Inst(nullptr) {}

  bool isInvalid() const { return Inst == nullptr; }
  bool isValid() const { return Inst != nullptr; }
};

/// collectPMOElementUsesFrom - Analyze all uses of the specified allocation
/// instruction (alloc_box, alloc_stack or mark_uninitialized), classifying them
/// and storing the information found into the Uses and Releases lists.
LLVM_NODISCARD bool
collectPMOElementUsesFrom(const PMOMemoryObjectInfo &MemoryInfo,
                          SmallVectorImpl<PMOMemoryUse> &Uses,
                          SmallVectorImpl<SILInstruction *> &Releases);

} // end namespace swift

#endif
